Skip to content

[finding] a non-blank but unparseable or zero timerDuration still parks a wait node forever with success: true — the #17928 hole reached through a different door, and the tree already pins it as expected #18179

Description

@os-warren

Filed by the domain:spec execution seat (#6017), session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-14T10:4xZ, from the out_of_scope_findings of the #17928 round (report 5662691490, PR #18175). ⛔ Not claimed, ⛔ not dispatched. Grading is triage's; domain:spec is set from the measured landing point (packages/spec/src/automation/** + packages/services/service-automation/src/builtin/wait-node.ts), ⛔ not guessed from the title.

⚠️ This card exists because the tree claimed it already did. A fixture comment on the #17928 branch asserted this residual had been "Filed separately". It had not — no such card existed. Commit 14cb2a66 corrects the comment rather than leaving it asserting a filing nobody made; this card discharges it for real.

Class (a) — a reproducible defect

#17928 / PR #18175 closes the absent waitEventConfig hole: a wait node with no block is refused at the contract, and a stored one fails the run loudly instead of parking forever with success: true.

A non-blank but unparseable or zero-valued timerDuration still reproduces the original defect, unchanged.

parseIsoDuration answers undefined for 'not-a-duration', 'P', 'PT0S' and '0' exactly as it did for the absent key. On that path:

  • no deadline is computed,
  • no job is armed,
  • no waitUntil is persisted,
  • and the node still returns { success: true, suspend: true }.

⇒ The run reports success and parks forever, which is precisely the shape decision batch #127 item 5 ruled unacceptable — reached through a different door.

The repro is already live in the tree

packages/services/service-automation/src/builtin/wait-node.test.ts「cancels nothing for a timer wait that armed no job」, which asserts scheduled is empty on a paused run. ⇒ The behaviour is pinned today as expected; this card is the argument that the pin describes a defect rather than a contract.

⚠️ Source/test reading carried from the #17928 round, ⛔ not re-measured by this seat at filing time. A taker owes the re-measurement first.

Why it was NOT fixed in place by #17928

Stated by that round against the bounded in-place-fix criteria, and this seat agrees:

⇒ criteria 2 and 4 fail ⇒ out of scope, filed rather than folded. ⚠️ Whoever takes this should expect Clause-② to need its own judgement: refusing malformed durations narrows, but if the fix instead declares a new refinement or error code, that is a different answer.

Sibling recorded on the same round, deliberately NOT a second card

The four non-timer eventTypes carry no required resumer key of their own{ eventType: 'signal' } with no signalName parses, and the executor correlates on undefined. Same shape as the row above rather than an independent finding ⇒ this card is its carrier too. (Also recorded in PR #18175's ## Acceptance notes.)

Dedupe words

timerDuration · parseIsoDuration · malformed duration · silent suspend · wait node

⛔ This seat did not run a dedupe search (per 「立卡者不查重,只附查重词」). ⚠️ A duplicate check must include closed cards — #17939 was a closed duplicate of #17928 and carried measurements that mattered.

Refs

#17928 (the absent-block half, ruled) · PR #18175 (its implementation; this residual is in its Acceptance notes) · #17939 (closed duplicate of #17928, carries the original real-run measurement) · #17843 (the measurement request)


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:specpriority:p1High: required for production / M2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions