Skip to content

chore(governance): one delivery cycle per session + a Bash-burn hook (ADR-G009) - #284

Draft
aperim-agent wants to merge 3 commits into
mainfrom
chore/reduce-token-burn
Draft

chore(governance): one delivery cycle per session + a Bash-burn hook (ADR-G009)#284
aperim-agent wants to merge 3 commits into
mainfrom
chore/reduce-token-burn

Conversation

@aperim-agent

Copy link
Copy Markdown
Collaborator

Draft deliberately — do not merge. The R2 cross-vendor review gate cannot run: codex exec fails 401, "Your access token could not be refreshed because your refresh token was revoked." Per ADR-G007 and the codex-review runbook, a fresh-context Claude pass is a labelled claude-fallback, not cross-vendor, and this repo never merges on a fallback verdict. Undraft once Codex auth lands and a real cross-vendor pass is recorded here.

Why

Measured over 7 days across 1,589 sessions: cache reads are 59.5% of true cost, and the 4% of sessions past 250 turns are 66% of spend — $0.47/session at 16–40 turns against $40.70 above 600. Cache read is prefix size × turn count, so the only lever that touches the 66% is ending the session. Instruction files are 0.4% of payload — shrinking documents cannot move this.

Multiview had no Stop hook, no supervisor, no resume-directive. Its self-continuation was three lines: step ⑨ RESCHEDULE of the orchestrate skill, and ADR-G007's Autonomy clause authorising a ScheduleWakeup-driven loop.

What changed

  • A cycle is one session. tick.sh starts the next in a fresh process under a single-owner lock; continuity lives in GitHub. ADR-G007's Autonomy clause + step ⑨ are superseded; its territory, sole-integrator, review and memory decisions stand.
  • The orchestrate skill is now repo-agnostic, reading orchestrate.config.json. Multiview's half — lane map, class-scaled gate, cross-vendor review, merge mechanics, salvage, build-cache discipline — moved to docs/runbooks/orchestrate.md, not deleted.
  • Partitioning and the DONE gate are pure tested code. Unknown lanes coerce to single-writer; DONE fails safe on every invalid input; the dry-cycle floor of 2 cannot be argued down.
  • A PreToolUse Bash hook stops unbounded reads/searches dumping into context.

Deviation from the brief, and why

The supplied hook redirected searches to the Grep and Glob tools. Neither exists in this harness — verified by direct call (No such tool available), including in the headless claude -p sessions tick.sh spawns. A block whose only remedy is an uncallable tool is worse than the burn it prevents.

The intent is kept; the remedy changed to one an agent can actually take:

Command Verdict
rg "pattern" src block — cap it: -l, -c, -m N, or `
rg -l "pattern" src · rg -m5 … allow (bounded)
find . -name '*.ts' block — add `
cat / head / tail / sed -n M,Np block — use Read (it exists)

Segments whose stdout is piped onward are exempt — those bytes never reach the transcript. That is why grep "${tarball}" checksums.txt | sha256sum -c - in gitleaks.yml still runs untouched.

Verification

  • 397 commands extracted from CI, the devcontainer, scripts/, xtask, web/package.json, runbooks and agent docs, replayed through the hook: 394 pass. The 3 blocked were unbounded rg examples in working-in-this-monorepo.md — exactly the target — and that caller was fixed.
  • node .claude/hooks/test-prefer-native-tools.mjs53/53 (includes the fail-open contract and an assertion that no block ever names Grep/Glob).
  • node .claude/skills/orchestrate/lib/test.mjs22/22.
  • tick.sh guard paths exercised: STOP, DONE, live lock respected, stale-pid lock reclaimed, lock released on exit.
  • check-inclusive-language.sh OK · check-markdown-links.sh OK · cargo fmt --all -- --check OK · cargo check --workspace OK.
  • scripts/classify.shR2. No Rust, CI, manifest or dependency file changed; clippy/test/deny were not re-run locally for that reason — CI runs them here.
  • Tests committed red first (both exit 1 at 9628f429; subjects absent from that tree).

Ships disarmed

No ACTIVE sentinel. Before this loop can dispatch, an operator must (1) create the status: ready / status: blocked / loop-state labels — none exist today; (2) open the loop-state issue; (3) decide the backlog substrate, since the backlog is still the 402 KB work-schedule.md board rather than issues. All three are recorded in the runbook.

🤖 Generated with Claude Code

aperim-agent and others added 3 commits July 27, 2026 09:30
…er (RED)

Both tests land before their subjects, per the R2 test-first rule.

