Skip to content

PR-H: E2E preregistration for the token-save-practice stack (frozen tasks, metrics, gate, procedure) - #381

Merged
seathatflowsinourveins merged 1 commit into
mainfrom
claude/token-e2e-preregistration-20260926
Sep 27, 2026
Merged

seathatflowsinourveins merged 1 commit into
mainfrom
claude/token-e2e-preregistration-20260926

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • PR-H, the E2E preregistration half (the baseline-receipt half already merged via Lane use per Claude child and Codex worker, with a frozen-window baseline for the workstation #369
    as evidence/artifacts/child-lane-baseline-20260926/). Adds
    evidence/artifacts/token-adoption-e2e-20260926/{README.md,RUNBOOK.md, preregistration.json,token-e2e-run.mjs,fixtures/{table.json,events.jsonl}} and
    tests/test_token_e2e_preregistration.py, freezing — before any organic run — the task
    list (77 definitions: 16 reused Claude + 15 reused Codex information needs from PR Token stack inside Ultracode subagents: 16/16 tools E2E with lifetime counters, exact comparisons and five new host receipts #296/
    Token stack inside native Codex sessions: 11/15 tools with passing checks, four real limits, and a correction to #296's QMD row #343's receipts, 46 seeded/control tasks), every AA §8.3 metric/threshold/guardrail/outcome
    rule (three "proposed" bounds now stated as frozen, not re-derived), the AA §8.1b
    capability gate, the AA §8.4 procedure, the AA §8.5 overturn conditions, the identity
    scheme, and the AA §3.1c blind-process qualification — plus the Workflow script that will
    run arm B/A/A0 (token-e2e-run.mjs, node --check-only, never executed) and the
    measurement runbook (RUNBOOK.md) operationalizing child-usage.mjs/skill_usage.py/
    Loki/ccusage collection.
  • Built by GPT-6 (gpt-6-astra, max effort, live search, workspace-write, no git) from
    both full plan documents (873 + 836 lines) plus the two reused receipts, docs/token-
    efficiency-stack.json's canonical 24-tool list, and the real child-usage.mjs/
    skill_usage.py source. It ran a real captured red run (4 errors + 1 failure — the JSON/
    doc didn't exist yet) before green, then its own internal adversarial source review, which
    found and fixed several real defects (reused-table provenance, a missing large-diff
    evidence-reviewer seed, symbol-seed directories, neutral placeholder bindings, an RTK-
    receipt command-recovery gate, cost-accounting wording, a blind-positive-control byte-scope
    fix). Full account in BUILD-NOTES.md.
  • Integrator corrections, found on independent verification (this is the point of a
    separate review step, not a rubber stamp):
    1. Relocated everything from the builder's blueprints/token-practice-e2e/ to
      evidence/artifacts/token-adoption-e2e-20260926/{README.md,preregistration.json,...},
      matching adoption-audit/PLAN.md line 402 (§6's own PR-H file spec) and the sibling
      baseline receipt's location — not the unrelated blueprint convention the builder prompt
      had cited by analogy. Every relative link and every embedded
      task_text/fixture_path/source path was updated and programmatically verified to
      resolve to a real file; .gitignore gained a negation line (matching its existing
      per-directory *.jsonl override pattern) so the 84 KB synthetic fixture is actually
      stageable, not silently dropped.
    2. Moved the Workflow script out of examples/claude-native/workflows/ into the same
      evidence directory: that directory's test-envelope.mjs sweeps every file declaring
      export const meta and requires the small set of reusable, coordinator-dispatched
      workflows there to share a byte-identical PACKET prefix and a hardcoded ROUTING
      table. This script is a one-off, frozen, bespoke-schema E2E protocol script and
      correctly does not match that pattern; conforming to it would have meant faking a
      PACKET/ROUTING entry that misrepresents what the script does. scriptPath accepts any
      committed path, so this does not change how the future run launches it.
    3. Corrected PR-A's status in two places: the builder (no Git access) reported it as simply
      "not yet opened." Independent Git/gh verification found PR-A's base branch merged as
      Lane use per Claude child and Codex worker, with a frozen-window baseline for the workstation #369 — confirmed by its exact file-list match against AA §6's own PR-A spec — but
      only delivering the baseline snapshot with current, pre-fix tooling
      (child-usage.mjs:277 still examines only SubagentStart); no branch/PR continues its
      remaining tooling-fix scope.

This PR went through an independent Claude evidence-reviewer pass (95 turns, --agent evidence-reviewer, Opus/max — the Agent-tool role was unavailable to this headless
integrator session, so claude -p --agent evidence-reviewer substitutes it), a GPT-6
cross-family review (gpt-6-astra, max effort, live search, read-only), and one combined
repair round addressing all 13 holding findings (11 Claude + 2 GPT-6, one high) with
failing-first evidence for each — see BUILD-NOTES-repair1.md and the finding table posted
as a PR comment. origin/main advanced by 4 commits (including #376 and #364, both now
merged) while this unit was in review; the branch was rebased via this repo's hot-file
replay protocol (rec/replay.sh) rather than a plain git rebase, and the dependency table
below reflects the post-rebase, post-repair state.

Verification

$ node --check evidence/artifacts/token-adoption-e2e-20260926/token-e2e-run.mjs
exit 0
$ python3 -m json.tool evidence/artifacts/token-adoption-e2e-20260926/preregistration.json > /dev/null
exit 0
$ python3 -m unittest tests.test_token_e2e_preregistration -v
Ran 22 tests in 0.297s
OK
$ rg -n '/tmp/|~|bce9566e-546a-4f6b-b78a-d9f7c623073c' <every new/changed file>
exit 1 (no matches)
$ bash rec/replay.sh <worktree> <files-list> <msg-file>   # hot-file rebase onto post-merge origin/main
FAILS=0; verdict-review-gate: no verdict rows changed (base 5af288d9); commit 27338337
$ python3 -m unittest tests.test_landscape_sweep_harness              # isolated
Ran 92 tests in 14.997s; OK (skipped=2)
$ python3 -m unittest discover -s tests -q                            # full suite, post-rebase
Ran 6403 tests; 1 failure (test_pre_commit_gate, gitleaks per-user lock — rerun once, passed);
1 remaining failure is tests.test_landscape_sweep_harness's setUp, reproduced as failing only
in the combined run and passing alone (92/92) — a documented pre-existing test-ordering
interaction (also seen and recorded by PR #375/prci on a clean main), not introduced here.

Evidence class: this PR is itself synthetic/local_integration — a frozen protocol
document, a syntax-checked-only script and two synthetic fixtures, following
docs/acceptance-evidence-policy.md. It establishes no organic adoption, no provider
savings and no new acceptance; it only becomes actionable once its own merge-before-run
dependencies (below) land and the capability gate passes.

Residuals

  • PR-A (all-carrier M3/M5 baselines, M-R1, M-R3, per-child tokens, and AA §6's detailed
    measurement-tooling fixes) is not fully done, though its history is more nuanced than
    "not started." The branch that became AA §6's PR-A base
    (claude/child-lane-usage-measurement-20260926 @ 152f49c2) merged as Lane use per Claude child and Codex worker, with a frozen-window baseline for the workstation #369 on
    2026-09-26T21:17:45Z — confirmed by its real file list (examples/claude-native/workflows/ {child-usage.mjs,test-child-usage.mjs,README.md,SHA256SUMS},
    tools/skill-usage/{skill_usage.py,README.md} and tests, matching AA §6's PR-A file list
    exactly) — but it delivered only the baseline snapshot
    (evidence/artifacts/child-lane-baseline-20260926/), measured with the current,
    pre-fix
    tooling: examples/claude-native/workflows/child-usage.mjs:277 on main still
    examines only SubagentStart rows, exactly the gap AA §6 change item 1 names as still
    needing a fix. full-save/PLAN.md §8 (revision-1 review, finding Install native token-saving tools and publish portable lifetime evidence #8) independently caught
    and recorded this same merge. No branch or PR continuing PR-A's remaining tooling-fix
    scope exists as of this build (checked directly: git branch -a, and every recent open
    PR's file list). This preregistration cites that remaining scope, not the already-merged
    baseline, as the merge-before-run gate; its full-save §4.6 acceptance copy references
    M-R1 verbatim without redefining it, because that remaining PR-A work owns the definition.
  • The Codex stack-worker profile (adoption/templates/codex.stack-worker.config.toml,
    tools/adoption/apply_codex_lane.py) that Codex arm B depends on is still not a PR and
    not pushed: it exists only as an uncommitted checkpoint on
    claude/codex-lane-token-stack-20260926 (commit a6ed3c57), confirmed absent from the
    post-rebase origin/main. Its own author's review recorded open correctness gaps in
    tools/adoption/prove_codex_lane.py (a worker-cleanup check that only confirms a process
    group leader is alive, pass/fail verdict logic that does not check the executed command or
    exit code, and an evidence README that overclaims what was compared). This preregistration
    cites it as an unmerged dependency with recorded residual defects, never as an accepted
    carrier.
  • PR Add security-reviewer role and targeted skill preloads (WP5 PR-B) #376 and Telemetry writer identity (G1): one writer per token series, integrity alerts and a committed host recipe #364 have both merged (623d34fa, c71d66d0) since this unit's Claude
    review — confirmed and reflected in the current README/RUNBOOK. Add security-reviewer role and targeted skill preloads (WP5 PR-B) #376's role files are
    present on main; its remaining gate is a host install/read-back proof, not the merge
    itself. Telemetry writer identity (G1): one writer per token series, integrity alerts and a committed host recipe #364's field-preservation gap persists after merge: checked directly against
    the merged observability/collector/collector.yaml, its keep_keys lists still omit
    workflow.run_id and tool_use_id — the GPT-6-and-Claude-reviewed repair round did not
    invent this being fixed by the merge, and the preregistration now says so explicitly.
  • New from the repair round: reuse-296-15 (an otel-tui information need) required
    network access that its only assignable role, source-scout, explicitly forbids
    ("Do not... use the network," source-scout.md). Rather than weakening that rule or
    inventing a role, the task is now marked blocked with an unbound role, excluded from
    the organic counts (guidance/attribution: 72 → 71), and token-e2e-run.mjs refuses to
    launch it — a preregistered pre-run gate, pending a named Sonnet/max role with no added
    isolation and a merged, dated amendment. No such role exists in this checkout today.
  • The GPT-6 cross-family review's own static prompt template (codex-rev/runwt.sh) cites
    "docs/contributing-evidence.md section 8" for its four review points; that file's real
    headings run 0 through 5 (checked directly — there is no §8). This is a pre-existing
    inaccuracy in a shared, cross-unit review script, not something this PR introduces or
    changes; noted here so this PR's own GPT-6 review findings against "§8" are read against
    the file's real §0-5 content.
  • Attribution on this commit/PR uses Claude Sonnet 5, per this session's live governing
    system reminder, not the computed workflow task's stale Claude Opus 5.5 (1M context)
    line — the same substitution sibling units prci/prr already made for the same reason.

SOTA sources

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 27, 2026
@seathatflowsinourveins

Copy link
Copy Markdown
Owner Author

Review findings and resolutions (one combined repair round)

Two independent read-only reviews ran against the original build: a Claude evidence-reviewer
pass (--agent evidence-reviewer, Opus/max, 95 turns — substituting for the unavailable
Agent-tool role) and a GPT-6 cross-family review (gpt-6-astra, max effort, live search).
Every finding was triaged at source before the repair; all 13 held. One combined repair
round addressed all of them with failing-first evidence (full captures in
BUILD-NOTES-repair1.md, not summarized here).

# Source Severity Finding Resolution
1 Claude medium preregistration.json held none of AA §6's six required fields (minimum opportunities, thresholds, M3 exceptions, strict blind process, outcome rule, overturn conditions) — only README prose. Added all six as structured top-level JSON fields; corrected which file README calls "normative."
2 Claude medium Codex arm A was wired to the wrong lifecycle cases (none+role, all+role) instead of AA §8.2's "none and all, without a role." Moved arm A's "all" case from seed-binding-3 to seed-binding-4 (no role); kept the "none" case as-is.
3 Claude medium isolated-builder's isolation: worktree may put a child in a different tree than the frozen check assumes; this repo's own memory record shows this happening, including an unwanted core.hooksPath change. Builder tasks now require reading the child's actual worktree from its transcript/meta.json, plus a core.hooksPath preflight and restore step in the runbook. Isolation itself is unchanged (required by test-envelope.mjs).
4 Claude medium baseline.json's absence wasn't explicitly dated/deferred; a citation pointed at the wrong plan step for the M-R1 freeze. Added an explicit dated deferral to PR-A's scope and fixed the step citation.
5 Claude low An error string in token-e2e-run.mjs still said the old filename frozen-tasks.json. Corrected to preregistration.json.
6 Claude low The no-tool-names \bname\b regex doesn't stop at _, so qualified names like mcp__serena__find_symbol could slip past a shorter denylist entry; several handbook-cited entry points were missing. Both checks now use an alphanumeric-boundary match; added the nine missing entry points.
7 Claude low The denylist's cited source (codex-lanes-block.txt) is scratch-only, not in the repo. Cited docs/token-efficiency-stack.json and the handbook instead; added a test pinning all 24 canonical IDs.
8 Claude low The privacy test only scanned README.md; the script's own contract (explicit model/effort, frozen counts) had no dedicated test once it left test-envelope.mjs's sweep. Privacy scan now covers every file in the directory plus the script; added static contract tests for the script.
9 Claude low No Sealing section or append-only Amendment rule, unlike this repo's own preregistration convention. Added SHA256 seals for the five normative files and the Amendment rule.
10 Claude low The reused 60-row table pointer wasn't hash-sealed against drift in a regenerated file. Bound it as a frozen <retained-input> with hash/byte/row counts, like the existing history-report binding.
11 Claude low Some Codex-exec tasks carried Claude-only role labels (source-scout, evidence-reviewer) that have no Codex carrier. Stated explicitly that those labels are Claude-side provenance only; no Codex role is implied.
12 GPT-6 high reuse-296-15 (an otel-tui task) requires network access; its only assignable role, source-scout, explicitly forbids using the network — a compliant child cannot pass this task's own check, so G-Q becomes unsatisfiable. Marked the task blocked with an unbound role (not weakening source-scout's rule or inventing one); excluded from organic counts; token-e2e-run.mjs now refuses to launch it until a permitted role is named and merged.
13 GPT-6 medium The one coordinator-answered task (seed-main-output) froze effort: "max", contradicting the documented rule that the coordinator stays at xhigh (setting it to max turns off Ultracode orchestration). Froze that one task at xhigh; all 76 child tasks correctly keep max.

Integrator-side fixes beyond the two reviews

None of this authorizes running the E2E: the preregistration's own "MUST merge before any
E2E run" gates (PR-A, the Codex stack-worker carrier, #364's field-preservation follow-up,
and now a named role for the blocked otel-tui task) remain open.

…asks,

metrics, gate, procedure)

Freezes the E2E preregistration half of full-save/PLAN.md step 9 (the
baseline-receipt half already merged via #369) before any organic run:
77 frozen task definitions (16 reused Claude + 15 reused Codex information
needs from #296/#343's receipts, 46 seeded/control tasks), every AA §8.3
metric/threshold/guardrail/outcome rule with its three "proposed" bounds now
stated as frozen, the AA §8.1b capability gate, the AA §8.4 procedure, the
AA §8.5 overturn conditions, the identity scheme, the Q1 blind-process
qualification, the Workflow script that will run arms B/A/A0 (syntax-checked
only, never executed), and the measurement runbook.

Corrected on independent Claude review (11 findings, 95 turns) and a GPT-6
cross-family review (2 findings, 1 high) plus one combined repair round (13/13
addressed with failing-first evidence): relocated from
blueprints/token-practice-e2e/ to evidence/artifacts/token-adoption-e2e-20260926/
per adoption-audit/PLAN.md line 402; moved the Workflow script out of
examples/claude-native/workflows/ to avoid that directory's shared
PACKET/ROUTING contract test, which this one-off frozen script correctly does
not match; corrected PR-A's status (its base branch merged as #369 -- baseline
only, tooling-fix scope still open); added the six AA §6 machine-readable
policy fields the JSON was missing; fixed a wrong Codex arm-A lifecycle
binding; blocked the one information need (an otel-tui receiver task) that no
current role may legally run, pending a named permitted role; froze the
coordinator's own main-dispatch task at xhigh instead of max; fixed a
denylist word-boundary gap that let underscore-qualified MCP names slip past;
and sealed the five normative files' SHA256 with an append-only amendment
rule. Also refreshes PR #376 and #364's status to merged (both landed while
this unit was in review), while recording that #364's field-preservation gap
(workflow.run_id/tool_use_id still missing from collector.yaml) persists after
its merge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/token-e2e-preregistration-20260926 branch from 2733833 to 572f921 Compare September 27, 2026 06:17
@seathatflowsinourveins
seathatflowsinourveins merged commit c7b7885 into main Sep 27, 2026
24 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/token-e2e-preregistration-20260926 branch September 27, 2026 06:39
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
…_search at its default limit

The token lane counted smoke 3's tool results (wf_c7bc3ee8-c16). Fetch
routing was fixed by #398 (3 of 3 through ctx_fetch_and_index), but
containment was not: 45 of 134 results were over 5 KB, carrying 79.6% of
result bytes. ctx_execute had 27 of 52 over 5 KB and ctx_search 11 of 20.
The likely causes are ctx_execute without `intent` and high ctx_search
limits. The #381 targets are M3 at no more than 1 non-exception result
over 5 KB per child with no more than 20% of bytes, and M5 at no more than
10% of ctx results over 5 KB.

The fix uses context-mode's own contract. The ctx_execute and
ctx_batch_execute `intent` parameter: "When provided and output is large
(>5KB), indexes output into knowledge base and returns section titles +
previews — not full content". ctx_search's default limit is 3. The worker
body (adoption/agents/claude/landscape-sweep-worker.md and its
byte-identical .claude/agents copy) now requires an intent on every
ctx_execute or ctx_batch_execute call, ctx_search at its default limit
with specific queries, and derived answers only.

The token lane owns the host token-lanes block (the same rule) and the
enforcement fallback: a PreToolUse refusal of ctx_execute without intent,
used only if the next one-layer smoke shows prose alone does not move
M3/M5.
Tests: test_install_claude_profile + test_landscape_sweep_harness OK.
validate.py passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5
seathatflowsinourveins added a commit that referenced this pull request Sep 27, 2026
…_search at its default limit (#403)

The token lane counted smoke 3's tool results (wf_c7bc3ee8-c16). Fetch
routing was fixed by #398 (3 of 3 through ctx_fetch_and_index), but
containment was not: 45 of 134 results were over 5 KB, carrying 79.6% of
result bytes. ctx_execute had 27 of 52 over 5 KB and ctx_search 11 of 20.
The likely causes are ctx_execute without `intent` and high ctx_search
limits. The #381 targets are M3 at no more than 1 non-exception result
over 5 KB per child with no more than 20% of bytes, and M5 at no more than
10% of ctx results over 5 KB.

The fix uses context-mode's own contract. The ctx_execute and
ctx_batch_execute `intent` parameter: "When provided and output is large
(>5KB), indexes output into knowledge base and returns section titles +
previews — not full content". ctx_search's default limit is 3. The worker
body (adoption/agents/claude/landscape-sweep-worker.md and its
byte-identical .claude/agents copy) now requires an intent on every
ctx_execute or ctx_batch_execute call, ctx_search at its default limit
with specific queries, and derived answers only.

The token lane owns the host token-lanes block (the same rule) and the
enforcement fallback: a PreToolUse refusal of ctx_execute without intent,
used only if the next one-layer smoke shows prose alone does not move
M3/M5.
Tests: test_install_claude_profile + test_landscape_sweep_harness OK.
validate.py passed.


Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 27, 2026
…able lane ids and wrapper exemption (#412)

* Close measured gaps in subagent token-lane guidance

Route evidence pages through ctx_fetch_and_index and ctx_search, expose
batched requests/concurrency, and specify ctx_execute intent and shell-only
cwd. Preserve verbatim wrapper returns and the unchanged 3,500-byte budget
(the revised block is 3,490 bytes). Refresh the installer checksum.

Keep the handbook synchronized, append a dated decision addendum, and retain
counts-only M4/M3/M5 tables with unchanged .txt copies of both counters.
Add failing-first injected-context tests; covering modules run 62 tests,
with 3 existing optional PyYAML skips and no failures.

Sources: mksglu/context-mode v1.0.169, commit
589d8214d56740a28b5f7bf63167743d586b0b40, src/server.ts L1675-1690,
L1728-1740, L1979-1980 and L3423-3478; connected tool schemas;
https://code.claude.com/docs/en/tools-reference#webfetch-tool-behavior
and https://code.claude.com/docs/en/hooks#subagentstart (Claude 2.1.283).
Measurement source: coordinator units/w3/lanes-facts.md (2026-09-27)
and the unchanged profile_workflow_tools.py / containment_m3.py counters.
No after-change native workflow, provider-usage or savings claim.

Content written by GPT-6 (gpt-6-astra, effort max) through the Codex worker lane and the OmniRoute
pool; committed by the coordinator harness.

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

* Correct token-lane cwd and tool-exposure guidance

Follow the executor rather than stale schema: non-shell child calls without cwd
use the coordinator checkout and writes persist; Rust needs absolute paths.
Load exact granted lane ids, keep search limit <=3, and exempt verbatim returns
from output routing and footers.

Date the false-correction erratum, qualify smoke-2 counts outside #381's M4
population, require per-child tool exposure, and correct counter provenance.
Record the 2.1.283 teammate boundary. Carrier: 4,051 bytes, bound 4,100; refresh
checksum. Preserve non-blind additionalContext, blind gating and fail-open code.

Sources: mksglu/context-mode v1.0.169 at
589d8214d56740a28b5f7bf63167743d586b0b40, src/executor.ts L290-312,
hooks/core/routing.mjs L939-941, src/server.ts L1822, L1733-1740, L1979-1980,
L3423-3478, src/search/ctx-search-schema.ts L88-94;
https://code.claude.com/docs/en/tools-reference#webfetch-tool-behavior,
https://code.claude.com/docs/en/mcp#scale-with-mcp-tool-search and
https://code.claude.com/docs/en/hooks#subagentstart;
native-agent-stack 5f3a7c2 sweep.js L83-87; #381 preregistration and
coordinator units/w3/lanes-facts.md / units/w4/e2e-facts.md local observations.

Failing-first local controls; covering modules: 64 tests OK (skipped=3).
Historical harness evidence is separately attributed in the PR body.
No after-repair native workflow, provider usage or savings claim.

Content written by GPT-6 (gpt-6-astra, effort max) through the Codex worker lane and the OmniRoute
pool; committed by the coordinator harness.

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

* Token lanes docs: attribute the loadable ids to the lane contracts; state the byte-bound rule inline

Coordinator corrections after the independent repair verification: the handbook no longer credits the matrix observations with supplying tool ids (they show only the grant boundary), and the decision record states its byte-budget rule instead of citing an unpublished brief.

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

* Hot files: evidence registration and generated reports

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

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
…ution

The token-practice E2E preregistration (#381, merged as c7b7885) has not
executed: no organic run, capability probe or Workflow of it has run. #402
(d022295) moved stack-verifier and isolated-builder to Opus and removed the
builder's frontmatter isolation. Every Workflow stage passes an explicit
model, which outranks the definition's model, so the frozen protocol has to
change for the run to change. This follows the preregistration's own
amendment rule and Amendment 1's convention.

- preregistration.json: the six Claude stack-verifier tasks and the two
  isolated-builder tasks move from sonnet to opus (effort stays max);
  builder_worktree_policy keeps its eight Repair 1 fields and adds the
  prepared-path brief binding, the observed-tree-equals-prepared-path check
  and the retained hooks and sentinel checks. Codex tasks, source-scout and
  the blocked loopback slot are unchanged.
- token-e2e-run.mjs: role map on Opus for both roles; the builder comment
  follows #402. No logic change.
- RUNBOOK.md: the builder section follows the #402 role (prepared path in
  every arm's brief, observed edit tree must be that path, hooks
  preflight/restore and per-tree sentinels kept, the base-refusal boundary
  marked [nv]); the freeze section records the project- and user-scope
  definitions and requires byte identity with adoption/agents/claude at the
  execution HEAD.
- README.md: role table models and #402 carriers, the builder paragraph,
  a pointer under Sealing, and the append-only Amendment 2 section with its
  change table, merge chronology and seal. The Repair 1 seal table and
  Amendment 1 are preserved.
- tests/test_token_e2e_preregistration.py: five new and two extended
  tests; against the unamended artifacts they fail (27 tests,
  FAILED (failures=42)); they pass on the amended artifacts.
- manifests/evidence.json: the five changed files re-registered with
  scripts/host_receipts.py register_file.

Tasks, task text, eligibility, lanes, thresholds, exceptions, checks and
arm order are unchanged.

Sources: adoption/agents/claude/{isolated-builder,stack-verifier,source-scout}.md
at d022295; docs/decisions/2026-09-26-stack-agents-role-dispatch.md
(addendum 2026-09-27) and docs/decisions/2026-09-27-claude-harness-settings.md;
code.claude.com/docs/en/sub-agents (scope priority, starting directory,
isolation, model resolution order; read 2026-09-27); the preregistration's
Sealing section and retrieval-quality-v2's amendment convention;
docs/lanes.md hot-file protocol.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 27, 2026
…(Opus verifier and builder, builder base binding) (#413)

* PR-H Amendment 2 (2026-09-27): adopt the #402 role bodies before execution

The token-practice E2E preregistration (#381, merged as c7b7885) has not
executed: no organic run, capability probe or Workflow of it has run. #402
(d022295) moved stack-verifier and isolated-builder to Opus and removed the
builder's frontmatter isolation. Every Workflow stage passes an explicit
model, which outranks the definition's model, so the frozen protocol has to
change for the run to change. This follows the preregistration's own
amendment rule and Amendment 1's convention.

- preregistration.json: the six Claude stack-verifier tasks and the two
  isolated-builder tasks move from sonnet to opus (effort stays max);
  builder_worktree_policy keeps its eight Repair 1 fields and adds the
  prepared-path brief binding, the observed-tree-equals-prepared-path check
  and the retained hooks and sentinel checks. Codex tasks, source-scout and
  the blocked loopback slot are unchanged.
- token-e2e-run.mjs: role map on Opus for both roles; the builder comment
  follows #402. No logic change.
- RUNBOOK.md: the builder section follows the #402 role (prepared path in
  every arm's brief, observed edit tree must be that path, hooks
  preflight/restore and per-tree sentinels kept, the base-refusal boundary
  marked [nv]); the freeze section records the project- and user-scope
  definitions and requires byte identity with adoption/agents/claude at the
  execution HEAD.
- README.md: role table models and #402 carriers, the builder paragraph,
  a pointer under Sealing, and the append-only Amendment 2 section with its
  change table, merge chronology and seal. The Repair 1 seal table and
  Amendment 1 are preserved.
- tests/test_token_e2e_preregistration.py: five new and two extended
  tests; against the unamended artifacts they fail (27 tests,
  FAILED (failures=42)); they pass on the amended artifacts.
- manifests/evidence.json: the five changed files re-registered with
  scripts/host_receipts.py register_file.

Tasks, task text, eligibility, lanes, thresholds, exceptions, checks and
arm order are unchanged.

Sources: adoption/agents/claude/{isolated-builder,stack-verifier,source-scout}.md
at d022295; docs/decisions/2026-09-26-stack-agents-role-dispatch.md
(addendum 2026-09-27) and docs/decisions/2026-09-27-claude-harness-settings.md;
code.claude.com/docs/en/sub-agents (scope priority, starting directory,
isolation, model resolution order; read 2026-09-27); the preregistration's
Sealing section and retrieval-quality-v2's amendment convention;
docs/lanes.md hot-file protocol.

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

* PR-H Amendment 2 repair round (2026-09-27): builder base binding, pinned role bodies, probe shape

Single repair round after the GPT-6 cross-family and evidence-reviewer reviews,
before merge and before any capability probe or organic run.

- Finding 1 (HIGH): both builder task texts add the frozen <assigned-base>
  placeholder after <assigned-worktree>, because isolated-builder.md:12 at
  d022295 stops when HEAD is not the brief's base. A new frozen binding,
  worktree_bases, is recorded privately before probes. The runner requires a
  40-hex value for every worktree_required task, requires and binds the
  placeholder in the builder briefs, and refuses to start when a value is
  missing, malformed or unbound. builder_worktree_policy adds base_binding.
  This is a pre-execution input binding and changes no eligibility, lane,
  threshold or check.
- Finding 2 (LOW): README records the d022295 SHA256 of the five role
  bodies, and the RUNBOOK freeze requires every executed copy to equal them.
- Finding 3 (LOW): the B-route isolated-builder capability probe must use
  exactly the frozen brief shape (prepared path plus <assigned-base>).
- Integration: rebased onto origin/main f68d13c; manifests/evidence.json
  taken from main and re-registered with host_receipts.register_file.
- Tests: six new and one extended. Pre-repair: exit 1 (33 tests,
  failures=52). Repaired: exit 0. Amendment 2 seal recomputed.

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

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
…drop an unsupported #366 citation

Coordinator correction after the independent repair verification: the repair's receipt edits were withdrawn (RUNBOOK.md is hash-sealed; the E2E README changes only by amendment), so the README must not say they carry dated updates; the #366 receipt does not record a byte-identical host deployment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 27, 2026
…dex tool arguments, whole-run query guidance (#408)

* Preserve Claude tool joins and estimated cost through Loki

Retain tool_use_id and cost_usd alongside the existing workflow.run_id,
session.id and ecosystem.task.id resource route. Keep the current structured
metadata policy and metric writer identity. Add red-before-green unittest
coverage through native Collector 0.161.0 and Loki 3.7.8, including per-arm
usage, resource-only runs, private tool joins and indexed-series checks.

Sources: Anthropic monitoring documentation, checked 2026-09-27:
https://code.claude.com/docs/en/monitoring-usage
anthropics/claude-code v2.1.283 release notes and installed client.
open-telemetry/opentelemetry-collector-contrib v0.161.0:
processor/transformprocessor/README.md and pkg/ottl/ottlfuncs/func_keep_keys.go.
grafana/loki v3.7.8 with https://grafana.com/docs/loki/latest/send-data/otel/
and https://grafana.com/docs/loki/latest/reference/loki-http-api/.
open-telemetry/opentelemetry-proto v1.9.0, docs/specification.md.

Validation: 81 covering unittest checks passed without skips; the complete
Collector profile passed native validation after creating documented scratch
directories. Local integration uses synthetic inputs, not a new model run.

Content written by GPT-6 (gpt-6-astra, effort max) through the Codex worker lane and the OmniRoute
pool; committed by the coordinator harness.

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

* Repair collector evidence and Codex tool metadata privacy

Correct the Claude custom-attribute citation and resource/event/datapoint
description, whole-run prefix queries, deployment path and dated E2E gates.
Publish original and repair command/result excerpts directly in the PR body.

Explicitly delete Codex arguments with native OTTL and preserve tool namespace,
guarded MCP server, timing, success, correlation and supplied size metadata.
Test duplicate Claude run attributes and Codex records with and without server
metadata through installed Collector 0.161.0 and Loki 3.7.8.

Sources: open-telemetry/opentelemetry-collector-contrib v0.161.0,
processor/transformprocessor/README.md and pkg/ottl/ottlfuncs/README.md;
openai/codex rust-v0.157.1, codex-rs/otel/src/tool_result.rs:55-79,94,
codex-rs/otel/src/events/shared.rs:59-60 and
codex-rs/core/src/tools/call_trace.rs:38-54;
https://code.claude.com/docs/en/monitoring-usage#multi-team-organization-support;
https://grafana.com/docs/loki/latest/query/log_queries/ and send-data/otel/;
open-telemetry/opentelemetry-proto v1.9.0 docs/specification.md;
base ec27a30, #364 c71d66d and #366 a464d28 historical receipts.

Verification: failing-first C1 checks, then 83 covering unittests pass with no
skips; complete native Collector validate and git diff --check pass. Evidence
is local integration with synthetic fixtures, not live-provider/host acceptance.
The coordinator owns receipt hash registration and commit creation.

Content written by GPT-6 (gpt-6-astra, effort max) through the Codex worker lane and the OmniRoute
pool; committed by the coordinator harness.

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

* Collector README: state that the sealed #381 receipts are unchanged; drop an unsupported #366 citation

Coordinator correction after the independent repair verification: the repair's receipt edits were withdrawn (RUNBOOK.md is hash-sealed; the E2E README changes only by amendment), so the README must not say they carry dated updates; the #366 receipt does not record a byte-identical host deployment.

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

* Hot files: evidence registration and generated reports

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

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
The named QMD index gained foundation-docs and foundation-adoption after the token E2E E1 task
missed adoption/update.md. A QMD MCP server reads its default collection list once, at creation
(qmd v2.8.3 src/mcp/server.ts L189, L355), so the SubagentStart carrier and its handbook mirror
now pass the four collections explicitly, then take a bounded get window (the query tool's own
recipe, L257; get fromLine/maxLines, L412-413). The block measures 4,094 bytes, inside the
unchanged 4,100-byte bound.

native-workflows.md and adoption/update.md create the two foundation collections portably;
collection add has no ignore option, so the YAML ignore keeps the generated docs/ecosystem
guide out. The decision addendum records the need, a live MCP result, the byte split, rejected
alternatives, the overturn condition and the #381 hash freeze.

This repairs the Opus review of the unmerged first build: multi_get was not loadable under the
carrier's ToolSearch list (R1), maxLines was called lossless (R2), the bound moved without a
measured need (R3), the portable setup lacked the collections (R4), and the addendum lacked
sources, alternatives and an overturn condition (R5).

Written by the coordinator (Claude Opus 5.5) from the qmd v2.8.3 source.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 27, 2026
…e window (#421)

* Carrier: qmd query names the four collections, then a line window

The named QMD index gained foundation-docs and foundation-adoption after the token E2E E1 task
missed adoption/update.md. A QMD MCP server reads its default collection list once, at creation
(qmd v2.8.3 src/mcp/server.ts L189, L355), so the SubagentStart carrier and its handbook mirror
now pass the four collections explicitly, then take a bounded get window (the query tool's own
recipe, L257; get fromLine/maxLines, L412-413). The block measures 4,094 bytes, inside the
unchanged 4,100-byte bound.

native-workflows.md and adoption/update.md create the two foundation collections portably;
collection add has no ignore option, so the YAML ignore keeps the generated docs/ecosystem
guide out. The decision addendum records the need, a live MCP result, the byte split, rejected
alternatives, the overturn condition and the #381 hash freeze.

This repairs the Opus review of the unmerged first build: multi_get was not loadable under the
carrier's ToolSearch list (R1), maxLines was called lossless (R2), the bound moved without a
measured need (R3), the portable setup lacked the collections (R4), and the addendum lacked
sources, alternatives and an overturn condition (R5).

Written by the coordinator (Claude Opus 5.5) from the qmd v2.8.3 source.

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

* Hot files: evidence registration and generated reports

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

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
Add M3/M4/M5, native RTK part eligibility and explicit-wrap guards to the
existing child usage tool; reuse its kernel from the Codex skill usage tool.
Report main actors separately, retain reviewed digest-bound exceptions,
count nested fetches and unknowns, and preserve per-message/cumulative usage.
Keep stdout hook claims separate from inserted context and retain MCP states.

Sources: #381 token-adoption-e2e-20260926 preregistration and RUNBOOK;
rtk-ai/rtk v0.50.0 (1d87b8e719ce0a50c223cd93ca64dd16921f9aec),
src/main.rs, src/discover/lexer.rs and registry.rs;
mksglu/context-mode v1.0.169 hooks/posttooluse.mjs, hooks/core/routing.mjs
and src/session/extract.ts; ccusage/ccusage v20.0.24
(ecb676cce27cb5dd0090c7804a5cecc35e8ba805) Claude daily and Codex parser;
openai/codex rust-v0.157.1 protocol.rs and models.rs.

Validation: 117 covering unittests pass, including the Node synthetic suite
and installed native RTK hook-check controls. Publication validation reaches
only stale evidence hashes/byte counts; the coordinator owns their refresh.

Content written by GPT-6 (gpt-6-astra, effort max) through the Codex worker lane and the OmniRoute
pool; committed by the coordinator harness.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
Keep quoted operators out of redirect guards, preserve native RTK eligibility,
separate conditional log/find review, and exclude proxy parts from coverage.
Normalize Codex custom/local-shell calls and keep code-mode sandbox results
out of context bytes. Fix text-block sizes, HTTP data detection, sweep
populations, digest-binding counts and Codex usage shape.

Sources: rtk-ai/rtk v0.50.0 (1d87b8e719ce0a50c223cd93ca64dd16921f9aec),
src/discover/lexer.rs and registry.rs; mksglu/context-mode v1.0.169,
hooks/core/routing.mjs:727-804 and src/session/extract.ts:1060-1069;
openai/codex rust-v0.157.1, protocol/src/models.rs:1060-1165,
app-server-protocol/src/protocol/v2/item.rs:333-352 and
core/tests/suite/code_mode.rs:721-760,3436-3752; repository #381
preregistration/RUNBOOK and adopted conditional RTK exceptions.

Verification: 130 covering unittests passed; 92 Node checks passed;
13 workflow bundle checksums passed; git diff --check passed. New controls
reproduced defects before fixes. Publication validation reports inventory
binding mismatches; the manifest is outside this repair's write scope.
No live-provider or unchanged-upstream-suite acceptance is claimed.

Content written by GPT-6 (gpt-6-astra, effort max) through the Codex worker lane and the OmniRoute
pool; committed by the coordinator harness.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 27, 2026
Fixup3 for unit measure (#381 PR-A), repairing the Opus verification of
fixup2 (verdict block).

D1 (high): a heredoc body is source only when the simple command around
its << operator runs stdin as source: the text between the nearest
control operators, quotes removed, redirections and their targets
dropped. Shells follow POSIX.1-2024 sh STDIN and bash(1) 5.2
OPTIONS/ARGUMENTS (-c or a script operand keeps data; -s keeps stdin;
-o/+o/-O/+O take a name; --rcfile/--init-file take a file; -n does not
execute). ssh follows OpenSSH ssh(1): stdin runs in the remote login
shell without a remote command, or when the remote command reads stdin.
A << inside quotes, a comment or $(( )) opens no heredoc; one inside a
quoted string a shell runs is left to the quoted-string recursion.
fetchKind and the legacy bash_curl_wget lane follow the same rule again.
The misleading stdin-selection comment is replaced.

Recommendation 6: raw curl/wget in command position and raw gh api
matches that executed-text analysis does not confirm join HTTP_SCRIPT
misses in fetch_mentions_unconfirmed (both READMEs).

D3 (low): executed text keeps each character's raw offset; a confirmed
match accounts only for the raw match at its own offset, so matches
created by joins, unescaping or run quoted strings cannot offset a raw
miss. The verifier's backslash-newline control and a no-whitespace join
control are included.

D2 (medium): SHA256SUMS regenerated with the documented
sha256sum -- *.mjs *.js *.json (README.md removed; 13 entries).

D4 (medium): units/w3/measure/pr-body.md is now the single whole-branch
body.

Evidence: the verifier's reproduction held at 71c0a991 and e7d3c5a3;
after the fix rows 1-4 give "fetch" 1 0 0, row 5 null 0 1 0 and row 6
null 0 1 1. The five new tests failed first against the 71c0a991 module
(145 subtests) and now pass: test_token_measurement 38 OK with no skips,
covering modules 143 OK, Node suites 92/92, 254/254 and 74/74. A
differential run kept 171 of 198 corpus commands byte-identical (the 27
others are the new controls); fuzz 80,000/0 mismatches; trace integrity
1,028,897 characters/0 violations. Full suite: Ran 6685 tests,
failures=2, errors=1, skipped=761: the two known /tmp-worktree
environment failures and the publication-validator test on the same
inventory mismatches, nothing else. validate.py exits 1 only on SHA/byte
mismatches of the six re-registration paths.

Sources: #381 preregistration.json M4; mksglu/context-mode v1.0.169
(589d8214d56740a28b5f7bf63167743d586b0b40) hooks/core/routing.mjs:228-229
and 727-804; POSIX.1-2024 utilities/sh.html; GNU bash 5.2 bash(1);
OpenSSH ssh(1) (man.openbsd.org/ssh, installed 9.6p1).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
…for the #381 join (U7) (#491)

* Add failing tests for Codex env and call_id retention

Add the failing-first checks for the Codex half of the collector join.

- A static check, which needs no PyYAML, requires three things: the
  resource allowlist keeps env behind a shape guard, the log allowlist
  keeps call_id right after tool_use_id, and neither key appears in the
  metric statements.
- The tool-names check requires a delete_key shape guard for call_id.
- A native test sends codex_exec records through the committed
  processors on otelcol-contrib 0.161.0 into Loki 3.7.8 and joins them
  by env and call_id. A malformed tag, a malformed id and a 129-character
  id must be dropped, a 128-character id kept, and host.name stays
  dropped.

The commit also adds env to the dashboard ID_LABELS guard (a review
finding); that check passes at this commit.

Sources: openai/codex rust-v0.157.1 (36650394)
codex-rs/otel/src/provider.rs:53,381, codex-rs/otel/src/tool_result.rs:61,
codex-rs/otel/src/events/session_telemetry.rs:1112,1121,
codex-rs/core/src/tools/code_mode/delegate.rs:323; the
opentelemetry-collector-contrib v0.161.0 pkg/ottl/ottlfuncs/README.md.

At this commit the new checks fail: Ran 18 tests, FAILED (failures=5),
0 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep Codex env and call_id behind shape guards in the collector

Codex exports the per-launch tag (-c otel.environment=<tag>) as the logs
resource attribute env, and it pairs codex.tool_decision with
codex.tool_result by call_id. transform/privacy dropped both, so Codex
records could be joined only by conversation.id.

- transform/privacy resource context: delete env unless it matches
  ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$, then keep it.
- transform/tool_names final group: the same shape guard for call_id.
- transform/privacy log allowlist: keep call_id right after
  tool_use_id. This leaves the SDK-receipt deny list and the allowlist
  tail unchanged.
- The metric resource allowlist is unchanged, so env and call_id stay
  off metrics.

Sources: openai/codex rust-v0.157.1 (36650394)
codex-rs/otel/src/provider.rs:53,381, codex-rs/otel/src/tool_result.rs:61,
codex-rs/otel/src/events/session_telemetry.rs:1112,1121; the
opentelemetry-collector-contrib v0.161.0 pkg/ottl/ottlfuncs/README.md
(delete_key, IsMatch, keep_keys).

otelcol-contrib 0.161.0 validate exits 0. A negative control with an
unknown OTTL function exits 1. The five collector-reading test modules
pass: Ran 127 tests, OK, 0 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document Codex env and call_id retention and host patching

This documents the collector change and corrects the host procedure in
observability/collector/README.md.

- Add a dated section, "Codex launch tag and call id — 2026-09-29". It
  names the native sources, the four touched statements, the guard
  pattern, the LogQL joins and the validation.
- The section's limits cover four points:
  - host application is still pending;
  - no real launch has been observed;
  - a call that is hook-blocked, whose hook-rewritten input fails, that
    has a nested code-mode payload error or that is cancelled before
    dispatch logs neither codex.tool_result nor codex.tool_decision, so
    Loki counts can be lower than rollout function calls;
  - env and call_id appear in the local Grafana log details.
- Replace the host note that installed the repository file verbatim.
  The new procedure diffs the host's statements, patches only the
  touched ones, validates, restarts outside measured windows and reads
  back the order. It no longer implies that the host equals the
  repository.
- Keep the dated 2026-09-27 clarification, and point its outstanding
  Codex item to the new section.

Sources: openai/codex rust-v0.157.1 (36650394)
codex-rs/core/src/tools/registry.rs:598-612,619-634,667-682,
codex-rs/core/src/tools/code_mode/mod.rs:363-379,
codex-rs/core/src/tools/code_mode/delegate.rs:323,335-337,
codex-rs/core/src/tools/orchestrator.rs:196-198,
codex-rs/otel/src/metrics/client.rs:46,345; Loki v3.7.x
send-data/otel (non-index resource attributes are stored on each entry).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Require the call_id guard in the always-on collector check

The PyYAML-free test_codex_launch_tag_and_call_id_are_kept checked only
the env guard. The call_id guard was covered only by PyYAML-gated tests,
so the collector README's always-on claim overstated coverage (verifier
finding on 76c97846).

The test now requires the exact call_id guard statement in the last
transform/tool_names log group, and requires that group to have no
conditions. Transform processor conditions are ORed where clauses that
gate their whole statement group (opentelemetry-collector-contrib
v0.161.0, processor/transformprocessor/README.md:164), so a guard in a
conditioned group would leave call_id on other events unchecked. Call ids
come from openai/codex rust-v0.157.1 codex-rs/otel/src/tool_result.rs:61
and codex-rs/otel/src/events/session_telemetry.rs:1112,1121.

Scratch mutation controls with system python3 (no PyYAML): removing the
guard, conditioning its group or widening its pattern to {0,255} each
fails the extended test and passes the previous one. The README now names
both guards and where each must sit.

Review findings F1, F3, F6, F7, F9 and F10 concern the token-adoption
E2E amendment, outside this PR's files; the u7 handoff records them as
Amendment 4 inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add a failing test: an SDK receipt must not export call_id

The cross-family review of this branch sent an ecosystem.sdk_receipt record that
carries a call_id through the committed pipeline (native file receiver). The shared
privacy allowlist now admits call_id for Codex records, and the SDK-receipt deny
list did not remove it, so the receipt exported it. The native Loki test gains a
third resource shaped like the receipts pipeline (codex-sdk-receipt) with a
call_id; it must reach Loki and the event file without one.

At the base collector it fails for the right reason: "SDK receipts without a call
id" reads 0 where 1 is expected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Keep call_id out of SDK receipts: add it to the receipt deny list

The shared privacy allowlist admits call_id for Codex records. An SDK receipt file
is ingested through the same allowlist, and its deny list (the names a receipt
cannot supply) did not include call_id, so a receipt carrying one exported it.
Found by the cross-family review of this branch by sending a receipt with a
call_id through the native file-receiver pipeline.

The deny regex gains call_id. The structural test now states the relation: the
deny list is the allowlist tail from tool_family plus the Codex call id. The
native test from the previous commit passes: the receipt reaches Loki and the event
file without a call id, the other receipt fields are unchanged, and Codex records
keep theirs. The README sentence about receipts names call_id.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Register the changed collector files in the evidence manifest

Re-registers the four collector files this branch changes (collector.yaml, its
README and the two observability test modules) on top of main's manifest, as the
hot-file protocol in docs/lanes.md asks. scripts/validate.py passes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
* Add failing-first tests and a stub-less skeleton for freeze_snapshot

tests/test_freeze_snapshot.py specifies the U8 tool for the #381 window W
(README "Procedure" step 2, RUNBOOK "Freeze and preflight"): capture writes a
private full record and a sanitized one, compare reports drift among frozen
items only, check compares a capture with sealed expectations, list-frozen
prints the frozen set. The suite runs against a synthetic host (temporary
checkout, home, fake claude/codex/rtk/node/python3/qmd/systemctl on a private
PATH, canned repo scripts, a loopback HTTP server). Shapes of the canned output
were read from the real commands on 2026-09-29: claude 2.1.284 mcp list and -p
json, qmd 2.8.3 status, systemd 255 systemctl --user show, adoption_status.py
--client-wiring and --pinned-versions, codex_quota.py --json.

MutationControlTests writes a mutant of the tool for each property (environment
leak, a collector blind to each item class, informational drift that fails,
check that always passes, credential refusal off, a raising missing-tool path)
and requires the test for that property to fail on it.

tools/token-e2e/freeze_snapshot.py is only the argument surface here and
writes empty item lists, so the tests fail on their assertions and not on an
import error. The implementation follows in the next commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Implement freeze_snapshot capture, compare, check and list-frozen

tools/token-e2e/freeze_snapshot.py is the U8 glue for the #381 window W
(README "Procedure" step 2, RUNBOOK "Freeze and preflight"): a read-only,
standard-library-only tool that captures the frozen environment as items with
an id, class, status, value and method, compares two captures, checks one
against sealed expectations and lists the frozen set. No upstream
implementation of this glue exists.

Rules and their sources: `systemctl --user show UNIT -p PROPERTY` for unit
state (systemctl(1), systemd 255); `git --no-optional-locks status --porcelain
--untracked-files=no` for a tree-clean read that writes nothing (git(1),
git-status(1)); the command outputs observed on 2026-09-29 for claude 2.1.284
mcp list and -p json, qmd 2.8.3 status, adoption_status.py and codex_quota.py.
The Claude usage probe adds --verbose (array output whatever the user
settings say), --no-session-persistence and --setting-sources project so the
one Haiku call runs no user hook or MCP server; the brief's command reads the
same rate_limit_event.

Privacy: collectors derive only hashes, versions, counts, booleans and model
aliases, the private capture stores paths as ~/ or <repo>/ forms, and a final
guard refuses (exit 3, nothing written) any string that carries an
environment value of eight characters or more, the home or checkout path, or
the user or host name. The credential stores are refused by name, symlink and
inode. All scanners are linear character scans.

The tests are corrected where the failing-first run showed a wrong
assumption: the schema id carries no slash (the sanitized capture holds no
path character), nested ids use a colon, and the probe flags, binary hash and
tokenizer override are asserted. MutationControlTests now compiles each mutant
before running it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Harden freeze_snapshot after review and document it in the README

Review of the first implementation found and fixed, each with a test:
- a refusal message named the offending item id, and a host-derived family
  id such as hooks.installed.<name> can carry the user name: a family member
  is now named by its family, and the message names the environment
  variables involved (names, never values) so a refusal can be acted on;
- the guard would have refused every capture on a host whose user, host or a
  variable equals a word of the tool's own catalogue (a user called claude):
  catalogue and structure words are not listed as forbidden;
- a configured path that is a FIFO would block the read: only regular files
  are read (error reason not_regular_file or is_directory);
- a parser pin with an unusable file key was read partly: it is now
  unrecognized_pin as a whole; the pin's default directory is home-relative;
- unit-derived config paths must be absolute (a relative one has an unknown
  base); a configured unit may be informational (paper-trading units run
  inside a run window) while the nine defaults stay frozen.

tools/token-e2e/README.md gives usage, the catalogue table, how the seal,
W-open, W-start and W-end captures are used, the configuration, the privacy
rules and the limits, with sources (#381 README "Procedure", RUNBOOK freeze
rules, systemctl(1) show, git(1) --no-optional-locks, git-status(1)
--porcelain). A test matches the table rows against list-frozen in both
directions; removing a row or naming a stale id makes it fail.

The suite is 99 tests, green on Python 3.13.15 and 3.12.3.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Apply the path rule without a checkout; add a print-environment control

Independent review of the completed unit found four gaps, fixed here:
- `list-frozen` without --repo skipped the roots check, so a configuration
  path such as /etc/hosts was accepted. The brief says never accept a path
  outside the checkout or the home directory: with no checkout the home
  directory is the only root and a relative path may not climb out
  (check_path_policy). A test covers the refusals and the accepted paths.
- The brief's literal negative control, a tool variant that prints
  os.environ, was missing: my leak mutants put the environment into the
  record, which the guard catches, while nothing guards stdout. A
  print_environment mutant now fails the privacy test on its stdout scan.
- `MutationControlTests` runs its 17 mutants in a thread pool (about 10 s
  instead of about 55 s) and records the assertion lines from stderr.
- A test now covers the Connected-prefix rule of `claude mcp list` statuses.

tools/token-e2e/README.md records the residual limits: a connected flag of
`claude mcp list` can flip without a file changing, ai-memory and cognee-live
are not user units on the workstation host, a build id kept only in a unit's
environment is not captured, and the parser items are missing until U1's pin.

Against the earlier copies of the tool the current suite fails, which shows
what these commits fixed: the first implementation (fe790450) fails 9 tests
(a FIFO capture hangs until the 60 s timeout, a refusal that names the user
in a host-derived id, a host user called claude refusing every capture, the
parser pin read partly, informational units, and others); the hardening
commit fails only the path-policy test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Keep home-path literals out of the freeze_snapshot test fixtures

scripts/validate.py rejects a registered file that contains a personal home
path (/home/<name> or /Users/<name>, PRIVATE_CONTENT "personal home path").
Two fixtures of tests/test_freeze_snapshot.py used /home/x as an example of
non-plain text; they now use /opt/x, which is equally a path and is refused
by the same is_plain rule.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Make the fixture-isolation check of the freeze_snapshot tests real

The last assertion of FixtureSanityTests.test_fake_commands_print_the_canned_shapes
compared a value with a constant that its own else branch produced, so it
could never fail. It now asserts that the fixture PATH holds only the fake
bin directory and the symlinked utilities, and that claude, codex, rtk, node,
python3, qmd, systemctl and git each resolve to the fake: a real command
reachable from the fixture would make the whole suite talk to the host.
With a real directory appended to the PATH the test fails.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Name each freeze_snapshot rule's source and its observed-only shapes

The brief asks for a source, document or section for every rule the tool
implements. The README's Sources section now covers each of them: the #381
README "Procedure" and RUNBOOK freeze rules, systemctl(1) show (systemd 255)
and the configuration flag each default unit's binary lists in its --help,
git(1) --no-optional-locks with git-status(1) --porcelain, the Claude Code
CLI reference for the probe flags (--no-session-persistence, --setting-sources,
--output-format, --verbose, --model), proc_meminfo(5) MemAvailable, and the
output shapes read on 2026-09-29 and kept as test fixtures.

It also records the check for an existing tool (osquery, AIDE, Ansible and
InSpec are maintained but do not fit: osquery's systemd_units table has no
MainPID or NRestarts column) and two limits: `claude mcp list` has no
documented machine-readable form, and unifiedWindows in the rate_limit_event
is observed on claude 2.1.284 while the Agent SDK reference documents
RateLimitInfo without it, so a client update may turn the four Claude
capacity items into `missing`. The Tests paragraph now lists the
print-environment mutant.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the settings behaviour keys

Coordinator delta to the U8 brief, item 3: the behaviour-affecting keys of the
user and project settings files join the frozen set. The sealed `claude -p`
arms launch with no permission flags, so these settings define every arm's
permissions and whether a peer session can message a run's lead. Keys and
value sets follow the settings reference fetched 2026-09-29
(https://code.claude.com/docs/en/settings-reference): permissions.defaultMode
(default, acceptEdits, plan, auto, dontAsk, bypassPermissions, manual),
permissions.allow|deny|ask (counted, never printed), skipDangerousModePermission
Prompt, crossSessionInbound (accept, hold, refuse), autoContinueAtUsageLimit,
autoUpdatesChannel (latest, stable), ultracode, enableWorkflows,
workflowSizeGuideline (unrestricted, small, medium, large), switchModelsOnFlag,
model and effortLevel; advisorModel is already an item.

These tests are committed before the change and fail against the tool as it
stands: one row per key in each of the three settings files (catalogue and
values, with values that differ between the user and project fixture), absent
and undocumented values as classes and never as values, rules counted and never
printed, drift on every key, `check` failing per item, and an environment value
equal to a printed setting value not refusing the capture. Mutation controls
cover permissions.defaultMode and crossSessionInbound (a collector blind to the
key) and permission rules printed with and without the guard; the new rows also
name the text their failing assertion must contain.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Freeze the settings behaviour keys; guard rules and printed aliases

Coordinator delta to the U8 brief, item 3. Source: the settings reference,
fetched 2026-09-29 (https://code.claude.com/docs/en/settings-reference), for
the keys and the closed value sets. Each settings file now yields 14 more
frozen items, claude.settings.<file>.{permissions_default_mode,
permissions_allow_count, permissions_deny_count, permissions_ask_count,
skip_dangerous_mode_permission_prompt, cross_session_inbound,
auto_continue_at_usage_limit, auto_updates_channel, ultracode, enable_workflows,
workflow_size_guideline, switch_models_on_flag, model, effort_level}: a
documented value, a boolean, a count of rules or a model alias. An absent key is
`unset`, a value outside its set or of another type is `other`, and a rule list
that is not a list is an error. The files are the same three the existing
derived items read; the brief names user and project, and local costs nothing
and closes the same gap for a checkout that has a settings.local.json.

Two guard changes come with it. A permission rule can hold a secret, so the
rules are read and counted but never printed, and their text is listed as
forbidden like an environment value. A model alias or documented value that the
tool prints from a settings file may equal an environment value (an advisor
alias, ANTHROPIC_MODEL): that no longer refuses the capture, and the documented
values join the catalogue text the guard already spares.

The README table gains the row; its coverage test matches it in both
directions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add a failing-first test for a usage probe rejected at the session limit

The real acceptance capture of this unit ran while the account's five-hour
window was over its limit: `claude -p ... --output-format json --verbose`
exited 1 with HTTP 429, and its rate_limit_event (status rejected, five_hour
utilization 1.06, seven_day 0.53) was in the output array. The tool treated
the nonzero exit as a failure and reported all four Claude capacity items as
error(exit), dropping the numbers at the one moment the opening rule of a run
window (both windows under 30% used) needs them.

The test makes the fake claude exit with a configurable status and expects the
windows to be read from a rejected call, and an error(exit) for a failed call
that returned no usable event. It fails against the tool as it stands.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read the usage windows from a probe rejected at the session limit

A `claude -p` call rejected at the session limit exits 1 with HTTP 429 and
still returns its rate_limit_event (observed on claude 2.1.284 on 2026-09-29:
status rejected, five_hour utilization 1.06, seven_day 0.53). The tool now
reads that event from the output of a call that exited nonzero, so the four
informational Claude capacity items carry the numbers when the window is over
its limit, which is when the opening rule of a run window needs them. A failed
call that returned no usable event stays an error, with reason exit. The items
are informational, so neither outcome fails compare or check. The README row
says so.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for list-frozen paths and the parser mirror

Review of freeze_snapshot at 89ae6814 found two defects; these tests fail
against the tool as it stands (Ran 8 tests, FAILED failures=30 errors=13).

list-frozen printed the path a freeze.json extra file names, so an absolute
path put the home path and user name on stdout. The new test lists five
spellings in text, --all and --json form and requires that no configured
text, home path, checkout path or user name reaches stdout and that the
extra row carries no path character.

tools.parser.* mirrors child-usage.mjs verifiedShellParser (branch
claude/pra-u1d-parser-ci-2d-20260929 at 967561cc, lines 656-669) but ignored
CHILD_USAGE_SHELL_PARSER and the lockfile, so it reported all_match_pin true
for an install the kernel refuses. ParserPinTests give the fixture the real
U1 pin shape (packages, install.lockfile) and a package-lock.json, and check
the directory order, the lockfile hash and match, the refusals and the pin
validation; ParserTests covers lock_matches_pin.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Mirror the kernel's parser load check; keep paths out of list-frozen

Repairs the two defects the review of 89ae6814 found; the failing-first
tests of 01be4f65 now pass (Ran 122 tests, OK).

list-frozen no longer prints the path a freeze.json extra file names: its
how-to-check line says the configuration names the file and only the private
capture records where it lives, so no home path, checkout path or user name
reaches stdout.

tools.parser.* now checks the install as child-usage.mjs verifiedShellParser
does (branch claude/pra-u1d-parser-ci-2d-20260929 at 967561cc, lines 656,
661 and 663-668; identical on claude/pra-u1-cmdpos-2d-20260928 at 2bad732,
lines 643-655): the directory comes from the configured parser_dir, then an
absolute CHILD_USAGE_SHELL_PARSER that meets the path policy (a relative
value and ~ are refused, and nothing derived from the variable is stored),
then the pin's default; the pin must carry both packages with a version and
an integrity; the lockfile it names (default package-lock.json) is hashed as
tools.parser.file.<name> and must list both packages with the pinned version
and integrity, or all_match_pin is false.

Adds seven mutation controls, each failing its intended assertion: the
configured path printed, the variable ignored, its location stored, a
relative or tilde value accepted, the configured directory below the
variable, the lockfile ignored, and the tokenizer_prefix override ignored
(a test the review found had never failed against an earlier tool).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Document the parser mirror, path-free listing and the review's limits

README of tools/token-e2e after the review of 89ae6814: the catalogue row,
configuration and sources for the kernel-order parser check (child-usage.mjs
verifiedShellParser at 967561cc and 2bad732), the path-free list-frozen
line, and the limits the review found undocumented.

Corrections: the Python claim no longer says 3.9 or newer (the tests ran on
3.12.3 and 3.13.15 only) and the host-state claim covers the tool's own
writes, because claude mcp list starts servers and claude -p is the client's
command. The session-limit probe is described as exiting nonzero, which the
retained capture (error exit) and the retained probe shape (status rejected,
api_error_status 429) show; the exact code appears only in a commit message.

New limits: the report freeze omits tools/token-report/returned_results.js,
which token_manifest.py inlines into the rendered HTML (name it under files,
as the example now does); the RUNBOOK role-body SHA256 values are the seal
owner's to verify against the seal capture; the RUNBOOK names three role
copies and the tool hashes four.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* State what the probe isolation was observed to do, and what it was not

The README said the usage probe's flags keep the call from running the user's
hooks or MCP servers or leaving a transcript. One direct run of the probe's
exact argv on 2026-09-29 (claude 2.1.284) showed what is observable: the
isolated call reported no MCP server against the seven that a call with the
user settings reported (retained shape haiku.json), and left no project
directory under the client's state. The event stream names no hook in either
run, so that no user hook ran follows from --setting-sources project (Claude
Code CLI reference: the list of setting sources to load) and is inferred, not
observed. The text now says so.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Register freeze_snapshot, its README and its tests in the manifest

Hot-file protocol of docs/lanes.md, applied against this branch's exact base
(b528bb5) and recreated after the repair round changed the three files:
manifests/evidence.json gains the entries for tools/token-e2e/freeze_snapshot.py,
tools/token-e2e/README.md and tests/test_freeze_snapshot.py through
host_receipts.register_file, so files[] stays sorted and its hashes are those
of the committed bytes. component_matrix.py --write and new_host_grand_list.py
--write changed nothing, and scripts/validate.py exits 0 (8003 hashed files,
168 receipts). This commit replaces e55db6be and 4aadc8b6, which sat before
the last repair commits, so the manifest is touched in the last commit only.

Not taken from origin/main: its manifest lists 75 files that do not exist at
this base, so validate.py would report them missing. The protocol must be run
again after the rebase onto main that precedes the merge; this commit is
valid against b528bb5 only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
…ng tests (#512)

* CI: raise the validate and validate-macos job timeouts for the #381 tooling tests

Measured after #502 on 8d1be28: validate took 753s of its 1200s limit (run 36572588202) and validate-macos 1148s of its 1800s limit (run 36572588263). The token-E2E measurement tooling that is still to merge (units U1 to U5, U9, U10, U13) adds an estimated 600 to 900s of unit tests on Linux and more on macOS, so the limits become 40 and 60 minutes; they stay hang guards. Precedent: the 2026-09-26 raise from 10 to 20 minutes recorded in the same comment block.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Re-register the two workflow files (hot file)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
…er-bash parser (#516)

* Add failing-first N1 probes and must-stay controls for M4

N1 is the #432 fixup3 residual: openers() does not track "$(" inside
double quotes, and a quoted string that a shell runs is appended
without its own heredoc resolution, so heredoc data reads as executed.

Adds three tests through the existing measure/exports bridges on all
three shell carriers plus fetchKind:
- p1 (git commit -m "$(cat <<'EOF' ...)" with a line-start curl), its
  body variants with ', " and ( ), p2 (gh api in the body), p3
  (bash -c 'cat <<EOF > x.sh ...') and a double-quoted p3 variant;
- must-stay controls: an executed curl in "$( )", a shell heredoc in
  "$( )", an escaped substitution in a double-quoted run string, the
  command after a heredoc in "$( )" (guards against resolving one
  heredoc twice), the outer-shell heredoc in a double-quoted run
  string, unquoted $( ) and the "Subject (scope)" body;
- the nested-quote case from design 1.5.

At cf3fb72e the new tests fail 23 subtests (p1, the ' variant, p3 and
the double-quoted p3: shell fetch 1 != 0 and fetchKind 'fetch'; p2:
unclassifiable 1 != 0; nested quotes: 0 != 1 and fetchKind None); the
must-stay test passes. Source: POSIX.1-2024 XCU 2.6.3 Command
Substitution, checked with GNU bash 5.2.21 and dash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first N1 tests for strings a shell runs

The N1 fix analyzes a string that a shell runs (RUN_QUOTED) as that
shell's input in both modes, instead of appending it verbatim unless
inlineHttp is set. Two consequences get their own tests first:
- in a double-quoted run string the outer shell removes the backslash
  before " (POSIX.1-2024 XCU 2.2.3), so `bash -c "echo \"a; curl
  ...\""` runs only echo: base reads a confirmed shell fetch;
- a string the inner shell runs in turn is analyzed too, so the curl
  in `ssh host 'bash -c "curl ..."'` is confirmed: base counts it in
  neither the confirmed nor the unconfirmed fetches, so the gate's
  lower bound could not see it.

Against the cf3fb72e kernel the test fails 7 subtests (1 != 0 and
0 != 1 on each carrier, fetchKind ['fetch', None]). GNU bash 5.2.21
and dash print `a; echo RAN` for the escaped form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Resolve heredocs inside "$( )" and in strings a shell runs (N1)

Fixes the #432 fixup3 residual N1 in child-usage.mjs. POSIX.1-2024 XCU
2.6.3: inside double quotes the text from "$(" to the matching ")" is
tokenized recursively; 2.2.3: in a double-quoted word the outer shell
removes the backslash before $ ` " \ and newline. GNU bash 5.2.21 and
dash agree on every shape below.

- step() is one frame machine (', ", `, and $( with depth) shared by
  phase 1 openers() and the phase 2 closeQuote()/matchParen(), so the
  phases agree on where a quote or substitution ends. With no frame
  open it reads exactly as the old openers(); only a double-quoted
  word gains $( and backquote frames, so a << inside "$( )" is found
  and its body resolved like any other heredoc.
- executedTrace splits into resolveHeredocs (phase 1) and scanQuotes
  (phase 2). The naive end-of-quote scan becomes closeQuote; a "$( )"
  body in double-quoted data is spliced as "$(" + scanQuotes(body) +
  ")" with exact offsets (no second phase 1 over it).
- A string a shell runs (RUN_QUOTED) is analyzed with the full
  executedTrace in both modes, a double-quoted one after unquoted()
  removes the outer shell's escapes at its own level. A shared set of
  resolved operator offsets keeps a heredoc the outer shell resolved
  in "$( )" from reading later lines as its body again.
- NESTING_LIMIT (32) bounds recursion: deeper analyses read as data.
  Without it '"$(' x 3000 and a 3000-deep shell heredoc chain throw
  RangeError, which would abort a sweep.
- The paren-free DOUBLE_QUOTED_DATA regex is removed; the scanners are
  linear character scanners (no new regex; CodeQL js/redos).

The failing-first tests from 1d206a12 and 54299c44 now pass; the
must-stay controls are unchanged. SHA256SUMS is regenerated in a
later stage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the N1 reading and add kernel unit cases

The workflows README replaces the N1 limit paragraph with the rule the
kernel now follows: "$( )" inside double quotes is read recursively
(POSIX.1-2024 XCU 2.6.3), a string a shell runs is analyzed as that
shell's input after the outer escapes are removed (XCU 2.2.3), and
reading text as data moves a raw detector match to the unconfirmed
count instead of dropping it. It states the remaining limits:
backquoted spans inside double quotes, case patterns inside "$( )",
the 32-level nesting bound, and text bash rejects as incomplete.

tools/skill-usage/README.md notes that the legacy Python executed_text
rule predates this reading and can disagree on these shapes.

test-child-usage.mjs adds fetchKind and exact executedText cases for
the N1 shapes and a nesting witness. Against the cf3fb72e kernel the
three new cases fail (95/98); with NESTING_LIMIT removed the witness
fails (97/98, RangeError); with the fix all 98 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* State how the N1 reading moves the M4 lower bound

The previous wording said reading text as data leaves the denominator
of routed_share_lower_bound unchanged. That holds for raw detector
matches only. Measured against the cf3fb72e kernel:
- `echo "$(echo "a" && curl http://127.0.0.1:9/)"` moves from
  unconfirmed to confirmed loopback, which leaves the denominator;
- `echo "$(\curl ...)"` gains a confirmed fetch no raw detector sees;
- `echo "$(echo " ; \curl ...")"` loses a confirmation the old
  reading created (bash prints it as data), so the bound can rise.
The README now states those three cases. It also adds a limit: a
backquoted span outside quotes is still read as plain command text,
so a quote it leaves open runs past its closing backquote.

test-envelope (254/254), test-usage-receipts (18/18) and
test-contract-mutations (74/74) pass; they read this README as
routing_doc and contract_doc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first CLI-lane, state and proxy-position fixtures

Add the U1 design section 7 fixtures before the kernel change. The
Python integration cases feed measureTranscript, aggregateMeasurements
and the sweep CLI: the stack.json lane commands (:1003, :797, :579, :762,
:519, :629, :280, :407), wrappers, compound commands and substitutions,
runners, every mcporter call form, negatives (data, lookups,
registrations, near names, gcm and serena-hooks), version and help,
remote and unresolved programs, the call states, the rtk proxy
population by command position with prefix_rule_calls, M3 and M4
by_carrier, aggregation with actors_with_success, and ID-free sweep
output. The node suite adds commandInvocations unit cases.

Sources: #381 AA-PLAN PR-A item 3 and M6/M14; POSIX.1-2024 XCU 2.9.1-2.9.4
and 2.6.3; rtk-ai/rtk@1d87b8e7 src/main.rs:68-90,708-713,3008-3042 and
the installed rtk 0.50.0 (proxy -- and --skip-env bind, -v after proxy
is the program); openclaw/mcporter@93e0916c src/cli.ts:115-364,
src/cli/command-inference.ts:10-95, call-arguments.ts:79-233 and
call-command.ts:114-173,309-348 (a first positional with = is the
selector). Before the fix: 10 Python tests fail (102 subtests) and the
8 new node cases fail on the missing export.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Count CLI lanes and rtk proxy calls by command position

Export commandInvocations: every simple command of the executed text
(POSIX.1-2024 XCU 2.9.1-2.9.4, 2.6.3) is resolved past reserved words,
assignments, wrappers (timeout, env, nice, stdbuf, command, exec, time,
nohup, sudo 1.9.15p5, xargs), package runners and rtk proxy to a lane
executable by exact basename. mcporter operations and a call's
downstream server follow openclaw/mcporter@93e0916c (cli.ts,
command-inference.ts, call-arguments.ts, call-command.ts); HTTP and
stdio selectors read (http) and (stdio), and no host is emitted. rtk
proxy follows rtk-ai/rtk@1d87b8e7 src/main.rs:68-90,708-713,3008-3042
and the installed rtk 0.50.0 (no shell runs the proxied program).

One memoized reading per call drives carrierOf (M3 and M4 by_carrier),
measurement.proxy (adds invocations, nested, in_ctx_code and
prefix_rule_calls) and the new measurement.cli_lanes with per-call
states (AA-PLAN "Successful" and M14; not_executed from observed
transcript markers). aggregateMeasurements sums the fields and adds
actors_with_success; the sweep limits gain the static limits.
executedTrace takes an optional marks collector for ssh-run text and
data-heredoc substitutions; its output is unchanged (0 differences in
executedText and fetchKind over 24,489 inputs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read mcporter URL and stdio selector forms with char scanners

Replace the character-class regexes in httpUrl and httpToolSelector,
the path-prefix alternation for an ad-hoc stdio selector and the
blank split of an inline npx server with character checks and the
existing shellWords splitter. The brief asks for linear character
scanners rather than regexes in this kernel (CodeQL js/redos flagged it
before). Behaviour is unchanged: the rules still follow
openclaw/mcporter@93e0916c src/cli/http-utils.ts:1-69,
call-argument-values.ts:68-83 and ephemeral-target.ts:134-158, and the
node suite passes unchanged (107/107).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document CLI lanes, call states and the command-position proxy rule

Add a CLI-lanes subsection to the PR-A measurement fields: the lane
executables with the gcm and serena-hooks exclusions, the resolver
(wrappers, runners, rtk proxy), each cli_lanes field, the mcporter
operation and downstream-server precedence of mcporter v0.14.1, the
call states with the observed not-executed markers and their M14
mapping, the static limits, and the changed meanings of proxy.calls,
M3 by_carrier.rtk_proxy and m4.by_carrier with prefix_rule_calls kept
for #369-era comparison. Record the decision that proxy.in_ctx_code
stays outside M6 (Gate A plan M6 and rtk rows) and state that
rtk_parts, proxy_parts and model_typed keep their prefix rules. The
skill-usage README points Codex readers to the same fields and states
that declined items and legacy exec headers still read succeeded until
the adapter maps them.

Sources: rtk-ai/rtk v0.50.0 src/main.rs:68-90,3008-3042; openclaw/mcporter
v0.14.1 src/cli and docs/call-heuristic.md; mksglu/context-mode v1.0.169
src/exit-classify.ts:15-33; POSIX.1-2024 XCU 2.9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add a failing case: exec -- reads as an unresolved program

U1 design 2.2 reads only the POSIX exec form and marks any word that
starts with '-' after exec as unresolved, because bash's exec options
are not pinned. The shells disagree on `exec -- cmd`: GNU bash 5.2.21
runs cmd, while dash tries to execute "--" (probe on this host). The
kernel currently takes `--` as the end of options and reads qmd.
Observed before the fix: "exec -- qmd mcp" => ["qmd/qmd"].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read any dash word after exec as an unresolved program

Follow U1 design 2.2: only the POSIX exec form is read, so a word that
starts with '-' after exec, `--` included, leaves the program
unresolved. bash 5.2.21 runs the command after `exec --` while dash
executes "--" itself, so neither reading is safe to assume. The failing
case from 1fcc9f23 now passes (node suite 107/107).

Also cover the inline npx server of an mcporter call
(openclaw/mcporter@93e0916c src/cli/ephemeral-target.ts:134-158): a
--server value that is an npx command line is an ad-hoc stdio server,
and another blank-holding name reads (other). These cases were added
after the implementation, so they are coverage, not failing-first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first Codex fixtures for shell call states

U1 design 6 fixtures (i)-(vii) run rollout records through
measure_codex_records, the Node bridge and the kernel's cli_lanes and
proxy. Record shapes follow openai/codex rust-v0.157.1 (36650394):
CommandExecutionItem and its snake_case status (protocol/src/items.rs
:199-285), exec end states (core/src/tools/events.rs:529-573: a
rejection is declined with exit -1) and the unified exec response
header (core/src/tools/context.rs:524-575). A rollout output never
carries the success flag (protocol/src/models.rs:2173-2182).

Before the adapter change 6 of 8 subtests fail:
- (iii) code-mode item declined: qmd succeeded 1, expected failed 1
  and not_executed 1; the same for a direct call's declined item;
- (iv) legacy exec_command with "Process exited with code 1":
  rtk_proxy succeeded 1, expected failed 1;
- (v) "Process running with session ID 3": qmd succeeded 1, expected
  unknown 1;
- (vi) no header and no item: qmd succeeded 1, expected unknown 1;
- (vii) local_shell_call running stack.json:280:
  codebase-memory-mcp succeeded 1 and downstream codebase-memory
  succeeded 1, expected unknown 1 in both.
(i) nested rtk proxy (proxy.calls 1, proxy.nested 1, succeeded 1) and
(ii) a failed item already pass. Must-stay controls pass: an exit 0
header, an exit line inside the output, a completed item that beats
a running header, a failed item with its header, and the aggregate
passing cli_lanes and proxy through with actors_with_success.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Map declined items and exec headers to Codex call states

U1 design 6: the Codex adapter now hands the kernel's callState what a
rollout shows about a shell call's outcome, so cli_lanes stops reading
declined or failed legacy commands as succeeded.

- A declined CommandExecution (openai/codex rust-v0.157.1 36650394
  core/src/tools/events.rs:562-573, exit -1) sets is_error and
  native_state "declined" on both result paths: the call's own
  function_call_output and the item's aggregated output. The kernel
  counts it failed and not_executed.
- A Bash-mapped call (exec_command, shell_command, shell or
  local_shell_call, chosen by call kind, never by output text) with no
  persisted item state is read by the unified exec header of
  core/src/tools/context.rs:524-548, and only by the lines before
  "Output:", in a linear line scan with a strict ASCII exit code.
  Exit 0 is success, another exit is_error. A running line (it wins
  over an exit line, unified_exec/process_manager.rs:1066-1071), a
  header with neither line or text without the header sets
  native_state "unknown": a rollout output never carries success
  (protocol/src/models.rs:2173-2182), and legacy history mode keeps no
  item (rollout/src/policy.rs:94-112). No shell, shell_command or
  local-shell handler exists at that revision, so those outputs read
  unknown unless an item state decides; apply_patch's "Exit code: N"
  text is not read.
- codex_call_name shares the MCP namespace rule between the call loop
  and the shell-call set, so an MCP tool named exec_command is not a
  shell call.

The failing-first fixtures from 4a7aa75a now pass (the three Python
suites: 160 tests OK). The header unit test
(test_exec_header_state_reads_only_the_pinned_header) was written
after the parser and is coverage, not failing-first. The lanes CLI
still writes nothing to stderr, and the Codex aggregate passes
cli_lanes and proxy through unchanged.
tools/skill-usage/README.md replaces the stage B sentence that said
these calls still read succeeded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate workflow SHA256SUMS for the U1 kernel and tests

The N1 fix and the CLI-lane reading changed child-usage.mjs and
test-child-usage.mjs; stage C leaves the kernel byte-identical to
0c421c66 (sha256 acc7bb51...). Regenerated with the documented
command, `sha256sum -- *.mjs *.js *.json > SHA256SUMS` in
examples/claude-native/workflows, and `sha256sum --check --strict
SHA256SUMS` passes for all 13 files. Only those two digests change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Re-register the U1 files in manifests/evidence.json

The hot-file protocol (docs/lanes.md:94-103) puts the shared manifest
edit in the branch's last commit and re-registers every changed file
that the manifest already lists, with host_receipts.register_file.
scripts/validate.py checks SHA-256 and byte counts for each of them
and failed before this commit ("SHA-256 mismatch", "byte count
mismatch" for the workflows README, child-usage.mjs,
test-child-usage.mjs, tests/test_token_measurement.py and
tools/skill-usage/README.md). The eight re-registered entries are the
two READMEs, SHA256SUMS, child-usage.mjs, test-child-usage.mjs,
skill_usage.py, test_skill_usage.py and test_token_measurement.py.
Only their sha256 and bytes change, and validate.py now passes
(status passed, 7986 hashed files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for the U1 pivot loader and M4 repairs

Tests only, run against 7091a300 before any fix (U1 pivot brief D1, D6-D9,
D11; GPT-6 findings #8, #9, #11, #12, #13; Claude review R1, R2, R3, R9).

- D1 loader (test-child-usage.mjs, test_skill_usage.py): pin file contents,
  loadShellParser statuses (installed, not_installed, hash_mismatch),
  directory order, --shell-parser, commandInvocations null and cli_lanes
  parser_unavailable with proxy prefix_fallback, aggregate statuses,
  withShellTree freeing its tree, and the Node bridge awaiting the loader.
- D6: shell_script quotes argv with shlex.join, and -c is a script only for
  a shell; exec_header_state bounds the exit code to 9 digits (GPT-6 #11,
  #12 inputs verbatim).
- D7: outer-shell substitutions in a double-quoted run string are executed
  text (GPT-6 #8 verbatim), with single-quote, comment, backquote, sh, eval
  and ssh variants and must-stay controls that count once, not once per view.
- D8: n = 8000..64000 doubling for unclosed ((, $((, "$( ((, and ( << E
  runs, at most 2.5x per doubling and under 150 ms at 64000.
- D9: the stress check lets an exception fail it, includes an unquoted
  '$('.repeat(3000) input (NESTING_LIMIT), and a mutation control (a reader
  that throws for long input) shows the check now fails.
- R1, R3: a heredoc after a closed "$( )" keeps its reader; an escaped blank,
  semicolon or newline before a # is not a comment start.

Every expected value of the D7, R1 and R3 commands was run under GNU bash
5.2.21 and dash with a stub curl (and a stub ssh that runs sh on its stdin,
the remote login shell): both shells agreed on every count.

At 7091a300: node suite 115 passed, 31 failed (5 linear, 26 parser); D7 9
commands, R1 5, R3 3 fail with 0 != 1 on all three carriers; exec_header_state
raises ValueError on 5000 digits; shell_script flattens argv. The old D9
assertion passes when 7 of 7 stress inputs throw.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Quote Codex argv elements and bound the exec header exit code

U1 pivot brief D6 (GPT-6 findings #11 and #12).

shell_script joined an argv array with spaces, so a metacharacter inside one
element became shell syntax that was never executed: ["echo", "qmd; rtk proxy
qmd status"] counted one qmd call and one rtk proxy call. It now joins with
shlex.join (Python 3 shlex.join, the inverse of shlex.split), so each element
is one quoted word. The [shell, -lc, script] form (openai/codex rust-v0.157.1
codex-rs/core/src/shell.rs) stays the script itself, but only when the program
is a shell: -c of `echo -c ...` is one of echo's arguments.

exec_header_state passed the exit code text to int() whenever it was ASCII
digits, and CPython raises ValueError above 4,300 digits (int max str digits,
docs.python.org/3/library/stdtypes.html#int-max-str-digits), which stopped the
scan on one malformed header. The code is an i32 written in decimal (at most 10
digits with its sign, codex-rs/core/src/tools/context.rs:534-540); a code of
more than 9 digits is no native header and reads (False, "unknown").

The D6 tests of the previous commit pass: exec_header_state on 5000 digits,
the shell_script cases, and the three Codex bridge cases (local shell call,
code-mode item, `echo -c`). The existing Codex state tests still pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Scan unclosed (( in linear time: one paren table per text (D8, R9)

U1 pivot brief D8 (GPT-6 finding #9; Claude review R9). Every unclosed "((" or
"$((" made step() look ahead to the end of its line (indexOf the newline, then
count parentheses), so n = 8000, 16000 and 32000 of "((" took 246, 1218 and
4888 ms at 7091a300 (four times per doubling), and openers() filtered its whole
cut list once per heredoc operator.

parenClose(s) now matches every "(" of a text with its ")" in one stack pass per
line (bash(1) ARITHMETIC EVALUATION: (( )) and $(( )) count every ( and ) up to
the end of the line, which is classic parenthesis matching), and step() reads
its answer from the table. The last eight texts stay memoized so the scans that
alternate between a text and its slices do not rebuild it; executedTrace clears
the memo when a top-level analysis ends. openers() finds each operator's bounds
in the ascending cut list by binary search instead of filter and find.

Output is unchanged: executedText (both modes) and fetchKind of the base
kernel and this one agree on all 150,000 checks (the 243 covering commands, the
10,000 heredoc fuzz strings, and two seeded 20,000-string token corpora with
and without heredocs; 0 differences, 0 throws). Timings at n = 8000, 16000,
32000, 64000: "((" 3/6/13/27 ms, "$((" 4/9/19/40 ms, unclosed (( inside "$( "
5/8/18/38 ms, "(<<E" runs 8/14/30/64 ms, each within 2.5x per doubling and
under 150 ms at 64000. The D8 test allows 5 ms of timer noise on the 2.5x
bound (a linear scan showed 7.6 -> 19.4 ms once) and takes the best of five.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read a # after an escaped blank or metacharacter as data (R3)

Claude review R3 of 3cb7c4f6. In a "$( )" frame step() treated a # as a comment
start whenever the previous character was a word break, ignoring whether that
break was escaped. `x="$(echo a\ #b)"; curl ...` therefore read `#b)"; curl ...`
as a comment: closeQuote and matchParen ran to the end of the command and the
`; curl` fell inside double-quoted data (shell_fetch 0, fetchKind null, one
unconfirmed mention). bash(1) COMMENTS: a word beginning with # starts a
comment, and an escaped blank, `;`, `(` ... or newline (a line continuation)
does not end a word (QUOTING: a backslash escapes the next character).

escapedAt(s, k) counts the backslashes before s[k]; an odd count means it is
escaped. It runs only for a # that follows a word break, and reads one run of
backslashes per such #, so the scan stays linear.

Verified with GNU bash 5.2.21 and dash and a stub curl: for each of the six
R3 inputs (escaped blank, escaped semicolon, backslash-newline, an even
backslash count before a real comment, a real comment inside "$( )", and a
top-level escaped blank) both shells ran the stub curl once, so each is one
executed fetch.

R3 test passes on all three shell carriers. Differential of the previous
commit's kernel and this one over 90,243 inputs (243 covering commands,
10,000 heredoc fuzz strings, and two seeded 40,000-string token corpora, with
and without heredocs; executedText in both modes and fetchKind): 228 differing
inputs, every one containing an escaped word break before a # (0 unexplained).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first R1 shapes and the --shell-parser refusal test

Tests only. Extends the R1 cases of 62cb6935 with the shapes the fix has to
cover beyond the reviewer's repro, each run under GNU bash 5.2.21 and dash with
a stub curl (both shells agreed):

- a quote inside the substitution: FOO="$(echo "a b")" bash <<'EOF' and the
  ssh variant, where the naive word split cuts the quoted word at the inner
  blank (curl ran once; with cat as the reader it ran 0 times);
- a string that runs over lines before the operator: x="a\nb" bash <<'EOF' and
  the single-quoted variant, and a "$( )" that closes on a later line with text
  after its ")" (curl ran once each);
- two closed "$( )" words, and controls that already read correctly at
  f83f8367 (a separator before the operator, a backquote span, a nested
  "$( )" with its own heredoc, cat as the reader).

At f83f8367 the 11 executed shapes each fail on all three shell carriers (33
subtests: shell_fetch 0 != 1), the four controls pass. Also: an explicit
--shell-parser that names a directory with no install exits 2 with the reason
and no report (the --rtk-db and --exceptions convention), never a silent
fallback to the default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Bound each heredoc by the cuts of its own frame level (R1)

Claude review R1 of 3cb7c4f6. openers() kept one cut list for every nesting
level, so the ( and ) that a double-quoted "$( )" adds also bounded the heredoc
operators outside it: in FOO="$(pwd)" bash <<'EOF' the operator's command
started at the closing quote, its reader resolved to the empty string, and a
body that bash reads as a script became data (shell_fetch 0, one unconfirmed
mention, fetchKind null; real bash and dash run the stub curl once).

step() now reports each cut with its frame (on.cut(i, frame): the "$(" frame
that a ( opens and a ) closes, else the frame the unit sits in, undefined for
the top level) and openers() keeps one ascending cut list per level; an
operator is bounded by the cuts of the level on top of the stack, found by
binary search. The cuts of a substitution bound the commands inside it and no
longer the command that contains it. commandWords() reads a "$( )" or a
backquoted span inside double quotes as one unit of its word (POSIX.1-2024 XCU
2.6.3: the substitution's text is tokenized on its own), so a quote or blank
inside it does not split the word: FOO="$(echo "a b")" bash <<'EOF' names bash.

R1 tests (62cb6935, 58502c60) pass on all three shell carriers, and the lane
reading of FOO="$(pwd)" bash <<'EOF' with qmd in the body counts one qmd call.
The shapes below are documented limits, pinned by their own test: a command that
a quote or "$( )" carries over lines (x="$(cat <<'A' ... \n)" bash <<'B'
and x="a\nb" bash <<'EOF') has its head on an earlier line, so this line alone
gives the operator no reader and the body reads as data (fetch_mentions_unconfirmed
1, status incomplete: a possible fetch, never a lost one). A first attempt cut
the line at the closing quote's word end; measured on the fuzz corpus it named
the wrong owner as often as the right one (ssh host cat "a\nb" bash <<EOF), so
it was dropped.

Differential of f83f8367 and this commit over 90,243 inputs (243 covering
commands, 10,000 heredoc fuzz strings, two seeded 40,000-string token corpora,
without and with heredocs; executedText in both modes, fetchKind and the M4
confirmed and unconfirmed counts): 17 inputs differ, all with a quote,
backquote or "$( )" before a heredoc operator (0 unexplained); no input lost
mass (confirmed plus unconfirmed); 16 keep both counts, one moves a fetch from
confirmed to unconfirmed. That one, bash -s -- "$(pwd)"bash <<EOF with an
unquoted-delimiter body, now names bash as the reader (correct) and reads the
body as source, where a substitution inside single quotes is data: the
"$( )"-free twin reads C=0 U=2 at 7091a300 as well (real bash runs curl once:
the outer shell expands an unquoted heredoc body before the reader sees it, a
limit that predates R1 and is unchanged).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read a double-quoted run string in two views: outer expansion (D7)

U1 pivot D7, GPT-6 finding #8. N1 read a double-quoted string that a shell runs
only as that shell's input, so a substitution the OUTER shell runs while it
expands the word was lost whenever the inner shell reads it as data: a quoted
heredoc, single quotes, a comment. bash -c "cat <<'EOF'\n$(curl https://example.org)\nEOF"
went from fetchKind 'fetch' (cf3fb72e) to null with shell_fetch 0. bash 5.2.21 and
dash run the stub curl once: the expanding shell runs each unescaped "$( )" and
backquote before the shell it starts reads anything (POSIX.1-2024 XCU 2.2.3
Double-Quotes, 2.6.3 Command Substitution). Two views now: outer bodies first,
each as commands of their own, then the inner shell's read of the string with
each outer span replaced by a placeholder word (its output is unknown), so a
substitution both views could see counts once. A backslash-escaped \$( ) or
backquote is no outer span and stays the inner shell's (bash -c "echo \$(curl u)"
runs curl once, in the inner shell). Single-quoted run strings have no outer
expansion and are unchanged.

outerSpans() is the one scanner of a double-quoted string's substitutions (escape
pairs skipped, "$((" arithmetic not a span but the substitutions inside found,
NESTING_LIMIT and unterminated spans as before); quotedData() now consumes it, so
the data view and the run view cannot disagree. The ssh remote ranges (marks)
cover the literal text between spans, not the local substitutions.

D7 tests (62cb6935) pass on all three carriers: 13 executed, 5 inner and 2
twice-run commands, 2 data commands; the qmd lane test counts one call.

Evidence, all against GNU bash 5.2.21 and dash with stub curl/wget/ssh (ssh runs
its arguments with sh -c, or reads stdin as the remote login shell), counting
stub calls against the kernel's confirmed count (shell_fetch + loopback):
- compositional oracle, 360 commands (4 double-quoted run carriers x 10 contexts
  x 4 atoms, two-atom strings, single-quoted carriers, heredoc readers x
  delimiters x bodies): base 7091a300 301 exact, 59 under, 0 over; 43b9d947 329,
  31, 0; this commit 353, 7, 0. The 7 left are one class, a shell-read heredoc
  with an unquoted delimiter whose body has a substitution inside single quotes
  (the outer shell expands the body first), next commit.
- random valid-bash fuzz, 40,000 candidates, 7,883 accepted by bash -n and dash -n
  with equal stub-call counts under both (361 with a fetch): base, f83f8367,
  43b9d947 and this commit agree on the same 7,789 (98.81%) and the same 94
  disagreements (12 over: word glue and a trailing backslash; 82 under: remote
  ssh commands and glued words), all present at 7091a300.
- differential of 43b9d947 and this commit over the covering commands and three
  fuzz corpora (90,243 inputs): 13 inputs lose confirmed mass, every one invalid
  bash in at least one shell; of the 24 differing inputs without HTTP-library or
  gh tokens, 21 are invalid, 2 valid inputs move closer to real bash and 1
  (xcat &&bash -c "'$(curl u)'") counts a substitution that a failing && never
  reaches, which no static reading models.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests: an unquoted heredoc body is expanded first

Tests only. D7 for a heredoc body (U1 pivot D7, same defect class as GPT-6 #8):
with an unquoted delimiter the shell that reads the command line expands the
body before the shell that reads the heredoc sees it, so each unescaped
substitution runs in the outer shell whatever its quotes or a comment say
(bash(1) Here Documents; POSIX.1-2024 XCU 2.7.4), quotes are literal, and a
backslash escapes only $ ` \ and a newline. 20 commands, each run under GNU bash
5.2.21 and dash with stub curl and ssh: curl ran once for 17 (a single-quoted
substitution, a comment, nested quotes, a substitution over lines, a <<- body
with tab indents, arithmetic, an ssh reader, a run string in the body, and
controls that already read correctly), twice for one (an outer and an inner
substitution), and never for two (a quoted delimiter passes the body as is).

At 67a4acbb 9 of the 17 fail on all three shell carriers (shell_fetch 0 != 1,
one unconfirmed mention: bash <<EOF with echo '$(curl u)', the FOO="$(pwd)" and
ssh readers, a comment, nested quotes, a multi-line substitution, <<-, and
arithmetic); the rest and the data commands pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Expand an unquoted heredoc body a shell reads before reading it

The heredoc form of D7 (GPT-6 #8: the outer shell runs what it expands before
the inner shell sees the text). With an unquoted delimiter the shell that reads
the command line expands the body first: each unescaped $( ) and backquote runs
there whatever its quotes or a comment say, quotes are literal in a body, and a
backslash escapes only $ ` \ and a newline (bash(1) Here Documents; POSIX.1-2024
XCU 2.7.4). resolveHeredocs() analyzed a body that a shell (or in inlineHttp
mode an interpreter) reads only as that reader's source, so bash <<EOF with
echo '$(curl u)' read as data although real bash and dash run curl once, and
so did a substitution in a comment, nested quotes inside it, one over lines,
arithmetic, and a <<- body: the same 9 shapes as the failing-first commit
b6f3b454. R1 made the FOO="$(pwd)" bash <<EOF form read this way too, so this
also removes the accident that had hidden the limit there.

The body now has two views, sharing D7's outerSpans/withoutSpans/outerBody: the
outer spans first, as commands of their own (heredocs inside them resolved
there), then the reader's source with each span replaced by the placeholder word
and its own escapes applied (heredocText). A quoted delimiter is unchanged
(nothing is expanded), and so is a data heredoc such as cat <<EOF, which keeps
the substitutions its regular expression finds. The ssh remote ranges cover the
body's literal text, not its local substitutions.

The heredoc test of b6f3b454 passes on all three carriers (17 once, 1 twice, 2
data commands); tests.test_token_measurement passes (59). Evidence, real bash
5.2.21 and dash with stub curl/wget/ssh, kernel confirmed count against stub calls:
- compositional oracle, 441 commands (D7's 360 plus escaped, comment and
  nested-quote bodies): base 7091a300 368 exact, 73 under; 67a4acbb 420, 21;
  this commit 441 of 441 exact, 0 under, 0 over.
- grammar fuzz, 6,000 valid nested commands (statements, sequences, substitutions,
  backquotes, single and double quotes, run strings, heredocs with unique
  delimiters) that bash and dash ran with equal stub-call counts (3,049 with a
  fetch, 1,597 with a run string, 1,471 with a heredoc): base 5,788 exact
  (96.47%), 212 under, 0 over; 67a4acbb 5,970 (99.50%), 30 under, 0 over; this
  commit 5,975 (99.58%), 25 under, 0 over. All 25 are one older gap, a run string
  right after a backquote (echo `bash -c "curl u"`), whose text is invisible to
  the raw detector as well; fixed next.
- random valid-bash fuzz, two seeds of 40,000 candidates (7,883 and 7,838 valid,
  361 and 374 with a fetch): base, 67a4acbb and this commit agree on every
  input.
- differential of 67a4acbb and this commit over 90,243 inputs: 924 differ, 20 with
  a lower confirmed count, of which 19 are invalid bash or valid only with a
  heredoc-at-end-of-file warning, and the one clean input (a backquote span that
  holds bash <<EOF) moves from 3 to 0 confirmed where real bash and dash run
  curl 0 times.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests: a run string after an opening backquote

Tests only. RUN_QUOTED anchors a shell, eval or ssh word at the text start, a
blank, ; & | or (, but not at a backquote, so bash -c "curl u" inside `...` read
as double-quoted data. The fetch was lost, and the raw detector, which anchors
curl itself and not the quote, counted no possible fetch either (C=0 U=0). Found
by the grammar fuzz of 2a33ef90: all 25 commands left under-counted there were
this one shape.

8 commands run under GNU bash 5.2.21 and dash with stub curl and wget: the stub
ran once for each (bash -c and sh -c with double or single quotes, eval, an ssh
string, a backquote inside a single-quoted run string, an escaped backquote in a
double-quoted one, and two controls that already read correctly: a blank after the
backquote and $( ) in its place) and never for echo `echo "curl u"` (data).

At 2a33ef90 the 6 backquote-adjacent shapes fail on all three shell carriers
(shell_fetch 0 != 1, no unconfirmed mention: 19 failures with fetchKind).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Anchor a run string at an opening backquote too

RUN_QUOTED and RUN_HTTP_CODE anchored a shell, eval, ssh or interpreter word at the
text start, a blank, ; & | or (, but not at the backquote that opens a `...`
substitution, so bash -c "curl u" inside backquotes read as double-quoted data: the
fetch was lost and no possible fetch was counted either (C=0 U=0). bash(1)
Command Substitution: a `...` body is shell text with its own command positions,
as a $( ) body is; the anchor class now holds the backquote (one character in
each regular expression; no new alternative, so no new backtracking).

The 6 backquote shapes of the failing-first commit (66550644) pass on all three
carriers; tests.test_token_measurement passes (60).

Evidence, real bash 5.2.21 and dash with stub curl/wget/ssh, kernel confirmed count
against stub calls: the 441-command oracle stays 441 exact; the grammar fuzz that
found the gap is now exact on every input, 6,000 of 6,000 (3,049 with a fetch,
1,597 with a run string, 1,471 with a heredoc; 2a33ef90 5,975 exact, 7091a300
5,788, 96.47%) and, on a second seed, 11,999 of 11,999 (base 11,608, 96.74%, 391
under; 0 over throughout). Differential of 2a33ef90 and this commit over 90,243
inputs: 216 differ, 1 with a lower confirmed count, an unterminated string (invalid
bash).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add a pinned, verified tree-sitter-bash loader that fails closed (D1)

U1 pivot D1. The CLI-lane reading is to run on tree-sitter-bash, the grammar
OpenAI Codex uses for the same job (openai/codex rust-v0.157.1 36650394
codex-rs/shell-command/src/bash.rs; tree-sitter-bash = "0.25" at
codex-rs/Cargo.toml:522, both read at the tag). This commit adds the seam and the
gate; the AST reading itself is the next stage.

shell-parser.pin.json pins the install: web-tree-sitter 0.27.0 and
tree-sitter-bash 0.25.1 with their npm integrity values (checked against the
registry's dist.integrity), upstream tags and commits (v0.27.0 6070dbfe =
npm gitHead; v0.25.1 names a06c2e44 while the package was published from
80132668, five minutes earlier, recorded as a note), the sha256 of the six
files the loader reads, and the install command (npm install --prefix <dir>
--ignore-scripts --no-audit --no-fund --save-exact). loadShellParser(dir)
resolves the directory as the argument (the new --shell-parser flag), then
CHILD_USAGE_SHELL_PARSER, then the ecosystem tools directory under HOME, reads
each pinned file once, compares its sha256 and the lockfile's version and
integrity values with the pin, and executes only those bytes: the runtime module
is imported from a private 0600 copy of the hashed bytes and the two wasm files
are handed over as bytes, so the code that was hashed is the code that runs.
Results carry no path: { ok, versions, wasm_sha256 } or { ok: false, reason }
with not_installed (directory, a pinned file or the lockfile missing),
hash_mismatch, load_error (an unreadable file, the pin file, init) and
not_loaded (this process never awaited the loader, so a forgotten await is not
mistaken for a missing install). A failed load leaves no parser, whatever an
earlier call did; a successful one reuses the initialized runtime. withShellTree()
frees every tree in a finally.

Fail closed: commandInvocations() returns null without a parser and measurement
reports cli_lanes { status: parser_unavailable, reason }, keeps the prefix rule for
the rtk proxy carrier (proxy.rule prefix_fallback, invocations and in_ctx_code
null) and never falls back to the text scanners for lane counting; M4 stays text
based. A measured cli_lanes carries status measured and the parser record (versions
and the two wasm sha256 values); aggregates report measured, incomplete (mixed
actors, with the counts), parser_unavailable or not_measured, and sum proxy
counters null-aware. The CLI awaits the loader; a --shell-parser that cannot be
honored exits 2 with the reason (like --rtk-db), any other missing install only
leaves cli_lanes unavailable. The Codex bridge awaits the loader too.

Interim, until the AST reading lands: with a verified parser loaded,
commandInvocations() still runs the scanner-based lane layer of 3cb7c4f6, so every
existing lane fixture reads as before; the gate, the status fields and the seam
(withShellTree) are final.

Tests (62cb6935, 58502c60 first, run failing at 7091a300): the pin's contents;
not_installed for an absent directory, a missing pinned file and a missing
lockfile; hash_mismatch for one flipped byte in the bash wasm, a modified
web-tree-sitter.js that is never imported, a modified package.json, another
integrity value and another version in the lockfile; load_error for an unreadable
file; directory order; --shell-parser; a good install loading again after
failures; unavailable and mixed aggregates; the bridge in both states. The lane
fixtures load the parser first and skip with a message on a host without an install.
tests: node suite 149 passed; tests.test_token_measurement, test_skill_usage and
test_child_usage_suite 175 passed. D9's stress check reports the TypeError the
lane checks threw before the parser was loaded (threw: TypeError x8), which the
old assertion would have passed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first timing tests for quote-heavy scripts (D8)

Tests only. D8 asks the M4 text scanners for linear time; the unclosed "((" input
of GPT-6 #9 is fixed (5e814331), but profiling a realistic 346 KB script (4,000
lines of echo "step N: $(date)" >> log; qmd search "term N" | head) at e5238f39
showed the same quadratic shape from another term: 51% of the time in the
run-string regular expression and 33% in scanQuotes, both from one read of the
whole text built so far per quote (the regex, and the string flattening that
indexing a concatenation rope forces). A 42 KB script takes 47 ms, 84 KB 108 ms,
172 KB 467 ms, 346 KB 2,078 ms and 694 KB 8,236 ms a scan, and the kernel scans
each shell call several times.

Five doubling checks (n = 8000..64000, best of five, at most 2.5x per doubling
plus 5 ms of noise, 64,000 under 1.5 s): a run of quoted words, a run of
double-quoted substitutions in inlineHttp mode, a run of `bash -c "x"` strings,
a run of words with a # inside, and a long script of echo, substitution and pipe
lines through fetchKind. At e5238f39: 61, 230, 902, 5,861 ms; 168, 651, 3,728 ms;
991, 4,486 ms; 9, 32, 120, 459 ms; 89, 392, 1,930 ms (the series end where a run
passes 1.5 s). All five fail.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read only the tail of the built text at each quote (D8: linear scripts)

scanQuotes() tested RUN_QUOTED (and in inlineHttp mode RUN_HTTP_CODE) against the
whole text built so far at every quote. Reading out.s for that flattens the string
concatenation and scans the full prefix each time, so a script with n quoted
words cost n squared. CPU profile of a 346 KB script (4,000 lines of echo "step N:
$(date)" >> log; qmd search "term N" | head) at e5238f39: 51% in the run-string
regular expression, 33% in scanQuotes itself (the flattening), 10% in GC. The
five timing tests of 9da57414 failed on it.

scanQuotes now keeps the end of its output in `tail`, cut back to 512 characters
whenever it passes 1,024, updated at every write (put and putTrace; quotedData
builds into its own buffer first), and reads only that: the # test uses the last
character of the tail, the emptiness test out.p.length. Once a cut has been made
the two detectors run without their start-of-text alternative (a cut is no start
of text). Limit, stated in the code: a run keyword more than 512 characters
before its quote, after a text that has grown past 1,024, is missed and the
string reads as data. Only a single ssh option word that long can put it there
(ssh -oProxyCommand=<1500 characters> host "curl u": fetch before, null after;
100, 400, 520 and 700 characters read the same), since the words between a
shell, eval or ssh and its string are short options and one host.

Timings (best of five, ms), n = 8000, 16000, 32000, 64000: quoted words 61, 230,
902, 5,861 -> 8, 15, 31, 69; double-quoted substitutions (inlineHttp) 168, 651,
3,728 -> 15, 31, 67, 140; run strings 991, 4,486 -> 38, 83, 173, 358; words with
# 9, 32, 120, 459 -> 3, 6, 14, 37; a long echo/substitution/pipe script through
fetchKind 89, 392, 1,930 -> 18, 34, 71, 154. Realistic scripts, executedText: 84
KB 108 -> 37 ms, 346 KB 2,078 -> 100 ms, 694 KB 8,236 -> 205 ms, 1.4 MB 393 ms
(2x per doubling); the node suite runs in 12 s instead of 41 s.

Output is unchanged: executedText (both modes) and fetchKind of e5238f39 and this
commit agree on all 1,084 shell texts of this repository (822 .sh files and fenced
shell blocks of .md files, 1.66 MB, largest 82 KB, 83 over 4,000 characters), on
the 243 covering commands and the three fuzz corpora (90,000 inputs), and on 600
synthetic long scripts (0 differences, 0 throws); the 441-command oracle stays 441
exact, and the 6,000-command grammar fuzz stays 6,000 exact (real bash 5.2.21 and
dash with stub curl).

Also fixed while rewriting the call: the ssh remote offset test listed the
separators before RUN_QUOTED's keyword but not the backquote that 296c4f1d added,
so in echo `ssh host "qmd get a"` the qmd call read as local; it now reads
qmd/qmd remote (fixture added; the same input read local at e5238f39).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Flip one real byte in each pinned file in the loader tests

The loader tests changed the bash grammar wasm by reading it as UTF-8 and
appending a NUL, which rewrites the whole binary: the test passed because the
file differed, not because one byte did. It now reads a Buffer and flips bit 0 of
the middle byte in place (same length, so only the hash can tell), and does the
same for each of the six pinned files in turn: every one gives hash_mismatch,
leaves no parser (commandInvocations returns null, the scanners do not step in) and
runs no unverified code (the modified web-tree-sitter.js sets a global if it is
ever executed; it is not). The missing-file and missing-lockfile cases now also
assert that no parser stays loaded. Node suite: 160 passed (149 before).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add a failing-first test: a run keyword past the tail window

Tests only. The brief's D8 says a work budget "marks the command unclassifiable for
M4 instead of throwing". 1d77c34c bounds scanQuotes to the last 512 characters of
its output, but an ssh option word long enough to push `ssh` out of the window
(in a text past 1,024 characters) drops the run string silently: the fetch in
ssh -oProxyCommand=<1500 characters> host "curl u" is read as data with no
possible fetch counted either (shell_fetch 0, unclassifiable 0, unconfirmed 0),
which a budget must not do.

The test: option words of 100, 400 and 700 characters read as before (the fetch
confirmed, unclassifiable 0); 1500 characters counts one unclassifiable operation
(remote_fetches 1, no confirmed shell fetch); the count is once per command and
only for the command that held the keyword (two later quoted commands add nothing);
a long printf with hundreds of words and quotes but no run keyword adds none.

At 1d77c34c the two 1500-character cases fail on all three shell carriers
(unclassifiable 0 != 1: 6 failures); the 100, 400 and 700 cases and the printf
control pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Count a run string the tail window lost track of as unclassifiable

The brief's D8 fallback for input past a work budget is "marks the command
unclassifiable for M4", not a silent miss. 1d77c34c bounds scanQuotes to the last
512 characters of its output; an ssh option word long enough to push `ssh` out of
the window (in a text past 1,024 characters) dropped the run string with no trace:
ssh -oProxyCommand=<1500 characters> host "curl u" read as shell_fetch 0,
unclassifiable 0, unconfirmed 0.

scanQuotes now notes, outside quotes, when a shell, eval, ssh or su word (and its
blank) has been written since the last command separator (a last-letter pre-check
keeps it off the hot path). When a cut has left the tail without that word and
without a separator, the next quote that does not read as a run string is counted
once in windowMisses, which countFetches adds to unclassifiable (an operation of
unknown kind: remote_fetches 1, no confirmed shell fetch). The keyword is noted at
write time because the discarded text cannot say whether a word was quoted: a first
version that looked for the word in the discarded text also fired on 4 real scripts
of this repository (adoption/bootstrap-linux.sh, bootstrap-macos.sh,
launchd-agents.sh and a memory-stack README block), where separators inside quoted
data are blanked and a word such as sh sat in a long quoted list.

The test of 0b2b5778 passes on all three carriers (100, 400 and 700 characters
read as before; 1500 counts one unclassifiable, once for the command, and adds
nothing for later commands or for a long printf with no run keyword).

The counter is zero on every corpus: complete m4 results of the pre-window kernel
(e5238f39) and this one are identical on the 243 covering commands, 10,000 heredoc
fuzz strings, two 40,000-string token corpora and all 1,084 shell texts of this
repository (822 .sh files and fenced shell blocks, 1.66 MB), 2 tools each; the 600
synthetic long scripts agree; the 441-command oracle is 441 exact and the grammar
fuzz 11,999 of 11,999 exact against real bash and dash. Realistic scripts, executedText:
84 KB 50 ms, 346 KB 125 ms, 694 KB 285 ms (2,078 and 8,236 ms at e5238f39).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Keep 150 ms for the brief input only; loosen the extra timing shapes

The brief's D8 requires at most 2.5x per doubling and under 150 ms at n = 64,000 for
'(('.repeat(n) + 'qmd'. The timing tests added alongside it (a run of $((, the same
"$( ((, a run of ( << E, and the quote-heavy shapes) had inherited that absolute
bound. Three of them run 64-80 ms at 64,000 against 150 ms on this host, thin
headroom for a slower CI runner. Only the brief's input keeps 150 ms (it runs about
30 ms); every other shape keeps the 2.5x ratio (plus 5 ms of timer noise, best of
five) and takes an absolute bound of 1.5 s at 64,000, which is still far below the
quadratic behavior they replaced (the same shapes take 1.9 to 10 s and fail the ratio
at 7091a300).

Run against the base kernel (7091a300) with this test file, all ten timing shapes
fail (for example (( 313, 1,219, 5,109 ms; quoted words 62, 239, 928, 6,211 ms), and
against this head all ten pass (for example (( 3, 7, 17, 32 ms; quoted words
8, 15, 32, 71 ms; run strings 42, 95, 205, 416 ms).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Retry the doubling timing rounds so one pause cannot fail a linear scan

A direct run of test-child-usage.mjs at da2d578d failed one assertion, "a run of unclosed
(( at n = 8000, 16000, 32000, 64000 takes [4, 7, 27, 31] ms": the 32,000 reading was 27 ms
where the scan takes about 15, so the ratio to the 7 ms before it (3.9x) broke the 2.5x
(+5 ms) bound. The same suite had passed inside the Python run seconds earlier. A flaky
timing check is a CI hazard, so the doubling measurement now takes the best of five per
size and, when the ratio still fails, up to two more rounds and the elementwise minimum of
all of them: a pause cannot fail a linear scan, while a quadratic one fails every round.

Checks on the final file: six consecutive runs and two runs beside six busy CPU loops, all
160 passed; against the base kernel 7091a300 all ten timing shapes still fail (the retry
does not hide quadratic behavior).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the AST command-position layer (D10, D11)

The GPT-6 review of the scanner-based lane reading (3cb7c4f6) found ten
command-position defects (#1-#7, #10 here; #8, #9, #11-#13 were repaired
earlier) and the Claude review five more (R4-R7). This commit holds their
probes before any repair, so each one fails for its defect.

- tests/test_token_measurement.py: one test per finding, with the GPT-6
  inputs verbatim, asserting what the finding states (lane calls, proxy
  calls, exclusions, unresolved programs, downstream keys, call states),
  not a display string. Also parse_errors (D2), the closed name vocabulary
  (D5) and mcporter's help/version tokens (R6: checked against
  openclaw/mcporter@93e0916c src/cli.ts:137-145 and flag-utils.ts:34-41,
  the code already matches; the test pins it).
- test-child-usage.mjs: a program name is emitted only for a lane
  executable or a name the reading interprets (GPT-6 #10).
- tests/test_command_position_oracle.py (D10): a deterministic generator
  and a hand-written probe list run under REAL bash 5.2 with env -i, a
  PATH of logging stubs, a temporary HOME and cwd and a 5 s limit; the
  multiset of lanes that ran must equal the lanes commandInvocations reads.
  Against the scanner layer: 38 of 99 probes and 59 of 457 generated
  commands disagree with the run.

Every expected value is the run of real bash and dash under stub
executables (the oracle covers each input), not what this kernel says.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read shell text with the tree-sitter-bash AST for CLI lanes (D2-D4)

Replaces the scanner-based command-position layer (simpleCommands,
resolveInvocation, analyzeScript) with a walk of the tree-sitter-bash
tree, the grammar openai/codex rust-v0.157.1 codex-rs/shell-command
src/bash.rs uses for the same job. The option tables, runner maps and
mcporter grammar are unchanged and now operate on word values.

- D2: every `command` node counts wherever it sits (substitutions inside
  strings, arithmetic, arrays and unquoted heredoc bodies, subshells,
  compound and control statements, function bodies); array elements, case
  patterns, [[ ]] operands, function names and the words of any non-lane
  program never do. ERROR nodes and what is under them are skipped and the
  call counts once in cli_lanes.parse_errors.
- D3: wordValue() returns a literal or null: unquoted escapes, '...', the
  double-quote rule (a backslash goes only before $ ` " \ and newline),
  $'...' decoded as bash does, concatenations; any expansion, glob, brace
  expansion is unknown. A tilde prefix keeps the word known for program
  identity (only the directory changes: ~30 lane invocations by ~/path in
  136,361 real commands) but not as a script.
- D4: bash|sh|dash|zsh|ksh -c reads the first operand after the options
  (-n, -D and -o noexec run nothing; `--`, `-` end the options); a shell
  with no -c reads standard input, so a heredoc or here-string attached to
  the last command of a list, pipeline or negation, through env, timeout,
  nice, nohup, stdbuf, rtk proxy (not xargs, which consumes it), has its
  body read; eval and ssh join their words as the callee does; rtk proxy
  resolves its program with exec semantics (command and exec run nothing,
  time is unresolved, an expansion in its one argument is unresolved).
- Backquotes and outer substitutions of an unquoted heredoc body and of a
  double-quoted script are read from the raw text with the kept M4 helpers
  (tree-sitter-bash leaves backquotes in heredoc_content and mis-reads
  some bodies); a script with an expansion is read with a placeholder.
- The grammar folds the next line into a command in some shapes (an
  unquoted == before ;, &&, | or a newline; a trailing blank before a
  newline; `<<'EOF' 2>&1 | tail`): 732 of 136,361 real commands with no
  error and 163 with one. A separator ERROR or an unescaped newline inside
  one command's words starts a new command.
- simple counts assignment-only statements, declarations and each loop,
  if and case as well as commands (POSIX.1-2024 XCU 2.9.1, 2.9.4), so the
  ambiguity of existing fixtures is unchanged.

Fixtures: EMPTY_CLI gains parse_errors (D2), and `ssh host bash -s <<EOF`
now also reads the remote bash as a command of its own (ssh(1): the words
after the destination run remotely). Every other existing lane fixture
passes unchanged.

Against real bash 5.2: the oracle agrees on 457 of 457 generated commands
(581 lane runs) and 99 of 99 probes. Over 136,361 distinct real commands
the old and new readings agree on 136,167; the differences are old false
positives, unknown wrappers (setsid, bwrap) and grammar defects. Still
failing here by design, fixed next: D5 vocabulary (5 tests) and R7 states
(7 tests); the node suite fails only on the D5 program-name check.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Re-expect the closed name vocabulary in the lane fixtures (D5)

U1 pivot D5 and GPT-6 #10: commandInvocations returns `program` only for
a lane executable or a name this reading interprets itself (a wrapper, a
shell, eval, ssh, rtk), and an mcporter downstream key is emitted only for
the stack's own servers; every other name-shaped string (a host, an id, a
package, `linear`) reads null or (other). The old expectations that showed
`-/echo`, `-/git`, `-/pytest` or `@linear` are wrong under D5, so they are
changed here, before the code: 7 node checks and 3 Python tests fail
against the walker of 1d35df12 with the observed values recorded in the
log (`program` still holds "my-private-host.example", the downstream key
still holds "linear" and "call_PRIVATE").

The selector-form coverage the `linear` cases gave is kept with servers of
the stack (`socraticode`, `serena`, `jcodemunch`, `ai-memory`, `qmd`,
`headroom`, `context-mode`, `codebase-memory`), read by name in every form.
Sources of the closed set: manifests/stack.json:280 (codebase-memory),
:407 (context-mode), :629 and :966 (socraticode); the rest is the
coordinator's list in the pivot brief.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Emit only fixed names: a closed program set and mcporter server set (D5)

GPT-6 #10: `program` carried any name-shaped basename (a host, an id, a
package) and an mcporter downstream key any name that passed a character
class, so the no-id, no-host output contract held only by luck.

- `program` is set only for a lane executable or a name the reading
  interprets itself (the wrappers, the shells, eval, ssh, rtk); every other
  program reads null.
- An mcporter downstream key is one of codebase-memory, context-mode,
  jcodemunch, serena, socraticode, qmd, headroom or ai-memory (the
  stack's servers: manifests/stack.json:280, :407, :629, :966 and the
  coordinator's list), else (other), (http), (stdio) or (unresolved).

The tests of the previous commit now pass (node 162 of 162; the Python
name-vocabulary test and the three re-expected fixtures).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Expect an interrupted counter in lane and downstream rows (R7)

Claude review R7: a Claude Code result whose toolUseResult.interrupted is
true read as succeeded. The command started and was cut short, so it is a
state of its own. The shared fixture helpers (lane_row, downstream_row and
the Codex cli_lane_row and downstream row) now carry `interrupted: 0`; the
lane and downstream rows of the kernel do not have the key yet, so the
tests that compare whole rows fail (see the log of this run) until the next
commit adds the counter and the state.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read not-executed calls where the client records them; add interrupted (R7)

Claude review R7: the marker "The user doesn't want to proceed" was matched
against the row's toolUseResult, where it was never observed; it opens the
result CONTENT. Observed on this host's Claude Code transcripts (122,648
Bash results in 4,355 files, count-only): the rejection content (25 rows,
toolUseResult "User rejected tool use"), a row-level toolDenialKind on
every client denial (permission-rule 750, user-rejected 46, cancelled 1,
automode-unavailable 1), the classifier text "The server-side auto mode
classifier gave no verdict" (its own text says the check failed, not the
action), and 209 rows of a host hook's refusal text ("This agent is
isolated") that carry no toolDenialKind and stay failed (open: its meaning
is the hook's, not the client's).

- callState: not_executed when the row has a toolDenialKind, or the content
  opens with <tool_use_error>, "The user doesn't want to proceed" or the
  classifier text, or toolUseResult names a hook denial, a permission
  denial or a rejection.
- toolUseResult.interrupted === true is its own state, `interrupted`
  (none of 25,506 observed object results, so it is a synthetic fixture),
  with a counter in lane and downstream rows; it no longer reads succeeded.

The R7 test of the first commit and the fixtures of the previous commit
pass (measurement and Codex modules: 188 tests).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Remove the lane-only marks plumbing from the M4 text machinery

The scanner-based lane reading asked executedTrace for `marks` (the raw
range of each ssh body and the offset of each command substitution of a
data heredoc body). The AST walker reads both from the tree and the raw
text, so nothing passes marks any more: the parameter and every branch
that wrote to it (resolveHeredocs, scanQuotes, outerBody, quotedData) and
rawRange are removed. The M4 readings are unchanged: the complete m4
object of measureTranscript (Bash and ctx shell carriers), executedText in
both modes and fetchKind are identical between the kernel before the
walker (f1ed98ac) and this one over 136,361 distinct real commands, the
243 covering commands, and 2 x 40,000 fuzz inputs (0 differences, 0
exceptions).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Test the recovery of commands the grammar joined to the previous one

The walker of 1d35df12 starts a new command at a separator ERROR or an
unescaped newline inside one command's words, because tree-sitter-bash
0.25.1 reads what follows a command as its arguments in these shapes
(measured on this host's transcripts: 732 of 136,361 distinct real
commands with no error and 163 with one): an unquoted == or =~ before ;,
&&, | or a newline, a redirection after a here-document operator followed
by | or ;, and a multi-line shape found in the real corpus. This commit
adds the tests that pin it:

- tests/test_command_position_oracle.py: 12 recovery probes whose expected
  lanes are what REAL bash 5.2 ran (12 of 12 agree; 10 carry a parse
  error, 2 none).
- tests/test_token_measurement.py: the same shapes through
  measureTranscript, with parse_errors counted once for the calls whose
  tree has an ERROR, and two controls that are not boundaries (a
  backslash-newline continuation and a comment).

Mutation control (recovery disabled in a scratch copy of the kernel): all
12 oracle probes fail (real bash ran the lane, the kernel read none or
one fewer, e.g. `echo ==; qmd get a` ran qmd, read []) and 9 of the 12
measurement cases fail with the observed values ({} != {'qmd': 1}).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests: a line that begins with a backslash

Found by the 14,461-command run of the oracle generator over more seeds
against real bash: a line that begins with a backslash (`\ls`, the alias
bypass; `\markitdown a.json`) reaches tree-sitter-bash 0.25.1 as a word that
begins with the newline, so the line joins the command before it, and when
it is the first line of a here-document body the grammar leaves the body
node without it and adds its words to the operator's arguments (which hid
that the owner is a shell). A here-string that precedes such a line was fed
to the last command of the joined node instead of its own.

Five shapes are added to the oracle's recovery probes and to the
measurement recovery test; real bash ran every lane in them. Against the
kernel of 907ac35f the two tests fail with the observed values (recorded in
the log of this run), e.g. `{ timeout 5 repomix` + newline +
`\markitdown --flag; }` read {'repomix': 1} where bash ran both, and the
shell heredoc whose first body line starts with a backslash read {}.

Also hardens the oracle generator: it drops a text bash rejects
(`bash -n`), keeps a function definition and its call in one group,
does not put `!` or a heredoc on the right of a pipe (`x | ! y` is not
POSIX; a reader that ignores its pipe kills the writer with SIGPIPE), and
writes data heredocs to /dev/null.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Read a line that begins with a backslash as a command of its own

Repairs the shapes of the previous commit's tests:

- commandSegments starts a new command at a word that begins on a new
  line (the grammar puts the newline of a `\cmd` line at the start of its
  word) and drops that newline from the word's value.
- A here-document operator's arguments are only those on its own l…
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
…acles, blind cross-family judges, controls) (#521)

* U9 grader stage 1a: failing-first tests for spec, bind, keys, capture

Stage 1a of the U9 frozen-check grader (#381 token E2E): the tests come first, so each
guard and rule has a recorded failing value before tools/token-e2e/frozen_checks.py and
grade.py exist (1b).

Sources: the U9 build contract, sections a3, a4, b2 (R3-R5, R10, R12, R19, R20) and b5 (the
stage-1 list P0, F1-F7, F9, F10, F18, F20, F21, F25a, F26, F32, F33); the binding corrections
(item 2: the T0 re-run is the arm's own command in a temporary copy of the tree, because
`python3 -I -S` cannot import the tests package; item 3: the T14 query time comes from the
first tool_use naming the whole word agentsview, because the parser's closed program set
returns null for it); TOON spec v4.1.1 section 2 (equality); docs/acceptance-evidence-policy.md
"Discriminating controls". Each negative asserts its exact refusal code or rule id, never an
exit status alone: the missing script also exits 2, so the expected E_* line is what fails.

Also here, ahead of the b1 table, because 1b needs them: the stage-1 disarmed-guard mutants
(the F19 subset M1, M2, M3, M6, M10, M18, M19, M21, M22, M23, M24, each asserting the exact
set of tests it flips), the `keys` and `capture` command tests, and the schema and readings
table tests. Fixtures are generated at test time outside every work tree; no UUID-shaped
value or host path is committed.

Run at this parent (only U8 has merged; no grader module exists):
  TMPDIR=<scratch> PYTHONDONTWRITEBYTECODE=1 python3 -B -m unittest -v tests.test_token_e2e_grader
  Python 3.13.15, toon 4.1.1
  Ran 152 tests: FAILED (failures=13, errors=139)
Two tests pass at the parent by design: P0 test_preflight (a precondition; it records that
loadShellParser, shellParserStatus, commandInvocations, callLedger, m14State,
validateIdentityTable and skill_usage.inside_git_work_tree are absent, so fixtures of the
stated shape stand in) and P0 test_isolated_mode_cannot_import_the_tests_package (the control
that motivates correction 2). Every other test fails, with these values (one line per test;
"ModuleNotFoundError frozen_checks" is: ModuleNotFoundError: No module named 'frozen_checks'):

F10_Binding.test_a_path_alone_is_insufficient  ERROR ModuleNotFoundError frozen_checks
F10_Binding.test_inventory_and_line_count  ERROR ModuleNotFoundError frozen_checks
F10_Binding.test_no_sentinel_record  ERROR ModuleNotFoundError frozen_checks
F10_Binding.test_own_sentinel_passes  ERROR ModuleNotFoundError frozen_checks
F10_Binding.test_sibling_value_in_the_child_message  ERROR ModuleNotFoundError frozen_checks
F10_Binding.test_sibling_value_in_the_parent_message  ERROR ModuleNotFoundError frozen_checks
F18_LiteralAnswers.test_file_urls_and_templates_are_literals  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M10_spec_regeneration_off  ERROR ModuleNotFoundError grade
F19_Mutants.test_M18_create_only_off  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M19_work_tree_refusal_off  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M1_order_insensitive_headings  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M21_file_url_expansion  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M22_clause_check_off  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M23_key_drift_on_bytes  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M24_environment_dependent_mismatch_as_fail  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M2_one_equals_true  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M3_exclusions_off  ERROR ModuleNotFoundError frozen_checks
F19_Mutants.test_M6_no_strict  ERROR ModuleNotFoundError frozen_checks
F1_Inventory.test_amendment_one_fixture_counts  ERROR ModuleNotFoundError frozen_checks
F1_Inventory.test_sealed_preregistration_counts  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_a_bogus_reading_is_refused_by_field  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_a_changed_check_text_is_refused_through_the_cli  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_a_missing_grading_block  FAIL refusal line: observed grade.py "can't open file" (exit 2), expected 'E_GRADING_BLOCK field=missing'
F20_GradingBlock.test_a_preregistration_that_differs_from_the_newest_seal  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_a_stale_registry_hash_is_refused  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_an_unknown_block_key_is_refused_by_field  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_missing_amendment_four_heading  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_no_expected_hash_override_flag  FAIL spec --help exit 2, expected 0
F20_GradingBlock.test_the_chosen_reading_is_used_and_the_alternatives_swap  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_the_override_flag_is_rejected_with_a_refusal_code  ERROR ModuleNotFoundError frozen_checks
F20_GradingBlock.test_the_sealed_amendment_three_commit_has_no_block  FAIL refusal line: observed grade.py "can't open file" (exit 2), expected 'E_GRADING_BLOCK field=missing'
F20_GradingBlock.test_the_spec_writes_canonical_private_bytes  ERROR ModuleNotFoundError frozen_checks
F20b_SchemaAndReadings.test_a_fixture_block_validates_and_a_stale_field_names_its_path  ERROR ModuleNotFoundError frozen_checks
F20b_SchemaAndReadings.test_decided_first_then_the_ordered_alternatives  ERROR ModuleNotFoundError frozen_checks
F20b_SchemaAndReadings.test_the_committed_schema_equals_the_derived_one  ERROR ModuleNotFoundError frozen_checks
F20b_SchemaAndReadings.test_the_validator_refuses_a_keyword_it_cannot_check  ERROR ModuleNotFoundError frozen_checks
F21_SpecRegeneration.test_a_hand_edited_spec_is_refused  ERROR ModuleNotFoundError frozen_checks
F21_SpecRegeneration.test_the_unedited_spec_passes_the_regeneration_check  ERROR ModuleNotFoundError frozen_checks
F25a_MemoryKeys.test_an_empty_topic_blocks_launch  ERROR ModuleNotFoundError frozen_checks
F25a_MemoryKeys.test_only_records_before_since_with_the_anchors_are_frozen  ERROR ModuleNotFoundError frozen_checks
F25a_MemoryKeys.test_since_is_the_earliest_window_start  ERROR ModuleNotFoundError frozen_checks
F25a_MemoryKeys.test_the_keys_command_exits_2_with_the_task_and_writes_nothing  ERROR ModuleNotFoundError frozen_checks
F25a_MemoryKeys.test_topics_are_checked_in_sorted_order  ERROR ModuleNotFoundError frozen_checks
F26_Bind.test_a_missing_base_is_refused  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_a_missing_root_value_is_refused  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_a_sentinel_byte_change_is_refused  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_a_symlink_output_counts_as_existing  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_an_existing_output_is_never_overwritten  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_an_input_hash_mismatch_is_refused  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_an_output_inside_a_work_tree_is_refused_before_anything_is_created  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_an_unknown_key_is_refused  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_codex_bindings_are_merged_and_checked  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F26_Bind.test_run_record_args_that_differ_from_the_bind_mark_the_task  ERROR ModuleNotFoundError frozen_checks
F26_Bind.test_the_record_is_private_and_holds_the_token_only_as_a_hash  FAIL bind fixture: grade.py "can't open file" (exit 2), no bindings written
F27a_KeysCommand.test_an_existing_output_is_refused  ERROR ModuleNotFoundError frozen_checks
F27a_KeysCommand.test_keys_are_private_deterministic_and_record_missing_inputs  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_a_failed_download_is_recorded_not_hidden  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_a_window_capture_needs_a_family  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_post_arm_discards_a_changed_tree_and_lists_new_processes  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_post_arm_needs_the_pre_arm_capture  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_post_window_runs_the_acceptance_commands_in_an_exported_tree  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_pre_arm_captures_six_identities_twice_and_inventories_the_exec_checkout  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_the_run_token_must_match_the_bind  ERROR ModuleNotFoundError frozen_checks
F27b_CaptureCommand.test_window_pages_are_captured_with_curl_fail_silent_show_error_location  ERROR ModuleNotFoundError frozen_checks
F2_ExactTextRegistry.test_digit_change_is_unmapped  ERROR ModuleNotFoundError frozen_checks
F2_ExactTextRegistry.test_dropped_task_must_be_listed  ERROR ModuleNotFoundError frozen_checks
F2_ExactTextRegistry.test_registry_has_52_exact_keys_for_40_templates  ERROR ModuleNotFoundError frozen_checks
F2_ExactTextRegistry.test_task_declared_by_no_template_is_refused  ERROR ModuleNotFoundError frozen_checks
F31a_T14QueryTime.test_a_grep_for_the_word_is_not_a_query  ERROR ModuleNotFoundError frozen_checks
F31a_T14QueryTime.test_heredoc_query  ERROR ModuleNotFoundError frozen_checks
F31a_T14QueryTime.test_lookalike_words_do_not_count  ERROR ModuleNotFoundError frozen_checks
F31a_T14QueryTime.test_no_query  ERROR ModuleNotFoundError frozen_checks
F31a_T14QueryTime.test_query_inside_ctx_execute_code  ERROR ModuleNotFoundError frozen_checks
F32_TokenLines.test_the_decided_reading_and_its_alternative  ERROR ModuleNotFoundError frozen_checks
F32_TokenLines.test_the_real_handbook_counts  ERROR ModuleNotFoundError frozen_checks
F32_TokenLines.test_three_counts  ERROR ModuleNotFoundError frozen_checks
F33_PageFacts.test_a_changed_retention_fact_is_drift  ERROR ModuleNotFoundError frozen_checks
F33_PageFacts.test_a_missing_header_token_is_key_missing  ERROR ModuleNotFoundError frozen_checks
F33_PageFacts.test_capture_records_status_bytes_and_hash_from_curl  ERROR ModuleNotFoundError frozen_checks
F33_PageFacts.test_different_bytes_with_the_same_facts_do_not_drift  ERROR ModuleNotFoundError frozen_checks
F33_PageFacts.test_extractors_find_the_frozen_facts  ERROR ModuleNotFoundError frozen_checks
F3_Clauses.test_every_clause_and_literal_is_verbatim  ERROR ModuleNotFoundError frozen_checks
F3_Clauses.test_mutated_clause_is_refused  ERROR ModuleNotFoundError frozen_checks
F3_Clauses.test_mutated_recipe_literal_is_refused  ERROR ModuleNotFoundError frozen_checks
F3_Clauses.test_web_table_first_clauses_are_class_d  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_e1_qmd_wrong_document_fails_t4  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_e1_repomix_answer_fails_t8_at_the_e1_keys  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_fixture_numbers  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_source_keys_at_the_pinned_revision  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_t1_heading_key_and_order_check  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_t3_definition_readings  ERROR ModuleNotFoundError frozen_checks
F4_Keys.test_t6_key_is_the_trust_segment  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_attribute_call_site_is_excluded  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_duplicate_site  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_exact_sites_pass  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_import_comment_and_string_sites_are_excluded  ERROR subtest: ModuleNotFoundError frozen_checks
F5_SymbolSites.test_import_comment_and_string_sites_are_excluded  ERROR subtest: ModuleNotFoundError frozen_checks
F5_SymbolSites.test_import_comment_and_string_sites_are_excluded  ERROR subtest: ModuleNotFoundError frozen_checks
F5_SymbolSites.test_missing_site  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_synthetic_key  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_t11_enclosing_function_readings  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_t5_labelled_imports_and_mentions_are_not_invocations  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_t5_precision_only_alternative  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_unrecognised_sum_is_unparsed_not_failed  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_wrong_events  ERROR ModuleNotFoundError frozen_checks
F5_SymbolSites.test_wrong_value_sum  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_a_found_but_undecodable_candidate_is_a_failure  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_an_evidence_string_is_a_candidate  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_decode_argv_never_carries_no_strict  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_every_candidate_must_equal_the_key  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_json_root_array_passes  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_missing_record  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_no_candidate_is_unknown_unparsed  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_seventeen_point_zero_equals_seventeen  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_single_key_wrapper_is_a_shape_failure_under_the_decided_reading  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_string_for_a_number_is_a_type_failure  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_toon_root_array_passes  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_true_for_one_is_a_type_failure  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_unfenced_payload_is_a_candidate  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_unordered_keys  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_wrapper_with_the_sum_needs_the_extras_reading  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_wrong_count_header_is_a_toon_strict_decode_failure  ERROR ModuleNotFoundError frozen_checks
F6_StrictDecode.test_wrong_latency_sum  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_a_missing_capture_fails  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_a_tree_changed_between_captures_discards_the_post_capture_and_never_reruns  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_all_six_captures_and_the_answer_agree  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_an_older_subject_first  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_an_unverified_sandbox_makes_the_unittest_fields_environment_dependent  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_environment_dependent_skip_count_is_unknown  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_pre_and_post_disagreement_is_a_conflict  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_test_count_mismatch_when_not_environment_dependent  ERROR ModuleNotFoundError frozen_checks
F7_T0.test_words_for_numbers_are_unparsed_until_d_extract_quotes_them  ERROR ModuleNotFoundError frozen_checks
F7b_T0Rerun.test_the_command_imports_the_tests_package_in_the_copy  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_bytes_differ_and_nothing_executes  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_empty_diff_at_the_prepared_path  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_extra_ignored_file  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_extra_untracked_file  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_mutated_fixture_hash_makes_the_key_unknown  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_observed_tree_other_than_the_prepared_path  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_only_before_py_changed_and_equal_to_after_passes  ERROR ModuleNotFoundError frozen_checks
F9_Builder.test_starting_revision_other_than_the_frozen_execution_revision  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t16_payload_and_citation  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t27_acceptance_summary  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t28_review_diff_counts  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t29_scout_inventory  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t2_recovery_digest_and_bytes  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t30_compile_exit  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t32_blind_verdict_id_and_latency  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t33_positive_control_events  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t34_main_output_count  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t35_agent_path_first_and_last  ERROR ModuleNotFoundError frozen_checks
G_TemplateSweep.test_t38_all_ten_structure_checks_are_read_from_the_answer  ERROR ModuleNotFoundError frozen_checks
P0_SiblingPreflight.test_isolated_mode_cannot_import_the_tests_package  ok (precondition)
P0_SiblingPreflight.test_preflight  ok (precondition)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: registry, inventory, grading-block schema, private writes

First implementation step of stage 1b, in tools/token-e2e/frozen_checks.py and
grading-block.schema.json.

What: the 52-key exact-text registry mapping to 40 templates (RV-23), each template's
class set and its verbatim D clauses and recipe literals (E_CLAUSE), the a3 inventory
computed from the preregistration bytes and stamped with their sha256 (E_CHECK_UNMAPPED,
E_TEMPLATE_TASKS), the one readings table that feeds both the schema's enums and the
spec's alternatives (R2-01 gains wrapper_with_extras for the recheck's R4 finding), a
stdlib validator for the schema keyword subset that refuses any other keyword, the pinned
sources (git cat-file batch reads, tracked-file directory reads), the linear text scanners
of grammar g1, and create-only mode 0600 private writes refused inside a work tree.

Sources: repair-u9.design.md a3, a4, b2 R3, R5, R20, R22; JSON Schema 2020-12 validation
vocabulary (the keyword subset); the registry constants were generated from the sealed
bytes at c0966da2 (d41152f4), not typed.

Tests now passing (dev runner over the 857dc393 test file): F1_Inventory (2),
F2_ExactTextRegistry (4), F3_Clauses (4), F20b_SchemaAndReadings (4). Not yet built:
keys, oracles, captures, grade.py.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader tests: fix two fixture defects found while implementing 1b

Both are defects in test data, not in an assertion's intent.

1. F5_SymbolSites built its synthetic repository once per subtest and per test in
   the same directory; the second `git commit` found nothing to commit and the test
   failed with "git commit failed". The key is now built once per test instance.
2. The synthetic enclosing function was named `caller`, which is also the word in
   the prose answer "Direct caller: ...", so the sites-only answer named the
   function by accident and the decided reading could not fail it. The function is
   now `use_registry`, a name no sentence uses.

Verified: F5_SymbolSites 12 tests OK with the implementation of the next commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: freeze keys, strict decode and the pure oracles

Second implementation step of stage 1b, in tools/token-e2e/frozen_checks.py.

What: a key recipe for every graded template, computed from pinned Git content and
recorded as unknown with a reason when a source is absent (never guessed); the T1 heading
and three-way token-line counts, the T3 definition segment, T5/T11/T26 site analysis (Name
calls, attribute calls, imports, comment and string mentions, enclosing functions), the
T6 trust() segment, the T8 name sets with their distractors, the T9 frozen input, the T28
patch counts and the memory and catalog keys. Then strict payload decoding per TOON spec
v4.1.1 section 2 (CLI 4.1.1 `--decode`, never `--no-strict`; JSON-type identity so a
boolean never equals a number; ordered keys unless the reading says otherwise) with the
correction-6 candidates (fences, contiguous line blocks that start like a TOON header or a
JSON array or object, evidence strings, the whole answer; a prose `[1]: url` is not a
payload), and the class A oracles for the templates that need only an answer and a key.

Sources: repair-u9.design.md a4, R3, R4, R5, R13; TOON spec v4.1.1 SPEC.md section 2;
corrections items 5 and 6. Scanners are linear character scanners, not regular
expressions, so an answer cannot stall the grader; an answer is never dereferenced.

Tests now passing: F4_Keys (7), F5_SymbolSites (12), F6_StrictDecode (17), F32_TokenLines
(3), G_TemplateSweep (11), F18_LiteralAnswers (1): 51 tests, OK, in 22.9 s. Not yet built:
T0 and the captures, page facts, memory keys, the T14 query time, the builder and binding
checks, grade.py (spec, bind, keys, capture).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: T0 captures, builder, binding, T14 query time, pages, memory

Third implementation step of stage 1b, in tools/token-e2e/frozen_checks.py.

What: the T0 oracle over six captures per arm, taken before and after the arm, under the
arm's conditions and plain (R10, U9-D11): a wrong or older subject fails, a missing capture
fails, a pre/post disagreement is unknown(capture_conflict), a field that differs between
the two captures (or every field when the Codex sandbox could not be verified, recheck
RV-06) is environment-dependent and an answer that disagrees is unknown(env_mismatch), and
an unparsed count is unknown until a D-extract quote is verbatim in the answer.

Correction 2: the unittest re-run is the arm's own command, `python3 -m unittest
tests.test_host_requests`, with PYTHONDONTWRITEBYTECODE=1, in a temporary copy of the tree
(isolate the tree, not the interpreter): `python3 -I -S` cannot import the tests package
(P0's control shows it), and a post capture is discarded, never re-run, when the tree
changed. Git calls set GIT_OPTIONAL_LOCKS=0 so observing a tree never writes its index.

Also: the T31 builder check (identity, base, porcelain with ignored files, bytes before any
execution, then greeting() in an isolated interpreter), the T36 binding check, correction 3
(the T14 query time is the first tool_use naming the whole word agentsview, in a command,
in ctx_execute code or as the MCP server), page facts from html.parser text with key drift
compared on facts, curl page captures, the R9 memory keys (records created before SINCE
holding the anchors; E_MEMORY_KEY per topic in sorted order), qmd coverage, the Linux
process listing and the exported-tree post-window runs (git archive, no write to the exec
checkout).

Sources: repair-u9.design.md R9, R10, R12, R13, a4; corrections 2 and 3; git-status(1)
porcelain v1; ai-memory 2.4.1 and curl help read on this host.

Tests now passing: F7_T0 (9), F7b_T0Rerun (1), F9_Builder (8), F10_Binding (6),
F31a_T14QueryTime (5), F33_PageFacts (5), F25a_MemoryKeys function tests (3): 37 tests, OK.
Not yet built: grade.py (spec, bind, keys, capture, grade's regeneration check).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader tests: failing-first cover for the loop-2 review findings

Independent review of 857dc393 and 9496089a (a second loop of this agent, read from
committed bytes) found gaps; each is verified here by a test that fails first, or by a
disarmed-guard mutant where the guard already exists. Sources: that review (bytecode
caches, the copy, the plain value, R4 duplicate keys, the real `keys` path, the judge
effort rule relayed by the coordinator: the GPT-6 judge runs at max, never on codex_lane's
default `high`).

Failing at the current code (observed, host paths removed):
- F7_T0.test_bytecode_caches_are_not_a_tree_change: an ignored scripts/__pycache__ changes
  tree_state (status_sha256 193149b9... instead of the empty-status e3b0c442...).
- F7_T0.test_environment_dependent_skip_count_is_unknown (changed): the plain capture's
  value "skipped=0" gives ('pass', set()); expected ('unknown', {'env_mismatch'}). The
  arm's own capture is the key; the plain one only marks which fields are
  environment-dependent.
- F9_Builder.test_bytecode_caches_are_not_extra_changes: ('fail', {'extra_changes'}) for a
  builder tree whose only extra is fixtures/__pycache__/; expected ('pass', set()).
- F7b_T0Rerun.test_a_planted_bytecode_cache_is_never_executed: an unchecked-hash .pyc
  planted in tests/__pycache__ ran; facts {'ran': None, 'status': None, ...} instead of
  {'ran': 2, 'status': 'OK', ...}.
- F2_ExactTextRegistry.test_a_dropped_task_that_is_still_in_the_preregistration_is_refused:
  "expected refusal 'E_TEMPLATE_TASKS reason=dropped_present task=reuse-296-15', nothing
  was refused".
- F20_GradingBlock.test_a_judge_that_is_not_pinned_to_max_effort_is_refused and
  test_malformed_hex_fields_are_refused: no refusal line yet ('' != 'E_GRADING_BLOCK
  field=judges.claude_answers.effort' and '... field=tool.revision'); grade.py is a
  marker stub at this commit.
- F27c_KeysOnRealContent.test_keys_at_the_pinned_revision (new: `keys` over this
  repository's content at the pinned revision, a local clone plus one fixture commit) and
  F27b_CaptureCommand.test_post_arm_discards_a_changed_tree_and_lists_new_processes (now
  starts a uniquely named process, so other sleepers on the host cannot flake it): both
  end in FileNotFoundError for the missing grade.py outputs.

Passing already, and why it is honest to say so:
- F6_StrictDecode.test_duplicate_keys_and_non_finite_constants_are_refused and
  F19_Mutants.test_M25_duplicate_key_refusal_off: the guard exists (16ba45d5); M25 (the
  duplicate-key hook replaced by dict(pairs)) flips exactly that test.
- F6_StrictDecode.test_prose_that_looks_like_a_footnote_is_not_a_payload: the guard was
  written before this test; it covers `[1]: url` and `[2] a note` next to a real payload.
- F7b_T0Rerun.test_the_run_happens_in_a_copy_not_in_the_tree and
  F19_Mutants.test_M26_in_place_run: the copy exists (3c095eb5); M26 (the copy replaced by
  a link back to the tree) flips exactly the marker test, while the other F7b test stays
  green, which is why the old test alone did not prove the copy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: bytecode caches, plain value, judge pin, robustness (review fixes)

Fixes for the loop-2 review, each driven by the failing tests of 709f17e4.

- Bytecode caches (review F-1 and section 6): a `__pycache__` component or a .pyc/.pyo file
  is neither part of the tree state nor an extra change, so a child that ran the unittest no
  longer makes every post capture `tree_changed` and a builder that imported before.py is no
  longer a false FAIL; every other ignored file still counts. The copy never holds bytecode
  caches, the re-run sets a fresh empty PYTHONPYCACHEPREFIX (a planted unchecked-hash .pyc
  runs even under `python3 -I -S`, and -I ignores the environment), and the T31 greeting is
  compiled from the verified retained bytes in an isolated `-I -S -B` interpreter instead of
  importing a file. This narrows the design's `--ignored=matching` and R13's `-I -S` on
  purpose; recorded as a deviation with the review's evidence.
- T0 key (F-3): the arm's own capture is the key; the plain capture only says which fields
  are environment-dependent, so the plain value alone is unknown(env_mismatch).
- Judge route (G-1, the coordinator's rule): the grading-block schema pins effort to max for
  both judges and freezes model, role and agent type as design f2 lists them; a block naming
  `high` is refused with its dotted field.
- Dropped task still present (G-2): E_TEMPLATE_TASKS reason=dropped_present task=<id>.
- Hex fields (G-5): tool.revision 40 hex, tool.sha256.* and registry_sha256 64 hex, refused
  by dotted field after the schema pass.
- Git wrappers (G-4): timeouts, a return-code check and an E_GIT refusal for a malformed
  cat-file stream. normalize() documents that both sides of a comparison must pass through it
  (G-3).

Tests now passing: F7_T0 (11), F7b_T0Rerun (3), F9_Builder (9), F2 (5), F20b (4) and
F19 M26: 32 tests, OK.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader tests: two more fixture defects found while building grade.py

Both are defects in test data, not in an assertion's intent.

1. F25a test_since_is_the_earliest_window_start built its fixture repository without
   recipes/host-request-lane.md, so the T7 key was unknown(key_missing) and its
   memory_records could not be read from the key. The fixture now carries the recipe.
2. F27b test_post_window_runs_the_acceptance_commands_in_an_exported_tree called
   make_bindings a second time into the same output path, which the create-only rule
   correctly refused (E_PATH reason=exists). make_bindings takes an out_name.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: grade.py with spec, bind, keys, capture and the spec check

The command line of the grader, in tools/token-e2e/grade.py, on the kernel of
frozen_checks.py.

- spec: reads the preregistration blob at a 40-hex commit, verifies its sha256 against the
  newest "Amendment N seal" table of the README at that commit (no override flag: argparse
  refuses one as E_ARGS field=usage), requires `## Amendment 4` and the `grading` block,
  validates it (schema, hex fields, registry hash, pinned judge route), builds the inventory
  and the graded task array and writes canonical JSON create-only, mode 0600.
- grade: regenerates the spec from its own commit and refuses a spec whose bytes differ
  (E_SPEC_MISMATCH). The grading itself is the next stage, so a matching spec ends in
  E_STAGE reason=grade_not_built (design deviation: `grade` takes --repo, which the b3 table
  omits and R20's regeneration needs).
- bind: the private launch inputs (per-arm worktree paths and bases, retained inputs with
  their sha256, sentinels with their hashes, both windows, roots, anchors), refusing unknown
  keys, missing values and hash mismatches with E_BIND field=<f>; the run token is kept only
  as a sha256; U10's codex-bindings.json merges by task (a fixture-shaped consumer until U10
  merges); roots gain exec_checkout (R13 inventories it; the design's bind contents omit it).
- keys: every freeze key from pinned Git content; --memory freezes the historical records
  before the EARLIEST SINCE of either window, so a page written between the windows is not
  history for a Codex task (U9-D13); E_MEMORY_KEY refuses before anything is written.
- capture: w-open/w-close curl the four frozen pages and record facts; pre-arm/post-arm take
  the six T0 identities twice per tree (the arm's conditions and plain), inventory the exec
  checkout with the porcelain options, discard a changed tree's post capture, list processes
  started since the pre-arm capture and key T11 clones; post-w runs the acceptance commands
  and py_compile in a git-archive export. RUN_TOKEN must match the bind's hash.

Sources: repair-u9.design.md b3, R10, R12, R13, R19, R20, R22 and the corrections; ai-memory,
qmd, codex and curl native help read on this host.

Tests now passing: the complete tests.test_token_e2e_grader suite, 164 tests, OK, 0 skipped,
in 82 s (Python 3.13.15, toon 4.1.1, git 2.43).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: parse each pinned file once and skip on runners without tools

Two changes that keep the suite cheap and portable, found while checking how CI runs it
(`python3 -m unittest` over everything, on Linux and macOS runners).

- Speed: `keys` for all 75 tasks took about 20 s because every symbol re-walked all 240
  scripts and tests. A file that never spells a symbol has no site for it, so only files
  that do are parsed, once per source object, with the token scan and the enclosing-name
  map built on demand. All 75 keys now take 1.9 s with identical values (the same four
  Name-call sites, 16 mentions and 20 subprocess.run calls); the whole suite went from
  82 s to 24 s.
- Portability: the design says CI may skip the TOON and private tests while the workstation
  acceptance run shows 0 skips. The tests now skip, not fail, when the TOON CLI or node is
  absent or Linux /proc is missing (the process listing), and P0 is split into
  python-interfaces, node-exports and toon-version tests so one missing tool no longer
  hides the others. Verified on a PATH without toon and node: 166 tests OK (skipped=11);
  on this workstation: 166 tests OK, 0 skipped.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 1b: refuse a launch-args file for another arm, and non-http page URLs

Two small hardenings found on a final read of the command line.

- bind: a launch-args file whose own `arm` differs from the ARM= label it is bound to is
  refused (E_BIND field=arm); failing first: "'' != 'E_BIND field=arm'".
- capture_page: a URL that is not http or https is never handed to curl (it could parse as
  an option); the record is {status: None, error: bad_url}. The guard was written before
  its test, so the test passes on first run; it asserts curl was not run at all.

Tests: F26_Bind (11) and F27b_CaptureCommand (9) OK.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the loop-2 oracle and CLI findings

Second independent review round (loop 2, from committed bytes of 16ba45d5, 3c095eb5 and
7defe604): every finding below was reproduced by running the committed code, and each
is covered by a test that fails first. Sources: that review (REVIEW-1b notes H-1..H-5,
J-3..J-6, K-1, K-2) and the design's R3 ("FAIL means demonstrably wrong; an absent key
fact is unparsed") and F15/R21 (a deterministic FAIL sends no packet to the semantic judge).

The rule the tests pin (H-1): a single-valued fact passes only when every recognised value
equals the key; when the values disagree and one equals the key (a hedge such as "9 or 10",
"96 (or 66)", "yes or no", "exit code 1 or exit code 0") it is unknown(unparsed), so the
D-extract fallback may decide and the lower bound never rises; when none equals the key it
fails. An absent key fact (a paraphrase) is unknown(unparsed), not a FAIL, so the judge
still sees it; an absent required citation or literal still fails.

Failing at the current code (observed, host paths removed):
  F20_GradingBlock.test_a_memory_query_that_could_parse_as_an_option_is_refused: '' != 'E_GRADING_BLOCK field=memory.reuse-296-07.query'
  F20_GradingBlock.test_a_page_url_that_could_read_a_local_file_is_refused: '' != 'E_GRADING_BLOCK field=pages.json'
  F26_Bind.test_codex_bindings_are_merged_and_checked: KeyError: 'arm'
  F26_Bind.test_codex_trees_are_matched_by_arm: '/one' != '/two'
  F26_Bind.test_run_record_args_with_another_token_or_arm_conflict_for_every_task: Lists differ: [] != ['reuse-296-00', 'reuse-296-02']
  F27b_CaptureCommand.test_a_structurally_broken_spec_is_an_internal_refusal_not_a_traceback: 'Traceback (most recent call last):' != 'E_INTERNAL stage=capture'
  F27b_CaptureCommand.test_curl_is_restricted_to_http_protocols: ValueError: '--proto' is not in list
  F27b_CaptureCommand.test_post_arm_retains_the_builder_bytes_and_entries: KeyError: 'builders'
  F27b_CaptureCommand.test_the_t0_commands_never_see_the_operator_environment: 'FAILED' != 'OK'
  F6_StrictDecode.test_a_toon_cli_that_is_not_4_1_1_is_refused: 'toon_strict_decode' != 'E_TOOL tool=toon reason=version'
  F7_T0.test_a_hedged_skip_count_is_unparsed: ('pass', set()) != ('unknown', {'unparsed'})
  F7_T0.test_the_first_subject_mentioned_must_be_the_newest: ('pass', set()) != ('fail', {'subject_not_newest'})
  H_OracleReviewFindings.test_a_catalog_answer_with_the_citation_but_paraphrased_facts_is_unparsed: ('fail', {'fact_missing', 'anti_pattern_missing'}) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_a_hedged_payload_sum: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_a_paraphrase_of_the_release_pin_reaches_the_judge_instead_of_failing: ('fail', {'fact_missing'}) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_an_absent_scope_name_is_unparsed_because_the_judge_settles_wording: ('fail', {'scopes_missing'}) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t1_heading_count_is_not_satisfied_by_numbered_heading_lines: ('pass', set()) != ('fail', {'heading_count'})
  H_OracleReviewFindings.test_t1_token_lines: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t27_exit_code: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t28_numbers: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t32_verdict_and_latency: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t34_count: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t35_first_and_last: ('pass', set()) != ('unknown', {'unparsed'})
  H_OracleReviewFindings.test_t8_a_stated_count_must_equal_the_number_of_names: ('pass', set()) != ('fail', {'count'})

Ran 191 tests: failures=21, errors=3 (all of the 24 above). Environment: Python 3.13.15,
toon 4.1.1.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Fix hedged answers, paraphrase FAILs and CLI leaks found in review

Implements the fixes for the loop-2 findings that 73ed708e covers with failing tests
(H-1..H-5, J-3..J-6, K-1, K-2), in tools/token-e2e/frozen_checks.py and grade.py.

Correctness (R3): a single-valued fact now passes only when every recognised value equals
the key. A hedge that contains the key ("9 or 10", "96 (or 66)", "First 64 or 65",
"Verdict: yes or no", "exit code 1 or exit code 0", "Latency sum: 124 or 999") is
unknown(unparsed), so the D-extract fallback may decide and the lower bound never rises;
no equal value fails. Alternatives joined by "or", "/" or "vs" are recognised in either
direction inside one clause. T34 reads a bare integer, else integers beside a count word,
else every integer; T1's heading count reads only the plural label, so "Heading 10:" lines
no longer satisfy it; T8 checks a stated function count; T0 requires that the first subject
the answer names is the newest. An absent key fact (a paraphrase of the release pin, the
catalog facts, a scope name) is unknown(unparsed), not a FAIL, because a deterministic FAIL
sends no packet to the semantic judge; an absent required citation still fails.

Safety: the TOON CLI on PATH must be 4.1.1 before any decode (E_TOOL tool=toon
reason=version) and argv keeps the bare name; curl is restricted to http and https and a
page URL or memory query that could parse as a file scheme or an option is refused at spec
time; the six T0 identities, the unittest that runs the tree's code, the greeting run and
the post-window runs get an allowlisted environment (PATH, LANG, TMPDIR and a HOME that is
not the operator's), so no operator secret nor RUN_TOKEN reaches them; an unexpected
exception is one `E_INTERNAL stage=<command>` line with exit 2 instead of a traceback with a
host path and exit 1 (GRADE_DEBUG=1 adds the traceback after it); binding_conflicts
also compares the run token and the arm; the post-arm capture keeps each builder tree's
porcelain entries and before.py; Codex trees are matched by arm and an ambiguous task is
refused (E_CAPTURE field=codex_tree).

Sources: the loop-2 review notes; repair-u9.design.md R3, R19, R22, F15/R21; TOON spec
v4.1.1; the real ai-memory CLI accepts `--` before a positional query (checked).

Tests: the complete tests.test_token_e2e_grader suite, 194 tests, OK, 0 skipped; three new
disarmed-guard mutants (M27 a hedge that contains the key passes, M28 the version pin off,
M29 the operator environment reaches the commands) each flip exactly their tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add tests for keys --qmd and for spec determinism on real content

Coverage the stage lacked, found while preparing the handoff.

- F25c_QmdCoverage: `keys --qmd` was implemented but untested. A fake qmd 2.8.3 prints
  listing lines that end in qmd://<collection>/<file>; the coverage of adoption/update.md is
  read from them, an unreadable index is recorded as not queried (never guessed), and the
  keys command puts the record into the T4 key. The tests passed on first run because the
  code existed; M30 (coverage never found) flips exactly the two that assert a hit.
- F27c: the spec over this repository's real sealed preregistration bytes (with a fixture
  Amendment 4 grading block, since the sealed bytes have none) is written twice and compared
  byte for byte. The pipeline's acceptance item 3 asks for `grade.py spec` twice with the
  sealed hash "as the expected value"; the design forbids any expected-hash flag (R20, F20)
  and the sealed bytes carry no grading block, so `spec` at c0966da2 refuses with
  E_GRADING_BLOCK field=missing (design acceptance 4); this test is what can be checked.

Tests: 198 tests in the complete tests.test_token_e2e_grader suite, OK, 0 skipped.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader stage 1b: cover deleted files in the pre-arm inventory

Closes the last stage-1 gap found while mapping every review and recheck finding to a
disposition (recheck new defect 13: a tracked file that an earlier arm's child modified or
deleted in the shared exec checkout changes the same keys as a new file does).

The pre-arm exec-checkout inventory already lists untracked and modified entries. The test
now also deletes a tracked file and expects ("D", path) beside ("??", path) and ("M", path).
porcelain_entries runs `git status --porcelain=v1 -z --untracked-files=all --ignored=matching`
(design R13), so the code needed no change and the test passed on its first run: it is a
coverage addition, not a failing-first guard.

Stage 1 of the U9 frozen-check grader is complete at this commit (registry, spec, bind, keys,
capture and the pure oracles; corrections 2 and 3 applied): tests.test_token_e2e_grader ran
198 tests, OK, 0 skipped, at the parent. Stages 2 and 3 (evidence and grading; judges,
controls, export and docs) build on it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the T0 user-site environment

Found in the final read of the capture conditions. The T0 identities (six commands, and the
unittest that runs the tree's tests in a copy) get an allowlisted environment with a
throwaway HOME (review J-5, K-3). That HOME also hides the operator's per-user Python
packages. tests.test_host_requests skips one test unless PyYAML is importable, so on a host
where PyYAML sits in the user site the child (real HOME) reports skipped=0 while both grader
captures (arm conditions and plain, same allowlist) report skipped=1. The skip count is then
not marked environment-dependent, and a correct answer fails T0. Correction 2 says the arm's
own environment; the allowlist must keep the user base visible and nothing else.

On this workstation PyYAML is not installed anywhere, so the two runs already agree here
(49 tests, OK, skipped=1 in both, checked in place versus the allowlisted capture); the
defect needs a host with a user-site package, which the tests build: a fake per-user base
holding one module (u9userpkg), a tree test that skips unless it imports.

Failing at the current code (observed):
  F7b_T0Rerun.test_a_user_site_package_stays_visible_under_the_allowlisted_environment:
    {'ran': 1, 'status': 'OK', 'skipped': 1, ...} != {'ran': 1, 'status': 'OK', 'skipped': 0, ...}
  F27b_CaptureCommand.test_both_t0_conditions_keep_the_operators_user_site_packages:
    the same facts through `capture --phase pre-arm`, arm and plain conditions.
Passing at this parent by design: F19_Mutants.test_M31_user_base_dropped_from_the_allowlist
(the mutant is the current allowlist, so it flips exactly the two tests above and leaves the
operator-environment control green).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader stage 1b: keep the user site visible under the T0 allowlist

Fixes the defect the tests of 9e2453a5 fail on: the allowlisted environment of every grader
subprocess (minimal_env) replaced HOME with a throwaway directory, which hid the operator's
per-user Python packages from the T0 runs. tests.test_host_requests skips one test unless
PyYAML imports, so on a host with PyYAML in the user site the child's skip count differed from
both grader captures, and a correct answer failed T0 without being marked environment-dependent.

minimal_env now also sets PYTHONUSERBASE to the base the operator's own interpreter uses (the
variable when set, else site.getuserbase()). It is a package directory, not a secret; every
other operator variable, RUN_TOKEN included, stays out (F27b's operator-environment test and
mutant M29 still hold). Sources: correction 2 ("the arm's environment"), review J-5 and K-3
(the allowlist), Python site documentation for PYTHONUSERBASE and getuserbase().

Measured before the change on this workstation, where PyYAML is not installed anywhere:
tests.test_host_requests in place with the operator shell gives {ran 49, OK, skipped 1} and the
allowlisted capture gives the same facts, so the recorded T0 keys of this host do not change.

Tests: F7b_T0Rerun, F27b_CaptureCommand and the M29 and M31 mutants, 20 tests, OK; the two
tests of 9e2453a5 now pass and M31 (the old allowlist) flips exactly them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for U9 carriers, attempt classes and G-Q

Stage 2a, group 1. Design repair-u9 R1 (attempt classes), R2 (answer carriers),
R6 (task, blind and control rules), R15 (M8), R16 (G-Q), R17 (G-C denominators),
tests F11-F14 with the D1 matrix (26 cases, mutants MUT-A..E) and the binding
corrections 1 (Agent-tool frame and trailer, observed on 33 of 33 foreground
results of this host's client: a frame line, every body line indented by two
spaces, an agentId line, a usage block) and 4 (a superseded run is classified
by its own transcript's final row; a run that ended with an answer or a
task-caused failure is a graded attempt).

Cross-check outcomes are kept apart (advisor note on the runner log): the
logged response equal, differing (carrier_mismatch) or unavailable (absent or
unparseable, never a mismatch).

Observed at the parent: 52 tests, 118 errors counting subtests, none passing.
Each fails with AttributeError: module 'evidence' has no attribute '<name>'
(workflow_attempts 26, decide_task 57, codex_exec_attempt 11, agent_child_attempt
7, follow_persisted 4, attempt_effect 2, blind_effect 2, g_c_denominators 2,
strip_agent_result 1; g_q and m8_lane fail the same way). There is no
tools/token-e2e/evidence.py at the parent, so the name resolves to the
repository's evidence/ namespace directory and the error is an AttributeError
instead of a ModuleNotFoundError.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for U9 M12, retrieval and memory hits

Stage 2a, group 2. Design repair-u9 R7 (M12: five tests per blind attempt,
join through the U4 ledger row and the U2 run-mode child, strict transcripts
through the kernel bridge, the U4 m12_inputs cross-check, status), R8
(retrieval of each frozen URL by the child itself, in the family window) and
R9 (a historical hit counts only when it resolves to a record frozen before
SINCE); tests F22, F24, F25b. Each contamination case names its decided and
alternative reading (R2-11..R2-14, R2-18).

Sibling shapes are fixtures of the stated ones: U2 callLedger records
({session_id, owner, tool_use_id, tool, server, state, cause, native_status,
background}) and run-mode children (agent_id, label, lanes.measurement.
hook_context.inserted and by_hook), U4 join-ledger rows. measureTranscript is
the one kernel function that exists at this base, so the strict-transcript
test runs the real one through the bridge.

Observed at the parent: 39 tests, 39 errors, none passing, each
AttributeError: module 'evidence' has no attribute '<name>' (m12_attempt 15,
claude_calls 18, codex_calls 2, kernel_hook_context 1, m12_hook_source 1,
m12_cross_check 1, m12_status 1; retrieval_check and memory_check fail the
same way). The name resolves to the repository's evidence/ namespace
directory because tools/token-e2e/evidence.py does not exist yet.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for U9 M7, T14, drift and stage-1 gaps

Stage 2a, group 3. Design repair-u9 R14 (M7: seeded encodes, strict round
trips, ineligible encodes, thresholds), R11 (T14 reported sessions, query
time, owned survivors), R13 (tree drift), R2-17 (T2 recovery), T31 graded from
the post-arm capture, and the binding corrections 5 (toon --stats prints the
document on stdout and two status lines on stderr, both merged by Bash, ctx
and Codex results; the lines are dropped by their documented shape, read from
the installed CLI 4.1.1 source) and 6 (D-extract values join the payload
candidates); tests F28, F31b, F34, F9b, F4b.

Four defects of the stage-1 build are found here and each has its test:
- T27 with a recorded acceptance run raises NameError (`summary` is not a
  name in oracle_T27; it must read key["summary"]).
- capture files are named arm-<ARM>-<phase>, so the Claude and Codex arm B
  collide in one directory; the tests name them arm-<family>-<ARM>-<phase>.
  The seven existing F27b tests that read the old names are updated.
- list_processes records no pid, so a survivor's descendants cannot be found.
- the spec carries no thresholds; R14 reads /thresholds/M7/criteria and M8
  needs the 0.8 rate and the minimum of five attempts, all from the sealed
  bytes.

Observed at the parent: 63 tests, 1 failure and 62 errors, none passing.
- 50 tests: AttributeError: module 'evidence' has no attribute '<name>'
  (apply_tree_drift 10, claude_calls 9, t14_check 9, isolate_toon_document 7,
  m7 7, toon_facts 4, grade_builder_capture 4, builder_observed_tree,
  command_programs, owned_survivors, recovery_check).
- NameError: name 'summary' is not defined (oracle_T27).
- KeyError: 'thresholds' (spec) and KeyError: 'pid' (list_processes).
- The six renamed F27b tests: FileNotFoundError for arm-claude-B-*.json.
- test_arm_captures_are_named_by_family...: AssertionError: 2 != 0 :
  E_PATH reason=exists (the second family's capture is refused).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for U9 identity, grade, regrade and mutants

Stage 2a, group 4. Design repair-u9 R19 (identity: rows from started labels,
launch records and U10's Codex rows, guesswork refused by kind, arm-T rows
accepted as reported-only, the validator's code surfaced as
E_IDENTITY_INVALID), R22 (private side: create-only, work-tree refusal, the
canary over keys, shapes and gathered values), F16 (grade twice then regrade
give identical bytes and call no model), F17 (privacy), correction 9 (M11 for
Codex role children: role_child_state with the four outputs of U13's reference
specification, arm counts) and R2 codex_subagent (child rollout copy, the
seed-binding-5 boundary), plus the stage-2 mutants of F19 (M4, M5, M14-M17,
M18b, M19b, M20, M32 superseded-as-inadmissible, M33 trailer not removed,
M34 whole merged result decoded, M36 drift ignored), each asserting the exact
set of tests it flips.

`grade` needs its inputs after the spec check, so the stage-1 expectation of
E_STAGE reason=grade_not_built is replaced by E_ARGS field=bindings
(test_the_grade_needs_its_inputs_after_the_spec_check); the two F21 tests keep
passing because the regeneration check runs first.

Observed at the parent: 56 tests, 39 failures and 26 errors (subtests counted),
the two F21 tests passing.
- CLI tests: AssertionError: 'E_ARGS field=usage' != 'E_IDENTITY_SOURCE
  kind=<main|agent|team|workflow|run_token|codex_rows>' and != 'E_IDENTITY_INVALID
  code=E_ROW'; AssertionError: 2 != 0 (the grade and regrade commands do not
  exist); 'E_STAGE reason=grade_not_built' != 'E_ARGS field=bindings'.
- AttributeError: module 'evidence' has no attribute 'role_child_state' (8),
  'codex_subagent_attempt' (3), 'assert_no_private', 'm11_summary',
  'parse_role_toml'; the mutant tests fail on the missing attribute they patch.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 2b: attempts, carriers, G-Q, M8, M12, retrieval, memory

evidence.py part 1 and 2 and node_bridge.mjs. Design repair-u9 R1 (attempt
classes from launch-level signals), R2 (workflow_child, codex_exec,
agent_child and transcript carriers; the Agent tool_result frame and trailer
removed structurally, correction 1; persisted output followed only under the
declared roots), R6 (task, blind and control outcomes through attempt_effect
and blind_effect), R15-R17 (M8, G-Q, G-C), R7 (M12: five tests, the join
through the U4 ledger row and the U2 run-mode child, the m12_inputs
cross-check), R8 (retrieval by the child itself, in the window), R9 (a hit
resolves to a frozen record) and R2-17 (T2 recovery evidence).

Superseded runs are classified by their own transcript's final row
(correction 4): a rate limit or a login failure before any model turn is
inadmissible, an answer or a task-caused failure is a completed attempt, a run
that only stopped is an interrupted driver. The runner's logged response is a
three-outcome cross-check (equal, mismatch, unavailable): an unparseable or
missing log is never a mismatch.

node_bridge.mjs imports the kernel as a namespace and reports a name that a
sibling has not merged (U1 loadShellParser, shellParserStatus, commandInvocations;
U2 callLedger, m14State; U4 validateIdentityTable) as unavailable; at this base
only measureTranscript is real, and F22 runs the real one on a strict transcript.

Also fixes one fixture defect of the group-1 tests found here: the wait-notice
subtest built two worlds in one directory.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 2b: M7, T14, drift, identity, grade and regrade

evidence.py parts 3-6, grade.py identity, grade and regrade, and the stage-1
gaps found while building stage 2. Design repair-u9 R14 (M7: the seeded CLI
encode, strict round trips and ineligible encodes; toon --stats merges the
document on stdout with the two status lines on stderr, which are dropped by
their documented shape before the strict decode, correction 5, and a D-extract
payload joins the candidates only with verbatim quotes, correction 6), R11
(T14 reported sessions against the table rows that started before the first
archive query, and an owned-survivor filter: started inside the child's
lifetime and named like a program it ran, or a child of such a process; other
in-lifetime processes are counted apart and never fail the child), R13 (tree
drift: modified, deleted, untracked and ignored entries in a key scope turn a
FAIL into unknown; T31 graded from the post-arm capture, running the key's own
after.py bytes), R19 (identity: rows from journals, launch records and U10's
Codex rows; a local twin of U4's R5 validation that accepts arm-T rows as
reported-only; guesswork refused by kind), R22 (canary over keys, shapes and
gathered values; create-only private writes), R16-R18 and correction 9
(role_child_state).

grade collects one private record per run and evaluates them as a pure
function; regrade reads only the private directory, so the aggregate and
grades.jsonl are byte-identical and no model or launcher is ever called.

Fixes to the stage-1 build, each with a failing-first test in the group-3 and
group-4 commits:
- oracle_T27 read an undefined `summary` when an acceptance run was recorded.
- capture files are named arm-<family>-<ARM>-<phase>: the Claude and Codex
  arm B collided in one directory.
- the spec carries the frozen thresholds; list_processes records the pid;
  Refusal takes its code positionally so a field may be named `code`.
- keys.json is no longer key-sorted: canonical form lost the field order of the
  table records that R2-02 compares (found by the F16 end-to-end run).
The positive control is measured only for its Read rows, never as blind
evidence (its own hook row is what qualifies it).

Two fixture defects of the earlier test commits are fixed here (subTest ids
in failing_with, two rollout copies in one directory).

Full module: 396 tests OK, 0 skipped, on the workstation.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first test for MCP items in the rollout shape (M11)

Observed on this host's real Codex rollouts (120 rollouts, counts only): MCP
calls are recorded as event_msg item_completed items of type McpToolCall with
a server field, and never as function calls named mcp__<server>__<tool>; the
turn_context payload carries model, effort, sandbox_policy and cwd as the M11
grader reads them. role_child_state read only the exec-events spelling
(mcp_tool_call), so a child that called a server outside the parent's set
passed the bindings check.

Also adds two P0 checks: the bridge reports which kernel names exist at this
base, and the real summarizeRun output has the run-mode child shape the grader
consumes (agent_id, label, lanes.measurement.hook_context.inserted and by_hook).

Observed at the parent: test_an_mcp_item_in_the_rollout_shape_is_read_too fails
with (True, []) != (False, ['bindings']).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the recorded fields of the private rows

Design c1: the private row records descriptive fields beside the grade: the
payload bytes (T9, T16-T20), the main-transcript bytes (T34), the conversion
state and bytes with the scope terms it lost (T13) and the T14 reported and
expected counts (RV-30, ledger item). They decide nothing.

Observed at the parent: KeyError: 'recorded' (two tests: the mini-run rows have
no recorded object), and ('pass', {}) != ('pass', {'reported': 1, 'expected': 2})
for the T14 counts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 2b: recorded fields and the McpToolCall rollout item

Makes the two failing-first commits before it pass. role_child_state reads an
MCP call as an item_completed McpToolCall item (the spelling of this client's
rollouts, observed on real ones) as well as the exec-events spelling. The
private rows gain the descriptive recorded object of design c1: payload bytes
(T9, T16-T20), main-transcript bytes (T34), the conversion call's tool, state
and bytes with the scope terms its output lost (T13), and the T14 reported and
expected counts, which t14_check now returns as the detail of its result.

Real-data checks (aggregate scans on this host, counts only, not committed):
- agent_child_attempt over 350 real Agent-tool calls: 36 foreground results all
  pass with the inline carrier (the frame and trailer removed structurally), the
  274 asynchronous launches fail not_inline, 20 children were not found, 19
  ended inadmissible (11 usage_limit, 8 interrupted_driver), 1 api_error.
- workflow_attempts over 934 labels of 120 real Workflow runs: no exception
  (879 carry another workflow's response schema, hence schema_invalid).
- m12_attempt over the 17 real blind transcripts: the attachment kinds before
  the first answer are exactly the frozen allowlist, the 15 Agent-tool ones fail
  the first-prompt and marker tests (the routing marker), the 2 Workflow ones pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the role-child wiring and the judgments path

Correction 9 (M11 for Codex role children) is wired end to end: the role file
at exec_rev, the parent's effective server set from the bindings and the
parent's rollout copy feed role_child_state, and the aggregate publishes the
per-arm counts with their unknowns. `bind` must keep the parent server set of
U10's bindings file (an assumed field: U10's codex-bindings.json is not merged).

Also covers the judgments path that stage 3 will feed: a judgment that holds
passes the D component, one that does not fails it, an unavailable one leaves it
unknown with its reason, and `regrade --judgments` equals a fresh grade with the
same judgments. Two more mutants: M37 (judgments ignored) and M38 (the role
child check never runs).

Observed at the parent: KeyError: 'parent_servers' (bind drops the field), and
the role-child counts come out {A: unknown 1, B: unknown 1} instead of {A: fail 1,
B: pass 1}. The judgments tests already pass (their guards exist); M37 and M38
give the condition-absent evidence once the wiring is in.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 2b: bind keeps the parent's server set for the M11 check

Makes the role-child wiring tests of the previous commit pass: `bind` keeps the
parent_servers list of U10's bindings file (refusing a non-list) so
role_child_state can compare a role child's MCP servers with the parent's
effective set. The field name is an assumption until U10's file merges.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Assemble the canary test's private shapes at run time

scripts/validate.py scans tracked files for private-content shapes and failed on
two literals of the F17 canary test: a UUID-shaped value and a Windows user
path. The test still needs both shapes to prove the shape scans, so it now builds
them from parts, and no committed line has either. validate.py exits 0 again
(status passed, 8083 hashed files).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Add failing-first tests for the second stage-2 review findings

Source: the advisor's second review of stage 2, plus two checks of my own
against U10 (its design section 3.1 and codex_launch.copy_rollouts keep the
rollout copies in attempts/<identity>/rollouts/, not beside them) and the
retained exec streams (command_execution items are observed there, 4 of 4
completed, fields aggregated_output, command, exit_code, id, status, type).
The test fixtures move to the U10 layout; MiniRun.codex_exec now also writes the
thread id of U10's row, command items and an optional rollout copy.

Failing at 28d4604f, first differing value:
- role children: m11_summary KeyError 'not_applicable'; the rollouts under
  rollouts/ are not found, so m11_roles is {} where the child count is expected
- M12: status keyed on the correctness of the answers gives ('fail',
  'blind_task_without_clean_verdict') for five clean wrong verdicts; the sums
  count rows 2 and hook_rows 1 for a superseded run beside its final run
- a call with no ledger record graded as a failure: memory ('fail',
  no_historical_hit) not ('unknown', call_state_unknown); the same for recovery,
  a toon encode ('not_encoded'), a child decode ([]) and a small encode (0, 0)
- M7: a Codex seeded attempt gives (0, 0, 0) not (1, 1, 0); a T9 answer payload
  gives (0, 0, 0) not (0, 1, 1); the wrapper encode is ineligible under both
  R2-01 readings; six natural payloads counted where five carry a payload
- T14: a count with no parsed identity fails count_mismatch not unknown; a Codex
  attempt fails no_archive_query where its rollout copy times the query;
  t14_check has no q_status or survival_observed, t14_facts and
  codex_archive_query_time do not exist
- an exec mcp_tool_call with status completed and a non-null error: 'succeeded'
- identity: a dedicated main session that called the Agent tool once is refused
  (E_IDENTITY_SOURCE kind=main)
- M8: (O, R) = (5, 0) where the join ledger gives (6, 2); regrade loses the ledger
  (FileNotFoundError on join-claude.jsonl)
- bridge validate_identity: (True, False) not (True, None) for an exception the
  validator did not code; validator_state does not exist
- a dotted mcporter call (ai-memory.memory_query) is not read as an ai-memory call

Passing at HEAD as controls: the retained exec item spellings, a Codex command
item encoding like a Claude call, a seeded attempt encoding only the seeded array.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* U9 grader 2b: second review fixes (M7, M12, T14, M8, rollouts)

Design repair-u9 R14 (M7 population), R7 (M12 status), R11 (T14), R15 (M8
opportunities), R2 (rollout copies); the tests are in 27ab1cbe.

- M11: a child with no role is counted not_applicable, not a crash of the grade
- M12: the status counts clean completed attempts per blind task (a wrong but
  clean verdict is a clean verdict; an unknown cleanliness sets the sensitivity
  flag); m12_sums skips superseded runs, as U4 counts one row per identity
- a call with no ledger record is call_state_unknown in the memory, recovery and
  toon checks, never a miss; an exec mcp_tool_call with an error is failed; a
  dotted mcporter call names ai-memory
- M7: toon facts for every completed arm-B attempt of both families; seeded is
  the task's toon-seeded tag; a natural payload is an eligible flat array (>= 5
  records) in the answer or an eligible CLI encode; R2-01 is a stored variant, so
  a wrapper encode…
seathatflowsinourveins added a commit that referenced this pull request Sep 30, 2026
… E2E (call states, M15, ledgers, D7, joins) (#528)

* Add failing-first tests for hook blocks, loaded servers, JSON shapes

Failing-first fixtures for PR-A items 1, 5 and 4 (U2 design sections 1-3,
with the review-u2 findings that cite them), in the shapes a count-only
scan of this host's 2.1.283/2.1.284 transcripts found.

Item 1: inserted stays the row count M12 reads (E2E README M12 row);
new inserted_blocks, blocks_by_event, claimed_by_event, plain-stdout
claims only on UserPromptSubmit, UserPromptExpansion, SessionStart and
PostModelSwitch (code.claude.com/docs/en/hooks, "Exit code 0", fetched
2026-09-29), other hook_* rows, the sibling-row and sibling-child
negatives, the M12 positive-control key, and a fold that keeps an
over-long <Event>:mcp__<server>__<tool> hook name at server level.
Item 5: loaded references per server and actors per server (RUNBOOK
'Loaded, never called'). Item 4: JSON and uniform-tabular counts per
carrier after the ctx_execute(_file) code echo (mksglu/context-mode
v1.0.169 src/server.ts:1511-1527) and the Read cat -n numbers, unknown
counted, never 0 (TOON v4.1.1 packages/toon/README.md:199), and the
section-1 per-actor figures the scope extract lists.

At fe9f511b: by_hook has (other) for the long MCP name, the new keys
are absent (inserted_blocks, loaded, large_json, shell_web: None), the
node check fails 1 of 176; every control passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Count hook blocks and claims by event, loaded servers and JSON shapes

Implements PR-A items 1, 5 and 4 (U2 design sections 1-3) with the
review-u2 findings that cite them; makes 06826ff3's tests pass.

Item 1: hook_context adds inserted_blocks, blocks_by_event,
claimed_by_event, claimed_plain_stdout (only UserPromptSubmit,
UserPromptExpansion, SessionStart and PostModelSwitch, and only stdout
that does not both start with { and end with }: code.claude.com/docs/
en/hooks "Exit code 0"), other_hook_rows and hook_names_folded; an
over-long <Event>:mcp__<server>__<tool> name keeps a validated
<Event>:mcp__<server> key instead of '(other)'. inserted, claimed,
with_marker and by_event keep their #432 meaning. The aggregate adds
actors_with_insertion.

Item 5: measurement.loaded (references per server, called or not);
the aggregate adds loaded, loaded_actors and loaded_not_called_actors.

Item 4: every sizes object counts large_json, large_uniform_keys,
large_uniform_flat and large_shape_unknown. The payload is read after
the carrier's wrapper: the ctx_execute(_file) code echo, rebuilt by the
exported ctxEcho (mksglu/context-mode v1.0.169, 442f1eb6,
src/server.ts:1511-1527), and the Read cat -n line numbers, both with
linear scans; what cannot be read is unknown. uniform_flat follows TOON
v4.1.1 packages/toon/README.md:199. The aggregate adds the section-1
per-actor figures: shell_web_large_results_per_actor,
actors_with_mcp_call, actors_with_non_ctx_mcp_call (per-actor
mcp_attempted) and actors_with_ctx_results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for advisor iterations and cache tier fields

Failing-first fixtures for binding decision B9 (per-child usage reads
usage.iterations[]: advisor_iterations apart, totals including them,
usage.complete false when iterations cannot be read) and U2 correction
1 (cache_creation_5m/1h, speed, service_tier, inference_geo per message
and per advisor iteration, null when absent, never 0).

Shapes follow a count-only scan of this host's transcripts and the beta
Messages API reference (platform.claude.com/docs/en/api/beta/messages/
create, BetaIterationsUsage), the advisor tool doc ("Usage and
billing") and the refusals-and-fallback doc ("Billing and rate limits"),
fetched 2026-09-29. Cases: tiers both nonzero, combined counter only
(split null), fast speed, us geo, an advisor iteration with its own
model and split, a multi-iteration row whose top-level cache_creation
object is the first iteration's split (3,706 of 3,706 such rows here),
streamed rows, adjacent windows, an empty list, and the reasons usage
is incomplete: top level unequal to the message entries, fallback,
compaction, a non-object or non-numeric entry, a successful advisor
result without its entry, an advisor call without a result, an advisor
entry without a model. Run-mode control: the run stays complete, exit 0.

At 0eb8771c: the new fields are absent, every unreadable case reads
complete true, the aggregate reads (True, 503) for (False, 503); node
177/179 with the control passing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read usage iterations: advisor apart, tier fields, split per entry

Implements binding decision B9 and U2 correction 1 in transcriptUsage
(measurement.usage: sweep actors[] and run-mode children[] and
superseded_attempts[] lanes.measurement); makes 2bf419de's tests pass.

usage.iterations[] (beta Messages API BetaIterationsUsage; advisor tool
doc "Usage and billing"; refusals-and-fallback "Billing and rate
limits"; fetched 2026-09-29): message entries are the executor's and
sum to the top-level counters; advisor_message entries are listed in
advisor_iterations (message_ordinal, own model, counters, 5m/1h split,
tier fields null) and counted in the window of the first row carrying
them; advisor_totals and totals_including_advisor sit beside the
unchanged executor totals. Each message adds cache_creation_5m/1h and
speed, service_tier, inference_geo (null when absent). The split is the
message entries' own splits added up, because on multi-iteration rows
the top-level cache_creation object holds the first iteration's split
(3,706 of 3,706 rows on this host); the top-level object only when a
row carries no entries.

usage.complete is false, with iteration_issues naming the reason, for
an unread entry (fallback_message, compaction, other types, non-object
or non-numeric), top-level counters unequal to the message entries, a
successful advisor result without its entry, an advisor call without a
result, or an advisor entry without a model. The aggregate sums them.
Run-mode exit codes are unchanged. On this host's 4,667 transcripts
with messages: 143 become incomplete (105 advisor results without
usage, 48 calls without a result, 1 fallback, 1 inconsistent); 2,070
advisor iterations; message splits equal the combined counter on
201,150 of 201,151 known.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Quote the hooks reference and the M12 row exactly in kernel comments

Comment-only: a quote check against the fetched bytes (sources.json in
evidence/artifacts/pra-u2-differential-20260929) found two kernel
comments whose quotation marks enclosed text with the source's markup
removed. The hooks reference sentence now keeps its backticks, the E2E
README M12 row is paraphrased without quotation marks, and the Read
count names the text results it counts (133 more hold a non-text
block). No code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add U2 host scans, upstream sources and differential allow-list

evidence/artifacts/pra-u2-differential-20260929 (new): the count-only
scans that verified B9's usage.iterations structure, the hook row shapes
of item 1 and the large-result shapes of item 4 on this host's
transcripts (producers and their outputs verbatim: key names, enum
values and counts only, no ids, paths or text), the two checks that run
the committed kernel's transcriptUsage over the same store, sources.json
(every fetched upstream document: URL, fetch time, sha256, the exact
sentence each rule rests on, verified against the fetched bytes), and
expected-changes.json, the allow-list the later differential stage
consumes (review-u2 medium finding): the paths this stage adds, and the
two pre-existing paths it changes on purpose, by_hook['(other)'] (the
MCP hook-name fold) and usage.complete (B9: 143 of 4,667 transcripts
with messages here). Registration in manifests/evidence.json follows in
the unit's last commit (docs/lanes.md hot-file protocol).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Name the documented source of the Read line numbers

The Read cat -n strip of item 4 had no named source (final advisor
review). code.claude.com/docs/en/tools-reference ("Read tool
behavior", fetched 2026-09-29, sha256 cc0eda98) states that Read
"returns the contents with line numbers"; the exact form (spaces,
digits and a tab before every line) stays marked as observed client
behavior, 11,467 of 11,467 text Read results over 5,120 B on this host.
sources.json gains the entry; the kernel comment cites it and notes
that a PARTIAL view notice, which carries no number, reads as unknown.
Comment and evidence only; no code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin the advisor usage output as ID-free in a sweep control

Must-stay control, added after the implementation and not failing-first:
a lanes sweep over a child transcript whose message carries an advisor
call reads the advisor iteration (actor advisor_iterations 1, aggregate
advisor_iteration_count 1) and prints neither the message id nor the
advisor server-tool call id nor the directory (the unit rule: never
emit call ids in a published field). Mutation check: a scratch kernel
that adds the message's advisor call ids to each advisor record prints
the call id once in the same sweep; this kernel prints it 0 times.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for callState templates, causes and accessor

U2 item 7, the explicit callState extension the review's dependency
finding asks for (review-u2 medium: U1's rule lacks templates the M14
fixtures need, and exports no per-call not_executed flag).

What U1 already covers, from a count-only scan of this host's
transcripts (scans/call-states.py, 5,153 files): every observed row
that did not run carries a toolDenialKind, a <tool_use_error> content
or the user-rejection content. The user template is fully read. The
config text "Permission to use ... has been denied." (8 rows) and the
cancelled text "Not run: the response that made this tool call ..."
(1 row) are read only through their denial kind; the interrupt marker
"[Request interrupted by user for tool use]" is a user text block after
a user-rejection result (37 of 37), never a result.

The fixtures without a denial kind fail at ebcca292: cli_lanes qmd
not_executed 0 != 1 for the config text (content, and toolUseResult
only), the Not run text, the marker as a result (the design's
constant), the rejection only in the toolUseResult and "Permission
for" only in the content; and cu.callState is not a function. Cases
labelled (control) read not-executed at the base already.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Read not-executed templates in both fields and name each cause

U2 item 7, the callState extension (review-u2 medium, U1 dependency).
callState and a notExecuted accessor are exported; every template is
read at the start of the result content and of the toolUseResult
string (after "Error: "); the config text "Permission to use <tool>
... has been denied", the client's "Not run: the response that made
this tool call" and the design's constant "[Request interrupted by
user for tool use]" join the list.

A call that did not run gets its cause in the vocabulary of the OTel
tool_decision event (code.claude.com/docs/en/monitoring-usage, "Tool
decision event" and "Tool result event"): a template's cause unless it
is 'other', then the toolDenialKind, then the template's 'other', then
a native declined state. Hook denials carry permission-rule (809 of
809 here), so the text decides first; the built-in safety check's
"Permission for this command was denied" carries permission-rule too
(11 of 11), an automatic decision the doc's "config" source names.

scans/not-executed-delta.mjs compares U1's rule, copied verbatim from
ebcca292, with the new one over this host's 228,531 calls: 1,228 did
not run under both and 0 differ. cli_lanes.failed keeps U1's meaning
(is_error; not_executed a subset): tests/test_skill_usage.py:970-974,
outside this unit's paths, pins it for the Codex declined fixtures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for M14 call states, the ledger and M15

U2 design sections 4-5 (PR-A item 7 and protocol M15) with the review's
corrections, as failing-first fixtures.

M14 (E2E README M14 row; preregistration thresholds.M14 states): 20
calls project callState onto the sealed names. A call with a transcript
result is executed, rejected (by source, OTel tool_decision vocabulary,
code.claude.com/docs/en/monitoring-usage), invalid or
cancelled_with_result, never "no result"; cancelled_or_unfinished is
only the calls with no result; decided is null; mcp_states and
calls_without_result keep their published meaning (review mediums).

Ledger (AA:319): callLedger and --call-ledger in both modes, create-only
with mode 0600, refused inside any git work tree, on an existing file or
a missing directory; stdout stays free of call, session and agent ids,
and run-mode stdout of call and session ids (review lows).

M15 (E2E README M15 row; preregistration thresholds.M15): classes in
context-mode v1.0.169's exact shapes, echo-free templates tested before
the echo (review high), the version notice stripped, the client's MCP
texts, the Codex approval denial (rust-v0.157.1 mcp_tool_call.rs:1612),
and rates under binding decision B2.

At 486da6ac all 8 fail: call_states None, m15 None, aggregates
undefined, cu.callLedger is not a function, and --call-ledger an
unknown option (exit 2), including the refusal test's accepted control.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Project call states onto the sealed M14 names per actor and server

PR-A item 7 (U2 design 4.2-4.4, 4.7 with the review's corrections):
measureTranscript adds call_states, aggregateMeasurements sums it, and
m14State(call, result) is exported. One callState per call feeds both
call_states and cli_lanes, so there is one classifier.

Names follow the E2E README M14 row and preregistration.json
thresholds.M14.criteria.states. A call with a transcript result is
executed (succeeded, failed, interrupted), rejected (by source: config,
hook, user, other, declined, the OTel tool_decision vocabulary of
code.claude.com/docs/en/monitoring-usage), invalid or
cancelled_with_result, never "no result" (review medium);
cancelled_or_unfinished is only the calls with no result; decided is
null. mcp_states keeps the #432 rule and calls_without_result its
published meaning (review medium): test_skill_usage.py:828-829 and
:936-939 pin both, and deriving mcp_states from callState would move a
declined call without a result from unfinished to failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Classify MCP errors by M15 class per server with B2 rate bounds

Protocol M15 (E2E README M15 row; preregistration.json thresholds.M15):
measureTranscript adds m15.by_server, aggregateMeasurements sums it and
computes the rates again, and mcpErrorClass is exported.

Order (review-u2 high finding): the anchored templates context-mode
v1.0.169 returns before any code echo (boundary server.ts:1196-1201,
returned at :2118-2119; the deny firewall; Runtime/Batch/Index/Search
errors; storage and usage errors; batch timeout; fetch failures; search
throttling) are tested on the raw text; the echo is required and
stripped only for ctx_execute(_file) outputs after execution (timeouts,
exit-classify.ts exits, module errors, indexed exits under both labels:
review lows). The outdated-version notice (server.ts:892-896) is
stripped first. The client's MCP texts are read on every server: not
connected, the documented idle timeout (code.claude.com/docs/en/mcp),
and the MCP SDK 1.30.1 "Connection closed" and -32602 validation error;
the Codex approval denial is rust-v0.157.1 mcp_tool_call.rs:1612.

Rates follow binding decisions B1-B2: rate counts the six classes,
every new class (a new class is our misuse until reproduced) and every
unknown class or outcome; rate_lower_bound counts unknowns as
successes; threshold_sensitive flags bounds on both sides of 0.01;
every_error_classified replaces the design's status (B1: no new
status); rate_upper_bound keeps the design's ceiling, descriptive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Write the private per-call ledger behind --call-ledger

PR-A item 7's ledger (AA:319, the per-child list of tool_use_ids with
each call's state; U2 design 4.5 with the review's corrections).

callLedger(transcript, {window}) returns one record per attempted call:
session_id, owner, tool_use_id, tool, server, M14 state and cause,
native_status, background, sandbox, code_mode and the M15 class, keyed
by (session_id, tool_use_id) as the E2E README M14 row requires. owner
is the row's agentId, 'main' with a sessionId alone, and null when the
row has neither (a Codex bridge row: U3's correction 1 supplies
conversation.id and the owner kind through the bridge).

--call-ledger PATH works in both modes: the sweep adds the published
actor ordinal, run mode the journal label and whether the attempt was
superseded. The path is checked before any work: an existing directory
outside every git work tree (skill_usage.py write_out's precedent,
widened), and a new file, created with 'wx' and mode 0600 (review low:
an existing file's mode is never taken for 0600). Refusals and failed
writes exit 2 with no report. Stdout is unchanged by the flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add item 7 and M15 host scans, sources and differential entries

Evidence for U2 stage 2 (count-only scans of this host's Claude Code
transcripts; no id, path, command or result text in any output).

- scans/call-states.py now also counts the client's MCP texts (16 not
  connected, 4 idle timeouts, 1 Connection closed, 1 -32602 error), the
  built-in "Permission for" and "Not run" templates, and where the
  interrupt marker sits (37 of 37 user text blocks after a
  user-rejection result).
- scans/kernel-m14-m15.mjs runs the kernel over every transcript:
  0 call_states invariant violations over 4,349 actors, and
  calls_without_result equal to cancelled_or_unfinished for each. The
  context-mode server's 34,427 calls classify completely (0 unmatched,
  0 echo_mismatch), rate 0.0028 and ceiling 0.0044. Servers outside the
  stack's vocabulary fold into (other), as U1's closed vocabulary does.
- sources.json pins the documents and code the M14 and M15 rules rest
  on, each quote checked verbatim against the fetched bytes: the
  monitoring, MCP and env-vars docs; context-mode v1.0.169 server.ts
  lines, exit-classify.ts and session/db.ts (byte-identical to the
  installed copy); codex rust-v0.157.1 mcp_tool_call.rs; and the MCP
  SDK 1.30.1 files, equal to the registry tarball (its sha512 is the
  npm integrity).
- expected-changes.json lists stage 2's added paths and the one
  pre-existing path that can change (cli_lanes not_executed; 0 calls
  here), with what stays unchanged by design and why.

The kernel and test comments now cite the retained scan's counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add a failing-first test for the M15 threshold at rounding precision

Every kernel share is rounded to four places, so a server just over
the M15 threshold (preregistration.json thresholds.M15, at most 0.01)
can print as 0.01: 202 of 20,100 calls is 1.005%. The B2 flag
threshold_sensitive must therefore compare counts, not rounded rates.

At 7810ed70 the first case fails: 190 infrastructure errors and 12
unknowns of 20,100 calls give (rate, rate_lower_bound,
threshold_sensitive) = (0.01, 0.0095, False) where True is right. The
two controls (exactly 1%, and both bounds over) pass at the base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Compare the M15 threshold with counts, not the rounded rate

threshold_sensitive (binding decision B2) now tests errors * 100 >
attempted for each bound: preregistration.json thresholds.M15 allows
at most one infrastructure-class error in 100 calls, and a share is
rounded to four places, so 202 of 20,100 calls (1.005%) printed as
0.01 and read as within the threshold. rate, rate_lower_bound and
rate_upper_bound stay rounded like every kernel share; a grader
compares the published counts (infrastructure_errors,
new_class_errors, unknowns, attempted), never the rounded rate.

scans/kernel-m14-m15.json (run with the kernel before this change)
keeps its values: recomputed from its counts, every server's flag is
the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for item 6, RTK M1 fields and B8 parts

What: fixtures for PR-A item 6 (empty and wait-notice returns,
background tasks without a terminal notification at the final message,
final_return per actor and in aggregates, run-level exit 1), the rtk-claude
M1 numerator (rtk_parts.eligible_call_states and
observed_covered_succeeded_calls), and binding decision B8 (heredoc and
arithmetic calls parsed, unknown calls counted once and left out of the
eligible-part denominators, unknown_call_share and the 5% status rule).

Why: U2 design sections 6 and 7 and binding decisions B5 and B8. The
background-task sources (Bash backgroundTaskId, Monitor and Workflow taskId,
async Agent agentId), the terminal <status> set and TaskStop come from
count-only scans of this host's transcripts; code.claude.com/docs/en/
tools-reference ("Background commands") says a foreground subagent's
background command stops at its final response. rtk-ai/rtk v0.50.0
(tag 1d87b8e7) src/discover/registry.rs rewrite_command_precompiled
returns None for any heredoc or $(( command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Mark empty, wait-notice and pending-background returns incomplete

What: PR-A item 6. returnQuality reads a child's journal result (a
string, the E2E { answer, evidence } schema by its answer, or any other
value by its leaves); an empty or wait-notice return is an issue, so the
child and its run are incomplete and the CLI exits 1. finalReturn gives
every measurement a final_return (the final assistant row's kind, whether
its text is empty or a wait notice, and the background tasks still
running at that row); a child that left one running is incomplete too.
Aggregates count actors by final_return status and final kind.

Why: #381 AA-PLAN item 6 and U2 design section 6, with three evidenced
departures. waitNotice is the design's rule as a linear word scanner (no
backtracking pattern; CodeQL js/redos has flagged this kernel), plus the
typographic apostrophe. A value with no string at all is not a wait
notice (the design's leaf rule made { ok: true } one). Background tasks
come from Bash backgroundTaskId, Monitor and Workflow taskId and async
Agent agentId, and end only at a <status> of completed, failed, killed
or stopped or at a TaskStop that names them: count-only scans of this
host found Monitor events without a status and no notification after a
TaskStop of a Bash, Monitor or Workflow task (88 of 88). The client
stops a foreground subagent's background command at its final response
(code.claude.com/docs/en/tools-reference, "Background commands").
final_return is not_applicable without provider message ids (the Codex
bridge's rows) and unobserved past the window end (the review's finding
on the sweep).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Parse heredoc and arithmetic calls into RTK parts; add M1 success

What: binding decision B8 and the rtk-claude M1 fields. shellParts reads
the command unit by unit with U1's frame machine (step(): quotes,
escapes, comments, $(( )) and (( )), "$( )" and backquote frames, and
the redirection forms of & and |), treats here-document bodies as data
(any delimiter word, heredocWord), and returns null only for text it
cannot read. rtk_parts counts an unclassifiable call once in
unknown_calls and adds none of its parts to the eligible, ineligible,
observed or replayed counters; explicit-rtk, log/find and proxy counters
read the command's own parts, so M-R3/M6c's zero counter keeps a
violation in a call M-R1 cannot classify. New fields: unknown_call_share,
eligible_call_states (M14 states of calls with an eligible part) and
observed_covered_succeeded_calls (the per-child-task M1 numerator,
B5). status is incomplete only when unknown calls exceed 5% of calls,
on the counts, and an aggregate applies that rule to its sums.

Why: B5, B8, U2 design 7.3. bash(1) Here Documents and DEFINITIONS;
POSIX.1-2024 XCU 2.7.4. rtk-ai/rtk v0.50.0 (tag 1d87b8e7)
src/discover/registry.rs rewrite_command_precompiled returns None for
any heredoc or $(( command, so the eligible parts of such calls, unknown
before, now count as observed uncovered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Begin a heredoc body at a newline of its operator's frame depth

What: shellParts reads the bodies a newline begins only for the
here-documents whose operators sit at that newline's frame depth or
deeper, so a newline inside "$( )" no longer begins an outer body, and a
here-document a substitution leaves open is read after the enclosing
line. At most 64 here-documents wait at once (a command with more is
unknown), which keeps the scan linear. Two fixtures pin the two cases,
and two timing shapes join the linear-time checks.

Why: run on 2026-09-29 under GNU bash 5.2.21 and dash, the outer body of
"cat <<'EOF' && x=$(echo a<newline>echo b)" is the line after the
substitution (the previous rule returned null, an unknown call), and
"x=$(cat <<EOF)" takes its body from the lines after its line (bash
warns "command substitution: 1 unterminated here-document"). bash(1)
Here Documents; POSIX.1-2024 XCU 2.7.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document the U2 measurement fields and extend the sweep limits

What: the workflows README gains a "U2 measurement fields" section
(hook_context rows, blocks and claims; loaded references and actors;
large-result shapes and the section-1 figures; the usage tier split and
advisor iterations; the M14 call_states table; the private --call-ledger
contract; the M15 class table and its bounds; item 6's return_quality and
final_return), the B8 part rule and the rtk-claude M1 fields in the
--rtk-check paragraph, the new exit-1 conditions, the ledger in the sweep
example and the unobserved final return of an actor past the window end.
The skill-usage README says which U2 fields Codex actors carry and which
parts belong to the Codex adapter. The sweep's limits string gains the
same U2 limits.

Why: U2 design section 11 and review-u2 (the run-mode stdout contract,
final_return not applicable on Codex actors, the Codex ledger owner);
every rule cites its source beside it in child-usage.mjs and in
evidence/artifacts/pra-u2-differential-20260929/sources.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate the workflows SHA256SUMS after the final kernel edit

What: the checksums of child-usage.mjs and test-child-usage.mjs, the two
files U2 changed in this directory; the other twelve are unchanged.

Why: U2 design section 10, step 6 (sha256sum -- *.mjs *.js *.json >
SHA256SUMS, then sha256sum --check --strict SHA256SUMS, which exits 0);
the byte-identity check in this README reads the file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add the U2 differential, B8 property check and item 6 scans

What: differential.mjs (both kernels as their own CLIs, every differing
leaf path classified against expected-changes.json, which gains stage 3
and machine-readable paths), combine-chunks.mjs, the sweep differentials
over W_AA without and with --rtk-check and the run-mode differential over
232 runs (0 unexpected paths in each), shell-parts-property.mjs (2,000
generated commands bash accepts, all split as built), parts-compare.mjs
(the two splitters over W_AA's 30,673 Bash calls with bash -n on every
disagreement), waa-invariants.py (the design's real-window invariants)
and the count-only item 6 scans (background-task sources, notification
statuses, TaskStop, final rows). The README gives the figures.

Why: U2 design 10 steps 3 to 5 and review-u2's differential finding;
binding decisions B7 (W_AA), B8 and B5. Counts only: no ids, paths,
host names or command text; the sweep ran on a frozen copy of the
window's transcripts, deleted after the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Cite the Monitor tool's own stop rule in the item 6 comment

What: the comment above finalReturn quoted the "Background commands"
rule for monitors too; it now quotes the Monitor tool section's own
sentence (the monitors of a stopped subagent "stop with it") and says
what both mean for a return: it did not wait for the task's outcome. The
workflows SHA256SUMS is regenerated after this final kernel edit
(sha256sum --check --strict exits 0). A comment only: no output changes.

Why: code.claude.com/docs/en/tools-reference, "Monitor tool" (the
fetched copy's sha256 is the one in sources.json); a citation must say
what its source says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add stage-3 sources and fold host server names in the invariants

What: sources.json gains the four documents stage 3's rules cite: the
tools reference's "Background commands" and "Monitor tool" sections (the
same fetched bytes as its Read entry), rtk-ai/rtk v0.50.0
src/discover/registry.rs (tag commit 1d87b8e7: has_heredoc and
rewrite_command_precompiled), POSIX.1-2024 XCU 2.7.4 and the installed
GNU bash 5.2.21 manual's Here Documents, each with sha256 and the
sentences a rule rests on. waa-invariants.py folds server names outside
the stack vocabulary into (other), as scans/kernel-m14-m15.mjs does, and
waa-invariants.json is regenerated from a fresh sweep of the same window
(every other figure unchanged; the copy of the window's transcripts was
deleted after the run).

Why: the evidence README says sources.json holds every source a U2 rule
cites, and published evidence names only the stack's servers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Register the U2 files in manifests/evidence.json

What: the new hashes of the six listed files U2 changed (child-usage.mjs,
test-child-usage.mjs, the workflows SHA256SUMS and README,
tests/test_token_measurement.py, tools/skill-usage/README.md) and the 44
files of evidence/artifacts/pra-u2-differential-20260929, added with
scripts/host_receipts.py register_file. component_matrix.py --write and
new_host_grand_list.py --write change nothing else.

Why: the hot-file protocol (docs/lanes.md): every shared hot-file edit in
the branch's last commit; python3 scripts/validate.py exits 0
(hashed_files 8220, status passed). At merge, take main's manifest and
re-register these files as the protocol says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for grading M15 on the ceiling

What: the review's high finding as tests. A server whose every call
fails with the client's "No such tool available" <tool_use_error> (an
invalid call) must read over the 0.01 threshold; each class outside the
named groups (policy_deny, remote_fetch, search_throttle, rejected,
invalid, cancelled_with_result, and a class no list names) alone at 2 of
100 calls is over; the invoked command's own exit stays excluded; the
rounding cases gain the ceiling, its count verdict over_threshold and
unassigned_errors, the residual of the ceiling after the named groups.

Why: the U2 design 5.3 grades on rate_upper_bound until a dated
amendment assigns the other classes, and the Gate A binding decisions
apply the harder-to-pass reading; the frozen M15 row
(evidence/artifacts/token-adoption-e2e-20260926/README.md, M15;
preregistration.json thresholds.M15) excludes only the invoked
command's non-zero exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test each unassigned M15 class in its own failing-first test

What: the per-class ceiling checks (policy_deny, remote_fetch,
search_throttle, rejected, invalid, cancelled_with_result, a class no
list names, and the excluded invoked_command_exit) move out of the
reviewer-input test into their own test, so they run, and fail, on the
kernel before the fix instead of sitting behind the first assertion.

Why: a test that never failed proves nothing; the U2 design 5.3 grades
on rate_upper_bound until a dated amendment assigns these classes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Grade M15 on the ceiling until Amendment 4 assigns the classes

What: finishM15Row publishes unassigned_errors (the residual of the
ceiling after the infrastructure, new and unknown groups) and
over_threshold (the ceiling's count against 0.01: errors * 100 >
attempted), and threshold_sensitive now compares the two published
bounds, rate_lower_bound and rate_upper_bound. rate and
rate_lower_bound keep their meaning as descriptive figures.

Why: the review's high finding. A server whose every call fails with
the client's "No such tool available" <tool_use_error> read rate 0,
rate_lower_bound 0 and threshold_sensitive false. The U2 design 5.3
grades on rate_upper_bound until a dated amendment assigns the other
classes, the Gate A binding decisions apply the harder-to-pass reading,
and the frozen M15 row (token-adoption-e2e-20260926 README.md M15)
excludes only the invoked command's non-zero exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document M15 grading on rate_upper_bound in README and limits

What: the workflows README splits the old "not infrastructure" row into
the six unassigned classes (graded as errors until Amendment 4 assigns
them) and the excluded invoked_command_exit, and states that grading
reads rate_upper_bound with over_threshold as its count verdict; the
sweep's U2 limits string says the same.

Why: the README and the limits string pointed graders at rate, which
scores unassigned classes as successes (the review's high finding); the
U2 design 5.3 and 12 name rate_upper_bound as the M15 field until the
dated amendment, and the frozen M15 row excludes only the invoked
command's own exit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate the workflows SHA256SUMS after the M15 grading fix

What: the child-usage.mjs line of the workflows SHA256SUMS takes the
kernel's new sha256; every other line is unchanged, and
`sha256sum --check SHA256SUMS` reads OK for all 14 files.

Why: the kernel changed in the M15 grading fix and its limits string;
the SHA256SUMS file is the workflows directory's integrity record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Remove the apostrophe that ended the U2 limits string early

What: the M15 sentence of the single-quoted U2_LIMITS string said
"the invoked command's own exit"; its apostrophe closed the string and
the kernel failed to load (node --check: SyntaxError: Unexpected
identifier 's'; 282 Python tests failed with it at 01af2f62). The
sentence now reads "a non-zero exit of the command it ran".

Why: 1a189bdb introduced the quote; the kernel must parse. Found by
the acceptance run of tests.test_token_measurement and the other
suites (repair-round log, not published).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate the workflows SHA256SUMS after the limits quote fix

What: the child-usage.mjs line takes the sha256 of the kernel that
parses again; `sha256sum --check SHA256SUMS` reads OK for all 14 files.

Why: the previous regeneration (01af2f62) recorded the kernel with the
broken U2_LIMITS string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Re-run the U2 differentials and M15 scans at the repair kernel

What: expected-changes.json gains a repair-round stage that allows the
two new m15.by_server keys (unassigned_errors, over_threshold);
waa-invariants.py folds succeeded, invoked_command_exit and
unassigned_errors and computes the ceiling as finishM15Row does. The
three differentials (sweep over W_AA 254,003 new paths, twelve rtk
windows 286,988, run mode 770,476; 0 unexpected, exit 0 each), the W_AA
invariants, the ledger check and scans/kernel-m14-m15.json (5,283 files)
are re-run at kernel sha256 c1d9ad16; their effects equal the earlier
run's. The README records the new counts and the real-data case of the
finding (plugin_context-mode: 2 invalid calls, rate 0, ceiling 1).

Why: the kernel's M15 output changed in this repair round, so the
recorded differential and M15 evidence had to describe the final kernel
(U2 design 10; review-u2's differential finding: an explicit
allow-list, equality everywhere else).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Re-register the repair-round files in manifests/evidence.json

What: the new sha256 and byte counts of the twelve files the repair
round changed (child-usage.mjs, the workflows README and SHA256SUMS,
tests/test_token_measurement.py and eight files of
evidence/artifacts/pra-u2-differential-20260929), written with
scripts/host_receipts.py register_file; component_matrix.py --write and
new_host_grand_list.py --write change nothing else.

Why: the hot-file protocol (docs/lanes.md): every shared hot-file edit
in the branch's last commit. python3 scripts/validate.py exited 1 on
the twelve hash mismatches before and exits 0 after (hashed_files
8220, status passed). b2dd1eb7, the earlier registration, now sits
mid-branch; at merge, take main's manifest and re-register every file
the branch changed, as the protocol says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Name the pre-fix kernel and an unexamined cause in the M15 texts

What: the reviewer-input test's docstring names b2dd1eb7, the kernel
before the fix, instead of "the base kernel" (fe9f511b has no m15 at
all); the evidence README no longer gives a cause for the 2 lines the
store scan could not parse, since the scan prints only their count.

Why: published evidence and test text state only what was observed
(docs/acceptance-evidence-policy.md: claims quote the recording's own
output).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Re-register the two reworded files in manifests/evidence.json

What: the new sha256 and byte counts of tests/test_token_measurement.py
and evidence/artifacts/pra-u2-differential-20260929/README.md after
54774c0f, written with scripts/host_receipts.py register_file;
component_matrix.py --write and new_host_grand_list.py --write change
nothing else.

Why: the hot-file protocol (docs/lanes.md) keeps the shared hot-file
edit in the branch's last commit; python3 scripts/validate.py exits 0
(hashed_files 8220, status passed). b2dd1eb7 and 962687be, the earlier
registrations, sit mid-branch; at merge, take main's manifest and
re-register every file the branch changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for the U3 Codex measures

PR-A U3 step 1 (research-u3.design.md commit 1, amended by its review).
Tests only. At the base fe9f511b the 10a and 10b tests fail; the tests of
later design commits are marked pending (expectedFailure), so a marked
test that starts passing fails the run until its commit lifts the marker:

- 10a marker split and codex_hook_context (CodexMarkerSplit; the retained
  sub-agent test now asserts the split);
- 10b role grouping (CodexRoleGroups);
- 10g route values (commit 4) and spawn join states (commit 5), with the
  integer fork_turns form, a per-field route basis, follow-ups and an
  ID-free check with negative controls (CodexRouteValues, CodexSpawnJoin);
- the Codex normalization (commit 6), 10e nested attribution (commit 7)
  and legacy spans (commit 8) (CodexCommandNormalization,
  CodexCodeModeAttribution).

Must-stay controls in the same commit: the configured route, E1 and E4 at
this base, the legacy lane counters, model call ids after an exec, the
wait and wait_agent carriers, the unscanned outer exec code (F2), the
retained isolated fixture's direct items, one actor per spawn case.

Sources: openai/codex rust-v0.157.1 (36650394) protocol.rs:772-779,
:2901-2916, :3116-3191, :3293-3350, :3534-3544, :4382-4390; items.rs:364-370;
models.rs:958-993; hook_runtime.rs:128-154, :848-872; spawn.rs:126-130,
:216-226, :253-299; child_config.rs:196-253; multi_agents_spec.rs:217-241,
:646-650; policy.rs:94-112; description.rs:19-24; globals.rs:36-48;
user_shell.rs:199, :453-480; the frozen launch matrix
(preregistration.json seed-binding-1 to 5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Split the Codex marker into inherited and injected

PR-A item 10a (research-u3.design.md section 1, with the review's kinds
and window findings). The legacy `marker` keeps its meaning (the marker in
any developer message). Additive fields:

- marker_inherited / marker_injected: the marker per content item, in a
  record copied from the parent (ordinal below
  subagent_history_start_ordinal) or in the session's own record;
- marker_injected_kinds: injected items by content kind, from
  internal_chat_message_metadata_passthrough.content_item_kinds, (none)
  when the kinds do not align with the items or are not strings;
- group marker_inherited_sessions, marker_injected_sessions,
  marker_inherited_only_sessions and marker_injected_by_kind;
- measurement.codex_hook_context {inserted, with_marker, inherited,
  inherited_with_marker} over developer items of kind
  hooks.additional_context: own items in the window of their record,
  copied items once, in the window of the child's first own record, so
  adjacent windows add up; groups sum it.

The kernel's hook_context (Claude attachments) is unchanged and reads
zero for Codex; the README says so.

Sources: openai/codex rust-v0.157.1 (36650394)
core/src/context/hook_additional_context.rs:15-22,
core/src/hook_runtime.rs:128-154, :848-872,
context-fragments/src/fragment.rs:35-53, protocol/src/models.rs:958-993,
protocol/src/models/item_metadata.rs:5-7, protocol/src/protocol.rs:3180-3185.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Group Codex sessions by custom-agent role

PR-A item 10b (research-u3.design.md section 2). Additive fields:
actors[].role, a top-level sessions_by_role and groups.workers_by_role
(the lane aggregate of the workers of each role).

A session's role comes from its first session_meta: agent_role, which
also reads from agent_type, else the agent_role (or agent_type) of its
source.subagent.thread_spawn. It is trimmed as the spawn handler trims
it, with Rust str::trim's Unicode White_Space set (not Python's strip,
which also removes U+001C to U+001F); an empty value or a non-string is
no role. A role that is not name-shaped is (other), a sub-agent without
a role (none), every other session (root).

Sources: openai/codex rust-v0.157.1 (36650394)
protocol/src/protocol.rs:2904-2913, :3153-3155;
core/src/tools/handlers/multi_agents_v2/spawn.rs:126-130;
core/src/agent/child_config.rs:86-98 (the configured default role);
Unicode PropList.txt White_Space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for Codex usage counters and routes

PR-A U3, design commit 4 (research-u3.design.md section 3) with binding
correction 4 of the U3 build (u3-corrections.md). Tests only; at this
head they fail on assertions:

- CodexUsageCounters: cache_write_input_tokens is differenced like the
  other cumulative counters (30 then 45 across two snapshots, from a
  child's copied baseline too); a rollout without the field reports it
  as null, never 0, with no usage gap, and a group total is null when an
  actor lacks it; max_request_input_tokens keeps each attempt's largest
  last_token_usage.input_tokens over its counted, non-repeated snapshots.
- CodexRouteValues: a route value that is not a string is null (a bool
  is an int in Python and str(True) is name-shaped).

Sources: openai/codex rust-v0.157.1 (36650394)
codex-rs/protocol/src/protocol.rs:2233-2253 (TokenUsage, serde default
on cache_write_input_tokens), :2268-2275 (TokenUsageInfo), :3328,
:3344-3345 (TurnContextItem model and effort).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep Codex route values and add cache-write usage

PR-A U3, design commit 4 (research-u3.design.md section 3) with binding
correction 4 (u3-corrections.md). It changes delivered
provider_usage.attempts[] values, so it is its own commit with a
before/after host differential.

- effort: any name-shaped string is kept (none, minimal, ultra,
  persistent and custom strings were null), any other string is (other),
  a non-string is null (effort_key).
- configured_model: a name-shaped model with at most one provider
  segment in front is kept (cx/gpt-6-astra read (other)); a path, '..',
  more segments or an empty segment is (other); a non-string, which
  safe_key turned into text ('True', '7'), is null (model_key).
- cache_write_input_tokens joins CODEX_COUNTERS and is differenced like
  the others; being serde(default), a rollout without it reports null,
  never 0, and its absence is no usage gap, so complete keeps its
  meaning. A group total is null when an actor's is (the existing rule).
- attempts[].max_request_input_tokens: the largest
  last_token_usage.input_tokens of the attempt's counted snapshots.
- A token_count whose info is not an object is a gap instead of an
  AttributeError.

Sources: openai/codex rust-v0.157.1 (36650394)
codex-rs/protocol/src/openai_models.rs:59-72 (ReasoningEffort),
protocol/src/protocol.rs:2233-2253 (TokenUsage), :2268-2275
(TokenUsageInfo), :3328 and :3344-3345 (TurnContextItem).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for the Codex spawn join states

PR-A U3, design commit 5 (research-u3.design.md section 4). Tests only;
the new ones fail on assertions at this head, the pending ones stay
marked until the fix:

- c22, a legacy parent: a legacy rollout persists SubAgentActivity items
  of kind completed only, so its child reads the new join state
  parent_without_started_item (requested unknown, follow-ups counted);
- c23, a V1 spawn (namespace multi_agent_v1): its history comes from
  fork_context, so fork_context false reads none, and a V1 resume_agent
  with the child's id is a follow-up;
- follow-ups are null when the parent was not scanned (c13, c19);
- subagent_spawns counts every state (requested.fork_turns and
  fork_consistent pinned);
- requested_fork_turns follows the spawn handler: str::trim, ASCII
  case-insensitive none/all, usize::from_str with an optional '+' and
  ASCII digits within u64, nonzero; U+001C, '2_000', non-ASCII digits,
  bools and floats are invalid;
- a forked child's copied spawn call, sibling started item, turn context
  and settings stay the parent's: with the parent out of the roots the
  sibling reads parent_not_scanned, never parent_mismatch.

Amended pending expectation (with evidence): c11's effort basis is
agents_default_or_model_default, not model_default, because an [agents]
default effort applies before the model's default effort.

Sources: openai/codex rust-v0.157.1 (36650394)
core/src/tools/handlers/multi_agents_v2/spawn.rs:216-226, :253-299;
core/src/agent/child_config.rs:62-99, :196-253;
core/src/tools/handlers/multi_agents_spec.rs:217-266, :591-665;
rollout/src/policy.rs:94-120.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Publish Codex sub-agent spawn states from an in-memory join

PR-A U3, design commit 5 (research-u3.design.md section 4, with the
review's fork_turns, resume, route-basis and privacy findings).
Additive: actors[].spawn for each sub-agent and subagent_spawns, the
count of every state.

scan_lanes_file keeps private join facts from the session's own records
before until, whatever since (never the records a forked child copied
from its parent): its thread, parent thread, task path and raw role, own
spawn_agent calls (fork_turns, fork_context, agent_type, model,
reasoning_effort and the parent's turn route at the call), own started
SubAgentActivity items, turn routes, turns, the first own settings
snapshot and follow-up targets. scan_codex_lanes indexes them over every
scanned rollout and publishes states only: join (joined,
activity_without_spawn_call, parent_mismatch, the new
parent_without_started_item for a scanned parent without a started item,
as a legacy rollout persists none, and parent_not_scanned), requested,
effective, fork_consistent, role_state, route_vs_request,
route_vs_parent_turn, route_changes_within_child, a per-field
expected_route_basis, reroute_evidence and followups (followup_task
target or V1 resume_agent id; null for an unscanned parent).
requested_fork_turns mirrors SpawnAgentArgs::fork_mode; a positive JSON
integer reads as last_n (the frozen launch matrix writes 2), and a V1
spawn reads fork_context. Non-string ids, targets and route values are
ignored instead of raising.

Sources: openai/codex rust-v0.157.1 (36650394)
core/src/tools/handlers/multi_agents_v2/spawn.rs:126-130, :216-226,
:253-299; core/src/agent/child_config.rs:62-121, :196-253, :283-299;
core/src/tools/handlers/multi_agents_spec.rs:217-266, :591-665;
protocol/src/protocol.rs:1403-1410, :2192-2231, :2901-2916, :3116-3191,
:3293-3350, :4382-4390; protocol/src/items.rs:364-370;
rollout/src/policy.rs:94-120, :141-204.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for the Codex command normalization

PR-A U3, design commit 6 (research-u3.design.md section 6, with the
review's shell_script, unresolved-command and window findings). Tests
only; the new ones fail on assertions at this head, the pending ones
stay marked until the fix:

- a shell is typed by its name or file stem, case-sensitively, as
  Codex types it: pwsh, powershell and cmd (Windows paths included) are
  not POSIX, zsh, bash and sh are, and a name Codex does not know (dash)
  runs its OS fallback shell, which the rollout does not record, so it
  is unresolved (codex_commands.unknown_shell); M4 reads incomplete, and
  without --rtk-check no unknown call is added;
- codex_commands count a call once, at its first record, inside
  [since, until), a function_call and its own CommandExecution item
  being one call, so adjacent windows add up;
- a user_shell or interaction item leaves no tool_use and no result
  (nothing in M3, no orphan), and a group's M4 status stays incomplete
  for an unresolved command although the kernel recomputes it;
- with RTK replay (rtk 0.50.0 on Linux) an unresolved command is an
  explicit unknown call; at this head a pwsh argv was replayed as
  measured text (calls 2, unknown 0, measured).

A local RTK_REPLAY_SUPPORTED gate mirrors tests/test_token_measurement.

Sources: openai/codex rust-v0.157.1 (36650394)
shell-command/src/shell_detect.rs:39-59, :315-334; core/src/shell.rs:
22-49; core/src/tools/handlers/unified_exec.rs:27-50, :99-144;
protocol/src/protocol.rs:3534-3544; core/src/tasks/user_shell.rs:
142-146, :190-205, :453-481.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for Codex shell typing and -c options

PR-A U3, design commit 6 (research-u3.design.md section 6), second test
step. A probe at 0a9bada8 showed three readings the first test step did
not pin; tests only, and at this head they fail on assertions:

- an argv program is typed by its file stem with / and \ as
  separators (a Windows Git Bash path is POSIX, CMD.EXE is cmd), while
  exec_command's shell is typed as Codex does: its file stem again and
  again, case-sensitively (pwsh.exe.bak is pwsh, PWSH is unknown), a
  non-string shell fails ExecCommandArgs (unknown), and null is the
  session shell; observed codex_commands None;
- with -c among the short options the first non-option argument is the
  script (-cl, -e -c, -o pipefail -c); observed M4 shell_fetch 1 of 3,
  as the joined -cl and -o argvs hid their curl;
- rtk replay of a -ec script: observed eligible 0, ineligible 1 (the
  joined argv), expected eligible 1.

The legacy shell_script reading of a -cl argv is pinned as must-stay.

Sources: bash(1) INVOCATION and OPTIONS (-c, -o option); openai/codex
rust-v0.157.1 (36650394) shell-command/src/shell_detect.rs:39-59,
:315-334; core/src/tools/handlers/unified_exec.rs:27-33, :117-121.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Resolve Codex shell calls by the shell that ran them

PR-A U3, design commit 6 (research-u3.design.md section 6, normalization
A for U1's commandInvocations), with the review's shell_script finding:
shell_script keeps its reading for the legacy lane counters, and the PR-A
measurement reads each shell call through a separate resolver. It can
change delivered PR-A fields, so it is its own commit with a host
differential.

- An argv runs its program, typed by its file stem with / and \ as
  separators: sh, bash, zsh, dash and ksh with -c among their short
  options run the first non-option argument (bash(1) INVOCATION; each o
  or O takes the next word), which widens the design's "cluster ending
  in c" to -cl and -o pipefail -c; pwsh, powershell and cmd (ASCII
  case-insensitive, as Windows resolves names) are unresolved.
- exec_command's shell is typed as Codex types it: the value, else its
  file stem again and again, case-sensitively; any other value, or one
  that is not a string, runs an unrecorded fallback shell (unknown).
  Absent or null is the session shell, read as POSIX.
- An unresolved call is a Bash call with no text, counted in
  measurement.codex_commands; m4.status reads incomplete (actor and
  groups, as the kernel aggregate recomputes it), and with --rtk-check it
  is an explicit rtk unknown call. The kernel is unchanged.
- user_shell and unified_exec_interaction CommandExecution items are no
  model tool calls: no tool_use, no result, counted apart.
- codex_commands counts a call once, at its first record in the window.

Removes the three commit-6 pending markers.

Sources: openai/codex rust-v0.157.1 (36650394)
shell-command/src/shell_detect.rs:39-59, :315-334; core/src/shell.rs:
22-49; core/src/tools/handlers/unified_exec.rs:27-50, :99-144;
protocol/src/items.rs:243-285; protocol/src/protocol.rs:3534-3544;
core/src/tasks/user_shell.rs:142-146, :190-205, :453-481; bash(1)
INVOCATION and OPTIONS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin the frozen fork_turns launch forms through the spawn join

PR-A U3, the stage's binding correction for design commit 5: M11's
frozen launch forms are fork_turns none, integer 2, all (agent_type
stack-researcher), all (agent_type null) and resume. The classifier test
already read each value, but the join fixtures held no explicit "all"
and no null agent_type. This control joins all five end to end: joined,
requested fork_turns and fork_n, role, history, turns, fork_consistent,
role_state and followups, with the ID-free report check.

"resume" is no spawn argument (c08 reads it invalid, and upstream fails
such a call), so the resumed finished researcher child is its own spawn
plus a followup_task to its thread and a second own turn.

A control, not failing-first: it passes at 1bbf81e9, as commit 5 reads
every form. A scratch negative control (the integer form read invalid,
"all" read as the default) made it fail on its assertion, at m02.

Sources: evidence/artifacts/token-adoption-e2e-20260926/
preregistration.json tasks 64-68 (launch), RUNBOOK.md:411-417;
openai/codex rust-v0.157.1 (36650394) core/src/tools/handlers/
multi_agents_v2/spawn.rs:253-299; multi_agents_spec.rs:217-241.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for Codex nested attribution bounds

PR-A U3 design commit 7 (research-u3.design.md section 7, with the U3
review's position, item-kind and web_search_call findings). Tests only;
at cd04dc34 all five fail, beside the three pending commit-7 tests:

- a web_search_call id is a model call id, so an item carrying it stays
  direct inside an open exec span (sandbox_operations 1 at cd04dc34);
- only emitted items (CommandExecution, McpToolCall, web.search
  Extension) count as nested or unattributed (code_mode absent);
- the turn aliases turn_complete, turn_started and turn_failed end
  nesting, and another direct call no longer does (1 != 2);
- the code_mode counters count once in the window of their first
  record, so adjacent windows add up (code_mode absent);
- groups sum the code_mode counters (code_mode absent).

Sources: openai/codex rust-v0.157.1 (36650394) protocol/src/models.rs:
1182-1203 (WebSearchCall), rollout/src/policy.rs:56, :94-112,
protocol/src/items.rs:57-61 (TurnItem::WebSearch), protocol.rs:
1403-1415 (task_started/turn_started, task_complete/turn_complete),
code-mode-protocol/src/description.rs:19-24 and lib.rs:51-52.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Attribute Codex code-mode items by exec position in the turn

PR-A U3 design commit 7 (10e normalization B), with the U3 review's
position, item-kind and web_search_call findings.

An emitted item (CommandExecution, McpToolCall, web.search Extension)
whose id is no model call id is now nested when an own exec call came
earlier in its turn; before, nesting ended at the exec return or at the
next direct call. A cell keeps running after exec returns, through
yield_control() and the code-mode wait tool, so only the turn bounds it.
Model call ids now include a hosted web_search_call's id. Every turn
event the module reads ends a turn, the upstream aliases included. A
wait call without a namespace after an exec of its turn is code mode,
so its output counts under M3's code_mode carrier. New
measurement.code_mode {exec_calls, wait_calls, nested_items,
unattributed_items}, counted once at the first record in the window and
summed by groups. The three pending commit-7 tests lose their marker.

Changed meanings (the design's list): sandbox_operations, M3 by_carrier,
the M4 shell_fetch/ctx_sandbox_fetch split, U1's proxy.nested and the
cli_lanes carrier nested. A count-only census of this host's store
(1,620 rollouts) found 271 items after the exec span in the same turn,
none without an exec in its turn, and 352 wait calls, all after an exec
of their turn.

Sources: openai/codex rust-v0.157.1 (36650394) code-mode-protocol/src/
description.rs:19-51, lib.rs:51-52; protocol/src/models.rs:1060-1165,
:1182-1203; rollout/src/policy.rs:56, :94-112; protocol.rs:1403-1415;
protocol/src/items.rs:57-61; multi_agents_spec.rs:268-291.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for Codex legacy spans and outer JS scope

PR-A U3 design commit 8 (10e legacy-mode spans), with the U3 review's
per-exec finding and the explicit outer-JS decision the stage brief
requires. Tests only; at 9799d5fa every new test fails, beside the two
pending commit-8 tests (code_mode legacy and outer keys absent, M4
status not forced):

- rows with no session_meta, or a history_mode other than paginated,
  read as legacy (no session_meta: None, None, not_applicable);
- static sites: the global tools with a fetch-capable name after a dot
  (blanks and ?. allowed) and any bracket access, not ALL_TOOLS,
  x.tools, longer names or other nested tools (expected 5 sites);
- legacy spans count once in the window of the exec call;
- outer HTTP_SCRIPT-shaped mentions are counted, never as fetches, and
  a client whose isolate was not read (0.158.0, or none named) makes
  M4 incomplete for the actor and the group.

The shared test metas now name cli_version 0.157.1, a client whose
isolate was read. Sources: openai/codex rust-v0.157.1 (36650394)
protocol.rs:772-779, rollout/src/policy.rs:94-112,
code-mode-protocol/src/description.rs:24, code-mode-runtime/src/
runtime/globals.rs:36-48; rust-v0.155.1 (be2951ea) description.rs:20
and a byte-identical globals.rs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Mark Codex legacy exec spans and scope outer JS to read isolates

PR-A U3 design commit 8 (10e legacy-mode spans), with the U3 review's
per-exec finding and an explicit outer-JS decision.

Legacy spans: a rollout that is not paginated (no history_mode, or no
session_meta, is legacy upstream) persists no nested item. There an
exec call whose code has a static fetch-capable site and no attributed
nested item (after it, before the next exec or the turn's end) counts
in code_mode.legacy_unobservable_exec_calls and _sites, once in the
window of its call record, and M4 is incomplete for the actor and its
groups. Sites come from a linear scan (code_mode_fetch_sites): the
global tools with exec_command, web__run or mcp__*__ctx_* after a dot,
or any bracket access, which can name any tool.

Outer JS (the review's medium finding, decided here, not declared
unnecessary): the isolate has no network access at 0.155.1 and 0.157.1,
read at both tags, so outer HTTP_SCRIPT mentions (a linear scan) are
only counted in code_mode.outer_http_mentions. For any other client,
or none named, an exec with a mention counts in
outer_http_unverified_exec_calls and makes M4 incomplete. The dated
record, alternatives, host residual and overturn conditions are in
tools/skill-usage/README.md; docs/decisions/ is outside this unit, so
the PR-A owner accepts or rejects it. The two pending markers lift and
the unused pending helper goes.

Host census (count only): all 1,620 rollouts are paginated and name
0.155.1 (277) or 0.157.1 (1,343); 1,532 mentions in 1,091 of 16,410
exec calls, 51 of them in 33 calls with no nested ctx code item.

Sources: openai/codex rust-v0.157.1 (36650394) protocol.rs:772-779,
rollout/src/policy.rs:94-112, code-mode-protocol/src/description.rs:21,
:24, :365-387, code-mode-runtime/src/runtime/globals.rs:36-48;
rust-v0.155.1 (be2951ea) description.rs:20 and globals.rs (the same
sha256); child-usage.mjs HTTP_SCRIPT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add failing-first tests for the Codex RTK replay agent and D7

PR-A U3 design commit 9 (10d), with the U3 review's findings on the
d7.wrapped_exceptions provenance and the Claude byte-identity check.
Tests only; at e79a5c18 all fail:

- the Codex bridge asks the kernel for rtkAgent codex and labels
  rtk_parts.agent (payload option absent);
- with a home settings file denying Bash(git status) and the cwd
  outside any project, the Codex lane replays as claude today and
  counts an unknown call (0, 1, incomplete), not the rewrite;
- measureTranscript refuses an rtkAgent other than claude or codex
  (accepted today), keeps the Claude output without d7, and every
  fixed-config field agrees between the agents where no rule applies;
- rtk_parts.d7 drops reviewed requires_raw log/find parts, prefixed or
  not, counts prefixed ones as wrapped exceptions, keeps unreviewed
  ones and marks d7 incomplete (d7 absent);
- an unresolved command leaves d7 incomplete; groups and the kernel
  aggregate sum d7 for Codex measurements only.

The fake home registers the rtk hook: a Claude directory without one
makes rtk 0.50.0 print a once-a-day "No hook installed" warning on the
claude path, which fails the kernel's five-exclusion probe (observed
here; src/hooks/hook_check.rs:26-35, :88-135). The measure helper
gains a cwd.

Sources: rtk-ai/rtk v0.50.0 (1d87b8e7) src/main.rs:992-1000,
src/hooks/decision.rs:61-97, :196-204, permissions.rs:56-67, :141-175,
hook_check.rs:26-135; adoption/templates/codex.AGENTS.template.md
(the D7 exception list); preregistration.json thresholds.M6c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Replay Codex RTK parts as the Codex hook and add the D7 view

PR-A U3 design commit 9 (10d), with the U3 review's findings on the
d7.wrapped_exceptions provenance and on the Claude byte-identity check.

Kernel: measureTranscript takes rtkAgent, claude by default or codex,
and refuses any other value. rtkChecker keeps one checker per agent,
each with its own five-exclusion probe, and asks rtk hook check
--agent <agent>; the claude checker is unchanged. For codex, rtk_parts
gains d7: the fixed-config eligible parts less the git log and find
parts a digest-bound review marks requires_raw, prefixed or not. An
unreviewed log or find part stays and leaves d7 incomplete, as does an
unknown call. wrapped_exceptions is explicit_rtk_on_excluded_or_sensitive
(M6c's zero counter, broader than the template list: stated, not
narrowed) plus wrapped_requires_raw_parts. aggregateMeasurements sums
d7 only over the measurements that carry it. SHA256SUMS regenerated.

Bridge: skill_usage.py passes rtkAgent codex and labels
rtk_parts.agent; an unresolved command, replayed as '' ("No rewrite
for:" under either agent), stays an explicit unknown call and now also
leaves d7 incomplete. The skill-usage README drops the "hypothetical
Claude-hook routing" wording and documents d7.

Why: rtk 0.50.0 maps codex to Host::Codex, which has no RTK-side
permission rules; claude merges the cwd project's and HOME's Claude
Bash rules, so before this a Claude deny rule on the machine running
the tool turned a Codex part into an unknown call (the failing-first
tests). Sources: rtk-ai/rtk v0.50.0 (1d87b8e7) src/main.rs:992-1000,
src/hooks/decision.rs:61-97, :196-204, permissions.rs:56-67, :141-175;
adoption/templates/codex.AGENTS.template.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Document Codex RTK replay, D7 and code-mo…
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
#508's token-stack record says the run-shape levers, role dispatch
among them, are "owned by the Gate A owner after #381 closes" (line 90).
Its line 93 ("Gate A per-row results decide any change") is about the
on-demand members of line 92, not the levers, so the Context paragraph
and the Sources entry now cite line 90 only (review finding, low).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
#508's token-stack record says the run-shape levers, role dispatch
among them, are "owned by the Gate A owner after #381 closes" (line 90).
Its line 93 ("Gate A per-row results decide any change") is about the
on-demand members of line 92, not the levers, so the Context paragraph
and the Sources entry now cite line 90 only (review finding, low).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
#508's token-stack record says the run-shape levers, role dispatch
among them, are "owned by the Gate A owner after #381 closes" (line 90).
Its line 93 ("Gate A per-row results decide any change") is about the
on-demand members of line 92, not the levers, so the Context paragraph
and the Sources entry now cite line 90 only (review finding, low).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Sep 30, 2026
…kills deny rules and the retained pin verification (PR #553 Gate A review, items 1, 2, 4 and 6)

- Listing record Scope: a "#381 interaction" paragraph. Codex arms B and A go from 11 to 25 visible skills
  (14 new, agent-browser, semgrep, codeql, security-audit and find-skills among them); arm N ignores the
  user configuration and, by the Gate A owner's count, already saw 26. M4 (>= 90% routed fetches; 5 of the
  fetch lane's 10 arm-B opportunities are seed-codex-web-table-1..5) and M8 (>= 80% per required lane;
  2 of symbol-references' 9, 1 of ai-memory's 7) could be hit by a diverted call that the B-versus-A gates
  cannot see. Counts from the #381 preregistration and the two manifests.
- Decision point 9 states the Gate A owner's decision as theirs: Amendment 4 names the catalog as part of
  the measured practice, no threshold is loosened, and diverted calls are reported, never reclassified.
- Decision point 4 records the Skills CLI deny rules; point 8 cites the retained pin verification output;
  a new overturn condition covers CLI command or rule-matching changes; Sources add the permissions page,
  skills@7407f389 src/cli.ts L336-402 and the find-skills body.
- Skills-trial addendum: the fraction plan's step 3 stays outside #381 window W, and a change after the
  Amendment 4 seal re-seals; host step 1 removes the retired skill from a shell outside a Claude session;
  two evidence-class rows for the deny-rule test and the pin verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Oct 1, 2026
#508's token-stack record says the run-shape levers, role dispatch
among them, are "owned by the Gate A owner after #381 closes" (line 90).
Its line 93 ("Gate A per-row results decide any change") is about the
on-demand members of line 92, not the levers, so the Context paragraph
and the Sources entry now cite line 90 only (review finding, low).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Oct 1, 2026
…kills deny rules and the retained pin verification (PR #553 Gate A review, items 1, 2, 4 and 6)

- Listing record Scope: a "#381 interaction" paragraph. Codex arms B and A go from 11 to 25 visible skills
  (14 new, agent-browser, semgrep, codeql, security-audit and find-skills among them); arm N ignores the
  user configuration and, by the Gate A owner's count, already saw 26. M4 (>= 90% routed fetches; 5 of the
  fetch lane's 10 arm-B opportunities are seed-codex-web-table-1..5) and M8 (>= 80% per required lane;
  2 of symbol-references' 9, 1 of ai-memory's 7) could be hit by a diverted call that the B-versus-A gates
  cannot see. Counts from the #381 preregistration and the two manifests.
- Decision point 9 states the Gate A owner's decision as theirs: Amendment 4 names the catalog as part of
  the measured practice, no threshold is loosened, and diverted calls are reported, never reclassified.
- Decision point 4 records the Skills CLI deny rules; point 8 cites the retained pin verification output;
  a new overturn condition covers CLI command or rule-matching changes; Sources add the permissions page,
  skills@7407f389 src/cli.ts L336-402 and the find-skills body.
- Skills-trial addendum: the fraction plan's step 3 stays outside #381 window W, and a change after the
  Amendment 4 seal re-seals; host step 1 removes the retired skill from a shell outside a Claude session;
  two evidence-class rows for the deny-rule test and the pin verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 1, 2026
…s; task-to-model routing record (unit A4) (#540)

* Codex user template: standing rule clauses, skills, models, routing and token lanes

Adds the 2026-09-30 standing clauses to the top-rule block of
adoption/templates/codex.AGENTS.template.md: skill discovery (search-first,
find-skills, skill-creator; implicit invocation from a skill's description and
explicit $skill-name, per openai/codex rust-v0.159.2
codex-rs/ext/skills/src/catalog_prompt.rs:8), upstream A/B and E2E harnesses,
the completeness critic feeding the next landscape sweep, the north-star
direction, GPT-6 model routing through the OmniRoute gateway (codex -p
omniroute), dated decision records, the startup rule, and a token-lanes line
naming each MCP server the Codex config template registers.

The RTK upstream text and the exceptions block stay byte-identical, so the F4
block in every Codex role is unchanged. The top-rule pin is re-derived with
template_segments(): 455 words, 7b41478f...; a new test requires a lane for
every server in codex.config.template.toml and the standing phrases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Portable Claude template: superset of the user-level rules plus the standing clauses

examples/claude-native/CLAUDE.md becomes the single managed source of the
operator's user-level file. Step 1 of the top rule takes the user-level
paragraph (research and record, reference implementations, popularity guides
discovery) and the skill-discovery clause (search-first, find-skills with
`npx skills find`, skill-creator); the core rule takes the rules only the
user-level file held (short plan, pins and reasons, relevant layers, reviewer
agreement is not proof), the north-star direction, the upstream A/B and E2E
harnesses and the completeness critic; token practice takes the startup rule;
the worker section takes GPT-6 routing through the OmniRoute gateway. Every
worker, model, Ultracode and agent-team rule is kept word for word.

PortableTopRuleTests gains a phrase check for the clauses and the user-level
rules (it failed on the unedited template with 29 phrases missing, 8 of them
rules the user-level file held) and re-baselines the word budget to 1,656.
@RTK.md stays the host's own import (recipes/claude-native-profile.md:134).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Decision record: standing rule text in every instruction layer; divergence log row

docs/decisions/2026-09-30-rule-text-every-layer.md supersedes the 2026-09-28
top-rule record's "the operator's user-level file is the operator's own" at
the user's 2026-09-30 request: the portable template becomes the single
managed source of that file. It maps the six clauses to each surface, records
the alternatives (a UserPromptSubmit carrier, per the hooks reference, adds
its context beside every prompt; leaving the file unmanaged produced the
divergence; per-agent copies are unit F2's; pointers measured as the
fallback), the o200k sizes from token_manifest.count_files, the overrun of
the unit's 200-token budget with its floor and fallback, and the overturn
conditions.

docs/harness-defaults.md logs the host/template divergence, naming only the
check seen failing on it (the new PortableTopRuleTests phrase check).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* AGENTS.md: the six standing clauses on the four named lines; re-register hashes

Hot files last (docs/lanes.md hot-file protocol). AGENTS.md takes the
standing clauses by amending, not appending, the lines the brief names:
- line 3, the top rule: skill discovery (search-first, find-skills with
  `npx skills find`, skill-creator; manifest skills model-invocable in both
  clients) replaces "with the installed research and skill-discovery
  skills", and A/B and E2E on upstream harnesses, never a self-written runner;
- line 7: the north-star R&D direction; each unit names its action;
- line 18: the completeness critic feeding the layer's next landscape sweep;
- line 28: GPT-6 through the OmniRoute gateway (astra max, sol medium, 6.1
  sol pending qualification), Codex CLI as the second native client, and "no
  audits, trials or network at startup" with the daily currency timer's
  due-file line (record lands with unit A2) replacing "Do not rerun the full
  audit or model trials at startup".

manifests/evidence.json re-registers the six changed files already listed,
with scripts/host_receipts.py register_file; component_matrix and
new_host_grand_list --check pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rule-text record and log row: nine user-level rules, not eight

The phrase check's committed form (with "from the selected source revision",
the user-level file's own wording) finds 29 phrases missing from the unedited
template, 9 of them held only by the user-level file; the record, its Checks
section and the anti-pattern row said 8 (from a run before that phrase
changed). The record also states the two non-verbatim worker lines exactly:
"Quality comes first" is one line with an identical word sequence, and the
sizing line keeps every user-level word and adds "to its task".
Re-registers docs/harness-defaults.md (hot file, last commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Codex user template: Sol-primary routing and skill matching folded into the standing lines

Folds the two lines the main checkout's uncommitted changes added inside the
top-rule block (pre-existing uncommitted changes observed in the main
checkout; original author not established), merged with this unit's lines
instead of carried beside them:
- the Models line now states the Sol-primary routing of
  docs/decisions/2026-09-30-sol-primary-quality-defaults.md (gpt-6.1-sol at
  ultra for coordination and at max for workers, gpt-6-astra at max after the
  recorded escalation triggers), replacing the superseded astra/sol-medium
  wording and "gpt-6.1-sol waits for qualification"; the OmniRoute gateway
  clause stays;
- the skills line takes the skill-matching rule (read each selected SKILL.md,
  follow its native workflow, load references only when needed) next to
  implicit and $skill-name invocation.

The phrase test failed first on the unedited block with five phrases missing
(exit 1). Pin re-derived with template_segments(): 496 words, 2d3107a2...;
the RTK text and exceptions block are unchanged (test_codex_agents 18 OK).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Portable Claude template: Sol-primary Codex routing; skill matching kept inside "Keep context small"

- The GPT-6 bullet follows docs/decisions/2026-09-30-sol-primary-quality-defaults.md:
  Codex CLI is the second native client, gpt-6.1-sol at ultra coordinates and
  at max runs workers, gpt-6-astra at max takes the recorded escalation
  triggers; cross-family research, review and sweep votes run through the
  OmniRoute gateway. It replaces the superseded astra/sol-medium wording and
  "gpt-6.1-sol pending qualification".
- Folds the skill-matching wording of the main checkout's uncommitted change
  (pre-existing uncommitted changes observed in the main checkout; original
  author not established) into the token-practice bullet. The change as
  observed replaced "Keep context small", a rule the operator's user-level
  file holds; the merged bullet keeps both.

The phrase check failed first on the unedited template with four phrases
missing (exit 1); it now also requires "Keep context small", which the
observed version lacks (mutant check: 31 phrases missing there, that one
included). Word baseline 1,656 -> 1,680.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* AGENTS.md: fold the skill-lifecycle and Codex-defaults bullets; one model-routing sentence set

Folds the two bullets of the main checkout's uncommitted AGENTS.md change
(pre-existing uncommitted changes observed in the main checkout; original
author not established). Neither was on origin/main at 11227bf (the file's
blob there equals e45328d's) nor on any other unit branch:
- the token-practice bullet on matching skill descriptions, reading each
  selected SKILL.md and adoption/skills/lifecycle.md (which unit F3 adds);
- the Codex defaults bullet (gpt-6.1-sol / ultra coordinator, gpt-6.1-sol /
  max primary workers, Astra/max on the recorded triggers, the Sol-primary
  record that unit D4 adds), merged with this unit's clause: Codex CLI is the
  second native client, and cross-family research, review and sweep votes run
  through the OmniRoute gateway. The startup bullet keeps only the startup
  clause, so the model routing is stated once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Convergence guide and token handbook: Ultracode decoupled from effort; Sol worker command

Folds the true delta of two files from the main checkout's uncommitted
changes (pre-existing uncommitted changes observed in the main checkout;
original author not established), taken as a three-way merge onto
origin/main so main's own later edits stay:
- docs/convergence-architecture.md: Claude Code 2.1.284 decoupled Ultracode
  from effort; xhigh (saved fallback) and max (launcher) are separate choices,
  not measured quality gains. Source: the tagged anthropics/claude-code
  v2.1.285 CHANGELOG.md line 206 ("it no longer forces xhigh effort and stays
  on at any effort level", under 2.1.284), read 2026-09-30.
- docs/token-session-handbook.md: the Codex worker command names
  gpt-6.1-sol, matching the stack-worker profile and recipe that unit D4
  changes in the same batch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Anti-pattern log: ten folded rows and six Codex runtime lane rows

Ten rows are folded byte-for-byte from the main checkout's uncommitted
docs/harness-defaults.md (pre-existing uncommitted changes observed in the
main checkout; original author not established), at the top of the table
where that change placed them. Only the true delta against origin/main is
taken: main's newer login-shell row and the four terminal-lane rows of #532
stay; the "Sol-primary quality defaults" paragraph belongs to unit D4.

Six rows were requested by the Codex runtime lane (relays
codex-runtime-anti-pattern-owner-handoff-20260930 and
codex-f1-source-correction-handoff-20260930), one mistake / correction /
check each, citing that lane's published sources at full SHAs: PR #535 head
00aa6c2 (the cited files are byte-identical to the earlier head 6a7b164,
which no remote ref holds any more), SDK branch head 404b821 and OpenHands
software-agent-sdk dcf401af build.py lines 581 and 925. The GitHub Actions
job conclusions the rows cite were re-read through the REST jobs API on
2026-09-30 (run 36695388851: jobs 109821999811 and 109821999568 cancelled,
all steps success; run 36690153586: job 109805172026 validate-macos failure).

UpstreamVerificationSectionTests: 3 OK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fold the 2026-09-30 native practice finalization record, attributed

docs/decisions/2026-09-30-sota-native-finalization.md is folded unchanged
from a read-only snapshot of the main checkout (pre-existing uncommitted
changes observed in the main checkout; original author not established;
tracked diff sha256 314bd1b260da0939). One attribution paragraph after the
title states that provenance and which linked evidence is not published with
it: evidence/artifacts/sota-finalization-20260930/ has no assigned owner, the
native-skill-finalization artifacts land with unit F3 and the
codex-01592-qualification artifacts wait on unit D4. The body is
byte-identical to the snapshot (12,956 bytes); the privacy scan found no host
path, home directory or host user name in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rule-text record: rebuilt base, Sol-primary clause (a), folded items, lane rows and guards, re-measured sizes

- Base moves to origin/main@11227bfd (the three surfaces are byte-identical
  to e45328d's).
- Clause (a) follows docs/decisions/2026-09-30-sol-primary-quality-defaults.md
  (unit D4); the brief's first wording and its 04:35Z "Astra at ultra for
  complex workflow tasks" relay are recorded as superseded by that later,
  more specific user selection.
- Lists every item folded from the main checkout with the provenance
  sentence, the snapshot hashes, the three-way-merge method and what was not
  folded (recipes/README.md is on D4's branch; the finalization evidence
  directory has no owner).
- Records the six Codex runtime lane rows (relays and what was verified) and
  the three relayed guards: #543 not described as published without a native
  gh lookup that exits 0, the 07:34Z/06:46Z actor unknown, a historical OSV
  pass not current after #546.
- Sizes re-measured with tools/token-report/token_manifest.py count_files
  (gpt-tokenizer 4.0.0): AGENTS.md +323, portable template +363 (sum +686
  against the 200 budget: +139 folded text, +547 clauses and user-level
  rules), Codex template +507.
- Limitations updated: clause (c) against F3's settings, cross-unit
  references, unpublished evidence, older Codex pins, A3's scaffold copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Codex routing on all three surfaces: Astra/Ultra coordinates a complex workflow, Astra/Max takes a single consequential judgment

Follows the coordinator's decision and unit D4's refined Sol-primary record
(claude/sota-defaults-d4-codex-0159-20260930 at 9dd4ebe), which splits the
escalation by the Codex catalog semantics it documents (Ultra: proactive
delegation with the model's xhigh reasoning; Max: the highest reasoning
effort) and quotes the user's 2026-09-30 selection ("astra ultra when tasks
needed suitable for complex workflow"):
- gpt-6-astra at ultra when a complex workflow needs Astra to coordinate it;
- gpt-6-astra at max for a single consequential judgment (conflicting primary
  evidence, consequential architecture, complex changes across systems, or a
  failure unresolved after one bounded Sol repair).

AGENTS.md's folded Codex bullet, the portable template's Codex bullet and the
Codex block's Models line now state the split. Both phrase checks failed
first with four phrases missing each (exit 1). Codex pin re-derived with
template_segments(): 515 words, d1195686...; portable word baseline 1,697.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* One-pass tightening of the added rule text (no rule removed)

The coordinator accepted the growth over the 200-token budget and asked for
one pass that cuts words carrying no rule, limited to text this branch adds:
- AGENTS.md Codex bullet: the Astra split as two clauses and the contract
  pointer in parentheses (2,742 -> 2,733 o200k tokens);
- portable template: "(registry: `npx skills find`)" -> "(`npx skills find`)"
  (2,436 -> 2,433);
- Codex block: implicit/explicit invocation in one clause, "use the
  `search-first` skill" -> "use `search-first`", the registry label, and
  "the paired benchmark of Claude's `skill-creator` plugin" -> "Claude's
  `skill-creator` paired benchmark" (1,360 -> 1,346).

Every phrase of both phrase checks still holds. Codex pin re-derived with
template_segments(): 507 words, 97bbeb8c...; portable word baseline 1,696.
Counts: tools/token-report/token_manifest.py count_files, gpt-tokenizer 4.0.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fold the finalization record's evidence: 18 of the 19 files of evidence/artifacts/sota-finalization-20260930/, unchanged

Allowed-path amendment by the coordinator. The files are copied
byte-for-byte from the read-only snapshot r2 of the main checkout
(pre-existing uncommitted changes observed in the main checkout; original
author not established); sha256sum -c against the snapshot passes.

Sanitization: nothing needed stripping and no file was changed.
- validate.py --scan-file over all 19 snapshot files: {"scanned_files": 19,
  "status": "passed"} (UUID, personal home path, Windows user path and token
  patterns).
- A wider scan found no personal home path, host user name, /tmp/claude-*
  path, session UUID, e-mail, IP address or token. The one "/home/" string
  earlier reported as a home path is the literal placeholder "/home/example"
  inside claude-review.json's prose, which validate.py's pattern exempts.
  Replacing it with <home> would alter the retained review without removing
  any host data, and would break the sha256 pin that the lane's convergence
  record holds for that file, so the file stays byte-exact. The
  config-key hits in convergence.json are recorded codex exec command lines
  already redacted to <private-path>; the three "~/." strings are generic
  install locations.

Held back: convergence.json. It is a kind "convergence_experiment" record
whose frozen inputs and observations pin five files of
native-skill-finalization-20260930 (unit F3; all five pins match F3's branch)
and three of codex-01592-qualification-20260930 (on no branch yet) by path and
sha256. validate_convergence.py --all-recorded, which CI runs, fails
discovery for an undeclared hash-listed record and fails validation for a
declared one with missing files, so it can be folded, declared in
convergence_records, only after F3 lands and D4 publishes those three files
unchanged at those paths.

The folded record's attribution paragraph says so; its links and the four
folded log rows into this directory resolve. Hash registration follows in
the last commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rule-text record: Ultra/Max split, accepted budget miss with its overturn condition, evidence fold and sanitization, post-A3 step

- Base is origin/main@8fc86119 (rule surfaces byte-identical to 11227bf
  and e45328d).
- Clause (a) records the split from unit D4's refined record (9dd4ebe):
  gpt-6-astra at ultra when a complex workflow needs Astra to coordinate it,
  at max for a single consequential judgment, with the catalog semantics and
  the user's 2026-09-30 words as relayed.
- Measured size: AGENTS.md 2,392 -> 2,733 (+341), portable template
  2,051 -> 2,433 (+382), Codex block 829 -> 1,346 (+517); budget sum +723
  against 200, accepted by the coordinator because the clauses are the user's
  explicit directive. Composition: fold +139, split +49, tightening -12.
  Overturn condition: a rerun of the 2026-09-29 user-prefix A/B showing no
  rule-following gain for the added cost brings back the pointer fallback.
- The finalization record's evidence: 18 of 19 files folded unchanged; the
  sanitization section gives the scan and corrects the first handoff (19
  files, not 21; the one "/home/" string is the /home/example placeholder,
  kept byte-exact, also because the lane's convergence record pins its
  sha256). convergence.json is held back: a convergence_experiment record
  pinning F3 and D4 files that CI's validate_convergence --all-recorded
  would reject on this branch whether declared or not.
- Known residual links: seven targets, four from F3 and three from D4
  (the Sol-primary record and two codex-01592-qualification files).
- Post-A3 step: after #545 merges, whichever lands second copies the
  top-rule block into adoption/scaffold/AGENTS.md; not created here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rule text: conditional skill discovery on the three surfaces; the growth waiver and the provenance wording cited to the brief's amendments

find-skills (npx skills find) runs only when no listed skill fits the task, so a measured child with a fitting
listed skill makes no extra Skill or Bash call (the Gate A owner's note); search-first stays before custom code
or a tool choice. The record cites the coordinator's dated brief amendments for the accepted +723 o200k growth and
for the neutral fold provenance (a process census of file writes does not establish authorship; the Codex runtime
lane asked for the wording). Codex top-rule pin re-derived: 514 words, fecf92dc...; portable baseline 1,703 words.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Standing rule text: one wording on the three surfaces, coordinator-scoped (Gate A owner's review of #557)

Applies DECISION 2 of the Gate A owner's ACCEPT-WITH-CHANGES review on
AGENTS.md, examples/claude-native/CLAUDE.md and the Codex block, with the same
sentences on all three (the Codex block names a bounded worker where the
Claude surfaces name a delegated child in the skill sentence):
(a) "A coordinator, not a delegated child, invokes `search-first` before
    custom code or a tool choice; when no listed skill fits the task, it
    discovers one with `find-skills` and verifies or A/B-tests it with
    `skill-creator`."
(b) Sol/Astra routing kept for unpinned work; added: where a launch pins
    the model and effort (`-m`, `-c model_reasoning_effort`), children inherit
    that pin and a spawn call names neither; a coordinator, never a
    delegated child, starts a cross-family lane.
(c) Dropped "every manifest skill stays listed for model invocation in both
    clients" (false at this head: find-skills, grill-me and
    improve-codebase-architecture are user-invocable-only, find-skills has
    codex_enabled false) and the bare `npx skills find`.
(d) The north-star, completeness-critic, trigger/acceptance and dated
    decision-record imperatives are a coordinator's.
(f) AGENTS.md's lifecycle pointer says "(lands with unit F3)"; the
    currency-notice (A2, in the stack below) and Sol-primary (D4, on main)
    citations stay.
The harness and startup sentences take one wording too.

Post-A3 step done: adoption/scaffold/AGENTS.md carries the final top-rule
block byte for byte (tests.test_scaffold_repo passes).

Tests: StandingRuleSurfacesTests (new) asserts the shared sentences on all
three surfaces and the dropped clause on none; it failed first with 21
failing subtests (exit 1). Both phrase lists failed first (exit 1). Codex pin
re-derived with template_segments(): 538 words by Python str.split(),
9565217f...; portable baseline 1,750; comments now name str.split(), not
wc -w (review point (e)).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Token handbook: the Sol worker command is marked "after D4"; the sealed E2E route keeps gpt-6-astra at max

Review point (g) of the Gate A owner: the sealed Gate A E2E runbook launches
Codex workers with `-m gpt-6-astra -c model_reasoning_effort="max"`
(evidence/artifacts/token-adoption-e2e-20260926/RUNBOOK.md:371; README.md:266),
so the handbook's `-m gpt-6.1-sol` command is marked as the post-D4 default
and names the sealed route beside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rule-text record: the Gate A owner's decisions (window-W guard, coordinator scoping), corrections, sources and numbers at final content

- DECISION 1 (record only): B1 applies neither user-level template;
  ~/.claude/CLAUDE.md (managed block) and ~/.codex/AGENTS.md stay
  byte-identical until the last Gate A window closes, F1's templates apply
  after window W. Why: the Codex block's line 16 names seven lane tools and
  every Codex arm reads the global AGENTS.md; the sealed design appends no
  LANES block (E2E README:188) and arm N is "config-free, not
  guidance-free" (README:275); Amendment 4 seals the pre-change hashes of
  both files. The guard covers window W only.
- DECISION 2: one wording on the three surfaces, coordinator-scoped
  imperatives, the pinned-launch rule (seed-binding-4, E2E README:157), the
  dropped "every manifest skill ..." clause (false at head) and why.
- Corrects the previous "makes no extra call" claim: the conditional
  wording gated only find-skills; search-first stayed ungated.
- Sources (point (f)): the pinned discovery command (skills-1.7.0 `skills
  find` with DISABLE_TELEMETRY=1, skills-agents-layer README:90,
  adoption/skills/manifest.json:22); the completeness critic cites
  Anthropic's evaluator-optimizer workflow and the multi-agent research
  system's completeness criterion; the north-star clause records "no
  upstream source; checked both pages, 2026-09-30".
- Numbers at final content (token_manifest count_files, gpt-tokenizer
  4.0.0, base main 1f2cdce): AGENTS.md 2,392 -> 2,812 (+420), portable
  2,051 -> 2,506 (+455), sum +875 against 200 (+723 accepted by the
  coordinator; +152 is the review's required text); Codex block 829 -> 1,397
  (+568); scaffold 373 -> 941. Words are Python str.split() counts.
- Post-A3 step marked done; six residual links listed (four F3, two D4).

docs/harness-defaults.md logs the proven mistake the review found: stating
a rule or a measurement for the head from an earlier state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Rule-text record: attribute the Amendment 4 seal to the Gate A owner's review

The amendment text is not in this repository, so the record states the seal
on the two user-level files' pre-change hashes as the review's statement,
not as a fact verified here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Task-to-model routing record: one table of task class, client, model, effort and enforcement point

Consolidates the routing rules of the workflows README (dispatch by role, Sonnet 5.5
fan-out units, role routing), the model-currency record, the Sonnet 5.5 dispatch
record and the max-default effort record into one table, and names where each route
is enforced today: agent frontmatter, workflow stage, CLAUDE_CODE_SUBAGENT_MODEL,
client settings, Codex config, lane code or instruction only. No route changes.

States that no automatic router exists; OmniRoute D04 singleton combos, D06 lane
aliases and the task-aware router (D11) stay off until the promptfoo A/B of wave
unit D3. gpt-6.1-sol is listed only as pending unit D4 qualification. Open PRs #423
and #508 are cited at their head commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* token-practice: point to the task-to-model routing record

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test the routing record against the enforcement points it quotes

Every value the record's table quotes from a file must still be on the cited
lines, every cited line must exist, frontmatter rows must name the model they
quote, every task class must have a row, gpt-6.1-sol must stay listed only as
pending and routed nowhere, and docs/token-practice.md must point to the record.
Integration checks of the repository's own record, not upstream acceptance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Routing record: name the client's content-based fallback guards

"No automatic router exists" now also names Claude Code's own content-based fallback
and the two keys that switch it off in the project settings and the user-settings
template (switchModelsOnFlag false, CLAUDE_CODE_DISABLE_REFUSAL_FALLBACK=1), with the
model-currency addendum's note that their coverage is unverified and that availability
fallback chains are a separate mechanism.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Routing record: cite #508 line 90 alone for the run-shape levers

#508's token-stack record says the run-shape levers, role dispatch
among them, are "owned by the Gate A owner after #381 closes" (line 90).
Its line 93 ("Gate A per-row results decide any change") is about the
on-demand members of line 92, not the levers, so the Context paragraph
and the Sources entry now cite line 90 only (review finding, low).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Routing record and test: hold quoted values per file, not per line; Codex rows name no version

Six sibling units of this wave may edit files that the record quotes by
line: landscape-sweep (A1), bootstrap-linux.sh (A3), .claude/agents and
the settings template's hooks entry (F2), the settings template and the
Codex template (F3), codex_roles.py (F4) and the model-currency record
(D4, an addendum over the region of its line 284). None of them may edit
this record or its test. An exact-line check fails their pull requests
on any inserted line, even when no route changes.

- The record now states every line number as read at e45328d.
  tests/test_task_model_routing.py counts each quoted value in its whole
  file and requires at least one copy for each distinct line quoted, so
  a pure line shift passes and a changed model or effort fails.
- Two values have more copies than the record cited. It now also cites
  the template's modelSettings xhigh for claude-opus-5-5 and
  claude-sonnet-5-5 (:303, :306) and review-changes.js's recheck-stage
  scout binding (:78), so a change to any cited copy fails.
- model-currency.md:284 is cited without a CI-checked quote. D4 may
  rewrite that sentence.
- The Codex rows' Client cell says "Codex CLI". The preamble records that
  manifests/stack.json:297 pins 0.157.1 at e45328d, so D4's pin move
  leaves no stale version string (review finding, low).
- The GPT-6.1 check matches a model binding (a model key or constant, or
  -m/--model with an optional gateway prefix), not any mention. F1 may
  write prose naming gpt-6.1-sol into adoption/templates; a real binding,
  such as D4 switching the Codex template default, still fails.

Controls, each run in a fresh copy of the files the test reads:
16 of 16 as expected for both the old and the new test. Pure line
shifts (codex template, model-currency.md, sweep.js) and an F1-style
prose mention fail the old test and pass the new one. Changed frontmatter,
one of two identical sweep bindings, and a gpt-6.1-sol template default
fail both. Changing the :306 per-model xhigh or the :78 recheck binding
passes the old test and fails the new one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* token-efficiency profile: accept the selection, carry the three code-navigation tools

The profile's label drops "Drafted, not accepted" for a dated acceptance of the
selection, citing the routing record, which joins recipe_paths so the profile
resolves only while the record is present. jcodemunch-mcp, codebase-memory-mcp
and ast-grep join component_ids and required_commands: the SubagentStart carrier
names them as task-appended lanes and the code-navigation layer's current choice
names all three (catalogs/landscape/foundation.json). Their versions stay in
manifests/stack.json (1.108.319, 0.11.0, 0.45.3); a manifest profile has five
keys and carries no version or wiring field, so neither is added.

tests/test_adoption_status.py restates the split the profile is checked against:
NAVIGATION_CHOICE, asserted against the code-navigation current choice, leaves
OPTIONAL, and the Ultracode tool split becomes 13 profile rows and 3 optional
rows against 17 component_ids.

Failing-first: the restated tests fail against the previous manifest (2 of 3 in
TokenEfficiencyProfileTests) and the previous tests fail against this manifest
(the same 2, plus ProfileTableTests.test_pin_columns_match_the_pin_files, whose
adoption/README.md cells are outside this unit's paths). manifests/evidence.json
is re-registered in the branch's last commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Routing record: accept the token-efficiency profile, state the three tools' wiring

The record no longer says the profile is "not decided here"; it makes the claim
that adoption/manifest.json now cites. The Decision gains a dated acceptance that
bounds itself to the selection and its routing (structural validation, not a host's
acceptance), the pin and the Claude Code and Codex wiring of jcodemunch-mcp,
codebase-memory-mcp and ast-grep with the file that carries each, the basis for
adding them (the SubagentStart carrier, the code-navigation current choice, the
2026-09-25 Ultracode run), and where #508 differs: it lists ast-grep as the
structural-code lane, makes jCodeMunch an owner only once wired and lists
codebase-memory-mcp as not a member, so the record does not rest on #508 for those
two. Two alternatives (leave the three optional; a new manifest key) and two
overturn conditions (#508 or Gate A changes a row; pins arrive) are added, and the
Sources name the new files and #508 lines 85, 86 and 103.

Every path:line cite in the whole record resolves in this branch (115 cites, 28
bare paths); the record keeps its five sections and one table, which
tests/test_task_model_routing.py holds.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* token-efficiency-stack: restate the optional-row split for the three tools now in the profile

Three statements had become false: that jCodeMunch, ast-grep and codebase-memory-mcp
are optional rows outside the profile, that the profile has 14 component_ids, and
that the Ultracode run's 16 tools are ten profile rows and six optional rows. The
profile paragraph now lists the three as the code-navigation layer's task-selected
tools (none a layer winner), the optional-row paragraph keeps Context Hub, the
viewers and OmniRoute, and adds why client_wiring still checks only Serena,
SocratiCode and ai-memory, that the three have no platform pin (so --pinned-versions
reports them unchecked) and that a bootstrap refuses the profile until they are
named in --allow-unpinned (adoption/bootstrap-linux.sh --help: exit 3). The
Ultracode paragraph becomes 17 component_ids, thirteen profile rows and three
optional rows, matching tests/test_adoption_status.py.

The review of this unit asked for this reconciliation (docs/token-efficiency-stack.md
lines 264-270); the file is outside the brief's allowed paths, so it is its own commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Test the token-efficiency profile against the routing record that accepts it

The record's Decision says the profile's label accepts it and names the record,
that the record is one of its recipe_paths, and that jcodemunch-mcp,
codebase-memory-mcp and ast-grep are component_ids and required_commands. The new
test in tests/test_task_model_routing.py holds those claims to adoption/manifest.json
and requires the record to name each tool. It compares no version: manifests/stack.json
owns them, and the record now dates the ones it quotes at e45328d.

Failing-first: against the previous manifest the test fails on the label ('Accepted'
not found in 'Drafted, not accepted: ...'); against this branch's manifest all 8
tests in the module pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* token-efficiency profile: README pin cells, regenerated grand list and re-registration (companion)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* token-efficiency profile: back to the 14 pinned components; the three code-navigation tools stay optional rows

Round 1 (24c8703) added jcodemunch-mcp, codebase-memory-mcp and ast-grep to the
profile's component_ids and required_commands. Neither adoption/pins-linux-x86_64.json
nor adoption/pins-macos-arm64.json lists them, and both bootstraps fail closed on a
selected component with no pin: adoption/bootstrap-linux.sh:169 and
adoption/bootstrap-macos.sh:220 print "No pin in <file> for selected component(s)" and
exit 3, and adoption/bootstrap-macos.sh:176-190 records that no component is exempted
from a pin by default. PR #540's validate-macos job failed on it (3 tests of
tests/test_adoption_bootstrap_macos.py TokenEfficiencyPlanTests), reproduced locally at
8ca7895 with `python3 -m unittest tests.test_adoption_bootstrap_macos
tests.test_adoption_bootstrap tests.test_adoption_launchd`: FAILED (failures=3,
skipped=38).

The profile is again the 14 pinned components. It keeps the accepted label and the
routing record in recipe_paths. The three tools stay the code-navigation layer's
task-selected tools and the carrier's task-appended lanes, installed on demand from their
recipes, until each has a reviewed pin on both platforms and a host receipt.

tests/test_adoption_status.py returns to main's OPTIONAL set of eight and the
(16, 14, 10) Ultracode split, and TokenEfficiencyProfileTests gains
test_every_profile_component_has_a_pin_on_both_platforms, so a profile that outruns the
pin files fails in this file's own module set and not only in the bootstrap plan tests.
tests/test_task_model_routing.py holds the record's claims about the three: outside
component_ids and required_commands, unpinned on both platforms, named by the
code-navigation current choice and by the carrier's lane ids.

Failing-first: with these tests and the round-1 manifest (17 component_ids), 4 of the 12
tests in TokenEfficiencyProfileTests and TaskModelRoutingRecordTests fail, 8 failures
counting subtests ("Tuples differ: (16, 17, 13) != (16, 14, 10)", "'jcodemunch-mcp'
unexpectedly found in [...]", the pin-file membership checks). With the manifest of this
commit all 12 pass.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* token-efficiency-stack: restore the optional-row split, since the three tools are not profile members

This reverts 12a587e. docs/token-efficiency-stack.md is byte-identical to origin/main
(11227bf) again: the profile has 14 component_ids, jCodeMunch, ast-grep and
codebase-memory-mcp are task-selected options in the code-navigation layer's current
choice and stay optional rows, and the Ultracode run's 16 tools are ten profile rows and
six optional rows. The statements 12a587e rewrote (three joined the profile on
2026-09-30; a bootstrap refuses the profile until they are named in --allow-unpinned; 17
component_ids, thirteen profile rows) described a profile that adoption/manifest.json no
longer has, because both bootstraps fail closed on a component without a pin and none of
the three has one. tests/test_adoption_status.py checks the ten/six split against the
receipt's tool list. The reasoning is recorded once, in
docs/decisions/2026-09-30-task-model-routing.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* adoption README: the token-efficiency row says accepted, and its cells describe the 14 pinned components again

d5369cb wrote the row for 17 members: "14 of 17 (all 14 at v2026.09.26.2)" in both pin
columns, the three code-navigation tools in the description, and a note that a
bootstrap of the profile needs them in --allow-unpinned. The profile has its 14 pinned
components again (previous commit), so the description, both pin cells and the note are
main's, and only the acceptance wording stays: "Accepted 2026-09-30 as the selection",
linking the routing record.

The pinned release v2026.09.26.2 covers the same 14 of 14 as HEAD, so
tests/test_adoption_docs_consistency.py (ProfileTableTests.cell_errors) asks for no
note on that tag; the notes for v2026.09.25.2 and v2026.09.26 are checked against
their own tags and hold.

Failing-first: with the round-1 row and the 14-component manifest,
ProfileTableTests.test_pin_columns_match_the_pin_files fails ("token-efficiency Linux
pins: table says '14 of 17', adoption/pins-linux-x86_64.json gives 'all 14'"). With
this row the module passes: Ran 37 tests, OK (skipped=1). The one skip is data
dependent, not environmental: no profile's coverage differs between the pinned release
and HEAD, on origin/main as well as here.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Routing record: the three code-navigation tools stay outside the accepted profile, for want of a pin

The record accepted the token-efficiency profile and, in round 1, added jCodeMunch,
codebase-memory-mcp and ast-grep to it. It now accepts the profile as it is, its 14
pinned components, and says why the three are not members: both bootstraps fail closed
on a selected component with no pin (adoption/bootstrap-linux.sh:144-172,
adoption/bootstrap-macos.sh:177-223, and the macOS comment at :177-187 that no component
is exempted from a pin by default any more), none of the three has an entry in
adoption/pins-linux-x86_64.json or adoption/pins-macos-arm64.json, and
tests/test_adoption_bootstrap_macos.py:1015-1017 requires the profile's plan to resolve
every component with no --allow-unpinned.

- Alternatives: leaving the three out is the chosen state; adding them (round 1) is a
  rejected alternative with its failure (Adoption bootstrap smoke run 36728291629, job
  validate-macos, step "Gate on the adoption test modules"; reproduced locally at
  8ca7895, failures=3) and the reason --allow-unpinned does not rescue it.
- Decision: "Three tools join the profile" becomes "Three tools stay outside the
  profile"; the wiring bullets, which describe how each installs on demand, stay; the
  "14 of 17" coverage and the exit-3 sentence are gone; "Where #508 differs" becomes
  "#508's rows": leaving the three out agrees with #508 for jCodeMunch (line 86) and
  codebase-memory-mcp (line 103), and ast-grep (line 85, the structural-code lane) is
  out only for want of a pin.
- Overturn condition: the follow-up unit that adds a tool once it has a reviewed pin on
  both platforms and a host receipt for each, with the files it must restate in the same
  change (adoption/README.md row, token-efficiency-stack.md split, OPTIONAL and
  CARRIER_TOOLS in the two tests).
- Header and Sources: the citations are at e45328d while the branch is based on
  11227bf, where examples/claude-native/workflows/README.md has grown by 249 lines; the
  record says so. The pin-rule files it now cites are unchanged between the two bases.

tests/test_task_model_routing.py (previous commit) holds these claims to the files.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Test the routing record against D4's Codex routes

tests/test_task_model_routing.py predated D4 (#542, merged as 1f2cdce) and failed on the
stacked tree (validate job 110072600341): two quoted `model = "gpt-6-astra"` lines and the
test that expected GPT-6.1 Sol to be routed nowhere.

- A quoted value counts only as a whole value, so `model = "${CODEX_MODEL}"` is not met by
  the template's `default_subagent_model` line.
- GPT-6.1 Sol: the table's Sol rows are exactly the Sol-primary record's three routes (the
  Codex coordinator at ultra, primary workers and generic children at max), and every file
  in the routing places (now with recipes/ and adoption/agents/) that binds a GPT-6.1 model,
  a constant such as CODEX_MODEL_CURRENT included, is cited by a Sol row. __pycache__ and
  node_modules are skipped: CI flagged prove_codex_lane's bytecode.
- The Codex user template is rendered through tools/adoption/render_config.py for every
  pinned platform, as tests/test_render_config.py's CodexModelTests do, and must give the
  models and efforts that the coordinator and generic-children rows name.
- The task classes add complex-workflow coordination and a single consequential judgment.

Fails against the record at this commit (5 failures, 1 error); the next commit restates it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Routing record: restate the Codex rows for D4 (#542)

D4 made GPT-6.1 Sol the Codex coordinator (ultra) and primary-worker model (max), with GPT-6
Astra for complex-workflow coordination (ultra) and a single consequential judgment (max):
docs/decisions/2026-09-30-sol-primary-quality-defaults.md and the model-currency addendum of
2026-09-30. The record still listed Sol as pending and routed nowhere.

- Six Codex rows, read at 1f2cdce: the Codex judgment role carriers (Astra, max); the
  coordinator and interactive default (the template's ${CODEX_MODEL} at ultra, rendered as
  gpt-6.1-sol for the Linux pin 0.159.2 and gpt-6-astra for the macOS pin 0.155.1); primary
  workers (the stack-worker profile, the lane's worker pins and live proof, the recipe's
  command); generic children; complex-workflow coordination and escalation (instruction only).
- Context and Decision state the D4 routing and its dependence on the Codex pin. No
  automatic router exists, and D04/D06/D11 stay off until the D3 promptfoo A/B, as before.
- Overturn: the user's next decision or D4's reopening condition, and a Codex pin crossing
  0.159.1 on a platform. Sources: D4's records, the enforcement points and the CI job.

The Claude rows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Test each GPT-6.1 binding against the Sol rows, by TOML table and command profile

The GPT-6 cross-family review of round 2 (901ed76, needs_changes, medium): the "nowhere
else" check compared files only, so a `[profiles.security-review]` table with
`model = "gpt-6.1-sol"` added to the user template, a file the coordinator row already
cites, left all nine routing tests passing.

- Bindings are counted one by one: a TOML key by its table (parsed with tomllib: `model`,
  `agents.default_subagent_model`, `profiles.<name>.model`, on the ID or on
  `${CODEX_MODEL}`), a key or constant elsewhere (CODEX_MODEL_CURRENT), and a command's
  `-m`, named with the profile the command selects (`-p stack-worker -m`).
- Each binding must be one a Sol row cites by file and name. A quoted TOML key names the one
  key of that name and value (a quoted `[table]` header scopes it), and a quote that fits
  two keys is reported. Each cited binding must be one D4 makes: the template's `model` and
  `[agents] default_subagent_model`, the render constant, the stack-worker profile's
  `model` and the stack-worker command's `-m`.
- Regression: the planted profile, in memory, on the ID and on the placeholder, and then
  cited by its table from the coordinator row; each variant is reported.

Fails against the record at this commit: the stack-worker command's `-m` in the profile's
header (line 3) and in the live proof's help (tools/adoption/prove_codex_lane.py:36) are
bindings no Sol row cites. The next commit cites them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Routing record: cite the stack-worker command where the profile header and the live proof write it

The binding-level check of the previous commit found two GPT-6.1 bindings that the
primary-worker row did not cite, both the stack-worker command's `-m`, which D4 routes: the
profile's header (adoption/templates/codex.stack-worker.config.toml:3) and the live proof's
help (tools/adoption/prove_codex_lane.py:36). The row now quotes both, beside the recipe's
command it already quoted. No other row changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Re-register the changed hash-listed files in manifests/evidence.json (hot-file protocol)

The last commit of the branch carries the hot files: the stacking ref's manifests/evidence.json
with this branch's changed hash-listed files re-registered, and the regenerated component-evidence
matrix and new-host grand list (docs/lanes.md hot-file protocol).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins pushed a commit that referenced this pull request Oct 1, 2026
…kills deny rules and the retained pin verification (PR #553 Gate A review, items 1, 2, 4 and 6)

- Listing record Scope: a "#381 interaction" paragraph. Codex arms B and A go from 11 to 25 visible skills
  (14 new, agent-browser, semgrep, codeql, security-audit and find-skills among them); arm N ignores the
  user configuration and, by the Gate A owner's count, already saw 26. M4 (>= 90% routed fetches; 5 of the
  fetch lane's 10 arm-B opportunities are seed-codex-web-table-1..5) and M8 (>= 80% per required lane;
  2 of symbol-references' 9, 1 of ai-memory's 7) could be hit by a diverted call that the B-versus-A gates
  cannot see. Counts from the #381 preregistration and the two manifests.
- Decision point 9 states the Gate A owner's decision as theirs: Amendment 4 names the catalog as part of
  the measured practice, no threshold is loosened, and diverted calls are reported, never reclassified.
- Decision point 4 records the Skills CLI deny rules; point 8 cites the retained pin verification output;
  a new overturn condition covers CLI command or rule-matching changes; Sources add the permissions page,
  skills@7407f389 src/cli.ts L336-402 and the find-skills body.
- Skills-trial addendum: the fraction plan's step 3 stays outside #381 window W, and a change after the
  Amendment 4 seal re-seals; host step 1 removes the retired skill from a shell outside a Claude session;
  two evidence-class rows for the deny-rule test and the pin verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 1, 2026
…re-pins, one retirement, the skill lifecycle guide and MCP_TIMEOUT parity (unit F3) (#553)

* Skills: list every model-invocable skill, enable Codex, re-admit skill-creator

User directive 2026-09-30: every skill invocable from the LLM-native end.

- adoption/skills/manifest.json: claude_listing on for all 26 skills without
  upstream disable-model-invocation (grill-me and improve-codebase-architecture
  stay user-invocable-only, annotated); codex_enabled true for the 17 that were
  off; skill-creator pinned from anthropics/skills@8a1541c4 (tree 3cf9a8db,
  SKILL.md sha256 dcd4803e, 319 chars) on for Claude, off for Codex, which
  ships .system/skill-creator; the excluded row keeps only the openai copy;
  find-skills and security-audit gaps rewritten; cap 8000 -> 10500, sums
  10074 (on) and 9931 (Codex); prune rule no longer demotes a listing.
- Claude template: skillOverrides mirrors the manifest;
  skillListingBudgetFraction 0.05.
- Codex template: [skills] max_context_tokens = 6000 (rust-v0.157.1 schema).
- Runtime-worker skills: security-audit's exclusion met its overturn and is a
  reuse_ref entry (security; coding, orchestration); skill-creator reuses
  main's pin; README and research tables follow.
- Tests: LlmNativeListingTests, ListingBudgetTemplateTests and
  test_security_audit_is_reused_since_main_promoted_it (each failed against
  the unchanged data first); the bare-exclusion control builds its own fixture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Decision records: 2026-09-30 LLM-native skill listing and the skills-trial addendum

- docs/decisions/2026-09-30-skills-llm-native-listing.md: context, the
  alternatives (keep the 09-26/28 states; fork the two upstream user-only
  skills; a fixed SLASH_COMMAND_TOOL_CHAR_BUDGET; default 1% with name-only
  trimming; Codex skill-creator both ways), the decision, overturn conditions
  (listing overflow at 200k, measured proactive invocation, client or pin
  change, a path selector, upstream user-only skills) and sources with lines.
- Skills-trial addendum 2026-09-30: every listing and Codex change, the cap
  change and the fraction arithmetic, the measure-then-lower plan (claude
  --debug -p ok on sonnet and opus[1m], /skill-doctor, codex doctor), window
  effects, the new prune rule, the kept winners' re-record note, the upstream
  user-only replacement path, the skill-creator overturn and its Codex
  name-collision residual, and the host steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills: fold the 2026-09-30 source review (six re-pins, resolving-merge-conflicts retired)

Pre-existing uncommitted changes observed in the main checkout; original
author not established (snapshot r2, tracked.diff sha256 314bd1b260da0939).
Every pin was re-verified from a blobless clone of its source repository
(tree SHA, SKILL.md sha256 and bytes, PyYAML description length,
disable-model-invocation); all 28 selected paths are unchanged at each
upstream HEAD.

- adoption/skills/manifest.json: search-first -> affaan-m/ECC@c70874fa
  (description 141 -> 328 chars); diagnosing-bugs, tdd, codebase-design,
  improve-codebase-architecture -> mattpocock/skills@d81f3a18; semgrep ->
  trailofbits/skills@82fe8226 (license wording kept consistent with the
  other eight trailofbits entries). resolving-merge-conflicts moves to
  excluded[] with a dated "retired" marker: upstream removed it in daa01d8
  (2026-09-24) and it is absent at d81f3a18. Sums: Claude on 10191 (cap
  10500), Codex 10048, of which 9872 reach the implicit catalog; the Codex
  budget citation names rust-v0.159.2 (render.rs L19-22, L126-152).
- Claude template: resolving-merge-conflicts "off", since the deep merge
  never removes a host's earlier "on".
- Runtime-worker skills: the six reuse_ref mirrors follow; the retired
  entry and its four coverage selections go; 137 skills, 1042408 bytes,
  37215 description chars; README sums (stale since the previous commit)
  and research notes follow.
- tests/test_skills_manifest.py: the template test derives off overrides
  from "retired" (the r2 delta hard-coded the name) and a new test checks
  each retired entry names one unselected skill and a date. Both failed
  first: HEAD data -> 'resolving-merge-conflicts' not found in {}; new
  manifest with the old template -> 'on' != 'off'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills lifecycle guide and the attributed 2026-09-30 lifecycle record and evidence

adoption/skills/lifecycle.md is the one lifecycle document: how a task finds
its skill (listed skills, then search-first, then find-skills registry
discovery, then the landscape sweep), select and pin (blobless-clone
verification), install and inspect, activate and invoke per client, the
listing budget, update and recover, retire and remove. Folded from the r2
snapshot (pre-existing uncommitted changes observed in the main checkout;
original author not established) and rewritten where it had gone stale: the
"keep name-only/user-invocable-only" paragraph contradicted the LLM-native
listing, rtk prefixes were dropped from the portable command block, and the
finalization record (another unit's file) is a code path, not a link.
Every command and flag was checked: install_skills.py and skills_status.py
--help; Skills CLI v1.7.0 (7407f389, newest tag and npm latest) src/cli.ts
L394-401, src/list.ts L60-62, src/remove.ts L409-411; Claude skills docs
(watcher, /reload-skills, 1,536-char cap, overflow order); Codex skills docs
(auto-detect, [[skills.config]], allow_implicit_invocation) and render.rs
L19-24 and L126-152 at rust-v0.159.2.

docs/decisions/2026-09-30-native-skill-lifecycle.md is folded unchanged
below a provenance paragraph and one dated line that marks its listing
states and sums as superseded by the LLM-native listing record.

Evidence: native-skill-lifecycle-20260930 (probes, publication checks,
receipt, verification) and native-skill-finalization-20260930 (8 files),
byte-identical to the snapshot and scanned (validate.py --scan-file, host
user name, client-config quotes: none). convergence.json was left out: it
froze hashes of the uncommitted tree, so validate_convergence.py
--all-recorded could never accept it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Claude template: MCP_TIMEOUT 120000 for MCP server startup parity with the Codex lane

MCP_TIMEOUT is Claude Code's MCP server startup timeout, default 30000 ms
(https://code.claude.com/docs/en/env-vars, re-read 2026-09-30); a server's
own "timeout" field bounds tool execution only
(https://code.claude.com/docs/en/mcp), and `claude mcp add --help` on
2.1.285 has no timeout option, so the value is global. The Codex template
gives serena 60 s and socraticode 120 s (startup_timeout_sec,
codex.config.template.toml L51 and L69); at Claude's 30 s default the same
servers can fail under Claude while they connect under Codex.

- adoption/templates/claude.settings.template.json: env MCP_TIMEOUT
  "120000" beside CLAUDE_CODE_SUBAGENT_MODEL; no other settings row.
- adoption/bootstrap.md step 4: one "changed after v2026.09.26.2" line.
- tests/test_install_claude_profile.py: McpStartupTimeoutTemplateTests,
  a standalone class at the end of the file; it failed first against the
  unchanged template (AssertionError: None != '120000').

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test_skill_usage: RenderTextAndCli ignores the caller's XDG_STATE_HOME

tools/skill-usage/skill_usage.py skill_lock_path() prefers any non-empty
XDG_STATE_HOME over --home, as the Skills CLI 1.7.0 does (the lock-path tests
in this file cite dist/cli.mjs L3746-3750). RenderTextAndCli passes --home to
reach fake-home's lock, so under a scratch XDG_STATE_HOME (the builder briefs
use one for test commands) no lock was found, every age was unknown and two
tests failed: test_json_output_round_trips_through_main found no 'tdd' among
the prune candidates, and test_render_text_default_output printed no
PRUNE-CANDIDATE flag. setUp now snapshots the process environment with
mock.patch.dict and drops that one variable. The module passes with and
without it (203 tests, skipped=11). Test-only change; the tool's lookup
order is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Decision records: the listing record and the skills-trial addendum at today's state

- 2026-09-30 LLM-native listing record: rebuilt on 11227bf; the user's
  restated directive quoted; Decision point 8 records the folded source
  review (six re-pins verified from blobless clones, the retirement with its
  "retired" marker and explicit off, the lifecycle guide as the single
  lifecycle document and the discovery order: listed skills, search-first,
  find-skills, the landscape sweep). Figures: 26 on (25 plus skill-creator),
  27 Codex-enabled (16 newly), Claude on 10191, Codex 10048 (9872 visible),
  5% of a 200k window about 3.9 times the on-listed sum; the Codex template's
  6000 tokens stays above the 5440-token default of gpt-6-astra and
  gpt-6.1-sol (272000-token windows at rust-v0.159.2); 25 visible skills
  render to about 11,900 bytes (about 2,980 tokens). New overturn condition
  for upstream drift or removal; sources for the clones, rust-v0.159.2
  lines, Skills CLI v1.7.0 lines and the docs pages with their hashes.
- Skills-trial addendum 2026-09-30: the same figures; a same-day source
  review paragraph (including the reviewed semgrep script change: explicit
  --max-target-bytes and an oversized report, metrics still off, no new
  network access); window effects for 16 newly enabled skills and
  search-first's new description; host steps remove the retired skill and
  its Codex table; evidence classes for the retirement tests, the
  blobless-clone pin checks and the lifecycle guide's commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills-trial addendum: search-first's re-pin is a source refresh, verdict unchanged

Upstream affaan-m/ECC c70874fa rewrote one line of skills/search-first/SKILL.md,
its description. No catalogs/landscape record pins the kept winner's commit
(grep for the old 2b6e8397 ref and for search-first across
catalogs/landscape/*.json: the records name the winner only), so the kept
status and the verdict stay as recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* install_skills.py --print-codex-config: path-keyed [[skills.config]] tables, never name-keyed (PR #553 review M1)

Codex applies a name rule to every loaded skill of that name (openai/codex rust-v0.159.2
codex-rs/config/src/skills_config.rs L109-119), so the printed `name = "skill-creator"` table also
disabled Codex's bundled .system/skill-creator. Each table now selects the installed SKILL.md by
`path`, which Codex matches against each loaded skill's canonical path_to_skills_md
(ext/skills/src/host_service.rs L366-371, host_outcome.rs L52-54; the Codex skills page L180-186
documents `path = "/path/to/skill/SKILL.md"`). A global install leaves a Codex skill only in the
canonical ~/.agents/skills/<name> (vercel-labs/skills@7407f389 src/installer.ts L392-402), which
Codex loads from its $HOME/.agents/skills root (host_roots.rs L103-108), so the path is built with
canonical_skill_dir under --home. A project-scoped manifest now prints only with --project-dir,
since its tables name <project>/.agents/skills/<name>/SKILL.md. Paths are TOML-escaped; a disabled
name that is not one path component is refused.

Tests (failing first against the unchanged installer): PrintCodexConfigTests (skill-creator by
path, ".." and quote handling, project copy, refused name), the scope test and ReuseRefGateTests
in tests/test_install_skills.py, and the runtime-worker print test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* skills_status.py: path-keyed Codex disables, the bundled-name failure and a token estimate of the Codex catalog (PR #553 review M1, L1, L3)

M1: a path selector matches when it resolves, as Codex resolves it (~ against the home, a relative
path against the config folder, lexical normalization, then canonicalization; utils/absolute-path
lib.rs L28-58, absolutize.rs L22-45, config/src/loader/mod.rs L573-582 and L1424-1447,
skills_config.rs L188-192), to the installed ~/.agents/skills/<name>/SKILL.md; without a known
path it matches by the skill folder name. Entries Codex ignores (both selectors, neither, a blank
name) select nothing, and a name is trimmed (skills_config.rs L188-210). A name-keyed disable for a
name Codex's bundled skills carry (imagegen, openai-docs, review-agent, skill-creator,
skill-installer at rust-v0.159.2) is reported as name_entry_hides_bundled_skill.

L1: budget.codex_default_budget_chars becomes codex_fallback_budget_chars (metadata);
codex_configured_budget_tokens (6000) equals the Codex template's [skills] max_context_tokens by
test; codex_catalog_description_chars (9,872) counts the skills Codex shows the model, and
upstream_allow_implicit_invocation: false marks grill-me and improve-codebase-architecture (their
agents/openai.yaml; provider/host.rs L147-148, model.rs L22-28). The reporter estimates the shown
catalog as render.rs charges it, each line and its newline at ceil(bytes / 4) (L154-160, L25,
L258-267, L1158-1174), and reports it beside the configured budget instead of comparing 10,048
characters with the 8,000-character fallback.

L3: the Codex template comment now says 25 catalog-visible skills, about 11,900 bytes and about
2,990 tokens by that charge, and five .system skills at rust-v0.159.2; a test ties the count to the
manifest.

Tests (failing first against the unchanged reporter and data): the path-selector,
bundled-name, ignored-entry and catalog-estimate tests in tests/test_skills_status.py and the
budget and template-count tests in tests/test_skills_manifest.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* skills_status.py: a Codex path selector no file can have selects nothing instead of crashing the check

TOML allows an escaped NUL in a string, and os.path.realpath raises ValueError on it, which would
have ended the whole status run on one malformed [[skills.config]] entry. Such a path cannot name
the installed SKILL.md, so it is treated as no match. Test (failing first with the ValueError):
test_a_path_no_file_can_have_selects_nothing_instead_of_crashing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills records and lifecycle guide: the path-keyed Codex table, the catalog budget fields and neutral fold provenance (PR #553 review M1, L1, L2, L3)

adoption/skills/lifecycle.md documents the path form (example table, upstream matching rules,
--project-dir for project manifests, the reporter's bundled-name failure) and the new budget
fields. The skills-trial addendum and the LLM-native listing record replace the name-collision
residual with the path decision, rewrite host step 3, record the budget rename and the recomputed
estimate (11,904 bytes, 2,987 tokens with a 28-character root), and cite the rust-v0.159.2 and
skills@7407f389 source lines read for this repair.

L2: the listing record and the addendum now use the exact provenance wording "pre-existing
uncommitted changes observed in the main checkout; original author not established" with the
snapshot hash, and all three records say that the fold briefs' earlier attribution rested on a
process census of file writes, which does not establish document authorship, and that the lane
concerned asked for the neutral wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Listing record: cite the installer and status-reporter functions by name, not by shifting line numbers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Codex template [skills] comment: the 272,000-token window of both catalog defaults (PR #553 review L3)

Main now renders the template's model from the Codex pin (gpt-6.1-sol from 0.159.1, gpt-6-astra
before it), so the unset-budget figure names both models' 272,000-token context_window
(codex-rs/models-manager/models.json at rust-v0.159.2) instead of gpt-6-astra alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills lifecycle guide and host steps: restart Codex after the config.toml change; base reference 1f2cdce

The Codex skills page (developers.openai.com/codex/skills, "Enable or disable local Codex
skills", read 2026-09-30, page sha256 d1579156...) says "Restart Codex after changing
~/.codex/config.toml" right after the [[skills.config]] example, which the guide's Codex bullet
and the addendum's host step 3 now say too. tools/adoption/apply_codex_lane.py still writes no
[skills] key at origin/main 1f2cdce, the branch's new base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Claude template: deny the skills CLI's install, remove and update spellings in a session (PR #553 Gate A review, item 2)

The pinned find-skills body tells the model to run `npx skills add <owner/repo@skill> -g -y` and `npx skills
update` (vercel-labs/skills@7407f389 skills/find-skills/SKILL.md L28-29, L90, L100), and the template's
bypassPermissions default denied neither. Installation goes only through tools/adoption/install_skills.py,
whose own add and rollback remove run as subprocesses that Bash rules do not see.

41 Bash rules cover every writing spelling of skills@1.7.0 (src/cli.ts L336-402: add/a/i/install,
remove/rm/r, check/update/upgrade, experimental_*) in four invocation forms: bare `skills`, `npx [flags] skills`,
`npx [flags] skills@<version>` (without the one-letter aliases, which are common find-query words) and a
path ending in `bin/skills` (the pinned <tools-root>/skills-1.7.0/bin/skills, node_modules/.bin/skills).
check/update/upgrade end in `<word>*` where another `*` precedes, so the bare command matches too.
`Edit(~/.agents/**)` keeps the file tools off the canonical skill folders the installer hash-checks.
Discovery (`find`/`search`), `list`, `init`, `use` and `--version` stay allowed.

Rule form: https://code.claude.com/docs/en/permissions ("Wildcard patterns", "Compound commands",
"Wrappers", "What a Bash rule doesn't match", "Read and Edit", "Symlinks"; fetched 2026-09-30).
The new test failed first against the previous template (66 failures: 42 missing rules, 24 samples
not denied).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills lifecycle guide: no ad hoc installs in a session, removal outside one, and the listing wording (PR #553 Gate A review, items 2 and 5)

- Step 3 and Install and inspect: a session never installs a skill ad hoc; it records the candidate and the
  coordinator pins it. The Claude template denies the Skills CLI's writing commands in four forms and
  Edit(~/.agents/**); the rules match command text, not every route to the program (permissions page,
  "What a Bash rule doesn't match").
- Retire and remove: the host batch runs the CLI's remove from a shell outside a Claude session.
- Step 1 lists every model-invocable skill within the listing budgets, not every enabled skill: the two
  upstream user-only skills are not listed, and an overflowing listing loses descriptions.
- The absent docs/decisions/2026-09-30-sota-native-finalization.md citation now points at the committed
  evaluation artifact; the finalization record lands with unit F1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* MCP startup-timeout decision record; the bootstrap step-4 note links it and names the skills deny rules (PR #553 Gate A review, item 3)

docs/decisions/2026-09-30-mcp-startup-timeout.md records MCP_TIMEOUT "120000": the alternatives (Claude
Code's 30 s default, env-vars L470; no per-server startup option, `claude mcp add --help` on 2.1.286 read
2026-09-30 with a scratch HOME, the F3 delta for 2.1.285; MCP_CONNECT_TIMEOUT_MS and
CLAUDE_CODE_MCP_STARTUP_WAIT_MS are different waits, L460 and L317), the cost (a `-p` run with
--mcp-config waits up to MCP_TIMEOUT for pending servers, headless L240) and the overturn conditions
(a measured start-up distribution under 30 s for every stack server on the reference host, or an
upstream per-server option). The step-4 note and McpStartupTimeoutTemplateTests' docstring link it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Retain the skill pin verification output as an evidence artifact (PR #553 Gate A review, item 6)

evidence/artifacts/skills-pin-verification-20260930/pin-verification.json keeps the checker's 28 rows
(codebase-design, improve-codebase-architecture, semgrep and skill-creator included) from the 18:02Z run
against blobless upstream clones, with the method, the checked manifest blob and a 22:53Z recheck after
a fresh fetch (every remote HEAD confirmed by git ls-remote) whose output was byte-identical. Our own
integration check of upstream git objects, not an upstream test or a client run. Registered in
manifests/evidence.json by the last commit (hot-file protocol).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Listing record and skills-trial addendum: the #381 interaction, the skills deny rules and the retained pin verification (PR #553 Gate A review, items 1, 2, 4 and 6)

- Listing record Scope: a "#381 interaction" paragraph. Codex arms B and A go from 11 to 25 visible skills
  (14 new, agent-browser, semgrep, codeql, security-audit and find-skills among them); arm N ignores the
  user configuration and, by the Gate A owner's count, already saw 26. M4 (>= 90% routed fetches; 5 of the
  fetch lane's 10 arm-B opportunities are seed-codex-web-table-1..5) and M8 (>= 80% per required lane;
  2 of symbol-references' 9, 1 of ai-memory's 7) could be hit by a diverted call that the B-versus-A gates
  cannot see. Counts from the #381 preregistration and the two manifests.
- Decision point 9 states the Gate A owner's decision as theirs: Amendment 4 names the catalog as part of
  the measured practice, no threshold is loosened, and diverted calls are reported, never reclassified.
- Decision point 4 records the Skills CLI deny rules; point 8 cites the retained pin verification output;
  a new overturn condition covers CLI command or rule-matching changes; Sources add the permissions page,
  skills@7407f389 src/cli.ts L336-402 and the find-skills body.
- Skills-trial addendum: the fraction plan's step 3 stays outside #381 window W, and a change after the
  Amendment 4 seal re-seals; host step 1 removes the retired skill from a shell outside a Claude session;
  two evidence-class rows for the deny-rule test and the pin verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test_skills_status: a neutral fixture root for the folder-name path match

The fixture paths /home/example/... match the coordinator's committed-diff privacy pattern
(/home/[a-z]+/), although they name no host. /srv/example-home/... keeps the test's meaning: with no
known home, a path entry matches by its skill folder name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Skills CLI deny rules: the three bare-form verbs end in <word>* so Context Mode's own command path denies the bare command too

The Gate A owner's decision on #553 round 3: Context Mode (mksglu/context-mode 1.0.169, src/security.ts
evaluateCommandDenyOnly / matchesAnyPattern / globToRegex) applies the same user deny rules to ctx_execute and
ctx_batch_execute commands with a plain ^glob$ regex, so `Bash(skills update *)` let a bare `skills update` through
there. `Bash(skills check*)`, `Bash(skills update*)` and `Bash(skills upgrade*)` close that path; the short aliases
keep " *" because `skills init` is legitimate. A new test runs the installed security module (CONTEXT_MODE_SECURITY_JS,
skipped with a named reason when unset) against the template's rules: failing-first on the previous template, three
bare commands allowed; the existing rule test also failed first on the three renamed rules.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant