diff --git a/scripts/src/hwlab-node-web-observe-analyzer-source.ts b/scripts/src/hwlab-node-web-observe-analyzer-source.ts index 3599e8d7..200784d5 100644 --- a/scripts/src/hwlab-node-web-observe-analyzer-source.ts +++ b/scripts/src/hwlab-node-web-observe-analyzer-source.ts @@ -1418,6 +1418,16 @@ function sessionInvariantNavigationWindows(control) { return windows; } +function sessionInvariantCanarySessionIds(control) { + const ids = new Set(); + for (const row of control || []) { + const detail = objectValue(row?.detail); + 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]); @@ -1438,10 +1448,14 @@ function sampleInControlledNavigationWindow(sample, windows) { 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) => { - if (ms < window.startMs || ms > window.endMs) return false; const expected = [window.canarySessionId, window.alternateSessionId].filter(Boolean); return expected.some((sessionId) => sessionId === routeSessionId || sessionId === activeSessionId); }); @@ -1458,7 +1472,7 @@ function isBlankHydrationProjectionSample(sample) { function controlledNavigationHydrationCrossPageDiff(row, windows, sampleBySeq) { if (row?.diffKind !== "projection") return false; - if (!sampleRefInControlledNavigationSessionWindow(row.control, windows) || !sampleRefInControlledNavigationSessionWindow(row.observer, windows)) 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); @@ -2272,6 +2286,7 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) { const domDiagnostics = []; const executionErrors = []; const baselineExecutionErrors = []; + const canarySessionIds = sessionInvariantCanarySessionIds(control); const firstPromptMs = promptTimes.length > 0 ? promptTimes[0] : Infinity; const firstSeenExecutionErrorMs = new Map(); for (const sample of samples) { @@ -2353,7 +2368,9 @@ function buildRuntimeAlerts(samples, control, network, consoleEvents, errors) { textHash, preview: limitText(candidate.text, 260) }; - if (baseline) baselineExecutionErrors.push(event); + const eventSessions = [event.routeSessionId, event.activeSessionId].filter(Boolean); + const nonCanarySession = canarySessionIds.size > 0 && !eventSessions.some((sessionId) => canarySessionIds.has(sessionId)); + if (baseline || nonCanarySession) baselineExecutionErrors.push({ ...event, baseline: true, baselineReason: nonCanarySession ? "non-canary-session" : "pre-first-prompt" }); else executionErrors.push(event); domDiagnostics.push({ seq: sample.seq ?? null,