Found while verifying the 17.0.0-rc.1 checklist on #3909 (section C — "Screen flows (#3528): … visibleWhen honored in render+validation"). Verified on main @ 1ee48bc60 with a purpose-built two-field screen flow.
The render half works. There is no validation half — POST …/runs/:runId/resume accepts anything.
Specimen
{ id: 'ask', type: 'screen', label: 'Triage', config: { fields: [
{ name: 'kind', label: 'Kind', type: 'text', required: true },
{ name: 'escalation_reason', label: 'Escalation reason', type: 'text',
required: true, visibleWhen: "kind == 'escalate'" },
]}}
Render — correct
The trigger response and GET …/runs/:runId/screen both carry the predicate intact, so a renderer has what it needs:
{"nodeId":"ask","title":"Triage","fields":[
{"name":"kind","label":"Kind","type":"text","required":true},
{"name":"escalation_reason","label":"Escalation reason","type":"text",
"required":true,"visibleWhen":"kind == 'escalate'"}]}
Resume — accepts every shape
| resumed with |
expected |
actual |
{"kind":"normal"} — conditional field hidden |
accept |
200 accept ✅ |
{"kind":"escalate"} — conditional field VISIBLE and required, value missing |
reject |
200 accept ❌ |
{} — kind itself required, unconditional, missing |
reject |
200 accept ❌ |
{"kind":"normal","totally_bogus":"x"} — undeclared key |
reject |
200 accept ❌ |
Every one completes the run (success: true, both nodes status: "success").
So the checklist's "honored in validation" reads as passing only vacuously: nothing is validated, so the conditional-required case cannot be got wrong. A client that skips the dialog and posts to resume directly bypasses every required the flow author declared.
Why this matters here specifically
Screen flows are the one place where the declared field contract is the only contract — there is no object schema behind a screen node to catch it downstream. The platform enforces the analogous contracts everywhere else this train touched: action params are strict by default with required/option/unknown-key checks (ADR-0104 D2, #4213 — verified working), record writes enforce required with per-field errors (ADR-0113, #4031 — verified), approval decisionOutputs reject a missing required key and an undeclared one (#3447 — verified, with the declared keys listed in the error). Screen-flow resume is the gap in that row.
Expected
resume validates the submitted bag against the suspended node's declared fields, evaluating visibleWhen against the submitted values first: a hidden field's required does not fire, a visible one's does, and an undeclared key is refused — the same shape enforceActionParams already produces (Invalid action params: …, 400).
Part of the #3909 rc.0/rc.1 verification (section C).
Found while verifying the 17.0.0-rc.1 checklist on #3909 (section C — "Screen flows (#3528): …
visibleWhenhonored in render+validation"). Verified onmain@1ee48bc60with a purpose-built two-field screen flow.The render half works. There is no validation half —
POST …/runs/:runId/resumeaccepts anything.Specimen
Render — correct
The trigger response and
GET …/runs/:runId/screenboth carry the predicate intact, so a renderer has what it needs:{"nodeId":"ask","title":"Triage","fields":[ {"name":"kind","label":"Kind","type":"text","required":true}, {"name":"escalation_reason","label":"Escalation reason","type":"text", "required":true,"visibleWhen":"kind == 'escalate'"}]}Resume — accepts every shape
{"kind":"normal"}— conditional field hidden{"kind":"escalate"}— conditional field VISIBLE and required, value missing{}—kinditself required, unconditional, missing{"kind":"normal","totally_bogus":"x"}— undeclared keyEvery one completes the run (
success: true, both nodesstatus: "success").So the checklist's "honored in validation" reads as passing only vacuously: nothing is validated, so the conditional-required case cannot be got wrong. A client that skips the dialog and posts to
resumedirectly bypasses everyrequiredthe flow author declared.Why this matters here specifically
Screen flows are the one place where the declared field contract is the only contract — there is no object schema behind a screen node to catch it downstream. The platform enforces the analogous contracts everywhere else this train touched: action params are strict by default with required/option/unknown-key checks (ADR-0104 D2, #4213 — verified working), record writes enforce
requiredwith per-field errors (ADR-0113, #4031 — verified), approvaldecisionOutputsreject a missing required key and an undeclared one (#3447 — verified, with the declared keys listed in the error). Screen-flow resume is the gap in that row.Expected
resumevalidates the submitted bag against the suspended node's declared fields, evaluatingvisibleWhenagainst the submitted values first: a hidden field'srequireddoes not fire, a visible one's does, and an undeclared key is refused — the same shapeenforceActionParamsalready produces (Invalid action params: …, 400).Part of the #3909 rc.0/rc.1 verification (section C).