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
[finding] action-execution.ts:1474 silently skips a nullish id — a SECOND undeclared tolerance in the very ActionEngineFacade.delete arm #15117 just made declared, while the test comment beside it says "never a null id" #17620
Filed unassigned by the domain:spec execution seat, out of the at-tier contract review of PR #17608 (card #15117). Recording only — no severity asserted, routing and grading are triage's.
The site
packages/runtime/src/action-execution.ts:1474, inside the ActionEngineFacade.delete arm:
⇒ A nullish element is silently skipped. Nothing refuses it, nothing warns, and the call resolves as though the deletion happened.
⭐ Why this is worth a card rather than a shrug
PR #15117 / #17608 exists to turn an undeclared runtime tolerance into a declared contract: the array form was accepted by the runtime, absent from the published type, and recorded only in a comment describing it as "an accident of two handler suites". That PR fixed exactly that.
⇒ ⭐ And this is a second undeclared tolerance sitting in the same arm — the one the card just finished making declared. The published type after #17608 is string | string[], so no typed caller reaches the guard; the reviewer was explicit that the new contract sentence is therefore not false. But an untyped caller (a JS host, a registerAction handler whose slot is still (ctx: any)) does reach it, and gets a silent no-op.
⚠️ The test's own comment at packages/spec/src/ui/action-params.test.ts:517 says "never a null id" — pinning the type while the runtime quietly accepts one. ⇒ The declaration and the implementation disagree about the same value, in the same direction the original card was filed about.
The two candidate dispositions (⛔ neither chosen here)
Remove it — if it is not intended, the guard should go, so a nullish element fails loudly instead of vanishing.
⇒ Which one is right is a question about whether that tolerance was ever a decision. ⚠️ ADR-0049's enforce-or-remove shape applies, and the answer is not this seat's to give.
⛔ No claim this reproduces for a typed caller. It does not; the population is untyped hosts.
⛔ No claim about when the guard was added or why. The seat did not read its history — asserting intent without that reading would be the same fault this card is about.
⛔ No sweep for sibling nullish guards elsewhere in the facade was run; asserting the rest are clean would be a zero nobody measured.
Filed unassigned by the
domain:specexecution seat, out of the at-tier contract review of PR #17608 (card #15117). Recording only — no severity asserted, routing and grading are triage's.The site
packages/runtime/src/action-execution.ts:1474, inside theActionEngineFacade.deletearm:⇒ A nullish element is silently skipped. Nothing refuses it, nothing warns, and the call resolves as though the deletion happened.
⭐ Why this is worth a card rather than a shrug
PR #15117 / #17608 exists to turn an undeclared runtime tolerance into a declared contract: the array form was accepted by the runtime, absent from the published type, and recorded only in a comment describing it as "an accident of two handler suites". That PR fixed exactly that.
⇒ ⭐ And this is a second undeclared tolerance sitting in the same arm — the one the card just finished making declared. The published type after #17608 is
string | string[], so no typed caller reaches the guard; the reviewer was explicit that the new contract sentence is therefore not false. But an untyped caller (a JS host, aregisterActionhandler whose slot is still(ctx: any)) does reach it, and gets a silent no-op.packages/spec/src/ui/action-params.test.ts:517says "never a null id" — pinning the type while the runtime quietly accepts one. ⇒ The declaration and the implementation disagree about the same value, in the same direction the original card was filed about.The two candidate dispositions (⛔ neither chosen here)
⇒ Which one is right is a question about whether that tolerance was ever a decision.⚠️ ADR-0049's enforce-or-remove shape applies, and the answer is not this seat's to give.
What this does NOT claim
domain:speclabel set,state=all, since 2026-07-01 ⇒ 400 issues ([security] datasource credential in a nested config position is served in cleartext on read — redaction is top-level-key-only #13405–The ADR-0087 ledger entry for the aggregate x field-type refusal says "no non-temporal pair changes behaviour" — PR #17559 makes that false, andobjectstack migrate meta/ the upgrade guide are its only readers #17561) grepped forid != null(0),buildActionEngineFacade(3:ActionEngineFacade.find(object, query)takes a bare filter whileinsert/update/deletetake explicit shapes — the type says neither, and reading it wrong returns empty with no error #14175 closed, ActionEngineFacade.delete declares id: string while the runtime facade accepts string | string[] and examples/app-todo relies on the array form through a hand-rolled context type #15117 itself, finding(spec):ActionEngineFacade.find'sFilterConditionslot still admits the ObjectQL envelope{ where: … }at compile time — closing the bar is a vocabulary claim (no field namedwhere) the spec does not declare #15124 — a different member),action-execution.ts(6, none about this guard). Lit controls on the same corpus:ActionEngineFacade→ 3,ActionHandlerContext→ 1 ⇒ the zeros are readings.since=2026-07-01and the corpus ceiling The ADR-0087 ledger entry for the aggregate x field-type refusal says "no non-temporal pair changes behaviour" — PR #17559 makes that false, andobjectstack migrate meta/ the upgrade guide are its only readers #17561.domain:*.packages/runtime⇒ likelydomain:cli, ⛔ not this seat's call.Refs: #15117 / PR #17608 (where it was found, and the tolerance it did declare).
domain:specexecution seat ·session_01MkQhmuuJAVDjmeWNixwDDH· filed 2026-09-11T04:18ZGenerated by Claude Code