diff --git a/robosoft/robots/core-integration.robot.mjs b/robosoft/robots/core-integration.robot.mjs index 47904d34..2e59e34d 100644 --- a/robosoft/robots/core-integration.robot.mjs +++ b/robosoft/robots/core-integration.robot.mjs @@ -127,8 +127,18 @@ export default { // may be "failed" (the probe manifest has findings) โ€” that is a REAL Core // evaluation, which is exactly what proves the round-trip. const data = unwrap(r.body); - check('Core returns a real evaluation verdict (gates evaluated)', r.body?.success === true && Array.isArray(data.gates), { - detail: Array.isArray(data.gates) ? summarizeVerdict(data) : r.body?.error?.message || 'no gates', + // `results.gate`, NOT `gates`. The Core's canonical EvaluationResult nests + // per-kind results under `results` (`gate`, `artifact`, `compliance`); it is + // the TRACKER's own DTO that flattens them to `gates`, and this robot talks + // to the Core through the gateway, so it gets the canonical shape. + // + // Asserting the flattened key made this step report "no gates" against a + // Core that had evaluated six of them โ€” a defect in the instrument that read + // as a defect in the product. Measured against a captured REST response: + // `data.gates` is undefined, `data.results.gate` has 6 entries. + const gates = gatesOf(data); + check('Core returns a real evaluation verdict (gates evaluated)', r.body?.success === true && Array.isArray(gates), { + detail: Array.isArray(gates) ? summarizeVerdict(data) : r.body?.error?.message || 'no gates', }); } @@ -177,8 +187,16 @@ function parseMcpInner(body) { } /** One-line summary of an evaluation verdict from the Core's `data` payload. */ +/** The canonical EvaluationResult nests gates under `results.gate`. */ +function gatesOf(data) { + return Array.isArray(data?.results?.gate) ? data.results.gate : undefined; +} + function summarizeVerdict(data) { - const gates = Array.isArray(data.gates) ? data.gates : []; - const failed = gates.filter((g) => String(g.verdict).toLowerCase() === 'failed').length; + const gates = gatesOf(data) ?? []; + // The Core emits `FAIL`, not `failed`. Comparing against 'failed' reported + // zero failures on a run where all six gates had failed โ€” a green-looking + // summary over a red result, which is worse than no summary. + const failed = gates.filter((g) => /^fail/i.test(String(g.verdict))).length; return `gates=${gates.length} ยท failed=${failed}`; }