Skip to content

Out-of-credits UX: doc + dashboard (sticky banner + control-gating) - #478

Merged
ZIJ merged 5 commits into
mainfrom
docs/out-of-credits-ux
Jul 3, 2026
Merged

Out-of-credits UX: doc + dashboard (sticky banner + control-gating)#478
ZIJ merged 5 commits into
mainfrom
docs/out-of-credits-ux

Conversation

@ZIJ

@ZIJ ZIJ commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

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)

  • A1 — sticky, stronger HaltBanner (app-shell.tsx): stays in view while scrolling, error-toned + icon → reads as a hard block, not a muted notice.
  • B3 — gate credit-gated controls at the moment of action via a shared useHalted() hook (dedupes the ['autumn-billing'] query with the banner):
    • SessionDetail composer: disabled + inline "Out of credits — top up to resume" CTA/placeholder when halted.
    • 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 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 build clean. Draft — do not merge without Igor's explicit go.

🤖 Generated with Claude Code

@ZIJ
ZIJ force-pushed the docs/out-of-credits-ux branch from 2515781 to aca4de4 Compare July 3, 2026 00:24
…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
ZIJ force-pushed the docs/out-of-credits-ux branch from aca4de4 to 6fc8e96 Compare July 3, 2026 00:24
ZIJ and others added 2 commits July 3, 2026 01:29
…, 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>
@ZIJ ZIJ changed the title docs: out-of-credits UX — unmissable state + fail-early on credit-gated ops Out-of-credits UX: doc + dashboard (sticky banner + control-gating) Jul 3, 2026
ZIJ and others added 2 commits July 3, 2026 01:51
… 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>
@ZIJ
ZIJ marked this pull request as ready for review July 3, 2026 01:10
@ZIJ
ZIJ merged commit 85c0adb into main Jul 3, 2026
4 checks passed
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