<!-- Generated by scripts/ax-docs.mts from the JSDoc in registry/ouro/ax/. Do not edit; run `pnpm run docs`. -->

# ax-flows

An adopter's agent flows as data, run in CI without a model: a flow is the calls a good agent makes for one task, and the runner checks the calls' exit codes, JSON shapes and fields, error text, time, size and voice. The findings are in the `flow-review/0` format (`findings.jsonl`, one check a line, and `report.json`), with an X-ray report attached when given. The X-ray checks what every command must do; flows check what a task needs, and each finds what the other can't.

```sh
node lib/ouro/ax/flows.ts run <suite module> [--out report] [--flows a,b] [--slack n] [--xray report.json]
```

```sh
npx shadcn@latest add ourostack/ouro-ui/ax-flows#v1.3.10
```

It installs `lib/ouro/ax/flows.ts`. Its tests are `ax-flows-tests`. Every item runs on Node 22.18 or later as it is copied: the files import each other as `./<file>.ts`. The [Ouro AX guide](ax.md) shows how they fit together.

## Exports

### `at`

The values at a path, and whether the path fanned out over an array (`[]` or a filter).

```ts
function at(doc: unknown, path: string): { values: unknown[]; fan: boolean; }
```

### `checkCall`

The findings for one call: one per check its `expect` declares, then the voice checks every CLI call gets: stderr stays plain (no "I", exclamation or emoji outside quotes), JSON alone under `--json`, and no brand name the suite forbids. A path through `why`, `by`, `agent`, `audience` or `who` is an honesty check.

```ts
function checkCall(flow: Pick<Flow, "id" | "surface">, step: Pick<RunStep, "id" | "expect" | "fix" | "known">, ran: Ran, options: { tool: string; slack?: number | undefined; argv?: readonly string[] | undefined; brandNames?: readonly string[] | undefined; }): Finding[]
```

### `defineFlow`

```ts
function defineFlow<T extends Flow>(flow: T) => T
```

### `defineSuite`

```ts
function defineSuite<T extends FlowSuite>(suite: T) => T
```

### `Driver`

Calls the CLI with the words after its name and the environment for the call.

```ts
type Driver = (argv: readonly string[], env: Readonly<Record<string, string>>) => Promise<Ran>;
```

### `Expect`

```ts
interface Expect {
  exit: number | readonly number[];
  jsonShape?: readonly string[];
  fields?: Readonly<Record<string, Match>>;
  stderrRules?: readonly StderrRule[];
  maxMs?: number;
  maxBytes?: number;
  text?: { has?: readonly string[]; not?: readonly string[] };
}
```

### `Finding`

One check's result: one line of findings.jsonl (`flow-review/0`).

```ts
interface Finding {
  flow: string;
  step: string;
  surface: Flow["surface"];
  cell: null;
  check: string;
  pass: boolean;
  measured?: unknown;
  threshold?: unknown;
  selector?: string;
  evidence?: string;
  fix?: string;
  ms: number;
  rubric?: Rubric;
  known?: string;
}
```

### `Flow`

```ts
interface Flow {
  id: string;
  surface: "agent-cli" | "agent-http";
  title: string;
  steps: readonly Step[];
  rubric: readonly Rubric[];
  source?: string;
  readOnly?: boolean;
  env?: Readonly<Record<string, string>>;
  known?: Readonly<Record<string, string>>;
}
```

### `FlowReport`

```ts
interface FlowReport {
  schema: "flow-review-report/0";
  summary: {
    checks: number;
    pass: number;
    fail: number;
    unknownFail: number;
    knownPassing: { flow: string; step: string; known: string }[];
    flows: { flow: string; pass: boolean; checks: number; failed: number; known: number }[];
  };
  findings: Finding[];
  attachments: { kind: "ouro-ax-xray"; path: string; claimSetHash: string | null; checkerVersion: string | null; conformant: boolean | null }[];
}
```

### `FlowSuite`

An adopter's flows and how to run them.

```ts
interface FlowSuite {
  tool: string;
  flows: readonly Flow[];
  cli: Driver;
  start(dir: string): Promise<Mock>;
  guard(mock: Mock): void | Promise<void>;
  state?: readonly string[];
  brandNames?: readonly string[];
}
```

### `Harness`

A request to the mock's control port, such as a reply arriving; it is set-up, not checked.

```ts
interface Harness {
  path: string;
  body?: unknown;
}
```

### `HarnessStep`

```ts
interface HarnessStep {
  id: string;
  harness: Harness;
}
```

### `inProcess`

