--- id: MAN-21 family: GR-MAN level: extended profiles: [storage-api] posture: normative-target added: "1.0" changed: "1.0" source: https://flowdrop.io/spec/rules/gr-man/man-21 specification: FlowDrop Workflow Specification 1.0-draft licence: CC BY 4.0 --- # MAN-21 — A contract entry states the port's lane as well as its schema *GR-MAN (Part I) · level: extended · profiles: storage-api · added in 1.0* A port's lane and its JSON Schema type are two different vocabularies. Answering one with the other made a contract entry contradict the port it was bound to. ## The rule > **Normative.** This is the rule. > > 1. An interface entry states its bound port's lane and its structural schema separately: the lane is the port's declared or derived data-type lane, and the schema is the JSON Schema fragment. > > 2. The lane is never the fragment's `type`. > > 3. It is resolved once, when the contract is built, against the vocabulary the implementation actually serves, and is pinned into the stored fragment, so a reader sees the lane the workflow was published against rather than one that could drift since. > > 4. Reading the contract only reads that pin; a stored contract built before the pin existed falls back to the lane derivable from the fragment's type, which is never wider than the truth, and self-corrects on the workflow's next rebuild. > > 5. The schema emitted alongside it is the structural contract only: the pinned lane, the title, the description, the examples and property-level required flags are stripped on the way out, because the entry states each of them itself. > > 6. None of this changes what a caller must pass: the launch check reads `type` and `enum` from the stored contract and never consults this projection. ## What it means A port's lane and its JSON Schema type are two different vocabularies, and they diverge exactly where a port carries a domain-specific meaning over an ordinary structural shape: a list of conversation messages is, structurally, just an array. Reading the schema's `type` and calling that the lane would answer `array` for a case the lane can name precisely. The lane is resolved once, when the contract is built, and pinned into the stored fragment — it is not re-derived on every read. A contract built before the lane existed has no pin to read, so it falls back to the lane the schema's `type` implies, which is never wider than the truth, and corrects itself the next time the contract is rebuilt. The schema published alongside an entry is stripped of the lane, the title, the description and the examples on the way out, because the entry states each of those itself; none of it changes what the launch check reads, which is still `type` and `enum` on the stored contract, never this projection. ## Example A node produces two outputs: one declares a `messages` lane over a JSON Schema `array`, the other declares nothing over an `object`. ```http title="Two outputs whose declared lane is not their JSON Schema type" verdict="201 stored" POST /api/flowdrop/workflows {"name": "Lane WF", "nodes": [ … ], "interface": {"outputs": [ {"id": "out_list", "bindings": [{"nodeId": "msg1", "portId": "messages"}]}, {"id": "out_one", "bindings": [{"nodeId": "msg1", "portId": "message"}]} ]}} ``` ```json title="What the published contract reports for those two ports" { "outputs": [ {"id": "out_list", "dataType": "messages", "schema": {"type": "array"}}, {"id": "out_one", "dataType": "json", "schema": {"type": "object"}} ] } ``` The node type's own fragment carries more than the pin keeps: ```json title="A node type's own schema fragment for a bound port" { "type": "array", "title": "Messages", "x-data-type": "messages" } ``` ```json title="What the built contract stamps onto that fragment" verdict="pinned" { "type": "array", "x-data-type": "messages" } ``` ## Related rules - Names: MAN-5, MAN-16, MAN-18, MAN-20 - Referenced by: MAN-5, MAN-16, MAN-18, MAN-20 --- 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.