Skip to content

CLI reference

The wt command ships 16 subcommands. This page is generated from windtunnel.cli's argparse tree.

Command Purpose
wt report Generate a report from a runs/ directory.
wt compare Compare results across variant labels.
wt results Summarize saved runs per label and scenario: pass counts and aggregated metrics.
wt review Record what a finished sweep changed: --decision TEXT, or --no-change. Summarized by wt results --ladder.
wt watch Follow a sweep's progress events (run started, run finished with verdict, sweep finished) and exit with the sweep's exit code.
wt run Run scenarios against a runtime.
wt batch Run a file of wt run specs (one per line, same options) back to back.
wt selftest Certify scenario gates with live golden and poison references.
wt rescore Re-score saved traces against current scenario definitions.
wt replay Replay a captured trace against a runtime.
wt doctor Bring-up check: run the reset-isolation canary against a live runtime.
wt surface Record or compare the agent's prompt-surface golden (surface diff ⇒ bench run before merge).
wt import Generate a scenario skeleton from a Contract A *.wtin.json trace.
wt validate Validate Contract A *.wtin.json interchange envelope(s).
wt triage Classify failed runs and emit a markdown report grouped by failure category.
wt skill Print or install the packaged Wind Tunnel agent skill.

Exit code conventions: 0 means success, 1 means a runtime failure, regression, world mismatch, or newly-scored outcome failure, and 2 means usage or configuration error. Two codes are specific: 75 means --no-wait found the runtime's lock held by another sweep (retry later; wt batch reports the highest code of its specs), and 124 means wt watch --timeout gave up before the sweep ended.

wt report

Generate a report from a runs/ directory.

Usage:

wt report [-h] [--runs DIR] [--out FILE] [--format {html,markdown,json}]

Arguments and options:

Name Required Default Help
--runs no runs Path to the runs/ directory (default: ./runs)
--out no Output path for file formats (HTML default: report.html).
--format no html Output format: html (default), markdown, or json. Choices: html, markdown, json.

wt compare

Compare results across variant labels.

Usage:

wt compare [-h] [--labels LABEL [LABEL ...]] [--runs DIR] [--json]

Arguments and options:

Name Required Default Help
--labels no [] Variant labels to compare (space-separated); the first label is the baseline.
--runs no runs Path to the runs/ directory (default: ./runs)
--json no false Print one JSON document: per-scenario verdicts by label, risk-ranked verdict changes, and per-scenario metric deltas against the baseline.

wt results

Summarize saved runs per label and scenario: pass counts and aggregated metrics.

Usage:

wt results [-h] [--runs DIR] [--label LABEL] [--scenario S] [--json] [--ladder]

Arguments and options:

Name Required Default Help
--runs no runs Path to the runs/ directory (default: ./runs)
--label no Variant label to summarize (the wt run --label value). Repeat for several; omit for every label under --runs.
--scenario no Only summarize scenarios matching S (shell-style globs). Repeat for multiple.
--json no false Print one JSON document with every run's trace path, verdict, and metrics alongside the per-scenario aggregates.
--ladder no false Instead of per-scenario results, summarize the experiment ladder per tier: sweeps, wall time, tokens, predictions held, and how often a reviewed sweep changed a decision (from experiments.ndjsonl).

wt review

Record what a finished sweep changed: --decision TEXT, or --no-change. Summarized by wt results --ladder.

Usage:

wt review [-h] [--runs DIR] (--decision TEXT | --no-change) SWEEP_ID

Arguments and options:

Name Required Default Help
sweep yes The sweep id wt run printed.
--runs no runs Path to the runs/ directory (default: ./runs)
--decision no What this sweep's result made you decide or change.
--no-change no false The sweep changed no decision.

wt watch

Follow a sweep's progress events (run started, run finished with verdict, sweep finished) and exit with the sweep's exit code.

Usage:

wt watch [-h] [--runs DIR] [--label LABEL] [--sweep ID] [--json] [--timeout SECONDS] [--poll SECONDS]

Arguments and options:

Name Required Default Help
--runs no runs The --runs-dir the sweep writes to (default: ./runs).
--label no Follow the running (or just-started) sweep with this variant label, or wait for the next one to start.
--sweep no Follow exactly this sweep id (printed by wt run on stderr); a sweep that already finished is replayed.
--json no false Print each event as its raw JSON line instead of a summary line.
--timeout no Give up after SECONDS and exit 124 (default: wait until the sweep ends).
--poll no 0.5 Seconds between reads of the event file (default: 0.5).

wt run

Run scenarios against a runtime.

Usage:

