Skip to main content

yam command line

What yam help prints, then every command's own --help, then the help topics. yam alone prints where you are and what is next. Every command that takes --json prints one JSON document and nothing else on stdout.

yam help

yam — describe a behaviour once, bind it to the real application, replay it without a model

yam init [dir] start a project here
yam explore let an agent drive the application; its exploration becomes a proposal
yam check read, lint and compile the flows; writes .yam/plan.json
yam record record what you do in the real application as a flow; --flow binds a written one
yam run replay the plan; the exit code is the verdict
yam heal repair the bindings the interface moved, from the last run
yam ui the terminal cockpit (--tmux for the workspace)
yam serve the local service, for Yam.app and other clients

yam <command> --help options and exit codes of one command
yam help <topic> flows · bindings · exit-codes · session · adapters · agents

More, one level down: yam bindings · workflow · tool · eval · surface · migrate · repl · trajectory · host

Commands

yam init

yam init [dir] [--name <project>] [--adapter <name>] [--url <local base URL>] [--endpoint name=url[@kind]]... [--yes] [--force]

Start a project. At a terminal it asks: the project's name, the adapter, where the application runs locally, and any remote endpoints (staging, production) with their kind. With --url or --yes it takes the flags and asks nothing. It writes yam.config.yaml, flows/ with a first story that works against any application, data.yaml, and the directories the other verbs use.

--name <project> the project's name (default: the directory's)
--adapter <name> playwright, bidi, http, appium, uia or ax (default: playwright)
--url <base URL> where the application runs locally
--endpoint name=url[@kind] a remote endpoint; kind is test, staging or production; repeatable
--yes ask nothing; take the flags and the defaults
--force write into a directory that already has a project

Exit codes: 0 ok · 64 usage

yam explore

yam explore [dir] [--name <story>] [--trajectory <path.jsonl>]

Let an agent write the first draft. Serves the MCP surface for one exploration and, when the agent disconnects, compiles what it did into a proposal under proposals/<date>/ for you to review. The agent opens its own target with `surface_connect`; this command records what it does, it does not choose what it drives. --trajectory compiles one that an MCP session or the app's Session screen wrote.

--name <story> the proposed story's name
--trajectory <path.jsonl> compile an existing trajectory instead of serving a session
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 64 usage · 1 failed

yam check

yam check [dir] [--tier2] [--tier3] [--allow-model-drift] [--json]

Read, lint and compile the flows in one verb, and write .yam/plan.json. lint and compile are its two halves, kept as commands of their own.

--tier2 let the local model compile sentences the grammar refuses
--tier3 let the frontier model compile what the local model cannot
--allow-model-drift compile even though the pinned model digest changed
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 2 compile errors · 3 model unavailable

yam lint

yam lint [dir] [--json]

Read the flows and report errors and warnings without writing a plan.

--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 2 compile errors

yam compile

yam compile [dir] [--stable] [--out .yam/plan.json] [--tier2] [--tier3] [--allow-model-drift] [--json]

Compile the flows into the plan. --stable makes the output byte for byte reproducible, which is what lets the plan be committed.

--stable reproducible output; the same flows give the same bytes
--out <path> where to write the plan
--tier2 let the local model compile sentences the grammar refuses
--tier3 let the frontier model compile what the local model cannot
--allow-model-drift compile even though the pinned model digest changed
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 2 compile errors · 3 model unavailable

yam record

yam record [dir] [--name <story>] | yam record --flow <file> | yam record --all [--story <name>] [--rebind] [--gateway human|anthropic|fake] [--force-production] [--json]

Alone: record a new flow from what you do. The browser opens at the application; drive it; each click and each value you enter becomes a sentence, each element you touch a binding. Enter at the terminal ends it and writes flows/<story>.flow. With --flow or --all: bind the targets of a flow somebody wrote, by driving it step by step — your click in the browser at each unbound target (human, the default at a terminal with no credential), or a model gateway.

