Commit Graph

354 Commits

Author SHA1 Message Date
boshu baaa9e22da feat!: prepare AgentOps 4.0 cathedral cut 2026-07-15 10:00:26 -04:00
boshu cb2a7ac398 refactor: complete the cathedral cut 2026-07-15 00:21:02 -04:00
boshu 4823077621 refactor: cut AgentOps to a single-pass evidence loop 2026-07-14 22:01:50 -04:00
boshu 3a57e64a73 feat(quickstart): one-screen diet output + environment-tailored golden paths
Default `ao quick-start` output was ~3 screens (double banner, 4-step LIVE
PATH block, a philosophy quote, and a full readiness table). Builds on #907
(command-string correctness) with a PRESENTATION + TAILORING pass:

- Default is now a single-screen diet (<=40 lines): a one-line completion
  header, a Created summary (GOALS.md marked "template — edit me"), a compact
  readiness checklist, environment-tailored golden paths, exactly one Next
  action (/plan) + one docs link (docs/first-value-path.md), and the tightened
  first-verdict close. The ASCII banner and the "Stateful environment..."
  quote are gone from the default.
- Golden paths are filtered by exec.LookPath: br/gt/codex/agy paths render only
  when their binary resolves here; an absent core tool (br) collapses to one
  short enable line instead of a run-this block. Skill paths (/plan) always fine.
- --verbose restores the full step-by-step long form for anyone who wants it.

Tests (L2, PATH fixtures via t.Setenv + t.TempDir):
TestQuickStart_OutputOneScreen (<=40 lines, one Next, no banner/quote),
TestQuickStart_GoldenPathsResolveInEnv (every rendered run-this argv[0]
resolves; absent tools omitted), TestQuickStart_GoldenPaths_AbsentToolEnableLine,
TestQuickStart_VerboseKeepsLongForm. Existing #907 guards
(AllAOCommandsResolve / NoRemovedCommands / journey tombstones) still pass.
2026-07-13 19:31:15 -04:00
boshu 8bb08edc9c feat(cli): add ao skills unlink — rollback inverse of skills link
Add the uninstall/rollback twin for `ao skills link`: `ao skills unlink`
removes exactly the live-tier symlinks link minted — those whose target
resolves into this repo's skills/ tree — across every installed runtime
(~/.claude, ~/.codex, ~/.gemini, ~/.cursor, ~/.pi). Idempotent and
non-destructive: foreign symlinks pointing elsewhere and real directories
(a foreign corpus such as jsm) are reported as foreign and never removed;
stale owned links (skill since removed from the repo) are still cleaned up.
Supports --dest, --dry-run (persistent), and --json, mirroring skills link.

Document the uninstall path in docs/install-day2-ops.md: per-runtime plugin/
skill removal (Claude, Codex, AGY, OpenCode), `brew uninstall agentops`,
`ao skills unlink` for clone-linked skills, and an explicit 'what is kept'
note that .agents/ and quick-start artifacts (CLAUDE.md block, GOALS.md) are
user-owned data the uninstall deliberately never touches.

Regenerate the affected command-surface projections (COMMANDS.md, cli-surface
.{json,md}, the eval surface matrix + smoke fixture). The matrix/smoke counts
also absorb pre-existing origin/main drift (checked-in expected sub=120 vs
actual tree 112); regen brings them to the truthful 113 (112 + unlink).

