--- id: MAN-16 family: GR-MAN level: core profiles: [storage-api] posture: normative-target added: "1.0" changed: "1.0" source: https://flowdrop.io/spec/rules/gr-man/man-16 specification: FlowDrop Workflow Specification 1.0-draft licence: CC BY 4.0 --- # MAN-16 — The value check enforces type and enum, and declares the rest *GR-MAN (Part I) · level: core · profiles: storage-api · added in 1.0* 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. ```json title="A string where the fragment declares an integer" verdict="refused" { "count": "five" } ``` ```json title="A value outside the fragment's enum" verdict="refused" { "direction": "up" } ``` ```json title="A structured value sent to a port with no declared type" verdict="accepted" { "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: ```json title="A value violating every other declared constraint at once" verdict="accepted" { "code": "abcdef" } ``` ## Why Recorded under OPEN-16. ## Related rules - Names: MAN-5, MAN-15, MAN-21 - Referenced by: MAN-15, MAN-21 --- Rule identifiers are permanent and are never renumbered. This specification carries no implementation status: each implementation publishes its own standing against these rules. Licensed CC BY 4.0.