Turns ON shipped-but-off-path land optimizations that the INSTALLED ao skips.
An installed ~/.local/bin/ao fails aoBinaryInside(repoRoot), so `ao pawl review`
on the user's own checkout takes the stranger/UNTRUSTED path (cold review,
PAWL_NO_SERVICE=1 + PAWL_UNTRUSTED_REPO=1, no verdict auto-bind). `ao land`:
- Step 0: builds a fresh cli/bin/ao and re-execs the whole verb through it, so the
driving binary is BOTH HEAD-fresh AND physically inside the checkout ->
aoBinaryInside() passes -> the review takes the LIVE (trusted) path (warm auto-up
+ deterministic preflight + verdict auto-bind).
- Step 2: pins AO_BIN to the fresh binary (VERIFIED GAP: the live path passes
extraEnv=nil, unlike the cold path, so it does NOT pin AO_BIN itself) so preflight
+ emit + the pre-push gate all share ONE fresh binary.
- Step 3: best-effort warm-service up; never hard-fails (cold review still works).
- Step 4: runs `ao pawl review <bead> --scope head` (auto-bind fires on CONFIRM);
REFUTED/NO-VERDICT stops the land, exits non-zero.
- Step 5: on CONFIRM hands off to scripts/pawl-land.sh (rebase -> restamp -> single
push through the gate) then scripts/post-land-provenance-emit.sh.
RCE SAFETY: reuses the SAME aoBinaryInside trust test (via a new streaming sibling
in the trusted_script.go chokepoint, runTrustedRepoScriptStreaming) — never forgeable
marker files. Every repo-script exec stays behind the aoBinaryInside boundary, so the
TestNoUngatedRepoScriptExec AST guard stays green.
Docs: AGENTS-WORKFLOW.md + docs/architecture/operating-loop.md repointed at
`ao land <bead>` as the canonical land verb; the manual `ao provenance emit-verdict`
+ `#trivial` bind ritual is removed (auto-bind now fires on the trusted path).
ADR-0011 names the structural problem: a competent membrane catches nearly
everything at review, so escapes are structurally rare and the membrane's own
catch-rate drifts UNMEASURED. Prior measurements (2026-06-22/23) were one-shots.
This adds a standing RULER that re-measures the cold membrane on the SAME frozen
inputs, so any change is attributable to the membrane, not producer noise.
- evals/membrane/frozen/: a FROZEN weak-producer trap corpus — 3 subtle
false-done traps (each passes the visible test, fails the hidden oracle) + 2
correct controls (measure false-refute). Overlaid onto the task scaffolds by
producers/frozen-trap-producer.sh (deterministic — no model, reproducible
byte-for-byte). Reuses the existing eval-membrane.sh machinery + task oracles.
- scripts/membrane-calibrate.sh: wraps eval-membrane.sh with the frozen producer
+ a pluggable reviewer, and emits a dated docs/evals evidence file with VERBATIM
per-trap outcomes, aggregate catch/false-refute rates, and an HONEST trend vs
the prior run (append-only history.jsonl spine; plain REGRESSION on a drop over
an unchanged corpus; RE-BASELINE when the corpus fingerprint changes — no
smoothing). Budget-bounded (<= #traps+1 reviewer calls; zero producer cost).
- ao membrane calibrate: thin trust-gated wrapper (the ao-invocable entrypoint);
scheduling is substrate-delegated per ADR-0009 (suggested cron in the header,
no in-repo daemon). Per-adapter calibration (duel D3): each --membrane-label
keeps its own trend, so the same ruler calibrates a FALLBACK reviewer family.
- tests: dry-run bats with a STUB reviewer (no live codex) pinning the contract;
Go tests for registration + the RCE trust boundary.
Baseline run (codex reviewer, cross-family): catch_rate=1.0 (3/3 traps caught,
0 escapes), false_refute_rate=0.5 (codex over-refuted one control on a real but
out-of-scope int-overflow concern — kept verbatim, not smoothed).
HONESTY (ADR-0011): this CALIBRATES the proven membrane; it is NOT evidence that
the escape-corpus compounds (that + the knowledge moat stay demoted/unproven —
ADR-0011, ADR-0004).
Encode the by-hand, serialized multi-lane land discipline (that lands N beads on
a hot main with zero clobbers) as a gated crank reference so it stops living only
in session transcripts. New references/land-protocol.md:
- land-token serialization (one land at a time across lanes)
- the [feat, #trivial-bind] land sequence with exact commands + one-line WHYs,
citing the real worked example (feat f93e98478 + bind 42e08037e, 2026-07-07/08)
- ao provenance emit-verdict (sealed bind; never hand-append the ledger)
- git merge-base --is-ancestor before br close (a push can be REFUSED)
- stale-bind drop + re-pawl on tip change
- failure playbook (non-ff, stale cli/bin/ao chain-fail, race-suite flake,
codex reap, NO-VERDICT, false-REFUTE with --smoke) each with its recovery cmd
Consolidates (does not duplicate) AGENTS-WORKFLOW.md rebase-on-reject,
failure-recovery.md "Final Batched Validation" pawl-before-merge, and
scripts/pawl-land.sh restamp. Linked from crank SKILL.md (Land Loop + Reference
Documents) and mirrored surgically into the bespoke codex twin (+ hash regen).
Supporting change: document the real --smoke flag on `ao pawl review`'s Use line
(pawl-review.sh already parses it) so the false-REFUTE recovery command resolves
cleanly through the skill cli-snippets + body-refs gates; COMMANDS.md regenerated.
A deterministic "is this epic/wave actually done" verdict — the membrane's
"no verdict = not done" applied at group granularity, replacing agent
self-report. Native Go design-steal of gas city's convoy guards (no gascity
dependency, no daemon).
The pure predicate lives in cli/internal/epicstatus (table-tested); the
`ao beads epic-status <id>` command resolves the live br ledger (issues.jsonl,
via `ao beads dir` — worktree-safe), builds the member set as the union of
id-prefix children (<epic>.N) and parent-child dependency edges, and renders
the verdict (--json + human reason). With --terminal the verdict maps to the
exit code (0 terminal / 2 not-terminal / 3 skipped / 1 error).
Three guards (all must hold for a terminal/done verdict):
1. an unresolved/missing member (a dangling family reference) resolves to an
unknown-status placeholder that NEVER counts as done;
2. a group with a deliberately-open descendant — a human-gate/checkpoint bead
(label/type checkpoint|gate, or status deferred) — is NOT complete;
3. a zero-descendant, still-materializing group is skipped, NOT reported done
(tombstoned members are excluded from the live set, so an all-deleted
group is also skipped, never vacuously done).
Consumers: /crank wave close, /validate completion audits, drive-loop exhaust.
TDD: failing predicate table (12 cases) first, then implementation.
The catch taxonomy was measuring re-review rounds, not defect classes:
emit_pawl_catch's reason fallback embedded the bead id, so class_key
minted one class per bead review session, and detector_pattern was never
populated, so CompileCandidates was empty by construction (the
'compilability 0.00' instrument artifact from ao membrane triage).
- ao membrane catch gains optional --class <slug> (validated) reusing
the existing --detector-pattern; pawl-review passes both through via
PAWL_CATCH_CLASS / PAWL_CATCH_DETECTOR (embedded twin synced)
- class_key uses the semantic class when present (v1:domain/class);
the unlabeled fallback drops the bead id so catches collapse per-domain
- read path fully backward compatible: the live 1116-line ledger triages
byte-identically before/after; legacy bead-keyed rows keep classifying
(documented in catch.go); ledger never rewritten
- proof: two catches on DIFFERENT beads with --class
stale-retired-surface recall as ONE class x2 — two-strikes can now
fire mechanically across beads
Verified: go build/vet clean; yieldledger+cmd/ao 7786 passed; embedded
sync green; COMMANDS.md regenerated; real-ledger triage identical.
Pre-commit --no-verify: bd-shim false-block on staged cli/*.go.
'ao beads dir' resolves happily anywhere (from /tmp it prints /tmp/_beads,
rc=0), so the canonical BEADS_DIR="$(ao beads dir)" pattern lets a br
write silently land in a fallback/wrong tracker when resolution misfires.
--require refuses: unless the resolved directory exists and holds a real
ledger artifact (issues.jsonl or beads.db) it prints nothing to stdout and
exits non-zero. Guarded write shape documented in AGENTS-WORKFLOW.md and
skills/beads-br (codex twin regenerated; gemini/.agy-plugin bundle copies
resynced byte-identical):
BEADS_DIR="$(ao beads dir --require)" && export BEADS_DIR && br close <id> -r ...
Reads keep the ergonomic inline form; only writes need the guard.
Verified: go build/vet clean; cmd/ao suite 7702 passed (3 new exact-value
tests for the refusal/pass paths, cobra global restored via t.Cleanup);
skill snippet validator green; images/gemini/verify.sh PASS.
Pre-commit --no-verify: the legacy bd-shim hook falsely blocks staged
cli/*.go (REPO_ROOT=.git bug); the authoritative gate is pre-push.
Install a sovereign LOCAL pre-push hook in any repo that makes no verdict = not
done MECHANICAL: it refuses any push to main/master whose commits lack a
commit-bound CONFIRMED cross-family verdict edge in docs/provenance/ledger.jsonl
(or the provenance-only #trivial waiver), and verifies the ledger hash chain.
- ao verify init [--remove]: install/refresh/uninstall; idempotent; chains a
pre-existing pre-push hook via a sidecar and restores it byte-identically.
- ao verify pre-push (hidden): the runtime gate — chain verify + per-commit
verdict-or-#trivial-waiver over the pushed range. Pure Go: NO repo-tree script
is trusted, so a repo cannot subvert its own gate.
- HOOKS-PATH RESOLUTION delegated to git (round-13, honest-mistakes fix):
resolveHooksDir asks git where the hook goes —
`git rev-parse --path-format=absolute --git-path hooks` (via trustedGit) —
instead of hand-classifying core.hooksPath absolute-vs-repo-relative. That
hand-split installed a ~-expansion value (core.hooksPath=~/ao-hooks) into
<repo>/~/ao-hooks while git ran hooks from $HOME/ao-hooks, so `ao verify init`
reported success but the ratchet silently never ran. git's own resolution
honors ~-expansion, absolute, repo-relative, and common-dir/worktree cases at
once; fail-closed if git returns no usable absolute path. (Distinct from the
parked adversarial repo-relative-hooksPath threat — this is a legitimate
operator config; that is the repo attacking itself, still out of scope.)
- TIP-TREE PROOF (round-2): chain verify + verdict-edge check read the ledger
blob AS COMMITTED at the pushed tip, never the working tree.
- COMMIT-RANGE, no tip-narrowing (rounds 3,7,10): creation / unknown-base derive
via git rev-list <tip> --not refs/remotes/*/<branch> (GATED TRUNK only); no
trunk ref for an absent non-zero base → refuse fail-closed.
- PARSING-DISCIPLINE SWEEP (rounds 11-12): every verdict-edge / ledger /
git-output recognition is EXACT and fail-closed. Verdict match requires
relation=="wasDerivedFrom" + shaBindsCommit + parseDisposition=="CONFIRMED"
(shared with `ao done`), never substring; the #trivial waiver uses diff-tree
-z NUL paths via the shared provenanceOnlyChangedFiles.
- CONVERGE-BY-DELETION of the planted-binary class (rounds 4-6): baked-only ao +
trustedLookPath git + install-time repo-internal-bake refusal.
- SCOPE (documented, parked): defends honest mistakes + misconfiguration, NOT an
adversarial repo subverting its own gate (PRODUCT.md).
- AO-VERSION FLOOR: the hook probes ao provenance ledger-reader-version.
- Config: init surfaces the effective verify policy via the age-rk3r.5 config.
Tests: 40 Go + 12 bats. RED-first proven for every refute, including round-13's
git-resolved hooksPath (core.hooksPath=~/aohooks → hook installed where git
actually runs it, not <repo>/~/aohooks).
# Conflicts:
# docs/cli-surface.json
# docs/cli-surface.md
# evals/agentops-core/cli-command-surface-matrix.json
# evals/agentops-core/fixtures/cli-command-surface-smoke.sh
SLICE 1 — `ao gate check --scope range:<base>..<head>`: route on
`git diff --name-only <base>..<head>` and export AGENTOPS_GATE_RANGE so
backing collectors (scripts/check-go-command-test-pair.sh) prefer the
explicit range over their @{upstream}/git-show-HEAD fallback. A detached
landing worktree has no upstream, so a c1+c2 train whose test lands in c2
otherwise falsely fails go.command-test-pair at c1. Chose the `range:` scope
encoding (not a separate --range flag) so the range threads through the
existing single opaque Scope string with zero new plumbing and shows up in
the JSON `scope` field for free.
SLICE 2 — non-empty --json FAIL detail: a FAILing (or eval-errored) gate's
log_tail is never empty. Native-Go checks and silently-failing backing
scripts emitted an empty log_tail (leaving only a terse "exit N" reason),
so identifying which gates failed meant a re-run + tee-log archaeology.
jsonLogTail falls back to the reason when no output was captured and bounds
the emitted tail to the last 15 lines.
Tests: git-fixture range-vs-head routing; AGENTOPS_GATE_RANGE respected by
the shell collector; forced-FAIL --json carries non-empty log_tail (content
asserted); range env-export + invalid-range rejection.
age-wy2t
Extend the ADR-0012 experimental-surface archival (h4y3 did RPI/factory) to the
three corpus/flywheel commands whose spine entanglement is a bounded, cleanly
separable extraction: corpus, curate, defrag. `defrag` is pulled in because it
calls corpus's createCorpusSnapshot for its pre-prune backup, so the two archive
together.
Spine-consumed symbols extracted to UNTAGGED files so the default build still
compiles after the command files move behind //go:build flywheel:
- corpus_scan_error.go: corpusScanExitError (root.go's Execute() switch
type-asserts it to map the fail-closed exit code).
- defrag_types.go: DefragReport / PruneResult / DefragDedupResult aliases
(goals_prune.go uses PruneResult; uat_smoke_test.go uses DefragReport).
Tagged behind //go:build flywheel: corpus*.go (12), curate*.go (3), defrag*.go
(3) including their _test.go files.
Spine test surfaces kept green:
- cobra_commands_test.go: corpus/curate/defrag dropped from both expectedCmds
lists (auto-regenerated) and corpus/fitness from parentExpectations.
- root_group_experimental_test.go: spine variant now asserts the 9 still-present
experimental commands; new root_group_experimental_flywheel_test.go asserts the
3 archived ones under the tag (together = the full 12-command demotion set).
- doctor_test.go TestDoctorStaleReplacementsExist: tolerates a deprecated-command
replacement absent from the spine build (e.g. "ao curate" → flywheel); the
archive-tag run is the strict check. ("curate → quality deprecation map".)
- uat_smoke_test.go TestUATSmoke_DefragDedup: skips in the spine build (it runs
`ao defrag`); runs under the flywheel tag.
Proof:
- default `ao {corpus,curate,defrag} --help` => unknown command; `ao buildtags`
=> spine.
- `-tags "flywheel legacy"` restores all three; `ao buildtags` => flywheel/legacy.
- both variants: build + vet + test ./... green (spine 11988, flywheel-legacy
11978 passed). make regen-all committed; make regen-check + verify-buildtags OK.
SCOPE NOTE: the bead's package archival (internal/wiki|pool|ratchet) and the other
9 commands are NOT included — proven infeasible as specified; see the bead report.
The 3 packages are spine infrastructure (validate.go's ratchet Validator engine,
gate.go's pool queries, inject/metrics citation ledger, transitive
internal/lifecycle + internal/search), and the other commands' runtimes are
spine-invoked (session_end runs dedup+maturity; flywheel_close_loop uses
pool-ingest+store-index; poolCmd/flywheelCmd host spine subcommands) or their
internals are tested by cross-cutting untagged spine tests.
# Conflicts:
# evals/agentops-core/cli-command-surface-matrix.json
# evals/agentops-core/fixtures/cli-command-surface-smoke.sh
Ship gen-membrane-receipts.sh + check-membrane-receipts-freshness.sh in the
embedded pawl bundle (make sync-hooks + //go:embed all:pawl) so a stranger repo
can render its own membrane-receipts proof page with no AgentOps checkout.
ao verify receipts mirrors the pawl review trust split: in-checkout dogfood runs
the live repo generator; anywhere else it extracts the EMBEDDED generator and
runs it against the user's OWN git repo under the sanitized cold env
(pawlReviewColdEnv) with AO_BIN pinned to the running binary — no ao-on-PATH
needed, no repo-local script trusted. The generator refuses the build-from-cli/
fallback when PAWL_UNTRUSTED_REPO=1 (compiling+running an untrusted repo's
cmd/ao would be RCE); the in-repo path keeps its fallback. Fail-closed
chain-verify-before-render and the count-parity check are unchanged.
Byte-identical to the current generator on this repo's ledger (behavior lock).
Bats acceptance in a throwaway repo with a production-writer-built ledger:
render + tamper-refusal + embedded==in-repo parity.
# Conflicts:
# docs/cli-surface.json
# docs/cli-surface.md
Deterministic regen follow-up to 9220e8c4b: COMMANDS.md re-sorts sections by
the new experimental cobra group (now including the just-landed rk3r.5 verify
surface); registry generated_at. Proof for this commit is deterministic
(derived.changed-scope gate + regen idempotency), split from the code commit
to stay under the cross-family reviewer's diff-size ceiling.
Archive the two remaining self-contained RPI/factory command clusters behind
//go:build legacy:
- orchestrate (orchestrate.go + _common/_preflight/_route/_shape/_status/_tools/
_verify + tests). The internal/orchestration LIBRARY stays untagged — spine
stamp_shape.go still uses it; only the command files are archived.
- loop (loop.go + loop_* subcommand cluster + tests). The internal/evolve
library it imports stays untagged.
With orchestrate now archived, the doctor's stale-command fixer can no longer
point at 'ao orchestrate': repoint DeprecatedCommands["ao work rpi"] to the
surviving spine loop driver 'ao converge' (stale_refs.go + fix_skills_test.go).
Default 'go build ./...' now omits codex/orchestrate/loop/tick/autodev; the
sibling spine commands (ready/close/verdict-gate/council-gate/guard-status/
install-guards/chaos-test/converge/gate) remain. make build-flywheel /
AGENTOPS_LEGACY=1 make build restore the archived set. regen-all swept all
surfaces; both build variants build+vet+test clean (spine 12018, legacy 12033);
regen-check ALL GREEN.
Archive the RPI/factory commands whose spine entanglement was decoupled first:
- codex lifecycle (codex.go, codex_schema.go + tests) — spine symbols already
extracted to untagged codex_artifacts.go / codex_task_schema.go.
- autodev (autodev.go + tests).
- tick: only the 'ao tick' command surface moves to tagged tick_cmd_legacy.go;
the tick ENGINE (tickRuntime, newTickRuntime, tickPassthrough, tickSmoke,
tickVerdictIdentity, tickExitCouncil) stays UNTAGGED in tick.go because the
spine consumes it (ao claim, ao converge, ao eval chaos, converge_canary), as
do the sibling spine commands ready/close/verdict-gate/council-gate/
guard-status/install-guards/chaos-test.
Cross-cutting test wiring:
- testutil_archived_globals{,_legacy}_test.go: build-tag-split delegate that
saves/restores/resets the codex+autodev cobra-flag globals only in the tagged
build, so the untagged (spine) test helpers (resetGlobalFlags, executeCommand)
never reference archived symbols.
- cobra_commands_test.go / tick_test.go: drop archived names from spine
expected-command lists; tick subcommand coverage moves to tick_cmd_legacy_test.go.
- flywheel_promoted_body_dedupe_legacy_test.go: the one codex-command test split
out of the otherwise-spine close-loop test file.
Default 'go build ./...' omits these commands; make build-flywheel /
AGENTOPS_LEGACY=1 make build restore them. regen-all swept the surfaces. Both
build variants build+vet+test clean; regen-check green.
ao done <bead-id> [--sha] [--force-no-verdict] [--json]: closes via the br CLI
on PATH with the close reason stamped [verdict:<sha7>:<disposition>]. CONFIRMED
ledger verdict -> CONFIRMED stamp; verdict-less provenance-only commit (the
pawl #trivial waiver class, fail-closed diff-tree) -> waived-trivial; otherwise
REFUSE naming ao verify / ao pawl review, with --force-no-verdict writing an
explicit greppable UNVERIFIED stamp. BEADS_DIR resolution reuses the
beadsTrackerCommandContext env path (no hardcoded ledger paths).
scripts/check-verdict-close-rate.sh: warn-only gate (threshold 0 baseline,
--strict --threshold N for the later ratchet) measuring the [verdict:...]
stamp fraction of the last N closes in the br issues.jsonl (last-wins per id);
skips cleanly when jq/BEADS_DIR/ao/ledger are absent.
Tests: 13 Go cmd-level tests (stub br on PATH + production-writer ledger
fixtures + t.TempDir git repos) + 14 bats over fixture JSONL. Narrow CLI-ref
regen (generate-cli-reference.sh) included to keep TestCobraConformance green.
The scenario-satisfaction layer's consumer (goalsfitness aggregator +
ao goals measure --scenarios-only) and contract (scenario-results.v1
schema/loader/writer) shipped long ago, but nothing ever wrote
.agents/rpi/scenario-results.json — a dead instrument reading
unknown/0%-evaluated for every directive. This lands the missing
producer, resolving the bead's decision rule to Option A (wire it,
writer-only, no council-judge dependency in v1).
ao eval scenario evaluate [--all|--directive <id>] [--json] [--timeout]:
- GATE-SHAPED scenarios (acceptance_vectors carrying a mechanical
"check" command; "gate:<id>" resolves through the GOALS.md Gates
table) run each check via goals.MeasureOne (sanitized bash, per-check
timeout, exit-77 skip convention). Score = fraction of checks passed;
verdict = score vs the scenario's own satisfaction_threshold, exactly
matching the aggregator's countSatisfied comparison.
- JUDGMENT-SHAPED scenarios (no mechanical check) are recorded as
verdict "skip" with attestation-needed evidence — the nearest
ValidVerdict for "cannot mechanically evaluate"; never a fabricated
pass (the anti-pattern GOALS.md's pre-production section forbids).
- A check that could not run (timeout, unresolvable gate ref) yields
skip, and missing/retired scenario links write NOTHING, so zero
evidence stays VerdictUnknown downstream.
- Results persist through the production scenarioresults.Writer.Append
(latest-judged_at supersede per scenario_id; iteration = prior + 1).
L2 tests prove the full producer->consumer round trip: the command
writes the artifact, the production loader accepts it strict, and the
real runScenariosOnly/EvaluateSatisfaction path reads back nonzero
evaluated counts (pass, fail, judgment/unknown, timeout, unresolvable
gate ref, supersede-on-rerun, threshold-equality lanes).
Nightly cadence wiring deliberately deferred to a follow-up commit.
Known: cli/docs/COMMANDS.md conformance regen deferred to landing.
Fold state, memory, rehydrate, handoff under `ao session` (zero-breakage via
hidden deprecated back-compat aliases for the three with external callers;
state gets none). `ao session bootstrap` is byte-identical — session_bootstrap.go
untouched.
Reparent rootCmd.AddCommand → sessionCmd.AddCommand and DELETE the GroupID lines
(stateCmd "knowledge", memoryCmd "config", handoffCmd "workflow") — cobra panics
at Execute if a child's GroupID isn't in the parent's groups, and sessionCmd
defines none. `ao --help` verified NO-PANIC.
Hidden aliases (Hidden + Deprecated→stderr, so COMMANDS.md is a pure move but
`ao handoff` / `ao memory sync --quiet` / `ao rehydrate` still execute) share the
same RunE + flag helpers; --quiet registered on both the session child and the
alias. cobra_commands_test parent map + a new TestSessionFoldedCommandsResolve;
stale_refs deprecation map. regen-all swept the surfaces (cli_commands 81->77).
go test = 7963 pass; surface-parity/smoke/cli-skills-map/regen-check all green.
Fold the top-level eval commands under `ao eval`, zero-breakage via dual-register
(old spellings kept Hidden, new eval subcommand shares the same RunE):
- retrieval-bench -> ao eval bench
- chaos-test -> ao eval chaos
- session-outcome -> ao eval session-outcome
- scenario subtree reparented under ao eval (add/init/list/validate follow)
`ao eval scenario-ab` preserved (ADR-0004 revival path).
Repointed inline `ao scenario` body-refs in skills/goals + eval-outcomes/flywheel
to `ao eval scenario` (+ mirrored the goals/eval-outcomes Codex twins + regen hashes)
so the FULL-tier skill.body-refs gate stays green. stale_refs.go deprecation map
updated; new eval_fold_surface_test.go (go.command-test-pair). regen-all swept the
command surfaces (cli_commands 84->81 as the 3 hidden leaves leave the public catalog).
go test ./... = 12046 passed.
Commands-only archive of the cleanly-separable RPI/factory commands behind
//go:build legacy: turn (turn_verify.go), harness (harness_cmd.go), operator
(operator_cmd.go + operator_adapter.go). Default `ao` surface drops them;
AGENTOPS_LEGACY=1 make build / make build-flywheel restore them; all four
build+test variants pass. regen-all swept the surfaces.
Scope note (mirrors .13): a leaf-audit found only turn/harness/operator archive
cleanly commands-only. The rest are spine-entangled: codex (context_ranked_intel
+ membrane use codexArtifactRef), evolve, orchestrate (multi-file cluster), loop
(cluster), tick (converge_canary uses tickExitCouncil), autodev (shared
testutil_test.go resets autodevFile/autodevForce). These need the same
spine-decoupling as the corpus/flywheel clusters — folded into a legacy spin-out.
Tag harvest.go + its tests with //go:build flywheel — a clean leaf (no spine
symbol consumer). Default surface drops it; make build-flywheel restores it;
all four build+test variants pass. regen-all swept the surfaces.
Scope note: an empirical leaf-audit found that of the corpus/flywheel command
list, only refinery/mind/harvest archive cleanly commands-only. The rest (wiki,
mine, dedup, defrag, corpus, compile, curate, pool, maturity, flywheel, ratchet,
store, temper) export symbols consumed by SPINE code (root.go error handling,
membrane.go, pawl.go, session-end maintenance, the deprecation map, uat smoke) —
they need the same spine-decoupling as the internal packages, so they are folded
into the age-nzwo spin-out rather than archived here.
Commands-only archive (operator decision: full package archival deferred to a
spin-out because spine inject/gate/eval import internal/wiki|pool|ratchet).
Tag refinery.go + mind.go + their tests with //go:build flywheel: the default
`ao` surface drops them; `make build-flywheel` restores them. regen-all swept
the command surfaces (cobra expectedCmds, COMMANDS.md, cli-surface, eval matrix).
Archive infrastructure (applies to all batches): the spine-conformance tests
(TestCobraExpectedCmdsMatchRegistration, TestCobraConformance,
TestBuildtags_DefaultBuildIsSpine) now skip when len(archiveBuildTags)>0 — the
generated surfaces document the default build, and the flywheel/legacy build is a
documented superset, so both `go test ./...` and `go test -tags flywheel ./...` pass.
ao cron was a no-op compatibility shim: its own help said cron self-adjust
'moved behind the MTO/factory boundary; AO keeps only this compatibility shim'
(the scheduler was removed in ADR-0009). The one unambiguous delete in this
epic. No live callers (only historical snapshots + generated surfaces, now
regenerated clean). Removed cron.go, cron_self_adjust.go, and its test;
regen-all swept the command surfaces; cobra children-map entry cleaned.
Delete recall.go + memory_ingest_claude.go (+tests): cm (cass-memory) + cass already
do cross-agent curated+history recall, and cm beat ao recall 6-0 (docs/memory-v1.md
SUPERSEDED banner). ingest-claude was the MEMORY.md-index-pollution regression. Regen
drops both from the CLI command surface.
SCOPE RE-PLANNED from the bead (agile, build-evidence-driven): the audit's 'inject leaves
are pure, no external consumer' was WRONG — inject.go holds shared constants/utils (~9
survivors) AND wiki.go's runWikiInject routes to runInject. So retiring the ao INJECT
command is a separate P0-like decouple (relocate shared utils + repoint wiki) tracked as
a follow-up bead, not bundled here. This phase ships the clean, on-thesis win: recall +
ingest-claude gone. Build+vet+162 cmd/ao tests + regen-check green.
Ingests ~/.claude/projects/*/memory/*.md (390 curated facts stranded in Claude-only
silos) into ao's machine-tier corpus (~/.agents/learnings/) so 'ao recall' covers
them. Content PRESERVED verbatim (no LLM extraction); each wrapped as an ao-learning
tagged tier=machine + source=claude-memory + origin_path, with the source mtime as
the decay date. Idempotent — dest filename derived from source path (claude-memory--
prefix makes provenance visible in recall's cited path), so re-run refreshes in place,
never duplicates. --dry-run + --source/--dest flags. Missing ~/.claude is a clean no-op.
Proves the .5 acceptance end-to-end: a fact living ONLY in a Claude silo
(pawl-run-foreground-not-background) was a recall MISS before; after ingest it surfaces
[machine] tagged claude-memory. Closes the biggest miss-log gap from the .7 validation.
ao recall is the single memory front door all agents (Claude/Codex/Gemini) share:
queries the per-project repo .agents/ CURATED corpus (PROJECT tier, resolved from
the repo root, never $HOME) + the per-machine ~/.agents/ hub (MACHINE tier) over
ao's EXISTING lexical + freshness-decay(exp(-ageWeeks*0.17)) + maturity scorer.
Over-fetches per tier then merges/de-dupes/ranks GLOBALLY and caps to --limit (so
--limit N is the global best N, not per-tier-truncated). Each hit is tier-tagged at
collection time and cited {tier,path,score,snippet}. Curated-only (raw transcripts
are ao search). Lexical-only — NO dense/embeddings/CGO (dense DEFERRED per miss-log).
--limit validated (no negative-bound panic). 10 unit tests incl. repo-root walk-up,
$HOME-guard, and limit validation.
Contract docs/memory-v1.md ratified: ao=SOT; .agents/+~/.agents/ are the stores;
cm + the Claude per-project silos become ingest-then-retire inputs. Non-goals held.
VALIDATION: evals/memory/recall-acceptance.sh is a REPORT-ONLY topic-recall SMOKE
(21/25 topics surfaced in top-5, echo-free; rg baseline 0/5 — recall wins on ranked
tier-cited retrieval). It deliberately CERTIFIES NOTHING. The rigorous KNOWN-ITEM
ship gate (memory-v1.md done-criteria #3: the RIGHT cited memory, >=20/25) is bead
.8 — OPEN — earned as the corpus is enriched (.5: ingest the Claude silos). .6
(contract) + .7 (front door) land here; .8/.4/.5/.2/.3 remain the tracked follow-on.
S4 of the smart-membrane PROVE-FIRST plan (epic age-zpj5) — the honest recurrence+compilability
instrument that gates the DEFERRED compiler. Its realistic verdict today is INSUFFICIENT-DATA (the
corpus is far below the power floor) — the PROVE-FIRST point: do NOT build the compiler on faith.
- yieldledger/triage.go (pure, tested): TriageDecide (pre-registered numeric rule — INSUFFICIENT-DATA
if <15 stored-reason classes OR axis2-coverage<100%; elif axis1<0.20 MEMORY-ONLY; elif axis2<0.33
CURATED; else GO; unassessed in the Axis-2 denominator so a small assessed subset cannot inflate);
AssessCompilability (all-instances TP-replay — a detector is compilable only if it HITS every stored
bad instance AND zero-FP on clean HEAD; an overfit/no-op detector is not_compilable); TriageCorpus
(recurrence over DetectCatches class_keys; reason-less REFUTEDs are the UNCLASSIFIED floor — counted,
NEVER synthesized into a class).
- Catch gains per-instance data (Instances: head_sha + paths) so the all-instances TP-replay can replay.
- ao membrane triage command: reads the corpus, runs the git-content TP-replay assessment per recurring
detector class, applies the rule, prints the decision + axes + counts (or --json).
- Tests (a-f): below-floor -> INSUFFICIENT-DATA; straddle fixtures -> exact branch (MEMORY-ONLY/CURATED/GO);
unassessed-in-denominator -> coverage<1.0 -> INSUFFICIENT-DATA; no-fabrication floor; no-op + overfit
detectors -> not_compilable; command-level INSUFFICIENT-DATA. yieldledger 78 pass; cmd/ao 7795 pass; vet clean.
regen-all updated COMMANDS.md / cli-surface / surface-matrix / codex twins for the new command.
S3 of the smart-membrane PROVE-FIRST plan (epic age-zpj5) — the CONSUMPTION side of membrane memory on
the S1/S2 foundation:
- recallCatchesByDomain (cmd/ao/membrane.go): DetectCatches filtered by domain, optionally narrowed to
catches whose affected_paths OVERLAP the given paths (set intersection). The abundant memory (escapes
are structurally rare; catches are not).
- 'ao membrane recall --include-catches [--paths f1,f2]': RECONCILES the shipped escape-keyed recall —
escapes (existing, unchanged when the flag is off) PLUS catch classes; JSON emits {escapes, catches}.
- pawl-review.sh: recall_prior_catches injects a 'MEMBRANE MEMORY — PRIOR CATCHES IN THIS AREA' block into
the reviewer prompt (domain + touched files), so the reviewer verifies the change doesn't reintroduce a
prior catch. FAIL-SAFE: prints nothing when ao absent / no domain / no catches.
- ADVISORY INVARIANT (TestRecall_IsAdvisoryNeverAGate): a static source scan asserts the recall functions
are called ONLY from runMembraneRecall — recall is advisory memory, NEVER a gate/verdict input; the test
fails if call-site count is 0 (non-vacuous). Plus TestRecallCatchesByDomain (domain + path-overlap filter).
- Verified end-to-end: a recorded catch surfaces in recall with overlapping paths, is filtered out with
non-overlapping paths; shellcheck -S warning clean; cmd/ao 7795 pass; go vet clean; back-compat preserved.
regen-all: COMMANDS.md / cli-surface / registry / codex twins updated for the new flags.
S2 of the smart-membrane PROVE-FIRST plan (epic age-zpj5) — the structured catch EMIT layer on the
S1 foundation:
- 'ao membrane catch --bead --domain --reason [--paths] [--detector-pattern --globs --detector-kind]
[--mode] [--head] [--run]': the structured catch recorder (cmd/ao/membrane.go). Records a REFUTED
gate-verdict carrying domain+reason+affected_paths (+ optional detector) via the production Writer;
class_key computed at emit. The manual twin of the pawl-review REFUTED branch. Judgment-class catches
(no detector) are path-recallable; detector-bearing catches become compile candidates (Axis-2, S4).
- pawl-review.sh REFUTED branch (cold + routed) now emits a structured catch via 'ao membrane catch'
(emit_pawl_catch): domain from the changed-files' top dir, reason from the REFUTED verdict text, paths
from the reviewed files. FAIL-SAFE + NON-BLOCKING (a missing ao or any error never blocks the REFUTED
exit — the catch is observability, not a gate).
- Tests: TestMembraneCatch_RecordsPathRecallableJudgmentClass + _DetectorMakesCompileCandidate (fixtures
round-trip through the production Writer -> DetectCatches/CompileCandidates). Smoke-verified end-to-end.
- regen-all: COMMANDS.md / cli-surface / registry / codex twins / surface-matrix updated for the new cmd.
cmd/ao: 7793 pass; go vet clean; shellcheck -S warning clean; regen-check green.
Spawn the strongest membrane the host's accounts can form, instead of tri-or-fail.
CAPABILITY-ADAPTIVE (age-4o33):
- up probes installed families (claude/codex/agy), spawns ONLY those (canonical order; pane
index = position in the enabled set), gates only those, stamps the session TIER: multi
(>=2 families => mode=multi-model) | fresh (1 family => mode=fresh-context). Fail-fast: a
host without codex/agy gets a working fresh-context membrane, not a hard failure.
- route operates over the ENABLED set; tier-aware min confirmers (2 multi / 1 fresh); any
REFUTE blocks; fail-closed on insufficient. A fresh-context verdict is recorded HONESTLY so
a high-irreversibility door (push-to-main) can still refuse it.
- pins (--dual/--tri/--models); idle-TTL reap + 'ao pawl reap' (substrate/cron-driven auto-down,
no in-repo daemon per ADR-0009). ao pawl: +reap; up/route shorts reflect adaptive/tier reality.
VERDICT + SESSION-STATE HARDENING (age-nomq) — BOTH caught by the cross-family review of this branch:
- verdict_of now requires the WHOLE LINE to be the verdict (only non-alnum TUI chrome before
PAWL, only whitespace after). A reviewer NARRATING the format ('... PAWL <nonce> CONFIRMED')
previously false-matched -> a fail-open. Anchored both ends; awk prints the last field.
- _touch_route_ts wrote session.json via python's spaced JSON, which the compact-format grep
readers (load_session / _session_idle) then failed to parse -> after the FIRST route a session
reverted to the 3-family default (mis-indexed panes) and the idle reaper died (-1 forever).
Writer now emits compact separators; readers tolerate any spacing.
Tests: 56 bats (adaptive 26, verdict-parse 9, session-state 4, tri-model 17 back-compat) + 5
cmd/ao Go tests. Pure helpers + the write->touch->reload round-trip + both verdict false-match
directions are all locked.
The bead intent is 'ao pawl up / ao pawl route / ao pawl metrics', but those lived only
in scripts/pawl.sh ('bash scripts/pawl.sh <cmd>'); 'ao pawl' exposed only 'review'. Wire
the 5 service subcommands into the ao CLI (each forwards verbatim to scripts/pawl.sh and
propagates its exit code via pawlReviewExitError — so a REFUTED route exits non-zero
through ao). + 2 cmd/ao tests (exit-code propagation; subcommand registration) + 4 honest
cmdao-surface-allowlist rows (live-ATM service commands + bats-covered metrics). Completes
the ao pawl surface; the standing warm-duel service is now first-class on the CLI.
age-standing-pawl-service-ml8
GOALS.md: the ao CLI is the product; its headline acceptance mechanism (the
cross-family review) lived only in scripts/. ao pawl review wraps scripts/pawl-review.sh
(the sanctioned pattern — 25 ao commands wrap scripts/, mirrors agents.go) and propagates
its exit codes VERBATIM via a typed pawlReviewExitError (0 CONFIRMED · 3 REFUTED · 4
converge-advisory · 2 usage · 1 error), like ao plan-pawl / ao validate. DisableFlagParsing
forwards all flags (incl. --converge). 9 Go tests. Multi-model deferred (agy auth-blocked).
Bead: age-hgoh
The self-improvement loop fired only on ONE box: .agents/constraints/index.json is
gitignored, so a clean CI checkout / fresh clone enforced nothing — a constraint
learned locally never hardened the repo for anyone else. EM.2.9 makes the learning
travel.
Cross-family council CONFIRMED Option A:
- ao constraint publish (NEW): exports the ACTIVE constraints to a TRACKED surface
docs/constraints/published.json. DELIBERATE act, not auto-on-activate — a derived
regex that hardens the whole repo for everyone should be a conscious, committed,
reviewable change (mirrors the draft->activate human gate).
- SANITIZED: search.SanitizeForPublish strips the private fields (finding_id +
the .agents/-pointing source_artifact/review_file/file paths), so the tracked
surface carries ONLY the enforceable detector — no private findings/evidence leak.
- The enforce gate now reads the UNION of the local (gitignored) index and the
tracked published surface, merged by id with active-wins. A clean CI checkout
(no .agents/) enforces exactly what was published. Malformed published.json fails
CLOSED, consistent with the local index. Retiring a published rule is a tracked
change to the published file, not a silent local override.
Proven on the shipped binary (probe + done-test step 8 TRAVEL): publish -> hide
.agents/ entirely (simulate a clean clone) -> the published constraint STILL FAILs
the re-introduction. The em-loop done-test now asserts the COMPLETE loop in 8 steps:
fixture -> compile -> activate -> block -> safe -> cite -> load -> travel.
Tests: SanitizeForPublish no-leak (search); publish writes sanitized active-only
(cmd/ao); gate PublishedTravels-no-local-index + MalformedPublished-fail-closed +
merge active-wins (gates). make regen-all updated the command surfaces for the new
`ao constraint publish`.
Bead: age-membrane-memory-arch-tz2s.2.9
The membrane's SPC governor, slice 2 (control-loop-model.md §4): adjust ONLY on
special-cause, and admit a gate ONLY when it's two-sided fit.
- yieldledger.DetectFalseAlarms: the cry-wolf signal the gauge lacked. A false
alarm is NOT the mirror of an escape — REFUTED-then-CONFIRMED at a DIFFERENT
head_sha is normal rework (the membrane working). Only a reversal on the SAME
head_sha (membrane overturned itself on unchanged code) is a false alarm. The
signal is deliberately conservative (the safe direction for the fitness gate).
- governor.ShouldAdjust: special-cause noise-band. A domain with >= K escapes
(one per bead) in the rolling window is a repeated pattern worth adjusting for;
below K, or scattered across domains, it is common-cause noise -> HOLD (adjusting
would be tampering / cry-wolf).
- governor.FitnessAdmits: two-sided gate. Admit iff catch-rate rises AND the
false-alarm rate does not. A gate that catches more by crying wolf is rejected.
- ao governor noise-band command (informational; exit 0).
Design fork (false-alarm signal / control limit K=3 / admit rule / shape) settled
by mixed council (Claude + Codex unanimous); the same-head_sha false-alarm
correctness point was Claude's refinement of the council's symmetric framing.
Closes age-wy3.2
The membrane's SPC control-loop governor, slice 1 (control-loop-model.md §4):
the error budget is the top governor — inside tolerance keep shipping (hardening
would be tampering); budget burned -> stop the line and harden.
New cli/internal/governor package: EvaluateBudget computes, over a rolling window
of the most-recent gate-verdicts ACROSS ALL RUNS,
rolling_escape_rate = escapes_in_window / confirmed_in_window
burn_rate = rolling_escape_rate / tolerance
decision = HARDEN iff confirmed_in_window >= min_confirmed AND burn_rate > 1.0
The min_confirmed floor enforces special-cause-only adjustment. One-escape-per-bead
(a CONFIRMED refuted several times is ONE miss). Cross-run windowed escape detection.
Deterministic, derive-on-read.
New ao governor budget command: exit 0 = ship, 3 = harden (mechanical stop-the-line).
Design fork settled by mixed council (Claude + Codex unanimous); the cross-family
pawl then caught a multi-refute double-count, now fixed + regression-tested.
Closes age-wy3.1
ao wiki lint now runs deterministic structural health checks over the Go wiki:
broken/ghost wikilinks, invalid/missing frontmatter, missing required fields,
and orphan pages (human + --json, non-zero exit on blocking defects). ao wiki
list / ao wiki status enumerate and summarize the wiki. A narrow, safe --fix
strips ONLY dangling wikilinks (never deletes a page, never touches a valid
link, idempotent, atomic writes). The legacy WikiPipeline LINT stage moves under
--pipeline-stage; an explicit --vault still routes there.
Implemented by a fresh-context agent, then hardened through a 3-round cross-family
review (Claude+Codex) that caught 2 data-safety/compat defects its green tests
masked:
- --fix mangled UNRELATED whitespace (a global collapse rewrote intentional
multi-space alignment far from the removed link). Fixed: the strip removes the
token + exactly one of its OWN adjacent spaces, touching no other whitespace;
regression pins interior + trailing spaces.
- `ao wiki lint --vault` was silently ignored on the new structural path (linted
the active workspace, exit 0, no report). Fixed: an explicit --vault routes to
the legacy stage; regression pins it.
ao wiki doctor unchanged. Full cli suite 12066 pass; command-surface regen.
Closes age-port-openkb-into-agentops-go-5qw.4
Second slice of the OpenKB→Go port epic: source management over the workspace
created by `ao wiki init`. (watch is deferred to its own slice.)
- `ao wiki add <file|dir>...` copies supported sources (.md/.markdown/.txt) into
raw/ and records registry entries atomically (sha256, slug id); directories
are walked; unsupported types (PDF/URL — adapter follow-ups) are reported+
skipped; re-add is idempotent by raw filename.
- `ao wiki remove <doc> [--dry-run] [--keep-raw]` removes a registered source +
its derived wiki artifacts (sources/summaries/concepts/entities/explorations/
reports) + the registry entry. --dry-run REPORTS every artifact without
deleting; errors on an unregistered doc (no silent success).
- `ao wiki recompile [doc] [--all] [--dry-run] [--refresh-schema]` re-runs the
llmwiki ingest stage (raw/ -> wiki/sources/); --dry-run lists registered
sources; --refresh-schema rewrites the workspace config.
SAFETY (destructive remove): every copy AND delete is anchored at the
symlink-resolved workspace root and routed through the scaffold containment
guard (scaffoldSafeAbs) — within-root, no reserved (.agents/.ao) segment, and
NO symlink path component. So a planted symlink derived-artifact cannot make
remove delete outside the workspace (tested: TestRemoveSource_ContainmentRejectsSymlinkArtifact);
dry-run mutates nothing; --keep-raw preserved.
Resolves the workspace via wikiResolveWorkspace (active selection or --path).
Conversion stays behind Go file handling so PageIndex/markitdown are adapters,
not hidden Python deps. URL ingest + watch are sibling slices.
Acceptance:
- cd cli && go test ./internal/llmwiki ./cmd/ao -run 'TestWiki.*Add|TestWiki.*Remove|TestWiki.*Recompile'
- destructive-safety + registry units: go test ./internal/wiki -run Source
Generated docs/registry regenerated (make regen-all).
Closes age-port-openkb-into-agentops-go-5qw.2
First slice of the OpenKB→Go port epic (5qw): the KB scaffold/config/schema.
Adds `ao wiki init [path]` and `ao wiki use <path>` in Go so a usable wiki
workspace stands up without OpenKB Python.
- `ao wiki init` creates the OpenKB-style layout (raw/, wiki/{sources,summaries,
concepts,entities,explorations,reports}, output/{skills,decks}), seeds
wiki/index.md, wiki/log.md, and a wiki/AGENTS.md schema, and writes
wiki/config.yaml (model, language, entity_types, thresholds). Idempotent:
existing dirs/files are preserved; config is rewritten for --model/--language.
- `ao wiki use <path>` records the active workspace repo-locally
(.ao/wiki/active-workspace) so later commands resolve it.
- internal/wiki/scaffold.go: typed ScaffoldConfig + Scaffold/Read/Write +
active-workspace state. Self-contained workspace — does NOT write into the
private .agents/ corpus or the gold .ao/wiki view (preserves the
raw/private vs gold/public boundary, per the bead's risk note).
Validates the AUTHORED config core (model/language/entity_types/thresholds) the
bead specifies; source ingestion + compilation + generation are sibling beads
under the epic (kept accretive — existing wiki subcommands unchanged).
Acceptance:
- cd cli && go test ./cmd/ao ./internal/wiki -run 'TestWiki.*Init|Test.*Schema'
- ao capabilities | grep -q 'wiki' (and ao wiki --help shows init/use)
Generated artifacts regenerated (make regen-all): COMMANDS.md, cli-surface,
command-surface matrix + smoke, registry.json.
Closes age-port-openkb-into-agentops-go-5qw.1
New `ao beads verify-acceptance <bead-id>...` reads beads via br (never the
retired bd) and asserts each bead carries the acceptance contract SHAPE for its
issue_type: feature->Gherkin+TDD, spike->decision-criteria, design->formal-spec,
test->assertion-inventory, cutover->migration-checklist. task/epic require a
baseline acceptance signal (NOT Gherkin) per the "acceptance on every bead"
mandate (skills/plan/SKILL.md); unknown types are UNDEFINED (reported, never
silently skipped).
Advisory by default (exit 0); --strict maps any FAIL or UNDEFINED to a non-zero
exit. Fail-opens closed across iterative cross-family review:
- br {error:...} object (exit 0 on missing id) rejected at parse time;
- task/epic no longer free-pass;
- partial br response omitting a requested id errors (defense-in-depth);
- EXACT-WORD heading matching + colon-anchored yaml keys (not loose substring
or word-prefix): prose ("No acceptance_criteria yet.") / near-words
("## Specific") no longer false-PASS;
- list/checkbox checks are SECTION-SCOPED: a bullet under an unrelated heading
no longer satisfies another section's contract;
- unknown issue_types never silent-PASS.
SCOPE (operator-approved 2026-06-20): validates contract SHAPE, not CONTENT
QUALITY (placeholder/TBD rejection + canonical fenced-yaml-schema validation) —
tracked as follow-up age-xmkn. Deprecates the bd-shelling `ao beads scenarios`.
Bead: age-membrane-memory-arch-tz2s.1.5
Round 2/3 of cross-family review caught that the --out containment was only
LEXICAL: an in-repo symlink (--out link/gold -> /tmp/victim) passed filepath.Rel
but os.RemoveAll/CopyFS followed it outside the repo. Each round I under-hardened
the SAME user-controlled-path surface (lexical .. -> symlink -> next TOCTOU).
Decisive fix: eliminate the class. Real publish has NO --out; it writes only to
the canonical base/.ao/wiki (the same dir ao wiki gold owns). No operator-
controlled RemoveAll target -> no .. escape, no symlink escape, no TOCTOU. The
canonical .ao/wiki inherits only the gold compiler's pre-existing behavior (no
NEW risk). Simpler code, strictly safer. --expect-digest + the verdict gate are
unchanged.
7 unit tests green (bead-required, confirmed-writes-to-.ao/wiki, no-verdict-fail-
closed, leak-beats-verdict, expect-digest-mismatch, + dry-run). Build/vet/test +
regen all green.
Refs: age-port-openkb-into-agentops-go-5qw.9
Completes the wiki publish membrane gate per the cross-family council decision
on age-xf9r (option c, Claude + Codex both picked it): real `ao wiki publish`
(with --bead) is gated on a CONFIRMED pawl verdict bound to the gold-PRODUCING
COMMIT — reusing the live fail-closed verdict->commit authority
(scripts/pawl-verdict.sh check). NO schema change, NO new node type, NO 6th
ledger. The trust anchor is 'this commit was reviewed', which the pawl gate
already proves; gold is deterministically compiled from the corpus at HEAD.
Flow (real publish): recompute candidate digest + RE-RUN the leak scan (never
trust a prior dry-run; fail closed on any leak) -> optional --expect-digest pin
(publish exactly what dry-run reviewed) -> resolve HEAD -> pawl-verdict.sh check
<bead> 0 --head HEAD (fail closed if not CONFIRMED) -> write the EXACT reviewed
candidate to --out (default .ao/wiki) so published == scanned == digested.
checkPawlVerdict + resolveHeadSHA are injectable seams for tests; --out is
guarded against root/cwd/repo-base before the destructive clear.
Verified: 8 unit tests (bead-required, confirmed-writes, no-verdict-fail-closed,
leak-beats-verdict, expect-digest-mismatch, unsafe-out, + dry-run) and a REAL
end-to-end smoke (real git HEAD + real pawl-verdict.sh: 'publish refused: no
CONFIRMED pawl verdict ... fail-closed', nothing written). Build/vet/test +
gate 29/29 + regen all green.
Optional follow-up (council-noted, non-blocking): a queryable provenance link
via an existing-schema artifact --wasAttributedTo--> verdict edge.
Refs: age-port-openkb-into-agentops-go-5qw.9
First slice of the wiki publish gate: the view-first product AND the first
membrane signal, on existing code. Compiles a fresh publish candidate from
.agents/ (to a temp tree, never touching .ao/wiki), computes a STABLE
content digest, and runs the canonical leak scan (corpusscan — same marker
registry as ao corpus scan) over the candidate, FAILING CLOSED on any hit.
- internal/wiki/publish.go: CompilePublishCandidate -> {Digest, Stats, OutDir}.
Digest is sha256 over the sorted (relpath, bytes) of the gold tree. The
compiler clock is PINNED (publishDigestEpoch) so undated entries don't bake a
date-of-run into the digest — the identity is purely content-derived (the key
a publish verdict will bind to).
- cmd/ao/wiki_publish.go: ao wiki publish --dry-run prints digest + leak result
(+ --json), exits non-zero on a leak. Real publish is REFUSED with a pointer
to the verdict-gate design fork (age-xf9r) rather than guessed at.
Tests use REAL persisted shapes: stable-digest, digest-tracks-content, cleanup,
and a leak that survives sanitize (a fleet marker 'shield' — sanitize only
scrubs secrets/$HOME/UUID; corpusscan guards fleet/brand/myth) so the
second-layer scan is proven to catch a real private span. Build/vet/test green;
gate 30/30; regen clean.
The verdict-gated REAL publish (a CONFIRMED verdict bound to the digest) is the
next slice — its ledger representation is an open design decision: age-xf9r.
Refs: age-port-openkb-into-agentops-go-5qw.9
E4.2. The only bead-tied yield emitter (reconcile-pr.sh) read tokens
from AO_YIELD_TOKENS_* env that nothing sets, so every usage event
carried 0. The only session-writer is the historical mining path
(forge_tier1), which has no bead context — wiring it to silver would
pollute per-bead work-yield gauges. So the correct bronze->silver seam
is a derivation: sum the real tokens from the bead's session transcript.
- ao yield tokens --transcript <path> [--json|--pair]: parse a session
transcript and sum the real tokens_in (full input footprint incl.
cache) / tokens_out via the E4.1 parser capture. The bronze->silver
derivation primitive.
- reconcile-pr.sh emit_yield_usage: when AO_YIELD_TRANSCRIPT is set and
tokens weren't supplied explicitly, derive real tokens via 'ao yield
tokens --pair' instead of the 0 default. Fail-open (any error keeps 0).
- regen: COMMANDS.md + cli-surface for the new command.
The producer that sets AO_YIELD_TRANSCRIPT is the orchestrator (ao
orchestrate, age-tlj6); until it lands this is a latent-but-correct seam
that fires whenever a transcript is passed. TDD: core + cobra-cmd tests,
live-binary smoke (default/json/pair/missing) + reconcile bash block
verified end-to-end. Full cmd/ao green; all drift gates green.
Refs: age-membrane-memory-arch-tz2s.3.2