Repository navigation
Price Claude at Anthropic's list rates on the day they applied: Opus 5.5, Fable 5.1 and Sonnet 5.5 rows, Sonnet 5 at $2 / $10, and 1-hour cache writes - #647
Conversation
…st instead of stopping the run #647 takes 7 for the Claude cache (its price table). Version numbers name one set of rules across both caches (testClaudeCacheStampedWithTheCodexVersion IsRejected holds them apart), so the Codex rules of P0-1b move to 8 and the history says 6 and 7 are Claude's. The note test read the subagent caveat with `!`. Negative control B removes the caveat, and the trap there ended the whole CLIPulseCore run, so none of B's rule-5 expectations after it were ever reported. XCTUnwrap fails the test and lets the run go on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up (2026-10-07), head
|
| mutation | where | failed |
|---|---|---|
the replacing line priced as .alreadyPriced, the reviewer's mutation |
CI nc/p647-fix-a, run 37500820418 |
3 of 4,041 tests: test_aResponseWrittenOverSeveralLinesIsPricedFromItsLastLine ($0.904 and $0.123) and test_aResponseWhoseLastLinesArriveInALaterReadIsRepricedFromThem ($0.904) |
the replacing line keeps the earlier line's pricing (line: existing.line) |
this Mac | both new tests ($0.904 each); 79 other tests pass |
a line that replaces an open row carried from an earlier read keeps that row's priced flag |
CI nc/p647-fix-b, run 37500827434 |
1 of 4,041 tests: only test_aResponseWhoseLastLinesArriveInALaterReadIsRepricedFromThem. The row is stored without a cost, so the day falls back to pricing its sum with every write at the 5-minute rate, which gives $0.904 |
Run locally, the first and third mutations failed exactly as on CI. Both nc/ branches are deleted.
CI on 7ff00eab: Swift CI run 37500794263 (full matrix) passed every job. CLIPulseCore ran 4,041 tests with 0 failures and 5 skipped, again under the Japanese, ROC and Buddhist calendars, plus 4,079 with 0 failures under -DDEVID_BUILD. Both new tests passed in all five passes. The push run 37500785984 also passed. CI Gate timed out after 40 minutes waiting for the queued Swift CI run and was re-run once Swift CI finished; attempt 2 passed. All 32 checks are green (2 skipped, as on every push run); the PR is blocked only by REVIEW_REQUIRED and is 0 commits behind main. On this Mac: the full suite ran 4,041 tests with 0 failures, the filtered suites 104 with 0 failures, and SwiftLint found 0 violations.
Not changed in this PR
- What's New wording (low). The 1.56 What's New is not written yet. The requirement is recorded in the 1.56 release notes: in all 7 store locales it should say that Claude estimates change, with some models lower and some higher, not that they rise. The PR body's "Effect on this Mac" paragraph now says the same.
- A Claude line in the Mac's "figures changed" note (low). Decision: not in 1.56; What's New covers the disclosure.
- The note's "counted the old way through …" line comes from the archive's record of which days had their Codex slice retaken (Count Codex cached input once in the usage history, and say on the Mac that Codex figures changed #622). Recount the older Codex days of the usage history under the 1.56 rules, on the Mac and in this Mac's cloud rows #649 is reworking that record.
- A Claude line would need its own record of which Claude days were re-priced.
- Count a Claude response once from its last line, and skip proxy estimates #624 already changes Claude figures in 1.56 without a note, so a single Claude note would have to cover both changes.
- What users will see in the meantime: archived Claude days older than the roughly 31-day routine read keep the old, borrowed prices. The Usage Dashboard and the iPhone history will show a step in Claude cost at that boundary.
- Coordination with Port two of CodexBar's subagent and fork rules, and say on the Mac what is still simplified #652 (low). A trial
git merge-treeagainst7ff00eabstill conflicts only in the doc block next to the version constants inCostUsageCache.swift. Whichever of the two PRs merges second rebases. It then re-runstest_claude_rate_changes_come_with_a_rules_version_bumpand the Codex fingerprint pin on the merged tree. Neither pin should move, because7ff00eabchanges no rate and no name resolution. - A device's day and provider replace their cloud rows, so a renamed model stops counting twice (v0.84, not applied) #648 / v0.84 (low). No change here, as the review suggests. Keep A device's day and provider replace their cloud rows, so a renamed model stops counting twice (v0.84, not applied) #648's 404 fallback and apply v0.84 as planned.
- Desktop parity (info). Recorded as a desktop follow-up: port
ClaudePricingTabletosrc-tauri/src/pricing.rs. That means the rows, the cache-hit multipliers, 1-hour writes, the per-request tier and the 4.6 dated rates, ideally with a fixture shared with the Mac. It does not block 1.56. - Public repo data (info). The PR body no longer quotes this Mac's 30-day dollar totals; only the percentages remain. Haiku 3.5 can get its own row later, with its own Claude rules version bump. Until then
claude-3-5-haiku-*stays unpriced.
🤖 Generated with Claude Code
…5.5, Fable 5.1 and Sonnet 5.5 rows, Sonnet 5 at $2 / $10, and 1-hour cache writes The Claude price table stopped at Opus 5, Sonnet 5 and Fable 5. Opus 5.5 borrowed Opus 5's $5 / $25 and $0.50 cache hits (its own: $4 / $20, $0.20), and Fable 5.1 borrowed Fable 5's $1 cache hits (its own: $0.25). Cache hits are most of a Claude Code day, so those two were charged about 2.5x and 4x for most of their tokens, and the "≈" on the cost card came from them. Sonnet 5 was charged $3 / $15, a scheduled increase Anthropic cancelled before it happened. Every cache write was charged at the 5-minute rate, although Claude Code writes much of its cache for an hour (2x input, not 1.25x). - ClaudePricingTable (new, shared with iOS like CodexPricingTable): every row from https://platform.claude.com/docs/en/about-claude/pricing, read 2026-10-03, in the page's column order (input, 5m write, 1h write, cache hit, output). New rows: claude-opus-5-5, claude-fable-5-1, claude-sonnet-5-5, claude-mythos-5-1, claude-mythos-5. Sonnet 5 at $2 / $10 / $0.20. - Dated rates: Opus 4.6 and Sonnet 4.6 paid the 200K long-context tier until their 1M window went to standard pricing on 2026-03-13 (API release notes), so those rows carry the tier in `superseded` until then. The page lists no tier for any current model; Sonnet 4 and 4.5 keep theirs. - The tier is Anthropic's rule (the pricing page as archived on 2026-03-01): a request whose prompt, cache reads and writes included, is over 200K pays the long-context rates on every token. It was split per token kind at 200K each, written when a day's sum was priced; responses are priced one by one now, and a day's sum (the cache-row fallback) never takes the tier. - The scanner reads usage.cache_creation.ephemeral_1h_input_tokens and prices each response at the rate in force at its own time. - costUsageCachePricingVersion 6 -> 7, so every Mac re-reads its Claude logs once. claudeRatesFingerprint() is pinned next to it, as Codex's is. - Tests: the table row by row against the page, the cache multipliers, the dated boundary to the second, 1-hour writes, the whole-request tier, the scanner end to end (Opus 5.5 with split writes, Sonnet 4.6 either side of 03-13, the stored cost), and the fingerprint. Existing expectations that named the old fallback targets or the per-kind split are updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A day row's tokens are many responses, so the fallback must not put a busy day on the 200K long-context tier. 300K Sonnet 4.5 tokens in one cached row cost $0.90, not the $1.80 of one 300K request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sumClaudeTotals, which gives each active session its cost, priced a cached day row without a stored cost through claudeCostUSD, which since the previous commit decides the 200K tier per request. A day row is many responses, so it would have put a busy session on long-context rates. It now uses claudeAggregateCostUSD, as entriesFromClaudeCache does. Found by review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… dating it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… its last line Claude Code writes a response over several lines with the same ids, and the last one counts, so nearly every real response is priced in the branch where a later line replaces an earlier one. The end-to-end tests wrote one line per response and only reached the first-sighting branch; dropping `line:` from the replace branch still compiled (Pending.line had a default) and passed every test. - Pending.line has no default. Each place that makes a Pending names the line that prices it, or `.alreadyPriced` for a row whose cost is already in it. - test_aResponseWrittenOverSeveralLinesIsPricedFromItsLastLine: an Opus 5.5 response over three lines, only the last with the 1-hour split, costs $1.084; a two-line Sonnet 4.6 response with a 301K prompt before 2026-03-13 costs $0.231. - test_aResponseWhoseLastLinesArriveInALaterReadIsRepricedFromThem: the same Opus 5.5 response, its first line in one read ($0.704) and the rest in an incremental one, costs $1.084. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7ff00ea to
f4662c5
Compare
…st instead of stopping the run #647 takes 7 for the Claude cache (its price table). Version numbers name one set of rules across both caches (testClaudeCacheStampedWithTheCodexVersion IsRejected holds them apart), so the Codex rules of P0-1b move to 8 and the history says 6 and 7 are Claude's. The note test read the subagent caveat with `!`. Negative control B removes the caveat, and the trap there ended the whole CLIPulseCore run, so none of B's rule-5 expectations after it were ever reported. XCTUnwrap fails the test and lets the run go on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at is still simplified (#652) * Port two of CodexBar's subagent and fork rules, and say on the Mac what is still simplified P0-1b. #625 counted subagent and fork rollouts with a deliberately small subset of CodexBar's rules. Compared with CodexBar's full rules (upstream 3bbf6bc4) on 33 rollouts built from its own fork and subagent tests, run through both scanners, CLI Pulse matched CodexBar on 8. This ports what was missing and local to one file; 24 of 33 now match, and every remaining difference is either deliberate or needs the parent's file. Ported: - Rule 4 (CodexTokenAccountant): until a child with a history ordinal counts its first tokens, a repeat of the counter it started from, or a copied snapshot (a total equal to its own request, at or above that counter), adds nothing. From CodexBar's replay checks on the first owned token (#3524). Fixed: a forked child that re-reports a larger copied snapshot after its boundary counted the whole snapshot as usage. - Rule 5: a subagent rollout written without a history ordinal (an older Codex) is read whole and classified by CodexBar's CodexSubagentRolloutShape.classify, ported verbatim under its MIT notice: its own history starts at a turn_context followed by a trigger_turn inter-agent message (after copied history, or confirmed by its first own event), or at an opening total with no request of its own. Events before it are copied. Fixed: compact subagents (#2285) and copied-history subagents (#2228) counted their parent's history. Such a file is read again from the start when it grows. Without such a start, the file counts by the other rules as before. Not ported, and stated in a new line under the Codex note's subagent reason (six languages): reading a fork's inherited counter from its parent's file. A fork whose first event repeats its parent's last snapshot, or a subagent without a history ordinal whose copied history has no turn marker, counts that request once more. A tripwire test removes the line when the limit goes. Kept on purpose: #625's reading of a history boundary with no marker ahead of it (a migrated rollout). No number changes on the machine this was measured on: the replica before and after agree on all 171 days of its history (1,083 files), and the Swift scanner and the replica agree on all 18 days of the 31-day window. Codex cache rules version 5 -> 7 (6 is the Claude cache's). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Codex cache rules version 8, not 7, and a missing caveat fails its test instead of stopping the run #647 takes 7 for the Claude cache (its price table). Version numbers name one set of rules across both caches (testClaudeCacheStampedWithTheCodexVersion IsRejected holds them apart), so the Codex rules of P0-1b move to 8 and the history says 6 and 7 are Claude's. The note test read the subagent caveat with `!`. Negative control B removes the caveat, and the trap there ended the whole CLIPulseCore run, so none of B's rule-5 expectations after it were ever reported. XCTUnwrap fails the test and lets the run go on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The simplified-rules line names the one request it can count twice, and a resumed read is tested to keep a child's inherited counter Review of #652 (10-07): - The line now says "some Codex subagent sessions" and "the last request copied in ... can be counted twice", in all six languages (zh: 生成/產生 wording dropped, 重复计入/重複計入 as on the cached-input line). Not "older Codex versions": on the machine measured, the one subagent rollout without a history ordinal among 176 was written by Codex 0.153.4, which gave its other 57 one. Rule 5's comment says so instead of "older". - The Usage Dashboard line's tooltip carries the caveat too (`dashboardHelp`, tested in zh-Hans). - The cache round-trip test sets and checks isSubagent, namesForkParent, inheritedReference and openingSettled, and an older entry decodes them as nil. - New scanner test: a child with a history ordinal read while only its copied history is written, then its replayed snapshot and own request appended. Only the own request counts after the resumed read, which depends on the persisted inheritedReference. - The header lists one more difference from upstream: the history ordinal is taken from the first session_meta only, not from a later repeat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * zh-Hant: 分叉 for a forked session, as in zh-Hans, and 依簡化規則計算 without the stray 是 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
What people saw
The two newest Claude models were charged their predecessors' prices. The Mac's Claude table ended at Opus 5, Sonnet 5 and Fable 5, so Claude Opus 5.5 and Claude Fable 5.1 went through the family fallback and borrowed the newest row in their family. Cache hits are most of a Claude Code day, and that is where the borrowed rates were furthest off:
claude-opus-5-5claude-fable-5-1claude-sonnet-5-5claude-sonnet-5Since #623 those borrowed figures carry "≈", so the first "≈" a Mac showed came from Claude.
Sonnet 5 was charged a rate it never had. It launched at $2 / $10, "introductory" through 2026-08-31, with an increase to $3 / $15 scheduled for 2026-09-01. On 2026-08-10 Anthropic made $2 / $10 the standard price and cancelled the increase. The table held the cancelled $3 / $15 for every day.
Every cache write was charged at the 5-minute rate. Anthropic charges a 5-minute cache write 1.25x input and a 1-hour write 2x. Claude Code splits its writes between the two in every response (
usage.cache_creation.ephemeral_5m_input_tokens/ephemeral_1h_input_tokens), and on this Mac, over the last 45 days, the 1-hour share of the writes was 16% on Opus 5.5, 44% on Fable 5.1, 56% on Opus 5 and 94% on Opus 4.8. The scanner ignored the split.The 200K long-context tier was computed per token kind. On a tiered row, the scanner charged the first 200K input tokens, the first 200K cache reads, the first 200K writes and even the first 200K output tokens at standard rates, and only the excess of each at the premium. Anthropic's rule is per request: a request whose prompt, cache reads and writes included, is over 200K pays the premium on every token, output included. It was also applied to Sonnet 4.6 forever, although its 1M window has been at standard pricing since 2026-03-13, and never to Opus 4.6, which paid the premium before that date.
What changed
The table (
ClaudePricingTable.swift, new, CLIPulseCore, shared with iOS likeCodexPricingTable). Every row was read against Anthropic's pricing page, https://platform.claude.com/docs/en/about-claude/pricing, on 2026-10-03, and is written in the page's column order: base input, 5-minute write, 1-hour write, cache hit, output.claude-opus-5-5(released 2026-09-22),claude-fable-5-1(2026-09-01),claude-sonnet-5-5(2026-09-28), andclaude-mythos-5-1/claude-mythos-5(Project Glasswing, Fable's prices). Dates are from the API release notes, https://platform.claude.com/docs/en/release-notes/api.claude-sonnet-5at $2 / $10, cache hits $0.20.Dated rates.
ClaudePricingTable.supersededholds the 200K tier for Opus 4.6 (1M beta from 2026-02-05) and Sonnet 4.6 (from its release on 2026-02-17) until 2026-03-13, when "the 1M token context window is out of beta for Claude Opus 4.6 and Sonnet 4.6, at standard pricing" (release notes). The instant is 00:00 UTC, the convention ofCodexPricingTable. The premium rates (Opus 4.6 $10 / $37.50, Sonnet 4.6 / 4.5 / 4 $6 / $22.50, with the caching multipliers on top) come from the page as it stood then: https://web.archive.org/web/20260301150515/https://platform.claude.com/docs/en/about-claude/pricing. The page lists no tier for any current model ("Claude 4.6 and later models ... include the full 1M token context window at standard pricing"). Sonnet 4 and 4.5 keep theirs: their 1M beta was retired on 2026-04-30, after which a request over 200K is an error.There is no dated rate for Sonnet 5: $2 / $10 is the only rate it ever had.
Each response is priced at its own time, with its 1-hour writes.
parseClaudeFilereads the line's instant (the same parse that gave its day) andcache_creation.ephemeral_1h_input_tokens, andPricing.claudeCostUSDtakes both (cacheCreation1hInputTokens:,at:; both default to the old behaviour). The 1-hour share is clamped to the writes it is part of. A log without the split is charged at the 5-minute rate, as before.The tier is per request.
ClaudePricingTable.requestCostUSDputs the whole response on the long-context rates when input + cache reads + cache writes is over 200K. The old per-kind split was written for pricing a day's sum; the scanner prices each response as it reads it. The two places that still price a sum now useaggregateCostUSD, which never takes the tier, so a busy day cannot land on long-context rates: the fallback for a cached day row with no stored cost (entriesFromClaudeCache), and a live session's total (sumClaudeTotals, found by review).Cache.
costUsageCachePricingVersion6 → 7: every Mac re-reads its Claude logs once, as #624's bump already does, so stored costs move to the new table.Pricing.claudeRatesFingerprint()(every row, every dated rate, and how a fixed list of names resolves) is pinned next to the version inClaudePricingTableTests, ascodexRatesFingerprint()is for Codex, so a future Claude price change without a bump fails the build. Codex is untouched (its fingerprint pin passes unchanged).Effect on this Mac. A read-only script over this Mac's Claude Code logs for the last 30 days (aggregates only, last line per response, the app's rules approximated) puts the Claude estimate 18% lower under the new table than under the old: Opus 5.5 −41%, Fable 5.1 −25%, Sonnet 5 −30%, while Opus 5 and Opus 4.8 rise about 5% from their 1-hour writes. For the What's New decision on disclosing that Claude estimates rise (#624, mostly in output tokens): with this PR the Claude direction is mixed and depends on the models used, and on this Mac the net is down. So What's New should say that Claude estimates change (some models lower, some higher), not that they rise.
Not in this PR.
usage.speedother thanstandard, and Price Codex at OpenAI's list rates on the day they applied, and mark a borrowed rate with "≈" #623 left Codex's Fast multipliers out too.inference_geo, 1.1x) and the Batch API discount: neither applies to Claude Code's interactive requests as logged.claude-opus-4-5@20251101) still do not normalize; that is unchanged.How it is tested
ClaudePricingTableTests(new, 20 tests)current, all five columns, against the page; a row added to the table without being listed in the test fails.anthropic., Bedrockus.anthropic.…-v1:0and dated spellings.Pending.lineno longer has a default, so each place that makes aPendingnames the line that prices it, or.alreadyPriced.ClaudeTieredAndCodexEdgeCostTests: the Claude half rewritten to the per-request rule (300K input pays $1.80, not $1.20; cache reads count toward the threshold; output does not decide it; exactly 200K is standard and one token more moves every token), with the archived page cited.Claude5PricingTests(Sonnet 5 at $2 / $10; an unknownclaude-opus-6now borrows Opus 5.5 andclaude-sonnet-9Sonnet 5.5;claude-opus-5-1still takes Opus 5 over Opus 4.8),CodexPricingTableTests(the Claude fallback target and its $4),CostUsageCacheIOTests(version 7).swift test(the suites above plusClaudePricingOpus47Tests,CostUsageScannerAccountingTests,UnpricedCoverageTests,ApproximatePriceCoverageTests,ArchiveTokenBasisTests,CodexTokenAccountingTests)acc987feacc987feClaudePricingTableTestsafter the review fix831f01a8swift testafter the 10-07 review fix (ClaudePricingTableTests,CostUsageScannerAccountingTests,Claude5PricingTests,ClaudeTieredAndCodexEdgeCostTests,CostUsageCacheIOTests,ClaudePricingOpus47Tests)7ff00eab7ff00eab8a5cff14(Swift CI run 37120631692)-DDEVID_BUILD4,077, 0 failures (7 skipped). All 18ClaudePricingTableTestsran and passed in all five passes7ff00eab(Swift CI run 37500794263)-DDEVID_BUILD4,079, 0 failures (7 skipped). Both new tests ran and passed in all five passes. Every build and package job of the full matrix passedNegative controls (throwaway
nc/branches on CI, deleted after; each mutation has a test that only it trips):nc/p156-claude-prices-a(run 37119510475)Claude5PricingTests.testPublishedRatesPerMillionTokensandtest_publishedRatesPerMillionTokens(Sonnet 5);test_cacheRatesFollowThePagesMultipliersandtest_aScannedOpus55ResponseIsChargedItsOwnRates($1.284) (Opus 5.5);test_theLongContextDateIsTheOneAnthropicPublished(date);test_sonnet_cache_reads_count_toward_the_threshold,test_sonnet_a_prompt_of_exactly_200K_pays_standard_ratesandtest_aScannedSonnet46RequestPaysTheTierOnlyBeforeMarch13($0.093) (tier); the fingerprint pinnc/p156-claude-prices-b(run 37119524297)test_aScannedOpus55ResponseIsChargedItsOwnRates($0.904);test_aScannedSonnet46RequestPaysTheTierOnlyBeforeMarch13($0.093);test_aTableChangeChangesTheClaudeFingerprint;test_aCachedDayRowWithoutAStoredCostIsPricedAsASum($1.80);testCurrentPricingVersionIsSeven; the pinnc/p156-claude-prices-c(run 37120002017)test_aSessionWhoseRowsHoldNoStoredCostIsPricedAsASum($1.80);test_cacheRatesFollowThePagesMultipliers,test_claudeCacheRowsCarryTheirResponsesCost($1.00),test_publishedRatesPerMillionTokensand the pin (Fable 5.1)nc/p647-fix-a(run 37500820418, after the 10-07 review).alreadyPriced(without its time or 1-hour split), the reviewer's mutationtest_aResponseWrittenOverSeveralLinesIsPricedFromItsLastLine($0.904 for Opus 5.5, $0.123 for Sonnet 4.6) andtest_aResponseWhoseLastLinesArriveInALaterReadIsRepricedFromThem($0.904)nc/p647-fix-b(run 37500827434)pricedflagtest_aResponseWhoseLastLinesArriveInALaterReadIsRepricedFromThem($0.904)line: existing.line)Review. Gemini through
agy-reviewon the diff, with the pricing page, the release notes and the archived page: one finding, fixed in831f01a8with a test (NC-C above):sumClaudeTotals, which gives a live session its cost, still priced a cached day row with no stored cost as one request, so a busy session could have landed on the tier. Codex CLI was out of quota.Second review, 2026-10-07, on this head (
8a5cff14): Gemini throughagy-reviewagain, on the same four questions (every rate and date against the page, the archived page and the release notes; event time and the 1-hour split on every path ofparseClaudeFile, including rows carried from an earlier incremental read; any other place that prices Claude, or text that is now false; whether the tests catch a wrong rate, a lost split, a lost time and a per-kind tier): APPROVE, no findings. Codex CLI was tried again and was out of quota again. A repo-wide search found no other Claude price table (the Companion, the backend and Android carry none) and no user-facing string that names the old rates or the 1.25x write.Third review, 2026-10-07, on
8a5cff14: APPROVE (non-blocking). Its one medium finding was a test gap: the branch where a later line of a response replaces an earlier one, which prices nearly every real response, had no test of its 1-hour writes or event time. That is fixed in7ff00eab, with the tests and negative controls above. The low and informational findings are answered in the PR comment of the same day.🤖 Generated with Claude Code