From a7249541df4648c21db613777a9337f8f8d24477 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 27 Sep 2026 23:20:36 +0200 Subject: [PATCH] Design 26: which assignment holds a seat is on record, and changes as one act MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until now the holder was derived — assigned and claiming — and a second eligible assignment was refused, so a seat could not pass from one holder to the next without a moment where nobody held it. The controller finds its own bus through one of these seats, and that moment took the control plane down on 2026-09-27. The holder is now a row the controller keeps, written by `seat --to /` in the same write that removes the previous one. No row means the old rule, so nothing changes for a mesh that never hands a seat over; with a row, another eligible assignment is silent rather than refused, which is what lets the next holder run beside the current one until the switch. A holding is the assignment's and goes when it does. Each rule names the test that checks it. Under ADR 0131; design 28 task 5.3 is the work. --- 03-DESIGN/01-to-be/26-the-seats.md | 32 +++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/03-DESIGN/01-to-be/26-the-seats.md b/03-DESIGN/01-to-be/26-the-seats.md index f6c1cd1..5f8f8f5 100644 --- a/03-DESIGN/01-to-be/26-the-seats.md +++ b/03-DESIGN/01-to-be/26-the-seats.md @@ -4,12 +4,15 @@ status: implemented code: - mesh-controller internal/catalogue/seats.go - mesh-controller internal/catalogue/resolve.go + - mesh-controller internal/inventory/seats.go + - mesh-controller internal/inventory/migrations/0039-a-seat-is-held-by-one-assignment-on-record.sql - mesh-controller cmd/mesh-controller/seats.go - mesh-controller cmd/mesh-controller/source.go - mesh-controller internal/inventory/migrations/0032-a-source-may-live-on-a-seat.sql - mesh-catalog modules/gitea/module.json updated: 2026-09-27 decisions: + - 02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md - 02-DECISIONS/0126-a-module-declares-its-own-seats.md - 02-DECISIONS/0128-the-mesh-bus-is-required-not-ambient.md - 02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md @@ -42,6 +45,31 @@ is refused. A seat makes a role singular, never a module. **The seat points at the assignment.** Everything the mesh knows about the holder is what it knows about that assignment: the node, the node's settings for the module, and what the module serves. +**Which assignment holds a seat is a fact on record, and changes as one act.** Revision, 2026-09-27 +([ADR 0131](../../02-DECISIONS/0131-everything-on-the-mesh-speaks-to-the-broker-seat.md)). Until +then the holder was derived — the module that is assigned and claims the seat holds it, and a second +eligible assignment was refused. That has no way to pass a seat from one holder to the next without a +moment in which nobody holds it, and the controller finds its own bus through one of these seats: that +moment took the control plane down for an evening. So the holder is now one row the controller keeps, +written by a handover — `seat --to /` — that names the seat and the assignment +taking it over and replaces the previous holder in the same write. Between two handovers the seat has +exactly one holder, and it is never none. + +Three consequences follow. **A seat with no row is held as it always was**: the sole eligible +assignment holds it, and two eligible ones are refused — so a mesh that has never handed a seat over +behaves exactly as before, and the row appears the first time somebody does. **With a row, any other +assignment whose module could hold the seat is eligible and silent**: neither refused nor holding. +That is what lets the next holder run beside the current one until the handover, which the bus's move +needs ([28](28-building-the-bus.md), task 5.3). **And a holding is the assignment's**: unassigning the +holder takes the row with it, so a seat never points at something that is not running anywhere, and +the seat falls back to derivation rather than to nothing. + +The handover refuses what would make the new holder wrong before anything is written: the seat must +exist, the assignment must exist, and the module must be able to hold the seat — claim it at its scope +and provide what it delivers, judged against the store's row and not against anything compiled into a +binary. It does not check that the module is running yet; `push` confirms that afterwards, and a +handover that could only be recorded after the new holder was up could not be the switch. + **The set is closed.** A seat the mesh does not define is refused wherever it is named, and so is one named at the wrong scope. Adding a seat is a decision, recorded, for the reason every addition to the host's vocabulary is one: the set is what a person reads to learn what a mesh can have, and an entry @@ -207,7 +235,9 @@ checked as their tables say: | `mesh-*` is the mesh's, and a module may not declare one | 0118: a registration test refusing a manifest that declares any `mesh-*` seat, naming the prefix. | | Two modules cannot declare the same seat | 0118: a registration test; the second is refused and the first untouched. | | A holder satisfies the seat's protocol | 0118: a claim whose module does not serve what the seat declares is refused at assignment. | -| A seat is held by one assignment, and only by one whose module can hold it | 0118: resolution tests for a second holder and for a seat the definition does not name. | +| A seat is held by one assignment, and only by one whose module can hold it | 0118: resolution tests for a second holder and for a seat the definition does not name. 0131: `CanHold` is the one judgement, shared by registration and the handover, and its test follows the store's row. | +| A holder on record settles the seat; another eligible assignment is silent, not refused | 0131: resolution tests with a recorded holder on the same machine, on another machine, and under a seat's former name; without a record, the old rule's tests still pass unchanged. | +| A handover replaces the holder as one write, needs an assignment to point at, and goes with it | 0131: store tests — a second handover leaves one row; a handover to a module not assigned where named is refused; unassigning the holder removes the row. | | A requirement naming a seat is answered by its holder; a foundation seat cannot be named | 0118: resolution tests with a second provider on the consumer's node, with the seat unheld, and naming `mesh-store`. | | Several providers and none local is a person's choice | 0118: an assignment test listing candidates with the seat's holder first and recording the pin. | | `secret` is reserved | 0118: the parser and resolution refusals for another provider and a pin. |