Skip to content

Commit ff65e44

Browse files
committed
release: v2.7.251 health/logs/audit operator surfaces + system overview page
1 parent 15125b7 commit ff65e44

File tree

6 files changed

+206
-1
lines changed

6 files changed

+206
-1
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44

55
All notable changes to this project will be documented in this file.
66

7+
## [2.7.251] — 2026-03-16
8+
9+
- feat(web/dashboard): New `system/page.tsx` — operator console showing Borg uptime, subsystem readiness checks (from `startupStatus` contract), blocking boot reasons, and navigation cards to Health, Logs, and Audit.
10+
- feat(web/dashboard): `health/page.tsx` — added `PageStatusBanner` (beta) for maturity labeling.
11+
- feat(web/dashboard): `logs/page.tsx` — added `PageStatusBanner` (beta) for maturity labeling.
12+
- feat(web/dashboard): `audit/page.tsx` — added `PageStatusBanner` (beta) for maturity labeling.
13+
- test(validation): `WEB_TSC_OK` — no TypeScript errors.
14+
715
## [2.7.250] — 2026-03-16
816

917
- fix(core/startup): `buildStartupStatusSnapshot` no longer blocks a **zero-server fresh install** on `mcp_config_sync_pending`. When `configuredServerCount === 0 && persistedServerCount === 0`, config sync is trivially satisfied — waiting for `lastCompletedAt` would stall the boot indefinitely with no MCP servers to sync.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.7.250
1+
2.7.251

