Repository navigation
fix(patch): make clodex patch work on Claude Code 2.1.304 instead of refusing every platform - #332
Conversation
…refusing every platform
Claude Code 2.1.304 gave its shared child-environment builder an optional
base-env parameter (`function is(e){…i={...e,...process.env}…}`). PATCH 10's
head admitted only a zero-arg function and its body check required a literal
`{...process.env`, so the required patch refused all eight published builds and
clodex patch left Claude Code unpatched.
The head now admits one plain parameter and the body check asks only that the
body spread `...process.env`. Transforms version 15 -> 16. Patched output over
2.1.296..2.1.303 is byte-identical to version 15's; the widened head still
matches exactly one function in every bundle measured.
In all eight 2.1.304 builds the only call passing the parameter reads just
`Object.keys` of the result, so the restore still decides which network values
a child command sees. Verified end to end: through the pristine 2.1.304 a child
shell sees clodex's injected proxy, CA and contract; through the patched one it
sees none of them.
The pr78 anchor harness also gets its two stale `../src` paths corrected to
`../../src` so it loads from .claude/harnesses/ again.
…er a base env
The version-16 comment and both docs said the only call passing the child-env builder's new
parameter reads just the key set. The Bash tool's spawn env passes it too (`{...is(w),…}` with the
shell snapshot's exports, collected under the desktop host), and those values reach the command.
Say why that is still safe on the clodex-only path instead: the snapshot shell is spawned with the
restored env, and its exports keep only names the builder leaves unset when they are parsed. The
existing settings-env and remote agent-proxy overlays still apply.
Re-pin the transform-source digest for the comment edit (version stays 16), and point the cc304
harness's run instructions at /tmp so they leave no config file in the repo root.
bman654
left a comment
There was a problem hiding this comment.
Approving. Thanks, Johan. This is the right root-cause fix, and an independent fix written before this PR was seen landed on the same two changes.
Verified on the maintainer side, all against a pristine 2.1.304:
- Canary matrix, all 8 platforms: the real
clodex patchapplies 12/12 sites on darwin-arm64 (host) and on linux-arm64 and linux-arm64-musl (containers), and those patched binaries start. The probe passes on all 8. - Older releases: across 89 bundles from 2.1.208 to 2.1.302, version 16's output is byte-identical to version 15's. The widened head matches exactly one function in every one of them.
- Mutations: reverting the head turns 4 behavioural tests red, and reverting the body literal turns 5 red. Widening the head to several parameters is caught by the two-parameter test. The harness passes 146/146 on all eight bundles.
- Runtime A/B on darwin-arm64: a stand-in Anthropic endpoint and a clodex-shaped injected env. On pristine, a Bash tool call prints the injected proxy, CA and contract, and the Agent tool offers 4 models. On patched, the child sees none of them and the Agent tool offers the clodex aliases.
One correction, which I pushed to your branch rather than asking for another round. The PR said the only call passing the new parameter reads just the key set. A second call passes it: the Bash tool's spawn env, {...is(w),SHELL:…}, with the shell snapshot's exports, and those values reach the command. A text search misses it because it's a spread (...is(w)), as are 18 of the 48 no-argument calls.
It's still safe on the clodex-only path. The snapshot shell is spawned with the patched builder's restored env, and its exports keep only names the builder leaves unset. The commit rewrites the version-16 comment, patcher.md and claude-code-internals.md to give that reason, scoped to the clodex-only path. It also re-pins the source digest (version stays 16) and moves the harness's run config to /tmp. I also corrected the paragraph in the PR description. The corrected docs were fact-checked against all eight bundles by an independent reviewer.
Claude Code 2.1.304 changed one of the internal functions clodex patches, so
clodex patchrefused to patch it at all: clodex models and aliases vanished from/modeland the Agent tool, large-context models fell back to a 200K window, and commands Claude Code ran could inherit clodex's private proxy settings. This makesclodex patchrecognise 2.1.304 on every platform again.The failure
clodex patchon 2.1.304 (all eight published builds):FAIL PATCH 10: child network environment — anchor not found. PATCH 10 is required, so the whole patch was refused. The autopatch LaunchAgent hit it on the morning the update landed.Root cause
2.1.304 gave Claude Code's shared child-environment builder an optional base-env parameter and spread the parent env over it:
PATCH 10's head only admitted a zero-arg
function X(){, and its body check required the literal{...process.env, which this body no longer contains. The builder isis(e)with the identical shape in all eight builds.The change
function [\w$]+\(\)\{→function [\w$]+\([\w$]*\)\{, so exactly one plain parameter. Defaults, destructuring and multiple parameters still refuse loudly, and a test pins the two-parameter refusal.'{...process.env'→'...process.env'.PATCH_TRANSFORMS_VERSION15 → 16, digest re-pinned..claude/harnesses/pr78-patch10-anchor-over-real-bundles.harness.ts: its zero-argboundFunctioncheck now admits one parameter, and its two stale../srcpaths are corrected to../../srcso it loads again.What the parameter carries. (Corrected by the maintainer; the original paragraph said the only call passing the parameter reads just the key set, which is wrong.) Two calls pass the parameter in all eight builds, both with the shell snapshot's exports.
Object.keys(is(this.#r))reads only the key set. The Bash tool's spawn env,{...is(w),SHELL:…}, carries the values into the command when the exports are collected (desktop host entrypoints, not Windows). On the clodex-only path that is still safe: the snapshot shell is spawned with the builder's restored env, so it does not inherit clodex's proxy, CA or contract, and its exports keep only names the builder leaves unset when they are parsed. That is a property of Claude Code's callers, not something the transform enforces;patcher.mdhas the details. A text search for callers misses this one because it is a spread (...is(w)), as are 18 of the 48 no-argument calls.Evidence
tests/patcher.test.ts): a 2.1.304-shaped fixture, execution with and without a base env (the restored proxy wins over a base carrying the injected one), a one-parameter decoy that must not steal the match, and the two-parameter refusal. Full-file mutation runs: reverting the head reddens 4 behavioural tests, reverting the body literal reddens 5, and widening the head to[^)]*reddens the refusal test. None of these relies on the digest pin alone..claude/harnesses/cc304-patch10-execute-real-builder.harness.ts, extracted from all eight 2.1.304 builds downloaded fromdownloads.claude.aiand checksum-verified against the release manifest): every patch site applies, the patched builder compiles, the token skeleton matches across builds, and it executes correctly on the early-return, credential-scrub, base-env and settings-env branches plus hostile contracts. 146/146, plus 9/9 on the widened pr78 anchor harness.applyClodexPatchesoutput on real 2.1.296, 2.1.299, 2.1.300, 2.1.301, 2.1.302 and 2.1.303 (darwin-arm64) is identical to version 15's. The widened head matches exactly one function in every one of those bundles and in all eight 2.1.304 builds.scripts/probe-patch-mechanism.mjs --expect-version 2.1.304PASS on darwin-arm64, darwin-x64, linux-arm64, linux-arm64-musl, linux-x64, linux-x64-musl, win32-arm64 and win32-x64 (17/17 checks each, all 12 sites applied).HOME, a stand-in Anthropic endpoint, clodex-shaped injected env set by hand): through the pristine 2.1.304 a Bash tool call prints the injectedHTTPS_PROXY,NODE_EXTRA_CA_CERTSand contract, and the Agent tool's model enum has four entries; through the patched one the child sees none of them and the enum carries the clodex aliases. This runs under the CLI entrypoint, where the shell exports are not collected, so the base-env path is covered by executing the extracted builder and parser rather than by a live desktop session.src/patch-transforms.tsandsrc/network-env.tsare byte-identical to this branch's, launched with the proxy/CA/contract env vars unset):clodex-claude --model haiku -prunningenv | grep -iE '^(https?_proxy|node_extra_ca_certs|CLAUDE_CODE_CLODEX_NETWORK_ENV)='in a Bash tool call. Through the pristine 2.1.304 the child shell printedHTTPS_PROXY=http://127.0.0.1:17645,NODE_EXTRA_CA_CERTS=…/clodex-ca.pemand the contract; through the patched one it printed nothing. The translated leg (gpt-5.6-luna) also answers on the patched binary.mainat 2.21.1 plus this commit):pnpm typecheck,pnpm buildandCLODEX_HOME=$(mktemp -d) pnpm testpass (3469 passed, 20 skipped), and both harnesses pass over the eight 2.1.304 bundles (155/155).Not verified