Orchestrata Ultimate › Plans › plan_vizint_phase_6 › sprint-1-resilience
Sprint 1: Resilience Foundation
Source: plans/plan_vizint_phase_6/sprint-1-resilience.md at 8a2cb42ad · last changed 2026-07-27 · first committed 2026-05-26 · 309 lines.
Prose pending.
Plan text
Sprint 1: Resilience Foundation
Duration: Week 1 (40 hours)
Goal: Close all 20 Article IX (Graceful Degradation) items + 10 Article VIII (Observability) items
Target: 30 constitutional items verified
Day 1: Error Boundaries & RPC Queue (8h)
Task 1.1: ErrorBoundary Class (6h) ✅ DONE
File: vizint-ui/lib/error-boundary.js (new)
export class ErrorBoundary {
constructor(container, onRetry) {
this._container = container;
this._onRetry = onRetry;
this._hasError = false;
}
async guard(asyncFn) {
if (this._hasError) {
this._renderError();
return;
}
try {
return await asyncFn();
} catch (err) {
this._hasError = true;
logger('error', 'Gadget error caught by boundary', { error: err.message });
this._renderError(err);
}
}
_renderError(err) {
this._container.innerHTML = `
<div class="error-boundary">
<div class="error-boundary__icon">⚠️</div>
<div class="error-boundary__message">Something went wrong</div>
<button class="error-boundary__retry" data-action="retry">Retry</button>
</div>
`;
this._container.querySelector('[data-action="retry"]')
.addEventListener('click', () => this._onRetry());
}
reset() {
this._hasError = false;
this._container.innerHTML = '';
}
}
CSS in styles/command-center.css:
.error-boundary {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 2rem;
color: var(--text-primary);
}
.error-boundary__icon { font-size: 2rem; }
.error-boundary__message { font-size: 0.875rem; }
.error-boundary__retry {
padding: 0.375rem 0.75rem;
background: var(--accent);
color: white;
border-radius: var(--radius-sm);
}
Integration: Modify app.js _mountGadget() to wrap gadget.mount() in ErrorBoundary.
Tests: tests/unit/error-boundary.test.js — 4 tests (catch error, render error UI, retry callback, reset)
Constitutional Items Closed:
- CONST-IX-006: Gadget error boundary catches exception
- CONST-IX-007: Other gadgets unaffected
- CONST-IX-014: Retry button in error state
Task 1.2: Bridge RPC Queue Verification (2h) ✅ DONE
File: vizint-ui/bridge/websocket-bridge.js
Verify existing queue behavior:
- Max 100 queued calls
- Oldest discarded when full
- Flush on reconnect
Test: Add to tests/unit/websocket-bridge.test.js:
- Queue 101 calls → first is discarded
- Reconnect flushes queue
Constitutional Items Closed:
- CONST-IX-003: RPC calls queued (up to 100)
- CONST-IX-013: Queued calls flushed on reconnect
Day 2: Auto-Re-subscription & Read-Only Mode (10h)
Task 2.1: Subscription Tracker (6h) ✅ DONE
File: vizint-ui/bridge/websocket-bridge.js
Add _subscriptions Map to track active subscriptions:
this._subscriptions = new Map(); // subId -> { topic, filters, callback }
On subscribe(): store in Map.
On unsubscribe(): remove from Map.
On reconnect: iterate Map and re-subscribe each.
Constitutional Items Closed:
- CONST-IX-008: Automatic re-subscription on reconnect
- CONST-IX-012: Reconnect re-authenticates and re-subscribes
Task 2.2: Read-Only Mode (4h) ✅ DONE
File: vizint-ui/styles/command-center.css
.gadget--readonly input,
.gadget--readonly button,
.gadget--readonly textarea,
.gadget--readonly select {
opacity: 0.5;
pointer-events: none;
}
.gadget--readonly::after {
content: 'Read-only (offline)';
position: absolute;
top: 0.25rem;
right: 0.5rem;
font-size: 0.625rem;
color: var(--warning);
background: var(--bg-secondary);
padding: 0.125rem 0.375rem;
border-radius: var(--radius-sm);
}
File: vizint-ui/app.js
In _updateConnectionStatus(): add/remove gadget--readonly class on all chrome wrappers.
Constitutional Items Closed:
- CONST-IX-001: Read-only mode with cached data
Day 3: MetricsCollector Core (8h)
Task 3.1: MetricsCollector Class (8h) ✅ DONE
File: vizint-ui/lib/metrics.js (new)
Track:
- Render times per gadget
- RPC latency per method
- Event delivery latency
- Connection state transitions
- Reconnection durations
- Subscription counts per topic
- Notification lifecycle
- Memory samples
- Frame rate
- Error counts per gadget
API:
export class MetricsCollector {
recordRender(gadgetId, duration) {}
recordRpc(method, duration) {}
recordEvent(topic, latency) {}
recordConnection(from, to) {}
recordReconnect(attempts, duration) {}
recordSubscription(topic, count) {}
recordNotification(action) {}
recordMemory(usedMB) {}
recordFps(fps) {}
recordError(gadgetId, error) {}
getSnapshot() {}
exportJson() {}
}
Tests: tests/unit/metrics.test.js — 10 tests
Constitutional Items Closed:
- CONST-VIII-001–010: All client-side metrics
Day 4: DebugPanel & PerformanceOverlay (12h)
Task 4.1: DebugPanel Gadget (8h) ✅ DONE
File: vizint-ui/gadgets/debug-panel/gadget.js (new)
Toggleable overlay showing:
- Active WebSocket subscriptions (topic, filter count)
- RPC queue depth
- Event bus traffic (events/minute)
- Connected clients (if multi-user)
- Memory usage bar
Design: Collapsible panel, fixed bottom-right, z-index above all.
Toggle: Ctrl+Shift+D or button in header.
Constitutional Items Closed:
- CONST-VIII-016: Debug panel with subscriptions, RPC queue, bus traffic
Task 4.2: PerformanceOverlay (4h) ✅ DONE
File: vizint-ui/lib/performance-overlay.js (new)
Small floating panel showing:
- FPS counter (requestAnimationFrame loop)
- Memory usage (performance.memory)
- Last render time per gadget
Design: Semi-transparent, top-right, collapsible.
Constitutional Items Closed:
- CONST-VIII-017: Performance overlay with FPS, memory, render times
Day 5: Gadget Health & Quota Handling (8h)
Task 5.1: GadgetHealthIndicator (4h) ✅ DONE
File: vizint-ui/app.js
Add to chrome titlebar:
- Green dot: healthy
- Yellow dot: warning (slow renders)
- Red dot: error state
- Spinner: loading
Constitutional Items Closed:
- CONST-VIII-015: Gadget health visible in chrome
Task 5.2: localStorage Quota Handler (4h) ✅ DONE
File: vizint-ui/lib/quota-manager.js (new)
export function checkQuota() {
const used = new Blob(Object.values(localStorage)).size;
const max = 5 * 1024 * 1024; // 5MB typical
if (used > max * 0.9) {
evictOldest();
}
}
function evictOldest() {
// Find oldest commandcenter.state.* key by timestamp
// Remove it
}
Call checkQuota() before every localStorage.setItem().
Constitutional Items Closed:
- CONST-IX-010: localStorage quota exceeded handling
Sprint 1 Verification
cd vizint-ui
npm test
npx playwright test tests/e2e/
npm run lint
node scripts/compliance-check.js
Expected: 30 constitutional items checked. All tests pass.
Sprint 1 Completion Summary (2026-05-26)
All 9 tasks completed. Implementation differs from original spec in minor ways:
| Task | File(s) | Tests | Notes |
|---|---|---|---|
| ErrorBoundary | lib/error-boundary.js |
tests/unit/error-boundary.test.js (7 tests) |
Added wrapMount() (async), retry with onRetry callback, vizint:error CustomEvent, metrics integration |
| RPC Queue | bridge/websocket-bridge.js |
tests/unit/websocket-bridge.test.js (10 tests) |
Verified 100-item limit + FIFO discard; added explicit queue flush + re-subscription tests |
| Auto-Re-subscription | bridge/websocket-bridge.js |
same | _subSpecs Map tracks topic/filters; _resubscribeAll() called on reconnect |
| Read-Only Mode | app.js, styles/command-center.css |
— | data-readonly attribute toggled on #app root; CSS dims grid and disables inputs |
| MetricsCollector | lib/metrics.js |
tests/unit/metrics.test.js (7 tests) |
Tracks renders, RPC, events, memory, FPS, errors; getSnapshot() / getReport() API |
| DebugPanel | gadgets/debug-panel/gadget.js |
— | Dev gadget consuming window.__vizintMetricsCollector snapshot; copy-report + clear-errors actions |
| PerformanceOverlay | lib/performance-overlay.js |
— | Floating HUD for FPS + memory; toggleable via stateSync.get('perfOverlay') |
| GadgetHealthIndicator | app.js, styles/command-center.css |
— | Green/yellow/red dot in chrome titlebar; updated via _updateGadgetHealth() |
| localStorage Quota | bridge/state-sync.js |
tests/unit/state-sync.test.js (6 tests) |
_evictOldest() removes oldest commandcenter.state.* entry on QuotaExceededError |
Test totals: 226 passing (21 files), 0 failing, 0 ERRORs in test-review. Constitutional items closed: CONST-IX-001, 003, 006, 007, 008, 010, 012, 013, 014 + CONST-VIII-001–010, 015, 016, 017 = ~23 items.