From 645709bb62dd1917376ddb1118f528a2c710a806 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 28 Jun 2026 02:24:56 +0000 Subject: [PATCH] fix: retry control page recovery hydration --- .../hwlab-node-web-observe-runner-source.ts | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 6e2cabea..10bd74ef 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -1655,7 +1655,7 @@ async function controlPageLivenessSnapshot(reason, timeoutMs = 1500) { async function ensureControlPageResponsiveForCommand(reason) { const beforeUrl = currentPageUrl(); - const liveness = await controlPageLivenessSnapshot(reason + "-preflight", 1500); + const liveness = await controlPageLivenessSnapshot(reason + "-preflight", 3000); if (liveness.ok) return { ok: true, recovered: false, reason, beforeUrl, afterUrl: currentPageUrl(), liveness, pageRole: "control", pageId, pageEpoch: controlPageEpoch, valuesRedacted: true }; const target = controlPageRecoveryTarget(liveness.snapshot, beforeUrl); await appendJsonl(files.control, eventRecord("control-page-unresponsive-before-command", { @@ -1668,24 +1668,32 @@ async function ensureControlPageResponsiveForCommand(reason) { pageEpoch: controlPageEpoch, valuesRedacted: true })); - await recreateControlPageForNavigation(reason + "-control-page-unresponsive", 1); let navigation = null; let hydration = null; let afterLiveness = null; - try { - navigation = await gotoTarget(target.targetPath); - } catch (error) { - navigation = { ok: false, targetPath: target.targetPath, error: errorSummary(error), valuesRedacted: true }; + const attempts = []; + let ok = false; + for (let attempt = 1; attempt <= 2; attempt += 1) { + await recreateControlPageForNavigation(reason + "-control-page-unresponsive", attempt); + try { + navigation = await gotoTarget(target.targetPath); + } catch (error) { + navigation = { ok: false, targetPath: target.targetPath, error: errorSummary(error), valuesRedacted: true }; + } + if (!navigation?.error && target.sessionId) { + hydration = await withHardTimeout( + waitForWorkbenchSessionHydrated(page, target.sessionId, { timeoutMs: 12000 }), + 14000, + "control page recovery hydration exceeded 14000ms" + ).catch((error) => ({ ok: false, error: errorSummary(error), valuesRedacted: true })); + } else { + hydration = null; + } + afterLiveness = await controlPageLivenessSnapshot(reason + "-post-recovery", 3000); + ok = !navigation?.error && afterLiveness.ok === true && (!target.sessionId || hydration?.ok === true); + attempts.push({ attempt, ok, navigation, hydration, afterLiveness, pageId, pageEpoch: controlPageEpoch, valuesRedacted: true }); + if (ok) break; } - if (!navigation?.error && target.sessionId) { - hydration = await withHardTimeout( - waitForWorkbenchSessionHydrated(page, target.sessionId, { timeoutMs: 10000 }), - 12000, - "control page recovery hydration exceeded 12000ms" - ).catch((error) => ({ ok: false, error: errorSummary(error), valuesRedacted: true })); - } - afterLiveness = await controlPageLivenessSnapshot(reason + "-post-recovery", 3000); - const ok = !navigation?.error && afterLiveness.ok === true && (!target.sessionId || hydration?.ok === true); const recovery = { ok, recovered: ok, @@ -1697,6 +1705,7 @@ async function ensureControlPageResponsiveForCommand(reason) { navigation, hydration, afterLiveness, + attempts, pageRole: "control", pageId, pageEpoch: controlPageEpoch,