Symptom
During a long agentic turn (many model calls + tool calls), the footer/sidebar cost never moves. In sessions dominated by a few giant turns the user never sees any $ estimate at all (reported live on a DeepSeek session).
Root cause
All cost accrual happened exclusively in the TurnComplete handler (event_loop.rs): session totals, cache records, and the priced audit. A CodeWhale turn is the whole agentic run, so nothing accrues until the run ends. The per-step Event::TurnUsage receipt (one per model call, emitted by run_turn, reviewer consults, and REPL bridge children) already existed but only fed the timing metrics strip — its usage was never priced.
Codex-comparison note: Codex emits a per-model-call TokenCount event and updates its token/cost surfaces immediately; our TurnUsage is the equivalent signal and was simply not wired to money.
Fix
Price each TurnUsage receipt against the turn's dispatched route (active_turn.route.cost_envelope().audit(usage) — the exact same audit path TurnComplete uses) into a display-only pending_turn_cost. Displayed total = settled + pending + subagents; TurnComplete clears pending and lands the authoritative cumulative price, so nothing counts twice and the #244 high-water mark keeps the display monotonic through the handoff. Pending also cleared defensively at TurnStarted.
Regression test: pending_turn_cost_moves_displayed_total_mid_turn.
Related: #5577 (compaction trigger investigation on the same session), longer-term event-architecture lane (per-domain reducers, Codex-style).
Symptom
During a long agentic turn (many model calls + tool calls), the footer/sidebar cost never moves. In sessions dominated by a few giant turns the user never sees any $ estimate at all (reported live on a DeepSeek session).
Root cause
All cost accrual happened exclusively in the
TurnCompletehandler (event_loop.rs): session totals, cache records, and the priced audit. A CodeWhale turn is the whole agentic run, so nothing accrues until the run ends. The per-stepEvent::TurnUsagereceipt (one per model call, emitted byrun_turn, reviewer consults, and REPL bridge children) already existed but only fed the timing metrics strip — its usage was never priced.Codex-comparison note: Codex emits a per-model-call TokenCount event and updates its token/cost surfaces immediately; our
TurnUsageis the equivalent signal and was simply not wired to money.Fix
Price each
TurnUsagereceipt against the turn's dispatched route (active_turn.route.cost_envelope().audit(usage)— the exact same audit pathTurnCompleteuses) into a display-onlypending_turn_cost. Displayed total = settled + pending + subagents;TurnCompleteclears pending and lands the authoritative cumulative price, so nothing counts twice and the #244 high-water mark keeps the display monotonic through the handoff. Pending also cleared defensively atTurnStarted.Regression test:
pending_turn_cost_moves_displayed_total_mid_turn.Related: #5577 (compaction trigger investigation on the same session), longer-term event-architecture lane (per-domain reducers, Codex-style).