FlowDrop Workflow Specification 1.0-draft

The turn door and the launch door judge inputs identically

The rule

Normative: this is the rule
  1. A session turn's inputs are 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.
  2. The same body earns the same verdict at both doors.
  3. A turn must not accept, by merging raw caller input into the run's initial data, anything the launch door would refuse.
  4. 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.

An input the workflow never declared400 — refused
POST /api/flowdrop/session/{session}/turn

{"content": "hi", "inputs": {"not_a_declared_input": "x"}}
The internal node-keyed shape: not a declared name, so unknown400 — refused
POST /api/flowdrop/session/{session}/turn

{"content": "hi", "inputs": {"chat_input.1": {"message": "seeded"}}}
A declared input202 — accepted
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.

Rule identifiers are permanent and are never renumbered. Each implementation publishes its own standing against these rules; this specification does not.spec 1.0-draft · API-2 · changed in spec 1.0