Skip to content

fix(analytics): codex tmux turns priced correctly — provider attribution, usage normalization, history heal (#860) - #861

Merged
olegbrok merged 4 commits into
mainfrom
fix/codex-cost-attribution-860
Jul 11, 2026
Merged

fix(analytics): codex tmux turns priced correctly — provider attribution, usage normalization, history heal (#860)#861
olegbrok merged 4 commits into
mainfrom
fix/codex-cost-attribution-860

Conversation

@olegbrok

Copy link
Copy Markdown
Collaborator

Fixes #860.

The bug

TmuxSession._log_turn_cost_and_analytics hardcoded provider="anthropic", so every CodexTmuxSession turn landed in analytics as anthropic/gpt-* — a (provider, model) pair the pricing lookup can never match (_provider_alias never crosses providers). All codex tmux burn priced at $0 and was invisible in every cost view. On prod today: 779 turns, ~64M input tokens, $0.00.

A second, latent half: codex reports input_tokens INCLUSIVE of the cached prefix (cached span under cached_input_tokens), while every daemon consumer follows the Anthropic disjoint convention. Even with a rate row, codex turns would have over-billed the cached span at the full input rate — ~7x on real fleet traffic (measured across the fleet's 2026-07 rollouts: 97.5% of codex input is cached reads).

The fix

  • _ANALYTICS_PROVIDER class attr on TmuxSession (anthropic); CodexTmuxSession overrides with codex_cli (the SDK path's value, aliased onto openai rate rows at read time).
  • _normalize_turn_usage hook: identity on the Claude transport; CodexTmuxSession converts the codex schema to the disjoint convention ONCE, before any consumer (accumulation, pricing, analytics, context gauge) reads the dict. Mirrors CodexSession.uncached_input_tokens on the SDK path. Missing/malformed cached counts pass through untouched — fail toward the visible undercount, never a silent double-bill.
  • pricing.RATE_TABLE: OpenAI family — gpt-5.6-sol / gpt-5.5 at $5/$30 (cached $0.50), gpt-5.3-codex at $1.75/$14 ($0.175). No cache-write billing.
  • Analytics seeds: gpt-5.6-sol row; seed/RATE_TABLE parity guard restructured to be provider-aware.
  • _migrate_codex_provider_attribution: heals deployed anthropic/gpt-% rows to codex_cli (turn usage + session facts; idempotent, narrow — no honest row can match). Rows carrying the pre-Codex tmux agents priced at $0: provider misattribution + missing gpt-5.6-sol rates + dropped cached-token split #860 signature (cached_input_tokens=0) also move their inclusive input count to the cached column: repricing it at the full rate would show ~$320 (~7x overstated); the shift bounds the error at roughly -20%, failing toward the undercount.
  • agent_registry: gpt-5.6-sol catalog seed; gpt-5.5 catalog price realigned to the official $5/$30 via _PRICE_CORRECTIONS (exact-stale-triple gated, operator overrides survive).

Evidence — prod DB copy through the migration

before/after: 779 codex turns from $0.00 to $33.43 with truthful attribution

Tests

31 new/updated across the five seams (attribution via class attr, normalization matrix + call-site ordering, gpt rates, provider-aware parity guard, migration heal/reprice/narrow/idempotent, catalog seed + price correction). Full suite: 4302 passed, 2 skipped.

Deploy note

Merge is HELD for tonight's release + daemon bounce (batched with #831). The migration runs on daemon init; the dashboard's codex history moves $0 → ~$33 notional.

🤖 Generated with Claude Code

olegbrok and others added 4 commits July 10, 2026 14:31
…ion, usage normalization, gpt-5.6-sol rates (#860)

Source changes (tests follow in next commit):

- TmuxSession._ANALYTICS_PROVIDER class attr (anthropic) used at the
  log_turn_usage call instead of a hardcoded "anthropic";
  CodexTmuxSession overrides with "codex_cli" (SDK-path default,
  aliased onto openai rates by _provider_alias).
- _normalize_turn_usage hook: identity on the Claude transport;
  CodexTmuxSession converts the codex schema (inclusive input_tokens +
  cached_input_tokens) to the daemon's disjoint convention ONCE, before
  any consumer reads the dict — mirrors CodexSession.uncached_input_tokens.
- pricing.RATE_TABLE: OpenAI family (gpt-5.6-sol, gpt-5.5 at 5/30/0.50;
  gpt-5.3-codex at 1.75/14/0.175); no cache-write billing.
- analytics seeds: gpt-5.6-sol row; _migrate_codex_provider_attribution
  heals deployed anthropic/gpt-% rows to codex_cli (idempotent, narrow).
- agent_registry: gpt-5.6-sol catalog seed; gpt-5.5 catalog price
  realigned to official 5/30/0.50 via _PRICE_CORRECTIONS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ched rate

Tests for every #860 seam:
- tmux_session: provider comes from _ANALYTICS_PROVIDER (subclass-driven),
  base _normalize_turn_usage is identity, hook runs BEFORE all consumers
  (accumulation, pricing, analytics see the same normalized dict).
- codex_tmux_session: _ANALYTICS_PROVIDER=codex_cli; normalization unit
  matrix (convert/pass-through/malformed/clamp/no-mutate); end-to-end
  turn-complete pricing a codex-schema usage dict at openai rates with the
  disjoint split.
- pricing: gpt-5.6-sol/gpt-5.5 ($5/$30/$0.50), gpt-5.3-codex
  ($1.75/$14/$0.175), cache writes bill $0.
- analytics_store: seed/RATE_TABLE parity guard restructured to be
  provider-aware (gpt rows must sit under openai); migration tests (heal,
  reprice, narrow, idempotent).
- agent_registry: gpt-5.6-sol catalog seed, openai seed/RATE_TABLE parity,
  gpt-5.5 stale-price correction + operator-override safety.

Migration amendment (src): the provider flip alone would over-bill
history ~7x — pre-#860 rows logged codex's INCLUSIVE input with the
cached span unrecorded (fleet rollouts measure 97.5% cached). Rows
carrying that signature (cached_input_tokens=0) move the whole count to
the cached column: error bounded ~-20%, failing toward the visible
undercount, same direction as _normalize_turn_usage. Verified against a
copy of the prod DB: 779 mislogged turns, $0.00 -> $33.43 (naive flip
would have shown ~$320).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…861 P2)

The sol model page (developers.openai.com, verified 2026-07-10)
documents cache writes at 1.25x the uncached input rate ($6.25/Mtok);
_GPT_FRONTIER hardcoded $0 for the whole family and the regression
locked it. Split gpt-5.6-sol into its own rate dict with 6.25 in both
write positions (OpenAI has no TTL split); gpt-5.5 and gpt-5.3-codex
keep $0 — their pages list no write tariff. Regressions updated: sol
write-billing pinned through both compute entry points; the shared-dict
identity assertion dropped.

Latent-only today (codex rollout usage carries no cache-creation
fields), so the #860 migration evidence is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@olegbrok
olegbrok merged commit cd33f02 into main Jul 11, 2026
11 checks passed
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.

Codex tmux agents priced at $0: provider misattribution + missing gpt-5.6-sol rates + dropped cached-token split

1 participant