From 31aa687cd451856cc4475975931c14bcefac3d01 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 28 Jun 2026 02:03:51 +0000 Subject: [PATCH] fix: bound quick verify artifact wait chunks --- scripts/src/hwlab-node-web-sentinel-cicd.ts | 60 ++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/scripts/src/hwlab-node-web-sentinel-cicd.ts b/scripts/src/hwlab-node-web-sentinel-cicd.ts index df7d96eb..f1661764 100644 --- a/scripts/src/hwlab-node-web-sentinel-cicd.ts +++ b/scripts/src/hwlab-node-web-sentinel-cicd.ts @@ -3931,11 +3931,11 @@ function waitForQuickVerifyPromptTurn(state: SentinelCicdState, observerId: stri valuesRedacted: true, }; } - const pollSleepMs = Math.max(250, Math.min(500, Math.trunc(pollIntervalMs / 2) || 250)); + const pollSleepMs = Math.max(1000, Math.min(3000, Math.trunc(pollIntervalMs * 2) || 1000)); while (Date.now() < deadline) { - const waitMs = Math.max(1000, Math.min(55_000, deadline - Date.now())); + const waitMs = Math.max(1000, Math.min(45_000, deadline - Date.now())); const script = quickVerifyPromptWaitScript(indexEntry.stateDir, promptIndex, waitMs, pollSleepMs); - const result = runCommand(["trans", `${state.spec.nodeId}:${state.spec.workspace}`, "sh"], repoRoot, { input: script, timeoutMs: waitMs + 5000 }); + const result = runCommand(["trans", `${state.spec.nodeId}:${state.spec.workspace}`, "sh"], repoRoot, { input: script, timeoutMs: waitMs + 8000 }); const payload = parseJsonObject(result.stdout); if (Array.isArray(payload?.observations)) observations.push(...payload.observations.map(record)); const status = typeof payload?.status === "string" ? payload.status : null; @@ -3951,10 +3951,23 @@ function waitForQuickVerifyPromptTurn(state: SentinelCicdState, observerId: stri valuesRedacted: true, }; if (result.exitCode !== 0 || payload === null || payload.ok === false && payload.failure !== "quick-verify-wait-chunk-timeout") { + const fallback = quickVerifyTurnSummaryFallback(state, observerId, promptIndex); + if (fallback.ok === true) { + return { + ok: true, + ...terminalPayload, + status: stringAtNullable(fallback, "status") ?? status, + traceId: stringAtNullable(fallback, "traceId") ?? payload?.traceId ?? null, + finalResponseEmpty: false, + fallback, + warnings: ["quick verify artifact wait command failed, but bounded turn-summary artifacts show this round completed; continuing validation."], + }; + } return { ok: false, failure: text(payload?.failure ?? "quick-verify-artifact-wait-failed"), ...terminalPayload, + fallback, }; } if (payload.ok === true) return { ok: true, ...terminalPayload }; @@ -4389,15 +4402,50 @@ function quickVerifyControlFindings(failure: string | null, promptIndex: number, }]; } -function quickVerifyHasDurableBusinessTurn(promptIndex: number, turnSummary: Record | null, traceFrame: Record | null): boolean { +function quickVerifyCompletedTurnSummaryRow(promptIndex: number, turnSummary: Record | null): Record | null { const rows = Array.isArray(record(turnSummary?.collect).rows) ? record(turnSummary?.collect).rows.map(record) : []; const scopedRows = promptIndex > 0 ? rows.filter((row) => numberAtNullable(row, "round") === promptIndex) : rows; - if (scopedRows.some((row) => { + return scopedRows.find((row) => { const finalResponse = record(row.finalResponse); return isQuickVerifyTurnSuccessful(stringAtNullable(row, "status")) && stringAtNullable(row, "traceId") !== null && finalResponse.empty !== true; - })) return true; + }) ?? null; +} + +function quickVerifyTurnSummaryFallback(state: SentinelCicdState, observerId: string, promptIndex: number): Record { + const turnSummary = collectObserveView(state, observerId, "turn-summary", null, 25); + const row = quickVerifyCompletedTurnSummaryRow(promptIndex, turnSummary); + const rows = Array.isArray(record(turnSummary.collect).rows) ? record(turnSummary.collect).rows.map(record) : []; + if (row === null) { + return { + ok: false, + source: "turn-summary-fallback", + collectOk: turnSummary.ok === true, + rowCount: rows.length, + promptIndex, + result: turnSummary.result ?? null, + valuesRedacted: true, + }; + } + const finalResponse = record(row.finalResponse); + return { + ok: true, + source: "turn-summary-fallback", + collectOk: turnSummary.ok === true, + rowCount: rows.length, + promptIndex, + status: stringAtNullable(row, "status"), + traceId: stringAtNullable(row, "traceId"), + finalResponseEmpty: finalResponse.empty === true, + finalResponseBytes: numberAtNullable(finalResponse, "textBytes"), + result: turnSummary.result ?? null, + valuesRedacted: true, + }; +} + +function quickVerifyHasDurableBusinessTurn(promptIndex: number, turnSummary: Record | null, traceFrame: Record | null): boolean { + if (quickVerifyCompletedTurnSummaryRow(promptIndex, turnSummary) !== null) return true; const renderedTrace = typeof traceFrame?.renderedText === "string" ? traceFrame.renderedText : ""; if (!renderedTrace) return false; if (/Final Response\s*\n\s*\(空内容\)/iu.test(renderedTrace)) return false;