Files
harness-tools/__archived/extensions/workflow/engine.test.ts
T

4368 lines
146 KiB
TypeScript

import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { createHash } from "node:crypto";
import {
mkdir,
mkdtemp,
readFile,
rm,
symlink,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import * as path from "node:path";
import test from "node:test";
import { promisify } from "node:util";
import {
DEFAULT_WORKFLOW_CONFIG,
type AgentProfile,
type GateDecision,
type GateKind,
type WorkflowAssignment,
type WorkflowRun,
} from "./domain.ts";
import {
decisionIsLegal,
WorkflowEngine,
type AssignmentExecutor,
type AssignmentRequest,
type HostCommandExecutor,
type StartWorkflowCommand,
type WorkflowCommand,
type WorkflowView,
type WorkspaceManager,
} from "./engine.ts";
import { loadAgentProfiles } from "./profiles.ts";
import { runScriptSandbox } from "./script-runtime.ts";
import { WorkflowStore } from "./store.ts";
import {
GitWorkspaceManager,
WorkspaceSafetyError,
type PrepareWorkspaceOptions,
type WorkspacePlan,
} from "./workspace.ts";
const execFileAsync = promisify(execFile);
async function withTempDir(run: (directory: string) => Promise<void>) {
const directory = await mkdtemp(path.join(tmpdir(), "workflow-engine-"));
try {
await run(directory);
} finally {
await rm(directory, {
recursive: true,
force: true,
maxRetries: 10,
retryDelay: 20,
});
}
}
class ScriptedExecutor implements AssignmentExecutor {
readonly requests: AssignmentRequest[] = [];
readonly outputs = new Map<WorkflowAssignment["role"], unknown[]>();
enqueue(role: WorkflowAssignment["role"], output: unknown) {
const queued = this.outputs.get(role) ?? [];
queued.push(output);
this.outputs.set(role, queued);
}
async run(request: AssignmentRequest) {
this.requests.push(structuredClone(request));
const queued = this.outputs.get(request.role) ?? [];
if (queued.length === 0)
throw new Error(`No scripted ${request.role} result`);
const output = queued.shift();
if (output instanceof Error) throw output;
return structuredClone(output);
}
}
class BlockingExecutor implements AssignmentExecutor {
readonly requests: AssignmentRequest[] = [];
readonly started: Promise<void>;
private readonly output: unknown;
private releaseRun?: () => void;
private markStarted?: () => void;
constructor(output: unknown) {
this.output = output;
this.started = new Promise((resolve) => {
this.markStarted = resolve;
});
}
async run(request: AssignmentRequest) {
this.requests.push(request);
this.markStarted?.();
await new Promise<void>((resolve) => {
this.releaseRun = resolve;
});
return structuredClone(this.output);
}
release() {
this.releaseRun?.();
}
}
class FakeWorkspace implements WorkspaceManager {
prepareCalls = 0;
createCalls = 0;
reconcileCalls = 0;
branchExists = false;
createError?: Error;
reconcileState: WorkflowRun["workspace"]["state"] = "ready";
beforePrepare?: () => Promise<void>;
beforeCreate?: (plan: WorkspacePlan) => Promise<void>;
async prepare(options: PrepareWorkspaceOptions): Promise<WorkspacePlan> {
this.prepareCalls++;
await this.beforePrepare?.();
return {
sourceRoot: options.sourceRoot,
worktreeRoot: path.join(options.sourceRoot, options.configuredRoot),
worktreePath: path.join(
options.sourceRoot,
options.configuredRoot,
`workflow-${options.featureName}-${options.runId}`,
),
branch: `feature/${options.featureName}`,
baseHead: "abc123",
branchExists: this.branchExists,
sourceChanges: [],
sourceChangesOmitted: 0,
dirtySourceAllowed: false,
};
}
async create(plan: WorkspacePlan) {
this.createCalls++;
await this.beforeCreate?.(plan);
if (this.createError) throw this.createError;
await mkdir(plan.worktreePath, { recursive: true });
return {
state: "ready" as const,
sourceRoot: plan.sourceRoot,
root: plan.worktreePath,
branch: plan.branch,
baseHead: plan.baseHead,
};
}
async reconcile(workspace: WorkflowRun["workspace"]) {
this.reconcileCalls++;
return {
...workspace,
state: this.reconcileState,
...(this.reconcileState === "failed"
? { error: "reconciliation failed" }
: { error: undefined }),
};
}
}
interface Fixture {
store: WorkflowStore;
workspace: FakeWorkspace;
executor: ScriptedExecutor;
profiles: AgentProfile[];
engine(
sessionId?: string,
overrides?: Partial<ConstructorParameters<typeof WorkflowEngine>[0]>,
): WorkflowEngine;
}
async function fixture(
directory: string,
storeOptions: ConstructorParameters<typeof WorkflowStore>[1] = {},
): Promise<Fixture> {
const store = new WorkflowStore(path.join(directory, "state"), storeOptions);
const workspace = new FakeWorkspace();
const executor = new ScriptedExecutor();
const profiles = (
await loadAgentProfiles({ cwd: directory, projectTrusted: false })
).profiles;
let id = 0;
const createId = () => `id-${++id}`;
return {
store,
workspace,
executor,
profiles,
engine(sessionId = "session-1", overrides = {}) {
return new WorkflowEngine({
store,
workspace,
executor,
hostCommands: {
async run() {
return { result: { code: 0, stdout: "passed", stderr: "" } };
},
},
reviewDiff: {
async read() {
return "diff --git a/src/index.ts b/src/index.ts";
},
},
prototypeValidator: {
async validateCandidate({ output }) {
return {
artifactFiles: [],
visualDigest: createHash("sha256")
.update(output.path)
.digest("hex"),
};
},
async validateCleanup() {},
},
sourceRoot: directory,
parentSessionId: sessionId,
effectiveConfig: structuredClone(DEFAULT_WORKFLOW_CONFIG),
profiles,
now: () => 10_000,
createId,
processId: 100,
isProcessAlive: () => true,
...overrides,
});
},
};
}
function startCommand(
overrides: Partial<Omit<StartWorkflowCommand, "action">> = {},
): StartWorkflowCommand {
return {
action: "start" as const,
request: "Build a searchable dashboard",
featureName: "searchable-dashboard",
idempotencyKey: "start-key",
...overrides,
};
}
function discoveryWithQuestions() {
return {
questions: [{ id: "data-source", prompt: "Which data source?" }],
candidatePaths: ["src/routes/dashboard.ts"],
languages: ["typescript"],
tags: ["frontend", "ui"],
riskTags: ["migration"],
};
}
function enqueueSuccessfulPipeline(setup: Fixture) {
setup.executor.enqueue("implement", {
summary: "Implemented the approved plan",
changedPaths: ["src/index.ts"],
focusedTests: ["feature behavior"],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Focused behavior verified",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
}
function design(uiImpact: "none" | "minor" | "non-trivial" = "minor") {
return {
plan: "# Approved plan\n\nImplement the dashboard.",
uiImpact,
uiImpactEvidence: ["Planner classified the dashboard change"],
affectedUiSurfaces: ["Dashboard"],
acceptanceCriteria: ["Changed behavior has focused tests"],
verificationCommands: [
{
executable: "npm",
args: ["test"],
ownedPaths: [],
kind: "test",
},
],
assignments: [
{
role: "implement",
profileName: "feature-implementer",
rationale: "Implement the approved plan",
mutates: true,
ownedPaths: ["src"],
},
{
role: "verify",
profileName: "feature-verifier",
rationale: "Verify the implementation",
mutates: false,
ownedPaths: [],
},
{
role: "review",
profileName: "independent-reviewer",
rationale: "Review independently",
mutates: false,
ownedPaths: [],
},
...(uiImpact === "non-trivial"
? [
{
role: "prototype",
profileName: "ui-prototyper",
rationale: "Prototype the non-trivial UI",
mutates: true,
ownedPaths: ["prototypes"],
},
]
: []),
],
};
}
async function createClaimedContextHandoff(
setup: Fixture,
): Promise<WorkflowView> {
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-claimed-context",
});
const selected = await setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "fresh-session",
},
idempotencyKey: "select-claimed-context",
});
const intent = selected.implementationContext;
assert.ok(intent);
const continued = await setup.engine().execute({
action: "continue-context",
runId: selected.runId,
gateId: intent.gateId,
preparationId: intent.preparationId,
planRevision: intent.planRevision,
contextRevision: intent.runRevision,
expectedRevision: selected.revision,
sourceSessionFile: "/sessions/source.jsonl",
});
const pending = continued.implementationContext;
assert.ok(pending);
return setup.engine().execute({
action: "claim-context-handoff",
runId: continued.runId,
gateId: pending.gateId,
preparationId: pending.preparationId,
planRevision: pending.planRevision,
contextRevision: pending.runRevision,
expectedRevision: continued.revision,
sourceSessionFile: "/sessions/source.jsonl",
targetSessionFile: "/sessions/target.jsonl",
reason: "new",
});
}
test("non-trivial UI design fails with the exact missing-prototype validation error", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
const missingPrototype = design("non-trivial");
missingPrototype.assignments = missingPrototype.assignments.filter(
(assignment) => assignment.role !== "prototype",
);
setup.executor.enqueue("design", missingPrototype);
const failed = await setup.engine().execute(startCommand());
assert.equal(failed.status, "failed");
assert.equal(failed.error?.code, "invalid-design-output");
assert.equal(
failed.error?.message,
"Design must select exactly one prototype Assignment",
);
});
});
test("feature designer contract requires the non-trivial prototype assignment", async () => {
const instructions = await readFile(
new URL("./agents/feature-designer.md", import.meta.url),
"utf8",
);
assert.match(
instructions,
/For `non-trivial` UI impact, also emit exactly one eligible `prototype` assignment/,
);
assert.match(instructions, /owned paths isolate prototype artifacts/);
assert.match(instructions, /one eligible `implement` assignment/);
assert.match(instructions, /one eligible `verify` assignment/);
assert.match(instructions, /one eligible independent `review` assignment/);
});
test("design Assignment execution times out with persisted evidence", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
const blocking = new BlockingExecutor(design("minor"));
const operation = setup.engine("session-1", {
executor: {
async run(request) {
if (request.role === "discover") {
return setup.executor.run(request);
}
return blocking.run(request);
},
},
assignmentExecutionTimeoutMs: 20,
assignmentAbortTimeoutMs: 5,
}).execute(startCommand());
await blocking.started;
const failed = await operation;
blocking.release();
assert.equal(failed.status, "failed");
assert.equal(failed.error?.code, "assignment-timeout");
assert.match(failed.error?.message ?? "", /Assignment design timed out after 20ms/);
const persisted = await setup.store.loadRun(failed.runId);
const designAssignment = persisted.assignments.find(
(assignment) => assignment.role === "design",
);
assert.equal(designAssignment?.status, "interrupted");
assert.match(designAssignment?.error ?? "", /timed out after 20ms/);
});
});
test("executor rejection clears its deadline before transcript persistence", async (context) => {
await withTempDir(async (directory) => {
context.mock.timers.enable({ apis: ["setTimeout"] });
const setup = await fixture(directory);
let markStarted = () => {};
const started = new Promise<void>((resolve) => {
markStarted = resolve;
});
let rejectExecution = (_error: Error) => {};
const execution = new Promise<never>((_resolve, reject) => {
rejectExecution = reject;
});
const executor: AssignmentExecutor = {
run() {
markStarted();
return execution;
},
};
const saveTranscript = setup.store.saveAssignmentTranscript.bind(setup.store);
let markTranscriptStarted = () => {};
const transcriptStarted = new Promise<void>((resolve) => {
markTranscriptStarted = resolve;
});
let finishTranscript = () => {};
const transcriptMayFinish = new Promise<void>((resolve) => {
finishTranscript = resolve;
});
setup.store.saveAssignmentTranscript = async (
runId,
assignmentId,
transcript,
) => {
markTranscriptStarted();
await transcriptMayFinish;
return saveTranscript(runId, assignmentId, transcript);
};
const operation = setup.engine("session-1", {
executor,
assignmentExecutionTimeoutMs: 10,
}).execute(startCommand());
await started;
context.mock.timers.tick(9);
rejectExecution(Object.assign(new Error("provider rejected near deadline"), {
assignmentMetadata: {
transcript: [{ role: "assistant", text: "partial response" }],
},
}));
await transcriptStarted;
context.mock.timers.tick(1);
finishTranscript();
const failed = await operation;
assert.equal(failed.status, "failed");
assert.equal(failed.error?.code, "assignment-failed");
assert.match(failed.error?.message ?? "", /provider rejected near deadline/);
assert.equal(failed.assignments[0]?.status, "failed");
});
});
test("assignment timeout retains its mutation fence until the executor settles", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const blocking = new BlockingExecutor({
summary: "late implementation",
changedPaths: ["src/index.ts"],
focusedTests: ["late behavior"],
});
const executor: AssignmentExecutor = {
async run(request) {
return request.role === "implement"
? blocking.run(request)
: setup.executor.run(request);
},
};
const engine = setup.engine("session-1", {
executor,
assignmentExecutionTimeoutMs: 20,
assignmentAbortTimeoutMs: 5,
});
const planned = await engine.execute(startCommand());
const approved = await engine.execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-timeout-plan",
});
const operation = engine.execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "start-timeout-implementation",
});
await blocking.started;
const timedOut = await operation;
assert.equal(timedOut.status, "waiting");
assert.equal(timedOut.openGate?.kind, "recovery");
assert.equal(timedOut.error?.code, "mutation-recovery-required");
const interrupted = timedOut.assignments.find(
(assignment) => assignment.role === "implement",
);
assert.equal(interrupted?.status, "interrupted");
assert.ok(interrupted?.executionToken);
assert.ok(
await setup.store.loadAssignmentLease(
timedOut.runId,
interrupted?.id ?? "missing",
),
);
await assert.rejects(
engine.execute({
action: "decide",
runId: timedOut.runId,
gateId: timedOut.openGate?.id ?? "missing",
expectedRevision: timedOut.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "recover-before-timeout-settlement",
}),
/still active/,
);
blocking.release();
for (let attempt = 0; attempt < 100; attempt++) {
const lease = await setup.store.loadAssignmentLease(
timedOut.runId,
interrupted?.id ?? "missing",
);
if (!lease) break;
await new Promise((resolve) => setTimeout(resolve, 1));
}
assert.equal(
await setup.store.loadAssignmentLease(
timedOut.runId,
interrupted?.id ?? "missing",
),
undefined,
);
setup.executor.enqueue("remediate", {
summary: "recovered implementation",
changedPaths: ["src/index.ts"],
focusedTests: ["recovered behavior"],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "verified after settlement",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const recovered = await engine.execute({
action: "decide",
runId: timedOut.runId,
gateId: timedOut.openGate?.id ?? "missing",
expectedRevision: timedOut.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "recover-after-timeout-settlement",
});
assert.equal(recovered.status, "completed", recovered.error?.message);
});
});
test("old heartbeat never permits takeover from a live run owner", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-before-live-owner-recovery",
});
const owned = await setup.store.loadRun(approved.runId);
assert.ok(owned.implementationContext);
owned.lease = {
processId: 901,
activeParentSessionId: "session-1",
heartbeatAt: 1,
worktreeIdentity: owned.workspace.root,
};
owned.revision++;
owned.implementationContext.runRevision = owned.revision;
await setup.store.saveRun(owned, owned.revision - 1);
assert.deepEqual(
await setup.engine("session-2", {
now: () => 100_000,
processId: 902,
isProcessAlive: (processId) => processId === 901,
}).recover(),
[],
);
const fenced = await setup.store.loadRun(owned.runId);
assert.equal(fenced.lease?.activeParentSessionId, "session-1");
assert.equal(
fenced.implementationContext?.activeParentSessionId,
"session-1",
);
assert.equal(fenced.lease?.processId, 901);
assert.equal(fenced.parentSessionIds.includes("session-2"), false);
});
});
test("startup recovery fails closed for a fresh live owner and atomically rotates dead ownership", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate!.id,
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-before-owner-recovery",
});
const abandoned = await setup.store.loadRun(approved.runId);
assert.ok(abandoned.implementationContext);
abandoned.lease = {
processId: 901,
activeParentSessionId: "session-1",
heartbeatAt: 1,
worktreeIdentity: abandoned.workspace.root,
};
abandoned.revision++;
abandoned.implementationContext.runRevision = abandoned.revision;
await setup.store.saveRun(abandoned, abandoned.revision - 1);
const liveRecovery = await setup.engine("session-2", {
processId: 902,
isProcessAlive: (processId) => processId === 901,
}).recover();
assert.deepEqual(liveRecovery, []);
const stillOwned = await setup.store.loadRun(abandoned.runId);
assert.equal(stillOwned.lease?.activeParentSessionId, "session-1");
assert.equal(
stillOwned.implementationContext?.activeParentSessionId,
"session-1",
);
await setup.engine("session-2", {
processId: 902,
isProcessAlive: () => false,
}).recover();
const adopted = await setup.store.loadRun(abandoned.runId);
assert.equal(adopted.lease?.activeParentSessionId, "session-2");
assert.equal(
adopted.implementationContext?.activeParentSessionId,
"session-2",
);
assert.equal(adopted.lease?.processId, 902);
assert.ok(adopted.parentSessionIds.includes("session-2"));
adopted.lease = undefined;
adopted.revision++;
adopted.implementationContext!.runRevision = adopted.revision;
await setup.store.saveRun(adopted, adopted.revision - 1);
await setup.engine("session-3", {
processId: 903,
isProcessAlive: () => false,
}).recover();
const adoptedFromAbsent = await setup.store.loadRun(abandoned.runId);
assert.equal(adoptedFromAbsent.lease?.activeParentSessionId, "session-3");
assert.equal(
adoptedFromAbsent.implementationContext?.activeParentSessionId,
"session-3",
);
assert.equal(adoptedFromAbsent.lease?.processId, 903);
});
});
test("analysis scripts run persisted read-only Assignments with tool intersection", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const engine = setup.engine("session-1", {
scriptRuntime: { run: runScriptSandbox },
});
const command = {
action: "start-analysis" as const,
request: "Audit persistence behavior",
source: `
stage("Persistence audit");
const result = await assign("review", "Inspect persistence behavior");
return result.structured;
`,
args: { scope: "store" },
activeToolNames: ["read", "grep", "bash"],
idempotencyKey: "analysis-script-key",
};
const completed = await engine.execute(command);
assert.equal(completed.status, "completed");
assert.equal(completed.featureName, undefined);
assert.equal(completed.request, "Audit persistence behavior");
assert.equal(setup.workspace.prepareCalls, 0);
const persisted = await setup.store.loadRun(completed.runId);
assert.equal(persisted.mode, "analysis");
assert.equal(persisted.script?.status, "completed");
assert.deepEqual(persisted.script?.stages, ["Persistence audit"]);
assert.equal(persisted.assignments.length, 1);
assert.equal(persisted.assignments[0]?.mutates, false);
assert.deepEqual(setup.executor.requests[0]?.allowedReadTools, ["read", "grep"]);
assert.deepEqual(await engine.execute(command), completed);
assert.equal(setup.executor.requests.length, 1);
});
});
test("script Recovery reuses completed request IDs without orphaning interrupted work", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const engine = setup.engine("session-1", {
scriptRuntime: { run: runScriptSandbox },
});
const completed = await engine.execute({
action: "start-analysis",
request: "Audit recovery",
source: `return await assign("review", "First request");`,
activeToolNames: ["read"],
idempotencyKey: "analysis-recovery-start",
});
const interrupted = await setup.store.loadRun(completed.runId);
assert.ok(interrupted.script);
interrupted.script.source = `
const first = await assign("review", "First request");
const second = await assign("review", "Second request");
return [first, second];
`;
interrupted.script.status = "running";
interrupted.script.executed = false;
interrupted.status = "waiting";
const firstAssignment = interrupted.assignments[0]!;
interrupted.assignments.push({
...structuredClone(firstAssignment),
id: "interrupted-script-request",
status: "interrupted",
result: undefined,
scriptRequestId: 2,
scriptRequestFingerprint: "interrupted",
});
const recoveryGateId = "script-recovery-gate";
interrupted.gates.push({
id: recoveryGateId,
kind: "recovery",
status: "open",
revision: interrupted.revision + 1,
openedAt: 10_000,
payload: { interruptedAssignmentIds: ["interrupted-script-request"] },
});
interrupted.revision++;
await setup.store.saveRun(interrupted, interrupted.revision - 1);
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const recovered = await engine.execute({
action: "decide",
runId: interrupted.runId,
gateId: recoveryGateId,
expectedRevision: interrupted.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "retry-analysis-script",
});
assert.equal(recovered.status, "completed", recovered.error?.message);
assert.equal(setup.executor.requests.length, 2);
const persisted = await setup.store.loadRun(recovered.runId);
assert.equal(
persisted.assignments.find(
(assignment) => assignment.id === "interrupted-script-request",
)?.status,
"interrupted",
);
assert.equal(
persisted.assignments.filter(
(assignment) => assignment.scriptRequestId === 1,
).length,
1,
);
});
});
test("script abort waits for active Assignments and fails closed on timeout", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const blocking = new BlockingExecutor({
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const engine = setup.engine("session-1", {
executor: blocking,
scriptRuntime: { run: runScriptSandbox },
assignmentAbortTimeoutMs: 10,
});
const start = engine.execute({
action: "start-analysis",
request: "Audit cancellation",
source: `return await assign("review", "Block until released");`,
activeToolNames: ["read"],
idempotencyKey: "blocking-analysis",
});
await blocking.started;
const [running] = await setup.store.listRuns();
assert.ok(running);
await assert.rejects(
engine.execute({
action: "abort",
runId: running.runId,
expectedRevision: running.revision,
idempotencyKey: "abort-blocking-analysis",
}),
/Timed out stopping active script/,
);
assert.notEqual((await setup.store.loadRun(running.runId)).status, "aborted");
blocking.release();
const failed = await start;
assert.equal(failed.status, "failed");
assert.match(failed.error?.message ?? "", /aborted/);
});
});
test("feature scripts are insertion-bound and cannot bypass Plan Approval", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine("session-1", {
scriptRuntime: { run: runScriptSandbox },
}).execute(
startCommand({
script: {
source: `
stage("Pre-design audit");
return await assign("review", "Inspect design precedents");
`,
insertionPoint: "before-design",
activeToolNames: ["read", "grep", "find", "ls", "bash"],
},
}),
);
assert.equal(planned.status, "waiting", planned.error?.message);
assert.equal(planned.openGate?.kind, "plan-approval");
assert.deepEqual(
setup.executor.requests.map((request) => request.role),
["discover", "review", "design"],
);
const persisted = await setup.store.loadRun(planned.runId);
assert.equal(persisted.script?.executed, true);
assert.equal(
persisted.assignments
.filter((assignment) =>
assignment.selection.rationale.startsWith("Custom read-only script"),
)
.some((assignment) => assignment.mutates),
false,
);
});
});
test("after-design feature scripts still leave Plan Approval mandatory", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const planned = await setup.engine("session-1", {
scriptRuntime: { run: runScriptSandbox },
}).execute(
startCommand({
script: {
source: `return await assign("review", "Audit the completed design");`,
insertionPoint: "after-design",
activeToolNames: ["read", "grep", "find", "ls"],
},
}),
);
assert.equal(planned.status, "waiting");
assert.equal(planned.openGate?.kind, "plan-approval");
assert.deepEqual(
setup.executor.requests.map((request) => request.role),
["discover", "design", "review"],
);
});
});
test("before-review feature scripts cannot replace mandatory verification or review", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
setup.executor.enqueue("implement", {
summary: "Implemented feature",
changedPaths: ["src/index.ts"],
focusedTests: ["feature behavior"],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Verified feature",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const engine = setup.engine("session-1", {
scriptRuntime: { run: runScriptSandbox },
});
const planned = await engine.execute(
startCommand({
script: {
source: `return await assign("review", "Perform pre-review analysis");`,
insertionPoint: "before-review",
activeToolNames: ["read", "grep", "find", "ls"],
},
}),
);
assert.equal(
(await setup.store.loadRun(planned.runId)).script?.executed,
false,
);
const approved = await engine.execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-before-review-script",
});
const completed = await engine.execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "context-before-review-script",
});
assert.equal(completed.status, "completed");
const roles = setup.executor.requests.map((request) => request.role);
assert.deepEqual(roles, [
"discover",
"design",
"implement",
"verify",
"review",
"review",
]);
assert.equal(
(await setup.store.loadRun(completed.runId)).script?.executed,
true,
);
});
});
test("feature scripts reject mutating roles and invalid insertion points", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
const failed = await setup.engine("session-1", {
scriptRuntime: { run: runScriptSandbox },
}).execute(
startCommand({
script: {
source: `return await assign("implement", "Mutate before approval");`,
insertionPoint: "before-design",
activeToolNames: ["read"],
},
}),
);
assert.equal(failed.status, "failed");
assert.match(failed.error?.message ?? "", /not read-only or supported/);
await assert.rejects(
setup.engine().execute({
...startCommand({ idempotencyKey: "invalid-insertion" }),
script: {
source: "return null",
insertionPoint: "during-implementation",
activeToolNames: ["read"],
},
} as unknown as WorkflowCommand),
/insertionPoint is invalid/,
);
assert.equal(setup.workspace.prepareCalls, 1);
});
});
test("background feature starts return after durable publication and continue to a Gate", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const blocking = new BlockingExecutor(discoveryWithQuestions());
const persistedDeliveries: string[] = [];
const engine = setup.engine("session-1", {
executor: blocking,
afterRunPersist(run) {
persistedDeliveries.push(
...run.deliveries
.filter((delivery) => delivery.state === "pending")
.map((delivery) => delivery.key),
);
},
});
const command = startCommand({
background: true,
idempotencyKey: "background-start",
});
const published = await engine.execute(command);
assert.equal(published.background, true);
assert.equal(published.workspace.state, "creating");
await blocking.started;
blocking.release();
let completed = published;
for (let attempt = 0; attempt < 20; attempt++) {
completed = await engine.execute({ action: "inspect", runId: published.runId });
if (completed.openGate?.kind === "questions") break;
await new Promise((resolve) => setTimeout(resolve, 1));
}
assert.equal(completed.openGate?.kind, "questions");
assert.equal(completed.status, "waiting");
for (let attempt = 0; attempt < 20; attempt++) {
if ((await setup.store.getStart(command.idempotencyKey))?.status === "completed") {
break;
}
await new Promise((resolve) => setTimeout(resolve, 1));
}
assert.equal(
(await setup.store.getStart(command.idempotencyKey))?.status,
"completed",
);
assert.equal(
persistedDeliveries.some((key) => key.startsWith("gate:")),
true,
);
assert.deepEqual(await engine.execute(command), completed);
await new Promise((resolve) => setTimeout(resolve, 20));
});
});
test("run concurrency may lower but never raise the configured ceiling", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(
startCommand({ concurrency: 2, idempotencyKey: "concurrency-two" }),
);
assert.equal(
(await setup.store.loadRun(started.runId)).effectiveConfig.concurrency.default,
2,
);
assert.equal(setup.executor.requests[0]?.runConcurrency, 2);
await assert.rejects(
setup.engine().execute(
startCommand({
concurrency: DEFAULT_WORKFLOW_CONFIG.concurrency.ceiling + 1,
idempotencyKey: "concurrency-too-high",
featureName: "other-feature",
}),
),
/cannot exceed ceiling/,
);
});
});
test("Questions and Plan Approval persist and resume across engine instances", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
setup.executor.enqueue("design", design("minor"));
setup.workspace.beforeCreate = async () => {
const [persisted] = await setup.store.listRuns();
assert.equal(persisted?.workspace.state, "creating");
assert.equal(persisted?.lease?.activeParentSessionId, "session-1");
assert.equal(persisted?.assignments.length, 0);
};
const started = await setup.engine().execute(startCommand());
assert.equal(started.status, "waiting");
assert.equal(started.openGate?.kind, "questions");
assert.equal(setup.workspace.createCalls, 1);
const answered = await setup.engine().execute({
action: "decide",
runId: started.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: started.revision,
decision: {
kind: "answer-questions",
answers: { "data-source": "Repository API" },
},
idempotencyKey: "answer-key",
});
assert.equal(answered.status, "waiting");
assert.equal(answered.openGate?.kind, "plan-approval");
assert.equal(answered.uiImpact, "minor");
assert.match(
await readFile(answered.planArtifact?.path ?? "missing", "utf8"),
/Implement the dashboard/,
);
const designRequest = setup.executor.requests.find(
(request) => request.role === "design",
);
assert.equal(designRequest?.decisions[0]?.value.kind, "answer-questions");
const inspected = await setup.engine().execute({
action: "inspect",
runId: answered.runId,
});
assert.deepEqual(inspected, answered);
const approved = await setup.engine().execute({
action: "decide",
runId: answered.runId,
gateId: answered.openGate?.id ?? "missing",
expectedRevision: answered.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-key",
});
assert.equal(approved.status, "waiting");
assert.equal(approved.openGate?.kind, "implementation-context");
assert.equal(approved.implementationContext?.status, "waiting");
assert.equal(
approved.stages.find((stage) => stage.kind === "design")?.status,
"completed",
);
const approvedPlan = await readFile(
approved.planArtifact?.path ?? "missing",
"utf8",
);
assert.match(approvedPlan, /Accepted Decisions/);
assert.match(approvedPlan, /Worktree/);
assert.match(approvedPlan, /UI Impact Evidence/);
assert.match(approvedPlan, /acceptedUiImpact/);
const persisted = await setup.store.loadRun(approved.runId);
assert.equal(persisted.decisions.length, 2);
assert.equal(persisted.deliveries.length, 3);
assert.deepEqual(
persisted.preflightEligibility.map((entry) => entry.role),
["discover", "design", "implement", "verify", "review"],
);
assert.ok(
persisted.preflightEligibility.every((entry) =>
entry.candidates.some((candidate) => candidate.eligible),
),
);
});
});
test("Implementation Context choices are persisted and Gate-bound", async () => {
for (const strategy of ["fresh-session", "current-session"] as const) {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
if (strategy === "current-session") enqueueSuccessfulPipeline(setup);
const planned = await setup.engine().execute(startCommand());
const contextGate = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: `approve-${strategy}`,
});
const selected = await setup.engine().execute({
action: "decide",
runId: contextGate.runId,
gateId: contextGate.openGate?.id ?? "missing",
expectedRevision: contextGate.revision,
decision: { kind: "select-implementation-context", strategy },
idempotencyKey: `context-${strategy}`,
});
if (strategy === "fresh-session") {
assert.equal(selected.status, "waiting");
assert.equal(selected.openGate?.kind, "implementation-context");
assert.equal(selected.implementationContext?.status, "handoff-pending");
} else {
assert.equal(selected.status, "completed");
assert.equal(selected.openGate, undefined);
assert.equal(selected.implementationContext?.status, "ready");
assert.equal(selected.implementationContext?.method, "unchanged");
}
});
}
});
test("Implementation Context rejects transitions while a child Assignment is active", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-active-context",
});
const persisted = await setup.store.loadRun(approved.runId);
const queued = persisted.assignments.find(
(assignment) => assignment.role === "implement",
);
assert.ok(queued);
queued.status = "running";
queued.executionToken = "active-context-token";
persisted.revision++;
await setup.store.saveRun(persisted, persisted.revision - 1);
await assert.rejects(
setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: persisted.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "select-active-context",
}),
/while an Assignment is active/,
);
});
});
test("fresh-session handoff is revision-bound and rotates the active parent only on consume", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
enqueueSuccessfulPipeline(setup);
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-handoff",
});
const selected = await setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "fresh-session",
},
idempotencyKey: "select-handoff",
});
const intent = selected.implementationContext;
assert.ok(intent);
const continued = await setup.engine().execute({
action: "continue-context",
runId: selected.runId,
gateId: intent.gateId,
preparationId: intent.preparationId,
planRevision: intent.planRevision,
contextRevision: intent.runRevision,
expectedRevision: selected.revision,
sourceSessionFile: "/sessions/source.jsonl",
});
const pending = continued.implementationContext;
assert.ok(pending);
const claimed = await setup.engine().execute({
action: "claim-context-handoff",
runId: continued.runId,
gateId: pending.gateId,
preparationId: pending.preparationId,
planRevision: pending.planRevision,
contextRevision: pending.runRevision,
expectedRevision: continued.revision,
sourceSessionFile: "/sessions/source.jsonl",
targetSessionFile: "/sessions/target.jsonl",
reason: "new",
});
assert.equal(
claimed.implementationContext?.status,
"claimed-target-awaiting-withSession",
);
const claim = claimed.implementationContext;
assert.ok(claim);
await assert.rejects(
setup.engine("unrelated-session", {
processId: 200,
isProcessAlive: (processId) => processId === 100,
}).execute({
action: "recover-context-intent",
runId: claimed.runId,
gateId: claim.gateId,
preparationId: claim.preparationId,
planRevision: claim.planRevision,
contextRevision: claim.runRevision,
expectedRevision: claimed.revision,
reason: "startup",
}),
/owned by live process 100/,
);
assert.equal(
(await setup.store.loadRun(claimed.runId)).implementationContext?.status,
"claimed-target-awaiting-withSession",
);
const consumed = await setup.engine("session-2").execute({
action: "consume-context-handoff",
runId: claimed.runId,
gateId: claim.gateId,
preparationId: claim.preparationId,
planRevision: claim.planRevision,
contextRevision: claim.runRevision,
expectedRevision: claimed.revision,
targetSessionFile: "/sessions/target.jsonl",
previousSessionFile: "/sessions/source.jsonl",
});
assert.equal(consumed.implementationContext?.status, "ready");
assert.equal(consumed.implementationContext?.activeParentSessionId, "session-2");
assert.equal(consumed.openGate, undefined);
await assert.rejects(
setup.engine("session-1").execute({
action: "abort",
runId: consumed.runId,
expectedRevision: consumed.revision,
idempotencyKey: "historical-abort",
}),
/Historical parent sessions/,
);
});
});
test("expired context recovery rejects another live process without mutation", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const claimed = await createClaimedContextHandoff(setup);
const claim = claimed.implementationContext;
assert.ok(claim);
const before = await setup.store.loadRun(claimed.runId);
await assert.rejects(
setup.engine("unrelated-session", {
now: () => 310_001,
processId: 200,
isProcessAlive: (processId) => processId === 100,
}).execute({
action: "recover-context-intent",
runId: claimed.runId,
gateId: claim.gateId,
preparationId: claim.preparationId,
planRevision: claim.planRevision,
contextRevision: claim.runRevision,
expectedRevision: claimed.revision,
reason: "expired",
}),
/owned by live process 100/,
);
assert.deepEqual(await setup.store.loadRun(claimed.runId), before);
});
});
test("expired context recovery succeeds when the owning process is dead", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const claimed = await createClaimedContextHandoff(setup);
const claim = claimed.implementationContext;
assert.ok(claim);
const recovered = await setup.engine("replacement-session", {
now: () => 310_001,
processId: 200,
isProcessAlive: () => false,
}).execute({
action: "recover-context-intent",
runId: claimed.runId,
gateId: claim.gateId,
preparationId: claim.preparationId,
planRevision: claim.planRevision,
contextRevision: claim.runRevision,
expectedRevision: claimed.revision,
reason: "expired",
});
assert.equal(recovered.revision, claimed.revision + 1);
assert.equal(recovered.status, "waiting");
assert.equal(recovered.implementationContext?.status, "handoff-pending");
assert.equal(recovered.implementationContext?.sourceSessionFile, undefined);
assert.equal(recovered.implementationContext?.targetSessionFile, undefined);
assert.match(recovered.implementationContext?.error ?? "", /during expired/);
});
});
test("expired context recovery succeeds for the owning process", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const claimed = await createClaimedContextHandoff(setup);
const claim = claimed.implementationContext;
assert.ok(claim);
const recovered = await setup.engine("session-1", {
now: () => 310_001,
processId: 100,
isProcessAlive: (processId) => processId === 100,
}).execute({
action: "recover-context-intent",
runId: claimed.runId,
gateId: claim.gateId,
preparationId: claim.preparationId,
planRevision: claim.planRevision,
contextRevision: claim.runRevision,
expectedRevision: claimed.revision,
reason: "expired",
});
assert.equal(recovered.revision, claimed.revision + 1);
assert.equal(recovered.status, "waiting");
assert.equal(recovered.implementationContext?.status, "handoff-pending");
assert.equal(recovered.implementationContext?.sourceSessionFile, undefined);
assert.equal(recovered.implementationContext?.targetSessionFile, undefined);
assert.match(recovered.implementationContext?.error ?? "", /during expired/);
});
});
test("smart compaction reports are fenced and unavailable alone enables fallback", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-smart",
});
const selected = await setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "smart-compact",
},
idempotencyKey: "select-smart",
});
const preparation = selected.implementationContext;
assert.equal(preparation?.status, "smart-compact-pending");
const unavailable = {
action: "decide" as const,
runId: selected.runId,
gateId: selected.openGate?.id ?? "missing",
expectedRevision: selected.revision,
decision: {
kind: "report-context-preparation" as const,
preparationId: preparation?.preparationId ?? "missing",
planRevision: preparation?.planRevision ?? -1,
method: "smart_compact" as const,
outcome: "unavailable" as const,
},
idempotencyKey: "smart-unavailable",
};
await assert.rejects(setup.engine().execute(unavailable), /activeToolNames/);
await assert.rejects(
setup.engine().execute({
...unavailable,
activeToolNames: ["smart_compact"],
}),
/smart_compact is active/,
);
const fallback = await setup.engine().execute({
...unavailable,
activeToolNames: ["read", "grep"],
});
assert.equal(fallback.status, "waiting");
assert.equal(fallback.openGate?.kind, "implementation-context");
assert.equal(fallback.implementationContext?.status, "normal-compact-pending");
assert.equal(fallback.implementationContext?.outcome, "unavailable");
const fallbackIntent = fallback.implementationContext;
assert.ok(fallbackIntent);
const compacting = await setup.engine().execute({
action: "continue-context",
runId: fallback.runId,
gateId: fallbackIntent.gateId,
preparationId: fallbackIntent.preparationId,
planRevision: fallbackIntent.planRevision,
contextRevision: fallbackIntent.runRevision,
expectedRevision: fallback.revision,
});
assert.equal(compacting.implementationContext?.status, "normal-compact-running");
const compactIntent = compacting.implementationContext;
assert.ok(compactIntent);
const failed = await setup.engine().execute({
action: "complete-normal-compaction",
runId: compacting.runId,
gateId: compactIntent.gateId,
preparationId: compactIntent.preparationId,
planRevision: compactIntent.planRevision,
contextRevision: compactIntent.runRevision,
expectedRevision: compacting.revision,
outcome: "failed",
error: "Native compaction failed",
});
assert.equal(failed.status, "waiting");
assert.equal(failed.openGate?.kind, "implementation-context");
assert.equal(failed.implementationContext?.status, "failed");
assert.equal(failed.implementationContext?.error, "Native compaction failed");
});
});
test("recovery marks an interrupted normal compaction intent failed", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-interrupted-compact",
});
const selected = await setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "smart-compact",
},
idempotencyKey: "select-interrupted-compact",
});
const intent = selected.implementationContext;
assert.ok(intent);
const fallback = await setup.engine().execute({
action: "decide",
runId: selected.runId,
gateId: intent.gateId,
expectedRevision: selected.revision,
decision: {
kind: "report-context-preparation",
preparationId: intent.preparationId,
planRevision: intent.planRevision,
method: "smart_compact",
outcome: "unavailable",
},
idempotencyKey: "unavailable-interrupted-compact",
activeToolNames: ["read"],
});
const fallbackIntent = fallback.implementationContext;
assert.ok(fallbackIntent);
await setup.engine().execute({
action: "continue-context",
runId: fallback.runId,
gateId: fallbackIntent.gateId,
preparationId: fallbackIntent.preparationId,
planRevision: fallbackIntent.planRevision,
contextRevision: fallbackIntent.runRevision,
expectedRevision: fallback.revision,
});
const recovered = await setup.engine("session-1", {
now: () => 100_000,
processId: 200,
isProcessAlive: () => false,
}).recover();
const view = recovered.find((candidate) => candidate.runId === fallback.runId);
assert.equal(view?.implementationContext?.status, "failed");
assert.match(view?.implementationContext?.error ?? "", /interrupted/);
assert.equal(view?.openGate?.kind, "implementation-context");
});
});
test("smart compaction success, no-op, and failure outcomes remain distinct", async () => {
for (const outcome of ["succeeded", "no-op", "failed"] as const) {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
if (outcome !== "failed") enqueueSuccessfulPipeline(setup);
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: `approve-smart-${outcome}`,
});
const selected = await setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "smart-compact",
},
idempotencyKey: `select-smart-${outcome}`,
});
const intent = selected.implementationContext;
assert.ok(intent);
const decision: GateDecision =
outcome === "failed"
? {
kind: "report-context-preparation",
preparationId: intent.preparationId,
planRevision: intent.planRevision,
method: "smart_compact",
outcome,
error: "smart_compact failed to prepare context",
}
: {
kind: "report-context-preparation",
preparationId: intent.preparationId,
planRevision: intent.planRevision,
method: "smart_compact",
outcome,
};
const report = await setup.engine().execute({
action: "decide",
runId: selected.runId,
gateId: intent.gateId,
expectedRevision: selected.revision,
decision,
idempotencyKey: `report-smart-${outcome}`,
activeToolNames: ["smart_compact"],
});
assert.equal(report.implementationContext?.outcome, outcome);
assert.equal(report.implementationContext?.method, "smart_compact");
if (outcome === "failed") {
assert.equal(report.status, "waiting");
assert.equal(report.openGate?.kind, "implementation-context");
assert.match(report.implementationContext?.error ?? "", /failed/);
} else {
assert.equal(report.status, "completed");
assert.equal(report.openGate, undefined);
assert.equal(report.implementationContext?.status, "ready");
}
});
}
});
test("implementation completes only after host verification and two-axis review", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", {
...design("minor"),
acceptanceCriteria: ["Search behavior works"],
verificationCommands: [
{
executable: "npm",
args: ["test", "--", "feature"],
ownedPaths: ["src"],
kind: "test",
},
],
});
enqueueSuccessfulPipeline(setup);
const hostCalls: string[] = [];
const hostCommands: HostCommandExecutor = {
async run(options) {
hostCalls.push(`${options.executable} ${options.args.join(" ")}`);
return { result: { code: 0, stdout: "passed", stderr: "" } };
},
};
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-lifecycle",
});
const completed = await setup.engine("session-1", { hostCommands }).execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "context-lifecycle",
});
assert.equal(completed.status, "completed");
assert.deepEqual(hostCalls, ["npm test -- feature"]);
const persisted = await setup.store.loadRun(completed.runId);
assert.equal(persisted.verificationEvidence?.commands[0]?.status, "succeeded");
assert.equal(persisted.verificationEvidence?.focusedBehavioralTests, true);
assert.equal(persisted.reviewVerdict?.specification.verdict, "approved");
assert.equal(persisted.reviewVerdict?.standards.verdict, "approved");
assert.deepEqual(persisted.mutatorProfileNames, ["feature-implementer"]);
assert.ok(persisted.reportArtifact);
assert.match(
await readFile(persisted.reportArtifact.path, "utf8"),
/Status: completed[\s\S]*Verification[\s\S]*Review/,
);
assert.equal(
persisted.deliveries.some((delivery) =>
delivery.key.startsWith(`terminal:${persisted.runId}:completed:`),
),
true,
);
const result = JSON.parse(
await readFile(
path.join(path.dirname(path.dirname(persisted.reportArtifact.path)), "result.json"),
"utf8",
),
) as { status?: string; revision?: number; snapshotRevision?: number };
assert.equal(result.status, "completed");
assert.equal(result.revision, persisted.reportArtifact.revision);
assert.equal(result.snapshotRevision, persisted.revision - 1);
const verifierRequest = setup.executor.requests.find(
(request) => request.role === "verify",
);
const reviewerRequest = setup.executor.requests.find(
(request) => request.role === "review",
);
assert.match(verifierRequest?.approvedPlan ?? "", /Acceptance Criteria/);
assert.equal(
verifierRequest?.verificationEvidence?.commands[0]?.status,
"succeeded",
);
assert.match(reviewerRequest?.reviewDiff ?? "", /diff --git/);
assert.equal(
reviewerRequest?.verificationEvidence?.focusedBehavioralTests,
true,
);
});
});
test("recovery consumes a persisted successful mutator without replay", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-before-crash",
});
const crashed = await setup.store.loadRun(approved.runId);
for (const gate of crashed.gates) gate.status = "satisfied";
assert.ok(crashed.implementationContext);
crashed.implementationContext.status = "ready";
crashed.implementationContext.strategy = "current-session";
crashed.implementationContext.method = "unchanged";
crashed.status = "running";
const implement = crashed.assignments.find(
(assignment) => assignment.role === "implement",
);
assert.ok(implement);
implement.status = "succeeded";
implement.result = {
summary: "Mutation was durably completed before the crash",
changedPaths: ["src/index.ts"],
focusedTests: ["feature behavior"],
};
implement.executionToken = undefined;
const implementStage = crashed.stages.find(
(candidate) => candidate.kind === "implement",
);
assert.ok(implementStage);
implementStage.status = "running";
crashed.lease = {
processId: 999,
activeParentSessionId: "session-1",
heartbeatAt: 0,
worktreeIdentity: crashed.workspace.root,
};
crashed.revision++;
crashed.implementationContext.runRevision = crashed.revision;
await setup.store.saveRun(crashed, crashed.revision - 1);
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Recovered implementation verified",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const [recovered] = await setup.engine("session-1", {
processId: 200,
now: () => 100_000,
isProcessAlive: () => false,
}).recover();
assert.equal(recovered?.status, "completed");
assert.equal(
setup.executor.requests.filter((request) => request.role === "implement")
.length,
0,
);
assert.equal(
(await setup.store.loadRun(crashed.runId)).assignments.find(
(assignment) => assignment.id === implement.id,
)?.status,
"succeeded",
);
});
});
test("Git or ownership safety failures open Recovery before remediation", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
setup.executor.enqueue("implement", {
summary: "Implementation",
changedPaths: ["src/index.ts"],
focusedTests: ["behavior"],
});
const hostCommands: HostCommandExecutor = {
async run() {
throw new WorkspaceSafetyError("HEAD changed during verification");
},
};
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-safety-recovery",
});
const waiting = await setup.engine("session-1", { hostCommands }).execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "context-safety-recovery",
});
assert.equal(waiting.status, "waiting");
assert.equal(waiting.openGate?.kind, "recovery");
assert.equal(
waiting.assignments.some((assignment) => assignment.role === "remediate"),
false,
);
});
});
test("Recovery retry reruns failed verification after the remediation limit", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
let commandRuns = 0;
let runId: string | undefined;
const hostCommands: HostCommandExecutor = {
async run() {
commandRuns++;
if (commandRuns === 2) {
assert.ok(runId);
const retrying = await setup.store.loadRun(runId);
const [command] = retrying.verificationEvidence?.commands ?? [];
assert.equal(command?.status, "running");
assert.equal(command?.stdout, undefined);
assert.equal(command?.stderr, undefined);
assert.equal(command?.exitCode, undefined);
assert.equal(command?.error, undefined);
}
return {
result: {
code: commandRuns === 1 ? 1 : 0,
stdout: commandRuns === 1 ? "failed" : "passed",
stderr: "",
},
};
},
};
const effectiveConfig = {
...structuredClone(DEFAULT_WORKFLOW_CONFIG),
maxReviewCycles: 1,
};
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
setup.executor.enqueue("implement", {
summary: "Initial implementation",
changedPaths: ["src/index.ts"],
focusedTests: ["initial test"],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Corrected implementation verified",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const planned = await setup.engine("session-1", {
hostCommands,
effectiveConfig,
}).execute(startCommand());
runId = planned.runId;
const approved = await setup.engine("session-1", {
hostCommands,
effectiveConfig,
}).execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-failed-verification-retry",
});
const waiting = await setup.engine("session-1", {
hostCommands,
effectiveConfig,
}).execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "context-failed-verification-retry",
});
assert.equal(waiting.openGate?.kind, "recovery");
assert.equal(commandRuns, 1);
const transientRecovery = setup.engine("transient-session", {
hostCommands,
effectiveConfig,
processId: 200,
isProcessAlive: (processId) => processId === 100,
});
assert.deepEqual(await transientRecovery.recover(), []);
const untouched = await setup.store.loadRun(waiting.runId);
assert.equal(untouched.revision, waiting.revision);
assert.equal(untouched.lease?.activeParentSessionId, "session-1");
const liveStolen = structuredClone(untouched);
liveStolen.revision++;
liveStolen.parentSessionIds.push("transient-session");
liveStolen.verificationEvidence!.commands[0]!.error = "stale host exception";
const interruptedImplement = liveStolen.assignments.find(
(assignment) => assignment.role === "implement",
);
assert.ok(interruptedImplement);
interruptedImplement.status = "interrupted";
liveStolen.lease = {
processId: 200,
activeParentSessionId: "transient-session",
heartbeatAt: 0,
worktreeIdentity: liveStolen.workspace.root,
};
await setup.store.saveRun(liveStolen, untouched.revision);
await assert.rejects(
setup.engine("session-1", {
hostCommands,
effectiveConfig,
processId: 300,
isProcessAlive: (processId) => processId === 200,
}).execute({
action: "decide",
runId: waiting.runId,
gateId: waiting.openGate?.id ?? "missing",
expectedRevision: liveStolen.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "live-owner-retry-failed-verification",
}),
/Historical parent sessions/,
);
const staleStolen = structuredClone(liveStolen);
staleStolen.revision++;
staleStolen.lease!.processId = 201;
await setup.store.saveRun(staleStolen, liveStolen.revision);
const completed = await setup.engine("session-1", {
hostCommands,
effectiveConfig,
processId: 300,
isProcessAlive: () => false,
}).execute({
action: "decide",
runId: waiting.runId,
gateId: waiting.openGate?.id ?? "missing",
expectedRevision: staleStolen.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "retry-failed-verification",
});
assert.equal(completed.status, "completed");
assert.equal(commandRuns, 2);
const persisted = await setup.store.loadRun(completed.runId);
assert.equal(persisted.verificationEvidence?.commands[0]?.status, "succeeded");
});
});
test("changes requested trigger serialized remediation, verification, and fresh review", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
setup.executor.enqueue("implement", {
summary: "Initial implementation",
changedPaths: ["src/index.ts"],
focusedTests: ["initial test"],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Initial verification",
});
setup.executor.enqueue("review", {
specification: {
verdict: "changes_requested",
findings: [{ severity: "concern", message: "Handle the edge case" }],
},
standards: { verdict: "approved", findings: [] },
});
setup.executor.enqueue("remediate", {
summary: "Handled the edge case",
changedPaths: ["src/index.ts"],
focusedTests: ["edge case test"],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Remediation verified",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const planned = await setup.engine().execute(startCommand());
const approved = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "approve-remediation",
});
const completed = await setup.engine().execute({
action: "decide",
runId: approved.runId,
gateId: approved.openGate?.id ?? "missing",
expectedRevision: approved.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "context-remediation",
});
assert.equal(completed.status, "completed");
const persisted = await setup.store.loadRun(completed.runId);
assert.equal(
persisted.assignments.filter((assignment) => assignment.role === "remediate")
.length,
1,
);
assert.equal(
persisted.assignments.filter(
(assignment) => assignment.role === "review" && assignment.status === "succeeded",
).length,
2,
);
assert.equal(persisted.reviewCycles, 2);
assert.equal(persisted.verificationCycles, 0);
const reviewerNames = persisted.assignments
.filter((assignment) => assignment.role === "review")
.map((assignment) => assignment.profileName);
assert.ok(reviewerNames.every((name) => !persisted.mutatorProfileNames.includes(name)));
});
});
test("non-trivial UI requires three prototypes, acceptance, and cleanup", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
questions: [],
candidatePaths: ["src/routes/layout.svelte"],
languages: ["svelte"],
tags: ["frontend", "responsive"],
riskTags: ["accessibility"],
});
setup.executor.enqueue("design", design("non-trivial"));
for (let index = 1; index <= 3; index++) {
setup.executor.enqueue("prototype", {
path: `prototypes/candidate-${index}`,
route: `/prototype?variant=${index}`,
structuralRationale: `Distinct structure ${index}`,
responsiveChecks: ["mobile 390px", "desktop 1440px"],
browserEvidence: [`browser screenshot ${index}`],
launchInstructions: `npm run dev then open variant ${index}`,
});
}
const planned = await setup.engine().execute(startCommand());
const prototypes = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "non-trivial",
},
idempotencyKey: "approve-prototype-plan",
});
assert.equal(prototypes.openGate?.kind, "prototype-acceptance");
const persistedCandidates = (
await setup.store.loadRun(prototypes.runId)
).prototypeCandidates;
assert.equal(persistedCandidates.length, 3);
assert.equal(
new Set(persistedCandidates.map((candidate) => candidate.path)).size,
3,
);
assert.ok(
persistedCandidates.every((candidate) =>
candidate.artifactPath.includes("artifacts/prototypes"),
),
);
await assert.rejects(
setup.engine().execute({
action: "decide",
runId: prototypes.runId,
gateId: prototypes.openGate?.id ?? "missing",
expectedRevision: prototypes.revision,
decision: {
kind: "accept-prototype",
candidateId: persistedCandidates[0]?.id ?? "missing",
candidateRevision: (persistedCandidates[0]?.revision ?? 0) + 1,
browserReviewed: true,
},
idempotencyKey: "stale-prototype",
}),
/candidate or revision is stale/,
);
const acceptedCandidate = persistedCandidates[1]!;
const accepted = await setup.engine().execute({
action: "decide",
runId: prototypes.runId,
gateId: prototypes.openGate?.id ?? "missing",
expectedRevision: prototypes.revision,
decision: {
kind: "accept-prototype",
candidateId: acceptedCandidate.id,
candidateRevision: acceptedCandidate.revision,
browserReviewed: true,
feedback: "Use the second navigation structure",
},
idempotencyKey: "accept-prototype",
});
assert.equal(accepted.openGate?.kind, "implementation-context");
assert.match(
await readFile(accepted.planArtifact?.path ?? "missing", "utf8"),
/Accepted Prototype[\s\S]*second navigation structure/,
);
setup.executor.enqueue("implement", {
summary: "Implemented accepted direction to production standards",
changedPaths: ["src/routes/layout.svelte"],
focusedTests: ["responsive navigation"],
});
setup.executor.enqueue("remediate", {
summary: "Removed prototype-only candidates and routes",
changedPaths: ["prototypes"],
focusedTests: [],
});
setup.executor.enqueue("verify", {
succeeded: true,
focusedBehavioralTests: true,
summary: "Responsive browser behavior verified",
});
setup.executor.enqueue("review", {
specification: { verdict: "approved", findings: [] },
standards: { verdict: "approved", findings: [] },
});
const completed = await setup.engine().execute({
action: "decide",
runId: accepted.runId,
gateId: accepted.openGate?.id ?? "missing",
expectedRevision: accepted.revision,
decision: {
kind: "select-implementation-context",
strategy: "current-session",
},
idempotencyKey: "prototype-context",
});
assert.equal(completed.status, "completed");
assert.equal(
(await setup.store.loadRun(completed.runId)).prototypeCleanupCompleted,
true,
);
});
});
test("prototype refinement produces a fresh three-candidate revision", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
questions: [],
candidatePaths: ["src/routes/layout.svelte"],
languages: ["svelte"],
tags: ["frontend", "responsive"],
riskTags: [],
});
setup.executor.enqueue("design", design("non-trivial"));
for (let index = 1; index <= 3; index++) {
setup.executor.enqueue("prototype", {
path: `prototypes/candidate-${index}`,
route: `/first?variant=${index}`,
structuralRationale: `Initial structure ${index}`,
responsiveChecks: ["mobile and desktop"],
browserEvidence: [`initial browser evidence ${index}`],
launchInstructions: "npm run dev",
});
}
const planned = await setup.engine().execute(startCommand());
assert.equal(planned.status, "waiting", planned.error?.message);
assert.equal(planned.openGate?.kind, "plan-approval");
const firstGate = await setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "non-trivial",
},
idempotencyKey: "approve-refinement-plan",
});
const refinementRevision = firstGate.revision + 1;
for (let index = 1; index <= 3; index++) {
setup.executor.enqueue("prototype", {
path: `prototypes/refinement-${refinementRevision}-candidate-${index}`,
route: `/refined?variant=${index}`,
structuralRationale: `Refined structure ${index}`,
responsiveChecks: ["mobile and desktop"],
browserEvidence: [`refined browser evidence ${index}`],
launchInstructions: "npm run dev",
});
}
const refined = await setup.engine().execute({
action: "decide",
runId: firstGate.runId,
gateId: firstGate.openGate?.id ?? "missing",
expectedRevision: firstGate.revision,
decision: {
kind: "refine-prototypes",
feedback: "Increase information density",
},
idempotencyKey: "refine-prototypes",
});
assert.equal(refined.openGate?.kind, "prototype-acceptance");
const persisted = await setup.store.loadRun(refined.runId);
assert.equal(persisted.prototypeCandidates.length, 6);
assert.equal(
persisted.gates.find((gate) => gate.id === firstGate.openGate?.id)?.status,
"rejected",
);
});
});
test("objective UI evidence upgrades a planner downgrade before approval", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
questions: [],
candidatePaths: ["src/routes/layout.svelte"],
languages: ["svelte"],
tags: ["frontend"],
riskTags: ["accessibility"],
});
const downgraded = design("minor");
downgraded.assignments.push({
role: "prototype",
profileName: "ui-prototyper",
rationale: "Prototype the objective non-trivial UI change",
mutates: true,
ownedPaths: ["prototypes"],
});
setup.executor.enqueue("design", downgraded);
const planned = await setup.engine().execute(
startCommand({ request: "Redesign the responsive navigation" }),
);
assert.equal(planned.uiImpact, "non-trivial");
assert.ok(
planned.uiImpactEvidence.some((evidence) =>
evidence.includes("responsive"),
),
);
await assert.rejects(
setup.engine().execute({
action: "decide",
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "minor",
},
idempotencyKey: "reject-ui-downgrade",
}),
/Approved UI impact does not match the current plan/,
);
});
});
test("concurrent Plan Approval cannot cross-link immutable plan content", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design("minor"));
const planned = await setup.engine().execute(startCommand());
const command = (key: string, comment: string) => ({
action: "decide" as const,
runId: planned.runId,
gateId: planned.openGate?.id ?? "missing",
expectedRevision: planned.revision,
decision: {
kind: "approve-plan" as const,
approved: true as const,
acceptedUiImpact: "minor" as const,
comment,
},
idempotencyKey: key,
});
const results = await Promise.allSettled([
setup.engine().execute(command("approve-a", "approval A")),
setup.engine().execute(command("approve-b", "approval B")),
]);
assert.equal(
results.filter((result) => result.status === "fulfilled").length,
1,
);
const persisted = await setup.store.loadRun(planned.runId);
assert.ok(persisted.planArtifact);
const contents = await readFile(persisted.planArtifact.path, "utf8");
assert.equal(
createHash("sha256").update(contents).digest("hex"),
persisted.planArtifact.digest,
);
const accepted = persisted.decisions.find(
(decision) => decision.value.kind === "approve-plan",
);
assert.ok(accepted?.value.kind === "approve-plan");
assert.match(contents, new RegExp(accepted.value.comment ?? "missing"));
});
});
test("plan revision runs a fresh Design Assignment with persisted feedback", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design());
setup.executor.enqueue("design", {
...design(),
plan: "# Revised plan\n\nApply the requested changes.",
});
const initial = await setup.engine().execute(startCommand());
assert.equal(initial.openGate?.kind, "plan-approval");
const revised = await setup.engine().execute({
action: "decide",
runId: initial.runId,
gateId: initial.openGate?.id ?? "missing",
expectedRevision: initial.revision,
decision: { kind: "revise-plan", feedback: "Keep the API smaller" },
idempotencyKey: "revise-plan",
});
assert.equal(revised.openGate?.kind, "plan-approval");
assert.notEqual(revised.openGate?.id, initial.openGate?.id);
assert.equal(
revised.assignments.filter(
(assignment) =>
assignment.role === "design" && assignment.status === "succeeded",
).length,
2,
);
const latestDesignRequest = setup.executor.requests
.filter((request) => request.role === "design")
.at(-1);
assert.deepEqual(latestDesignRequest?.decisions.at(-1)?.value, {
kind: "revise-plan",
feedback: "Keep the API smaller",
});
});
});
test("recovery reuses a revised Design result persisted before gate publication", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", design());
setup.executor.enqueue("design", {
...design(),
plan: "# Revised durable plan",
});
const initial = await setup.engine().execute(startCommand());
const command = {
action: "decide" as const,
runId: initial.runId,
gateId: initial.openGate?.id ?? "missing",
expectedRevision: initial.revision,
decision: {
kind: "revise-plan" as const,
feedback: "Use the smaller design",
},
idempotencyKey: "durable-revision",
};
await assert.rejects(
setup.engine("session-1", {
afterAssignmentPersist(assignment) {
if (assignment.role === "design") {
throw new Error("crash before revised gate");
}
},
}).execute(command),
/crash before revised gate/,
);
const resumed = await setup.engine().execute(command);
assert.equal(resumed.openGate?.kind, "plan-approval");
assert.equal(
setup.executor.requests.filter((request) => request.role === "design").length,
2,
);
});
});
test("planner selections are eligibility-checked and persisted as queued Assignments", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const profileFor = (role: "implement" | "verify" | "review") => {
const profile = setup.profiles.find((candidate) => candidate.roles.includes(role));
assert.ok(profile);
return profile;
};
const implementer = profileFor("implement");
const verifier = profileFor("verify");
const reviewer = profileFor("review");
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", {
...design(),
assignments: [
{
role: "implement",
profileName: implementer.name,
rationale: "Own the implementation diff",
mutates: true,
ownedPaths: ["src"],
},
{
role: "verify",
profileName: verifier.name,
rationale: "Run focused verification",
mutates: false,
ownedPaths: [],
},
{
role: "review",
profileName: reviewer.name,
rationale: "Review independently",
mutates: false,
ownedPaths: [],
},
],
});
const planned = await setup.engine().execute(startCommand());
assert.equal(planned.openGate?.kind, "plan-approval");
const queued = planned.assignments.filter((assignment) =>
["implement", "verify", "review"].includes(assignment.role),
);
assert.deepEqual(
queued.map((assignment) => assignment.role),
["implement", "verify", "review"],
);
assert.ok(
queued.every((assignment) =>
assignment.selection.eligibleProfiles.some(
(candidate) =>
candidate.profileName === assignment.profileName && candidate.eligible,
),
),
);
assert.notEqual(
queued.find((assignment) => assignment.role === "implement")?.profileName,
queued.find((assignment) => assignment.role === "review")?.profileName,
);
});
});
test("planner output fails closed when required Assignments are missing", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", {
...design(),
assignments: [],
});
const failed = await setup.engine().execute(startCommand());
assert.equal(failed.status, "failed");
assert.match(failed.error?.message ?? "", /exactly one implement Assignment/);
});
});
test("planner output fails closed on an ineligible profile", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const ineligible = setup.profiles.find((profile) =>
profile.roles.includes("discover"),
);
const verifier = setup.profiles.find((profile) => profile.roles.includes("verify"));
const reviewer = setup.profiles.find((profile) => profile.roles.includes("review"));
assert.ok(ineligible && verifier && reviewer);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: [],
});
setup.executor.enqueue("design", {
...design(),
assignments: [
{
role: "implement",
profileName: ineligible.name,
rationale: "Invalid selection",
mutates: true,
ownedPaths: ["src"],
},
{
role: "verify",
profileName: verifier.name,
rationale: "Verify",
mutates: false,
ownedPaths: [],
},
{
role: "review",
profileName: reviewer.name,
rationale: "Review",
mutates: false,
ownedPaths: [],
},
],
});
const failed = await setup.engine().execute(startCommand());
assert.equal(failed.status, "failed");
assert.match(failed.error?.message ?? "", /ineligible implement profile/);
});
});
test("production execution metadata and transcripts are persisted", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const executor: AssignmentExecutor = {
async run() {
return {
kind: "workflow-assignment-result" as const,
output: discoveryWithQuestions(),
metadata: {
childSessionId: "child-session-1",
provider: "provider-a",
model: "model-a",
effort: "high",
contextCapacity: 128_000,
transcript: [{ role: "assistant", text: "Discovery complete" }],
},
};
},
};
const started = await setup.engine("session-1", { executor }).execute(startCommand());
const assignment = started.assignments.find(
(candidate) => candidate.role === "discover",
);
assert.equal(assignment?.childSessionId, "child-session-1");
assert.equal(assignment?.effectiveProvider, "provider-a");
assert.equal(assignment?.effectiveModel, "model-a");
assert.equal(assignment?.effectiveEffort, "high");
assert.equal(assignment?.contextCapacity, 128_000);
assert.match(
await readFile(assignment?.transcriptArtifact?.path ?? "missing", "utf8"),
/Discovery complete/,
);
});
});
test("capacity reservation precedes the persisted running transition", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const observed: WorkflowAssignment["status"][] = [];
const executor: AssignmentExecutor = {
reservesCapacity: true,
async run(request) {
const before = await setup.store.loadRun(request.runId);
observed.push(
before.assignments.find((assignment) => assignment.role === request.role)
?.status ?? "failed",
);
await request.onReserved?.();
const after = await setup.store.loadRun(request.runId);
observed.push(
after.assignments.find((assignment) => assignment.role === request.role)
?.status ?? "failed",
);
return discoveryWithQuestions();
},
};
await setup.engine("session-1", { executor }).execute(startCommand());
assert.deepEqual(observed, ["queued", "running"]);
});
});
test("failed execution metadata and transcripts are persisted", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const executor: AssignmentExecutor = {
async run() {
throw Object.assign(new Error("provider failed"), {
assignmentMetadata: {
childSessionId: "child-session-failed",
provider: "provider-a",
model: "model-a",
effort: "low",
contextCapacity: 32_000,
transcript: [{ role: "assistant", text: "partial response" }],
},
});
},
};
const failed = await setup.engine("session-1", { executor }).execute(startCommand());
const assignment = failed.assignments.find(
(candidate) => candidate.role === "discover",
);
assert.equal(failed.status, "failed");
assert.equal(assignment?.status, "failed");
assert.equal(assignment?.childSessionId, "child-session-failed");
assert.match(
await readFile(assignment?.transcriptArtifact?.path ?? "missing", "utf8"),
/partial response/,
);
});
});
test("start cancellation during slow preflight settles before rejecting without publication", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
let markPreparing = () => {};
let releasePrepare = () => {};
const preparing = new Promise<void>((resolve) => {
markPreparing = resolve;
});
const prepareBlocked = new Promise<void>((resolve) => {
releasePrepare = resolve;
});
setup.workspace.beforePrepare = async () => {
markPreparing();
await prepareBlocked;
};
const engine = setup.engine();
const operation = engine.execute(startCommand());
await preparing;
const cancellation = engine.cancelStart(
"start-key",
"cancel-slow-preflight",
new Error("cancelled during slow preflight"),
);
let cancellationSettled = false;
void cancellation.then(() => {
cancellationSettled = true;
});
await Promise.resolve();
assert.equal(cancellationSettled, false);
releasePrepare();
await assert.rejects(operation, /cancelled during slow preflight/);
await cancellation;
assert.equal(setup.workspace.createCalls, 0);
assert.equal(setup.executor.requests.length, 0);
assert.deepEqual(await setup.store.listRuns(), []);
});
});
test("start cancellation after reservation prevents later run and worktree publication", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const reserveStart = setup.store.reserveStart.bind(setup.store);
let markReserved = () => {};
let releaseReservation = () => {};
const reserved = new Promise<void>((resolve) => {
markReserved = resolve;
});
const reservationBlocked = new Promise<void>((resolve) => {
releaseReservation = resolve;
});
setup.store.reserveStart = async (key, fingerprint, runId) => {
const reservation = await reserveStart(key, fingerprint, runId);
markReserved();
await reservationBlocked;
return reservation;
};
const engine = setup.engine();
const operation = engine.execute(startCommand());
await reserved;
const cancellation = engine.cancelStart(
"start-key",
"cancel-reserved-start",
new Error("cancelled after reservation"),
);
let cancellationSettled = false;
void cancellation.then(() => {
cancellationSettled = true;
});
await Promise.resolve();
assert.equal(cancellationSettled, false);
releaseReservation();
await assert.rejects(operation, /cancelled after reservation/);
await cancellation;
assert.equal(setup.workspace.createCalls, 0);
assert.equal(setup.executor.requests.length, 0);
assert.deepEqual(await setup.store.listRuns(), []);
});
});
test("start cancellation after publication durably aborts a settled run", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const engine = setup.engine();
const started = await engine.execute(startCommand());
await new Promise((resolve) => setImmediate(resolve));
await engine.cancelStart(
"start-key",
"cancel-published-start",
new Error("cancelled after publication"),
);
const aborted = await setup.store.loadRun(started.runId);
assert.equal(aborted.status, "aborted");
assert.equal(
(await setup.store.getStart("start-key"))?.status,
"completed",
);
assert.equal(
((await setup.store.getStart("start-key"))?.result as WorkflowView).status,
"aborted",
);
});
});
test("start and decision idempotency prevent replay and reject changed commands", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
setup.executor.enqueue("design", design());
const engine = setup.engine();
const command = startCommand();
const first = await engine.execute(command);
const repeated = await engine.execute(command);
assert.deepEqual(repeated, first);
assert.equal(setup.workspace.prepareCalls, 1);
assert.equal(setup.workspace.createCalls, 1);
assert.equal(setup.executor.requests.length, 1);
await assert.rejects(
engine.execute({ ...command, allowDirtySource: true }),
/another start command/,
);
const decide = {
action: "decide" as const,
runId: first.runId,
gateId: first.openGate?.id ?? "missing",
expectedRevision: first.revision,
decision: {
kind: "answer-questions" as const,
answers: { "data-source": "Repository API" },
},
idempotencyKey: "answer-key",
};
const decided = await engine.execute(decide);
assert.deepEqual(await engine.execute(decide), decided);
assert.equal(
setup.executor.requests.filter((request) => request.role === "design")
.length,
1,
);
await assert.rejects(
engine.execute({
...decide,
decision: {
kind: "answer-questions",
answers: { "data-source": "Different answer" },
},
}),
/another command/,
);
});
});
test("concurrent starts share one authoritative run and workspace mutation", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const blocking = new BlockingExecutor(discoveryWithQuestions());
const first = setup.engine("session-1", { executor: blocking });
const second = setup.engine("session-1", { executor: blocking });
const firstStart = first.execute(startCommand());
await blocking.started;
const secondStart = second.execute(startCommand());
assert.equal(setup.workspace.prepareCalls, 1);
assert.equal(setup.workspace.createCalls, 1);
blocking.release();
const [firstResult, secondResult] = await Promise.all([
firstStart,
secondStart,
]);
assert.deepEqual(secondResult, firstResult);
assert.equal((await setup.store.listRuns()).length, 1);
assert.equal(blocking.requests.length, 1);
});
});
test("cross-process starts claim the published run before workspace mutation", async () => {
await withTempDir(async (directory) => {
const state = path.join(directory, "state");
const stateAlias = path.join(directory, "state-alias");
await mkdir(state);
await symlink(state, stateAlias);
const profiles = (
await loadAgentProfiles({ cwd: directory, projectTrusted: false })
).profiles;
const workspace = new FakeWorkspace();
const executor = new BlockingExecutor(discoveryWithQuestions());
let leftId = 0;
let rightId = 0;
const makeEngine = (
store: WorkflowStore,
processId: number,
createId: () => string,
) =>
new WorkflowEngine({
store,
workspace,
executor,
sourceRoot: directory,
parentSessionId: "session-1",
effectiveConfig: structuredClone(DEFAULT_WORKFLOW_CONFIG),
profiles,
now: () => 10_000,
createId,
processId,
isProcessAlive: () => true,
});
const left = makeEngine(
new WorkflowStore(state),
100,
() => `left-${++leftId}`,
);
const right = makeEngine(
new WorkflowStore(stateAlias),
200,
() => `right-${++rightId}`,
);
const leftStart = left.execute(startCommand());
const rightStart = right.execute(startCommand());
await executor.started;
assert.equal(workspace.createCalls, 1);
executor.release();
const [leftResult, rightResult] = await Promise.all([
leftStart,
rightStart,
]);
assert.equal(leftResult.runId, rightResult.runId);
assert.equal(workspace.createCalls, 1);
assert.equal(executor.requests.length, 1);
assert.equal((await new WorkflowStore(state).listRuns()).length, 1);
});
});
test("live workspace creation cannot be stolen after its heartbeat ages", async () => {
await withTempDir(async (directory) => {
const state = path.join(directory, "state");
const stateAlias = path.join(directory, "state-alias");
await mkdir(state);
await symlink(state, stateAlias);
const profiles = (
await loadAgentProfiles({ cwd: directory, projectTrusted: false })
).profiles;
const workspace = new FakeWorkspace();
let creationStarted: (() => void) | undefined;
let releaseCreation: (() => void) | undefined;
const startedCreating = new Promise<void>((resolve) => {
creationStarted = resolve;
});
const creationWait = new Promise<void>((resolve) => {
releaseCreation = resolve;
});
workspace.beforeCreate = async () => {
creationStarted?.();
await creationWait;
};
const executor = new ScriptedExecutor();
executor.enqueue("discover", discoveryWithQuestions());
let now = 10_000;
let leftId = 0;
let rightId = 0;
const makeEngine = (
store: WorkflowStore,
processId: number,
createId: () => string,
) =>
new WorkflowEngine({
store,
workspace,
executor,
sourceRoot: directory,
parentSessionId: "session-1",
effectiveConfig: structuredClone(DEFAULT_WORKFLOW_CONFIG),
profiles,
now: () => now,
createId,
processId,
isProcessAlive: () => true,
});
const owner = makeEngine(
new WorkflowStore(state),
100,
() => `owner-${++leftId}`,
);
const contender = makeEngine(
new WorkflowStore(stateAlias),
200,
() => `contender-${++rightId}`,
);
const ownerStart = owner.execute(startCommand());
await startedCreating;
now = 100_000;
const contenderResult = await contender.execute(startCommand());
assert.equal(contenderResult.workspace.state, "creating");
assert.equal(workspace.createCalls, 1);
assert.deepEqual(await contender.recover(), []);
assert.equal(workspace.reconcileCalls, 0);
releaseCreation?.();
const completed = await ownerStart;
assert.equal(completed.openGate?.kind, "questions");
assert.equal(workspace.createCalls, 1);
assert.equal(executor.requests.length, 1);
const startRecord = await new WorkflowStore(state).getStart("start-key");
assert.equal(
(startRecord?.result as { revision?: number } | undefined)?.revision,
completed.revision,
);
});
});
test("a pending start resumes after interrupted initial publication", async () => {
await withTempDir(async (directory) => {
let interrupt = true;
const setup = await fixture(directory, {
beforeRename(target) {
if (interrupt && target.endsWith("request.md")) {
throw new Error("simulated initial publication crash");
}
},
});
setup.executor.enqueue("discover", discoveryWithQuestions());
await assert.rejects(
setup.engine().execute(startCommand()),
/simulated initial publication crash/,
);
interrupt = false;
const resumed = await setup.engine().execute(startCommand());
assert.equal(resumed.status, "waiting");
assert.equal(resumed.openGate?.kind, "questions");
assert.equal(setup.workspace.prepareCalls, 2);
assert.equal(setup.workspace.createCalls, 1);
assert.equal((await setup.store.listRuns()).length, 1);
});
});
test("a durable ready revision survives convenience-snapshot interruption", async () => {
await withTempDir(async (directory) => {
let workflowWrites = 0;
let interrupt = true;
const setup = await fixture(directory, {
beforeRename(target) {
if (target.endsWith("workflow.json")) {
workflowWrites++;
if (interrupt && workflowWrites === 2) {
throw new Error("simulated ready snapshot crash");
}
}
},
});
setup.executor.enqueue("discover", discoveryWithQuestions());
await assert.rejects(
setup.engine().execute(startCommand()),
/simulated ready snapshot crash/,
);
assert.equal(setup.workspace.createCalls, 1);
interrupt = false;
const resumed = await setup.engine().execute(startCommand());
assert.equal(resumed.workspace.state, "ready");
assert.equal(resumed.openGate?.kind, "questions");
assert.equal(setup.workspace.createCalls, 1);
assert.equal(setup.workspace.reconcileCalls, 0);
});
});
test("real Git persisted readiness precedes retry preparation", async () => {
await withTempDir(async (directory) => {
const repository = path.join(directory, "repository");
await mkdir(repository);
const git = (args: string[], cwd = repository) =>
execFileAsync("git", args, { cwd, encoding: "utf8" });
await git(["init", "-b", "main"]);
await git(["config", "user.email", "workflow@example.invalid"]);
await git(["config", "user.name", "Pi Workflow Test"]);
await writeFile(
path.join(repository, ".gitignore"),
".worktrees/\n",
"utf8",
);
await writeFile(path.join(repository, "README.md"), "# Fixture\n", "utf8");
await git(["add", ".gitignore", "README.md"]);
await git(["commit", "-m", "initial fixture"]);
let workflowWrites = 0;
let interrupt = true;
const store = new WorkflowStore(path.join(directory, "state"), {
beforeRename(target) {
if (target.endsWith("workflow.json")) {
workflowWrites++;
if (interrupt && workflowWrites === 2) {
throw new Error("simulated real ready snapshot crash");
}
}
},
});
const executor = new ScriptedExecutor();
executor.enqueue("discover", discoveryWithQuestions());
const profiles = (
await loadAgentProfiles({ cwd: repository, projectTrusted: false })
).profiles;
let id = 0;
const makeEngine = () =>
new WorkflowEngine({
store,
workspace: new GitWorkspaceManager(),
executor,
sourceRoot: repository,
parentSessionId: "session-1",
effectiveConfig: structuredClone(DEFAULT_WORKFLOW_CONFIG),
profiles,
now: () => 10_000,
createId: () => `real-id-${++id}`,
processId: 100,
isProcessAlive: () => true,
});
await assert.rejects(
makeEngine().execute(startCommand()),
/simulated real ready snapshot crash/,
);
interrupt = false;
const resumed = await makeEngine().execute(startCommand());
assert.equal(resumed.workspace.state, "ready");
assert.equal(resumed.openGate?.kind, "questions");
assert.equal(executor.requests.length, 1);
const sourceStatus = await git(["status", "--porcelain"]);
assert.equal(sourceStatus.stdout, "");
});
});
test("persisted discovery output resumes during recovery without replay", async () => {
await withTempDir(async (directory) => {
let interrupt = true;
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const crashing = setup.engine("session-1", {
afterAssignmentPersist(assignment) {
if (interrupt && assignment.role === "discover") {
interrupt = false;
throw new Error("simulated post-assignment crash");
}
},
});
await assert.rejects(
crashing.execute(startCommand()),
/simulated post-assignment crash/,
);
const [persisted] = await setup.store.listRuns();
assert.equal(persisted.assignments[0]?.status, "succeeded");
if (persisted.lease) {
persisted.lease.processId = 999;
persisted.lease.heartbeatAt = 0;
}
persisted.revision++;
await setup.store.saveRun(persisted);
const recovery = setup.engine("session-2", {
now: () => 100_000,
processId: 200,
isProcessAlive: () => false,
});
const [resumed] = await recovery.recover();
assert.equal(resumed?.openGate?.kind, "questions");
assert.equal(setup.executor.requests.length, 1);
const replayedStart = await recovery.execute(startCommand());
assert.equal(replayedStart.openGate?.kind, "questions");
assert.equal(setup.executor.requests.length, 1);
});
});
test("pending decision continuation consumes persisted design output once", async () => {
await withTempDir(async (directory) => {
let interrupt = true;
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
setup.executor.enqueue("design", design());
const crashing = setup.engine("session-1", {
afterAssignmentPersist(assignment) {
if (interrupt && assignment.role === "design") {
interrupt = false;
throw new Error("simulated decision continuation crash");
}
},
});
const started = await crashing.execute(startCommand());
const command = {
action: "decide" as const,
runId: started.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: started.revision,
decision: {
kind: "answer-questions" as const,
answers: { "data-source": "Repository API" },
},
idempotencyKey: "answer-key",
};
await assert.rejects(
crashing.execute(command),
/simulated decision continuation crash/,
);
const interrupted = await setup.store.loadRun(started.runId);
assert.equal(
interrupted.commandReceipts.find(
(receipt) => receipt.key === "answer-key",
)?.status,
"pending",
);
assert.equal(
interrupted.assignments.find((assignment) => assignment.role === "design")
?.status,
"succeeded",
);
if (interrupted.lease) {
interrupted.lease.processId = 999;
interrupted.lease.heartbeatAt = 0;
}
interrupted.revision++;
await setup.store.saveRun(interrupted);
const recovery = setup.engine("session-2", {
now: () => 100_000,
processId: 200,
isProcessAlive: () => false,
});
const [resumed] = await recovery.recover();
assert.equal(resumed?.openGate?.kind, "plan-approval");
assert.equal(
(await recovery.execute(command)).openGate?.kind,
"plan-approval",
);
assert.equal(
setup.executor.requests.filter((request) => request.role === "design")
.length,
1,
);
assert.equal(
(await setup.store.loadRun(started.runId)).commandReceipts.find(
(receipt) => receipt.key === "answer-key",
)?.status,
"completed",
);
});
});
test("a concurrent duplicate decision cannot complete an active continuation", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(startCommand());
const blocking = new BlockingExecutor(design());
const engine = setup.engine("session-1", {
executor: blocking,
assignmentHeartbeatMs: 2,
});
const command = {
action: "decide" as const,
runId: started.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: started.revision,
decision: {
kind: "answer-questions" as const,
answers: { "data-source": "Repository API" },
},
idempotencyKey: "answer-key",
};
const deciding = engine.execute(command);
await blocking.started;
const duplicate = await engine.execute(command);
assert.equal(duplicate.status, "running");
assert.equal(
(await setup.store.loadRun(started.runId)).commandReceipts.find(
(receipt) => receipt.key === "answer-key",
)?.status,
"pending",
);
blocking.release();
assert.equal((await deciding).openGate?.kind, "plan-approval");
});
});
test("active assignment leases heartbeat and block concurrent recovery", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const blocking = new BlockingExecutor(discoveryWithQuestions());
let now = 10_000;
const engine = setup.engine("session-1", {
executor: blocking,
now: () => now,
processId: 100,
assignmentHeartbeatMs: 2,
});
const start = engine.execute(startCommand());
await blocking.started;
now = 100_000;
await new Promise((resolve) => setTimeout(resolve, 10));
const [running] = await setup.store.listRuns();
const assignment = running.assignments[0];
assert.equal(assignment?.status, "running");
const lease = await setup.store.loadAssignmentLease(
running.runId,
assignment?.id ?? "missing",
);
assert.equal(lease?.heartbeatAt, now);
assert.deepEqual(
await setup
.engine("session-2", {
now: () => now,
processId: 100,
isProcessAlive: () => false,
})
.recover(),
[],
);
blocking.release();
assert.equal((await start).openGate?.kind, "questions");
});
});
test("a live read-only assignment remains fenced when its heartbeat is old", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const blocking = new BlockingExecutor(discoveryWithQuestions());
let now = 10_000;
const start = setup
.engine("session-1", {
executor: blocking,
now: () => now,
processId: 100,
assignmentHeartbeatMs: 1_000,
})
.execute(startCommand());
await blocking.started;
now = 100_000;
const recovery = setup.engine("session-2", {
now: () => now,
processId: 200,
isProcessAlive: () => true,
});
assert.deepEqual(await recovery.recover(), []);
const [running] = await setup.store.listRuns();
assert.equal(running?.assignments[0]?.status, "running");
blocking.release();
assert.equal((await start).openGate?.kind, "questions");
});
});
test("a live mutating assignment is not reclaimed on heartbeat age alone", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(startCommand());
const run = await setup.store.loadRun(started.runId);
const assignment = run.assignments[0];
assert.ok(assignment);
assignment.status = "running";
assignment.mutates = true;
assignment.executionToken = "live-mutating-token";
for (const gate of run.gates) gate.status = "satisfied";
run.status = "running";
if (run.lease) {
run.lease.processId = 100;
run.lease.heartbeatAt = 0;
}
run.revision++;
await setup.store.saveRun(run);
await setup.store.createAssignmentLease(run.runId, assignment.id, {
token: assignment.executionToken,
processId: 100,
heartbeatAt: 0,
});
const recovery = setup.engine("session-2", {
now: () => 100_000,
processId: 200,
isProcessAlive: () => true,
});
assert.deepEqual(await recovery.recover(), []);
assert.equal(
(await setup.store.loadRun(run.runId)).assignments[0]?.status,
"running",
);
});
});
test("an executor cannot persist after losing its assignment fence", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const blocking = new BlockingExecutor(discoveryWithQuestions());
const start = setup
.engine("session-1", {
executor: blocking,
assignmentHeartbeatMs: 1_000,
})
.execute(startCommand());
await blocking.started;
const [running] = await setup.store.listRuns();
const assignment = running.assignments[0];
assert.ok(assignment?.executionToken);
await setup.store.releaseAssignmentLease(
running.runId,
assignment.id,
assignment.executionToken,
);
blocking.release();
await assert.rejects(start, /lost its execution lease/);
assert.equal(
(await setup.store.loadRun(running.runId)).assignments[0]?.status,
"running",
);
});
});
test("runtime command schemas reject malformed and open shapes", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const engine = setup.engine();
const malformed: unknown[] = [
{
...startCommand(),
unexpected: true,
},
{
...startCommand(),
background: "yes",
},
{
action: "inspect",
runId: "run-1",
extra: true,
},
{
action: "decide",
runId: "run-1",
gateId: "gate-1",
expectedRevision: 0,
decision: {
kind: "approve-plan",
approved: false,
acceptedUiImpact: "none",
},
idempotencyKey: "decision-key",
},
{
action: "decide",
runId: "run-1",
gateId: "gate-1",
expectedRevision: 0,
decision: { kind: "recover", resolution: "continue" },
idempotencyKey: "decision-key",
},
];
for (const command of malformed) {
await assert.rejects(engine.execute(command as WorkflowCommand));
}
assert.equal(setup.workspace.prepareCalls, 0);
});
});
test("decisions require the single active Gate and its waiting stage", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const engine = setup.engine();
const started = await engine.execute(startCommand());
const run = await setup.store.loadRun(started.runId);
run.gates.push({
id: "second-open-gate",
kind: "recovery",
status: "open",
revision: run.revision,
openedAt: 10_000,
});
run.revision++;
await setup.store.saveRun(run);
await assert.rejects(
engine.execute({
action: "decide",
runId: run.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: run.revision,
decision: {
kind: "answer-questions",
answers: { "data-source": "Repository API" },
},
idempotencyKey: "multiple-gates-key",
}),
/single active Gate/,
);
});
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const engine = setup.engine();
const started = await engine.execute(startCommand());
const run = await setup.store.loadRun(started.runId);
const discover = run.stages.find(
(candidate) => candidate.kind === "discover",
);
assert.ok(discover);
discover.status = "completed";
run.revision++;
await setup.store.saveRun(run);
await assert.rejects(
engine.execute({
action: "decide",
runId: run.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: run.revision,
decision: {
kind: "answer-questions",
answers: { "data-source": "Repository API" },
},
idempotencyKey: "wrong-stage-key",
}),
/waiting Discover stage/,
);
});
});
test("required profile eligibility fails before workspace mutation", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const profiles = structuredClone(setup.profiles);
for (const profile of profiles) {
if (profile.roles.includes("implement")) {
profile.selection.allTags = ["not-present"];
}
}
await assert.rejects(
setup.engine("session-1", { profiles }).execute(startCommand()),
/No eligible implement Agent Profile/,
);
assert.equal(setup.workspace.prepareCalls, 0);
assert.deepEqual(await setup.store.listRuns(), []);
});
});
test("stale and illegal decisions fail without changing the run", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const engine = setup.engine();
const started = await engine.execute(startCommand());
await assert.rejects(
engine.execute({
action: "decide",
runId: started.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: started.revision - 1,
decision: { kind: "answer-questions", answers: {} },
idempotencyKey: "stale-key",
}),
/Stale workflow revision/,
);
await assert.rejects(
engine.execute({
action: "decide",
runId: started.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: started.revision,
decision: { kind: "answer-questions", answers: {} },
idempotencyKey: "missing-answer-key",
}),
/requires an answer/,
);
await assert.rejects(
engine.execute({
action: "decide",
runId: started.runId,
gateId: started.openGate?.id ?? "missing",
expectedRevision: started.revision,
decision: {
kind: "approve-plan",
approved: true,
acceptedUiImpact: "none",
},
idempotencyKey: "illegal-key",
}),
/not legal for questions/,
);
assert.equal(
(await setup.store.loadRun(started.runId)).revision,
started.revision,
);
});
});
test("the closed Gate Decision matrix is exhaustive", () => {
const gateKinds: GateKind[] = [
"questions",
"plan-approval",
"prototype-acceptance",
"implementation-context",
"recovery",
];
const decisionKinds: GateDecision["kind"][] = [
"answer-questions",
"approve-plan",
"revise-plan",
"accept-prototype",
"refine-prototypes",
"select-implementation-context",
"report-context-preparation",
"recover",
];
const expected: Record<GateKind, GateDecision["kind"][]> = {
questions: ["answer-questions"],
"plan-approval": ["approve-plan", "revise-plan"],
"prototype-acceptance": ["accept-prototype", "refine-prototypes"],
"implementation-context": [
"select-implementation-context",
"report-context-preparation",
],
recovery: ["recover"],
};
for (const gateKind of gateKinds) {
for (const decisionKind of decisionKinds) {
assert.equal(
decisionIsLegal(gateKind, decisionKind),
expected[gateKind].includes(decisionKind),
`${decisionKind} at ${gateKind}`,
);
}
}
});
test("every illegal Gate Decision pair is rejected through execute", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const engine = setup.engine();
const started = await engine.execute(startCommand());
const run = await setup.store.loadRun(started.runId);
const gate = run.gates.find(
(candidate) => candidate.id === started.openGate?.id,
);
assert.ok(gate);
const decisions: GateDecision[] = [
{
kind: "answer-questions",
answers: { "data-source": "Repository API" },
},
{ kind: "approve-plan", approved: true, acceptedUiImpact: "none" },
{ kind: "revise-plan", feedback: "Revise it" },
{
kind: "accept-prototype",
candidateId: "candidate-1",
candidateRevision: 1,
browserReviewed: true,
},
{ kind: "refine-prototypes", feedback: "Refine it" },
{ kind: "select-implementation-context", strategy: "current-session" },
{
kind: "report-context-preparation",
preparationId: "preparation-1",
planRevision: 1,
method: "smart_compact",
outcome: "unavailable",
},
{ kind: "recover", resolution: "retry" },
];
const gateKinds: GateKind[] = [
"questions",
"plan-approval",
"prototype-acceptance",
"implementation-context",
"recovery",
];
for (const gateKind of gateKinds) {
gate.kind = gateKind;
gate.payload =
gateKind === "questions"
? { questions: [{ id: "data-source", prompt: "Which data source?" }] }
: undefined;
const discover = run.stages.find(
(candidate) => candidate.kind === "discover",
);
const designStage = run.stages.find(
(candidate) => candidate.kind === "design",
);
assert.ok(discover && designStage);
discover.status = gateKind === "questions" ? "waiting" : "completed";
designStage.status = gateKind === "plan-approval" ? "waiting" : "pending";
run.revision++;
gate.revision = run.revision;
await setup.store.saveRun(run);
for (const decision of decisions) {
if (decisionIsLegal(gateKind, decision.kind)) continue;
await assert.rejects(
engine.execute({
action: "decide",
runId: run.runId,
gateId: gate.id,
expectedRevision: run.revision,
decision,
idempotencyKey: `matrix-${gateKind}-${decision.kind}`,
}),
new RegExp(`not legal for ${gateKind}`),
);
}
}
});
});
test("abort cancels an active reserved Assignment before persisting terminal state", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
let resolveStarted = () => {};
const started = new Promise<void>((resolve) => {
resolveStarted = resolve;
});
let cancelled = false;
const executor: AssignmentExecutor = {
reservesCapacity: true,
async run(request) {
await request.onReserved?.();
resolveStarted();
return new Promise((resolve, reject) => {
request.signal.addEventListener(
"abort",
() => {
cancelled = true;
const error = Object.assign(new Error("cancelled"), {
assignmentMetadata: {
childSessionId: "child-aborted",
provider: "provider-a",
model: "model-a",
effort: "medium",
contextCapacity: 64_000,
transcript: [{ role: "assistant", text: "cancelled child" }],
},
});
reject(error);
},
{ once: true },
);
});
},
};
const startPromise = setup
.engine("session-1", { executor })
.execute(startCommand());
await started;
const [active] = await setup.store.listRuns();
assert.ok(active);
const aborted = await setup.engine("session-1", { executor }).execute({
action: "abort",
runId: active.runId,
expectedRevision: active.revision,
idempotencyKey: "abort-active",
});
const startResult = await startPromise;
assert.equal(cancelled, true);
assert.equal(startResult.status, "aborted");
assert.equal(aborted.status, "aborted");
const persisted = await setup.store.loadRun(active.runId);
const assignment = persisted.assignments.find(
(candidate) => candidate.role === "discover",
);
assert.equal(persisted.status, "aborted");
assert.equal(assignment?.status, "interrupted");
assert.equal(assignment?.childSessionId, "child-aborted");
assert.match(
await readFile(assignment?.transcriptArtifact?.path ?? "missing", "utf8"),
/cancelled child/,
);
});
});
test("abort times out without persisting terminal state when an executor will not stop", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
let resolveStarted = () => {};
const started = new Promise<void>((resolve) => {
resolveStarted = resolve;
});
let rejectRun = (_error: Error) => {};
const executor: AssignmentExecutor = {
reservesCapacity: true,
async run(request) {
await request.onReserved?.();
resolveStarted();
return new Promise((_resolve, reject) => {
rejectRun = reject;
});
},
};
const startPromise = setup.engine("session-1", { executor }).execute(startCommand());
await started;
const [active] = await setup.store.listRuns();
assert.ok(active);
await assert.rejects(
setup.engine("session-1", {
executor,
assignmentAbortTimeoutMs: 5,
}).execute({
action: "abort",
runId: active.runId,
expectedRevision: active.revision,
idempotencyKey: "abort-timeout",
}),
/Timed out stopping active Assignments/,
);
assert.notEqual((await setup.store.loadRun(active.runId)).status, "aborted");
rejectRun(new Error("executor finally stopped"));
await startPromise;
const stopped = await setup.store.loadRun(active.runId);
const retried = await setup.engine("session-1", { executor }).execute({
action: "abort",
runId: stopped.runId,
expectedRevision: stopped.revision,
idempotencyKey: "abort-after-timeout",
});
assert.equal(retried.status, "aborted");
assert.equal(
retried.assignments.find((assignment) => assignment.role === "discover")
?.status,
"interrupted",
);
});
});
test("abort fails closed for an Assignment owned by another live process", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(startCommand());
const current = await setup.store.loadRun(started.runId);
const profile = setup.profiles.find((candidate) =>
candidate.roles.includes("implement"),
);
const evidence = current.preflightEligibility.find(
(candidate) => candidate.role === "implement",
);
assert.ok(profile && evidence);
current.assignments.push({
id: "external-assignment",
stageId: `${current.runId}:implement`,
role: "implement",
profileName: profile.name,
profileDigest: profile.digest,
selection: {
eligibleProfiles: structuredClone(evidence.candidates),
selectedProfileName: profile.name,
rationale: "External owner fixture",
},
status: "running",
mutates: true,
ownedPaths: ["src"],
executionToken: "external-token",
});
current.status = "running";
current.revision++;
await setup.store.saveRun(current, current.revision - 1);
await setup.store.createAssignmentLease(
current.runId,
"external-assignment",
{
token: "external-token",
processId: 200,
heartbeatAt: 10_000,
},
);
await assert.rejects(
setup.engine("session-1", {
processId: 100,
isProcessAlive: (processId) => processId === 200,
}).execute({
action: "abort",
runId: current.runId,
expectedRevision: current.revision,
idempotencyKey: "abort-external",
}),
/Cannot safely abort Assignment external-assignment owned by live process 200/,
);
assert.equal((await setup.store.loadRun(current.runId)).status, "running");
});
});
test("cross-instance abort fails closed for a live timed-out mutator lease", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(startCommand());
const current = await setup.store.loadRun(started.runId);
const profile = setup.profiles.find((candidate) =>
candidate.roles.includes("implement"),
);
const evidence = current.preflightEligibility.find(
(candidate) => candidate.role === "implement",
);
assert.ok(profile && evidence);
current.assignments.push({
id: "timed-out-external-mutator",
stageId: `${current.runId}:implement`,
role: "implement",
profileName: profile.name,
profileDigest: profile.digest,
selection: {
eligibleProfiles: structuredClone(evidence.candidates),
selectedProfileName: profile.name,
rationale: "Timed-out mutator owned by another engine instance",
},
status: "interrupted",
mutates: true,
ownedPaths: ["src"],
executionToken: "timed-out-external-token",
error: "Assignment implement timed out",
});
current.status = "waiting";
current.revision++;
await setup.store.saveRun(current, current.revision - 1);
await setup.store.createAssignmentLease(
current.runId,
"timed-out-external-mutator",
{
token: "timed-out-external-token",
processId: 200,
heartbeatAt: 10_000,
},
);
await assert.rejects(
setup.engine("session-1", {
processId: 100,
isProcessAlive: (processId) => processId === 200,
}).execute({
action: "abort",
runId: current.runId,
expectedRevision: current.revision,
idempotencyKey: "abort-timed-out-external",
}),
/Cannot safely abort Assignment timed-out-external-mutator owned by live process 200/,
);
assert.equal((await setup.store.loadRun(current.runId)).status, "waiting");
});
});
test("abort is revision checked and idempotent", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const engine = setup.engine();
const started = await engine.execute(startCommand());
const abort = {
action: "abort" as const,
runId: started.runId,
expectedRevision: started.revision,
idempotencyKey: "abort-key",
};
const aborted = await engine.execute(abort);
assert.equal(aborted.status, "aborted");
assert.deepEqual(await engine.execute(abort), aborted);
await assert.rejects(
engine.execute({ ...abort, idempotencyKey: "other-key" }),
/Stale workflow revision/,
);
});
});
test("stale running assignments become interrupted once and require Recovery", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(startCommand());
const run = await setup.store.loadRun(started.runId);
for (const gate of run.gates) gate.status = "satisfied";
const completed = structuredClone(run.assignments[0]);
assert.ok(completed);
completed.id = "completed-assignment";
completed.status = "succeeded";
const running: WorkflowAssignment = {
...structuredClone(completed),
id: "mutating-assignment",
role: "implement",
status: "running",
mutates: true,
};
run.assignments = [completed, running];
run.status = "running";
run.revision++;
if (run.lease) {
run.lease.processId = 999;
run.lease.heartbeatAt = 0;
}
await setup.store.saveRun(run);
const recoveryEngine = setup.engine("session-2", {
now: () => 100_000,
processId: 200,
isProcessAlive: () => false,
});
const [recovered] = await recoveryEngine.recover();
assert.equal(recovered?.openGate?.kind, "recovery");
const recoveredRun = await setup.store.loadRun(run.runId);
assert.equal(
recoveredRun.assignments.find((item) => item.id === "mutating-assignment")
?.status,
"interrupted",
);
assert.equal(
recoveredRun.assignments.find(
(item) => item.id === "completed-assignment",
)?.status,
"succeeded",
);
assert.deepEqual(await recoveryEngine.recover(), []);
await assert.rejects(
setup.engine("session-1").execute({
action: "abort",
runId: run.runId,
expectedRevision: recoveredRun.revision,
idempotencyKey: "old-session-abort",
}),
/Historical parent sessions/,
);
const inspected = await recoveryEngine.execute({
action: "decide",
runId: run.runId,
gateId: recovered?.openGate?.id ?? "missing",
expectedRevision: recovered?.revision ?? -1,
decision: { kind: "recover", resolution: "inspect" },
idempotencyKey: "inspect-recovery-key",
});
assert.equal(inspected.openGate?.kind, "recovery");
const retry = await recoveryEngine.execute({
action: "decide",
runId: run.runId,
gateId: inspected.openGate?.id ?? "missing",
expectedRevision: inspected.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "recovery-key",
});
assert.equal(
retry.assignments.find((item) => item.id === "mutating-assignment")
?.status,
"interrupted",
);
assert.equal(retry.openGate?.kind, "recovery");
});
});
test("workspace creation failures persist recoverable intent", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
setup.workspace.createError = new Error("git worktree failed");
const engine = setup.engine();
const failed = await engine.execute(startCommand());
assert.equal(failed.status, "waiting");
assert.equal(failed.workspace.state, "creating");
assert.equal(failed.openGate?.kind, "recovery");
assert.match(failed.error?.message ?? "", /git worktree failed/);
assert.deepEqual(await engine.execute(startCommand()), failed);
assert.equal(setup.workspace.createCalls, 1);
await assert.rejects(
setup
.engine("session-1", {
processId: 200,
isProcessAlive: () => true,
})
.execute({
action: "decide",
runId: failed.runId,
gateId: failed.openGate?.id ?? "missing",
expectedRevision: failed.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "live-owner-retry-key",
}),
/still owned by a live process/,
);
assert.equal(
(await setup.store.loadRun(failed.runId)).gates.find(
(gate) => gate.id === failed.openGate?.id,
)?.status,
"open",
);
setup.workspace.createError = undefined;
const recovered = await engine.execute({
action: "decide",
runId: failed.runId,
gateId: failed.openGate?.id ?? "missing",
expectedRevision: failed.revision,
decision: { kind: "recover", resolution: "retry" },
idempotencyKey: "workspace-retry-key",
});
assert.equal(recovered.workspace.state, "ready");
assert.equal(recovered.openGate?.kind, "questions");
assert.equal(setup.workspace.reconcileCalls, 1);
});
});
test("post-creation discovery failures retain the ready worktree", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", { invalid: true });
const failed = await setup.engine().execute(startCommand());
assert.equal(failed.status, "failed");
assert.equal(failed.workspace.state, "ready");
assert.equal(failed.error?.code, "invalid-discovery-output");
});
});
test("discovery question bounds preserve complete model-facing Gate payloads", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", {
...discoveryWithQuestions(),
questions: Array.from({ length: 21 }, (_, index) => ({
id: `question-${index}`,
prompt: `Question ${index}?`,
})),
});
const failed = await setup.engine().execute(
startCommand({ idempotencyKey: "too-many-questions" }),
);
assert.equal(failed.status, "failed");
assert.match(failed.error?.message ?? "", /at most 20 questions/);
});
});
test("startup reconciliation resolves persisted workspace creation intent", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
setup.executor.enqueue("discover", discoveryWithQuestions());
const started = await setup.engine().execute(startCommand());
const run = await setup.store.loadRun(started.runId);
for (const gate of run.gates) gate.status = "satisfied";
run.workspace.state = "creating";
run.status = "running";
run.revision++;
if (run.lease) run.lease.heartbeatAt = 0;
await setup.store.saveRun(run);
const recovery = setup.engine("session-2", {
now: () => 100_000,
processId: 200,
isProcessAlive: () => false,
});
const [recovered] = await recovery.recover();
assert.equal(setup.workspace.reconcileCalls, 1);
assert.equal(recovered?.workspace.state, "ready");
assert.equal(recovered?.openGate?.kind, "recovery");
});
});
test("feature names and branch collisions fail with actionable guidance", async () => {
await withTempDir(async (directory) => {
const setup = await fixture(directory);
const engine = setup.engine();
await assert.rejects(
engine.execute(startCommand({ featureName: "Bad Feature" })),
/lowercase kebab case/,
);
assert.equal(setup.workspace.prepareCalls, 0);
setup.workspace.branchExists = true;
await assert.rejects(
engine.execute(startCommand({ idempotencyKey: "collision-key" })),
/choose another descriptive feature name/,
);
setup.workspace.branchExists = false;
setup.executor.enqueue("discover", discoveryWithQuestions());
await engine.execute(startCommand({ idempotencyKey: "owner-key" }));
setup.workspace.branchExists = true;
const [owner] = await setup.store.listRuns();
await assert.rejects(
engine.execute(startCommand({ idempotencyKey: "second-owner-key" })),
(error: unknown) => {
assert.match((error as Error).message, /inspect or decide/);
assert.ok((error as Error).message.includes(owner!.runId));
return true;
},
);
});
});