Skip to content

fix: prevent duplicate A2A replies after message delivery#111012

Merged
steipete merged 1 commit into
mainfrom
codex/a2a-message-mirror-dedupe
Jul 18, 2026
Merged

fix: prevent duplicate A2A replies after message delivery#111012
steipete merged 1 commit into
mainfrom
codex/a2a-message-mirror-dedupe

Conversation

@steipete

Copy link
Copy Markdown
Contributor

Closes #110996

What Problem This Solves

Fixes an issue where an agent-to-agent reply delivered through the message tool could appear twice when a gateway plugin returned its provider message ID inside a nested message object. The first send succeeded, but empty-response recovery treated it as invisible and asked the agent to send again.

Why This Change Was Made

Recognize a non-empty nested provider message.id as the same delivery evidence as existing top-level message, poll, and receipt IDs. Existing dry-run, suppressed, no-op, and error guards still run before the result can complete a message-tool-only source reply.

User Impact

Agent-to-agent visible replies through gateway-backed channel plugins are delivered once instead of being repeated by empty-response recovery.

Evidence

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS maintainer Maintainer-authored PR labels Jul 18, 2026
@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: fix: prevent duplicate A2A replies after message delivery This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@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. P1 High-priority user-facing bug, regression, or broken workflow. labels Jul 18, 2026
@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 18, 2026, 7:27 PM ET / 23:27 UTC.

Summary
The branch treats a non-empty nested gateway-provider message.id as successful message-delivery evidence so empty-response recovery does not duplicate an A2A reply.

PR surface: Source +5, Tests +30. Total +35 across 3 files.

Reproducibility: yes. The linked issue gives a focused current-main reproduction using a2a-message-tool-mirror-dedupe, with one sessions_send, two successful message sends, and two requester-visible markers before this patch.

Review metrics: 1 noteworthy metric.

  • Delivery evidence expanded: 1 nested receipt signal added. This single condition controls whether empty-response recovery requests a second user-visible message send.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster ✨ media proof bonus
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:

  • none.

Risk before merge

  • [P1] Delivery classification is merge-sensitive: if a gateway plugin can emit a nested message.id without having actually delivered a visible message, recovery would be suppressed; the linked QA contract supports the intended shape, but the protected maintainer review should confirm that contract is shared by the affected gateway path.

Maintainer options:

  1. Confirm the receipt semantics, then merge (recommended)
    Approve once the responsible reviewer agrees that a nested provider message.id is emitted only for successful visible delivery in the affected gateway contract.
  2. Strengthen the negative receipt contract
    Before merge, add a focused case only if the gateway can return nested message metadata for non-delivered, suppressed, or failed sends.

Next step before merge

  • [P2] No mechanical repair is identified; a responsible reviewer must decide whether the nested gateway provider ID is an authoritative delivery receipt before merge.

Maintainer decision needed

  • Question: Should the gateway result contract treat a non-empty nested details.message.id as authoritative successful-delivery evidence for A2A empty-response recovery?
  • Rationale: The patch intentionally expands the condition that prevents recovery from requesting another visible send, so a maintainer must accept the gateway receipt semantics before landing a message-delivery behavior change.
  • Likely owner: steipete — The supplied context identifies this contributor as the only concrete routing candidate for the active A2A QA scenario and classifier path; confidence is limited because repository history could not be inspected.
  • Options:
    • Accept the nested receipt contract (recommended): Approve the narrow classifier extension because the linked QA scenario shows this gateway result shape only after a successful visible send.
    • Require contract clarification first: Ask for owner-level confirmation or an additional negative contract test if nested message IDs may exist before visible delivery completes.

Security
Cleared: The exact-head diff only adjusts in-process delivery-result classification and focused tests; it adds no dependency, workflow, secret, permission, artifact, or package-resolution surface.

Review details

Best possible solution:

