You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A scan of local transcripts (2026-07-20) found two JSONL envelope additions ccpulse doesn't parse:
ποΈ effort β top-level on assistant lines since Claude Code 2.1.212 (first seen 2026-07-17). Records the effort level the request ran at (e.g. "xhigh").
π message.usage.iterations β on every assistant line since Claude Code 2.1.119 (April 2026). A list of per-attempt usage objects, each with its own model, full token breakdown (cache_creation included), and type ("message" | "fallback_message").
Normally iterations is a single entry duplicating top-level usage. But on refusal-fallback turns it has one entry per attempt, and top-level usage covers only the serving attempt. Real example from local history:
ccpulse attributes this turn entirely to Opus and silently drops the refused Fable attempt (434 output tokens + 686K cache reads β billed, since it refused mid-stream). 13 multi-attempt turns exist in local history today. This is the same per-model attribution gap as #455 approaches from the API side: the Fable weekly limit is consumed by attempts ccpulse can't see.
Related markers checked and deliberately skipped: fallbackModel/originalModel on system lines (redundant with iterations), usage.speed (uniformly "standard"; only matters if fast mode is used), usage.server_tool_use (zero across all history), service_tier/inference_geo (constant).
π¦ Scope
pkg/parse: extend the envelope with top-level effort and message.usage.iterations (per-attempt model, token fields, cache_creation, type). Null-tolerant: both absent on pre-2.1.119 / pre-2.1.212 lines.
pkg/cache: store effort as a column on messages; store per-attempt usage in a way cost/token aggregation can consume.
Accounting: token/cost aggregation (OutputTokenBuckets, cost view, status) becomes iterations-aware β sum billed attempts at each attempt's own model pricing instead of trusting top-level usage + line-level model.
(b) explode into synthetic per-model message rows β breaks message.id dedupe semantics; probably a dead end;
(c) single-entry fast path β when iterations has one entry it duplicates top-level usage; decide whether to store child rows always (uniform reads) or only for multi-attempt turns (tiny table, special-cased reads).
β οΈ Interaction with #374 dedupe: multiple JSONL lines of one assistant turn share message.id and repeat the same usage object β iterations must be counted once per message.id, not once per line.
β Acceptance
Assistant lines with effort land it in the DB; pre-2.1.212 lines store NULL.
Multi-attempt turns contribute each billed attempt's tokens/cost under that attempt's model; single-attempt turns produce results identical to today.
Rebuild backfills historic effort/iterations from existing JSONL.
Cost view / status --json totals change only on the multi-attempt turns (13 locally) β verifiable before/after.
π§ Context
A scan of local transcripts (2026-07-20) found two JSONL envelope additions ccpulse doesn't parse:
effortβ top-level on assistant lines since Claude Code 2.1.212 (first seen 2026-07-17). Records the effort level the request ran at (e.g."xhigh").message.usage.iterationsβ on every assistant line since Claude Code 2.1.119 (April 2026). A list of per-attempt usage objects, each with its ownmodel, full token breakdown (cache_creationincluded), andtype("message"|"fallback_message").Normally
iterationsis a single entry duplicating top-level usage. But on refusal-fallback turns it has one entry per attempt, and top-level usage covers only the serving attempt. Real example from local history:ccpulse attributes this turn entirely to Opus and silently drops the refused Fable attempt (434 output tokens + 686K cache reads β billed, since it refused mid-stream). 13 multi-attempt turns exist in local history today. This is the same per-model attribution gap as #455 approaches from the API side: the Fable weekly limit is consumed by attempts ccpulse can't see.
Related markers checked and deliberately skipped:
fallbackModel/originalModelonsystemlines (redundant withiterations),usage.speed(uniformly"standard"; only matters if fast mode is used),usage.server_tool_use(zero across all history),service_tier/inference_geo(constant).π¦ Scope
pkg/parse: extend the envelope with top-leveleffortandmessage.usage.iterations(per-attemptmodel, token fields,cache_creation,type). Null-tolerant: both absent on pre-2.1.119 / pre-2.1.212 lines.pkg/cache: storeeffortas a column onmessages; store per-attempt usage in a way cost/token aggregation can consume.OutputTokenBuckets, cost view,status) becomes iterations-aware β sum billed attempts at each attempt's own model pricing instead of trusting top-level usage + line-levelmodel.limitsarray (per-model weekly limits)Β #455'susage_samples, this data source is the JSONL itself β a rebuild retroactively backfills effort + iterations for every transcript still on disk.π Open design question (brainstorm before implementing)
How to represent a turn that consumed two models' tokens in a one-row-per-line schema:
message_attempts(message_id, attempt_idx, model, token columns, is_fallback) β keyed bymessage.idso it dedupes naturally across multi-content-block lines sharing an id (see π cache: dedupe usage by message.id β multi-content-block turns over-count tokens/cost up to ~100Γ (Opus 4.8)Β #374);message.iddedupe semantics; probably a dead end;iterationshas one entry it duplicates top-level usage; decide whether to store child rows always (uniform reads) or only for multi-attempt turns (tiny table, special-cased reads).message.idand repeat the sameusageobject β iterations must be counted once permessage.id, not once per line.β Acceptance
effortland it in the DB; pre-2.1.212 lines store NULL.status --jsontotals change only on the multi-attempt turns (13 locally) β verifiable before/after.Related: #455 (API-side per-model limits storage).