Skip to content

Add a local Claude review worker for essential pull request heads - #953

Merged
seathatflowsinourveins merged 18 commits into
mainfrom
claude/api-actions-review-worker-20261009
Oct 12, 2026
Merged

seathatflowsinourveins merged 18 commits into
mainfrom
claude/api-actions-review-worker-20261009

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Scope

  • What this PR changes: it adds a local Claude review worker, tools/claude-review-worker/.
    • A systemd user timer reviews each essential pull request head of us-equities-trading and native-agent-stack with Claude Code headless (Opus 5.5 at max) on an Anthropic API key.
    • It posts a claude-review/local commit status. On the private repository it also posts one sanitized comment.
  • Base commit: 86fa38442 (rebased from 8c95a50d1 after Regenerate the final-stack manifest after #1018 changed one of its sources #1025 landed; all 18 commits patch-identical, manifests/evidence.json taken from main and re-registered per commit). The first rebase, from the PR's earlier base b0b4b1097, is under "Rebase corrections".
  • Lane: lane:foundation.
  • Owned paths touched:
    • tools/claude-review-worker/** (new);
    • tests/test_claude_review_worker.py (new);
    • docs/decisions/2026-10-09-claude-review-worker.md (new);
    • docs/github-automation.md (a section);
    • manifests/evidence.json.

How it works:

  • Key: the repository's credential runner injects the key. The chain is srt (the network boundary, started without the key) → credential_run.py → bwrap → the pinned claude binary. The key is never in argv, a unit property or a file.

  • Sandbox: bubblewrap with no home directory, gh login or key store. Main's checkout is the working directory, read-only; the head (pr-head/) and the diff are data.

  • Instruction fence: CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 in every session, plus --restricted, --setting-sources user and --permission-mode dontAsk. Only Read, Glob and Grep are available. Hooks, plugins and MCP are off.

  • Spend: each run is debited (CRW) in the api-actions ledger before it starts. Each review is bounded at $11, the day at $55. Keys fill first, in the owner's order (api-4, api-3, api-2, api-5, anthropic-api, then api-6 to api-10; api-2 third since the owner's direction of 2026-10-11): the worker stays on a key until Anthropic reports it out of credit (402, or 400 credit too low) or revoked (401), skips a slot the credential runner refuses before any debit, and writes a switch row for each move.

  • Upstream alignment: every trading head gets the upstream-alignment rule (the command center's ruling of 2026-10-10 00:15Z). Each claim about external behaviour must cite its upstream source at a pin; an uncited or deviating claim is an [upstream] finding.

  • Security review: a Claude security-reviewer pass found three Medium issues, all fixed in 2acade9.

    • Head-derived citation labels were placed in the prompt's instructions; they now go to a data file.
    • The private-repository comment could render images and links; the findings now sit in a code fence that cannot be closed.
    • The gate did not cover the worker or the credential inventory; both are now essential paths, with the command center's additions.
    • Residual risks R1 to R3 are recorded as accepted boundaries.
  • After the GPT read at 4b8027b (CHANGES_REQUESTED, seven P2): all seven are fixed in forward commits.

    • P2-1: the pull request is read just before each POST; a moved or closed head marks what is unsent superseded, and a move after posting adds a superseded line to the comment.
    • P2-2: each POST is saved as sending first; a retry first finds the commit's latest claude-review/local status and this attempt's comment (a hidden marker line) by the same gh login, so a lost reply or a stopped tick never posts twice.
    • P2-4: a stream with lines but no readable record is unreadable_stream: the reservation settles as unknown and the attempt counts.
    • P2-5: a settle or void needs the ref's open debit, under the ledger lock (the harness's _open_debit); recovery leaves a ref another writer closed.
    • P2-6: L7, L8 and L9 name their required calls (7, 2 and 6) and fail when any call or result is missing or a required denial is answered.
    • P2-7: the decision record paraphrases the routing row instead of quoting it.
    • P2-3: each review now runs inside srt, Anthropic's sandbox runtime (@anthropic-ai/sandbox-runtime 0.0.79, the command center's ruling on the measured options): srt's namespace has only lo, and its host proxy passes api.anthropic.com alone. The inner bwrap stays the filesystem fence. Preflight refuses the tick before any key is read unless node (the mise 24.21.0), the pinned srt install, socat and a keyless check through the same chain all pass (lo only, the API through the proxy, example.com 403, no direct route, a host loopback listener never reached). R2 is closed.
  • After the GPT read at d784fe0 (CHANGES_REQUESTED, four P2), one forward commit:

    • Supersession is durable: a comment whose reply was lost before a head move is found by its marker and gets the superseded line; a failed superseding PATCH is retried on later ticks; open posting work is found from the stored attempt markers, not from the listed heads.
    • srt starts without the key. The inventory runner is srt's command and injects the key into bwrap and the client only; srt's node process, shells and socat bridges never hold it (measured by a process-environment scan with a fake key; the key-first order put it in node, sh, bash and three socat).
    • RecordRuleTest rejects a restored quotation in the record's direction sections (synthetic text), wrapped across lines or not; its detector finds the 25-word wrapped quotation of the 4b8027b form (1 span) and none in the current form, and that form spliced back fails the test on the detector's assertion. (At 3d0bead the detector stopped at line breaks and missed it; the CC's read found that.)
    • The final head read after posting is open work (verify: pending) until a head read settles it, so a failed final read or a stopped tick no longer leaves a superseded comment unmarked (the GPT micro at 3d0bead).
    • The ledger reference (the api-actions harness common.py, then not in git; under local git since d97aad49) is pinned by sha256 362cb74c…9a71 with line locators and a read-only copy, and the worker was run against it on one temporary ledger; a close of another workload's debit is now refused.
  • After the command center's read at 3d0bead and the GPT read at 0d08404 (forward commits, first pushed now):

    • bdc2df4b5: the CC's P3s. R1 names where the key lives (the runner in memory; bwrap, the client and its children in their environment), names CLAUDE_CODE_SUBPROCESS_ENV_SCRUB as unmeasured, and weighs srt's credential masking. A superseding PATCH now ends: 404 marks the comment deleted, and the worker gives up after 24 failed tries (a new test fails on the pushed worker). The ledger reference is also pinned by commit.
    • 04a3d74f6: the records-rule detector cites the Python 3.13 re and str.split() behaviour it rests on.
    • fb041ded9 and 9305bb01a: fill-first key order and per-run tokens (the owner's directions of 2026-10-10, relayed by the CC). Each settle records per-model tokens and the cache hit rate, and each settle or void row carries a use case. fb041ded9 adds four tests; seven tests fail on bdc2df4b5.
  • Rebase corrections (rebased onto main 8c95a50d1; Retire Claude review inside GitHub; AI reviews run locally #981, 8bffa2c4, removed the in-GitHub Claude workflows and so made these statements false):

    • docs/github-automation.md: this PR's copies of the three retired bullets (harness audit, PR review, toolkit read) are dropped in the conflict resolution. Main's "Claude review inside GitHub: retired" bullet is kept unchanged, and this PR adds only its "Local review of essential heads" bullet after it.
    • tools/claude-review-worker/README.md (two places): the paths input of claude-pr-review.yml is no longer offered as a way to review a refused head; it is named as removed by Retire Claude review inside GitHub; AI reviews run locally #981. anthropic-api-3 no longer "serves CI review first": its CI-review purpose ended with Retire Claude review inside GitHub; AI reviews run locally #981.
    • tools/claude-review-worker/worker.py (three comments): the stream-bounds pattern and the 1.10 cost factor are attributed to claude-pr-review.yml as it was before Retire Claude review inside GitHub; AI reviews run locally #981, not as a live workflow, and anthropic-api-3's CI-review purpose is named as ended.
    • docs/decisions/2026-10-09-claude-review-worker.md (seven places): anthropic-api-3 "served" CI review through the claude-review environment, which no workflow on main uses since Retire Claude review inside GitHub; AI reviews run locally #981; the removed tests/test_claude_pr_review_workflow.py is named as removed. The "Why" section no longer says the cloud path still waits on the federation fix. The fence flags are traced to the workflow until Retire Claude review inside GitHub; AI reviews run locally #981 removed it. The cloud path is no longer listed as an optional alternative. The overturn condition "the federation fix landing" becomes an owner decision to bring a cloud review back. The source read is pinned to 65cf2730, the last main commit with the workflow.
    • Nothing else changed: the other 15 commits apply as they were, with manifests/evidence.json taken from main and re-registered per commit.
  • The terminal commit 317cdd3e (the CC's micro at a3881b19, CHANGES_REQUESTED; the CC's adjudication at 1a610ba7 after the GPT read there):

    • B4, and the GPT read's P2 at 1a610ba7: when credential_run.py --check refused every key, the loop only skipped and still ended in its else suite (CPython 3.13.16 Doc/reference/compound_stmts.rst:165, :173), so each tick published "every configured key is out of credit" with no request made and no attempt recorded. Such a run is now key_check_refused: one error status saying the credential check refused every key, an uncounted, non-final attempt, no ledger row; the first tick with a usable key reviews the head as usual. A mix of credit and revoked refusals now says both.
    • B3: the boundary tests put a stand-in socat on their own search path; CI's ubuntu-24.04 runner has none, and validate-shard-0 failed on it.
    • B2: the record says no hosted run had succeeded (scheduled run 37988961127 ran on main on 2026-10-09 and failed). B5: the record counts the module's 94 tests.
    • Owner direction of 2026-10-11 ~22:33Z (via the CC): the default key order is api-4, api-3, api-2, api-5, anthropic-api, then api-6 to api-10.
    • Prompt caching (CC): the prompt puts its stable part (rules, task, upstream rule, answer shape) first and the pull request's line and citation counts last (anthropics/skills@dbd4588f prompt-caching.md:7, :27); the settings set promptCacheTtl: "1h" (anthropics/claude-code CHANGELOG.md:2970, 2.1.243), subagents unchanged.
    • The gh-login comment is back on self.login.
  • Which heads are essential: tools/claude-review-worker/essential-paths.json:

    • every us-equities-trading PR;
    • native-agent-stack PRs touching blueprints/us-equities/**, .github/**, scripts/validate*.py, tools/credentials/**, adoption/hooks/** and the other listed paths.

    This file is for the command center to approve.

SOTA sources

  • Claude Code (the pinned native client, 2.1.296), as installed:
  • bubblewrap (bwrap), the distribution package on the host; the profile and its measurements are in the decision record.
  • Anthropic's sandbox runtime, anthropics/sandbox-runtime@d9aac2098351ca17f3743fbaf6ecbd0051b7e00e (tag v0.0.79; npm @anthropic-ai/sandbox-runtime@0.0.79, tarball sha256 5a730e4367c264ccc4b592af01dab038a6c39db7c184efbd132841688fa854f1): src/sandbox/linux-sandbox-utils.ts:1343-1363,3378 (--unshare-net plus a host filtering proxy over a bound socket), src/sandbox/sandbox-manager.ts:355-415 (the allowlist).
  • GitHub REST API, commit statuses and issue comments: https://docs.github.com/en/rest/commits/statuses, https://docs.github.com/en/rest/issues/comments.
  • The api-actions harness ledger protocol (the coordination record's harness/common.py: an fcntl lock on <ledger>.lock, and debit before submit), followed row for row.
  • Claude Code CHANGELOG.md:2970 at anthropics/claude-code@3deb873 (2.1.243): promptCacheTtl and subagentPromptCacheTtl.
  • The claude-api skill, anthropics/skills@dbd4588f skills/claude-api/shared/prompt-caching.md: :7 and :27 (stable content before volatile content, the prompt's order), :130 (caches per organization, fill-first).
  • CPython 3.13.16 at python/cpython@cbc944f4, Doc/reference/compound_stmts.rst:165 and :173: a loop that only skipped ends in its else suite (the refused-check fix).
  • The command center's ruling ROW-cc-20261010T0015Z-api-routing.md (essential review routing and the upstream-alignment rule).

Evidence-class table

Claim Evidence class Command / receipt
The sandbox has only the bound paths: no home directory, gh login or key store; the API host is reachable native_proven probes.py offline O3, O4 (coordination receipt crw-probes/probes/20261010T004458Z)
No instruction file reaches a request: a control arm sends all five planted facts, the fenced arm none (nor the head's .claude rule fact; the skill fact has no positive control) native_proven (with a scripted stand-in model) probes.py offline L1 at 2acade9 (crw-probes/probes/20261010T013843Z, 18 of 18 PASS), rerun with the stricter grader (20261010T052121Z, 18 of 18)
Bash is unavailable; Read of /etc/hostname, .git, .env, .pem, outside files and a planted gh login are denied; no planted value or key reaches a request by any /proc (self, thread-self, 1) or /dev/fd route; a link in main's checkout is not followed out of the working directory; a run's config directory keeps neither the key nor a stored key response native_proven (with a scripted stand-in model) probes.py offline L4, L5, L7 to L11, O8; L7, L8 and L9 now with 7, 2 and 6 required calls answered (20261010T052121Z)
With Opus 5.5, the fenced arm knows none of the five facts and the control arm knows all; no MCP server loads; a planted secret-like string is not echoed; an uncited upstream claim yields an [upstream] finding native_proven probes.py live P1, P2, P6, P11 on api-4, $0.5557 (receipt crw-probes/probes/20261010T004508Z; the receipt reads FAIL as a whole because of the refused tool arm, and its P4 PASS was vacuous: no Bash call was made)
A live tool-call probe asking the model to try reading credentials was refused by the Opus 5.5 safeguard ([cyber]), so that arm is dropped; the offline L checks stand native_proven the same receipt, run tools
Ledger rows are compatible both ways: the worker reads the harness's 611 rows, and the harness counts a CRW row in the key's column local_integration a copy of the ledger (coordination record)
Selection, ledger (open-debit check), posting (head read before each POST, reconcile before a retry), the fenced comment, the citations data file, the whole-stream mask bound, the unreadable-stream class, the size cap and the attempt markers behave as described synthetic tests/test_claude_review_worker.py, 94 tests at 317cdd3e; 21 weakened copies of the code guards fail their tests in 25 runs (readers/crw-mutants-20261010/mutants3.py)
The network boundary is closed: only lo in the run's namespace, the API reachable through srt's proxy, example.com refused (CONNECT 403), no direct route, a host loopback listener never reached; the same check with example.com allowed fails native_proven (keyless) the worker's boundary_self_check through srt and bwrap, 2026-10-10 06:05Z; RealBoundaryChainTest with CRW_TEST_SRT/CRW_TEST_NODE set
A real review streams through srt: probes.py live on api-4 passes P1, P2, P6 and P11; three runs end_turn native_proven at d784fe0: crw-probes/probes/20261010T060816Z, $0.6252, review 204 s; through the reordered chain: crw-probes/probes/20261010T095638Z, 20 of 20, $0.7766, review 261 s
srt's node process, shells and socat bridges never hold the key; the client does (a process-environment scan during a run with a fake key); with the runner outside srt the same test fails native_proven RealBoundaryChainTest with CRW_TEST_SRT/CRW_TEST_NODE set
The worker's ledger and the harness's common.py (sha256 362cb74c…9a71) refuse the same double, cross and orphan closes on one ledger; recovery skips a ref the harness closed; the harness counts the worker's rows local_integration coordination record readers/ledger-compat-20261010/: common.py.pinned sha256 362cb74c3178a8715a28c97ae5e98dfefe044dfd68f5f09d00a85eb062e99a71, compat.json sha256 15d9f39979016c9d11b7b9948b20ccf758fbd1b45207d3300591b04c1bca3b00, ledger_compat.py sha256 4be8f465c55a54a335dc7623dbbd1a25ff8706cf9524f54a4df650132242a115
The records rule: no quoted direction in the direction sections synthetic RecordRuleTest; the 4b8027b section form gives 1 span and fails it on the detector's assertion (readers/crw-mutants-20261010/prior_record_check.py)
The rebased head behaves as before: offline probes pass and the worker's tests pass local_integration at a3881b19f: probes.py offline 16 PASS, 9 INFO, 0 FAIL (state receipt probes/20261011T212536Z), 91 worker tests OK; at 317cdd3e: 94 worker tests OK with and without socat on the search path, validate exit 0
A check that refuses every key posts one non-credit status and records one attempt; a usable key later reviews as usual synthetic the two regressions fail at 1a610ba7 (rc 1) and pass at 317cdd3e (rc 0); the GPT read's review_regressions.py passes at 317cdd3e
No real pull request has been reviewed yet — the first ticks run with CLAUDE_REVIEW_POST=0

Local commands run

$ python3 -I tools/claude-review-worker/probes.py offline
18 PASS, exit 0 (at 2acade96a); 18 PASS with the stricter L7 to L9 grader; 16 PASS at the head (api-4 only, so O6 once), with srt's static checks
$ CLAUDE_REVIEW_KEYS=anthropic-api-4 API_ACTIONS_LEDGER=<ledger> python3 -I tools/claude-review-worker/probes.py live
P1 P2 P6 P11 PASS (P4 vacuous); P5 P7-P10 FAIL (tool arm refused by the safeguard, not exercised; arm dropped since)
$ CLAUDE_REVIEW_KEYS=anthropic-api-4 CLAUDE_REVIEW_SRT=<srt prefix> CLAUDE_REVIEW_NODE=<mise node> API_ACTIONS_LEDGER=<ledger> python3 -I tools/claude-review-worker/probes.py live   # at the head, through srt
PASS, native_proven: boundary check passed; P1 P2 P6 P11 PASS; $0.6252
$ python3 -m unittest tests.test_claude_review_worker   # at 0d08404e
Ran 87 tests, OK (skipped=1: RealBoundaryChainTest; run separately with CRW_TEST_SRT and CRW_TEST_NODE set: OK)
$ TMPDIR=/var/tmp/claude-tmp/<dir> python3 -m unittest <the 45 test modules that name the worker, its record or manifests/evidence.json>   # at 0d08404e3
Ran 3345 tests, OK (skipped=34). (At d784fe0e with TMPDIR=/tmp, test_token_e2e_grader F27c failed on a cross-device git clone; on /var/tmp it passes.)
$ python3 mutants3.py <scratch copy>   # each code guard of both rounds undone in turn
25 of 25 fail their named test
$ python3 scripts/validate.py
exit 0
$ gitleaks git . --log-opts="$(git merge-base origin/main HEAD)..HEAD" --max-target-megabytes 2 --config .gitleaks.toml --redact
no leaks found
$ uvx ruff check --select F tools/claude-review-worker/ tests/test_claude_review_worker.py
All checks passed!

At the rebased head a3881b19f (2026-10-11; 1a610ba7c changes comments and records only):

$ CLAUDE_REVIEW_SRT=<srt 0.0.79 prefix> CLAUDE_REVIEW_NODE=<mise node 24.21.0> python3 -I tools/claude-review-worker/probes.py offline
exit 0: 16 PASS, 9 INFO (O6i for each later fill-first slot), 0 FAIL
$ python3 -m unittest tests.test_claude_review_worker
Ran 91 tests, OK (skipped=1)
$ python3 scripts/validate.py
exit 0
$ python3 scripts/validate_shards.py run --shard N --shards 8   # N = 0..7, in parallel, as validate.yml runs them
5 shards OK; shards 2, 4 and 7 failed under a host load average above 100, in four modules:
  tests.test_workflow_hardening (BetterleaksTrial fixture), tests.test_secret_path_guard (timing bounds), tests.test_child_usage_suite:
    the three together, 213 tests OK on this head at a load near 45 (unchanged main 8c95a50d: each OK)
  tests.test_adaptive_paper_credential_race: the same 5 failures on this head and on unchanged main 8c95a50d
$ gitleaks git --log-opts="$(git merge-base origin/main HEAD)..HEAD"
no leaks found

At the terminal head 317cdd3e (2026-10-12, on main 86fa38442):

$ python3 -m unittest tests.test_claude_review_worker tests.test_evidence_manifest tests.test_github_automation_practice
Ran 107 tests, OK (skipped=1)
$ bwrap --dev-bind / / --ro-bind /dev/null /usr/bin/socat python3 -m unittest tests.test_claude_review_worker   # no socat
Ran 94 tests, OK (skipped=1)
$ python3 -m unittest tests.test_claude_review_worker.ReviewRunTest.test_a_check_that_refuses_every_key_posts_one_status_and_records_one_attempt   # and ...test_a_key_usable_on_a_later_tick_reviews_the_head_as_usual
rc 1 at 1a610ba7 (the test file copied there), rc 0 here, each
$ python3 scripts/validate.py
exit 0
$ gitleaks git --log-opts="$(git merge-base origin/main HEAD)..HEAD"
no leaks found

The GPT read at 317cdd3e (fix4): ACK, no blocker. Its P3s join the follow-ups below; the first, the Spend bullet's old key order, is fixed in this body.

The command center's Claude delta at 317cdd3e: PASS_WITH_P3 (pr953-claude-micro-317cdd3e.md). Its P3s and the research row join the follow-ups below; the three pinned sources are now in this body's SOTA sources.

Follow-ups (the reads' P3s): when every key is refused, only the first eligible head gets the not-started status, since the tick stops there; the dedupe matches any earlier key_check_refused attempt rather than the latest; and its log line is not at warning priority. The TTL in effect is not observable: record ephemeral_1h_input_tokens from the stream's per-message usage on the first real runs. The record's "no two heads shared any of the prompt" is loose (the old prompts shared 32 bytes, below Opus 5.5's 512-token minimum prefix, prompt-caching.md:131, :135). The record's SOTA sources (:681) should list the three new pinned sources too. Cosmetic: the trading rule's "not exported," line break (worker.py:1523-1525) and record :202-203 past the wrap width. Research-queue row 31 (co-op): measure whether the one-hour TTL's doubled cache writes pay back through prefix reuse across heads, and keep "1h" only if the measured reuse covers it. A retained primary capture for B2's historical run (37988961127) and a red-before guard for the B2/B5 prose corrections; the switch row written before the next debit succeeds; the refusal and switch-row wording around the key loop; the evidence summary's historical counts; the hostname references in the manifest's existing content; moving line locators in pre-existing files; the merged-tree verification gaps (new main modules such as tests.test_active_model_currency); the CC's P3s at a3881b19 (the harness pin after d97aad49, the 404 handling, R1's allowPlaintextInject note, the Python citations as mirror paths, the automation bullet's path groups, the old SHAs above).

Decision record

docs/decisions/2026-10-09-claude-review-worker.md: the run, the instruction fence, upstream alignment, the sandbox measurements, the probes and the live results, the decisions beyond the brief, alternatives, what would overturn it, the evidence class and the SOTA sources.

Checklist

  • No GitHub Actions workflow is added or changed.
  • No secrets are printed, logged or committed. The key reaches only the sandboxed client's environment, and the stream is masked.
  • No new paid hosting, subscription or billing surface. It spends the existing Anthropic API keys under the ledger.
  • Peer-owned untracked files and worktrees were preserved.

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 10, 2026
Comment thread tools/claude-review-worker/probes.py Dismissed
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
Fixes from the security-reviewer pass and the coordinator's live probe run on #953:

- Upstream citation labels come from the pull request, so they no longer enter the
  prompt's instructions: they go to the data file /review/input/upstream-citations.txt
  (each label capped at 200 characters, each list at 50), and the prompt gives counts only.
- The private repository's comment shows the findings inside one fenced code block,
  with every backtick replaced, so no image, link, HTML or reference in model text renders.
- The native-agent-stack essential paths cover the gate itself: this tool, the credential
  inventory and schema, the guard and git hooks, .gitleaks.toml and the agent instruction
  files (command center decision on #953, 2026-10-10 01:17Z).
- The masked-credential bound reads the whole raw stream, tool results and unparseable
  lines included, for both [REDACTED: and [REDACTED-PARTIAL: markers.
- A stream line that fails to parse for any reason (RecursionError included) is counted,
  and an unreadable stream still settles its debit as unknown.
- A head already in main, or with an empty diff, gets no attempt and no status and is not
  selected again. A head over 40,000 files or 600,000,000 bytes is refused before anything
  is extracted or spent.
- A budget stop posts error; the next tick deletes a stopped run's leftover scratch.
- The service refreshes the live clone in a fail-closed ExecStartPre and drops
  PYTHONDONTWRITEBYTECODE (python3 -I ignores it).
- probes.py offline adds /proc and /dev/fd routes, links in main's checkout (L11), the
  head's own .claude rules and skill facts, and a CLAUDE_CONFIG_DIR check (O8); all 18
  checks pass (receipt readers/crw-probes/probes/20261010T013330Z).
- The record states the live native_proven set (P1, P2, P6, P11; P4 was vacuous), the
  command center's decisions, residual risks R1-R3 and the head export's known limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 10, 2026
- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
* Retire Claude review inside GitHub; AI reviews run locally

The owner decided on 2026-10-10 (about 17:15Z, relayed by the command center)
that Claude review no longer runs inside GitHub: AI reviews run on the local
host and GitHub enforces the gates. The API refused every request that carried
claude-code-action's attribution (CLAUDE_CODE_ENTRYPOINT=claude-code-github-action,
base-action/src/parse-sdk-options.ts:284 at 2dca132f) on all five Console keys,
while the same keys work from the CLI and the Agent SDK (the command center's
correction #141, measured).

Removed: claude-pr-review.yml, claude-pr-toolkit-review.yml, harness-audit.yml
and their three test modules, their zizmor-coverage entries and their
local-pages inventory paths. test_claude_federation_diagnostics keeps its
retained-snapshot and decision-claim tests. A new
InGitHubClaudeReviewRetiredTests keeps the three files absent and no workflow
running the Claude action or naming an Anthropic key.

New record docs/decisions/2026-10-10-retire-in-github-claude-review.md (cause,
what remains, revisit triggers, inverse); supersession notes in the four
claude-actions/federation records and the decision-record index;
docs/github-automation.md's current practice.

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

* Pin the repository grant count at 90 after the retirement

The three removed workflows were in the local-pages repository grants. At the
landing base, main f38116f (93 grants), the test's own derivation
(grants.inventory_paths over git ls-files) gives 90 for this branch, equal to
the policy's repository grants, and the test pins 90. At the earlier bases
eb31d23 and 9d4c00c the same derivation gave 90 and 89.

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

* Take up the command center's notes on the retirement

- .github/actions-permissions.json drops anthropics/claude-code-action@* and
  oven-sh/setup-bun@*: no workflow uses them; a new test keeps every allowed
  pattern in use, and the record's inverse puts them back.
- docs/github-automation.md: the allow-list text, no "harness description",
  no "still awaits hosted acceptance" clause.
- The retirement test catches the claude-review environment in its plain,
  name: and quoted forms, and reads *.yaml as well.
- Record nits: #953 marked open, the check is named sota-sources, and the
  federation-diagnostics note says the three workflows.

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

* Cite #968's real merge commit in the inverse

The record's inverse named a SHA that does not exist; #968's merge commit is
9d4c00c (git rev-parse 9d4c00c). The
federation note now says the history of those workflows.

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

* Read each job's environment in the retirement guard

The GPT read at 48e4bd3 found three valid YAML forms of the claude-review
environment that the regex guard let through: a flow mapping, a quoted name
key, and a comment after the key. The guard now reads each job's environment
as a scalar or a mapping's name (job_environments), cross-checked against
PyYAML when it is installed; all three forms are regressions, each caught here
and missed at e46f68e, and actionlint accepts each fixture.

The record names the claude-native-practice skill rows as a dated follow-up
for cc-native-practice (the command center's ruling at 20:19Z).

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

* Word the skill-row follow-up without the pinned-copy premise

The command center's correction #146: no personal copy of claude-native-practice
is installed on this host; sessions load the checkout's project skill. The
follow-up is now stated neutrally: the skill rows for the retired route are
updated in a cc-native-practice pull request, then re-pinned.

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

* State what the environment reading covers, and what PyYAML covers

The docstring claimed comments in general; the text reading handles a comment
after the key, not a comment line before name: at the job's indentation or a
multi-line flow mapping. The PyYAML comparison covers those, and CI runs it
(validate run 38084944806, passed, not skipped). The command center's micro at
08a76ab.

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

* Cite the environment forms' sources, and test the inverse pin against git

The GPT read at 13d1eb7 asked, under the correction rule, for:
- the upstream sources of the environment reading: GitHub's workflow syntax
  for jobs.<job_id>.environment (a name, or an object with name and url) and
  YAML 1.2.2's block and flow mappings, quoted keys and comments, cited with
  page digests in the record and next to the regression cases;
- a regression for the corrected inverse pin: RetirementInversePinTests reads
  the pin from the record and checks it is #968's merge commit holding the
  three workflows. It fails on the former SHA (no such commit) and is skipped,
  with the reason, only outside a worktree or in a shallow clone (CI clones
  with fetch-depth 0).

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
…ew (#1001)

* Retire the skill's Claude-in-GitHub rows and point them to local review

The claude-native-practice skill still selected the route that
docs/decisions/2026-10-10-retire-in-github-claude-review.md retired:
the github-action row (anthropics/claude-code-action at a pin) and the
Actions part of pr-review-pre-cue.

- slots.json, slots.md and the layer pages: `retired` joins the status
  vocabulary; github-action becomes retired with a dated block (record,
  correction #141, scope, the three workflows, the former default, route,
  alternatives, evidence and notes as a snapshot, the revisit triggers);
  pr-review-pre-cue becomes default (scoped) and lists the routes
  available now (local Claude reads, GPT reads, Codex's GitHub review,
  the required verdict-review-gate) apart from the open #953 worker.
- The scope is the owner's repositories (the command center's ruling of
  2026-10-10), not "this repository": a hermetic A/B showed a session in
  another repository concluding that the action was still fine there.
  The same ruling retires the managed Claude Code Review GitHub App.
- Stale texts the independent pre-read and the stricter check found: the
  release-tracking note (Dependabot as the action's tracker), the
  scheduled-tasks route (GitHub Actions schedules only for jobs that run
  no model) and its source mapping, the usage-cost-monitoring note
  (Actions under WIF), and the pre-cue source mapping (in Actions through
  #909). The pre-cue route no longer lists a read the record does not name.
- tests/test_native_practice_retired_routes.py: no current row selects a
  retired route; a retired route is named elsewhere only in a sentence or
  list item that says it is retired; every line of slots.json, slots.md,
  the layer pages and cross-client.md is read (duplicates kept, the source
  mappings of live slots included); only the dated snapshots are exempt.
  Positive controls are the rows as they stood; the mutation table is in
  the pull request description.
- The slot manifest of #923 is relabeled against the new slots.json
  bytes, so the exact-difference check of the provenance test runs again
  instead of skipping.
- docs/decisions/2026-10-09-claude-code-native-practice.md: a dated
  pointer to the retirement, as #981 did for the older records.

Source read for this change: anthropics/claude-code-action at
2dca132ff0e0c4094ce6048b422c6915a071210b, base-action/src/
parse-sdk-options.ts:283-284 (CLAUDE_CODE_ENTRYPOINT is set to
claude-code-github-action).

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

* Close the holes in the retirement guard and qualify the one-default rule for retired slots

Answers the Codex review in GitHub of #1001 at ba6ab3a (four P2s) and the co-op's GPT first read at the same head (three P2s, the first three below); each was reproduced before the change.
- Sentence units split at a full stop or a semicolon whatever follows, and at a line break: "This route was retired; the current default is claude-code-action." was one unit that said retired.
- The generic phrases "in Actions", "through Actions" and "under WIF" count as the retired route only with Claude context, judged per leaf string; the named workflows, "through #909" and the managed app count
  everywhere. The routes the decision retains (every bullet of its "What remains" section, a catalog job in GitHub Actions, a non-model gate under WIF) are accepted.
- The skill's entry point SKILL.md is scanned, and a test requires every file of the skill folder to be read by a check.
- The one-default rule names the retired exception in slots.json, in the slots.md copy and in step 3 of SKILL.md (a retired row has none and says what replaced the route).
- The ruled default text is pinned exactly (the read's measured boundary: the date changed on all three surfaces passed), and a check compares every row's Overturn when across slots.json, the index and the
  layer pages (one step wider than the review of #1005, where an edit left the index cell behind).
Each finding fails its own test when its fix is undone alone, and 43 single-edit mutations are caught; the old guard accepts the four bad scenarios of the read and rejects its two legitimate ones.

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

* Bind the manifest label to the shipped slots file and close the remaining holes of the retirement guard

Answers the CC's Claude first read of #1001 at 6e97620 (two P2s, six P3s) and Codex's second review in GitHub of the same head (two P2 threads); each was reproduced before the change.
- P2-1: the slot manifest was labeled against the slots.json of ba6ab3a, so the exact-difference check of the provenance test skipped at the head (OK, skipped=1). The label is the sha256 of the shipped file again,
  the check no longer skips, and a new test fails when a change to slots.json does not carry its label (the CC's ruling: a silent skip is this defect's class, so it fails loud).
- P2-2: a live source mapping (skills-library, primary_sources[5]) still ran `claude -p` "in a GitHub workflow ... plus a nightly cron"; the guard had no such form. It is reworded to say the run is retired,
  and "in a GitHub workflow" (also from, via, through, using, as) counts as the retired route when the sentence is about Claude.
- P3-1: the Claude Code GitHub Action, /install-github-app and an @claude mention are the retired route under other names (claude-code-security-review stays out: a documented rejection).
- P3-4: the Former default exemption of a layer page applies to a retired slot only.
- P3-5: a retired slot's own source mapping is read, and now says retired.
- P3-2: "e.g." and "i.e." no longer end a sentence, and wrapped lines of prose are one paragraph (the slots.md prose and SKILL.md were judged line by line).
- P3-6: the manifest prose lists the fields beyond the exported ones that differ from the snapshot.
- P3-7: the entry point directs ("a retired row has no default, so follow the replacement it names"), and the step-3 pin no longer fails with an IndexError on a renumbering.
Each correction fails its own test when it is undone alone, and the mutation table is extended.

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

* Judge retired-route prose one physical line at a time and close the guard's other gaps

Answers the command center's Claude micro of #1001 at ef3f3e7 (P2-1, P3-1 to P3-5) and Codex's four threads of the same head (:43, :63, :122, :211). Each case was reproduced before the change:
compare-guards-ef3f3e7b-vs-third-commit.txt runs 19 scenarios through the guard of ef3f3e7 and this one; the old guard is wrong on 13 (it accepts a retired route that is reintroduced), this one on none.
- P2-1 (correction #160): joining wrapped lines into one paragraph let "Retired 2026-10-10" followed by a line that says the current default is claude-code-action pass on four prose surfaces (SKILL.md, slots.md,
  cross-client.md, layer-page preambles), the scenario Codex raised at ba6ab3a line 41. Prose is judged one physical line at a time again, a line break ends a unit, and the message of every line-judged surface
  states the rule (RULE; the legend of slots.md states it too and a test pins the two to each other).
- P3-1 and Codex :43: three more names of the same route, all in the vendor's own text: the page's title "Claude Code GitHub Actions" and its "installs the Claude GitHub App"
  (code.claude.com/docs/en/github-actions.md, capture sha256 87b99aef, lines 5, 9, 33, 63-64), the mirror claude-code-base-action (anthropics/claude-code-action@2dca132ff0e0:
  .github/scripts/check_workflow_hardening.py:18, .github/workflows/sync-base-action.yml:1 and 43, CLAUDE.md:20) and the carrier named first ("Use the GitHub Action for Claude reviews", generic
  like "in Actions": it counts when the field or unit is about Claude). The families "in Actions" and "through Actions" take on, with, inside and via.
- Codex :122 and the command center's P3-3 and P3-5: a unit that negates the retirement outright ("not retired", "never retired", "no longer retired", "isn't retired", "unretired") no longer counts as saying
  retired. The other half of that scenario, "was retired, but is now current", is the boundary the command center ruled (the guard is a tripwire for stale text; review reads what it accepts); the module names it.
- Codex :211 and P3-3: a layer-page section with a label twice (a second Route or Overturn when bullet) is a violation, because the page-to-slots.json comparison keeps the first bullet only.
- Codex :63 and P3-4: the target of every slots.md row is checked against <page>#<id>, from the layer of the slot.
- The description line of SKILL.md names the topic that invokes the skill; it is pinned by sha256 to the text the routing was measured with and read as a topic, not as a route.
- Scheduled-tasks (Codex on #1005, scheduling-supervision.md:9, and the command center's ruling): the route no longer bans every model-backed job. The retirement record
  (docs/decisions/2026-10-10-retire-in-github-claude-review.md:5-6, :43) retires Claude review inside GitHub and keeps Codex's GitHub review, so the route says GitHub Actions schedules repository jobs and a
  scheduled Claude task runs locally; a test pins the wording on slots.json, the layer page and the alternatives.
The label of claude-native-slot-manifest.json equals the sha256 of the shipped slots.json again (relabel last); test_the_label_names_the_shipped_slots_file and test_no_other_exported_field_differs
(no skip) are unchanged from ef3f3e7. 72 of 72 single-edit mutants of the guard and its data are caught and 5 controls pass (mutations-v4-forward-commit-3.md).

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

* Align the manifest's scheduled-tasks note, state the rule in every explaining unit, split units after a closing delimiter and pin the scope of the name patterns

The final fix round of #1001 under the command center's convergence ruling: its Claude micro at efaa077 (blockers B1 to B3, P3-2) and its adjudication of 2026-10-11T13:50Z of Codex's third review. Each item was reproduced on that
head before the change.
- claude-native-slot-manifest.json:47 (Codex P2, B1, a text of this PR that is false at the head): the note on the differences from the snapshot still said the route of scheduled-tasks was narrowed "to jobs that run no model",
  while the shipped route says that GitHub Actions schedules repository jobs, that Claude runs inside GitHub are retired (2026-10-10, docs/decisions/2026-10-10-retire-in-github-claude-review.md:5-6, :43) and that a scheduled
  Claude task runs locally. The note says so now, and test_the_scheduled_tasks_route_retires_the_claude_routes_and_not_every_model_backed_job reads the manifest as well as the route (fails with the note undone: M79).
- B2: the module claims that the message of every line-judged surface states the rule, and it did not for the labelled bullets of a layer page or for the Overturn when cell of an index row. Every explaining unit states it now
  (a selecting field names no unit and states nothing); the three expected messages carry the rule and test_every_explaining_unit_of_a_layer_page_and_of_the_index_states_the_rule_and_a_selecting_field_does_not pins all three
  kinds (mutants M83 to M85).
- tests/test_native_practice_retired_routes.py:83 (Codex P2, P3-2): a closing quote, parenthesis or bracket between the full stop and the white space kept `The route was "retired." The current default is claude-code-action.` in one
  unit that said retired. The sentence pattern takes closing delimiters (the curly quotes are built with chr(), so the file stays ASCII); test_a_closing_quote_parenthesis_or_bracket_after_the_full_stop_does_not_join_two_claims fails
  without it (M78, M82) and keeps `claude-code-action was retired (see the record.) No row selects it.` accepted (control C6).
- :48 (Codex P2, workflow-first phrasing) is a follow-up and the class closes (rule 4 of the convergence ruling): the module states that the guard pins the named retired artifacts and the retired slot rows, that generic phrasing of a
  Claude run in a GitHub workflow cannot be enumerated and is out of its scope (the retirement record governs it), and test_generic_workflow_phrasing_is_out_of_scope_and_the_module_says_so pins the boundary. No pattern is added.
- B3 is the description: the two earlier command blocks name the 37 modules they ran (the 46 of the first block without the nine tests.test_local_pages_* modules).
80 single-edit mutants of the guard and its data are caught and 6 controls pass (mutations_retire_v6.py).

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

* Close the sentence-splitter class by documentation, pin the module's ASCII and the scope list

The command center's confirmation micro of #1001 at bdf60fc (research-cc-role-20261008/pr1001-claude-micro-bdf60fc6.md: blockers 1 and 2, P3s) and its correction #163 of 2026-10-11T16:18Z on Codex's three threads of 15:39:21Z.
No code of the guard changes in this commit.
- Blocker 2: the module said that it stays ASCII and held one U+2026 in a comment (added by efaa077). It is three full stops now, and test_the_source_of_this_module_is_printable_ascii pins the claim; it fails on the module
  of bdf60fc (['U+2026'] != []).
- Blocker 1 (the pull request description, and this message corrects the message of bdf60fc): the earlier statement that the 37 modules ran "without its nine tests.test_local_pages_* modules" was false. They are the first 37
  modules of the 46-module list, and the nine left out are its last nine (tests.test_local_pages_fleet_execution to tests.test_local_pages_workstation, 144 tests, run and counted); 29 of the 46 are local-pages modules.
- Codex :92 (an exclamation mark or a question mark does not end a unit) is not patched: the sentence splitter has drawn findings in five consecutive rounds (the lowercase clause, "e.g.", the joined lines, the closers,
  the exclamation mark and the question mark) and was patched four times, so rule 4 of the convergence ruling closes the class the way line 48 closed. The module docstring states exactly what the guard accepts as a unit end
  and that review reads everything else; test_the_unit_ends_and_the_scope_of_the_name_patterns_are_documented pins the sentence and the behavior, and fails on the module of bdf60fc, where the sentence is missing.
  A pinned upstream sentence segmenter is a research-queue item (rule 8, layer instructions-skills).
- Codex :51 (a carrier-first name of the managed Code Review app) is confirmed within the closed class: the same scope paragraph says that reordered phrasings of a pinned artifact are out of the guard's scope.
- P3-1: the scope list named 11 of the 17 patterns; it names all 17 now, and test_the_scope_of_the_name_patterns_names_every_pattern compares the list itself with PATTERNS (it fails on the module of bdf60fc).
  P3-2: the right single quotation mark closer is tested (its mutant survived). P3-3: the comment at the sentence pattern says what the code does.
- Codex :170 (retired.on is checked for its shape only) is a follow-up under rule 5: the shipped value is the date of the record, so 2099-12-31 is a mutant and not a reproduced failure of the shipped state.
88 single-edit mutants of the guard and its data are caught and 6 controls pass (mutations_retire_v9.py).

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

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
seathatflowsinourveins added a commit that referenced this pull request Oct 11, 2026
Fixes from the security-reviewer pass and the coordinator's live probe run on #953:

- Upstream citation labels come from the pull request, so they no longer enter the
  prompt's instructions: they go to the data file /review/input/upstream-citations.txt
  (each label capped at 200 characters, each list at 50), and the prompt gives counts only.
- The private repository's comment shows the findings inside one fenced code block,
  with every backtick replaced, so no image, link, HTML or reference in model text renders.
- The native-agent-stack essential paths cover the gate itself: this tool, the credential
  inventory and schema, the guard and git hooks, .gitleaks.toml and the agent instruction
  files (command center decision on #953, 2026-10-10 01:17Z).
- The masked-credential bound reads the whole raw stream, tool results and unparseable
  lines included, for both [REDACTED: and [REDACTED-PARTIAL: markers.
- A stream line that fails to parse for any reason (RecursionError included) is counted,
  and an unreadable stream still settles its debit as unknown.
- A head already in main, or with an empty diff, gets no attempt and no status and is not
  selected again. A head over 40,000 files or 600,000,000 bytes is refused before anything
  is extracted or spent.
- A budget stop posts error; the next tick deletes a stopped run's leftover scratch.
- The service refreshes the live clone in a fail-closed ExecStartPre and drops
  PYTHONDONTWRITEBYTECODE (python3 -I ignores it).
- probes.py offline adds /proc and /dev/fd routes, links in main's checkout (L11), the
  head's own .claude rules and skill facts, and a CLAUDE_CONFIG_DIR check (O8); all 18
  checks pass (receipt readers/crw-probes/probes/20261010T013330Z).
- The record states the live native_proven set (P1, P2, P6, P11; P4 was vacuous), the
  command center's decisions, residual risks R1-R3 and the head export's known limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/api-actions-review-worker-20261009 branch from 0d08404 to a3881b1 Compare October 11, 2026 21:28
@seathatflowsinourveins
seathatflowsinourveins marked this pull request as ready for review October 11, 2026 21:29
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/api-actions-review-worker-20261009 branch from a3881b1 to 1a610ba Compare October 11, 2026 21:40
A systemd user timer reviews each essential head of us-equities-trading and native-agent-stack with Claude Code
headless (Opus 5.5 at max) on an Anthropic API key, injected by the credential runner, in a bubblewrap sandbox with no
home directory. Main's checkout is the working directory and the head is data; CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 keeps
every instruction file out. It posts a claude-review/local commit status (and a sanitized comment on the private
repository), debits the api-actions ledger before each run, and carries the upstream-alignment rule for trading heads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes from the security-reviewer pass and the coordinator's live probe run on #953:

- Upstream citation labels come from the pull request, so they no longer enter the
  prompt's instructions: they go to the data file /review/input/upstream-citations.txt
  (each label capped at 200 characters, each list at 50), and the prompt gives counts only.
- The private repository's comment shows the findings inside one fenced code block,
  with every backtick replaced, so no image, link, HTML or reference in model text renders.
- The native-agent-stack essential paths cover the gate itself: this tool, the credential
  inventory and schema, the guard and git hooks, .gitleaks.toml and the agent instruction
  files (command center decision on #953, 2026-10-10 01:17Z).
- The masked-credential bound reads the whole raw stream, tool results and unparseable
  lines included, for both [REDACTED: and [REDACTED-PARTIAL: markers.
- A stream line that fails to parse for any reason (RecursionError included) is counted,
  and an unreadable stream still settles its debit as unknown.
- A head already in main, or with an empty diff, gets no attempt and no status and is not
  selected again. A head over 40,000 files or 600,000,000 bytes is refused before anything
  is extracted or spent.
- A budget stop posts error; the next tick deletes a stopped run's leftover scratch.
- The service refreshes the live clone in a fail-closed ExecStartPre and drops
  PYTHONDONTWRITEBYTECODE (python3 -I ignores it).
- probes.py offline adds /proc and /dev/fd routes, links in main's checkout (L11), the
  head's own .claude rules and skill facts, and a CLAUDE_CONFIG_DIR check (O8); all 18
  checks pass (receipt readers/crw-probes/probes/20261010T013330Z).
- The record states the live native_proven set (P1, P2, P6, P11; P4 was vacuous), the
  command center's decisions, residual risks R1-R3 and the head export's known limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner direction relayed by the command center (2026-10-10 01:49Z): anthropic-api-4 is the one key; api-3 and api-2 are
tried only after a credit-exhausted refusal, and a run on a spare is logged at journal warning priority. The key is
still injected per command by the credential runner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The worker's settle, settle_unknown and void now take the ledger lock, read the
ref's latest row, and append only when that row is its open debit, as the
api-actions harness's _open_debit does. The key and the unknown amount come from
that debit. Recovery rechecks each ref under the lock and leaves a ref another
writer closed meanwhile (GPT P2-5 at 4b8027b).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A nonempty stream in which no line parses was classified no_stream, so its
debit was voided at zero and its attempt went uncounted. It is now
unreadable_stream: the reservation settles as unknown and the attempt counts
toward the two-attempt limit. Only an empty or absent stream is voided
(GPT P2-4 at 4b8027b).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
publish() reads the pull request just before the status POST and again before
the comment POST; a head that moved, or a closed PR, marks what is unsent
superseded. If the head moves after both were posted, the comment gets a
superseded line (PATCH). Each POST is saved as "sending" first and "posted"
after; a retry of a sending or failed POST first looks on GitHub for the
commit's latest claude-review/local status and for this attempt's comment,
found by a hidden marker line, by the same gh login (GPT P2-1 and P2-2 at
4b8027b).

The test stand-in for gh now keeps statuses and comments across ticks and can
move a head, close a PR, or accept a write and lose its reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each check was exercised by any tool call at all, so removing the calls it
stands on left it PASS. required_calls() now names, per check, the scripted
calls and whether each must be denied; a check fails when any of them, or its
result, is missing, or when a required denial was answered. A subtest removes
each call, each result, and flips each denial on its own: every one turns its
check FAIL (GPT P2-6 at 4b8027b). The offline probes still pass 18/18 against
the pinned 2.1.296 client (7, 2 and 6 required calls answered).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decision record's upstream-alignment section quoted the command center's
routing row verbatim; it now gives a dated, attributed paraphrase with the same
source citation (GPT P2-7 at 4b8027b). The record also describes the
publication checks, the reconcile before a retry, the unreadable-stream class,
the ledger's open-debit check, the stricter L7 to L9 grader and its offline
receipt, narrows the cleanup claim to a tick that passes preflight, and marks
R2 open for the command center's decision on the network boundary (P2-3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins and others added 10 commits October 11, 2026 20:45
… API host

The worker's bwrap shared the host network: the open internet, host loopback,
the LAN and host abstract sockets were all reachable from the sandbox (GPT P2-3
at 4b8027b, measured). Per the command center's ruling, srt
(@anthropic-ai/sandbox-runtime 0.0.79, from the pinned npm tarball) now sits
between the credential runner and bwrap. srt unshares the network and its host
proxy passes api.anthropic.com only; the inner bwrap stays the filesystem fence.

Each run gets a private directory under XDG_RUNTIME_DIR (srt's settings,
TMPDIR, HOME and an empty working directory), removed afterwards. Preflight
refuses the tick, before any key is read, when node (>= 22.12, the mise
install), the srt install (version and npm integrity), socat or the socket
path room is wrong, and when a keyless check through the same chain does not
show lo alone, the API reachable through the proxy, example.com refused, no
direct route and no reach to a host loopback listener. probes.py offline
runs the static checks only; live runs all of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The decision record now describes srt as the worker's network boundary (the
command center's ruling on P2-3): its source pin, the chain, the settings, the
fail-closed preflight, the keyless check and its control, the live probe run
through srt (native_proven, $0.6252 on api-4), R2 closed, R1's wider set of
processes holding the key, and the untested items as known limits. The README
covers the srt install, its two settings, posting and the inverse. probes.py
now copies the worker's settings whole (dataclasses.replace), so its runs keep
the srt and node paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GPT read at d784fe0 asked for changes with four P2 findings:

- Supersession was not durable: an accepted comment whose reply was lost,
  then a head move, ended superseded with no PATCH, and a failed superseding
  PATCH was never retried. The marker lookup and the PATCH are now open work
  in the attempt marker (comment_marked false) until GitHub's side is
  settled, and every tick finds open posting work from the stored attempts,
  not from the listed heads.
- The key reached srt's node process, its shell and its socat bridges,
  because the runner injected it before srt started (srt spawns its command
  with shell: true and the inherited environment, src/cli.ts:545-548 at
  d9aac209). srt now starts without the key; the inventory runner is srt's
  command and injects it into bwrap and the client only. A native test reads
  every process environment during a run with a fake key: the client holds
  it, srt's node, shells and bridges do not; with the runner outside srt, node,
  sh, bash and three socat processes held it.
- The records correction had no regression test: RecordRuleTest rejects a
  restored quotation (straight, curly or block) in the direction sections,
  on synthetic text; the 4b8027b form of the section fails it.
- The ledger reference was unpinned: the harness common.py (not in git) is
  pinned by sha256 with line locators and a read-only copy, and the worker
  was run against it on one temporary ledger. A close of another workload's
  debit is now refused too.

85 tests; 25 weakened copies of the code guards each fail their test. The
offline probes and a live probe run through the reordered chain pass
(20 of 20, native_proven, $0.7766 on api-4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The command center's read at 3d0bead found that RecordRuleTest's detector
stopped at a line break, so the 4b8027b quotation, wrapped across lines,
gave no span; the prior form failed the test only on its other assertions.
The detector now lets a quotation span line breaks (not a blank line),
collapses whitespace before counting words, and joins consecutive block-quote
lines. New wrapped cases cover straight, curly and block forms, and a
paragraph break ends a span. On the 4b8027b section the detector finds one
25-word span, and that form spliced back fails the test on the detector's
own assertion; a line-bound detector fails the new cases. The record and the
PR body now say exactly that.

The GPT read at 3d0bead found that with both parts posted, a head move during
posting and a failed final head read left the comment unmarked for good. Before
any POST the attempt is now marked verify: pending, cleared only by a head read
that settles the outcome; publish_pending finds it from the stored attempts.
Regressions cover the failed final read and a tick stopped before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- R1 now says the runner holds the key in memory, while bwrap and the client
  hold it in their environment, and the client's children inherit it. It
  names CLAUDE_CODE_SUBPROCESS_ENV_SCRUB as present in 2.1.296 and
  unmeasured here, and it weighs srt's own credential masking: that needs
  tlsTerminate, so srt's proxy would read every request.
- Boundary.prefix's docstring no longer says the key passes through srt.
- A superseding PATCH now ends: HTTP 404 marks the comment deleted, and after
  24 failed tries the worker gives up with a warning, so a broken PATCH is no
  longer retried every tick for good. A new regression fails on the pushed
  worker.
- The ledger reference is now also pinned by commit: the api-actions harness
  is under local git (d97aad49), and common.py's content hash is unchanged.
- The mutation accounting is corrected: 21 weakened copies, 25 failing test
  runs. Later fixes were checked against the worker before them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GPT read at 0d08404 asked for the upstream source of the corrected
detector's matching. The test and the decision record now cite the Python 3.13
documentation (the tests ran on 3.13.16): the complemented set and the negative
lookahead in re's syntax, re.MULTILINE's line-start ^, and str.split()'s
whitespace collapsing. The splice result is labelled as the coordinator's
external measurement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner's direction of 2026-10-10 (about 14:19Z, relayed by the command
center) replaces single-key mode with fill-first. The default order is api-4,
api-5, anthropic-api, api-2, then api-6 to api-10. anthropic-api-3 is CI
review's (the main-only claude-review environment secret) and is no longer
listed.

The worker stays on a key until Anthropic reports it out of credit (402, or
400 credit too low) or revoked (401), and never round-robins, since prompt
caches never cross organizations (anthropics/skills@dbd4588f
skills/claude-api/shared/prompt-caching.md:130). A slot the credential runner
refuses (--check) is skipped before any debit. Each move writes a switch row
(the harness's schema) and a warning journal line. Each settle records
per-model tokens and the run's cache hit rate (prompt-caching.md:186-192).
The attempt record keys off the run's first debit, not the first key's index.

The probes' O6 requires the first key, and reports later slots as O6i (info).
Five new tests fail on the previous worker; 91 tests pass, and the offline
probes pass with the default order (16 PASS, 8 INFO).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner's direction of about 15:12Z (relayed by the command center): the
order is api-4, api-3, api-5, anthropic-api, api-2, then api-6 to api-10.
anthropic-api-3 serves CI review first and local quality work second. Each
settle and void row carries a human use_case read from its debit ("PR review
nas#953", "review-worker probe (facts-control)").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#981 (8bffa2c) removed the in-GitHub Claude workflows. The worker's
README, comments and decision record still described claude-pr-review.yml
as live or as an optional cloud path; each reference now says it was
removed by #981, and the record's overturn condition names an owner
decision to bring a cloud review back instead of the federation fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- B3: fake_boundary and the no-node case put a stand-in socat on their
  own search path, so the boundary tests pass on a runner without socat
  (ubuntu-24.04 has none; validate-shard-0 failed on it).
- B4: when credential_run.py --check refuses every key, the run is
  key_check_refused: one error status per head saying so, an uncounted,
  non-final attempt, no ledger row, and no claim about credit. Before,
  each tick posted "every configured key is out of credit" again. When
  some keys were skipped and the rest refused, the status now says
  "every usable key"; a mix of credit and revoked refusals says both.
  The co-op's GPT read at 1a610ba reproduced the same defect (a loop
  that only skips still ends in its else suite: CPython 3.13.16
  Doc/reference/compound_stmts.rst:165, :173); the CC ruled it into
  this commit. Two regressions: two ticks with every slot refused give
  one uncounted attempt and one non-credit status; a key usable on a
  later tick reviews the head as usual.
- B2: the record says no hosted run had succeeded (run 37988961127 ran
  on main on 2026-10-09 and failed).
- B5: the record counts the module's tests (94 at this commit).
- The gh-login comment is back on self.login (a P3 of both reads).
- Owner direction of 2026-10-11 ~22:33Z (via the CC): the default key
  order is api-4, api-3, api-2, api-5, anthropic-api, api-6 to api-10.
- Prompt caching (CC): the prompt puts its stable part (rules, task,
  upstream rule, answer shape) first and the pull request's line and
  citation counts last (anthropics/skills@dbd4588f prompt-caching.md:7,
  :27), and the settings set promptCacheTtl "1h" (anthropics/claude-code
  CHANGELOG.md:2970, 2.1.243); subagents keep the default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants