Skip to content

fix(agents): classify embedded prompt lock error as permanent announce failure#92274

Merged
obviyus merged 8 commits into
openclaw:mainfrom
fsdwen:fix/subagent-announce-permanent-lock-error
Jul 1, 2026
Merged

fix(agents): classify embedded prompt lock error as permanent announce failure#92274
obviyus merged 8 commits into
openclaw:mainfrom
fsdwen:fix/subagent-announce-permanent-lock-error

Conversation

@fsdwen

@fsdwen fsdwen commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Subagent completion announcements can retry after an embedded prompt lock-change race even when the first attempt already produced a visible outbound send. On Telegram this can show the same completion message multiple times.

Fixes #91527.

Why This Change Was Made

The announce path now treats session-file-changed failures as retryable only when there is no send evidence. If the error chain contains committed send evidence, the failure is terminal for that announce attempt, so dispatch does not fallback-steer or retry into a duplicate send.

The cleanup pass also carries terminal delivery state through subagent lifecycle cleanup so completed runs are finalized instead of scheduling another completion retry.

User Impact

Users should see one subagent completion message for this prompt-lock race instead of repeated duplicate completion messages.

Evidence

  • node scripts/run-vitest.mjs src/agents/subagent-announce-delivery.test.ts src/agents/subagent-announce.test.ts src/agents/subagent-registry-lifecycle.test.ts
    • 3 files passed
    • 150 tests passed
  • .agents/skills/autoreview/scripts/autoreview --mode local
    • clean: no accepted/actionable findings
  • git diff --check
    • clean

Proof gap: no live Telegram transcript or recording was added in this branch. The fix is covered by focused source-level regression tests for duplicate-send prevention, retry-without-evidence, and lifecycle cleanup finalization.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 11, 2026
@clawsweeper

clawsweeper Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 27, 2026, 1:01 PM ET / 17:01 UTC.

Summary
The PR adds send-evidence-aware terminal handling for embedded prompt lock-change subagent announce failures and carries that terminal state into completion cleanup tests.

PR surface: Source +73, Tests +246. Total +319 across 5 files.

Reproducibility: yes. at source level: current main lacks the lock-change terminal classifier and only credits delivered announce results, matching the linked duplicate-send report. I did not run the live Telegram race in this read-only review.

Review metrics: none identified.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #91527
Summary: This PR is the active candidate fix for the canonical duplicate Telegram subagent announce send report.

Members:

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

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🐚 platinum hermit
Result: blocked until real behavior proof from a real setup is added.

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

Rank-up moves:

  • [P1] Add redacted Telegram or runtime-log proof showing one completion message and no duplicate retries after the fix.

Proof guidance:

  • [P1] Needs real behavior proof before merge: Only focused tests and local command output are supplied; the contributor should add redacted Telegram recording, transcript, runtime logs, or equivalent live output showing one completion and no duplicates. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Mantis proof suggestion
A live Telegram transcript is the clearest proof that this change prevents duplicate completion delivery in the reported transport. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis telegram live: verify the embedded prompt lock-change subagent completion race sends one Telegram completion message, not duplicate retries.

Risk before merge

  • [P1] The final branch still needs redacted Telegram or comparable runtime proof that the reported prompt-lock race sends one completion message and no retry duplicates.
  • [P1] The diff intentionally changes message-delivery retry and cleanup accounting; if send-evidence detection is wrong, it could replay visible sends or stop retrying a pre-send failure.

Maintainer options:

  1. Require live duplicate-delivery proof (recommended)
    Before merge, get a redacted Telegram transcript, recording, or runtime log showing the final branch sends one completion and does not retry duplicates for the prompt-lock race.
  2. Accept source proof only
    Maintainers can intentionally land on the focused code review and regression tests, but they would own the unproven live transport behavior.
  3. Pause behind the canonical issue
    If runtime proof cannot be produced or accepted, keep the linked issue open and avoid treating this branch as land-ready.

Next step before merge

  • [P1] The remaining blocker is contributor or maintainer proof acceptance, not a narrow code repair that automation can supply.

Security
Cleared: The diff changes TypeScript agent delivery logic and tests only; no dependency, CI, package, permission, secret, or code-execution surface changed.

Review details

Best possible solution:

Keep this PR open as the canonical fix path and land it only after redacted Telegram or equivalent runtime-log proof, unless a maintainer explicitly accepts source proof for this transport risk.

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

Yes at source level: current main lacks the lock-change terminal classifier and only credits delivered announce results, matching the linked duplicate-send report. I did not run the live Telegram race in this read-only review.

Is this the best way to solve the issue?

