1054 Commits

Author SHA1 Message Date
boshu 06b0cbe773 refactor(loop): route wave feedback through learn 2026-07-13 09:31:20 -04:00
boshu 3243af0cc0 merge(main): refresh validation-learning split (age-nw28h.7.8) 2026-07-13 09:06:31 -04:00
boshu 776b87c3bb refactor(loop): split validation from learning 2026-07-13 08:57:08 -04:00
boshu d9619b8dcc fix(integration): select current compatibility oracle (age-nw28h.7.8) 2026-07-13 08:53:02 -04:00
boshu 112e80d00a fix(compat): seal terminal family lineage state (age-nw28h.7.9) 2026-07-13 08:53:02 -04:00
boshu b3fc5bf506 test(go-cli): correct integration RED contract (age-nw28h.7.8) 2026-07-13 08:53:02 -04:00
boshu f362c4db0f test(go-cli): capture stale integration baseline (age-nw28h.7.8) 2026-07-13 08:53:02 -04:00
boshu a2423cfaca refactor(loop): require four umbrella receipts 2026-07-13 08:23:27 -04:00
boshu 9c6a662203 feat(loop): refactor the four-umbrella operating loop
Bead: age-four-umbrella-loop-refactor-xz4ps.1
2026-07-13 06:48:50 -04:00
boshu 412474d7ab feat(compat): add source-bound oracle v2 (age-nw28h.7.9) 2026-07-12 20:21:32 -04:00
boshu 15bc5119d9 fix(membrane): make helper escalation terminal (age-izmjq.1) 2026-07-12 17:16:28 -04:00
boshu 8f8f1bcfd6 feat(skill-factory): enforce deep conformance at release waists 2026-07-12 17:00:02 -04:00
boshu a192905e7a fix: route retry breakers through helper escalation 2026-07-12 16:06:50 -04:00
boshu b53832edec feat(rpi): consult pawl before raising andon (age-h433.60) 2026-07-12 08:26:59 -04:00
boshu 4f48f41828 fix(age-ghk3i): resolve GPT-5 codebase audit findings 2026-07-12 07:32:19 -04:00
boshu 287c2797eb feat(pawl): surface the embedded bundle stamp in ao pawl doctor — trust-honest (age-pawl-intent-zhndq.19)
F4 stamped the embedded pawl bundle with a deterministic content hash; this surfaces it in
`ao pawl doctor` so the landed!=installed class (an installed ao silently running OLD pawl scripts —
it bit this very session) is diagnosable.

THE DESIGN IS THE LESSON. Two cross-family codex refutes killed the obvious implementation:
  round 1 — I hashed any repo containing scripts/pawl-*.sh and reported STALE. A stranger repo with
            same-named files would be falsely failed.
  round 2 — I gated that on "identity markers" (the verdict schema + pawls contract). Codex refuted
            again, correctly: FILE PRESENCE IS NOT IDENTITY. Every marker is attacker-controlled and
            spoofable, so no marker list can ever make it safe.
The real fix is not a better marker list — it is a TRUST BOUNDARY. A staleness FAILURE must never be
asserted against a repo the binary does not trust. So:
  - trusted dogfood path (untrustedRoot == "", i.e. the ao binary lives inside the checkout — the ONE
    trust decision the whole pawl surface is built on): the LIVE scripts run in place, so there is
    nothing to compare and nothing to warn about;
  - untrusted/installed path: surface the stamp INFORMATIONALLY (so the operator can diagnose
    landed!=installed themselves and rebuild), and assert NO verdict about a repo we cannot trust.
This is the honest feature, not a weaker one: the diagnostic signal is preserved while doctor cannot
be weaponized into failing on an untrusted tree.

Tests: TestPawlBundleStaleness — dogfood surfaces the stamp and never says STALE; an untrusted repo
is never reported STALE *whatever it contains*, including a repo that SPOOFS every identity marker a
naive check might have used.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 19:49:05 -04:00
boshu a914ae8757 fix(pawl): defuse the evidence-floor auto-flip — 76% false-positive rate measured (age-pawl-intent-zhndq.10)
THE ADVISORY WINDOW DID EXACTLY ITS JOB, and the answer is NOT the one the bead expected.

Measured over the 261 local verdicts whose head_sha is a live commit: 26 PASS, **199 FLOOR-fail
(76%)**, 36 fail for unrelated (stale/missing) reasons. The FLOOR failures include verdicts from
unambiguously SUBSTANTIVE reviews — codex caught 3 real defects in this session and those verdicts
still fail the floor.

ROOT CAUSE: pawl_evidence_floor demands a `file:line` finding OR a reviewed-scope attestation. But a
CONFIRMED review has, by definition, NO DEFECT TO CITE — a clean review legitimately reads "no
blocking defects". The floor CONFLATES "no defects found" with "no review performed", so every clean
CONFIRM from codex/agy fails it. Right idea, wrong signal.

CONSEQUENCE: FLOOR_ENFORCE_AFTER=2026-07-16 was FIVE DAYS AWAY and would have started HOLDing ~76%
of real reviews on a CALENDAR TRIGGER with nobody watching — silently breaking the membrane.

THIS COMMIT DEFUSES THE CLOCK ONLY (the minimal, safe, time-critical fix): the date is pushed to a
deliberately distant 2027-01-01 so no calendar event can auto-enforce a floor with a measured 76%
false-positive rate. The floor still MEASURES and WARNs (unchanged), and PAWL_FLOOR_ENFORCE=1 still
forces enforcement for tests/opt-in operators — enforcement is now an EXPLICIT, evidence-gated
decision instead of a silent clock event.

FIXING THE SIGNAL stays on .10: a clean CONFIRM must be provable-substantive WITHOUT a defect
citation, which means changing the REVIEWER PROMPT + verdict writer (emit an explicit reviewed-scope
attestation every time), then re-measuring, then flipping deliberately.

