fix(bluebubbles): dedupe updated-message follow-ups by base guid#75526
fix(bluebubbles): dedupe updated-message follow-ups by base guid#75526zqchris wants to merge 1 commit into
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. for the helper-level bug path: current main commits the base GUID and would claim a no-attachment Real behavior proof Next step before merge Security Review detailsBest possible solution: Land the BlueBubbles-owned dedupe change once required CI and maintainer review are satisfied, keeping the behavior local to the plugin and preserving late media updates. Do we have a high-confidence way to reproduce the issue? Yes for the helper-level bug path: current main commits the base GUID and would claim a no-attachment Is this the best way to solve the issue? Yes for the proposed patch boundary: the change stays in the BlueBubbles dedupe helper, uses the existing What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 95a1c915312a. |
aac0b74 to
f7f6df3
Compare
|
@clawsweeper re-review |
f7f6df3 to
5227bbe
Compare
5227bbe to
9264fd0
Compare
|
@clawsweeper re-review Updated this PR in response to the prior review:
|
|
Noting upstream change for triage: commit Per the related changelog entry: "remove the bundled BlueBubbles channel surface and docs, make `imsg` the supported iMessage setup path". The fix this PR carried is now obsolete on the channel-removal direction. Closing as obsolete unless the maintainers want it preserved as part of the BlueBubbles deprecation history. Happy to re-target the underlying behaviour at the new |
|
Closing per the upstream BlueBubbles plugin removal. Branch is preserved on the fork; happy to reopen + rebase if a maintainer prefers to land it as historical record. |
Summary
new-messageplus a follow-upupdated-messagefor the same GUID. The current dedupe key suffixes:updated, so once the agent has already replied to the base GUID, the no-attachment follow-up still passes the dedupe check and re-triggers a reply (especially after losing group chat context).updated-messageevents without attachments, after explicit-claim resolution check whether the base GUID is already committed (hasRecenton the base namespace). If so, returnduplicatebefore claiming the suffixed key. Late attachment-bearing updates for the same GUID stay claimable so attachment indexing still goes through.Real behavior proof
Behavior or issue addressed: BlueBubbles
new-message+ no-attachmentupdated-messagefollow-up for the same GUID re-entersclaimBlueBubblesInboundMessageand gets re-claimed, causing the agent to reply twice. Late attachment-bearing updates for the same GUID must still claim through (so attachment indexing is preserved).Real environment tested: Local OpenClaw checkout on macOS Darwin 25.4.0, Node 22, pnpm 10.33.2 against the rebased PR head
9264fd0206on top of upstream/main95a1c91531. The actual productionclaimBlueBubblesInboundMessageexported fromextensions/bluebubbles/src/inbound-dedupe.tsis invoked directly from anoderunner (pnpm exec tsx proof.ts) with redacted message GUIDs to walk the new-message → updated-message → updated-message-with-attachment sequence.Exact steps or command run after this patch:
pnpm install.proof.tsthat imports the productionclaimBlueBubblesInboundMessageand_resetBlueBubblesInboundDedupForTest, then walks the production code path: claim anew-messageGUID, claim a no-attachmentupdated-messagefollow-up for the same GUID, and finally claim an attachment-bearingupdated-messagefollow-up for the same GUID.pnpm exec tsx proof.tsand capture stdout.Evidence after fix (redacted runtime log excerpt + copied live
nodeconsole output):On plain upstream/main (
git checkout upstream/main -- extensions/bluebubbles/src/inbound-dedupe.ts extensions/bluebubbles/src/monitor-processing.ts && pnpm exec tsx proof.ts), the no-attachment updated-message GUID is NOT recognized as a duplicate — it claims through as a fresh inbound, which is exactly what re-triggers a reply in production traffic. After the patch, the same path returns"outcome":"duplicate"for the no-attachment follow-up, while the late attachment-bearing update still claims through.Observed result after fix: The
noderepro shows the production dedupe path now returnsduplicatefor the no-attachmentupdated-messagefollow-up of an already-committed base GUID, while attachment-bearing updates of the same GUID still claim through. This matches theclaimBlueBubblesInboundMessagesemantics thatmonitor-processing.tscalls for every inbound BlueBubbles webhook event.What was not tested: A paired live BlueBubbles → iMessage server roundtrip (would require a paired BlueBubbles deployment); the
noderepro above exercises the exact dedupe path the monitor invokes for every webhook event.Note on the prior review's "test typo" finding
ClawSweeper's previous review claimed that
extensions/bluebubbles/src/inbound-dedupe.test.ts:155callsclaimAndFinalize(newMessage(...))withoutaccountId. Re-reading the file at the same SHA the review cited (5227bbef217a) shows both calls in the overlong-guid test passaccountIdcorrectly:Both targeted tests pass under
pnpm test. The finding appears to be a parse-level mismatch by the reviewer; the helper-call shape is intact.Change Type
Scope
Linked Issue/PR
Root Cause
extensions/bluebubbles/src/inbound-dedupe.tsalways suffixes:updatedforupdated-messageevents; the suffixed key has never been claimed even when the basenew-messagewas already processed, so the follow-up re-enters the agent.Regression Test Plan
extensions/bluebubbles/src/inbound-dedupe.test.ts.treats no-attachment updated-message follow-ups as duplicates once the base GUID committedpreserves late attachment-bearing updated-message processing after the base committedlets an updated-message-first webhook through when the base GUID was never committedUser-visible / Behavior Changes
Same-GUID
new-message+ no-attachmentupdated-messageno longer produces a duplicate reply. Late attachment-bearing updates for the same GUID still process normally.Security Impact
Verification
pnpm test extensions/bluebubbles/src/inbound-dedupe.test.ts— 16 tests passing on PR head; the new regressions fail on plain upstream/main when onlyinbound-dedupe.ts+monitor-processing.tsare reverted.pnpm check:changed --base upstream/mainexit 0.Compatibility / Migration