Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deterministic-extract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
node-version: "20"

- name: Install tools
run: sudo apt-get update && sudo apt-get install -y binutils
run: sudo apt-get update && sudo apt-get install -y binutils python3

- name: Resolve target version
id: resolve
Expand Down
13 changes: 9 additions & 4 deletions FLAGGED.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ artifacts:

## How this is enforced

`scripts/validate-extraction.sh` gate (c) fails loudly and exits non-zero if any
codename outside the public allowlist (`opus`, `sonnet`, `haiku`, `fable`)
appears in a published artifact. Held evidence lives only in the gitignored
`_held/` directory, which is never published or scanned.
Two layers, so the policy is applied rather than remembered:

- `scripts/extract-binary.sh` moves any config string keyed on a codename
outside the allowlist into `_held/unreleased_codenames.txt` and strips it from
every published artifact, as part of the extraction itself.
- `scripts/validate-extraction.sh` gate (c) then fails loudly and exits non-zero
if such a codename reached a published artifact anyway. Held evidence lives
only in the gitignored `_held/` directory, which is never published or
scanned.

## Decision needed

Expand Down
104 changes: 77 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ models, providers, transports, feature flags, and runtime behavior. This
repository documents which variables each published release reads, and how that
set changes from version to version.

The latest documented release, **v2.1.197**, reads **655** environment
variables.
The latest documented release, **v2.1.234**, reads **994** environment
variables — **867** of them declared in the build's own typed registry, the rest
read literally at a call site.

## What this is

Expand All @@ -29,24 +30,37 @@ with Anthropic.

```text
extractions/<version>/
all_vars.txt every process.env.<NAME> the release reads
all_vars.txt every env var the release reads:
registry.txt U direct_reads.txt
(LC_ALL=C sort -u)
model_provider_env_strings.txt model/provider config keys that appear as
static allowlist strings (v2.1.170+)
registry.tsv the build's declared env registry, typed:
name, parser type, parser arg. Absent for
pre-v2.1.170 builds, which have no registry
registry.txt just the names from registry.tsv
direct_reads.txt literal process.env.<NAME> reads
concat_artifacts.txt captures that are provably strings(1)
boundary accidents and could not be
resolved; excluded rather than guessed at
model_provider_env_strings.txt the model/provider config surface in full;
some of these are also in all_vars.txt
new_vs_<prev>.txt variables added since <prev>
removed_vs_<prev>.txt variables removed since <prev>
SUMMARY.md counts, notable changes, source

scripts/
fetch-release.sh npm dist-tags + npm pack + sha512 manifest
extract-binary.sh emit all_vars + model/provider strings
extract-registry.py the declared typed env registry
extract-direct-reads.py literal process.env.<NAME> reads
extract-binary.sh runs both, unions them, emits model/provider
strings, holds unreleased codenames
compare-release.sh comm-based added/removed between two versions
validate-extraction.sh gates: locale, secrets, codenames, counts

FLAGGED.md items held out of public artifacts and why
```

Versions tracked: `v2.1.121`, `v2.1.139`, `v2.1.170`, `v2.1.197`.
Versions tracked: `v2.1.121`, `v2.1.139`, `v2.1.170`, `v2.1.197`, `v2.1.218`,
`v2.1.234`.

> v2.1.121 is the first version captured here. Its `new_vars.txt` /
> `removed_vars.txt` are the delta against the prose documentation that preceded
Expand All @@ -57,18 +71,45 @@ Versions tracked: `v2.1.121`, `v2.1.139`, `v2.1.170`, `v2.1.197`.

Recent Claude Code releases ship as a compiled, self-contained executable rather
than readable JavaScript. The configuration surface still survives as string
literals in the artifact's constant pool: each `process.env.<NAME>` read and
each model/provider configuration key is present as a literal string. Reading
those literals is a deterministic, repeatable operation.
literals in the artifact's constant pool, so reading it is a deterministic,
repeatable operation.

There are two places to read it from, and using only one of them is wrong.

**The declared registry (v2.1.170+).** Modern builds do not read
`process.env.X` at each call site. They declare the whole set once, typed, and
install it as a proxy object:

```js
var NS={}; yt(NS,{CLAUDE_CODE_FOO:()=>gFoo, ...}); // the declaration
gFoo = Ve.bool() // the type
V = LTs({...NS, ...}, proto) // call sites read V.FOO
```

`extract-registry.py` detects the schema helper (`Ve` above — a different
minified name in every build) by frequency rather than hardcoding it, then
reports every declared name with its parser type. That set is name-complete and
does not shift when the minifier renames things.

