--- id: MEM-16 family: GR-MEM level: extended profiles: [storage-api, runtime] posture: normative-target added: "1.0" changed: "1.0" source: https://flowdrop.io/spec/rules/gr-mem/mem-16 specification: FlowDrop Workflow Specification 1.0-draft licence: CC BY 4.0 --- # MEM-16 — Text becomes a message under three closed rules *GR-MEM (Part I) · level: extended · profiles: storage-api, runtime · added in 1.0* The adapter every text producer needs to reach a message-shaped node. Its defaults are chosen so a malformed turn never reaches a provider. ## The rule > **Normative.** This is the rule. > > 1. A text-to-message node turns a text value and a role into one message, emitted both as a one-element message-typed list and as the same row on a plain object-typed output. > > 2. Content is coerced exactly as conversation normalization coerces it: a scalar is cast, a non-scalar is serialized to text rather than blanked, and unserializable content falls back to the empty string. > > 3. Three rules are closed. > > 4. The role is one of `user`, `assistant`, `system` or `tool`; any other value, an absent one included, resolves to `user`, so a role a provider would reject never leaves the node. > > 5. A tool call id is attached only on role `tool`, trimmed, and omitted when blank; on any other role it names no pairing and is discarded. > > 6. Empty content emits an empty list and an empty object rather than an empty turn, so an unwired producer cannot become a silent request on a blank prompt, a `tool` row with no content included, because synthesizing an answer for an unanswered call belongs to tool-pairing repair. ## What it means The node closes three questions an author is not asked to answer for every piece of text a workflow turns into a message. A role a provider would reject — an unrecognised value, or none at all — never reaches the message; it becomes `user` instead of a validation error, so a producer that forgets to set a role, or sets one from a value the author never anticipated, still emits something a provider will accept. A pairing id only means something on a `tool` row: on any other role it is discarded rather than carried through, because attaching it there would claim a pairing that is not real. And a blank pairing id on a `tool` row is treated as no id at all, not as an empty one. ## Example The same text and role pair, differing only in the role's provider validity. ```json title="Text declared with a role no provider defines" {"text": "hello", "role": "operator"} ``` ```json title="What the node emits" verdict="normalized" {"role": "user", "content": "hello"} ``` A pairing id attached where only a `tool` row can carry one: ```json title="A pairing id declared on a user-role row" {"text": "hello", "role": "user", "tool_call_id": "call_1"} ``` ```json title="What the node emits" verdict="discarded" {"role": "user", "content": "hello"} ``` ## Related rules - Names: MEM-3, MEM-4, MEM-5 - Referenced by: MEM-4, MEM-5 --- 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.