Tests: the floor suite is green (it pins behavior via the env override, clock-independent) + a new
regression pinning that the date cannot auto-fire in the near term.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 19:32:24 -04:00
boshu 83d6849e2f fix(pawl): bounded pane-death re-probe — catch a gone pane in seconds, not at the deadline (age-pawl-intent-zhndq.17)
F13 item 2. cod_dead/agy_dead treated an unreadable `pane_current_command` as ALIVE, so a pane that
was actually GONE was only caught minutes later by the engage deadline. Two shapes now route into
the new bounded _pane_gone re-probe:
  - an EMPTY read; and
  - a FAILED read — a VANISHED pane makes display-message exit NON-ZERO, and the old
    `cmd="$(...)" || return 1` short-circuited straight to ALIVE, so the very case this bead targets
    never reached the re-probe at all (caught by a cross-family codex refute; test 4 pins it).

_pane_gone declares death only when BOTH hold: the second read is also unreadable AND the pane
produced NO scrollback change across the probe (a producing pane is alive whatever the read says). A
real foreground command on the re-probe keeps it alive. The conservative bias is preserved for the
genuinely uncertain case; an actually-gone pane now hits the existing respawn path in seconds.
PAWL_PANE_REPROBE_SLEEP tunes the pause (default 2s).

Tests: pawl-pane-reprobe.bats (4): DEAD on two unreadable reads + no activity; ALIVE on a real
re-probe command; ALIVE while still producing output; and the codex-refuted FAILING-first-read case
now correctly reporting DEAD.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 18:40:44 -04:00
boshu cecaf479ca feat(pawl): embedded-bundle provenance stamp + doctor stamp row (age-pawl-intent-zhndq.4)
landed!=installed is a recurring class (an installed ao carries whatever pawl scripts were
embedded at build time, silently running old logic — it bit this very session). F4 adds the
MECHANISM: `make sync-hooks` writes a DETERMINISTIC content-hash stamp (sha256 of the three
embedded review scripts) to cli/embedded/pawl/BUNDLE_STAMP — it changes ONLY when they change, in
the SAME sync-hooks run (no always-dirty). `ao pawl doctor` gains a bundle row: the installed-binary
path shows the embedded stamp, the in-checkout dogfood path says live scripts run in place. The row
deliberately does NOT read $ROOT/scripts (on the embedded/stranger path $ROOT is the UNTRUSTED
caller repo; the full race suite's security guard TestEmbeddedPawlBundleCarriesVerdictWriterSibling
correctly caught a first attempt that did). A go:embed drift test (TestEmbeddedBundleStampMatchesScripts)
catches an embedded-script edit that forgot to regenerate the stamp.

Scoped: the trust-aware live-vs-installed STALE comparison (Go doctor layer) + the review-time
staleness warning split to .19 — they need the Go trust split to safely read a checkout.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 17:57:35 -04:00
boshu 1f5f8f6666 feat(pawl): persisted ao pawl bench/unbench verb + doctor bench-source (age-pawl-intent-zhndq.18)
F7-followup: benching was an invisible ambient env footgun (PAWL_BENCHED_FAMILIES="${VAR-agy}" —
exporting it empty silently un-benched everything, and nothing showed the effective set). Now it is
a persisted, visible state: `ao pawl bench <cc|cod|agy>` / `unbench <fam>` write
.agents/pawl/config.json (symlink-guarded, atomic, jq-backed), bare `bench` lists the effective set
+ source. Resolution precedence: env (set, even empty) wins as a one-shot override → persisted
config → the "agy" default; `ao pawl doctor` now prints the effective benched set AND its source
(default|config|env). `unbench` with no arg gives a clean usage line + exit 2 (no bash ${N:?} trace).

Tests: pawl-bench-verb.bats (config round-trip, source: config vs env, unknown-family reject,
clean usage). Existing pawl.sh-sourcing + swarm-bin suites green.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 17:05:14 -04:00
boshu 307895723f feat(pawl): per-panel agreement breakdown + collapse flag in ao pawl metrics (age-pawl-intent-zhndq.7)
F7 (core): the panel's own health was recorded but never surfaced — the audit found the tri
panel (cc cod agy) reached FULL agreement 0/22 times while dual (cc cod) was fine, and nothing
warned. 'ao pawl metrics' now groups routes by the family-set that served them and prints, per
panel, full-agreement rate + p50 latency, flagging AGREEMENT-COLLAPSE when a panel's
full-agreement is <30% over >=5 routes. On this repo's real corpus it correctly surfaces
'cc cod agy: 0/22 agree (0.0) COLLAPSE' vs 'cc cod: 10/19 (0.526)'. --json gains by_families.
Legacy null-family rows tolerated.

Tests: pawl-metrics-by-panel.bats (collapsing tri + healthy dual fixture; asserts the flag fires
on the right panel, not the healthy one). Existing pawl-metrics suite green.

Scoped: the persisted 'ao pawl bench/unbench' verb + doctor bench-source line split to a
follow-up (config.json write) so the high-value metrics legibility lands now.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 16:48:39 -04:00
boshu 587278f52e fix(pawl): portable jq idle clock + reap fail-safe on an unwritable clock (age-pawl-intent-zhndq.14)
F13 item 1. _touch_route_ts updated last_route_ts via python3 ONLY — on a host without python3 it
silently no-op'd, freezing the idle clock at the up-time, so 'ao pawl reap' would tear down an
ACTIVELY-serving session once PAWL_IDLE_TTL elapsed. Now it prefers jq (already a route-path hard
dep; same compact "key":value form the grep readers parse; atomic tmp+mv) with the python3 block
kept as a fallback. And cmd_reap FAILS SAFE: when the clock cannot be written at all (no jq AND no
python3, via new _clock_writable), it keeps the session warm + warns rather than reaping on a
frozen timestamp — never kill on a broken clock.

Tests: tests/scripts/pawl-idle-clock.bats (jq round-trip bumps + reads the clock near-zero; parseable
by the compact reader; _clock_writable true/false). Existing pawl.sh-sourcing suites unchanged.

