import Database from 'better-sqlite3'; /** * entity-name-dedup.ts — one-time destructive migration for the v0.26 * canonical-key fix. * * Pre-v0.26 `upsertEntity` keyed on (LOWER(name), entity_type, namespace), * so the same real-world entity accumulated one row per extractor-assigned * type ("NeuroMCP" as project + concept + tool). v0.26 drops entity_type * from the key for NEW writes; this pass merges the duplicates that already * exist. * * Winner selection per duplicate group (same LOWER(TRIM(name)) + namespace): * most memory_entities links wins; ties go to the oldest row (it has the * longest provenance). Losers' memory links and relations are re-pointed * to the winner, then the losers are soft-deleted (tombstoned, not * physically removed — reversible by hand if a merge was wrong). * * DESTRUCTIVE in the sense that the merge collapses rows; always run with * dryRun: true first and review the proposed plan (the CLI wrapper in * scripts/ defaults to dry-run). */ interface NameDedupPair { readonly key: string; readonly winnerId: string; readonly winnerName: string; readonly winnerType: string; readonly loserId: string; readonly loserType: string; } interface NameDedupResult { readonly proposed: readonly NameDedupPair[]; readonly merged: number; readonly relinked_memory_entities: number; readonly relinked_relations: number; } declare function mergeDuplicateEntityNames(db: Database.Database, options?: { dryRun?: boolean; }): NameDedupResult; export { type NameDedupPair, type NameDedupResult, mergeDuplicateEntityNames };