test-prefer-native-tools.mjs asserts three things about the PreToolUse hook:
the repo's own contract commands still run, unbounded reads/searches are
caught, and — the load-bearing one — no block ever names a tool this harness
does not have. Grep and Glob do not exist here (verified 2026-07-27 by direct
call), so a block recommending them would strand the agent with no action
available. It also pins the fail-open contract on malformed payloads.

lib/test.mjs pins wave partitioning and the DONE gate: unknown lanes coerce to
single-writer, the wave cap defers rather than drops, DONE fails safe on every
invalid input, and the dry-cycle requirement cannot be argued below 2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Measured over 7 days across 1,589 sessions: cache reads are 59.5% of true
cost, and the 4% of sessions past 250 turns are 66% of spend — $0.47/session
at 16-40 turns against $40.70 above 600. Cache read is prefix size x turn
count, so the only lever that touches the 66% is ending the session.
Instruction files are 0.4% of payload; shrinking documents cannot move this.

Multiview had no Stop hook, no supervisor and no resume-directive. Its
self-continuation was three lines: step 9 RESCHEDULE of the orchestrate skill
and ADR-G007's Autonomy clause authorising a ScheduleWakeup-driven loop. Both
are withdrawn. A cycle is now one session; tick.sh starts the next one in a
fresh process under a single-owner lock, and continuity lives in GitHub.

The orchestrate skill becomes short and repo-agnostic, reading
orchestrate.config.json. Multiview's half — the lane map and the specific file
that forces each lane serial, the class-scaled gate, the cross-vendor review
and its never-merge-on-fallback rule, merge mechanics, salvage, build-cache
discipline — moves to docs/runbooks/orchestrate.md rather than being deleted.
Wave partitioning and the DONE gate become pure tested code: unknown lanes
coerce to single-writer, and DONE fails safe on every invalid input.

The Bash hook deviates from the sweep's supplied version, deliberately. That
version redirected searches to Grep and Glob tools; neither exists in this
harness, verified by direct call, including in the headless sessions tick.sh
spawns. A block whose only remedy is an uncallable tool is worse than the burn
it prevents, so searches must bound themselves (-l, -c, -m N, or | head)
instead. Read does exist, so the file-read rules stand as written. Segments
whose stdout is piped onward are exempt — those bytes never reach the
transcript, which is why the gitleaks checksum verify still runs.

Verified: 397 commands extracted from CI, the devcontainer, scripts/, xtask,
web/package.json, the runbooks and agent docs; 394 pass. The 3 blocked were
unbounded rg examples in working-in-this-monorepo.md, and that caller was
fixed. Hook 53/53, partitioner 22/22, tick.sh guard paths exercised (STOP,
DONE, live lock, stale-lock reclaim, lock release), links and inclusive
language clean, cargo fmt and cargo check --workspace green.

Ships disarmed: no ACTIVE sentinel, and the ready/blocked/loop-state labels do
not exist yet. Arming is an explicit operator action.

R2 (agent policy). No invariant touched; no test weakened; no Rust, CI,
manifest or dependency file changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e map

Three defects in the hook adaptation, all now pinned by tests:

- Piped file reads were blocked while telling the agent to use Read, which
  cannot feed a pipe. `cat pkg.json | jq .version` and
  `head -100 build.log | grep error` had no available remedy — and the second
  is the very thing AGENTS.md now advises. The pipes-onward exemption ran
  after the file-read rules instead of before them, contradicting both the
  file header and ADR-G009.
- ripgrep's -L is --follow, which WIDENS a search; it was counted as a
  bounding flag. It stays bounded for grep and ag, where -L really is
  files-without-match.
- A repeated segment was resolved by first textual occurrence, so
  `rg foo src | head -5 && rg foo src` let the unbounded copy through, and
  `rg foo src > /tmp/a; rg foo src | head -5` was wrongly blocked. Segments
  now carry their own separators, and `||`/`&&` are recorded as control flow
  rather than pipes.

Lane map: multiview-hal becomes its own lane. ADR-G007 split it across two
territories — load.rs to engine, select.rs to gpu — but both are declared in
one hal/src/lib.rs and share one Cargo.toml, so the split put a shared file in
two concurrently-dispatchable lanes, which the partitioner cannot express.

scopedLanes ships empty. gpu and audio would each parallelise safely per
crate, but partition.mjs admits a second item into a scoped lane when the
first declares no scope — it reserves the lane, not the scope namespace. That
defect is upstream in a file this repo received verbatim, so it is reported
and worked around rather than silently patched. Same for the two tick.sh lock
defects (recycled pid stalls the loop; stale-lock reclaim is not atomic),
which are documented with symptoms and recovery in the runbook.

