From 40b2a8065344547a507b174488f60184721d1eb5 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Mon, 27 Jul 2026 16:11:52 -0700 Subject: [PATCH] fix: restrict server-session adoption to comparison phases The CI-only astro accept hang: the carbonize source edit triggers a framework reload, and on a slow runner the reloaded page rehydrated the still-non-terminal carbonize_required session back into GENERATING, stranding the bar over a decided comparison. Adoption now uses a positive allowlist of comparison phases (generate_requested, variants_ready, generating, cycling); accept/carbonize/steer/manual phases are agent-side work and never adoptable. Regression guard pins the allowlist. This work was produced with AI assistance (Claude Code). Co-Authored-By: Claude Code --- skill/scripts/live-browser.js | 16 +++++++++++----- tests/live-browser-regression.test.mjs | 11 +++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/skill/scripts/live-browser.js b/skill/scripts/live-browser.js index 308b494bd..b95d8b5a8 100644 --- a/skill/scripts/live-browser.js +++ b/skill/scripts/live-browser.js @@ -8670,6 +8670,16 @@ void main() { * not evidence that it belongs to THIS page, and adopting it would hijack an * unrelated route. */ + // Phases in which the user is (or should be) comparing variants. Only these + // are adoptable by a browser with no local record. Steer and manual-edit + // sessions have no wrapper to restore, and accept/carbonize phases are + // agent-side work: a reload mid-carbonize must not resurrect the bar over a + // page whose comparison is already decided (a slow-CI reload hit exactly + // that window and left the bar stranded after accept). + const ADOPTABLE_SESSION_PHASES = new Set([ + 'generate_requested', 'variants_ready', 'generating', 'cycling', + ]); + function findAdoptableServerSession(activeSessions) { if (!Array.isArray(activeSessions)) return null; return activeSessions.find((session) => ( @@ -8679,12 +8689,8 @@ void main() { && session.pageUrl && pageMatchesCurrent(session.pageUrl) && (session.previewFile || session.sourceFile) - // Only variant comparisons are adoptable. A steer or manual-edit - // session is non-terminal and carries a sourceFile, but restoring it - // as a comparison hunts for a variant wrapper that never existed and - // wedges the bar before the user's next pick. && Number(session.expectedVariants) > 0 - && !/^(steer|manual_edit)/.test(String(session.phase || '')) + && ADOPTABLE_SESSION_PHASES.has(String(session.phase || '')) )) || null; } diff --git a/tests/live-browser-regression.test.mjs b/tests/live-browser-regression.test.mjs index c46a9456f..4811f20c4 100644 --- a/tests/live-browser-regression.test.mjs +++ b/tests/live-browser-regression.test.mjs @@ -1357,8 +1357,15 @@ describe('live-browser.js regression guards', () => { ); assert.match( SOURCE, - /function findAdoptableServerSession\([\s\S]{0,900}?\^\(steer\|manual_edit\)/, - 'adoption must exclude steer and manual-edit phases', + /function findAdoptableServerSession\([\s\S]{0,900}?ADOPTABLE_SESSION_PHASES\.has\(String\(session\.phase/, + 'adoption must be limited to the comparison-phase allowlist', + ); + // Accept/carbonize phases are agent-side work; adopting them resurrects + // the bar over a decided comparison (the slow-CI astro accept hang). + assert.doesNotMatch( + SOURCE, + /ADOPTABLE_SESSION_PHASES = new Set\(\[[\s\S]{0,200}?(accept_requested|carbonize|steer|manual_edit)/, + 'accept, carbonize, steer, and manual-edit phases must not be adoptable', ); });