PR #679 implemented the swap and falsified the note's parity premise: vitest's fake clock does not intercept node:timers/promises, so the change traded deterministic fast tests (llm-retry ~4s->~10s real sleeps, two pty teardown tests rewritten real-time, a weakened workflow grace-timer guard) for ~10 deleted lines. Moved the note proposed -> rejected with the verdict on the Status line; the frozen proposal body is kept per the rejected-lifecycle contract.
2.7 KiB
Agent Note: Use node:timers/promises for hand-rolled cancellable sleeps
Status: rejected — implementation (PR #679) falsified the parity premise: vitest's fake clock does not intercept node:timers/promises, so the swap costs deterministic fast tests for ~10 deleted lines
English | 中文
Problem
Three packages hand-roll promise-wrapped timers that the node:timers/promises builtin already provides, while other packages (dsh-llm-mock-server pause(), dsh-lsp-local, dsh-acp-snapshot) already use the builtin — so the hand-rolled copies are also a consistency gap:
packages/llm/llm-retry/src/index.tscancellableDelay()(~14 lines):new Promise+setTimeout+ manual abort-listener add/remove, resolvingtrueon elapse andfalseon abort, consumed once for the backoff wait.packages/workflow/workflow-workerthread/src/host.tssleep()(~7 lines): promise-wrapped unref'dsetTimeoutused as the dispose-grace bound.packages/pty/pty-local/src/session.tsdelay()(~4 lines): bare promise-wrappedsetTimeoutused in polling/teardown waits.
Proposal
Replace all three with import { setTimeout } from 'node:timers/promises':
- llm-retry:
try { await setTimeout(delayMs, undefined, { signal }); /* retry */ } catch { /* abort → fail */ }— with a signal, the promise rejects only with the abort error, and a pre-aborted signal rejects immediately; behavior is identical, including timer clearing on abort. The emptycatchnames the abort rejection per the repo's empty-catch rule. - workflow-workerthread:
setTimeout(ms, undefined, { ref: false })— exact semantics including not holding the event loop open. - pty-local:
import { setTimeout as delay } from 'node:timers/promises'— identical signature, call sites unchanged.
No dedicated tests pin the helpers themselves; the packages' behavior suites keep passing.
Alternatives considered
p-timeout/p-deferstyle packages. Rejected: the builtin covers both call sites exactly; an external package for a one-line await is negative-net.- Leave them. Rejected only weakly — the cost is small, but the repo already uses the builtin idiom elsewhere, and two hand-rolled variants of a builtin invite a third.
Acceptance criteria
- None of the three packages defines a promise-wrapped
setTimeouthelper; all import fromnode:timers/promises. - The
llm-retry,workflow-workerthread, andpty-localtest suites pass unchanged (behavioral parity).
Risks
Essentially none: no model-visible output, no platform concerns, no new dependency. The llm-retry rewrite changes a boolean-returning helper into try/catch control flow — a local readability judgment the implementing PR makes.