Yes for the code shape: the merge result distinguishes post-send lock-change failures from pre-send recovery and propagates terminal state through cleanup. The remaining blocker is real behavior proof or explicit maintainer acceptance of the proof gap.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 2720ac06b7c8.

Label changes

Label justifications:

  • P1: The PR targets a real agent/channel workflow where one Telegram subagent completion can be sent multiple times.
  • merge-risk: 🚨 message-delivery: The diff changes retry and terminal cleanup behavior for requester-visible subagent completion delivery.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: Only focused tests and local command output are supplied; the contributor should add redacted Telegram recording, transcript, runtime logs, or equivalent live output showing one completion and no duplicates. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • mantis: telegram-visible-proof: Mantis should capture Telegram visible proof. The PR changes visible Telegram duplicate completion behavior and can be demonstrated in a short Telegram proof.
Evidence reviewed

PR surface:

Source +73, Tests +246. Total +319 across 5 files.

View PR surface stats
Area Files Added Removed Net
Source 2 86 13 +73
Tests 3 249 3 +246
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 5 335 16 +319

What I checked:

Likely related people:

  • Vincent Koc: Current blame on the announce classifier, dispatch terminal handling, and cleanup bookkeeping points to Vincent Koc's recent snapshot; the PR discussion also has a member note that this branch was repaired in place. (role: recent area contributor and reviewer; confidence: high; commits: fa78cfbfb783; files: src/agents/subagent-announce-delivery.ts, src/agents/subagent-announce-dispatch.ts, src/agents/subagent-announce.ts)
  • Ayaan Zaidi: The PR is currently assigned to obviyus, and the latest branch commit distills the announce send-evidence implementation. (role: current assignee and PR branch refactor owner; confidence: medium; commits: d7377d048f1d; files: src/agents/subagent-announce-delivery.ts)
  • Sally O'Malley: Recent history includes a subagent completion announce preservation change in the same delivery file family. (role: recent adjacent contributor; confidence: medium; commits: 95c87e31e295; files: src/agents/subagent-announce-delivery.ts)
  • harjothkhara: The related merged outbound mirror fix that this PR builds on credited this contributor and changed the sibling durable delivery path. (role: adjacent outbound-delivery contributor; confidence: medium; commits: 79896a24d9bb; files: src/infra/outbound/deliver.ts, src/infra/outbound/deliver.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.

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 11, 2026
@clawsweeper clawsweeper Bot added the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 11, 2026
@fsdwen

fsdwen commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

@vincentkoc would you mind reviewing this? Thanks!

A one-line fix in subagent-announce-delivery.ts: adds the embedded prompt lock error to permanent error patterns, preventing 3x duplicate announce retries. Mirrors the approach from #89812.

  • 196 existing tests pass
  • 1 new behavior test added
  • Real behavior proof: accepted
  • All CI checks green

@fsdwen

fsdwen commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 12, 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.

Re-review progress:

@fsdwen

fsdwen commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@vincentkoc would you mind reviewing this? Thanks!

@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. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. and removed rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. labels Jun 12, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 15, 2026
fsdwen added a commit to fsdwen/openclaw that referenced this pull request Jun 17, 2026
…nounce error

Per ClawSweeper review feedback on openclaw#92274: the unconditional permanent-regex
approach may prevent duplicates but can also suppress recovery for pre-send lock
failures. This commit adds structured send-evidence checks so the session-file-
changed takeover error is only treated as permanent when a visible send has
already occurred.

Changes:
- Extract SESSION_FILE_CHANGED_ANNOUNCE_RE as a named constant
- Add hasAnnounceSendEvidence() — recursive error-chain walker that detects
  send evidence from OutboundDeliveryError.sentBeforeError, generic
  sentBeforeError/visibleReplySent flags, and the promptError field of
  EmbeddedAttemptPromptErrorWithCleanupTakeoverError
- Add isSessionFileChangedAnnounceError() helper
- Safety valve in isTransientAnnounceDeliveryError(): session-file-changed
  errors without send evidence are treated as transient (retry allowed)
- Safety valve in sendSubagentAnnounceDirectly catch block: permanent errors
  without send evidence fall through to fallback paths
- 7 focused tests covering send-evidence detection and retry behavior

Fixes openclaw#91527

Co-Authored-By: Claude <noreply@anthropic.com>
@clawsweeper clawsweeper Bot removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. mantis: telegram-visible-proof Mantis should capture Telegram visible proof. labels Jun 17, 2026
fsdwen added a commit to fsdwen/openclaw that referenced this pull request Jun 17, 2026
…nounce error

Per ClawSweeper review feedback on openclaw#92274: the unconditional permanent-regex
approach may prevent duplicates but can also suppress recovery for pre-send lock
failures. This commit adds structured send-evidence checks so the session-file-
changed takeover error is only treated as permanent when a visible send has
already occurred.

Changes:
- Extract SESSION_FILE_CHANGED_ANNOUNCE_RE as a named constant
- Add hasAnnounceSendEvidence() — recursive error-chain walker that detects
  send evidence from OutboundDeliveryError.sentBeforeError, generic
  sentBeforeError/visibleReplySent flags, and the promptError field of
  EmbeddedAttemptPromptErrorWithCleanupTakeoverError
- Add isSessionFileChangedAnnounceError() helper
- Safety valve in isTransientAnnounceDeliveryError(): session-file-changed
  errors without send evidence are treated as transient (retry allowed)
- Safety valve in sendSubagentAnnounceDirectly catch block: permanent errors
  without send evidence fall through to fallback paths
- 7 focused tests covering send-evidence detection and retry behavior

Fixes openclaw#91527

Co-Authored-By: Claude <noreply@anthropic.com>
@fsdwen
fsdwen force-pushed the fix/subagent-announce-permanent-lock-error branch from 4791a82 to d46a64b Compare June 17, 2026 06:46
fsdwen added a commit to fsdwen/openclaw that referenced this pull request Jun 17, 2026
…nounce error

Per ClawSweeper review feedback on openclaw#92274: the unconditional permanent-regex
approach may prevent duplicates but can also suppress recovery for pre-send lock
failures. This commit adds structured send-evidence checks so the session-file-
changed takeover error is only treated as permanent when a visible send has
already occurred.

Changes:
- Extract SESSION_FILE_CHANGED_ANNOUNCE_RE as a named constant
- Add hasAnnounceSendEvidence() — recursive error-chain walker that detects
  send evidence from OutboundDeliveryError.sentBeforeError, generic
  sentBeforeError/visibleReplySent flags, and the promptError field of
  EmbeddedAttemptPromptErrorWithCleanupTakeoverError
- Add isSessionFileChangedAnnounceError() helper
- Safety valve in isTransientAnnounceDeliveryError(): session-file-changed
  errors without send evidence are treated as transient (retry allowed)
- Safety valve in sendSubagentAnnounceDirectly catch block: permanent errors
  without send evidence fall through to fallback paths
- 7 focused tests covering send-evidence detection and retry behavior

Fixes openclaw#91527

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

fsdwen commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@fsdwen

fsdwen commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 19, 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 rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 19, 2026
obviyus pushed a commit to rosenlo/openclaw that referenced this pull request Jun 25, 2026
… failure with send evidence

When a required-mode batch send fails mid-batch after an earlier payload
already succeeded, the wrapper catch in deliverOutboundPayloadsWithQueueCleanup
called failDelivery. failDelivery only bumps retryCount/lastError; it does
not advance recoveryState, so the entry stayed in send_attempt_started (set
earlier by markDeliveryPlatformSendAttemptStarted via onPlatformSendStart).

On the next Telegram reconnect, drainQueuedEntry sees send_attempt_started
and calls reconcileUnknownQueuedDelivery. When adapter reconciliation
misreports not_sent (the message was actually sent, per the outbound send
ok / messageId evidence), the entry is replayed and the user receives a
duplicate.

Fix: when the error carries send evidence (OutboundDeliveryError with
sentBeforeError === true and platformSendStarted === true), call
markQueuedPlatformOutcomeUnknown instead of failDelivery. This advances the
entry to unknown_after_send, which drain already routes through
reconcileUnknownQueuedDelivery, preserving the entry for adapter
reconciliation rather than leaving it in send_attempt_started for replay.

When there is no send evidence (sentBeforeError === false), failDelivery
remains correct: nothing reached the channel, so retrying is safe.

This is a third duplicate path distinct from openclaw#89812 (mirror best-effort)
and openclaw#92274 (subagent-announce-delivery retry); it is the outbound/deliver
wrapper catch, which neither prior fix covers.

Tests:
- regression: two payloads, first succeeds, second throws; asserts
  markDeliveryPlatformOutcomeUnknown called, failDelivery/ackDelivery not.
- guard: no send evidence; failDelivery still called.
obviyus pushed a commit that referenced this pull request Jun 25, 2026
… failure with send evidence

When a required-mode batch send fails mid-batch after an earlier payload
already succeeded, the wrapper catch in deliverOutboundPayloadsWithQueueCleanup
called failDelivery. failDelivery only bumps retryCount/lastError; it does
not advance recoveryState, so the entry stayed in send_attempt_started (set
earlier by markDeliveryPlatformSendAttemptStarted via onPlatformSendStart).

On the next Telegram reconnect, drainQueuedEntry sees send_attempt_started
and calls reconcileUnknownQueuedDelivery. When adapter reconciliation
misreports not_sent (the message was actually sent, per the outbound send
ok / messageId evidence), the entry is replayed and the user receives a
duplicate.

Fix: when the error carries send evidence (OutboundDeliveryError with
sentBeforeError === true and platformSendStarted === true), call
markQueuedPlatformOutcomeUnknown instead of failDelivery. This advances the
entry to unknown_after_send, which drain already routes through
reconcileUnknownQueuedDelivery, preserving the entry for adapter
reconciliation rather than leaving it in send_attempt_started for replay.

When there is no send evidence (sentBeforeError === false), failDelivery
remains correct: nothing reached the channel, so retrying is safe.

This is a third duplicate path distinct from #89812 (mirror best-effort)
and #92274 (subagent-announce-delivery retry); it is the outbound/deliver
wrapper catch, which neither prior fix covers.

Tests:
- regression: two payloads, first succeeds, second throws; asserts
  markDeliveryPlatformOutcomeUnknown called, failDelivery/ackDelivery not.
- guard: no send evidence; failDelivery still called.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 26, 2026
… failure with send evidence

When a required-mode batch send fails mid-batch after an earlier payload
already succeeded, the wrapper catch in deliverOutboundPayloadsWithQueueCleanup
called failDelivery. failDelivery only bumps retryCount/lastError; it does
not advance recoveryState, so the entry stayed in send_attempt_started (set
earlier by markDeliveryPlatformSendAttemptStarted via onPlatformSendStart).

On the next Telegram reconnect, drainQueuedEntry sees send_attempt_started
and calls reconcileUnknownQueuedDelivery. When adapter reconciliation
misreports not_sent (the message was actually sent, per the outbound send
ok / messageId evidence), the entry is replayed and the user receives a
duplicate.

Fix: when the error carries send evidence (OutboundDeliveryError with
sentBeforeError === true and platformSendStarted === true), call
markQueuedPlatformOutcomeUnknown instead of failDelivery. This advances the
entry to unknown_after_send, which drain already routes through
reconcileUnknownQueuedDelivery, preserving the entry for adapter
reconciliation rather than leaving it in send_attempt_started for replay.

When there is no send evidence (sentBeforeError === false), failDelivery
remains correct: nothing reached the channel, so retrying is safe.

This is a third duplicate path distinct from openclaw#89812 (mirror best-effort)
and openclaw#92274 (subagent-announce-delivery retry); it is the outbound/deliver
wrapper catch, which neither prior fix covers.

Tests:
- regression: two payloads, first succeeds, second throws; asserts
  markDeliveryPlatformOutcomeUnknown called, failDelivery/ackDelivery not.
- guard: no send evidence; failDelivery still called.
@obviyus obviyus self-assigned this Jun 27, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 27, 2026
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
… failure with send evidence

When a required-mode batch send fails mid-batch after an earlier payload
already succeeded, the wrapper catch in deliverOutboundPayloadsWithQueueCleanup
called failDelivery. failDelivery only bumps retryCount/lastError; it does
not advance recoveryState, so the entry stayed in send_attempt_started (set
earlier by markDeliveryPlatformSendAttemptStarted via onPlatformSendStart).

On the next Telegram reconnect, drainQueuedEntry sees send_attempt_started
and calls reconcileUnknownQueuedDelivery. When adapter reconciliation
misreports not_sent (the message was actually sent, per the outbound send
ok / messageId evidence), the entry is replayed and the user receives a
duplicate.

Fix: when the error carries send evidence (OutboundDeliveryError with
sentBeforeError === true and platformSendStarted === true), call
markQueuedPlatformOutcomeUnknown instead of failDelivery. This advances the
entry to unknown_after_send, which drain already routes through
reconcileUnknownQueuedDelivery, preserving the entry for adapter
reconciliation rather than leaving it in send_attempt_started for replay.

When there is no send evidence (sentBeforeError === false), failDelivery
remains correct: nothing reached the channel, so retrying is safe.

This is a third duplicate path distinct from openclaw#89812 (mirror best-effort)
and openclaw#92274 (subagent-announce-delivery retry); it is the outbound/deliver
wrapper catch, which neither prior fix covers.

Tests:
- regression: two payloads, first succeeds, second throws; asserts
  markDeliveryPlatformOutcomeUnknown called, failDelivery/ackDelivery not.
- guard: no send evidence; failDelivery still called.
fsdwen and others added 8 commits July 1, 2026 12:51
…e failure

The "session file changed while embedded prompt lock was released" error
is a concurrent-session race: the parent transcript is being modified by
turn-maintenance while the subagent completion tries to inject. Retrying
does not help because each retry independently triggers a duplicate
outbound send (3x duplicate Telegram messages reported in openclaw#91527).

Add the error message pattern to PERMANENT_ANNOUNCE_DELIVERY_ERROR_PATTERNS
so it stops triggering retries, mirroring the approach used in PR openclaw#89812
for the general outbound delivery path.

Fixes openclaw#91527

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

Per ClawSweeper review feedback on openclaw#92274: the unconditional permanent-regex
approach may prevent duplicates but can also suppress recovery for pre-send lock
failures. This commit adds structured send-evidence checks so the session-file-
changed takeover error is only treated as permanent when a visible send has
already occurred.

Changes:
- Extract SESSION_FILE_CHANGED_ANNOUNCE_RE as a named constant
- Add hasAnnounceSendEvidence() — recursive error-chain walker that detects
  send evidence from OutboundDeliveryError.sentBeforeError, generic
  sentBeforeError/visibleReplySent flags, and the promptError field of
  EmbeddedAttemptPromptErrorWithCleanupTakeoverError
- Add isSessionFileChangedAnnounceError() helper
- Safety valve in isTransientAnnounceDeliveryError(): session-file-changed
  errors without send evidence are treated as transient (retry allowed)
- Safety valve in sendSubagentAnnounceDirectly catch block: permanent errors
  without send evidence fall through to fallback paths
- 7 focused tests covering send-evidence detection and retry behavior

Fixes openclaw#91527

Co-Authored-By: Claude <noreply@anthropic.com>
@obviyus
obviyus force-pushed the fix/subagent-announce-permanent-lock-error branch from f6e1edd to 8c055a8 Compare July 1, 2026 19:52
@obviyus
obviyus merged commit 44cb3f7 into openclaw:main Jul 1, 2026
39 checks passed
@obviyus

obviyus commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Landed via squash onto main.

  • Scoped tests: node scripts/run-vitest.mjs src/agents/subagent-announce-delivery.test.ts src/agents/subagent-announce.test.ts src/agents/subagent-registry-lifecycle.test.ts (3 files, 156 tests)
  • Type proof: node scripts/run-tsgo.mjs -p tsconfig.core.json; node scripts/run-tsgo.mjs -p test/tsconfig/tsconfig.core.test.agents.json
  • Formatting: oxfmt --check on touched agent files; git diff --check
  • Autoreview: .agents/skills/autoreview/scripts/autoreview --mode local clean
  • Changelog: release-owned, not edited
  • Land commit: 8c055a8
  • Merge commit: 44cb3f7

Thanks @fsdwen!

wheakerd pushed a commit to wheakerd/clawdbot that referenced this pull request Jul 15, 2026
… failure with send evidence

When a required-mode batch send fails mid-batch after an earlier payload
already succeeded, the wrapper catch in deliverOutboundPayloadsWithQueueCleanup
called failDelivery. failDelivery only bumps retryCount/lastError; it does
not advance recoveryState, so the entry stayed in send_attempt_started (set
earlier by markDeliveryPlatformSendAttemptStarted via onPlatformSendStart).

On the next Telegram reconnect, drainQueuedEntry sees send_attempt_started
and calls reconcileUnknownQueuedDelivery. When adapter reconciliation
misreports not_sent (the message was actually sent, per the outbound send
ok / messageId evidence), the entry is replayed and the user receives a
duplicate.

Fix: when the error carries send evidence (OutboundDeliveryError with
sentBeforeError === true and platformSendStarted === true), call
markQueuedPlatformOutcomeUnknown instead of failDelivery. This advances the
entry to unknown_after_send, which drain already routes through
reconcileUnknownQueuedDelivery, preserving the entry for adapter
reconciliation rather than leaving it in send_attempt_started for replay.

When there is no send evidence (sentBeforeError === false), failDelivery
remains correct: nothing reached the channel, so retrying is safe.

This is a third duplicate path distinct from openclaw#89812 (mirror best-effort)
and openclaw#92274 (subagent-announce-delivery retry); it is the outbound/deliver
wrapper catch, which neither prior fix covers.

Tests:
- regression: two payloads, first succeeds, second throws; asserts
  markDeliveryPlatformOutcomeUnknown called, failDelivery/ackDelivery not.
- guard: no send evidence; failDelivery still called.

(cherry picked from commit 71422a9)
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. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

3 participants