diff --git a/test/main/services/team/ChangeExtractorService.test.ts b/test/main/services/team/ChangeExtractorService.test.ts index 5076c321..19ca95c6 100644 --- a/test/main/services/team/ChangeExtractorService.test.ts +++ b/test/main/services/team/ChangeExtractorService.test.ts @@ -79,6 +79,7 @@ async function writeOpenCodeDeliveryLedger( taskId: string; displayId: string; teamName: string; + taskRefs: { taskId: string; displayId: string; teamName: string }[]; }> ): Promise { const memberName = overrides?.memberName ?? 'bob'; @@ -108,7 +109,7 @@ async function writeOpenCodeDeliveryLedger( observedAssistantMessageId: overrides?.observedAssistantMessageId ?? null, prePromptCursor: null, postPromptCursor: null, - taskRefs: [ + taskRefs: overrides?.taskRefs ?? [ { taskId: overrides?.taskId ?? TASK_ID, displayId: overrides?.displayId ?? 'abc12345', @@ -1335,6 +1336,65 @@ describe('ChangeExtractorService', () => { expect(workerClient.computeTaskChanges).toHaveBeenCalledTimes(1); }); + it('ignores OpenCode delivery records that only mention related tasks', async () => { + tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'change-extractor-service-')); + setClaudeBasePathOverride(tmpDir); + await writeTaskFile(tmpDir, { displayId: 'abc12345', owner: 'bob' }); + const projectDir = path.join(tmpDir, 'project-dir'); + const projectPath = path.join(tmpDir, 'repo'); + await fs.mkdir(projectDir, { recursive: true }); + await fs.mkdir(projectPath, { recursive: true }); + await writeOpenCodeDeliveryLedger(tmpDir, { + taskId: 'related-task', + displayId: 'def67890', + memberName: 'bob', + }); + + const backfillOpenCodeTaskLedger = vi.fn(async () => { + throw new Error('related-only delivery record must not backfill'); + }); + const workerClient = { + isAvailable: vi.fn(() => true), + computeTaskChanges: vi.fn(async () => + makeTaskChangeResult(TASK_ID, { content: '', confidence: 'fallback' }) + ), + }; + + const service = new ChangeExtractorService( + { + getLogSourceWatchContext: vi.fn(async () => ({ + projectDir, + projectPath, + sessionIds: [], + })), + findLogFileRefsForTask: vi.fn(async () => []), + findMemberLogPaths: vi.fn(async () => []), + } as any, + { + parseBoundaries: vi.fn(async () => ({ + boundaries: [], + scopes: [], + isSingleTaskSession: true, + detectedMechanism: 'none' as const, + })), + } as any, + { getConfig: vi.fn(async () => ({ projectPath })) } as any, + undefined, + workerClient as any, + { backfillOpenCodeTaskLedger } as any, + { getMeta: vi.fn(async () => ({ providerId: 'opencode' })) } as any + ); + + const result = await service.getTaskChanges(TEAM_NAME, TASK_ID, { + owner: 'bob', + status: 'completed', + }); + + expect(result.files).toHaveLength(0); + expect(backfillOpenCodeTaskLedger).not.toHaveBeenCalled(); + expect(workerClient.computeTaskChanges).toHaveBeenCalledTimes(1); + }); + it('does not run OpenCode backfill for explicit non-OpenCode teams even if stale runtime files exist', async () => { tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'change-extractor-service-')); setClaudeBasePathOverride(tmpDir);