diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d0c0d5..7d00c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ project adheres to [Semantic Versioning](https://semver.org/). - Playwright setup note in the Quick start section of `README.md`. ### Fixed +- The integrator example now removes its scratch directory when decide fails + or policy refuses the response. `process.exit` skipped the cleanup, so each + failed run left an `aas-integrator-` directory behind. - `aas inspect --root` now rejects a blank path, matching the other saved-case commands. A whitespace root used to be treated as a directory and failed with a filesystem error. diff --git a/examples/review-handoff.mjs b/examples/review-handoff.mjs index 42558f2..6874897 100644 --- a/examples/review-handoff.mjs +++ b/examples/review-handoff.mjs @@ -45,9 +45,16 @@ const DOMAIN_FIXTURES = { }, }; +class HandoffExit extends Error { + constructor(code) { + super("handoff exit"); + this.exitCode = code; + } +} + function fail(reason) { process.stderr.write(`integrator example failed: ${reason}\n`); - process.exit(1); + throw new HandoffExit(1); } function parseArgs(argv) { @@ -111,7 +118,7 @@ function main() { const evaluation = readJson("decide", decided); if (evaluation.passed !== true) { note("decide: policy refused the response; act and prove are skipped (fail-closed)"); - process.exit(1); + throw new HandoffExit(1); } note(`decide: policy ${evaluation.policy_id} passed (${evaluation.rule_results.length} rules)`); @@ -218,4 +225,12 @@ function main() { } } -main(); +try { + main(); +} catch (error) { + if (error instanceof HandoffExit) { + process.exitCode = error.exitCode; + } else { + throw error; + } +} diff --git a/test/stack.test.mjs b/test/stack.test.mjs index adb80e8..abc47b7 100644 --- a/test/stack.test.mjs +++ b/test/stack.test.mjs @@ -2075,6 +2075,33 @@ test('filtered case pages preserve scan bounds and continuation across nonmatche for(const args of [['--domain','real'],['--outcome','paid'],['--search',''],['--search','x'.repeat(201)],['--domain','refund','--domain','inventory']]) assert.equal((await captureMain(['cases',...args,'--json'])).exitCode,2); }); +test("review handoff removes its scratch directory when decide cannot start", () => { + const dir = mkdtempSync(join(tmpdir(), "aas-handoff-scratch-")); + const preload = join(dir, "preload.cjs"); + writeFileSync(preload, [ + "if (typeof process.argv[1] === 'string' && process.argv[1].includes('sys.version_info')) {", + " process.stdout.write('3.13\\n');", + " process.exit(0);", + "}", + "", + ].join("\n")); + const result = spawnSync(process.execPath, [join(ROOT, "examples/review-handoff.mjs"), "--response", "fail"], { + cwd: ROOT, + encoding: "utf8", + timeout: 15000, + env: { + ...process.env, + TMPDIR: dir, + TMP: dir, + TEMP: dir, + AAS_PYTHON: process.execPath, + NODE_OPTIONS: `--require ${JSON.stringify(preload)}`, + }, + }); + assert.equal(result.status, 1, `${result.stderr}\n${result.stdout}`); + assert.deepEqual(readdirSync(dir).filter((name) => name.startsWith("aas-integrator-")), []); +}); + test('review handoff example honors the explicit interpreter override', () => { const child=spawnSync(process.execPath,[join(ROOT,'examples/review-handoff.mjs')],{cwd:ROOT,encoding:'utf8',timeout:5000,env:{...process.env,AAS_PYTHON:'aas-synthetic-missing-python'}}); assert.equal(child.status,1);assert.match(child.stderr,/AAS_PYTHON/);assert.match(child.stderr,/did not report a usable Python version/);