Files
Bo 5224f1c808 ao gate check --dry-run: real plan-only mode (#1009)
## Summary

Bead `age-gate-check-dryrun-y8hgz` — the last High from the 2026-07-24
Go audit's dry-run family that remained OPEN after #1008. `ao gate check
--dry-run` accepted the global flag and silently executed the full
registry; it is now a real plan.

- `gates.Plan` reuses the orchestrator's existing execution-free
selection (`selectCheckPlans`) — the same scope/tier/changed-file
routing as `Run`, inside one `CheckService.Execute`, so plan and run
cannot drift structurally.
- Output: every selected and skipped check with name, tier,
blocking/advisory, and the selection reason; `--json` emits a
plan-shaped subset of the run-report contract marked `dry_run: true`;
exit 0 on a successful plan, plan-construction errors exit 2.
- Selection performs read-only repo probes (git changed-file resolution,
repo detection) but never a check body — stated in the contract comment.

## Witness tests

- A blocking would-FAIL check with a side-effect recorder neither runs
nor affects exit code under `--dry-run`; the same request without the
flag runs it and exits 1. A gates-level runner that fails the test if
*any* check routes through it is never touched by `Plan`.
- Selection parity across default (fast), `--full`, and
changed-file-routed invocations; skip reasons rendered; no PASS/FAIL
marks leak into a plan.
- Binary smoke: fast plan 29 selected/40 skipped, full plan 68/1, valid
JSON, ~0.28s (vs a real run spawning go build + python + git).

## Validation

- `go build`/`go vet` clean; `go test ./...` 2873 pass / 70 packages;
golangci 0 issues; COMMANDS.md check current
- Cross-family review: round-1 findings 1–3 were diff artifacts of the
pre-#1008 branch base (rebase resolved; nothing reverted — verifiable in
this diff); finding 4 (scope of the "executes nothing" claim) addressed
with the read-only-probes contract wording

Tracker: `age-gate-check-dryrun-y8hgz`
2026-07-29 12:59:49 +00:00
..