fix(credential): a subscription paid for the run, and one print line decided it

ANTHROPIC_API_KEY is now the ONLY accepted credential. Through v0.1.0 the
preflight cleared on CLAUDE_CODE_OAUTH_TOKEN, and run_s10 went further: an
unset key printed "note: relying on the CLI's own credentials" and carried
on. That note was not a warning, it was a decision - made silently, on the
operator's behalf, about who pays. Both paths are gone; a run with no key
refuses with exit 2 before anything is opened.

Red first, both halves: _check_credentials refuses an OAuth-only env, and
the run entrance is driven as a real subprocess with a deliberately missing
bundle, so the credential refusal must win the race against the bundle
error. Detach it and the process reaches navigate_bundle instead - a
different exit code, no refusal line, the fallback back in the output. The
positive control (key set) gets past the gate and fails on the bundle, so
the gate is a gate and not a wall. 997 -> 1002, offline, no key in env.

The SDK exception is now stated where a reader meets it, not implied: this
framework runs on the Claude Agent SDK, which starts the Claude Code CLI it
bundles as a subprocess. That is the SDK's intended use WITH an API key,
and it is a deliberate, stated exception to the owner's rule that his own
code never starts Claude Code. Rewriting to direct HTTP calls was weighed
and declined - measuring what the Agent SDK offers is the point of D7. The
repo is closed as a worked example.

Two prose claims were corrected rather than left standing: run_s10.py is no
longer byte-frozen (it carries exactly one change, and runs/s10/ is still
the v0.1.0 run), and its two round() call sites moved 110->118, 130->138.

The credential paragraph is prose under an existing heading, not a new
section: test_readme_anchors_loadbearing.py pins 14 heading ids MEASURED on
the published page and forbids re-deriving them. This order forbids push, so
a new heading could not have been honestly re-measured.

Version 0.1.1: pyproject.toml, uv.lock self-entry, CHANGELOG - 3 of 3. No
version badge in README, no constant in src. v0.1.0 stands as released.

Order 20260920T131502Z-7496226791-from-.claude. The older D7 mirroring order
20260913T053840Z-9473220509 is retired unexecuted: po closes at v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-20 15:24:55 +02:00
commit f92b04bf62
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
14 changed files with 210 additions and 37 deletions

View file

@ -58,8 +58,10 @@ uv run ruff format --check .
uv run mypy src
```
Tests run offline without an API key; live runs use the Claude API — keep `ANTHROPIC_API_KEY` in
your environment, never commit it.
Tests run offline without an API key. A live run requires your own `ANTHROPIC_API_KEY` — keep it
in your environment, never commit it. It is the only accepted credential: a consumer Claude Code
subscription is not supported and must not be used to pay for a run, so a `CLAUDE_CODE_OAUTH_TOKEN`
is refused by the preflight and by the run entrance.
## Questions?