Skip to content

Bound post-deploy mail delivery so a stalled ack fails loud (CL-6644) - #316

Merged
TheGreatAxios merged 2 commits into
mainfrom
cl-6644-mail
Aug 22, 2026
Merged

Bound post-deploy mail delivery so a stalled ack fails loud (CL-6644)#316
TheGreatAxios merged 2 commits into
mainfrom
cl-6644-mail

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Contributor

CL-6644: https://linear.app/abklabs/issue/CL-6644/cold-wake-never-completes-and-its-failure-leaves-zero-log-trace

Per the ticket's last comment, #312/#313/#314/#315 traced the whole intake→fanout→wake chain healthy: recipients resolve, dispatch starts, the wake-deploy completes ("Deployed agent", "Wrote run grants") — then nothing. No mail line, no run start, no inference, no server notice. dispatchTurn's promise stayed pending in the one hop none of the earlier fixes bound: post-deploy mail delivery.

Root cause

sendFoldedMailWithReclaimRetry (packages/chat/src/platform-adapter.ts) retries a mail send that throws "agent is unreachable", with a ~7.75s backoff (RECLAIM_RETRY_DELAYS_MS). That backoff only runs between attempts that already failed loud. An attempt that instead stalls — a wedged sidecar ack, or anything else in sendFoldedMail's call chain that never settles — hangs the loop's await forever with nothing logged, because nothing ever throws. This matches the observed symptom exactly.

wakeByAddressBounded already puts a wall-clock bound on the wake step for the same reason (CL-6644 part B / #314); the mail-delivery step had no equivalent bound.

Fix

  • Extracted wakeByAddressBounded's timeout logic into a shared withTimeout helper.
  • Wrapped each sendFoldedMail attempt inside sendFoldedMailWithReclaimRetry in withTimeout, via a new injectable mailDeliveryTimeoutMs deps field (defaults to DEFAULT_WAKE_TIMEOUT_MS).
  • A stalled attempt now rejects with a clear message instead of hanging. The rejection is not treated as "agent is unreachable", so it isn't retried — it propagates through sendMail to dispatchTurnBatch's existing reportError catch (chat: log the undelivered-turn cause and thread a reportError refId (CL-6644, part A) #313), which posts the undelivered notice with a refId.

Test

Added a red/green test in packages/chat/test/platform-adapter.test.ts: makes sessionService.sendUserMessage return a promise that never settles, and asserts sendMail rejects within a short injected deadline instead of hanging. Verified red without the fix (test timed out with the fix reverted) and green with it (~1s).

Not run

Live repro against a real Ollama-backed stack (send → typing → real reply) — timeboxed, same as prior sessions on this ticket. The fix is proven by the unit test above plus code reading of the retry loop; a live session would additionally confirm nothing upstream of this hop still stalls silently.

Checks

  • WORKBENCH_CHECK_SINCE=origin/main bun run typecheck — pass
  • WORKBENCH_CHECK_SINCE=origin/main bun run test — pass (652 pass / 0 fail in @corbits/chat)
  • bun run lint — pass (pre-existing warnings only)

sendFoldedMailWithReclaimRetry's own retry delays only run between
attempts that already threw "agent is unreachable" — an attempt that
instead stalls forever without ever settling hangs the caller's await
with it. Proves the hang by making sessionService.sendUserMessage
never resolve and asserting sendMail rejects within a short injected
deadline instead of timing out the test.
Fixes the last hop in CL-6644's cold-wake chase: after a wake-deploy
completes, sendFoldedMailWithReclaimRetry's reclaim-retry loop only
bounds attempts that fail loud with "agent is unreachable" — an
attempt that instead stalls (a wedged ack, or anything else in
sendFoldedMail's call chain that never settles) hangs the loop's await
forever with nothing logged, matching the observed symptom exactly:
Deployed agent / Wrote run grants, then silence.

Wraps each attempt in the same kind of wall-clock bound
wakeByAddressBounded already puts on the wake itself (extracted as a
shared withTimeout helper), via a new injectable mailDeliveryTimeoutMs
deps field defaulting to DEFAULT_WAKE_TIMEOUT_MS. A stalled attempt now
rejects instead of hanging, propagating through sendMail to
dispatchTurnBatch's existing reportError catch (#313), which posts the
undelivered notice with a refId.
@TheGreatAxios
TheGreatAxios merged commit e13d5c9 into main Aug 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant