fix: Convert sync file I/O to async in agent spawn path to unblock event loop

writeInputFiles, spawnDetached, and diagnostic writes now use
fs/promises (mkdir, writeFile) instead of mkdirSync/writeFileSync.
File writes in writeInputFiles are batched with Promise.all.
openSync/closeSync for child process stdio FDs remain sync as
spawn() requires the FDs immediately.
This commit is contained in:
Lukas May
2026-03-04 12:15:31 +01:00
parent 70fd996fa1
commit bd0aec4499
7 changed files with 87 additions and 79 deletions

View File

@@ -43,7 +43,7 @@ describe('generateId', () => {
});
describe('writeInputFiles', () => {
it('writes initiative.md with frontmatter', () => {
it('writes initiative.md with frontmatter', async () => {
const initiative: Initiative = {
id: 'init-1',
name: 'Test Initiative',
@@ -55,13 +55,13 @@ describe('writeInputFiles', () => {
updatedAt: new Date('2026-01-02'),
};
writeInputFiles({ agentWorkdir: testDir, initiative });
await writeInputFiles({ agentWorkdir: testDir, initiative });
const filePath = join(testDir, '.cw', 'input', 'initiative.md');
expect(existsSync(filePath)).toBe(true);
});
it('writes phase.md with frontmatter', () => {
it('writes phase.md with frontmatter', async () => {
const phase = {
id: 'phase-1',
initiativeId: 'init-1',
@@ -73,13 +73,13 @@ describe('writeInputFiles', () => {
updatedAt: new Date(),
} as Phase;
writeInputFiles({ agentWorkdir: testDir, phase });
await writeInputFiles({ agentWorkdir: testDir, phase });
const filePath = join(testDir, '.cw', 'input', 'phase.md');
expect(existsSync(filePath)).toBe(true);
});
it('writes task.md with frontmatter', () => {
it('writes task.md with frontmatter', async () => {
const task = {
id: 'task-1',
name: 'Test Task',
@@ -93,14 +93,14 @@ describe('writeInputFiles', () => {
updatedAt: new Date(),
} as Task;
writeInputFiles({ agentWorkdir: testDir, task });
await writeInputFiles({ agentWorkdir: testDir, task });
const filePath = join(testDir, '.cw', 'input', 'task.md');
expect(existsSync(filePath)).toBe(true);
});
it('writes pages to pages/ subdirectory', () => {
writeInputFiles({
it('writes pages to pages/ subdirectory', async () => {
await writeInputFiles({
agentWorkdir: testDir,
pages: [
{ id: 'page-1', parentPageId: null, title: 'Root', content: null, sortOrder: 0 },
@@ -112,13 +112,13 @@ describe('writeInputFiles', () => {
expect(existsSync(join(testDir, '.cw', 'input', 'pages', 'page-2.md'))).toBe(true);
});
it('handles empty options without error', () => {
writeInputFiles({ agentWorkdir: testDir });
it('handles empty options without error', async () => {
await writeInputFiles({ agentWorkdir: testDir });
expect(existsSync(join(testDir, '.cw', 'input'))).toBe(true);
});
it('writes context/index.json grouping tasks by phaseId', () => {
writeInputFiles({
it('writes context/index.json grouping tasks by phaseId', async () => {
await writeInputFiles({
agentWorkdir: testDir,
tasks: [
{ id: 't1', name: 'Task A', phaseId: 'ph1', status: 'pending', category: 'execute', type: 'auto', priority: 'medium' } as Task,
@@ -140,8 +140,8 @@ describe('writeInputFiles', () => {
});
});
it('does not write context/index.json when no tasks', () => {
writeInputFiles({ agentWorkdir: testDir });
it('does not write context/index.json when no tasks', async () => {
await writeInputFiles({ agentWorkdir: testDir });
expect(existsSync(join(testDir, '.cw', 'input', 'context', 'index.json'))).toBe(false);
});
});