fc70d56afb
The original Phase 3 dropped the legacy global UNIQUE(tag.name) at the end, which meant Phase 1's prefix-strip UPDATEs ran with the old constraint still in place. On any DB that had a pre-existing bare-name row colliding with the stripped form of a prefixed row (e.g. 'character:Marcille Donato' stripping to 'Marcille Donato' where another 'Marcille Donato' already existed), the migration blew up with UniqueViolation on tag_name_key. Fix: drop tag_name_key at the very top of upgrade() so both phases operate against a uniqueness-free table. Add explicit collision handling to Phase 1 (mirrors the existing Phase 2 auto-merge): when the stripped name already matches an existing row under the shape of the incoming partial-index (same kind, or same kind+fandom_id for characters), reassign image_tags onto the winner, cascade-rename tag_reference_embedding, and delete the loser. Report now shows phase1_merged alongside phase1_renames. Backwards-compatible for DBs where the failing migration already rolled back — rerunning picks up the fix. The migration's transaction atomicity means the DB is still in pre-migration state after the earlier failure. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>