ADR 0133 and 0134: who runs migrations, and the mesh saying what it applied

0133 — a module owns its migrations and the mesh owns when they run. A container declares what must
run before it; the mesh derives the gated step from the resource it precedes, so the image, the
environment and the credentials come from the one place they are described. The module owns the SQL,
the dialect and the lock; the mesh owns the moment and refuses to start a version whose step failed.
Per node, with no level: a step that ran once somewhere leaves every other machine ungated, and
'once, mesh-wide' is what holding a seat already means.

0134 — the pipeline is observable from a merge to an artifact and goes dark at the machine. What a
node now runs, and what it refused, become facts under the control plane's own seat, emitted when
what a machine runs changes rather than on every convergence pass.

Design 32's lifecycle carries both; issue 133 points at them as what ends the matter it opened.
This commit is contained in:
2026-09-28 11:45:47 +02:00
parent 83cbeb8db8
commit 891c7a945e
5 changed files with 323 additions and 2 deletions
@@ -3,7 +3,7 @@ status: resolved
opened: 2026-09-28
located-in: [mesh-controller module.json]
fixed-by: mesh-controller — the control plane's module declares a run-once `migrate` step before its server, which is the shape ADR 0052 prescribes for exactly this. A step's record of having run is the digest of its declaration and the image is part of that digest, so a new build of the control plane re-runs it; and because a run-once step gates what the declaration places after it, a migration that fails stops the new server from starting at all rather than letting it run against a schema it does not have.
amended-design:
amended-design: 03-DESIGN/01-to-be/32-what-a-module-declares.md
---
# 133 — The control plane's schema is migrated at birth and never again
@@ -54,6 +54,14 @@ such step; it went straight from a state directory to the server.
server starts, which means the old binary briefly runs against the new schema. A migration that
removes or renames something would break the running control plane in the window between the two.
**A hand-written step is one the next module forgets**, which is why this fix is not where the matter
ends: [ADR 0133](../../02-DECISIONS/0133-a-module-owns-its-migrations-and-the-mesh-owns-when-they-run.md)
makes the step derived — a container declares what must run before it, and the mesh composes the gated
resource — so the control plane stops being the only module that had to remember. The same record
settles the level question HAL answered with stages, and
[ADR 0134](../../02-DECISIONS/0134-the-mesh-says-what-it-applied.md) answers the second open question
below: what a machine applied, and what it refused, become facts on the bus rather than a line in a log.
**The mesh now has two shapes for one problem.** The catalogue module migrates its own schema in its
own code when it starts; the control plane migrates in a step the host gates on. Both work and the
reasons differ — a module that owns its store entirely can do it at start, while a step is visible in