Skip to content

Repair anchorless iMessage watch payload routing#86150

Merged
omarshahine merged 1 commit into
openclaw:mainfrom
omarshahine:maint/imessage-anchorless-watch-repair-84470
May 25, 2026
Merged

Repair anchorless iMessage watch payload routing#86150
omarshahine merged 1 commit into
openclaw:mainfrom
omarshahine:maint/imessage-anchorless-watch-repair-84470

Conversation

@omarshahine

@omarshahine omarshahine commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • recover malformed anchorless iMessage watch payloads by GUID before debounce/routing
  • drop unrecoverable anchorless payloads fail-closed instead of routing them to the sender DM
  • add unit coverage for the repair/drop helper plus a monitor-level route assertion that the final dispatch target is the recovered group

Why

A group link-preview watch payload can arrive with chat_id=0, empty conversation identifiers, and is_group=false. Without a pre-routing repair/drop guard, OpenClaw can treat the payload as a direct message and send the reply to imessage:<sender> instead of the originating group.

Closes #84470.
Refs #84503.

Credit: this follows the bug report and mitigation direction from @zqchris and the contributor work in #84503 by @zhangguiping-xydt, but reapplies the fix on current main with broader anchorless detection and route-level assertions.

Verification

  • node scripts/run-vitest.mjs extensions/imessage/src/monitor/conversation-repair.test.ts extensions/imessage/src/monitor.last-route.test.ts extensions/imessage/src/monitor.watch-subscribe-retry.test.ts extensions/imessage/src/monitor.gating.test.ts
  • node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo
  • pnpm lint:extensions:bundled
  • pnpm exec oxfmt --check CHANGELOG.md extensions/imessage/src/monitor/conversation-repair.ts extensions/imessage/src/monitor/conversation-repair.test.ts extensions/imessage/src/monitor/monitor-provider.ts extensions/imessage/src/monitor.last-route.test.ts
  • git diff --check

Live iMessage proof

Ran on the Lobster Mac against the live imsg rpc bridge from this PR branch (c89bbe2862). The probe used chats.list and messages.history, built a synthetic anchorless payload from a real group-history message, and called the same repair helper added by this PR.

Repair proof, redacted:

{
  "ok": true,
  "sourceWasAnchorless": true,
  "repairedWasAnchorless": false,
  "groupCount": 3,
  "selectedChatId": 18,
  "repairedChatId": 18,
  "sourceGuid": "A3294...CCF2E5",
  "repairedChatGuid": "any;+...b7e6aa",
  "repairedIsGroup": true,
  "participantsRecovered": 8
}

Fail-closed proof with a non-existent anchorless GUID, redacted:

{
  "ok": true,
  "sourceWasAnchorless": true,
  "result": "dropped",
  "diagnosticCount": 1
}

Re-verified locally with a fast-patched gateway (Lobster Mac)

Re-ran end-to-end against a different real group (3 participants) and exercised the wired-in code path on a live gateway.

Harness against the live imsg rpc bridge (3 cases):

Case Input Result
Positive Real recent group message, anchor fields stripped, real guid preserved Recovered chat_id, chat_guid, is_group=true, 3 participants in 2 RPC calls (chats.list + 1 messages.history)
Negative Anchorless payload with a fabricated bogus GUID Dropped (null) after scanning 10 chats; no fallback DM to sender
Pass-through Payload with valid anchor Returned identical; 0 RPC calls — no overhead on the happy path

Live gateway regression — fast-patched this branch's dist into the running gateway, then sent a real iPhone message into a 3-person test group:

imessage inbound: chatId=13 from=imessage:group:13 len=114 preview="…"
message received: channel=imessage chatId=chat_id:13 messageId=299
  sessionKey=agent:lobster:imessage:group:13 source=dispatchInboundMessage
message queued: queueDepth=1
session state: prev=idle new=processing
session turn created: runId=… channel=imessage
agent harness selected

Anchored payload took the repair wrapper's pass-through path — no recovered anchorless / dropping anchorless log entries — and end-to-end routing was unchanged.

@openclaw-barnacle openclaw-barnacle Bot added channel: imessage Channel integration: imessage size: M maintainer Maintainer-authored PR labels May 24, 2026
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed May 24, 2026, 7:05 PM ET / 23:05 UTC.

