FlowDrop Workflow Specification 1.0-draft

The value check enforces type and enum, and declares the rest

A published contract says more than the launch boundary enforces. That is a real distinction and worth stating, so a caller does not read an unenforced keyword as a guarantee.

The rule

Normative: this is the rule
  1. The per-value stage of the launch check enforces exactly two things from a contract fragment: type and enum.
  2. Every other published key (minimum, maximum, minLength, maxLength, pattern, and nested properties or items) is declared in the contract and not enforced here, as is a type that is not a single string.
  3. The top-level required list is the one declared key that is enforced, and it is enforced by the earlier missing-input stage.
  4. Values are never coerced.
  5. Every structured type collapses to a single check that the value is a collection.
  6. The types meaning "any value" waive the type check entirely, matching every value including null and a collection, while still honouring enum; they are declared no-constraint types, and a boundary that refused what the runtime accepts would give a port's declared type two meanings.
  7. enum compares by identity, so a number and its string spelling are different values.
  8. Violations across several inputs are aggregated into one refusal, and an input with no contract fragment skips the value check.

What it means

A published contract fragment says more than the boundary checks. Past the required list — enforced earlier, by the stage that checks presence — the value stage reads exactly two keys: type and enum. minLength, maxLength, pattern, minimum, nested properties and items are all published, all declared, and none of them is enforced here. A caller who violates every one of them at once still gets through this stage.

A type meaning "any value" is not a stricter case of type — it waives the type check entirely, matching a collection, a scalar or null alike, while still honouring enum if one is published. Every structured type collapses to one check: is the value a collection at all. Nothing is coerced, and enum compares by identity, so a number and its string spelling are different values. An input with no contract fragment skips the value check outright, and violations across several inputs are aggregated into one refusal rather than answered one at a time.

Example

The workflow declares three inputs against three fragments: count against {"type": "integer"}, direction against {"type": "string", "enum": ["asc", "desc"]}, and variables against no type at all.

A string where the fragment declares an integerrefused
{ "count": "five" }
A value outside the fragment's enumrefused
{ "direction": "up" }
A structured value sent to a port with no declared typeaccepted
{ "variables": { "live_state": "{\"a\":1}" } }

A fourth input, code, is published against {"type": "string", "minLength": 8, "maxLength": 3, "pattern": "^\\d+$", "minimum": 5} — a fragment no single value could satisfy. It still launches:

A value violating every other declared constraint at onceaccepted
{ "code": "abcdef" }

Why

Recorded under OPEN-16.

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 · MAN-16 · changed in spec 1.0