diff --git a/cmd/mesh-controller/changeplan.go b/cmd/mesh-controller/changeplan.go index 5d3bfb08..b895614d 100644 --- a/cmd/mesh-controller/changeplan.go +++ b/cmd/mesh-controller/changeplan.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "path" "slices" "sort" "strings" @@ -114,12 +115,21 @@ const classNone = "none" // changeClassOf is a change plan's class (novox/hq ADR 0313 ยง2): that of the slowest-budgeted module it moves, // itself or as a dependent โ€” core when one is on the controller's own path or holds the mesh's resolver, leaf -// otherwise, a module it adds a directory for included โ€” and none when it moves nothing. +// otherwise โ€” and none when it moves nothing. A module it adds a directory for counts by its own class: the +// catalogue names a module's directory by the module, so the directory's last element is its name. The graph holds +// no manifest of it yet, so a new module is core by its name alone (on the controller's own path), never by a +// resolver claim its manifest makes. func changeClassOf(p link.ChangePlan, entries []inventory.Entry) string { if len(p.Moved) == 0 && len(p.Dependents) == 0 && len(p.New) == 0 { return classNone } - return classOfModules(append(append([]string{}, p.Moved...), p.Dependents...), entries) + modules := append(append([]string{}, p.Moved...), p.Dependents...) + for _, dir := range p.New { + if name := path.Base(strings.Trim(dir, "/")); name != "." && name != "/" && name != "" { + modules = append(modules, name) + } + } + return classOfModules(modules, entries) } // grantsStepWord names the grants step (novox/hq issue 490): the bus's user list sent alone to the machine diff --git a/cmd/mesh-controller/fixclass_test.go b/cmd/mesh-controller/fixclass_test.go index a6e828c2..86e61c44 100644 --- a/cmd/mesh-controller/fixclass_test.go +++ b/cmd/mesh-controller/fixclass_test.go @@ -43,6 +43,9 @@ func TestAChangePlanSaysTheClassOfWhatItMoves(t *testing.T) { {"the mesh's resolver", []string{"modules/dnsmasq/module.json"}, inventory.ClassCore}, {"a leaf and a core module together: the slowest-budgeted", []string{"modules/gitea/index.ts", "modules/dnsmasq/module.json"}, inventory.ClassCore}, {"nothing any module's build reads", []string{"README.md"}, "none"}, + {"a new leaf module alone", []string{"modules/weather/module.json"}, inventory.ClassLeaf}, + {"a new module on the controller's own path, by its own class", []string{"next/mesh-delivery/module.json"}, inventory.ClassCore}, + {"a new leaf module beside a core one moved", []string{"modules/weather/module.json", "modules/dnsmasq/module.json"}, inventory.ClassCore}, } { if got := plan(c.paths...).Class; got != c.want { t.Errorf("%s: class %q, want %q", c.why, got, c.want)