Restores two things the rewrite dropped: the board RECORD discipline (flip the
Part-2 box, set Part-3 Status, red-to-green SHAs and PR number inline) and the
proactive qdrant-store mandate.

Hook 61/61, partitioner 22/22, links and inclusive language clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aperim-agent

Copy link
Copy Markdown
Collaborator Author

Review record — claude-fallback, not cross-vendor

The R2 cross-vendor gate is NOT satisfied. codex exec fails 401: "Your access token could not be refreshed because your refresh token was revoked. Please log out and sign in again." Per the codex-review runbook, this repo never merges on a fallback verdict. This PR stays draft until Codex auth lands and a real cross-vendor pass is recorded here.

Separately, codex exec "<prompt>" < diff also returned unsupported content type: data did not match any variant of untagged enum CodexInputItem on codex-cli 0.145.0 — the runbook's documented stdin-piping invocation may itself be stale. Worth re-verifying once auth works.

Fallback pass (fresh-context Claude, no prior context on the change)

It found 8 defects. Three were in the hook adaptation and are fixed in 7631848, each pinned by a new test:

  1. Piped file reads were blocked with no remedyif (consumed) continue ran after the cat/head/tail rules, so cat pkg.json | jq .version and head -100 build.log | grep error were blocked and told to "use the Read tool", which cannot feed a pipe. The second is exactly what AGENTS.md now advises doing.
  2. rg -L counted as bounded — in ripgrep -L is --follow, which widens a search. It remains bounded for grep/ag, where it really is files-without-match.
  3. cmd.indexOf(seg) resolved repeated segments to the first occurrencerg foo src | head -5 && rg foo src let the unbounded copy through, and rg foo src > /tmp/a; rg foo src | head -5 was wrongly blocked. Segments now carry their own separators; ||/&& are control flow, not pipes.

Two were in the lane map and are fixed:

  1. multiview-hal was split across two lanes (load.rs→engine, select.rs→gpu) but both are declared in one hal/src/lib.rs and share one Cargo.toml — a shared file in two concurrently-dispatchable lanes. It is now its own lane.
  2. Two knowledge items had been dropped in the rewrite — the board RECORD discipline and the proactive qdrant-store mandate. Both restored to the runbook.

Three are upstream defects in files this repo received verbatim. They are reported rather than silently patched, and worked around:

  1. partition.mjs admits a second item into a scoped lane when the first declares no scope — it reserves the lane but not the scope namespace, so an unscoped item and a scoped sibling land in the same wave. Worked around: scopedLanes ships empty, every lane single-writer.
  2. partition.mjs silently drops a malformed candidate instead of deferring it (its own supplied test asserts this behaviour).
  3. tick.sh stalls the loop forever on a recycled pid (kill -0 1 succeeds), and its stale-lock reclaim is rm-then-create rather than atomic. Both documented with symptoms and recovery in the runbook.

Verified clean by the reviewer: fail-open holds on every malformed payload (never exits 2); no catastrophic backtracking in the tokeniser; the DONE gate's K floor survives float/string/Infinity/negative; set -e does not trip the sentinel guards; singleWriterLanes ∪ scopedLanes ⊆ knownLanes.

Highest residual risk

The hook is a hand-rolled shell parser sitting in front of every Bash call. Three parser defects were found in one pass, which is evidence the space is not exhausted — quoting, subshells and process substitution are only partly covered. It fails open on exceptions, so the realistic failure is a false block, and the # raw: prefix plus NATIVE_TOOL_HOOK=off bound the blast radius. test-prefer-native-tools.mjs is the regression net and should grow with every false positive found in use.

@aperim-agent

Copy link
Copy Markdown
Collaborator Author

CI: cargo-deny red is pre-existing on main, not a regression from this PR

This PR changes zero dependency files — no Cargo.toml, no Cargo.lock, no deny.toml. Verified by running the same gate against the root checkout, which is a clean mirror of main at 0ddf6272 with this branch absent:

$ git rev-parse --abbrev-ref HEAD && git status --porcelain | wc -l
main
0
$ cargo deny check
error[yanked]: detected yanked crate (try `cargo update -p spin`)

spin was yanked upstream, so the advisories check fails on main independently of this branch. Flagging rather than papering over it: it is still a genuine red that must be cleared before anything merges, but it needs its own dependency-lane fix (cargo update -p spin), not a change here.

Every other check that has reported is green, including docs link sanity, inclusive language, secret scan, change class, OpenAPI/AsyncAPI spec freshness, i18n catalog freshness and the feature-gated clippy matrix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant