feat: scaffold central monitor workbench

This commit is contained in:
AgentRun Codex
2026-07-13 03:18:43 +00:00
parent a7e2f42ea3
commit 6de541221e
8 changed files with 363 additions and 0 deletions
@@ -106,6 +106,48 @@ test("run detail returns normalized locator metadata without artifact bytes", as
assert.equal(response.valuesRedacted, true);
});
test("monitor workspace composes runs while runtime facts remain unavailable without metadata", async () => {
const target = service();
await target.ingest({ ...input("run-ok", "2026-07-12T11:00:00.000Z"), findings: [{ id: "finding-a" }] });
await target.ingest({ ...input("run-failed", "2026-07-12T10:00:00.000Z"), status: "failed", findings: [{ id: "finding-b" }, { id: "finding-c" }] });
const response = await target.fetch(new Request("http://local/api/monitor/workspace?sentinelId=sentinel-a&node=NC01&lane=v03&status=failed&from=2026-07-12T09:00:00.000Z&limit=20"));
const body = await response.json() as any;
assert.equal(body.selection.defaultRunId, "run-failed");
assert.equal(body.runs.items[0].findingCount, 2);
assert.equal(body.health.state, "degraded");
assert.equal(body.freshness.state, "unavailable");
assert.equal(body.cadence.state, "unavailable");
assert.equal(body.schemaVersion, undefined);
assert.equal(body.capability, undefined);
});
test("monitor workspace projects cadence freshness and health only from runtime metadata", async () => {
const store = createInMemoryMonitorCentralStore();
const normalized = normalizeMonitorCentralIngest(input("run-metadata"));
await store.importSource({ manifestId: "monitor-workbench-runtime", sourceFingerprint: "sha256:runtime", rowCount: 1 }, [normalized], [
{ sentinelId: "sentinel-a", node: "NC01", lane: "v03", key: "monitor.health", value: { state: "healthy" }, updatedAt: "2026-07-12T10:01:00.000Z" },
{ sentinelId: "sentinel-a", node: "NC01", lane: "v03", key: "monitor.freshness", value: { state: "current", ageSeconds: 30 }, updatedAt: "2026-07-12T10:01:00.000Z" },
{ sentinelId: "sentinel-a", node: "NC01", lane: "v03", key: "monitor.cadence", value: { state: "available", label: "owning-yaml-projected" }, updatedAt: "2026-07-12T10:01:00.000Z" },
]);
const target = createMonitorCentralService({ store, pageSize: 50 });
const body = await (await target.fetch(new Request("http://local/api/monitor/workspace?sentinelId=sentinel-a&node=NC01&lane=v03"))).json() as any;
assert.equal(body.health.state, "healthy");
assert.equal(body.freshness.ageSeconds, 30);
assert.equal(body.cadence.label, "owning-yaml-projected");
assert.equal(body.cadence.sourceKey, "monitor.cadence");
});
test("monitor run detail projects stable ids without a client join", async () => {
const target = service();
await target.ingest({ ...input("run-workbench"), payload: { diagnostics: { state: "ready" } }, timeline: [{ id: "event-done", phase: "done" }], findings: [{ id: "finding-visible" }] });
const response = await target.fetch(new Request("http://local/api/monitor/runs/run-workbench?sentinelId=sentinel-a&node=NC01&lane=v03"));
const body = await response.json() as any;
assert.equal(body.timeline[0].id, "event-done");
assert.equal(body.findings[0].id, "finding-visible");
assert.equal(body.artifacts[0].id, "artifact-0");
assert.deepEqual(body.diagnostics, { state: "ready" });
});
test("configuration is explicit and missing environment projection fails structurally", async () => {
const target = service();
assert.equal((await target.health()).ok, true);
@@ -163,6 +163,9 @@ export function createMonitorCentralService(options: MonitorCentralServiceOption
return json(result, result.ok === true ? 200 : 503);
}
if (request.method === "GET" && url.pathname === "/api/overview") return json({ ok: true, overview: await store.overview(scopeFrom(url)), valuesRedacted: true });
if (request.method === "GET" && url.pathname === "/api/monitor/workspace") return json(await monitorWorkspace(store, url, pageSize));
const monitorRunMatch = request.method === "GET" ? url.pathname.match(/^\/api\/monitor\/runs\/([^/]+)$/u) : null;
if (monitorRunMatch) return monitorRunDetail(store, url, decodeURIComponent(monitorRunMatch[1]));
if (request.method === "GET" && url.pathname === "/api/runs") return json(runPage(await store.runs(scopeFrom(url), runQueryOptions(url, pageSize))));
if (request.method === "GET" && url.pathname === "/api/findings") return json(findingPage(await store.findings(scopeFrom(url), findingQueryOptions(url, pageSize))));
if (request.method === "GET" && url.pathname.startsWith("/api/runs/")) {
@@ -184,6 +187,66 @@ export function createMonitorCentralService(options: MonitorCentralServiceOption
return { ingest, health, fetch };
}
async function monitorWorkspace(store: MonitorCentralStore, url: URL, pageSize: number): Promise<Record<string, unknown>> {
const scope = scopeFrom(url);
const options = runQueryOptions(url, pageSize);
const status = optional(url, "status");
const from = optionalDate(url, "from");
const to = optionalDate(url, "to");
const source = await store.runs(scope, { ...options, limit: pageSize });
const items = source.filter((run) => (!status || run.status === status) && (!from || run.updatedAt >= from) && (!to || run.updatedAt <= to));
const findings = await store.findings(scope, { limit: pageSize, cursor: null });
const findingCounts = new Map<string, number>();
for (const item of findings) findingCounts.set(String(item.runId), (findingCounts.get(String(item.runId)) ?? 0) + 1);
const runs = items.map((run) => ({ ...run, findingCount: findingCounts.get(run.runId) ?? 0 }));
const runtime = await monitorRuntimeProjection(store, scope);
return {
ok: true,
query: { sentinelId: scope.sentinelId ?? null, node: scope.node ?? null, lane: scope.lane ?? null, status: status ?? null, from: from ?? null, to: to ?? null },
health: runtime.health,
freshness: runtime.freshness,
cadence: runtime.cadence,
summary: { totalRuns: runs.length, statusCounts: countBy(runs, (run) => run.status), findingCount: runs.reduce((total, run) => total + run.findingCount, 0) },
runs: { items: runs, nextCursor: source.length === pageSize && source.at(-1) ? cursor(source.at(-1)) : null },
selection: { defaultRunId: runs[0]?.runId ?? null },
valuesRedacted: true,
};
}
async function monitorRuntimeProjection(store: MonitorCentralStore, scope: MonitorScope): Promise<Record<string, Record<string, unknown>>> {
if (!scope.sentinelId || !scope.node || !scope.lane) return unavailableRuntime("full_scope_required");
const metadata = await store.runtimeMetadata(scope as Required<MonitorScope>);
const values = new Map(metadata.map((item) => [item.key, item]));
const health = projectedRuntimeValue(values, ["monitor.health", "scheduler.health"]);
const freshness = projectedRuntimeValue(values, ["monitor.freshness", "scheduler.freshness"]);
const cadence = projectedRuntimeValue(values, ["monitor.cadence", "scheduler.cadence"]);
return {
health: health ?? { state: "degraded", reason: "runtime_metadata_unavailable" },
freshness: freshness ?? { state: "unavailable", reason: "runtime_metadata_unavailable" },
cadence: cadence ?? { state: "unavailable", reason: "runtime_metadata_unavailable" },
};
}
function projectedRuntimeValue(values: ReadonlyMap<string, MonitorRuntimeMetadata>, keys: readonly string[]): Record<string, unknown> | null {
for (const key of keys) {
const item = values.get(key);
if (item) return { ...recordOrEmpty(item.value), sourceKey: item.key, updatedAt: item.updatedAt };
}
return null;
}
function unavailableRuntime(reason: string): Record<string, Record<string, unknown>> {
return { health: { state: "degraded", reason }, freshness: { state: "unavailable", reason }, cadence: { state: "unavailable", reason } };
}
async function monitorRunDetail(store: MonitorCentralStore, url: URL, runId: string): Promise<Response> {
const scope = requireFullScope(scopeFrom(url));
const run = await store.run(scope, runId);
if (!run) return json({ ok: false, error: { code: "run_not_found" }, valuesRedacted: true }, 404);
const payload = recordOrEmpty(run.payload);
return json({ ok: true, run: { sentinelId: run.sentinelId, node: run.node, lane: run.lane, runId: run.runId, updatedAt: run.updatedAt, status: run.status, payloadHash: run.payloadHash }, timeline: arrayOrEmpty(run.timeline).map((event, index) => ({ id: objectId(event, `event-${index}`), event })), findings: arrayOrEmpty(run.findings).map((finding, index) => ({ id: objectId(finding, `finding-${index}`), finding })), diagnostics: recordOrEmpty(payload.diagnostics), artifacts: arrayOrEmpty(run.artifactLocators).map((artifact, index) => ({ id: `artifact-${index}`, ...recordOrEmpty(artifact) })), valuesRedacted: true });
}
export function createPostgresMonitorCentralStore(databaseUrl: string, options: PostgresMonitorCentralStoreOptions): MonitorCentralStore {
const sql = postgres(databaseUrl, { max: requirePositiveInteger(options.poolMax, "monitor_central_pg_pool_max_missing"), idle_timeout: requirePositiveInteger(options.idleTimeoutSeconds, "monitor_central_pg_idle_timeout_missing") });
return {
@@ -423,6 +486,7 @@ async function requestRecord(request: Request): Promise<Record<string, unknown>>
function scopeFrom(url: URL): MonitorScope { return { sentinelId: optional(url, "sentinelId"), node: optional(url, "node"), lane: optional(url, "lane") }; }
function requireFullScope(scope: MonitorScope): Required<MonitorScope> { if (!scope.sentinelId || !scope.node || !scope.lane) throw Object.assign(invalid("run detail requires sentinelId, node, and lane"), { code: "scope_required" }); return scope as Required<MonitorScope>; }
function optional(url: URL, name: string): string | undefined { return url.searchParams.get(name) || undefined; }
function optionalDate(url: URL, name: string): string | undefined { const value = optional(url, name); if (!value) return undefined; const timestamp = Date.parse(value); if (!Number.isFinite(timestamp)) throw invalid(`${name} must be an ISO timestamp`); return new Date(timestamp).toISOString(); }
function boundedLimit(value: string | null, pageSize: number): number { const parsed = Number(value ?? pageSize); return Number.isInteger(parsed) && parsed > 0 ? Math.min(parsed, pageSize) : pageSize; }
function runQueryOptions(url: URL, pageSize: number): RunQueryOptions { return { limit: boundedLimit(url.searchParams.get("limit"), pageSize), cursor: parseRunCursor(url.searchParams.get("cursor")) }; }
function findingQueryOptions(url: URL, pageSize: number): FindingQueryOptions { return { limit: boundedLimit(url.searchParams.get("limit"), pageSize), cursor: parseFindingCursor(url.searchParams.get("cursor")) }; }
@@ -432,6 +496,10 @@ function parseCursor(value: string | null, finding: boolean): RunCursor | Findin
function runPage(items: readonly MonitorRun[]): Record<string, unknown> { const last = items.at(-1); return { ok: true, items, nextCursor: last ? cursor(last) : null, valuesRedacted: true }; }
function findingPage(items: readonly Record<string, unknown>[]): Record<string, unknown> { const last = items.at(-1); return { ok: true, items, nextCursor: last ? cursor({ updatedAt: last.updatedAt, runId: last.runId, findingIndex: last.findingIndex }) : null, valuesRedacted: true }; }
function cursor(value: unknown): string { return Buffer.from(JSON.stringify(value)).toString("base64url"); }
function countBy<T>(items: readonly T[], key: (item: T) => string): Record<string, number> { const counts: Record<string, number> = {}; for (const item of items) counts[key(item)] = (counts[key(item)] ?? 0) + 1; return counts; }
function recordOrEmpty(value: unknown): Record<string, unknown> { return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {}; }
function arrayOrEmpty(value: unknown): readonly unknown[] { return Array.isArray(value) ? value : []; }
function objectId(value: unknown, fallback: string): string { const record = recordOrEmpty(value); return String(record.id ?? record.errorId ?? fallback); }
function json(body: Record<string, unknown>, status = 200): Response { return Response.json(body, { status }); }
function httpStatus(error: unknown): number { return (error as { code?: string })?.code === "payload_hash_conflict" ? 409 : error instanceof MonitorCentralConfigurationError ? 503 : 400; }
export function structuredMonitorCentralError(error: unknown): Record<string, unknown> { return error instanceof MonitorCentralConfigurationError ? error.toJSON().error as Record<string, unknown> : { code: (error as { code?: string })?.code ?? "invalid_request", message: errorMessage(error) }; }
@@ -0,0 +1,18 @@
import assert from "node:assert/strict";
import { test } from "bun:test";
import { monitorRouteHref, readMonitorRoute, withMonitorRoute } from "../assets/web-probe-sentinel-monitor-workbench/state/monitor-route.js";
Object.assign(globalThis, { window: { location: { origin: "https://monitor.example.test" } } });
test("monitor route roundtrips run detail tab and filters", () => {
const route = readMonitorRoute("https://monitor.example.test/monitor/runs/run-1/findings/finding-2?sentinelId=s1&node=NC01&lane=v03&status=failed&tab=findings");
assert.equal(route.kind, "detail");
assert.equal(route.detailType, "finding");
assert.equal(monitorRouteHref(route), "/monitor/runs/run-1/findings/finding-2?sentinelId=s1&node=NC01&lane=v03&status=failed&tab=findings");
});
test("monitor route rejects unsupported paths and filter changes clear selection", () => {
assert.deepEqual(readMonitorRoute("https://monitor.example.test/dashboard"), { kind: "invalid", reason: "unsupported_path" });
const next = withMonitorRoute(readMonitorRoute("https://monitor.example.test/monitor/runs/run-1"), { kind: "workspace", runId: "", filters: { status: "failed" } });
assert.equal(monitorRouteHref(next), "/monitor?status=failed");
});