caadd59441
Move the following audited-and-implemented changes into openspec/changes/archive/2026-06-12-completed-changes-archive/: - backend-frontend-refactoring - config-profile-git-mounts - config-profile-includes-ui - config-profile-multi-repo-mounts - container-monitoring-notifications - git-mount-url-validation - home-path-expansion - mobile-terminal-ux - mount-specificity-ordering - notification-center - persistent-terminal-sessions - session-list-overhaul - ssh-key-mounting - terminal-fullscreen-unified-header - tool-session-progress-and-updates Also regenerated .pi-map*.md files for openspec/changes so the remaining active changes (multi-session-terminal-ux, reorganize-long-files, working-copies, workspace-first-ui) reflect the new layout.
7.2 KiB
7.2 KiB
PR-2 Apply Report: Frontend UI for Container Monitoring & Notifications
Status: COMPLETE
All assigned PR-2 tasks have been implemented and validated.
Changed Files
New Files (9)
| File | Purpose |
|---|---|
apps/web/src/types/events.ts |
TypeScript InstanceEventPayload + InstanceEventMetadata interfaces |
apps/web/src/api/events.ts |
Thin EventSource wrapper + probeEventStreamStatus for 401/429 detection |
apps/web/src/hooks/use-events.ts |
useEvents() hook with SSE connect, exponential backoff reconnect, jitter |
apps/web/src/hooks/use-events.test.ts |
Unit tests for useEvents (7 tests) |
apps/web/src/components/toast-rules.ts |
Event-to-toast mapping + deduplication logic |
apps/web/src/components/toast-rules.test.ts |
Unit tests for toast rules (7 tests) |
apps/web/src/state/toast.tsx |
Custom lightweight toast system: ToastContext, ToastProvider, ToastContainer |
apps/web/src/state/events.tsx |
EventProvider context that wraps useEvents() and exposes events to consumers |
apps/web/src/components/event-toast-bridge.tsx |
Bridge component that consumes EventContext and triggers toasts via toast-rules |
Modified Files (4)
| File | Change |
|---|---|
apps/web/src/components/app-shell.tsx |
Mount EventProvider + ToastProvider + EventToastBridge on all authenticated routes |
apps/web/src/components/instance-list.tsx |
Removed 30s health polling; added SSE-driven real-time status updates; retained 60s list refresh |
apps/web/src/components/session-card.tsx |
Updated statusConfig badge colors: starting/probing → blue, unhealthy → amber |
apps/web/src/styles.css |
Added .status-badge.starting, .status-badge.probing, .status-badge.unhealthy + toast animation keyframes |
Implementation Summary
MON-PR2-001 / MON-PR2-002: useEvents() Hook + events.ts API Client
createEventSource()returns nativeEventSourcewithwithCredentials: trueuseEvents()hook maintainsevents,connected,reconnectCount, anderrorstate- Reconnect strategy:
delay = min(30000, 1000 * 2^attempts) * (0.8 + Math.random() * 0.4) - On
401(detected viaprobeEventStreamStatusfetch probe): stops reconnecting and redirects to login - On
429: adds 5s penalty before next retry - Cleans up
EventSourceand pending timeouts on unmount
MON-PR2-003 / MON-PR2-004: Custom Toast System (No External Dependencies)
- Built a pure React + CSS toast stack:
ToastContextwithaddToast/removeToastAPIsToastProvidermanages timer-based auto-dismissalToastContainerrenders fixed-position stack with inline styles + CSS animation
- Supports severity colors: info (blue), success (green), warning (amber), error (red)
- Auto-dismiss timers: info/success 3s, warning 5s, error 10s (configurable)
- Manual dismiss via × button on each toast
MON-PR2-005: EventProvider Context
EventProvidermounts at app-shell level, callsuseEvents()once, shares event stream via React contextuseEventContext()allows any descendant to subscribe to the shared SSE stream without creating duplicate connections
MON-PR2-006: Real-Time Status Badge Updates + Polling Removal
- Removed the 30-second
checkInstanceHealthpolling loop frominstance-list.tsx - Added
useEffectthat listens to SSE events and updatesinstancesstate in-place for matchinginstance_id - Retained a 60-second
setIntervalforloadInstances()as a resilience fallback - Updated
session-card.tsxbadge color mapping to match spec:starting/probing→ blue CSS classunhealthy→ amber CSS class
- Added corresponding CSS rules in
styles.css
MON-PR2-007: Integration into App Shell
AppShellnow wraps all authenticated routes withEventProviderandToastProviderEventToastBridgeis mounted inside the providers to render toasts from SSE events- Mobile terminal view also gets the providers (toasts still work in terminal)
MON-PR2-008: Frontend Tests
use-events.test.ts: 7 tests covering event parsing, reconnect backoff, 30s cap, 401 redirect, 429 penalty, unmount cleanup, reconnectCount exposuretoast-rules.test.ts: 7 tests covering event-to-toast mapping (started, running, unhealthy, error) and deduplication within 1s window
Test Commands & Exit Codes
# Focused new tests
$ cd apps/web && npx vitest run src/hooks/use-events.test.ts src/components/toast-rules.test.ts
# Exit code: 0 (14 passed)
# Broader regression check on modified page/component tests
$ cd apps/web && npx vitest run src/hooks/use-events.test.ts src/components/toast-rules.test.ts src/pages/dashboard.test.tsx src/components/terminal-session-tabs.test.tsx src/components/protected-route.test.tsx
# Exit code: 0 (25 passed)
# TypeScript type check
$ cd apps/web && npx tsc --noEmit
# Exit code: 0 (no errors)
# Lint on new/modified files only
$ cd apps/web && npx eslint <new ts/tsx files> --ext ts,tsx --report-unused-disable-directives --max-warnings 0
# Exit code: 0 (all clean)
Note: The full
npx vitest runshows 4 pre-existing failures inrepositories-settings-tab.test.tsx(unrelated to this PR). The fullnpx eslintalso shows 3 pre-existing errors interminal.tsxandtool-workshop.tsx.
Surprises & Decisions
- No sonner dependency: The orchestrator instructed not to install
sonnerbecausenpm installhangs in this environment. Implemented a custom ~170-line toast system instead using pure React + inline CSS. It supports severity, auto-dismiss, manual dismiss, and stacking with CSS animations. - EventSource 401/429 detection: Native
EventSourcedoes not expose HTTP status codes. Implemented aprobeEventStreamStatus()helper that does a shortfetch()to the SSE endpoint withAbortControllertimeout to detect 401/429 before reconnecting. - Badge color CSS classes: The existing
session-card.tsxused raw color strings ("green", "yellow", etc.) as CSS class names, but no corresponding CSS classes existed. Added explicit.status-badge.starting,.status-badge.probing, and.status-badge.unhealthyrules tostyles.css. - Tunnel health removal: The 30s polling loop in
instance-list.tsxwas the source ofhealthStatusstate used for "tunnel error" badges. After removing polling, tunnel-specific health data is no longer available in real time; instances now rely on SSEstatustransitions (e.g.,unhealthy). The tunnel error badge was removed frominstance-list.tsxas redundant with the status badge. - App.tsx vs app-shell.tsx: This codebase has no
App.tsx;AppShellinapp-shell.tsxis the layout component that wraps all authenticated routes. Providers were mounted there instead.
PR Boundary
This PR includes the complete frontend UI for container monitoring and notifications:
- SSE client hook with reconnect backoff
- Custom toast notification system
- Event provider context
- Real-time status badge updates
- Polling removal from instance list
The next PR (PR-3) should cover:
- Integration tests for lifecycle event flow
- E2E tests for container start → toast and crash detection
- Performance tuning (connection limits, queue bounds, jitter)
- Documentation updates
- Final cleanup and regression validation