From 97fe9c9c6f917a65d4c5bb4fb5383e7c2a7713fb Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 6 Jul 2026 17:22:19 +0000 Subject: [PATCH] fix: deduplicate sentinel reclassified findings --- scripts/src/hwlab-node-web-sentinel-p5.ts | 12 +++++++++--- scripts/src/hwlab-node-web-sentinel-service.ts | 11 ++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/scripts/src/hwlab-node-web-sentinel-p5.ts b/scripts/src/hwlab-node-web-sentinel-p5.ts index 86419d4a..6bb3c8fc 100644 --- a/scripts/src/hwlab-node-web-sentinel-p5.ts +++ b/scripts/src/hwlab-node-web-sentinel-p5.ts @@ -366,8 +366,12 @@ function offlineQuickVerifyReclassify(state: SentinelCicdState, run: Record, findings: readonly Record[]): Record[] { const hasObserverCommandFailure = findings.some((item) => sentinelReportFindingIdentityCandidates(item).includes("observer-command-failed")); + const hasObserverCommandFailureEvidence = findings.some((item) => sentinelReportFindingIdentityCandidates(item).includes("observer-command-failed") && hasCommandFailureEvidence(item)); const catalog = sentinelReportCheckCatalogById(state); - return findings.map((item) => enrichSentinelReportFindingWithCatalog(reclassifySentinelReportFinding(rowFailure(run), item, hasObserverCommandFailure), catalog)); + return findings + .map((item) => reclassifySentinelReportFinding(rowFailure(run), item, hasObserverCommandFailure, hasObserverCommandFailureEvidence)) + .filter((item): item is Record => item !== null) + .map((item) => enrichSentinelReportFindingWithCatalog(item, catalog)); } function rowFailure(row: Record): string | null { @@ -376,8 +380,10 @@ function rowFailure(row: Record): string | null { ?? stringAtNullable(record(row.businessStatus), "failure"); } -function reclassifySentinelReportFinding(failure: string | null, item: Record, hasObserverCommandFailure: boolean): Record { +function reclassifySentinelReportFinding(failure: string | null, item: Record, hasObserverCommandFailure: boolean, hasObserverCommandFailureEvidence: boolean): Record | null { const ids = sentinelReportFindingIdentityCandidates(item); + if (ids.includes("observer-command-failure-evidence-missing") && hasObserverCommandFailureEvidence) return null; + if (ids.includes("no-samples-artifact-evidence-missing") && hasObserverCommandFailure) return null; if (ids.includes("quick-verify-command-sequence-failed")) { return retagSentinelReportFinding(item, "account-session-command-failed-before-observation", { summary: "account/session setup command failed before any business turn could be observed.", @@ -388,7 +394,7 @@ function reclassifySentinelReportFinding(failure: string | null, item: Record, findings: readonly Record[], limit: number): readonly Record[] { const hasObserverCommandFailure = findings.some((item) => findingIdentityCandidates(item).includes("observer-command-failed")); + const hasObserverCommandFailureEvidence = findings.some((item) => findingIdentityCandidates(item).includes("observer-command-failed") && hasCommandFailureEvidence(item)); const failure = stringOrNull(record(row.summary).failure) ?? stringOrNull(row.failure) ?? stringOrNull(record(row.businessStatus).failure); return findings - .map((item) => enrichFindingWithCheck(config, reclassifyVisibleFinding(item, failure, hasObserverCommandFailure))) + .map((item) => reclassifyVisibleFinding(item, failure, hasObserverCommandFailure, hasObserverCommandFailureEvidence)) + .filter((item): item is Record => item !== null) + .map((item) => enrichFindingWithCheck(config, item)) .slice(0, limit); } @@ -1817,8 +1820,10 @@ function findingIdentityCandidates(item: Record): readonly stri return [...new Set(candidates.filter((value): value is string => value !== null))]; } -function reclassifyVisibleFinding(item: Record, failure: string | null, hasObserverCommandFailure: boolean): Record { +function reclassifyVisibleFinding(item: Record, failure: string | null, hasObserverCommandFailure: boolean, hasObserverCommandFailureEvidence: boolean): Record | null { const ids = findingIdentityCandidates(item); + if (ids.includes("observer-command-failure-evidence-missing") && hasObserverCommandFailureEvidence) return null; + if (ids.includes("no-samples-artifact-evidence-missing") && hasObserverCommandFailure) return null; if (ids.includes("quick-verify-command-sequence-failed")) { return retagVisibleFinding(item, "account-session-command-failed-before-observation", { summary: "account/session setup command failed before any business turn could be observed.", @@ -1829,7 +1834,7 @@ function reclassifyVisibleFinding(item: Record, failure: string nextAction: "Inspect the first failed account/session/workbench command and rerun quick verify after that command succeeds.", }); } - if (ids.includes("observer-command-failed") && !hasCommandFailureEvidence(item)) { + if (ids.includes("observer-command-failed") && !hasObserverCommandFailureEvidence && !hasCommandFailureEvidence(item)) { return retagVisibleFinding(item, "observer-command-failure-evidence-missing", { summary: "report says observer control commands failed, but the first failed command evidence is missing.", rootCause: "observer-command-failure-evidence-missing",