Skip to content

Dedupe duplicated conversation participants instead of quarantining the snapshot - #160

Merged
MaxGhenis merged 2 commits into
mainfrom
fix/dedupe-conversation-participants
Aug 1, 2026
Merged

Dedupe duplicated conversation participants instead of quarantining the snapshot#160
MaxGhenis merged 2 commits into
mainfrom
fix/dedupe-conversation-participants

Conversation

@MaxGhenis

Copy link
Copy Markdown
Owner

What

Google conversation snapshots can list the same participant identity twice. Live occurrence 2026-08-01 ~04:54 ET: a burst of 8 conversation snapshots arrived on google-primary; 3 of them (revisions of 3 group threads) carried the account's own number twice in the roster and were quarantined by refreshConversation's duplicate-identity check. Their roster/title updates were silently dropped, and every future revision of those groups would quarantine the same way.

Diagnosed by replaying the live inbox frames (from a read-only copy of the v2 store) through the real worker apply path: exact error conversation participant identity "+1650…" is duplicated on exactly the 3 quarantined frames; all 8 apply cleanly with this fix. No messages were involved — conversation metadata only.

The fix

Identical natural key (kind + canonical) means identical identity, so the second entry can only be a duplicate — keep the first, log a warning, apply the snapshot. Quarantining the whole snapshot over a benign roster artifact permanently drops metadata the next revision won't restore.

Verification

  • New regression test TestWorkerConversationSnapshotDedupesDuplicateParticipants replays the duplicated-roster shape through the worker-path harness: snapshot applies, 2 participants after dedupe, first occurrence's display name kept.
  • GOWORK=off go test ./... green.
  • The 8 live frames (including the 3 quarantined) all apply under this change; unfixed main reproduces the 3 quarantines.

Notes

  • The error path had no test coverage before this.
  • Quarantined frames are terminal today (marked processed, never re-driven). A maintenance re-drive for previously quarantined frames is a possible follow-up; the next Google snapshot revision of each group will repopulate the metadata anyway once this deploys.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits August 1, 2026 06:24
…he snapshot

Google conversation snapshots can list the same participant identity twice
(observed live 2026-08-01: the account's own number appeared twice in three
group rosters). refreshConversation treated that as a deterministic apply
error, so the whole snapshot quarantined and those conversations' roster and
title updates were silently dropped — recurring on every future revision of
the same groups.

Identical natural key means identical identity: keep the first entry, log a
warning, and apply the snapshot. Regression test replays the duplicated-roster
shape through the worker path harness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per Sol's adversarial review: parse every occurrence's role up front; a
duplicate that agrees on membership state (role + active) is benign — keep the
first entry and OR-merge IsSelf (the live frames differ only there: Google
lists the account once as the self entry and once as a plain member). A
duplicate that disagrees on role/active is ambiguous and still refuses the
snapshot. The warning is now one aggregated line per snapshot instead of one
per duplicate.

Tests: both entry orders pin IsSelf=true on the merged identity plus kept
row role/active; a conflicting-duplicate case pins the quarantine
classification via processRecordResult.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MaxGhenis

Copy link
Copy Markdown
Owner Author

Addressed Sol's review (adversarial, gpt-5.6-sol ultra):

  1. Conflicting Role/Active duplicates now refuse the snapshot — dedupe only accepts state-agreeing duplicates. Live evidence drove the rule: the three quarantined frames' duplicate entries differ only in IsSelf (Google lists the account once as the self entry, once as a plain member; role/active identical), so IsSelf is OR-merged (same semantics as ensureIdentity) rather than treated as conflict — otherwise the fix would not fix the live frames.
  2. Warning bounded: one aggregated line per snapshot (deduplicated=N), not one per duplicate.
  3. Tests pin the full contract: both entry orders (IsSelf surfaces on the kept identity either way), kept row role/active, first-occurrence display name, and a conflicting-duplicate case asserting quarantine classification through processRecordResult.

All 8 live frames (including the 3 quarantined) still apply under the revised rule; GOWORK=off go test ./... green.

@MaxGhenis
MaxGhenis merged commit 6c9399a into main Aug 1, 2026
5 checks passed
@MaxGhenis
MaxGhenis deleted the fix/dedupe-conversation-participants branch August 1, 2026 10:43
mwhobrey pushed a commit to mwhobrey/om-tui that referenced this pull request Aug 24, 2026
…he snapshot (MaxGhenis#160)

* Dedupe duplicated conversation participants instead of quarantining the snapshot

Google conversation snapshots can list the same participant identity twice
(observed live 2026-08-01: the account's own number appeared twice in three
group rosters). refreshConversation treated that as a deterministic apply
error, so the whole snapshot quarantined and those conversations' roster and
title updates were silently dropped — recurring on every future revision of
the same groups.

Identical natural key means identical identity: keep the first entry, log a
warning, and apply the snapshot. Regression test replays the duplicated-roster
shape through the worker path harness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Address review: conflict-aware dedupe, IsSelf OR-merge, bounded warning

Per Sol's adversarial review: parse every occurrence's role up front; a
duplicate that agrees on membership state (role + active) is benign — keep the
first entry and OR-merge IsSelf (the live frames differ only there: Google
lists the account once as the self entry and once as a plain member). A
duplicate that disagrees on role/active is ambiguous and still refuses the
snapshot. The warning is now one aggregated line per snapshot instead of one
per duplicate.

Tests: both entry orders pin IsSelf=true on the merged identity plus kept
row role/active; a conflicting-duplicate case pins the quarantine
classification via processRecordResult.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 6c9399a)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant