Merge pull request 'Two graphs, and a build chain that orders itself' (#36) from feat/two-graphs-and-the-build-chain into main
This commit was merged in pull request #36.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
---
|
||||
topic: the tiers
|
||||
status: accepted
|
||||
date: 2026-09-12
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 0070-the-catalogue-owns-the-module-graph.md
|
||||
---
|
||||
|
||||
# 72. Two graphs, and a build chain that orders itself
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0070](0070-the-catalogue-owns-the-module-graph.md) gave the catalogue the module graph and
|
||||
said the control plane consumes it — that resolving a requirement and composing what a machine
|
||||
should be *"both need to know what modules are, so the dependency runs from the control plane to
|
||||
the catalogue."*
|
||||
|
||||
**That paragraph is wrong, and this record corrects it.** It was written before the two graphs had
|
||||
been told apart, and it creates a dependency that does not need to exist: a catalogue that is down
|
||||
would leave the control plane unable to compose the declaration that would repair it.
|
||||
|
||||
## Decision
|
||||
|
||||
**There are two graphs, with different owners, and they meet only when something is installed.**
|
||||
|
||||
| Graph | Owner | What it links | Answers |
|
||||
|---|---|---|---|
|
||||
| the module graph | the **catalogue** | module-versions to each other | what was this built against · what must rebuild now · what does this need |
|
||||
| the runtime graph | the **control plane** | module-versions to nodes | what runs where · who consumes this provision · what breaks if this machine goes |
|
||||
|
||||
The catalogue does not know nodes exist. The control plane holds module-versions and nodes, along
|
||||
with capabilities and claims, because deciding whether a machine qualifies needs every node.
|
||||
|
||||
**So the control plane never asks the catalogue anything.** It holds what it needs to compose a
|
||||
declaration. A catalogue that is down stops new installs and stops the rebuild fan-out, and does not
|
||||
touch anything already running or the ability to repair it.
|
||||
|
||||
**Everything between them travels as events over the broker**, like all module-to-module
|
||||
communication. The builder finishes and announces that a module was built. The catalogue registers
|
||||
it, places it among what it depends on, and announces that a module was upgraded. The control plane
|
||||
reacts to *that*, not to build output — a semantic fact rather than an artifact.
|
||||
|
||||
**Nothing is lost if a receiver is down.** A consuming module's queue is durable with a dead-letter
|
||||
exchange, declared by the mesh rather than by the module, so an event waits for a consumer that is
|
||||
not there.
|
||||
|
||||
**Build order is not computed. It emerges from the chain.** The builder never consults the graph: it
|
||||
builds what it is asked for, one at a time. The catalogue asks for the next build after the previous
|
||||
registration, so *"do not start this until that is registered"* holds by construction rather than by
|
||||
a schedule somebody maintains.
|
||||
|
||||
**The catalogue's rule is a condition, not an order:** ask for a module to be rebuilt once everything
|
||||
it was built against is current. That handles a chain and a diamond with one rule, where an
|
||||
order-based approach needs to know the shape in advance.
|
||||
|
||||
**Two refusals belong to the catalogue.** A cycle, because the chain would never settle. And a
|
||||
rebuild whose artifacts are identical to what it replaced, which is not an upgrade and must not be
|
||||
announced as one — or a single change ripples outward forever through modules that did not change.
|
||||
|
||||
**Genesis does none of this.** The init builder has a fixed, short list — control plane, catalogue,
|
||||
builder — in a written order, because there is no catalogue yet to ask.
|
||||
|
||||
## Consequences
|
||||
|
||||
The builder stays simple, and independent of the catalogue. That is what makes genesis possible at
|
||||
all: the thing that builds the catalogue cannot require the catalogue.
|
||||
|
||||
The two sides can be briefly out of step — the catalogue may hold a module-version a moment before
|
||||
the control plane knows of it. Assigning in that instant fails, and should say why rather than
|
||||
report that no such module exists.
|
||||
|
||||
A module's declared events stop being documentation and become its permissions: an account is
|
||||
scoped from what a module emits and consumes, so a builder that announces what it built is granted
|
||||
what it needs by the ordinary mechanism rather than by a special case.
|
||||
|
||||
## Open
|
||||
|
||||
**Whether an upgrade is applied or merely noticed.** Today the system this replaces deploys
|
||||
automatically, and that is a defensible default for core modules on a mesh its operator runs. But
|
||||
the design as it stands does the opposite: it records that the source moved ahead, makes it visible,
|
||||
and waits to be told. This must become a setting with a chosen default rather than inherited
|
||||
behaviour — and the choice matters most on the day a bad commit reaches something that carries mail.
|
||||
|
||||
**Whether a module assigned to several machines upgrades on all of them at once.** Doing so makes
|
||||
one bad commit simultaneous everywhere. Doing one machine and pausing turns it into one casualty.
|
||||
@@ -104,6 +104,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0069** — [A module is a repository and a path within it](0069-a-module-is-a-repository-and-a-path.md)
|
||||
- **0070** — [The catalogue owns the module graph, and genesis builds rather than carries](0070-the-catalogue-owns-the-module-graph.md)
|
||||
- **0071** — [Genesis clones from a mesh, and checks what it got](0071-where-genesis-gets-its-source.md)
|
||||
- **0072** — [Two graphs, and a build chain that orders itself](0072-two-graphs-and-the-build-chain.md)
|
||||
|
||||
### What runs on them, and how it gets there
|
||||
|
||||
|
||||
Reference in New Issue
Block a user