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 <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-07-27 16:11:52 -07:00
co-authored by Claude Code
parent f27bea5bc0
commit 40b2a80653
2 changed files with 20 additions and 7 deletions
+11 -5
View File
@@ -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;
}
+9 -2
View File
@@ -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',
);
});