fix: show quick verify timeout titles

This commit is contained in:
Codex
2026-07-02 06:52:52 +00:00
parent c8143143a0
commit 167a72eb1f
5 changed files with 113 additions and 19 deletions
@@ -527,7 +527,7 @@ function renderFindingItemCollapsed(item) {
const code = item.code || item.findingId || "finding";
const latestRunId = item.latestRunId || "-";
const hasLatestRun = latestRunId !== "-";
const codeLabel = displayFindingCode(code);
const codeLabel = displayFindingTitle(item, code);
const rootCause = findingRootCauseText(item);
const evidence = findingEvidenceText(item);
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 class="mono">${escapeHtml(item.finding_id || item.findingId || "-")}</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 class="mono">${escapeHtml(shortText(item.report_json_sha256 || item.reportJsonSha256 || "-", 24))}</td>
</tr>`).join("")}</tbody></table></div>
@@ -1296,6 +1296,8 @@ function displayFindingCode(code) {
const normalized = String(code || "").toLowerCase();
const labels = {
"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 状态三元组不一致",
"session-rail-title-fallback-root-cause": "INV-02 会话标题 fallback 根因",
"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");
}
function displayFindingTitle(item, code) {
const dynamicTitle = item?.displayTitleZh || item?.errorTitleZh || item?.timeoutDisplay?.titleZh;
return dynamicTitle ? String(dynamicTitle) : displayFindingCode(code);
}
function displayFindingSummary(code, summary) {
const normalized = String(code || "").toLowerCase();
const summaries = {
@@ -1486,6 +1486,8 @@ function rootCauseText(item) {
}
function findingTitle(item) {
const dynamicTitle = safeUserText(item?.displayTitleZh || item?.errorTitleZh || item?.timeoutDisplay?.titleZh);
if (dynamicTitle) return dynamicTitle;
const display = checkDisplay(item);
if (display.title) return display.title;
return safeUserText(item?.checkTitleZh || item?.check?.titleZh) || "未登记监测项";
@@ -1608,9 +1610,10 @@ function checkDisplay(item) {
const rawCode = rawCheckCode(item);
const rawId = rawFindingId(item);
const serviceCode = publicCheckCode(item?.checkCode || item?.check?.code);
const dynamicTitle = safeUserText(item?.displayTitleZh || item?.errorTitleZh || item?.timeoutDisplay?.titleZh);
return {
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) || "已记录监测项详情,见报告原文。",
action: safeUserText(item?.checkActionZh || item?.check?.actionZh) || "查看详情后处理",
};
+2 -1
View File
@@ -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(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.status,
targetValidationBusiness.status ?? "-",
targetValidation.errorTitleZh ?? targetValidation.failureTitleZh ?? targetValidationBusiness.errorTitleZh ?? "-",
targetValidation.scenarioId,
targetValidation.runId,
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);
for (let index = 0; index < repeat; index += 1) {
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 });
return recordQuickVerify(state, finalizeQuickVerifyFailure(state, {
runId,
@@ -200,6 +201,8 @@ export function runSentinelQuickVerify(state: SentinelCicdState, reason: string,
promptIndex,
steps,
failure: "quick-verify-timeout-over-budget",
failureTitleZh: stringAtNullable(timeoutDisplay, "titleZh"),
timeoutDisplay,
elapsedMs: elapsedMs(),
warnings: mergeWarnings(`quick verify exceeded the hard ${hardBudgetSeconds}s execution budget after the configured ${warningBudgetSeconds}s targetValidation warning budget.`, elapsedWarnings()),
promptSource: prompts.summary,
@@ -486,10 +489,15 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
readonly promptIndex: number;
readonly steps: readonly Record<string, unknown>[];
readonly failure: string;
readonly failureTitleZh?: string | null;
readonly timeoutDisplay?: Record<string, unknown> | null;
readonly promptSource?: Record<string, unknown>;
readonly elapsedMs?: number;
readonly warnings?: readonly 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>[] = [];
if (input.promptIndex > 0) {
const cancel = runChildCli([
@@ -519,7 +527,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
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 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 artifactFindings = Array.isArray(artifactSummaryRecord.findings) ? artifactSummaryRecord.findings.map(record) : [];
const visibilityFindings = quickVerifyAnalysisVisibilityFindings(analysis, artifactSummary);
@@ -533,7 +541,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
&& record(artifactSummary).ok === true
&& controlFindings.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 {
ok: recoveredWaitFailure,
runId: input.runId,
@@ -543,6 +551,9 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
observerId: input.observerId,
elapsedMs: input.elapsedMs ?? null,
businessStatus,
failureTitleZh: recoveredWaitFailure ? null : failureTitle,
errorTitleZh: recoveredWaitFailure ? null : failureTitle,
timeoutDisplay: recoveredWaitFailure ? null : failureDisplay,
stateDir: indexEntry?.stateDir ?? null,
reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"),
findingCount: findings.length,
@@ -552,8 +563,8 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
steps: [...input.steps, ...cleanupSteps],
analysis: artifactSummary,
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") }) },
"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") }) },
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"), 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 },
"trace-frame": { renderedText: typeof traceFrame.renderedText === "string" ? traceFrame.renderedText : null, ok: traceFrame.ok },
},
@@ -563,7 +574,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
warnings: mergeWarnings(
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."] : [],
targetValidationElapsedWarnings(input.elapsedMs ?? null, "quick verify confirm-wait", numberAt(state.cicd, "targetValidation.maxSeconds")),
targetValidationElapsedWarnings(input.elapsedMs ?? null, "quick verify confirm-wait", targetValidationSeconds),
),
valuesRedacted: true,
};
@@ -716,6 +727,9 @@ function recordQuickVerify(state: SentinelCicdState, payload: Record<string, unk
businessStatus: payload.businessStatus ?? null,
elapsedMs: payload.elapsedMs,
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 : [],
analysis: compactQuickVerifyRecordAnalysis(payload.analysis),
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 [];
const turnDiagnostics = quickVerifyTurnSummaryDiagnostics(promptIndex, turnSummary);
const traceDiagnostics = quickVerifyTraceFrameDiagnostics(traceFrame);
@@ -1834,13 +1848,17 @@ function quickVerifyControlFindings(failure: string | null, promptIndex: number,
if (noPromptScenario && failure === null) return [];
if (noPromptScenario && failure !== null) {
const observerStartFailure = failure === "observe-start-failed";
const displayTitleZh = stringAtNullable(display ?? {}, "titleZh");
return [{
id: observerStartFailure ? "quick-verify-observer-start-failed" : "quick-verify-command-sequence-failed",
severity: "red",
count: 1,
summary: observerStartFailure
summary: displayTitleZh ?? (observerStartFailure
? "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,
promptIndex,
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 emptyFinal = /Final Response[\s\S]*\(\)/iu.test(rendered);
if (!noTrace && !emptyFinal && failure !== "observe-start-failed") return [];
const displayTitleZh = stringAtNullable(display ?? {}, "titleZh");
return [{
id: "quick-verify-no-business-turn",
severity: "red",
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}.`,
rootCauseStatus: "confirmed",
rootCauseConfidence: "high",
@@ -2030,6 +2052,8 @@ function quickVerifyBusinessStatus(
observerTimeout,
scenarioComplete: durableBusinessTurn,
failure: failure ?? null,
errorTitleZh: observerTimeout ? stringAtNullable(timeoutDisplayForQuickVerifyFailure(failure, promptIndex, budgetSeconds, null), "titleZh") : null,
timeoutDisplay: observerTimeout ? timeoutDisplayForQuickVerifyFailure(failure, promptIndex, budgetSeconds, null) : null,
promptIndex,
elapsedMs: elapsed,
budgetSeconds,
@@ -2044,6 +2068,42 @@ function isRecoverableQuickVerifyWaitFailure(failure: string): boolean {
|| 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 } {
return {
...compactCommand(result),
@@ -2062,16 +2122,18 @@ function renderQuickVerifySummary(input: Record<string, unknown>): string {
? artifact.findings.map(record).slice(0, 8)
: [];
const findingCount = numberAtNullable(input, "findingCount") ?? numberAtNullable(artifact, "findingCount") ?? findings.length;
const failureTitle = stringAtNullable(input, "failureTitleZh") ?? stringAtNullable(input, "errorTitleZh") ?? stringAtNullable(record(input.timeoutDisplay), "titleZh");
return [
"Web Probe Sentinel Quick Verify",
"=======================================================",
`run=${input.runId ?? "-"} scenario=${input.scenarioId ?? "-"} observer=${input.observerId ?? "-"}`,
failureTitle === null ? "" : `errorTitle=${failureTitle} failure=${input.failure ?? "-"}`,
`report=${artifact.reportJsonSha256 ?? "-"} artifacts=${artifact.artifactCount ?? "-"} findings=${findingCount}`,
`publicOrigin=${input.publicOrigin ?? "-"}`,
"",
"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"),
].join("\n");
findings.length === 0 ? "-" : findings.map(formatQuickVerifyFindingLine).join("\n"),
].filter((line) => line !== "").join("\n");
}
function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown>): string {
@@ -2099,6 +2161,7 @@ function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown
"Auth Session Switch Quick Verify",
"=======================================================",
`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 ?? "-"}`,
`accountEnv=${accountEnv.envCount ?? "-"} valuesRedacted=true`,
"",
@@ -2106,8 +2169,14 @@ function renderAuthSessionSwitchQuickVerifySummary(input: Record<string, unknown
commandSteps.length === 0 ? "-" : commandSteps.join("\n"),
"",
"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"),
].join("\n");
findingRows.length === 0 ? "-" : findingRows.map(formatQuickVerifyFindingLine).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 {
+16 -2
View File
@@ -1004,6 +1004,9 @@ function dashboardFindings(config: WebProbeSentinelServiceConfig, db: Database,
latestRunId: latestRun === null ? null : stringOrNull(latestRun.id),
latestReportJsonSha256: latestRun === null ? null : stringOrNull(latestRun.report_json_sha256),
summary: stringOrNull(row.summary),
displayTitleZh: stringOrNull(latestDetail?.displayTitleZh),
errorTitleZh: stringOrNull(latestDetail?.errorTitleZh),
timeoutDisplay: record(latestDetail?.timeoutDisplay),
rootCause: stringOrNull(latestDetail?.rootCause),
rootCauseStatus: stringOrNull(latestDetail?.rootCauseStatus),
rootCauseConfidence: stringOrNull(latestDetail?.rootCauseConfidence),
@@ -1860,6 +1863,9 @@ function enrichFindingRowWithStoredDetail(config: WebProbeSentinelServiceConfig,
...row,
code: 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),
rootCauseStatus: stringOrNull(detail?.rootCauseStatus),
rootCauseConfidence: stringOrNull(detail?.rootCauseConfidence),
@@ -1904,6 +1910,9 @@ function compactStoredFinding(value: unknown): Record<string, unknown> {
severity,
count: numberOr(item.count, numberOr(item.sampleCount, 1)),
summary: summary.slice(0, 500),
displayTitleZh: stringOrNull(item.displayTitleZh),
errorTitleZh: stringOrNull(item.errorTitleZh),
timeoutDisplay: record(item.timeoutDisplay),
rootCause: stringOrNull(item.rootCause),
rootCauseStatus: stringOrNull(item.rootCauseStatus),
rootCauseConfidence: stringOrNull(item.rootCauseConfidence),
@@ -1982,6 +1991,9 @@ function enrichFindingWithCheck(config: WebProbeSentinelServiceConfig, value: Re
checkTitleZh: stringOrNull(check.titleZh),
checkSummaryZh: stringOrNull(check.summaryZh),
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,
blocking: value.blocking === true || check.blocking === true,
valuesRedacted: true,
@@ -2580,7 +2592,7 @@ function reportRunView(config: WebProbeSentinelServiceConfig, db: Database, view
function formatStoredFindingLine(item: Record<string, unknown>): string {
const check = record(item.check);
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 rootCause = stringOrNull(item.rootCause);
const status = stringOrNull(item.rootCauseStatus);
@@ -2619,10 +2631,12 @@ function renderStoredSummary(
const timing = dashboardRunTiming(config, row, stored);
const reportSha = stringOrNull(row.report_json_sha256) ?? stringOrNull(artifactSummary.reportJsonSha256);
const findingCount = visibleFindingTypeCount(row, findings, artifactSummary);
const errorTitle = stringOrNull(summary.errorTitleZh) ?? stringOrNull(summary.failureTitleZh) ?? stringOrNull(record(summary.timeoutDisplay).titleZh);
return [
"Web Probe Sentinel Report",
"=======================================================",
`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) ?? "-"}`,
`report=${reportSha ?? "-"} artifacts=${String(row.artifact_count ?? 0)} findings=${String(findingCount)}`,
`timing=${formatRunTimingSummary(timing)}`,
@@ -2631,7 +2645,7 @@ function renderStoredSummary(
"",
"Findings",
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 {