Keep one canonical delivery-evidence classifier, retain both focused classifier and terminal-recovery coverage, and merge only after the responsible reviewer confirms that nested provider message.id is a successful-delivery receipt for the gateway result contract.

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

Yes. The linked issue gives a focused current-main reproduction using a2a-message-tool-mirror-dedupe, with one sessions_send, two successful message sends, and two requester-visible markers before this patch.

Is this the best way to solve the issue?

Yes, based on the supplied diff and QA evidence. Extending the existing delivery-evidence classifier is narrower and less error-prone than changing empty-response recovery or introducing a parallel gateway-specific path.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 0483001ef65c.

Label changes

Label justifications:

  • P1: The linked current-main reproduction shows a user-visible A2A reply can be sent twice after a successful first delivery.
  • merge-risk: 🚨 message-delivery: The patch changes the success signal that determines whether recovery sends another visible message.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (linked_artifact): The PR body links an after-fix QA Gateway child and qa-channel scenario run that exercises the eight-second duplicate window and reports the improved result; no private credentials or identifiers are required in the supplied evidence.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body links an after-fix QA Gateway child and qa-channel scenario run that exercises the eight-second duplicate window and reports the improved result; no private credentials or identifiers are required in the supplied evidence.
Evidence reviewed

PR surface:

Source +5, Tests +30. Total +35 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 1 +5
Tests 2 30 0 +30
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 36 1 +35

What I checked:

  • Narrow delivery-classifier change: The proposed classifier adds message.id from a nested result object alongside existing top-level message, poll, and receipt delivery evidence. (src/agents/embedded-agent-message-tool-source-reply.ts:84, 5d47341e06f7)
  • Classifier coverage: The focused classifier test asserts that a gateway-style result with details.message.id is considered delivered. (src/agents/embedded-agent-message-tool-source-reply.test.ts:319, 5d47341e06f7)
  • Terminal recovery coverage: The terminal message-tool hook test covers the same nested gateway result, confirming it completes a message-tool-only source reply rather than invoking recovery. (src/agents/embedded-agent-runner/run/message-tool-terminal.test.ts:54, 5d47341e06f7)
  • Observed problem and after-fix proof: The linked report records a current-main QA reproduction with one sessions_send followed by two successful message sends; the PR body links an after-fix QA Gateway/qa-channel run with the duplicate window scenario passing. (5d47341e06f7)
  • Current-head provenance: The active branch head is the single commit fix(agents): recognize nested message delivery receipts; it is not part of the supplied current-main SHA, so this remains an open merge candidate rather than an implemented-on-main close. (5d47341e06f7)

Likely related people:

  • steipete: The available PR and linked-bug context ties this contributor directly to the current A2A delivery reproduction, focused QA scenario, and proposed classifier repair; current-main feature ownership could not be established from the supplied read-only context. (role: recent area contributor; confidence: low; commits: 5d47341e06f7; files: src/agents/embedded-agent-message-tool-source-reply.ts, src/agents/embedded-agent-runner/run/message-tool-terminal.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.
Review history (2 earlier review cycles)
  • reviewed 2026-07-18T23:01:24.119Z sha 239488d :: needs maintainer review before merge. :: none
  • reviewed 2026-07-18T23:18:10.593Z sha b981c31 :: needs maintainer review before merge. :: none

@steipete
steipete force-pushed the codex/a2a-message-mirror-dedupe branch from 239488d to b981c31 Compare July 18, 2026 23:15
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. label Jul 18, 2026
@steipete
steipete force-pushed the codex/a2a-message-mirror-dedupe branch from b981c31 to 5d47341 Compare July 18, 2026 23:23
@steipete
steipete merged commit 4362e1a into main Jul 18, 2026
117 checks passed
@steipete
steipete deleted the codex/a2a-message-mirror-dedupe branch July 18, 2026 23:33
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling maintainer Maintainer-authored PR merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. 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: XS 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.

[Bug]: A2A source reply repeats after successful message delivery

1 participant