You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(comments): legacy id-less comment rows superseded by attributed twins are never reconciled #266
#261/#262 added comment dedup: insert_comment matches incoming comments by threads_comment_id, and backfills a legacy id-less row (threads_comment_id IS NULL) only when no exact-id row exists yet.
That leaves a permanent class of orphaned rows: legacy id-less rows fetched before #262 that already have an attributed twin in the same post. Once the attributed row exists, every future fetch takes the exact-match branch and the id-less twin is never revisited — no cleanup, migration, or reconcile path exists for it.
Evidence (production, 2026-09-11)
Post 01a084a5-75e0-7383-a87c-ad00ef90d3cb (account guyonlelucon, published 2026-09-09):
Same text, byte-identical (verified). Live re-fetch on 2026-09-11 confirmed: rows stay at 2, the attributed row updates in place, the id-less row is ignored forever. Impact: the single real comment is double-counted in sentiment (2× negative) and any per-comment workflow (reply targeting, priority).
Proposed fix
One-time reconciliation at startup (alongside existing migrations) or as a migration step: delete id-less rows that are superseded — where an attributed row with the same (post_id, text) exists:
DELETEFROM comments
WHERE threads_comment_id IS NULLAND EXISTS (
SELECT1FROM comments c2
WHEREc2.post_id=comments.post_idANDc2.text=comments.textANDc2.threads_comment_idIS NOT NULL
);
Constraints to preserve (mirror #261's safety rules):
Only supersede when the attributed row's author matches or the legacy row is anonymous (author_username IS NULL), same as the existing backfill guard.
Never delete an id-less row that has reply_status != 'new' or stored reply_text — it carries workflow state the attributed twin lacks.
Log the number purged (tracing::info!) for observability.
Testing
Unit test: id-less row + attributed twin → purge removes exactly the id-less row.
Unit test: id-less row with reply_status != 'new' → NOT purged.
Unit test: id-less row without attributed twin → NOT purged (id-less is still the only row for Meta's standard-access behavior).
Activity