**Literal reads.** `extract-direct-reads.py` collects what is still read as
`process.env.X` — mostly vendored dependencies (Azure SDK, gRPC, Bun runtime,
sharp, google-auth) plus a few first-party call sites that never joined the
registry. Pre-registry builds read everything this way.

Using the literal grep alone undercounts a modern build by hundreds of names,
and — worse — reports a variable as *removed* when a build merely moved it
behind the proxy. The originally published v2.1.197 → v2.1.218 diff claimed 126
removals on exactly that basis; re-measured against the registry it is 20. Every
version from v2.1.170 on has been re-extracted with the current method.

The pipeline:

1. `fetch-release.sh` resolves the version via npm dist-tags, packs the exact
version with `npm pack`, and records a `sha512` integrity manifest. The
artifact itself is never committed.
2. `extract-binary.sh` reads the string literals and emits two sorted-unique
lists: `process.env.<NAME>` reads, and the focused model/provider config
allowlist strings.
2. `extract-binary.sh` runs both extractors, unions them into `all_vars.txt`,
emits the model/provider config strings, and moves any unreleased-codename
string into the gitignored hold directory.
3. `compare-release.sh` diffs against the previous version with `comm`.
4. `validate-extraction.sh` gates the result (see below).

Expand All @@ -82,14 +123,15 @@ that exists in the published release artifact. That bounds what we can and
cannot claim:

- **High confidence — the variable is referenced.** If a name appears in
`all_vars.txt`, the release contains a literal `process.env.<NAME>` read for
it. The diff counts are reproducible: re-running the pipeline yields the same
`all_vars.txt`, the release either declares it in its typed env registry or
contains a literal `process.env.<NAME>` read for it. The diff counts are reproducible: re-running the pipeline yields the same
numbers, and the validator enforces that the documented counts match the raw
line counts.
- **Documented, not interpreted.** This repository does not claim to know each
variable's default, type, or runtime effect. It records that the variable is
read, not what reading it does. A literal string is evidence of a reference,
not of behavior.
- **Type, yes. Behavior, no.** For registry entries, `registry.tsv` records the
parser the build applies (`str` / `bool` / `triBool` / `int` / `enum`, plus
enum options and int defaults where they are inline). That is read straight
from the declaration. What the value then *does* is not recorded here — a
declaration is evidence of a knob, not of its effect.
- **String literals can outlive their use.** A name may persist in the constant
pool after the code path that used it is gone. Presence means "referenced in
this artifact," which is a slightly weaker claim than "active in this
Expand All @@ -102,19 +144,27 @@ cannot claim:

```bash
# 1. fetch the exact release and record its sha512 (artifact is gitignored)
scripts/fetch-release.sh 2.1.170
scripts/fetch-release.sh 2.1.234

# 2. unpack the tarball under build/2.1.170/ and point the extractor at the
# release executable, then extract
scripts/extract-binary.sh build/2.1.170/package/<artifact> 2.1.170
# 2. the compiled executable ships in a platform-specific optionalDependency,
# not in the wrapper package -- fetch and unpack that one
(cd build/2.1.234 && npm pack @anthropic-ai/claude-code-linux-x64@2.1.234)
mkdir -p build/2.1.234/package
tar -xzf build/2.1.234/anthropic-ai-claude-code-linux-x64-*.tgz \
-C build/2.1.234/package --strip-components=1

# 3. diff against the previous tracked version
scripts/compare-release.sh 2.1.139 2.1.170
# 3. extract (registry + direct reads + model/provider strings)
scripts/extract-binary.sh build/2.1.234/package/claude v2.1.234

# 4. gate the result (must exit 0 before publishing)
scripts/validate-extraction.sh 2.1.170
# 4. diff against the previous tracked version
scripts/compare-release.sh v2.1.218 v2.1.234

# 5. gate the result (must exit 0 before publishing)
scripts/validate-extraction.sh v2.1.234
```

Requires `binutils` (for `strings`), `grep`, and `python3`.

## Validation gates

`validate-extraction.sh` blocks an extraction unless all of these hold:
Expand Down
5 changes: 5 additions & 0 deletions extractions/v2.1.121/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ literals in the published release artifact.
This is the first version captured in this repository. `all_vars.txt` holds the
`process.env.<NAME>` reads, `LC_ALL=C sort -u`.

This build predates the typed env registry introduced around v2.1.170, so
every read is a literal `process.env.<NAME>` and `all_vars.txt` ==
`direct_reads.txt`. `registry.txt` is absent here by design; the shared
pipeline emits the same file set for every version.

## Counts

| Metric | Value |
Expand Down
Loading