Repository navigation
PR-H: E2E preregistration for the token-save-practice stack (frozen tasks, metrics, gate, procedure) - #381
Merged
seathatflowsinourveins merged 1 commit intoSep 27, 2026
Conversation
Owner
Author
Review findings and resolutions (one combined repair round)Two independent read-only reviews ran against the original build: a Claude evidence-reviewer
Integrator-side fixes beyond the two reviews
None of this authorizes running the E2E: the preregistration's own "MUST merge before any |
…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
force-pushed
the
claude/token-e2e-preregistration-20260926
branch
from
September 27, 2026 06:17
2733833 to
572f921
Compare
seathatflowsinourveins
deleted the
claude/token-e2e-preregistration-20260926
branch
September 27, 2026 06:39
4 tasks done
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>
This was referenced Sep 27, 2026
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>
This was referenced Sep 27, 2026
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>
5 of 7 tasks
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>
This was referenced Sep 29, 2026
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>
This was referenced Sep 29, 2026
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>
5 tasks done
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…
5 tasks done
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>
5 tasks done
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>
4 of 6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
as
evidence/artifacts/child-lane-baseline-20260926/). Addsevidence/artifacts/token-adoption-e2e-20260926/{README.md,RUNBOOK.md, preregistration.json,token-e2e-run.mjs,fixtures/{table.json,events.jsonl}}andtests/test_token_e2e_preregistration.py, freezing — before any organic run — the tasklist (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 themeasurement runbook (
RUNBOOK.md) operationalizingchild-usage.mjs/skill_usage.py/Loki/ccusage collection.
gpt-6-astra, max effort, live search,workspace-write, no git) fromboth 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.pysource. 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.separate review step, not a rubber stamp):
blueprints/token-practice-e2e/toevidence/artifacts/token-adoption-e2e-20260926/{README.md,preregistration.json,...},matching
adoption-audit/PLAN.mdline 402 (§6's own PR-H file spec) and the siblingbaseline 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/sourcepath was updated and programmatically verified toresolve to a real file;
.gitignoregained a negation line (matching its existingper-directory
*.jsonloverride pattern) so the 84 KB synthetic fixture is actuallystageable, not silently dropped.
examples/claude-native/workflows/into the sameevidence directory: that directory's
test-envelope.mjssweeps every file declaringexport const metaand requires the small set of reusable, coordinator-dispatchedworkflows there to share a byte-identical
PACKETprefix and a hardcodedROUTINGtable. 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.
scriptPathaccepts anycommitted path, so this does not change how the future run launches it.
"not yet opened." Independent Git/
ghverification found PR-A's base branch merged asLane 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:277still examines onlySubagentStart); no branch/PR continues itsremaining 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 headlessintegrator session, so
claude -p --agent evidence-reviewersubstitutes it), a GPT-6cross-family review (
gpt-6-astra, max effort, live search, read-only), and one combinedrepair round addressing all 13 holding findings (11 Claude + 2 GPT-6, one high) with
failing-first evidence for each — see
BUILD-NOTES-repair1.mdand the finding table postedas a PR comment.
origin/mainadvanced by 4 commits (including #376 and #364, both nowmerged) while this unit was in review; the branch was rebased via this repo's hot-file
replay protocol (
rec/replay.sh) rather than a plaingit rebase, and the dependency tablebelow reflects the post-rebase, post-repair state.
Verification
Evidence class: this PR is itself
synthetic/local_integration— a frozen protocoldocument, a syntax-checked-only script and two synthetic fixtures, following
docs/acceptance-evidence-policy.md. It establishes no organic adoption, no providersavings and no new acceptance; it only becomes actionable once its own merge-before-run
dependencies (below) land and the capability gate passes.
Residuals
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 on2026-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 listexactly) — 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:277onmainstillexamines only
SubagentStartrows, exactly the gap AA §6 change item 1 names as stillneeding a fix.
full-save/PLAN.md§8 (revision-1 review, finding Install native token-saving tools and publish portable lifetime evidence #8) independently caughtand 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 openPR'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.
stack-workerprofile (adoption/templates/codex.stack-worker.config.toml,tools/adoption/apply_codex_lane.py) that Codex arm B depends on is still not a PR andnot pushed: it exists only as an uncommitted checkpoint on
claude/codex-lane-token-stack-20260926(commita6ed3c57), confirmed absent from thepost-rebase
origin/main. Its own author's review recorded open correctness gaps intools/adoption/prove_codex_lane.py(a worker-cleanup check that only confirms a processgroup 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.
623d34fa,c71d66d0) since this unit's Claudereview — 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 mergeitself. 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, itskeep_keyslists still omitworkflow.run_idandtool_use_id— the GPT-6-and-Claude-reviewed repair round did notinvent this being fixed by the merge, and the preregistration now says so explicitly.
reuse-296-15(an otel-tui information need) requirednetwork access that its only assignable role,
source-scout, explicitly forbids("Do not... use the network,"
source-scout.md). Rather than weakening that rule orinventing 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.mjsrefuses tolaunch 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.
codex-rev/runwt.sh) cites"
docs/contributing-evidence.mdsection 8" for its four review points; that file's realheadings 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.
Claude Sonnet 5, per this session's live governingsystem reminder, not the computed workflow task's stale
Claude Opus 5.5 (1M context)line — the same substitution sibling units
prci/prralready made for the same reason.SOTA sources
agent()/pipeline()/parallel()/phase()/log()/theargsglobal, prompt-cache sharing across matching siblings, the ban onDate.now()/Math.random()/no-argnew Date(), and the 4,096-item-per-call /1,000-agent-per-run size guideline) — Anthropic's own docs, mirrored in
examples/claude-native/workflows/README.md(checked againstclaude --version2.1.278on 2026-09-22, per that file's own version-gates note).
codex exec --help(checked directly against the installed binary):-m/--model,-c key=valueconfig overrides,-s {read-only,workspace-write, danger-full-access},-p/--profile,--skip-git-repo-check.adoption/templates/codex.stack-worker.config.toml(unmerged, branchclaude/codex-lane-token-stack-20260926@a6ed3c57) for the realstack-workerprofileinvocation shape (its own header comment).
examples/claude-native/workflows/child-usage.mjs(its ownUSAGEstring and argumentparser, lines 596-626) and
tools/skill-usage/skill_usage.py(itsargparsedefinitions)for the measurement runbook's real, confirmed CLI flags.
evidence/artifacts/token-e2e-ultracode-20260925/receipt.json(PR Token stack inside Ultracode subagents: 16/16 tools E2E with lifetime counters, exact comparisons and five new host receipts #296, merged2026-09-25T22:40:49Z) and
evidence/artifacts/token-e2e-codex-20260926/receipt.json(PR Token stack inside native Codex sessions: 11/15 tools with passing checks, four real limits, and a correction to #296's QMD row #343, merged 2026-09-26T14:24:54Z) for the reused task set (AA §8.1's "16 checked
tasks of Token stack inside Ultracode subagents: 16/16 tools E2E with lifetime counters, exact comparisons and five new host receipts #296 and the Codex set of Token stack inside native Codex sessions: 11/15 tools with passing checks, four real limits, and a correction to #296's QMD row #343").
docs/token-efficiency-stack.json(rows[].component_id, 24 entries, checked directly)as the canonical tool-name list behind the frozen task list's no-tool-names denylist.
evidence/artifacts/child-lane-baseline-20260926/README.md(PR Lane use per Claude child and Codex worker, with a frozen-window baseline for the workstation #369, merged 2026-09-26,confirmed as PR-A's base-branch merge via its file list) for the baseline-receipt half of
PR-H and its "three views, never summed" methodology, which the measurement runbook stays
consistent with;
full-save/PLAN.md§8 finding Install native token-saving tools and publish portable lifetime evidence #8 for the independent confirmation thatLane use per Claude child and Codex worker, with a frozen-window baseline for the workstation #369 is PR-A's merge and that its remaining tooling-fix scope is still open.
blueprints/retrieval-quality-v2/PREREGISTRATION.mdfor this repository's own establishedpreregistration structure, its Sealing/Amendment convention, and its append-only rule.
adoption/agents/claude/source-scout.md(PR Add security-reviewer role and targeted skill preloads (WP5 PR-B) #376, merged623d34fa) for the role's ownexplicit "do not... use the network" restriction behind the blocked otel-tui task.
observability/collector/collector.yamlat the mergedorigin/mainhead (c71d66d0),checked directly, for the persisting
workflow.run_id/tool_use_idfield-preservation gap.docs/token-efficiency-stack.json's 24component_idrows anddocs/token-session-handbook.md:106-151(not the scratch-onlycodex-lanes-block.txt) asthe denylist's real canonical sources, per the Claude review's finding.
adoption-audit/PLAN.md§3.1-§3.2, §4, §5, §7, §8 andfull-save/PLAN.md§4 — the frozenspec this PR operationalizes; cited throughout the committed document by section label,
never by its scratch path.
🤖 Generated with Claude Code