From 0d1bbe967dc15941066e9508250f7584aa6decff Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 2 Nov 2026 11:22:54 +0100 Subject: [PATCH] Answer the node seat's packages from the engine's own key alone The controller's declared-packages mirror ended with the controller (issue 1126) and is deleted after group B's delivery, so the verb no longer reads or compares it. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL --- cmd/mesh-host/engineseat.go | 15 ++++------- internal/link/enginepackages.go | 48 +++++++++------------------------ internal/link/handover_test.go | 15 +++++------ 3 files changed, 24 insertions(+), 54 deletions(-) diff --git a/cmd/mesh-host/engineseat.go b/cmd/mesh-host/engineseat.go index c9ec77f..6cba6a6 100644 --- a/cmd/mesh-host/engineseat.go +++ b/cmd/mesh-host/engineseat.go @@ -333,8 +333,8 @@ func composeFrom(current *agentNow, composer *atomic.Pointer[selfcompose.Compose // packagesReadFor bounds the verb packages' reads and its write. const packagesReadFor = 20 * time.Second -// packagesFrom answers the verb packages (novox/hq ADR 0565 §6, §15): this node's key of the engine's state packages -// beside the controller's mirror's key for it and their difference; with write, the key written again from what the +// packagesFrom answers the verb packages (novox/hq ADR 0565 §6, §15): this node's key of the engine's state packages; +// with write, the key written again from what the // composer that runs now handed, refused in words while the node composes none of its own. func packagesFrom(node string, session *link.Session, composer *atomic.Pointer[selfcompose.Composer]) func(write bool) (any, error) { return func(write bool) (any, error) { @@ -344,8 +344,8 @@ func packagesFrom(node string, session *link.Session, composer *atomic.Pointer[s if write { c := composer.Load() if c == nil { - return nil, errors.New("this node composes none of its own modules now (it applies the controller's " + - "compositions, or its node agent is starting), so it has no packages of its own to write") + return nil, errors.New("this node composes none of its own modules now (its node agent is starting), " + + "so it has no packages of its own to write") } w, err := c.WritePackages(ctx) if err != nil { @@ -354,12 +354,7 @@ func packagesFrom(node string, session *link.Session, composer *atomic.Pointer[s wrote = &w } key, keyErr := link.EnginePackages{Session: session}.Get(ctx, node) - var mirror []byte - h, mirrorErr := session.KVHistory(ctx, link.MirrorPackagesBucket, node) - if mirrorErr == nil && len(h) > 0 && !h[len(h)-1].Deleted { - mirror = h[len(h)-1].Value - } - v := link.PackagesAnswer(node, key, mirror, keyErr, mirrorErr) + v := link.PackagesAnswer(node, key, keyErr) v.Wrote = wrote return v, nil } diff --git a/internal/link/enginepackages.go b/internal/link/enginepackages.go index 50e7adf..3e7d16c 100644 --- a/internal/link/enginepackages.go +++ b/internal/link/enginepackages.go @@ -17,10 +17,6 @@ import ( // mesh-host.packages (delivery.EnginePackagesRead). const PackagesBucket = "mesh-host_" + delivery.EnginePackagesState -// MirrorPackagesBucket is the controller's declared-packages mirror (ADR 0507 §5), which the engine reads its own node's -// key of until the lease write, to compare (the node seat's verb packages). -const MirrorPackagesBucket = "mesh-controller_declared-packages" - // packagesStore binds PackagesBucket over the link open now: bound, never created. func (l *natsLink) packagesStore() (nats.KeyValue, error) { kv, err := l.js.KeyValue(PackagesBucket) @@ -83,51 +79,33 @@ func (p EnginePackages) Put(_ context.Context, key string, value []byte) error { return err } -// PackagesView is the node seat's verb packages' answer (ADR 0565 §6): this node's key of the engine's state packages, -// and until the lease write the controller's mirror's key for the node and every package they hold differently. +// PackagesView is the node seat's verb packages' answer (ADR 0565 §6): this node's key of the engine's state packages. +// The controller's mirror it was once compared with ended with the controller (issue 1126). type PackagesView struct { Node string `json:"node"` // Key is the node's key as the bus holds it (Source); null when the engine has written none. Source string `json:"source"` Key *delivery.DeclaredPackages `json:"key"` KeyError string `json:"key_error,omitempty"` - // Mirror is the controller's mirror's key for the node (MirrorSource); null when it holds none or cannot be read. - MirrorSource string `json:"mirror_source"` - Mirror *delivery.DeclaredPackages `json:"mirror"` - MirrorError string `json:"mirror_error,omitempty"` - // Differences is every package the two hold differently, read only when both are; Agree when there is none. - Differences []delivery.PackageDifference `json:"differences"` - Agree bool `json:"agree"` // Wrote says, for write, whether the key was written again (false: it already held what the engine composed). Wrote *bool `json:"wrote,omitempty"` Note string `json:"note,omitempty"` } -// PackagesAnswer is the verb's answer from the two keys as read (nil: no key), each with its read's error. -func PackagesAnswer(node string, key, mirror []byte, keyErr, mirrorErr error) PackagesView { - v := PackagesView{Node: node, Source: delivery.EnginePackagesRead, MirrorSource: "mesh-controller.declared-packages", - Differences: []delivery.PackageDifference{}} - read := func(raw []byte, err error, into **delivery.DeclaredPackages, said *string) { - if err == nil && raw != nil { - var d delivery.DeclaredPackages - if err = json.Unmarshal(raw, &d); err == nil { - *into = &d - } - } - if err != nil { - *said = err.Error() +// PackagesAnswer is the verb's answer from the key as read (nil: no key), with its read's error. +func PackagesAnswer(node string, key []byte, keyErr error) PackagesView { + v := PackagesView{Node: node, Source: delivery.EnginePackagesRead} + if keyErr == nil && key != nil { + var d delivery.DeclaredPackages + if keyErr = json.Unmarshal(key, &d); keyErr == nil { + v.Key = &d } } - read(key, keyErr, &v.Key, &v.KeyError) - read(mirror, mirrorErr, &v.Mirror, &v.MirrorError) switch { - case v.Key != nil && v.Mirror != nil: - v.Differences = delivery.DiffDeclaredPackages(*v.Key, *v.Mirror) - v.Agree = len(v.Differences) == 0 - case v.Key == nil && v.KeyError == "": - v.Note = "the engine has written no key for this node yet: pacman reads the controller's mirror for it" - case v.Mirror == nil && v.MirrorError == "": - v.Note = "the controller's mirror holds no key for this node: nothing to compare" + case keyErr != nil: + v.KeyError = keyErr.Error() + case v.Key == nil: + v.Note = "the engine has written no key for this node yet" } return v } diff --git a/internal/link/handover_test.go b/internal/link/handover_test.go index 27cb742..8be95ba 100644 --- a/internal/link/handover_test.go +++ b/internal/link/handover_test.go @@ -241,15 +241,13 @@ func TestALinkThatHearsHandOversIsGivenTheQueues(t *testing.T) { return map[string]any{"module": module, "digest": digest, "diff": diff}, nil }, Compose: func(module string) (any, error) { return map[string]any{"composed": true, "retried": module}, nil }, - // ADR 0565 §6, §15: the node's key beside the controller's mirror's, the mirror's from its writer's test. + // ADR 0565 §6, §15: the node's key of the engine's state packages. Packages: func(write bool) (any, error) { key := []byte(`{"node":"n1","packages":[{"package":"curl","modules":["backup","web"],"absent":false}]}`) - mirror := []byte(`{"node":"n1","packages":[{"package":"curl","modules":["backup","web"],"absent":false},` + - `{"package":"nano","modules":["web"],"absent":true}]}`) if !write { - return PackagesAnswer("n1", key, mirror, nil, nil), nil + return PackagesAnswer("n1", key, nil), nil } - v := PackagesAnswer("n1", nil, nil, nil, errors.New("not granted")) + v := PackagesAnswer("n1", nil, nil) wrote := true v.Wrote = &wrote return v, nil @@ -265,11 +263,10 @@ func TestALinkThatHearsHandOversIsGivenTheQueues(t *testing.T) { // The recovery verb compose (issue 774) makes a pass now, with a module's apply tried again when named. {"compose", EngineToolSubject("compose", "n1"), `"composed":true,"retried":""`}, {"compose", EngineToolSubject("compose", "n1") + " " + `{"module":"route-proxy"}`, `"retried":"route-proxy"`}, - // The read verb packages (ADR 0565 §6) names both sources and their difference; with write, what it wrote, and a - // key the engine has not written, or a mirror it may not read, is said and not an error. - {"packages", EngineToolSubject("packages", "n1"), `"differences":[{"package":"nano","absent":true,"engine":[],"mirror":["web"]}],"agree":false`}, + // The read verb packages (ADR 0565 §6) names its source; with write, what it wrote, and a key the engine has not + // written is said and not an error. {"packages", EngineToolSubject("packages", "n1"), `"source":"mesh-host.packages","key":{"node":"n1"`}, - {"packages", EngineToolSubject("packages", "n1") + " " + `{"write":true}`, `"mirror":null,"mirror_error":"not granted","differences":[],"agree":false,"wrote":true`}, + {"packages", EngineToolSubject("packages", "n1") + " " + `{"write":true}`, `"key":null,"wrote":true`}, {"packages", EngineToolSubject("packages", "n1") + " " + `{"write":"true"}`, `"note":"the engine has written no key for this node yet`}, {"unserved", "mesh.seat.node-engine.call.unserved.n1.x", `"error":"the node seat node-engine serves no verb \"unserved\""`}, } {