Skip to content

Fix cross-session cost display confusion (throttle starvation + header label) - #23

Merged
gvonnessi merged 3 commits into
mainfrom
fix/session-cost-label
Aug 11, 2026
Merged

gvonnessi merged 3 commits into
mainfrom
fix/session-cost-label

Conversation

@gvonnessi

Copy link
Copy Markdown
Contributor

Summary

Two related fixes to how API-key spend is displayed and persisted:

  • Per-session write throttle. log_sample's throttle was gated on the single most-recent entry in the whole shared usage-history.json, regardless of which session wrote it. A session rendering on a clean cadence could win that shared slot on nearly every tick, starving every other concurrently-running session's writes for as long as it kept rendering. Since each pane's day total is its own live cost plus everyone else's last-written cost, a starved session could read tens of minutes stale in every other pane while its own pane's figure kept climbing — reproduced on real usage data (two actively-spending sessions went 30-40+ minutes without a write while a third held the shared slot almost continuously). The throttle is now keyed per session, which also subsumes the narrower changes_shape/hold_probe exemptions added in 1.3.2 to patch the same root cause for one case; both are removed. MAX_ENTRIES_API grows 1500 → 12,500 to keep the day figure's 26h lookback intact now that write volume scales with concurrent-session count.
  • Header label. The API-key header's dollar figure is the session's own lifetime cost (can span multiple days via --resume), shown right above a day line that means something narrower — today's movement, summed across sessions. Unlabelled, the two read as directly comparable and aren't. Now renders as sess $X.

Test plan

  • cargo fmt --check, cargo clippy --all-targets -- -D warnings clean
  • cargo test — 87 passed (release and debug)
  • Manually verified rendering against a copy of real production usage-history.json (not the live file)
  • Added a_busy_sessions_renders_never_starve_an_unrelated_peers_writes as a direct regression test for the starvation scenario, and a_sample_with_no_session_id_falls_back_to_the_shared_slot for the sid-less fallback path
  • Independent review pass on the throttle change (truncation-collision safety, retention sizing, exemption-removal correctness) — two minor findings (stale comment, missing test coverage) fixed

The header's dollar figure is the session's own lifetime cost
(cost.total_cost_usd off the payload, which can span multiple days
across a --resume), rendered right above a day line that means
something narrower: today's movement, summed across every session.
Unlabelled, the two read as directly comparable and aren't — a
long-running session's all-time total can exceed a same-day
cross-session sum without either number being wrong. Labelling the
header sess makes the distinction visible instead of silently
confusing.
The shared usage-history.json write slot was gated on the single
most-recent entry in the whole file, regardless of which session
wrote it. A session rendering on a clean cadence could win that slot
on nearly every tick, starving every other concurrently-running
session's writes for as long as it kept rendering.

Starved meant invisible: every other pane's day total reads a
session's last-written cost, however old, so an indefinitely-starved
session could sit tens of minutes stale in every other pane while its
own live figure kept climbing — reproduced on real usage data, where
two actively-spending API-key sessions went 30-40+ minutes without a
write while a third, frequently-rendering session held the shared
slot almost continuously.

Keying the throttle on this session's own most recent entry (falling
back to the shared slot only for samples with no session id at all)
bounds staleness to one throttle interval per session regardless of
peer activity. This also subsumes two narrow exemptions
(changes_shape, hold_probe) that had been added in 1.3.2 to patch the
same starvation problem for one specific case; both are now handled by
the general fix and are removed along with the now-unused
SubSessions::last_sample.

MAX_ENTRIES_API grows from 1500 to 12,500 since write volume now
scales with concurrent-session count instead of a fixed global 1/min,
sized to keep supporting the full 26h lookback for several
concurrently active sessions while staying well under READ_CAP_BYTES.
@gvonnessi
gvonnessi merged commit c1a8896 into main Aug 11, 2026
3 checks passed
@gvonnessi
gvonnessi deleted the fix/session-cost-label branch August 11, 2026 09:19
@gvonnessi gvonnessi mentioned this pull request Aug 11, 2026
3 tasks done
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