On this page
Architectural property
COADF P-1 states the minimum: a probabilistic component returns a value together with a confidence and the method the value was obtained by, never a bare value, and it never reaches an output directly. This pattern is about the second half, which is the half implementations lose. A model's answer may enter the system. What it may not do is become, by any path, the thing the system treats as established.
Three things have to hold at once:
- The output has a type of its own. A proposal and an authoritative record are different types, different tables or different messages. Code holding one cannot pass it where the other is expected without an explicit step somebody can review.
- The boundary validates at run time. The contract is checked when the reply arrives, in the process that receives it, whatever the sender says it checked.
- Provenance travels with the value. The method, the model and its revision, the prompt revision and the location in the source stay attached through every step, serialisation included. Without them P-4 cannot reconstruct and P-5 cannot disclose.
P-1 also has the component report a confidence. How confidence is expressed, assigned and used belongs to P-2, which COADF publishes as a principle only. This Companion does not describe it, and nothing below depends on it.
Why it matters
A model's output comes from a process that cannot be reproduced from its inputs the way a parser's output can: sampling, model updates and prompt changes all move it. That is acceptable for a proposal and unacceptable for a fact. The risk is not that a model is sometimes wrong; everything is sometimes wrong. The risk is losing the ability to say which values came from where, because every later control depends on it. A gate stops only what it can see, a disclosure names only what is marked, and a reviewer checks only what still points at its source.
The loss is rarely a decision. It happens on an ordinary line of code: an ORM entity populated from the model's JSON because the field names happened to match, a response model that drops the provenance because nobody added it, a batch import that bypasses the form where the validation lived. The pattern exists to make that line impossible to write without noticing.
Valid implementation strategies
Isolation is architectural, not necessarily physical
Microservices are not required. The property holds when no code path lets the probabilistic side produce an authoritative object, and a single process can keep it as well as a network can. Choose the topology for its own reasons (scaling an inference workload separately, isolating a vendor's client library, deploying independently) and then build the boundary in whichever shape you chose. The four figures below keep the same property in four shapes.
Example architecture · Non-normative
Modular monolith
- Data flow in this example
- Optional: may be unavailable
Text description. Inside one process: a model client, marked probabilistic, passes its raw reply to a boundary module, which passes a Proposal to the domain core. A classification adapter, also inside the process, may pass an enrichment to the domain core, and it looks up an external classification service outside the process. The domain core writes entries to the audit trail, a separate database. The only path from the model client to the domain core runs through the boundary.
Example architecture · Non-normative
Service-oriented
- Data flow in this example
- Optional: may be unavailable
Text description. An inference service contains a model client, marked probabilistic, and a boundary that validates what it sends. It sends over HTTP, under a JSON contract, to a domain service, whose own boundary validates what it receives and passes a Proposal to the domain core. The domain service also contains a classification adapter, which looks up an external service and may pass an enrichment to the domain core, and the audit trail, to which the domain core writes entries. The inference service has no connection to the audit trail.
Example architecture · Non-normative
Event-driven
- Data flow in this example
Text description. An inference worker contains a model client, marked probabilistic, and a boundary. It publishes a message to a topic of proposals. The message body carries the audit trace_id; its headers carry the execution context as a W3C traceparent. A domain consumer reads the topic, validates each message on receipt, and passes a Proposal to the domain core, which writes entries to the audit trail.
Example architecture · Non-normative
Cloud-native deployment
- Data flow in this example
Text description. In the inference namespace, an inference pod runs the model client and its boundary. It may send to the model endpoint in the model-serving namespace, and to the proposal intake in the records namespace, on its API port only. The proposal intake validates what it receives and passes a Proposal to the domain core, which writes to the audit trail and records database. A note states that the inference namespace has no route and no credential to that database.
The same four elements in every topology
- Distinct types. The probabilistic side can construct a proposal and nothing else. The authoritative record is built only by domain code, from a proposal plus something the probabilistic side cannot produce.
- Runtime validation on every receiving side. A strict schema: undeclared fields rejected, no type coercion, closed vocabularies for categorical values, bounded lengths. In a distributed topology each receiver validates, the domain side included when the sender is your own service: "we validated before sending" is a claim about another deployable, possibly an older version of it.
- Provenance in the contract. Declared, required, and preserved by every mapper and serialiser on the way out.
- An explicit promotion step. The only route from proposal to authoritative record is a function, endpoint or command that demands what the probabilistic side cannot supply. For values derived by a language model, COADF's public fence F-03 requires human verification before they reach a published output. The promotion is itself recorded in the audit trail.
Deterministic validation around stochastic output
Checks that read only the value (its form, its vocabulary, its unit, the ranges the domain itself defines) run before any person looks at it. They do not make a value true. They make an impossible value impossible, and they do it the same way every time, which a person reviewing the value can rely on.
Enforcement options, from weakest to strongest
| Mechanism | Stops | Does not stop |
|---|---|---|
| Type separation | Accidental promotion in code that type-checks | Casts, dynamic typing, reflection |
| Runtime schema validation | Malformed or over-claiming replies | A well-formed value that is wrong |
| Architecture test on imports | The probabilistic module importing record-writing code | Imports made by name at run time; data paths that are not imports |
| Database grants per component | A component writing the authoritative store | Components that share credentials |
| Separate deployment and network policy | A separately deployed component reaching the store at all | Anything inside one process; clusters whose network plugin does not enforce the policy |
Most systems want the first three always, and the last two when the probabilistic component is deployed on its own. The Python and FastAPI profile implements the first three; the Cloud-Native profile adds the last two.
Failure modes
Model output written straight to authoritative storage
The reply is parsed into the entity the rest of the system reads as fact, because the fields matched. No single line is wrong. The boundary simply does not exist.
Proposals indistinguishable from verified data
One table, one type, and a flag that says which is which. The flag defaults to the convenient value, or the model's own JSON can set it.
Provenance lost on the way out
A mapper, a response model or an export omits the method and the source. Frameworks that filter output to a declared schema do this without a sound: FastAPI filters a response to its response model, and Fastify's serialiser leaves out properties a response schema does not list unless the schema allows additional properties.
Validation only in the user interface
The form validates. The batch import, the administration script and the second client do not.
Downstream systems cannot tell inferred from verified
The export format has no field for the method, so the next system receives a value and nothing else, and every control it has is blind to the difference.
The raw reply trusted without runtime validation
A cast in TypeScript, a dictionary in Python, a lenient mapper in Java: the contract exists in the code's types and nowhere in its behaviour. TypeScript's type annotations are erased at compile time, and its standard typings declare the result of
JSON.parseasany.Coercion hides the model's error
Lax parsing turns
"12"into12and"true"intoTrue. The number looks right and was never a number. Pydantic's strict mode refuses that coercion; Fastify's default validator configuration enables type coercion. Know which one you are running.Silent repair
The reply fails validation, so the call is retried until a reply passes, and only the last one is kept. The failures were evidence about the model, and they are gone.
Validation bypassed for speed
An optimisation that constructs objects without validation becomes the easiest path, and then the bypass. Pydantic's
model_constructcreates a model without validating it, exactly as documented.
Verification
Unit test
Passes when: Each malformed reply is rejected: an undeclared field, a coercible type, a missing provenance, an attribute nobody asked for.
Proof of teeth: Delete one constraint in a scratch copy (for instance the rule that rejects undeclared fields): the matching test must fail.
Architecture test
Passes when: The probabilistic module has no import path to the code that writes authoritative records.
Proof of teeth: Add the forbidden import in a scratch copy: the contract fails and the build stops.
Contract test
Passes when: Producer and consumer agree on the proposal schema, with the provenance fields required.
Proof of teeth: Remove a provenance field from the producer's schema: the consumer's contract test fails.
Integration test
Passes when: When the probabilistic component is deployed on its own, its credentials cannot write the authoritative store.
Proof of teeth: Attempt the write with those credentials: the database refuses it.
End-to-end test
Passes when: An output whose attribute came from a proposal that nobody verified cannot be published.
Proof of teeth: Plant an unverified proposal and request publication: refused, and the refusal is in the audit trail.
Alternative realizations
- Suggestion-only interfaces. Model output is shown to a person as a suggestion and never stored as a value. Simple and strong; it gives up the traceability of what was suggested unless suggestions are recorded as well.
- A shared, language-neutral contract (JSON Schema, Protocol Buffers) instead of language-native models. Better across languages; it moves the strictness question into the schema, where additional properties and formats have to be decided explicitly.
- Structured-output modes of model APIs. They reduce malformed replies at the producer. They do not relieve the consumer of validating, because the consumer cannot verify how the producer was configured.
- Separate storage for proposals and records, two tables or two stores rather than one table with a status. Heavier, and it makes the separation visible to every query anybody writes.
Limitations
- The boundary controls where model output can go. It does not make model output correct, and a well-formed wrong value passes every check in this pattern.
- Type separation stops accidents, not intent. A developer with write access to the domain can construct an authoritative object by hand; code review and the audit trail are what make that visible.
- This pattern does not say when a proposal needs a person, or how confidence is expressed. Those belong to P-2 and P-3, which COADF publishes as principles only.
- Validation is only as good as the contract. A permissive schema, validated strictly, is still permissive.
Sources
- FastAPI: Response model: return type and data filtering · official documentation · FastAPI 0.141 · Checked on 2026-09-11
- Fastify: fast-json-stringify: additionalProperties · project repository · Checked on 2026-09-11
- TypeScript: The Basics: erased types · official documentation · Checked on 2026-09-11
- TypeScript: lib.es5.d.ts: JSON.parse · project repository · Checked on 2026-09-11
- Pydantic: Strict mode · official documentation · Pydantic 2.13 · Checked on 2026-09-11
- Fastify: Validation and serialization · official documentation · Fastify 5.12 · Checked on 2026-09-11
- Pydantic: Models: creating models without validation · official documentation · Pydantic 2.13 · Checked on 2026-09-11
