Image 82045 had ImageProvenance rows pointing at BOTH post 5411 (the keep) AND post 14147 (the drop) — legitimately, because the same image was attached to two different Posts that we're now merging. When the migration tried UPDATE image_provenance SET post_id = 5411 WHERE post_id = 14147, the new row (82045, 5411) collided with the existing one.
My prior code did "UPDATE then dedupe-DELETE" but uq_image_provenance_image_post (alembic 0021) fires row-by-row during the UPDATE — the after-the-fact dedupe never runs.
Fix
Pre-DELETE image_provenance rows under drop whose image_record_id already has a provenance under keep. Those rows are redundant — the keep-side provenance already represents that (image, post) link. Then the remaining drop-side provenance rows get UPDATEd cleanly.
Also includes the dev-only ruff B007 fix (eee1077) and the cache-step removal (1803a09).
Test plan
Deploy :latest. Alembic should apply 0022 cleanly through all three collision patterns now (Post epid collision case A, Post epid collision case B, ImageProvenance pre-existing pair).
## Summary
Third hotfix for v26.05.26.1's alembic 0022 migration. Same row-by-row UNIQUE pattern as v26.05.26.2/3, just one level deeper.
### What broke this time
```
IntegrityError: duplicate key (image_record_id, post_id)=(82045, 5411) already exists
[parameters: {'keep': 5411, 'drop_': 14147}]
```
Image 82045 had `ImageProvenance` rows pointing at BOTH post 5411 (the keep) AND post 14147 (the drop) — legitimately, because the same image was attached to two different Posts that we're now merging. When the migration tried `UPDATE image_provenance SET post_id = 5411 WHERE post_id = 14147`, the new row (82045, 5411) collided with the existing one.
My prior code did "UPDATE then dedupe-DELETE" but `uq_image_provenance_image_post` (alembic 0021) fires row-by-row during the UPDATE — the after-the-fact dedupe never runs.
### Fix
Pre-DELETE `image_provenance` rows under `drop` whose `image_record_id` already has a provenance under `keep`. Those rows are redundant — the keep-side provenance already represents that (image, post) link. Then the remaining drop-side provenance rows get UPDATEd cleanly.
Also includes the dev-only ruff B007 fix (`eee1077`) and the cache-step removal (`1803a09`).
## Test plan
- [ ] Deploy `:latest`. Alembic should apply 0022 cleanly through all three collision patterns now (Post epid collision case A, Post epid collision case B, ImageProvenance pre-existing pair).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Third hotfix for v26.05.26.1's alembic 0022 migration. Same row-by-row UNIQUE pattern as v26.05.26.2/3, just one level deeper.
What broke this time
Image 82045 had
ImageProvenancerows pointing at BOTH post 5411 (the keep) AND post 14147 (the drop) — legitimately, because the same image was attached to two different Posts that we're now merging. When the migration triedUPDATE image_provenance SET post_id = 5411 WHERE post_id = 14147, the new row (82045, 5411) collided with the existing one.My prior code did "UPDATE then dedupe-DELETE" but
uq_image_provenance_image_post(alembic 0021) fires row-by-row during the UPDATE — the after-the-fact dedupe never runs.Fix
Pre-DELETE
image_provenancerows underdropwhoseimage_record_idalready has a provenance underkeep. Those rows are redundant — the keep-side provenance already represents that (image, post) link. Then the remaining drop-side provenance rows get UPDATEd cleanly.Also includes the dev-only ruff B007 fix (
eee1077) and the cache-step removal (1803a09).Test plan
:latest. Alembic should apply 0022 cleanly through all three collision patterns now (Post epid collision case A, Post epid collision case B, ImageProvenance pre-existing pair).🤖 Generated with Claude Code