fix(review-agent): complete fingerprint migration — dual-index all three keys

- existingPublished now indexes finding.fingerprint, modern, and legacy
- rememberPublished merges published status with true-wins semantics
- regression test: legacy published finding stays published after migration to modern fingerprint
This commit is contained in:
jeffusion
2026-05-27 00:04:16 +08:00
committed by Jeffusion
parent 27f4ac6a18
commit 2ee9f570c4
2 changed files with 62 additions and 4 deletions

View File

@@ -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);
});
});

View File

@@ -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<string, boolean>();
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) => ({