fix(gsd): update test files for removed completedUnits, writeLock signature, and type changes

- Remove completedUnits from WorkerInfo/SessionLockData test object literals
- Remove verifyExpectedArtifact/writeUnitRuntimeRecord from LoopDeps mocks
- Fix writeLock call signatures (remove numeric completedUnits arg)
- Fix idle-recovery imports (moved to auto-recovery.ts)
- Add full_plan_md to TaskRow test objects
- Fix WorkflowEvent type in test (exclude session_id from Omit)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson 2026-03-25 08:50:48 -06:00
parent 63dea156c3
commit 8119e12ce9
14 changed files with 34 additions and 61 deletions

View file

@ -367,9 +367,6 @@ function makeMockDeps(
getPriorSliceCompletionBlocker: () => null,
getMainBranch: () => "main",
closeoutUnit: async () => {},
verifyExpectedArtifact: () => true,
clearUnitRuntimeRecord: () => {},
writeUnitRuntimeRecord: () => {},
recordOutcome: () => {},
writeLock: () => {},
captureAvailableSkills: () => {},
@ -1990,7 +1987,6 @@ test("autoLoop does NOT reject non-execute-task units with 0 tool calls (#1833)"
});
},
getLedger: () => mockLedger,
verifyExpectedArtifact: () => true,
postUnitPostVerification: async () => {
deps.callLog.push("postUnitPostVerification");
s.active = false;

View file

@ -195,9 +195,6 @@ function makeMockDeps(overrides?: Partial<LoopDeps>): LoopDeps & { callLog: stri
getPriorSliceCompletionBlocker: () => null,
getMainBranch: () => "main",
closeoutUnit: async () => {},
verifyExpectedArtifact: () => true,
clearUnitRuntimeRecord: () => {},
writeUnitRuntimeRecord: () => {},
recordOutcome: () => {},
writeLock: () => {},
captureAvailableSkills: () => {},

View file

@ -7,7 +7,7 @@ import {
writeBlockerPlaceholder,
verifyExpectedArtifact,
buildLoopRemediationSteps,
} from "../auto.ts";
} from "../auto-recovery.ts";
import { describe, test, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';

View file

@ -92,9 +92,6 @@ function makeMockDeps(
getPriorSliceCompletionBlocker: () => null,
getMainBranch: () => "main",
closeoutUnit: async () => {},
verifyExpectedArtifact: () => true,
clearUnitRuntimeRecord: () => {},
writeUnitRuntimeRecord: () => {},
recordOutcome: () => {},
writeLock: () => {},
captureAvailableSkills: () => {},

View file

@ -322,7 +322,6 @@ test("budget — refreshWorkerStatuses updates worker state from disk", async ()
const workers = getWorkerStatuses();
assert.equal(workers.length, 1);
assert.equal(workers[0]!.state, "paused", "worker state should be updated from disk");
assert.equal(workers[0]!.completedUnits, 5, "completedUnits should be updated from disk");
assert.equal(workers[0]!.cost, 2.5, "cost should be updated from disk");
} finally {
resetOrchestrator();

View file

@ -71,7 +71,6 @@ test('Test 1: persistState writes valid JSON', () => {
worktreePath: "/tmp/wt-M001",
startedAt: Date.now(),
state: "running",
completedUnits: 3,
cost: 0.15,
},
],
@ -114,7 +113,6 @@ test('Test 3: restoreState filters dead PIDs', () => {
worktreePath: "/tmp/wt-M001",
startedAt: Date.now(),
state: "running",
completedUnits: 0,
cost: 0,
},
{
@ -124,7 +122,6 @@ test('Test 3: restoreState filters dead PIDs', () => {
worktreePath: "/tmp/wt-M002",
startedAt: Date.now(),
state: "running",
completedUnits: 0,
cost: 0,
},
],
@ -153,7 +150,6 @@ test('Test 4: restoreState keeps alive PIDs', () => {
worktreePath: "/tmp/wt-M001",
startedAt: Date.now(),
state: "running",
completedUnits: 5,
cost: 0.25,
},
{
@ -163,7 +159,6 @@ test('Test 4: restoreState keeps alive PIDs', () => {
worktreePath: "/tmp/wt-M002",
startedAt: Date.now(),
state: "running",
completedUnits: 0,
cost: 0,
},
],
@ -176,7 +171,6 @@ test('Test 4: restoreState keeps alive PIDs', () => {
assert.deepStrictEqual(result!.workers.length, 1, "restoreState: filters out dead PID");
assert.deepStrictEqual(result!.workers[0].milestoneId, "M001", "restoreState: keeps alive worker");
assert.deepStrictEqual(result!.workers[0].pid, process.pid, "restoreState: preserves PID");
assert.deepStrictEqual(result!.workers[0].completedUnits, 5, "restoreState: preserves progress");
} finally {
rmSync(basePath, { recursive: true, force: true });
}
@ -194,7 +188,6 @@ test('Test 5: restoreState skips stopped/error workers even with alive PIDs', ()
worktreePath: "/tmp/wt-M001",
startedAt: Date.now(),
state: "stopped",
completedUnits: 10,
cost: 0.50,
},
],

View file

@ -70,7 +70,6 @@ function makeWorker(overrides: Partial<WorkerInfo> = {}): WorkerInfo {
worktreePath: "/tmp/test",
startedAt: Date.now(),
state: "stopped",
completedUnits: 3,
cost: 1.5,
...overrides,
};
@ -132,16 +131,16 @@ test("determineMergeOrder — by-completion sorts by startedAt (earliest first)"
assert.deepEqual(order, ["M003", "M002", "M001"]);
});
test("determineMergeOrder — only includes stopped workers with completedUnits > 0", () => {
test("determineMergeOrder — only includes stopped workers", () => {
const workers = [
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 3 }),
makeWorker({ milestoneId: "M002", state: "running", completedUnits: 2 }),
makeWorker({ milestoneId: "M003", state: "stopped", completedUnits: 0 }),
makeWorker({ milestoneId: "M004", state: "error", completedUnits: 5 }),
makeWorker({ milestoneId: "M005", state: "paused", completedUnits: 1 }),
makeWorker({ milestoneId: "M001", state: "stopped" }),
makeWorker({ milestoneId: "M002", state: "running" }),
makeWorker({ milestoneId: "M003", state: "stopped" }),
makeWorker({ milestoneId: "M004", state: "error" }),
makeWorker({ milestoneId: "M005", state: "paused" }),
];
const order = determineMergeOrder(workers, "sequential");
assert.deepEqual(order, ["M001"]);
assert.deepEqual(order, ["M001", "M003"]);
});
test("determineMergeOrder — empty workers returns empty array", () => {

View file

@ -297,7 +297,6 @@ describe("parallel-orchestrator: lifecycle", () => {
worktreePath: "/tmp/wt-M001",
startedAt: Date.now(),
state: "running",
completedUnits: 2,
cost: 0.25,
},
],
@ -309,7 +308,6 @@ describe("parallel-orchestrator: lifecycle", () => {
const workers = getWorkerStatuses(base);
assert.equal(workers.length, 1);
assert.equal(workers[0].milestoneId, "M001");
assert.equal(workers[0].completedUnits, 2);
assert.equal(isParallelActive(), true);
} finally {
resetOrchestrator();
@ -416,7 +414,6 @@ describe("parallel-orchestrator: lifecycle", () => {
const workers = getWorkerStatuses();
assert.equal(workers.length, 1);
assert.equal(workers[0].state, "running");
assert.equal(workers[0].completedUnits, 4);
} finally {
resetOrchestrator();
rmSync(base, { recursive: true, force: true });
@ -552,7 +549,6 @@ function makeWorker(overrides: Partial<WorkerInfo> = {}): WorkerInfo {
worktreePath: "/tmp/test-worktree",
startedAt: Date.now() - 60_000,
state: "stopped",
completedUnits: 5,
cost: 2.50,
...overrides,
};
@ -563,9 +559,9 @@ function makeWorker(overrides: Partial<WorkerInfo> = {}): WorkerInfo {
describe("parallel-merge: determineMergeOrder sequential", () => {
it("returns milestone IDs sorted alphabetically by default", () => {
const workers = [
makeWorker({ milestoneId: "M003", state: "stopped", completedUnits: 1 }),
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 2 }),
makeWorker({ milestoneId: "M002", state: "stopped", completedUnits: 3 }),
makeWorker({ milestoneId: "M003", state: "stopped" }),
makeWorker({ milestoneId: "M001", state: "stopped" }),
makeWorker({ milestoneId: "M002", state: "stopped" }),
];
const order = determineMergeOrder(workers, "sequential");
assert.deepEqual(order, ["M001", "M002", "M003"]);
@ -573,27 +569,27 @@ describe("parallel-merge: determineMergeOrder sequential", () => {
it("excludes workers that are still running", () => {
const workers = [
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 5 }),
makeWorker({ milestoneId: "M002", state: "running", completedUnits: 0 }),
makeWorker({ milestoneId: "M003", state: "stopped", completedUnits: 2 }),
makeWorker({ milestoneId: "M001", state: "stopped" }),
makeWorker({ milestoneId: "M002", state: "running" }),
makeWorker({ milestoneId: "M003", state: "stopped" }),
];
const order = determineMergeOrder(workers, "sequential");
assert.deepEqual(order, ["M001", "M003"]);
});
it("excludes workers with zero completedUnits even if stopped", () => {
it("includes all stopped workers", () => {
const workers = [
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 0 }),
makeWorker({ milestoneId: "M002", state: "stopped", completedUnits: 3 }),
makeWorker({ milestoneId: "M001", state: "stopped" }),
makeWorker({ milestoneId: "M002", state: "stopped" }),
];
const order = determineMergeOrder(workers, "sequential");
assert.deepEqual(order, ["M002"]);
assert.deepEqual(order, ["M001", "M002"]);
});
it("returns empty array when no workers are completed", () => {
const workers = [
makeWorker({ milestoneId: "M001", state: "running", completedUnits: 0 }),
makeWorker({ milestoneId: "M002", state: "paused", completedUnits: 0 }),
makeWorker({ milestoneId: "M001", state: "running" }),
makeWorker({ milestoneId: "M002", state: "paused" }),
];
const order = determineMergeOrder(workers);
assert.deepEqual(order, []);
@ -601,8 +597,8 @@ describe("parallel-merge: determineMergeOrder sequential", () => {
it("uses sequential order as the default when no order arg provided", () => {
const workers = [
makeWorker({ milestoneId: "M002", state: "stopped", completedUnits: 1 }),
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 1 }),
makeWorker({ milestoneId: "M002", state: "stopped" }),
makeWorker({ milestoneId: "M001", state: "stopped" }),
];
// Call with no second argument — should default to "sequential"
const order = determineMergeOrder(workers);
@ -614,9 +610,9 @@ describe("parallel-merge: determineMergeOrder by-completion", () => {
it("returns milestones sorted by startedAt (earliest first)", () => {
const now = Date.now();
const workers = [
makeWorker({ milestoneId: "M003", state: "stopped", completedUnits: 1, startedAt: now - 30_000 }),
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 1, startedAt: now - 90_000 }),
makeWorker({ milestoneId: "M002", state: "stopped", completedUnits: 1, startedAt: now - 60_000 }),
makeWorker({ milestoneId: "M003", state: "stopped", startedAt: now - 30_000 }),
makeWorker({ milestoneId: "M001", state: "stopped", startedAt: now - 90_000 }),
makeWorker({ milestoneId: "M002", state: "stopped", startedAt: now - 60_000 }),
];
const order = determineMergeOrder(workers, "by-completion");
assert.deepEqual(order, ["M001", "M002", "M003"]);
@ -625,9 +621,9 @@ describe("parallel-merge: determineMergeOrder by-completion", () => {
it("excludes paused workers from by-completion order", () => {
const now = Date.now();
const workers = [
makeWorker({ milestoneId: "M001", state: "stopped", completedUnits: 2, startedAt: now - 90_000 }),
makeWorker({ milestoneId: "M002", state: "paused", completedUnits: 1, startedAt: now - 60_000 }),
makeWorker({ milestoneId: "M003", state: "stopped", completedUnits: 3, startedAt: now - 30_000 }),
makeWorker({ milestoneId: "M001", state: "stopped", startedAt: now - 90_000 }),
makeWorker({ milestoneId: "M002", state: "paused", startedAt: now - 60_000 }),
makeWorker({ milestoneId: "M003", state: "stopped", startedAt: now - 30_000 }),
];
const order = determineMergeOrder(workers, "by-completion");
assert.deepEqual(order, ["M001", "M003"]);

View file

@ -155,7 +155,6 @@ describe("parallel-worker-monitoring", () => {
worktreePath: "/tmp/wt-M001",
startedAt: Date.now(),
state: "running",
completedUnits: 1,
cost: 0.1,
},
],
@ -191,7 +190,6 @@ describe("parallel-worker-monitoring", () => {
refreshWorkerStatuses(base, { restoreIfNeeded: true });
const workers = getWorkerStatuses();
assert.deepStrictEqual(workers[0].state, "running", "live session status restored");
assert.deepStrictEqual(workers[0].completedUnits, 3, "completed units restored from status file");
} finally {
resetOrchestrator();
rmSync(base, { recursive: true, force: true });

View file

@ -52,6 +52,7 @@ function makeTaskRow(overrides?: Partial<TaskRow>): TaskRow {
key_files: [],
key_decisions: [],
full_summary_md: '',
full_plan_md: '',
description: 'Test description',
estimate: '30m',
files: ['src/test.ts'],

View file

@ -103,7 +103,7 @@ describe('session-lock-regression', async () => {
try {
acquireSessionLock(base);
updateSessionLock(base, 'execute-task', 'M001/S01/T01', 5, '/tmp/session.json');
updateSessionLock(base, 'execute-task', 'M001/S01/T01', '/tmp/session.json');
const data = readSessionLockData(base);
assert.ok(data !== null, 'lock data readable after update');
@ -111,7 +111,6 @@ describe('session-lock-regression', async () => {
assert.deepStrictEqual(data.pid, process.pid, 'lock data has correct PID');
assert.deepStrictEqual(data.unitType, 'execute-task', 'lock data has correct unit type');
assert.deepStrictEqual(data.unitId, 'M001/S01/T01', 'lock data has correct unit ID');
assert.deepStrictEqual(data.completedUnits, 5, 'lock data has correct completed count');
assert.deepStrictEqual(data.sessionFile, '/tmp/session.json', 'lock data has session file');
}
@ -136,7 +135,6 @@ describe('session-lock-regression', async () => {
unitType: 'execute-task',
unitId: 'M001/S01/T01',
unitStartedAt: new Date(Date.now() - 3600000).toISOString(),
completedUnits: 3,
};
writeFileSync(lockFile, JSON.stringify(staleLock, null, 2));
@ -233,7 +231,6 @@ describe('session-lock-regression', async () => {
unitType: 'execute-task',
unitId: 'M001/S01/T01',
unitStartedAt: new Date().toISOString(),
completedUnits: 0,
}, null, 2));
const status = getSessionLockStatus(base);

View file

@ -64,7 +64,7 @@ test("stopAutoRemote cleans up stale lock (dead PID) and returns found:false", (
const base = makeTmpBase();
try {
// Write a lock with a PID that doesn't exist
writeLock(base, "execute-task", "M001/S01/T01", 3);
writeLock(base, "execute-task", "M001/S01/T01");
// Overwrite PID to a dead one
const lock = readCrashLock(base)!;
const staleData = { ...lock, pid: 999999999 };
@ -111,7 +111,6 @@ test("stopAutoRemote sends SIGTERM to a live process and returns found:true", {
unitType: "execute-task",
unitId: "M001/S01/T01",
unitStartedAt: new Date().toISOString(),
completedUnits: 0,
};
writeFileSync(join(base, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2), "utf-8");
@ -143,7 +142,7 @@ test("lock file should be discoverable at project root, not worktree path", () =
try {
// Simulate: auto-mode writes lock to project root (the fix)
writeLock(projectRoot, "execute-task", "M001/S01/T01", 0);
writeLock(projectRoot, "execute-task", "M001/S01/T01");
// Second terminal checks project root — should find the lock
const lock = readCrashLock(projectRoot);

View file

@ -22,7 +22,7 @@ function cleanupDir(dirPath: string): void {
try { fs.rmSync(dirPath, { recursive: true, force: true }); } catch { /* best effort */ }
}
function makeEvent(cmd: string, params: Record<string, unknown> = {}): Omit<WorkflowEvent, 'hash'> {
function makeEvent(cmd: string, params: Record<string, unknown> = {}): Omit<WorkflowEvent, 'hash' | 'session_id'> {
return { cmd, params, ts: new Date().toISOString(), actor: 'agent' };
}

View file

@ -54,6 +54,7 @@ function makeTask(overrides: Partial<TaskRow> = {}): TaskRow {
key_files: [],
key_decisions: [],
full_summary_md: '',
full_plan_md: '',
inputs: [],
expected_output: [],
observability_impact: '',