Summary
The branch adds an iMessage monitor conversation-anchor repair helper, calls it before watch debounce/routing, adds repair/drop and final-route regression tests, and updates the changelog.

PR surface: Source +187, Tests +250, Docs +1. Total +438 across 5 files.

Reproducibility: yes. source inspection plus the linked live report give a high-confidence reproduction path: current main accepts the chat_id=0, blank-anchor, is_group=false payload and can route it as a sender DM. I did not run live iMessage tests in this read-only review.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Get explicit iMessage owner approval for the fail-closed default and bounded recovery search.
  • If the owner wants stronger transport proof, add a redacted replay or monitor log showing final group dispatch or fail-closed drop.

Mantis proof suggestion
A redacted real or replayed iMessage watch-path proof would still materially help maintainers evaluate the routing-boundary behavior before merge. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

visual task: verify a redacted iMessage anchorless watch payload is repaired to the group route or dropped without sender-DM delivery.

Risk before merge

  • The PR intentionally changes malformed iMessage handling from possible sender-DM routing to fail-closed drop when GUID recovery cannot prove a conversation, which is safer for privacy but can suppress inbound events for users whose bridge emits unrecoverable anchorless payloads.
  • The recovery path adds bounded chats.list and messages.history RPCs before routing malformed payloads; maintainers should accept the default limits and latency tradeoff for real iMessage installations.
  • The protected maintainer label means this privacy-sensitive iMessage routing change needs explicit human owner review even though the patch has no discrete blocking code finding.

Maintainer options:

  1. Owner-approve the fail-closed default (recommended)
    Merge after an iMessage owner explicitly accepts dropping unrecoverable anchorless payloads as the safer default than any sender-DM fallback.
  2. Tighten compatibility before merge
    If owners are not comfortable with the default drop behavior or search bounds, add the chosen compatibility path and focused tests before landing.
  3. Pause for bridge-side resolution
    If maintainers want openclaw/imsg to stop emitting malformed watch payloads first, pause this PR and keep the linked issue as the OpenClaw-side safety requirement.

Next step before merge
Human iMessage owner review is needed for the protected maintainer label and intentional fail-closed routing behavior; there is no narrow automated repair to queue.

Security
Cleared: The diff adds no dependencies, workflows, credential handling, or supply-chain surface; the security-sensitive routing change is an intended privacy hardening tradeoff.

Review details

Best possible solution:

Land the focused iMessage plugin fix after iMessage owner review accepts the fail-closed default, bounded recovery search, live proof, and regression coverage.

Do we have a high-confidence way to reproduce the issue?

Yes, source inspection plus the linked live report give a high-confidence reproduction path: current main accepts the chat_id=0, blank-anchor, is_group=false payload and can route it as a sender DM. I did not run live iMessage tests in this read-only review.

Is this the best way to solve the issue?

Mostly yes: repairing by GUID before debounce/routing is the narrowest maintainable fix boundary for the observed bug. The remaining decision is whether maintainers accept fail-closed drop and the bounded search limits as the default compatibility behavior.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 3c8d101f5a85.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes redacted live output from a real imsg rpc setup on the PR branch showing both GUID-based repair to a group anchor and fail-closed drop for an unrecoverable anchorless GUID.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The updated PR body includes redacted live output from a real imsg rpc setup on the PR branch showing both GUID-based repair to a group anchor and fail-closed drop for an unrecoverable anchorless GUID.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The PR addresses a real iMessage routing/privacy bug where a group-triggered reply can be sent to a sender DM.
  • merge-risk: 🚨 compatibility: The diff changes runtime fallback behavior for malformed iMessage watch payloads and can drop events current main might route.
  • merge-risk: 🚨 message-delivery: The patch directly changes iMessage target selection and introduces a fail-closed path for unrecovered inbound messages.
  • merge-risk: 🚨 security-boundary: The affected behavior crosses a conversation boundary by potentially replying from a group context into a private sender DM.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The updated PR body includes redacted live output from a real imsg rpc setup on the PR branch showing both GUID-based repair to a group anchor and fail-closed drop for an unrecoverable anchorless GUID.
  • proof: sufficient: Contributor real behavior proof is sufficient. The updated PR body includes redacted live output from a real imsg rpc setup on the PR branch showing both GUID-based repair to a group anchor and fail-closed drop for an unrecoverable anchorless GUID.
Evidence reviewed

PR surface:

