// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0 // Exercises the browser-side Workbench performance probe without sending network traffic. import assert from "node:assert/strict"; import test from "node:test"; import type { ChatMessage, TraceEvent } from "../src/types/index.ts"; import { acknowledgeWorkbenchVisible, drainWorkbenchPerformanceEventsForTest, failWorkbenchSubmitJourney, finishWorkbenchOpenFullLoad, finishWorkbenchSessionSwitchFullLoad, markWorkbenchSubmitApiAccepted, markWorkbenchTraceEventsReceived, markWorkbenchTraceProjected, recordWorkbenchApiRequest, recordWorkbenchRuntimeDiagnostic, resetWorkbenchPerformanceForTest, startWorkbenchOpenJourney, startWorkbenchSessionSwitch, startWorkbenchSubmitJourney } from "../src/utils/workbench-performance.ts"; test("Workbench performance probe records journeys and strips high-cardinality identifiers", () => { resetWorkbenchPerformanceForTest(); startWorkbenchOpenJourney({ route: "/workbench/sessions/ses_secret", cache: "cold", authState: "warm" }); acknowledgeWorkbenchVisible({ messages: [], activeSessionId: "ses_secret", detailLoading: false }); finishWorkbenchOpenFullLoad("ok"); startWorkbenchSessionSwitch({ sessionId: "ses_secret", source: "rail", targetState: "running", cache: "warm" }); acknowledgeWorkbenchVisible({ messages: [userMessage("ses_secret")], activeSessionId: "ses_secret", detailLoading: false }); finishWorkbenchSessionSwitchFullLoad("ses_secret", "ok"); const wallBase = Date.now(); const createdAt = new Date(wallBase - 300).toISOString(); const appendedAt = new Date(wallBase - 220).toISOString(); const serverSentAt = new Date(wallBase - 120).toISOString(); startWorkbenchSubmitJourney({ traceId: "trc_secret", sessionId: "ses_secret", entry: "existing", backend: "codex", transport: "sse" }); markWorkbenchSubmitApiAccepted("trc_secret"); markWorkbenchTraceEventsReceived({ traceId: "trc_secret", transport: "sse", serverSentAt, traceSeq: 42, events: [{ type: "assistant_message", eventType: "assistant", backend: "agentrun-v01/codex", createdAt, appendedAt, ts: createdAt, sourceSeq: 42 } as TraceEvent] }); markWorkbenchTraceProjected("trc_secret"); acknowledgeWorkbenchVisible({ messages: [agentMessage("ses_secret", "trc_secret")], activeSessionId: "ses_secret", detailLoading: false }); const events = drainWorkbenchPerformanceEventsForTest(); const payload = JSON.stringify(events); assert.ok(events.some((event) => event.kind === "workbench_journey" && event.journey === "workbench_open_first_visible")); assert.ok(events.some((event) => event.kind === "workbench_journey" && event.journey === "session_switch_first_visible")); assert.ok(events.some((event) => event.kind === "workbench_journey" && event.journey === "submit_to_first_visible" && event.backend === "agentrun-v01/codex")); assert.ok(events.some((event) => event.kind === "workbench_event_phase" && event.phase === "created_to_append" && event.eventType === "assistant")); assert.ok(events.some((event) => event.kind === "workbench_event_phase" && event.phase === "append_to_sse" && event.eventType === "assistant")); assert.ok(events.some((event) => event.kind === "workbench_event_phase" && event.phase === "sse_to_receive" && event.eventType === "assistant")); assert.ok(events.some((event) => event.kind === "workbench_event_phase" && event.phase === "receive_to_project" && event.eventType === "assistant")); assert.ok(events.some((event) => event.kind === "workbench_event_phase" && event.phase === "project_to_paint" && event.eventType === "assistant")); assert.ok(events.some((event) => event.kind === "workbench_backend_event_visible" && event.eventType === "assistant")); assert.doesNotMatch(payload, /trc_secret|ses_secret|sessionId|runId|commandId/u); }); test("Workbench submit failures do not pollute first visible output SLI", () => { resetWorkbenchPerformanceForTest(); startWorkbenchSubmitJourney({ traceId: "trc_network", sessionId: "ses_secret", entry: "new", backend: "codex", transport: "sse" }); failWorkbenchSubmitJourney("trc_network", "network"); const events = drainWorkbenchPerformanceEventsForTest(); assert.equal(events.some((event) => event.kind === "workbench_journey" && event.journey === "submit_to_first_visible"), false); assert.ok(events.some((event) => event.kind === "workbench_journey" && event.journey === "submit_to_failure" && event.outcome === "network")); }); test("Workbench runtime diagnostics expose root cause labels without raw identifiers", () => { resetWorkbenchPerformanceForTest(); const restoreBrowserRuntime = installBrowserProbeRuntime(); try { recordWorkbenchRuntimeDiagnostic({ module: "workbench-stream-transport", sessionId: "ses_secret", traceId: "trc_secret", outcome: "network", diagnostic: { code: "workbench_sse_recovery", rootCause: "eventsource-error", recoveryAction: "refresh-session-messages,schedule-session-list", transportState: "degraded", scopedKey: "workbench.realtime|ses_secret|trc_secret" } }); const events = drainWorkbenchPerformanceEventsForTest(); const event = events.find((item) => item.kind === "workbench_ui_event" && item.eventType === "runtime_diagnostic"); assert.equal(event?.module, "workbench-stream-transport"); assert.equal(event?.diagnosticCode, "workbench_sse_recovery"); assert.equal(event?.rootCause, "eventsource-error"); assert.equal(event?.recoveryAction, "refresh-session-messages,schedule-session-list"); assert.equal(event?.transportState, "degraded"); assert.match(event?.scopedKey ?? "", /^scope_[0-9a-f]{8}$/u); assert.doesNotMatch(JSON.stringify(events), /ses_secret|trc_secret|workbench\.realtime\|/u); } finally { resetWorkbenchPerformanceForTest(); restoreBrowserRuntime(); } }); test("Workbench SSE flush diagnostics expose bounded queue cost fields", () => { resetWorkbenchPerformanceForTest(); const restoreBrowserRuntime = installBrowserProbeRuntime(); try { recordWorkbenchRuntimeDiagnostic({ module: "workbench-events", sessionId: "ses_secret", traceId: "trc_secret", outcome: "ok", diagnostic: { code: "workbench_sse_flush", reason: "yield", eventCount: 5, deliveredCount: 4, dropped: 1, chunkCount: 2, replacedByKey: 1, flushDurationMs: 17, maxItemsPerChunk: 4, maxChunkMs: 8 } }); const events = drainWorkbenchPerformanceEventsForTest(); const event = events.find((item) => item.kind === "workbench_ui_event" && item.eventType === "runtime_diagnostic"); assert.equal(event?.state, "sample"); assert.equal(event?.reason, "sse_flush"); assert.equal(event?.valueMs, 17); assert.equal(event?.eventCount, 5); assert.equal(event?.deliveredCount, 4); assert.equal(event?.droppedCount, 1); assert.equal(event?.chunkCount, 2); assert.equal(event?.replacedByKey, 1); assert.equal(event?.flushDurationMs, 17); assert.equal(event?.maxItemsPerChunk, 4); assert.equal(event?.maxChunkMs, 8); assert.doesNotMatch(JSON.stringify(events), /ses_secret|trc_secret/u); } finally { resetWorkbenchPerformanceForTest(); restoreBrowserRuntime(); } }); test("Workbench SSE flush diagnostics request prompt web-performance delivery", async () => { resetWorkbenchPerformanceForTest(); const timers: { delay: number; handler: () => void }[] = []; const posts: { url: string; body: string }[] = []; const restoreBrowserRuntime = installBrowserProbeRuntime({ setTimeout: (handler, timeout) => { timers.push({ delay: Number(timeout ?? 0), handler: () => invokeTimerHandler(handler) }); return timers.length; }, clearTimeout: () => undefined, fetch: (input, init) => { posts.push({ url: String(input), body: String(init?.body ?? "") }); return Promise.resolve(new Response(null, { status: 204 })); } }); try { recordWorkbenchRuntimeDiagnostic({ module: "workbench-events", sessionId: "ses_secret", traceId: "trc_secret", outcome: "ok", diagnostic: { code: "workbench_sse_flush", eventCount: 3, deliveredCount: 2, dropped: 1, chunkCount: 2, flushDurationMs: 9, maxItemsPerChunk: 4, maxChunkMs: 8 } }); const priorityTimer = timers.find((timer) => timer.delay === 250); assert.ok(priorityTimer); priorityTimer.handler(); await Promise.resolve(); assert.equal(posts.length, 1); assert.equal(posts[0]?.url, "/v1/web-performance"); assert.match(posts[0]?.body ?? "", /workbench_sse_flush/u); assert.match(posts[0]?.body ?? "", /"eventCount":3/u); assert.match(posts[0]?.body ?? "", /"maxChunkMs":8/u); assert.equal(drainWorkbenchPerformanceEventsForTest().length, 0); } finally { resetWorkbenchPerformanceForTest(); restoreBrowserRuntime(); } }); test("Workbench API timing enriches from ResourceTiming and clears the browser buffer", () => { resetWorkbenchPerformanceForTest(); let clearCount = 0; let configuredBufferSize = 0; const startedAtEpochMs = 1_000_000; const entries = [resourceTimingEntry({ name: "https://hwlab.example.test/v1/workbench/sessions/ses_secret/messages?limit=100", startTime: 10, duration: 42, responseStart: 37, responseEnd: 52 })]; const restoreBrowserRuntime = installBrowserProbeRuntime({ performance: { timeOrigin: startedAtEpochMs - 10, now: () => 123, getEntriesByType: (type: string) => type === "resource" ? entries : [], setResourceTimingBufferSize: (value: number) => { configuredBufferSize = value; }, clearResourceTimings: () => { clearCount += 1; entries.splice(0, entries.length); } } }); try { recordWorkbenchApiRequest({ route: "/v1/workbench/sessions/ses_secret/messages?limit=100", method: "GET", status: 200, outcome: "ok", startedAtEpochMs, endedAtEpochMs: startedAtEpochMs + 60 }); const events = drainWorkbenchPerformanceEventsForTest(); const event = events.find((item) => item.kind === "workbench_ui_event" && item.eventType === "api_request"); assert.equal(configuredBufferSize, 2000); assert.equal(clearCount, 1); assert.equal(entries.length, 0); assert.equal(event?.route, "/v1/workbench/sessions/:id/messages"); assert.equal(event?.resourceDurationMs, 42); assert.equal(event?.resourceRequestWaitMs, 15); assert.equal(event?.resourceResponseTransferMs, 15); } finally { resetWorkbenchPerformanceForTest(); restoreBrowserRuntime(); } }); test("Workbench API timing labels metadata-only session detail distinctly", () => { resetWorkbenchPerformanceForTest(); const restoreBrowserRuntime = installBrowserProbeRuntime(); try { const startedAtEpochMs = 1_000_000; recordWorkbenchApiRequest({ route: "/v1/workbench/sessions/ses_secret?includeMessages=false", method: "GET", status: 200, outcome: "ok", startedAtEpochMs, endedAtEpochMs: startedAtEpochMs + 12 }); const events = drainWorkbenchPerformanceEventsForTest(); const event = events.find((item) => item.kind === "workbench_ui_event" && item.eventType === "api_request"); assert.equal(event?.route, "/v1/workbench/sessions/:id?includeMessages=false"); } finally { resetWorkbenchPerformanceForTest(); restoreBrowserRuntime(); } }); test("Workbench session switch treats visible empty sessions as successful first paint", () => { resetWorkbenchPerformanceForTest(); startWorkbenchSessionSwitch({ sessionId: "ses_empty_visible", source: "rail", targetState: "empty", cache: "cold" }); acknowledgeWorkbenchVisible({ messages: [], activeSessionId: "ses_empty_visible", detailLoading: false }); const events = drainWorkbenchPerformanceEventsForTest(); const switchEvent = events.find((event) => event.kind === "workbench_journey" && event.journey === "session_switch_first_visible"); assert.equal(switchEvent?.outcome, "ok"); assert.equal(switchEvent?.targetState, "empty"); }); test("Workbench submit first visible waits for terminal final or tool output", () => { resetWorkbenchPerformanceForTest(); const wallBase = Date.now(); const createdAt = new Date(wallBase - 12_000).toISOString(); const appendedAt = new Date(wallBase - 11_500).toISOString(); const backendEvent = { label: "agentrun:run:createdAgentRun", backend: "agentrun-v01/codex", createdAt, appendedAt, message: "AgentRun created." } as TraceEvent; startWorkbenchSubmitJourney({ traceId: "trc_secret", sessionId: "ses_secret", entry: "existing", backend: "codex", transport: "sse" }); markWorkbenchTraceEventsReceived({ traceId: "trc_secret", transport: "rest_gap", events: [backendEvent] }); markWorkbenchTraceProjected("trc_secret"); acknowledgeWorkbenchVisible({ messages: [traceOnlyAgentMessage("ses_secret", "trc_secret", [backendEvent])], activeSessionId: "ses_secret", detailLoading: false }); const backendOnlyEvents = drainWorkbenchPerformanceEventsForTest(); assert.equal(backendOnlyEvents.some((event) => event.kind === "workbench_journey" && event.journey === "submit_to_first_visible"), false); assert.equal(backendOnlyEvents.some((event) => event.kind === "workbench_event_phase" && event.phase === "sse_to_receive"), false); assert.ok(backendOnlyEvents.some((event) => event.kind === "workbench_event_phase" && event.phase === "created_to_append" && event.eventType === "backend" && event.transport === "rest_gap")); assert.ok(backendOnlyEvents.some((event) => event.kind === "workbench_event_phase" && event.phase === "receive_to_project" && event.eventType === "backend" && event.transport === "rest_gap")); assert.equal(backendOnlyEvents.some((event) => event.kind === "workbench_backend_event_visible" && event.eventType === "backend" && event.outcome === "stale"), false); acknowledgeWorkbenchVisible({ messages: [traceOnlyAgentMessage("ses_secret", "trc_secret", [{ type: "assistant_message", status: "running", message: "progress only" } as TraceEvent])], activeSessionId: "ses_secret", detailLoading: false }); const progressEvents = drainWorkbenchPerformanceEventsForTest(); assert.equal(progressEvents.some((event) => event.kind === "workbench_journey" && event.journey === "submit_to_first_visible"), false); acknowledgeWorkbenchVisible({ messages: [traceOnlyAgentMessage("ses_secret", "trc_secret", [{ type: "tool_call", command: "pwd" } as TraceEvent])], activeSessionId: "ses_secret", detailLoading: false }); const toolEvents = drainWorkbenchPerformanceEventsForTest(); assert.ok(toolEvents.some((event) => event.kind === "workbench_journey" && event.journey === "submit_to_first_visible" && event.outcome === "ok")); }); function userMessage(sessionId: string): ChatMessage { return { id: "msg_user", role: "user", title: "User", text: "hello", status: "sent", createdAt: new Date().toISOString(), sessionId }; } function agentMessage(sessionId: string, traceId: string): ChatMessage { return { id: "msg_agent", role: "agent", title: "Code Agent", text: "visible", status: "completed", createdAt: new Date().toISOString(), sessionId, traceId, runnerTrace: { traceId, sessionId, eventCount: 1, events: [{ type: "assistant_message" }] } }; } function traceOnlyAgentMessage(sessionId: string, traceId: string, events: TraceEvent[]): ChatMessage { return { id: "msg_agent_trace", role: "agent", title: "Code Agent", text: "", status: "running", createdAt: new Date().toISOString(), sessionId, traceId, runnerTrace: { traceId, sessionId, eventCount: events.length, events } }; } function installBrowserProbeRuntime(options: { performance?: FakePerformance; setTimeout?: BrowserSetTimeout; clearTimeout?: BrowserClearTimeout; fetch?: BrowserFetch } = {}): () => void { const previousWindow = Object.getOwnPropertyDescriptor(globalThis, "window"); const previousDocument = Object.getOwnPropertyDescriptor(globalThis, "document"); const previousPerformance = Object.getOwnPropertyDescriptor(globalThis, "performance"); const previousFetch = Object.getOwnPropertyDescriptor(globalThis, "fetch"); Object.defineProperty(globalThis, "window", { configurable: true, value: { location: { href: "https://hwlab.example.test/workbench", pathname: "/workbench", hash: "" }, crypto: globalThis.crypto, addEventListener: () => undefined, setTimeout: options.setTimeout ?? globalThis.setTimeout, clearTimeout: options.clearTimeout ?? globalThis.clearTimeout } }); Object.defineProperty(globalThis, "document", { configurable: true, value: { visibilityState: "visible", addEventListener: () => undefined } }); if (options.performance) { Object.defineProperty(globalThis, "performance", { configurable: true, value: options.performance }); } if (options.fetch) { Object.defineProperty(globalThis, "fetch", { configurable: true, value: options.fetch }); } return () => { restoreGlobalProperty("window", previousWindow); restoreGlobalProperty("document", previousDocument); restoreGlobalProperty("performance", previousPerformance); restoreGlobalProperty("fetch", previousFetch); }; } type BrowserSetTimeout = (handler: TimerHandler, timeout?: number, ...arguments_: unknown[]) => number; type BrowserClearTimeout = (handle?: number) => void; type BrowserFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise; interface FakePerformance { timeOrigin: number; now: () => number; getEntriesByType: (type: string) => unknown[]; setResourceTimingBufferSize?: (value: number) => void; clearResourceTimings?: () => void; } function invokeTimerHandler(handler: TimerHandler): void { if (typeof handler === "function") { handler(); return; } throw new TypeError("string timer handlers are not supported by this test runtime"); } function resourceTimingEntry(input: { name: string; startTime: number; duration: number; responseStart: number; responseEnd: number }): PerformanceResourceTiming { return { name: input.name, startTime: input.startTime, duration: input.duration, fetchStart: input.startTime, requestStart: input.startTime + 12, responseStart: input.responseStart, responseEnd: input.responseEnd, transferSize: 1000, encodedBodySize: 800, decodedBodySize: 1600, nextHopProtocol: "h2", initiatorType: "fetch", serverTiming: [] } as unknown as PerformanceResourceTiming; } function restoreGlobalProperty(key: "window" | "document" | "performance" | "fetch", descriptor: PropertyDescriptor | undefined): void { if (descriptor) { Object.defineProperty(globalThis, key, descriptor); return; } Reflect.deleteProperty(globalThis, key); }