Skip to content

fix(cost): repair the cost pipeline — pricing table, transcript dedupe, path source - #17

Merged
iaj6 merged 1 commit into
mainfrom
fix/cost-pipeline
Jul 12, 2026
Merged

iaj6 merged 1 commit into
mainfrom
fix/cost-pipeline

Conversation

@iaj6

@iaj6 iaj6 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

The July revival audit found the cost pipeline — the flagship feature — broken in three independent ways. This PR fixes all three.

1. Stale pricing table → current models silently priced at $0

ANTHROPIC_PRICING topped out at Opus 4.7 / Sonnet 4.6, and computeCost() returned 0 for unknown models — so every Opus 4.8 / Sonnet 5 / Fable 5 session read $0.00 and CostCeiling/budget policies failed open. Opus 4.7/4.6 were also priced at the pre-4.5 $15/$75 tier (actual: $5/$25 — 3× over).

  • Table refreshed against published rates (verified 2026-07-11): Fable 5 / Mythos 5 $10/$50, Opus 4.5–4.8 $5/$25, Sonnet 4.5/4.6/5 $3/$15, Haiku 4.5 $1/$5, legacy Opus 4.0/4.1 $15/$75.
  • BEDROCK_PRICING now spreads ANTHROPIC_PRICING, so a newly added model can never price at $0 on one backend only.
  • Unknown models are loud now: surfaced on SessionUsage.unknownModels, and hooks warn on stderr that this spend is invisible to cost policies.

2. Transcript double-count (2–7× inflation)

Claude Code writes one transcript line per content block, each repeating the same message.id with identical usage. Summing per line multiplied real cost by blocks-per-message (verified on a live transcript: 47 usage lines, 16 unique messages, one id repeated 7×). readSessionUsage now counts each message.id once; id-less lines (older formats) are still counted individually.

3. Wrong transcript path for non-slash characters

transcriptPath only replaced / when encoding the cwd, so any project dir containing ./_/space resolved to a nonexistent path and read $0. Hooks now prefer the transcript_path field Claude Code already sends in every hook payload (new readHookUsage helper collapses the four duplicated call sites); the reconstruction fallback encodes [^a-zA-Z0-9] → - to match Claude Code's real encoding.

Verification

  • Full workspace suite green: 1,278 tests, including new coverage for message-id dedupe, id-less lines, unknown-model reporting, dotted-path encoding, and the new model generation.
  • End-to-end against a live Fable 5 transcript: previously $0.00 (unknown model), now $14.42 with dedupe applied.

Caveat

Historical run costs in existing databases were computed with the old table + double count (old Opus runs ~3×·dupes over; Fable/Opus 4.8 runs $0). This PR does not backfill them.

🤖 Generated with Claude Code

…e, path source

The cost pipeline was wrong in both directions:

1. Stale pricing table: no entries for Opus 4.8 / Sonnet 5 / Fable 5, so
   computeCost() silently returned $0 for every current-model session and
   CostCeiling/budget policies failed open. Opus 4.7/4.6 were priced at the
   pre-4.5 $15/$75 tier instead of the actual $5/$25 — a 3x overestimate.
   Table refreshed and verified against published rates 2026-07-11;
   BEDROCK_PRICING now spreads ANTHROPIC_PRICING so new models can never
   price at $0 on one backend only.

2. Per-line double count: Claude Code writes one transcript line per
   content block, each repeating the same message.id with identical usage.
   Summing per line inflated cost 2-7x. readSessionUsage now counts each
   message.id once (id-less lines still counted individually).

3. Wrong transcript path for non-slash-encoded cwds: hooks now prefer the
   transcript_path field Claude Code sends in every hook payload; the
   reconstruction fallback encodes [^a-zA-Z0-9] -> "-" to match Claude
   Code's real project-dir encoding.

Unknown models are no longer silent: they surface on
SessionUsage.unknownModels and the hook warns on stderr that their spend
is invisible to cost policies.

Verified end-to-end against a live Fable 5 transcript: previously $0.00,
now $14.42 with dedupe applied.

Note: historical run costs in existing databases were computed with the
old table and the double count; this change does not backfill them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@iaj6
iaj6 merged commit e1fc493 into main Jul 12, 2026
3 checks passed
@iaj6
iaj6 deleted the fix/cost-pipeline branch July 12, 2026 03:45
iaj6 added a commit that referenced this pull request Jul 12, 2026
…24)

Doc/config drift flagged by the July audit:

- CLAUDE.md was the stalest file in the repo and actively misled agent
  sessions: claimed 6 policy types incl. a nonexistent RequiredApproval
  (actual: 8, with guard/check modes), "eight tables" (actual: 16 — the
  whole auth/webhook/audit/budget layer was missing), no mention of the
  auth model, login/doctor/user/admin/cleanup commands, or the transcript
  cost pipeline. Also marks the jobs/locks/dispatch layer as vestigial
  and documents the stale-dist gotcha for web tests.
- CHANGELOG now covers the June audit sweep (PRs #1-#16) and the July
  revival audit (PRs #17-#23); previously it stopped before both.
- `agentops serve` printed "The API is currently unauthenticated" when
  binding beyond localhost — false since the auth layer shipped. The
  warning now says what's true: auth is required, but traffic is plain
  HTTP, so use the caddy profile beyond a trusted LAN.
- Litestream sidecar mounted the DB read-only, which breaks replication
  outright (litestream needs the checkpoint lock + sidecar files). Now
  rw, with a comment explaining why. The S3-compatible endpoint comment
  now says explicitly that setting AGENTOPS_S3_ENDPOINT alone does
  nothing while the config line stays commented.
- scripts/demo-seed.mjs (previously untracked) is committed, now gated
  behind --yes since it wipes runs/events/policy_results/run_metrics.
- packages/cli/src/build-info.ts is generated on every build, so the
  working tree was permanently dirty. Gitignored; the generator now also
  runs as pretest so fresh clones can run cli tests before a build.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant