Skip to content

[failproofai-434] dashboard aesthetic redesign — audit-forward, calmer others#434

Merged
NiveditJain merged 5 commits into
mainfrom
luv-434
Jun 11, 2026
Merged

[failproofai-434] dashboard aesthetic redesign — audit-forward, calmer others#434
NiveditJain merged 5 commits into
mainfrom
luv-434

Conversation

@NiveditJain

@NiveditJain NiveditJain commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

Brainstorming + design for redesigning the failproofai dashboard aesthetic, referencing demo videos under C:\Users\mathr\OneDrive\Desktop\demos. The audit pane will be pushed further into the brutalist pixel-craft direction to encourage quirky sharing; policies / projects / project-detail get calmer so they read as instruments rather than billboards.

The aesthetic-redesign spec + implementation will land as follow-up commits on this branch.

Landed so far

  • Audit reliability fix/audit's first run no longer fails on the first click, and the scan is no longer time-capped. POST /api/audit/run is now fire-and-forget (starts runAudit() detached, returns 202); the client polls /api/audit/status with no duration cap (only a ~10-poll lost-connection backstop stops it); the _state run-lock's 5-minute auto-expiry is removed; server-side run errors surface via the status endpoint; and the default scan window becomes the user's entire session history instead of the last 30 days. New tests cover the fire-and-forget route, the unbounded poll, and the run-state machine. (commits ecc243e, 2203835)
  • Chore — pin a few @types/* + typescript, ignore .tmp/, and reconcile bun.lock so CI's --frozen-lockfile install passes.

Test plan

  • CHANGELOG.md entry under the current version section
  • Unit suite green (bun run test:run — 1858 tests), bun run lint clean, tsc --noEmit clean
  • Visual diff against current /audit, /policies, /projects, /project/[name] (aesthetic redesign — pending)
  • Verify share flow on /audit (clipboard + native share + downloads)
  • Verify no global atmosphere (grid + grain + scanline) bleeds into the calm pages
  • Verify accessibility (focus ring, keyboard nav) still intact

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Reruns now start immediately (background execution); status polling reports server-side run errors.
  • User-facing Changes

    • Reruns and first-run scans default to covering all sessions ("since: all").
    • Progress, CTA, and helper text reflect longer, variable scan durations.
    • Polling for run completion no longer enforces a fixed overall timeout.
  • Chores

    • Updated dev dependency versions and added local scratch ignore rule.
  • Tests

    • Added/updated tests for rerun kickoff, polling, and run-state behavior.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@NiveditJain, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 20 minutes and 16 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 93ea51a8-e62a-4a5e-8d7c-784cbafbdab6

📥 Commits

Reviewing files that changed from the base of the PR and between 2203835 and 3db564b.

📒 Files selected for processing (3)
  • __tests__/api/audit-run-route.test.ts
  • app/api/audit/run/route.ts
  • src/audit/dashboard-cache.ts
📝 Walkthrough

Walkthrough

Implements fire-and-forget server audit runs with explicit finishRun(error) signaling, extends status responses with run errors, rewrites client rerun polling to poll indefinitely with network-failure tolerance, updates UI copy/params to use since: "all", adds/updates tests, and pins dev tooling versions plus .gitignore entry.

Changes

Development Configuration Updates

Layer / File(s) Summary
Local scratch file ignore rule
.gitignore
Added /tmp/ to .gitignore under a "local scratch (dev logs, probe scripts)" comment to exclude transient development artifacts from version control.
Development dependency version pinning
package.json
Pinned three devDependencies in package.json: @types/node -> 25.9.3, @types/react -> 19.2.17, and typescript -> 6.0.3.

Audit run/rerun flow and UI

Layer / File(s) Summary
Shared run state (server) · interface and transitions
app/api/audit/_state.ts
Added `error: string
Server run route: fire-and-forget kickoff
app/api/audit/run/route.ts
POST /api/audit/run now starts runAudit detached, immediately returns 202 { status: "started" }, and background task calls `finishRun(error
Status endpoint includes run error
app/api/audit/status/route.ts
GET /api/audit/status now returns error from run state alongside running, startedAt, and cachedAt.
Client triggerRun polling logic and tests
app/audit/_components/rerun-button.tsx, __tests__/audit/rerun-button.test.ts
Rewrote polling to run indefinitely, count consecutive status-fetch failures (fail after threshold), tolerate transient errors, and propagate server-side run errors as RerunError('post_failed'); tests cover kickoff, polling, error kinds, 409 handling, and long runs.
UI: rerun params and copy updates
app/audit/_components/*
Changed rerun params to since: "all" in dashboard/empty-state, updated run-progress command and timing copy, and adjusted post-failed error copy in progress-strip.
Server route tests: run kickoff and detached error handling
__tests__/api/audit-run-route.test.ts
New tests mock runAudit and cache writer to assert immediate 202, concurrent 409 behavior, detached rejection stores error and clears running, and input validation returns 400.
Run state tests
__tests__/api/audit-state.test.ts
Updated tests to verify finishRun records/clears errors, tryAcquireRun clears prior error, and locks do not auto-expire.
Changelog entry
CHANGELOG.md
Added 0.0.11-beta.8 entry documenting fire-and-forget execution, polling/status changes, scan-window default adjustments, UI copy updates, and referenced tests.

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

"I'm a rabbit in the codewood, I hop and I hum—
Fire-and-forget runs now echo a drum.
Polling that waits and errors that speak,
Pinned dev tools keep builds calm and sleek.
Hooray for tidy logs, and changes well spun!"

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes a dashboard aesthetic redesign but actual changes focus on audit reliability fixes, dependency updates, and chores rather than the visual redesign mentioned. Revise the title to accurately reflect the current changes, such as: '[failproofai-434] audit reliability fix — fire-and-forget execution and unbounded polling' or clarify that the aesthetic redesign is pending.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly outlines objectives, summarizes landed changes with specific commit references, includes a test plan with checkboxes, and addresses both the reliability fix and chore work.
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.

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


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.

NiveditJain and others added 2 commits June 11, 2026 01:04
…#434)

The audit first run failed on the first click (a retry worked). triggerRun
POSTed the audit run route through fetchWithTimeout's 15s default, but the route
ran runAudit synchronously, so a cold scan (~17s) was aborted client-side while
the server kept going and warmed the caches — making the second click succeed.

Per the user's direction (no timeout at all; scan all history):

- Server: the run route (app/api/audit/run/route.ts) is now fire-and-forget.
  It starts runAudit() as a detached task in the long-lived server process and
  returns 202 immediately; the route maxDuration cap is dropped. _state.ts gains
  an error field and finishRun(error); the 5-min lock auto-expiry is removed so
  a long-but-healthy run is never mistaken for a wedged one. The status route
  surfaces the error.
- Client: triggerRun polls the status route with no duration cap; only ~10
  consecutive lost-server polls abort it (network). A finished run carrying an
  error rejects post_failed; otherwise resolves (cache written before
  finishRun(null)).
- Default window drops from 30 days to all history; empty-state and run-progress
  copy updated to "may take a while".

Tests: rewrite the audit-state and rerun-button suites (finishRun/error,
no-auto-expiry, and triggerRun cases: 202+resolve, long-run-no-cap, error to
post_failed, consecutive-failures to network, non-OK POST, 409 to poll) and add
a route test asserting the POST returns 202 without awaiting the run, 409s
concurrents, and records a detached-run error. Full suite (1858) + lint + tsc
green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… install passes (#434)

c75edad pinned the @types and typescript dev dependencies but left bun.lock
inconsistent with package.json, so CI's frozen-lockfile install failed at the
Install dependencies step (test, test-e2e, hook-log-file jobs). Regenerate
bun.lock to match; a local frozen install now exits 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@socket-security

socket-security Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​types/​node@​25.9.2 ⏵ 25.9.3100 +1100100 +20100 +5100

View full report

@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

🧹 Nitpick comments (3)
__tests__/audit/rerun-button.test.ts (1)

68-86: 💤 Low value

Consider removing the implementation-detail check.

Lines 84-85 verify that no explicit timeout override is passed to the kickoff POST. This tests an implementation detail (absence of a parameter) rather than observable behavior. If the internal implementation changes (e.g., a future refactor adds a default timeout), this assertion would fail even though the behavior is correct.

Recommended alternative

Focus on behavior: either remove the check entirely (the test already validates the 202 kickoff succeeds), or verify that the kickoff completes quickly (e.g., assert it resolves within a small timeout like 100ms).

-    // The kickoff POST uses the default fast timeout (no explicit override) —
-    // it only times the 202 kickoff, not the run.
-    const runCall = fetchMock.mock.calls.find((c) => c[0] === "/api/audit/run");
-    expect(runCall?.[2]).toBeUndefined();
🤖 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 `@__tests__/audit/rerun-button.test.ts` around lines 68 - 86, Remove the
implementation-detail assertion that inspects the kickoff POST options: delete
the lines that find runCall from fetchMock.mock.calls and assert runCall?.[2] is
undefined; instead rely on the existing behavioral assertions (the test already
checks the 202 kickoff and that triggerRun resolves) or, if you prefer a timing
check, add a short behavioral assertion that the kickoff completes quickly
(e.g., assert triggerRun settles within a small timeout) referencing the test's
triggerRun call, fetchMock mock, and POLL_INTERVAL_MS.
app/audit/_components/rerun-button.tsx (2)

59-119: ⚡ Quick win

Consider adding cancellation support to prevent resource leaks.

The triggerRun async function has no mechanism to cancel the polling loop once started. If the calling component unmounts (e.g., user navigates away during a scan), the loop continues to fetch /api/audit/status every second indefinitely until completion. While the practical impact is limited because the fetch has built-in timeouts and the /audit page rarely unmounts during active scans, supporting cancellation would be cleaner.

♻️ Recommended approach using AbortSignal

Accept an optional AbortSignal and check it in the loop:

-export async function triggerRun(scanParams: ScanParams): Promise<void> {
+export async function triggerRun(scanParams: ScanParams, signal?: AbortSignal): Promise<void> {
+  if (signal?.aborted) throw new RerunError("network", "cancelled");
+
   // Start the run...
   try {
     const res = await fetchWithTimeout("/api/audit/run", {
       method: "POST",
       headers: { "content-type": "application/json" },
       body: JSON.stringify(paramsToBody(scanParams)),
+      signal,
     });
     // ...
   }
   
   let consecutiveFailures = 0;
   for (;;) {
+    if (signal?.aborted) throw new RerunError("network", "cancelled");
     await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
     // ...
   }
}

Callers can then wire it up with an AbortController in a useEffect cleanup.

🤖 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 `@app/audit/_components/rerun-button.tsx` around lines 59 - 119, The triggerRun
function lacks cancellation support causing the polling loop to continue after
caller unmounts; modify triggerRun to accept an optional AbortSignal parameter
(e.g., add signal?: AbortSignal to its signature) and thread it into
fetchWithTimeout calls and the polling loop: check signal.aborted before each
fetch and after the timeout sleep (POLL_INTERVAL_MS), abort ongoing fetches by
passing the signal to fetchWithTimeout, and throw a RerunError("aborted", ...)
or rethrow an AbortError when signal.aborted; ensure existing logic around
consecutiveFailures, MAX_CONSECUTIVE_POLL_FAILURES, and handling of
fetchWithTimeout remains unchanged.

89-94: ⚡ Quick win

Add runtime validation for the status response shape.

The type assertion as { running: boolean; error?: string | null } on line 93 is unsafe. If the server ever returns a malformed response (e.g., { running: "true" } as a string instead of boolean), the assertion will lie to TypeScript and cause subtle runtime bugs when checking !status.running later.

Since /api/audit/status is an internal endpoint, the risk is low, but adding a minimal runtime guard would make this more robust:

♻️ Simple validation guard
     let status: { running: boolean; error?: string | null } | null = null;
     try {
       const sres = await fetchWithTimeout("/api/audit/status", { cache: "no-store" });
       if (sres.ok) {
-        status = (await sres.json()) as { running: boolean; error?: string | null };
+        const parsed = await sres.json();
+        if (parsed && typeof parsed.running === "boolean") {
+          status = parsed as { running: boolean; error?: string | null };
+        }
       }
     } catch {
       // Transient fetch/JSON failure...
     }
🤖 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 `@app/audit/_components/rerun-button.tsx` around lines 89 - 94, The current
type assertion for status after fetchWithTimeout("/api/audit/status") is unsafe;
instead parse the JSON into a temp value, perform a runtime guard that checks
typeof temp.running === "boolean" and (temp.error === undefined || temp.error
=== null || typeof temp.error === "string"), and only then assign status = temp
as { running: boolean; error?: string | null }; if the guard fails set status =
null (or log/throw) so later checks like !status.running are safe; update the
code around the status assignment in rerun-button.tsx (the fetchWithTimeout
response handling) to implement this validation.
🤖 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 `@__tests__/api/audit-run-route.test.ts`:
- Around line 29-79: Add a new test that simulates a successful detached run:
mock runAuditMock to return a Promise that you resolve later, call
POST(req("{}")) and assert immediate 202 and running lock set via
getRunState().running, then resolve the background promise, await a tick or two
for its .then to settle, and assert writeDashboardCache (writeCacheMock) was
called and the lock was cleared by finishRun(null) (i.e., getRunState().running
is false and error is null); reference POST(req("{}")), runAuditMock,
writeCacheMock, finishRun, and getRunState to locate code.

In `@app/api/audit/run/route.ts`:
- Around line 91-93: writeDashboardCache currently swallows exceptions so
finishRun(null) is called even when the cache write failed; change the call site
in the route where runAudit, writeDashboardCache, finishRun are used to detect
actual cache write success by either (A) updating writeDashboardCache to return
a boolean/Promise<boolean> or rethrow errors, and then await its result here, or
(B) wrap writeDashboardCache(opts, result) in a try/catch that passes the caught
error into finishRun(error) on failure and finishRun(null) on success; ensure
finishRun receives a real error when the cache write did not persist.

---

Nitpick comments:
In `@__tests__/audit/rerun-button.test.ts`:
- Around line 68-86: Remove the implementation-detail assertion that inspects
the kickoff POST options: delete the lines that find runCall from
fetchMock.mock.calls and assert runCall?.[2] is undefined; instead rely on the
existing behavioral assertions (the test already checks the 202 kickoff and that
triggerRun resolves) or, if you prefer a timing check, add a short behavioral
assertion that the kickoff completes quickly (e.g., assert triggerRun settles
within a small timeout) referencing the test's triggerRun call, fetchMock mock,
and POLL_INTERVAL_MS.

In `@app/audit/_components/rerun-button.tsx`:
- Around line 59-119: The triggerRun function lacks cancellation support causing
the polling loop to continue after caller unmounts; modify triggerRun to accept
an optional AbortSignal parameter (e.g., add signal?: AbortSignal to its
signature) and thread it into fetchWithTimeout calls and the polling loop: check
signal.aborted before each fetch and after the timeout sleep (POLL_INTERVAL_MS),
abort ongoing fetches by passing the signal to fetchWithTimeout, and throw a
RerunError("aborted", ...) or rethrow an AbortError when signal.aborted; ensure
existing logic around consecutiveFailures, MAX_CONSECUTIVE_POLL_FAILURES, and
handling of fetchWithTimeout remains unchanged.
- Around line 89-94: The current type assertion for status after
fetchWithTimeout("/api/audit/status") is unsafe; instead parse the JSON into a
temp value, perform a runtime guard that checks typeof temp.running ===
"boolean" and (temp.error === undefined || temp.error === null || typeof
temp.error === "string"), and only then assign status = temp as { running:
boolean; error?: string | null }; if the guard fails set status = null (or
log/throw) so later checks like !status.running are safe; update the code around
the status assignment in rerun-button.tsx (the fetchWithTimeout response
handling) to implement this validation.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 086e1e24-4bc9-48f1-b9d9-e23c63b50174

📥 Commits

Reviewing files that changed from the base of the PR and between 7939938 and ecc243e.

📒 Files selected for processing (12)
  • CHANGELOG.md
  • __tests__/api/audit-run-route.test.ts
  • __tests__/api/audit-state.test.ts
  • __tests__/audit/rerun-button.test.ts
  • app/api/audit/_state.ts
  • app/api/audit/run/route.ts
  • app/api/audit/status/route.ts
  • app/audit/_components/audit-dashboard.tsx
  • app/audit/_components/audit-progress-strip.tsx
  • app/audit/_components/empty-state.tsx
  • app/audit/_components/rerun-button.tsx
  • app/audit/_components/run-progress.tsx
✅ Files skipped from review due to trivial changes (2)
  • app/audit/_components/audit-progress-strip.tsx
  • CHANGELOG.md

Comment thread __tests__/api/audit-run-route.test.ts
Comment thread app/api/audit/run/route.ts Outdated
Addresses CodeRabbit review on the fire-and-forget run. writeDashboardCache
swallowed its own IO errors and returned void, so the run route called
finishRun(null) even when the result was never persisted; since the cache is
the only channel by which a detached run's result reaches the client, the
poller would report success while the user got stale data.

writeDashboardCache now returns a boolean (true on persist, false on a
swallowed write error) and the route reports a run error via finishRun when
the write fails. Adds the success-path route test (cache written, lock
cleared, no error) and a write-failure test (error surfaced), per the review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@NiveditJain NiveditJain merged commit 9440f98 into main Jun 11, 2026
12 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.

2 participants