// SPEC: PJ2026-01040111 long-running Workbench observation. // Responsibility: Analyzer Workbench session invariant and controlled-navigation root-cause source fragment. export function nodeWebObserveAnalyzerSessionSource(): string { return String.raw`function buildSessionInvariantFindings(control, manifest = {}) { const findings = []; for (const row of control || []) { if (row?.type !== "assertSessionInvariant" || row?.phase !== "completed") continue; const detail = objectValue(row.detail); const messageOrder = objectValue(detail.messageOrder); if (messageOrder.userClustered !== true) continue; const afterRound = numberOrNull(detail.afterRound ?? row.input?.afterRound); const consecutiveUserMessageCount = numberOrNull(messageOrder.consecutiveUserMessageCount) ?? 0; const sentinelRange = stringOrNull(messageOrder.sentinelRange) ?? stringOrNull(detail.expectedSentinelRange); const traceIds = arrayStrings(messageOrder.traceIds).slice(0, 12); const findingId = stringOrNull(detail.findingId) ?? "workbench-message-order-user-clustered-after-navigation"; const severity = stringOrNull(detail.severity) ?? "amber"; const rootCause = "session_message_role_clustered"; const rootCauseStatus = "confirmed-from-controlled-refresh-dom;check-otel-session_messages_read-role-sequence"; const rootCauseConfidence = "medium"; const nextAction = "Use OTel session_messages_read/session detail for the same canarySessionId and traceIds. Compare roleSequencePrefix and adjacentSameRoleCount; if the read model is already clustered, fix Workbench projection/read-model timeline ordering before changing renderer code."; findings.push({ id: findingId, severity, summary: "message-order root cause visible: controlled refresh/switch-back afterRound=" + (afterRound ?? "-") + " left consecutive user message cards without interleaved assistant/code-agent terminal cards" + (sentinelRange ? " (" + sentinelRange + ")" : ""), rootCause, rootCauseStatus, rootCauseConfidence, nextAction, count: Math.max(1, consecutiveUserMessageCount), blocking: detail.blocking === true ? true : false, afterRound, canarySessionId: stringOrNull(detail.canarySessionId), routeSessionId: stringOrNull(detail.routeSessionId), activeSessionId: stringOrNull(detail.activeSessionId), consecutiveUserMessageCount, sentinelRange, sampleSeq: numberOrNull(detail.sampleSeq), traceIds, pageRole: stringOrNull(detail.pageRole) ?? "control", pageId: stringOrNull(detail.pageId), observerId: stringOrNull(manifest.jobId), stateDir: stringOrNull(manifest.stateDir), commandId: stringOrNull(row.commandId), commandTs: stringOrNull(row.ts), evidence: { afterRound, consecutiveUserMessageCount, sentinelRange, sampleSeq: numberOrNull(detail.sampleSeq), traceIds, canarySessionId: stringOrNull(detail.canarySessionId), routeSessionId: stringOrNull(detail.routeSessionId), activeSessionId: stringOrNull(detail.activeSessionId), valuesRedacted: true, }, messageOrder: { sequence: Array.isArray(messageOrder.sequence) ? messageOrder.sequence.slice(-20) : [], clusters: Array.isArray(messageOrder.clusters) ? messageOrder.clusters.slice(0, 8) : [], valuesRedacted: true, }, valuesRedacted: true, }); } return findings; } function buildControlledNavigationRootCauseFindings(control, manifest = {}) { const commands = []; for (const row of control || []) { if (row?.phase !== "completed") continue; if (row?.type !== "refreshCurrentSession" && row?.type !== "switchAwayAndBack") continue; const detail = objectValue(row.detail); const navigation = objectValue(detail.navigation); const readiness = objectValue(navigation.readiness); const snapshot = objectValue(readiness.snapshot); const pageProvenance = objectValue(navigation.pageProvenance); const blankShell = snapshot.workbenchShellVisible === false && snapshot.sessionRailPresent === false && snapshot.commandInputPresent === false && snapshot.bodyTextHash === "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; const shellOrComposerMissing = snapshot.workbenchShellVisible === false || snapshot.sessionRailPresent === false || snapshot.commandInputPresent === false; const degraded = navigation.degraded === true || readiness.ok === false || detail.routeOk === false || blankShell || (detail.activeOk === false && shellOrComposerMissing) || (detail.composerReady === false && snapshot.commandInputPresent === false); if (!degraded) continue; const rootCause = stringOrNull(navigation.degradedReason) ?? (readiness.ok === false ? stringOrNull(readiness.reason) : null) ?? (detail.routeOk === false ? "route-session-not-hydrated" : null) ?? (blankShell ? "workbench-blank-shell-after-navigation" : null) ?? (detail.activeOk === false && shellOrComposerMissing ? "active-session-not-hydrated" : null) ?? (detail.composerReady === false && snapshot.commandInputPresent === false ? "composer-not-ready" : null) ?? "controlled-navigation-degraded"; commands.push({ commandId: stringOrNull(row.commandId), type: stringOrNull(row.type), commandTs: stringOrNull(row.ts), afterRound: numberOrNull(detail.afterRound ?? row.input?.afterRound), rootCause, blocking: true, canarySessionId: stringOrNull(detail.canarySessionId), routeSessionId: stringOrNull(detail.routeSessionId), activeSessionId: stringOrNull(detail.activeSessionId), routeOk: detail.routeOk === true, activeOk: detail.activeOk === true, composerReady: detail.composerReady === true, navigation: { httpStatus: numberOrNull(navigation.httpStatus), degraded: navigation.degraded === true, degradedReason: stringOrNull(navigation.degradedReason), beforePath: urlPath(navigation.beforeUrl), afterPath: urlPath(navigation.afterUrl), valuesRedacted: true, }, readiness: { ok: readiness.ok === true, reason: stringOrNull(readiness.reason), durationMs: numberOrNull(readiness.durationMs), path: stringOrNull(snapshot.path), readyState: stringOrNull(snapshot.readyState), workbenchShellVisible: snapshot.workbenchShellVisible === true, sessionCreatePresent: snapshot.sessionCreatePresent === true, sessionRailPresent: snapshot.sessionRailPresent === true, commandInputPresent: snapshot.commandInputPresent === true, activeTabPresent: snapshot.activeTabPresent === true, loginVisible: snapshot.loginVisible === true, blankShell, bodyTextHash: stringOrNull(snapshot.bodyTextHash), valuesRedacted: true, }, pageProvenance: { pageLoadSeq: numberOrNull(pageProvenance.pageLoadSeq), reason: stringOrNull(pageProvenance.reason), observedAt: stringOrNull(pageProvenance.observedAt), urlPath: stringOrNull(pageProvenance.urlPath), documentReadyState: stringOrNull(pageProvenance.documentReadyState), timeOrigin: numberOrNull(pageProvenance.timeOrigin), httpStatus: numberOrNull(pageProvenance.httpStatus), assetFingerprint: stringOrNull(pageProvenance.assetFingerprint), scriptCount: numberOrNull(pageProvenance.scriptCount), stylesheetCount: numberOrNull(pageProvenance.stylesheetCount), scripts: arrayStrings(pageProvenance.scripts).slice(0, 8), stylesheets: arrayStrings(pageProvenance.stylesheets).slice(0, 8), valuesRedacted: true, }, observer: { ok: detail.observer?.ok === true, pageRole: stringOrNull(detail.observer?.pageRole), pageId: stringOrNull(detail.observer?.pageId), changed: detail.observer?.changed === true, valuesRedacted: true, }, observerId: stringOrNull(manifest.jobId), stateDir: stringOrNull(manifest.stateDir), valuesRedacted: true, }); } if (commands.length === 0) return []; return [{ id: "workbench-controlled-navigation-degraded-root-cause", severity: "red", summary: "controlled Workbench refresh/switch completed degraded; route may be correct but app shell, active session, or composer was not ready, so later Code Agent turns cannot continue", count: commands.length, blocking: true, rootCauses: Array.from(new Set(commands.map((item) => item.rootCause))).slice(0, 12), commands: commands.slice(0, 20), next: "Investigate the first degraded command, then correlate browser requestfailed/static asset failures and Workbench hydration state before changing Code Agent/provider logic.", valuesRedacted: true, }]; } function sessionInvariantNavigationWindows(control) { const started = new Map(); const windows = []; for (const row of control || []) { if (row?.type !== "switchAwayAndBack" && row?.type !== "refreshCurrentSession") continue; const commandId = stringOrNull(row.commandId) ?? String(row.seq ?? ""); if (row.phase === "started") { started.set(commandId, row); continue; } if (row.phase !== "completed") continue; const detail = objectValue(row.detail); const canarySessionId = stringOrNull(detail.canarySessionId); const alternateSessionId = stringOrNull(detail.alternateSessionId); const startRow = started.get(commandId); const startMs = timestampMs(startRow?.ts ?? row.ts); const endMs = timestampMs(row.ts); if (!canarySessionId || !Number.isFinite(startMs) || !Number.isFinite(endMs)) continue; windows.push({ commandId, afterRound: numberOrNull(detail.afterRound ?? row.input?.afterRound), startMs: Math.max(0, startMs - 1000), endMs: endMs + 5000, startAt: new Date(startMs).toISOString(), endAt: new Date(endMs).toISOString(), canarySessionId, alternateSessionId, routeOk: detail.routeOk === true, activeOk: detail.activeOk === true, valuesRedacted: true, }); } return windows; } function sessionInvariantCanarySessionIds(control) { const ids = new Set(); for (const row of control || []) { const detail = objectValue(row?.detail); if (row?.type === "newSession" && row?.phase === "completed") { const sessionId = stringOrNull(detail.sessionId) ?? stringOrNull(detail.result?.sessionId) ?? stringOrNull(detail.createSession?.createdSessionId); if (sessionId) ids.add(sessionId); } const canarySessionId = stringOrNull(detail.canarySessionId); if (canarySessionId) ids.add(canarySessionId); } return ids; } function sessionChangeSamplesOutsideControlledNavigation(samples, key, windows) { const canaryIds = new Set((windows || []).map((item) => item.canarySessionId).filter(Boolean)); if (canaryIds.size === 0) return samples.filter((item) => item?.[key]); return (samples || []).filter((sample) => { const value = stringOrNull(sample?.[key]); if (!value) return false; if (canaryIds.has(value)) return false; return !sampleInControlledNavigationWindow(sample, windows); }); } function sampleInControlledNavigationWindow(sample, windows) { const ms = timestampMs(sample?.ts); if (!Number.isFinite(ms)) return false; return (windows || []).some((window) => ms >= window.startMs && ms <= window.endMs); } function sampleRefInControlledNavigationSessionWindow(sample, windows) { const ms = timestampMs(sample?.ts); if (!Number.isFinite(ms)) return false; if (!sampleRefMatchesControlledNavigationSession(sample, windows)) return false; return (windows || []).some((window) => ms >= window.startMs && ms <= window.endMs); } function sampleRefMatchesControlledNavigationSession(sample, windows) { const routeSessionId = stringOrNull(sample?.routeSessionId); const activeSessionId = stringOrNull(sample?.activeSessionId); return (windows || []).some((window) => { const expected = [window.canarySessionId, window.alternateSessionId].filter(Boolean); return expected.some((sessionId) => sessionId === routeSessionId || sessionId === activeSessionId); }); } function isBlankHydrationProjectionSample(sample) { if (!sample) return false; const messageCount = Array.isArray(sample.messages) ? sample.messages.length : Number(sample.messageCount ?? 0); const traceRowCount = Array.isArray(sample.traceRows) ? sample.traceRows.length : Number(sample.traceRowCount ?? 0); return !stringOrNull(sample.activeSessionId) && Number(messageCount) === 0 && Number(traceRowCount) === 0; } function controlledNavigationHydrationCrossPageDiff(row, windows, sampleBySeq) { if (row?.diffKind !== "projection") return false; if (!sampleRefMatchesControlledNavigationSession(row.control, windows) || !sampleRefMatchesControlledNavigationSession(row.observer, windows)) return false; const control = sampleBySeq.get(Number(row?.control?.seq)); const observer = sampleBySeq.get(Number(row?.observer?.seq)); return isBlankHydrationProjectionSample(control) || isBlankHydrationProjectionSample(observer); } function crossPageDiffHasWorkbenchAppShellNotReady(row, sampleBySeq) { const control = sampleBySeq.get(Number(row?.control?.seq)); const observer = sampleBySeq.get(Number(row?.observer?.seq)); return workbenchSampleAppShellNotReady(control) || workbenchSampleAppShellNotReady(observer); } function workbenchSampleAppShellNotReady(sample) { if (!sample || !isWorkbenchPathSample(sample)) return false; const routeSessionId = stringOrNull(sample.routeSessionId) || workbenchSessionIdFromPath(samplePathname(sample)); if (!routeSessionId) return false; const messageCount = Array.isArray(sample.messages) ? sample.messages.length : Number(sample.messageCount ?? 0); const traceRowCount = Array.isArray(sample.traceRows) ? sample.traceRows.length : Number(sample.traceRowCount ?? 0); const turnCount = Array.isArray(sample.turns) ? sample.turns.length : Number(sample.turnCount ?? 0); const loadingCount = Array.isArray(sample.loadings) ? sample.loadings.length : 0; const diagnosticCount = Array.isArray(sample.diagnostics) ? sample.diagnostics.length : 0; const railVisibleCount = Number(sample?.sessionRail?.visibleCount ?? 0); const composer = objectValue(sample.composer); const composerPresent = composer.inputPresent === true || composer.submitPresent === true; const provenance = objectValue(sample.pageProvenance); const hasWorkbenchAssets = Number(provenance.scriptCount ?? 0) > 0 || Number(provenance.stylesheetCount ?? 0) > 0; return !stringOrNull(sample.activeSessionId) && Number(messageCount) === 0 && Number(traceRowCount) === 0 && Number(turnCount) === 0 && Number(loadingCount) === 0 && Number(diagnosticCount) === 0 && Number(railVisibleCount) === 0 && !composerPresent && hasWorkbenchAssets; } function detectWorkbenchAppShellNotReady(samples) { const rows = (Array.isArray(samples) ? samples : []) .filter(workbenchSampleAppShellNotReady) .map((sample) => workbenchAppShellNotReadyRef(sample)); return annotateWorkbenchAppShellNotReadyTiming(rows); } function annotateWorkbenchAppShellNotReadyTiming(rows) { const groups = new Map(); const splitGapMs = Math.max(1000, Number(alertThresholds.crossPageProjectionDivergenceRedMs || alertThresholds.visibleLoadingSlowMs || 10_000)); for (const row of rows.slice().sort((a, b) => timestampMs(a.ts) - timestampMs(b.ts))) { const ms = timestampMs(row.ts); const key = [row.pageRole || "control", row.pageId || "default", row.routeSessionId || row.url || ""].join(":"); const group = groups.get(key) || []; let segment = group.at(-1); if (!segment || !Number.isFinite(ms) || !Number.isFinite(segment.lastMs) || ms - segment.lastMs > splitGapMs) { segment = { rows: [], firstMs: Number.isFinite(ms) ? ms : null, lastMs: Number.isFinite(ms) ? ms : null }; group.push(segment); } segment.rows.push(row); if (Number.isFinite(ms)) { if (segment.firstMs === null || ms < segment.firstMs) segment.firstMs = ms; if (segment.lastMs === null || ms > segment.lastMs) segment.lastMs = ms; } groups.set(key, group); } const result = []; for (const group of groups.values()) { for (let segmentIndex = 0; segmentIndex < group.length; segmentIndex += 1) { const segment = group[segmentIndex]; const observedSpanMs = segment.firstMs === null || segment.lastMs === null ? null : segment.lastMs - segment.firstMs; for (const row of segment.rows) { result.push({ ...row, segmentIndex, observedFirstAt: segment.firstMs === null ? null : new Date(segment.firstMs).toISOString(), observedLastAt: segment.lastMs === null ? null : new Date(segment.lastMs).toISOString(), observedSpanMs, }); } } } return result; } function workbenchAppShellNotReadyRef(sample) { const provenance = objectValue(sample?.pageProvenance); const performance = workbenchAssetPerformanceSummary(sample); return { ...ref(sample), title: stringOrNull(sample?.title), messageCount: Array.isArray(sample?.messages) ? sample.messages.length : 0, turnCount: Array.isArray(sample?.turns) ? sample.turns.length : 0, traceRowCount: Array.isArray(sample?.traceRows) ? sample.traceRows.length : 0, sessionRailVisibleCount: Number(sample?.sessionRail?.visibleCount ?? 0), composerInputPresent: sample?.composer?.inputPresent === true, composerSubmitPresent: sample?.composer?.submitPresent === true, pageProvenance: { documentReadyState: stringOrNull(provenance.documentReadyState), pageLoadSeq: numberOrNull(provenance.pageLoadSeq), timeOrigin: numberOrNull(provenance.timeOrigin), assetFingerprint: stringOrNull(provenance.assetFingerprint), scriptCount: numberOrNull(provenance.scriptCount), stylesheetCount: numberOrNull(provenance.stylesheetCount), scripts: arrayStrings(provenance.scripts).slice(0, 8), stylesheets: arrayStrings(provenance.stylesheets).slice(0, 8), valuesRedacted: true }, assetPerformance: performance, valuesRedacted: true }; } function workbenchAssetPerformanceSummary(sample) { const assets = (Array.isArray(sample?.performance) ? sample.performance : []) .filter((entry) => /^(script|link|css)$/iu.test(String(entry?.initiatorType || "")) || /\.(?:js|css)$/iu.test(String(entry?.name || ""))) .map((entry) => ({ path: urlPath(entry?.name), initiatorType: entry?.initiatorType ?? null, duration: numberOrNull(entry?.duration), responseStatus: numberOrNull(entry?.responseStatus), transferSize: numberOrNull(entry?.transferSize), encodedBodySize: numberOrNull(entry?.encodedBodySize), decodedBodySize: numberOrNull(entry?.decodedBodySize), nextHopProtocol: entry?.nextHopProtocol ?? null, valuesRedacted: true })); const responseStatusCounts = {}; for (const item of assets) { const key = item.responseStatus === null ? "unknown" : String(item.responseStatus); responseStatusCounts[key] = (responseStatusCounts[key] || 0) + 1; } return { assetCount: assets.length, zeroStatusCount: assets.filter((item) => item.responseStatus === 0).length, missingStatusCount: assets.filter((item) => item.responseStatus === null).length, responseStatusCounts, assets: assets.slice(0, 12), valuesRedacted: true }; } function objectValue(value) { return value && typeof value === "object" && !Array.isArray(value) ? value : {}; } function stringOrNull(value) { return typeof value === "string" && value.length > 0 ? value : null; } function numberOrNull(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } function arrayStrings(value) { return Array.isArray(value) ? value.map((item) => String(item || "")).filter(Boolean) : []; } function timestampMs(value) { const parsed = Date.parse(String(value || "")); return Number.isFinite(parsed) ? parsed : NaN; } `; }