wt run [-h] [--scenario S] [--tag TAG] [--pack PACK] [--pack-source SOURCE] [--all-packs] [--owner OWNER] [--soul PATH] [--agents PATH] [--runtime RUNTIME] [--hook HOOK] [--label LABEL] [--runs N] [--runs-dir DIR] [--format {junit,json}] [--out FILE] [--question TEXT] [--expect {pass,fail}] [--if-pass TEXT] [--if-fail TEXT] [--budget SECONDS] [--from-trace PATH] [--from-turn K] [--scheduler SCHEDULER] [--max-concurrency N] [--no-wait]

Arguments and options:

Name Required Default Help
--scenario no Scenario name(s) to run. Repeat for multiple. Omit to run all registered scenarios (the built-in dims plus any pack installed under the 'windtunnel.scenario_packs' entry-point group) — or, with --pack-source and no --pack, all scenarios of the sourced pack(s). Shell-style globs such as 'lookup_*' are supported.
--tag no Run scenarios carrying TAG. Repeat for OR matching within tags; composes with other selectors by AND.
--pack no Run scenarios from pack PACK. Repeat for OR matching within packs; composes with other selectors by AND.
--pack-source no Load an additional local scenario pack from module:attr or path/to/file.py:attr. Repeat for multiple sources. Without --pack, the run is limited to the pack(s) these sources define (see --all-packs).
--all-packs no false With --pack-source and no --pack, sweep every registered pack (built-ins, installed packs, and the sources) instead of only the sourced pack(s).
--owner no Run scenarios from packs whose owner matches OWNER. Repeat for OR matching within owners; composes with other selectors by AND.
--soul no Path to SOUL.md / persona doc to inject.
--agents no Path to an AGENTS.md operating-notes doc to inject (routed to set-docs --agents; does not touch agent code).
--runtime no in_memory Runtime to use (default: in_memory). Either the built-in 'in_memory' (zero-infrastructure scripted runtime — no network; useful for learning the scoring model and testing scenario definitions in CI), the name of an installed runtime plugin (discovered via the 'windtunnel.runtimes' entry-point group — e.g. 'acme' from a platform driver package), or a 'module:attr' dotted path to a RuntimePlugin instance or class.
--hook no Lifecycle hook to activate for this run. Repeat for multiple hooks; built-ins include 'debrief'.
--label no Variant label for this run (recorded in traces).
--runs no 1 Number of runs per scenario (default: 1).
--runs-dir no runs Directory to write trace files (default: ./runs).
--format no Machine-readable run output format. Must be paired with --out. Choices: junit, json.
--out no Path for --format junit/json output. Must be paired with --format.
--question no What this sweep is meant to find out. Recorded in the ledger; required once the runs directory has history.
--expect no The verdict you predict (pass or fail). Recorded, and compared with the result at the end; required once the runs directory has history. Choices: pass, fail.
--if-pass no What you will do if the sweep passes. Optional; recorded, and repeated back when that is the outcome.
--if-fail no What you will do if the sweep fails. Optional; recorded, and repeated back when that is the outcome.
--budget no Wall-clock budget, counted from acquiring the runtime. May lower but not raise the tier cap (probe 300s, focused 900s by default; set in [tool.windtunnel.ladder]). Regression sweeps are uncapped by default.
--from-trace no Probe: replay a saved trace's scenario with the turns before --from-turn frozen as history, running the rest live. Needs a runtime that consumes full message history.
--from-turn no With --from-trace: the 1-based user turn to resume at (default: the last, scored user turn).
--scheduler no How scenario jobs execute: 'sequential' (default: one scenario at a time), 'concurrent' (a thread pool of up to --max-concurrency jobs, each provisioning its own handle), or 'package.module:Class' / 'path/to/file.py:Class' naming a windtunnel.spi.Scheduler subclass.
--max-concurrency no Most scenario jobs to run at once under a concurrent scheduler (default: the runtime's declared limit, or 4 when it declares none). Never exceeds the runtime plugin's max_concurrency, which defaults to 1.
--no-wait no false If another wt run holds this runtime's machine-wide lock, exit 75 at once instead of waiting for it (runtimes that declare no concurrency limit, such as in_memory, are never locked).

wt batch

Run a file of wt run specs (one per line, same options) back to back.

Usage:

wt batch [-h] [--runs-dir DIR] [--scheduler SCHEDULER] [--max-concurrency N] [--no-wait] FILE

Arguments and options:

Name Required Default Help
file yes Run specs, one per line: the options wt run takes, shell-quoted, with '#' comments and an optional leading 'wt run'. '-' reads standard input. Every line is validated before the first spec runs.
--runs-dir no Default --runs-dir for every spec (a spec's own value wins).
--scheduler no How scenario jobs execute: 'sequential' (default: one scenario at a time), 'concurrent' (a thread pool of up to --max-concurrency jobs, each provisioning its own handle), or 'package.module:Class' / 'path/to/file.py:Class' naming a windtunnel.spi.Scheduler subclass.
--max-concurrency no Most scenario jobs to run at once under a concurrent scheduler (default: the runtime's declared limit, or 4 when it declares none). Never exceeds the runtime plugin's max_concurrency, which defaults to 1.
--no-wait no false If another wt run holds this runtime's machine-wide lock, exit 75 at once instead of waiting for it (runtimes that declare no concurrency limit, such as in_memory, are never locked).

wt selftest

Certify scenario gates with live golden and poison references.

Usage:

wt selftest [-h] [--scenario S] [--tag TAG] [--pack PACK] [--pack-source SOURCE] [--owner OWNER] --runtime RUNTIME [--soul PATH] [--agents PATH] [--label LABEL] [--runs-dir DIR] [--format {junit,json}] [--out FILE]

Arguments and options:

Name Required Default Help
--scenario no Scenario name or glob to certify. Repeat for multiple; omit for all.
--tag no Certify scenarios carrying TAG. Repeat for OR matching within tags.
--pack no Certify scenarios from pack PACK. Repeat for multiple packs.
--pack-source no Load a local scenario pack from module:attr or path.py:attr.
--owner no Certify scenarios from packs whose owner matches OWNER.
--runtime yes Reference-capable runtime plugin to certify against.
--soul no Path to SOUL.md / system instructions to inject.
--agents no Path to an AGENTS.md operating-notes document to inject.
--label no Variant-label prefix for reference traces (default: selftest).
--runs-dir no runs/selftest Directory to write reference traces (default: ./runs/selftest).
--format no Machine-readable self-test output. Must be paired with --out. Choices: junit, json.
--out no Path for --format junit/json output. Must be paired with --format.

wt rescore

Re-score saved traces against current scenario definitions.

Usage:

wt rescore [-h] (--runs DIR | --trace PATH [PATH ...]) [--write] [--label LABEL] [--json] [--scenario S] [--tag TAG] [--pack PACK] [--pack-source SOURCE] [--owner OWNER]

Arguments and options:

Name Required Default Help
--runs no Walk a runs/ directory and re-score every saved trace.
--trace no Explicit trace JSON path(s) to re-score.
--write no false Update .score.json sidecars. Trace files are never modified.
--label no Only re-score traces recorded under variant label LABEL (the wt run --label value). Repeat for multiple labels; exits 2 when no trace matches.
--json no false Print one JSON document instead of per-trace lines: per trace, the old (sidecar) and new verdict and detail of every layer, the headline verdict, old and new metrics, and a summary.
--scenario no Only re-score traces whose scenario_id matches S. Repeat for multiple; shell-style globs such as 'lookup_*' are supported.
--tag no Restrict scenario definitions to packs/scenarios carrying TAG.
--pack no Restrict scenario definitions to pack PACK.
--pack-source no Load an additional local scenario pack from module:attr or path/to/file.py:attr before resolving traces.
--owner no Restrict scenario definitions to packs whose owner matches OWNER.

wt replay

Replay a captured trace against a runtime.

Usage:

wt replay [-h] --trace PATH [--runtime RUNTIME] [--runs-dir DIR]

Arguments and options:

Name Required Default Help
--trace yes Path to the trace JSON file to replay.
--runtime no in_memory Runtime to replay against: built-in 'in_memory', an installed plugin name (entry-point group 'windtunnel.runtimes'), or a 'module:attr' dotted path to a RuntimePlugin.
--runs-dir no runs Directory to write replayed traces (default: ./runs).

wt doctor

Bring-up check: run the reset-isolation canary against a live runtime.

Usage:

wt doctor [-h] [--runtime RUNTIME] [--soul PATH] [--label LABEL]

Arguments and options:

Name Required Default Help
--runtime no in_memory Runtime to check (default: in_memory). Resolved exactly like wt run --runtime: built-in 'in_memory', an installed plugin name (entry-point group 'windtunnel.runtimes'), or a 'module:attr' dotted path to a RuntimePlugin. Runs the canary in RECALL mode, which requires a live model behind the runtime — doctor is a bring-up tool, not a CI check. For CI runners without a live model, call run_reset_canary(..., probe_recall=False, state_probe=...) directly from pytest instead.
--soul no Path to SOUL.md / persona doc to inject (mirrors wt run --soul).
--label no Variant label recorded for this check (default: wt_doctor).

wt surface

Record or compare the agent's prompt-surface golden (surface diff ⇒ bench run before merge).

Usage:

wt surface [-h] {record,diff,check} ...

Subcommands:

Command Purpose
wt surface record Probe the runtime's surface and write the golden (per-segment hashes; no prompt text unless --store-text).
wt surface diff Show per-segment changes vs the golden. Informative: exits 0 even when the surface changed.
wt surface check CI gate: exit 1 on ANY surface change (or an invalid/absent surface where the golden promises one). A change means: bench before merge. An unchanged surface proves nothing — never use a passing check to skip runs.

Arguments and options:

Name Required Default Help
(none) no

wt surface record

Probe the runtime's surface and write the golden (per-segment hashes; no prompt text unless --store-text).

Usage:

wt surface record [-h] [--runtime RUNTIME] [--soul PATH] [--label LABEL] [--golden PATH] [--store-text]

Arguments and options:

Name Required Default Help
--runtime no in_memory Runtime to probe (default: in_memory). Resolved exactly like wt run --runtime. The probe provisions, resets, asks describe_surface(), and tears down — no scenarios run, no model calls.
--soul no Path to SOUL.md / persona doc to inject (mirrors wt run --soul).
--label no Variant label for the probe (default: wt_surface).
--golden no surface.golden.json Golden file path (default: surface.golden.json).
--store-text no false ALSO store the full segment text in the golden. The text is a human-facing sidecar — comparison only ever reads hashes — and it embeds the complete prompt surface: treat the file as sensitively as the system prompt itself.

wt surface diff

Show per-segment changes vs the golden. Informative: exits 0 even when the surface changed.

Usage:

wt surface diff [-h] [--runtime RUNTIME] [--soul PATH] [--label LABEL] [--golden PATH]

Arguments and options:

Name Required Default Help
--runtime no in_memory Runtime to probe (default: in_memory). Resolved exactly like wt run --runtime. The probe provisions, resets, asks describe_surface(), and tears down — no scenarios run, no model calls.
--soul no Path to SOUL.md / persona doc to inject (mirrors wt run --soul).
--label no Variant label for the probe (default: wt_surface).
--golden no surface.golden.json Golden file path (default: surface.golden.json).

wt surface check

CI gate: exit 1 on ANY surface change (or an invalid/absent surface where the golden promises one). A change means: bench before merge. An unchanged surface proves nothing — never use a passing check to skip runs.

Usage:

wt surface check [-h] [--runtime RUNTIME] [--soul PATH] [--label LABEL] [--golden PATH]

Arguments and options:

Name Required Default Help
--runtime no in_memory Runtime to probe (default: in_memory). Resolved exactly like wt run --runtime. The probe provisions, resets, asks describe_surface(), and tears down — no scenarios run, no model calls.
--soul no Path to SOUL.md / persona doc to inject (mirrors wt run --soul).
--label no Variant label for the probe (default: wt_surface).
--golden no surface.golden.json Golden file path (default: surface.golden.json).

wt import

Generate a scenario skeleton from a Contract A *.wtin.json trace.

Usage:

wt import [-h] --trace PATH --out DIR [--force]

Arguments and options:

Name Required Default Help
--trace yes Path to the Contract A *.wtin.json trace envelope.
--out yes Directory to write scenario.py, scorer.py, fixture.universe.json, and IMPORTED.md.
--force no false Allow writing into an existing non-empty directory.

wt validate

Validate Contract A *.wtin.json interchange envelope(s).

Usage:

wt validate [-h] [--strict] PATH [PATH ...]

Arguments and options:

Name Required Default Help
paths yes Path(s) to *.wtin.json envelope file(s) to validate.
--strict no false Exit 1 if any file produces a lint warning (e.g. truncated/redacted values, unpaired tool_call_response ids), not only on schema errors.

wt triage

Classify failed runs and emit a markdown report grouped by failure category.

Usage:

wt triage [-h] [--runs DIR] [--classifier {rule_based}]

Arguments and options:

Name Required Default Help
--runs no runs Path to the runs/ directory (default: ./runs). Each trace must have a sibling .score.json file.
--classifier no rule_based Classifier to use (default: rule_based, deterministic). Choices: rule_based.

wt skill

Print or install the packaged Wind Tunnel agent skill.

Usage:

wt skill [-h] {path,install} ...

Subcommands:

Command Purpose
wt skill path Print the absolute path of the installed Wind Tunnel skill directory.
wt skill install Install the Wind Tunnel skill into an agent skills directory.

Arguments and options:

Name Required Default Help
(none) no

wt skill path

Print the absolute path of the installed Wind Tunnel skill directory.

Usage:

wt skill path [-h]

Arguments and options:

Name Required Default Help
(none) no

wt skill install

Install the Wind Tunnel skill into an agent skills directory.

Usage:

wt skill install [-h] [--dest DIR] [--copy]

Arguments and options:

Name Required Default Help
--dest no .agents/skills Directory that will receive a windtunnel skill entry (default: .agents/skills).
--copy no false Copy instead of symlinking. The copy survives package uninstall but may go stale.