// Coverage for 41-validate-or-regression.sh — the per-target gate the operator window calls in step [a]. // It enforces the migration's RATIFIED contract ("introduce no regression"), NOT the stricter zero-failure // gate that made run-operator-window.sh STOP on voyage's / ai-psychosis's pre-existing in-repo red. 41 is // thin glue over already-tested detectors (sc2-regression.sh — sc-checks.test.mjs; capture-fails.sh — // capture-fails.test.mjs) and over 40-validate-standalone.sh (strict). This suite drives the REAL 41 inside // a self-contained fake migration tree: the heavy extract pipeline (40) is STUBBED so the regression-branch // glue is exercised hermetically, with no /tmp/polyrepo-migration state and no real plugin extracts. // // Branches covered: strict-PASS passthrough; regression-relative PASS (standalone ⊆ in-repo); genuine // regression FAIL (standalone-only failure); structure-validator FAIL is NOT regression-eligible; the // sc2_gate target stays strict (PASS + FAIL). import test from 'node:test'; import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; import { mkdtempSync, rmSync, writeFileSync, mkdirSync, copyFileSync, chmodSync } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const here = path.dirname(fileURLToPath(import.meta.url)); // One node:test file = imports ONCE + a failing test() per name. (Concatenating per-name single-file // strings would duplicate `import test` → SyntaxError → a file-level not-ok instead of per-test names.) const failSuite = (names) => "import test from 'node:test';\nimport assert from 'node:assert/strict';\n" + names.map((n) => `test(${JSON.stringify(n)}, () => assert.equal(1, 2));`).join('\n') + '\n'; function write(p, content, mode) { mkdirSync(path.dirname(p), { recursive: true }); writeFileSync(p, content); if (mode) chmodSync(p, mode); } // Build a fake migration tree: SCRIPT_DIR nested 3 deep so 41's REPO_ROOT ($SCRIPT_DIR/../../..) is the root. function buildTree() { const root = mkdtempSync(path.join(os.tmpdir(), 'mig41-')); const mig = path.join(root, 'a', 'b', 'c'); // SCRIPT_DIR; ../../.. === root const work = path.join(root, '_work'); // $WORK mkdirSync(mig, { recursive: true }); // real scripts under test + reused detectors for (const f of ['41-validate-or-regression.sh', 'capture-fails.sh', 'sc2-regression.sh']) { copyFileSync(path.join(here, f), path.join(mig, f)); } // stub strict 40-validate-standalone.sh: passes ONLY for the designated strict-pass target. write(path.join(mig, '40-validate-standalone.sh'), '#!/usr/bin/env bash\n[ "${1:-}" = "strictpass" ] && exit 0\nexit 1\n', 0o755); // sc2_gate stubs write(path.join(mig, 'gate-pass.sh'), '#!/usr/bin/env bash\nexit 0\n', 0o755); write(path.join(mig, 'gate-fail.sh'), '#!/usr/bin/env bash\nexit 1\n', 0o755); const NT = "node --test 'tests/**/*.test.mjs'"; const map = { targets: { strictpass: { test_cmd: NT }, subset: { test_cmd: NT }, regress: { test_cmd: NT }, structure: { test_cmd: 'bash validate-plugin.sh' }, gatepass: { test_cmd: NT, sc2_gate: 'gate-pass.sh' }, gatefail: { test_cmd: NT, sc2_gate: 'gate-fail.sh' }, }, }; write(path.join(mig, 'plugin-map.json'), JSON.stringify(map, null, 2)); // node:test suites for the regression-eligible targets. dest = $WORK/ (needs .git); baseline = root/plugins/. const suite = (key, names) => { mkdirSync(path.join(work, key, '.git'), { recursive: true }); write(path.join(work, key, 'tests', 'x.test.mjs'), failSuite(names.standalone)); write(path.join(root, 'plugins', key, 'tests', 'x.test.mjs'), failSuite(names.baseline)); }; suite('subset', { standalone: ['shared red'], baseline: ['shared red', 'other in-repo red'] }); suite('regress', { standalone: ['shared red', 'extract regressed'], baseline: ['shared red'] }); return { root, mig, work }; } function run41(tree, key) { // Strip NODE_TEST_CONTEXT: 41 → capture-fails → `node --test`, which emits no TAP if it inherits the // harness's nested-test context (Step-6 env-clean gotcha). The real window runs as plain bash, unnested. const env = { ...process.env, WORK: tree.work }; delete env.NODE_TEST_CONTEXT; return spawnSync('bash', [path.join(tree.mig, '41-validate-or-regression.sh'), key], { encoding: 'utf8', env }); } test('strict-PASS passthrough: 40 passes → 41 PASS (standalone strict), exit 0, no capture', () => { const tree = buildTree(); try { const r = run41(tree, 'strictpass'); assert.equal(r.status, 0, `strict pass must exit 0: ${r.stderr}`); assert.match(r.stdout, /PASS \(standalone strict\)/, r.stdout); } finally { rmSync(tree.root, { recursive: true, force: true }); } }); test('regression-relative PASS: standalone failing-set ⊆ in-repo → exit 0, reports N pre-existing', () => { const tree = buildTree(); try { const r = run41(tree, 'subset'); assert.equal(r.status, 0, `a subset of in-repo red is not a migration regression: ${r.stdout}${r.stderr}`); assert.match(r.stdout, /pre-existing, regression-relative/, r.stdout); } finally { rmSync(tree.root, { recursive: true, force: true }); } }); test('genuine regression FAIL: a standalone-only failure (absent in-repo) → non-zero, names the regression', () => { const tree = buildTree(); try { const r = run41(tree, 'regress'); assert.notEqual(r.status, 0, 'a new failure introduced by extraction must STOP the window'); assert.match(r.stderr, /regression/, r.stderr); assert.match(r.stderr, /extract regressed/, `the regressing test name must be surfaced: ${r.stderr}`); } finally { rmSync(tree.root, { recursive: true, force: true }); } }); test('structure-validator FAIL is NOT regression-eligible: bash validator fail → non-zero, never softened', () => { const tree = buildTree(); try { const r = run41(tree, 'structure'); assert.notEqual(r.status, 0, 'a deterministic structure-validator failure is a genuine defect'); assert.match(r.stderr, /not regression-eligible/, r.stderr); } finally { rmSync(tree.root, { recursive: true, force: true }); } }); test('sc2_gate target stays strict: gate PASS → exit 0', () => { const tree = buildTree(); try { const r = run41(tree, 'gatepass'); assert.equal(r.status, 0, r.stderr); assert.match(r.stdout, /PASS \(gate-pass\.sh\)/, r.stdout); } finally { rmSync(tree.root, { recursive: true, force: true }); } }); test('sc2_gate target stays strict: gate FAIL → non-zero', () => { const tree = buildTree(); try { const r = run41(tree, 'gatefail'); assert.notEqual(r.status, 0, 'a failing dedicated gate must STOP the window'); assert.match(r.stderr, /FAIL \(gate-fail\.sh\)/, r.stderr); } finally { rmSync(tree.root, { recursive: true, force: true }); } });