Skip to content

fix(agents): keep merged delivery routes account-bound - #98240

Merged
steipete merged 2 commits into
openclaw:mainfrom
yetval:fix/merge-delivery-context-account-guard
Jul 1, 2026
Merged

steipete merged 2 commits into
openclaw:mainfrom
yetval:fix/merge-delivery-context-account-guard

Conversation

@yetval

@yetval yetval commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Multi-account deployments can merge a completion origin for one account with the saved target and thread of another account on the same channel. That can misroute or drop subagent, cron, media, and agent-harness completions.

The account guard was added in 1ed8592467, then removed while the media route-pin path was introduced in 025db6cf9e / #89949. Current main therefore allows a Telegram bot-a origin with no target to inherit bot-b's to and threadId.

Why This Change Was Made

mergeDeliveryContext is the canonical owner of route-field crossing. It now treats two explicit, differing account IDs as a route conflict and keeps the primary route isolated: fallback channel, target, thread, and account do not cross the account boundary. Same-account and missing-account merges retain the existing backfill behavior.

The patch also removes the media-only duplicate account guard and sends media route pinning through the same resolveAnnounceOrigin path used by subagent, cron, and harness completions. This is the best fix because all callers share one invariant instead of carrying caller-specific defenses.

User Impact

Completions from one configured account can no longer inherit another same-channel account's destination or thread. Single-account, same-account, and channel-conflict behavior is unchanged.

Evidence

  • Focused production-path suite: 138 tests passed across delivery-context, subagent announcement delivery, media completion routing, and the plugin SDK harness runtime.
  • Blacksmith Testbox tbx_01kwdkbva1c8zh4f73zv22rkjs (jade-shrimp): pnpm check:changed passed, including core/core-test type checks, lint, dependency guards, database-first/media/runtime guards, and import-cycle checks.
  • Production before/after probe used real resolveAnnounceOrigin and resolveExternalBestEffortDeliveryTarget for same-channel and account-only primary origins. Current main produced {channel:"telegram",to:"peer-b",accountId:"bot-a",threadId:99} and a deliverable bot-a/peer-b target; this head produces {channel:"telegram",accountId:"bot-a"} (or {accountId:"bot-a"}) and {deliver:false}.
  • Fresh autoreview after the refinement: no findings; patch correct (0.97 confidence).
  • oxfmt --check and git diff --check: clean.

The credentialed Telegram Desktop proof lane was attempted, but its harness provisions only one bot account and cannot express this two-account condition. The strongest feasible live-like proof is therefore the real production resolver/target path on retained Testbox with two distinct account IDs; no Bot API message was sent with two real bot tokens.

No linked issue was found. No docs or changelog change is needed for this internal routing correction.

@clawsweeper

clawsweeper Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 30, 2026, 9:27 PM ET / 01:27 UTC.

Summary
The PR adds account-id conflict handling to shared delivery-context merging, adds regressions for same-channel cross-account fallback, and routes media completion pinning through the shared announce-origin resolver.

PR surface: Source -15, Tests +46. Total +31 across 4 files.

Reproducibility: yes. at source level: current main and the latest release merge fallback route fields across same-channel contexts because mergeDeliveryContext only checks channel mismatch. I did not run a live two-account Telegram repro in this read-only review.

Review metrics: 1 noteworthy metric.

  • Route ownership guard: 1 added shared conflict condition; 1 removed media-local guard. The PR moves account-conflict handling from a media-only helper into shared route merging, changing fallback behavior for all completion callers that use this helper.

Root-cause cluster
Relationship: canonical
Canonical: #98240
Summary: This PR is the focused candidate for restoring the shared account-bound delivery-context invariant; related route PRs are provenance or adjacent delivery work, not replacements.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
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:

  • [P2] Add or request live two-account transport proof only if maintainers require confirmation beyond the production resolver/target path.

Mantis proof suggestion
A real multi-account transport proof would materially reduce the remaining delivery-routing uncertainty if Mantis can provision two Telegram accounts. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram live: verify a same-channel two-account completion for bot-a does not inherit bot-b's chat or thread target.

Risk before merge

  • [P1] No live two-bot Telegram/Bot API send is attached; the PR body exercises the production resolver and target path, but maintainers may still want transport-level confirmation for a same-channel multi-account deployment.
  • [P2] The fix intentionally fails closed for explicit account conflicts, so stale or conflicting account metadata can suppress a completion instead of borrowing fallback target fields from another account.

