diff --git a/.claude/skills/regression/run.mjs b/.claude/skills/regression/run.mjs index b6ad732c..7e20c0a9 100644 --- a/.claude/skills/regression/run.mjs +++ b/.claude/skills/regression/run.mjs @@ -64,3 +64,14 @@ export function parseLychee(stdout) { const errors = stdout.match(/(\d+)\s+Errors?/i)?.[1] ?? '0'; return `lychee ${ok}/${errors}`; } + +// ── pure: verdict ────────────────────────────────────────────────── +export function computeVerdict(results) { + const skipped = results.filter((r) => r.skipped).map((r) => r.label); + const failed = results + .filter((r) => !r.skipped && r.code !== 0) + .map((r) => r.label); + if (skipped.length) return { verdict: 'RED-INCOMPLETE', exitCode: 1, failed, skipped }; + if (failed.length) return { verdict: 'RED', exitCode: 1, failed, skipped }; + return { verdict: 'GREEN', exitCode: 0, failed, skipped }; +} diff --git a/.claude/skills/regression/run.test.mjs b/.claude/skills/regression/run.test.mjs index db25c1b9..a0ad0272 100644 --- a/.claude/skills/regression/run.test.mjs +++ b/.claude/skills/regression/run.test.mjs @@ -4,6 +4,7 @@ import { resolveBinary, buildHeader, parseExit, parsePest, parseVitest, parseViteBuild, parseLarastan, parseGitleaks, parseLychee, + computeVerdict, } from './run.mjs'; test('resolveBinary: win32 → bin\\.exe', () => { @@ -79,3 +80,40 @@ test('parseLychee: with broken links', () => { const out = '🔍 327 Total\n✅ 325 OK\n🚫 2 Errors'; assert.equal(parseLychee(out), 'lychee 325/2'); }); + +test('computeVerdict: all exit 0 → GREEN, exit code 0', () => { + const v = computeVerdict([ + { label: 'Pint', code: 0, skipped: false }, + { label: 'ESLint', code: 0, skipped: false }, + ]); + assert.equal(v.verdict, 'GREEN'); + assert.equal(v.exitCode, 0); + assert.deepEqual(v.failed, []); +}); +test('computeVerdict: one non-zero exit → RED, exit code 1', () => { + const v = computeVerdict([ + { label: 'Pint', code: 0, skipped: false }, + { label: 'Larastan', code: 1, skipped: false }, + ]); + assert.equal(v.verdict, 'RED'); + assert.equal(v.exitCode, 1); + assert.deepEqual(v.failed, ['Larastan']); +}); +test('computeVerdict: a skipped check → RED-INCOMPLETE', () => { + const v = computeVerdict([ + { label: 'Pint', code: 0, skipped: false }, + { label: 'gitleaks', code: null, skipped: true }, + ]); + assert.equal(v.verdict, 'RED-INCOMPLETE'); + assert.equal(v.exitCode, 1); + assert.deepEqual(v.skipped, ['gitleaks']); +}); +test('computeVerdict: skipped takes precedence over a failure', () => { + const v = computeVerdict([ + { label: 'Larastan', code: 1, skipped: false }, + { label: 'lychee', code: null, skipped: true }, + ]); + assert.equal(v.verdict, 'RED-INCOMPLETE'); + assert.deepEqual(v.failed, ['Larastan']); + assert.deepEqual(v.skipped, ['lychee']); +});