CLI reference

whatifd --help

Commands

Command

Purpose

whatifd fork

Fork production traces, replay with a proposed change, score the diff, emit a verdict.

whatifd diff

Compare two report.json files and emit a Markdown diff.

whatifd exec-check

Validate that an exec: runner speaks the whatifd-exec/1 protocol (handshake → probe replay → shutdown).

whatifd report-migrate

Migrate a report to the current schema (v0.1 → v0.2). Human-readable indented JSON by default (--indent/--no-indent).

whatifd cache rebuild

Wipe <cache-root>/entries/ while preserving meta + lock.

whatifd cache unlock

Remove <cache-root>/.lock after a PID-alive safety check.

whatifd cache verify

Verify cache-entry structural integrity.

Check the installed version via python -c "import whatifd; print(whatifd.__version__)". __version__ reads from importlib.metadata so it always matches the distribution metadata.

whatifd fork

The main verb. Ingests traces, replays, scores, emits report. Config-file driven — there are no --source, --target, --change CLI flags; everything comes from the YAML.

whatifd fork --config whatifd.config.yaml
whatifd fork -c whatifd.config.yaml --profile review   # override reporting profile

Flags

Flag

Default

Description

--config / -c

whatifd.config.yaml

Path to the whatifd config file (.yaml / .yml / .json).

--profile / -p

(config value)

Reporting profile override; must match the config’s reporting.profile. forensic requires the forensic_acknowledgment block per cardinal #7 (two-affirmation).

--output-json

(dated default)

Write the ReportV01 JSON to this exact path instead of ./reports/whatifd-fork-<date>.json. Parent dirs are created. (v0.3)

--output-md

(dated default)

Write the Markdown report to this exact path instead of ./reports/whatifd-fork-<date>.md. (v0.3)

--print-paths

off

After writing, emit only a JSON object {report_json, report_md, verdict} to stdout (the verdict still drives the exit code). Lets CI capture the written paths + verdict without parsing the human summary. (v0.3)

Config schema lives at config reference.

The runner reference (target.runner: python:<module>:<attr>) resolves from your project root — run whatifd fork from your project directory and your own runner module imports, no install or PYTHONPATH needed. (v0.3)

Output

By default whatifd fork writes two files to ./reports/:

  • whatifd-fork-<YYYY-MM-DD>.json — the full ReportV01 JSON; consumers validate against the v0.1 schema.

  • whatifd-fork-<YYYY-MM-DD>.md — the rendered Markdown report.

Use --output-json / --output-md to control the destinations, or --print-paths to have the CLI report exactly where it wrote (and the verdict) for a CI step to consume:

whatifd fork --config whatifd.config.yaml --print-paths
# {"report_json": "reports/whatifd-fork-2026-06-04.json", "report_md": "...", "verdict": "ship"}

Exit codes

Code

Verdict

Meaning

0

Ship

Floor passed, no blocks_ship finding.

1

Don’t Ship

Floor passed, at least one blocks_ship finding (regression case).

2

Inconclusive or setup failure

Floor failed, a blocks_all finding fired, or the run never started (bad config, unknown adapter, runner load error, two-affirmation refused). Floor failure always wins per cardinal #2.

whatifd enforces your declared policy. Exit code 0 is not a safety certification; it means the run satisfied the policy you declared.

whatifd diff

Compare two reports. Descriptive, not a verdict — exits 0 on any successful render, 2 on file-level errors (missing file, parse failure, non-mapping JSON).

whatifd diff reports/2026-05-03-v3.json reports/2026-05-04-v4.json

Output is Markdown to stdout. Surfaces verdict transitions, cohort row deltas, decision-findings added/removed, and failure-count delta. The diff intentionally reads raw JSON dicts (not ReportV01) so cross-version diffs during schema migration don’t fail spuriously.

whatifd exec-check

Validate that an exec: runner speaks the whatifd-exec/1 protocol before wiring it into a full run.

whatifd exec-check "exec:node ./replay-agent.js"

Spawns the child and drives it through hello → a probe replay_request (answering any tool_lookup from a synthetic cache) → clean shutdown, printing pass/fail per behavior. Checks protocol conformance (framing, handshake, a valid ReplayOutput, teardown), not agent answer quality. Exits 0 if the runner conforms, 2 otherwise.

whatifd report-migrate

whatifd report-migrate path/to/old-report.json          # writes old-report.v0.2.json
whatifd report-migrate path/to/old-report.json --in-place
whatifd report-migrate path/to/old-report.json --no-indent   # compact canonical form

Migrates a report to the current schema (v0.1 → v0.2, injecting the required experiment_shape field). The artifact is human-readable indented JSON by default (its audience is an operator diffing v0.1 vs v0.2); pass --no-indent for the compact canonical form. Already-current reports are reported as no-ops with exit 0; malformed input exits 2 with a structured message.

whatifd cache

Cache management subcommands. The cache stores scorer results keyed by (judge_provider, judge_model_id, rubric_id, rubric_text_hash, scoring_params) so identical scoring inputs across runs are reused (cardinal #10 reproducibility, NOT validity).

whatifd cache rebuild

whatifd cache rebuild --force [--cache-root .whatifd/cache]

Wipes <cache-root>/entries/. Preserves meta.json and the lock file so the storage-layer schema-version contract stays intact; only cached values are removed.

Flag

Required

Description

--force

yes (for delete)

Safety belt: without --force the command exits with code 2 and a refusing to delete without --force message. With --force, entries are removed and exit code is 0.

--cache-root

no

Override the default .whatifd/cache path.

Exit codes:

  • 0 — clean rebuild OR no-op when <cache-root>/entries/ is already absent.

  • 2--force was omitted while entries exist (typo-protection refuse).

whatifd cache unlock

whatifd cache unlock [--allow-alive] [--cache-root .whatifd/cache]

Removes <cache-root>/.lock after a PID-alive safety check. Refuses to unlock if the recorded PID is still running. Use after a previous run was hard-killed.

Flag

Required

Description

--allow-alive

no

Override the live-PID safety check. Use only when you’re sure the recorded process is gone (e.g., the PID was inherited by an unrelated process the OS just recycled to).

--cache-root

no

Override the default .whatifd/cache path.

whatifd cache verify

whatifd cache verify [--cache-root .whatifd/cache]

Verifies cache-entry structural integrity — checks every entry parses, the digest matches its filename, and the schema version is recognized. Prints a counts summary.

Two-affirmation for forensic mode

The forensic reporting profile (which controls Sensitive[T] redaction depth — see report anatomy) requires both the config’s reporting.profile: forensic AND --profile forensic on the CLI. Either alone fails per cardinal #7. This is intentional: forensic mode reveals more user content in the artifact, and a single misconfigured field shouldn’t be enough to flip the bit.