From 426db57d3fe41ce409a1e77b107d8fdcc04fe131 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 22 Oct 2026 14:04:06 +0200 Subject: [PATCH] =?UTF-8?q?Hold=20what=20a=20walk's=20merges=20change=20on?= =?UTF-8?q?=20a=20machine=20it=20sends=20nothing=20to,=20and=20name=20only?= =?UTF-8?q?=20where=20it=20sent=20(ADR=200505=20=C2=A72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../messenger/cmd/messenger/adr505_test.go | 30 +++++++++++++++++++ modules/messenger/cmd/messenger/walks.go | 25 ++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/modules/messenger/cmd/messenger/adr505_test.go b/modules/messenger/cmd/messenger/adr505_test.go index 1bbec051..2bc1f92f 100644 --- a/modules/messenger/cmd/messenger/adr505_test.go +++ b/modules/messenger/cmd/messenger/adr505_test.go @@ -89,3 +89,33 @@ func TestTheControllersOwnConditionsAreNotHeldByAWalkOfAnotherModule(t *testing. } } } + +// shanksDeclared is a touch as the controller says a machine whose declaration the walk's merges change and to which +// it sends nothing (mesh-controller walk_said.go walkTouch, `declared`, ADR 0505 §2b). +var shanksDeclared = Touch{Machine: "shanks", Modules: []string{"mesh-build-image"}, Declared: true} + +// ADR 0505 §2b: a machine whose declaration a walk's merges change is held like a machine it sends to, and is not +// named among the machines the walk reached. +func TestAMachineWhoseDeclarationTheWalkChangesIsHeldAndNotNamedAsReached(t *testing.T) { + ev, ok, err := DecodeWalk([]byte(`{"id":"plan-d","walk":"plan-d","phase":"started","budget_class":"leaf",` + + `"bound_seconds":600,"touches":[{"machine":"novox","modules":["gitea"]},` + + `{"machine":"shanks","modules":["mesh-build-image"],"declared":true}]}`)) + if err != nil || !ok || len(ev.Touches) != 2 || !ev.Touches[1].Declared || ev.Touches[0].Declared { + t.Fatalf("a declared touch was not read: %+v %v %v", ev.Touches, ok, err) + } + h, tg, dt, c, _ := newHolder(t) + walk(t, h, "plan-d", WalkStarted, "", c.now(), 600, novox("gitea"), shanksDeclared) + k := cond("machine.shanks.uncomposable", Urgent, "shanks cannot get new instructions") + k.Machine, k.Raised = "shanks", c.now() + h.Condition(EventRaised, k) + runFor(h, c, time.Minute) + if !has(outcomes(h, k.Key), "held") || heard(headlineOf(k.Key), tg, dt) { + t.Fatalf("a condition on the declared machine was not held: %v", outcomes(h, k.Key)) + } + if got := touchWords(mergeTouches([]Touch{novox("gitea"), shanksDeclared}, nil)); got != "gitea on novox" { + t.Fatalf("what the walk reached reads %q, want only novox", got) + } + if got := mergeTouches([]Touch{shanksDeclared}, []Touch{{Machine: "shanks", Modules: []string{"buildah"}}}); got[0].Declared { + t.Fatalf("a machine the walk also sends to stayed declared: %+v", got) + } +} diff --git a/modules/messenger/cmd/messenger/walks.go b/modules/messenger/cmd/messenger/walks.go index 33123fb5..0b056cfa 100644 --- a/modules/messenger/cmd/messenger/walks.go +++ b/modules/messenger/cmd/messenger/walks.go @@ -94,6 +94,10 @@ func (h *Holder) settle() time.Duration { type Touch struct { Machine string `json:"machine"` Modules []string `json:"modules"` + // Declared: the walk sends nothing to this machine, but its merges change the machine's declaration (an + // assignment, a provision or a requirement added or removed, ADR 0505 §2b). Held like any touch; never named + // among the machines the walk reached. + Declared bool `json:"declared,omitempty"` } // WalkEvent is a walk's start or end as the controller says it. @@ -229,9 +233,10 @@ func DecodeWalk(body []byte) (WalkEvent, bool, error) { } if v, ok := raw["touches"]; ok && string(v) != "null" { var list []struct { - Machine string `json:"machine"` - Node string `json:"node"` - Modules []string `json:"modules"` + Machine string `json:"machine"` + Node string `json:"node"` + Modules []string `json:"modules"` + Declared bool `json:"declared"` } if json.Unmarshal(v, &list) != nil { return WalkEvent{}, false, fmt.Errorf("walk %s: touches is not a list of {machine, modules}", ev.Walk) @@ -242,7 +247,7 @@ func DecodeWalk(body []byte) (WalkEvent, bool, error) { m = t.Node } if m != "" { - ev.Touches = append(ev.Touches, Touch{Machine: m, Modules: t.Modules}) + ev.Touches = append(ev.Touches, Touch{Machine: m, Modules: t.Modules, Declared: t.Declared}) } } } @@ -266,9 +271,11 @@ func DecodeWalk(body []byte) (WalkEvent, bool, error) { return ev, true, nil } -// mergeTouches is what two sayings of a walk's touches name together. +// mergeTouches is what two sayings of a walk's touches name together. A machine is declared only while no saying +// sends to it (ADR 0505 §2b). func mergeTouches(a, b []Touch) []Touch { by := map[string]map[string]bool{} + sent := map[string]bool{} var order []string for _, list := range [][]Touch{a, b} { for _, t := range list { @@ -279,6 +286,9 @@ func mergeTouches(a, b []Touch) []Touch { for _, m := range t.Modules { by[t.Machine][m] = true } + if !t.Declared { + sent[t.Machine] = true + } } } sort.Strings(order) @@ -289,7 +299,7 @@ func mergeTouches(a, b []Touch) []Touch { mods = append(mods, m) } sort.Strings(mods) - out = append(out, Touch{Machine: machine, Modules: mods}) + out = append(out, Touch{Machine: machine, Modules: mods, Declared: !sent[machine]}) } return out } @@ -684,6 +694,9 @@ func touchWords(touches []Touch) string { mods := map[string]bool{} var machines []string for _, t := range touches { + if t.Declared { + continue // its declaration changed; nothing was sent to it (ADR 0505 §2b) + } machines = append(machines, t.Machine) for _, m := range t.Modules { mods[m] = true