Spec 025 — Overview uptime KPI + Reverb live updates + perf chart (closes phase 5)#76
Merged
Merged
Conversation
Last spec of phase 5. Replaces MOCK_KPIS['uptime'] with a real volume- weighted aggregate across all user's websites, broadcasts every persisted check via Reverb so the Show page reflects live data, and adds a response-time Sparkline of the last 50 checks.
…art (spec 025)
Closes phase 5. Replaces MOCK_KPIS['uptime'] with a real cross-website
aggregate, broadcasts every persisted check via Reverb so the per-
website Show page reflects live data, and renders a response-time
Sparkline of the last 50 checks.
- GetMonitoringUptimeKpiQuery: volume-weighted 24h uptime % across all
websites (locked decision B — busy site with one failure dominates
a quiet 100% site, the truest system-wide measure). Returns
{overall, change, sparkline (12 days oldest-first), status}.
- overall: float|null; null on empty 24h window.
- change: 24h delta vs prior 24h; 0 when either window is empty.
- sparkline: daily uptime %; days with no checks default to 100.0
("no failures observed" reads better than "everything down" on
a fresh account; documented limitation).
- status: muted (null) | success (≥99) | warning (≥95) | danger.
- GetOverviewDashboardQuery::handle() calls the new query for the
uptime slice; MOCK_KPIS['uptime'] removed; docblock updated.
- New WebsiteCheckRecorded ShouldBroadcastNow event:
- Constructor takes pre-resolved (checkId, websiteId, ownerUserId)
ints — mirrors spec 021's WorkflowRunUpserted pattern, avoids
broadcast-time relation walking.
- Broadcasts on users.{ownerUserId}.monitoring with light pulse
{check_id, website_id}; client uses it as a trigger, not a
source of truth.
- Per-user channel + client-side filter trade-off documented;
revisit at ~1k monitors or sub-30s intervals.
- routes/channels.php authorizes users.{userId}.monitoring (per-user
gate matching the activity / deployments channels).
- RecordWebsiteCheckAction dispatches the event after every persisted
check (steady-state runs included). Spec 024's transition activity
events still fire separately on healthy↔failed swings.
- Show.vue subscribes via Echo on mount, filters by website_id,
partial-reloads website + summary + checks on matching pulses;
realtimeConnected ref drives an offline pill. Sparkline of last
50 response_time_ms with leading-null skip + carry-forward fill;
<2 data points renders the "not enough data" placeholder.
Tests: 18 net new passing tests across 3 new files + 2 extended.
Full suite 357 passed (was 339); 0 regressions.
Self-review pass via superpowers:code-reviewer flagged 3
recommendations, all addressed:
- usePage() hoisted to setup top-level (was inside onMounted —
idiomatically wrong, Inertia could validate the call site).
- responseTimeSeries skips leading-null Error checks so the line
doesn't anchor at the 0ms floor (Sparkline's min/max normalizer
was pulling the chart to the baseline).
- broadcastOn() docblock documents the per-user-channel-vs-per-
website-channel trade-off for future scaling.
PHASE 5 COMPLETE (3/3 specs). The only remaining MOCK_KPIS slices
are 'services' (phase 6) and 'alerts' (phase 7).
Spec frontmatter + tracker bookkeeping that didn't make the previous commit (Edit-without-Read guard). Trailing companion to the implementation commit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #75
Spec: specs/phase-5-monitoring/025-overview-and-realtime.md
Last spec of phase 5. Replaces the long-standing
MOCK_KPIS['uptime']placeholder on Overview with a real volume-weighted cross-website aggregate, broadcasts every persisted check via Reverb so the per-website Show page reflects live data, and renders a response-time Sparkline of the last 50 checks.Summary
GetMonitoringUptimeKpiQuery— volume-weighted 24h uptime % across all websites (decision B: a busy site with one failure dominates a quiet 100% site, the truest system-wide measure). Returns{overall, change, sparkline (12d oldest-first), status}. Empty 24h window →nulloverall +mutedstatus. Days with no checks default to 100% on the sparkline (documented "no failures observed" framing — better than rendering as a 0% flatline on fresh accounts).GetOverviewDashboardQuery;MOCK_KPIS['uptime']removed; docblock graduates uptime to "real today."WebsiteCheckRecordedShouldBroadcastNowevent with pre-resolved owner id (mirrors spec 021'sWorkflowRunUpsertedpattern). Broadcasts onusers.{ownerUserId}.monitoring. Light pulse{ check_id, website_id }.routes/channels.phpauthorizes the new channel (per-user gate matching activity / deployments).RecordWebsiteCheckActiondispatches the event after every persisted check (steady-state runs included). Spec 024's transition activity events still fire separately on healthy↔failed swings.Show.vuesubscribes via Echo on mount, filters client-side bywebsite_id, partial-reloadswebsite + summary + checkson matching pulses.realtimeConnectedref drives an offline pill. Response-timeSparklineof last 50response_time_mswith leading-null skip + carry-forward fill; <2 data points renders the "not enough data" placeholder.Test plan
vendor/bin/pint --testpasses.php artisan test— 18 net new passing tests across 3 new files (KPI query 10, event 5, dispatch + transition extensions 3) + 2 extended (record action + dashboard query). Full suite 357 passed (was 339); 50 failures are env-CSRF baseline; CI passes them.npm run buildclean.php artisan schedule:worktick triggers a partial reload + the Sparkline updates without a manual refresh.Self-review notes
Self-review pass via
superpowers:code-reviewerflagged 3 recommendations, all addressed:usePage()hoisted to setup top-level — was insideonMounted, idiomatically wrong (Inertia could validate the call site at any release).responseTimeSeriesnow skips leading null Error checks rather than carry-forwarding a0— the Sparkline's min/max-normalized rendering was pulling the line to the 0ms floor when the first point was a transport-error row.WebsiteCheckRecorded::broadcastOn()docblock documents the per-user-channel-vs-per-website-channel trade-off for future scaling (~1k monitors / sub-30s intervals).Phase-1 acknowledgements (PR-body-only)
last_checked_atgoing stale on the index/show pages and via the right rail's lost activity heartbeat. Future polish: switch to null + Sparkline gap rendering.GetMonitoringUptimeKpiQueryfromGetOverviewDashboardQuery::handle()viaapp(...)— matches the existing class's no-constructor pattern. Refactor opportunity for a follow-up if more queries graduate.Phase 5 status
Phase complete. The only remaining
MOCK_KPISslices areservices(phase 6) andalerts(phase 7) — they ride with their own future phases.After merge, what's next: