diff --git a/02-DECISIONS/0074-the-wire-is-specified-not-the-types.md b/02-DECISIONS/0074-the-wire-is-specified-not-the-types.md index 8517c20..e1ffe2f 100644 --- a/02-DECISIONS/0074-the-wire-is-specified-not-the-types.md +++ b/02-DECISIONS/0074-the-wire-is-specified-not-the-types.md @@ -19,16 +19,24 @@ There is already more than one. **The contracts are expressed twice** — as Go plane and the host, and as TypeScript types in the SDK — and nobody has felt it because both live in one repository and one head. -**They already disagree.** Not in some future where a second language is added; today: +**A correction, made after inspecting the wire rather than the types** (2026-09-16). This record +first claimed the two implementations already disagreed — `resource` vs `Provision`, `consumer` +meaning the module in one and the node in the other, headers declared on one side and emitted by +neither. **On inspection the live wire agrees**, and the claim was wrong: -| | TypeScript | Go | -|---|---|---| -| the provision's field | `resource` | `Provision` | -| what `consumer` means | **the module** | **the node**; the module is `From` | -| event headers | six, including `x-causation-id` and `x-schema` | four — the other two are never written | +- The grant types that disagreed (`Grant`, `Interface`, `Credential` in the SDK's `contracts`) + were **dead** — exported and imported by nothing. The live provisioning wire is the contributions + file, whose shape (`as`, `secret`, `node`, `at`, `values`) is the same on both sides. Those dead + types have been removed. +- The envelope agrees too: Go emits all five required headers, and `x-causation-id`/`x-schema` are + **optional** — the SDK sets them when a handler has a causation or a schema, and a bare event + carrying neither is correct, not a drift. -So one word means two things in the two halves of one mesh, and the header that exists so a body's -shape can change without silent misreads is declared on one side and emitted by neither. +So the danger was never live disagreement. It was **dead types that contradicted the live wire**, +which read as the contract and were not — and are exactly what led this record to assert a drift +that inspection did not find. That is a sharper reason for the decision below, not a weaker one: a +type is only as good as its being the wire, and the way to guarantee that is to specify the wire and +check implementations against it, rather than to trust a hand-kept type to still describe it. A failure of this kind does not announce itself. Two implementations that disagree about an envelope do not fail to compile — they ignore each other's messages, and a mesh where a module @@ -144,9 +152,10 @@ pieces, rather than believed. with a version on the body is what lets a mesh hold a module built against an older SDK, which is the ordinary state of any mesh that has been running for a while. -**The two current implementations will be found wrong.** They disagree, so at least one is. Fixing -that is the point rather than a cost, but it is not free: something is emitting or expecting -something it should not. +**The two current implementations agree on the live wire** — inspection showed it. What was wrong +was a set of dead types beside the wire, now removed. The suite's job here is therefore prevention: +to keep that agreement true as the wire changes, and to hold a new language's SDK to it, rather than +to repair a break that exists today. **This does not make the mesh polyglot by itself**, and should not be reported as though it does. It makes polyglot possible to do correctly. A Rust SDK is still a Rust SDK.