Files
hq/02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md
T
jschoubben 1111bd84d7 Establish the repo for the completed Phase 0-3 build
Settles the design repository now that the self-upgrade build is on main:
- Records the two decisions that shipped without a record — ADR 0077 (the
  controller/foundation/node vocabulary) and ADR 0078 (the store and broker are
  ordinary modules); accepts ADR 0075 and 0076, which shipped work rests on.
- Fills issue 051's amended-design and wires ADR 0078 into 07-the-foundation.
- Sweeps the repo rename (mesh-control -> mesh-controller) into the mutable docs
  now that the forge repo is renamed; updates the glossary note and repos.md.
- Fixes the six broken links from the design-doc renames, indexes the glossary,
  regenerates the decisions reading order.

Both checks (records.py, index.py) are green. Statuses stay honest: the build is
on main and lab-proven but not deployed as the production mesh, so the to-be docs
remain in-progress and the as-is layer (the hal mesh) is unchanged — graduation
to implemented + as-is belongs to deployment, not merge.

https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2026-09-17 00:04:58 +02:00

5.5 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
what runs on it accepted 2026-08-31 jochen false 02-DECISIONS/0009-modules-and-the-graph.md

27. A provision names what the consumer is coupled to, not the role it plays

Context

Provisions are named after roles. The catalogue and every test fixture built so far say:

provides: database
requires: database

Nothing distinguishes one engine from another. A module requiring database is satisfied by any module providing database, so a module written against PostgreSQL can be matched to a provider of Microsoft SQL Server, resolve as satisfied, deploy, and fail on its first query.

The mesh runs several engines — PostgreSQL, Microsoft SQL Server, MariaDB, and others behind products that expose their own. This is not a hypothetical collision.

The failure is in the direction that hides. Resolution succeeds. Nothing is refused, nothing is logged, and the breakage surfaces later as an error inside an application, on a machine, with nothing connecting it back to a match made elsewhere by something that thought it had done its job. A wrong answer delivered confidently costs more than a refusal, and the whole point of refusing on ambiguity (ADR 0009) was to not do this.

How it got in: every test written for the resolver had exactly one provider of each name, so no mismatch was expressible and none was caught. The fixtures agreed with the design. That is the same fault as 04-ISSUES/005's imagined output and 019's unchecked comment, at the level of a name rather than a line.

Considered Options

  1. Keep role names; let the operator assign correctly. The mesh would refuse ambiguity when two providers exist, so a person picks. Rejected. It makes correctness depend on somebody knowing that the module they are assigning speaks a particular dialect — which is exactly the knowledge the provisioning model exists to remove. And with one provider of each name, nothing is ambiguous and nothing is asked.

  2. A role name plus a flavour: or engine: qualifier, matched as a second field. Rejected. Two fields that must agree is a constraint the resolver has to enforce and a manifest author has to remember, to express something one field already can. The name is the contract; splitting it invites a requirement that names a role and forgets the qualifier, which then matches everything again.

  3. The name says what the consumer is coupled to. Adopted.

Decision

A provision is named for the thing a consumer's code is written against.

provides: postgres-database
requires: postgres-database

The test is whether the consumer can tell the difference. If swapping the provider would break the consumer, the name must say which provider — because a match that breaks the consumer is not a match. If the consumer genuinely cannot tell, a role name is correct and better.

provision why
postgres-database, mssql-database specific applications are written against a dialect; a swap breaks them
route role the consumer wants its name reachable and does not care what proxies it
resolver role the consumer wants names to resolve
artifact-store role the consumer fetches by digest over a protocol, and nothing else

database is not a provision and may not be provided. There is no context in which an application talks to a generic database: it talks to PostgreSQL or it talks to SQL Server. A name that cannot be true of any real consumer should not be expressible.

This is about coupling, not about products. Two providers of postgres-database — a container on this node and a managed instance elsewhere — are interchangeable and should both match. What may not be interchangeable is what the consumer's queries are written in.

Consequences

Every manifest that names a database changes. Doing this now costs a rename across a handful of examples. Doing it after modules are migrated costs it across all of them, plus every deployment that resolved against the old name.

Wrong requirements now fail loudly, and at the right moment. A module requiring postgres-database where only mssql-database is provided is unsatisfiable, so it is refused at resolution with both names visible — rather than deployed and broken later. This is the property that was lost, restored.

Generic role names are still right, and the rule says when. This does not push specificity everywhere; it puts it exactly where a consumer is coupled. Naming route after a particular proxy would be the same error in the other direction, and would prevent a swap that genuinely changes nothing.

It is checked, not merely stated (00-META/how-we-build.md §5). A manifest providing a name known to be engine-generic is refused, naming what to say instead. Without that, this record is a convention, and a convention is what the previous naming was.

References

  • ADR 0009 — provisions, and refusing on ambiguity
  • 03-DESIGN/01-to-be/07-the-foundation.md — the provisioning model uses databases, roles and schemas as PostgreSQL means them, which is this record's point made about the substrate before it was made about modules