test(cli): the owner loop's signaled-event resume path has no test
Problem
The CLI owner loop added by #3316 has one untested branch: a signal arriving while the owning process sleeps through an event wait.
awaitDurableWaitDeadline polls the run row every 5 s and returns 'resume' as soon as the cursor reports signaled (packages/cli/src/commands/workflow.ts:3505, cursor built at :3476-3484). The integration suite covers the neighbouring paths — a duration wait resuming at its deadline, two sequential waits under one owner, an event wait expiring at its deadline (status: 'expired'), an attention wait releasing its owner, and a wait another process released — but nothing drives a real signal into a run while its owner is asleep and asserts the wait completes satisfied.
Both reviewers on #3316 named it independently: the pack review recorded it under evidence it could not obtain, and CodeRabbit raised it as a Minor functional-correctness finding.
Why it matters
It is the success path of event waits under CLI ownership. Everything asserted today is a wait that ends because time ran out; nothing asserts a wait that ends because the thing it was waiting for actually happened. A regression that made the poll miss signaledAt — or that let the superseded-cursor guard at :3513 treat a signal as a released wait and return 'stop' — would leave the run parked until its deadline and still pass the whole suite, looking like a slow success rather than a broken one.
The guard is the specific risk. It compares stepName and resumeAt and stops the loop when either moves. A signal is expected to change neither, so the branch depends on an implementation detail of how signalling writes the wait context, with no test pinning it.
Why now
#3316 merged as c15da5166, so the code is on dev untested. The gap was accepted as non-blocking for that PR because /signal requires a running server, which puts the path outside the CLI-only scenario #3312 was about. That reasoning bounds the risk; it does not cover the combination that remains reachable — a server running while a foreground or detached CLI process owns the run — and it will stop applying if signalling ever gains a non-server route.
Desired outcome
An integration test starts a run that pauses on an event wait with a distant deadline, signals it through the ordinary path while the owning CLI process is mid-sleep, and asserts the run resumes and completes with the wait node satisfied — not expired, and not still paused.
Invariants
- The assertion is on observable run state (wait status and run terminal), not on the poll's internals, so a behaviour-preserving rewrite of the loop keeps passing.
- The deadline in the fixture is far enough out that expiry cannot be what completes the run; a test that passes because the deadline fired proves nothing.
- The existing event-wait expiry case stays, and stays distinguishable from this one.
- No production behaviour change is in scope here. If the test proves the branch broken, that is a separate fix.
Acceptance
- Seen red first: stubbing the
signaledshort-circuit atworkflow.ts:3505leaves the run paused until its deadline, and the new test fails. - After: the test passes against current
dev, and the run reaches terminal with the event waitsatisfiedwell before its deadline. - The superseded-cursor guard at
:3513is exercised by the same run rather than assumed — a signal must not read as a released wait.
Evidence
PR #3316 (merged as c15da5166), its review report, and CodeRabbit's outside-diff finding on packages/cli/src/commands/workflow.ts:3445-3575. Existing cases: packages/cli/src/commands/workflow-wait.integration.spec.ts:837, :863, :889, :935, :968.
Source: coleam00/Archon