Skip to content

Commit 2afca97

Browse files
mchwangclaude
andcommitted
Cover task storage release on the foreign-result and launch-failure paths
After the rebase onto main, the coordinator releases task storage after the terminal write on main's foreign-result path too. Neither that path nor the launch-failure path had a test that failed without the release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent a6d57c4 commit 2afca97

1 file changed

Lines changed: 14 additions & 1 deletion

File tree

‎test/runner-execution.test.ts‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const manifest = (changes: ManifestChange[], over: Partial<ChangeManifest> = {})
2424
({ changes, agentCommits: [], metadataChanged: false, linkTargetChanges: [], nestedGitlinkContent: [], digest: `digest-${changes.length}`, ...over });
2525

2626
function setup(options: { manifests?: Record<string, ChangeManifest & { digest: string }>; exit?: Record<string, Partial<InvocationResult>>;
27-
commit?: (item: string) => Promise<void>; release?: () => Promise<void> } = {}) {
27+
commit?: (item: string) => Promise<void>; release?: () => Promise<void>; startError?: Error } = {}) {
2828
const dir = mkdtempSync(join(tmpdir(), 'codeboost-exec-')); dirs.push(dir);
2929
const store = new Store(join(dir, 'state.sqlite'));
3030
store.createPlan(JSON.stringify(plan), 'json', context, oid(1), oid(2));
@@ -50,6 +50,7 @@ function setup(options: { manifests?: Record<string, ChangeManifest & { digest:
5050
const sources: ExecutionSources = { planContext: () => context, issue: () => ({ number: 1, title: 'Issue', body: 'Please fix', comments: [] }), lessons: () => [], vendor: () => 'claude' };
5151
const prompts: string[] = [], argv: (readonly (readonly string[])[])[] = [], owners: string[] = [];
5252
const deps = executionDeps(store, workspace, (input, prompt, ws) => {
53+
if (options.startError) throw options.startError;
5354
log.push(`start ${itemOf(ws)}`); prompts.push(prompt); argv.push(input.approvedArgv); owners.push(input.runnerOwner);
5455
return { attemptId: input.attemptId, settled: Promise.resolve({ attemptId: input.attemptId, context: input.context, exitCode: 0, signal: null, stdout: 'done', stderr: '', ...options.exit?.[itemOf(ws)] }), cancel: () => undefined };
5556
}, sources, RUNNER_OWNER);
@@ -124,4 +125,16 @@ describe('item execution', () => {
124125
await expect(executor.runTask(identity)).rejects.toThrow(/Needs restart/);
125126
expect(runner.status(identity).unresolved).toMatchObject({ reason: 'result-not-saved' });
126127
});
128+
it('releases task storage after the terminal write when D settles with another attempt\'s result', async () => {
129+
const { store, executor, log } = setup({ exit: { P1: { attemptId: '00000000-0000-4000-8000-000000000000' } } });
130+
expect(await executor.runTask(identity)).toMatchObject({ kind: 'stopped', item: 'P1', state: 'failed' });
131+
expect(log.some(line => line.startsWith('inspect'))).toBe(false);
132+
expect(log).toContain('release P1 after failed');
133+
expect(store.getSnapshot(identity).head).toBe(oid(2));
134+
});
135+
it('releases task storage after the terminal write when D\'s start call throws', async () => {
136+
const { executor, log } = setup({ startError: new Error('docker refused') });
137+
expect(await executor.runTask(identity)).toMatchObject({ kind: 'stopped', item: 'P1', state: 'failed', reason: 'Launch failed: docker refused' });
138+
expect(log).toContain('release P1 after failed');
139+
});
127140
});

0 commit comments

Comments
 (0)