fix: a Discord message that re-posts a file is grouped, and joins the drop it repeats
CI and images / lint (push) Successful in 3s
CI and images / extension-version (push) Successful in 3s
CI and images / frontend-build (push) Successful in 19s
CI and images / backend-lint-and-test (push) Successful in 32s
CI and images / integration (push) Successful in 2m17s
CI and images / sign-extension (push) Successful in 4s
CI and images / build-agent (push) Successful in 6s
CI and images / build-web (push) Successful in 1m40s
CI and images / smoke-web (push) Successful in 53s
CI and images / promote (push) Skipped

Grouping read a message's images through primary_post_id alone, which only
the first message imported with a file holds. The backfill runs newest-first,
so the original message usually owned nothing: 101 of Yellowroom's messages
(mostly 2018-2020) could never be grouped. Every image they carried also sat
in another message (296 links, measured on the live instance).

_message_images unions ownership with provenance for the candidate query,
the drop seed, the member image links, and the merge pass. Nothing is
re-owned. A later drop carrying the very file an earlier one carries now
merges by a new same_image route, which nearest-neighbour could not see
because it skips the image itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
This commit is contained in:
2026-09-24 15:21:05 -04:00
co-authored by Claude Opus 5.5
parent 20cb813809
commit 7cc34b5724
2 changed files with 110 additions and 11 deletions
+61
View File
@@ -20,6 +20,7 @@ from sqlalchemy import select
from backend.app.models import (
Artist,
ImageProvenance,
ImageRecord,
Post,
PostAssociation,
@@ -251,3 +252,63 @@ async def test_a_checked_drop_is_not_examined_again(db):
for drop in await _drops(db, source):
assert drop.synthesis_details["trickle_checked"] is True
assert "nearest_message_ids" in drop.synthesis_details
# --- the same file posted twice -----------------------------------------------
# 101 of Yellowroom's messages were never grouped: every image they carried also
# sat in another message, which the newest-first backfill had made its owner.
async def _repost(db, artist, source, *, at, of, text=None):
"""A message carrying a file another message already owns."""
n = next(_n)
post = Post(source_id=source.id, artist_id=artist.id,
external_post_id=f"msg-{n}", post_date=at, description=text)
db.add(post)
await db.flush()
image_id = (await db.execute(
select(ImageRecord.id).where(ImageRecord.primary_post_id == of.id)
)).scalar_one()
db.add(ImageProvenance(image_record_id=image_id, post_id=post.id, source_id=source.id))
await db.flush()
return post
@pytest.mark.asyncio
async def test_a_message_that_only_reposts_an_image_is_still_grouped(db):
artist, source = await _seed(db, "repost-artist")
later = await _message(db, artist, source, at=T0, angle=0.0)
original = await _repost(db, artist, source, at=T0 - timedelta(days=400), of=later,
text="first posted here")
await db.commit()
await group_source(db, source, max_distance=0.10, window_minutes=60)
await db.commit()
await db.refresh(original)
assert original.absorbed_by_post_id is not None
# Grouping links, it never re-owns: the image keeps its primary post.
owner = (await db.execute(
select(ImageRecord.primary_post_id).join(
ImageProvenance, ImageProvenance.image_record_id == ImageRecord.id,
).where(ImageProvenance.post_id == original.absorbed_by_post_id)
)).scalar_one()
assert owner == later.id
@pytest.mark.asyncio
async def test_the_same_file_posted_a_day_apart_is_one_drop(db):
"""The live pair: 32554 (Aug 25) and 32553 (Aug 26) carry one file.
Nearest-neighbour cannot see it — it skips the image itself."""
artist, source = await _seed(db, "twice-artist")
second = await _message(db, artist, source, at=T0 + timedelta(days=1), angle=0.0)
first = await _repost(db, artist, source, at=T0, of=second)
await db.commit()
await _group_then_merge(db, source)
(drop,) = await _drops(db, source)
assert set(drop.synthesis_details["member_post_ids"]) | {
m for r in drop.synthesis_details.get("merged", []) for m in r["message_ids"]
} >= {first.id, second.id}
assert drop.synthesis_details["merged"][0]["route"] == "same_image"