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.