--name <story> the story's name when recording what you do (asked otherwise)
--all bind every unbound target in the plan
--flow <file> only this flow
--story <name> only this story; repeatable
--rebind record elements that already have a binding
--gateway human|anthropic|fake who grounds a phrase to an element: you, in the browser; a model; or the committed answers
--force-production record against a production configuration anyway
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 2 compile errors · 4 grounding failed · 5 expectation failed · 10 refused

yam run

yam run [dir] [--host playwright|none] [--flow <file>] [--story <name>] [--workers <n>] [--resume <runId> --from <stepId>] [--no-check] [--json]

Replay the plan. The plan is checked first when the flows changed; the run directory holds results, summary, audit, checkpoints and screenshots; the exit code is the verdict.

--host playwright|none inside Playwright Test, or the standalone executor (default: none)
--flow <file> only this flow
--story <name> only this story; repeatable
--workers <n> flows in parallel
--resume <runId> --from <stepId> pick a run up at a checkpoint
--no-check run the plan on disk as it is; refused when it is stale
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 1 failed · 2 compile errors · 6 healed · 11 aborted · 12 cannot resume

yam heal

yam heal [--run <id> | --from-bind-failures] [--project <dir>] [--apply] [--no-model] [--json]

Repair the bindings the interface moved. With no arguments, the last run of this project. A repair is a proposed diff and a report; --apply writes it to the store.

--run <id> the run to heal (default: the last run)
--from-bind-failures the failures a plain Playwright project wrote under .yam/
--project <dir> the project (default: the nearest one above the working directory)
--apply write the accepted repairs into the store
--no-model relocalize only; never ask a model to re-ground
--dir <bindings> the store (default: bindings/)
--runs <dir> where runs are (default: runs/)
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 7 some unrepaired · 64 usage

yam ui

yam ui [dir] [--screen flows|run] [--flow <file>] [--run <id>] [--story <name>] [--url <url> --token <t>] [--tmux] [--json] [--capture <ms>]

The terminal cockpit: four panes, the same screens and actions as Yam.app. --tmux opens the workspace: the cockpit, a shell, the audit tail and your editor in one tmux session.

Appearance follows the terminal: YAM_THEME=light|dark states it outright, and COLORFGBG is read when it does not — so the cockpit and Yam.app are on the same theme as the machine. YAM_COLOR=24bit|256|none says how much colour to send, and NO_COLOR is honoured.

--screen flows|run open on a screen
--flow <file> open on a flow
--run <id> open on a run
--story <name> open on a story
--url <url> attach to a service that is already running
--token <t> that service's bearer token
--tmux the workspace, in a tmux session named for the project
--capture <ms> draw for that long, then quit; for scripts
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 64 usage

yam runs tail

yam runs tail [--url <url> --token <t>]

The current run's events as they happen, one line each, from a running service. The workspace's bottom-right pane runs this.

--url <url> the service
--token <t> its bearer token

Exit codes: 0 ok · 64 usage

yam workspace

yam workspace [dir]

The same as yam ui --tmux.

Exit codes: 0 ok · 64 usage

yam serve

yam serve [dir] [--port 0] [--token <t>]

The local service on 127.0.0.1, behind a bearer token printed once on stdout. Yam.app, the cockpit and the SDK talk to it; every handler calls the same functions the command line calls.

--port <n> the port (default: one the system chooses)
--token <t> the bearer token (default: generated)

Exit codes: 0 ok

yam status

yam status [dir] [--json] (the same as yam with nothing after it)

Where you are and what is next: the flows, the plan, the unbound targets, the last run, and the one verb to run now. This is what yam alone prints.

--json one JSON document on stdout, nothing else

Exit codes: 0 ok

yam doctor

yam doctor [dir] [--json]

Check the host and the project: Node, the adapters, the config, the flows, the bindings, the data.

--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam repl