Scoped: item 2 (bounded pane-death re-probe for cod_dead/agy_dead) touches the live route loop and
is split to a follow-up so this idle-clock fix lands clean and fast.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 16:37:35 -04:00
boshu 97fe459315 fix(pawl): friendly route usage + mixed-push progress breadcrumbs (age-pawl-intent-zhndq.11)
F10 UX batch (shell items): (1) 'ao pawl route' with missing args now prints a clean
'usage: ao pawl route <bead> <packet-file> [pr]' and exits 2, instead of leaking a raw
bash ${N:?} trace ('scripts/pawl.sh: line NNNN: 1: route needs <bead>'). (3) the pre-push
mixed-push gate (a full ~40s cockpit gate PER non-trivial commit behind a #trivial tip —
an otherwise-silent multi-minute hang) now prints 'gating commit K of N: <sha> <subject>'
before each and a per-commit elapsed after, turning the stall into a legible progress log.
Existing asserted substrings ('re-targeting the cockpit gate', 'cockpit gate PASSED for
non-trivial commit <sha>') preserved — 18/18 pre-push tests green, +2 usage tests.

Scoped: item 2 (cosmetic pawl.go dry-run 'atm spawn' -> seam-derived string) split off — it
touches the Go layer + its dry-run tests and would drag the ~500s Go gate into this shell
bead; tracked as a follow-up so this lands clean and fast.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 14:46:41 -04:00
boshu 60f26fcbf1 fix(pawl): fail-closed verdict->ledger edge emit — EDGE-UNBOUND exit 6 (age-pawl-intent-zhndq.2)
'No verdict = not done' is enforced on the ledger EDGE (ao done / verify_prepush read
the edge, not the verdict file), but emit_verdict_edge_checked was fail-OPEN: a failed
'ao provenance emit-verdict' only WARNed, so a CONFIRMED verdict FILE could exist with no
edge — a silent desync where pawl-review says 'ready to push' but ao done refuses the close.

F2 makes the emit fail-CLOSED: on a genuine emit failure (no trusted ao, or emit exits
non-zero) emit_verdict_edge_checked returns a distinct EDGE-UNBOUND code (7) IN-PROCESS (no
shared status file — race-free per pre-mortem), do_write propagates it, and pawl-review's
cold + strict write sites map it to exit 6 (distinct from a hard write failure = exit 1),
keep the verdict file (the recovery input), and print the exact re-emit command. Governed:
- PAWL_EDGE_FAIL_OPEN=1 restores warn-and-continue (status 0);
- a SUCCESSFUL emit is byte-identical (status 0);
- PREPUSH parking (emit succeeded, autobind deferred) stays a SUCCESS, never misclassified;
- the rebind/restamp paths stay FAIL-OPEN (|| true) — F2 governs the review/write path only.

Tests: pawl-verdict-edge-failclosed.bats (4: fail-closed/knob/happy/parking) +
pawl-review-edge-unbound.bats (2: e2e exit-6 + knob). The 2 autobind emit-failure tests
updated from the old fail-open (status 0) to the new fail-closed (status 7) contract.
Differential: pristine 0 fail, mine 0 fail across 7 suites — zero unintended regression.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 14:32:40 -04:00
boshu 2d3b1d1ac1 feat(pawl): PAWL_REQUIRE_SERVICE/--require-warm — warm-or-HOLD for eligible reviews (age-pawl-intent-zhndq.1)
The warm NTM panel is the intended review substrate but was never guaranteed: any
warm failure (auto-up failure, or a routed verdict that fails the real gate) silently
fell through to a cold codex-exec. PAWL_REQUIRE_SERVICE=1 / --require-warm converts that
silent cold fallback into an honest HOLD (exit 5) for warm-ELIGIBLE modes (non-strict,
non-converge, head/upstream, codex reviewer). Strict/converge/staged/non-codex remain
documented COLD exceptions (LAW 0) — the knob prints a notice, never silently ignores
itself. An untrusted/stranger repo requesting the knob HOLDs naming why (warm never runs
there). Default OFF: byte-identical to pre-F1.

The HOLD chokepoint sits BEFORE the cold refuter (no cold spawn on a HOLD) and removes a
NON-gate-valid routed verdict so the HOLD leaves no invalid artifact — but never deletes a
pre-existing gate-valid verdict (gated on the real check; two cross-family refutes hardened
this). Security (pre-mortem): untrusted-repo precedence checked so an injected
PAWL_NO_SERVICE cannot silently neutralize the knob.

Tests: tests/scripts/pawl-require-warm.bats (7, all green); differential confirms zero
regression (46 pre-existing env-dependent failures unchanged on pristine).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 13:37:05 -04:00
boshu e76a715b00 feat(cli): enforce command architecture boundaries (age-nw28h.1.5) 2026-07-11 13:25:12 -04:00
boshu b712416f71 feat(cli): freeze four-profile compatibility oracle (age-nw28h.1.1) 2026-07-11 10:32:22 -04:00
boshu f4cc56a698 feat(skills): route andon breaker trips through a bounded helper pass before human (age-yoicl)
The three-tier andon hardwired 'any breaker trip' to human: goal-design
routers, the packet scaffold, pawls.md escalation, flywheel doctrine, and
rpi error-handling all sent stuck-N-rounds / oscillation / scope-creep
straight to the operator. This restores the lost rung: a stuck context
consults a fresh one before it consults the operator.

- Tier 2 widens from council to helper (fresh context, cross-family model,
  or /council — council is its heaviest form); stuckness is
  model-adjudicable too.
- Breaker trips take exactly ONE bounded helper pass per blocker class;
  the helper returns UNSTUCK (concrete next action) or ESCALATE. Advisor,
  never a second driver — it does not take over the work.
- Human is reserved for: refusal lane, explicit judgment flag, exhausted
  budget, and blockers that survive their helper pass. Those classes skip
  the helper entirely.
- Scaffold + dispatch prompt now state the auto -> helper -> human ladder;
  route_back_rules values carry the helper clause (schema v1 unchanged).
- TDD: extended bats andon/prompt tests red -> green; codex twins regen'd
  (goal-design parity) / hand-mirrored (rpi bespoke).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-11 10:24:44 -04:00
boshu a94f77bbd4 fix(scripts): make check-epic-children-closed.sh tracker-agnostic via ao beads exec (age-5w8fd)
The no-epic-close-with-open-child gate was bd-only ('bd dep list'), so on br
repos (age-*) it errored 'no issue found' and was silently inert. Route child
enumeration through 'ao beads exec children <epic> --json' (br: plain id
lines; bd: JSON array — both parsed) and per-child status through
'ao beads exec show <child> --json' (canonical shape on both trackers).

Error contract, exactly as tested: a children-ENUMERATION failure is a hard
exit 4 (a gate that cannot see the children must not pass); a per-child
status read failure is fail-closed as an offender (WARN + exit 1) so one
unreadable child blocks the close without aborting the other offenders'
report. Hermetic bats suite rewritten to stub ao with both tracker shapes
plus both failure modes; proven live against br epics in both directions
(all-closed exit 0, 52-open-children exit 1).
2026-07-11 09:23:37 -04:00
boshu 233cbf9906 feat(skills): gate frozen JSM and ms projections (age-h433.2) 2026-07-10 23:45:18 -04:00
boshu 8e47876ff3 feat(gates): atomic-write adoption ratchet — first NEW consumer of the ratchet lib (age-ratchet-lib-extraction-bv7d.9)
Closes the 3-sweep crash-safety convergence gap: hand-rolled tmp+rename atomic
writes outside cli/internal/storage (24 grandfathered at cutoff, hand-audited;
the full comment stripper dropped one comment-only false entry vs the naive
count. Sanctioned own-rename packages are GRANDFATHERED not exempted, so
shrink pressure applies when they delegate).

Detector (pre-registered corpus, pre-mortem r2-r3 + pawl round): non-comment
os.Rename( AND non-comment (os.CreateTemp( | ".tmp) where non-comment means
AFTER stripping // tails and /* */ blocks (a comment mention never counts —
string-literal limitation pre-declared). Added-hunk guard fires EVEN on
grandfathered files. Growth guard ON. _test.go exempt. ADVISORY PERMANENTLY
at file-level fidelity; blocking requires a precision detector in its own arc.

Twin 15/15 on the pre-registered corpus incl. inline-// and /* block */
comment-strip exercisers. seed.go row go.atomic-write-ratchet (Full, advisory)
+ self-routing paths. NOTE the sweep's pool.atomicMove no-fsync claim was
stale (fixed a8267ddc8) — the payload is adoption convergence.

The lib's promise holds: a new shrink-only gate cost one detector + one
pinned file.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 22:12:53 -04:00
boshu 703db62eff refactor(gates): migrate check-doc-skill-refs --all-docs baseline onto scripts/lib/ratchet.sh (age-ratchet-lib-extraction-bv7d.8)
The riskiest migration (BLOCKING in the FAST tier, runs on every push),
scoped exactly as planned: only the --all-docs baseline load
(ratchet_load_pinned trailing-comment) and stale sweep
(ratchet_stale_entries over the still-offending HIT set) delegate to the lib;
the DEFAULT mode's byte-identical contract is untouched and never consults
the baseline.

Differential evidence (byte stdout/stderr/exit): DEFAULT, default --strict,
--all-docs, --all-docs --strict (the gate's operative mode), usage-error,
--help — all parity. Stale-report order becomes deterministic LC_ALL=C
(pre-declared sanctioned delta; was hash order). Both twins 21/21 — they
execute the repo script directly, so per the FM5 per-site table NO skeleton
lib copy is needed (and none was added). seed.go docSkillRefPaths gains the
lib (FM3). All 7 migrations complete: every pinned-list gate now shares one
ratchet implementation.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 21:43:21 -04:00
boshu 37df02f6d9 refactor(gates): migrate check-scenario-test-linkage onto scripts/lib/ratchet.sh (age-ratchet-lib-extraction-bv7d.7)
BLOCKING-gate migration with zero semantic change: allowlist parse ->
ratchet_load_pinned trailing-comment (original strip-#-then-trim on all real
shapes); stale sweep -> ratchet_stale_entries_by with the gate's ORIGINAL
exists-only predicate (full stale-prune upgrade stays a separate decision,
age-scenario-linkage-stale-upgrade-570y). Ambiguity-error path untouched.

Differential (byte stdout/stderr/exit): --json, --warn-only, usage-error all
parity; default mode parity except the pre-declared sanctioned delta (stale
entries now emitted in deterministic allowlist order, was bash hash order —
same set, same count, same rc). Twin 13/13 with skeleton lib copy.

FM3 self-routing REPAIR: the row previously matched only skillPaths — its own
script, allowlist, bats, and the shared lib were un-routed; new
scenarioLinkagePaths closes that.

Fix-forward: pruned 5 stale allowlist entries (compile/curate/perf/recover/
review .feature — retire-wave deletions; shrink-only direction). The gate's
remaining 10 uncovered-scenario errors are PRE-EXISTING on main (old gate
agrees) — routed to age-scenario-linkage-uncovered-backlog-szgyu.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 21:29:10 -04:00
boshu 9197d4235f refactor(gates): move docs-cli-snippets baseline arithmetic from python heredoc to ratchet lib (age-ratchet-lib-extraction-bv7d.6)
Second python-seam restructure (sibling of .5): python emits US-delimited
findings (rel/lineno/token/suggestion/snippet); bash owns baseline parse
(strip mode), two-way set arithmetic, and byte-identical messages incl. the
did-you-mean hint and repo-relative baseline path. python3 failure is a loud
rc 2 naming the cause.

Differential evidence (byte stdout/stderr/exit, same injected AO bin): real
repo PASS w/ counts, new-offender FAIL w/ suggestion hint, baselined PASS,
stale FAIL — all parity. Twin 11/11 with fixture lib copy (FM5). seed.go
docsCliSnippetsPaths gains the lib (FM3).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 21:11:45 -04:00
boshu 3d89804b0e refactor(gates): move ao-invocations baseline arithmetic from python heredoc to ratchet lib (age-ratchet-lib-extraction-bv7d.5)
Python-seam restructure (one ratchet implementation, not two languages'):
python now emits offender findings only (rel<US>lineno<US>sub<US>line records);
bash owns baseline parse (ratchet_load_pinned strip = original line.strip()),
two-way set arithmetic (ratchet_new_violations / ratchet_stale_entries), and
byte-identical messages.

Differential evidence (byte stdout/stderr/exit, same injected AGENTOPS_AO_BIN
for both sides): real-repo PASS path w/ counts, new-offender FAIL, baselined
PASS, stale FAIL — all parity. Twin 12/12 incl. new python3-missing -> loud
rc 2 (f-2026-07-09-proof-critical-tests-fail-closed) + fixture lib copy (FM5).
seed.go gains the explicit lib literal (FM3).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 21:03:21 -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 f9976105f4 feat(pawl): resolve public ntm binary first, atm alias fallback, via single seam (age-hk5zg.3)
The standing pawl-service hardcoded the operator's personal 'atm' alias at 8
invocation sites, so the shipped scripts only worked on the operator's machine.
One resolver seam (_pawl_swarm_bin: PAWL_SWARM_BIN override -> ntm -> atm) now
feeds every swarm invocation through "$SWARM"; the doctor 'atm-alias' check
becomes 'swarm-bin' and reports what actually resolved. Embedded copy synced
byte-identical. Locked by tests/scripts/pawl-swarm-bin.bats.
2026-07-10 20:41:32 -04:00
boshu eeb87e9823 refactor(gates): migrate check-new-scripts-use-preamble onto scripts/lib/ratchet.sh (age-ratchet-lib-extraction-bv7d.4)
The ORIGINAL grandfather machinery (the jsonl gate's port source) now
delegates to the shared lib: name-status changed-scope collection (incl.
upstream), base-ref map, intersection authority, shrink-only guard
(308 -> 218 lines; message shapes preserved by thin wrappers). Parse mode raw.

Differential evidence (byte stdout/stderr/exit): auto (no args), head,
worktree, usage-error, head+UTF-8 — all parity on the real repo; twin 10/10.
FM5 catch: the twin's SECOND fixture skeleton (initial-snapshot test) needed
the lib copy — exactly the per-copy-site landmine the pre-mortem pre-registered.
seed.go preambleRatchetPaths gains the explicit lib literal (FM3 closure test).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 20:27:32 -04:00
boshu 30b4cce1eb refactor(gates): migrate check-jsonl-scanner-ratchet onto scripts/lib/ratchet.sh (age-ratchet-lib-extraction-bv7d.3)
The extraction template comes home: grandfather load/base-ref/intersection
authority/shrink-only/changed-scope/added-hunk/regenerate all delegate to the
lib (368 -> 281 lines; message shapes preserved by thin wrappers). Parse mode
raw = original entry parsing. Regenerate now atomic (tmp+mv) with byte-identical
output (golden-compared old vs new in a fixture repo).

Differential evidence (ratchet-difftest, byte stdout/stderr/exit): real-repo
clean head scope (C + UTF-8), usage-error, worktree scope — all parity; twin
7/7. Sanctioned deltas: unreadable grandfather now loud rc 2; head scope gains
--root -m --first-parent (root/merge commits no longer certify empty — lib
handled-shapes contract). seed.go jsonlScannerRatchetPaths gains the lib (FM3).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 20:15:37 -04:00
boshu 32e92ca392 refactor(gates): migrate check-docs-demoted-claims onto scripts/lib/ratchet.sh (age-ratchet-lib-extraction-bv7d.2)
First consumer migration. Baseline parse -> ratchet_load_pinned cr-strip (the
gate's original CR-strip + comment/blank shape); stale computation ->
ratchet_stale_entries. Differential evidence: byte-parity (stdout, stderr,
exit) old-vs-new on clean real repo (C + en_US.UTF-8 locales), new-violation
fixture, stale fixture, CRLF baseline fixture via ratchet-difftest.bash.

Sanctioned deltas (fail-loud class, documented): unreadable baseline is now a
loud rc 2 (was a strict-mode abort rc 1); multi-stale report order is now
LC_ALL=C sorted (was unspecified hash order) — both pinned by new twin cases.
Twin skeleton copies ratchet.sh; seed.go demotedClaimsPaths gains the lib so
lib edits re-run this gate (FM3; self-extending routing test now covers it).

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 20:04:31 -04:00
boshu c0d12e4004 feat(gates): extract shared shrink-only ratchet lib (age-ratchet-lib-extraction-bv7d.1)
scripts/lib/ratchet.sh: the one implementation of the grandfather/baseline
ratchet mechanics 7 gates hand-roll today — per-consumer parse modes
(raw|cr-strip|strip|trailing-comment), scope-aware changed-file collection
incl. upstream, fail-closed growth guard (comm -13 + intersection authority,
RATCHET_GROWTH_GUARD=off escape for migrated gates), parameterized stale
predicates, generic regenerate (header-fn + LC_ALL=C sorted body), added-hunk
guard with ENVIRON-passed ERE.

tests/scripts/lib/ratchet-difftest.bash: byte-parity differential harness
(stdout, stderr, exit code separately) for the migration slices.

tests/scripts/ratchet-lib.bats: 24 cases — real pinned-shape round-trips per
parse mode, growth rejection, self-allowlist refusal, stale by set-diff and
by predicate, regenerate golden, added-hunk guard, harness smoke.

cli/internal/gates/checks/ratchet_routing_test.go: self-extending FM3 closure —
every check script sourcing the lib must route lib edits via its Match globs.

Pre-mortem: .agents/council/2026-07-10-pre-mortem-ratchet-lib.md (3 rounds).
Lib is FROZEN for the rest of the arc per FM6.

Claude-Session: https://claude.ai/code/session_01RDWPYn4ZS2SyBkcBvAM18u
2026-07-10 19:47:31 -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 80504d2419 feat(pawl): harden ao pawl service — dry-run correctness, single-JSON plans, installed-binary trust, atomic route lease, identifier containment, crash-safe state (age-l3xj)
Twelve defects, each with a regression test written to fail first. Five were the
operator's; SEVEN more were found by the cross-family refuter across THREE REFUTED
rounds on this very change — the membrane repeatedly catching its own hardening,
which is the product working as intended.

D1 dry-run was ignored. pawl leaves set DisableFlagParsing, so cobra never parsed
the inherited persistent flags: `ao --dry-run --json pawl up` reached RunE with
the flags still in args and the globals FALSE, so the service script ran for real
(observed in production: it created agentops--pawl-service). Mutating verbs
(up/down/reap/route/review) now extract the flags from raw args, report the planned
action, and execute NOTHING. Round 2 caught that the fix matched only the BARE
token, so `--dry-run=true` still spawned; both forms handled now, unparseable value
fails closed.

D2 JSON purity. A dry-run with --json emits exactly one parseable object: action,
dry_run, mutated, session, families, tier, planned_steps.

D3 cross-repo contract. `ao pawl up` from an ordinary repo died with "locating
AgentOps repo root". Service verbs now resolve like `ao pawl review`: live scripts
only when the ao binary lives inside the checkout, else the embedded bundle against
the caller's own git repo. Outside any git repo it fails closed. Round 3 caught that
`up`'s atm spawn still hardcoded PROJECT=agentops, so panes came up in the wrong
repo; PROJECT now defaults to the resolved repo (basename of ROOT), PAWL_PROJECT
overrides. Round 7 caught the other half: SESSION was still hardcoded
'agentops--pawl-service' while atm spawn creates '${PROJECT}--${LABEL}', so `up`
from personal-site spawned 'personal-site--pawl-service' but ran readiness and
lifecycle against 'agentops--pawl-service' (an orphaned pane). SESSION now defaults
to '${PROJECT}--${LABEL}' — the exact name atm creates — so spawn, readiness, route,
health, and teardown target one session; from the agentops checkout it stays
'agentops--pawl-service' (the operator's live session), PAWL_SESSION overrides.
Round 8 caught the Go side of that same rename: the dry-run planner still hardcoded
'agentops--pawl-service', so the plan misreported the target from any other repo.
The planner now derives the session exactly as the script does (pawlPlannedSession:
${PROJECT}--${LABEL}, PROJECT from PAWL_PROJECT or the repo basename), proven at
parity with the script live.

D4 installed-binary trust. pawlServiceCmd executed scripts/pawl.sh from the
marker-resolved checkout with no aoBinaryInside guard, so a forged repo's planted
script would run. Now behind the same trust split with pawlServiceColdEnv. Round 1
caught the same RCE one hop deeper (verdict writer invoked via the caller-derived
ROOT = the caller's repo; resolved script-relative via PAWL_SCRIPT_DIR). Round 2
caught that the verdict DATA then defaulted into the deleted temp bundle; both route
write sites pass --dir into the caller's repo.

D5 route safety. Bead ids are containment-validated before any write. The route
lease is an atomic exclusive lock DIRECTORY with owner metadata; down/reap acquire
it before teardown (no check-then-kill TOCTOU); session.json writes are atomic;
metrics reports skipped_corrupt explicitly. Round 2 found the lease was keyed by
repo not session (two repos, one tmux session, both routed), published owner
non-atomically (a publication-window contender declared a fresh lease stale), and
recursively deleted the unvalidated PAWL_ROUTE_LOCK override (data-loss). All fixed:
keyed by session outside any repo; dir mtime as the atomic freshness fallback;
rmdir-only, non-lease paths refused. Round 3 found the stale-reclaim was still not
single-winner (the reclaim returned success on a no-op AND renamed unconditionally,
so two contenders both acquired). Round 5 found the deeper interleaving that STILL
broke a rename-based reclaim: A finishes acquisition and holds a fresh lease, then
B — carrying the OLD generation it observed — renames A's fresh lease aside without
re-checking staleness, and both route. Fixed with a GENERATION-SCOPED break-token:
one breaker acts per stale generation, and it removes the lease only after
re-confirming (while holding the token) that it is STILL that same stale
generation, so a peer's fresh lease (a different generation) is never touched;
mkdir remains the sole ownership barrier. Wedge-free (a crashed breaker's token
ages out). Proven live: 8-way and staggered-entry concurrent races on a stale lease
each yield exactly one winner across repeated trials.

Round 4 caught a rebase hazard, not a logic bug: rebuilding pawl.sh from a stale
base during a hot-main reland had silently reverted a concurrent lane's
_route_emit_catch (age-2yh2), reintroducing the pawl-review-refuted-see-evidence
escape. Reconciled via 3-way merge (my changes + the intervening emitter restored)
and guarded by a standing test that the routed-REFUTE membrane-catch emitter stays
defined and reachable.

Round 24 closed two more: the dry-run planner now VALIDATES args the way the real
command does (route requires bead+packet and a valid id; `up --models` must name
known families), so `--dry-run pawl route` with no args exits 1 instead of
reporting a bogus plan (round 25 refined this to validate route args POSITIONALLY,
exactly as cmd_route does — a leading-dash `route --bogus …` is a bead the real
command rejects, so the dry-run must too, not skip it); and the route's verdict dir (`.agents/pawl-verdicts`) is
now symlink-guarded like the state dir, so a committed symlink can't redirect the
verdict write outside the repo.

Round 23 closed a heartbeat TOCTOU: the background heartbeat's ownership-check and
owner-write are not atomic, and heartbeat_stop killed without waiting — so a delayed
heartbeat could, after the route released and a successor acquired, overwrite the
successor's owner with our PID, dispossessing a live successor. heartbeat_stop now
WAITS for the heartbeat to die before the release runs, so any in-flight write lands
only in our own (about-to-be-removed) lease dir, never the successor's.

Round 22 + Round 26 closed a Python sys.path RCE: `pawl metrics` / `_touch_route_ts`
/ `resolve_engage_deadline` run `python3 - <<PY; import json` with the untrusted
repo as cwd, and Python puts cwd first on sys.path — so a repo-planted json.py would
be imported and executed even for read-only `ao pawl metrics`. ALL THREE invocations
use isolated mode (`python3 -I`), which drops cwd from sys.path and ignores
PYTHONPATH/user-site; a grep-guard test fails any future non-isolated invocation
(round 26 was a third one a merge added after the round-22 fix).

Round 20 extended the state-dir symlink guard to LEAF files: the chain guard
covered ancestors, but a committed `.agents/pawl/metrics.jsonl` (or session.json)
symlink was still followed by the `>>` append / write. Each leaf write now unlinks
a symlink there first (removing only the link, leaving the external target
untouched), so the write creates a real in-repo file.

Round 19 closed a state-dir symlink-escape edge: on the installed-binary path in an
untrusted repo, a committed `.agents` or `.agents/pawl` SYMLINK escaping the repo
would make session.json/metrics writes land outside it and `down` delete the
target's session.json. A symlink anywhere in the ROOT→STATE_DIR chain is now
refused before any state write (up/route die; down skips the rm), so repo-local
state can never escape the repo.

Round 18 + Round 21 resolved the session-state locality tension. The session's
family/pane LAYOUT is a property of the (global) tmux session, not of any repo, so
it now lives in a SESSION-scoped shared file (${TMPDIR}/pawl-session-<session>.json)
rather than repo-local `.agents/pawl/session.json`. This makes cross-repo route to
one existing PAWL_SESSION read the SAME layout `up` wrote (round 21: repo-local
state defaulted a shared `cc agy` session to `cc cod agy`, mislabeling the AGY pane
as codex), and the injective session-slug filename means one session never reads
another's state (round 18). A defensive stored-session match check remains. The
per-repo metrics.jsonl stays repo-local under `.agents/pawl/`.

Round 17 made the session→path map genuinely injective and closed a same-session
cross-repo evidence collision. The round-16 cksum suffix was NOT injective (the
refuter constructed two distinct sessions with the same CRC), so the slug now uses
a reversible HEX encoding — distinct sessions ALWAYS get distinct paths. And
because the cross-repo contract lets two repos target one existing PAWL_SESSION,
evidence is now scoped by session AND repo (hex(ROOT) appended), so two repos never
share `${bead}.packet.md` under one session.

Round 16 closed two more edges: the session→slug map was not injective (`sess+a`
and `sess=a` both sanitized to `sess_a`, colliding on one lease/evidence path for
two distinct sessions) — a suffix now disambiguates; and stale reclaim would
`rm -f` any regular file at ROUTE_LOCK as a "legacy lock" — a legacy lock is now
validated to hold only a numeric epoch, so an unrelated file (e.g. an operator's
/etc/hosts pointed at by PAWL_ROUTE_LOCK) is refused, never deleted.

Round 15 floored the freshness window: the supported PAWL_ROUTE_TIMEOUT=0 tunable
collapsed 2*ROUTE_TIMEOUT to 0, making every live lease instantly stale (age < 0 is
impossible). The window (2*ROUTE_TIMEOUT, floored to 2) is now a single helper
shared by _route_lock_fresh and the heartbeat-interval derivation, so the two can
never disagree.

Round 14 closed a symlink data-loss edge (a symlink at PAWL_ROUTE_LOCK is followed
by [ -d ]/mv/rmdir, so reclaiming it would delete the TARGET's owner — is_lease now
refuses any symlink) and corrected docs/contracts/pawls.md to match the shipped
behavior (session-scoped /tmp lease, projects_base-verified `up`, generation-token
+ 2*ROUTE_TIMEOUT reclaim, heartbeat) rather than the pre-fix description.

Round 13 caught the atm-project-model limit of the cross-repo `up`: `atm spawn
<project>` roots panes at projects_base/<project> (no cwd flag — atm's own help:
"the session name must match an actual directory under projects_base"), so
basename(ROOT) targets the wrong/missing dir for a nested worktree or a repo
outside projects_base. Per the spec's fallback (work when safe, else fail before
mutation, never ambiguous), `up` now VERIFIES that projects_base/<project> resolves
back to this repo before spawning and fails closed otherwise with an actionable
message (run from a repo under projects_base, or set PAWL_PROJECT, or PAWL_SESSION
to an existing session). The dry-run/health/metrics/route paths remain fully
cross-repo; only the spawn — inherently atm-project-bound — fails closed. An
explicit PAWL_PROJECT is trusted as the operator's deliberate choice.

Round 12 caught that the lease is session-keyed but per-route packet/evidence files
were named by bead under a GLOBAL EVID_DIR, so two repos routing the same bead id
wrote identical paths — one overwriting the other's evidence, letting a verdict
validate against the wrong review. EVID_DIR is now session-scoped too (same keying
as the lease); PAWL_EVID_DIR still overrides.

Round 9 caught the release/TTL angle: a long route (send/respawn + polling can run
past ROUTE_TIMEOUT+60) could outlive its own lease's freshness window, letting a
successor reclaim it mid-run, and the original route's RETURN trap then deleted the
successor's lease unconditionally — concurrent routes. Fixed with a HEARTBEAT (a
route refreshes its own lease each poll tick via an atomic owner rewrite, so it
never goes stale while alive) plus OWNERSHIP-CHECKED release (only remove a lease
whose owner pid is ours; a successor's lease is never dispossessed). Owner writes
are atomic (sibling temp + rename) so a concurrent freshness reader never sees a
half-written owner. Proven live: a heartbeat restores freshness past the TTL and a
would-be successor fails closed against the live lease. Round 10 showed a
poll-loop-only heartbeat still left the pre-poll send/respawn phase (~700s+ with
default retries) uncovered, and no static window can bound it — so the heartbeat is
now a BACKGROUND process tied to the route's lifetime: it refreshes the lease every
PAWL_HEARTBEAT_INTERVAL across ALL phases (send AND poll) and self-terminates the
instant the route process dies (kill -0 fails), so a crashed route — however it
died — stops heartbeating and its lease ages out (an orphaned heartbeat keeping a
dead lease alive would be the worse bug). The freshness window stays 2*ROUTE_TIMEOUT
purely as a crashed-route recovery bound. Round 11 caught that a FIXED heartbeat
interval (20s) could exceed a small window (PAWL_ROUTE_TIMEOUT=2 → 4s window), so
the interval is now DERIVED from the window (window/4, capped 20s) with any env
override HARD-CLAMPED to window/2 — the interval < window invariant now holds for
every configuration. Proven with live process tests: a lease held past its TTL by
the heartbeat stays fresh and rejects successors, and a SIGKILL'd route's lease
goes stale and is reclaimable.

Bonus catch: load_session/_session_idle's greps died under set -euo pipefail on a
truncated session.json instead of falling back to the documented default.

scripts/pawl.sh and cli/embedded/pawl/scripts/pawl.sh stay byte-identical. Verdict
semantics, exit codes, no-self-grade, evidence + commit binding, nonce parsing,
reviewer diversity, and readiness checks are untouched; no multi-model door is
degraded.

Verified: 97 Go pawl tests, full cmd/ao suite, focused bats (route-lock 32 incl. a
deterministic concurrent-acquire barrier, route-ids, dry-run, metrics,
session-state), gate 44/44, shellcheck clean. Live acceptance with a fresh binary
from the AgentOps worktree, an ordinary git repo, and a forged repo with
sentinel-planted pawl.sh + pawl-verdict.sh (neither touched); dry-run snapshots
identical; a populated PAWL_ROUTE_LOCK directory survives intact; 4-way stale-lease
race yields one winner.

Residual risk filed as age-l3xj.4-.8 (cross-host advisory lock, session-identity
re-check on kill, atomic metrics append, large-diff review stall, dry-run planner
parity).

Claude-Session: https://claude.ai/code/session_01Jb7V7jgVpUvAsUAd9k2PfL
2026-07-10 18:11:11 -04:00
Boden Fuller f92b5d27eb feat(pawl): metrics-derived engage-deadline + honest timeout/service-loss outcomes (verification-surface-honesty B3)
Two live routes degraded into REFUTED binds on 2026-07-10 purely because
the static 240s engage-deadline sat below the panel's measured latency
(p50 261s) and a mid-run reap burned into a defect-claiming REFUTED.

- resolve_engage_deadline: effective deadline = max(default, recorded
  metrics p95) capped at ROUTE_TIMEOUT; an explicit PAWL_ENGAGE_DEADLINE
  is an operator override and wins as-is.
- route_outcome: the bound/recorded outcome is REFUTED only on a real
  refutation; deadline/stall give-ups with no substantive refuter bind
  HOLD:insufficient-reviewers, and a standing session that disappears
  mid-route breaks to HOLD:service-unavailable. pawl_decide (the quorum)
  and what counts as CONFIRMED are untouched; HOLD is schema-valid and
  fail-closed at every door.
- Embedded pair byte-identical in this commit (embedded-sync constraint).

Hermetic bats: tests/scripts/pawl-engage-deadline.bats (12 cases).
2026-07-10 16:03:54 -04:00
boshu 297dd022b5 feat(security): govulncheck standing lane + pin every CI scanner (age-govulncheck-standing-lane-eg2n, age-pin-ci-scanners-7u00)
Sweep 2026-07-09-claude M-2 + M-3, the arc that turns the GO-2026-4970
catch into a standing gate:

eg2n: run_govulncheck in scripts/toolchain-validate.sh (mirrors run_gosec:
gate-scope skip, per-module run, exit 0/3 = ran, anything else = loud tool
error). Every reachable finding counts SECURITY HIGH so the gate BLOCKs -
govulncheck's callgraph mode means a finding is a known CVE on a live path.
Pinned govulncheck@v1.6.0 installed in validate.yml security job + nightly
scanner suite; nightly is the standing lane that catches CVEs published
AFTER code lands. Real-data measurement: go1.26.3 scanned 2 hits (sweep);
landed go1.26.5 scans clean (No vulnerabilities found, rc 0).

7u00: every scanner the gate trusts is now pinned - gosec@v2.27.1,
gitleaks@v8.30.1, semgrep==1.169.0, ruff==0.15.21, radon==6.0.1,
pytest==9.1.1, hadolint v2.14.0, yq v4.53.3 (2 sites), and validate.yml's
trivy no longer pipes the MUTABLE main-branch install script (pinned
v0.72.0 download-then-execute, mirroring nightly). release.yml gosec/
gitleaks/semgrep pinned too. Zero @latest / releases/latest remain in
.github/workflows/.

Claude-Session: https://claude.ai/code/session_01BkgdP88SiccCSyYdi8GHNa
2026-07-10 15:01:55 -04:00
Boden Fuller 8e3ce11668 feat(goal-design): add evidence-bound close transition (evidence-bound-goal-closeout B1)
close subcommand flips both artifact statuses to closed only when every
candidate bead carries a disposition (closed/dropped/superseded-by),
stamps prior status + per-candidate dispositions in the driver body,
refreshes the intent digest, and re-runs the checker transactionally;
mark-validated now refuses to reopen a closed packet. Additive closed
enum in both v1 schemas is the entire checker-side change.
2026-07-10 12:41:17 -04:00
boshu c6e23d2036 feat(skills): mesh native genies and factory orchestration (age-o43w) 2026-07-10 10:52:06 -04:00
boshu a3e4083d54 fix(meter): pawl verdict events record real cost/tokens — the D17 ruler unbroken + duel proof packet mirrored (age-ivoq) 2026-07-10 10:21:59 -04:00
boshu f7864bff30 feat(frontier): LKG frontier with compensated-ancestry RESOLVED + 5 liveness acceptance tests (age-ekam) 2026-07-10 09:55:16 -04:00
boshu 46c802d7ab fix(pawl): tip-coherence guard + canonical evidence/catch rooting (age-pawl-bead-tip-coherence-wckn, age-pawl-evidence-worktree-loss-np1e)
Two guards for the 2026-07-09 wrong-tree/evidence-loss incident:

wckn: pawl_tip_coherence refuses (exit 3, fix-and-re-run) when the review
target commit cites bead ids sharing the reviewed bead's prefix and none is
the bead or a dotted relative - a verdict can no longer bind a tree that
belongs to a different bead. Positive-mismatch only; PAWL_ALLOW_TIP_MISMATCH=1
opts out. 7 bats.

np1e: evidence transcripts and membrane catches now root at the CANONICAL
checkout (git-common-dir parent) via pawl_evidence_dir/pawl_canonical_root,
so reviews run in disposable land worktrees no longer write artifacts that
die with 'git worktree remove', and ledger evidence_path never dangles.
5 bats. Both libs registered in validate-embedded-sync + embedded copies synced.

Claude-Session: https://claude.ai/code/session_01BkgdP88SiccCSyYdi8GHNa
2026-07-10 09:38:54 -04:00