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
feat(spec)!: retire waitEventConfig.timeoutMs / .onTimeout — wait never had a timeout (#4158)
Both keys described a timeout and neither delivered one, so protocol 18 removes
the pair rather than leaving a promise the runtime does not keep (PD #10).
`onTimeout` had ZERO readers. No path ever inspected it, so neither 'fail' nor
'continue' ever happened — and its `.default('fail')` stamped a decision nothing
made onto every wait node. The showcase set `onTimeout: 'continue'`, which did
nothing; it is removed here.
`timeoutMs` said "maximum wait time before timeout" while its only reader used it
as the timer DURATION when `timerDuration` was absent. It did something, just not
what it claimed.
Together they declared a timeout `wait` does not have: a run resumes when its
timer elapses or its signal arrives, never on a deadline. Real timeout semantics
are left unimplemented deliberately — they should be built to a requirement, not
retrofitted to fit two keys that happened to be declared.
`timeoutMs` CONVERTS to `timerDuration` rather than being dropped, because that
is what it did — stringified on the way, since the target is `z.string()` while
`timeoutMs` was `z.number()` and `parseIsoDuration` reads a bare numeric string as
milliseconds. Moving the number unstringified would have produced a block that no
longer parses; a test pins that. With `timerDuration` already set it is dropped
instead, having been dead metadata the executor's `??` never reached.
Both leave the load path, which is the split the registry already draws: a key
retired for being RENAMED keeps a load window, because punishing an author for a
spelling nobody warned them about is pointless; a key that MISDESCRIBED itself
does not, because silently absorbing it lets the author keep believing they
configured a timeout. `api.requireAuth`, the tool/app/flow inert keys and RLS
`priority` all left it for the same reason.
Three existing tests asserted the opposite — that both keys parse, and that
`onTimeout` defaults to 'fail'. They were encoding a contract the runtime never
honoured, so they are flipped rather than deleted: the accept cases drop the
retired keys, and a new case asserts they are REJECTED with a prescription rather
than silently stripped.
One fixture interaction, caught by the harness itself: the #4045 lift fixture used
`waitEventConfig.timeoutMs` for its fourth ledger entry, and the fixture harness
replays the whole table — so its `after` described an end state protocol 18 makes
unreachable. It lifts `eventType` instead.
Verified: spec 274 files / 7144 tests, service-automation 460, all 8 generated
artifacts current (`check:generated`), root gates pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QoA8AV99Ss1RRkLLAYmDzq
|**outputSchema**|`any`| optional |[REMOVED]`flow.nodes[].outputSchema` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — it was never validated: the engine does not check node outputs against it, so it documented a contract nothing enforced. Delete the key. Downstream nodes read prior outputs via expressions (`{{nodeId.field}`}) regardless of any declaration. |
0 commit comments