fix(ci): fail closed on newest full release proof (#656)

Issue: #656
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-09-26 10:02:40 +03:00
parent 9287f798b3
commit 43fab645b0
9 changed files with 59 additions and 21 deletions
+4 -5
View File
@@ -434,16 +434,15 @@ export function evaluateCiProof({
}
/**
* A complete green proof is content-addressed evidence for the candidate and
* remains valid regardless of a later duplicate run (#619). When no green
* proof exists, keep the newest decisive state so failures still fail closed.
* Evaluations arrive newest first. Cancelled and stale runs do not describe
* the requested policy; the newest remaining run is the verdict. In
* particular, a later failed full run must not be hidden by an older green
* proof for the same candidate (#656).
*/
export function selectCiProofVerdict(evaluations) {
const relevant = (evaluations || []).filter(
(item) => item?.status !== 'cancelled' && item?.status !== 'stale',
);
const green = relevant.find((item) => item?.status === 'green');
if (green) return green;
if (relevant.length) return relevant[0];
return { status: 'missing', note: 'no run carries a proof for the requested policy', url: null };
}
+5 -5
View File
@@ -76,11 +76,11 @@ export async function classifyValidateProofs({
}
}
const current = evaluations.at(-1);
// #619: a complete proof is immutable evidence for this exact SHA/tree.
// A later duplicate may fail for workflow topology rather than product
// content, so only a green proof ends the search; failures remain the
// fallback verdict when no run proves the candidate green.
if (current.status === 'green') break;
// #656: runs are newest first. A stale/light or cancelled run does not
// answer the release policy, so search past it. Every compatible state —
// including pending, missing and failed — is decisive and fails closed;
// an older green proof must not hide a newer full failure.
if (current.status !== 'cancelled' && current.status !== 'stale') break;
}
return selectCiProofVerdict(evaluations);
}