feat(prd-142): Wave 0 "Is it working?" vitals on Command Centre (US-007)#402
Conversation
Add the five-tile "Is it working?" strip to the existing System Dashboard — no new route, no new page. Four tiles read the real Wave 0 endpoints (activation, mission success rate, error rate by subsystem, widget engagement); the fifth (per-primitive health, US-006) is deferred to Wave 3 and renders an explicit "not yet measured" placeholder rather than a fake green. - api-client: 4 typed getters + response interfaces over request() - use-analytics-api: 4 react-query hooks - is-it-working.tsx: new child section reusing the glass-card/motion tile pattern; loading + empty + error states, no hardcoded values - dashboard.tsx: render <IsItWorking /> after <MetricCards />
📝 WalkthroughWalkthroughThis PR implements PRD-142 Wave 0 "Is it working?" dashboard feature by introducing metric API contracts, analytics hooks, and integrating new IsItWorking display components across command-center, activity, and main dashboard pages. It also consolidates the app's navigation architecture to use chat as the primary home route. ChangesIs it Working Dashboard — PRD-142 Wave 0
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
frontend/lib/api-client.ts (1)
2241-2247: 💤 Low valueRedundant default parameters across client and hook layers.
Both
getErrorsBySubsystemandgetWidgetEngagementspecify defaultwindowvalues ('24h'and'7d'), but the hook layer also provides identical defaults. While this defensive approach is harmless, it creates redundancy. Consider removing defaults from one layer for clarity, or document that the client-layer defaults serve as a safety fallback if the methods are called directly without hooks.🤖 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 `@frontend/lib/api-client.ts` around lines 2241 - 2247, The client methods getErrorsBySubsystem and getWidgetEngagement in frontend/lib/api-client.ts redundantly declare default window values that are already provided by the hook layer; remove the default parameter values from these two signatures (leave them as window: string) so the hooks supply the canonical defaults, and ensure any direct callers pass an explicit window or rely on hook-provided defaults; update JSDoc/comments to note the client methods are a safety-pass-through without their own defaults.frontend/components/dashboard/is-it-working.tsx (1)
91-97: 💤 Low valueConsider removing non-null assertions by restructuring the conditional.
Lines 94 and 96 use non-null assertions (
m!) after checkinghasMissions. While technically safe, TypeScript should infer non-nullability from the check. Consider restructuring to avoid!:♻️ Proposed refactor
- const m = mission.data - const hasMissions = !!m && m.total_executions > 0 - const missionValue = hasMissions ? `${Math.round(m!.value)}%` : '0%' - const missionSub = hasMissions - ? `${m!.successful_executions}/${m!.total_executions} missions` - : 'No missions yet' + const hasMissions = !!mission.data && mission.data.total_executions > 0 + const missionValue = hasMissions ? `${Math.round(mission.data.value)}%` : '0%' + const missionSub = hasMissions + ? `${mission.data.successful_executions}/${mission.data.total_executions} missions` + : 'No missions yet'🤖 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 `@frontend/components/dashboard/is-it-working.tsx` around lines 91 - 97, The code uses non-null assertions (m!) in missionValue and missionSub after computing hasMissions; instead restructure to narrow m's type (e.g., compute with an if (m && m.total_executions > 0) block or use const m = mission.data; if (m && m.total_executions > 0) { const missionValue = `${Math.round(m.value)}%`; const missionSub = `${m.successful_executions}/${m.total_executions} missions`; } else { const missionValue = '0%'; const missionSub = 'No missions yet'; }) so TypeScript can infer non-nullability and you can remove the m! assertions while keeping the same logic in missionValue and missionSub (referencing mission, m, hasMissions, missionValue, missionSub).
🤖 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.
Nitpick comments:
In `@frontend/components/dashboard/is-it-working.tsx`:
- Around line 91-97: The code uses non-null assertions (m!) in missionValue and
missionSub after computing hasMissions; instead restructure to narrow m's type
(e.g., compute with an if (m && m.total_executions > 0) block or use const m =
mission.data; if (m && m.total_executions > 0) { const missionValue =
`${Math.round(m.value)}%`; const missionSub =
`${m.successful_executions}/${m.total_executions} missions`; } else { const
missionValue = '0%'; const missionSub = 'No missions yet'; }) so TypeScript can
infer non-nullability and you can remove the m! assertions while keeping the
same logic in missionValue and missionSub (referencing mission, m, hasMissions,
missionValue, missionSub).
In `@frontend/lib/api-client.ts`:
- Around line 2241-2247: The client methods getErrorsBySubsystem and
getWidgetEngagement in frontend/lib/api-client.ts redundantly declare default
window values that are already provided by the hook layer; remove the default
parameter values from these two signatures (leave them as window: string) so the
hooks supply the canonical defaults, and ensure any direct callers pass an
explicit window or rely on hook-provided defaults; update JSDoc/comments to note
the client methods are a safety-pass-through without their own defaults.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b55042e5-4463-4eaa-81a2-7b530938dbb8
📒 Files selected for processing (4)
frontend/components/dashboard/dashboard.tsxfrontend/components/dashboard/is-it-working.tsxfrontend/hooks/use-analytics-api.tsfrontend/lib/api-client.ts
US-007's vitals strip was wired into the orphaned /dashboard route. Move it to the live Command Centre surface (both variants) and delete the dead route. - Studio desktop: new IsItWorkingStrip (cc-stats idiom) rendered under StatsStrip in command-center-shell - Classic/mobile: second StatsBar vitals row in ActivityPage - Delete dead /dashboard route + dashboard.tsx, the glass-card is-it-working.tsx, and 4 orphaned children (metric-cards, system-health, quick-actions, performance-chart) - Fix dangling /dashboard refs: post-sign-in/up redirect -> /chat, drop the nav-highlight lines in main-layout and studio-menu Per-primitive health (US-006) stays an honest placeholder, deferred to Wave 3.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@frontend/components/activity/activity-page.tsx`:
- Around line 143-198: The vitals cards currently treat failed or loading
queries the same as valid empty data because the hooks (useActivationMetrics,
useMissionSuccessRate, useErrorsBySubsystem, useWidgetEngagement) are only
reading .data; update each hook usage to also read their isLoading/isError flags
and, when isLoading show neutral "Loading metrics…" and when isError show
"Metrics unavailable" in the vitals entries instead of the zero-data strings
(e.g. replace "No workspaces yet", "None · 24h", "No widget activity" with the
new messages); ensure the logic that computes totalWs/activationPct,
missionTotal/missionPct, errorTotal/worstSubsystem, and
widgetSessions/widgetEvents uses the presence of isLoading/isError to choose
those fallback strings and not compute numeric fallbacks when queries are not
ready (you can centralize into a small helper used when building the vitals
array).
In `@frontend/components/command-center/is-it-working-strip.tsx`:
- Around line 30-80: The tiles collapse failed or loading analytics into healthy
zero-state because the component only reads data from useActivationMetrics,
useMissionSuccessRate, useErrorsBySubsystem, and useWidgetEngagement; update the
component to check each hook's readiness (isLoading/isError/isFetching/ready
flags) before deriving totals/percentages and building the cells array, and when
a query is not ready render neutral placeholders like '—' for value and 'metrics
unavailable' or 'loading…' for delta (instead of 0/'none · 24h'/'no
workspaces'), specifically change the logic that computes totalWs/activationPct,
missionTotal/missionPct, errorTotal/worst, sessions/widgetEvents and the
corresponding delta/tone assignments in the cells definition so failed requests
show a neutral tone and copy rather than green/ok or 0.
🪄 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
Run ID: 644a9150-f955-4027-b0a0-a77cc98ed2db
📒 Files selected for processing (13)
frontend/app/dashboard/page.tsxfrontend/app/page.tsxfrontend/components/activity/activity-page.tsxfrontend/components/auth/user-profile-button.tsxfrontend/components/command-center/command-center-shell.tsxfrontend/components/command-center/is-it-working-strip.tsxfrontend/components/dashboard/dashboard.tsxfrontend/components/dashboard/metric-cards.tsxfrontend/components/dashboard/performance-chart.tsxfrontend/components/dashboard/quick-actions.tsxfrontend/components/dashboard/system-health.tsxfrontend/components/layout/main-layout.tsxfrontend/lib/studio-menu.ts
💤 Files with no reviewable changes (9)
- frontend/components/dashboard/system-health.tsx
- frontend/components/dashboard/performance-chart.tsx
- frontend/components/dashboard/quick-actions.tsx
- frontend/components/layout/main-layout.tsx
- frontend/components/dashboard/metric-cards.tsx
- frontend/app/page.tsx
- frontend/components/dashboard/dashboard.tsx
- frontend/lib/studio-menu.ts
- frontend/app/dashboard/page.tsx
| const { data: activation } = useActivationMetrics() | ||
| const { data: mission } = useMissionSuccessRate() | ||
| const { data: errors } = useErrorsBySubsystem('24h') | ||
| const { data: widget } = useWidgetEngagement('7d') | ||
|
|
||
| const totalWs = activation?.total_workspaces ?? 0 | ||
| const activationPct = totalWs > 0 ? Math.round((activation?.rate ?? 0) * 100) : 0 | ||
|
|
||
| const missionTotal = mission?.total_executions ?? 0 | ||
| const missionPct = missionTotal > 0 ? Math.round(mission?.value ?? 0) : 0 | ||
|
|
||
| const errorTotal = errors?.total ?? 0 | ||
| const worstSubsystem = errors?.by_subsystem?.length | ||
| ? [...errors.by_subsystem].sort((a, b) => b.count - a.count)[0] | ||
| : null | ||
|
|
||
| const widgetSessions = widget?.sessions ?? 0 | ||
| const widgetEvents = widget?.by_event_type?.reduce((sum, ev) => sum + ev.count, 0) ?? 0 | ||
|
|
||
| const vitals: StatItem[] = [ | ||
| { | ||
| label: 'Activation', | ||
| value: totalWs > 0 ? `${activationPct}%` : '—', | ||
| change: totalWs > 0 ? `${activation?.activated ?? 0}/${totalWs} workspaces` : 'No workspaces yet', | ||
| icon: Zap, | ||
| iconColor: 'text-primary', | ||
| }, | ||
| { | ||
| label: 'Mission success rate', | ||
| value: missionTotal > 0 ? `${missionPct}%` : '—', | ||
| change: missionTotal > 0 ? `${mission?.successful_executions ?? 0}/${missionTotal} missions` : 'No missions yet', | ||
| icon: CheckCircle2, | ||
| iconColor: 'text-[hsl(var(--success))]', | ||
| }, | ||
| { | ||
| label: 'Error rate by subsystem', | ||
| value: errorTotal, | ||
| change: errorTotal > 0 && worstSubsystem ? `${worstSubsystem.subsystem} (${worstSubsystem.count}) · 24h` : 'None · 24h', | ||
| icon: AlertTriangle, | ||
| iconColor: errorTotal > 0 ? 'text-destructive' : 'text-[hsl(var(--success))]', | ||
| }, | ||
| { | ||
| label: 'Widget engagement', | ||
| value: widgetSessions, | ||
| change: widgetSessions > 0 ? `${widgetEvents} events · 7d` : 'No widget activity', | ||
| icon: MessageSquare, | ||
| iconColor: 'text-[hsl(var(--info))]', | ||
| }, | ||
| { | ||
| label: 'Per-primitive health', | ||
| value: '—', | ||
| change: 'Not yet measured', | ||
| icon: Boxes, | ||
| iconColor: 'text-muted-foreground', | ||
| }, | ||
| ] |
There was a problem hiding this comment.
Don't present analytics outages as empty-state copy.
These hooks ignore query status, so a backend failure produces the same strings as real empty data (No workspaces yet, None · 24h, No widget activity). That makes the vitals bar look healthy/empty when the source is actually unavailable. Gate the vitals copy on isLoading/isError before falling back to zero-data messaging.
🐛 Sketch of the fix
- const { data: activation } = useActivationMetrics()
- const { data: mission } = useMissionSuccessRate()
- const { data: errors } = useErrorsBySubsystem('24h')
- const { data: widget } = useWidgetEngagement('7d')
+ const activationQuery = useActivationMetrics()
+ const missionQuery = useMissionSuccessRate()
+ const errorsQuery = useErrorsBySubsystem('24h')
+ const widgetQuery = useWidgetEngagement('7d')
+
+ const { data: activation } = activationQuery
+ const { data: mission } = missionQuery
+ const { data: errors } = errorsQuery
+ const { data: widget } = widgetQuery
+ const metricsUnavailable = [activationQuery, missionQuery, errorsQuery, widgetQuery].some(
+ (query) => query.isError,
+ )Then render neutral "Loading metrics…" / "Metrics unavailable" copy for the vitals cards instead of the current zero-data strings whenever the queries are not ready.
🤖 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 `@frontend/components/activity/activity-page.tsx` around lines 143 - 198, The
vitals cards currently treat failed or loading queries the same as valid empty
data because the hooks (useActivationMetrics, useMissionSuccessRate,
useErrorsBySubsystem, useWidgetEngagement) are only reading .data; update each
hook usage to also read their isLoading/isError flags and, when isLoading show
neutral "Loading metrics…" and when isError show "Metrics unavailable" in the
vitals entries instead of the zero-data strings (e.g. replace "No workspaces
yet", "None · 24h", "No widget activity" with the new messages); ensure the
logic that computes totalWs/activationPct, missionTotal/missionPct,
errorTotal/worstSubsystem, and widgetSessions/widgetEvents uses the presence of
isLoading/isError to choose those fallback strings and not compute numeric
fallbacks when queries are not ready (you can centralize into a small helper
used when building the vitals array).
| const { data: activation } = useActivationMetrics() | ||
| const { data: mission } = useMissionSuccessRate() | ||
| const { data: errors } = useErrorsBySubsystem('24h') | ||
| const { data: widget } = useWidgetEngagement('7d') | ||
|
|
||
| const totalWs = activation?.total_workspaces ?? 0 | ||
| const activationPct = totalWs > 0 ? Math.round((activation?.rate ?? 0) * 100) : 0 | ||
|
|
||
| const missionTotal = mission?.total_executions ?? 0 | ||
| const missionPct = missionTotal > 0 ? Math.round(mission?.value ?? 0) : 0 | ||
|
|
||
| const errorTotal = errors?.total ?? 0 | ||
| const worst = errors?.by_subsystem?.length | ||
| ? [...errors.by_subsystem].sort((a, b) => b.count - a.count)[0] | ||
| : null | ||
|
|
||
| const sessions = widget?.sessions ?? 0 | ||
| const widgetEvents = widget?.by_event_type?.reduce((sum, ev) => sum + ev.count, 0) ?? 0 | ||
|
|
||
| const cells: Cell[] = [ | ||
| { | ||
| label: 'ACTIVATION', | ||
| value: totalWs > 0 ? `${activationPct}%` : '—', | ||
| tone: totalWs > 0 ? 'ok' : 'muted', | ||
| delta: totalWs > 0 ? `${activation?.activated ?? 0}/${totalWs} workspaces` : 'no workspaces', | ||
| }, | ||
| { | ||
| label: 'MISSIONS', | ||
| value: missionTotal > 0 ? `${missionPct}%` : '—', | ||
| tone: missionTotal > 0 ? 'ok' : 'muted', | ||
| delta: missionTotal > 0 | ||
| ? `${mission?.successful_executions ?? 0}/${missionTotal} ok` | ||
| : 'no missions yet', | ||
| }, | ||
| { | ||
| label: 'ERRORS', | ||
| value: String(errorTotal), | ||
| tone: errorTotal > 0 ? 'err' : 'ok', | ||
| delta: errorTotal > 0 && worst ? `${worst.subsystem} (${worst.count}) · 24h` : 'none · 24h', | ||
| }, | ||
| { | ||
| label: 'WIDGET', | ||
| value: String(sessions), | ||
| tone: sessions > 0 ? 'info' : 'muted', | ||
| delta: sessions > 0 ? `${widgetEvents} events · 7d` : '— · 7d', | ||
| }, | ||
| { | ||
| label: 'PRIMITIVES', | ||
| value: '—', | ||
| tone: 'muted', | ||
| delta: 'metric pending', |
There was a problem hiding this comment.
Don't collapse failed analytics calls into healthy tiles.
These hooks only read data, so loading/error states fall through to the same copy as real zero-data. The worst case is the ERRORS tile: a failed request renders 0 with ok, which is a false green on an “is it working?” strip. Render an explicit pending/unavailable state before using the current empty-state copy.
🐛 Sketch of the fix
-export function IsItWorkingStrip() {
- const { data: activation } = useActivationMetrics()
- const { data: mission } = useMissionSuccessRate()
- const { data: errors } = useErrorsBySubsystem('24h')
- const { data: widget } = useWidgetEngagement('7d')
+export function IsItWorkingStrip() {
+ const activationQuery = useActivationMetrics()
+ const missionQuery = useMissionSuccessRate()
+ const errorsQuery = useErrorsBySubsystem('24h')
+ const widgetQuery = useWidgetEngagement('7d')
+
+ const { data: activation } = activationQuery
+ const { data: mission } = missionQuery
+ const { data: errors } = errorsQuery
+ const { data: widget } = widgetQuery
+ const metricsUnavailable = [activationQuery, missionQuery, errorsQuery, widgetQuery].some(
+ (query) => query.isError,
+ )Then switch each cell to neutral copy such as '—' / 'metrics unavailable' (or 'loading…') when a query is not ready, instead of 0, none · 24h, and no workspaces.
🤖 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 `@frontend/components/command-center/is-it-working-strip.tsx` around lines 30 -
80, The tiles collapse failed or loading analytics into healthy zero-state
because the component only reads data from useActivationMetrics,
useMissionSuccessRate, useErrorsBySubsystem, and useWidgetEngagement; update the
component to check each hook's readiness (isLoading/isError/isFetching/ready
flags) before deriving totals/percentages and building the cells array, and when
a query is not ready render neutral placeholders like '—' for value and 'metrics
unavailable' or 'loading…' for delta (instead of 0/'none · 24h'/'no
workspaces'), specifically change the logic that computes totalWs/activationPct,
missionTotal/missionPct, errorTotal/worst, sessions/widgetEvents and the
corresponding delta/tone assignments in the cells definition so failed requests
show a neutral tone and copy rather than green/ok or 0.
Summary
Surfaces the visible half of PRD-142 Wave 0: a five-cell "Is it working?" vitals strip on the live Command Centre (
/command-center), consuming the four real measurement endpoints that merged in #400.What the user sees
/command-centerrenders two variants by theme/viewport — both now carry the vitals:IsItWorkingStrip(thecc-statsstrip idiom) rendered directly under the existingStatsStripincommand-center-shell.tsx.StatsBarvitals row inactivity-page.tsx, beneath the operational stats row.Five cells over real endpoints:
GET /api/analytics/activationGET /api/analytics/dashboard/success-rate(canonical noun Mission)GET /api/analytics/errors/by-subsystem?window=24hGET /api/analytics/widget-engagement?window=7dImplementation
lib/api-client.ts— 4 typed getters + response interfaces (kept from the first commit; reused by both strips).hooks/use-analytics-api.ts— 4 react-query hooks (kept).components/command-center/is-it-working-strip.tsx(new) — mirrorsStatsStripexactly (cells, tones, inlineDot, honest "metric pending").components/command-center/command-center-shell.tsx— render<IsItWorkingStrip />under<StatsStrip />.components/activity/activity-page.tsx— second<StatsBar>of vitalsStatItem[], reusing the existing glass-card pattern.Deletions (dead
/dashboardroute)app/dashboard/page.tsx+components/dashboard/dashboard.tsxcomponents/dashboard/is-it-working.tsx(wrong idiom for Command Centre, superseded)dashboard.tsx:metric-cards,system-health,quick-actions,performance-chart/dashboard→/chat(user-profile-button.tsx); drop/dashboardnav-highlight lines inmain-layout.tsxandstudio-menu.tsType-check
tsc --noEmitintroduces zero new errors attributable to the changed files (verified: new files/symbols absent from tsc output; deletions leave no "Cannot find module"). The repo carries a large pre-existing baseline unrelated to this change; this PR net-reduces it.Test plan
/command-center(Studio desktop) shows the vitals strip directly under the top StatsStrip/command-center(classic/mobile) shows the second StatsBar vitals row/dashboardis gone (404); post-sign-in lands on/chatSummary by CodeRabbit
Release Notes
New Features
Changes