fix(dashboard): explain the credits pill instead of hiding it when the usage scrape is opted out - #7628
Conversation
1d05f96 to
ab538a7
Compare
ab538a7 to
d62bb63
Compare
0a6bcac to
2a4b7bf
Compare
2a4b7bf to
f8d2658
Compare
First Principles Review (Fable 5, fork) — ✅ PASSPremise-level review of I've verified the change against the trusted base: the First-Principles-Verdict: PASS A reported defect (#7623) fixed at its cause — the reused bare sentinel — by extending the existing What this change shipsIntent: let a user whose credits pill silently vanished see why and learn the setting that restores it. This is a FIX.
Counts run: [FIRST-PRINCIPLES-REVIEWED] 25e0b2a |
Design Review (Fable 5, fork) — ✅ PASSDesign-level review of The verification confirms everything: the Design-Verdict: PASS Extends the existing [DESIGN-REVIEWED] 25e0b2a |
GPT 5.6 Review (fork) — ✅ no blocking findingsReviewed Review detailsNo findings. |
UX Review (Fable 5, fork) — ✅ PASSUX-level review of The knob is re-read via UX-Verdict: PASS A silently-vanishing pill becomes an explained, actionable state that mirrors the existing api-key pattern; the fix instruction works live, no restart needed. Suggestions
[UX-REVIEWED] 25e0b2a |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed Review detailsI've independently verified the change:
No candidate to falsify (discovery produced none), and no new grounded defect at 80+. No findings. [OPUS-REVIEWED] 25e0b2a |
f8d2658 to
40b4abd
Compare
…e usage scrape is opted out The free GetUsageLimits read returning no plan plus the (default-off) billed /usage scrape degraded to available:false, which the frontend answers by hiding the top-bar credits segment entirely — users coming from v0.1.3 saw the pill silently vanish with no hint a knob exists. Carry reason=scrape_disabled on the unavailable marker and render the same terminal dash the api-key state uses, with a label and modal message that name dashboard.usage_text_scrape_enabled. Fixes kirodotdev#7623
40b4abd to
25e0b2a
Compare
Open PR relationship auditThis 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
No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit. |
bolichen97
left a comment
There was a problem hiding this comment.
Approved as Tech Lead review (also satisfies the kirocrew-ux-reviewers requirement of ruleset ux-review-required for the website/src/** files).
Correctness. The fix is at the cause: _cache_without_scrape gained an optional reason and only the scrape-disabled call site passes reason="scrape_disabled". This extends the pre-existing api_key_auth reason mechanism rather than inventing a parallel one, and the definitive kiro-cli-absent marker stays exactly {"available": False} so non-Kiro providers keep hiding the pill — pinned by test_no_kiro_bin_marker_stays_reason_free. The frontend explains rather than misleads: the tooltip and modal both name the exact config key dashboard.usage_text_scrape_enabled, and the modal additionally discloses that enabling it spends credits per refresh, so the user is not steered into a billed path unknowingly.
i18n. No hardcoded user-facing strings — both new strings go through i18nT, the keys land in en.manual.json (correct, since en.json is generated) plus all 11 target catalogues, and en-XA was regenerated via the pseudo-locale script. Union parity holds.
Scope. 18 files but only 3 are non-test/non-locale, and the deliberately-excluded sibling (the transient scrape-backoff marker) is called out with a rationale rather than silently skipped.
Screenshot Evidence gate. I applied the documented no-screenshots maintainer waiver after verifying the claim rather than merging over a red gate. The new 'scrape-disabled' branch in App.tsx is byte-identical to the existing 'api-key' branch (same ${seg} text-muted opacity-60, same Coins size={12}, same — glyph); only the i18n key differs. That is precisely the "non-visual change to a visual file" the waiver exists for.
Non-blocking follow-up. The UX lane's nit is fair and worth a future one-line fix: app.kiro_credit_usage_scrape_disabled reads "live fetch is opted out" in English, which implies an act the user never performed (the scrape is off by default) — and every translated catalogue already renders it as "disabled"/"deactivated", so English is the outlier. Not blocking: the string names the exact knob, so the repair path is unambiguous, and the modal copy states "off by default" accurately.
Problem / Motivation
The credits usage indicator vanished from the dashboard's top bar between v0.1.3 and v0.4.1 (#7623). The reporter's account has a working plan (
kiro-cli /usageanswers fine by hand), yet the top bar shows only CPU/MEM/DSK — no credits slot at all, on any dashboard surface, with no settings toggle to be found.Why it matters
The pill didn't break — it was silently degraded. #2039 rightly made the billed
/usagetext scrape opt-in (dashboard.usage_text_scrape_enabled, default off), and degraded toavailable: falsewhen it's off and the freeGetUsageLimitsread returns no plan. The frontend answers that signal by hiding the pill entirely. For every account where the API path yields no plan, users went from "pill rendered (via scrape)" to "pill silently absent", with nothing anywhere naming the knob that restores it. A permanent, user-addressable state was rendered indistinguishable from "this provider has no credits".What changed (motivation → approach → change)
Observed symptom: pill absent → root cause: the scrape-disabled degrade reuses the same bare
available: falsemarker as the definitive "kiro-cli absent" verdict, so the frontend cannot tell "nothing to show, ever" from "off until you flip a documented setting" → change: distinguish the states and explain instead of hiding, following the existingapi_key_authprecedent (a terminal state that renders a dimmed dash + explanation rather than hiding):sessions.py:_cache_without_scrapeaccepts an optionalreason; the scrape-disabled call site passesreason="scrape_disabled". The kiro-cli-absent and scrape-backoff markers are unchanged (the former should keep hiding the pill; the latter is a transient parked state, out of scope here).App.tsx: classifiesreason === 'scrape_disabled'into a new'scrape-disabled'state and renders the same terminal dash as'api-key', with a tooltip namingdashboard.usage_text_scrape_enabled.KiroAccountModal.tsx: the'scrape-disabled'state gets a fuller explanation (free API returned no plan, billed fallback off by default, how to enable it, and that enabling spends a small number of credits per refresh).en.manual.jsonplus translations in all 11 target catalogs;en-XAregenerated vianpm run i18n:pseudo.Tests
test_disabled_marker_names_the_reason— the scrape-disabled unavailable marker carriesreason: "scrape_disabled".test_no_kiro_bin_marker_stays_reason_free— the definitive kiro-cli-absent marker stays exactly{"available": False}, so the hide-the-pill behavior for non-Kiro providers is pinned.KiroAccountModal.test.tsx: the'scrape-disabled'state renders the knob-naming explanation, not the generic unavailable line, and does not spin.test/test_session_usage.pyand all 12 inKiroAccountModal.test.tsxpass;tsc -bclean.Manual verification
N/A beyond unit coverage for the state plumbing — the new state's render path is byte-for-byte the existing
'api-key'branch (same classes, same dash glyph); only thetitle/aria-labelstring and the modal message differ, both asserted in tests.Screenshots / video
The rendered pixels are identical to the existing API-key unavailable state (dimmed Coins glyph +
—dash, same classes) — no new visual variant is introduced; the change is which STATE reaches that rendering plus the explanatory strings. Happy to add a seeded-harness capture if reviewers want one.Related Issues
Fixes #7623
Pattern harvest
Rule candidate: review-prompt
Pattern: "a degrade path reuses another state's sentinel payload, making a user-addressable condition indistinguishable from a terminal one — when adding an opt-out that degrades UI, carry a machine-readable reason and surface the knob"
Checklist
feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)