From d8aa565fee8a5023bfd28f3cb4f5a49da3bf4434 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 25 Oct 2026 01:47:57 +0200 Subject: [PATCH] Keep the filled paths of the modules moved into a node's own composition, which the node-engine otherwise wrote empty (issue 1945) --- cmd/mesh-controller/composed_objects_test.go | 44 ++++++++++++++++++++ cmd/mesh-controller/composed_writer.go | 22 ++++++++-- go.mod | 2 +- go.sum | 4 +- testdata/beside/CAPTURED | 2 +- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/cmd/mesh-controller/composed_objects_test.go b/cmd/mesh-controller/composed_objects_test.go index 5716ec24..7fe9b8d9 100644 --- a/cmd/mesh-controller/composed_objects_test.go +++ b/cmd/mesh-controller/composed_objects_test.go @@ -7,6 +7,10 @@ import ( "strings" "testing" + "encoding/json" + + sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery" + sdkdecl "git.novox.be/novox/mesh-sdk/go/declaration" ) @@ -56,3 +60,43 @@ func TestACompositionNamingAnObjectTheBucketLacksIsRefused(t *testing.T) { t.Fatalf("a composition naming a missing object was written: %v", err) } } + +// Every file the provisioner fills stays filled in the compositions, a moved module's too: the node's own composition +// carries the path, so the node-engine leaves its content to the provisioner (issue 1945, live on 2026-10-25 at 01:22: +// ace's node-engine wrote claude-code's, node-env's and power's filled files empty, because the compositions dropped the +// filled paths of every module moved into the node's own, and their resources say no content). +func TestEveryFilledFileStaysFilledInTheCompositions(t *testing.T) { + for _, node := range []string{"ace", "g14"} { + body, fps := capturedBody(t, node) + var d struct { + Filled []string `json:"filled_files"` + } + if err := json.Unmarshal(body, &d); err != nil || len(d.Filled) == 0 { + t.Fatalf("%s: the captured declaration fills nothing (%v)", node, err) + } + // power, claude-code and node-env have no fingerprint of the new form, so they move into the node's own. + for _, m := range []string{"power", "claude-code", "node-env"} { + delete(fps, m) + } + comps, _, err := compositionsOf(node, body, fps, nil, nil) + if err != nil { + t.Fatalf("%s: %v", node, err) + } + filled := map[string]bool{} + for _, c := range comps { + for _, p := range c.Filled { + filled[p] = true + } + } + for _, p := range d.Filled { + if !filled[p] { + t.Errorf("%s: %s is filled by the provisioner, and no composition says so", node, p) + } + } + for key, c := range comps { + if _, err := sdkdelivery.EncodeComposed(c); err != nil { + t.Errorf("%s: %s: %v", node, key, err) + } + } + } +} diff --git a/cmd/mesh-controller/composed_writer.go b/cmd/mesh-controller/composed_writer.go index be268e93..a6fec709 100644 --- a/cmd/mesh-controller/composed_writer.go +++ b/cmd/mesh-controller/composed_writer.go @@ -99,11 +99,16 @@ func compositionsOf(node string, body []byte, fps map[string]string, manifests m } resources = append(resources, raw) } - // A filled file of a module moved into the node's own composition is no module's file any more: the provisioner - // fills it all the same, but a composition carries only its own module's filled paths. - var filled []string + // A filled file of a module moved into the node's own composition is no module's file any more, and the provisioner + // fills it all the same: the node's own composition carries its path, so the node-engine leaves the content to the + // provisioner and accepts its write. Dropping the path had the node-engine write those files empty, since their + // resources say no content (issue 1945, live on 2026-10-25: claude-code's, node-env's and power's files on ace, g14 + // and shanks). + var filled, ownFilled []string for _, p := range envelope.Filled { - if !filledByMoved(envelope.Resources, p, inModule) { + if filledByMoved(envelope.Resources, p, inModule) { + ownFilled = append(ownFilled, p) + } else { filled = append(filled, p) } } @@ -113,6 +118,15 @@ func compositionsOf(node string, body []byte, fps map[string]string, manifests m if err != nil { return nil, nil, err } + if len(ownFilled) > 0 { + own, err := sdkdelivery.ComposedNodeKey(node) + if err != nil { + return nil, nil, err + } + c := out[own] + c.Filled = append(c.Filled, ownFilled...) + out[own] = c + } // A module the node runs that composes no resource of its own (all its needs met by others) still has its // composition, empty, at its fingerprint: its want names that composition (the assigner's seventh rule), and without // one no want could ever be written. diff --git a/go.mod b/go.mod index a6bd6973..9f42d778 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/novox/mesh-controller go 1.26.0 require ( - git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024210118-748c564cdf7c + git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024234636-10b6e202320f github.com/jackc/pgx/v5 v5.10.0 github.com/nats-io/nats-server/v2 v2.11.17 github.com/nats-io/nats.go v1.54.0 diff --git a/go.sum b/go.sum index 1f380c29..8d8e9173 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ git.novox.be/novox/mesh-host v0.0.0-20261018171808-9d9e3a46c4f5 h1:E5stfIeCN6uYgA9TCv+4K8JgaURg3zQppvUgJLaDMP4= git.novox.be/novox/mesh-host v0.0.0-20261018171808-9d9e3a46c4f5/go.mod h1:4wyoCr6IIIIarEUuROThWDj7DAvJvIidqMjWcpSnOwE= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024210118-748c564cdf7c h1:pY2YpHSSsfDPBrOEiWTNSIPn0zdnAayRmtmmyQ3hCHY= -git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024210118-748c564cdf7c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024234636-10b6e202320f h1:lUEyNPPF3w4Q0LIgcUUD6ofkKVhCF0n7dVjMoixQmXo= +git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024234636-10b6e202320f/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/testdata/beside/CAPTURED b/testdata/beside/CAPTURED index 7e778f5a..62c0a6ec 100644 --- a/testdata/beside/CAPTURED +++ b/testdata/beside/CAPTURED @@ -9,7 +9,7 @@ a second place to clean besides the catalogue itself. mesh-catalog 3a1017f5594217092a654dd1e2d72226766bc16a modules/*/module.json, modules/*/files, modules/*/templates, predefined.json mesh-host 9d9e3a46c4f5943cfffb413d962ce48b75e2fd39 examples/foundation-first-node.lock -mesh-sdk 748c564cdf7cd2cad57073cdf54bd752c1947025 conformance/events/module-event.json +mesh-sdk 10b6e202320f38301c67a6822b0734e0b870ea30 conformance/events/module-event.json To move them, from this repository's root, with the two repositories checked out beside it: