Repository navigation
Run the review worker on a retained, hash-checked copy of the latest Claude Code (2.1.302) - #1127
Merged
seathatflowsinourveins merged 8 commits intoOct 17, 2026
Conversation
… 2.1.299 Native auto-update is on here (claude doctor, 2026-10-15T00:44:57Z: Auto-updates enabled, channel latest). The native installer deletes old versions from ~/.local/share/claude/versions/, keeping only the launcher's version, any version a running session uses and the two newest (https://code.claude.com/docs/en/setup, home directories on network storage). So the worker's pinned versions/2.1.299 path can vanish at the next release. - DEFAULT_CLAUDE_BIN is the command center's retained copy outside versions/, ~/.local/share/claude-retained/2.1.299/claude. - DEFAULT_CLAUDE_BIN_SHA256 is 2d894a4b..., the linux-x64 checksum of the GPG-verified signed 2.1.299 manifest (manifest sha256 5052df4b...; good signature, fingerprint 31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE). The retained copy and versions/2.1.299 both hash to it. - Preflight hashes the client binary at every tick and refuses one that differs, before any key is read. CLAUDE_BIN_SHA256 overrides the digest; a CLAUDE_BIN override is checked only when it is set. A malformed digest is a ConfigError. - The vendor's other option, a custom launcher (which keeps every version), is not taken: it would change the binary every other session runs. Tests: 96, OK (1 skipped, as on main). Against main's worker the settings test fails and the new preflight test errors in all three subtests. The real retained binary passes the check through file_sha256 (0.24 s). Co-Authored-By: Claude Opus 5.5 <[email protected]>
5 tasks done
On 2026-10-14 the owner directed (paraphrased, through the command center)
that the headless roles follow the latest Claude Code: pinned roles move
to each release through a retained, hash-checked copy after a function
check of their exact invocation, not a paired quality run.
- DEFAULT_CLAUDE_BIN is ~/.local/share/claude-retained/2.1.300/claude.
DEFAULT_CLAUDE_BIN_SHA256 is 674deba0...5453, the linux-x64 checksum of
the GPG-verified signed 2.1.300 manifest (manifest sha256 fb62dae2...).
The retained copy and versions/2.1.300 both hash to it. The preflight
check is unchanged.
- Function check, 2026-10-15, run against retained 2.1.299 and 2.1.300:
- claude_flags(): every flag is present in both --help texts. The only
change is --restricted's description, which drops REPL from the tools
it removes; the worker passes --tools Read,Glob,Grep.
- The exact flag list, run with no key from a scratch config dir, gives
the same init record on both: tools Glob, Grep and Read, no MCP
server, permission mode dontAsk.
- The offline probe suite could not run on this host: srt 0.0.79 is not
installed, since the worker is not enabled yet. That stays #1107's
live-probe step before enabling.
Tests: 96 OK (1 skipped, as on main). With the previous worker.py the
settings test fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins
added a commit
that referenced
this pull request
Oct 15, 2026
On 2026-10-14 the owner directed (paraphrased, through the command center) that the headless roles follow the latest Claude Code. The floor addendum, the pins' notes and the client-core note no longer say that the review worker and the trading runner stay on 2.1.299 pending a paired check. They now state the rule: a pinned role runs a retained copy outside versions/, checks its sha256 against the release's signed manifest at launch, and moves to a new release after a function check of its exact invocation, not a paired quality run. The retained copy protects the role against the native installer's version cleanup and against a broken release. At this addendum both roles move to 2.1.300: the trading runner by its own dated copy, the review worker in #1127. Derived records regenerated: final-stack.json, the snapshot label, the evidence registry. Tests: the affected modules pass (bootstrap 79 and 153, final_stack 75, handbook 88, slot checks, validate 86, push gate 127). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…read errors; recheck the pin at the run Fix round 1 for #1127 (GPT full1 blocker 1, CC micro F1-F3, GPT P3-3 and P3-4): - F1 / blocker 1: settings_from() raises ConfigError when CLAUDE_BIN names another path than the default and CLAUDE_BIN_SHA256 is unset or empty, so the command line exits 2 before a worker, a launcher or a GitHub call exists. The default path, however it is spelled, keeps the built-in digest. An explicit path/digest pair still works. - F2 / P3-3: an OSError from the hash read (unreadable, or removed after the is_file check) is a "not ready" problem on the tick's own record path: a log line and a ticks.jsonl row, not a traceback. - F3: run_keys() compares the run's re-hash with the pin before its first debit; a binary replaced after the tick's check starts nothing and debits nothing (no_stream: not counted, the tick stops). - P3-4: the README records the rollback to the retained 2.1.299 copy (path and the 2.1.299 release manifest's linux-x64 checksum, with the checks before enabling) and its undo; the override note now says an override needs its digest. The test comment names 2.1.300. Fail-first: the four new or changed tests failed at e6046ea (7 failures/errors, each for the defect: no ConfigError, None digest, a worker built, a PermissionError traceback, a launch and two ledger rows after the binary changed) and pass here; the module runs 99 tests OK. Co-Authored-By: Claude Opus 5.5 <[email protected]>
GPT read of #1127 fix1 at 795232e (P2), the same finding as the local review's P2 at worker.py:2054-2055: binary_identity() cached the digest by (path, size, mtime_ns), so a replacement keeping size and mtime (shutil.copystat copies nanosecond timestamps: python/cpython@cbc944f4 v3.13.16 Lib/shutil.py:348,385) passed the pin check with the old digest. run_keys() now calls binary_identity(fresh=True), which hashes the file every run, and refuses with nothing debited when the file cannot be read. Regression test_a_replacement_keeping_size_and_mtime_is_caught_by_a_fresh_hash warms the cache with a first review, replaces the binary with same-size bytes at the same mtime_ns, and expects no second launch or debit. Fail-before: Tuples differ: (2, ['debit', 'settle', 'debit', 'settle']) != (1, ['debit', 'settle']). After: tests.test_claude_review_worker 100 tests OK (skipped=1). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…rvives an unreadable binary CC adjudication of #1127 at 0fb64d8 (the final round): - (a) both families' P2 (worker.py:2056-2057): the run hashed the binary once, so a binary replaced while key 1 was refused for credit was launched under key 2. Before each further key, run_keys hashes the file again; a mismatch or a read failure stops the run before the switch row, the debit and the launch. test_a_binary_replaced_before_the_next_key_starts_nothing_more, fail-before: Lists differ: ['anthropic-api-4', 'anthropic-api-3'] != ['anthropic-api-4']. - (e) Astra P3s: the read-error refusal of fix 2 gets its own test, and the receipt's binary field reads the file through readable_binary_identity(), so a binary that stays unreadable no longer escapes while the refusal's receipt is written. test_a_binary_that_cannot_be_read_at_the_run_is_a_recorded_refusal, fail-before: PermissionError: [Errno 13] Permission denied. After: tests.test_claude_review_worker 102 tests OK (skipped=1). Co-Authored-By: Claude Opus 5.5 <[email protected]>
…302; hold srt at 0.0.79 Claude Code 2.1.302 is the latest release. Its signed manifest (https://downloads.claude.ai/claude-code-releases/2.1.302/manifest.json, sha256 d7d4f193bfebb48733027b0dc43d6996d99f5335d152961a729730e55bbd5aa1) verifies with gpg against https://downloads.claude.ai/keys/claude-code.asc (fingerprint 31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE) in an empty keyring. Its linux-x64 checksum, 60d7eb7f...29a5, matches the installed versions/2.1.302 and the retained copy under claude-retained/2.1.302. Function check from 2.1.300: the two `--help` texts are byte-identical, and the exact claude_flags() list, run with no key from a scratch config directory, gives the same init record (tools Glob, Grep, Read; no MCP server; no plugin; permission mode dontAsk). The CHANGELOG entries for 2.1.301 and 2.1.302 (anthropics/claude-code@03b5892e and @c017428c) change none of the flags, settings or the permission mode the worker uses. The README's rollback pair moves to 2.1.300 (674deba0...5453, size 266972344, from its signed manifest). srt stays at 0.0.79. 0.0.80 (anthropics/sandbox-runtime@626a5967) refuses the sandboxed command new user namespaces by default (9a277e5, #588; README.md:715 at the tag), and the worker's inner bwrap --unshare-user then stops: boundary_self_check passes on 0.0.79 and fails on 0.0.80. Whether to adopt the allowNestedUserNamespaces opt-out is the command center's decision; worker.py records the finding next to the pin. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ck; absolute, pinned client paths Fold of the CC verdict on #1127 at 37dfd70 (B1 to B8). B1. srt moves to 0.0.80 (anthropics/sandbox-runtime@626a5967, tag v0.0.80). Its seccomp helper refuses socket(AF_VSOCK) with EPERM (vendor/seccomp-src/seccomp-unix-block.c:212-229; #691; absent at v0.0.79), and a network namespace does not fence VM sockets on WSL2. Boundary.settings sets allowNestedUserNamespaces (README.md:592, example :445-446; src/sandbox/sandbox-config.ts:1179-1187), which lifts only the namespace filter and the max_user_namespaces limit (apply-seccomp.c:887-888, :954-958), so the inner bwrap keeps its user namespace and the socket filter stays. Pinned from the registry: integrity sha512-WG4P8faU...MLpxnA== (npm view dist), tarball sha256 7da66e1a5161bbcb57cf8a6f7ba7d85f1e6bb7897040b295885dc74c1a255f95, the same from npm pack; npm audit signatures verifies it. boundary_self_check gains a vsock line (socket creation only, never a connect; only EPERM passes). Measured keyless on 2026-10-17: 0.0.79 fails on vsock alone (the socket opens inside the chain); 0.0.80 without the opt-out fails ("bwrap: No permissions to create a new namespace"); 0.0.80 with it passes, vsock refused EPERM. allowAllUnixSockets, which drops the helper, joins SRT_FORBIDDEN. The README and the check's pass text say what the boundary refuses. B2. settings_from refuses a relative HOME and any path setting that is not absolute after ~ expansion; Settings refuses a relative claude_bin, so the hashed file and the bound file are one. B3. README states that the hash check is by path, with its same-uid bound. B4. Settings requires a 64-hex claude_sha256 (None or malformed raises ConfigError), and the three hash gates no longer skip on a missing pin. B5. A test for the per-key read-failure branch (again is None). B6. The unreadable-binary test raises from the hash function instead of chmod(0), which root ignores. An undebited refusal is recorded as an uncounted attempt, so the next attempt gets its own number and receipt. After a per-key stop the log names the key that ran and was refused. B7. The override test states the lexical comparison and pins a .. spelling. B8. The sha256-check test dates 2.1.302's adoption 2026-10-17. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Comment-only fold of the CC delta verdict on #1127 at 738e24e. B1. The srt pin comment also cites apply-seccomp.c:804-811 at v0.0.80: under allowNestedUserNamespaces the helper's outer stub skips PR_SET_NO_NEW_PRIVS as well as its namespace filter, and with no helper the opt-out drops bubblewrap's --disable-userns (linux-sandbox-utils.ts:3844-3852). The command still gets PR_SET_NO_NEW_PRIVS (:943-945) and the socket filter. The v0.0.79 stub set neither (apply-seccomp.c:764-769 at v0.0.79), so nothing widens against it. B2. settings_from's comment and the settings test's comment say the default-path test is a pathlib Path comparison: it folds repeated slashes, `.` segments and a trailing slash, but not `..` or a leading `//`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
seathatflowsinourveins
deleted the
claude/cc-native-practice-retained-2299-pin-20261015
branch
October 17, 2026 13:41
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.
Scope
~/.local/share/claude/versions/. srt moves to 0.0.80 withallowNestedUserNamespaces, and the keyless boundary check now also requiressocket(AF_VSOCK)to be refused (fold round). Every path setting must be absolute, and a sha256 pin is required. Its preflight refuses a client binary whose sha256 is not the pinned one. ACLAUDE_BINoverride naming another path needs its ownCLAUDE_BIN_SHA256, or the worker stops at its settings (exit 2); the run rechecks the pin before its first debit.a339425ca56ec3fb87dff0fa8ad5dffc83354168lane:foundationtools/claude-review-worker/worker.py,tools/claude-review-worker/README.md,tests/test_claude_review_worker.py,manifests/evidence.jsonSOTA sources
68b9b31f8e22702599ed1d6e08bfbd74da20f5fdfb88f88f49340c29767815dc), line 498. The native installer deletes old versions from~/.local/share/claude/versions/, keeping the launcher's version, any version a session on the machine is running, and the two newest. With a custom launcher it keeps every version.claude doctorat 2026-10-15T00:44:57Z printsAuto-updates: enabled, channellatest. TheautoUpdates: falsein~/.claude.jsonis the native installer's own legacy-updater marker, written withautoUpdatesProtectedForNative: true. The disabled check counts it only without that protection (Claude Code 2.1.300 binary, byte offsets 229504330 and 218781814).37dfd70a): the linux-x64 checksum of the signed 2.1.302 manifest, https://downloads.claude.ai/claude-code-releases/2.1.302/manifest.json (read 2026-10-17T07:13:43Z, sha256d7d4f193bfebb48733027b0dc43d6996d99f5335d152961a729730e55bbd5aa1; build commit2ef144e6f98c1c99d082b735e4b38f50e3391e55, buildDate 2026-10-16T16:13:51Z):60d7eb7f12fbb28a52533fe8be5884097403562aa02dc73991a021c7067e29a5, size 285793528. Itsmanifest.json.sig(sha256fa7329ee7da147f019dfc898d4ab978522d3900d25844fabb7b68d1026b6459c) passesgpg --verifywith https://downloads.claude.ai/keys/claude-code.asc (sha256bd70a5e4a268002704024ceba7f8446024114e94f3f0bdd11c23a9e592be81c6) in an empty keyring, fingerprint31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE. The 2.1.300 manifest (sha256fb62dae2893ff954bbf8caa9f4b9345737b98a210e3a6085bf9feec9ddf5d9ca) passes the same check again and gives the rollback pair (674deba0…5453, size 266972344).03b5892ee77de289bcd5dfa39f541525ade74aff(tag v2.1.301) and @c017428c8c27e604840435048d5bce6a9a22c040(tag v2.1.302),CHANGELOG.md(read 2026-10-17T07:15:15Z; sha2560551b63e…3979and57fde96b…00da). Lines 3-67 at v2.1.302 cover 2.1.302 and lines 69-121 cover 2.1.301; the 2.1.301 section is the same at both tags. No entry changes a flagclaude_flags()passes, a keyreview_settings()sets, ordontAsk. The entries nearest the worker are listed under the 37dfd70 round.738e24eb): anthropics/sandbox-runtimev0.0.80(626a59671ee2be556d70a290a8036224be996193; fromv0.0.79d9aac2098351ca17f3743fbaf6ecbd0051b7e00e, 69 commits).vendor/seccomp-src/seccomp-unix-block.c:212-229: the helper refusessocket(AF_UNIX)andsocket(AF_VSOCK)withEPERM. The AF_VSOCK rule (Spend diagnosis 2026-10-04: run volume, Opus advisor, coordinator dispatch rule (receipt + decision record) #691) is absent at v0.0.79, and a network namespace does not fence VM sockets on WSL2.vendor/seccomp-src/apply-seccomp.c:887-888and:954-958:allowNestedUserNamespaceslifts only the namespace filter and theuser.max_user_namespaceslimit, while the socket filter is always installed. The option is documented atREADME.md:592(example at:445-446) andsrc/sandbox/sandbox-config.ts:1179-1187.README.md:715: by default the command may not create user namespaces (commit9a277e5, U11 design (for review): merit-neutral selection rules for the sweep refuter and verdict eligibility #588), so a nested bubblewrap fails.README.md:717: underallowAllUnixSocketsthere is no helper. npm@anthropic-ai/[email protected](dist-tag latest;npm view … dist2026-10-17T11:55:54Z): integritysha512-WG4P8faU/JDH90I8NLUBPmg7e72zhanlW3AVCD0OF06JCtId9IDFewFgv4YRvHLcnn//h1r6a1AMosBsMLpxnA==, and the downloaded tarball's sha512 equals it. Its sha256 is7da66e1a5161bbcb57cf8a6f7ba7d85f1e6bb7897040b295885dc74c1a255f95, andnpm pack(12:15:13Z) gives the same bytes.npm audit signaturesverifies the install.e6046ea3, superseded by37dfd70a): the linux-x64 checksum of the signed 2.1.300 manifest, https://downloads.claude.ai/claude-code-releases/2.1.300/manifest.json (sha256fb62dae2…),674deba0…5453, verified the same way, at 2026-10-14T23:55:04Z. The owner directed on 2026-10-14 (paraphrased, through the command center) that pinned roles follow the latest release through a retained copy after a function check. The first commit pinned the linux-x64 checksum of the signed 2.1.299 manifest, https://downloads.claude.ai/claude-code-releases/2.1.299/manifest.json (sha2565052df4b25f669eb175a888bf9a43b9d3b9745e91f23f845d5ce48a230c43262; build commitc416f56e…, buildDate 2026-10-13T22:05:00Z). Itsmanifest.json.sig(sha256b53f8ef4…) verifies with https://downloads.claude.ai/keys/claude-code.asc in an empty keyring, fingerprint31DD DE24 DDFA B679 F42D 7BD2 BAA9 29FF 1A7E CACE(the procedure at https://code.claude.com/docs/en/setup#binary-integrity-and-code-signing).Evidence-class table
claude-retained/2.1.302/claude,versions/2.1.302and the signed 2.1.302 manifest's linux-x64 checksum are the same digest,60d7eb7f…29a5(the binary prints2.1.302 (Claude Code))native_provengpg --verify,sha256sumon this host, 2026-10-17T07:13:57Zclaude-retained/2.1.300/claudematches the signed 2.1.300 manifest,674deba0…5453native_provengpg --verify,sha256sumon this host, 2026-10-17--helptexts, and the exact flag list gives the same init record with no keynative_provenboundary_self_checkwith the vsock line (srt → bwrap → probe, no key): 0.0.79 fails on vsock alone, 0.0.80 without the opt-out fails at the inner bwrap, and 0.0.80 with it passes with vsock refusedEPERMnative_provenbwrap --unshare-userand the key path work through srt 0.0.80 with the opt-outlocal_integrationRealBoundaryChainTestwith a fake key and a stand-in client, rc 0probes.py offlineon 2.1.302 and 2.1.300 through the srt 0.0.80 scratch prefix: 15 PASS each, the same O5 initnative_proven20261017T121205Zand20261017T121214Zversions/2.1.299and the signed manifest's linux-x64 checksum are the same digest,2d894a4b…d077native_provengpg --verify,sha256sumon this host, 2026-10-15T00:46:54Zfile_sha256passes the real retained binary (0.24 s)local_integrationsettings_fromplusfile_sha256on this hostsynthetictest_preflight_refuses_a_client_binary_that_fails_its_sha256_check,test_settings_without_a_pin_are_refused,test_a_relative_client_binary_is_refused_even_with_its_digestsynthetictest_settings_read_the_environmentFunction check, 2.1.300 → 2.1.302 (2026-10-17)
claude_flags()andreview_settings(): the two--helptexts are byte-identical (both sha256223ec71c6e89368ce9d8d4b36118ec535a3af755728b7868fd60f69f6af7c64e, run from a scratchHOMEandCLAUDE_CONFIG_DIR). All 17 flags the worker passes are present in both. No description changed.apiKeySourcenone, permission mode dontAsk, and the same agent list. 2.1.302 adds two init fields,additional_directories(the--add-dirpath) andoriginal_cwd. The worker reads onlytools,mcp_serversandpluginsfrom the init record (analyze()).probes.py offlineran keylessly in the fold round, against a scratch srt 0.0.80 prefix, with a scratch HOME that holds no key store. Both clients gave 15 PASS and the same O5 init; O6 fails by design. The run in the worker's own prefix stays Re-pin the review worker's Claude Code client to 2.1.299 #1107's step before enabling.Function check, 2.1.299 → 2.1.300 (2026-10-15)
claude_flags(): every flag is present in both--helptexts. The one change is--restricted's description, which drops REPL from the tools it removes; the worker passes--tools Read,Glob,Grep.probes.py offlinecannot run on this host (srt 0.0.79 is not installed while the worker is disabled). It stays Re-pin the review worker's Claude Code client to 2.1.299 #1107's step before enabling.Local commands run
Decision record
No new decision. This carries out the command center's B2 ruling of 2026-10-15: protect the pinned roles before anything else, using the vendor-supported retained-binary option. The floor decision's 2026-10-14 addendum (in the floor-raise PR) records the cleanup exposure. The trading lane's equivalent change is its own private, dated runner copy.
Fix round 1 (795232e)
From GPT full1 (
GPT-VERDICT-e6046ea38.md) and the command center's micro (pr1127-claude-micro-e6046ea3.md); the command center ruled at 2026-10-15T03:32Z that they land here, in one commit:settings_from()raisesConfigErrorwhenCLAUDE_BINnames another path andCLAUDE_BIN_SHA256is unset or empty; the default path keeps the built-in digesttest_settings_read_the_environment(4 subtests: noConfigError, aNonedigest);test_an_override_without_its_digest_never_builds_a_worker(crw.main(["select"])built a worker; now exit 2)OSErrorescapedtick()'s record pathpreflight()turns it into a "not ready" problem: a log line and aticks.jsonlrowtest_an_unreadable_client_binary_is_a_recorded_refusal(aPermissionErrortraceback)run_keys()compares it before the first debit; a mismatch starts nothing and debits nothing (no_stream: not counted, the tick stops)test_a_binary_that_changes_after_preflight_starts_no_run(one launch and two ledger rows after the binary changed)2d894a4b…d077with size 265342136 from the 2.1.299 release manifest, read 2026-10-15T03:37Z), the checks before enabling, and the undo; the test comment names 2.1.300Own logic; no upstream mechanism changes.
python3 -m unittest tests.test_claude_review_worker: 99 tests OK (1 skip).scripts/validate.pyon the merge of 795232e with main 0f4ee07: rc 0. On the branch alone it reports the model-currency date checks that main's #1124 refreshed.Fix round 2 (0fb64d8)
From GPT fix1 at 795232e (
fix1-pr1127/GPT-VERDICT-795232e70.md), blocker 1 (P2). The local review at 795232e (review_output.json) found the same thing atworker.py:2054-2055. The command center released this commit at 2026-10-15T10:49Z.binary_identity()cached by (path, size, mtime_ns), and a copy can keep both (python/cpython@cbc944f4 v3.13.16Lib/shutil.py:348,385copies nanosecond timestamps)run_keys()callsbinary_identity(fresh=True), which hashes the file on every run. A read failure is a recorded refusal with nothing debited.test_a_replacement_keeping_size_and_mtime_is_caught_by_a_fresh_hash: the first review warms the cache, then same-size bytes go in at the same mtime_ns. It failed with(2, ['debit', 'settle', 'debit', 'settle']) != (1, ['debit', 'settle']).python3 -m unittest tests.test_claude_review_worker: 100 tests OK (1 skip). On the branch alone,scripts/validate.pyreports only the model-currency date checks that main has since refreshed. origin/main 4204520 passes (rc 0), and the landing rebase takes them.Fix round 3, final (d7ca288)
The command center adjudicated the fix2 reads at 0fb64d8 (Astra fix2 delta
fix2-pr1127/GPT-VERDICT-0fb64d83b.md; local review at 0fb64d8). This is the final round: below P1, findings close as Follow-ups.worker.py:2056-2057): a key switch launched without a new integrity checkrun_keys()hashes the file again. A mismatch or a read failure stops the run before the switch row, the debit and the launch.test_a_binary_replaced_before_the_next_key_starts_nothing_more:Lists differ: ['anthropic-api-4', 'anthropic-api-3'] != ['anthropic-api-4']readable_binary_identity(); the read-error refusal has its own testtest_a_binary_that_cannot_be_read_at_the_run_is_a_recorded_refusal:PermissionError: [Errno 13] Permission deniedpython3 -m unittest tests.test_claude_review_worker: 102 tests OK (1 skip). merge-tree against main 06f4ed2: rc 0.Repin round (37dfd70)
Both runtime pins were stale, and the currency rule refreshes a stale pin at once. The command center dispatched this one commit on 2026-10-17.
DEFAULT_CLAUDE_BIN→~/.local/share/claude-retained/2.1.302/claude,DEFAULT_CLAUDE_BIN_SHA256→60d7eb7f…29a5; README env lines,--helpnote and rollback pair (now 2.1.300)test_settings_read_the_environment:'~/.local/share/claude-retained/2.1.300/claude' != '~/.local/share/claude-retained/2.1.302/claude'README.md:715, commit9a277e5CHANGELOG entries near the worker:
claude -pwith no permission mode starts indefaulton third-party providers. The worker passes--permission-mode dontAskagainst the Anthropic API.claude -p. The worker's--tools Read,Glob,Grepoffers no Agent tool, and the init record's tools did not change.srt 0.0.80, what changes for the worker: the worker's chain is srt →
credential_run.py→bwrap --unshare-user …→ claude (SandboxLauncher.command). Since 0.0.80, by default srt's seccomp helper refuses the user-namespace flag tounshareandcloneand setsuser.max_user_namespacesto 0 (commit9a277e5). The inner bwrap then stops. Measured with the worker's ownboundary_self_check, keyless, on scratch installs of both tarballs:lo only, API through srt, the rest refused);network boundary check exit 1: bwrap: No permissions to create a new namespace;allowNestedUserNamespaces: trueadded tosrt.json(measurement only, not committed): pass.Adopting that opt-out trades away srt's write denies against the command. The inner bwrap's own read-only root stays. That trade is the command center's decision; its verdict at 37dfd70 adopted the opt-out with srt 0.0.80, and the fold round carries it out (0.0.79 had no namespace limit at any layer, so nothing is given up against it). The rest of the range checks out: the settings keys the worker writes are unchanged,
domain-pattern.tsonly takes over the validator thatsandbox-config.tsused to hold, and the TMPDIR socket names are stillclaude-http-<id>.sockandclaude-socks-<id>.sock(src/sandbox/linux-sandbox-utils.ts:1513,1516at v0.0.80).Commands at 37dfd70:
Fold round (738e24e)
The command center's verdict at 37dfd70 (CHANGES_REQUESTED: one P2 from the Claude security read, one P2 and six P3 from Muse) asked for B1 to B8 in one commit on 37dfd70.
"allowNestedUserNamespaces": trueinBoundary.settings.SRT_VERSION,SRT_INTEGRITYand the tarball sha256 come from the registry (SOTA sources).BOUNDARY_PROBEgains a vsock line: it createssocket(AF_VSOCK)and never connects, and onlyEPERMpasses. The README and the check's pass text name what the boundary refuses. The hold comment is replaced with the sources and the measurement.allowAllUnixSockets, which drops the seccomp helper, joinsSRT_FORBIDDEN.test_the_pinned_srt_is_0_0_80_from_its_registry_tarball_with_nested_namespaces_allowed:'0.0.79' != '0.0.80'.test_the_review_runs_inside_srt_which_allows_the_api_host_only: the settings lackallowNestedUserNamespaces.test_the_boundary_check_passes_only_what_was_measured_closed: the 3 vsock subtests (opened,EAFNOSUPPORT, missing) giveTrue is not false, and the pass text lackssocket(AF_VSOCK) refused.CLAUDE_BINwas hashed from the worker's cwd and bound from srt's run directorysettings_fromrefuses a relativeHOME, and any path setting that is not absolute after~expansion (CLAUDE_BIN, state, ledger, upstream, srt, node).Settingsrefuses a relativeclaude_bin.test_a_relative_client_binary_is_refused_even_with_its_digest: 9 subtests,ConfigError not raised(a relative path with its valid digest among them).CLAUDE_BIN_SHA256lines) states that the check is by path: hashed at the tick and before each key, then bound by path. A writer with the worker's own uid could swap the file in between, and that uid can already editworker.py. Thesettings_fromcomment no longer makes the claim.claude_sha256=Noneswitched every hash gate offSettings.claude_sha256must be 64 hex characters, soNoneor a malformed value raisesConfigError,dataclasses.replaceincluded. The three gates no longer skip on a missing pin. The test fixtures pass a real digest.test_settings_without_a_pin_are_refused: 5 failures,ConfigError not raised. The preflight test drops its(None, False)case.test_a_binary_that_becomes_unreadable_before_the_next_key_starts_nothing_moreagain = binary), it fails withLists differ: ['anthropic-api-4', 'anthropic-api-3'] != ['anthropic-api-4'], while the mismatch sibling still passes.chmod(0)under root; an undebited refusal's receipt was overwritten; the log after a per-key stopchmod(0), so it holds under any euid, and it checks that the receipt'sbinaryis null. (b) An undebitedno_streamrefusal is appended as an uncounted attempt (an identity check,is), so the next attempt gets its own number and directory. (c) When keys ran and were refused, the log names them and the check that stopped the next key.test_a_refusal_before_any_debit_keeps_its_receipt_when_the_next_attempt_runs:Lists differ: [] != [(1, 'launch_failed', False)]. (c)test_a_binary_replaced_before_the_next_key_starts_nothing_more:True is not falseon "did not start".settings_fromcomment state the lexical comparison. A subtest pins that a..spelling of the default path needs its own digest.Fail-first, overall: 107 tests,
FAILED (failures=22, skipped=1), rc 1. All 22 failures are in the seven tests named above, and the other 100 tests pass on 37dfd70's source.srt measurements: worker
boundary_self_check, keyless, run through the real srt → bwrap chain with the retained 2.1.302 binary bound. Scratch installs of both registry tarballs are under/var/tmp/cc-cc-work/build1127-f-scratch/, never the worker's prefix.lo only, API through srt, the rest refusedvsock: opened, not refused with EPERM; every IP line as beforenetwork boundary check exit 1: bwrap: No permissions to create a new namespaceallowNestedUserNamespaces: true)interfaces lo; api 0 404 200; other 7 000 403; direct 6 000 000; loopback_direct 7 000 000; loopback_proxy 0 403 000; vsock refused EPERMOn the host itself,
/dev/vsockiscrw-rw-rw-.RealBoundaryChainTest(fake key, temporary store, stand-in client) also passes on the 0.0.80 install with this head's settings: the innerbwrap --unshare-userruns under the opt-out.Commands at 738e24e (each run from the worktree with
/var/tmp/cc-cc-work/venv-ci/binfirst on PATH, without-I):Comment fold (b61cb4a)
The command center's delta verdict at 738e24e (CHANGES_REQUESTED, two P3 from the Claude security read; Muse PASS) asked for one comment-only commit. No code or test logic changes.
allowNestedUserNamespacesliftsSRT_VERSIONalso citesvendor/seccomp-src/apply-seccomp.c:804-811at v0.0.80: the helper's outer stub then skipsPR_SET_NO_NEW_PRIVSas well as its namespace filter. With no helper, the opt-out drops bubblewrap's--disable-userns(src/sandbox/linux-sandbox-utils.ts:3844-3852). The command still getsPR_SET_NO_NEW_PRIVS(:943-945) and the socket filter. The v0.0.79 stub set neither (apply-seccomp.c:764-769at v0.0.79), so nothing widens against it.settings_fromcomment and the settings test's comment say it is a pathlib Path comparison: it folds repeated slashes,.segments and a trailing slash, but not..or a leading//(checked under Python 3.13.16 and 3.14).Follow-ups
Closed in the fold round: CC FULL at d7ca288, P3-2, P3-3 and P3-4 (verdict B5 and B6).
docs/decisions/2026-10-09-claude-review-worker.md:431-432and:448still name srt 0.0.79 and its tarball. The record is outside this PR's owned paths, so it is left for the command center.B6 (b) note:
publish_pendingposts only the last attempt's unsent result. An uncountedlaunch_failedattempt after a counted attempt that is still unposted (CLAUDE_REVIEW_POST=0) makes that one not the last, as a debitedlaunch_failedattempt already did. The head is then reviewed again, and that result is posted.Fix2 reads, local P2 (
worker.py:102): the retained executable is selected by default before it is provisioned. Micro F5's install step owns this, and the worker is not enabled.Fix2 reads, local P2 (
worker.py:94-96): the cleanup correction lacks a pinned upstream citation.Fix2 reads, Astra P2s (inherited): the model-currency age checks fail on this branch until the landing rebase, which main has already refreshed; the plugin-guard native verification is still outstanding.
GPT P2 (inherited): add
cc-plugin-telemetry@builtinto the disabled built-in plugins and rerun the native probes; the function check covers tools, MCP and permission mode, not the zero-plugin invariant.GPT P2 (inherited incident): the precomputed catalog-freshness test repeats the model-currency age failure.
Micro F4: no test pins the hash check before the sandbox self-check, and no tick-level mismatch test.
Micro F5: the remaining 2.1.299 text (
worker.py:88-93, README:61 and :18-19) and an install step that creates the retained copy (a fresh host stops with "CLAUDE_BIN is not a file").Micro F6: the ledger
clientand receiptbinary.namenow read "claude";file_sha256duplicatessha256_file.GPT fix1 P3: the default-path spelling support is narrower than the Notes state, and the logic corrections of round 1 carry no pinned upstream mechanism citation.
Notes for the reads
allowNestedUserNamespacesin its settings, adds the vsock line to the keyless boundary check, refuses settings without a sha256 pin or with a relative path, and records an undebited refusal as an uncounted attempt.CLAUDE_BIN_SHA256overrides the digest. ACLAUDE_BINoverride naming another path needs it, and the default path keeps the built-in digest. The test is a pathlib Path comparison after~expansion: it folds repeated slashes,.segments and a trailing slash, but not..or a leading//, so such a spelling needs its own digest (fold round B7, corrected in the comment fold).Checklist
🤖 Generated with Claude Code