#!/usr/bin/env tsx import { promises as fs } from "fs"; type Check = | { id: string; category: string; description: string; op: "equalsOrTrue" | "includesAll"; path?: string; expected?: any; severity: "blocker" | "major" | "minor"; conditions?: Check[]; } | { id: string; category: string; description: string; op: "and" | "or"; severity: "blocker" | "major" | "minor"; conditions: Check[]; }; const getPath = (obj: any, path?: string) => { if (!path) return undefined; return path.split(".").reduce((o, k) => (o ? o[k] : undefined), obj); }; const evalCheck = (facts: any, c: Check): { ok: boolean; reason?: string } => { switch (c.op) { case "equalsOrTrue": { const v = getPath(facts, c.path); const ok = c.expected === true ? !!v : v === c.expected; return ok ? { ok } : { ok, reason: `Expected ${c.path} == ${JSON.stringify( c.expected )} but got ${JSON.stringify(v)}`, }; } case "includesAll": { const v = String(getPath(facts, c.path) ?? ""); const need = (c.expected ?? []) as string[]; const miss = need.filter((n) => !v.includes(n)); return miss.length === 0 ? { ok: true } : { ok: false, reason: `Missing substrings ${miss.join(", ")} in ${c.path}`, }; } case "and": { const results = (c.conditions ?? []).map((x) => evalCheck(facts, x)); const allOk = results.every((r) => r.ok); return allOk ? { ok: true } : { ok: false, reason: results.find((r) => !r.ok)?.reason }; } case "or": { const results = (c.conditions ?? []).map((x) => evalCheck(facts, x)); const anyOk = results.some((r) => r.ok); return anyOk ? { ok: true } : { ok: false, reason: "All OR branches failed" }; } default: return { ok: false, reason: "Unknown op" }; } }; (async () => { const facts = JSON.parse( await fs.readFile("conductor/artifacts/facts.json", "utf8") ); const matrix = JSON.parse( await fs.readFile("conductor/rules/regression-matrix.json", "utf8") ); const failures: { id: string; severity: string; reason: string }[] = []; for (const c of matrix.checks as Check[]) { const res = evalCheck(facts, c); if (!res.ok && (c.severity === "blocker" || c.severity === "major")) { failures.push({ id: c.id, severity: c.severity, reason: res.reason ?? "failed", }); } } if (failures.length) { console.error("⛔ Regression Shield failed:"); for (const f of failures) console.error(` - [${f.severity}] ${f.id}: ${f.reason}`); process.exit(2); } console.log("✅ Regression Shield passed"); })();