scripts/regen-all.sh content is byte-identical; only the mode moves
100644 -> 100755. Verified two independent ways: the git blob is unchanged at
0e3c47e39b (content sha256
88eb93e41392329115448c43ada1ed94fc6f028c754a99d627f6d25f95ca0583), and the
effect kernel classifies the path MODE_CHANGED rather than MODIFIED.
The script carried a #!/usr/bin/env bash shebang while tracked non-executable,
so direct invocation exited 126. Every shipped caller passes an explicit
interpreter and the existing suite copied then ran it as `bash <path>` -- blind
to the mode by construction -- which is why the regression stayed green.
Three direct-execution witnesses added. Right-reason RED observed before the
mode fix, with witnesses present and mode still 100644:
ok 1 regen-all stops before a later projection (old, mode-blind: still green)
not ok 2 regen-all.sh is tracked executable (failed on 100755*)
not ok 3 direct execution ... is not a 126 refusal (failed on -ne 126)
ok 4 a non-executable copy is refused with 126 (control already correct)
Test 4 is the discriminating control: asserting merely non-zero in test 3 could
not distinguish a mode regression from the seeded failure being proven.
After the fix: direct ./scripts/regen-all.sh --check exits 1, not 126, and the
suite is 4/4.
This is the LAST tracked mutation. The final subject manifest was frozen after
it and before any check ran, so every check-receipt.v1 binds the FINAL manifest
digest 347a3ff1aa6b8b4a35baa97a1d0333b795892186c0a8dbde7563761d44d9d367 -- the
repair3 defect being closed. No verdict was minted.
Claude-Session: https://claude.ai/code/session_013fqQZAMZVFswA3oFrCDAFf
compute_grandfather_set becomes tri-state over grep's real contract (0
matches / 1 legitimate-empty / >1 death): a dead candidate enumeration or a
dead per-file whole-file scan returns 2 with stderr preserved, and entries
are accumulated and emitted only on complete success, so a partial
enumeration can never reach ratchet_regenerate's tmp+mv write. The lib's
existing entries-fn refusal then leaves the original grandfather bytes
untouched. Ordinary shrink and genuinely-empty regeneration are preserved
(rc 1 remains a legal empty set). Check-mode code, lib, registrations, and
blocking flags untouched. GREEN: jsonl suite 15/15 including the three RED
witnesses from the parent.
Claude-Session: https://claude.ai/code/session_01DUtFAN2gC4ryUmkMEhDVuR
file_trips becomes tri-state (0 trips / 1 not / 2 scan died, loud), mirroring
the lib's added-hunk matcher contract, and the loop distinguishes rc 1
(skip, unchanged semantics) from any other nonzero rc (refuse, exit 2,
helper stderr preserved). Closes the skip-to-green the lib's tri-state was
built to make distinguishable: an awk/grep death mid-loop no longer certifies
'no new sites' over a scan nobody completed. The matcher-internal dead-git
degradation keeps flagging via the whole-file scan (pinned by witness).
No grandfather byte, lib line, blocking flag, or other consumer changes.
GREEN: jsonl suite 12/12 including the two RED witnesses from the parent.
Claude-Session: https://claude.ai/code/session_01DUtFAN2gC4ryUmkMEhDVuR
preamble gate: the swallowed '2>/dev/null || true' around
collect_changed_files becomes the python-ratchet capture idiom — rc preserved,
library stderr preserved, exit 2 with a refusal instead of certifying an
empty change set nobody read.
jsonl gate: the '< <(collect | sort -u)' process substitution — which
discards the collector's rc 2 even under pipefail — becomes the
check-atomic-write-ratchet capture pattern: collect first under pipefail,
abort loudly on failure, iterate the captured list.
No blocking flag, grandfather byte, governed regex, lib line, or unrelated
gate changes. GREEN: both sibling suites 21/21 including the four RED
witnesses from the parent commit.
Claude-Session: https://claude.ai/code/session_01DUtFAN2gC4ryUmkMEhDVuR
Repairs the two fresh-Sol ADR-0016 review findings. Turns the RED witnesses
recorded in the previous commit green without touching the governed regex, the
growth guard, the allowlist, or scripts/.skill-python-grandfather.
1. check-skill-python-ratchet.sh no longer swallows the collector contract.
scripts/lib/ratchet.sh gives every chosen collection command an explicit
"|| return 2" because the gate runs in a shell with no ambient strict mode. The
caller then discarded exactly that: ratchet_changed_files_status ... 2>/dev/null
|| true turned a dead Git read into an EMPTY changed set, the governed loop ran
zero times, and the gate printed its green banner over a diff nobody read.
Measured before this change, with a diff-tree shim over a tree carrying a real
unpinned violation: "PASS: skill-python ratchet" / exit 0. The rc is now
preserved and the gate exits 2, with stderr no longer sent to /dev/null so the
library's own reason survives.
2. head scope now HAS the first-parent semantics it declared.
git diff-tree -m --first-parent does not mean "diff only against parent 1":
-m splits a merge into one diff block PER PARENT and emits them all, while
--first-parent constrains history traversal rather than which parent blocks
print. The parent-2 block lists every path already settled on the first-parent
line, so a merge commit re-attributed long-landed files to itself. Sol measured
this on the live integration lineage: at 88c2692f2 the old form emitted 14
governed rows, 12 from the second-parent block, none introduced by that merge.
Head collection becomes one owner, ratchet__head_diff, doing an explicit
two-tree HEAD^1..HEAD diff, with the --root empty-tree diff kept for root
commits (HEAD^1 does not resolve there, and a root commit's contents are exactly
what it introduced). An unreadable parent list fails closed at rc 2 rather than
returning an empty set. The helper replaces all SIX open-coded copies of the old
command -- head scope plus the upstream-without-upstream and auto fallbacks, in
both the name-only and name-status collectors -- so the same defect cannot
survive in a path nobody looked at. diff-tree is retained (not git diff) to
preserve the existing rename-detection posture; only ref selection changes.
The lib header no longer claims -m --first-parent means first-parent.
Verification:
- tests/scripts/check-skill-python-ratchet.bats 15/15 (N7, N8 were RED)
- tests/scripts/ratchet-lib.bats 41/41 (merge case was RED)
- all six other ratchet-lib consumer suites: unchanged from base
- go test ./internal/gates/... ok (negative-witness closure)
- shellcheck -S warning on both changed scripts: clean
- git diff --check: clean
- live gate sweep over all eight consumers, run identically at the unmodified
base 0ba51a5f6 and here: byte-identical results. The one pre-existing
failure (check-atomic-write-ratchet stale-entry rc=1, and its bats case 13)
reproduces at base with real history and is NOT caused by this change.
Refs: /tmp/agentops-adr0016-go-promotion-review-sol.md (REQUEST_CHANGES,
"Two ratchet corrections are still required"); ADR-0016 section 3.
P3 from the 2026-07-25 review: fix the inert-check CLASS, not just the two
instances #995 patched.
A gate verified only against a green tree is indistinguishable from `exit 0` --
delete its detector and CI stays green. Measured across the registry: 32 of 54
BLOCKING script-backed gates had no test anywhere under tests/ demonstrating
they FAIL on what they detect. 17 had no test at all, including skill.schema
and skill.triggers, which gate every SKILL.md in the corpus.
The closure lives in Go because the registry does. An earlier bash/Python
projection of seed.go undercounted by four -- some gates register outside that
file. The authority is gates.Default.All(), and reading it any other way
reproduces exactly the projection drift ADR-0016 is named after.
Mechanism is the repo's proven shrink-only ratchet: current gaps pinned in
scripts/.gate-negative-witness-grandfather; a NEW blocking gate must ship with a
witness; a gate that gains one must be pruned; a gate that leaves the registry
must be pruned; the growth guard reads HEAD so a change cannot exempt its own
new gate. The surviving count prints on every run.
Ratcheted down immediately to prove the mechanism moves rather than just counts:
tests/scripts/validate-skill-schema.bats is a real witness for skill.schema
(32 -> 31), driving the actual validator against a fixture tree.
Two things worth recording:
- The first draft's negatives passed for the WRONG reason. Descriptions in this
corpus embed `Triggers: "x"`; unquoted, that colon makes YAML read a nested
mapping, so the gate failed on a PARSE error rather than the schema violation
each test claimed to witness. Caught only because the positive case failed
too. A negative that fires for the wrong reason is not a witness.
- go.test-isolation is ON the allowlist as lacking a proven negative, and it
caught a real defect in this very PR (a git exec without per-command env
scrubbing). The pin measures TEST COVERAGE of a gate, not its efficacy. An
unproven gate may work fine; the point is that nothing would tell us if it
stopped.
All four of the meta-test's own failure directions were seeded and observed, so
the check-liveness ratchet is not itself an unproven check.
Two fixes from the 2026-07-25 skill-overhaul progress review, both independent
of the unlanded tranche fleet.
1. skill.python-ratchet — the gate ADR-0016 said it already had. Section 3
fixed "Python never ships in skills" on 2026-07-18 and called its own
violation "a gate failure, not a style nit", while zero checks enforced it.
Now a blocking shrink-only ratchet over skills/*/scripts/**/*.py: the 24
current files are pinned, new ones hard-fail, a promoted file must be
pruned, and the growth guard rejects a change that allowlists itself. The
surviving count prints on every run. ADR-0016 records the enforcing check by
name and the skills/*/tests/** carve-out with its rationale, so the
exemption cannot decay into the same inert prose.
2. The RPI/Validate acceptance-digest disagreement. run_once.py digested a
canonical-JSON re-serialization of the parsed intent mapping, validate.py
digested the raw intent bytes, and run_once.py hard-compared the two, so the
composed contract could not succeed. Both suites were green because RPI's
test mocked Validate with RPI's own digest function. RPI stops being a
second digest authority: it carries the digest Plan declares over the bytes
Plan snapshotted and cross-checks Validate's independently re-derived value.
3. The cathedral-cut probe, the same blind spot found again. It set
intent_bytes = canonical_bytes(resolved_intent) — precisely the one input
where the two digests coincide — so like the unit test it was built around
the coincidence and could not observe the bug.
Test shape is liveness-first: every gate negative is a seeded witness the gate
must be shown to FAIL on (12/12 bats), and the digest fix landed against a RED
witness that reproduces the real production error.
## Outcome
Ships AgentOps 3.3 as a thin native Gas City pack and removes the
accidental second orchestration control plane.
- Removes the custom GC delivery command/package, packet/schema family,
feeder/program engine, reducer/order, reliability registry, Beads
capability mirror, and fork-baseline runtime checks (about 29k deleted
lines).
- Retains six bounded adapters for pinned toolchain materialization,
clean bootstrap, native sling/status/doctor invocation, bead-isolated
worktrees, moving-main PR delivery, and teardown.
- Uses official Gas City and Beads behavior as the state owners, with
required OTEL configuration.
- Preserves Fable Mayor/Refiner, Sol-high planning and fresh validation,
Terra-high default implementation, Opus-medium overflow, and
support-only Luna.
- Supports automatic Refiner merge after hosted CI or a manual-review
toggle without locking `main`.
## Evidence
- Exact official GC: `8ffc009ded781a2ada2077f3a29bd712b2def0bf`
- Exact official BD: `8e4e59d39f3459a43cf21a3236a13eca4dd874f7`
- Full pinned native boundary: 9/9 passed in 114.047s
- Exact source-bead route replay: passed in 98.051s
- Release replay: manifest integrity, thin tests and both pack lints,
generated projections, contract compatibility, Bats contract,
ShellCheck, test-removal ratchet, and `go test ./...` all passed
- Fresh independent semantic verdict: PASS (Sol-high)
- Exact delivered head: `e805f0e26ce21c2eae9e720f57fff2a705f3185b`
- Subject manifest:
`8554d065076f70d0e639ca353409d2a75dca7ac01e4cb65cb4a118e98c0770fc`
- Verdict:
`3f89376b675de854832f38b46ccfa7ad7ca54079ffde578769c33e1f118c68c1`
## Release boundary
After this PR merges, the release qualification runs one fresh mixed
Terra/Opus canary from merged `main`, with at most one external repair
and one terminal retry. No live city repairs itself.
The only known local fast-gate exception is `workflow.install-drift`:
the installed workflow symlink resolves to the separate dirty primary
checkout, outside this candidate. All candidate-owned gates passed.
## Outcome
Correct the one AgentOps-owned defect exposed by the first post-merge
3.3 canary: the operator README placed a literal argument boundary
inside the discovered Gas City command, so Python never received
source-bead.
This adds a managed invocation boundary that:
- consumes its own explicit wrapper separator and forwards
discovered-command flags directly;
- binds the schema-5 city marker, exact GC digest, private supervisor
home, and isolated mode;
- projects the marker-selected GC/BD telemetry endpoints or an explicit
disabled state;
- clears ambient generic OTLP fallback configuration;
- records the stopped pre-admission canary without claiming a Gas City
or Beads defect.
## Evidence
- focused managed-invocation Bats: 4/4 PASS
- combined Gas City contract gate: PASS (33 Bats plus Python/static
contracts)
- adjacent teardown/toolchain Bats: 16/16 PASS
- exact full local release gate: PASS at
.agents/releases/local-ci/20260722T141141Z
- two fresh exact-toolchain bootstrap/start/status/teardown cycles:
PASS, zero workers/sessions, clean teardown
- fresh Sol-high pre-delivery binding: PASS, 11/11 criteria
## Deferred boundary
This PR does not claim final 3.3 release readiness. After protected
merge and post-merge conformance, exactly one new required-telemetry
mixed Terra-high/Opus-medium live canary remains.
## Summary
- bind the GC 3.3 bead-native workflow: Fable Mayor, fresh Sol-high plan
and validation, Terra-high default or Opus-medium overflow
implementation, Luna support-only
- add the bounded one-shot graph feeder, strict packet/worktree
identity, semantic terminalization, and model-free protected delivery
sweep
- make official Gas City v1.3.5 plus Beads v1.1.0 bootstrap repeatable,
disable unstable event propulsion/hooks, and prove quiescent teardown
## Reliability boundaries
- no Gas City self-repair loop, daemon, private scheduler, model
fallback, or main mutex
- semantic completion is independent of delivery; protected
PR/CI/rebase/merge is deterministic and moving-main aware
- event hooks are disabled because 3.3 uses explicit program admission
and cooldown delivery; native controller bead observation remains active
## Evidence
- full local release CI PASS at
.agents/releases/local-ci/20260722T113355Z
- two pristine standalone-clone cycles with official GC v1.3.5 8ffc009d
and BD v1.1.0 8e4e59d: repeat bootstrap, exact rig Dolt context, absent
event hooks, bounded events, enabled sealed delivery, and delayed
zero-process teardown
- fresh independent Sol-high pre-delivery binding PASS over exact head
e8c1253b40
- binding verdict SHA-256:
4f8d81811cd5b2eb35159c98159db2f8917d3ef54167f3c0d59d8378de14b2d1
## Tracker
Beads: age-gc-scope-failclosed-release-gktia.2
The one bounded mixed-provider live canary remains a post-merge release
gate; this PR does not claim final 3.3 release readiness before that
proof.
## Summary
- replace the unreachable Python factory lifecycle with an optional
typed Go delivery reducer
- make Beads the delivery lifecycle authority with deterministic
moving-main successor epochs
- add native Git/worktree, PR, hosted-check, auto-merge, landing, and
cold-replay boundaries
- retire obsolete v1 role/factory schemas and preserve the pinned
historical capability harness
## Validation
- fresh Sol-high PASS over exact 43-path manifest
ec6a2a4bf0b180782411b1ae4afd4a2ad8384129ff037ce8c119480317626b02
- go test ./...
- go test -race ./internal/gcadapter/delivery
- go vet ./...
- scripts/check-gc-executor.sh
- GC 3.3 schema, migration, provenance, factory-doctor, and bootstrap
gates
Bead: age-gc-scope-failclosed-release-gktia.1
## Outcome
Defines the GC 3.3 factory as one semantic intelligence loop with
deterministic delivery:
- Fable Mayor plus a bounded, nonbinding ambiguity adviser
- Sol-high for plan binding and fresh exact-subject validation
- Terra-high as the default writer and Opus 4.8 medium as explicit
overflow/repair
- Luna dormant and unroutable until later qualification
- no model-driven delivery-policy/refinery loop
- native GC sling/claim/scale-from-zero semantics retained
- exact provider/model/reasoning/fallback runtime attestation
- legacy composed program/refinery entry points and opposite-provider
lifecycle roles removed
Controller dispatch to the Fable adviser stays fail-closed until GC33-4
proves process/filesystem isolation. This does not claim that manual
operator sling to the named on-demand target is impossible.
## Evidence
Fresh Sol-high `verdict.v2`: PASS
(`bb31806fc5e0714363a9e539121ee7aa19231431fb04ddfee1fce8376b57d272`)
over subject manifest
`119b7a6db2380af47d87a24aced60d96858dd9e413a54dc640376e27767e8d1e` (52
paths, 18 deletions).
- focused acceptance: 124 tests PASS
- projection owner: 4 tests PASS
- bootstrap fixtures: 25 Bats tests PASS
- complete GC static contract: PASS
- factory/executor doctors: PASS
- generated projection check: PASS
- exact official GC v1.3.5 and Beads v1.1.0 provenance replay: PASS
- `git diff --check`: PASS
No city, model session, supervisor, tmux session, Dolt server, or canary
was started for this pre-canary bead.
## Root cause (CI flake, run 29785505667 attempt 1, PR #944)
The bats test `adding only the temp-signal half to a file with an
EXISTING rename still trips` failed once on ubuntu with the ratchet
printing a clean `PASS … grandfathered=0` where the fixture
deterministically trips. Diagnosed by analysis per the test-isolation
discipline (not repro-chasing):
- bats errexit proves every fixture `git commit` succeeded; the fixture
repo is fully isolated.
- `grandfathered=0` in the PASS line fingerprints the run as in-fixture
(real repo = 4).
- Zero stderr in the captured output rules out every loud failure
handler.
What remains: **three silent paths where a scan helper (grep/awk/git)
dying under 4-way-parallel runner load reads as "no finding"** — the
gate then certifies an unchecked diff:
1. `file_trips`: strip/grep failure returned 1 ("does not trip") → file
silently skipped.
2. Detector loop read from `< <(ratchet_changed_files | sort)` — a
collector rc 2 inside process substitution is discarded, certifying an
EMPTY change set (the exact fail-open the ratchet-lib header warns
about).
3. `ratchet_added_hunk_matches`: helper death propagated as arbitrary
nonzero, swallowed by `|| continue`.
## Fix
- `file_trips` is tri-state (0 trips / 1 no / 2 refuse-to-certify,
loud); signal greps read full input (no `-q` early-exit) so pipefail can
never surface a SIGPIPE'd printf as a phantom failure on large files.
- Changed files are collected into a variable under pipefail with an
explicit `exit 2` handler.
- `ratchet_added_hunk_matches` maps fallback-grep/awk death to loud rc 2
(0/1 unchanged; existing `|| continue` consumers skip on 2 exactly as
before, now loudly).
- `compute_grandfather_set` aborts `--regenerate` on helper failure —
never a truncated grandfather list.
## Pinned
Dying-awk PATH-shim tests at gate and lib level: helper death is rc 2
"refusing to certify", never PASS. Same fail-closed class as the
pawl-refuted empty-stream certify (2026-07-10).
## Validation
- 58/58 in `check-atomic-write-ratchet.bats` + `ratchet-lib.bats` on
fresh origin/main
- All six ratchet-lib consumer gate suites green
- `shellcheck -S warning` clean; real-repo gate PASS (grandfathered=4);
`--regenerate` byte-identical
- 65 repetitions of the formerly-flaky test green
Residual: the external kill source is unprovable from one occurrence; a
recurrence now exits 2 naming the failed helper + file instead of
printing PASS.
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.
Resolves every spellbreaking finding (the blocker + all 13 majors) from
the 3.3.0 release-readiness audit
([docs/audits/release-readiness-3.3-2026-07-20.md](docs/audits/release-readiness-3.3-2026-07-20.md),
included in this PR).
## Finding → fix map
**CLI self-documentation (M1–M3)**
- **M1** `ao robot-docs` prescribed removed `ao inject` → line removed
from the canonical agent workflow; `inject` added to the removed-command
hint table **and** the MIGRATION.md map (drift test
`TestRemovedVerbsHaveMigrationRows` enforces the pair).
- **M2** `ao config --help` documented ~14 env vars for removed
subsystems (RPI/Dream/Council/tiers) → help text and the `--show` env
panel pruned to the 5 vars the binary consumes; mirrored list in
`internal/config` pruned identically.
- **M3** `ao flywheel status` read only legacy `.agents/<section>` while
`ao doctor fix` migrates learnings to canonical `.agents/ao/learnings` →
new `quality.KnowledgeSectionDirs` dual-roots every knowledge reader
(tier counts, new/stale artifacts, retros, health delta, utility, loop
metrics, retrievable-citation stats — plus the golden-signals readers
`ComputeResearchClosure`/`ComputeReuseConcentration` that the fresh
verification pass caught as missed). Sandbox-proven twice: a learning
existing only under `.agents/ao/learnings` appears in all metrics, and a
research file only under `.agents/ao/research` flips closure from
`starved/0` to `unmined/1 orphan`.
**Release story (M4–M6)**
- **M4** CHANGELOG `[3.3.0]` omitted post-07-17 surfaces → folded in `ao
eval` (#921), default-build `ao flywheel`, the PreToolUse policy engine,
and the #919 cleanup; date moved to 2026-07-20; `docs/CHANGELOG.md`
re-synced (changelog.sync gate green).
- **M5** MIGRATION.md attributed `ao eval` to a nonexistent "3.4" → now
"returned in 3.3".
- **M6** four release surfaces claimed a 50-skill corpus vs 48
everywhere real → all counts now 48 (CHANGELOG ×2, docs/3.3.md,
release-notes page ×2).
**Install story (M7 — product decision by Bo)**
npx first (universal — installs into all coding agents), **plugins for
Claude Code/Codex encouraged**, checkout + `ao skills link` as the
source-tracked/contributor path; curl installers stay tombstones.
Harmonized across README, UPGRADING, install-day2-ops, MIGRATION,
3.3.md, CHANGELOG, the release-notes page, all six installer tombstone
messages (`install.sh` + claude/codex/agy/opencode/`codex.ps1`), and the
site's CLI page. All "legacy migration-only / not the recommended path"
plugin branding removed.
**Docs site (B1, M8–M12)**
- **B1** generated site CLI page instructed a tombstoned curl installer,
nonexistent `ao rpi phased`, and wrong skills dir → `emit_index()`
rewritten to the real install menu + a quickstart of commands that
exist; semantic loop correctly attributed to skills.
- **M8** deploy workflow's `--strict` contradicted mkdocs.yml's declared
non-strict policy and aborted the build → flag dropped (lychee +
validate-links.sh own link checking).
- **M9** site banner said "AgentOps 2.x" → now 3.3.
- **M10** ~176 internal files (audits/plans/handoffs/… + TEMP scratch
doc) published and dominated search → `exclude_docs` extended; built
site verified free of them; search index 4,751 → 2,444 entries.
- **M11/M12** newcomer-guide skill links and all six SCHEMAS.md links
404'd on the site → absolute GitHub URLs (resolve on both GitHub and the
site). The fresh verification pass found the same class on
`docs/contracts/index.md` (nav-listed),
`docs/contracts/corpus-learning-seam.md`,
`docs/templates/slice-validation.md`, and five
`docs/architecture/gas-city-factory.md` links into now-excluded
`docs/audits/` — all repointed to absolute GitHub URLs.
- Also from the verification pass: robot-docs exit-code table no longer
says "bead claimed" (removed concept), and the docs.yml comment now
cites the link checker that actually runs
(`tests/docs/validate-links.sh` via doc-release checks) instead of
lychee.
**Skills corpus (M13)**
- rch skill instructed 5 nonexistent scripts + 8 nonexistent reference
files as recovery steps → pruned to the 7 real references; the
wire-level `printf | rch` probe replaces the phantom `protocol_test.sh`;
codex twin regenerated (parity gates green).
## Verification
- `go vet` clean; **full test suite 60/60 packages pass** (includes the
new-shape flywheel/quality/config tests and the inject↔MIGRATION drift
test).
- **`ao gate check --full --scope worktree`: 67/67 pass, 0 warnings**
over this exact change set (changelog sync, shellcheck on the edited
installer, skill mesh + codex parity, manifests/schema/triggers,
provenance chain).
- `scripts/regen-all.sh --check`: all generated projections current.
- Rebuilt binary re-exercised: `robot-docs` clean, `ao inject` tombstone
live, config help clean, flywheel sandbox proof above.
- `mkdocs build` exit 0; warnings 84 → 46 (remainder is the accepted
out-of-tree-link class per mkdocs.yml's declared policy).
- Fresh-context adversarial verification workflow over all four fix
groups (results in session log).
## Known residuals (deliberately out of scope)
- `ao config models` subcommand still renders tier config (its two env
vars ARE consumed; `COUNCIL_CLAUDE_MODEL` in its display list is not —
follow-up).
- Same-class single-rooted readers off the flywheel path:
`learning.coherence` gate glob (`.agents/learnings/**` only),
`quality.CountConstraints`, config `Paths` defaults feeding eval sandbox
deny-lists.
- `scripts/docs-build.sh` still uses `--strict` with its own allowlist
(not wired into any workflow or gate).
- Audit minors 1–8 (rc fallback version string, `config --show`
legacy-fallback display, `ao init` vs doctor layout, doctor's `ao beads
dir` hint, dead `tracker:` key in the example config, doc-skill phantom
scripts, ROADMAP dead links, documentation-index root links).
## Summary
- pin and materialize an exact colocated Gas City and official Beads
toolchain without editing installed binaries
- add deterministic isolated bootstrap, recovery, teardown, exact
workspace binding, and tracked .gitignore preservation
- add bead-native Mayor, plan review, isolated Codex/Claude workers,
binding Validators, rejection-to-Mayor rescope, fenced Refinery,
integration validation, qualification, and protected PR delivery
- make the local quick release gate deterministic after CLI cleanup
## Validation
- fresh GC-managed Codex Sol verdict: PASS (verdict
d1a1e03808b4605e49cc97423c7570b04d581c9ee1fad9aa8f1daf6e72d6f0ef)
- focused GC executor/factory/projection suite: PASS (32 packet + 77
factory + 4 projection + 21 bootstrap tests, plus doctors and static
contracts)
- teardown suite: 7/7 PASS
- local-ci-fast: PASS
- exact v16 mixed-provider live canary: Terra and Opus 4.8 workers in
parallel, opposite-family candidate validators, Opus Refiner, Sol
integration validator, all lifecycle beads closed, clean managed
teardown
## Scope
The follow-on Fable 5 Mayor/Refiner, Sol-every-bead validation, and Luna
support-worker topology is deliberately not part of this historical v16
baseline; it will build on this foundation in a separate bead program.
## Doctrine flip: enforcement hooks on by default
Operator directive: hooks reach **every** user regardless of install
path. Injection hooks stay dead (#511, delta=0) — the
hookless-cold-start gate is untouched and passing.
### Delivery per install path
| Path | Delivery |
|---|---|
| Claude Code plugin (marketplace) | **Automatic** — new
`hooks/hooks.json` at plugin root; Claude wires the policy dispatcher on
install (`${CLAUDE_PLUGIN_ROOT}` paths, 10s timeout, Bash + Edit\|Write)
|
| npx skills / skills.sh copies | Skill package now carries its own
installer: `skills/cc-hooks/scripts/install-hooks.sh` (resolves
everything relative to the skill dir; works with no repo present) |
| git clone / brew | `scripts/install-policy-dispatch.sh` → thin
delegator to the skill-embedded installer |
### Also in this PR
- 4th registry policy `core.skills:edit-installed-copy` (Edit|Write,
`file_path`-only) — registry twin of the standalone guard, so dispatcher
users get the full protection set.
- Dispatcher hardening: malformed stdin now fully silent (fail-open,
zero stderr) — validator security-pass finding.
- README admission-control section; cc-hooks SKILL.md posture flip
(description, constraints, delivery table).
- Codex twin force-synced (incl. the new installer file), skill mesh +
catalog regenerated.
### Evidence
- 28/28 policy-dispatch bats (incl. plugin-layout registry-fallback
test); 16/16 on the injection-doctrine + standalone-guard suites.
- `ao gate check`: 28 pass / 0 fail on the exact subject.
- Fresh-context validation: first round returned FAIL (uncommitted regen
outputs + a stderr leak on malformed stdin) — repaired, amended,
re-validation in flight; merge follows a PASS verdict.
- Adversarial security pass on the dispatcher (it auto-activates for
plugin users): empty/malformed/1MB stdin, invalid registry, concurrent
fires — all fail-open, silent, correct.
Epic: age-4qw1 (H1/H2 landed previously as 0eaf21f0c). Out of scope:
Codex-plugin hook parity (follow-up on the epic).
flywheel compare direct-test coverage moved from cmd/ao into internal/flywheelapp;
mirror the provenance/skills carve-out allowlist pattern and regen cli-surface.
Remove fully-dead Go CLI packages and orphan files per the independently
validated keep/delete/extract manifest (docs/audits/gocli-dead-weight-manifest-2026-07-19.md).
D1 already extracted the two live slices (constraint-index -> internal/constraintindex,
RedactBytes -> internal/redact), leaving internal/search and internal/llm as pure
dead remainder with zero production importers.
Deleted whole packages: internal/search, internal/llm, internal/notebook
(sole importer was search/inject_run.go), internal/reviewerhealth,
internal/adapters/reviewerhealth (both reachable only via the orphan
cmd/ao/reviewer_health_composition.go), internal/canon and internal/vibecheck
(empty-package TestMain husks).
Deleted orphan cmd/ao helper files: reviewer_health_composition.go,
advertised_commands.go, shell_quote.go, beads_json_compat.go, template_detect.go,
federation.go (each entry symbol appeared in exactly one file; no cobra command
wired; command surface projections regenerate unchanged).
Deleted the 12 dead internal/ports groups (agent_mail, ci_status, citation,
context_compiler, corpus_reader, corpus_writer, finding_compiler dead portion +
inmemory_finding_compiler, finding_recurrence, freshness_policy, harness, llm.go,
wiki_index) with zero external ports.X references. finding_compiler.go is a
PARTIAL keep: DetectorReplayResult and DetectorPrecisionEvidence stay (the
Precision field type forces the transitive keep; DetectorReplayResult is consumed
by the surviving constraint-index slice). gate_runner.go, inmemory_gate_runner.go,
frontmatter_codec.go, and doc.go are untouched keeps.
EXCLUDED (live, do not delete): internal/evidence + the flywheel compute block,
live via the aliased import ratchet "…/internal/evidence" in flywheel_metrics.go.
Regenerated command-surface projections (cli/docs/COMMANDS.md,
docs/cli-surface.{json,md}, evals/agentops-core/cli-command-surface-matrix.json,
evals cli-command-surface-smoke.sh) via their owning commands
(generate-cli-reference.sh, regen-command-surfaces.sh,
check-cmdao-surface-parity.sh --write-surface); no drift — the deleted files
carried no command surface.
Pruned stale FILENAME-pinned entries (deleted files only, list-shrinks-only) from
scripts/.atomic-write-grandfather (llm/review.go, llm/session_writer.go,
search/findings_ops.go) and scripts/.jsonl-scanner-grandfather (notebook/notebook.go,
search/{index,learnings,search_cass,sessions}.go).
go build ./... && go vet ./... && go test ./... green (2587 tests).
go list -deps ./cmd/... contains none of the deleted packages.
ao gate check --full: 67 checks, 67 pass (after ratchet-baseline pruning).
Test-Removal-Reason: Every deleted *_test.go file was the test of code deleted in this same commit as dead weight per the validated dead-weight manifest (no live production consumer). Covers all removed tests under internal/search, internal/llm, internal/notebook, internal/reviewerhealth, internal/adapters/reviewerhealth, internal/canon, internal/vibecheck, internal/ports (the 12 dead groups + inmemory_finding_compiler + finding_compiler_shadow), and cmd/ao (template_detect, federation). No behavioral test coverage was lost — the subjects under test no longer exist.
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
Fixes the three gremlins observed 2026-07-18 (bead
age-gate-scripts-worktree-gitdir-p62wo):
1. **Gate scripts broke under hook env from linked worktrees** (#919
regression): new scripts/lib/repo-root.sh (GIT_*-scrubbed resolution,
BASH_SOURCE anchor, worktree-correct) swept across 32 scripts. Proof:
this PR's own push ran the pre-push gates from a linked worktree and
passed.
2. **core.bare=true recurrence**: mechanism proven (leaked GIT_DIR +
no-arg git init --bare rewrites the SHARED config); writer traced with
high confidence to a live Gas City city process (concrete suspect:
gascity cmd_rig_test.go runGitInTest, unscrubbed env + no-arg init
--bare — fork-side fix filed separately). Defense: new
always.git-config-hygiene gate fails fast on core.bare/test identities
with the repair command inline (--self-test proves fail-closed).
3. **Test/test@test.com identity leak**: writer convicted — our own
pre-push chain leaked GIT_DIR into go test, whose helpers wrote the
shared config via git -C (mechanism proven). Cut at both ends:
validate-go-fast scrubs env for children; every test git-helper set-site
scrubs; check-test-isolation gains a WARN-ratchet rule (baseline 3 →
FAIL at 0).
Gates: shellcheck rc=0 across 36 scripts; go build/vet clean;
655+379+149 tests; hygiene self-test; both originally-failing validators
pass under the repro env.
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
## Summary
- add a bead-native Gas City factory pack with Mayor, fresh plan review,
Codex/Claude Worker and Validator pools, and a fenced Refiner
- make the bead graph the sole work/lifecycle authority; JSON artifacts
are digest-bound evidence only
- isolate each experiment in its own branch, worktree, Git index, and
lease, then admit exact PASSed candidates to Refinery
- make lease, verdict, rejection, successor, assembly, reassembly, PR
landing, and partial closure transitions crash-replayable
- enforce fresh-worker rescope after FAIL/NOT_PROVEN and an atomic
max-attempt HOLD
- encode the operating contract, live canary, role archaeology, and Bun
Rust-port lessons in AgentOps docs
## Validation
- live GC canary: Claude Worker -> Codex Validator -> Claude integration
Validator -> Codex Refiner -> PR #916 -> landed
`b80a752aad3843af66160b08a823aaed57e07169`
- `./scripts/check-gc-executor.sh`: 20 packet tests, 29 factory tests, 4
projection tests, and 8 bootstrap Bats tests PASS
- both GC packs lint PASS
- Ruff, ShellCheck, Python compile, and generated projection checks PASS
- branch-built `ao gate check --fast`: 13/13 PASS
- branch-built `ao gate check --full`: 66/66 PASS
- independent fresh-context audit of exact head
`1b18cf663ece790a12cd840f6eed2856852c552f`: PASS
## Contract
The unit of work is the bead. The pack defines roles, prompts, commands,
and policy; it does not own a parallel pack/JSON lifecycle. A rejected
experiment must be terminal and closed before any Mayor rescope or
successor reduction can proceed.
## Summary
Closes the findings from the v3.3.0 release-viability audit of main.
**Surface truth (High):** `ao constraint`, `ao goals trace`, `ao session
memory`, and `ao skills edit` are production tombstones but
docs/cli-surface.md documented them as `public-tested`/`covered` (the
test binary keeps constraint live, and the generated doc reflected the
test build). Added `deprecated` allowlist rows, dropped the stale
constraint-subcommand rows, regrouped constraint with its tombstone
peers in the default spine, regenerated both surface projections, and
added `TestCathedralCutTombstonesSurvivePruning` pinning the invariant
that every Cathedral Cut verb keeps an executable tombstone.
**Installer hardening (Medium):** `install-bd.sh` now fails closed when
no published checksum exists for a release asset (explicit `--no-verify`
overrides), and the curl|bash path verifies the fetched
`installer-common.sh` against a pinned SHA-256 before sourcing it
(drift-guarded by a new install-bd.bats test).
**Docs (Medium/Low):** UPGRADING.md no longer links the MIGRATION-3.0.md
file the Cathedral Cut deleted; CHANGELOG 3.3.0 `### Removed` now names
`yield`, `constraint`, and the 3.2 `verify` front door and links the
MIGRATION.md map.
**Gate scripts (Low):** flywheel/provenance gate scripts build `ao` into
`mktemp -d` scratch dirs instead of fixed world-known /tmp paths
(symlink/TOCTOU on shared hosts).
**Bonus (surfaced by the push gate):** `scripts/lib/preamble.sh`
mis-resolved REPO_ROOT under git hook env (GIT_DIR without
GIT_WORK_TREE), which made `go.cli-architecture` refuse every
worktree-origin push touching `cli/cmd/ao/**`. Fixed by scrubbing the
hook-injected discovery env around the rev-parse.
## Validation
- `go build && go vet && go test ./...`: 5,085 tests green,
golangci-lint clean
- `ao gate check --fast`: 27/27 pass, including under simulated hook env
- bats install-bd suite 6/6 incl. new pin drift test; shellcheck clean
on all touched scripts
- markdownlint clean; doc-release gate passes; tombstone exit codes
verified on the built binary
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.
## Summary
- replace removed nightly retrieval commands with the maintained smoke
fixture and focused retrieval coverage
- repair Windows smoke, workflow/actionlint, skill projections,
installer fixtures, and stale Cathedral Cut tests
- refactor verdict validation below the complexity budget while
preserving its error contract
- incorporate the pending cross-runtime hook projection already
represented in the repository manifest
## Validation
- `go test -race -shuffle=on ./...`
- full Bats suite: 874/874 passed
- `scripts/ci-local-release.sh --quick`
- `ao gate check --full --json --workflow-coverage
--require-workflow-parity`: 65/65
- `ao gate check --fast --json`: 43/43
This PR repairs the common causes represented by open nightly issues
#900, #901, #903, #909, #910, and #912. Those issues will be closed only
after the corresponding workflows pass on `main`.
---------
Co-authored-by: boshu <241868352+boshu2@users.noreply.github.com>
Co-authored-by: Test <test@test.com>
The project config (tracker pin) moved from .agentops/config.yaml into
.agents/ao/config.yaml; the no-tracked-agents gate now permits exactly that
one file and still rejects any other tracked or staged .agents state and any
broader gitignore re-include.
Co-authored-by: Cursor <cursoragent@cursor.com>
Intent snapshots and verdict artifacts now default to
.agents/ao/{intents,verdicts}/sha256/ so all session/evidence state lives
under .agents/ as the single agent-state root. Updates validate.py defaults,
ao init/status/demo, the cathedral conformance probe, craft_score.py digest
resolution, and every doc/skill mention. Existing artifacts migrated in
place; .agentops/ retains only the tracked project config.yaml.
Co-authored-by: Cursor <cursoragent@cursor.com>
One canonical root for skill create/heal/audit. Mode routing table absorbs
all heal-skill triggers; every live consumer (gates seed, quality checks,
release scripts, bats, docs, catalogs, parity twins) updated to relocated
paths; all projections regenerated via owning commands.
Validated fresh: verdict PASS (e9b6cdb8), intent 26a4f2be, all five
acceptance scenarios re-executed by an independent context.
Co-authored-by: Cursor <cursoragent@cursor.com>