Compare commits

...
Author SHA1 Message Date
jschoubben 1cfe6be9c4 The move ends with the old broker going, not staying
`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.
2026-09-27 23:04:59 +02:00
jschoubben 4e4481b6f2 Merge pull request 'The store owns the seat set, so only the control plane may judge a claim' (#89) from fix/the-store-owns-the-seat-set into main 2026-09-27 20:00:05 +00:00
3 changed files with 17 additions and 20 deletions
+5 -6
View File
@@ -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()
+5 -8
View File
@@ -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
}
+7 -6
View File
@@ -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)
}
}