The turn door and the launch door judge inputs identically
The rule
- A session turn's
inputsare checked exactly as a launch's are: an undeclared key is refused, a declared required input is enforced, values are checked against the declared schema, and resolution into the run's initial data is strict. - The same body earns the same verdict at both doors.
- A turn must not accept, by merging raw caller input into the run's initial data, anything the launch door would refuse.
- A turn whose inputs are refused is refused with 400.
What it means
A workflow can be run through two doors: launched directly, or driven a turn at
a time inside a session. The author decided what the workflow accepts once, by
declaring its inputs (MAN-1). This rule says that decision means one thing
regardless of which door a caller comes through. The four checks the launch door
runs (MAN-13, MAN-15) are the four checks the turn door runs, on the same
inputs object, with the same answer.
The clause about merging is the one that bites. A turn carries its inputs
alongside the message, and the shortest implementation copies them straight into
the run's initial data. That is a second, unguarded door: a caller who knows a
workflow's internal node-keyed shape (chat_input.1, say) could seed state the
author never published, at the turn door, when the launch door would have
refused the identical body. Not a declared input name is not a declared input
name, whichever door it arrives at.
"Identically" is a property of the whole surface, not of the cases someone happened to test at both doors. Two checkers maintained side by side drift exactly where one door has a case the other has not seen; the observable requirement is that no such case exists.
Example
The workflow declares one input, greeting. The same three inputs objects,
sent as a turn, get the verdicts the launch door gives them.
POST /api/flowdrop/session/{session}/turn
{"content": "hi", "inputs": {"not_a_declared_input": "x"}}POST /api/flowdrop/session/{session}/turn
{"content": "hi", "inputs": {"chat_input.1": {"message": "seeded"}}}POST /api/flowdrop/session/{session}/turn
{"content": "hi", "inputs": {"greeting": "hello"}}The first refusal names the inputs the workflow does accept, as MAN-13 requires
of the launch door. A turn with no inputs at all is valid: absent is not the
same as undeclared.