import assert from "node:assert/strict"; import { describe, it } from "node:test"; import { prepareWorkflowLaunchParams } from "../../src/runs/foreground/subagent-executor.ts"; describe("workflow launch params", () => { it("keeps omitted workflow child async foreground", () => { assert.deepEqual( prepareWorkflowLaunchParams( {}, { agent: "worker", task: "Run" }, "workflow-run", "run", ), { agent: "worker", task: "Run", async: false, workflowParentRunId: "workflow-run", workflowKey: "run", }, ); }); it("preserves explicit async workflow children", () => { assert.deepEqual( prepareWorkflowLaunchParams( {}, { agent: "worker", task: "Run", async: true }, "workflow-run", "run", ), { agent: "worker", task: "Run", async: true, workflowParentRunId: "workflow-run", workflowKey: "run", }, ); }); it("keeps a bridge override scoped to the target workflow child", () => { assert.deepEqual( prepareWorkflowLaunchParams( {}, { agent: "worker", task: "Run", intercomBridge: { mode: "off" } }, "workflow-run", "isolated", ), { agent: "worker", task: "Run", intercomBridge: { mode: "off" }, async: false, workflowParentRunId: "workflow-run", workflowKey: "isolated", }, ); assert.equal(prepareWorkflowLaunchParams({}, { agent: "worker", task: "Run" }, "workflow-run", "sibling").intercomBridge, undefined); }); it("places workflow child gates inside managed worktree tasks", () => { assert.deepEqual( prepareWorkflowLaunchParams( {}, { agent: "worker", task: "Implement", worktree: true, gate: "npm test" }, "workflow-run", "gated", ), { worktree: true, async: false, workflowParentRunId: "workflow-run", workflowKey: "gated", tasks: [{ agent: "worker", task: "Implement", acceptance: { level: "verified", verify: [{ id: "gate", command: "npm test" }] }, }], }, ); }); it("preserves a bridge override for retained workflow children", () => { assert.deepEqual( prepareWorkflowLaunchParams( {}, { resume: "retained-run", task: "Continue", intercomBridge: { mode: "off" } }, "workflow-run", "continue", ), { action: "resume", id: "retained-run", message: "Continue", workflowParentRunId: "workflow-run", workflowKey: "continue", intercomBridge: { mode: "off" }, }, ); }); it("rejects gate defaults on retained resume items", () => { assert.throws( () => prepareWorkflowLaunchParams( { gate: "npm test" }, { resume: "retained-run", task: "Continue" }, "workflow-run", "continue", ), /gate is not supported with retained resume/, ); assert.throws( () => prepareWorkflowLaunchParams( {}, { resume: "retained-run", task: "Continue", gate: "npm test" }, "workflow-run", "continue", ), /gate is not supported with retained resume/, ); }); it("preserves execution limits and fan-out identity when routing retained resume items", () => { assert.deepEqual( prepareWorkflowLaunchParams( { turnBudget: { maxTurns: 8 }, toolBudget: { hard: 12, block: ["read"] } }, { resume: " retained-run ", task: "Continue carefully", maxRuntimeMs: 5_000, turnBudget: { maxTurns: 3, graceTurns: 1 }, toolBudget: { soft: 2, hard: 4, block: "*" }, }, "workflow-run", "continue", { missionDetached: true, runFanoutBudget: { version: 1, rootRunId: "root-run", directory: "/tmp/fanout", limit: 64, parentPath: "parent" } }, ), { action: "resume", id: "retained-run", message: "Continue carefully", workflowParentRunId: "workflow-run", workflowKey: "continue", runFanoutBudget: { version: 1, rootRunId: "root-run", directory: "/tmp/fanout", limit: 64, parentPath: "parent/workflow[continue]" }, mission: false, timeoutMs: 5_000, turnBudget: { maxTurns: 3, graceTurns: 1 }, toolBudget: { soft: 2, hard: 4, block: "*" }, }, ); }); });