From 3167f0104f794603446eb60299f91cc036cd17b2 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 12 Nov 2026 22:16:27 +0100 Subject: [PATCH] H1 (review r2): the foundation host is the entry that claims the seat node-engine, not a module named mesh-host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds: design 47 G §527, §634 --- internal/compose/resolution/seat_dependencies.go | 7 +++++-- .../compose/resolution/seat_dependencies_test.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/internal/compose/resolution/seat_dependencies.go b/internal/compose/resolution/seat_dependencies.go index a6556734..42fca172 100644 --- a/internal/compose/resolution/seat_dependencies.go +++ b/internal/compose/resolution/seat_dependencies.go @@ -29,6 +29,10 @@ var appliedThrough = map[string]string{ // A variable rather than a constant only so a test can hold both behaviours; nothing else sets it. var enforceSeatDependencies = true +// nodeEngineSeat is the seat the node-engine's own module claims (design 47 G §508, §527, §634): the +// foundation's host is the entry that claims it, never a module named here. +const nodeEngineSeat = "node-engine" + // foundationModules are the pieces genesis lays before any module exists (novox/hq ADR 0207 §5): // the host and the private network. Registered as modules so they can be assigned, but what they // declare is the installation's, not a module's, so it is never judged. The third piece, the @@ -38,7 +42,6 @@ var enforceSeatDependencies = true // The private network's module is overlay.Name, written out because the overlay package composes // on top of this one; its resources are computed, which exempts it by the rule below as well. var foundationModules = map[string]bool{ - "mesh-host": true, "mesh-wireguard": true, } @@ -46,7 +49,7 @@ var foundationModules = map[string]bool{ // module whose resources are computed is the mesh's by construction — the private network's peer // list and its tools are the controller's, written per node — so it counts whatever its name. func isFoundation(m mcat.Manifest) bool { - return foundationModules[m.Module] || m.Computed != "" + return foundationModules[m.Module] || m.ClaimsSeat(nodeEngineSeat) || m.Computed != "" } // DependsOn is every seat a module needs held on its node: derived from the resource types it diff --git a/internal/compose/resolution/seat_dependencies_test.go b/internal/compose/resolution/seat_dependencies_test.go index c514f16a..806fdf7e 100644 --- a/internal/compose/resolution/seat_dependencies_test.go +++ b/internal/compose/resolution/seat_dependencies_test.go @@ -95,3 +95,17 @@ func TestWithTheSwitchFlippedAnUnmetDependencyRefusesTheNode(t *testing.T) { t.Errorf("the first pass refused an unmet dependency: %v", err) } } + +func TestTheFoundationHostIsTheEntryThatClaimsTheNodeEngineSeat(t *testing.T) { + svc := res("service", "x") + // Whatever its name, the entry claiming the seat is the foundation's: nothing to depend on. + engine := withResources(mod("the-engine", nil, nil, nil, mcat.Claim{Name: "node-engine"}), svc) + if got := DependsOn(nil, engine); len(got) != 0 { + t.Errorf("the entry claiming node-engine depends on %v, want nothing (§527)", got) + } + // A module named as the old code named the engine, claiming no such seat, is an ordinary module. + named := withResources(mod("mesh-host", nil, nil, nil), svc) + if got := DependsOn(nil, named); !reflect.DeepEqual(got, []string{mcat.ServiceManagerSeat}) { + t.Errorf("a module merely named mesh-host depends on %v, want the service manager seat", got) + } +}