Tests (L2 round-trip, t.TempDir): RemovesOnlyOwnLinks (foreign symlink + real
dir survive), DryRunWritesNothing, Idempotent, MissingDestIsNoop,
RemovesStaleOwnedLink, EmptySrcFailsClosed, ResilientAcrossDests.
2026-07-13 18:50:29 -04:00
Bo 1d4c003e05 Cut plan: gate registry diet, legacy bash gate removal, CI collapse, pawl warm-service removal (#906)
## Summary
- **Wave 1** — remove the `always.wiring-closure` meta-gate, demote
`docs.skill-refs` to Full tier, delete 8 orphan scripts.
- **Wave 2** — delete the legacy bash pre-push gate (`pre-push-gate.sh`,
2,279 lines) and its parity machinery; collapse `validate.yml` from 18
jobs to 5.
- **Wave 3** — remove the pawl warm NTM standing service; the cold
review spine (pawl-review / pawl-verdict / pre-push check) is untouched.
- **Wave 4** — gate registry diet: cut 24 doctrine/meta check IDs and
~70 backing scripts/workflows/bats; trim nightly knowledge-cycle job.

Net: gate registry 112 → 83 checks (incl. main's additions), ~24k lines
of scripts/CI/bats deleted. Routine one-file fast gate now runs in ~19s.

## Test plan
- [x] `go build` / `go vet` / gates+goals+ports package tests green
- [x] `ao gate check --fast --scope range:origin/main..HEAD` — 66
passed, 0 failed, 1 skipped (199 changed files)
- [x] `validate-ci-policy-parity.sh` PASS; validate.yml / nightly.yml
parse
- [x] `regen-changed-scope --check` green; registry.json regenerated
- [x] Grepped tree for dangling references to every deleted script/check
ID

Made with [Cursor](https://cursor.com)

---------

Co-authored-by: boshu <241868352+boshu2@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-13 14:45:20 -04:00
boshu ce805178e7 Merge remote-tracking branch 'origin/main' into crank/age-nw28h.7.8-stack-v3
# Conflicts:
#	docs/contracts/four-umbrella-write-manifests.json
#	skills/catalog.json
2026-07-13 10:34:48 -04:00
boshu d38b1b4e6b feat: shadow mechanical findings before blocking 2026-07-13 10:19:00 -04:00
boshu b7b0f7ea27 merge(go-cli): integrate sealed families on current main (age-nw28h.7.8) 2026-07-13 08:51:37 -04:00
boshu bb6f4b58cf refactor(gate): cut over to internal command module 2026-07-12 01:29:48 -04:00
boshu 8f779fea9e test(eval): cover migrated command surface 2026-07-12 00:33:01 -04:00
boshu e100b8b132 docs(cli): refresh doctor command help (age-nw28h.2.7.4) 2026-07-11 20:52:20 -04:00
boshu 8a9c3293a8 test(doctor): cover mutation command wiring (age-nw28h.2.7.3) 2026-07-11 19:42:37 -04:00
boshu 48a6bc3635 docs(cli): refresh commands after beads module (age-nw28h.2.1) 2026-07-11 15:43:26 -04:00
boshu 663f5c1756 feat(provenance): ao provenance reconcile — detect/repair verdict-file↔ledger-edge desyncs (age-pawl-intent-zhndq.3)
F2 makes the verdict->edge emit fail-closed at WRITE time, but history + crash windows can
still leave a verdict FILE with no bound EDGE — a silent desync where 'ao done' (which reads
the LEDGER, not the file) refuses the close, discovered one-refused-close at a time. This
command scans .agents/pawl-verdicts/*.json and, for every CONFIRMED/REBOUND verdict, reports
whether a matching verdict->commit edge is bound, reusing the EXACT recognizer the push gate
and ao done use (hasConfirmedVerdictEdge — exact-token disposition, sha-bound, never substring).

--emit re-emits missing edges (idempotent — only the UNBOUND ones detected this pass, so a
second run is a no-op); --force allows a dirty ledger (default: refuse, age-7krl — never sweep
another lane's rows); --json for machine output. Exit 0 clean / 1 unbound-or-emit-failed / 2
usage. Report-only default is safe (no mutation).

Operational data (this repo, per the bead): 251 verdicts scanned, 1 unbound — and it is a
harmless orphan from this session's own messy F1 land (a dead sha 6cbcc0f5 reset away during
the concurrent-session recovery), NOT a systemic desync. Validates F2 (prevents new) + F3 (finds
existing). Tests: TestScanReconcileVerdicts (bound/unbound/REFUTED-skipped/malformed) + empty-dir.

Regenerated CLI surface (COMMANDS.md, cli-surface.{json,md}, eval matrix + smoke) for the new command.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 15:28:12 -04:00
boshu 0514f82ec3 feat(pawl): present review as the user front door; warm verbs grouped operator-only (age-hk5zg.2)
'ao pawl --help' listed the warm standing-service verbs as flat peers of review,
pushing users toward operator machinery (NTM, projects_base) they cannot run.
Cobra groups now split the surface: review under 'Use the membrane (the front
door — needs no NTM, no setup)', up/down/reap/health/doctor/smoke/route/metrics
under 'Operate the standing warm service (operator-only; requires the NTM swarm
substrate)'. Grouping/labeling only — every verb keeps its name and behavior.
docs/contracts/pawls.md carries the same operator-only note.

Fix swept in: scripts/lib/sku_extract.py's cobra help parser only recognized
'*Commands:' section titles, so EVERY grouped command's subcommands were
invisible to the SKU catalog ('ao goals' was already affected; this change made
'ao pawl' the second victim and surfaced the bug). The parser now enters any
non-reserved column-0 title block and requires the 2+-space name/short gap, so
grouped subcommands resolve precisely (registry.json reflects the corrected
projection for ao goals too). Locked by TestPawlHelpPresentsReviewAsFrontDoor.
2026-07-10 20:44:30 -04:00
boshu 4e584f2297 feat(cli): deliver truthful executable CLI spine (age-cli-truthful-spine-2f5n.19) 2026-07-10 19:30:53 -04:00
boshu 851dd4da3f feat(cli): ao skills link — add Pi (~/.pi/skills) to the runtime fan-out (age-sh3b6)
Adds .pi to the detected runtimes, so `ao skills link` also links into
~/.pi/skills when ~/.pi exists (Pi agent runtime). Same detection + resilient
fan-out; --dest, Long help, README, and the fan-out test updated. Follow-up to
age-d686g.
2026-07-10 18:59:51 -04:00
boshu 9d1b1ed2fb feat(cli): ao skills link fans out to every installed runtime, not just Claude (age-d686g)
Bo found the gap: the command only linked ~/.claude/skills, but Codex reads
~/.codex/skills and AGY/Gemini reads ~/.gemini/skills — so Codex and AGY users
got nothing. Default now links into every runtime whose config dir exists under
$HOME (~/.claude, ~/.codex, ~/.gemini, ~/.cursor), matching link-skill --all;
--dest still overrides to a single dir. Falls back to ~/.claude/skills when no
runtime is detected. resolveTargetDests + 3 subtests (explicit/fan-out/fallback,
HOME-isolated); help guard extended to assert the Codex/AGY dirs are documented.
README + --help repositioned to the live-symlink-from-a-clone distinction across
runtimes (supersedes the reframe-only age-ivcba). E2E: --dry-run fans out to all
4 installed runtime dirs.

Non-destructive unchanged: a foreign real dir is a conflict, never clobbered.
2026-07-10 18:41:54 -04:00
boshu 1d1ddd3ce8 feat(cli): ao skills link — sync repo skills missing from ~/.claude/skills (age-u031)
Scans skills/ against a destination live-tier dir (default ~/.claude/skills)
and symlinks every skill dir that has no entry yet. Idempotent and
non-destructive: existing symlinks are left as present, and a name owned by a
real dir (a foreign corpus like jsm) is reported as a conflict, never clobbered.
Honors the global --dry-run; --dest overrides the target.

The focused fix for 'a new skill landed on main but Claude cannot see it':
merging a skill dir mints no symlink and /reload-skills only re-reads existing
links. 5 L2 tests cover link/idempotent/dry-run/conflict/non-skill-dir; whole
cmd/ao package green (7853 tests).

Note: the regen also re-sorts a few eval-scenario/session cli-surface entries to
the current canonical (ASCII) order — latent sort drift on the base, benign, and
required for regen-check to pass.

Reviewed: fail-closed guard added per codex-fresh-review
2026-07-10 15:54:35 -04:00
boshu c6e23d2036 feat(skills): mesh native genies and factory orchestration (age-o43w) 2026-07-10 10:52:06 -04:00
boshu 5c96a49e64 feat(yield): ao yield report — the on-the-loop governance surface (yield + andon queue) (age-mv67)
The one command a returning operator reads after hours of autonomy, no
transcript required (docs/architecture/the-flywheel.md — the human moves
from in the loop to ON it):

  YIELD        gate-verdict counts (CONFIRMED/REFUTED/ESCALATE/HOLD) since
               the cutoff, catches recorded (DetectCatches over the windowed
               ledger), beads closed in the window (tracker-agnostic).
  ANDON QUEUE  blocked beads, ESCALATE/HOLD pawl verdicts, and any REFUTED
               verdict whose bead is still open (stalled slice) — id, why
               parked, age; deduped, oldest-parked first.

--since accepts RFC3339 or a duration (default 24h); --json emits the full
struct. Honest empty-states (andon queue: empty — nothing parked). Beads
access reuses the ao beads exec internals (resolveTracker +
canonicalizeBDReadJSON, canonical br {issues:[...]} shape) behind an
injectable seam; tracker failure degrades to a reported beads_error, never
fatal. Executed-red TDD: seeded production-writer ledger + stubbed beads.
Flips the flywheel status-ledger row for the async governance surface to
landed.
2026-07-09 20:23:59 -04:00
boshu d91e010439 feat(membrane): ao membrane digest --deltas — per-class recurrence before/after for the producer-defect register (age-de5t)
The register's "Recurrence before → after" column — the catch→producer
loop's honesty check (ADR-0014) — was hand-typed "(pending next triage)".
This makes it mechanical: --deltas --since <ISO-date|RFC3339> splits every
catch class's round-collapsed hits around the cutoff (a producer fix's land
date) and prints before=N since=M per class, sorted still-recurring first
(since DESC, before DESC, ClassKey ASC); a 0-since class is marked improved.
Read-only: writes no checklist, never edits the register — BP.7's runner
records the numbers. --json emits the machine shape; placeholder classes
filtered as in default mode.

Timestamp source: the ledger envelope ts (RFC3339, validated on load),
carried onto CatchInstance.TS at the FIRST REFUTED round of each distinct
(bead, head) occurrence in DetectCatches (round-collapse preserved).

Docs: producer-defect-register How-to-use step 5 + BP.7 measurement
sentence (skills/post-mortem + bespoke codex twin, hashes regenerated);
COMMANDS.md + registry.json regenerated.
2026-07-09 15:18:21 -04:00
boshu bfa21dd523 feat(membrane): ao membrane catch --evidence — Go reason-extraction replaces emit_pawl_catch bash (age-ulab)
Absorb emit_pawl_catch's reason-extraction from scripts/pawl-review.sh into
the ao CLI (shift-left epic age-tc0l): --evidence <file> applies the same
two-tier REFUTED salvage in Go (last 'VERDICT: REFUTED <text>' sentinel, else
the first substantive 'REFUTED: <finding>' prose line — age-9931 parity —
else the placeholder), resolves domain from the first changed file's top dir
and affected paths (first 20) from git by --scope head|staged, all via the
existing buildCatchInput + yieldledger.Writer sink. Explicit --reason /
--domain / --paths win over extraction; all prior flags unchanged.

emit_pawl_catch collapses 53->30 lines to a single fail-safe ao call keeping
the || true non-blocking contract and the PAWL_CATCH_CLASS/DETECTOR
passthrough; embedded copy re-synced (make sync-hooks, validate-embedded-sync
PASS). New seam bats lock the delegated flags + non-blocking contract;
table-driven Go tests cover the salvage tiers and git-derived domain/paths.
2026-07-09 15:00:03 -04:00
Boden Fuller fbba8af5ac feat: land audit hardening and goal-design workflow 2026-07-09 12:20:53 -04:00
boshu 1f346c741a feat(membrane): ao membrane digest filters reason-less placeholder classes so the checklist is actionable (age-7758)
After class-normalization the digest collapsed 22->11 classes, but the top-5 was
STILL all reason-less `pawl-review REFUTED (see evidence)` placeholders (historical,
evidence gone) — the one substantive class (a `[gates]` gate-routing gap finding) sat
buried at rank 6. Injecting placeholders into the pre-mortem checklist = pure noise.

- yieldledger.IsPlaceholderReason: conservative predicate for a non-substantive
  reason — a bare/near-bare token ("r"), the pawl verdict STAMP itself (anchored
  regex on "pawl-review REFUTED/CONFIRMED ..." so digit-less bead ids stripBeadRefs
  leaves intact are still caught), or a reason that normalizes to only disposition
  boilerplate + bead-id fragments. A real defect sentence keeps its content tokens
  and is NEVER flagged (guarded by tests, incl. a reason that mentions the pawl
  mid-sentence).
- ao membrane digest EXCLUDES placeholder classes by default so real-reason classes
  lead the emitted .agents/pre-mortem-checks/catch-digest.md; --include-placeholders
  restores them for corpus auditing, always ranked BELOW every actionable class.
  The checklist reports how many placeholders it filtered (honest, no moat language).

Real-ledger payoff: BEFORE top-5 was 5 placeholders; AFTER the default digest shows
exactly the 1 substantive [gates] class and filters all 10 placeholders — an honest
result that the corpus is still placeholder-dominated and needs real-reason catches
to accrue.
2026-07-09 09:42:17 -04:00
boshu df2200f285 feat(membrane): ao membrane digest — global recurring-catch checklist into .agents/pre-mortem-checks (age-xbmf) 2026-07-09 07:30:01 -04:00
boshu b9d2103b28 feat(land): ao land <bead> — fresh in-checkout binary → trusted pawl path → atomic pawl-land handoff (age-m21d)
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).
2026-07-08 20:23:16 -04:00
boshu 10aed08971 feat(beads): ao beads exec — tracker-agnostic CRUD passthrough for bd + br (age-3mdu) 2026-07-08 16:08:56 -04:00
boshu eb8351d7c7 feat(beads): ao dual-tracker detection + resolution + ao beads tracker (age-fvr8) 2026-07-08 15:16:33 -04:00
boshu deaa2087b4 feat(evals): standing membrane calibration harness + baseline run (age-e508.2)
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).
2026-07-08 10:17:12 -04:00
boshu 7b9d6ab46a docs(crank): fold the serialized multi-lane land protocol into crank references (age-e508.3)
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.
2026-07-08 08:02:42 -04:00
boshu efbde52d42 feat(beads): ao beads epic-status --terminal group-terminality predicate (age-gascity-port-slate-irye.4)
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.
2026-07-07 22:45:23 -04:00
boshu bb1b612921 feat(membrane): catches record SEMANTIC classes — cross-bead recurrence becomes measurable (age-jjt8)
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.
2026-07-04 17:45:01 -04:00
boshu 8426604f37 feat(beads): ao beads dir --require — fail-closed ledger resolution for br writes (age-gstf)
'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.
2026-07-04 10:08:41 -04:00
boshu cc559b21cb feat(pawl): strict two-family cold quorum — opt-in, refuses to degrade, honest-UNAVAILABLE until a second strict-eligible family (age-rk3r.13) 2026-07-02 19:37:26 -04:00
boshu 04456783e4 feat(verify): ao verify init — portable pre-push verdict ratchet + chain verify (age-rk3r.6)
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
2026-07-02 14:13:13 -04:00
boshu 092ae748f9 feat(gates): --scope range + non-empty --json FAIL detail for landing loops
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
2026-07-02 10:08:52 -04:00
boshu 0ad6a16f0a chore(cli): archive corpus + curate + defrag commands behind flywheel tag (age-nzwo)
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
2026-07-02 01:07:22 -04:00
boshu bc9fb32709 feat(verify): ao verify receipts — portable proof page from the embedded bundle (age-rk3r.12)
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
2026-07-02 00:46:05 -04:00
boshu e8aee1f6c9 feat(verify): ao verify stats — cost-of-verified-done instrument from the ledger (age-rk3r.4) 2026-07-02 00:39:12 -04:00
boshu 83bd46aa13 chore(cli): regen command surfaces for experimental help grouping (age-h4y3)
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.
2026-07-01 23:50:49 -04:00
boshu 7acd272c8c chore(cli): archive orchestrate + loop commands behind legacy tag + regen (age-h4y3)
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.
2026-07-01 23:24:17 -04:00
boshu 4e96fee1e3 chore(cli): archive codex + autodev + tick commands behind legacy tag (age-h4y3)
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.
2026-07-01 22:58:23 -04:00
boshu 0b7be07ec3 feat(cli): ao done — verdict-referenced bead close with warn-first close-rate gate (age-wedge-all-in-dyr0.4)
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.
2026-07-01 18:56:43 -04:00
boshu e9d30d6647 feat(cli): ao verify — canonical front-door verb over the pawl review engine (age-wedge-all-in-dyr0.1) 2026-07-01 17:57:37 -04:00
boshu da0181f886 feat(goals): ao scenario evaluate — the scenario-satisfaction producer (age-wedge-all-in-dyr0.10)
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.
2026-07-01 17:36:35 -04:00
boshu 820bae2e62 feat(provenance): ao provenance show — per-sha/bead human verdict lineage renderer (age-wedge-all-in-dyr0.7) 2026-07-01 16:37:53 -04:00
boshu 543df54fbf refactor(cli): fold session-continuity commands under ao session (age-focus-membrane-bookkeeper-m1wg.17)
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.
2026-07-01 08:40:16 -04:00
boshu 15c83640da refactor(cli): consolidate eval-family commands under ao eval (age-focus-membrane-bookkeeper-m1wg.16)
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.
2026-07-01 08:14:26 -04:00