Source +187, Tests +250, Docs +1. Total +438 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 189 2 +187
Tests 2 250 0 +250
Docs 1 1 0 +1
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 440 2 +438

What I checked:

  • Repository policy requires owner review for this kind of change: Root policy treats provider routing, channel delivery, fallback behavior, fail-closed changes, and security-sensitive routing as compatibility-sensitive merge risk, and requires full policy plus scoped AGENTS review. (AGENTS.md:24, 8ae997749d3a)
  • Scoped plugin boundary is appropriate: extensions/AGENTS.md keeps bundled plugin runtime behavior inside the owning plugin boundary and does not require a core API change for this local iMessage repair. (extensions/AGENTS.md:27, 8ae997749d3a)
  • Current main still enqueues parsed watch messages without repair: On current origin/main, handleMessage parses the RPC payload and immediately enqueues it into the inbound debouncer; there is no pre-routing GUID recovery or fail-closed guard in this path. (extensions/imessage/src/monitor/monitor-provider.ts:875, 3c8d101f5a85)
  • Current main can classify the reported shape as direct: Inbound routing derives isGroup from message.is_group or explicit config, so the reported chat_id=0/is_group=false payload can proceed through direct routing rather than the group guard. (extensions/imessage/src/monitor/inbound-processing.ts:395, 3c8d101f5a85)
  • No existing main implementation supersedes the PR: A current-main symbol search found no repairIMessageConversationAnchor, isIMessageAnchorless, or conversation-repair implementation under the iMessage plugin or core source. (3c8d101f5a85)
  • PR repair helper implements detect, recover, and fail-closed behavior: The PR adds isIMessageAnchorless and repairIMessageConversationAnchor, using chats.list plus messages.history by GUID, then returning null when the anchor cannot be recovered. (extensions/imessage/src/monitor/conversation-repair.ts:42, c89bbe2862fc)

