Skip to content

wave8: status/error UX (#94, #100, #46) - #123

Merged
AndresL230 merged 13 commits into
mainfrom
wave8/status-error-ux
May 24, 2026
Merged

wave8: status/error UX (#94, #100, #46)#123
AndresL230 merged 13 commits into
mainfrom
wave8/status-error-ux

Conversation

@AndresL230

@AndresL230 AndresL230 commented May 23, 2026

Copy link
Copy Markdown
Contributor

Closes #94
Closes #100
Closes #46

Summary

  • Extension validateRcApiKey() treats 404 as "valid in dev mode" #94validateApiKey() no longer treats a 404 from /auth/me as "valid in dev mode" (was a fail-open at the auth boundary). Return type narrowed from AuthMeUser | null to AuthMeUser.
  • Rate-limit 429 from scan submission is not surfaced in the extension UI #100 — Scan-submit 429 now surfaces a scanNotification with Retry-After wait text and still publishes local results. apiFetch parses the header into err.retryAfterSeconds (silently drops HTTP-date form per RFC 7231).
  • Indicator for valid key still finicky #46 — Status-bar indicator stops flickering:
    • Bug A — Scan-side 401/403 now refreshes the status bar via a new refreshStatusBar callback wired through ReCostSidebarProvider instead of waiting for the next window focus.
    • Bug B — Generation counter in updateStatusBar drops stale concurrent writes after every await boundary; lastValidationAt only updates on non-stale completions.
    • Bug CwindowFocused refresh is debounced to 60 seconds; all other trigger reasons bypass the debounce.
    • Bug D — Removed the onDidChangeWorkspaceFolders trigger (workspace folder identity is unrelated to auth state).

New exported type: ApiClientError in src/api-client.ts carries status + optional retryAfterSeconds.

Test plan

  • npm run test:scanner — existing suite + new scan-publishing-handler test (4 cases) + updated api-client tests (10 cases total) all pass
  • npm run build — dashboard + webview + extension build clean
  • D1 benchmark Δ +0.00pp sanity (no scanner change expected)
  • EDH gate 1 — Revoke key in dashboard → run scan → status bar flips to "Invalid Key" (not just sidebar). (Bug A)
  • EDH gate 2 — Alt-tab away and back 5x rapidly while connected → status stays "Connected", no flicker. (Bugs B + C)
  • EDH gate 3 — Configure a fresh key whose /auth/me returns 404 → status bar shows "Invalid Key", not "Connected". (Extension validateRcApiKey() treats 404 as "valid in dev mode" #94)
  • EDH gate 4 — Submit 11 scans within 60s → 11th run shows the rate-limit scanNotification with a wait time. (Rate-limit 429 from scan submission is not surfaced in the extension UI #100)

Plan: docs/superpowers/plans/2026-05-23-wave8-status-error-ux.md. Design spec: docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md.

🤖 Generated with Claude Code

AndresL230 and others added 13 commits May 22, 2026 18:48
The tsbuildinfo incremental-build cache was tracked and went dirty after
every webview build. Untrack it and add `*.tsbuildinfo` to .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundled design for #94 (404-as-valid removal), #100 (429 surfacing
with Retry-After), and #46 (status-bar indicator audit: race guard,
focus debounce, scan-side refresh hook, drop noise listener).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…Bug B)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements Wave 8 — Status / Error UX, a coordinated fix addressing three functional areas: removing the 404-as-valid behavior from API key validation, handling HTTP 429 rate limits with Retry-After header parsing and user notification, and fixing status-bar refresh races through generation-based protection and focused debouncing.

Changes

Wave 8 — Status / Error UX

Layer / File(s) Summary
API client error handling and validateApiKey semantics
src/api-client.ts
ApiClientError type with HTTP status and optional retryAfterSeconds is added; apiFetch now parses Retry-After header on 429 responses; validateApiKey removes 404 null-return fallback and now consistently throws on all non-200 responses.
API client test updates for 404 and 429 handling
src/test/api-client.test.ts
validateApiKey 404 test updated to expect throw instead of null; three new 429 test cases verify retryAfterSeconds is correctly parsed from numeric headers and remains undefined for missing or HTTP-date formats.
Extension status bar race prevention and refresh debouncing
src/extension.ts
Module-level generation counter and timestamp tracking added; updateStatusBar refactored with generation guards to prevent stale completions; window-focus refreshes debounced to 60-second minimum; workspace-folders listener removed.
Scan publishing handler 429 rate-limit notification and auth refresh
src/webview/scan-publishing-handler.ts
refreshStatusBar() method added to handler context; HTTP 429 errors now trigger user notification with optional retry-wait message before publishing local results; auth-like failures (401) trigger status bar refresh before opening keys UI.
Scan publishing handler test coverage for 429 and 401
src/test/scan-publishing-handler.test.ts
New Node-based test file with module stubbing; validates 429 handling with/without retryAfterSeconds (including singular/plural phrasing) and 401 ordering semantics for refreshStatusBar() invocation.
Webview provider integration with refreshStatusBar callback
src/webview-provider.ts
ReCostSidebarProvider constructor now accepts and stores refreshStatusBar callback, forwarding it to ScanPublishingHandler to enable scan publishing failures to trigger extension status bar updates.
Wave 8 design spec, implementation plan, and config updates
docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md, docs/superpowers/plans/2026-05-23-wave8-status-error-ux.md, .gitignore, package.json, CLAUDE.md, webview/tsconfig.tsbuildinfo
Design specification and detailed implementation plan document all tasks with code/test strategies; CLAUDE.md updated with corrected API validation semantics; *.tsbuildinfo added to .gitignore; test:scanner includes new scan-publishing-handler test; stale build metadata removed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • recost-dev/extension#87: Main PR extends the ScanPublishingHandler/webview-provider work introduced in PR #87 by adding new refreshStatusBar plumbing plus 429 (Retry-After) handling and auth-like failure UI refresh changes in the scan submission error paths.

Poem

🐰 A rabbit hops through error states so grand,
429s now speak—"wait a moment, friend!"
With generations guarding from the race,
Status bars stay calm, keep steady pace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'wave8: status/error UX (#94, #100, #46)' directly summarizes the main changes: implementing Wave 8 status/error UX fixes across three linked issues.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wave8/status-error-ux

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
package.json (1)

192-192: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

npm run build does not include dashboard build.

The current build script skips build:dashboard, which violates the required build contract and can leave dashboard-dist/ missing.

Suggested fix
-    "build": "npm run build:webview && npm run build:ext",
+    "build": "npm run build:dashboard && npm run build:webview && npm run build:ext",

As per coding guidelines: "npm run build must build all three (dashboard + webview + extension)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 192, The top-level npm "build" script currently runs
only "build:webview" and "build:ext"; update the "build" npm script to include
"build:dashboard" (e.g., run "build:dashboard" alongside "build:webview" and
"build:ext") so that the dashboard is built and dashboard-dist/ is produced;
modify the "build" script entry that references "build:webview" and "build:ext"
to also invoke "build:dashboard" in the sequence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md`:
- Line 244: Update the contradictory "Files touched" conclusion in
docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md (the line that
currently reads "No new files") so it matches the table above; change that
sentence to acknowledge the new documentation and test files included in this
wave (e.g., "Includes new docs and a new test file as listed above") and ensure
the phrase "Files touched" or the concluding sentence references the same items
shown in the table.

In `@src/webview/scan-publishing-handler.ts`:
- Around line 793-794: The 429 error path is calling
publishLocalOnlyResults(...) again, producing duplicate emissions because scan
results were already published before remote submission; modify the handler to
avoid re-publishing by tracking whether local publication has already occurred
(e.g., a localPublished boolean scoped to the publish flow or a flag on
this.ctx) and only call publishLocalOnlyResults(manualProjectId ??
this.ctx.getProjectId() ?? "local", `local-${Date.now()}`) when that flag is
false, or remove the redundant call in the 429 branch so publishLocalOnlyResults
is invoked exactly once per scan; update the publish flow around
publishLocalOnlyResults and the 429 handling to set the flag when first
published.

---

Outside diff comments:
In `@package.json`:
- Line 192: The top-level npm "build" script currently runs only "build:webview"
and "build:ext"; update the "build" npm script to include "build:dashboard"
(e.g., run "build:dashboard" alongside "build:webview" and "build:ext") so that
the dashboard is built and dashboard-dist/ is produced; modify the "build"
script entry that references "build:webview" and "build:ext" to also invoke
"build:dashboard" in the sequence.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f9526149-3cb4-4b6e-87ae-25901fbd37e0

📥 Commits

Reviewing files that changed from the base of the PR and between aa2314c and f9e5a2f.

📒 Files selected for processing (12)
  • .gitignore
  • CLAUDE.md
  • docs/superpowers/plans/2026-05-23-wave8-status-error-ux.md
  • docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md
  • package.json
  • src/api-client.ts
  • src/extension.ts
  • src/test/api-client.test.ts
  • src/test/scan-publishing-handler.test.ts
  • src/webview-provider.ts
  • src/webview/scan-publishing-handler.ts
  • webview/tsconfig.tsbuildinfo
💤 Files with no reviewable changes (1)
  • webview/tsconfig.tsbuildinfo

| `src/test/scan-publishing-handler.test.ts` (new) | 429 scanNotification tests |
| `CLAUDE.md` | Rename `validateRcApiKey` → `validateApiKey` in the auth section |

No new files. No removed files.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix internal contradiction in “Files touched” conclusion.

Line 244 says “No new files,” but this wave explicitly includes new docs (and a new test file in the implementation plan). Please reword to avoid mismatch with the table above.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md` at line
244, Update the contradictory "Files touched" conclusion in
docs/superpowers/specs/2026-05-22-wave8-status-error-ux-design.md (the line that
currently reads "No new files") so it matches the table above; change that
sentence to acknowledge the new documentation and test files included in this
wave (e.g., "Includes new docs and a new test file as listed above") and ensure
the phrase "Files touched" or the concluding sentence references the same items
shown in the table.

Comment on lines +793 to +794
publishLocalOnlyResults(manualProjectId ?? this.ctx.getProjectId() ?? "local", `local-${Date.now()}`);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid re-publishing identical local results in the 429 path.

Line 703 already publishes local-only results before remote submission. Calling publishLocalOnlyResults(...) again here duplicates scanResults emissions and debug-export writes for the same scan.

Suggested fix
         if (status === 429) {
           const retryAfter = apiErr.retryAfterSeconds;
           const waitText = retryAfter !== undefined
             ? `Try again in ${retryAfter} second${retryAfter === 1 ? "" : "s"}.`
             : "Try again in a moment.";
           this.ctx.postMessage({
             type: "scanNotification",
             message: `ReCost scan rate limit reached. ${waitText} Showing local results.`,
           });
-          publishLocalOnlyResults(manualProjectId ?? this.ctx.getProjectId() ?? "local", `local-${Date.now()}`);
           return;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
publishLocalOnlyResults(manualProjectId ?? this.ctx.getProjectId() ?? "local", `local-${Date.now()}`);
return;
if (status === 429) {
const retryAfter = apiErr.retryAfterSeconds;
const waitText = retryAfter !== undefined
? `Try again in ${retryAfter} second${retryAfter === 1 ? "" : "s"}.`
: "Try again in a moment.";
this.ctx.postMessage({
type: "scanNotification",
message: `ReCost scan rate limit reached. ${waitText} Showing local results.`,
});
return;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/webview/scan-publishing-handler.ts` around lines 793 - 794, The 429 error
path is calling publishLocalOnlyResults(...) again, producing duplicate
emissions because scan results were already published before remote submission;
modify the handler to avoid re-publishing by tracking whether local publication
has already occurred (e.g., a localPublished boolean scoped to the publish flow
or a flag on this.ctx) and only call publishLocalOnlyResults(manualProjectId ??
this.ctx.getProjectId() ?? "local", `local-${Date.now()}`) when that flag is
false, or remove the redundant call in the 429 branch so publishLocalOnlyResults
is invoked exactly once per scan; update the publish flow around
publishLocalOnlyResults and the 429 handling to set the flag when first
published.

@AndresL230 AndresL230 mentioned this pull request May 24, 2026
5 tasks
@AndresL230
AndresL230 merged commit 63f408d into main May 24, 2026
3 checks passed
AndresL230 added a commit that referenced this pull request May 27, 2026
Wave 3 (#114/#115/#116, PR #126) merged; also corrected stale statuses for
waves 7 (#122), 8 (#123), 10 (#124) that merged earlier but were never marked
complete. All platform waves (6-10) + accuracy Wave 3 now shipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant