From 1cfe6be9c4b24305db5517c92a10f309ce6fb03a Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 27 Sep 2026 23:04:59 +0200 Subject: [PATCH] The move ends with the old broker going, not staying MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `rollout check` said the old broker stays running as an ordinary provider of amqp, and this was not its retirement. That was ADR 0127, which ADR 0131 has superseded: AMQP is not a provision, so once every machine reports on the new bus nothing of the mesh speaks to the old broker and its module is unassigned. The plan says so, as its last step. The flag that made the "it stays" line conditional is gone with the line — there is no case in which the broker is kept. The test that pinned the opposite now pins this, and says which record changed under it. The stale citation of a record numbered 0119 is corrected while here. --- cmd/mesh-controller/rollout.go | 11 +++++------ internal/broker/readiness.go | 13 +++++-------- internal/broker/readiness_test.go | 13 +++++++------ 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/cmd/mesh-controller/rollout.go b/cmd/mesh-controller/rollout.go index c386490..13b9f24 100644 --- a/cmd/mesh-controller/rollout.go +++ b/cmd/mesh-controller/rollout.go @@ -25,11 +25,11 @@ import ( // against a mesh that is serving. It answers from records: what is missing, and what would happen. // `rollout` itself refuses unless the check is clean. // -// **The old broker is not switched off by this.** It stays an ordinary provider of `amqp` for whatever -// else uses it — on this installation, a whole automation layer that has nothing to do with the mesh -// ([ADR 0119](../../02-DECISIONS/0119-amqp-is-a-provision-not-the-bus.md)). Only the mesh's own -// traffic moves, which is why this is survivable at all: what breaks if it goes wrong is the mesh's -// ability to change things, not the services its modules are serving. +// **The old broker goes with the move, and goes last** (novox/hq ADR 0131): AMQP is not a provision, +// so once every machine reports on the new bus its module is unassigned. Only the mesh's own traffic +// is what moves, which is why this is survivable at all: what breaks if it goes wrong is the mesh's +// ability to change things, not the services its modules are serving — measured on 2026-09-27, when +// a seat emptied mid-change and the control plane looped for two hours while every service stayed up. const rolloutUsage = "rollout check | rollout --confirm" @@ -105,7 +105,6 @@ func readinessOf(ctx context.Context, inv *inventory.Inventory) (broker.Readines ModuleCredentialled: map[string]bool{}, // The old broker keeps its other clients on this installation, and saying so is how the plan // stops reading as a retirement. - OldBusHasOtherClients: true, } address, _, err := broker.OnNATS() diff --git a/internal/broker/readiness.go b/internal/broker/readiness.go index a1302d3..0f978b9 100644 --- a/internal/broker/readiness.go +++ b/internal/broker/readiness.go @@ -35,10 +35,6 @@ type Readiness struct { Modules []string // ModuleCredentialled is which of those has one. ModuleCredentialled map[string]bool - // StillOnTheOldBus is whether anything of the mesh's own still needs the bus it is leaving — - // which is not a reason to stop, because that broker stays as an ordinary provider of `amqp` - // (ADR 0119). Recorded so nobody reads the move as a retirement. - OldBusHasOtherClients bool } // NotReady is every reason this mesh cannot move its bus yet, in the order somebody would fix them. @@ -120,10 +116,11 @@ func WhatMoves(r Readiness) []string { out = append(out, fmt.Sprintf("move %d module runtime(s), and confirm each answers", len(r.Modules))) } - if r.OldBusHasOtherClients { - out = append(out, "leave the old broker running: it stays an ordinary provider of `amqp` for "+ - "whatever else uses it (ADR 0119), and this move is not its retirement") - } + // **The old broker goes, and it goes last** (novox/hq ADR 0131). AMQP is not a provision, so once + // every machine reports on the new bus nothing of the mesh is left speaking to it, and its module + // is unassigned. Said as a step so nobody reads the move as leaving a second bus behind. + out = append(out, "then unassign the old broker's module: AMQP is not a provision (ADR 0131), and "+ + "once every machine reports on the new bus nothing of the mesh speaks to it") return out } diff --git a/internal/broker/readiness_test.go b/internal/broker/readiness_test.go index 827b786..97ff917 100644 --- a/internal/broker/readiness_test.go +++ b/internal/broker/readiness_test.go @@ -79,9 +79,8 @@ func TestEachThingMissingNamesItsOwnRemedy(t *testing.T) { // What the move would do is written out rather than summarised, because this is the one step with // nothing to inspect afterwards — so reading it is the last chance to disagree. -func TestWhatMovesNamesEveryMachineAndSaysTheOldBrokerStays(t *testing.T) { +func TestWhatMovesNamesEveryMachineAndEndsWithTheOldBrokerGoing(t *testing.T) { r := aMeshReadyToMove() - r.OldBusHasOtherClients = true steps := strings.Join(WhatMoves(r), "\n") for _, want := range []string{"anchor", "laptop", "user list", "module runtime"} { @@ -89,9 +88,11 @@ func TestWhatMovesNamesEveryMachineAndSaysTheOldBrokerStays(t *testing.T) { t.Errorf("the plan does not mention %q:\n%s", want, steps) } } - // Said explicitly, so nobody reads the move as switching the old broker off — it stays serving - // whatever else uses it, and that is a decision already taken. - if !strings.Contains(steps, "not its retirement") { - t.Errorf("the plan does not say the old broker stays:\n%s", steps) + // Said explicitly, and last: AMQP is not a provision (novox/hq ADR 0131), so the move ends with + // the old broker's module unassigned, not left behind as a second bus. An earlier version of this + // test pinned the opposite, under a record 0131 superseded. + lines := WhatMoves(r) + if last := lines[len(lines)-1]; !strings.Contains(last, "unassign the old broker") { + t.Errorf("the plan does not end with the old broker going:\n%s", steps) } } -- 2.54.0