Likely related people:

  • omarshahine: Authored recent merged iMessage approval-routing work touching monitor-provider.ts and inbound-processing.ts, and this PR also credits a current-main reapplication rather than only proposing a branch. (role: recent area contributor; confidence: high; commits: 5c7980fa1132, c89bbe2862fc; files: extensions/imessage/src/monitor/monitor-provider.ts, extensions/imessage/src/monitor/inbound-processing.ts)
  • TurboTheTurtle: Recent iMessage catchup/live-cursor work changed monitor-provider.ts and monitor.last-route.test.ts around the same live notification and cursor flow this repair touches. (role: recent adjacent contributor; confidence: medium; commits: 102555c6e027; files: extensions/imessage/src/monitor/monitor-provider.ts, extensions/imessage/src/monitor.last-route.test.ts, extensions/imessage/src/monitor/catchup-bridge.ts)
  • Ayaan Zaidi: Prior iMessage inbound-processing fixes touched echo/self-chat behavior in the same routing decision surface that determines direct versus group dispatch. (role: inbound routing contributor; confidence: medium; commits: e3e2a19ab7f1, 1ee4a1606e9a, 4a5885df3a36; files: extensions/imessage/src/monitor/inbound-processing.ts)
  • zqchris: Filed the linked live report with the malformed payload shape and linked a local mitigation commit that established the recover-or-drop direction. (role: bug reporter and mitigation source; confidence: medium; commits: 7c56a6bba852; files: extensions/imessage/src/monitor/conversation-repair.ts, extensions/imessage/src/monitor/monitor-provider.ts, extensions/imessage/src/monitor/inbound-processing.ts)
  • zhangguiping-xydt: Opened the earlier unmerged anchorless-payload repair PR that this PR explicitly credits and reapplies on current main with broader assertions. (role: prior related PR contributor; confidence: medium; commits: ee15ec620f4b; files: extensions/imessage/src/monitor/conversation-repair.ts, extensions/imessage/src/monitor/monitor-provider.ts, extensions/imessage/src/monitor.watch-subscribe-retry.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels May 24, 2026
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

✨ Hatched: 🥚 common Velvet Proofling

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.

Rarity: 🥚 common.
Trait: sniffs out flaky tests.
Image traits: location green-check meadow; accessory review stamp; palette moss green and polished brass; mood sparkly; pose pointing at a small proof artifact; shell matte ceramic shell; lighting gentle morning glow; background smooth stones and checkmarks.
Share on X: post this hatch
Copy: My PR egg hatched a 🥚 common Velvet Proofling in ClawSweeper.

What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@omarshahine omarshahine force-pushed the maint/imessage-anchorless-watch-repair-84470 branch from ff1d9da to c89bbe2 Compare May 24, 2026 22:45
@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels May 24, 2026
@omarshahine

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@omarshahine omarshahine marked this pull request as ready for review May 24, 2026 22:58
@clawsweeper

clawsweeper Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 24, 2026
@omarshahine

Copy link
Copy Markdown
Contributor Author

Local verification on Lobster Mac (macOS, real iMessage account)

Unit testspnpm test conversation-repair.test.ts monitor.last-route.test.ts → 13/13 passing.

Live imsg rpc harness against real chat.db (3 cases):

Case Anchorless input Result
Positive Stripped chat_id/chat_guid/chat_identifier from a real recent group message; preserved real guid Recovered chat_id=13, chat_guid=any;+;72bc206e…, is_group=true, 3 participants in 2 RPC calls (chats.list + 1 messages.history)
Negative Anchorless payload with a fabricated bogus GUID Dropped (returned null) after scanning 10 chats; no false attribution back to sender
Pass-through Payload with valid anchor Returned identical; 0 RPC calls (no overhead on the happy path)

Live gateway regression — fast-patched the PR build into the running gateway, then sent a real iPhone message into a 3-person group chat:

imessage inbound: chatId=13 from=imessage:group:13 len=114
  preview="[iMessage Lobster Test Group id:13 …] ***: Lobster how is your weekend going?"
message received: channel=imessage chatId=chat_id:13 messageId=299
  sessionKey=agent:lobster:imessage:group:13 source=dispatchInboundMessage
message queued: sessionKey=agent:lobster:imessage:group:13 queueDepth=1
session state: prev=idle new=processing
session turn created: runId=… channel=imessage
agent harness selected

The anchored payload took the repair wrapper's pass-through path — no recovered anchorless / dropping anchorless log entries — and normal routing flow was unchanged end-to-end.

@omarshahine

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

Re-review progress:

@omarshahine

Copy link
Copy Markdown
Contributor Author

Maintainer verification before merge:

  • Head: c89bbe2
  • Base checked by GitHub: 8ae9977
  • Mergeability: MERGEABLE
  • ClawSweeper: proof: sufficient, status: ready for maintainer look; second @clawsweeper re-review requested after the updated real-machine proof.
  • GitHub checks: 69 passed, 27 skipped, 0 failed, 0 pending.

Local proof run from the PR branch:

node scripts/run-vitest.mjs extensions/imessage/src/monitor/conversation-repair.test.ts extensions/imessage/src/monitor.last-route.test.ts extensions/imessage/src/monitor.watch-subscribe-retry.test.ts extensions/imessage/src/monitor.gating.test.ts
node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.extensions.test.json --incremental --tsBuildInfoFile .artifacts/tsgo-cache/extensions-test.tsbuildinfo
pnpm lint:extensions:bundled
pnpm exec oxfmt --check CHANGELOG.md extensions/imessage/src/monitor/conversation-repair.ts extensions/imessage/src/monitor/conversation-repair.test.ts extensions/imessage/src/monitor/monitor-provider.ts extensions/imessage/src/monitor.last-route.test.ts
git diff --check

Real-machine proof on Lobster Mac:

  • Live imsg rpc against real chat.db: stripped anchors from a real recent group message, preserved its GUID, and recovered the correct group chat id/GUID and participants.
  • Negative live imsg rpc case: fabricated anchorless GUID returned null, proving fail-closed behavior rather than sender-DM routing.
  • Pass-through live case: valid anchored payload returned unchanged with no repair lookup overhead.
  • Live gateway regression: real iPhone message into a 3-person group routed to agent:lobster:imessage:group:13; no repair/drop diagnostics on the normal anchored path.

Known proof note: the exact malformed bridge event is rare; proof uses real iMessage bridge/chat history plus a synthetic anchorless transform matching the reported failure shape, and a real anchored gateway group-message route check.

@omarshahine omarshahine merged commit f37fbc9 into openclaw:main May 25, 2026
175 of 183 checks passed
@omarshahine omarshahine deleted the maint/imessage-anchorless-watch-repair-84470 branch May 25, 2026 04:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: imessage Channel integration: imessage maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: M status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iMessage group link-preview watch payload can lose conversation anchor and route reply to DM

1 participant