yam repl [dir] [--adapter <name>] [--headless] [--gateway anthropic|fake|none] [--tier2] [--tier3] [--out <flows>] [--name <flow name>] [--json]

Type sentences and watch them run against a live session; save what worked as a flow.

--adapter <name> which adapter opens the session
--headless no browser window
--gateway anthropic|fake|none who grounds a phrase
--tier2 the local model for refused sentences
--tier3 the frontier model
--out <flows> where a saved flow goes
--name <flow name> the saved flow's name
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 64 usage

yam migrate

yam migrate <src> <dest> [--keep-original] [--json] · yam migrate <dest> --from-prototype <db dir> [--project <name>]

Bring v1 and v2 flows, or a prototype database, into a v3 project.

--keep-original leave the source files beside the migrated ones
--from-prototype <dir> import the prototype's database instead of flow files
--project <name> the imported project's name
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 8 unmapped

yam host generate

yam host generate [dir] [--out .yam/specs]

Write one Playwright Test spec per flow, for a project that runs under Playwright Test directly.

--out <dir> where the specs go

Exit codes: 0 ok

yam workflow run

yam workflow run <story> [dir] [--input k=v] [--allow-side-effects] [--resume <runId> --from <stepId>] [--json]

Run one story as a function: typed inputs in, outputs on stdout as JSON. Checkpoints and audit are always on; a story not marked idempotent is refused against production.

--allow-side-effects run a story that is not marked idempotent against production
--resume <runId> --from <stepId> pick the run up at a checkpoint
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 1 failed · 6 healed · 10 refused · 11 aborted · 12 cannot resume

yam tool serve

yam tool serve [dir] [--expose "Story one,Story two"] [--stdio] [--allow-side-effects] [--json]

An MCP server whose tools are the stories: each tool's schema comes from the story's signature, each call is an audited run.

--expose <stories> which stories to expose, comma separated
--stdio serve over stdio
--allow-side-effects list stories that are not marked idempotent, even in production
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok

yam trajectory compile

yam trajectory compile <trajectory.jsonl> [dir] [--name "Story name"] [--out proposals] [--app proposed] [--json]

Turn an agent's exploration into a proposal: a flow draft, a plan fragment and unverified bindings under proposals/, for a person to read.

--name <story> the proposed story's name
--out <dir> where the proposal goes (default: proposals/)
--app <name> the app name the bindings are filed under
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 64 usage

yam bindings list

yam bindings list [--dir <bindings>] [--json]

Every binding in the store, with its phrases.

--dir <bindings> the store (default: bindings/)
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam bindings show

yam bindings show <id> [--dir <bindings>] [--json]

One binding: its candidates, its fingerprint, its provenance.

--dir <bindings> the store (default: bindings/)
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam bindings verify

yam bindings verify [--adapter <name>] [--id <id>] [--json]

Resolve every binding against the live application without acting, and report which still find exactly one element.

--adapter <name> which adapter opens the session
--id <id> only this binding; repeatable
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 1 failed

yam bindings prune

yam bindings prune [--used-in <dirs>] [--apply] [--json]

Find bindings no flow or test names any more; --apply removes them.

--used-in <dirs> where to look for uses
--apply delete what nothing uses
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface targets

yam surface targets [--url <url>] [--adapter <name>] [--json]

Discover available targets and adapter readiness on this machine.

--url <url> filter targets that can drive this URL
--adapter <name> filter to a specific adapter
--json one JSON document on stdout, nothing else

Exit codes: 0 ok

yam surface connect

yam surface connect [--url <url> | --app <name> | --attach <endpoint>] [--adapter <name>] [--headed] [--json]

Connect to a target and open a surface session. Prints the session ID on stdout. Name one target: a URL launches a browser, --attach joins one that is already running, --app drives an application that is already running.

--url <url> the URL to connect to; launches a browser
--app <name> an application that is already running, by process name
--attach <endpoint> a browser that is already running, by its DevTools endpoint
--adapter <name> which adapter to use (default: playwright)
--headed show the browser
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface describe

