fix: show quick verify timeout titles
This commit is contained in:
@@ -527,7 +527,7 @@ function renderFindingItemCollapsed(item) {
|
|||||||
const code = item.code || item.findingId || "finding";
|
const code = item.code || item.findingId || "finding";
|
||||||
const latestRunId = item.latestRunId || "-";
|
const latestRunId = item.latestRunId || "-";
|
||||||
const hasLatestRun = latestRunId !== "-";
|
const hasLatestRun = latestRunId !== "-";
|
||||||
const codeLabel = displayFindingCode(code);
|
const codeLabel = displayFindingTitle(item, code);
|
||||||
const rootCause = findingRootCauseText(item);
|
const rootCause = findingRootCauseText(item);
|
||||||
const evidence = findingEvidenceText(item);
|
const evidence = findingEvidenceText(item);
|
||||||
const nextAction = item.nextAction || findingNextAction(code);
|
const nextAction = item.nextAction || findingNextAction(code);
|
||||||
@@ -775,7 +775,7 @@ function detailFindings(findings) {
|
|||||||
<td><span class="severity-pill ${severityClass(item.severity)}">${escapeHtml(displaySeverity(item.severity))}</span></td>
|
<td><span class="severity-pill ${severityClass(item.severity)}">${escapeHtml(displaySeverity(item.severity))}</span></td>
|
||||||
<td class="mono">${escapeHtml(item.finding_id || item.findingId || "-")}</td>
|
<td class="mono">${escapeHtml(item.finding_id || item.findingId || "-")}</td>
|
||||||
<td>${escapeHtml(String(item.count ?? 0))}</td>
|
<td>${escapeHtml(String(item.count ?? 0))}</td>
|
||||||
<td>${escapeHtml(shortText(displayFindingSummary(item.finding_id || item.findingId || "", item.summary || ""), 220))}</td>
|
<td>${escapeHtml(shortText(displayFindingTitle(item, item.finding_id || item.findingId || ""), 220))}</td>
|
||||||
<td>${escapeHtml(shortText(findingRootCauseText(item) || item.nextAction || "-", 240))}</td>
|
<td>${escapeHtml(shortText(findingRootCauseText(item) || item.nextAction || "-", 240))}</td>
|
||||||
<td class="mono">${escapeHtml(shortText(item.report_json_sha256 || item.reportJsonSha256 || "-", 24))}</td>
|
<td class="mono">${escapeHtml(shortText(item.report_json_sha256 || item.reportJsonSha256 || "-", 24))}</td>
|
||||||
</tr>`).join("")}</tbody></table></div>
|
</tr>`).join("")}</tbody></table></div>
|
||||||
@@ -1296,6 +1296,8 @@ function displayFindingCode(code) {
|
|||||||
const normalized = String(code || "").toLowerCase();
|
const normalized = String(code || "").toLowerCase();
|
||||||
const labels = {
|
const labels = {
|
||||||
"quick-verify-no-business-turn": "quick verify 未触达业务 turn",
|
"quick-verify-no-business-turn": "quick verify 未触达业务 turn",
|
||||||
|
"quick-verify-timeout-over-budget": "quick-verify 超时",
|
||||||
|
"observe-turn-terminal-wait-failed": "Workbench 等待终态超时",
|
||||||
"workbench-turn-state-triad-inconsistent": "Workbench 状态三元组不一致",
|
"workbench-turn-state-triad-inconsistent": "Workbench 状态三元组不一致",
|
||||||
"session-rail-title-fallback-root-cause": "INV-02 会话标题 fallback 根因",
|
"session-rail-title-fallback-root-cause": "INV-02 会话标题 fallback 根因",
|
||||||
"trace-events-page-read-404-root-cause": "INV-07 trace events 404 根因",
|
"trace-events-page-read-404-root-cause": "INV-07 trace events 404 根因",
|
||||||
@@ -1312,6 +1314,11 @@ function displayFindingCode(code) {
|
|||||||
return labels[normalized] || String(code || "finding");
|
return labels[normalized] || String(code || "finding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function displayFindingTitle(item, code) {
|
||||||
|
const dynamicTitle = item?.displayTitleZh || item?.errorTitleZh || item?.timeoutDisplay?.titleZh;
|
||||||
|
return dynamicTitle ? String(dynamicTitle) : displayFindingCode(code);
|
||||||
|
}
|
||||||
|
|
||||||
function displayFindingSummary(code, summary) {
|
function displayFindingSummary(code, summary) {
|
||||||
const normalized = String(code || "").toLowerCase();
|
const normalized = String(code || "").toLowerCase();
|
||||||
const summaries = {
|
const summaries = {
|
||||||
|
|||||||
@@ -1486,6 +1486,8 @@ function rootCauseText(item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findingTitle(item) {
|
function findingTitle(item) {
|
||||||
|
const dynamicTitle = safeUserText(item?.displayTitleZh || item?.errorTitleZh || item?.timeoutDisplay?.titleZh);
|
||||||
|
if (dynamicTitle) return dynamicTitle;
|
||||||
const display = checkDisplay(item);
|
const display = checkDisplay(item);
|
||||||
if (display.title) return display.title;
|
if (display.title) return display.title;
|
||||||
return safeUserText(item?.checkTitleZh || item?.check?.titleZh) || "未登记监测项";
|
return safeUserText(item?.checkTitleZh || item?.check?.titleZh) || "未登记监测项";
|
||||||
@@ -1608,9 +1610,10 @@ function checkDisplay(item) {
|
|||||||
const rawCode = rawCheckCode(item);
|
const rawCode = rawCheckCode(item);
|
||||||
const rawId = rawFindingId(item);
|
const rawId = rawFindingId(item);
|
||||||
const serviceCode = publicCheckCode(item?.checkCode || item?.check?.code);
|
const serviceCode = publicCheckCode(item?.checkCode || item?.check?.code);
|
||||||
|
const dynamicTitle = safeUserText(item?.displayTitleZh || item?.errorTitleZh || item?.timeoutDisplay?.titleZh);
|
||||||
return {
|
return {
|
||||||
code: serviceCode || publicCheckCode(rawId || rawCode) || stableCheckCode(rawCode || rawId),
|
code: serviceCode || publicCheckCode(rawId || rawCode) || stableCheckCode(rawCode || rawId),
|
||||||
title: safeUserText(item?.checkTitleZh || item?.check?.titleZh) || "未登记监测项",
|
title: dynamicTitle || safeUserText(item?.checkTitleZh || item?.check?.titleZh) || "未登记监测项",
|
||||||
summary: safeUserText(item?.checkSummaryZh || item?.check?.summaryZh) || "已记录监测项详情,见报告原文。",
|
summary: safeUserText(item?.checkSummaryZh || item?.check?.summaryZh) || "已记录监测项详情,见报告原文。",
|
||||||
action: safeUserText(item?.checkActionZh || item?.check?.actionZh) || "查看详情后处理",
|
action: safeUserText(item?.checkActionZh || item?.check?.actionZh) || "查看详情后处理",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4382,10 +4382,11 @@ function renderControlPlaneResult(result: Record<string, unknown>): string {
|
|||||||
"",
|
"",
|
||||||
Object.keys(sourceMirrorSync).length === 0 ? "SOURCE_MIRROR_SYNC\n-" : table(["OK", "PHASE", "JOB", "COMMIT", "STAGE_REF", "ELAPSED"], [[sourceMirrorSync.ok, sourceMirrorSync.phase, sourceMirrorSync.jobName, short(record(sourceMirrorSync.payload).mirrorCommit), short(record(sourceMirrorSync.payload).stageRef), sourceMirrorSync.elapsedMs ?? "-"]]),
|
Object.keys(sourceMirrorSync).length === 0 ? "SOURCE_MIRROR_SYNC\n-" : table(["OK", "PHASE", "JOB", "COMMIT", "STAGE_REF", "ELAPSED"], [[sourceMirrorSync.ok, sourceMirrorSync.phase, sourceMirrorSync.jobName, short(record(sourceMirrorSync.payload).mirrorCommit), short(record(sourceMirrorSync.payload).stageRef), sourceMirrorSync.elapsedMs ?? "-"]]),
|
||||||
"",
|
"",
|
||||||
Object.keys(targetValidation).length === 0 ? "TARGET_VALIDATION\n-" : table(["OK", "STATUS", "BUSINESS", "SCENARIO", "RUN", "OBSERVER", "REPORT", "FINDINGS", "ARTIFACTS"], [[
|
Object.keys(targetValidation).length === 0 ? "TARGET_VALIDATION\n-" : table(["OK", "STATUS", "BUSINESS", "ERROR_TITLE", "SCENARIO", "RUN", "OBSERVER", "REPORT", "FINDINGS", "ARTIFACTS"], [[
|
||||||
targetValidation.ok,
|
targetValidation.ok,
|
||||||
targetValidation.status,
|
targetValidation.status,
|
||||||
targetValidationBusiness.status ?? "-",
|
targetValidationBusiness.status ?? "-",
|
||||||
|
targetValidation.errorTitleZh ?? targetValidation.failureTitleZh ?? targetValidationBusiness.errorTitleZh ?? "-",
|
||||||
targetValidation.scenarioId,
|
targetValidation.scenarioId,
|
||||||
targetValidation.runId,
|
targetValidation.runId,
|
||||||
targetValidation.observerId,
|
targetValidation.observerId,
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ export function runSentinelQuickVerify(state: SentinelCicdState, reason: string,
|
|||||||
const repeat = Math.max(1, typeof item.repeat === "number" && Number.isFinite(item.repeat) ? Math.trunc(item.repeat) : 1);
|
const repeat = Math.max(1, typeof item.repeat === "number" && Number.isFinite(item.repeat) ? Math.trunc(item.repeat) : 1);
|
||||||
for (let index = 0; index < repeat; index += 1) {
|
for (let index = 0; index < repeat; index += 1) {
|
||||||
if (Date.now() >= deadline) {
|
if (Date.now() >= deadline) {
|
||||||
|
const timeoutDisplay = timeoutDisplayForQuickVerifyFailure("quick-verify-timeout-over-budget", promptIndex, warningBudgetSeconds, timeoutSeconds);
|
||||||
printQuickVerifyProgress(state, runId, "timeout", "failed", { observerId, promptIndex, elapsedMs: elapsedMs(), hardBudgetSeconds });
|
printQuickVerifyProgress(state, runId, "timeout", "failed", { observerId, promptIndex, elapsedMs: elapsedMs(), hardBudgetSeconds });
|
||||||
return recordQuickVerify(state, finalizeQuickVerifyFailure(state, {
|
return recordQuickVerify(state, finalizeQuickVerifyFailure(state, {
|
||||||
runId,
|
runId,
|
||||||
@@ -200,6 +201,8 @@ export function runSentinelQuickVerify(state: SentinelCicdState, reason: string,
|
|||||||
promptIndex,
|
promptIndex,
|
||||||
steps,
|
steps,
|
||||||
failure: "quick-verify-timeout-over-budget",
|
failure: "quick-verify-timeout-over-budget",
|
||||||
|
failureTitleZh: stringAtNullable(timeoutDisplay, "titleZh"),
|
||||||
|
timeoutDisplay,
|
||||||
elapsedMs: elapsedMs(),
|
elapsedMs: elapsedMs(),
|
||||||
warnings: mergeWarnings(`quick verify exceeded the hard ${hardBudgetSeconds}s execution budget after the configured ${warningBudgetSeconds}s targetValidation warning budget.`, elapsedWarnings()),
|
warnings: mergeWarnings(`quick verify exceeded the hard ${hardBudgetSeconds}s execution budget after the configured ${warningBudgetSeconds}s targetValidation warning budget.`, elapsedWarnings()),
|
||||||
promptSource: prompts.summary,
|
promptSource: prompts.summary,
|
||||||
@@ -486,10 +489,15 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
|||||||
readonly promptIndex: number;
|
readonly promptIndex: number;
|
||||||
readonly steps: readonly Record<string, unknown>[];
|
readonly steps: readonly Record<string, unknown>[];
|
||||||
readonly failure: string;
|
readonly failure: string;
|
||||||
|
readonly failureTitleZh?: string | null;
|
||||||
|
readonly timeoutDisplay?: Record<string, unknown> | null;
|
||||||
readonly promptSource?: Record<string, unknown>;
|
readonly promptSource?: Record<string, unknown>;
|
||||||
readonly elapsedMs?: number;
|
readonly elapsedMs?: number;
|
||||||
readonly warnings?: readonly unknown[];
|
readonly warnings?: readonly unknown[];
|
||||||
}): Record<string, unknown> {
|
}): Record<string, unknown> {
|
||||||
|
const targetValidationSeconds = numberAt(state.cicd, "targetValidation.maxSeconds");
|
||||||
|
const failureDisplay = input.timeoutDisplay ?? timeoutDisplayForQuickVerifyFailure(input.failure, input.promptIndex, targetValidationSeconds, null);
|
||||||
|
const failureTitle = input.failureTitleZh ?? stringAtNullable(failureDisplay, "titleZh");
|
||||||
const cleanupSteps: Record<string, unknown>[] = [];
|
const cleanupSteps: Record<string, unknown>[] = [];
|
||||||
if (input.promptIndex > 0) {
|
if (input.promptIndex > 0) {
|
||||||
const cancel = runChildCli([
|
const cancel = runChildCli([
|
||||||
@@ -519,7 +527,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
|||||||
const turnSummary = collectObserveView(state, input.observerId, "turn-summary", null, 30);
|
const turnSummary = collectObserveView(state, input.observerId, "turn-summary", null, 30);
|
||||||
const traceFrame = collectObserveView(state, input.observerId, "trace-frame", input.promptIndex > 0 ? input.promptIndex : null, 30);
|
const traceFrame = collectObserveView(state, input.observerId, "trace-frame", input.promptIndex > 0 ? input.promptIndex : null, 30);
|
||||||
const durableBusinessTurn = quickVerifyHasDurableBusinessTurn(input.promptIndex, turnSummary, traceFrame);
|
const durableBusinessTurn = quickVerifyHasDurableBusinessTurn(input.promptIndex, turnSummary, traceFrame);
|
||||||
const controlFindings = quickVerifyControlFindings(input.failure, input.promptIndex, turnSummary, traceFrame);
|
const controlFindings = quickVerifyControlFindings(input.failure, input.promptIndex, turnSummary, traceFrame, failureDisplay);
|
||||||
const artifactSummaryRecord = record(artifactSummary);
|
const artifactSummaryRecord = record(artifactSummary);
|
||||||
const artifactFindings = Array.isArray(artifactSummaryRecord.findings) ? artifactSummaryRecord.findings.map(record) : [];
|
const artifactFindings = Array.isArray(artifactSummaryRecord.findings) ? artifactSummaryRecord.findings.map(record) : [];
|
||||||
const visibilityFindings = quickVerifyAnalysisVisibilityFindings(analysis, artifactSummary);
|
const visibilityFindings = quickVerifyAnalysisVisibilityFindings(analysis, artifactSummary);
|
||||||
@@ -533,7 +541,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
|||||||
&& record(artifactSummary).ok === true
|
&& record(artifactSummary).ok === true
|
||||||
&& controlFindings.length === 0
|
&& controlFindings.length === 0
|
||||||
&& blockingFindings.length === 0;
|
&& blockingFindings.length === 0;
|
||||||
const businessStatus = quickVerifyBusinessStatus(input.failure, input.promptIndex, turnSummary, traceFrame, input.elapsedMs ?? null, numberAt(state.cicd, "targetValidation.maxSeconds"));
|
const businessStatus = quickVerifyBusinessStatus(input.failure, input.promptIndex, turnSummary, traceFrame, input.elapsedMs ?? null, targetValidationSeconds);
|
||||||
return {
|
return {
|
||||||
ok: recoveredWaitFailure,
|
ok: recoveredWaitFailure,
|
||||||
runId: input.runId,
|
runId: input.runId,
|
||||||
@@ -543,6 +551,9 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
|||||||
observerId: input.observerId,
|
observerId: input.observerId,
|
||||||
elapsedMs: input.elapsedMs ?? null,
|
elapsedMs: input.elapsedMs ?? null,
|
||||||
businessStatus,
|
businessStatus,
|
||||||
|
failureTitleZh: recoveredWaitFailure ? null : failureTitle,
|
||||||
|
errorTitleZh: recoveredWaitFailure ? null : failureTitle,
|
||||||
|
timeoutDisplay: recoveredWaitFailure ? null : failureDisplay,
|
||||||
stateDir: indexEntry?.stateDir ?? null,
|
stateDir: indexEntry?.stateDir ?? null,
|
||||||
reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"),
|
reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"),
|
||||||
findingCount: findings.length,
|
findingCount: findings.length,
|
||||||
@@ -552,8 +563,8 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
|||||||
steps: [...input.steps, ...cleanupSteps],
|
steps: [...input.steps, ...cleanupSteps],
|
||||||
analysis: artifactSummary,
|
analysis: artifactSummary,
|
||||||
views: {
|
views: {
|
||||||
summary: { renderedText: renderQuickVerifySummary({ runId: input.runId, scenarioId: input.scenarioId, observerId: input.observerId, artifactSummary, findings, findingCount: findings.length, steps: input.steps, publicOrigin: stringAt(state.publicExposure, "publicBaseUrl") }) },
|
summary: { renderedText: renderQuickVerifySummary({ runId: input.runId, scenarioId: input.scenarioId, observerId: input.observerId, artifactSummary, findings, findingCount: findings.length, steps: input.steps, publicOrigin: stringAt(state.publicExposure, "publicBaseUrl"), failure: recoveredWaitFailure ? null : input.failure, failureTitleZh: recoveredWaitFailure ? null : failureTitle, timeoutDisplay: recoveredWaitFailure ? null : failureDisplay }) },
|
||||||
"auth-session-switch-summary": { renderedText: renderAuthSessionSwitchQuickVerifySummary({ runId: input.runId, scenarioId: input.scenarioId, observerId: input.observerId, artifactSummary, steps: input.steps, findings, publicOrigin: stringAt(state.publicExposure, "publicBaseUrl") }) },
|
"auth-session-switch-summary": { renderedText: renderAuthSessionSwitchQuickVerifySummary({ runId: input.runId, scenarioId: input.scenarioId, observerId: input.observerId, artifactSummary, steps: input.steps, findings, publicOrigin: stringAt(state.publicExposure, "publicBaseUrl"), failure: recoveredWaitFailure ? null : input.failure, failureTitleZh: recoveredWaitFailure ? null : failureTitle, timeoutDisplay: recoveredWaitFailure ? null : failureDisplay }) },
|
||||||
"turn-summary": { renderedText: typeof turnSummary.renderedText === "string" ? turnSummary.renderedText : null, ok: turnSummary.ok },
|
"turn-summary": { renderedText: typeof turnSummary.renderedText === "string" ? turnSummary.renderedText : null, ok: turnSummary.ok },
|
||||||
"trace-frame": { renderedText: typeof traceFrame.renderedText === "string" ? traceFrame.renderedText : null, ok: traceFrame.ok },
|
"trace-frame": { renderedText: typeof traceFrame.renderedText === "string" ? traceFrame.renderedText : null, ok: traceFrame.ok },
|
||||||
},
|
},
|
||||||
@@ -563,7 +574,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
|
|||||||
warnings: mergeWarnings(
|
warnings: mergeWarnings(
|
||||||
Array.isArray(input.warnings) ? input.warnings : [],
|
Array.isArray(input.warnings) ? input.warnings : [],
|
||||||
recoveredWaitFailure ? ["quick verify wait command timed out, but collected turn-summary/trace-frame artifacts show a durable completed business turn; treating the wait timeout as a non-blocking tool finding."] : [],
|
recoveredWaitFailure ? ["quick verify wait command timed out, but collected turn-summary/trace-frame artifacts show a durable completed business turn; treating the wait timeout as a non-blocking tool finding."] : [],
|
||||||
targetValidationElapsedWarnings(input.elapsedMs ?? null, "quick verify confirm-wait", numberAt(state.cicd, "targetValidation.maxSeconds")),
|
targetValidationElapsedWarnings(input.elapsedMs ?? null, "quick verify confirm-wait", targetValidationSeconds),
|
||||||
),
|
),
|
||||||
valuesRedacted: true,
|
valuesRedacted: true,
|
||||||
};
|
};
|
||||||
@@ -716,6 +727,9 @@ function recordQuickVerify(state: SentinelCicdState, payload: Record<string, unk
|
|||||||
businessStatus: payload.businessStatus ?? null,
|
businessStatus: payload.businessStatus ?? null,
|
||||||
elapsedMs: payload.elapsedMs,
|
elapsedMs: payload.elapsedMs,
|
||||||
failure: payload.failure,
|
failure: payload.failure,
|
||||||
|
failureTitleZh: payload.failureTitleZh ?? payload.errorTitleZh ?? null,
|
||||||
|
errorTitleZh: payload.errorTitleZh ?? payload.failureTitleZh ?? null,
|
||||||
|
timeoutDisplay: payload.timeoutDisplay ?? null,
|
||||||
warnings: Array.isArray(payload.warnings) ? payload.warnings : [],
|
warnings: Array.isArray(payload.warnings) ? payload.warnings : [],
|
||||||
analysis: compactQuickVerifyRecordAnalysis(payload.analysis),
|
analysis: compactQuickVerifyRecordAnalysis(payload.analysis),
|
||||||
promptSource: payload.promptSource,
|
promptSource: payload.promptSource,
|
||||||
@@ -1822,7 +1836,7 @@ function observerCommandFailureBlocks(item: Record<string, unknown>): boolean {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function quickVerifyControlFindings(failure: string | null, promptIndex: number, turnSummary: Record<string, unknown> | null, traceFrame: Record<string, unknown> | null): Record<string, unknown>[] {
|
function quickVerifyControlFindings(failure: string | null, promptIndex: number, turnSummary: Record<string, unknown> | null, traceFrame: Record<string, unknown> | null, display: Record<string, unknown> | null = null): Record<string, unknown>[] {
|
||||||
if (quickVerifyHasDurableBusinessTurn(promptIndex, turnSummary, traceFrame)) return [];
|
if (quickVerifyHasDurableBusinessTurn(promptIndex, turnSummary, traceFrame)) return [];
|
||||||
const turnDiagnostics = quickVerifyTurnSummaryDiagnostics(promptIndex, turnSummary);
|
const turnDiagnostics = quickVerifyTurnSummaryDiagnostics(promptIndex, turnSummary);
|
||||||
const traceDiagnostics = quickVerifyTraceFrameDiagnostics(traceFrame);
|
const traceDiagnostics = quickVerifyTraceFrameDiagnostics(traceFrame);
|
||||||
@@ -1834,13 +1848,17 @@ function quickVerifyControlFindings(failure: string | null, promptIndex: number,
|
|||||||
if (noPromptScenario && failure === null) return [];
|
if (noPromptScenario && failure === null) return [];
|
||||||
if (noPromptScenario && failure !== null) {
|
if (noPromptScenario && failure !== null) {
|
||||||
const observerStartFailure = failure === "observe-start-failed";
|
const observerStartFailure = failure === "observe-start-failed";
|
||||||
|
const displayTitleZh = stringAtNullable(display ?? {}, "titleZh");
|
||||||
return [{
|
return [{
|
||||||
id: observerStartFailure ? "quick-verify-observer-start-failed" : "quick-verify-command-sequence-failed",
|
id: observerStartFailure ? "quick-verify-observer-start-failed" : "quick-verify-command-sequence-failed",
|
||||||
severity: "red",
|
severity: "red",
|
||||||
count: 1,
|
count: 1,
|
||||||
summary: observerStartFailure
|
summary: displayTitleZh ?? (observerStartFailure
|
||||||
? "quick verify observer failed to start before the no-prompt scenario could run."
|
? "quick verify observer failed to start before the no-prompt scenario could run."
|
||||||
: "quick verify no-prompt command sequence failed before the account/session workflow completed.",
|
: "quick verify no-prompt command sequence failed before the account/session workflow completed."),
|
||||||
|
displayTitleZh,
|
||||||
|
errorTitleZh: displayTitleZh,
|
||||||
|
timeoutDisplay: display,
|
||||||
failure,
|
failure,
|
||||||
promptIndex,
|
promptIndex,
|
||||||
valuesRedacted: true,
|
valuesRedacted: true,
|
||||||
@@ -1849,11 +1867,15 @@ function quickVerifyControlFindings(failure: string | null, promptIndex: number,
|
|||||||
const noTrace = /无\s*sendPrompt|no\s+sendPrompt|无\s*trace\s*rows|no\s+trace\s+rows|traceId=-|routeSession=-|activeSession=-/iu.test(rendered);
|
const noTrace = /无\s*sendPrompt|no\s+sendPrompt|无\s*trace\s*rows|no\s+trace\s+rows|traceId=-|routeSession=-|activeSession=-/iu.test(rendered);
|
||||||
const emptyFinal = /Final Response[\s\S]*\(空内容\)/iu.test(rendered);
|
const emptyFinal = /Final Response[\s\S]*\(空内容\)/iu.test(rendered);
|
||||||
if (!noTrace && !emptyFinal && failure !== "observe-start-failed") return [];
|
if (!noTrace && !emptyFinal && failure !== "observe-start-failed") return [];
|
||||||
|
const displayTitleZh = stringAtNullable(display ?? {}, "titleZh");
|
||||||
return [{
|
return [{
|
||||||
id: "quick-verify-no-business-turn",
|
id: "quick-verify-no-business-turn",
|
||||||
severity: "red",
|
severity: "red",
|
||||||
count: 1,
|
count: 1,
|
||||||
summary: "quick verify did not reach a durable business turn/session/trace rows/final response; public dashboard health cannot be treated as HWLAB recovery.",
|
summary: displayTitleZh ?? "quick verify did not reach a durable business turn/session/trace rows/final response; public dashboard health cannot be treated as HWLAB recovery.",
|
||||||
|
displayTitleZh,
|
||||||
|
errorTitleZh: displayTitleZh,
|
||||||
|
timeoutDisplay: display,
|
||||||
rootCause: `quick verify could not confirm a durable completed turn: turn-summary scopedRows=${String(turnDiagnostics.scopedRowCount ?? 0)} rowCount=${String(turnDiagnostics.rowCount ?? 0)}, traceFrame traceIdPresent=${traceDiagnostics.traceIdPresent === true} finalResponseEmpty=${traceDiagnostics.finalResponseEmpty === true}.`,
|
rootCause: `quick verify could not confirm a durable completed turn: turn-summary scopedRows=${String(turnDiagnostics.scopedRowCount ?? 0)} rowCount=${String(turnDiagnostics.rowCount ?? 0)}, traceFrame traceIdPresent=${traceDiagnostics.traceIdPresent === true} finalResponseEmpty=${traceDiagnostics.finalResponseEmpty === true}.`,
|
||||||
rootCauseStatus: "confirmed",
|
rootCauseStatus: "confirmed",
|
||||||
rootCauseConfidence: "high",
|
rootCauseConfidence: "high",
|
||||||
@@ -2030,6 +2052,8 @@ function quickVerifyBusinessStatus(
|
|||||||
observerTimeout,
|
observerTimeout,
|
||||||
scenarioComplete: durableBusinessTurn,
|
scenarioComplete: durableBusinessTurn,
|
||||||
failure: failure ?? null,
|
failure: failure ?? null,
|
||||||
|
errorTitleZh: observerTimeout ? stringAtNullable(timeoutDisplayForQuickVerifyFailure(failure, promptIndex, budgetSeconds, null), "titleZh") : null,
|
||||||
|
timeoutDisplay: observerTimeout ? timeoutDisplayForQuickVerifyFailure(failure, promptIndex, budgetSeconds, null) : null,
|
||||||
promptIndex,
|
promptIndex,
|
||||||
elapsedMs: elapsed,
|
elapsedMs: elapsed,
|
||||||
budgetSeconds,
|
budgetSeconds,
|
||||||
@@ -2044,6 +2068,42 @@ function isRecoverableQuickVerifyWaitFailure(failure: string): boolean {
|
|||||||
|| failure === "observe-turn-terminal-wait-failed";
|
|| failure === "observe-turn-terminal-wait-failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function timeoutDisplayForFailure(failure: string | null, phase: string, targetValidationSeconds: number | null, runnerTimeoutSeconds: number | null): Record<string, unknown> {
|
||||||
|
const targetSeconds = typeof targetValidationSeconds === "number" && Number.isFinite(targetValidationSeconds) ? Math.max(1, Math.trunc(targetValidationSeconds)) : null;
|
||||||
|
const runnerSeconds = typeof runnerTimeoutSeconds === "number" && Number.isFinite(runnerTimeoutSeconds) ? Math.max(1, Math.trunc(runnerTimeoutSeconds)) : null;
|
||||||
|
const effectiveSeconds = targetSeconds ?? runnerSeconds;
|
||||||
|
const timeout = isTimeoutFailure(failure);
|
||||||
|
const phaseTitle = phase === "observe-wait-turn-terminal" ? "Workbench 等待终态" : phase === "observe-wait-startup-ready" ? "Workbench 启动等待" : "quick-verify";
|
||||||
|
const titleZh = timeout && effectiveSeconds !== null ? `${phaseTitle} 超时(${effectiveSeconds} 秒)` : failure === null ? "" : `${phaseTitle} 失败`;
|
||||||
|
return {
|
||||||
|
titleZh,
|
||||||
|
budgetKind: targetSeconds !== null ? "targetValidation" : runnerSeconds !== null ? "runnerTimeoutSeconds" : "unknown",
|
||||||
|
budgetSeconds: effectiveSeconds,
|
||||||
|
targetValidationSeconds: targetSeconds,
|
||||||
|
runnerTimeoutSeconds: runnerSeconds,
|
||||||
|
phase,
|
||||||
|
failure,
|
||||||
|
valuesRedacted: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeoutDisplayForQuickVerifyFailure(failure: string | null, promptIndex: number, targetValidationSeconds: number | null, runnerTimeoutSeconds: number | null): Record<string, unknown> {
|
||||||
|
if (!isTimeoutFailure(failure) || promptIndex <= 0) return timeoutDisplayForFailure(failure, "quick-verify", targetValidationSeconds, runnerTimeoutSeconds);
|
||||||
|
const base = timeoutDisplayForFailure(failure, "observe-wait-turn-terminal", targetValidationSeconds, runnerTimeoutSeconds);
|
||||||
|
const budgetSeconds = typeof base.budgetSeconds === "number" && Number.isFinite(base.budgetSeconds) ? Math.trunc(base.budgetSeconds) : null;
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
titleZh: budgetSeconds === null ? `Workbench 第 ${promptIndex} 轮等待终态超时` : `Workbench 第 ${promptIndex} 轮等待终态超时(${budgetSeconds} 秒)`,
|
||||||
|
round: promptIndex,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTimeoutFailure(failure: string | null): boolean {
|
||||||
|
return failure === "quick-verify-timeout-over-budget"
|
||||||
|
|| failure === "quick-verify-wait-chunk-timeout"
|
||||||
|
|| failure === "observe-turn-terminal-wait-failed";
|
||||||
|
}
|
||||||
|
|
||||||
function compactCommandWithTail(result: CommandResult): CompactCommandResult & { stdoutTail: string; stderrTail: string } {
|
function compactCommandWithTail(result: CommandResult): CompactCommandResult & { stdoutTail: string; stderrTail: string } {
|
||||||
return {
|
return {
|
||||||
...compactCommand(result),
|
...compactCommand(result),
|
||||||
@@ -2062,16 +2122,18 @@ function renderQuickVerifySummary(input: Record<string, unknown>): string {
|
|||||||
? artifact.findings.map(record).slice(0, 8)
|
? artifact.findings.map(record).slice(0, 8)
|
||||||
: [];
|
: [];
|
||||||
const findingCount = numberAtNullable(input, "findingCount") ?? numberAtNullable(artifact, "findingCount") ?? findings.length;
|
const findingCount = numberAtNullable(input, "findingCount") ?? numberAtNullable(artifact, "findingCount") ?? findings.length;
|
||||||
|
const failureTitle = stringAtNullable(input, "failureTitleZh") ?? stringAtNullable(input, "errorTitleZh") ?? stringAtNullable(record(input.timeoutDisplay), "titleZh");
|
||||||
return [
|
return [
|
||||||
"Web Probe Sentinel Quick Verify",
|
"Web Probe Sentinel Quick Verify",
|
||||||
"=======================================================",
|
"=======================================================",
|
||||||
`run=${input.runId ?? "-"} scenario=${input.scenarioId ?? "-"} observer=${input.observerId ?? "-"}`,
|
`run=${input.runId ?? "-"} scenario=${input.scenarioId ?? "-"} observer=${input.observerId ?? "-"}`,
|
||||||
|
failureTitle === null ? "" : `errorTitle=${failureTitle} failure=${input.failure ?? "-"}`,
|
||||||
`report=${artifact.reportJsonSha256 ?? "-"} artifacts=${artifact.artifactCount ?? "-"} findings=${findingCount}`,
|
`report=${artifact.reportJsonSha256 ?? "-"} artifacts=${artifact.artifactCount ?? "-"} findings=${findingCount}`,
|
||||||
`publicOrigin=${input.publicOrigin ?? "-"}`,
|
`publicOrigin=${input.publicOrigin ?? "-"}`,
|
||||||
"",
|
"",
|
||||||
"Findings",
|
"Findings",
|
||||||
findings.length === 0 ? "-" : findings.map((item) => `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"}${formatQuickVerifyTimingSuffix(item)} ${item.summary ?? item.message ?? ""}`).join("\n"),
|
findings.length === 0 ? "-" : findings.map(formatQuickVerifyFindingLine).join("\n"),
|
||||||
].join("\n");
|
].filter((line) => line !== "").join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown>): string {
|
function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown>): string {
|
||||||
@@ -2099,6 +2161,7 @@ function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown
|
|||||||
"Auth Session Switch Quick Verify",
|
"Auth Session Switch Quick Verify",
|
||||||
"=======================================================",
|
"=======================================================",
|
||||||
`run=${input.runId ?? "-"} scenario=${input.scenarioId ?? "-"} observer=${input.observerId ?? "-"}`,
|
`run=${input.runId ?? "-"} scenario=${input.scenarioId ?? "-"} observer=${input.observerId ?? "-"}`,
|
||||||
|
stringAtNullable(input, "failureTitleZh") === null && stringAtNullable(input, "errorTitleZh") === null ? "" : `errorTitle=${stringAtNullable(input, "failureTitleZh") ?? stringAtNullable(input, "errorTitleZh") ?? "-"}`,
|
||||||
`status=${artifact.ok === true ? "ok" : "blocked"} report=${artifact.reportJsonSha256 ?? "-"} publicOrigin=${input.publicOrigin ?? "-"}`,
|
`status=${artifact.ok === true ? "ok" : "blocked"} report=${artifact.reportJsonSha256 ?? "-"} publicOrigin=${input.publicOrigin ?? "-"}`,
|
||||||
`accountEnv=${accountEnv.envCount ?? "-"} valuesRedacted=true`,
|
`accountEnv=${accountEnv.envCount ?? "-"} valuesRedacted=true`,
|
||||||
"",
|
"",
|
||||||
@@ -2106,8 +2169,14 @@ function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown
|
|||||||
commandSteps.length === 0 ? "-" : commandSteps.join("\n"),
|
commandSteps.length === 0 ? "-" : commandSteps.join("\n"),
|
||||||
"",
|
"",
|
||||||
"Findings",
|
"Findings",
|
||||||
findingRows.length === 0 ? "-" : findingRows.map((item) => `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"}${formatQuickVerifyTimingSuffix(item)} ${item.summary ?? item.message ?? ""}`).join("\n"),
|
findingRows.length === 0 ? "-" : findingRows.map(formatQuickVerifyFindingLine).join("\n"),
|
||||||
].join("\n");
|
].filter((line) => line !== "").join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatQuickVerifyFindingLine(item: Record<string, unknown>): string {
|
||||||
|
const title = stringAtNullable(item, "displayTitleZh") ?? stringAtNullable(item, "errorTitleZh") ?? stringAtNullable(record(item.timeoutDisplay), "titleZh");
|
||||||
|
const summary = title ?? item.summary ?? item.message ?? "";
|
||||||
|
return `${item.severity ?? item.level ?? "-"} ${item.kind ?? item.id ?? item.code ?? "-"} count=${item.count ?? "-"}${formatQuickVerifyTimingSuffix(item)} ${summary}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatQuickVerifyTimingSuffix(item: Record<string, unknown>): string {
|
function formatQuickVerifyTimingSuffix(item: Record<string, unknown>): string {
|
||||||
|
|||||||
@@ -1004,6 +1004,9 @@ function dashboardFindings(config: WebProbeSentinelServiceConfig, db: Database,
|
|||||||
latestRunId: latestRun === null ? null : stringOrNull(latestRun.id),
|
latestRunId: latestRun === null ? null : stringOrNull(latestRun.id),
|
||||||
latestReportJsonSha256: latestRun === null ? null : stringOrNull(latestRun.report_json_sha256),
|
latestReportJsonSha256: latestRun === null ? null : stringOrNull(latestRun.report_json_sha256),
|
||||||
summary: stringOrNull(row.summary),
|
summary: stringOrNull(row.summary),
|
||||||
|
displayTitleZh: stringOrNull(latestDetail?.displayTitleZh),
|
||||||
|
errorTitleZh: stringOrNull(latestDetail?.errorTitleZh),
|
||||||
|
timeoutDisplay: record(latestDetail?.timeoutDisplay),
|
||||||
rootCause: stringOrNull(latestDetail?.rootCause),
|
rootCause: stringOrNull(latestDetail?.rootCause),
|
||||||
rootCauseStatus: stringOrNull(latestDetail?.rootCauseStatus),
|
rootCauseStatus: stringOrNull(latestDetail?.rootCauseStatus),
|
||||||
rootCauseConfidence: stringOrNull(latestDetail?.rootCauseConfidence),
|
rootCauseConfidence: stringOrNull(latestDetail?.rootCauseConfidence),
|
||||||
@@ -1860,6 +1863,9 @@ function enrichFindingRowWithStoredDetail(config: WebProbeSentinelServiceConfig,
|
|||||||
...row,
|
...row,
|
||||||
code: stringOrNull(row.finding_id),
|
code: stringOrNull(row.finding_id),
|
||||||
findingId: stringOrNull(row.finding_id),
|
findingId: stringOrNull(row.finding_id),
|
||||||
|
displayTitleZh: stringOrNull(detail?.displayTitleZh),
|
||||||
|
errorTitleZh: stringOrNull(detail?.errorTitleZh),
|
||||||
|
timeoutDisplay: record(detail?.timeoutDisplay),
|
||||||
rootCause: stringOrNull(detail?.rootCause),
|
rootCause: stringOrNull(detail?.rootCause),
|
||||||
rootCauseStatus: stringOrNull(detail?.rootCauseStatus),
|
rootCauseStatus: stringOrNull(detail?.rootCauseStatus),
|
||||||
rootCauseConfidence: stringOrNull(detail?.rootCauseConfidence),
|
rootCauseConfidence: stringOrNull(detail?.rootCauseConfidence),
|
||||||
@@ -1904,6 +1910,9 @@ function compactStoredFinding(value: unknown): Record<string, unknown> {
|
|||||||
severity,
|
severity,
|
||||||
count: numberOr(item.count, numberOr(item.sampleCount, 1)),
|
count: numberOr(item.count, numberOr(item.sampleCount, 1)),
|
||||||
summary: summary.slice(0, 500),
|
summary: summary.slice(0, 500),
|
||||||
|
displayTitleZh: stringOrNull(item.displayTitleZh),
|
||||||
|
errorTitleZh: stringOrNull(item.errorTitleZh),
|
||||||
|
timeoutDisplay: record(item.timeoutDisplay),
|
||||||
rootCause: stringOrNull(item.rootCause),
|
rootCause: stringOrNull(item.rootCause),
|
||||||
rootCauseStatus: stringOrNull(item.rootCauseStatus),
|
rootCauseStatus: stringOrNull(item.rootCauseStatus),
|
||||||
rootCauseConfidence: stringOrNull(item.rootCauseConfidence),
|
rootCauseConfidence: stringOrNull(item.rootCauseConfidence),
|
||||||
@@ -1982,6 +1991,9 @@ function enrichFindingWithCheck(config: WebProbeSentinelServiceConfig, value: Re
|
|||||||
checkTitleZh: stringOrNull(check.titleZh),
|
checkTitleZh: stringOrNull(check.titleZh),
|
||||||
checkSummaryZh: stringOrNull(check.summaryZh),
|
checkSummaryZh: stringOrNull(check.summaryZh),
|
||||||
checkActionZh: stringOrNull(check.actionZh),
|
checkActionZh: stringOrNull(check.actionZh),
|
||||||
|
displayTitleZh: stringOrNull(value.displayTitleZh) ?? stringOrNull(value.errorTitleZh),
|
||||||
|
errorTitleZh: stringOrNull(value.errorTitleZh) ?? stringOrNull(value.displayTitleZh),
|
||||||
|
timeoutDisplay: record(value.timeoutDisplay),
|
||||||
checkRegistered: check.registered === true,
|
checkRegistered: check.registered === true,
|
||||||
blocking: value.blocking === true || check.blocking === true,
|
blocking: value.blocking === true || check.blocking === true,
|
||||||
valuesRedacted: true,
|
valuesRedacted: true,
|
||||||
@@ -2580,7 +2592,7 @@ function reportRunView(config: WebProbeSentinelServiceConfig, db: Database, view
|
|||||||
function formatStoredFindingLine(item: Record<string, unknown>): string {
|
function formatStoredFindingLine(item: Record<string, unknown>): string {
|
||||||
const check = record(item.check);
|
const check = record(item.check);
|
||||||
const code = stringOrNull(item.checkCode) ?? stringOrNull(check.code) ?? stringOrNull(item.finding_id) ?? stringOrNull(item.findingId) ?? stringOrNull(item.code) ?? "-";
|
const code = stringOrNull(item.checkCode) ?? stringOrNull(check.code) ?? stringOrNull(item.finding_id) ?? stringOrNull(item.findingId) ?? stringOrNull(item.code) ?? "-";
|
||||||
const title = stringOrNull(item.checkTitleZh) ?? stringOrNull(check.titleZh) ?? stringOrNull(item.summary) ?? "";
|
const title = stringOrNull(item.displayTitleZh) ?? stringOrNull(item.errorTitleZh) ?? stringOrNull(record(item.timeoutDisplay).titleZh) ?? stringOrNull(item.checkTitleZh) ?? stringOrNull(check.titleZh) ?? stringOrNull(item.summary) ?? "";
|
||||||
const summary = stringOrNull(item.checkSummaryZh) ?? stringOrNull(check.summaryZh) ?? stringOrNull(item.summary) ?? "";
|
const summary = stringOrNull(item.checkSummaryZh) ?? stringOrNull(check.summaryZh) ?? stringOrNull(item.summary) ?? "";
|
||||||
const rootCause = stringOrNull(item.rootCause);
|
const rootCause = stringOrNull(item.rootCause);
|
||||||
const status = stringOrNull(item.rootCauseStatus);
|
const status = stringOrNull(item.rootCauseStatus);
|
||||||
@@ -2619,10 +2631,12 @@ function renderStoredSummary(
|
|||||||
const timing = dashboardRunTiming(config, row, stored);
|
const timing = dashboardRunTiming(config, row, stored);
|
||||||
const reportSha = stringOrNull(row.report_json_sha256) ?? stringOrNull(artifactSummary.reportJsonSha256);
|
const reportSha = stringOrNull(row.report_json_sha256) ?? stringOrNull(artifactSummary.reportJsonSha256);
|
||||||
const findingCount = visibleFindingTypeCount(row, findings, artifactSummary);
|
const findingCount = visibleFindingTypeCount(row, findings, artifactSummary);
|
||||||
|
const errorTitle = stringOrNull(summary.errorTitleZh) ?? stringOrNull(summary.failureTitleZh) ?? stringOrNull(record(summary.timeoutDisplay).titleZh);
|
||||||
return [
|
return [
|
||||||
"Web Probe Sentinel Report",
|
"Web Probe Sentinel Report",
|
||||||
"=======================================================",
|
"=======================================================",
|
||||||
`run=${stringOrNull(row.id) ?? "-"} scenario=${stringOrNull(row.scenario_id) ?? "-"} status=${stringOrNull(row.status) ?? "-"}`,
|
`run=${stringOrNull(row.id) ?? "-"} scenario=${stringOrNull(row.scenario_id) ?? "-"} status=${stringOrNull(row.status) ?? "-"}`,
|
||||||
|
errorTitle === null ? "" : `errorTitle=${errorTitle} failure=${stringOrNull(summary.failure) ?? "-"}`,
|
||||||
`observer=${stringOrNull(row.observer_id) ?? "-"} stateDir=${stringOrNull(row.state_dir) ?? "-"}`,
|
`observer=${stringOrNull(row.observer_id) ?? "-"} stateDir=${stringOrNull(row.state_dir) ?? "-"}`,
|
||||||
`report=${reportSha ?? "-"} artifacts=${String(row.artifact_count ?? 0)} findings=${String(findingCount)}`,
|
`report=${reportSha ?? "-"} artifacts=${String(row.artifact_count ?? 0)} findings=${String(findingCount)}`,
|
||||||
`timing=${formatRunTimingSummary(timing)}`,
|
`timing=${formatRunTimingSummary(timing)}`,
|
||||||
@@ -2631,7 +2645,7 @@ function renderStoredSummary(
|
|||||||
"",
|
"",
|
||||||
"Findings",
|
"Findings",
|
||||||
findings.length === 0 ? "-" : findings.slice(0, 12).map(formatStoredFindingLine).join("\n"),
|
findings.length === 0 ? "-" : findings.slice(0, 12).map(formatStoredFindingLine).join("\n"),
|
||||||
].join("\n");
|
].filter((line) => line !== "").join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderStoredFindings(row: Record<string, unknown>, findings: readonly Record<string, unknown>[], artifactSummary: Record<string, unknown>): string {
|
function renderStoredFindings(row: Record<string, unknown>, findings: readonly Record<string, unknown>[], artifactSummary: Record<string, unknown>): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user