diff --git a/src/review-agent/__tests__/deterministic-publish-adapter.test.ts b/src/review-agent/__tests__/deterministic-publish-adapter.test.ts index 5534c16..2ca279a 100644 --- a/src/review-agent/__tests__/deterministic-publish-adapter.test.ts +++ b/src/review-agent/__tests__/deterministic-publish-adapter.test.ts @@ -114,4 +114,59 @@ describe('applyDeterministicPublishAdapter deduplication', () => { .slice(0, 24); expect(legacy).not.toBe(modern); }); + + it('preserves published=true when migrating from legacy to modern fingerprint', async () => { + const legacy = createHash('sha256') + .update('security:src/auth.ts:42:SQL injection') + .digest('hex') + .slice(0, 24); + + const store = { + getRunDetails: async () => ({ + findings: [ + { + id: 'old-1', + runId: 'run-migrate', + category: 'security', + severity: 'high', + path: 'src/auth.ts', + line: 42, + title: 'SQL injection', + detail: 'Use parameterized queries.', + evidence: '', + suggestion: '', + confidence: 0.9, + fingerprint: legacy, + published: true, + }, + ], + comments: [], + }), + addFindings: async () => {}, + addCommentRecord: async () => {}, + } as any; + + const result = await applyDeterministicPublishAdapter({ + store, + runId: 'run-migrate', + submission: { + summaryMarkdown: 'Found SQL injection.', + findings: [ + { + category: 'security', + severity: 'high', + path: 'src/auth.ts', + line: 42, + title: 'SQL injection', + detail: 'Use parameterized queries.', + evidence: '', + suggestion: '', + confidence: 0.9, + fingerprint: '', + }, + ], + }, + }); + expect(result.findings[0].published).toBe(true); + }); }); diff --git a/src/review-agent/deterministic-publish-adapter.ts b/src/review-agent/deterministic-publish-adapter.ts index 15442a3..ca4bd91 100644 --- a/src/review-agent/deterministic-publish-adapter.ts +++ b/src/review-agent/deterministic-publish-adapter.ts @@ -139,17 +139,20 @@ export async function applyDeterministicPublishAdapter(params: { const normalized = dedupeFindings(params.submission.findings); const details = await params.store.getRunDetails(params.runId); const existingPublished = new Map(); + function rememberPublished(key: string, published: boolean): void { + existingPublished.set(key, (existingPublished.get(key) ?? false) || published); + } for (const finding of details?.findings ?? []) { - existingPublished.set(finding.fingerprint, finding.published); + const modern = buildFingerprint(finding.category, finding.path, finding.line, finding.title); const legacy = buildLegacyFingerprint( finding.category, finding.path, finding.line, finding.title ); - if (legacy !== finding.fingerprint) { - existingPublished.set(legacy, finding.published); - } + rememberPublished(finding.fingerprint, finding.published); + rememberPublished(modern, finding.published); + rememberPublished(legacy, finding.published); } const findings: Finding[] = normalized.map((finding) => ({