fix: surface web probe target readiness blockers

This commit is contained in:
Codex
2026-07-02 07:37:54 +00:00
parent acb60dc60d
commit 457cbd1e38
5 changed files with 146 additions and 7 deletions
@@ -785,7 +785,15 @@ async function gotoTarget(rawTarget, options = {}) {
if (!readiness.ok) {
const pageProvenance = await refreshPageProvenance("goto-degraded", httpStatus).catch(() => null);
attempts.push({ attempt, ok: false, degraded: true, httpStatus, readiness, failureKind: readiness.reason || "workbench-app-not-ready" });
return { beforeUrl, afterUrl: currentPageUrl(), httpStatus, pageId, degraded: true, degradedReason: readiness.reason || "workbench-app-not-ready", pageProvenance: compactPageProvenance(pageProvenance), readiness, attempts };
if (!readinessRetryable(readiness)) {
return { beforeUrl, afterUrl: currentPageUrl(), httpStatus, pageId, degraded: true, degradedReason: readiness.reason || "workbench-app-not-ready", pageProvenance: compactPageProvenance(pageProvenance), readiness, attempts };
}
if (attempt >= maxAttempts) {
return { beforeUrl, afterUrl: currentPageUrl(), httpStatus, pageId, degraded: true, degradedReason: readiness.reason || "workbench-app-not-ready", pageProvenance: compactPageProvenance(pageProvenance), readiness, attempts };
}
await recreateControlPageForNavigation("retryable-readiness-" + (readiness.reason || "target-not-ready"), attempt).catch((resetError) => appendJsonl(files.errors, eventRecord("navigation-page-reset-error", { commandId: activeCommandId, attempt, error: errorSummary(resetError) })));
await page.waitForTimeout(1500 * attempt).catch(() => {});
continue;
}
const pageProvenance = await refreshPageProvenance("goto", httpStatus);
attempts.push({ attempt, ok: true, httpStatus, readiness });
@@ -931,6 +939,11 @@ function isRetryableNavigationError(message) {
return /net::ERR_NETWORK_CHANGED|net::ERR_ABORTED|net::ERR_CONNECTION_RESET|net::ERR_NAME_NOT_RESOLVED|Navigation timeout|page\.goto:\s*timeout|timeout\s+\d+ms\s+exceeded|workbench-app-not-ready/iu.test(String(message || ""));
}
function readinessRetryable(readiness) {
const reason = String(readiness?.reason || "");
return !/nav-access-denied|workbench-blank-document|login-visible/iu.test(reason);
}
function navigationFailureKind(message) {
const text = String(message || "");
if (/net::ERR_NETWORK_CHANGED/iu.test(text)) return "net::ERR_NETWORK_CHANGED";
@@ -990,9 +1003,10 @@ async function waitForTargetPageReady(targetPage, targetUrl, options = {}) {
}, null, { timeout: timeoutMs }).catch(() => null);
const snapshot = await workbenchReadinessSnapshot(targetPage);
const ok = snapshot.workbenchShellVisible === true;
const reason = workbenchReadinessReason(snapshot, ok);
return {
ok,
reason: ok ? "workbench-ready" : snapshot.loginVisible ? "login-visible" : "workbench-app-not-ready",
reason,
durationMs: Date.now() - started,
snapshot,
valuesRedacted: true
@@ -1010,9 +1024,13 @@ async function workbenchReadinessSnapshot(targetPage) {
const sessionCreate = document.querySelector("#session-create");
const sessionRail = document.querySelector("#session-sidebar");
const sessionCollapseToggle = document.querySelector("#session-collapse-toggle");
const params = new URLSearchParams(window.location.search || "");
const bodyText = String(document.body?.innerText || "");
return {
url: window.location.href,
path: window.location.pathname,
search: window.location.search || "",
blockedReason: params.get("blocked"),
readyState: document.readyState,
workbenchShellVisible: visible(document.querySelector("#workspace, .workbench-route")),
sessionCreatePresent: Boolean(sessionCreate),
@@ -1026,7 +1044,8 @@ async function workbenchReadinessSnapshot(targetPage) {
activeTabPresent: visible(document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']")),
warningPresent: visible(document.querySelector(".composer-warning")),
loginVisible: visible(document.querySelector("form.login-card, .login-card, [data-testid='login']")),
bodyTextPreview: String(document.body?.innerText || "").slice(0, 2000),
bodyTextBytes: new TextEncoder().encode(bodyText).length,
bodyTextPreview: bodyText.slice(0, 2000),
valuesRedacted: true
};
}).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
@@ -1037,6 +1056,23 @@ async function workbenchReadinessSnapshot(targetPage) {
return snapshot;
}
function workbenchReadinessReason(snapshot, ok = false) {
if (ok) return "workbench-ready";
const blocked = String(snapshot?.blockedReason || "");
if (blocked === "nav_access_denied") return "nav-access-denied";
if (snapshot?.loginVisible === true) return "login-visible";
if (
snapshot?.readyState === "complete"
&& snapshot?.workbenchShellVisible === false
&& snapshot?.sessionRailPresent === false
&& snapshot?.commandInputPresent === false
&& (snapshot?.bodyTextBytes === 0 || snapshot?.bodyTextHash === "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
) {
return "workbench-blank-document";
}
return "workbench-app-not-ready";
}
async function projectManagementReadinessSnapshot(targetPage) {
const selectors = projectManagement.readinessSelectors;
return targetPage.evaluate((input) => {