---
name: ansight-ui-testing
description: Create, edit, validate, and run Ansight UI testing definitions through the CLI. Use when Codex should author agentic workspace tests under ansight/tests, deterministic repository tasks under ansight/tasks, event-driven triggers under ansight/triggers, or choose the right definition for a reusable Ansight verification workflow. Follow the current workspace-definition contracts and use structured CLI output for discovery and verification.
---

# Ansight UI Testing

Create repository-owned Ansight tests, tasks, and triggers beside the app source. Keep definitions app-scoped, observable, and reusable through the Ansight CLI.

## Choose The Definition

| Definition | Location | Use when |
| --- | --- | --- |
| Test | `ansight/tests/**/*.json` | A bounded agent should execute a natural-language scenario and verify its final state |
| Task | `ansight/tasks/**/*.ts` | A user or agent should explicitly invoke a deterministic, repeatable workflow with named assertions |
| Trigger | `ansight/triggers/**/*.ts` | Ansight should react automatically to one normalized app or session event |

Do not put executable task or trigger behavior in a test. Tests discover repository tasks and triggers independently for the selected app. Do not use a trigger as a scheduler or general background listener.

## Authoring Workflow

1. Resolve the repository root and exact App ID with `ansight app list --json`. Inspect existing `ansight` definitions first and preserve their naming style.
2. Choose the definition from the table. Prefer a test for agentic end-to-end behavior, a task for an on-demand deterministic cycle, and a trigger only for an event-driven reaction.
3. Create a `.ts` file in the matching directory. Derive a stable ID from its relative path by removing the extension and replacing directory separators with dots; for example, `ansight/tasks/map/validate-areas.ts` becomes `map.validate-areas`.
4. Use contract version `1`. Keep task and trigger descriptors as statically extractable JSON-compatible object literals with double-quoted keys and values. Do not use variables, spreads, functions, or computed values inside a descriptor.
5. Reuse the canonical `ansight-task.d.ts` or `ansight-automation.d.ts` declaration already in the module tree and make the `import type` path relative to it. When declarations are absent, run `ansight workspace init <repository-root> --app-id <app-id>` to create the canonical support files without replacing existing definitions. The matching Ansight host source is also authoritative; never invent the runtime API. `.d.ts` files are editor-only and ignored during discovery.
6. Use standardized `ansight` methods and `app` suites directly. Discover user-defined app-tool IDs and schemas with `ansight session list --connected --app-id <app-id> --json` followed by `ansight app tools <session-id> --json`; do not guess them. Use `hostTools` only for an extra registered host tool called through `ansight.callTool`.
7. Validate syntax, types, and workspace discovery locally with the CLI.
8. Run a new test or task only when the user requested execution or verification. Connect a trigger only when the user asked to enable or verify it because connected triggers execute trusted local code in response to future events.

## Create A Test

Write one JSON object beneath `ansight/tests`. Require `appId`, `prompt`, and `validation`. Allow `id` to default to the path-derived ID and `name` to default to a humanized filename. Keep actions in `prompt` and observable success criteria in `validation`.

```json
{
  "schemaVersion": 1,
  "id": "onboarding.complete",
  "name": "Complete onboarding",
  "appId": "com.example.app",
  "prompt": "Launch the app and complete the onboarding flow.",
  "validation": {
    "prompt": "Verify the final signed-in home state through Ansight.",
    "assertions": [
      "The onboarding screen is absent",
      "The signed-in account name is visible"
    ]
  }
}
```

Allow `validation` to be one non-empty string. Require a validation object to contain a non-empty `prompt`, at least one non-empty assertion, or both. Keep every assertion observable through available Ansight tools. Use portable strict JSON.

## Create A Task

Write a module that exports `task` and a default function receiving `{ run, input, ansight, check }`:

```ts
import type { TaskDefinition, TaskInvocation } from "./ansight-task.d.ts";

export const task = {
  "schemaVersion": 1,
  "title": "Verify signed-in home",
  "description": "Checks that the signed-in home screen is visible.",
  "feature": "onboarding",
  "keywords": ["login", "home"],
  "inputSchema": {
    "type": "object",
    "properties": {},
    "additionalProperties": false
  },
  "timeoutSeconds": 30,
  "maximumActions": 1
} satisfies TaskDefinition;

export default async function verifySignedInHome(
  { ansight, check }: TaskInvocation
) {
  const result = await ansight.ui.assert({
    automationId: "signed-in-home",
    exists: true,
    expectedVisible: true
  });
  check.equal(result.passed, true, "signed-in-home-visible");
  return { matchCount: result.matchCount };
}
```

