## What
Extend `ao provenance mine-session` with `--view excerpts` and an
explicit instruction target. Native agents can inspect bounded
Codex/Claude records with literal text, JSON field pointers, exact byte
spans and SHA-256 identities, then propose a supported skill, AGENTS.md
or task-prompt edit. Existing event JSONL and checkpoint behavior stay
the default.
## Why
Instruction improvement needs precise session evidence. The existing
normalized parser truncates long text and does not provide bounded,
directly citable extraction. This view supplies the deterministic
reading step; native agents retain interpretation and review.
## How I tested
- Focused application and command regressions passed, including legacy
checkpoints, native message/tool forms, long Unicode text, malformed
data, continuation, limits and writer errors.
- Source-built AO extracted nine selected records from real AgentOps
sessions. All selected range hashes matched; analysis produced one
candidate prompt clarification and one justified no-change finding.
Private source material and proposals remain outside Git. This
demonstrates usability, not causal uplift.
- Go build/vet/race-shuffle passed. Full gates: 73/73 passed, including
lint. Aggregate: 10 passed, one optional absence. Generated projections
passed. All seven GitHub checks passed at
`31c128015a2e48a2a787b165e02966938381cf65`, including Linux, Windows and
security.
- A fresh author-distinct reviewer verified all eight changed paths,
exact source ranges and targets, the private proposal/no-change support,
and the clean-commit demo binary; no implementation or support findings.
The command reads explicit authorized files and writes JSON to stdout.
It runs no model, creates no index or checkpoint in excerpt mode, and
automatically edits or publishes nothing. It does not enforce
restricted-source isolation or redact output.
## Checklist
- [x] Required Go build, vet and tests pass
- [x] No private session content or credentials added to this diff
- [x] Existing event interface preserved; new flags documented
## Change
Make native coding-agent execution the default AgentOps entry path with
zero mandatory skills. Preserve full bundles and add repeatable `ao
skills link --skill NAME` selection, validating the entire selection
before writes. Align product, installation, architecture and generated
command documentation.
Extend the existing trial readout to separate endpoint test results,
execution state and independently accepted work. Bind supplied judgments
to exact content, acceptance and native evidence. Reject empty
implementation subjects and require the caller's complete criterion ID
set before reporting acceptance. Preserve genuine nonempty and
deletion-only subjects, valid failures and missing-proof outcomes.
## Validation
- Native onboarding from empty home/consumer directories produces no
setup files; selective/full linking and failure boundaries are covered.
- Actual RED/GREEN regressions cover empty subjects and the
partial-criterion omission found by independent review.
- Full Go build, vet and race/shuffle tests; affected Go lint; 88 Python
readout/statistics tests passed.
- All 73 gates, generated projections, strict documentation build and
local aggregate passed (10 passed; one documented optional absence).
- All nine PR checks succeeded at
`7df0d42b12f35ffc22008cc10a40339afcfbb6a0`.
- Fresh author-distinct review passed all six acceptance criteria over
all 59 changed paths, with no findings or unchecked scope, after
repairing the criterion-coverage finding.
## Evidence limits
The real native coding repair demonstrates usability, not comparative
skill uplift. The strict live-session machine replay remains NOT_PROVEN
where execution/identity observations are unavailable; the source review
PASS is retained separately. Existing cohort limits and the historical
aggregate-enforcement gap remain unwaived. No new comparative cohort,
scheduler, skill-corpus deletion, memory migration or global
installation is included.
A failed mining-checkpoint write could truncate the saved watermark and
cause retry to replay older events. The CLI also wrote evidence to
external roots that status could not inspect. This batch fixes those
behaviors and removes duplicate normalization from skill search.
- Mining checkpoints use the existing atomic storage writer. A real
partial-write regression test proves old bytes survive and retry retains
stable event IDs. Existing mode bits are preserved; new checkpoints use
0600. Symlink and special-file destinations are rejected before reading.
Before replacement, an empty same-directory probe checks ownership and
permission metadata, including ACLs and inherited permissions.
Unverifiable or different metadata returns an error and leaves the prior
checkpoint intact. This is a conservative refusal, not ACL migration. A
directory-sync error after rename can leave the new state visible.
- `ao status --evidence-root PATH` inspects an explicit existing non-Git
store, with matching text/JSON/YAML reports, no fallback on invalid
roots, and no reads through evidence symlinks. Omitted-flag behavior
remains unchanged.
- Skill-query normalization has one implementation, preserving
repetition versus first-occurrence semantics. Nine fixed shipped-catalog
queries remain byte-identical against a source-pinned baseline.
Validation: Go build, vet, tests, race/shuffle with atomic coverage,
repository Bats and aggregate suites, regeneration, applicable gates and
lint passed. Final Linux and Windows correctness CI and all required
checks passed. A fresh author-distinct review verified every acceptance
criterion across all 23 changed paths with no unchecked scope. Nine
production-query outputs match the source-pinned baseline.
The first CI attempt exposed a test-child coverage flush under its
temporary file-size limit; the test now restores that limit before exit.
Fresh review then exposed ACL loss despite green CI. The permission
guard and native regression tests repair that defect while preserving
the original access requirement. The failure cases and repair costs are
retained in the evaluation.
Adds two explicit read-only operations for the context delivery
lifecycle: bounded raw source reads with reversible bytes and integrity
checks, and structural checking of the pinned AgentOps OKF page profile.
Source reads require independently selected context policy and enforce a
measured serialized-output bound before emitting content. Emitted bytes
do not establish host delivery or understanding; restricted-source
processing remains unavailable without native enforcement. The OKF
checker rejects missing status and incompatible profiles, and never
grants truth, disclosure, or usefulness approval.
Validation: focused tests and Linux/Windows source-reader builds passed.
The combined candidate is undergoing the required full repository checks
and fresh independent review before landing.
Add explicit, recoverable private context routing through `ao config
context`, binding native source, owner, task, model and destination to
existing policy and external storage. Recovery reads the original Beads
maintenance anchor; configuration reports native access enforcement as
unattested.
Add `ao provenance verify-judgments` to check required review profiles
against exact native transcript receipts, independent subject and
acceptance, distinct contexts, completion and permitted providers.
Requested identity and unreported effort do not count as runtime
evidence. The verdict schema is unchanged.
Repair the existing cleanup test: a 0.3-second budget could expire
during preparation before either fixture process started. A separate
controlled-delay test now proves preparation cannot renew that deadline.
The running-cleanup case requires parent/child readiness, preserved
partial output, the postlaunch cleanup result and both processes stopped
within its existing four-second bound. Production timeout behavior is
unchanged.
Validation: fresh author-distinct review passed the exact 55-path final
subject and all T05/T21 acceptance. The complete local Bats run passed
(1,333 passed, two existing skips), as did Go build/vet/test/race, all
72 full-mode gates, the aggregate and generated-output checks.
Ubuntu/Windows CI, security and both installation jobs passed on the
final commit. The final evidence scan found no new orphaned bindings; 73
historical bindings remain preserved. Earlier failed results and private
evidence remain outside the PR.
AO now performs intent snapshots, subject manifests, strict evidence
verification, atomic verdict storage, and orphan inspection through the
Go binary. The command handler keeps verification separate from
presentation so it meets the existing complexity limit. These operations
preserve the existing evidence formats, require explicit protected
storage where applicable, and run outside a checkout without Python. The
unchanged Python implementation remains a developer oracle; agents still
provide semantic judgment.
Codex and Claude skills now default to a fresh reviewer from the
author’s model family. Callers can explicitly request cross-model review
or pin its model. Reviewer adapters use a finite caller timeout or
remaining deadline instead of a fixed ten-minute default, while
retaining output limits and abnormal-termination cleanup.
Validation: Go build, vet, tests and race/shuffle tests; 1,334 shell
tests; aggregate runner; regeneration check; 72 full-mode gates.
Independent checks exercised 84 storage-boundary rejections and 21
evidence operations with an empty PATH. Both canonical and generated RPI
reference suites pass all 48 tests after updating the migrated oracle
import without weakening assertions.
Change-sensitive checks explicitly compare the final committed candidate
with the original PR base. Linux, Windows, installer, security, and
required summary checks are green.
## 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.
Summary:
- lands the audited current WIP lanes and excludes stale/process-only
material
- hardens prune path confinement, probe-v3 evidence binding,
codebase-recon identity, handoff/release/reverse-engineer behavior, and
Codex prompt handling
- truth-labels static skill scoring and regenerates all owning
projections
Validation:
- fresh independent PASS on commit
427098ed10
- full Go suite and full Go race suite
- quick local release CI
- focused Bats, scenario/linkage, native-skill, reverse-engineer,
Cathedral, Ruff, Python ratchet, projection, and diff checks
Residual boundaries:
- final-basename ABA remains unclaimed
- live skill-probe coverage remains honestly 0/12
- release-only cross-build, SBOM, vulnerability, and release-evidence
checks are left to delivery CI
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)
## Defect
Gas City materializes Codex session homes with project-local hooks. The
first
Codex process in an untrusted home can stop at the interactive
workspace/hook
trust dialog, leaving the agent pane alive but unable to take dispatched
work.
Codex persists two independent decisions in `$CODEX_HOME/config.toml`:
1. workspace trust under `[projects."<dir>"]`
2. one content hash per hook under `[hooks.state."<hook-key>"]`
Trusting a parent directory does not trust a session home, and the hook
digest
input is intentionally owned by Codex rather than reimplemented here.
## Change
`ao gc prepare` now discovers the Gas City directories that exist when
it runs
(city and rig roots, materialized agent homes, and materialized rig
worktrees)
and pre-seeds both trust layers for those exact targets.
- Hook identities and current hashes come from Codex's `hooks/list`
app-server
method.
- Returned hooks are restricted to the discovered targets; user- or
plugin-level hooks are never granted trust by this command.
- Explicit operator decisions are preserved. An untrusted workspace,
modified
hook, disabled hook, unusable hash, malformed response, or malformed
TOML
fails loudly rather than being rewritten or accepted as complete.
- The merged TOML is validated in memory and installed with the CLI's
durable
atomic writer while preserving existing permissions.
- `ao gc check` verifies the same values from local files only. It
starts no
Codex subprocess and writes nothing.
## Deliberate boundary
Discovery is filesystem-based. A home Gas City creates *after* `prepare`
is not
pre-seeded by an earlier invocation. `prepare` compares configured agent
identities with materialized homes and warns about missing homes,
including the
real dotted-name shape (`gastown.mayor` → `.gc/agents/mayor`). The
operational
rule is documented explicitly:
```text
prepare → start the city → prepare again → dispatch
```
This PR does not claim that one pre-start invocation covers future homes
or
that every future pane can never encounter a prompt.
## Evidence
Automated tests cover:
- value-based workspace and hook trust, including `enabled = false`
- malformed/unexpected `hooks/list` responses
- regular local `hooks.json` files that derive zero hook identities
(`{}`,
`{"hooks":null}`, and `{"hooks":{}}`), keeping `prepare` and `check`
aligned
- real TOML spellings, invalid merges, idempotence, and mode
preservation
- target filtering and derived hook-key fidelity
- subprocess-free `check`
- missing-home identity reporting for nested and dotted qualified names
- operation with no Codex binary
- package-wide HOME isolation
An isolated real-Codex smoke on a disposable Gas City home established
the
behavioral differential: with the home's trust entries removed, Codex
rendered
the trust dialog; after seeding the same home, it reached the composer
without
the prompt. This proves the existing-home mechanism, not future-home
timing.
Final recovery checks on commit
`a4b52b2354b9f96e5e10e07b2916339c87190bfc`:
```text
go test -count=1 ./internal/gcmaintainer
ok github.com/boshu2/agentops/cli/internal/gcmaintainer 11.943s
go test -race -shuffle=on -count=2 ./internal/gcmaintainer
PASS
go test -count=1 ./internal/testsupport
PASS
go vet ./internal/gcmaintainer ./internal/testsupport
PASS
scripts/check-test-home-isolation.sh
PASS
scripts/check-test-isolation.sh
PASS (raw os.Setenv remains at the 10/10 baseline)
GOCACHE=/private/tmp/agentops-gocache \
GOLANGCI_LINT_CACHE=/private/tmp/agentops-golangci-cache \
WORKTREE_DISPOSITION_CI_SKIP=1 \
./bin/ao gate check --full --workflow-coverage --require-workflow-parity
PASS (68/68 full/head checks)
GOCACHE=/private/tmp/agentops-gocache bash scripts/regen-all.sh --check
PASS
git diff --check
PASS
```
Recovery fixed the prior CI findings with `storage.AtomicWriteFile`,
package-wide
HOME isolation, and `json.Encoder.Encode`. The first repaired CI replay
exposed
one further ratchet: raw `os.Setenv` calls in the new `_test.go`
TestMain raised
the repository baseline from 10 to 12. The final commit moves that
one-time
setup into the existing shared test-support boundary, keeps environment
changes
outside `m.Run`, and teaches the HOME-isolation gate only the exact safe
helper
shape. CI will rerun on the exact pushed commit.
Four defects observed live in a fresh-install smoke test of the `ao`
CLI: two in `ao gate check`, one in `ao doctor`, one in `ao init`. Each
is fixed at its root and pinned by L2 tests driven through the real
entry points in fixture repos.
## 1. `ao gate check` in a zero-commit repo died raw
**Observed** — in a repository between `git init` and the first commit:
```
gates: detect changed files: git show --name-only --pretty=format: HEAD: exit status 128
```
**Fix** (`cli/internal/gates/changedfiles.go`) —
`GitChangedFiles.Changed` translates the failure into the
`ErrUnbornHead` sentinel with a remedy. Translation runs only on the
failure path (the happy path keeps its single git invocation) and only
for HEAD-resolving scopes. Two probes keep the neighbouring causes
distinct: `rev-parse --git-dir` separates "not a git repository",
`rev-parse --verify HEAD` separates "bad revision in an explicit range".
`--scope staged` is excluded on purpose — `git diff --cached` works
before the first commit, which is why the message points there.
Live, after the fix:
```
ao gate: gate check: gates: detect changed files: no commits yet (unborn HEAD): scope "head"
needs a commit to compare against — make an initial commit, or run with an explicit scope
such as --scope staged after 'git add'
```
**Tests** (`changedfiles_test.go`, `gates_test.go`) — unborn-HEAD
fixture across `head`/`worktree`/`upstream`/`range`:
`errors.Is(ErrUnbornHead)`, no leaked git exit status, remedy text
present. Plus: the advertised `--scope staged` escape hatch actually
returns the staged set; two negative witnesses (non-repo, nonexistent
range base) keep git's own error; an orchestrator-level test proves the
message survives the `gates: detect changed files: %w` wrap.
## 2. Gate failed shellcheck on AgentOps' own installed skill scripts,
with an unusable repair hint
**Observed** — after `ao init` and a first commit in a user's own repo,
`shell.shellcheck-changed` FAILED on
`.agents/skills/cass/scripts/multi_machine_search.sh` (matched by the
`**/*.sh` glob), and the repair text read `inspect native gate
shell.shellcheck-changed in cli/internal/gates` — a path that does not
exist on a machine that installed the CLI.
**Fix A — scope** (`gates/routing.go`, `gates/orchestrator.go`,
`checks/native_inline.go`): paths under `.agents/skills/`,
`.claude/skills/`, `.codex/skills/`, `.gemini/skills/`,
`.cursor/skills/`, `.pi/skills/`, `agent/skills/` are installed copies
owned by their upstream source, never repository source. They are
dropped from the change set once, in the orchestrator, so routing and
every check's `RunContext` describe the same in-scope set; the native
checks' own `origin/main...HEAD` fallback applies the same filter so
Full mode cannot re-admit them. The agentops repository tracks nothing
under those prefixes (`git ls-files` → 0), so the exclusion cannot hide
a first-party change from a gate.
**Fix B — hints** (`gates/gates.go`, `checks/native_inline.go`): the
derived native-check hint now names the gate ID and the published docs
(`GateDocsURL`) instead of a Go source path, and all four native checks
carry an explicit plain-language remedy. Script-backed hints are
unchanged by design — `ScriptRunner` returns a first-class
not-applicable SKIP outside the agentops repo, so their `bash
scripts/...` rerun addresses a reader who has the checkout by
construction.
Live, after the fix — installed copies pass, a first-party file still
fails:
```
FAIL shell.shellcheck-changed | selected: changed file "scripts/deploy.sh" matched "**/*.sh"
| repair: run 'shellcheck -S warning <file>' on each reported shell file and fix the
warnings (install shellcheck if it is missing)
```
**Tests** (`checks/native_inline_test.go`, `gates/routing_test.go`,
`gates/gates_test.go`) — L2 through the real registry + real
orchestrator + real git + real shellcheck: a fixture repo whose only
shellcheck-triggering files are installed skill copies exits 0;
**negative witness** — the same bad script under `scripts/` still exits
1, so the filter narrows scope rather than defanging the gate. Plus a
routing-layer selection test with the same witness, a
path-classification table including near-misses (`skills/`,
`vendor/.claude/skills/`, `.agents/ao/learnings/`), and a registry-wide
invariant that no native check's effective repair hint names a
source-checkout path.
## 3. `ao doctor` gave installed users checkout-only advice and could
overcount broken links
**Observed** — audience `installed-user` was told to run `ao skills link
--dry-run` "from the AgentOps checkout" (they have none, and the command
fails closed outside one), alongside a "2 broken" count that a
dangling-symlink sweep did not corroborate.
**Fix** (`cli/internal/adapters/doctor/legacy.go`):
- *Advice*: the no-checkout branch now names the affected root and the
dangling count, and gives a remedy performable from where the reader
stands — remove the dangling links, then reinstall skills the way they
were installed (plugin, brew, or npx).
- *Counting*: `countLiveSkillLinks` derived brokenness from a single
`os.Stat(<link>/SKILL.md)` probe, which fails for **three** unrelated
reasons — the link dangles, the link resolves to something that is not a
skill package (a shared reference dir, or a plain file), or the target
is unreadable — and reported all three as broken. That conflation is the
overcount. It now returns a tri-state census: `Broken` means **dangling
and only dangling** (exactly what `find -L <root> -maxdepth 1 -type l`
prints), `Foreign` is a link that resolves but names no skill package,
`Live` is a working skill. Non-symlink entries (a plugin's real
directory) are counted in no bucket.
Live, after the fix — and the two reported links are genuinely dangling,
which the new wording now makes checkable:
```
! Skill Links 48 live portable skill link(s), 2 dangling (target no longer exists) under
/Users/…/.agents/skills; remove the dangling link(s), then reinstall skills
the way you installed them (plugin, brew, or npx)
```
**Tests** (`legacy_test.go`) — installed-user detail/Fix contain no
checkout-only command and *do* name the root, the count, and a remedy; a
fixture holding live + dangling + foreign-dir + foreign-file + real-dir
entries pins the exact census `{Live:2, Broken:1, Foreign:2}`; a
cross-check computes the dangling count independently (not via
production code) and asserts doctor's `Broken` equals it — the test that
closes the "doctor says N, find says fewer" contradiction.
## 4. `ao init` scaffolded `.agents/ao/**` with no ignore guidance
**Observed** — after one loop the tree was full of untracked scratch and
every user had to invent the same `.gitignore` rules by hand.
**Policy decision** (`cli/internal/initapp/initapp.go`): `ao init`
appends one commented, marker-delimited block to the working directory's
`.gitignore`, creating the file if absent. It ignores only machine-local
scratch — `.agents/ao/index/` (derived), `.agents/ao/sessions/`
(private), `.agents/ao/provenance/` (per-machine, merge-hostile),
`__pycache__/`. It **deliberately does not** ignore
`.agents/ao/intents/` or `.agents/ao/verdicts/`: whether loop evidence
belongs in version control is the consumer repository's policy, and
AgentOps owns no policy there (product boundary). Delete the block to
track everything.
The block targets the working directory rather than the enclosing git
root, so its relative patterns match the `.agents/ao/**` the same run
just created. Idempotency keys on the begin marker, not the body — a
user who trims the lines inside has made a local decision, and a second
init reports and respects it. Documented in the command's `Long` help
and the regenerated `cli/docs/COMMANDS.md`.
**Tests** (`initapp_test.go`, `commands/init/module_test.go`) — init
twice in a fresh dir leaves the marker present exactly once, both at the
app layer and L2 through the cobra command; existing `.gitignore`
content is preserved verbatim with no glued last line; an edited block
is left untouched; dry-run writes nothing and announces the append; help
documents both what is ignored and what is deliberately trackable. The
pre-existing assertion that init never touches ignore state was replaced
by a narrower one — no repository is initialized — since the ignore
block is now the intended behavior.
---
## Verification
- `cd cli && go build ./... && go vet ./... && go test ./...` → **2947
passed in 73 packages**, exit 0 (captured to a file; not piped).
- `golangci-lint run` → no issues.
- `bash scripts/regen-all.sh --check` → all generated projections
current (`cli/docs/COMMANDS.md` regenerated via
`scripts/generate-cli-reference.sh`).
- All four defects re-smoked end-to-end against a freshly built binary
in a throwaway repo.
Write scope stayed inside `cli/**` plus the generated
`cli/docs/COMMANDS.md`. No `skills/**`, `AGENTS.md`, or
`docs/architecture/**` changes.
## What
Ports `scripts/gc-maintainer-ops.sh` (425 lines of bash: prepare / check
/ recover-affinity for stock Gas City rigs) into the Go CLI as **`ao gc
prepare|check|recover-affinity`**, per ADR-0016 (skill logic ships in Go
via `ao`; shell stays thin glue).
**Why:** skills ship via plugin/npx as SKILL.md only — a user without a
repo checkout could not run the commands the shipped `using-gc` skill
teaches. The skill said "From an AgentOps checkout", which was disclosed
but weak.
## Changes
- **`cli/internal/gcmaintainer`** — full port: rig/import pin
verification, bundled pack-cache resolution, PyYAML-capable python
selection, atomic runtime staging, managed check wrappers, skill links
into city/rig Codex sinks, macOS LaunchAgent + doctor/status health
checks, bounded affinity recovery. Output and refusal-message parity
with the shell script (incl. refuse-before-mutation ordering).
- **`cli/internal/commands/gc` + `cmd/ao/gc_composition.go`** — cobra
module on the shared `clicontract.HostOptions` seam; global `--dry-run`
always overrides `--apply`.
- **Skills source resolution without a checkout**: `--skills-source` >
enclosing agentops checkout > installed skills root (`~/.agents/skills`,
`~/.claude/skills`). Existing rigs stay recognized: the `managed-by:
agentops gc-maintainer-ops` wrapper marker is unchanged.
- **Tests migrated**: `tests/python/test_gc_maintainer_ops.py` (7 cases)
→ Go L2 tests in `cli/internal/gcmaintainer` with the same fake-`gc`
harness, plus module wiring tests. `scripts/check-gc-executor.sh` no
longer runs the python suite.
- **`scripts/gc-maintainer-ops.sh`** reduced to a thin wrapper exec'ing
`ao gc`, pinning `--skills-source` to its checkout to preserve
historical semantics (`--ao-bin` now selects the ao binary).
- **Docs/projections**: `skills/using-gc/SKILL.md` now teaches `ao gc
...`; codex, gemini, and executor-pack projections regenerated via their
owning generators; spine/COMMANDS.md/surface artifacts regenerated.
## Verification
- `go build ./... && go vet ./... && go test ./...` — 2923 passed, 73
packages
- `golangci-lint run` on new/touched packages — clean
- `shellcheck -S warning` on wrapper + gate script — clean
- `bash scripts/check-gc-executor.sh` — OK
- Smoke: built `ao`, ran wrapper → `ao gc` delegation end-to-end
## Summary
Bead `age-skill-overhaul-reboot-sjv7v.12` — reconciliation of the
2026-07-24 Go CLI deep audit against current main. Full table with
evidence:
`docs/audits/2026-07-28-skill-overhaul-reboot/wave-reports/s12-go-residue.md`.
**Fixed here (4):**
- **Eval identifier path containment** [High] — new
`evalsubstrate.ValidateID` at every identifier-to-path join, hardened
through two review rounds: rejects separators, absolute/volume refs,
leading/trailing space-or-dot (defeats Win32 trailing-strip
renormalization), C0+C1+DEL controls, non-UTF-8, non-NFC,
whitespace-only, >128 bytes; `ms:*` colons handled by injective one-way
`%3A` encoding at the checked `ModelSpecPath` sink (raw `%` reserved so
encoding cannot alias), both callers migrated, no unchecked join
remains.
- **`provenance add --dry-run`** [High] — was wired but never read; now
honored with a no-write witness test.
- **Live-runtime isolation dirs** — owned, cleaned on all paths, never
claims a caller-supplied root.
- **Stale eval help text** — corrected; COMMANDS.md regenerated via its
owner.
**Already landed (1):** the `--json`/`-o json` divergence for
provenance/skills was resolved by the cmd/ao carve-out (probes confirm
identical output).
**Recorded OPEN with reproductions (3):** `gate check --dry-run`
plan-only mode, bounded subprocess output streaming, and
context/process-group cancellation — each a cross-package refactor (the
audit's own G1/G2 programs), documented with fix sketches rather than
half-fixed here.
## Validation
- `go build` / `go vet` clean; `go test ./...` 2872+ pass across 70
packages; golangci-lint 0 issues on touched packages; CLI reference
check current
- Cross-family review two rounds: round 1 three findings (Windows
renormalization traversal, canonicality bounds, unchecked sink) all
fixed; round 2's one residual (non-injective colon encoding) fixed with
witness cases
Tracker: `age-skill-overhaul-reboot-sjv7v.12`
Workflows get the skills treatment (operator decision): canonical source
in the product tree, installed by a product verb, Claude-only labeled as
such.
**What moves:** all seven Claude workflow scripts + README migrate from
force-added exceptions inside the gitignored `.claude/` to a tracked
top-level `workflows/` (sibling of `skills/`) — the four existing
conveyors plus `audit-dimensions`, `verify-fixes`, `implement-wave`:
three thin, args-parameterized orchestration conveyors extracted from
this session's hand-rolled waves, contract-reviewed, and smoke-proven
through the real Workflow runtime (the smoke caught two contract gaps
static review could not: an `export default` wrapper the runtime never
invokes, and args arriving as a JSON string — both fixed, string-args
tolerance now built in).
**New verb:** `ao workflows link` / `unlink` mirror `ao skills link`
semantics — dry-run `--json`, refuse to replace real files or foreign
links, unlink only checkout-owned links — targeting the project-local
`.claude/workflows/` where Claude Code resolves named workflows
(`--into` overrides). Checkout identity reuses the skillsapp marker
discipline, fail-closed. Claude-only runtime adapter, same doctrine as
the Codex-only `skills-codex/`.
**Legacy surfaces repointed:** `install-workflows.sh` (user-global $HOME
installer), `check-workflow-drift.sh` + gate comment,
`check-bdd-foundry-markers.sh`; spine allowlist + YAML-probe excuse +
go-cli.md spine region gain the workflows group; COMMANDS.md,
cli-surface projections, and surface-count fixture regenerated; new
tests carry per-command git-env scrubbing (test-isolation ratchet back
at baseline).
**Built BY the workflow being canonized** — `implement-wave`
orchestrated its own canonization: two disjoint-ownership lanes plus a
seam-checking verifier that ran the real binary's link → resolve →
unlink cycle in the live tree (both lanes RESOLVED). The lanes correctly
*refused* to self-approve their command into the spine invariants and
handed integration three flagged edits instead.
**Expected local gate note:** `workflow.install-drift` correctly FAILS
on machines whose user-global `~/.claude/workflows` links still point at
the old location — that is the transition it exists to catch. CI stays
green (absent→skip). **Post-merge operator step:** `cd ~/dev/agentops &&
git pull && bash scripts/install-workflows.sh`.
**Verified:** full suite 63/63 pkgs; golangci-lint clean; `gate check
--full` over this range = 66/67 with only the documented install-drift
environment finding; workflows smoke-run evidence in session logs.
Wave 4 (residue) of the new-user happy-path arc. Three scoped
implementer lanes + fresh adversarial verifier (4 RESOLVED; 1 INCOMPLETE
= stale generated projections, closed in integration).
**Doctor**: the dev-version detector now reuses the Binary Freshness
resolution — a from-source build matching its checkout is healthy (a
novice building from source can finally see `ao doctor` exit 0);
findings fire only on genuine drift, shadowed duplicate `ao` binaries,
or an informational from-source note outside any checkout. `ao doctor
diff` gains `--only` so the fix-plan preview can be scoped the way
remediation text implies.
**Config**: the dead `ao config models` surface is removed end-to-end
(lane re-verified zero consumers before deleting; `--show` proven
byte-identical before/after; removed-child hint + MIGRATION row;
existing `models:` config sections still parse and are ignored).
**Dual-root stragglers**: learning-coherence gate globs,
`quality.CountConstraints`, and the eval sandbox corpus deny-list now
cover canonical `.agents/ao/<section>` alongside legacy roots.
**Doc-link hygiene**: the strict docs-link backstop's allowlist was 100%
stale (53/53 entries referenced Cathedral-Cut-deleted docs) — refreshed
to 10 verified accepted-class entries; ROADMAP dead links fixed;
documentation-index generator emits GitHub URLs for repo-root targets;
doc-skill references instruct only shipped scripts; codex twins +
CLI-surface projections + surface-count fixture regenerated.
**Deferred by design**: the `3.3.0-rc` fallback version bump belongs
inside the v3.3.0 tag-cut commit.
**Verified**: full suite 61/61 pkgs (2825 tests); golangci-lint clean;
`ao gate check --full` 67/67 over this range; Test-Removal-Reason
trailer covers the 6 deliberately deleted models tests.
Registers the fully-built-but-never-wired eval module into the CLI spine
and repairs its one real instrument bug. Three commits, RPI-shaped: wire
+ fix, drift guards + docs, fixture counts.
## What
- **`ao eval` joins the spine** (cmd/ao/eval_composition.go):
deterministic suite runs, run-record
compare/baseline/scorecard/coverage, locked Tasks, holdout scenarios,
suite A/B verdicts with power-derived n-required, and the holdout-safe
Outcomes projection. This is the operating contract's *Learn* seat — a
read/measure consumer of evidence. It reports numbers and owns no retry,
scheduling, promotion, or lifecycle authority. The retired Aliases/Bench
seats stay nil and their subcommands are omitted.
- **Instrument bug fixed**: both scenario-ab arms were corpus-denied at
runtime — the with-gold treatment was only a prompt injection through
the retired `ao lookup`, so the A/B silently guaranteed a zero delta.
Treatment is now environment-shaped: the with-gold arm runs with the
corpus readable, the control arm stays sandbox-confined (the fail-closed
empty-deny guard is untouched), and judges are always corpus-denied.
- **Schema drift guards**: a run record persisted by the production
writer must validate against eval-run.v1.schema.json (and the minimal
suite against eval-suite.v1.schema.json) — closing the same silent-fork
class the verdict.v2 golden corpus closed.
- **Hermetic test builds hardened**: keep the real HOME's Go caches
(TestMain isolation was forcing module re-downloads) and skip VCS
stamping.
- docs/architecture/go-cli.md gains the eval section; MIGRATION.md notes
`ao eval`'s return; COMMANDS.md, cli-surface, and surface counts
regenerated; new leaves carry honest allowlist reasons; new cmd/ao L2
tests drive eval run/compare/help through the full production wiring.
## Evidence
- Full cli suite 2,801 green; race green on cmd/ao + the eval island;
vet clean
- Full-tier gate: 65/66 on the penultimate commit; the single failure
(surface counts) is the final fixture commit, re-verified PASS
standalone — no other check reads those fixtures
- Fresh independent validation (Codex, frozen subject 86022a3c6, clean
tree at start and end): **OVERALL PASS**, 6/6 — including a live
end-to-end `ao eval run` producing a passing run record
- Rubric-relocation decision honored: true holdout rubrics live in the
external measurement register; the repo keeps only leak-guarded
projections
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
Ship the Cathedral Cut on the 3.x line with the intentional semver deviation documented for v3.3.0. Includes the complete release rebadge, release-gate artifacts, removed-command tombstones, and a legacy config read fallback that preserves existing settings when saving the new canonical path.\n\nValidated by the full local Go/release suite and all required GitHub checks on head 78027b009c.
Follow-up to the evidence-store move: the project and home config now resolve
at .agents/ao/config.yaml (config.go, gateway, doctor cliconfig fixes, help
text), the Claude hook-manifest probe reads ~/.agents/ao/hooks.json, and the
cc-hooks guardrail telemetry default AGENTOPS_HOME falls back to ~/.agents/ao.
The tracked tracker-pin config moved via git mv with a narrow gitignore
negation (.agents/ stays untracked except ao/config.yaml). The .agentops/
directory no longer exists and nothing in the repo writes to it.
Co-authored-by: Cursor <cursoragent@cursor.com>
Default `ao quick-start` output was ~3 screens (double banner, 4-step LIVE
PATH block, a philosophy quote, and a full readiness table). Builds on #907
(command-string correctness) with a PRESENTATION + TAILORING pass:
- Default is now a single-screen diet (<=40 lines): a one-line completion
header, a Created summary (GOALS.md marked "template — edit me"), a compact
readiness checklist, environment-tailored golden paths, exactly one Next
action (/plan) + one docs link (docs/first-value-path.md), and the tightened
first-verdict close. The ASCII banner and the "Stateful environment..."
quote are gone from the default.
- Golden paths are filtered by exec.LookPath: br/gt/codex/agy paths render only
when their binary resolves here; an absent core tool (br) collapses to one
short enable line instead of a run-this block. Skill paths (/plan) always fine.
- --verbose restores the full step-by-step long form for anyone who wants it.
Tests (L2, PATH fixtures via t.Setenv + t.TempDir):
TestQuickStart_OutputOneScreen (<=40 lines, one Next, no banner/quote),
TestQuickStart_GoldenPathsResolveInEnv (every rendered run-this argv[0]
resolves; absent tools omitted), TestQuickStart_GoldenPaths_AbsentToolEnableLine,
TestQuickStart_VerboseKeepsLongForm. Existing #907 guards
(AllAOCommandsResolve / NoRemovedCommands / journey tombstones) still pass.
Add the uninstall/rollback twin for `ao skills link`: `ao skills unlink`
removes exactly the live-tier symlinks link minted — those whose target
resolves into this repo's skills/ tree — across every installed runtime
(~/.claude, ~/.codex, ~/.gemini, ~/.cursor, ~/.pi). Idempotent and
non-destructive: foreign symlinks pointing elsewhere and real directories
(a foreign corpus such as jsm) are reported as foreign and never removed;
stale owned links (skill since removed from the repo) are still cleaned up.
Supports --dest, --dry-run (persistent), and --json, mirroring skills link.
Document the uninstall path in docs/install-day2-ops.md: per-runtime plugin/
skill removal (Claude, Codex, AGY, OpenCode), `brew uninstall agentops`,
`ao skills unlink` for clone-linked skills, and an explicit 'what is kept'
note that .agents/ and quick-start artifacts (CLAUDE.md block, GOALS.md) are
user-owned data the uninstall deliberately never touches.
Regenerate the affected command-surface projections (COMMANDS.md, cli-surface
.{json,md}, the eval surface matrix + smoke fixture). The matrix/smoke counts
also absorb pre-existing origin/main drift (checked-in expected sub=120 vs
actual tree 112); regen brings them to the truthful 113 (112 + unlink).
Tests (L2 round-trip, t.TempDir): RemovesOnlyOwnLinks (foreign symlink + real
dir survive), DryRunWritesNothing, Idempotent, MissingDestIsNoop,
RemovesStaleOwnedLink, EmptySrcFailsClosed, ResilientAcrossDests.
F2 makes the verdict->edge emit fail-closed at WRITE time, but history + crash windows can
still leave a verdict FILE with no bound EDGE — a silent desync where 'ao done' (which reads
the LEDGER, not the file) refuses the close, discovered one-refused-close at a time. This
command scans .agents/pawl-verdicts/*.json and, for every CONFIRMED/REBOUND verdict, reports
whether a matching verdict->commit edge is bound, reusing the EXACT recognizer the push gate
and ao done use (hasConfirmedVerdictEdge — exact-token disposition, sha-bound, never substring).
--emit re-emits missing edges (idempotent — only the UNBOUND ones detected this pass, so a
second run is a no-op); --force allows a dirty ledger (default: refuse, age-7krl — never sweep
another lane's rows); --json for machine output. Exit 0 clean / 1 unbound-or-emit-failed / 2
usage. Report-only default is safe (no mutation).
Operational data (this repo, per the bead): 251 verdicts scanned, 1 unbound — and it is a
harmless orphan from this session's own messy F1 land (a dead sha 6cbcc0f5 reset away during
the concurrent-session recovery), NOT a systemic desync. Validates F2 (prevents new) + F3 (finds
existing). Tests: TestScanReconcileVerdicts (bound/unbound/REFUTED-skipped/malformed) + empty-dir.
Regenerated CLI surface (COMMANDS.md, cli-surface.{json,md}, eval matrix + smoke) for the new command.
Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
'ao pawl --help' listed the warm standing-service verbs as flat peers of review,
pushing users toward operator machinery (NTM, projects_base) they cannot run.
Cobra groups now split the surface: review under 'Use the membrane (the front
door — needs no NTM, no setup)', up/down/reap/health/doctor/smoke/route/metrics
under 'Operate the standing warm service (operator-only; requires the NTM swarm
substrate)'. Grouping/labeling only — every verb keeps its name and behavior.
docs/contracts/pawls.md carries the same operator-only note.
Fix swept in: scripts/lib/sku_extract.py's cobra help parser only recognized
'*Commands:' section titles, so EVERY grouped command's subcommands were
invisible to the SKU catalog ('ao goals' was already affected; this change made
'ao pawl' the second victim and surfaced the bug). The parser now enters any
non-reserved column-0 title block and requires the 2+-space name/short gap, so
grouped subcommands resolve precisely (registry.json reflects the corrected
projection for ao goals too). Locked by TestPawlHelpPresentsReviewAsFrontDoor.
Adds .pi to the detected runtimes, so `ao skills link` also links into
~/.pi/skills when ~/.pi exists (Pi agent runtime). Same detection + resilient
fan-out; --dest, Long help, README, and the fan-out test updated. Follow-up to
age-d686g.
Bo found the gap: the command only linked ~/.claude/skills, but Codex reads
~/.codex/skills and AGY/Gemini reads ~/.gemini/skills — so Codex and AGY users
got nothing. Default now links into every runtime whose config dir exists under
$HOME (~/.claude, ~/.codex, ~/.gemini, ~/.cursor), matching link-skill --all;
--dest still overrides to a single dir. Falls back to ~/.claude/skills when no
runtime is detected. resolveTargetDests + 3 subtests (explicit/fan-out/fallback,
HOME-isolated); help guard extended to assert the Codex/AGY dirs are documented.
README + --help repositioned to the live-symlink-from-a-clone distinction across
runtimes (supersedes the reframe-only age-ivcba). E2E: --dry-run fans out to all
4 installed runtime dirs.
Non-destructive unchanged: a foreign real dir is a conflict, never clobbered.
Scans skills/ against a destination live-tier dir (default ~/.claude/skills)
and symlinks every skill dir that has no entry yet. Idempotent and
non-destructive: existing symlinks are left as present, and a name owned by a
real dir (a foreign corpus like jsm) is reported as a conflict, never clobbered.
Honors the global --dry-run; --dest overrides the target.
The focused fix for 'a new skill landed on main but Claude cannot see it':
merging a skill dir mints no symlink and /reload-skills only re-reads existing
links. 5 L2 tests cover link/idempotent/dry-run/conflict/non-skill-dir; whole
cmd/ao package green (7853 tests).
Note: the regen also re-sorts a few eval-scenario/session cli-surface entries to
the current canonical (ASCII) order — latent sort drift on the base, benign, and
required for regen-check to pass.
Reviewed: fail-closed guard added per codex-fresh-review
The one command a returning operator reads after hours of autonomy, no
transcript required (docs/architecture/the-flywheel.md — the human moves
from in the loop to ON it):
YIELD gate-verdict counts (CONFIRMED/REFUTED/ESCALATE/HOLD) since
the cutoff, catches recorded (DetectCatches over the windowed
ledger), beads closed in the window (tracker-agnostic).
ANDON QUEUE blocked beads, ESCALATE/HOLD pawl verdicts, and any REFUTED
verdict whose bead is still open (stalled slice) — id, why
parked, age; deduped, oldest-parked first.
--since accepts RFC3339 or a duration (default 24h); --json emits the full
struct. Honest empty-states (andon queue: empty — nothing parked). Beads
access reuses the ao beads exec internals (resolveTracker +
canonicalizeBDReadJSON, canonical br {issues:[...]} shape) behind an
injectable seam; tracker failure degrades to a reported beads_error, never
fatal. Executed-red TDD: seeded production-writer ledger + stubbed beads.
Flips the flywheel status-ledger row for the async governance surface to
landed.
The register's "Recurrence before → after" column — the catch→producer
loop's honesty check (ADR-0014) — was hand-typed "(pending next triage)".
This makes it mechanical: --deltas --since <ISO-date|RFC3339> splits every
catch class's round-collapsed hits around the cutoff (a producer fix's land
date) and prints before=N since=M per class, sorted still-recurring first
(since DESC, before DESC, ClassKey ASC); a 0-since class is marked improved.
Read-only: writes no checklist, never edits the register — BP.7's runner
records the numbers. --json emits the machine shape; placeholder classes
filtered as in default mode.
Timestamp source: the ledger envelope ts (RFC3339, validated on load),
carried onto CatchInstance.TS at the FIRST REFUTED round of each distinct
(bead, head) occurrence in DetectCatches (round-collapse preserved).
Docs: producer-defect-register How-to-use step 5 + BP.7 measurement
sentence (skills/post-mortem + bespoke codex twin, hashes regenerated);
COMMANDS.md + registry.json regenerated.
Absorb emit_pawl_catch's reason-extraction from scripts/pawl-review.sh into
the ao CLI (shift-left epic age-tc0l): --evidence <file> applies the same
two-tier REFUTED salvage in Go (last 'VERDICT: REFUTED <text>' sentinel, else
the first substantive 'REFUTED: <finding>' prose line — age-9931 parity —
else the placeholder), resolves domain from the first changed file's top dir
and affected paths (first 20) from git by --scope head|staged, all via the
existing buildCatchInput + yieldledger.Writer sink. Explicit --reason /
--domain / --paths win over extraction; all prior flags unchanged.
emit_pawl_catch collapses 53->30 lines to a single fail-safe ao call keeping
the || true non-blocking contract and the PAWL_CATCH_CLASS/DETECTOR
passthrough; embedded copy re-synced (make sync-hooks, validate-embedded-sync
PASS). New seam bats lock the delegated flags + non-blocking contract;
table-driven Go tests cover the salvage tiers and git-derived domain/paths.
After class-normalization the digest collapsed 22->11 classes, but the top-5 was
STILL all reason-less `pawl-review REFUTED (see evidence)` placeholders (historical,
evidence gone) — the one substantive class (a `[gates]` gate-routing gap finding) sat
buried at rank 6. Injecting placeholders into the pre-mortem checklist = pure noise.
- yieldledger.IsPlaceholderReason: conservative predicate for a non-substantive
reason — a bare/near-bare token ("r"), the pawl verdict STAMP itself (anchored
regex on "pawl-review REFUTED/CONFIRMED ..." so digit-less bead ids stripBeadRefs
leaves intact are still caught), or a reason that normalizes to only disposition
boilerplate + bead-id fragments. A real defect sentence keeps its content tokens
and is NEVER flagged (guarded by tests, incl. a reason that mentions the pawl
mid-sentence).
- ao membrane digest EXCLUDES placeholder classes by default so real-reason classes
lead the emitted .agents/pre-mortem-checks/catch-digest.md; --include-placeholders
restores them for corpus auditing, always ranked BELOW every actionable class.
The checklist reports how many placeholders it filtered (honest, no moat language).
Real-ledger payoff: BEFORE top-5 was 5 placeholders; AFTER the default digest shows
exactly the 1 substantive [gates] class and filters all 10 placeholders — an honest
result that the corpus is still placeholder-dominated and needs real-reason catches
to accrue.