The audit path already had mechanical detectors backed by mutation-checked tests. The capture path - the half that writes to your files - was prose and prompting only. This release gives it a deterministic core, so capture and audit compute the same quantities from the same code instead of describing them twice.
The core
Seven subcommands behind scripts/notation-core.py: open, register, measure, route, price, price-record, gate, close. Stdlib-only Python, no dependencies, 3.9 floor.
stdout carries JSON only, stderr carries the explanation, and the exit code carries the verdict - 0 clean, 1 gate refused, 2 usage or internal error, never conflated. A crash returning 1 would read as a refusal; a crash returning 0 would read as clean.
What it changes in practice
The budget is now enforced across a whole run, not per proposal. Three additions that are each individually legal can still take the global file over budget, and nothing that looks at one proposal at a time can see that. A run-scoped ledger is what makes the verdict possible.
notate-all is the main beneficiary. It applies every proposal with no interactive picker, so previously nothing stood between a bad extraction and ~/.claude/CLAUDE.md. The gate is neither optional nor skippable there.
Capture now checks its own work. After the writes land, the run re-measures and compares predicted against actual with zero tolerance. An append that succeeded while its removal did not is now a reported finding naming the file and the gap, rather than a silent success. Interference from another session is reported separately from your own edit not landing - they are different problems with different fixes.
A zero says what it searched. Ranked note candidates come back with the count of notes searched and the tokens that matched, so "searched 87, none fit" cannot be mistaken for "searched 0 because the path was wrong" - the second would otherwise license minting a new note every time.
Scope stays asymmetric. The global file gets a hard gate; a project CLAUDE.md gets the same measurements as advice and is never refused. If a verdict cannot be produced at all, a global write is blocked and a project write proceeds with a warning.
Thresholds
Every size threshold now lives in one file. Roughly forty restatements were removed from the audit references, both command docs, the README, and the plugin description, and a drift test fails the build if one comes back. Changing a threshold is one edit, and prose can no longer disagree with the gate.
Verification
./scripts/verify.sh remains the one command, now around 4.4 seconds across fourteen suites. It also runs on Linux and on Python 3.9, 3.11 and 3.13 in CI, which caught a portability problem on its first run.
Follow-ups tracked as #27 (ledger retention), #28 (a path-collision case), #29 (a PreToolUse hook so the gate cannot be skipped), and #30 (measuring the skill listing against its own budget).