## Loop restore: converge and crank as control flow under the verdict
contract (ADR-0017)
Intent source: `docs/plans/2026-09-03-loop-restore.md` (in this PR).
Decision record:
`docs/adr/ADR-0017-loop-as-control-flow-not-knowledge.md`.
**Why.** The 2026-07-14 single-pass cut (`482307762`) removed the
iterate loop (discovery, crank, converge, evolve, the learn write-half)
together with the unproven compounding claim, although ADR-0011 demoted
only the latter. The control flow was never demoted, and its absence
showed on 2026-09-02, when a three-lane fix needed eight validators and
two stops because the contract had no repair phase. This restores the
loop as control flow and nothing else: no knowledge store, no `ao
converge`/`ao crank`, no evolve, no canary. ADR-0004 and ADR-0011 stay
in force.
**What changes.**
- **RPI gains a bounded repair phase.** On `FAIL` or `NOT_PROVEN` with
findings, repair and re-validate freshly under the convergence law:
caller-declared `repair_rounds` (default 2); open finding set keyed by
stable `findings[].id`, union across validator families, non-growing; no
closed id reopens; the subject digest changed or, for `NOT_PROVEN`, new
digest-bound evidence resolved a named gap. Converged = fresh PASS plus
cross-family PASS on risky surfaces. Plan and Implement keep their
single dispatch. `skills/rpi/scripts/run_once.py` models the law as pure
data (33 tests): rounds are validated for shape (digest required, no
duplicate ids, no PASS with findings, no FAIL without findings),
condition 4's evidence branch needs a NOT_PROVEN previous round, a
non-FAIL current round, new evidence, and a resolved finding, and a PASS
over unchanged bytes after a FAIL is a flip that reports NOT_PROVEN.
`workflows/rpi.js` runs validation as legs (spawned or external primary,
plus a caller-supplied `crossFamily.command` on risky surfaces) merged
worst-of with a union of stable ids; a risky surface without a
cross-family leg is `diversity_unsatisfied` and never converges or
enters repair; a failed repair or re-validation returns NOT_PROVEN with
no stale verdict. Validators return `subjectDigest`, stable finding ids,
and `evidenceRefs`.
- **crank returns as a thin wave executor** (113 lines): the caller
selects the wave and the repair bound, crank invokes RPI per lane
(parallel only on disjoint write and regen scopes), runs the wave
acceptance once, returns evidence, and stops. No retry, budget, queue,
claim, lease, Git, closure, or next-work ownership. Routing golden
`rq-07-wave-execution` ranks it first.
- **validate is cross-family by default on risky surfaces**
(`cli/internal/gates/**`, `scripts/check-*.sh`, `tests/**`,
`skills/*/scripts/**`, hook policies, `lib/**`, security-scanned paths)
with the LAW-0 dispatch table: Claude orchestrating uses read-only
`codex exec`; Codex orchestrating uses an interactive Claude session in
an NTM pane, never `claude -p`. No live adapter means
`diversity_unsatisfied`, which on a risky surface is `NOT_PROVEN`. The
full literal CI command set runs once on the final integrated subject;
routine rounds keep the receipt-driven freshness contract.
- **Conformance assertions flipped under ADR-0017 only:**
`scripts/check-cathedral-cut-conformance.py` (crank live; "Stop
regardless" replaced by positive canaries for the law's four conditions;
a bounded `for` loop that compares against `repair_rounds` is required
in `run_repair_phase`, and the gate executes the law's canaries against
the reference behavior), `workflows/rpi.js`,
`skills/rpi/scripts/validate.sh`,
`evals/agentops-core/rpi-behavior.json`,
`skills/rpi/references/rpi.feature`. Every single-pass public surface
(README, AGENTS.md, PRODUCT.md, CI-CD, agent-workflow-reference,
rpi-traversal, cli/README, quickstart and demo commands, the
operating-contract and product-boundary bats, the Codex-description
oracle) now states repair to convergence.
**Known approximation, disclosed.** The Claude conveyor has no
deterministic shell primitive, so changed paths are derived by the fresh
validator (git status and diff against the clean pre-run tree) and
unioned with the implementer's report; risk is classified over that
union and unreported paths are coverage findings. A validator is still a
model; runtime derivation outside every agent is a follow-up. Family
distinctness of the cross-family leg is asserted by the caller's choice
of command and not verified by the script.
**Not in scope.** Premortem stays a single advisory judge and Plan still
only names the first check (phase boundaries unchanged). No `verdict.v2`
or `rpi-report.v1` change. The loop's own effect on outcomes is
unmeasured and owed a seeded-defect probe, like the rest of the corpus.
**Evidence on the tip.** Regen check clean; full gate green with a
HEAD-built binary; CI's bats command green; Go build/vet/test green;
golangci-lint clean; security gate quick PASS; one fresh validator over
the whole diff; one cross-family read of the design before
implementation (13 findings folded) and two of the integrated diff (9
findings in round one, 11 by round two, 15 by round three, each round
repaired and re-reviewed; the fresh validator passed the tip after round
two and the final tip 1e8adb72d passed a fresh validator (14-scenario
independent harness of the law, full gate 71/71 with a HEAD-built
binary, CI bats 1164/0) and a cross-family read by Gemini 3.8 via AGY,
which closed all six remaining residues with no new findings; Codex was
unreachable at push time).
**Follow-ups filed from the final reviews, not blockers:** the JS
violation check tests growth before reopen while Python tests reopen
first (same stop, different label when both occur in one round);
`cli/testdata/compatibility-baseline/families/{demo,quickstart}/case.json`
assert help-text substrings Cobra never prints (pre-existing, no
consumer); runtime derivation of changed paths outside every agent in
the Claude conveyor.
> **Review findings closed.** The re-check's residue (app-seam family
count) is applied in `9a2790ae7` along with the full-tier CI
settlements: regenerated documentation index (generated file, hand-edit
drifted it), regenerated CLI-surface count fixtures (top=18 sub=44
all=62), `Test-Removal-Reason` trailer for the deliberate test
deletions, and the release-tag bats output list updated to the real
changes-job set. 67/67 full-tier gates green locally. Merging on Bo's
instruction.
## What
Deletes the provably-dead 28% of the `ao` CLI and every reference to it,
per the 2026-08-23 estate audit. −19.5K lines in the lane commit plus
integration fixups.
**Removed (each with zero live consumers, verified by consumer-grep +
`go list -deps`):**
- `ao eval` — 13 subcommands, ~10.9K LOC. Its would-be consumers were
already tombstones (`scripts/eval-agentops.sh` printed `RETIRED`),
`release.yml` hardcoded `--eval pass`, release evidence recorded
`suite_count: 0`, and three of its module tests exercised subcommands
that could never register (nil composition seats).
- `ao redact` — its only declared caller
(`skills/compile/scripts/compile.sh`) never existed.
- `cli/internal/types/memrl_policy.go` + the orphan cascade it and eval
left behind (`internal/scenario`, `internal/wiki`,
`internal/runtimecmd`, `internal/redact`) — all with zero importers,
verified before and after.
- `scripts/check-memrl-health.sh` +
`examples/schedules/feedback-drain-hourly.yaml` — a health check for the
feedback loop amputated on 2026-07-14; it exits 1 on main today and the
example instructs a verb (`ao feedback-loop`) that no longer exists.
- `corpus.secret-scan` gate — vacuous: its file filter excluded the
single tracked path its globs could match, so it scanned zero files;
secrets are covered by the pinned gitleaks steps in nightly and release
(validate's quick toolchain mode skips gitleaks).
- Docs for the deleted surface:
`docs/architecture/eval-architecture.md`,
`docs/code-map/eval-lid-primitives.md`; `contracts/eval-baseline-ab.md`
already carried a RETIRED banner and stays as history (delisted from the
live index).
**Kept, deliberately:**
- `ao robot-docs` — the audit's "duplicate of `doctor robot-docs`"
premise was false: they render different handbooks (whole-CLI vs
doctor-scoped). Verified before acting.
- `completion`, `demo`, `quick-start` — interactive human furniture, not
dead code.
- `corpus.witness-dolt-jsonl-crosscheck` gate — retargeted, not retired:
its backing script is a hermetic self-test over real tracked fixtures;
globs now point at the paths it actually exercises.
- `cli/internal/evalsubstrate` — Go-dead but it is the declared mirror
of `schemas/outcomes-rubric.v1.schema.json`; retiring it needs a paired
schemas/docs/scripts decision (package doc comment records this).
- `scripts/ci-local-release.sh` eval-evidence stanza — self-contained
honest bookkeeping (`status: not_applicable`), invokes nothing removed.
**Tombstones + migration:** `eval` and `redact` added to
`removed_command_hint.go` and `docs/MIGRATION.md`; the now-false "(`ao
eval` returned in 3.3 …)" parenthetical deleted; `go-cli.md` spine and
the "Eval — the Learn seat" section updated; the dated research snapshot
got a HISTORICAL banner via the docs-scope self-declaration mechanism
(history not rewritten).
## Why
v3.6.0 binary downloads: 4 darwin-arm64, 3 linux-amd64. Only 7 of 53
shipped skills invoke `ao` at all, and none of them touch this surface.
The eval family was the single largest command surface in the CLI with
zero live consumers — 28% of non-test Go maintained for nobody.
## Evidence
- `cd cli && go build ./... && go vet ./... && go test ./...` — exit 0
(previously-failing `TestGoCLIDocSpineMatchesApprovedSpine` and
`TestRemovedVerbsHaveMigrationRows` now pass)
- `scripts/check-docs-cli-snippets.sh` PASS ·
`check-cmdao-surface-parity.sh` PASS (54 leaf commands) ·
`check-corpus-path-guard.sh` PASS · `check-new-scripts-use-preamble.sh`
PASS · `ao gate check --dry-run` PASS
- Implemented by a worktree-isolated lane, independently validated by a
fresh context that re-ran the suite itself; the two failures it found
were doc files outside the lane's write scope, fixed in the integration
commit. Cross-family (Codex) review verdict included in the final
session report.
## Cross-family review (Codex, fresh context)
First pass: **FAIL** with two majors — (1) `quality.DeprecatedCommands`
still mapped five rewrite entries onto the removed eval family, so `ao
doctor --fix` would have introduced dead commands; (2) retained docs
(formal-verification research links, applied-ood README run block,
evalsubstrate hint strings) still prescribed removed commands. Both
repaired in `4da85a0d4` (one bounded round), plus its two minors
(types/AGENTS.md row, .gitignore unignore, family counts,
gitleaks-coverage comment). Re-verified: full suite green, snippets gate
PASS. Focused re-check: first-round findings confirmed closed; one new
residue (the family count above) stopped the loop under the spiral rule.
## Follow-ups (not in this PR)
- `cli/internal/quality/stale_refs.go` `DeprecatedCommands`: the five
eval-target entries are pruned here; the older pre-existing dead targets
(forge, inject, flywheel, ratchet, …) still need a map-wide
reconciliation against the live registry.
- `cli/internal/evalsubstrate` retirement decision (paired
schemas/docs/scripts change).
- `evals/scenarios/applied-ood/`, `evals/tier2-premortem/`,
`evals/_stats/` retain historical `ao eval` mentions in prereg/holdout
records — dated artifacts, left as history.
Executes docs/plans/2026-08-07-agentops-operations-layer-alignment.md:
AgentOps is the operations layer for agentic engineering; the federated
integration graph is the topology, the semantic work-and-proof protocol
is the contract, and RPI is the standard one-experiment traversal.
Retires the ao flywheel command family and all knowledge-flywheel
product state, tombstones the seven-move operating-loop workflow,
narrows ao init and the .agents state writers to declared destinations,
renames the core architecture page to rpi-traversal.md with a
compatibility redirect, aligns AGENTS.md, 25 skills, public and package
copy, regenerates every owned projection, and strengthens the
conformance gates with planted-negative proofs.
Both the alignment subject and the follow-up gate-bookkeeping commit
carry fresh author-distinct validation PASS verdicts with empty
not_checked scope.
Test-Removal-Reason: dead knowledge-flywheel and session-store surfaces were deleted with their tests (operations-layer alignment)
Keep fresh author-distinct validation mandatory while making verdict and report persistence consumer-driven. Align the RPI/Validate contracts, executable behavior, current guidance, regression coverage, and generated projections; preserve the Gas City 1.4 cutover.
The compatibility oracle that consumed these baselines was removed: its driver
script scripts/check-go-cli-compatibility.sh (926 lines) was deleted in
482307762. Re-verified reachability repo-wide (scripts/, .github/workflows/,
Makefiles, Go tree): nothing references compatibility-baseline/{profiles,v2}.
The live archcheck loaders (internal/archcheck/family.go, inventory.go) read
only families/<f>/{ownership,lineage}.json, which is left intact. v3/ is also
left intact.
Also delete tests/go_cli_clean_architecture.bats: it is dead (CI runs only
tests/scripts/*.bats plus named files; this top-level bats file is invoked by
no runner, no scenario linkage references its @test names) and three of its
@test blocks (S5, S2) call the already-deleted check-go-cli-compatibility.sh.
Removed bats @test blocks: S5, S1 (x2), S4 (x2), S2, S3, S6.
Completes the Go CLI migration: one composition model, no tombstones, no
dead packages, hermetic tests, and a cross-language verdict contract
corpus. Net −88k lines.
## What
- **Tombstones + prune deleted**: the 21 registered 'Removed in the
Cathedral Cut' stubs, `zzz_default_spine.go`'s runtime prune, and the
~30 registered-but-pruned legacy command families are gone. The
registered tree IS the production tree; retired verbs fail with exit 1 +
a data-driven replacement hint pointing at docs/MIGRATION.md (now
covering nested verbs). Non-runnable parents reject unknown subcommands
instead of printing help with exit 0.
- **~35 unreachable packages deleted** (cliapp, canon, pawl-era verdict
parser, pool, harvest, knowledge, wiki engine, vibecheck, cli/embedded,
…) after import analysis + script/CI consumer verification. Retained
with named consumers: archcheck (arch gate scripts), eval island (rubric
schema drift guards), drwitness/drrebuild (witness-crosscheck).
- **Composition decision recorded**: single cmd/ao root +
internal/commands/<family> modules + clicontract; the never-adopted
cliapp.BuildRoot is removed. New overview: docs/architecture/go-cli.md
(repairs two broken links).
- **Hermetic tests**: flag_matrix_test.go builds the exec'd binary from
package source — the stale cli/bin/ao failure class is closed;
TestHermeticBinaryMatchesSourceSurface pins source↔binary
correspondence.
- **Verdict contract consolidated**: verification extracted from
status.go into cli/internal/verdictcheck; 19-case golden corpus
(tests/fixtures/verdict-contract) runs through Go + Python + JSON schema
via blocking gate `contract.verdict-corpus`. One divergence closed
(empty-string context ids now rejected by all three).
- **Fossil guard chains retired**: embedded-sync (mirror had zero
importers), retired-family compat baselines, dead
go_cli_compatibility.bats, stale gate globs/grandfather entries.
## Evidence
- Full-tier deterministic gate: **66/66 PASS** on the rebased tip (fresh
source-built binary)
- cli suite 2,795 tests green; race suite green on touched packages; vet
+ pinned golangci-lint clean
- Cathedral Cut conformance PASS; CLI reference/surface projections
regenerated and checked
- Independent cross-family validation (fresh Codex context, frozen SHA):
5/6 claims CONFIRMED; 6th NOT_PROVEN only due to sandbox networking (its
cached rerun of the same suite passed)
- Test-count decrease is deliberate and carried by a Test-Removal-Reason
trailer