fix(signal): retry inbound flushes on reply session init conflict#103218
Conversation
|
@clawsweeper review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
708ef3a to
ef26709
Compare
|
Land-ready proof for the refreshed Signal fix:
The fix now retries the original batch inline while retaining the same-key debounce execution slot, so newer buffered Signal messages cannot overtake it. Retry backoff is abortable and unref'ed; the monitor tracks and drains retries, and the production monitor boundary regression proves abort prevents post-stop dispatch. No live linked Signal account was available, so live send/receive remains the explicit proof gap. |
|
Merged via squash.
|
…enclaw#103218) * fix(signal): retry inbound flushes on reply session init conflict * fix(signal): bind conflict retries to monitor lifecycle --------- Co-authored-by: Peter Steinberger <[email protected]>
…ct (openclaw#108320) When reply dispatch fails with 'reply session initialization conflicted', the Feishu channel now retries with exponential backoff (250ms to 2s, up to 4 retries) instead of silently dropping the inbound message. Matches the retry pattern already used by Slack (openclaw#99647) and Signal (openclaw#100944, openclaw#103218). - Fixes off-by-one in retry/attempt counting: first attempt is outside the retry loop, and MAX_RETRIES = 4 consistently with the delay array - Fixes eslint no-promise-executor-return lint error at bot.ts:121 - Adds 6 focused unit tests covering schedule, exhaustion, and shutdown - Exports runFeishuDispatchWithSessionInitRetry for testing
What Problem This Solves
Fixes #100944. Signal could silently drop an already-authorized inbound message when reply-session initialization lost its final optimistic concurrency race. The generic inbound debouncer logged the failed flush but did not retain the Signal payload needed for another delivery attempt.
Why This Change Was Made
The shared reply-session initializer already retries once under its writer lock, so another unbounded session-layer loop would hide sustained contention. Signal is the narrow owner that still has the original inbound payload, making a bounded channel retry the appropriate recovery boundary.
This revision:
causeanderrorwrappers;User Impact
Signal follow-up messages that hit this transient session race are retried instead of being silently lost. No configuration or migration is required. Monitor shutdown cancels pending backoff and does not dispatch stale retries afterward.
Evidence
corepack pnpm test extensions/signal/src/monitor/event-handler.reply-session-conflict.test.ts extensions/signal/src/monitor.tool-result.pairs-uuid-only-senders-uuid-allowlist-entry.test.tson Blacksmith Testboxtbx_01kx591b6gg472mwcn1mhhhcqt: 11/11 passed across 2 files (run 29072172902).corepack pnpm check:changedon the same Testbox passed all guards, all four TypeScript lanes, and core lint. Extension lint found three local fixup issues; all were corrected and targeted extension oxlint is clean.monitorSignalProvider, aborts the monitor during backoff, and proves no post-stop dispatch.node:timers/promisesabortable unref delay behavior (AbortError).oxfmtandgit diff --check: clean.ef26709dcd74d632d07e6654b4f5870d7f555bc4reported no actionable findings (confidence 0.82).Live Signal account proof was not available in this checkout; the monitor-level test is the closest credential-free production-path proof.