-
Notifications
You must be signed in to change notification settings - Fork 0
refresh usage after turn
Date: 2026-08-12
Tracking: Issue #143; sub-issues #144, #145, #146, #147
Implementation: codex/feature-refresh-usage-after-turn, PR #142
Remaining Usage Flyout (ADR-005) scoped usage freshness to a connection generation and a 60-second TTL: the first signed-in Ready state fetches once, opening the popup refreshes only after the TTL, and a Busy-to-Ready turn transition by itself does not refetch. That kept the display stable across ordinary turns, but it also meant a completed turn — which has just consumed model calls — could show a stale remaining-usage percentage for up to a minute. This plan adds an explicit usage-consumption boundary on top of the existing model, without touching any of its guarantees.
- Treat a completed conversation turn, and a completed context compaction, as explicit usage-consumption boundaries, independent of the Busy-to-Ready state transition ADR-005 already excludes.
- After the existing UI transcript/status projection for the event finishes, force one
worker/account/rateLimitsread. "Force" means the read bypasses the 60-second TTL check but still goes through the existing connection-generation and push-version guards, so a concurrent push or a lifecycle invalidation still wins over a stale in-flight read. -
TurnCompletedalso covers turns the app-server reports as interrupted — it still arrives asturn/completed. A transport-level failure (network loss, app-server exit) is different: it surfaces as aDegradedconnection state instead, under which usage is already considered unavailable, so it is explicitly not a forced-refresh trigger. The last successful snapshot simply remains visible until reconnection restores availability. - Context compaction needed its own trigger because
/compactconsumes model calls but the app-server does not always follow completion withturn/completed— the Worker already special- cases this same gap to restore theReadyconnection state after compaction. - If the forced read itself fails, the last successful snapshot and its timestamp stay visible, and
the next turn/compaction boundary (or the existing TTL/
/statuspaths) retries it.
- Design and ADR (#144) — amend ADR-005 and the usage-freshness sections of the design/implementation docs with the new trigger and the explicit turn-failure exclusion.
-
Implementation and tests (#145)
— force the refresh from both
ChatViewModel.OnConversationEventAsync(onTurnCompleted) andChatViewModel.OnContextCompactedAsync(on completed compaction), reusing the existing refresh gate, connection generation, push version, and lifetime cancellation. No Worker/RPC contract, XAML, or package change. -
Manual verification (#146) —
confirm in a Visual Studio Experimental Instance that the header, Usage popup, and updated
timestamp stay in sync after a normal turn, after
/compact, and after an interrupted turn. -
Terminal-failure follow-up
(#147) — confirm whether the
app-server protocol has a distinct terminal-turn-failure notification separate from
turn/completed, and extend the forced-refresh trigger to it if one exists.
- A
TurnCompletedevent forces exactly one additional rate-limit read within the TTL window, and the header/popup reflect the new percentage and updated time. - The same holds for a completed
context/compactedevent; an in-progress compaction event is a no-op. - No forced read occurs when usage is unavailable (signed out, disconnected, or
Degraded). - A failed forced read preserves the last successful snapshot and is retried on the next boundary.
- Existing regression coverage remains green: Busy-to-Ready alone does not refetch, a push always wins over a stale in-flight read, and a disposed view model discards late responses.
-
dotnet build CodexForVisualStudio.slnx -c Releaseis warning-free; fullCodex.VisualStudio.Core.TestsandCodex.VisualStudio.Ui.Testspass.
No Worker/RPC contract version bump, no new NuGet package, no XAML change, and no forced-refresh
trigger for a turn that ends via Degraded (tracked separately in
#147 as an open question, not assumed
here).