diff --git a/04-ISSUES/170-assigning-a-module-claims-every-seat-it-could-hold/00-report.md b/04-ISSUES/170-assigning-a-module-claims-every-seat-it-could-hold/00-report.md new file mode 100644 index 0000000..d4c6d80 --- /dev/null +++ b/04-ISSUES/170-assigning-a-module-claims-every-seat-it-could-hold/00-report.md @@ -0,0 +1,63 @@ +--- +status: open +opened: 2026-09-30 +located-in: + - mesh-controller internal/catalogue/resolve.go (holdings are derived from every resolved assignment's manifest `claims`) + - mesh-controller cmd/mesh-controller/seats.go (the deliberate act exists — HoldSeat, "recording … as its standing holder" — beside it) +fixed-by: +amended-design: +--- + +# 170 — Assigning a module claims every seat it could hold + +## What was observed + +ace's migration needs a postgres of its own: the operator's decision is that a `postgres` module +assigned on ace provides `postgres-database` to ace's modules and has **nothing to do with the +`mesh-store` seat**, which novox's assignment holds by a deliberate act already taken ("make +novox's postgres the mesh-store"). + +`assign ace postgres` (2026-09-30): + +``` +ace is assigned postgres + +AND 1 other machine(s) cannot be worked out as things stand, so nothing will be sent to them: + novox + - postgres on novox claims "mesh-store", which postgres on ace already holds — one per mesh +mesh-controller: these assignments cannot be applied: + - postgres on ace claims "mesh-store", which postgres on novox already holds — one per mesh +``` + +The second assignment did not merely fail: it made **the control plane's own store's +assignment unresolvable** until unassigned. Nothing was pushed; the state is restored. + +## Why + +`resolve.go` derives what a node holds from the manifest's `claims` of every module resolved on +it, so a claim in a definition is a claim by every assignment of that module. The deliberate +act ADR 0110 describes exists beside it — `seat …` records "X on Y as its standing holder" +(`HoldSeat`) — but resolution does not consult that record; it consults the manifests. + +## What was decided + +[ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md): + +> **A definition says which seats a module *can* hold. An assignment says which it *does* +> hold.** The store module can hold `mesh-store`, and it may be assigned to every node. Exactly +> one of those assignments holds the seat, because that assignment said so. + +The manifest's `claims` is being read as *does hold*. + +## What would be right + +Resolution takes the holder of a seat from the recorded holding (the seat's standing holder), +not from the manifests: a module whose definition can hold a seat is assignable anywhere, and only +the assignment recorded as holder claims it — with the refusal reserved for a second *recorded* +holder at the seat's scope. Assigning postgres to ace is then exactly what the operator said it +is: a database provider on ace, and no more. + +## Until then + +`postgres` cannot be assigned on any second node; ace's database windows (baserow, letta, n8n, +car-hunter, txt-game) wait on this.