Skip to content

Re-pin the review worker's Claude Code client to 2.1.299 - #1107

Merged
seathatflowsinourveins merged 1 commit into
mainfrom
claude/api-actions-review-worker-client-2299-20261014
Oct 14, 2026
Merged

seathatflowsinourveins merged 1 commit into
mainfrom
claude/api-actions-review-worker-client-2299-20261014

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Scope

  • What this PR changes, in one or two sentences: the review worker's default Claude Code client (DEFAULT_CLAUDE_BIN in tools/claude-review-worker/worker.py) moves from 2.1.296 to 2.1.299, the release the command center adopted on 2026-10-14. The settings test now pins that path.
  • Base commit: 07ca4bd6 (main, after Choose ns-run's Anthropic slot in fill-first order and move on when credit is exhausted #1103). One commit, e2932581. The head merges cleanly with main 67469fb6 (git merge-tree rc 0).
  • Lane: lane:foundation
  • Owned paths touched:
    • tools/claude-review-worker/worker.py: the pin, its comment, and the claude_flags() docstring;
    • tools/claude-review-worker/README.md: the two lines that name the client version;
    • tests/test_claude_review_worker.py: test_settings_read_the_environment pins the default path;
    • manifests/evidence.json: re-registration.

Why. The official CHANGELOG's 2.1.299 section fixes two 2.1.296 faults that a headless worker can meet:

  • "Fixed a regression in 2.1.296 where cloud and headless sessions could run an approved tool call twice if a new message arrived as the session resumed after a restart";
  • "Fixed MCP tool calls in headless and SDK sessions sometimes failing or running twice when a background reconnect closed the connection they were running on".

What was checked on 2.1.299, first-hand and at no cost.

  • claude --help is byte-identical on both releases (22,116 bytes), so the flag checks behind claude_flags() and review_settings() carry over.
  • The worker's own sandbox_self_check starts both binaries in its sandbox without network.
  • The worker's offline probes (probes.py offline: no key, no model call, nothing sent off the host) give 16 PASS, 9 INFO and no FAIL, the same counts as the last 2.1.296 receipt. They cover the sandbox paths, the client's start and init record (Read, Glob and Grep only, no MCP server, no plugin), the instruction fence, and the permission checks against the loopback stand-in.

What is not changed.

  • Comments in worker.py that name a measurement on 2.1.296 keep that version: they date the measurement. Only the lines re-checked here name 2.1.299.
  • Other files that carry 2.1.296 (the surface-watch baseline and dispositions, the two workflows' Bun step, the skill and adoption status tools) belong to other lanes and are not touched.

Follow-ups (not in this change):

  • The paid live probes (probes.py live, three runs) were not run on 2.1.299. The install steps in the README run them before the worker is enabled.
  • The worker is not enabled on this host now (no env file and no srt install at its default path). The probes here used the README-pinned srt 0.0.79 tarball in a scratch directory, with its sha256 checked against the README pin. Enabling the worker follows the README from step 0.
  • Choose ns-run's Anthropic slot in fill-first order and move on when credit is exhausted #1103 landed first (main 07ca4bd6), and this branch was rebased onto it once. The registry was rebuilt from main's file with this branch's three paths re-registered. worker.py had one adjacent-line conflict: Choose ns-run's Anthropic slot in fill-first order and move on when credit is exhausted #1103 added credit_exhausted directly below the pin's line. Both sides are kept, and the changed lines are identical before and after the rebase. An earlier note here said the two branches conflicted only in the registry; that was wrong about worker.py.

SOTA sources

  • Claude Code CHANGELOG, section 2.1.299 (https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md, capture of a public page, read 2026-10-14T03:40:31Z, 1,015,061 bytes): the two fix lines quoted above. The section sits directly above 2.1.296; there is no 2.1.297 or 2.1.298 section.
  • The installed clients, ~/.local/share/claude/versions/2.1.296 and 2.1.299 (--version, --help).
  • The command center's adoption screen for 2.1.299 (claude-code-2.1.299-screen-20261014.md, cited by name).
  • @anthropic-ai/sandbox-runtime 0.0.79, the tarball pinned in tools/claude-review-worker/README.md (step 0), used for the offline probes.

Evidence-class table

Claim Evidence class Command / receipt
The pin test fails on the 2.1.296 pin synthetic tests.test_claude_review_worker with main's worker.py: FAILED (failures=1, skipped=1), '~/.local/share/claude/versions/2.1.296' != '~/.local/share/claude/versions/2.1.299'
The flag set is unchanged local_integration cmp of the two --help outputs: identical, 22,116 bytes each
The worker's offline checks hold on 2.1.299 local_integration probes.py offline, receipt probes/20261014T034535Z/receipt.json (scratch state): binary 2.1.299, result PASS, 16 PASS, 9 INFO; the last 2.1.296 receipt (20261011T212536Z): 16 PASS, 9 INFO
The two faults are fixed in 2.1.299 source_review (changelog text; not exercised) the CHANGELOG lines above

Local commands run

$ python3 -m unittest tests.test_claude_review_worker   # with main's worker.py
FAILED (failures=1, skipped=1)
$ python3 -m unittest tests.test_claude_review_worker tests.test_runtime_profiles tests.test_validate tests.test_evidence_manifest tests.test_registry_rows tests.test_upstream_surface_watch tests.test_task_model_routing
Ran 370 tests in 30.623s
OK (skipped=7)
$ python3 -m unittest tests.test_runtime_worker_openhands_push_gate
Ran 127 tests in 81.267s
OK
$ cmp h296.txt h299.txt   # `--help` of each installed release
(identical)
$ CLAUDE_REVIEW_SRT=<scratch>/srt CLAUDE_REVIEW_STATE=<scratch>/state python3 -I tools/claude-review-worker/probes.py offline
16 PASS, 9 INFO, 0 FAIL (rc 0)
$ python3 scripts/evidence_manifest.py --check
{"files": 11961, "status": "passed"}
$ python3 scripts/validate.py
{"components": 71, "hashed_files": 11961, "profiles": 4, "receipts": 240, "status": "passed"}
$ gitleaks git --log-opts="origin/main..HEAD" --redact .
no leaks found
$ git merge-tree --write-tree origin/main HEAD   # main 67469fb6, before the push
rc 0

Decision record

None: the adoption of 2.1.299 is the command center's decision (its screen and row, cited by name). This PR applies it to the one client pin this lane owns.

Host evidence

Not applicable (no evidence/hosts/ change).

Checklist

  • New/changed GitHub Actions are pinned to a full commit SHA with a
    version comment (no floating tags). (none changed)
  • New/changed workflows declare top-level permissions: {} and grant each job
    only what it needs (contents: read, or an explicitly justified addition). (none changed)
  • No secrets are printed, logged or committed; no new required secret was
    added without a documented owner.
  • No new paid hosting, subscription or billing surface was introduced.
  • Peer-owned untracked files and worktrees were preserved (not deleted,
    moved or overwritten).

🤖 Generated with Claude Code

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 14, 2026
The command center adopted Claude Code 2.1.299. The review worker's default
client binary (DEFAULT_CLAUDE_BIN) was the one pin of this lane still at
2.1.296.

The official CHANGELOG's 2.1.299 section fixes two 2.1.296 faults a headless
worker can meet: an approved tool call run twice when a message arrived as the
session resumed after a restart, and MCP tool calls failing or running twice
when a background reconnect closed their connection.

`claude --help` is byte-identical on both releases, so the flag checks in
claude_flags() and review_settings() carry over; the comments that name a
measurement on 2.1.296 stay as dated provenance. The settings test pins the
default path and fails on the 2.1.296 pin.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@seathatflowsinourveins
seathatflowsinourveins force-pushed the claude/api-actions-review-worker-client-2299-20261014 branch from d49057b to e293258 Compare October 14, 2026 04:22
@seathatflowsinourveins
seathatflowsinourveins marked this pull request as ready for review October 14, 2026 04:22
@seathatflowsinourveins
seathatflowsinourveins merged commit a339425 into main Oct 14, 2026
33 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/api-actions-review-worker-client-2299-20261014 branch October 14, 2026 22:23
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: 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 17, 2026
…Claude Code (2.1.302) (#1127)

* Run the review worker on a retained, hash-checked copy of Claude Code 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]>

* Move the review worker to the retained, hash-checked Claude Code 2.1.300

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]>

* Fail closed on a CLAUDE_BIN override without its digest; record hash-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]>

* Hash the client binary afresh before every review run

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]>

* Review worker: hash the binary before each key's launch; a receipt survives 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]>

* Move the review worker to the retained, hash-checked Claude Code 2.1.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]>

* Review worker: srt 0.0.80 with nested user namespaces and a vsock check; 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]>

* Review worker: correct two comments (srt opt-out scope, Path comparison)

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]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant