From 37856199670c23bd337752efbbda1efe3c3855dc Mon Sep 17 00:00:00 2001 From: jedarden Date: Fri, 3 Jul 2026 16:54:15 -0400 Subject: [PATCH] feat(fusion): assert engine node positions at startup (bf-1tsm) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bf-6s3d added TestEngine_SeedNodePositions, the host/unit-test half of the node-position invariant (it replays main.go's seeding loop against the engine and asserts NodeCount()/NodePositions() hold distinct, non-(0,0,1) positions). This bead adds the complementary half the NodePositions() docstring (fusion.go:140-159) already advertises: "a logged startup assertion" exercised in the live path. Right after the main.go seeding loop (which calls fusionEngine.SetNodePosition(node.MAC, node.PosX, node.PosY, node.PosZ) per fleet node), the engine now inspects NodePositions() and logs: - n == 0 → INFO "no nodes positioned yet" (valid; IO-1 reaches ready with no nodes attached) - atDefault == n → WARN "all N seeded nodes collapsed to (0,0,1)" (fleet never positioned → degenerate fusion) - otherwise → INFO "N nodes seeded, D distinct, A at (0,0,1)" The (0,0,1) collapse is the failure mode: when no node has been positioned, every node retains the nodes-table schema default (pos_x=0, pos_y=0, pos_z=1) and Fuse emits only degenerate peaks. The assertion surfaces this without blocking startup (the hard gate stays the bf-6s3d unit test). Also gofmt's the file (drive-by): bf-3f6q's "fusion" import sat after "ingestion" instead of alphabetically after "floorplan". go build ./... / go vet ./... / go test ./... all pass; gofmt clean. Co-Authored-By: Claude Bead-Id: bf-1tsm --- mothership/cmd/mothership/main.go | 35 ++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/mothership/cmd/mothership/main.go b/mothership/cmd/mothership/main.go index 0e58ed4..621dc1b 100644 --- a/mothership/cmd/mothership/main.go +++ b/mothership/cmd/mothership/main.go @@ -43,11 +43,11 @@ import ( "github.com/spaxel/mothership/internal/falldetect" "github.com/spaxel/mothership/internal/fleet" "github.com/spaxel/mothership/internal/floorplan" + "github.com/spaxel/mothership/internal/fusion" guidedtroubleshoot "github.com/spaxel/mothership/internal/guidedtroubleshoot" "github.com/spaxel/mothership/internal/health" featurehelp "github.com/spaxel/mothership/internal/help" "github.com/spaxel/mothership/internal/ingestion" - "github.com/spaxel/mothership/internal/fusion" "github.com/spaxel/mothership/internal/learning" "github.com/spaxel/mothership/internal/loadshed" "github.com/spaxel/mothership/internal/localization" @@ -1061,6 +1061,39 @@ func main() { } } + // bf-1tsm: Startup assertion over fusionEngine.NodePositions() (the + // fusion.go:140-159 docstring notes this startup-assertion use). After the + // seeding loop above, the engine must hold populated, distinct positions — + // NOT every node collapsed to the nodes-table schema default (pos_x=0, + // pos_y=0, pos_z=1 → NodePosition{X:0,Y:0,Z:1}). A fleet where every seeded + // node still sits at that default was never positioned: all nodes are + // co-located, so Fuse emits only degenerate peaks and cannot localize. + // This logs the assertion result but does NOT block startup, because an + // empty or freshly-onboarded fleet is a valid state (IO-1: the server + // reaches ready with no nodes positioned). The hard regression gate for + // the invariant is TestEngine_SeedNodePositions (bf-6s3d). + { + type posKey struct{ x, y, z float64 } + positions := fusionEngine.NodePositions() + atDefault := 0 + seen := make(map[posKey]struct{}, len(positions)) + for _, p := range positions { + if p.X == 0 && p.Y == 0 && p.Z == 1 { + atDefault++ + } + seen[posKey{p.X, p.Y, p.Z}] = struct{}{} + } + switch n := len(positions); { + case n == 0: + log.Printf("[INFO] 3D fusion engine node-position assertion: no nodes positioned yet") + case atDefault == n: + log.Printf("[WARN] 3D fusion engine node-position assertion FAILED: all %d seeded nodes collapsed to (0,0,1) — position nodes in the dashboard before fusion can localize", n) + default: + log.Printf("[INFO] 3D fusion engine node-position assertion OK: %d nodes seeded, %d distinct positions, %d at (0,0,1) default", + n, len(seen), atDefault) + } + } + // Start the self-improving localization system selfImprovingLocalizer.Start() log.Printf("[INFO] Self-improving localization started (room: %.1fx%.1fm, interval: %v)",