Stage 4: Deduplication
The fourth stage merges idea-level duplicates by embedding cosine similarity. This is where OKT solves one of the core problems: it collapses facts that mean the same thing despite different phrasing.
Entry point: (*DeduplicateFactsWorker).Work — backend/internal/taskmanager/tasks/deduplicate_facts.go:100.
This is where embedding distance maps the flow of ideas. The algorithm (commented at lines 84-99):
- Acquire
pg_advisory_xact_lock(hashtext(repository_id))(deduplicate_facts.go:144) — serializes per-repo dedup so concurrent sources don't interleave. - Load
new+stablefacts (ListFactsForDedup), sort UUID-ascending. - For each
newfact:qdrant.SearchSimilarByID(ctx, nfID, repoUUID, nfID, threshold, 1)(deduplicate_facts.go:206) — finds the nearest neighbor by cosine similarity in embedding space, excluding self, with score >=dedupCfg.Threshold.- Hit
stablefact -> mark thenewfact asto_delete; relink sources to the stable survivor. - Hit
newfact -> lexicographically-larger UUID loses; survivor inherits sources. - Hit
to_deletefact -> skip.
- Hit
mergeSources(deduplicate_facts.go:363): relinksfact_sources,fact_references(DeleteDuplicateFactReferences+RelinkFactReferences), andfact_concepts(RelinkFactConcepts) onto the winner. Dedup preserves all provenance — the survivor accumulates every source and sentence reference the loser had.- Promote surviving
new->stable(MarkFactsStableByRepo); update Qdrant payloads. - Chain out: enqueues
extract_concepts(deduplicate_facts.go:327).
Why this matters
Dedup is idea-level, not string-match. Two facts phrased differently but meaning the same thing merge. This means a fact from a Wikipedia article and a fact from a research paper saying the same thing become one fact with two sources. The embedding distance is what later allows mapping the flow of ideas across sources — you can trace how the same idea appears, propagates, and mutates across the corpus.
The fact lifecycle
new -> stable (after dedup, survived)
new -> to_delete (after dedup, lost to a duplicate)
to_delete -> deleted (after cleanup_facts, stage 6a chain-out)
cleanup_facts (enqueued by embed_concepts at embed_concepts.go:223) is the terminal cleanup that deletes to_delete facts from both Qdrant and Postgres.