From 1c200a2e0dc372ea48aad1415df3cb219ba06244 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 16:37:19 +0000 Subject: [PATCH] Reject an unknown act domain before spawning the rail CLI. runDecide already refused a domain other than refund or inventory. runAct passed any other value through as the demo command. --- CHANGELOG.md | 3 +++ bin/aas.mjs | 3 +++ test/stack.test.mjs | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 402fd4b..9853f33 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 +- `runAct` now rejects a domain other than `refund` or `inventory` before it + spawns the rail CLI. Any other value was passed through as the demo + command. - An act result that explicitly reports `fault: null` is stored as null. The report used to replace that null with the requested fault, so a cleared fault was recorded as `duplicate` when `--fault duplicate` was passed. diff --git a/bin/aas.mjs b/bin/aas.mjs index 82a2489..c7fa65e 100644 --- a/bin/aas.mjs +++ b/bin/aas.mjs @@ -675,6 +675,9 @@ export function runAct( domain = "refund", } = {}, ) { + if (!DEMO_DOMAINS.has(domain)) { + throw new Error("act domain must be refund or inventory"); + } const crctl = join(depsDir, "consequence-rail", "cmd", "crctl.js"); if (runner === runCapture && !existsSync(crctl)) { throw missingChildTool("act CLI (deps/consequence-rail/cmd/crctl.js)"); diff --git a/test/stack.test.mjs b/test/stack.test.mjs index 9b2d8c2..b45cc80 100644 --- a/test/stack.test.mjs +++ b/test/stack.test.mjs @@ -1838,6 +1838,19 @@ test("runAct targets the requested rail demo domain", () => { assert.equal(seen[0][2], "inventory"); }); +test("runAct rejects an unknown domain before spawning the rail CLI", () => { + let called = false; + assert.throws(() => runAct("none", { + depsDir: "deps", + domain: "payments", + runner: () => { + called = true; + return { status: 0, stdout: '{"outcome":"settled","state":"CLOSED","fault":"none","action_id":"a"}\n', stderr: "", error: null }; + }, + }), /act domain must be refund or inventory/); + assert.equal(called, false); +}); + test("runDecide rejects an unknown domain instead of loading the refund policy", () => { let called = false; assert.throws(() => runDecide("unused", {