apps/web/src/app/dashboard/audit/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import { useState } from 'react';
4+
import { PageStatusBanner } from '@/components/PageStatusBanner';
45
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@borg/ui";
56
import { Button } from "@borg/ui";
67
import { Input } from "@borg/ui";
@@ -50,6 +51,7 @@ export default function AuditDashboard() {
5051
return (
5152
<div className="p-8 space-y-8 h-full overflow-y-auto w-full max-w-[1200px] mx-auto">
5253
{/* Header */}
54+
<PageStatusBanner status="beta" message="Audit trail captures system events. Export and retention-policy settings are planned." />
5355
<div className="flex justify-between items-center">
5456
<div>
5557
<h1 className="text-3xl font-bold tracking-tight text-white flex items-center gap-3">

apps/web/src/app/dashboard/health/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@borg/ui";
4+
import { PageStatusBanner } from '@/components/PageStatusBanner';
45
import { Badge } from "@borg/ui";
56
import { Button } from "@borg/ui";
67
import { Activity, Server, AlertTriangle, RefreshCcw, HardDrive, Cpu, Network, Radio } from "lucide-react";
@@ -61,6 +62,7 @@ export default function HealthDashboard() {
6162

6263
return (
6364
<div className="p-8 space-y-8 h-full overflow-y-auto">
65+
<PageStatusBanner status="beta" message="Health monitoring is functional. Resource-usage charts and advanced alerting are planned." />
6466
<div className="flex justify-between items-center">
6567
<div>
6668
<h1 className="text-3xl font-bold tracking-tight text-white flex items-center gap-3">

apps/web/src/app/dashboard/logs/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import { useState } from 'react';
4+
import { PageStatusBanner } from '@/components/PageStatusBanner';
45
import { Badge, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@borg/ui";
56
import { Activity, Trash2, Search, RefreshCcw, BarChart3, AlertTriangle, CheckCircle2, Clock } from "lucide-react";
67
import { trpc } from '@/utils/trpc';
@@ -62,6 +63,7 @@ export default function LogsDashboard() {
6263

6364
return (
6465
<div className="p-8 space-y-8 h-full overflow-y-auto w-full max-w-[1600px] mx-auto">
66+
<PageStatusBanner status="beta" message="Execution logs are filterable and searchable. Log streaming and advanced querying are planned." />
6567
<div className="flex justify-between items-center">
6668
<div>
6769
<h1 className="text-3xl font-bold tracking-tight text-white flex items-center gap-3">
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
"use client";
2+
3+
import Link from 'next/link';
4+
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@borg/ui";
5+
import { Badge } from "@borg/ui";
6+
import { Button } from "@borg/ui";
7+
import { Activity, FileText, Shield, Server, Clock, CheckCircle2, AlertCircle, Loader2 } from "lucide-react";
8+
import { trpc } from '@/utils/trpc';
9+
import { PageStatusBanner } from '@/components/PageStatusBanner';
10+
11+
function formatUptime(seconds: number): string {
12+
if (seconds < 60) return `${Math.floor(seconds)}s`;
13+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`;
14+
const h = Math.floor(seconds / 3600);
15+
const m = Math.floor((seconds % 3600) / 60);
16+
return `${h}h ${m}m`;
17+
}
18+
19+
export default function SystemOverview() {
20+
const { data: startupStatus, isLoading } = trpc.startupStatus.useQuery(undefined, { refetchInterval: 10000 });
21+
22+
const checks = startupStatus?.checks;
23+
const subsystems: { label: string; ready: boolean | undefined }[] = [
24+
{ label: 'MCP Aggregator', ready: checks?.mcpAggregator?.liveReady },
25+
{ label: 'Config Sync', ready: checks?.configSync?.ready },
26+
{ label: 'Memory', ready: checks?.memory?.ready },
27+
{ label: 'Browser', ready: checks?.browser?.ready },
28+
{ label: 'Session Supervisor', ready: checks?.sessionSupervisor?.ready },
29+
{ label: 'Extension Bridge', ready: checks?.extensionBridge?.ready },
30+
{ label: 'Execution Env', ready: checks?.executionEnvironment?.ready },
31+
];
32+
33+
const readyCount = subsystems.filter((s) => s.ready === true).length;
34+
const overallReady = startupStatus?.ready === true;
35+
36+
return (
37+
<div className="p-8 space-y-8 h-full overflow-y-auto w-full max-w-[1200px] mx-auto">
38+
<PageStatusBanner status="beta" message="System overview aggregates subsystem readiness from the startup status contract." />
39+
40+
<div className="flex justify-between items-center">
41+
<div>
42+
<h1 className="text-3xl font-bold tracking-tight text-white flex items-center gap-3">
43+
<Server className="h-8 w-8 text-violet-500" />
44+
System Overview
45+
</h1>
46+
<p className="text-zinc-500 mt-1">
47+
Borg operator console — subsystem health, uptime, and quick navigation.
48+
</p>
49+
</div>
50+
{startupStatus && (
51+
<Badge className={overallReady ? 'bg-green-900 text-green-300' : 'bg-amber-900 text-amber-300'}>
52+
{overallReady ? 'Ready' : 'Pending'}
53+
</Badge>
54+
)}
55+
</div>
56+
57+
{/* Stats row */}
58+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
59+
<Card className="bg-zinc-900 border-zinc-800">
60+
<CardContent className="pt-6 flex items-center gap-4">
61+
<Clock className="h-8 w-8 text-zinc-400 shrink-0" />
62+
<div>
63+
<p className="text-xs text-zinc-500 uppercase tracking-wider">Uptime</p>
64+
<p className="text-2xl font-bold text-white">
65+
{isLoading ? '—' : formatUptime(startupStatus?.uptime ?? 0)}
66+
</p>
67+
</div>
68+
</CardContent>
69+
</Card>
70+
<Card className="bg-zinc-900 border-zinc-800">
71+
<CardContent className="pt-6 flex items-center gap-4">
72+
<Activity className="h-8 w-8 text-zinc-400 shrink-0" />
73+
<div>
74+
<p className="text-xs text-zinc-500 uppercase tracking-wider">Subsystems</p>
75+
<p className="text-2xl font-bold text-white">
76+
{isLoading ? '—' : `${readyCount}/${subsystems.length}`}
77+
</p>
78+
</div>
79+
</CardContent>
80+
</Card>
81+
<Card className="bg-zinc-900 border-zinc-800">
82+
<CardContent className="pt-6 flex items-center gap-4">
83+
<Server className="h-8 w-8 text-zinc-400 shrink-0" />
84+
<div>
85+
<p className="text-xs text-zinc-500 uppercase tracking-wider">Version</p>
86+
<p className="text-2xl font-bold text-white">
87+
{isLoading ? '—' : (startupStatus?.runtime?.version ?? '—')}
88+
</p>
89+
</div>
90+
</CardContent>
91+
</Card>
92+
</div>
93+
94+
{/* Subsystem checks */}
95+
<Card className="bg-zinc-900 border-zinc-800">
96+
<CardHeader>
97+
<CardTitle className="text-white text-lg">Subsystem Status</CardTitle>
98+
<CardDescription>Real-time readiness from the startup contract.</CardDescription>
99+
</CardHeader>
100+
<CardContent>
101+
{isLoading ? (
102+
<div className="flex items-center gap-2 text-zinc-500">
103+
<Loader2 className="h-4 w-4 animate-spin" />
104+
<span>Loading...</span>
105+
</div>
106+
) : (
107+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
108+
{subsystems.map((sub) => (
109+
<div key={sub.label} className="flex items-center gap-3 p-3 rounded-lg bg-zinc-800/50">
110+
{sub.ready === true ? (
111+
<CheckCircle2 className="h-4 w-4 text-green-500 shrink-0" />
112+
) : (
113+
<AlertCircle className="h-4 w-4 text-amber-400 shrink-0" />
114+
)}
115+
<span className="text-sm text-zinc-300">{sub.label}</span>
116+
<Badge className={`ml-auto text-xs ${sub.ready ? 'bg-green-900/50 text-green-400' : 'bg-amber-900/50 text-amber-400'}`}>
117+
{sub.ready ? 'Ready' : 'Pending'}
118+
</Badge>
119+
</div>
120+
))}
121+
</div>
122+
)}
123+
</CardContent>
124+
</Card>
125+
126+
{/* Blocking reasons */}
127+
{!overallReady && startupStatus?.blockingReasons && startupStatus.blockingReasons.length > 0 && (
128+
<Card className="bg-amber-950/20 border-amber-800/40">
129+
<CardHeader>
130+
<CardTitle className="text-amber-300 text-base flex items-center gap-2">
131+
<AlertCircle className="h-4 w-4" /> Boot Pending
132+
</CardTitle>
133+
</CardHeader>
134+
<CardContent>
135+
<ul className="space-y-1">
136+
{startupStatus.blockingReasons.map((reason) => (
137+
<li key={reason.code} className="text-sm text-amber-200/80">
138+
<span className="font-mono text-amber-400 mr-2">[{reason.code}]</span>
139+
{reason.detail}
140+
</li>
141+
))}
142+
</ul>
143+
</CardContent>
144+
</Card>
145+
)}
146+
147+
{/* Navigation cards */}
148+
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
149+
<Card className="bg-zinc-900 border-zinc-800 hover:border-zinc-600 transition-colors">
150+
<CardHeader className="pb-2">
151+
<CardTitle className="text-white text-base flex items-center gap-2">
152+
<Activity className="h-4 w-4 text-green-500" /> Health
153+
</CardTitle>
154+
<CardDescription>Server health, MCP servers, crash rates.</CardDescription>
155+
</CardHeader>
156+
<CardContent>
157+
<Button asChild variant="outline" size="sm" className="border-zinc-700 hover:bg-zinc-800">
158+
<Link href="/dashboard/health">Open Health</Link>
159+
</Button>
160+
</CardContent>
161+
</Card>
162+
<Card className="bg-zinc-900 border-zinc-800 hover:border-zinc-600 transition-colors">
163+
<CardHeader className="pb-2">
164+
<CardTitle className="text-white text-base flex items-center gap-2">
165+
<FileText className="h-4 w-4 text-cyan-500" /> Logs
166+
</CardTitle>
167+
<CardDescription>Execution logs, tool calls, error rates.</CardDescription>
168+
</CardHeader>
169+
<CardContent>
170+
<Button asChild variant="outline" size="sm" className="border-zinc-700 hover:bg-zinc-800">
171+
<Link href="/dashboard/logs">Open Logs</Link>
172+
</Button>
173+
</CardContent>
174+
</Card>
175+
<Card className="bg-zinc-900 border-zinc-800 hover:border-zinc-600 transition-colors">
176+
<CardHeader className="pb-2">
177+
<CardTitle className="text-white text-base flex items-center gap-2">
178+
<Shield className="h-4 w-4 text-indigo-500" /> Audit
179+
</CardTitle>
180+
<CardDescription>System audit trail for operator review.</CardDescription>
181+
</CardHeader>
182+
<CardContent>
183+
<Button asChild variant="outline" size="sm" className="border-zinc-700 hover:bg-zinc-800">
184+
<Link href="/dashboard/audit">Open Audit</Link>
185+
</Button>
186+
</CardContent>
187+
</Card>
188+
</div>
189+
</div>
190+
);
191+
}

0 commit comments

Comments
 (0)