Skip to content

fix(dashboard): credits panel fails fast on API-key auth (#5728) - #5750

Merged
bolichen97 merged 1 commit into
mainfrom
fix/credits-apikey-fail-fast-5728
Aug 25, 2026
Merged

fix(dashboard): credits panel fails fast on API-key auth (#5728)#5750
bolichen97 merged 1 commit into
mainfrom
fix/credits-apikey-fail-fast-5728

Conversation

@CrysisDeu

Copy link
Copy Markdown
Collaborator

Summary

Closes #5728

On accounts authenticated with an API key, the dashboard's Kiro Credits panel showed a warming spinner while the backend usage refresh spent its timeout inside fetch_usage_limits searching for SSO/OIDC bearer tokens that this auth type never has; with the text-scrape fallback disabled (the production default) no explanatory terminal state ever reached the frontend — the panel spun through the timeout, then the pill silently hid, and every 30s refresh repeated the cycle.

Backend

In _refresh (src/kiro_crew/dashboard/handlers/sessions.py), right after the _fetch_whoami identity read: when account_type is kiro-cli's ApiKey AuthMethod variant (the compare normalizes case and strips separators, so an upstream respelling like API_KEY still fails fast instead of silently regressing to the slow path), publish the existing unavailable-marker shape with a reason — {"available": false, "reason": "api_key_auth"} — log at info, and return before the credential search and the billed scrape.

Frontend

The reasoned marker becomes a new terminal 'api-key' state:

  • the top-bar credit segment renders the same terminal dash as 'failed', with a label that says why (app.kiro_credit_usage_api_key);
  • the account modal explains it (components.kiroAccountModal.credit_usage_api_key_auth) instead of spinning or showing the generic unavailable line;
  • a reasonless available: false (non-Kiro provider) keeps its existing hide-the-pill behavior.

New i18n keys across all 13 authored catalogs + regenerated en-XA.

Scope guard (per the issue's fix contract)

The text-scrape fallback, fetch_usage_limits credential-search behavior for SSO accounts, and _identity_matches_account are untouched.

Screenshots

The existing capture-credit-pill-states.mjs harness gained an api-key scenario (real built SPA, stubbed network; the harness self-checks the segment's aria-label at capture time).

Before (what API-key users saw, permanently) After: terminal dash After: capsule
warming spinner that never resolved header with terminal dash capsule with dash

Account modal in the new state:

modal explaining API key auth

Testing

  • Backend: new TestApiKeyAuthFailFast (6 tests) — short-circuit before fetch_usage_limits + exact marker shape; respelling-tolerant compare parametrized over apikey / APIKEY / " ApiKey " / API_KEY / Api-Key; no billed scrape spawned even with the scrape opted in; SSO negative control still reaches the API. Mutation-verified: disabling the fail-fast branch turns 5 of 6 red (the negative control stays green by design). Full test/test_session_usage.py: 93 passed.
  • Frontend: pill test pins the api_key_auth payload as terminal (dash + explanatory label, no spinner) and the reasonless payload as still-hidden; modal test pins the specific message over the generic one. 17 passed across both files; npx tsc -b clean.
  • Gates: isort / flake8 / mypy (1101 files) / black baseline / brand / harness-parity / i18n:check / lint:i18n all green locally.

Pre-push review

Two model-pinned lanes reviewed the commit diff before opening this PR: GPT (gpt-5.6-sol, codex-review contract) — PASS, no findings; Opus (claude-opus-5, claude-review + AUTOSDE contract) — PASS, 0 blocking, 4 advisory. Adopted: A1 (docstring/commit-message overclaim reworded), A3 (respelling-tolerant compare + parametrize), A4 (i18n key order in translation catalogs). A2 (surfacing the whoami identity beside the marker so the modal can show the account email) is a two-sided change beyond this fix's minimal surface and is left as a follow-up.

API-key accounts hold no SSO/OIDC bearer token, so the usage refresh spent
~30s inside fetch_usage_limits walking credential stores that cannot contain
one, and with the text scrape disabled no EXPLANATORY terminal state ever
reached the frontend: the credits panel spun through the timeout and then hid
itself with no explanation, every refresh.

Backend: right after the whoami identity read, an account_type of ApiKey
(kiro-cli's AuthMethod enum variant; the compare normalizes case and strips
separators so an upstream respelling still fails fast) publishes the existing
unavailable-marker shape with a reason
({available: false, reason: 'api_key_auth'}) and returns before the
credential search and the billed scrape.

Frontend: the reasoned marker becomes a terminal 'api-key' state — the
top-bar segment renders the same dash as 'failed' with a label that says why,
and the account modal explains it instead of spinning or claiming a generic
failure. New i18n keys across all 13 catalogs. Reasonless available:false
keeps its existing hide-the-pill behavior.

Closes #5728
@CrysisDeu
CrysisDeu requested a review from a team August 25, 2026 02:25
@CrysisDeu
CrysisDeu requested a review from a team as a code owner August 25, 2026 02:25
@CrysisDeu
CrysisDeu requested a review from cixuuz August 25, 2026 02:25
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — ✅ PASS

UX-level review of 473c608944c298bbb807bfbdd0c5e3d8d2aeeead — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

UX-Verdict: PASS

A permanent state that previously spun forever now says exactly why it's terminal, in plain words at both the pill and the modal.

The new strings pass the cold read ("Credit usage isn't available for API key authentication" asserts the held state, no hedging), the pill reuses the established terminal-dash pattern from 'failed' with a reason-bearing label and aria-label, click-through escalates to the modal's fuller sentence, and all four screenshots match the PR's claims with no visual defects.

[UX-REVIEWED] 473c608

@github-actions

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 473c608944c298bbb807bfbdd0c5e3d8d2aeeead and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 473c608

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 473c608944c298bbb807bfbdd0c5e3d8d2aeeead: <one-sentence reason>

@github-actions

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 473c608944c298bbb807bfbdd0c5e3d8d2aeeead — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All the code checks out — the fix is at the true root cause (the usage API fundamentally requires an SSO/OIDC token that API-key auth never has), the reason field is an additive wire contract, a misdetected or renamed upstream enum degrades to the pre-PR slow path rather than a new failure, and the "terminal" state is recomputed on every 30s refetch so an auth-type switch self-heals. Description matches the diff bidirectionally.

Design-Verdict: PASS

Fail-fast at the auth-type root cause, additive marker contract, and every miss degrades to the pre-existing behavior — sound and proportionate.

[DESIGN-REVIEWED] 473c608

@github-actions

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — ✅ PASS

Premise-level review of 473c608944c298bbb807bfbdd0c5e3d8d2aeeead — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

I have everything I need. Let me run the final checks on my counts before emitting: reason field — 1 real consumer (website/src/App.tsx:1909); kiro_credit_usage_api_key — 1 consumer (App.tsx pill segment); credit_usage_api_key_auth — 1 consumer (KiroAccountModal.tsx:239); temp-screenshots/ — 2,288 tracked files, an established repo convention; the available: False unavailable-marker shape already existed at sessions.py:718 and the new marker rides it rather than duplicating it.

First-Principles-Verdict: PASS

A reported defect (#5728), fixed where the auth type is first knowable, riding an existing marker shape — every item declared and consumed.

What this change ships

Intent: stop API-key-authenticated accounts' credits panel from spinning forever, and tell them why usage is unavailable — a FIX.

  1. Usage refresh returns immediately on API-key auth instead of burning its timeout — justified (the reported defect's cause: no SSO/OIDC token can exist for this auth type).
  2. Unavailable marker gains a reason field — justified; rides the existing available: false shape (sessions.py:718), 1 counted consumer (App.tsx:1909).
  3. Credit pill shows a terminal dash with an explanatory label instead of hiding — declared, justified (the silent-hide was the reported symptom).
  4. Account modal explains the auth-type limitation instead of the generic unavailable line — declared, 1 consumer of the new key.
  5. Reasonless available: false keeps hiding the pill — unchanged behavior, pinned by a negative control.
  6. Respelling-tolerant account_type compare — declared; derived from upstream owning the enum spelling, failure degrades to the old slow path rather than widening.
  7. New i18n keys across 13 catalogs + en-XA — mandated by the documented i18n invariant.
  8. Screenshot harness gains an api-key scenario + 4 checked-in shots — declared; temp-screenshots/ is the repo's established deliverable convention (2,288 tracked files).

No undeclared items, no riders, no duplicated mechanism, no zero-consumer surface. The scope guard the issue demanded (leave fetch_usage_limits and _identity_matches_account untouched) is honored by the diff.

[FIRST-PRINCIPLES-REVIEWED] 473c608

@github-actions

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 473c608944c298bbb807bfbdd0c5e3d8d2aeeead — this comment is updated in place on each push.

Review details

The discovery pass found no candidates. The backend re import exists (line 10), account_type is populated by _fetch_whoami, and the fail-fast logic is sound. The change is well-scoped with no grounded defect on the changed lines.

No findings.

[OPUS-REVIEWED] 473c608

Verdict parsed from the review's SHA-scoped output markers for commit 473c608944c298bbb807bfbdd0c5e3d8d2aeeead.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 473c608944c298bbb807bfbdd0c5e3d8d2aeeead: <one-sentence reason>

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 25, 2026

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving per triage sweep: readiness passed, no blocking reviews, fix-type change. Auto-merge will be enabled; branch protection still gates.

@bolichen97
bolichen97 merged commit 14ce40e into main Aug 25, 2026
65 checks passed
@bolichen97
bolichen97 deleted the fix/credits-apikey-fail-fast-5728 branch August 25, 2026 08:53
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 25, 2026
@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion.

Relationship findings

  • PR #7628 is OVERLAPPING relative to this PR. The goals differ or the implementations can complement each other; this is not a duplicate claim. Recommended action for PR #7628: KEEP. Precedent, not coverage: 5750 established the reason field and the terminal-dash rendering for a different state, which is why 7628 is small and additive. Files: src/kiro_crew/dashboard/handlers/sessions.py, website/src/App.tsx.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

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.

Kiro Credits panel should fail fast on API Key auth instead of spinning forever

2 participants