Open Standard · Working Draft
mekaniskt·kontrakt

Requests for comment  ·  RFC 0010

Receipts: coded reasons, term ids, and what a receipt may quote

Every rail this standard rides encodes its rejections - payment status reasons, document response codes, application acknowledgements. The standard's own judgments carry prose strings. This RFC proposes declared reason codes with captions and clause links, defines exactly what an envelope and a body may contain, and fixes a sentence that welds two different concepts together: sealed is not the same as opaque.

STATUS
Open for comment
TOPICS
Rejection codes, captions, what a receipt may reveal, sealed versus opaque, cross-contract statistics, language neutrality

This is a request for comment, not part of the specification. The syntax is a strawman and much of it is pseudo-code; it exists to be argued with, not adopted by default.

The embarrassment, in one observation

Every rail this design rides encodes its rejections:

Rail How a rejection travels
Payment status messages a status code plus structured reason information drawn from an external code list
Document-level message responses a coded response status with coded reasons
The 1990s application acknowledgements coded application and syntax errors
The web a status code - and HTTP/2 dropped the human reason phrase entirely, because nobody could parse it
This standard’s own faults typed, riding a standard problem-details format

And this standard’s own judgments - the artifact the whole design exists to produce:

reject "role not on rate card"
reject "senior pool exhausted"
reject "takvolym breach"
reject "för sent att åberopa låneklausulen"

Fourteen distinct prose strings across the worked contracts, in two languages, parseable by nothing. The receipt meant to end disputes cannot be counted.

Coded reasons

reasons:                                    # declared, closed, per contract or profile
  ceiling-breach         implements: #klausul-7   caption: "Beställningen överskrider takvolymen."
  role-not-on-ratecard   implements: #klausul-6   caption: "Rollen saknas i prisbilagan."
  pool-exhausted         implements: #klausul-6   caption: "Avropad volym överstiger återstående timmar."

rule on invoice.submit:
  check document.BT-13 references this.id   else reject missing-contract-reference
  • A rejection names a declared code, never a string. An undeclared code is a parse error, like every other undeclared name.
  • The prose moves to a caption: one legal sentence per code, hashed with the document, rendered in the projection, and never carried in the receipt.
  • Each code links to the clause it enforces, so the coverage report gains a dimension for free: which clauses can actually reject, and with what.
  • Profiles publish shared code lists and map them outward where an equivalent exists, so a rejection can travel on the rail it arrived on. The mapping is a governed artifact, versioned like any other.

Four things this buys that prose cannot:

  1. Counting. “How many ceiling breaches did this authority’s contracts reject last year” becomes a query over envelopes rather than a text search.
  2. Language neutrality. Keywords are English permanently while prose stays in its original language; a coded reason is the only part of a receipt that is neither, so a Swedish contract’s rejection renders in any language without translating anything that governs.
  3. Confidentiality by construction. The problem with quoting is that a rejection diff citing a rate card leaks sealed pricing to whoever holds the receipt. A coded receipt cites the code, the term identifier and the clause - and quotes nothing.
  4. Stable third-party tooling. A dispute viewer can be built against a code list; it cannot be built against our sentence structure.

What a receipt may quote

The envelope and body split already says where things live. What it does not yet say is what may appear at all:

Layer May contain Must not contain
Envelope verdict, reason code, contract and version hash, port, submission id, document hash, chain hashes, timestamp, seal any value from the contract or the document
Body (detachable) the rule trace as term ids and clause ids, plus the numeric diff (billed / expected / excess) captions, statute text, personal data beyond what the disclosure policy declares
Rendering (not the receipt) captions, statute text, human sentences - resolved from the pinned document at read time -

And the rule that follows: an opaque value is not the same as a sealed one. The type vocabulary currently calls the opaque type “the type of sealed data”, but the confidentiality rules are explicit that a computational field can be sealed from a viewer while the evaluator keeps its value. So:

  • Opaque = data the contract carries, hashes, and compares for equality, but never branches on - therefore non-computational, therefore erasable. A national identity number.
  • Sealed = a disclosure-policy property, orthogonal to type. A sealed money amount is still money: the evaluator needs it, a viewer does not get it.

Two words, two concepts. One sentence in the standard currently welds them.

Honest creaks

  1. Codes are permanent, like error numbers. A code list is a governed artifact: additions are receipted events and a code’s meaning may never be re-pointed. Wrong granularity is forever - which argues for coarse codes plus a structured diff, not a code per branch.
  2. Over-coding is a real failure mode. Some rejections genuinely are one-off; the answer is a contract-scoped code, never a free string. But a contract with forty local codes has probably mis-modelled its clauses.
  3. Captions can drift from the terms they describe, and no linter can catch it - a caption is prose about a mechanic, and nothing checks the correspondence. Mitigations remain co-signing, the two-spine rendering, and prose governing on conflict.
  4. Outward mapping is lossy. Our reasons are contract-level judgments; the rails’ codes are document- and payment-level. A mapping will sometimes be many-to-one, and the artifact should say so instead of pretending the vocabularies are isomorphic.
  5. The diff still carries numbers. A rejection showing billed-versus-contractual necessarily reveals the contractual figure to whoever holds the body - which is why the body is detachable and scoped, and why audience views are the mechanism rather than an afterthought.

This RFC is exploratory. It is published as a basis for discussion, not as normative text; where it touches the standard, the standard governs until a decision is recorded.