yam surface describe --session <id> --ref <ref> [--json]

Everything the surface knows about one element: its role, name, attributes, text and box.

--session <id> the session
--ref <ref> the element, from a snapshot
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface capabilities

yam surface capabilities --session <id> [--json]

What this target supports: which actions, which reads, which checks, and why anything is unavailable.

--session <id> the session
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface events

yam surface events --session <id> [--json]

What this session did: its redacted events, and the steps a proposal would compile from. This is what `Save as automation` promotes.

--session <id> the session
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface control

yam surface control --session <id> [--take] [--release] [--holder <name>] [--force] [--json]

Take a target, give it up, or ask who holds it. A person and an agent can drive the same session; this is how they hand over instead of racing each other. While a target is held, an action from anyone else is refused and told who has it.

--session <id> the session
--take take control
--release give it up
--holder <name> who you are
--force take a target its holder has not given up
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface request

yam surface request --session <id> --url <url> [--method GET] [--input <file.json>] [--json]

Send an HTTP request on an HTTP surface and return the response. An HTTP target has no elements to click; this is how one is driven.

--session <id> the session
--url <url> the URL, or a path joined to the session's base URL
--method <verb> GET by default
--input <file.json> the whole request — headers, body, auth — or `-` for stdin
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface screenshot

yam surface screenshot --session <id> [--path <file.png>] [--json]

A picture of the target, when the adapter can take one.

--session <id> the session
--path <file.png> where to write it
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface snapshot

yam surface snapshot --session <id> [--root <ref>] [--max-nodes <n>] [--interactive-only] [--json]

Take a semantic snapshot of the current surface state.

--session <id> the session to snapshot
--root <ref> subtree root ref
--max-nodes <n> max nodes to return
--interactive-only only interactive elements
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface act

yam surface act --session <id> --action <name> [--ref <ref>] [--ref2 <ref>] [--input <args.json>] [--json]

Perform an action on the surface.

--session <id> the session
--action <name> the action to perform (click, type, etc.)
--ref <ref> the element reference
--ref2 <ref> second reference (for dragTo)
--input <args.json> action arguments as a JSON file or - for stdin
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface read

yam surface read --session <id> --kind <kind> [--ref <ref>] [--name <attr>] [--json]

Read a value from the surface: text, value, attribute, title, url, or result.

--session <id> the session
--kind <kind> what to read: text, value, attribute, title, url, result
--ref <ref> the element reference
--name <attr> the attribute name (when kind=attribute)
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface check

yam surface check --session <id> --input <check.json> [--ref <ref>] [--json]

Check a predicate against the surface.

--session <id> the session
--input <check.json> the check predicate as a JSON file or - for stdin
--ref <ref> the element reference
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 20 · 1 failed

yam surface close

yam surface close --session <id> [--json]

Close a surface session.

--session <id> the session to close
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface sessions

yam surface sessions [--json]

List active surface sessions.

--json one JSON document on stdout, nothing else

Exit codes: 0 ok

yam surface conform

yam surface conform --adapter <name> [--only <ids>] [--report <path.md>] [--json]

Run the conformance suite against an adapter; exit 0 is conformant.

--adapter <name> the adapter under test
--only <ids> a subset of cases
--report <path.md> write the report
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 1 failed

yam surface doctor

yam surface doctor [--adapter <name>] [--json]

Whether this host can reach each adapter: the browser, the pty, the server, the permission, the session, screen recording.

--adapter <name> one of playwright, bidi, ax, uia, atspi, process, http, appium
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam surface grant

yam surface grant [--dry-run] [--json]

Ask macOS for Accessibility and Screen Recording, naming the program they attach to.

--dry-run report what would be asked, and ask nothing
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed

yam eval healing

yam eval healing [--no-model] [--report <path.md>] [--json]

The healing numbers over the sample application's variants.

