From d48abe549ddce0d3eb358af0780ef21d7d50321c Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Fri, 6 Mar 2026 01:47:28 -0800 Subject: [PATCH] #207 factory|fix: Skip interactive run directories in project scanner Interactive sessions create run directories ending with `-interactive` that have no run-index.json, causing noisy ENOENT warnings on every scan. --- .../__tests__/project-scanner.test.ts | 32 +++++++++++++++++++ .../src/server/services/project-scanner.ts | 1 + 2 files changed, 33 insertions(+) diff --git a/packages/factory/src/server/services/__tests__/project-scanner.test.ts b/packages/factory/src/server/services/__tests__/project-scanner.test.ts index b6c58304..f7b7156a 100644 --- a/packages/factory/src/server/services/__tests__/project-scanner.test.ts +++ b/packages/factory/src/server/services/__tests__/project-scanner.test.ts @@ -346,6 +346,38 @@ describe('ProjectScanner', () => { expect(silent.error).not.toHaveBeenCalled(); }); + it('silently skips interactive run directories without logging a warning', async () => { + using silent = silencedConsole(); + const scanner = new ProjectScanner('/test/projects'); + + mockReaddirResult(['proj']); + mockStatDirectory(); + mockReaddirResult(['tickets']); + mockReaddirResult(['TICKET-1']); + mockStatDirectory(); // stat for TICKET-1 directory + mockReaddirResult(['20260224-1030Z-interactive', 'good-run']); + // No stat call for the interactive directory — it's skipped before stat + mockStatDirectory(); // stat for good-run directory + + mockedParseRunData.mockResolvedValueOnce( + createMockStatus({ + runId: 'good-run', + startedAt: '2026-03-01T00:00:00Z', + }), + ); + + const result = await scanner.scan(); + + expect(result.projects).toHaveLength(1); + const runs = result.projects[0]?.tickets[0]?.runs; + expect(runs).toHaveLength(1); + expect(runs?.[0]?.runId).toBe('good-run'); + + expect(mockedParseRunData).toHaveBeenCalledOnce(); + expect(silent.warn).not.toHaveBeenCalled(); + expect(silent.error).not.toHaveBeenCalled(); + }); + it('does not scan direct entries when tickets/ directory exists', async () => { const scanner = new ProjectScanner('/test/projects'); diff --git a/packages/factory/src/server/services/project-scanner.ts b/packages/factory/src/server/services/project-scanner.ts index cc8cd6fa..d52d9c17 100644 --- a/packages/factory/src/server/services/project-scanner.ts +++ b/packages/factory/src/server/services/project-scanner.ts @@ -131,6 +131,7 @@ export class ProjectScanner { for (const runId of runDirs) { if (runId.startsWith('.')) continue; + if (runId.endsWith('-interactive')) continue; const runPath = join(ticketPath, runId); try {