Out-of-credits UX: doc + dashboard (sticky banner + control-gating) - #478
Merged
Conversation
ZIJ
force-pushed
the
docs/out-of-credits-ux
branch
from
July 3, 2026 00:24
2515781 to
aca4de4
Compare
…it-gated ops Working doc from the credits-exhaustion triage (a halted autumn org → deep 402 + runtime.fallback loop, surfaced as a generic runtime error). Two gaps: the halted state isn't unmissable at the moment of action, and credit-gated ops fail late + opaque instead of early + clear. Frames it as gap-fill on what already exists (the HaltBanner in app-shell, the edge halt-gates on create/wake, the runtime insufficient_credits signal, and Slice 1 of billing-usage-and-topup.md) — not a rebuild. Two parts: - A: make the halted state unmissable (sticky/stronger banner + surface it at the composer/new-session/run controls). - B: fail credit-gated ops early — preflight the halt on the v3 turn/session path (mirror the edge gates), classify 402-credits as terminal to kill the fallback loop, and gate the UI controls when halted. Includes a which-changes-where table, the flow, and its relationship to billing-usage-and-topup.md (refines Slice 1; defers usage-breakdown/monthly-cap/ attribution to that doc). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ZIJ
force-pushed
the
docs/out-of-credits-ux
branch
from
July 3, 2026 00:24
aca4de4 to
6fc8e96
Compare
…, don't drop
Review pass with two topology facts that reshape Part B:
- The public /v3 API is direct-to-Fly — the CF edge halt-gates can never
be authoritative (SDK users bypass them). Authority must live in
sessions-api.
- sessions-api has no local halt state, and a per-turn cross-service
halt check is the wrong trade (a hop on every turn to save one doomed
provision on the rare halted org).
Resolution: classify the provision 402-credits as TERMINAL (kills the
~6x runtime.fallback loop — that ladder is for missing snapshots, not
billing), then MEMO the halt (TTL ~60s, cleared on success): later
create/message calls fail synchronously with a typed 402, accept parks.
Zero new coupling, self-healing on top-up.
Also names the input-disposition decision the draft skipped: PARK the
unconsumed input and re-kick on un-halt ("top up and your agent resumes
where you left off") instead of consume-and-fail — the durable-sessions
promise, from machinery that already exists. Open questions 1-3
answered in place; added memo-storage as the one genuinely open item.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s (Part A + B3) Implements the dashboard half of the out-of-credits doc on the same branch. A1 — HaltBanner (app-shell) is now sticky (stays in view while scrolling) and error-toned with an icon, so the halt reads as a hard block, not a muted notice. B3 — gate the controls at the moment of action (a user who never scrolls up still can't miss it), via a shared useHalted() hook (dedupes the ['autumn-billing'] query with the banner): - SessionDetail composer: disabled + "Out of credits — top up to resume" inline CTA and placeholder when halted; send guarded. - Sessions "Start session": disabled with a top-up tooltip when halted. Client: ApiError now carries the API's typed `type`, so a 402 insufficient_credits is distinguishable. On a create/steer credits refusal (race / stale state), invalidate ['autumn-billing'] so the banner + gating appear immediately instead of on the poll. Pairs with the runtime/API fail-fast (sessions-api #54, merged). web tsc -b + vite build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… button The runtime posts a user-level agent.message coded insufficient_credits when a turn dies on a provision-402 (sessions-api #54). Render that message with a "Top up" button → /billing (the top-up flow), so the reason isn't just text. Keyed off the event body's `code`, so ordinary agent messages are unaffected. web tsc -b + vite build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
useHalted() was placed after `if (isLoading) return`, so on session start the loading→loaded transition changed the hook count → "Rendered more hooks than during the previous render" → the view's error boundary. Move it to the top with the other hooks (before any early return). Sessions.tsx was already top-level. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The out-of-credits working doc and its dashboard implementation (Part A + B3), on one branch.
Doc
.agents/work/out-of-credits-ux.md— the plan from the credits triage (reviewer-refined: the provision 402 is the preflight; memo it; park, don't drop).Dashboard (Part A + B3)
HaltBanner(app-shell.tsx): stays in view while scrolling, error-toned + icon → reads as a hard block, not a muted notice.useHalted()hook (dedupes the['autumn-billing']query with the banner):ApiErrornow carries the API's typedtype, so a402 insufficient_creditsis distinguishable; on a create/steer credits refusal (race / stale state), invalidate['autumn-billing']so the banner + gating appear at once (not on the 30s poll).Backend (separate, shipped)
The runtime/API fail-fast half — B1 terminal-402 + B2 halt memo + accept-park — is sessions-api #54 (merged).
Not in scope
B4 park-and-resume (auto-resume the parked turn on top-up) — depends on Slice 1c (fast un-halt hook).
Verified: web
tsc -b+vite buildclean. Draft — do not merge without Igor's explicit go.🤖 Generated with Claude Code