--no-model relocalize only
--report <path.md> write the report
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 1 failed

yam eval grounding

yam eval grounding [--gateway anthropic|fake] [--cases <path.jsonl>] [--limit <n>] [--report <path.md>] [--json]

The recorder's grounding accuracy over a case file.

--gateway anthropic|fake who grounds
--cases <path.jsonl> the cases
--limit <n> only the first n
--report <path.md> write the report
--json one JSON document on stdout, nothing else

session options: see `yam help session`

Exit codes: 0 ok · 1 failed

yam eval compiler

yam eval compiler [--tier2] [--tier3] [--gateway local|anthropic|fake] [--only tier1,tier2] [--report <path.md>] [--json]

Exact-match accuracy of the compiler over the golden set, per tier.

--tier2 include the local model
--tier3 include the frontier model
--gateway local|anthropic|fake which backend
--only <tiers> a subset of tiers
--report <path.md> write the report
--json one JSON document on stdout, nothing else

Exit codes: 0 ok · 1 failed · 3 model unavailable

yam eval self

yam eval self [--update] [--report <path.md>] [--only <check-id>] [--side yam|external]

Yam verifies Yam: every check run two ways, and the agreement between them.

--update refresh the committed reports
--report <path.md> write the report elsewhere
--only <check-id> one check
--side yam|external one side only

Exit codes: 0 ok · 1 failed

yam eval finetune corpus

yam eval finetune corpus [--json]

Describe the local model's training corpus: the refused sentences with their reviewed steps.

--json one JSON document on stdout, nothing else

Exit codes: 0 ok

yam eval finetune export

yam eval finetune export [--out <path.jsonl>] [--json]

Export the corpus as training pairs.

--out <path.jsonl> where to write them
--json one JSON document on stdout, nothing else

Exit codes: 0 ok

Topics

yam help flows

Flows — plain sentences with a signature

A flow file has blocks. A story is the unit of behaviour; a scenario is a
story that runs in file order when there is no run block; compose names a
sequence of stories; test and run say what a run executes.

story (tags=smoke): Sign in
inputs: username: string, password: secret
Go to "/login"
Type {input.username} into the username field
Type {input.password} into the password field
Click the sign in button
The dashboard heading should be visible

test: Sign in

Targets are phrases, never selectors: "the username field", "the sign in
button". Values come from {input.name}, {data.path}, {name} for a capture in
this story, or {Story.name} for another story's capture.

Ten sentences that cover most flows:

Go to "/path" Click the sign in button
Type "text" into the username field Select "Option" in the country select
Press Enter Wait for the dashboard heading to be visible
The page title should contain "Home" The URL should contain "/dashboard"
Remember the text of the total as amount Only if the cookie banner is visible, Click the accept button

Guards: "Only if <predicate>, <sentence>" and "Unless …" skip a step without
acting. A story's onFailure is stop, continue or compensate:<story>; idempotent
is what lets a story be exposed to an agent in production.

Every pattern, with two examples each, is in the flow language reference:
docs/flow-language.md in the repository, or https://yam.svatah.com.

yam help bindings

Bindings — an element named once, in a file

A binding lives at bindings/<app>/<page>/<element>.yaml and carries a ranked
list of candidates (a test id, an id, a role and name, a label, a CSS path…)
plus a structural fingerprint. The resolver tries the candidates in order and
accepts one only when it matches exactly one element. The fingerprint is used
only when every candidate fails: healing scores every element on the page
against it, and a clear winner is proposed, never silently applied.

Three ways a binding comes to exist:

yam record --all drive the plan; click each unbound element, or let a
model gateway ground it; review, then it is written
YAM_MODE=record the same, from a plain Playwright test using bind()
yam heal propose a repair for a binding that stopped resolving

Reading the store:

yam bindings list every binding and its phrases
yam bindings show <id> one binding in full
yam bindings verify resolve every binding live, act on nothing
yam bindings prune find bindings nothing names any more