- Use optional `appId` when one repository serves multiple registered App IDs or an embedded host supplies repository paths.
- Keep `inputSchema` rooted at an object. Contract v1 supports root `properties`, `required`, `additionalProperties`, and scalar property `type`, `enum`, `default`, `minimum`, and `maximum`.
- Standard host methods, standard app suites, and custom `app.callTool(...)` calls need no descriptor declaration. If `ansight.callTool(...)` is necessary, list its registered name in `hostTools`.
- Await tool calls serially. Treat an undeclared explicit host call, overlapping calls, or excessive calls as rejected.
- Establish success with at least one stable, unique `check` assertion. Treat a normal return without assertions as `Inconclusive`, not `Passed`.
- Treat `outputSchema` as descriptive in contract v1; the runtime does not enforce it against the returned value.
- Keep `timeoutSeconds` within 1–300 and `maximumActions` within 1–100.

Discover tasks with `ansight task list --app-id <app-id> --repository <path> --json`. Run the exact returned task ID with `ansight task run <task-id> --app-id <app-id> --repository <path> --session-id <session-id> --json`, and treat terminal status plus named assertions as authoritative evidence.

## Create A Trigger

Write a module that exports `trigger` and a default function receiving `{ run, event, app }`:

```ts
import type {
  TriggerContext,
  TriggerDefinition
} from "./ansight-automation.d.ts";

type ErrorPayload = {
  priority: string;
  sourceEventId?: string;
};

export const trigger = {
  "schemaVersion": 1,
  "eventKind": "session.log.received",
  "conditions": [
    { "field": "payload.priority", "operator": "equals", "value": "Error" }
  ],
  "functionTimeoutMs": 100,
  "actionTimeoutSeconds": 20,
  "retry": {
    "maxAttempts": 3,
    "initialDelayMs": 250,
    "backoffMultiplier": 2,
    "maxDelayMs": 2000
  }
} satisfies TriggerDefinition;

export default async function captureFailure(
  { event, app }: TriggerContext<ErrorPayload>
) {
  return app.artifacts.request({
    providerId: "example.diagnostics",
    artifactId: "state",
    arguments: { sourceEventId: event.payload.sourceEventId ?? "" }
  });
}
```

- Match one exact normalized `eventKind`. Supported v1 kinds include `app.lifecycle.changed`, `app.event`, `app.pairing.discoveryReceived`, `app.pairing.accepted`, `app.pairing.rejected`, `session.capture.started`, `session.capture.updated`, `session.capture.stopped`, `session.capture.finalized`, `session.transfer.telemetry`, `session.transfer.log`, `session.transfer.appEvent`, `session.transfer.appProfile`, `session.transfer.screenshot`, `session.transfer.visualTree`, `session.transfer.touchInput`, `session.transfer.annotatedFeedback`, and `session.log.received`.
- Keep all conditions declarative and ANDed. Target envelope values or nested `payload.*` paths. Use `equals`, `notEquals`, `contains`, `startsWith`, `endsWith`, `exists`, or `notExists`; omit `value` only for the last two.
- Treat `eventSchema` as descriptive in contract v1. Use conditions, not that schema, to determine matches.
- Return no action or one standardized `app` method or `app.callTool(...)` action. Do not call unrelated host tools from a trigger.
- Keep `functionTimeoutMs` within 10–1,000, `actionTimeoutSeconds` within 1–300, and `retry.maxAttempts` within 1–5. Do not specify retry delays when only one attempt is allowed.
- Inspect trigger definitions with `ansight repo automation inspect <app-id> <repository-path> --json` and use only exact returned IDs.

## Validate Definitions

Run the checks that match the files changed:

```sh
tsc -p ansight/tasks/tsconfig.json
tsc -p ansight/triggers/tsconfig.json
ansight test validate . --json
ansight repo automation inspect <app-id> . --json
```

Run `tsc` only when the matching config exists. Resolve every CLI validation warning. The runtime uses Node's native type stripping for TypeScript and does not type-check modules at execution time.

## Report Results

Report the definition type, path, derived ID, App ID scope, APIs used, validation performed, and whether CLI discovery or execution was requested and completed. For task runs, report terminal status and named assertions. For trigger verification, report the matched event and attempt status without treating a missing run as success.
