From eee107766e7a57f0005592088c12a7c6de2511bf Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 26 May 2026 18:03:11 -0400 Subject: [PATCH 1/2] fix(migration-0022): rename unused _epid loop var (ruff B007) Co-Authored-By: Claude Opus 4.7 (1M context) --- alembic/versions/0022_source_per_artist_platform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alembic/versions/0022_source_per_artist_platform.py b/alembic/versions/0022_source_per_artist_platform.py index f85b96a..db4e090 100644 --- a/alembic/versions/0022_source_per_artist_platform.py +++ b/alembic/versions/0022_source_per_artist_platform.py @@ -109,7 +109,7 @@ def upgrade() -> None: by_epid: dict = {} for epid, post_id, src_id in all_posts: by_epid.setdefault(epid, []).append((post_id, src_id)) - for epid, posts in by_epid.items(): + for _epid, posts in by_epid.items(): if len(posts) <= 1: continue # Prefer a Post already under canonical as the keep. From f3e8f30a8f226dd7e24840157215a50057c0af13 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 26 May 2026 18:05:49 -0400 Subject: [PATCH 2/2] =?UTF-8?q?fix(migration-0022):=20pre-DELETE=20collidi?= =?UTF-8?q?ng=20image=5Fprovenance=20rows=20before=20the=20UPDATE=20post?= =?UTF-8?q?=5Fid=20=E2=80=94=20same=20row-by-row=20UNIQUE=20pattern=20as?= =?UTF-8?q?=20the=20post-collision=20case,=20just=20one=20level=20deeper.?= =?UTF-8?q?=20When=20image=20X=20has=20provenance=20under=20both=20keep=20?= =?UTF-8?q?and=20drop,=20UPDATE=20drop=E2=86=92keep=20would=20fire=20uq=5F?= =?UTF-8?q?image=5Fprovenance=5Fimage=5Fpost=20on=20the=20row=20that'd=20c?= =?UTF-8?q?ollide=20with=20the=20existing=20(X,=20keep).=20Pre-delete=20th?= =?UTF-8?q?ose=20rows=20(their=20info=20is=20already=20represented=20by=20?= =?UTF-8?q?the=20keep-side=20provenance)=20before=20the=20UPDATE=20moves?= =?UTF-8?q?=20the=20rest.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- .../0022_source_per_artist_platform.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/alembic/versions/0022_source_per_artist_platform.py b/alembic/versions/0022_source_per_artist_platform.py index db4e090..d3e75dd 100644 --- a/alembic/versions/0022_source_per_artist_platform.py +++ b/alembic/versions/0022_source_per_artist_platform.py @@ -120,6 +120,25 @@ def upgrade() -> None: keep_id = posts[0][0] # already sorted by id ASC drop_ids = [p[0] for p in posts if p[0] != keep_id] for drop_id in drop_ids: + # Pre-delete image_provenance rows under drop_ whose + # image_record_id ALREADY has a provenance under keep — + # the UPDATE below would otherwise repoint them and + # trip uq_image_provenance_image_post (alembic 0021) + # row-by-row before any after-the-fact dedupe could + # run. Operator's v26.05.26.3 deploy 2026-05-26 tripped + # this at line 123. + conn.execute( + text(""" + DELETE FROM image_provenance + WHERE post_id = :drop_ + AND image_record_id IN ( + SELECT image_record_id FROM image_provenance + WHERE post_id = :keep + ) + """), + {"keep": keep_id, "drop_": drop_id}, + ) + # Now safe to repoint the survivors. conn.execute( text(""" UPDATE image_provenance SET post_id = :keep @@ -134,15 +153,6 @@ def upgrade() -> None: """), {"keep": keep_id, "drop_": drop_id}, ) - # Repointed provenance may now collide on - # uq_image_provenance_image_post (alembic 0021). Dedupe. - conn.execute(text(""" - DELETE FROM image_provenance ip1 - USING image_provenance ip2 - WHERE ip1.image_record_id = ip2.image_record_id - AND ip1.post_id = ip2.post_id - AND ip1.id > ip2.id - """)) conn.execute( text("DELETE FROM post WHERE id = :drop_"), {"drop_": drop_id},