Maintainer options:

  1. Accept Resolver-Level Proof (recommended)
    Maintainers can accept the production resolver/target output plus shared-helper regressions as sufficient proof for this bounded account-conflict guard.
  2. Request Two-Account Transport Proof
    Maintainers can ask for a redacted Telegram or equivalent multi-account proof showing a completion for one account no longer inherits another account's chat or thread target.
  3. Pause For Route-Contract Decision
    If dropping conflicting fallback route fields is not acceptable, pause this PR and decide the desired recovery behavior for mismatched account metadata.

Next step before merge

  • No automated repair is needed; maintainers should decide whether resolver-level proof is enough or request two-account transport proof before merge.

Security
Cleared: The diff tightens TypeScript delivery routing and tests only; no security or supply-chain regression was found.

Review details

Best possible solution:

Land the shared account-bound route guard with the restored regression coverage after maintainers accept the fail-closed account-conflict behavior and exact-head validation is satisfactory.

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

Yes, at source level: current main and the latest release merge fallback route fields across same-channel contexts because mergeDeliveryContext only checks channel mismatch. I did not run a live two-account Telegram repro in this read-only review.

Is this the best way to solve the issue?

Yes. The shared merge helper is the best fix boundary because subagent, media, harness, session, sentinel, and outbound paths already centralize route-field crossing there; keeping per-caller account guards would leave drift.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 1241885db6ad.

Label changes

Label justifications:

  • P2: The PR fixes a concrete multi-account completion routing bug with bounded blast radius in agent delivery paths.
  • merge-risk: 🚨 message-delivery: The diff changes how conflicting account routes fall back, which can suppress a completion rather than send it to a wrong account target.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes after-fix live output from the production resolver/target path showing the cross-account target/thread is no longer inherited, while clearly noting that no live two-bot Telegram send was possible.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix live output from the production resolver/target path showing the cross-account target/thread is no longer inherited, while clearly noting that no live two-bot Telegram send was possible.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The change can affect visible Telegram completion delivery/non-delivery in multi-account deployments, though the current one-bot proof lane cannot express the exact condition.
Evidence reviewed

PR surface:

Source -15, Tests +46. Total +31 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 2 19 34 -15
Tests 2 46 0 +46
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 4 65 34 +31

What I checked:

