release: v1.10.0

This commit is contained in:
coso
2026-04-13 13:07:08 +08:00
parent 0f15b8a71e
commit bf03e44ac7
273 changed files with 12344 additions and 26363 deletions
+42 -157
View File
@@ -7,16 +7,13 @@ import process from "node:process";
import { fileURLToPath } from "node:url";
import { renderHarnessDashboardHtml } from "./lib/harness-dashboard-core.mjs";
import {
deriveVerificationHistoryRecordFacts as deriveSharedVerificationHistoryRecordFacts,
} from "./lib/harness-verification-facts.mjs";
const RUNNER_PATH = "scripts/harness-eval-runner.mjs";
const TREND_PATH = "scripts/harness-eval-trend-report.mjs";
const CLEANUP_PATH = "scripts/report-generated-slop.mjs";
const RECOVERED_VERIFICATION_OUTCOMES = new Set([
"repaired",
"success",
"passed",
"clean",
]);
function parseArgs(argv) {
const result = {
@@ -257,6 +254,14 @@ function writeUniqueHistorySummary(historyDir, payload) {
throw new Error(`无法在历史目录中创建唯一 summary 文件: ${historyDir}`);
}
function normalizeString(value) {
return typeof value === "string" ? value.trim() : "";
}
function normalizeNumber(value) {
return typeof value === "number" && Number.isFinite(value) ? value : 0;
}
function trimHistoryFiles(historyDir, retain) {
const files = collectHistoryFiles(historyDir).sort((left, right) =>
right.localeCompare(left),
@@ -285,145 +290,24 @@ function buildDefaultArtifactPaths(historyDir) {
};
}
function toVerificationFailureOutcomeFocus(cleanupReport) {
const currentEntries = Array.isArray(
cleanupReport?.focus?.currentObservabilityVerificationOutcomes,
)
? cleanupReport.focus.currentObservabilityVerificationOutcomes
: [];
const fallbackEntries = Array.isArray(
cleanupReport?.focus?.observabilityVerificationOutcomes,
)
? cleanupReport.focus.observabilityVerificationOutcomes
: [];
const entries =
currentEntries.length > 0 ? currentEntries : fallbackEntries;
return entries
.map((entry) => {
const signal = typeof entry?.signal === "string" ? entry.signal.trim() : "";
const outcome =
typeof entry?.outcome === "string" ? entry.outcome.trim() : "";
return signal && outcome ? `${signal}:${outcome}` : "";
})
.filter(Boolean);
}
function toCurrentRecoveredBaselineFocus(cleanupReport) {
const explicitRecoveredEntries = Array.isArray(
cleanupReport?.focus?.currentRecoveredObservabilityVerificationOutcomes,
)
? cleanupReport.focus.currentRecoveredObservabilityVerificationOutcomes
: [];
const currentEntries = Array.isArray(
cleanupReport?.focus?.currentObservabilityVerificationOutcomes,
)
? cleanupReport.focus.currentObservabilityVerificationOutcomes
: [];
const fallbackEntries = Array.isArray(
cleanupReport?.focus?.observabilityVerificationOutcomes,
)
? cleanupReport.focus.observabilityVerificationOutcomes
: [];
const entries =
explicitRecoveredEntries.length > 0
? explicitRecoveredEntries
: currentEntries.length > 0
? currentEntries
: fallbackEntries;
return entries
.filter((entry) =>
RECOVERED_VERIFICATION_OUTCOMES.has(
typeof entry?.outcome === "string" ? entry.outcome.trim() : "",
),
)
.map((entry) => {
const signal = typeof entry?.signal === "string" ? entry.signal.trim() : "";
const outcome =
typeof entry?.outcome === "string" ? entry.outcome.trim() : "";
return signal && outcome ? `${signal}:${outcome}` : "";
})
.filter(Boolean);
}
function toVerificationOutcomeCounts(cleanupReport) {
const summary =
cleanupReport &&
typeof cleanupReport === "object" &&
cleanupReport.summary &&
cleanupReport.summary.verificationOutcomes &&
typeof cleanupReport.summary.verificationOutcomes === "object"
? cleanupReport.summary.verificationOutcomes
: {};
const currentSummary =
summary &&
typeof summary.current === "object" &&
!Array.isArray(summary.current)
? summary.current
: {};
const degradedSummary =
summary &&
typeof summary.degraded === "object" &&
!Array.isArray(summary.degraded)
? summary.degraded
: {};
return {
failureCaseCount:
typeof summary.failureCaseCount === "number" &&
Number.isFinite(summary.failureCaseCount)
? summary.failureCaseCount
: 0,
blockingFailureCaseCount:
typeof currentSummary.blockingFailureCaseCount === "number" &&
Number.isFinite(currentSummary.blockingFailureCaseCount)
? currentSummary.blockingFailureCaseCount
: 0,
advisoryFailureCaseCount:
typeof currentSummary.advisoryFailureCaseCount === "number" &&
Number.isFinite(currentSummary.advisoryFailureCaseCount)
? currentSummary.advisoryFailureCaseCount
: 0,
recoveredCaseCount:
typeof summary.recoveredCaseCount === "number" &&
Number.isFinite(summary.recoveredCaseCount)
? summary.recoveredCaseCount
: 0,
currentRecoveredCaseCount:
typeof currentSummary.recoveredCaseCount === "number" &&
Number.isFinite(currentSummary.recoveredCaseCount)
? currentSummary.recoveredCaseCount
: 0,
degradedBlockingFailureCaseCount:
typeof degradedSummary.blockingFailureCaseCount === "number" &&
Number.isFinite(degradedSummary.blockingFailureCaseCount)
? degradedSummary.blockingFailureCaseCount
: 0,
};
}
function toTrendCurrentRecoveredBaselineFocus(trendReport) {
const entries = Array.isArray(
trendReport?.classificationDeltas?.currentRecoveredObservabilityVerificationOutcomes,
)
? trendReport.classificationDeltas.currentRecoveredObservabilityVerificationOutcomes
: [];
return deriveSharedVerificationHistoryRecordFacts({
summary: null,
trendReport,
cleanupReport: null,
}).currentRecoveredBaselineFocus.slice(0, 3);
}
return entries
.filter((entry) => {
const latestCaseCount =
typeof entry?.latest?.caseCount === "number" &&
Number.isFinite(entry.latest.caseCount)
? entry.latest.caseCount
: 0;
return latestCaseCount > 0;
})
.map((entry) =>
typeof entry?.name === "string" ? entry.name.trim() : "",
)
.filter(Boolean)
.slice(0, 3);
export function deriveHistoryRecordVerificationFacts({
summary,
trendReport,
cleanupReport,
}) {
return deriveSharedVerificationHistoryRecordFacts({
summary,
trendReport,
cleanupReport,
});
}
function renderOutput(result, format) {
@@ -660,32 +544,33 @@ function runHistoryRecordCli() {
},
);
cleanupReport = JSON.parse(cleanupOutput);
const verificationFailureOutcomeFocus =
toVerificationFailureOutcomeFocus(cleanupReport);
const currentRecoveredBaselineFocus =
toCurrentRecoveredBaselineFocus(cleanupReport);
const verificationOutcomeCounts =
toVerificationOutcomeCounts(cleanupReport);
const verificationFacts = deriveHistoryRecordVerificationFacts({
summary,
trendReport,
cleanupReport,
});
result.cleanup = {
trendSampleCount: cleanupReport.summary?.trend?.sampleCount ?? 0,
currentObservabilityGapCaseCount:
cleanupReport.summary?.trend?.latestCurrentObservabilityGapCaseCount ?? 0,
degradedObservabilityGapCaseCount:
cleanupReport.summary?.trend?.latestDegradedObservabilityGapCaseCount ?? 0,
verificationFailureOutcomeFocus,
verificationFailureOutcomeFocus:
verificationFacts.verificationFailureOutcomeFocus,
verificationFailureCaseCount:
verificationOutcomeCounts.failureCaseCount,
verificationFacts.verificationOutcomeCounts.failureCaseCount,
verificationBlockingFailureCaseCount:
verificationOutcomeCounts.blockingFailureCaseCount,
verificationFacts.verificationOutcomeCounts.blockingFailureCaseCount,
verificationAdvisoryFailureCaseCount:
verificationOutcomeCounts.advisoryFailureCaseCount,
verificationFacts.verificationOutcomeCounts.advisoryFailureCaseCount,
verificationDegradedBlockingFailureCaseCount:
verificationOutcomeCounts.degradedBlockingFailureCaseCount,
verificationFacts.verificationOutcomeCounts.degradedBlockingFailureCaseCount,
verificationRecoveredCaseCount:
verificationOutcomeCounts.recoveredCaseCount,
verificationFacts.verificationOutcomeCounts.recoveredCaseCount,
currentVerificationRecoveredCaseCount:
verificationOutcomeCounts.currentRecoveredCaseCount,
currentRecoveredBaselineFocus,
verificationFacts.verificationOutcomeCounts.currentRecoveredCaseCount,
currentRecoveredBaselineFocus:
verificationFacts.currentRecoveredBaselineFocus,
outputJsonPath: cleanupJsonPath,
outputMarkdownPath: cleanupMarkdownPath,
};
+60 -1
View File
@@ -23,6 +23,12 @@ const REVIEW_DECISION_RISK_LEVEL_SET = new Set([
"unknown",
]);
const OBSERVABILITY_GAP_SUITE_TAG = "observability-gap";
const RECOVERED_VERIFICATION_OUTCOMES = new Set([
"artifactValidator:repaired",
"browserVerification:success",
"guiSmoke:passed",
"guiSmoke:clean",
]);
function parseArgs(argv) {
const result = {
@@ -193,6 +199,12 @@ function aggregateCaseBreakdown(cases, selector) {
});
}
function collectRecoveredVerificationOutcomes(outcomes) {
return mergeUniqueStrings(outcomes).filter((entry) =>
RECOVERED_VERIFICATION_OUTCOMES.has(entry),
);
}
function isDegradedObservabilityGapCase(entry) {
return (
entry.observabilityGapCount > 0 &&
@@ -891,6 +903,22 @@ function buildSummary(manifest, suites, options) {
const degradedObservabilityGapCases = observabilityGapCases.filter((entry) =>
isDegradedObservabilityGapCase(entry),
);
const currentObservabilityDiagnosticCases = allCases.filter((entry) =>
isCurrentObservabilityDiagnosticCase(entry),
);
const currentRecoveredObservabilityVerificationOutcomes =
aggregateCaseBreakdown(
currentObservabilityDiagnosticCases,
(entry) =>
collectRecoveredVerificationOutcomes(
entry.observabilityVerificationOutcomes,
),
);
const currentRecoveredVerificationCaseCount =
currentRecoveredObservabilityVerificationOutcomes.reduce(
(total, entry) => total + entry.caseCount,
0,
);
return {
manifestVersion: String(manifest.manifestVersion ?? "unknown"),
@@ -910,6 +938,7 @@ function buildSummary(manifest, suites, options) {
observabilityGapCaseCount: observabilityGapCases.length,
currentObservabilityGapCaseCount: currentObservabilityGapCases.length,
degradedObservabilityGapCaseCount: degradedObservabilityGapCases.length,
currentRecoveredVerificationCaseCount,
},
breakdowns: {
suiteTags: aggregateCaseBreakdown(allCases, (entry) => entry.tags),
@@ -932,9 +961,10 @@ function buildSummary(manifest, suites, options) {
(entry) => entry.observabilityVerificationOutcomes,
),
currentObservabilityVerificationOutcomes: aggregateCaseBreakdown(
allCases.filter((entry) => isCurrentObservabilityDiagnosticCase(entry)),
currentObservabilityDiagnosticCases,
(entry) => entry.observabilityVerificationOutcomes,
),
currentRecoveredObservabilityVerificationOutcomes,
degradedObservabilityVerificationOutcomes: aggregateCaseBreakdown(
allCases.filter((entry) => isDegradedObservabilityDiagnosticCase(entry)),
(entry) => entry.observabilityVerificationOutcomes,
@@ -958,6 +988,7 @@ function renderText(summary) {
`[harness-eval] observability-gap cases: ${summary.totals.observabilityGapCaseCount}`,
`[harness-eval] current observability-gap cases: ${summary.totals.currentObservabilityGapCaseCount}`,
`[harness-eval] degraded observability-gap cases: ${summary.totals.degradedObservabilityGapCaseCount}`,
`[harness-eval] current recovered verification cases: ${summary.totals.currentRecoveredVerificationCaseCount}`,
];
const topFailureModes = summary.breakdowns.failureModes.slice(0, 5);
@@ -1014,6 +1045,20 @@ function renderText(summary) {
}
}
const topCurrentRecoveredVerificationOutcomes =
summary.breakdowns.currentRecoveredObservabilityVerificationOutcomes.slice(
0,
5,
);
if (topCurrentRecoveredVerificationOutcomes.length > 0) {
lines.push("[harness-eval] current recovered verification outcomes:");
for (const entry of topCurrentRecoveredVerificationOutcomes) {
lines.push(
` - ${entry.name}: case=${entry.caseCount}, ready=${entry.readyCount}, invalid=${entry.invalidCount}`,
);
}
}
for (const suite of summary.suites) {
lines.push(
`[harness-eval] suite ${suite.id}: ready ${suite.stats.readyCount} / ${suite.stats.caseCount}`,
@@ -1069,6 +1114,7 @@ function renderMarkdown(summary) {
`- observability gap case:${summary.totals.observabilityGapCaseCount}`,
`- current observability gap case:${summary.totals.currentObservabilityGapCaseCount}`,
`- degraded observability gap case:${summary.totals.degradedObservabilityGapCaseCount}`,
`- current recovered verification case:${summary.totals.currentRecoveredVerificationCaseCount}`,
"",
];
@@ -1152,6 +1198,19 @@ function renderMarkdown(summary) {
lines.push("");
}
if (summary.breakdowns.currentRecoveredObservabilityVerificationOutcomes.length > 0) {
lines.push("## Current Recovered Verification Outcome 分布");
lines.push("");
lines.push("| Outcome | case | ready | invalid |");
lines.push("| --- | --- | --- | --- |");
for (const entry of summary.breakdowns.currentRecoveredObservabilityVerificationOutcomes) {
lines.push(
`| ${entry.name} | ${entry.caseCount} | ${entry.readyCount} | ${entry.invalidCount} |`,
);
}
lines.push("");
}
for (const suite of summary.suites) {
lines.push(`## ${suite.title}`);
lines.push("");
+50 -21
View File
@@ -256,10 +256,8 @@ function buildNormalizedTotals(summary) {
const rawTotals =
summary?.totals && typeof summary.totals === "object" ? summary.totals : {};
const gapTotals = buildObservabilityGapTotals(summary);
const currentVerificationOutcomeEntries = getBreakdownEntries(
summary,
"currentObservabilityVerificationOutcomes",
);
const currentRecoveredVerificationEntries =
getCurrentRecoveredVerificationEntries(summary);
return {
suiteCount: normalizeNumber(rawTotals.suiteCount),
caseCount: normalizeNumber(rawTotals.caseCount),
@@ -274,11 +272,10 @@ function buildNormalizedTotals(summary) {
currentObservabilityGapCaseCount: gapTotals.current,
degradedObservabilityGapCaseCount: gapTotals.degraded,
currentRecoveredVerificationCaseCount:
currentVerificationOutcomeEntries.length > 0
? getBreakdownCaseCount(
summary,
"currentObservabilityVerificationOutcomes",
RECOVERED_VERIFICATION_OUTCOMES,
currentRecoveredVerificationEntries.length > 0
? currentRecoveredVerificationEntries.reduce(
(total, entry) => total + normalizeNumber(entry?.caseCount),
0,
)
: normalizeNumber(rawTotals.currentRecoveredVerificationCaseCount),
};
@@ -326,6 +323,21 @@ function getBreakdownMap(summary, key) {
);
}
function getCurrentRecoveredVerificationEntries(summary) {
const explicitEntries = getBreakdownEntries(
summary,
"currentRecoveredObservabilityVerificationOutcomes",
);
if (explicitEntries.length > 0) {
return explicitEntries;
}
return getBreakdownEntries(
summary,
"currentObservabilityVerificationOutcomes",
).filter((entry) => RECOVERED_VERIFICATION_OUTCOMES.has(entry.name));
}
function buildSuiteDeltas(baseline, latest) {
const baselineSuites = getSuiteMap(baseline);
const latestSuites = getSuiteMap(latest);
@@ -428,6 +440,32 @@ function buildFilteredBreakdownDeltas(baseline, latest, key, predicate) {
);
}
function buildCurrentRecoveredVerificationDeltas(baseline, latest) {
const baselineExplicitEntries = getBreakdownEntries(
baseline,
"currentRecoveredObservabilityVerificationOutcomes",
);
const latestExplicitEntries = getBreakdownEntries(
latest,
"currentRecoveredObservabilityVerificationOutcomes",
);
if (baselineExplicitEntries.length > 0 || latestExplicitEntries.length > 0) {
return buildBreakdownDeltas(
baseline,
latest,
"currentRecoveredObservabilityVerificationOutcomes",
);
}
return buildFilteredBreakdownDeltas(
baseline,
latest,
"currentObservabilityVerificationOutcomes",
(entry) => RECOVERED_VERIFICATION_OUTCOMES.has(entry.name),
);
}
function buildStatusSignals(baseline, latest, sampleCount) {
const signals = [];
@@ -507,12 +545,8 @@ function buildStatusSignals(baseline, latest, sampleCount) {
);
}
const currentRecoveredVerificationDeltas = buildFilteredBreakdownDeltas(
baseline,
latest,
"currentObservabilityVerificationOutcomes",
(entry) => RECOVERED_VERIFICATION_OUTCOMES.has(entry.name),
);
const currentRecoveredVerificationDeltas =
buildCurrentRecoveredVerificationDeltas(baseline, latest);
for (const entry of currentRecoveredVerificationDeltas.filter(
(candidate) => candidate.delta.caseCount < 0,
)) {
@@ -631,12 +665,7 @@ function buildTrendReport(samples, repoRoot) {
"observabilityVerificationOutcomes",
),
currentRecoveredObservabilityVerificationOutcomes:
buildFilteredBreakdownDeltas(
baseline,
latest,
"currentObservabilityVerificationOutcomes",
(entry) => RECOVERED_VERIFICATION_OUTCOMES.has(entry.name),
),
buildCurrentRecoveredVerificationDeltas(baseline, latest),
currentObservabilityVerificationOutcomes: buildBreakdownDeltas(
baseline,
latest,
+95 -636
View File
@@ -4,6 +4,22 @@ import {
getTextCountStatus,
getTextStatus,
} from "./legacy-surface-report-summary.mjs";
import {
buildAdvisoryVerificationRecommendationRationale,
buildBlockingVerificationRecommendationRationale,
buildObservabilityRecommendationBacklog,
buildObservabilityRecommendationRationale,
buildVerificationOutcomeSignalMessages,
buildRecoveredVerificationRecommendationRationale,
buildAdvisoryVerificationFollowUp,
buildBlockingVerificationFollowUp,
buildRecoveredVerificationFollowUp,
buildVerificationOutcomeSummary,
deriveVerificationOutcomePresentationFromTrend,
formatVerificationOutcomeCompactLabel,
formatVerificationOutcomeCompactLabels,
getVerificationOutcomeRole,
} from "./harness-verification-facts.mjs";
const PRIORITY_RANK = {
P0: 0,
@@ -544,277 +560,6 @@ function buildObservabilityFocusEntries(entries, sampleCount) {
});
}
const OBSERVABILITY_VERIFICATION_FAILURE_OUTCOMES = new Set([
"issues_present",
"fallback_used",
"failure",
"unknown",
"failed",
]);
const OBSERVABILITY_VERIFICATION_RECOVERED_OUTCOMES = new Set([
"repaired",
"success",
"passed",
"clean",
]);
function isObservabilityVerificationFailureOutcome(outcome) {
return OBSERVABILITY_VERIFICATION_FAILURE_OUTCOMES.has(
normalizeString(outcome),
);
}
function isObservabilityVerificationRecoveredOutcome(outcome) {
return OBSERVABILITY_VERIFICATION_RECOVERED_OUTCOMES.has(
normalizeString(outcome),
);
}
const BLOCKING_VERIFICATION_FAILURES = new Set([
"browserVerification:failure",
"guiSmoke:failed",
]);
function getObservabilityVerificationOutcomeRole(signal, outcome) {
const normalizedSignal = normalizeString(signal);
const normalizedOutcome = normalizeString(outcome);
const fingerprint = `${normalizedSignal}:${normalizedOutcome}`;
if (BLOCKING_VERIFICATION_FAILURES.has(fingerprint)) {
return "blocking_failure";
}
if (isObservabilityVerificationFailureOutcome(normalizedOutcome)) {
return "advisory_failure";
}
if (isObservabilityVerificationRecoveredOutcome(normalizedOutcome)) {
return "recovered";
}
return "other";
}
function getObservabilityVerificationOutcomeWeight(outcome) {
switch (normalizeString(outcome)) {
case "failed":
return 140;
case "failure":
return 130;
case "unknown":
return 115;
case "fallback_used":
return 110;
case "issues_present":
return 100;
case "repaired":
return 70;
default:
return 0;
}
}
function buildObservabilityVerificationFocusEntries(entries, sampleCount) {
const normalizedEntries = Array.isArray(entries) ? entries : [];
return normalizedEntries
.map((entry) => {
const latest = isObject(entry?.latest) ? entry.latest : {};
const delta = isObject(entry?.delta) ? entry.delta : {};
const baseline = isObject(entry?.baseline) ? entry.baseline : {};
const parsed = splitObservabilitySignalName(entry?.name);
const positiveDeltaCase = Math.max(0, normalizeNumber(delta.caseCount));
const latestCase = normalizeNumber(latest.caseCount);
const weight = getObservabilityVerificationOutcomeWeight(parsed.status);
const score = positiveDeltaCase * 140 + latestCase * weight;
let state = "stable";
if (sampleCount < 2 && latestCase > 0 && weight > 0) {
state = "seed-risk";
} else if (positiveDeltaCase > 0 && weight > 0) {
state = "regressing";
} else if (latestCase > 0 && weight > 0) {
state = "present";
}
return {
name: normalizeString(entry?.name, "(unknown)"),
signal: parsed.signal || "(unknown)",
outcome: parsed.status || "unknown",
baseline: {
caseCount: normalizeNumber(baseline.caseCount),
readyCount: normalizeNumber(baseline.readyCount),
invalidCount: normalizeNumber(baseline.invalidCount),
pendingRequestCaseCount: normalizeNumber(
baseline.pendingRequestCaseCount,
),
needsHumanReviewCount: normalizeNumber(
baseline.needsHumanReviewCount,
),
},
latest: {
caseCount: latestCase,
readyCount: normalizeNumber(latest.readyCount),
invalidCount: normalizeNumber(latest.invalidCount),
pendingRequestCaseCount: normalizeNumber(
latest.pendingRequestCaseCount,
),
needsHumanReviewCount: normalizeNumber(
latest.needsHumanReviewCount,
),
},
delta: {
caseCount: normalizeNumber(delta.caseCount),
readyCount: normalizeNumber(delta.readyCount),
invalidCount: normalizeNumber(delta.invalidCount),
pendingRequestCaseCount: normalizeNumber(delta.pendingRequestCaseCount),
needsHumanReviewCount: normalizeNumber(delta.needsHumanReviewCount),
},
state,
score,
};
})
.filter(
(entry) =>
entry.score > 0 ||
(entry.latest.caseCount > 0 &&
isObservabilityVerificationFailureOutcome(entry.outcome)),
)
.sort((left, right) => {
if (right.score !== left.score) {
return right.score - left.score;
}
return left.name.localeCompare(right.name);
});
}
function buildVerificationOutcomeEntriesFromDeltas(entries) {
const normalizedEntries = Array.isArray(entries) ? entries : [];
return normalizedEntries
.map((entry) => {
const latest = isObject(entry?.latest) ? entry.latest : {};
const delta = isObject(entry?.delta) ? entry.delta : {};
const baseline = isObject(entry?.baseline) ? entry.baseline : {};
const parsed = splitObservabilitySignalName(entry?.name);
const latestCase = normalizeNumber(latest.caseCount);
const deltaCase = normalizeNumber(delta.caseCount);
let state = "stable";
if (deltaCase > 0) {
state = "expanding";
} else if (deltaCase < 0) {
state = "shrinking";
} else if (latestCase > 0) {
state = "present";
}
return {
name: normalizeString(entry?.name, "(unknown)"),
signal: parsed.signal || "(unknown)",
outcome: parsed.status || "unknown",
baseline: {
caseCount: normalizeNumber(baseline.caseCount),
readyCount: normalizeNumber(baseline.readyCount),
invalidCount: normalizeNumber(baseline.invalidCount),
pendingRequestCaseCount: normalizeNumber(
baseline.pendingRequestCaseCount,
),
needsHumanReviewCount: normalizeNumber(
baseline.needsHumanReviewCount,
),
},
latest: {
caseCount: latestCase,
readyCount: normalizeNumber(latest.readyCount),
invalidCount: normalizeNumber(latest.invalidCount),
pendingRequestCaseCount: normalizeNumber(
latest.pendingRequestCaseCount,
),
needsHumanReviewCount: normalizeNumber(
latest.needsHumanReviewCount,
),
},
delta: {
caseCount: deltaCase,
readyCount: normalizeNumber(delta.readyCount),
invalidCount: normalizeNumber(delta.invalidCount),
pendingRequestCaseCount: normalizeNumber(delta.pendingRequestCaseCount),
needsHumanReviewCount: normalizeNumber(delta.needsHumanReviewCount),
},
state,
score: latestCase * 10 + Math.abs(deltaCase) * 5,
};
})
.filter((entry) => entry.latest.caseCount > 0 || entry.delta.caseCount !== 0)
.sort((left, right) => {
if (right.latest.caseCount !== left.latest.caseCount) {
return right.latest.caseCount - left.latest.caseCount;
}
if (Math.abs(right.delta.caseCount) !== Math.abs(left.delta.caseCount)) {
return Math.abs(right.delta.caseCount) - Math.abs(left.delta.caseCount);
}
return left.name.localeCompare(right.name);
});
}
function buildVerificationOutcomeSummary(focusEntries) {
const entries = Array.isArray(focusEntries) ? focusEntries : [];
const blockingFailureEntries = entries.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) ===
"blocking_failure",
);
const advisoryFailureEntries = entries.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) ===
"advisory_failure",
);
const failureEntries = [...blockingFailureEntries, ...advisoryFailureEntries];
const recoveredEntries = entries.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) ===
"recovered",
);
return {
focusCount: entries.length,
failureFocusCount: failureEntries.length,
recoveredFocusCount: recoveredEntries.length,
blockingFailureFocusCount: blockingFailureEntries.length,
advisoryFailureFocusCount: advisoryFailureEntries.length,
failureCaseCount: failureEntries.reduce(
(total, entry) => total + normalizeNumber(entry?.latest?.caseCount),
0,
),
blockingFailureCaseCount: blockingFailureEntries.reduce(
(total, entry) => total + normalizeNumber(entry?.latest?.caseCount),
0,
),
advisoryFailureCaseCount: advisoryFailureEntries.reduce(
(total, entry) => total + normalizeNumber(entry?.latest?.caseCount),
0,
),
recoveredCaseCount: recoveredEntries.reduce(
(total, entry) => total + normalizeNumber(entry?.latest?.caseCount),
0,
),
topFailureOutcomes: failureEntries
.slice(0, 3)
.map((entry) => `${entry.signal}:${entry.outcome}`),
topBlockingFailureOutcomes: blockingFailureEntries
.slice(0, 3)
.map((entry) => `${entry.signal}:${entry.outcome}`),
topAdvisoryFailureOutcomes: advisoryFailureEntries
.slice(0, 3)
.map((entry) => `${entry.signal}:${entry.outcome}`),
topRecoveredOutcomes: recoveredEntries
.slice(0, 3)
.map((entry) => `${entry.signal}:${entry.outcome}`),
};
}
function buildDocFreshnessSummary(docFreshnessReport) {
const summary = isObject(docFreshnessReport?.summary)
? docFreshnessReport.summary
@@ -993,8 +738,8 @@ export function assertGeneratedSlopReportContract(report) {
if (
verificationFailureFocus.some(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) ===
"recovered",
getVerificationOutcomeRole(entry?.signal, entry?.outcome) ===
"recovered",
)
) {
throw new Error(
@@ -1005,183 +750,6 @@ export function assertGeneratedSlopReportContract(report) {
return report;
}
function hasObservabilityVerificationOutcome(entries, signal, outcome) {
const normalizedEntries = Array.isArray(entries) ? entries : [];
const normalizedSignal = normalizeString(signal);
const normalizedOutcome = normalizeString(outcome);
return normalizedEntries.some(
(entry) =>
normalizeString(entry?.signal) === normalizedSignal &&
normalizeString(entry?.outcome) === normalizedOutcome,
);
}
function buildCurrentBlockingVerificationFollowUp(
focusCurrentObservabilityVerificationOutcomes,
) {
const hasCurrentGuiSmokeFailure = hasObservabilityVerificationOutcome(
focusCurrentObservabilityVerificationOutcomes,
"guiSmoke",
"failed",
);
const hasCurrentBrowserVerificationFailure =
hasObservabilityVerificationOutcome(
focusCurrentObservabilityVerificationOutcomes,
"browserVerification",
"failure",
);
const commands = ["npm run harness:eval", "npm run harness:eval:trend"];
const backlogTools = [];
const rationale = [];
if (hasCurrentGuiSmokeFailure) {
commands.push("npm run verify:gui-smoke");
rationale.push(
"current 样本已出现 guiSmoke:failed,先恢复 GUI 壳 / DevBridge / Workspace 主路径的最小可启动性。",
);
backlogTools.push(
"优先收敛 GUI 壳 / DevBridge / Workspace 主路径,再复跑 `npm run verify:gui-smoke`。",
);
}
if (hasCurrentBrowserVerificationFailure) {
rationale.push(
"current 样本已出现 browserVerification:failure,应先回看 browser replay / verification 失败样本,把失败断言回挂到受影响主路径。",
);
backlogTools.push(
"回看 browser replay / browser verification 失败样本,并把失败断言回挂到受影响主路径。",
);
}
if (backlogTools.length === 0) {
backlogTools.push("按受影响主路径追加 `npm run verify:gui-smoke` 或专项 smoke");
}
return {
commands: dedupeNonEmptyStrings(commands),
backlogTools: dedupeNonEmptyStrings(backlogTools),
rationale: dedupeNonEmptyStrings(rationale),
};
}
function buildCurrentAdvisoryVerificationFollowUp(
focusCurrentObservabilityVerificationOutcomes,
) {
const hasArtifactValidatorIssuesPresent = hasObservabilityVerificationOutcome(
focusCurrentObservabilityVerificationOutcomes,
"artifactValidator",
"issues_present",
);
const hasArtifactValidatorFallbackUsed = hasObservabilityVerificationOutcome(
focusCurrentObservabilityVerificationOutcomes,
"artifactValidator",
"fallback_used",
);
const hasBrowserVerificationUnknown = hasObservabilityVerificationOutcome(
focusCurrentObservabilityVerificationOutcomes,
"browserVerification",
"unknown",
);
const rationale = [];
const backlogTools = [];
if (hasArtifactValidatorIssuesPresent) {
rationale.push(
"current 样本已出现 artifactValidator:issues_present,应先回看 validator issue 明细,再收敛 artifact 导出字段。",
);
backlogTools.push(
"回看 artifact validator issue 明细,并收敛 evidence pack / artifacts.json / analysis handoff 的 artifact 字段。",
);
}
if (hasArtifactValidatorFallbackUsed) {
rationale.push(
"current 样本已出现 artifactValidator:fallback_used,说明 artifact 主路径仍不稳定,不能继续依赖 fallback 充当事实。",
);
backlogTools.push(
"补齐 artifact 主路径导出与修复链,减少 fallback_used 持续留在 current 样本。",
);
}
if (hasBrowserVerificationUnknown) {
rationale.push(
"current 样本已出现 browserVerification:unknown,需要先把浏览器验证结果收敛成明确 outcome,再继续扩大分析。",
);
backlogTools.push(
"回看 browser verification 导出链,确保 evidence pack / replay / analysis handoff 写出明确 success 或 failure,而不是 unknown。",
);
}
if (backlogTools.length === 0) {
backlogTools.push(
"先对齐 current verification outcome 到 artifact/browser/gui 主路径,再继续补 observability 证据。",
);
}
return {
rationale: dedupeNonEmptyStrings(rationale),
backlogTools: dedupeNonEmptyStrings(backlogTools),
};
}
function buildCurrentRecoveredVerificationFollowUp(
focusCurrentRecoveredObservabilityVerificationOutcomes,
) {
const hasArtifactValidatorRepaired = hasObservabilityVerificationOutcome(
focusCurrentRecoveredObservabilityVerificationOutcomes,
"artifactValidator",
"repaired",
);
const hasBrowserVerificationSuccess = hasObservabilityVerificationOutcome(
focusCurrentRecoveredObservabilityVerificationOutcomes,
"browserVerification",
"success",
);
const hasGuiSmokePassed = hasObservabilityVerificationOutcome(
focusCurrentRecoveredObservabilityVerificationOutcomes,
"guiSmoke",
"passed",
);
const commands = ["npm run harness:eval", "npm run harness:eval:trend"];
const rationale = [];
const backlogTools = [];
if (hasArtifactValidatorRepaired) {
rationale.push(
"current 样本已出现 artifactValidator:repaired,说明 artifact 修复链已经回到可复用的主路径。",
);
backlogTools.push(
"在 evidence pack / analysis handoff 里同时保留 artifact issue 与 repaired outcome,避免只剩修复结论而丢失修复上下文。",
);
}
if (hasBrowserVerificationSuccess) {
rationale.push(
"current 样本已出现 browserVerification:success,可把浏览器验证成功样本固化成主路径正向基线。",
);
backlogTools.push(
"把 browser verification 成功样本固定进 current replay 基线,后续 failure 或 unknown 直接对比这条正向路径。",
);
}
if (hasGuiSmokePassed) {
commands.push("npm run verify:gui-smoke");
rationale.push(
"current 样本已出现 guiSmoke:passed,可继续把 GUI smoke 通过链路当成桌面主路径的正向守卫。",
);
backlogTools.push(
"主路径变更时优先复跑 `npm run verify:gui-smoke`,确认 GUI 壳 / DevBridge / Workspace 不从 passed 回退。",
);
}
return {
commands: dedupeNonEmptyStrings(commands),
rationale: dedupeNonEmptyStrings(rationale),
backlogTools: dedupeNonEmptyStrings(backlogTools),
};
}
function buildRecommendations({
trendSummary,
verificationOutcomeSummary,
@@ -1213,21 +781,25 @@ function buildRecommendations({
const topObservabilitySignals = focusObservabilitySignals
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.status})`);
const topVerificationFailureOutcomes = focusVerificationFailureOutcomes
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.outcome})`);
const topVerificationFailureOutcomes = formatVerificationOutcomeCompactLabels(
focusVerificationFailureOutcomes,
3,
);
const topCurrentVerificationFailureOutcomes =
focusCurrentObservabilityVerificationOutcomes
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.outcome})`);
formatVerificationOutcomeCompactLabels(
focusCurrentObservabilityVerificationOutcomes,
3,
);
const topCurrentRecoveredVerificationOutcomes =
focusCurrentRecoveredObservabilityVerificationOutcomes
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.outcome})`);
formatVerificationOutcomeCompactLabels(
focusCurrentRecoveredObservabilityVerificationOutcomes,
3,
);
const topDegradedVerificationFailureOutcomes =
focusDegradedObservabilityVerificationOutcomes
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.outcome})`);
formatVerificationOutcomeCompactLabels(
focusDegradedObservabilityVerificationOutcomes,
3,
);
const topRecommendedVerificationFailureOutcomes =
topCurrentVerificationFailureOutcomes.length > 0
? topCurrentVerificationFailureOutcomes
@@ -1241,15 +813,15 @@ function buildRecommendations({
const degradedVerificationSummary =
verificationOutcomeSummary?.degraded ?? buildVerificationOutcomeSummary([]);
const currentBlockingVerificationFollowUp =
buildCurrentBlockingVerificationFollowUp(
buildBlockingVerificationFollowUp(
focusCurrentObservabilityVerificationOutcomes,
);
const currentAdvisoryVerificationFollowUp =
buildCurrentAdvisoryVerificationFollowUp(
buildAdvisoryVerificationFollowUp(
focusCurrentObservabilityVerificationOutcomes,
);
const currentRecoveredVerificationFollowUp =
buildCurrentRecoveredVerificationFollowUp(
buildRecoveredVerificationFollowUp(
focusCurrentRecoveredObservabilityVerificationOutcomes,
);
@@ -1314,16 +886,13 @@ function buildRecommendations({
rationale: [
`当前 failure mode 焦点:${topFailureModes.join("、") || "暂无"}。`,
"先用 replay / eval 固化失败,再按受影响主路径补最小 smoke,而不是直接凭印象清理。",
topCurrentVerificationFailureOutcomes.length > 0
? `当前 current verification failure outcome 焦点:${topCurrentVerificationFailureOutcomes.join("、")}。`
: "当前没有额外的 verification failure outcome 焦点。",
...buildBlockingVerificationRecommendationRationale({
topCurrentVerificationFailureOutcomes:
focusCurrentObservabilityVerificationOutcomes,
currentVerificationSummary,
degradedVerificationSummary,
}),
...currentBlockingVerificationFollowUp.rationale,
currentVerificationSummary.blockingFailureCaseCount > 0
? `其中 current blocking verification failure 共 ${currentVerificationSummary.blockingFailureCaseCount} 个 case:${currentVerificationSummary.topBlockingFailureOutcomes.join("、") || "暂无"}。`
: "当前没有额外的 blocking verification failure。",
degradedVerificationSummary.blockingFailureCaseCount > 0
? `另有 ${degradedVerificationSummary.blockingFailureCaseCount} 个 degraded blocking verification failure 样本作为诊断基线,不直接抬高主线优先级。`
: "当前没有额外的 degraded blocking verification baseline。",
],
commands: currentBlockingVerificationFollowUp.commands,
backlogTools: currentBlockingVerificationFollowUp.backlogTools,
@@ -1409,38 +978,27 @@ function buildRecommendations({
: "P2",
title: "先补 observability 证据覆盖,再扩大外部分析与回归",
rationale: [
trendSummary.latestCurrentObservabilityGapCaseCount > 0
? `当前仍有 ${trendSummary.latestCurrentObservabilityGapCaseCount} 个 current case 带着 observability 证据缺口进入 replay/eval。`
: "当前 trend 已检测到 observability coverage 漂移,需先修证据而不是空谈根因分析。",
trendSummary.latestDegradedObservabilityGapCaseCount > 0
? `另有 ${trendSummary.latestDegradedObservabilityGapCaseCount} 个 degraded gap 样本作为诊断基线保留,它们不应直接被当成主线回归。`
: "当前没有额外保留的 degraded observability gap 样本。",
`当前缺口焦点:${topObservabilitySignals.join("、") || "暂无"}。这些缺口会直接降低 analysis handoff、人工审核和 cleanup report 的判断质量。`,
topCurrentVerificationFailureOutcomes.length > 0
? `当前 current verification failure outcome 焦点:${topCurrentVerificationFailureOutcomes.join("、")}。可用它们直接定位先补 artifact/browser/gui 哪一层。`
: "当前没有额外的 verification failure outcome 焦点。",
...buildObservabilityRecommendationRationale({
trendSummary,
topObservabilitySignals,
topCurrentVerificationFailureOutcomes:
focusCurrentObservabilityVerificationOutcomes,
topDegradedVerificationFailureOutcomes:
focusDegradedObservabilityVerificationOutcomes,
currentVerificationSummary,
}),
...currentAdvisoryVerificationFollowUp.rationale,
currentVerificationSummary.advisoryFailureCaseCount > 0
? `当前 current advisory verification failure 共 ${currentVerificationSummary.advisoryFailureCaseCount} 个 case:${currentVerificationSummary.topAdvisoryFailureOutcomes.join("、") || "暂无"}。`
: "当前没有额外的 advisory verification failure。",
topDegradedVerificationFailureOutcomes.length > 0
? `当前保留的 degraded verification baseline:${topDegradedVerificationFailureOutcomes.join("、")}。`
: "当前没有额外的 degraded verification baseline。",
],
commands: [
"npm run harness:eval",
"npm run harness:eval:trend",
"npm run harness:cleanup-report",
],
backlogTools: [
"优先补 request telemetry 关联键、artifact validator outcome、browser/gui smoke 结果到 evidence pack / analysis handoff / replay。",
...(topCurrentVerificationFailureOutcomes.length > 0
? [
`先对齐 current verification failure outcome:${topCurrentVerificationFailureOutcomes.join("、")}。`,
]
: []),
...currentAdvisoryVerificationFollowUp.backlogTools,
],
backlogTools: buildObservabilityRecommendationBacklog({
topCurrentVerificationFailureOutcomes:
focusCurrentObservabilityVerificationOutcomes,
advisoryFollowUpBacklogTools: currentAdvisoryVerificationFollowUp.backlogTools,
}),
focusFailureModes: topFailureModes,
focusSuiteTags: topSuiteTags,
focusReviewDecisionStatuses: topReviewDecisionStatuses,
@@ -1493,9 +1051,11 @@ function buildRecommendations({
: "P3",
title: "把 recovered verification outcome 固化成 current 正向基线",
rationale: [
topCurrentRecoveredVerificationOutcomes.length > 0
? `当前 current recovered outcome 焦点:${topCurrentRecoveredVerificationOutcomes.join("、")}。`
: `当前 current recovered outcome 共 ${currentVerificationSummary.recoveredCaseCount} 个 case。`,
...buildRecoveredVerificationRecommendationRationale({
topCurrentRecoveredVerificationOutcomes:
focusCurrentRecoveredObservabilityVerificationOutcomes,
currentVerificationSummary,
}),
...currentRecoveredVerificationFollowUp.rationale,
"恢复成功的 outcome 不应只停留在统计卡里,还应继续回挂到 replay / smoke / evidence 主链,作为后续回退判断的正向对照。",
],
@@ -1590,117 +1150,31 @@ export function buildGeneratedSlopReport({
trendReport?.classificationDeltas?.observabilitySignals,
trendSummary.sampleCount,
);
const rawObservabilityVerificationOutcomes =
buildObservabilityVerificationFocusEntries(
trendReport?.classificationDeltas?.observabilityVerificationOutcomes,
trendSummary.sampleCount,
);
const explicitRecoveredObservabilityVerificationOutcomes =
buildVerificationOutcomeEntriesFromDeltas(
trendReport?.classificationDeltas?.observabilityVerificationOutcomes,
).filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) ===
"recovered",
);
const focusVerificationFailureOutcomes =
rawObservabilityVerificationOutcomes.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) !==
"recovered",
);
const rawCurrentObservabilityVerificationOutcomes =
buildObservabilityVerificationFocusEntries(
trendReport?.classificationDeltas?.currentObservabilityVerificationOutcomes,
trendSummary.sampleCount,
);
const explicitCurrentRecoveredObservabilityVerificationOutcomes =
buildVerificationOutcomeEntriesFromDeltas(
trendReport?.classificationDeltas?.currentRecoveredObservabilityVerificationOutcomes,
);
const focusCurrentObservabilityVerificationOutcomes =
rawCurrentObservabilityVerificationOutcomes.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) !==
"recovered",
);
const focusCurrentRecoveredObservabilityVerificationOutcomes =
explicitCurrentRecoveredObservabilityVerificationOutcomes.length > 0
? explicitCurrentRecoveredObservabilityVerificationOutcomes
: rawCurrentObservabilityVerificationOutcomes.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(
entry?.signal,
entry?.outcome,
) === "recovered",
);
const rawDegradedObservabilityVerificationOutcomes =
buildObservabilityVerificationFocusEntries(
trendReport?.classificationDeltas?.degradedObservabilityVerificationOutcomes,
trendSummary.sampleCount,
);
const focusDegradedObservabilityVerificationOutcomes =
rawDegradedObservabilityVerificationOutcomes.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(entry?.signal, entry?.outcome) !==
"recovered",
);
const verificationPresentation = deriveVerificationOutcomePresentationFromTrend({
trendReport,
sampleCount: trendSummary.sampleCount,
});
const mergedVerificationFailureOutcomes =
focusVerificationFailureOutcomes.length > 0
? focusVerificationFailureOutcomes
: [
...focusCurrentObservabilityVerificationOutcomes,
...focusDegradedObservabilityVerificationOutcomes,
].sort((left, right) => {
if (right.score !== left.score) {
return right.score - left.score;
}
return left.name.localeCompare(right.name);
});
const verificationFailureSummary = buildVerificationOutcomeSummary(
mergedVerificationFailureOutcomes,
);
const recoveredVerificationSummary = buildVerificationOutcomeSummary(
explicitRecoveredObservabilityVerificationOutcomes.length > 0
? explicitRecoveredObservabilityVerificationOutcomes
: mergedVerificationFailureOutcomes.filter(
(entry) =>
getObservabilityVerificationOutcomeRole(
entry?.signal,
entry?.outcome,
) === "recovered",
),
);
const verificationOutcomeSummary = {
...verificationFailureSummary,
recoveredFocusCount: recoveredVerificationSummary.recoveredFocusCount,
recoveredCaseCount: recoveredVerificationSummary.recoveredCaseCount,
topRecoveredOutcomes: recoveredVerificationSummary.topRecoveredOutcomes,
};
const currentVerificationFailureSummary = buildVerificationOutcomeSummary(
focusCurrentObservabilityVerificationOutcomes,
);
const currentRecoveredVerificationSummary = buildVerificationOutcomeSummary(
focusCurrentRecoveredObservabilityVerificationOutcomes,
);
const currentVerificationOutcomeSummary = {
...currentVerificationFailureSummary,
recoveredFocusCount: currentRecoveredVerificationSummary.recoveredFocusCount,
recoveredCaseCount: currentRecoveredVerificationSummary.recoveredCaseCount,
topRecoveredOutcomes: currentRecoveredVerificationSummary.topRecoveredOutcomes,
};
const degradedVerificationOutcomeSummary = buildVerificationOutcomeSummary(
focusDegradedObservabilityVerificationOutcomes,
);
verificationPresentation.mergedVerificationFailureOutcomes;
const focusVerificationFailureOutcomes = mergedVerificationFailureOutcomes;
const focusCurrentObservabilityVerificationOutcomes =
verificationPresentation.focusCurrentVerificationFailureOutcomes;
const focusCurrentRecoveredObservabilityVerificationOutcomes =
verificationPresentation.focusCurrentRecoveredVerificationOutcomes;
const focusDegradedObservabilityVerificationOutcomes =
verificationPresentation.focusDegradedVerificationFailureOutcomes;
const combinedVerificationOutcomeSummary =
verificationPresentation.verificationOutcomeSummary;
const verificationOutcomeSummary = combinedVerificationOutcomeSummary;
const currentVerificationOutcomeSummary =
combinedVerificationOutcomeSummary.current;
const degradedVerificationOutcomeSummary =
combinedVerificationOutcomeSummary.degraded;
const currentRecoveredVerificationOutcomes =
focusCurrentRecoveredObservabilityVerificationOutcomes
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.outcome})`);
const combinedVerificationOutcomeSummary = {
...verificationOutcomeSummary,
current: currentVerificationOutcomeSummary,
degraded: degradedVerificationOutcomeSummary,
};
formatVerificationOutcomeCompactLabels(
focusCurrentRecoveredObservabilityVerificationOutcomes,
3,
);
const governanceSurfaces = buildGovernanceSurfaceEntries(governanceReport);
const governanceSummary = buildGovernanceSummary(
governanceReport,
@@ -1771,30 +1245,15 @@ export function buildGeneratedSlopReport({
trendSummary.latestDegradedObservabilityGapCaseCount > 0
? `当前保留 ${trendSummary.latestDegradedObservabilityGapCaseCount} 个 degraded observability gap 样本作为诊断基线。`
: "当前没有额外保留的 degraded observability gap 样本。",
focusVerificationFailureOutcomes.length > 0
? `当前 verification failure outcome 焦点:${focusVerificationFailureOutcomes
.slice(0, 3)
.map((entry) => `${entry.signal} (${entry.outcome})`)
.join("、")}。`
: "当前没有额外的 verification failure outcome 焦点。",
verificationOutcomeSummary.failureCaseCount > 0
? `当前 verification failure 聚焦 ${verificationOutcomeSummary.failureFocusCount} 类 outcome,共 ${verificationOutcomeSummary.failureCaseCount} 个 case。`
: "当前没有额外的 verification failure case。",
currentVerificationOutcomeSummary.blockingFailureCaseCount > 0
? `当前 current 样本里有 ${currentVerificationOutcomeSummary.blockingFailureCaseCount} 个 blocking verification failure。`
: "当前没有额外的 blocking verification failure。",
currentVerificationOutcomeSummary.advisoryFailureCaseCount > 0
? `当前 current 样本里有 ${currentVerificationOutcomeSummary.advisoryFailureCaseCount} 个 advisory verification failure。`
: "当前没有额外的 advisory verification failure。",
currentVerificationOutcomeSummary.recoveredCaseCount > 0
? `当前 current recovered verification baseline:${currentRecoveredVerificationOutcomes.join("、") || "暂无"}。`
: "当前没有额外的 current recovered verification baseline。",
degradedVerificationOutcomeSummary.blockingFailureCaseCount > 0
? `当前保留 ${degradedVerificationOutcomeSummary.blockingFailureCaseCount} 个 degraded blocking verification failure 样本作为诊断基线。`
: "当前没有额外的 degraded blocking verification baseline。",
verificationOutcomeSummary.recoveredCaseCount > 0
? `当前 verification recovered 聚焦 ${verificationOutcomeSummary.recoveredFocusCount} 类 outcome,共 ${verificationOutcomeSummary.recoveredCaseCount} 个 case。`
: "当前没有额外的 verification recovered case。",
...buildVerificationOutcomeSignalMessages({
focusVerificationFailureOutcomes,
verificationOutcomeSummary,
currentVerificationOutcomeSummary,
degradedVerificationOutcomeSummary,
currentRecoveredVerificationOutcomes:
focusCurrentRecoveredObservabilityVerificationOutcomes,
labelLimit: 3,
}),
],
focus: {
failureModes: focusFailureModes.slice(0, 5),
@@ -1899,7 +1358,7 @@ export function renderGeneratedSlopText(report) {
lines.push("[harness-cleanup] top observability verification outcomes:");
for (const entry of report.focus.observabilityVerificationOutcomes) {
lines.push(
` - ${entry.signal} (${entry.outcome}): state=${entry.state}, latest_case=${entry.latest.caseCount}, delta_case=${entry.delta.caseCount}, score=${entry.score}`,
` - ${formatVerificationOutcomeCompactLabel(entry)}: state=${entry.state}, latest_case=${entry.latest.caseCount}, delta_case=${entry.delta.caseCount}, score=${entry.score}`,
);
}
}
+46 -111
View File
@@ -1,3 +1,8 @@
import {
describeVerificationOutcome,
deriveVerificationDashboardPresentation,
} from "./harness-verification-facts.mjs";
function normalizeNumber(value) {
return typeof value === "number" && Number.isFinite(value) ? value : 0;
}
@@ -141,49 +146,39 @@ function renderRecommendationList(recommendations) {
.join("");
}
function describeVerificationOutcome(entry) {
const signal = normalizeString(entry?.signal, "unknown");
const outcome = normalizeString(entry?.outcome, "unknown");
if (signal === "artifactValidator" && outcome === "issues_present") {
return "当前 evidence 已记录 artifact 校验问题,优先回看 validator issue 明细。";
}
if (signal === "artifactValidator" && outcome === "fallback_used") {
return "当前 artifact 导出仍触发 fallback,说明产物结构或修复链未完全稳定。";
}
if (signal === "browserVerification" && outcome === "failure") {
return "浏览器验证已有明确失败结果,优先回挂到 replay 或 smoke 断言。";
}
if (signal === "browserVerification" && outcome === "unknown") {
return "浏览器验证结果仍不明确,需要先补 outcome 再继续扩分析。";
}
if (signal === "guiSmoke" && outcome === "failed") {
return "GUI smoke 已明确失败,应优先收敛到受影响主路径。";
}
if (signal === "guiSmoke" && outcome === "passed") {
return "GUI smoke 已通过,可继续把注意力放回 gap 与其它失败面。";
}
if (signal === "artifactValidator" && outcome === "repaired") {
return "artifact validator 已执行修复,可结合 issues/fallback 判断是否还需继续治理。";
}
if (signal === "browserVerification" && outcome === "success") {
return "浏览器验证已有成功样本,可作为 current 主线路径的正向基线。";
function deriveTrendSummary(trendReport, cleanupReport) {
const cleanupTrendSummary =
cleanupReport &&
typeof cleanupReport === "object" &&
cleanupReport.summary &&
cleanupReport.summary.trend
? cleanupReport.summary.trend
: null;
if (cleanupTrendSummary) {
return cleanupTrendSummary;
}
return "当前 verification outcome 已进入 cleanup 主线,可直接据此定位先修哪层。";
}
const latestTotals =
trendReport && typeof trendReport === "object" && trendReport.latest?.totals
? trendReport.latest.totals
: {};
const delta = trendReport && typeof trendReport === "object" ? trendReport.delta : {};
const RECOVERED_VERIFICATION_OUTCOMES = new Set([
"repaired",
"success",
"passed",
"clean",
]);
function isRecoveredVerificationOutcome(entry) {
return RECOVERED_VERIFICATION_OUTCOMES.has(
normalizeString(entry?.outcome, "unknown"),
);
return {
sampleCount: normalizeNumber(trendReport?.sampleCount),
latestCurrentObservabilityGapCaseCount: normalizeNumber(
latestTotals.currentObservabilityGapCaseCount,
),
latestDegradedObservabilityGapCaseCount: normalizeNumber(
latestTotals.degradedObservabilityGapCaseCount,
),
currentObservabilityGapCaseDelta: normalizeNumber(
delta?.currentObservabilityGapCaseCount,
),
degradedObservabilityGapCaseDelta: normalizeNumber(
delta?.degradedObservabilityGapCaseCount,
),
};
}
function renderFocusTable(title, entries, columns) {
@@ -240,13 +235,7 @@ export function renderHarnessDashboardHtml({
summaryReport && typeof summaryReport === "object" && summaryReport.totals
? summaryReport.totals
: {};
const trendSummary =
cleanupReport &&
typeof cleanupReport === "object" &&
cleanupReport.summary &&
cleanupReport.summary.trend
? cleanupReport.summary.trend
: {};
const trendSummary = deriveTrendSummary(trendReport, cleanupReport);
const governanceSummary =
cleanupReport &&
typeof cleanupReport === "object" &&
@@ -254,13 +243,12 @@ export function renderHarnessDashboardHtml({
cleanupReport.summary.governance
? cleanupReport.summary.governance
: {};
const verificationSummary =
cleanupReport &&
typeof cleanupReport === "object" &&
cleanupReport.summary &&
cleanupReport.summary.verificationOutcomes
? cleanupReport.summary.verificationOutcomes
: {};
const verificationPresentation = deriveVerificationDashboardPresentation({
summaryReport,
trendReport,
cleanupReport,
});
const verificationSummary = verificationPresentation.verificationSummary;
const currentVerificationSummary =
verificationSummary &&
typeof verificationSummary.current === "object" &&
@@ -281,64 +269,11 @@ export function renderHarnessDashboardHtml({
? cleanupReport.recommendations
: [];
const sampleRows = Array.isArray(trendReport?.samples) ? trendReport.samples : [];
const currentVerificationFocusRows = Array.isArray(
cleanupReport?.focus?.currentObservabilityVerificationOutcomes,
)
? cleanupReport.focus.currentObservabilityVerificationOutcomes.map((entry) => ({
...entry,
role: "current",
}))
: [];
const degradedVerificationFocusRows = Array.isArray(
cleanupReport?.focus?.degradedObservabilityVerificationOutcomes,
)
? cleanupReport.focus.degradedObservabilityVerificationOutcomes.map(
(entry) => ({
...entry,
role: "degraded",
}),
)
: [];
const fallbackVerificationFocusRows = Array.isArray(
cleanupReport?.focus?.observabilityVerificationOutcomes,
)
? cleanupReport.focus.observabilityVerificationOutcomes.map((entry) => ({
...entry,
role: "mixed",
}))
: [];
const explicitCurrentRecoveredVerificationRows = Array.isArray(
cleanupReport?.focus?.currentRecoveredObservabilityVerificationOutcomes,
)
? cleanupReport.focus.currentRecoveredObservabilityVerificationOutcomes.map(
(entry) => ({
...entry,
role: "current",
}),
)
: [];
const verificationFocusRows =
currentVerificationFocusRows.length > 0 ||
degradedVerificationFocusRows.length > 0
? [...currentVerificationFocusRows, ...degradedVerificationFocusRows]
: fallbackVerificationFocusRows;
const verificationFocusRows = verificationPresentation.verificationFocusRows;
const currentRecoveredVerificationRows =
explicitCurrentRecoveredVerificationRows.length > 0
? explicitCurrentRecoveredVerificationRows
: currentVerificationFocusRows.length > 0
? currentVerificationFocusRows.filter((entry) =>
isRecoveredVerificationOutcome(entry),
)
: fallbackVerificationFocusRows.filter((entry) =>
isRecoveredVerificationOutcome(entry),
);
const currentRecoveredVerificationSummary = currentRecoveredVerificationRows
.slice(0, 3)
.map(
(entry) =>
`${normalizeString(entry?.signal, "-")} (${normalizeString(entry?.outcome, "-")})`,
)
.join("、");
verificationPresentation.currentRecoveredRows;
const currentRecoveredVerificationSummary =
verificationPresentation.currentRecoveredSummaryLabel;
return `<!doctype html>
<html lang="zh-CN">
+135
View File
@@ -3,6 +3,141 @@ import { describe, expect, it } from "vitest";
import { renderHarnessDashboardHtml } from "./harness-dashboard-core.mjs";
describe("harness-dashboard-core", () => {
it("应优先使用 trend 与 summary 的 verification facts,而不是 cleanup 渲染面", () => {
const html = renderHarnessDashboardHtml({
title: "Harness Engine Dashboard",
summaryReport: {
generatedAt: "2026-04-12T08:00:00.000Z",
totals: {
readyCount: 1,
invalidCount: 0,
},
breakdowns: {
observabilityVerificationOutcomes: [
{ name: "guiSmoke:failed", caseCount: 1 },
{ name: "browserVerification:success", caseCount: 1 },
],
currentObservabilityVerificationOutcomes: [
{ name: "guiSmoke:failed", caseCount: 1 },
{ name: "browserVerification:success", caseCount: 1 },
],
currentRecoveredObservabilityVerificationOutcomes: [
{ name: "browserVerification:success", caseCount: 1 },
],
degradedObservabilityVerificationOutcomes: [],
},
},
trendReport: {
generatedAt: "2026-04-12T08:01:00.000Z",
sampleCount: 2,
delta: {
currentObservabilityGapCaseCount: 0,
degradedObservabilityGapCaseCount: 0,
},
latest: {
totals: {
currentObservabilityGapCaseCount: 0,
degradedObservabilityGapCaseCount: 0,
currentRecoveredVerificationCaseCount: 1,
},
},
signals: ["current gap 保持为 0。"],
samples: [],
classificationDeltas: {
observabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentObservabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentRecoveredObservabilityVerificationOutcomes: [
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
degradedObservabilityVerificationOutcomes: [],
},
},
cleanupReport: {
generatedAt: "2026-04-12T08:02:00.000Z",
signals: [],
recommendations: [],
focus: {
currentObservabilityVerificationOutcomes: [
{
signal: "artifactValidator",
outcome: "fallback_used",
state: "regressing",
latest: { caseCount: 2 },
delta: { caseCount: 2 },
},
],
currentRecoveredObservabilityVerificationOutcomes: [
{
signal: "artifactValidator",
outcome: "repaired",
state: "expanding",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
degradedObservabilityVerificationOutcomes: [],
},
summary: {
trend: {
sampleCount: 2,
latestCurrentObservabilityGapCaseCount: 0,
latestDegradedObservabilityGapCaseCount: 0,
currentObservabilityGapCaseDelta: 0,
degradedObservabilityGapCaseDelta: 0,
},
verificationOutcomes: {
recoveredCaseCount: 0,
current: {
blockingFailureCaseCount: 0,
advisoryFailureCaseCount: 2,
recoveredCaseCount: 0,
},
degraded: {
blockingFailureCaseCount: 0,
advisoryFailureCaseCount: 0,
},
},
governance: {
violationCount: 0,
},
},
},
});
expect(html).toMatch(/Current Blocking<\/span>\s*<strong>1<\/strong>/);
expect(html).toMatch(/Current Recovered<\/span>\s*<strong>1<\/strong>/);
expect(html).toContain("browserVerification (success)");
expect(html).toContain("GUI smoke 已明确失败");
expect(html).not.toContain("artifactValidator (repaired)、");
expect(html).not.toContain("fallback_used");
});
it("应把 summary、trend、cleanup 渲染成单一事实源 dashboard", () => {
const html = renderHarnessDashboardHtml({
title: "Harness Engine Dashboard",
@@ -4,6 +4,8 @@ import path from "node:path";
import { execFile, execFileSync } from "node:child_process";
import { afterEach, describe, expect, it } from "vitest";
import { deriveHistoryRecordVerificationFacts } from "../harness-eval-history-record.mjs";
const repoRoot = process.cwd();
const tempRoots: string[] = [];
@@ -59,6 +61,140 @@ afterEach(() => {
});
describe("harness-eval-history-record", () => {
it("应优先使用 summary 与 trend 的 verification facts,而不是 cleanup 反算结果", () => {
const summary = {
totals: {
currentRecoveredVerificationCaseCount: 2,
},
breakdowns: {
observabilityVerificationOutcomes: [
{ name: "guiSmoke:failed", caseCount: 2 },
{ name: "artifactValidator:issues_present", caseCount: 1 },
{ name: "browserVerification:success", caseCount: 1 },
{ name: "guiSmoke:passed", caseCount: 1 },
],
currentObservabilityVerificationOutcomes: [
{ name: "guiSmoke:failed", caseCount: 2 },
{ name: "artifactValidator:issues_present", caseCount: 1 },
{ name: "browserVerification:success", caseCount: 1 },
{ name: "guiSmoke:passed", caseCount: 1 },
],
currentRecoveredObservabilityVerificationOutcomes: [
{ name: "browserVerification:success", caseCount: 1 },
{ name: "guiSmoke:passed", caseCount: 1 },
],
degradedObservabilityVerificationOutcomes: [
{ name: "browserVerification:failure", caseCount: 1 },
],
},
};
const trendReport = {
latest: {
totals: {
currentRecoveredVerificationCaseCount: 2,
},
},
classificationDeltas: {
observabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 2 },
delta: { caseCount: 2 },
},
{
name: "artifactValidator:issues_present",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentObservabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 2 },
delta: { caseCount: 2 },
},
{
name: "artifactValidator:issues_present",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "guiSmoke:passed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentRecoveredObservabilityVerificationOutcomes: [
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "guiSmoke:passed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
},
};
const cleanupReport = {
summary: {
verificationOutcomes: {
failureCaseCount: 99,
recoveredCaseCount: 0,
current: {
blockingFailureCaseCount: 88,
advisoryFailureCaseCount: 77,
recoveredCaseCount: 0,
},
degraded: {
blockingFailureCaseCount: 66,
},
},
},
focus: {
observabilityVerificationOutcomes: [
{ signal: "artifactValidator", outcome: "fallback_used" },
],
currentObservabilityVerificationOutcomes: [
{ signal: "artifactValidator", outcome: "fallback_used" },
],
currentRecoveredObservabilityVerificationOutcomes: [
{ signal: "artifactValidator", outcome: "repaired" },
],
},
};
const result = deriveHistoryRecordVerificationFacts({
summary,
trendReport,
cleanupReport,
});
expect(result.verificationFailureOutcomeFocus).toEqual([
"guiSmoke:failed",
"artifactValidator:issues_present",
]);
expect(result.currentRecoveredBaselineFocus).toEqual([
"browserVerification:success",
"guiSmoke:passed",
]);
expect(result.verificationOutcomeCounts).toEqual({
failureCaseCount: 3,
blockingFailureCaseCount: 2,
advisoryFailureCaseCount: 1,
recoveredCaseCount: 2,
currentRecoveredCaseCount: 2,
degradedBlockingFailureCaseCount: 1,
});
});
it("默认入口应产出完整 harness artifact 套件", () => {
const tempRoot = createTempRoot();
const historyDir = path.join(tempRoot, ".lime", "harness", "history");
@@ -59,6 +59,7 @@ describe("Harness repo fixtures", () => {
expect(summary.totals.observabilityGapCaseCount).toBe(1);
expect(summary.totals.currentObservabilityGapCaseCount).toBe(0);
expect(summary.totals.degradedObservabilityGapCaseCount).toBe(1);
expect(summary.totals.currentRecoveredVerificationCaseCount).toBe(3);
const currentCase = repoFixtureSuite.cases.find(
(entry: { caseId: string }) =>
@@ -148,6 +149,20 @@ describe("Harness repo fixtures", () => {
"browserVerification:success",
);
const currentRecoveredVerificationOutcomeBreakdownNames =
summary.breakdowns.currentRecoveredObservabilityVerificationOutcomes.map(
(entry: { name: string }) => entry.name,
);
expect(currentRecoveredVerificationOutcomeBreakdownNames).toContain(
"artifactValidator:repaired",
);
expect(currentRecoveredVerificationOutcomeBreakdownNames).toContain(
"browserVerification:success",
);
expect(currentRecoveredVerificationOutcomeBreakdownNames).toContain(
"guiSmoke:passed",
);
expect(summary.breakdowns.degradedObservabilityVerificationOutcomes).toEqual(
[],
);
@@ -350,6 +350,7 @@ describe("Harness review decision / eval integration", () => {
expect(summary.totals.observabilityGapCaseCount).toBe(1);
expect(summary.totals.currentObservabilityGapCaseCount).toBe(1);
expect(summary.totals.degradedObservabilityGapCaseCount).toBe(0);
expect(summary.totals.currentRecoveredVerificationCaseCount).toBe(3);
expect(summary.breakdowns.reviewDecisionStatuses).toEqual(
expect.arrayContaining([
expect.objectContaining({
@@ -402,6 +403,24 @@ describe("Harness review decision / eval integration", () => {
}),
]),
);
expect(
summary.breakdowns.currentRecoveredObservabilityVerificationOutcomes,
).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: "artifactValidator:repaired",
caseCount: 1,
}),
expect.objectContaining({
name: "browserVerification:success",
caseCount: 1,
}),
expect.objectContaining({
name: "guiSmoke:passed",
caseCount: 1,
}),
]),
);
expect(summary.breakdowns.degradedObservabilityVerificationOutcomes).toEqual(
[],
);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,524 @@
import { describe, expect, it } from "vitest";
import {
buildAdvisoryVerificationFollowUp,
buildAdvisoryVerificationRecommendationRationale,
buildBlockingVerificationFollowUp,
buildBlockingVerificationRecommendationRationale,
buildObservabilityRecommendationBacklog,
buildObservabilityRecommendationRationale,
buildVerificationOutcomeSignalMessages,
buildRecoveredVerificationRecommendationRationale,
deriveVerificationDashboardPresentation,
describeVerificationOutcome,
formatVerificationOutcomeCompactLabels,
buildRecoveredVerificationFollowUp,
buildVerificationOutcomeEntriesFromBreakdowns,
buildVerificationOutcomeEntriesFromDeltas,
buildVerificationFocusEntriesFromDeltas,
buildVerificationOutcomeSummary,
deriveVerificationOutcomePresentationFromTrend,
formatVerificationOutcomeCompactLabel,
getVerificationOutcomeRole,
getVerificationOutcomeWeight,
hasVerificationOutcome,
isVerificationFailureOutcome,
isVerificationRecoveredOutcome,
splitVerificationOutcomeName,
} from "./harness-verification-facts.mjs";
describe("harness-verification-facts", () => {
it("应统一解析 outcome 名称、角色与权重", () => {
expect(splitVerificationOutcomeName("browserVerification:failure")).toEqual({
name: "browserVerification:failure",
signal: "browserVerification",
outcome: "failure",
});
expect(splitVerificationOutcomeName("guiSmoke")).toEqual({
name: "guiSmoke",
signal: "guiSmoke",
outcome: "",
});
expect(getVerificationOutcomeRole("browserVerification", "failure")).toBe(
"blocking_failure",
);
expect(getVerificationOutcomeRole("artifactValidator", "issues_present")).toBe(
"advisory_failure",
);
expect(getVerificationOutcomeRole("browserVerification", "success")).toBe(
"recovered",
);
expect(getVerificationOutcomeRole("other", "noop")).toBe("other");
expect(isVerificationFailureOutcome("fallback_used")).toBe(true);
expect(isVerificationRecoveredOutcome("repaired")).toBe(true);
expect(getVerificationOutcomeWeight("failed")).toBe(140);
expect(getVerificationOutcomeWeight("repaired")).toBe(70);
expect(getVerificationOutcomeWeight("noop")).toBe(0);
});
it("应统一从 delta 与 breakdown 派生 verification entries 与 summary", () => {
const deltaEntries = buildVerificationOutcomeEntriesFromDeltas([
{
name: "guiSmoke:failed",
latest: { caseCount: 2 },
delta: { caseCount: 1 },
},
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 0 },
},
]);
const breakdownEntries = buildVerificationOutcomeEntriesFromBreakdowns([
{
name: "artifactValidator:issues_present",
caseCount: 1,
},
]);
expect(deltaEntries.map((entry) => entry.name)).toEqual([
"guiSmoke:failed",
"browserVerification:success",
]);
expect(breakdownEntries[0]).toMatchObject({
signal: "artifactValidator",
outcome: "issues_present",
latest: { caseCount: 1 },
delta: { caseCount: 0 },
});
expect(
buildVerificationOutcomeSummary([...deltaEntries, ...breakdownEntries]),
).toMatchObject({
failureCaseCount: 3,
blockingFailureCaseCount: 2,
advisoryFailureCaseCount: 1,
recoveredCaseCount: 1,
topBlockingFailureOutcomes: ["guiSmoke:failed"],
topRecoveredOutcomes: ["browserVerification:success"],
});
});
it("应从 trend classification deltas 派生 cleanup 可复用的 verification presentation", () => {
const presentation = deriveVerificationOutcomePresentationFromTrend({
sampleCount: 2,
trendReport: {
classificationDeltas: {
observabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "artifactValidator:repaired",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentObservabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "artifactValidator:repaired",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentRecoveredObservabilityVerificationOutcomes: [
{
name: "artifactValidator:repaired",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
degradedObservabilityVerificationOutcomes: [
{
name: "browserVerification:failure",
latest: { caseCount: 1 },
delta: { caseCount: 0 },
},
],
},
},
});
expect(
buildVerificationFocusEntriesFromDeltas([
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
], 2)[0],
).toMatchObject({
signal: "guiSmoke",
outcome: "failed",
state: "regressing",
});
expect(
presentation.focusCurrentVerificationFailureOutcomes.map(
(entry) => entry.name,
),
).toEqual(["guiSmoke:failed"]);
expect(
presentation.focusCurrentRecoveredVerificationOutcomes.map(
(entry) => entry.name,
),
).toEqual(["artifactValidator:repaired"]);
expect(
presentation.focusDegradedVerificationFailureOutcomes.map(
(entry) => entry.name,
),
).toEqual(["browserVerification:failure"]);
expect(presentation.verificationOutcomeSummary).toMatchObject({
failureCaseCount: 1,
recoveredCaseCount: 1,
current: {
blockingFailureCaseCount: 1,
recoveredCaseCount: 1,
},
degraded: {
blockingFailureCaseCount: 1,
},
});
});
it("应统一生成 blocking/advisory/recovered follow-up 建议", () => {
const blockingEntries = [
{
signal: "guiSmoke",
outcome: "failed",
},
{
signal: "browserVerification",
outcome: "failure",
},
];
const advisoryEntries = [
{
signal: "artifactValidator",
outcome: "issues_present",
},
{
signal: "browserVerification",
outcome: "unknown",
},
];
const recoveredEntries = [
{
signal: "artifactValidator",
outcome: "repaired",
},
{
signal: "browserVerification",
outcome: "success",
},
{
signal: "guiSmoke",
outcome: "passed",
},
];
expect(
hasVerificationOutcome(blockingEntries, "guiSmoke", "failed"),
).toBe(true);
expect(
hasVerificationOutcome(blockingEntries, "guiSmoke", "passed"),
).toBe(false);
expect(buildBlockingVerificationFollowUp(blockingEntries)).toMatchObject({
commands: [
"npm run harness:eval",
"npm run harness:eval:trend",
"npm run verify:gui-smoke",
],
backlogTools: expect.arrayContaining([
"优先收敛 GUI 壳 / DevBridge / Workspace 主路径,再复跑 `npm run verify:gui-smoke`。",
"回看 browser replay / browser verification 失败样本,并把失败断言回挂到受影响主路径。",
]),
rationale: expect.arrayContaining([
"current 样本已出现 guiSmoke:failed,先恢复 GUI 壳 / DevBridge / Workspace 主路径的最小可启动性。",
"current 样本已出现 browserVerification:failure,应先回看 browser replay / verification 失败样本,把失败断言回挂到受影响主路径。",
]),
});
expect(buildAdvisoryVerificationFollowUp(advisoryEntries)).toMatchObject({
backlogTools: expect.arrayContaining([
"回看 artifact validator issue 明细,并收敛 evidence pack / artifacts.json / analysis handoff 的 artifact 字段。",
"回看 browser verification 导出链,确保 evidence pack / replay / analysis handoff 写出明确 success 或 failure,而不是 unknown。",
]),
rationale: expect.arrayContaining([
"current 样本已出现 artifactValidator:issues_present,应先回看 validator issue 明细,再收敛 artifact 导出字段。",
"current 样本已出现 browserVerification:unknown,需要先把浏览器验证结果收敛成明确 outcome,再继续扩大分析。",
]),
});
expect(buildRecoveredVerificationFollowUp(recoveredEntries)).toMatchObject({
commands: [
"npm run harness:eval",
"npm run harness:eval:trend",
"npm run verify:gui-smoke",
],
backlogTools: expect.arrayContaining([
"在 evidence pack / analysis handoff 里同时保留 artifact issue 与 repaired outcome,避免只剩修复结论而丢失修复上下文。",
"把 browser verification 成功样本固定进 current replay 基线,后续 failure 或 unknown 直接对比这条正向路径。",
"主路径变更时优先复跑 `npm run verify:gui-smoke`,确认 GUI 壳 / DevBridge / Workspace 不从 passed 回退。",
]),
rationale: expect.arrayContaining([
"current 样本已出现 artifactValidator:repaired,说明 artifact 修复链已经回到可复用的主路径。",
"current 样本已出现 browserVerification:success,可把浏览器验证成功样本固化成主路径正向基线。",
"current 样本已出现 guiSmoke:passed,可继续把 GUI smoke 通过链路当成桌面主路径的正向守卫。",
]),
});
});
it("应统一为 dashboard 派生 verification presentation 与说明文案", () => {
const presentation = deriveVerificationDashboardPresentation({
summaryReport: {
breakdowns: {
observabilityVerificationOutcomes: [
{ name: "guiSmoke:failed", caseCount: 1 },
{ name: "browserVerification:success", caseCount: 1 },
],
currentObservabilityVerificationOutcomes: [
{ name: "guiSmoke:failed", caseCount: 1 },
{ name: "browserVerification:success", caseCount: 1 },
],
currentRecoveredObservabilityVerificationOutcomes: [
{ name: "browserVerification:success", caseCount: 1 },
],
},
},
trendReport: {
classificationDeltas: {
observabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentObservabilityVerificationOutcomes: [
{
name: "guiSmoke:failed",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
currentRecoveredObservabilityVerificationOutcomes: [
{
name: "browserVerification:success",
latest: { caseCount: 1 },
delta: { caseCount: 1 },
},
],
degradedObservabilityVerificationOutcomes: [],
},
},
cleanupReport: {
focus: {
currentObservabilityVerificationOutcomes: [
{
signal: "artifactValidator",
outcome: "fallback_used",
latest: { caseCount: 2 },
delta: { caseCount: 2 },
},
],
},
summary: {
verificationOutcomes: {
current: {
advisoryFailureCaseCount: 2,
},
},
},
},
});
expect(presentation.verificationSummary).toMatchObject({
current: {
blockingFailureCaseCount: 1,
recoveredCaseCount: 1,
},
});
expect(
presentation.verificationFocusRows.map((entry) => entry.name),
).toEqual(["guiSmoke:failed"]);
expect(
presentation.currentRecoveredRows.map((entry) => entry.name),
).toEqual(["browserVerification:success"]);
expect(presentation.currentRecoveredSummaryLabel).toBe(
"browserVerification (success)",
);
expect(
formatVerificationOutcomeCompactLabel("artifactValidator:repaired"),
).toBe("artifactValidator (repaired)");
expect(
formatVerificationOutcomeCompactLabels(
[
{ signal: "artifactValidator", outcome: "repaired" },
"browserVerification:success",
],
2,
),
).toEqual([
"artifactValidator (repaired)",
"browserVerification (success)",
]);
expect(
describeVerificationOutcome({
signal: "browserVerification",
outcome: "success",
}),
).toContain("浏览器验证已有成功样本");
});
it("应统一生成 verification summary signal 文案", () => {
expect(
buildVerificationOutcomeSignalMessages({
focusVerificationFailureOutcomes: [
{ signal: "browserVerification", outcome: "failure" },
],
verificationOutcomeSummary: {
failureFocusCount: 1,
failureCaseCount: 1,
recoveredFocusCount: 1,
recoveredCaseCount: 1,
},
currentVerificationOutcomeSummary: {
blockingFailureCaseCount: 1,
advisoryFailureCaseCount: 0,
recoveredCaseCount: 1,
},
degradedVerificationOutcomeSummary: {
blockingFailureCaseCount: 0,
},
currentRecoveredVerificationOutcomes: [
{ signal: "artifactValidator", outcome: "repaired" },
],
}),
).toEqual([
"当前 verification failure outcome 焦点:browserVerification (failure)。",
"当前 verification failure 聚焦 1 类 outcome,共 1 个 case。",
"当前 current 样本里有 1 个 blocking verification failure。",
"当前没有额外的 advisory verification failure。",
"当前 current recovered verification baseline:artifactValidator (repaired)。",
"当前没有额外的 degraded blocking verification baseline。",
"当前 verification recovered 聚焦 1 类 outcome,共 1 个 case。",
]);
});
it("应统一生成 recommendation 用的 verification rationale 片段", () => {
expect(
buildBlockingVerificationRecommendationRationale({
topCurrentVerificationFailureOutcomes: [
{ signal: "browserVerification", outcome: "failure" },
],
currentVerificationSummary: {
blockingFailureCaseCount: 1,
topBlockingFailureOutcomes: ["browserVerification:failure"],
},
degradedVerificationSummary: {
blockingFailureCaseCount: 0,
},
}),
).toEqual([
"当前 current verification failure outcome 焦点:browserVerification (failure)。",
"其中 current blocking verification failure 共 1 个 case:browserVerification:failure。",
"当前没有额外的 degraded blocking verification baseline。",
]);
expect(
buildAdvisoryVerificationRecommendationRationale({
topCurrentVerificationFailureOutcomes: [
{ signal: "artifactValidator", outcome: "issues_present" },
],
topDegradedVerificationFailureOutcomes: [
{ signal: "guiSmoke", outcome: "failed" },
],
currentVerificationSummary: {
advisoryFailureCaseCount: 1,
topAdvisoryFailureOutcomes: ["artifactValidator:issues_present"],
},
}),
).toEqual([
"当前 current verification failure outcome 焦点:artifactValidator (issues_present)。可用它们直接定位先补 artifact/browser/gui 哪一层。",
"当前 current advisory verification failure 共 1 个 case:artifactValidator:issues_present。",
"当前保留的 degraded verification baseline:guiSmoke (failed)。",
]);
expect(
buildRecoveredVerificationRecommendationRationale({
topCurrentRecoveredVerificationOutcomes: [
{ signal: "artifactValidator", outcome: "repaired" },
],
currentVerificationSummary: {
recoveredCaseCount: 1,
},
}),
).toEqual([
"当前 current recovered outcome 焦点:artifactValidator (repaired)。",
]);
});
it("应统一生成 observability recommendation 的混合文案与待办", () => {
expect(
buildObservabilityRecommendationRationale({
trendSummary: {
latestCurrentObservabilityGapCaseCount: 1,
latestDegradedObservabilityGapCaseCount: 0,
},
topObservabilitySignals: ["requestTelemetry (known_gap)"],
topCurrentVerificationFailureOutcomes: [
{ signal: "artifactValidator", outcome: "issues_present" },
],
topDegradedVerificationFailureOutcomes: [
{ signal: "guiSmoke", outcome: "failed" },
],
currentVerificationSummary: {
advisoryFailureCaseCount: 1,
topAdvisoryFailureOutcomes: ["artifactValidator:issues_present"],
},
}),
).toEqual([
"当前仍有 1 个 current case 带着 observability 证据缺口进入 replay/eval。",
"当前没有额外保留的 degraded observability gap 样本。",
"当前缺口焦点:requestTelemetry (known_gap)。这些缺口会直接降低 analysis handoff、人工审核和 cleanup report 的判断质量。",
"当前 current verification failure outcome 焦点:artifactValidator (issues_present)。可用它们直接定位先补 artifact/browser/gui 哪一层。",
"当前 current advisory verification failure 共 1 个 case:artifactValidator:issues_present。",
"当前保留的 degraded verification baseline:guiSmoke (failed)。",
]);
expect(
buildObservabilityRecommendationBacklog({
topCurrentVerificationFailureOutcomes: [
{ signal: "artifactValidator", outcome: "issues_present" },
],
advisoryFollowUpBacklogTools: [
"回看 artifact validator issue 明细,并收敛 evidence pack / artifacts.json / analysis handoff 的 artifact 字段。",
],
}),
).toEqual([
"优先补 request telemetry 关联键、artifact validator outcome、browser/gui smoke 结果到 evidence pack / analysis handoff / replay。",
"先对齐 current verification failure outcome:artifactValidator (issues_present)。",
"回看 artifact validator issue 明细,并收敛 evidence pack / artifacts.json / analysis handoff 的 artifact 字段。",
]);
});
});