You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
From the improvement opportunities of the 2026-08 architecture review. #52 documented the behavior; this is the ergonomic follow-up.
Problem
Timeout reads as a scheduling primitive and is not one. It is a plain condition: it compares getLastStateHasChangedDate() + timeoutMs against Date.now() when something asks. Nothing fires at the deadline. A transition guarded by Timeout fires only when someone calls checkTransitions() after the deadline has passed.
The name invites the wrong mental model, and the failure is silent — the workflow simply never advances, which is easy to miss in development where a test calls checkTransitions() explicitly and hard to diagnose in production where nothing does. #52 added an explicit warning and a polling snippet to docs/conditions.md, but every user still hand-rolls the same loop.
Proposed direction
A small opt-in driver, kept out of the core so the zero-dependency, runtime-agnostic guarantee holds:
Skip a tick while the machine is still draining (coordinate with whenIdle() / the isRunning() accessor proposed in Expose queue depth and runner state for observability #60) so slow operations cannot pile up overlapping checkTransitions() calls.
Route rejections to a caller-supplied handler; an unhandled rejection inside a timer is a bad default.
unref() the timer under Node so it cannot hold a process open — behind a capability check, not a bare Node import.
Consider deriving the next tick from the nearest Timeout deadline instead of a fixed interval, if that can be done without coupling the driver to condition internals.
An alternative worth weighing first: rename or alias the condition (ElapsedSince?) so the name stops implying a scheduler at all. That is breaking, so it belongs with the v5 batch if chosen — but it may be the more honest fix, with the helper as a complement rather than a substitute.
Acceptance criteria
A documented way to drive timeout transitions without hand-rolling a polling loop.
Core remains dependency-free and free of unconditional Node built-ins.
Overlapping ticks cannot queue up behind a slow operation.
docs/conditions.md points at the helper from the existing warning.
From the improvement opportunities of the 2026-08 architecture review. #52 documented the behavior; this is the ergonomic follow-up.
Problem
Timeoutreads as a scheduling primitive and is not one. It is a plain condition: it comparesgetLastStateHasChangedDate() + timeoutMsagainstDate.now()when something asks. Nothing fires at the deadline. A transition guarded byTimeoutfires only when someone callscheckTransitions()after the deadline has passed.The name invites the wrong mental model, and the failure is silent — the workflow simply never advances, which is easy to miss in development where a test calls
checkTransitions()explicitly and hard to diagnose in production where nothing does. #52 added an explicit warning and a polling snippet todocs/conditions.md, but every user still hand-rolls the same loop.Proposed direction
A small opt-in driver, kept out of the core so the zero-dependency, runtime-agnostic guarantee holds:
Design points worth settling:
whenIdle()/ theisRunning()accessor proposed in Expose queue depth and runner state for observability #60) so slow operations cannot pile up overlappingcheckTransitions()calls.unref()the timer under Node so it cannot hold a process open — behind a capability check, not a bare Node import.Timeoutdeadline instead of a fixed interval, if that can be done without coupling the driver to condition internals.An alternative worth weighing first: rename or alias the condition (
ElapsedSince?) so the name stops implying a scheduler at all. That is breaking, so it belongs with the v5 batch if chosen — but it may be the more honest fix, with the helper as a complement rather than a substitute.Acceptance criteria
docs/conditions.mdpoints at the helper from the existing warning.