Likely related people:

  • steipete: Authored the earlier account-bound fallback guard and the related media requester route commit, and later pushed the current PR refinement. (role: feature-history contributor and current follow-up owner; confidence: high; commits: 1ed859246715, 025db6cf9e80, 06942bcad217; files: src/utils/delivery-context.shared.ts, src/utils/delivery-context.test.ts, src/agents/tools/media-generate-background-shared.ts)
  • obviyus: Merged the related media route-pinning PR and authored its final media lifecycle fixture distillation commit. (role: merger and recent adjacent contributor; confidence: medium; commits: 780f83bcfb33, 6a2b993c57fc; files: src/agents/tools/media-generate-background-shared.ts, src/agents/subagent-announce-delivery.test.ts)
  • vincentkoc: Recent history around delivery-context splitting and media/background lifecycle refactors touches the central helper and adjacent agent delivery surfaces. (role: recent delivery-context seam contributor; confidence: medium; commits: 2028fd53f0c5, 543c14a4edd1, 363c2bc17139; files: src/utils/delivery-context.shared.ts, src/agents/tools/media-generate-background-shared.ts)
  • Takhoffman: Authored earlier work unifying session delivery invariants in the same route-context family, useful for owner-boundary review if the route contract needs widening. (role: prior session-routing invariant contributor; confidence: medium; commits: 7f2708a8c369; files: src/utils/delivery-context.shared.ts, src/config/sessions/store.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 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. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 30, 2026
@steipete steipete self-assigned this Jul 1, 2026
yetval and others added 2 commits June 30, 2026 18:17
mergeDeliveryContext gated route-field crossing on channel only, so a
completion origin that knew its account but not a concrete target
inherited a different account's to/threadId on the same channel. A
subagent, cron, or media completion for bot-a could be addressed to
bot-b's chat but sent through bot-a (cross-account misroute) or dropped.

This restores the account-bound guard added in 1ed8592 and removed as
collateral by 025db6c (PR openclaw#89949); same-account and missing-account
merges still backfill so the media route-pin path is preserved. Restores
the deleted regression test.
@steipete
steipete force-pushed the fix/merge-delivery-context-account-guard branch from 41b734d to 06942bc Compare July 1, 2026 01:22
@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S and removed size: XS labels Jul 1, 2026
@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Land-ready verification for 06942bcad217678393c5af94b89a4a44278eac57:

  • Best-fix verdict: the shared mergeDeliveryContext boundary is the canonical owner. An explicit account mismatch now prevents fallback channel, target, account, and thread inheritance, including an account-only primary. Same-account and missing-account merges are unchanged. Media now uses the same resolveAnnounceOrigin path as subagent, cron, and harness completions instead of carrying a second account guard.
  • Regression provenance: 1ed859246715ce1a27e2159f7067cfaf9ac7947f added the account guard; 025db6cf9e805d55cfdc18dd7b564b0fde680749 / fix(media): pin requester delivery route when task starts #89949 removed it while adding media route pinning. The retained fallback contract is valid, but it must stay account-bound.
  • Focused proof: node scripts/run-vitest.mjs src/utils/delivery-context.test.ts src/agents/tools/media-generate-background-shared.test.ts src/agents/subagent-announce-delivery.test.ts src/plugin-sdk/agent-harness-task-runtime.test.ts passed 3 shards / 138 tests. New utility and persisted-session resolver coverage is in delivery-context.test.ts and subagent-announce-delivery.test.ts.
  • Changed gates: Blacksmith Testbox-through-Crabbox tbx_01kwdkbva1c8zh4f73zv22rkjs (jade-shrimp) passed pnpm check:changed, including core/core-test types, lint, import-cycle, database-first, media/runtime, webhook, and pairing guards (Testbox Actions run). A supplemental broad pnpm test:changed run reached the full agents shard and exposed two log-capture assertions in untouched src/agents/agent-tools.policy.test.ts; the isolated rerun on the same retained Testbox passed all 27 tests. Exact-head hosted CI passed all 44 executed jobs with no failures: CI run 28486936463.
  • Before/after production path: on main 614032676d16bf2dd98030e7925f14ad52f5618e, real resolveAnnounceOrigin plus resolveExternalBestEffortDeliveryTarget merged {channel:"telegram", accountId:"bot-a"} (and the account-only form) with bot-b's saved route into {channel:"telegram", to:"peer-b", accountId:"bot-a", threadId:99} and deliver:true. This head returns {channel:"telegram", accountId:"bot-a"} (or {accountId:"bot-a"}) and deliver:false; bot-b's target/thread never cross the account boundary.
  • Review: fresh structured autoreview after the refinement reported no findings and patch is correct (0.97 confidence). Review artifacts validate with zero findings; the guarded OPENCLAW_TESTBOX=1 scripts/pr prepare-run 98240 completed with hosted exact-head gates and no branch rewrite.
  • Known proof gap: the credentialed Telegram Desktop lane provisions one bot account, so it cannot express a two-account same-channel conflict; the attempted lane documented that limitation (run 28468552408). No two-token Bot API message was sent. The real two-account production resolver and downstream delivery decision above are the strongest feasible proof.

No review threads, linked issue, or open duplicate was found. Changelog is not required for this internal routing correction.

@steipete
steipete merged commit 150ca2f into openclaw:main Jul 1, 2026
115 of 118 checks passed
@steipete

steipete commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 1, 2026
* fix(agents): keep merged delivery routes account-bound

mergeDeliveryContext gated route-field crossing on channel only, so a
completion origin that knew its account but not a concrete target
inherited a different account's to/threadId on the same channel. A
subagent, cron, or media completion for bot-a could be addressed to
bot-b's chat but sent through bot-a (cross-account misroute) or dropped.

This restores the account-bound guard added in 35e8b60 and removed as
collateral by 523bae1 (PR openclaw#89949); same-account and missing-account
merges still backfill so the media route-pin path is preserved. Restores
the deleted regression test.

* fix(agents): centralize account-bound completion routes

---------

Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
chenyangjun-xy pushed a commit to chenyangjun-xy/openclaw that referenced this pull request Jul 1, 2026
* fix(agents): keep merged delivery routes account-bound

mergeDeliveryContext gated route-field crossing on channel only, so a
completion origin that knew its account but not a concrete target
inherited a different account's to/threadId on the same channel. A
subagent, cron, or media completion for bot-a could be addressed to
bot-b's chat but sent through bot-a (cross-account misroute) or dropped.

This restores the account-bound guard added in 1ed8592 and removed as
collateral by 025db6c (PR openclaw#89949); same-account and missing-account
merges still backfill so the media route-pin path is preserved. Restores
the deleted regression test.

* fix(agents): centralize account-bound completion routes

---------

Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling mantis: telegram-visible-proof Mantis should capture Telegram visible proof. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S 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.

2 participants