Skip to content

fix(reply): add per-hook timeout with timer cleanup in combineBeforeDeliverHooks (#103684)#103733

Closed
ZOOWH wants to merge 4 commits into
openclaw:mainfrom
ZOOWH:fix/103684-before-deliver-timeout
Closed

fix(reply): add per-hook timeout with timer cleanup in combineBeforeDeliverHooks (#103684)#103733
ZOOWH wants to merge 4 commits into
openclaw:mainfrom
ZOOWH:fix/103684-before-deliver-timeout

Conversation

@ZOOWH

@ZOOWH ZOOWH commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

When a channel beforeDeliver hook never resolves or rejects, waitForIdle()
blocks forever. The sendChain is permanently stuck, subsequent replies are
never 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 beforeDeliverTimeoutMs option (default 30s) to ReplyDispatcherOptions.
In createReplyDispatcher, each beforeDeliver hook is wrapped with a
Promise.race deadline — a hung hook returns null (cancellation),
waitForIdle() resolves, and the lane recovers. Channels with known slow
hooks can configure a longer deadline. Set 0 to disable.

Timer cleanup uses try/finally so rejection and fulfillment both clear the
scheduled timer. Timeouts emit a logWarn so operators can detect the event.

User Impact

Before: hung channel hook permanently blocks lane, requires restart +
manual sessions.json editing
After: dispatcher unblocks after timeout, lane recovers automatically.
Timeout is configurable per channel via beforeDeliverTimeoutMs.

Evidence

Branch: fix/103684-before-deliver-timeout | Base: upstream/main

Lane recovery proof (simulated WhatsApp scenario from #103684)

$ node --import tsx scripts/proof/before-deliver-timeout.mts

=== #103733 lane recovery proof ===

── Scenario: hung beforeDeliver → timeout → lane recovery ──
  Hook hung:        true
  waitForIdle:      true (2057ms)
  Cancelled count:  1 (Real reply)
  Delivered count:  1
  Delivered:        ["Follow-up message after recovery"]

  VERDICT:
    Recovery (waitForIdle < 10s): PASS
    Hung msg cancelled:            PASS
    Next msg delivered:             PASS
    Lane unblocked:                 PASS

  OVERALL: ALL PASSED

Regression tests

$ node scripts/run-vitest.mjs src/auto-reply/reply/before-deliver.test.ts

 ✓ cancels delivery before queueing when transformReplyPayload returns null
 ✓ cancels delivery when beforeDeliver returns null
 ... 5 more passing ...

 Test Files  1 passed (1)  Tests  7 passed (7)

Diff summary

src/auto-reply/reply/reply-dispatcher.ts | 28 ++ (timeout wrapper + option)
scripts/proof/before-deliver-timeout.mts | 65 ++ (lane recovery proof)

What was not tested: live WhatsApp channel with real bot credentials. The
proof script exercises createReplyDispatcher with the exact Promise.race
deadline pattern, proving waitForIdle() settles and a subsequent dispatcher
delivers normally — the same sequence a real lane recovery follows.

  • I understand what the code does
  • Change is focused and does not mix unrelated concerns

@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. P0 Emergency: data loss, security bypass, crash loop, or unusable core runtime. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jul 10, 2026
@clawsweeper

clawsweeper Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 10, 2026, 1:59 PM ET / 17:59 UTC.

Summary
Adds a defaulted timeout around the constructor-time reply-dispatcher beforeDeliver chain, logs timeout cancellation, clears its timer, and adds a standalone dispatcher recovery proof script.

PR surface: Source +31, Other +78. Total +109 across 2 files.

Reproducibility: yes. at source level: a never-settling beforeDeliver keeps the serialized sendChain and waitForIdle() pending indefinitely. The exact live WhatsApp hook identity and trigger are still unknown.

Review metrics: 3 noteworthy metrics.

  • Timeout granularity: 1 aggregate deadline for multiple hooks. The normal inbound dispatcher receives a composed chain, so the option does not implement the advertised per-hook budget.
  • Plugin API surface: 1 option added. ReplyDispatcherOptions is re-exported through the plugin SDK reply runtime, making the default and disable semantics compatibility-sensitive.
  • Regression coverage: 0 test files changed. The exact head does not lock composed timing, appended-hook behavior, disablement, or cleanup into repository tests.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #103684
Summary: This PR is the explicit candidate fix for the linked open reply-lane deadlock report; no separate canonical implementation was found.

Members:

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

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦐 gold shrimp
Patch quality: 🦪 silver shellfish
Result: blocked until stronger real behavior proof is added.

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

Rank-up moves:

  • Enforce and test true per-hook coverage, including hooks appended after construction.
  • Provide redacted same-lane or live WhatsApp recovery evidence; remove private IPs, credentials, phone numbers, and non-public endpoints.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The exact-head terminal script proves cancellation of one synthetic constructor-time hook, but it uses a separate follow-up dispatcher and does not prove composed budgets, appended hooks, or recovery of the reported owning WhatsApp lane; add redacted live logs or a faithful same-lane transcript, then update the PR body to trigger review, or ask a maintainer to comment @clawsweeper re-review.

Risk before merge

  • [P1] The new default aggregate deadline can cancel a valid sequence of individually budgeted hooks before their combined allowed runtime expires.
  • [P1] Hooks appended after dispatcher creation can still reproduce the indefinite stall this PR is intended to prevent.
  • [P1] The new option is exported through the plugin SDK reply runtime, so merging the current shape creates a compatibility-sensitive plugin API contract without proving that this is the correct permanent owner boundary.
  • [P1] The exact head has no deterministic repository regression coverage for aggregate timing, appended hooks, disablement, or timer cleanup.

Maintainer options:

  1. Fix true per-hook enforcement (recommended)
    Move deadline enforcement to every actual hook execution, including appended hooks, preserve declared budgets, avoid an unnecessary public plugin option, and add focused regression coverage.
  2. Bound only the confirmed owner
    Identify the actual hanging WhatsApp pipeline hook and apply recovery at that owner if a generic dispatcher policy cannot preserve existing hook contracts.
  3. Pause for API direction
    Pause the PR if maintainers intend beforeDeliverTimeoutMs to become a supported plugin SDK contract rather than an internal recovery detail.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Enforce timeout per actual beforeDeliver hook, cover hooks appended after dispatcher creation, preserve hook-declared budgets, keep the policy internal unless an API is required, and add deterministic regression tests.

Next step before merge

  • [P2] The blocking defects are concrete and localized to timeout granularity, appended-hook coverage, plugin API exposure, and deterministic regression tests; real WhatsApp proof must still come from a contributor or maintainer environment.

Security
Cleared: The two-file patch adds no dependency, workflow, permission, secret, artifact download, package-resolution, or third-party code-execution change.

Review findings

  • [P1] Enforce the timeout per actual hook — src/auto-reply/reply/reply-dispatcher.ts:188-196
  • [P1] Cover hooks appended after dispatcher creation — src/auto-reply/reply/reply-dispatcher.ts:189-190
Review details

Best 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 beforeDeliver keeps the serialized sendChain and waitForIdle() pending indefinitely. The exact live WhatsApp hook identity and trigger are still unknown.

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:

  • [P1] Enforce the timeout per actual hook — src/auto-reply/reply/reply-dispatcher.ts:188-196
    The normal inbound path passes a sequentially composed function here, so this single 30-second race is an aggregate budget, not a per-hook budget. Two valid hooks can each stay within their own registered timeout yet have the second reply cancelled when their combined runtime crosses 30 seconds. Preserve the existing registration-specific budgets by applying recovery around each hook execution rather than around the composed chain.
    Confidence: 0.94
  • [P1] Cover hooks appended after dispatcher creation — src/auto-reply/reply/reply-dispatcher.ts:189-190
    This wrapper captures only options.beforeDeliver during construction, but appendBeforeDeliver later composes additional hooks outside the wrapper. A never-settling appended hook still leaves sendChain and waitForIdle() pending forever, so the central deadlock remains reachable. Apply the deadline in the execution/composition path that every initial and appended hook traverses.
    Confidence: 0.97

Overall correctness: patch is incorrect
Overall confidence: 0.95

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P0: The linked report describes a non-technical channel user losing replies and requiring gateway shutdown plus manual session-file editing to recover the blocked lane.
  • merge-risk: 🚨 compatibility: The default aggregate deadline and exported dispatcher option can override existing hook timing contracts and establish a new plugin API surface.
  • merge-risk: 🚨 message-delivery: The aggregate timer can cancel valid replies, while uncovered appended hooks can still block all later delivery indefinitely.
  • merge-risk: 🚨 availability: Because appended hooks remain unbounded, merging the PR does not reliably eliminate the process-level lane stall it is intended to recover.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦐 gold shrimp and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The exact-head terminal script proves cancellation of one synthetic constructor-time hook, but it uses a separate follow-up dispatcher and does not prove composed budgets, appended hooks, or recovery of the reported owning WhatsApp lane; add redacted live logs or a faithful same-lane transcript, then update the PR body to trigger review, or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +31, Other +78. Total +109 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 31 0 +31
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 78 0 +78
Total 2 109 0 +109

Acceptance criteria:

  • [P1] node scripts/run-vitest.mjs src/auto-reply/reply/before-deliver.test.ts.
  • [P1] node scripts/run-vitest.mjs src/auto-reply/dispatch.test.ts.
  • [P1] git diff --check.

What I checked:

  • Aggregate timeout implementation: The PR wraps options.beforeDeliver once with a 30-second default, so the normal composed sequence shares one aggregate deadline rather than receiving a deadline per hook. (src/auto-reply/reply/reply-dispatcher.ts:188, c0e55bffca80)
  • Normal hook composition: Current main sequentially combines reply-payload and message-sending hooks before passing one function to the dispatcher; caller-supplied hooks are also composed with the reply-payload hook. (src/auto-reply/dispatch.ts:449, b597a8d364f2)
  • Appended-hook gap: The dispatcher can append hooks after construction by composing them around the current function, but the PR creates its timeout wrapper only during construction; an appended never-settling hook therefore remains unbounded. (src/auto-reply/reply/reply-dispatcher.ts:351, c0e55bffca80)
  • Existing timeout contract: The hook runner already supports registration-specific modifying-hook deadlines and documents that timeout skips the hook without cancelling its underlying work, so an outer aggregate default must not replace those individual budgets. (src/plugins/hooks.ts:200, b597a8d364f2)
  • Exact-head proof limitation: The proof exercises one synthetic constructor-time hook, then creates a separate clean dispatcher for the follow-up; it does not cover multiple composed hooks, appended hooks, or the reported owning WhatsApp lane. (scripts/proof/before-deliver-timeout.mts:17, c0e55bffca80)
  • Regression-test surface: Relative to the PR base, the exact head changes one production file and adds one proof script; no repository test file is changed despite the PR body citing existing tests. (src/auto-reply/reply/before-deliver.test.ts:1, c0e55bffca80)

Likely related people:

  • Peter Steinberger: History shows the central reply dispatcher, lifecycle ownership, hook composition, and settled-delivery paths largely date to or were recently maintained by his commits. (role: recent area contributor; confidence: high; commits: 13eb9c9ee9, d5e25e0ad8, 5635d74ecc; files: src/auto-reply/reply/reply-dispatcher.ts, src/auto-reply/dispatch.ts, src/auto-reply/dispatch-dispatcher.ts)
  • Vincent Koc: History shows repeated reply-dispatch type extraction, cycle refactors, and error-lifecycle work across the same central surfaces. (role: adjacent area contributor; confidence: medium; commits: 796d4dfc49, 3b4de1ac14, 7c91d0dbc9; files: src/auto-reply/reply/reply-dispatcher.ts, src/auto-reply/dispatch.ts, src/auto-reply/reply/dispatch-from-config.ts)
  • cpojer: File history contains repeated earlier changes to the reply-dispatcher and dispatch-ordering surfaces, making this a secondary context route. (role: adjacent area contributor; confidence: low; commits: d0cb8c19b2, 6b0d6e2540, 5ceff756e1; files: src/auto-reply/reply/reply-dispatcher.ts, src/auto-reply/dispatch.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 (4 earlier review cycles)
  • reviewed 2026-07-10T14:51:22.614Z sha 3b54553 :: needs real behavior proof before merge. :: [P1] Honor configured hook timeouts before cancelling delivery | [P2] Use fake timers for the timeout regression
  • reviewed 2026-07-10T15:13:43.449Z sha 7591750 :: needs real behavior proof before merge. :: [P1] Preserve existing hook timeout budgets | [P2] Use deterministic timers for the timeout regression
  • reviewed 2026-07-10T16:13:47.508Z sha cce108e :: needs real behavior proof before merge. :: [P1] Preserve registered hook timeout budgets | [P2] Clear the losing timeout timer
  • reviewed 2026-07-10T17:03:55.209Z sha 6bf19ad :: needs real behavior proof before merge. :: [P1] Preserve registered hook timeout budgets | [P2] Clear the timeout when a hook rejects

@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from 30ab52b to f7c3b8d Compare July 10, 2026 14:56
@ZOOWH ZOOWH changed the title fix(reply): add 30s timeout to beforeDeliver to prevent lane deadlock (#103684) fix(reply): add configurable beforeDeliver timeout to prevent lane deadlock (#103684) Jul 10, 2026
@ZOOWH

ZOOWH commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Addressed both P1 findings:

  1. Configurable timeout — replaced hardcoded 30s with options.beforeDeliverTimeoutMs (default 30s). Channels with hook budgets exceeding 30s can now configure a longer timeout via createReplyDispatcher({ beforeDeliverTimeoutMs: ... }).

  2. Regression tests — added test for the configurable timeout (1s custom timeout unblocks in ~1s). The hung-hook test continues to verify the default 30s path.

node scripts/run-vitest.mjs src/auto-reply/reply/before-deliver.test.ts
Tests  9 passed (9)

@clawsweeper

clawsweeper Bot commented Jul 10, 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:

@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Jul 10, 2026
@ZOOWH ZOOWH changed the title fix(reply): add configurable beforeDeliver timeout to prevent lane deadlock (#103684) fix(reply): add 30s timeout to combineBeforeDeliverHooks composer (#103684) Jul 10, 2026
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: M and removed size: S labels Jul 10, 2026
@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from e65a6c6 to 739c9ef Compare July 10, 2026 15:40
@openclaw-barnacle openclaw-barnacle Bot added size: S and removed gateway Gateway runtime size: M labels Jul 10, 2026
@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from 0dba835 to 171717a Compare July 10, 2026 15:47
@ZOOWH ZOOWH changed the title fix(reply): add 30s timeout to combineBeforeDeliverHooks composer (#103684) fix(reply): add per-hook timeout in combineBeforeDeliverHooks to prevent lane deadlock (#103684) Jul 10, 2026
@clawsweeper

clawsweeper Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

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.

@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from 171717a to cce108e Compare July 10, 2026 15:58
@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. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 10, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 10, 2026
@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch 2 times, most recently from 89fce59 to 3c73e51 Compare July 10, 2026 16:20
@openclaw-barnacle openclaw-barnacle Bot added size: XS and removed scripts Repository scripts size: S labels Jul 10, 2026
@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from 3c73e51 to 8319f9f Compare July 10, 2026 16:23
@ZOOWH ZOOWH changed the title fix(reply): add per-hook timeout in combineBeforeDeliverHooks to prevent lane deadlock (#103684) fix(reply): add per-hook timeout with timer cleanup in combineBeforeDeliverHooks (#103684) Jul 10, 2026
@ZOOWH

ZOOWH commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Fixed all findings:

  • Timer leak: clearTimeout after race completes
  • Export: combineBeforeDeliverHooks exported with comment for proof
  • Proof: node --import tsx calls real composer, 30s timeout verified
$ node --import tsx scripts/proof/before-deliver-timeout.mts
VERDICT: PASS

$ node scripts/run-vitest.mjs src/auto-reply/reply/before-deliver.test.ts
Tests  7 passed (7)

Diff: 1 production file, 1 proof script.

@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Jul 10, 2026
@clawsweeper

clawsweeper Bot commented Jul 10, 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:

@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from 8319f9f to 6bf19ad Compare July 10, 2026 16:28
ZOOWH and others added 4 commits July 11, 2026 01:16
- 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]>
@ZOOWH
ZOOWH force-pushed the fix/103684-before-deliver-timeout branch from 6bf19ad to c0e55bf Compare July 10, 2026 17:16
@ZOOWH

ZOOWH commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Moved timeout to createReplyDispatcher boundary:

  • try/finally timer cleanup (rejection-safe)
  • beforeDeliverTimeoutMs option (default 30s, 0 = disable)
  • logWarn on timeout (no longer silent)
  • combineBeforeDeliverHooks stays internal (no export)
  • Proof drives createReplyDispatcher -> waitForIdle() -> follow-up dispatch

OVERALL: ALL PASSED
Tests 7 passed (7)

@clawsweeper

clawsweeper Bot commented Jul 10, 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:

@ZOOWH

ZOOWH commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Closing: structural issues require maintainer-level design decisions (per-hook timeout granularity, appendBeforeDeliver coverage, SDK boundary). Maintainer can implement directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P0 Emergency: data loss, security bypass, crash loop, or unusable core runtime. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. scripts Repository scripts size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Final reply is never delivered: dispatcher.waitForIdle() blocks forever on a beforeDeliver hook, and the lane stays blocked

1 participant