A driver that runs the tool's own entry in this process (fast): `main(argv, { stdout, stderr, env })` returns the exit code. stderr's `write` calls its callback, so a tool that waits for an announcement to flush before acting goes on.

```ts
function inProcess(main: (argv: string[], io: { stdout: { write(s: string): boolean; }; stderr: { write(s: string, done?: ((error?: Error | null | undefined) => void) | undefined): boolean; }; env: Record<string, string>; }) => number | Promise<number>): Driver
```

### `listedCommands`

The command words the overview lists: from `help --json` (the commands not hidden, with their aliases), else from `--help` text.

```ts
function listedCommands(help: Ran, overview: Ran): Set<string>
```

### `main`

The command: run a suite module, print one line per flow and per failure, write the files. Exits 1 on a failure no `known` names, and on a `known` defect that now passes.

```ts
function main(argv: readonly string[], io?: { stdout: { write(s: string): unknown; }; stderr: { write(s: string): unknown; }; }): Promise<number>
```

### `Match`

A value a JSON field must have: the value itself, a pattern (ignoring case), one of several values, or a type.

```ts
type Match = string | number | boolean | null | { re: string } | { oneOf: readonly (string | number | boolean | null)[] } | { type: "string" | "number" | "boolean" | "array" | "object" | "null" };
```

### `matches`

```ts
function matches(value: unknown, m: Match): boolean
```

### `Mock`

A started mock: where its API and control port are, what the CLI's calls need in their environment, and how to stop it.

```ts
interface Mock {
  url?: string;
  control?: string;
  env?: Readonly<Record<string, string>>;
  vars?: Readonly<Record<string, string>>;
  stop(): void | Promise<void>;
}
```

### `Ran`

What a call gave: its exit code (or HTTP status), stdout, stderr and how long it took.

```ts
interface Ran {
  code: number;
  out: string;
  err: string;
  ms: number;
}
```

### `report`

The `flow-review/0` report: a summary per flow, the findings, and the X-ray's report as an attachment. A step marked `known` that passes is listed in `knownPassing`, so the mark is removed once the defect is fixed.

```ts
function report(findings: readonly Finding[], xray?: { path: string; doc: unknown; } | undefined): FlowReport
```

### `RequestStep`

A request to the mock's HTTP API; `exit` is the status.

```ts
interface RequestStep extends StepBase {
  request: { method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; path: string; body?: unknown };
}
```

### `Rubric`

An adopter's agent flows as data, run in CI without a model: a flow is the calls a good agent makes for one task, and the runner checks the calls' exit codes, JSON shapes and fields, error text, time, size and voice. The findings are in the `flow-review/0` format (`findings.jsonl`, one check a line, and `report.json`), with an X-ray report attached when given. The X-ray checks what every command must do; flows check what a task needs, and each finds what the other can't.

```sh
node lib/ouro/ax/flows.ts run <suite module> [--out report] [--flows a,b] [--slack n] [--xray report.json]
```

```ts
type Rubric = "discoverable" | "predictable" | "honest" | "forgiving" | "fast" | "beautiful";
```

### `runFlows`

Runs the flows in order against one started mock and returns the findings. The suite's guard runs first, before any call; a flow's `harness` steps set up what happens next, and its `save`d values fill later steps' `{name}`.

```ts
function runFlows(suite: FlowSuite, mock: Mock, options: { dir: string; slack?: number | undefined; flows?: readonly Flow[] | undefined; }): Promise<Finding[]>
```

### `RunStep`

A call to the CLI: the words after the tool's name; `{name}` is a saved value.

```ts
interface RunStep extends StepBase {
  run: readonly string[];
}
```

### `runSuite`

Starts the suite's mock in `<out>/work`, runs the flows, writes `findings.jsonl` and `report.json` to `out`, and stops the mock.

```ts
function runSuite(suite: FlowSuite, options: { out: string; only?: readonly string[] | undefined; slack?: number | undefined; xray?: string | undefined; }): Promise<FlowReport>
```

### `spawned`

A driver that runs a built or installed CLI as its own process: the command's words, then the step's.

```ts
function spawned(command: readonly string[], timeoutMs?: number): Driver
```

### `StderrRule`

Nothing on stderr ("empty"), or one line ("oneLine") that names what to do instead ("namesFix"), without a stack trace ("noStack").

```ts
type StderrRule = "empty" | "oneLine" | "namesFix" | "noStack";
```

### `Step`

```ts
type Step = RunStep | RequestStep | HarnessStep;
```

### `unlisted`

The command words a flow runs successfully that the overview does not list: an agent reading `--help` alone won't find them.

```ts
function unlisted(flow: Flow, listed: ReadonlySet<string>): string[]
```
