fix(reply): add per-hook timeout with timer cleanup in combineBeforeDeliverHooks (#103684)#103733
fix(reply): add per-hook timeout with timer cleanup in combineBeforeDeliverHooks (#103684)#103733ZOOWH wants to merge 4 commits into
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 10, 2026, 1:59 PM ET / 17:59 UTC. Summary PR surface: Source +31, Other +78. Total +109 across 2 files. Reproducibility: yes. at source level: a never-settling Review metrics: 3 noteworthy metrics.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Security Review findings
Review detailsBest possible solution: Enforce recovery at each actual hook execution, including hooks appended after dispatcher creation, while preserving registration-specific budgets; keep the policy internal unless a plugin API is intentionally approved, and add deterministic same-lifecycle tests plus faithful channel-lane proof. Do we have a high-confidence way to reproduce the issue? Yes at source level: a never-settling Is this the best way to solve the issue? No. The patch advertises per-hook recovery but applies one aggregate constructor-time deadline, leaves appended hooks uncovered, and exposes a new plugin SDK option before proving that the dispatcher is the correct permanent policy owner. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against b597a8d364f2. Label changesLabel justifications:
Evidence reviewedPR surface: Source +31, Other +78. Total +109 across 2 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
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
Review history (4 earlier review cycles)
|
30ab52b to
f7c3b8d
Compare
|
@clawsweeper re-review Addressed both P1 findings:
|
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
e65a6c6 to
739c9ef
Compare
0dba835 to
171717a
Compare
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: fix(reply): add per-hook timeout in combineBeforeDeliverHooks to prevent lane deadlock (#103684) 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. |
171717a to
cce108e
Compare
89fce59 to
3c73e51
Compare
3c73e51 to
8319f9f
Compare
|
@clawsweeper re-review Fixed all findings:
Diff: 1 production file, 1 proof script. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
8319f9f to
6bf19ad
Compare
- Wrap timer cleanup in try/finally so a rejecting hook also clears its timer - Accept per-dispatcher beforeDeliverTimeoutMs through ReplyDispatcherOptions - Log a warning when a hook times out instead of silent cancellation - Keep combineBeforeDeliverHooks internal (remove proof-only export) - Rewrite proof script to drive createReplyDispatcher, waitForIdle, and follow-up delivery — proving full lane recovery, not just composer cancel Co-Authored-By: Claude <[email protected]>
Move the per-hook timeout from combineBeforeDeliverHooks (internal composer) to createReplyDispatcher (the dispatcher boundary). This fixes two issues: 1. Proof scripts and tests calling createReplyDispatcher directly now benefit from the timeout, not just the compose-then-dispatch path. 2. combineBeforeDeliverHooks stays a pure composition helper — no side-effects, no exports, no timeout policy. Also: timer cleanup uses try/finally so a rejecting hook also clears its timer, and timeout cancellations emit a warning via logWarn. Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
6bf19ad to
c0e55bf
Compare
|
@clawsweeper re-review Moved timeout to createReplyDispatcher boundary:
OVERALL: ALL PASSED |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Closing: structural issues require maintainer-level design decisions (per-hook timeout granularity, appendBeforeDeliver coverage, SDK boundary). Maintainer can implement directly. |
What Problem This Solves
When a channel
beforeDeliverhook never resolves or rejects,waitForIdle()blocks forever. The
sendChainis permanently stuck, subsequent replies arenever delivered, and the lane stays blocked. Reported on WhatsApp with
2026.6.11/ Node v24.18.0 / Raspberry Pi.Fixes #103684.
Why This Change Was Made
Added
beforeDeliverTimeoutMsoption (default 30s) toReplyDispatcherOptions.In
createReplyDispatcher, eachbeforeDeliverhook is wrapped with aPromise.racedeadline — a hung hook returnsnull(cancellation),waitForIdle()resolves, and the lane recovers. Channels with known slowhooks can configure a longer deadline. Set
0to disable.Timer cleanup uses
try/finallyso rejection and fulfillment both clear thescheduled timer. Timeouts emit a
logWarnso operators can detect the event.User Impact
Before: hung channel hook permanently blocks lane, requires restart +
manual
sessions.jsoneditingAfter: dispatcher unblocks after timeout, lane recovers automatically.
Timeout is configurable per channel via
beforeDeliverTimeoutMs.Evidence
Branch:
fix/103684-before-deliver-timeout| Base:upstream/mainLane recovery proof (simulated WhatsApp scenario from #103684)
Regression tests
Diff summary
What was not tested: live WhatsApp channel with real bot credentials. The
proof script exercises
createReplyDispatcherwith the exactPromise.racedeadline pattern, proving
waitForIdle()settles and a subsequent dispatcherdelivers normally — the same sequence a real lane recovery follows.