A run that only passed because a binding was healed exits 6, not 0. The
repair is a diff under .yam/ for a person to read; --apply writes it.

yam help exit-codes

Exit codes — what a script or a CI job branches on

0 ok everything passed, or the command did what it was asked
1 failed a step failed, or a check found something wrong
2 compile errors the flows, data or config have errors; nothing was written or run
3 model unavailable a model tier was asked for and its backend could not be reached
4 grounding failed record could not bind a target to an element
5 expectation failed an expectation failed while recording
6 healed the run passed only because a binding was healed; review the repair
7 some unrepaired heal could not repair every failure
8 unmapped migrate found something in the source it has no mapping for
10 refused a story that is not marked idempotent, against a production configuration
11 aborted a flow stopped under its failure policy; a compensating story may have run
12 cannot resume the plan or the bindings changed since the checkpoint
64 usage the command line itself was wrong

A healed run is its own code on purpose: the application works and a binding drifted, and a job should decide about that deliberately.

yam help session

Session options — the same on every command that opens a session

--base-url <url> where the application is
--storage-state <path.json> a saved browser state to start from (cookies, local storage)
--input k=v a value for a story's declared input; repeatable
--headed show the browser
--out <dir> where runs are written (default: runs/)
--run-id <id> name the run yourself
--endpoint <name> one of the config's endpoints: its base URL, storage
state and environment kind replace app: and environment:

Where each value comes from, first match wins:

1. the flag on the command line
2. the environment: YAM_BASE_URL, YAM_STORAGE_STATE, YAM_INPUT_<NAME>
3. the project's yam.config.yaml, under app: — or under endpoints:<name>
when --endpoint or YAM_ENDPOINT names one

A secret input belongs in the environment (YAM_INPUT_PASSWORD=…), never on
the command line, where it would be visible in the process list. It reaches
the story and is redacted in every file a run writes.

yam help adapters

Adapters — the platforms a plan can replay on

The adapter is named in yam.config.yaml under adapter:, and every adapter
implements the same surface, so the plan and the bindings do not change
between them.

playwright web, the default; needs a browser: npx playwright install chromium
bidi web through WebDriver BiDi in a stock browser (Firefox, Chrome)
http named requests from api/*.yaml; no browser
appium Android Chrome and native apps; needs an Appium server
uia Windows applications through UI Automation; Windows only
ax macOS applications through Accessibility; needs the permission
granted to the terminal (System Settings → Privacy & Security)

yam surface doctor --adapter ax|uia says whether this host is ready for a
desktop adapter. yam surface conform --adapter <name> runs the conformance
suite against one, which is how a new adapter proves itself.

yam help agents

Agents — the same plan, called by something that is not a person

yam tool serve --expose "Story one,Story two"
An MCP server whose tools are the stories. Each tool's input schema is
the story's signature; each call is a run with the agent recorded as the
invoker. A story not marked idempotent is not listed in production.

yam explore
The agent writes the first draft. Point an MCP host at `yam explore
<project>` as its server; the agent explores through the raw surface,
saying what it is trying to do at each call, and when it disconnects
the exploration is compiled into a proposal under proposals/<date>/
for you to review. `yam` then names it as the next thing to do.

yam mcp
The operations and the raw surface (snapshot, act, read, check) over
stdio, for an agent that explores. Every surface call is recorded as a
trajectory; yam trajectory compile turns one into a proposal under
proposals/ for a person to read.

The operations are compile, lint, run, record, heal, bindings and
results. Record binds the targets of a flow that exists, through a
model or the committed fixture answers — never by asking a person,
since nobody is at the session. Heal proposes repairs and writes only
when the call says apply.

yam workflow run <story> --input k=v
One story as a function, outputs on stdout as JSON; what a scheduler or
a script calls.

Replay never calls a model: what a model decided, it decided at authoring
time and it is in the files. The audit line in runs/<id>/audit.jsonl is the
only account of why the system changed.