Where Claude Code keeps its prices, and how the vocab is pinned to them

Issue #3878. The divergence review called models.vocab.json a second source of truth beside CC's own pricing and suggested reading CC's numbers instead. Measured on CC 2.1.258: there is nothing to read, so the fix is a pin, not a redirect.

Three surfaces, measured on the 2.1.258 binary

surfacewhat it isreadable from outside?
baked cataloga pricing_tiers table (8 tiers with input, output, 5m and 1h cache write, cache read) plus rows like {id:"claude-fable-5-1",…,pricing:"tier_10_50_cache_read_0_25",…}, 19 rowsonly by scanning the binary
modelPricing managed settingmultiplier in (0, 1] plus per-model overrides; log lines say "priced at the managed modelPricing setting; catalog: list price"an enterprise override, not a source of list price
claude -p --output-format jsonmodelUsage[model].costUSD with costBasis: "list"; one haiku call reproduced list arithmetic to the centyes, but it costs a real API call, so never in CI

The tier table, as read from the 2.1.258 binary

Tier names are opaque labels: haiku_45 sits beside tier_10_50_cache_read_0_25, and the first draft of this canary, which decoded names as a grammar, failed on haiku 4.5 within one run. The binary carries the numbers themselves in a pricing_tiers table, so the canary reads that and never guesses. Pick a tier to see what the vocab must say.


Dollars per million tokens. The vocab stores input, output, cache read and the 5-minute cache write; the 1-hour write (2x input) is in the binary but not in the vocab.

The canary

scripts/check-model-pricing-tiers.mjs --check runs in the nightly CC Contract Probe, the one workflow that installs the binary. It reads the pricing_tiers table and every {id:"claude-…" row with a tier name, matches vocab models by row id or by first_party id, and compares four numbers.

exitverdictmeaning
0OKevery priced vocab model with a catalog row matches
1DRIFTa vocab row disagrees with the binary; fix the vocab
2CANNOT-OBSERVEno binary, or no rows parsed; nothing was verified and the gate says so
3UNKNOWN-TIERa catalog row names a tier the table does not define; read the binary before changing anything

Not in this change: honouring the managed modelPricing override inside the cost estimator, so an org-contract user sees the same number in the plugin as in /cost. That stays open on #3878.