2026 03 04 Observatory Redesign
Documentation for Plans
Observatory Redesign — Unified Vitto Super-Agent Monitoring
Session: 176.3 Date: 2026-03-04 Status: Approved design, pending implementation
Vision
Vitto is the super-agent that orchestrates, monitors, and governs all AI activity. The Observatory is Vitto's eyes — the unified surface where humans see what Vitto sees. Every piece of monitoring data queryable by Vitto through MCP has a corresponding visualization in the Observatory.
Current State
Two separate observatory surfaces exist:
/observatory(Sessions 104-175) — 3 tabs: Agents, System, Sandbox. Real-time ops monitoring./vitto/observatory(Sessions 111-158) — 6 tabs: Approvals, Audit, Costs, Trust, Failure Chains, Specialization. Administrative governance.
Both are functional with real backend integrations. The split happened historically: /observatory was built when monitoring was a platform concern; /vitto/observatory was built when Vitto became a standalone product. Now that Vitto IS the platform's AI layer, they should be one surface.
Architecture Decision
Vitto's Observatory becomes THE canonical observatory.
/vitto/observatorygains all 9 tabs organized in 3 groups/observatoryrenders the sameObservatoryPanelComponentshell — one codebase, two URLs- Chrome extension updates to load the unified surface
- AI panel (
<ai-panel-tabs>) continues embeddingAgentObservatoryComponentdirectly (no change)
Tab Structure
/vitto/observatory (OR /observatory)
├── Operations (all roles)
│ ├── agents → AgentObservatoryComponent (enhanced)
│ ├── system → ObservatoryDashboardComponent (enhanced)
│ └── sandbox → SandboxTabComponent (fixed)
├── Governance (all roles)
│ ├── approvals → ObservatoryApprovalsComponent (fixed)
│ ├── audit → ObservatoryAuditComponent
│ └── trust → TrustDashboardComponent
└── Intelligence (developer/admin only)
├── costs → CostDashboardComponent
├── failures → FailureChainsComponent (fixed)
└── specialization → SpecializationMatrixComponent (fixed)
Backend Responsibilities (Unchanged)
| Backend | Port | Observatory Role |
|---|---|---|
| backend-ai-api | 3001 | Data platform: health checks, LLM costs, sandbox mgmt, agent specialization, MCP data |
| backend-vitto-api | 3003 | Orchestrator: tasks, mind, trust, security audit, skills, task costs, smart routing |
The frontend abstracts this split — observatory services talk to both backends and present a unified view.
Implementation Plan
Part 1: Unified Surface (Route Consolidation)
Goal: Both /observatory and /vitto/observatory render the same shell with all 9 tabs.
Changes:
- Enhance
ObservatoryPanelComponentto accept all 9 child routes grouped into Operations / Governance / Intelligence - Add
agents,system,sandboxas new child routes under/vitto/observatory - Update
/observatoryinapps/gograb-aito renderObservatoryPanelComponentwith the same child route config - Update Chrome extension sidepanel iframe URL
- Role-based tab visibility: Intelligence group hidden for non-developer roles
Files:
libs/libs-ai/vitto-hub/src/lib/components/observatory-panel/observatory-panel.component.ts— enhance with tab groupslibs/libs-ai/vitto-hub/src/lib/components/observatory-panel/observatory-panel.component.html— grouped tab layoutlibs/libs-ai/vitto-hub/src/lib/routes/vitto-hub.routes.ts— add 3 new child routesapps/gograb-ai/src/app/pages/observatory/observatory-page.component.ts— renderObservatoryPanelComponentapps/gograb-ai/src/app/routes/— update observatory route configapps/vitto-chrome-extension/sidepanel/sidepanel.js— update iframe URL
Part 2: Bug Fixes (11 confirmed issues)
| # | Issue | File | Fix |
|---|---|---|---|
| 1 | Server infrastructure hardcoded in template | sandbox-tab.component.html:80-105, observatory.controller.ts, .env.local |
Backend sandbox-providers returns serverInfo from env vars (SANDBOX_SERVER_LABEL, SANDBOX_SERVER_HARDWARE). Template reads from signal. |
| 2 | setInterval violates async rules |
observatory-approvals.component.ts:77 |
Replace with timer(0, 30_000).pipe(takeUntilDestroyed(this.destroyRef)) |
| 3 | VittoMonitorService singleton race condition | vitto-monitor.service.ts |
Reference-counting: _connectCount incremented on connect, decremented on disconnect. SSE only closes when count reaches 0. |
| 4 | Dead vittoApiUrl injection |
sandbox-tab.component.ts:26 |
Remove unused injection |
| 5 | getModelEntries() template method |
observatory-dashboard.component.ts:75 |
Convert to computed() signal |
| 6 | Legacy *ngIf/*ngFor syntax |
observatory-dashboard.component.html |
Convert to @if/@for control flow |
| 7 | (res as any) untyped responses |
All 6 vitto hub observatory tabs | Create proper DTOs in vitto.types.ts, type the VittoApiService methods |
| 8 | Non-functional taskTypeFilter | failure-chains.component.html |
Add <input> bound to taskTypeFilter signal with debounce |
| 9 | any[] typing |
specialization-matrix.component.ts:40 |
Type as SharedPattern[] |
| 10 | destroySession() no error handling |
sandbox-tab.component.ts |
Add error toast, loading state, optimistic removal + refresh on success |
| 11 | Non-joined HTTP calls | specialization-matrix.component.ts |
Join with forkJoin for single loading cycle |
Part 3: Wire Unwired Code
Goal: Every component built by a previous session gets wired to its intended home.
| Component | Intent | Wire To |
|---|---|---|
GoGrabObservatoryComponent (embeddable-bridge) |
Session 104.2: Embed observatory in any host app | Import into gograb-space admin layout. Shows observatory for space admins without navigating away. |
createObservatoryPlugin (vitto-chat) |
Session 149: Observatory tab in unified chat | Add to createStandardPluginSet in plugin-presets.ts for T0+T1 tiers. Pass AgentObservatoryComponent. |
VittoMonitorComponent unique features |
Session 103: Original monitor had model/provider in history + phase token counts | Merge both into AgentObservatoryComponent template. Mark VittoMonitorComponent as @deprecated. |
recentDecisions signal |
OrchestrationClientService: agent decisions feed | New "Agent Decisions" card in AgentObservatoryComponent full mode |
loadedContext signal |
OrchestrationClientService: context loading indicator | New "Context Loaded" section in full mode |
patternMatches signal |
OrchestrationClientService: pattern matching feed | New "Pattern Matches" feed in full mode |
learningStats signal |
OrchestrationClientService: learning system health | New "Learning System" status card in full mode |
Part 4: LLM Cost Persistence (MongoDB)
Goal: LLM costs survive server restarts. Historical trends available.
Backend changes (backend-ai-api):
New Mongoose schema:
@Schema({ collection: 'llm-usage', timestamps: true })
export class LlmUsageDocument {
@Prop() model: string;
@Prop() tokensIn: number;
@Prop() tokensOut: number;
@Prop() estimatedCostUsd: number;
@Prop() userId: string;
@Prop() sessionId?: string;
@Prop() timestamp: Date;
}
TTL index: auto-expire after 90 days (configurable).
LlmCostTrackerService.recordUsage():
- Continues updating in-memory 24h window (fast reads)
- Also writes to MongoDB (fire-and-forget, non-blocking)
New endpoints:
GET /infrastructure/observatory/llm-history?days=30— daily cost aggregates for chartingGET /infrastructure/observatory/llm-by-user?days=7— per-user cost breakdown
Frontend changes:
ObservatoryDashboardComponentgains:- 30-day cost trend chart (simple SVG, no heavy charting lib)
- Per-user cost breakdown table (the
byUserfield currently fetched but never rendered) - Backend health
detailsfield rendered (currently silently dropped)
Part 5: Unified Alert Pipeline
Goal: All alert sources flow through one service, persist to MongoDB, push to frontend in real-time.
Backend (backend-ai-api):
New AlertAggregatorService:
@Injectable()
export class AlertAggregatorService implements OnModuleInit {
// Subscribes to:
// - PipelineMetricsService events (pipeline.alert, pipeline.unhealthy, pipeline.degraded)
// - ConditionMonitorService events (monitor.alert, monitor.recovered)
// - ObservatoryService health results (backend down/degraded)
// - Vitto proactive alerts (forwarded from vitto-api)
// Persists to 'alerts' collection (deduplication by message hash + 1h window)
// Emits to WebSocket for real-time frontend push
// Exposes via REST for polling fallback
}
Mongoose schema:
@Schema({ collection: 'alerts', timestamps: true })
export class AlertDocument {
@Prop() severity: 'critical' | 'warning' | 'info';
@Prop() source: string; // 'pipeline' | 'condition-monitor' | 'health-check' | 'vitto-proactive'
@Prop() check: string; // e.g., 'api-health', 'build-health', 'dependency-audit'
@Prop() message: string;
@Prop() details?: string;
@Prop() resolved: boolean;
@Prop() resolvedAt?: Date;
@Prop() firstSeenAt: Date;
@Prop() lastSeenAt: Date;
@Prop() occurrenceCount: number;
}
TTL index: auto-expire resolved alerts after 30 days.
New endpoint:
GET /infrastructure/observatory/alerts?severity=critical&resolved=false&limit=50POST /infrastructure/observatory/alerts/:id/resolve— manual resolution
Frontend:
- New "Alerts" section in System tab with persistent alert history
- Alert badge count on Observatory navigation item
- Existing proactive alerts in Agent Monitor continue via SSE (complementary, not replaced)
Part 6: MCP Convergence — Vitto Self-Monitoring
Goal: Every observatory visualization maps to an MCP tool. Vitto can self-diagnose.
New MCP actions (in MCP/mcp-gograb-ai/):
stats({ action: "alerts" })— returns active unresolved alerts- Calls
GET /infrastructure/observatory/alerts?resolved=false - Vitto uses this to self-diagnose when backends are degraded
- Calls
stats({ action: "costs", days: 7 })— returns cost breakdown by model/user- Calls
GET /infrastructure/observatory/llm-history?days=7 - Vitto can monitor spend and warn proactively
- Calls
intelligence({ action: "trust", actionType: "deploy::execute" })— returns trust score- Calls
GET /engine/trust-scoreson vitto-api - Vitto checks its own trust before attempting risky actions
- Calls
Existing MCP tool ↔ Observatory mapping:
| MCP Tool | Observatory Tab | Status |
|---|---|---|
preflight (health check) |
System tab | Exists |
stats.enhanced (system stats) |
System tab | Exists |
intelligence.specializations |
Specialization tab | Exists |
intelligence.calibrate |
Trust tab | Exists |
intelligence.recall |
Failure Chains tab | Partial match |
agent.handoff_pending |
Approvals tab | Exists |
stats.alerts |
System tab (new Alerts section) | New |
stats.costs |
System tab (cost charts) | New |
intelligence.trust |
Trust tab | New |
Vitto self-monitoring flow:
- On task start:
preflightalready checks system health - If
preflightdetects degraded health: Vitto can decide to pause, warn, or proceed with caution - Vitto can call
stats.alertsproactively to surface issues - After task completion:
outcome.reportfeeds the learning pipeline (already happens)
Part 7: Wire Key Unused Backend Endpoints
Goal: 8 existing VittoApiService methods with high value get wired to observatory tabs.
| # | Method | Wire To | User Value |
|---|---|---|---|
| 1 | estimateCost(params) |
Costs tab — pre-flight cost prediction | "This task will cost ~$X" before execution |
| 2 | getMcpUsageHistory() |
Costs > AI Infrastructure — usage trend | Daily/weekly MCP usage chart |
| 3 | getRoutingDecisions() |
Costs > Routing — decision audit | Why SmartRouter chose each provider |
| 4 | getComplianceReport(start, end) |
Audit tab — date-range report | Structured compliance view |
| 5 | getAuditTrail(params) |
Audit tab — extended with pagination | Replaces 50-entry limit with scrollable history |
| 6 | Mind health endpoints | System tab — new "AI Health" section | Memory health, consolidation, stale count |
| 7 | getThoughtTrace(taskId) |
Agent Monitor — expandable per task | Vitto's reasoning chain visualization |
| 8 | getMemoryInsights(userId) |
Intelligence — insight card | AI-generated summary of learned knowledge |
Data Flow
┌─────────────────────────────────────────┐
│ Observatory UI (Angular) │
│ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ VittoMonitor│ │ Orchestration │ │
│ │ Service │ │ ClientService │ │
│ │ (SSE) │ │ (WebSocket) │ │
│ └──────┬──────┘ └────────┬─────────┘ │
│ │ │ │
│ ┌──────┴───────────────────┴─────────┐ │
│ │ ObservatoryDataService (REST poll) │ │
│ │ VittoApiService (REST on-demand) │ │
│ └──────┬───────────────────┬─────────┘ │
└─────────┼───────────────────┼───────────┘
│ │
┌─────────▼─────────┐ ┌──────▼──────────┐
│ backend-ai-api │ │ backend-vitto-api│
│ (port 3001) │ │ (port 3003) │
│ │ │ │
│ • Health checks │ │ • Task engine │
│ • LLM costs (DB) │ │ • Mind/memories │
│ • Sandbox mgmt │ │ • Trust scores │
│ • Alerts (DB) │ │ • Security audit │
│ • Agent specs │ │ • Task costs │
│ • MCP data │ │ • Smart routing │
│ • Computer use │ │ • Skills/personas │
│ • Command center │ │ • A2A protocol │
└───────────────────┘ └──────────────────┘
│ │
┌─────────▼───────────────────▼───────────┐
│ MCP Server │
│ (same data, programmatic access) │
│ │
│ stats.alerts → backend-ai-api │
│ stats.costs → backend-ai-api │
│ intelligence.trust → backend-vitto-api │
│ preflight → both backends │
└─────────────────────────────────────────┘
Dead Code Resolution
| Item | Original Intent | Resolution |
|---|---|---|
GoGrabObservatoryComponent |
Session 104.2: embed in host apps | Wire to gograb-space layout |
createObservatoryPlugin |
Session 149: chat tab integration | Wire to createStandardPluginSet |
VittoMonitorComponent |
Session 103: original monitor | Merge unique features, deprecate |
| OrchestrationClientService 4 signals | WS event receivers for decisions/context/patterns/learning | Wire to AgentObservatoryComponent full mode |
Overlap Resolution
| Overlap | Location A | Location B | Resolution |
|---|---|---|---|
| Backend health cards | System tab (ObservatoryDashboardComponent) |
Costs > AI Infrastructure | Remove generic health from Costs; keep MCP-specific data (decisions, patterns, circuit breaker). System tab owns health. |
| Checkpoint approvals | Agent Monitor inline | Approvals tab dedicated | Keep both — different user journeys (real-time ops vs governance review). Same SSE data source, no wasted work. |
Files Touched (Estimated)
Modified: ~25 files
New: ~5 files (alert service, alert schema, LLM usage schema, new MCP actions, shared DTOs)
Deprecated: 1 file (VittoMonitorComponent marked @deprecated)
Success Criteria
- Single observatory surface accessible from both
/observatoryand/vitto/observatory - All 9 tabs render real data from their respective backends
- Zero
(res as any)patterns — all responses typed - LLM costs persisted to MongoDB with 30-day trend chart
- Alerts aggregated, persisted, and visible in System tab
- 3 new MCP actions for Vitto self-monitoring
- All 4 OrchestrationClientService signals rendered in Agent Monitor (full mode)
- All previously unwired components connected to their intended homes
- All 11 confirmed bugs fixed
- 8 high-value unused backend endpoints wired to observatory tabs