From 005ae09ae76041cd27175cab667dc0c7928d90a0 Mon Sep 17 00:00:00 2001 From: jedarden Date: Tue, 7 Jul 2026 07:33:40 -0400 Subject: [PATCH] docs(bf-4th1x): post synthesized REJECT/token root-cause comment on bf-4iewr Synthesize the three bf-34lwt children (bf-5ig3e, bf-29wyl, bf-2hdbg) into the definitive root-cause finding on the grandparent bf-4iewr, with file:line citations for all six confirmed points. States explicitly that current 'no reject' is a 24h migration-window MASK (not a real fix) and that the token-supply path is currently dead (X-Spaxel-Token header ignored, hello.Token always empty). Co-Authored-By: Claude --- .beads/issues.jsonl | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 6640273..be65d12 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,17 +1,21 @@ +{"id":"bf-11dw","title":"Land the IO-6 hard-gate test behind a build tag so go test ./... stays green","description":"Split from bf-5312 (IO-6 hard-gate scenario + triage gate). Child 1 of 4 — the unblocker. Depends on nothing.\n\nWHY bf-5312 FAILED 3x: NEEDLE runs `cd mothership && go test ./...` (no -short) on every bead close (.needle.yaml:42,67). The IO-6 hard-gate test TestIO6HardGate_WalkerProducesTrackedBlob currently lives in mothership/tests/e2e/e2e_test.go with NO build tag, so the default suite picks it up. It is correctly strict and therefore RED (zero tracked blobs) until upstream bf-4q5w wires fusion Engine.SetNodePosition — so every close attempt fails NEEDLE's mandatory test run. The ~217 IO-6 lines are also currently UNCOMMITTED in the working tree.\n\nSCOPE (do NOT weaken the assertion — it stays byte-for-byte strict):\n- Move the IO-6-only code out of e2e_test.go into a NEW file mothership/tests/e2e/io6_gate_test.go with a `//go:build io6_gate` build constraint as the FIRST line. IO-6-only symbols (confirmed used nowhere else in the e2e dir): TestIO6HardGate_WalkerProducesTrackedBlob, CaptureIO6Diagnostics, io6RunMaxima, GetNodeRecords, GetStatus, StatusResponse.\n- Leave the SHARED helpers (GetBlobCount, GetEvents, WatchDashboardWS, AssertBlobObserved, AssertDetectionEventsObserved, AssertDuringRun, RunSimulator) in e2e_test.go / assertions_test.go — UNTAGGED — because TestFullE2EIntegration and TestDetectionEvents reuse them.\n- The assertion body is unchanged: still t.Fatalf on zero blobs, still NO skip-on-zero-blobs, still NO log-downgrade.\n- Commit the move.\n- Document the gated invocation as a runbook comment at the top of io6_gate_test.go: `cd mothership && go test -tags io6_gate -run TestIO6HardGate ./tests/e2e/...`\n\nACCEPTANCE CRITERIA:\n- `cd mothership && go test ./...` is GREEN (IO-6 gate excluded by the build tag — this is what unblocks NEEDLE closure for the sibling e2e work).\n- `cd mothership && go vet ./...` is clean.\n- `cd mothership && go test -tags io6_gate ./tests/e2e/...` COMPILES (it is expected to FAIL/RED until bf-4q5w; child 2 verifies the RED state).\n- The assertion in TestIO6HardGate_WalkerProducesTrackedBlob is unchanged (strict).\n- The change is committed.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T01:23:27.490090045Z","updated_at":"2026-07-07T01:23:27.490090045Z","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-13gwk","title":"Spatial quick actions (Component 32)","description":"## Goal\nRight-click (desktop) or long-press (mobile) anywhere in 3D view to get context-sensitive actions based on what's under cursor.\n\n## Scope\nActions per target:\n- On blob: 'Who is this?' (BLE assignment), 'Why is this here?' (explainability), 'Follow' (camera tracks), 'Create automation here', 'Mark incorrect' (thumbs-down), 'Track history'\n- On node: 'Diagnostics' (CSI plot), 'Blink LED', 'Reposition', 'Update firmware', 'Show links', 'Disable/Enable'\n- On empty floor space: 'What happened here?' (filter timeline), 'Add trigger zone', 'Add virtual node', 'Coverage quality'\n- On zone label: 'Zone history', 'Edit zone', 'Create automation', 'Crowd flow'\n- On portal: 'Crossing log', 'Edit portal', 'Reverse direction'\n- On trigger volume: 'Edit trigger', 'Test', 'View log', 'Disable/Enable'\n\n## Implementation\nThree.js Raycaster determines what's under cursor\nSingle context menu component renders appropriate options\nEach action dispatches to existing dashboard functions (no new backend endpoints needed)\n\n## Acceptance\n- Right-click/long-press on blob shows blob-specific actions\n- Right-click/long-press on node shows node-specific actions\n- Right-click/long-press on empty space shows space-specific actions\n- Actions execute correctly\n- Works on both desktop (right-click) and mobile (long-press)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:06:11.548191949Z","updated_at":"2026-05-06T08:32:57.207248413Z","closed_at":"2026-05-06T08:32:57.207248413Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-13s0","title":"Locate blob data structure definition","description":"## Objective\nFind where the blob/person data structure is defined in the 3D dashboard frontend.\n\n## Scope\n- Search the dashboard codebase for blob/person type definitions\n- Identify the file(s) where the blob data structure is defined\n- Document the current structure and where it's used\n\n## Acceptance Criteria\n- [ ] Located the primary blob data structure definition\n- [ ] Identified all files that create or use blob data\n- [ ] Documented the current blob structure fields\n- [ ] Know which TypeScript interface/type file to modify\n\n## Notes\nResearch-only task. No code changes yet.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T04:41:16.980053426Z","updated_at":"2026-07-06T05:01:27.282036255Z","closed_at":"2026-07-06T05:01:27.282036255Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-15hy","title":"Build spaxel:0.1.354, deploy to ardenone-cluster, re-flash ESP32","description":"After bf-49fp (Dockerfile cache-bust fix) is merged:\n\n1. Bump VERSION file to 0.1.354\n2. Push to git to trigger spaxel-build CI on iad-ci\n3. Monitor workflow until ronaldraygun/spaxel:0.1.354 is pushed\n4. In ~/declarative-config, update the spaxel Deployment image tag to 0.1.354\n5. Push declarative-config, ArgoCD syncs (Recreate strategy, ~30s downtime expected)\n6. Verify mothership is healthy at spaxel.ardenone.com/healthz\n\nThen re-flash the ESP32:\n- Navigate to spaxel.ardenone.com provision page\n- Connect ESP32-S3 via USB\n- Run flash — mothership will now serve the 4MB firmware (no more crash loop)\n\nSuccess criteria: ESP32 boots, starts captive portal or connects to WiFi, no reboot loop.\nCI submit cmd: kubectl --kubeconfig=~/.kube/iad-ci.kubeconfig create -f - (spaxel-build workflow)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":0,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-06-04T03:13:21.294634213Z","updated_at":"2026-07-02T18:58:14.160082365Z","closed_at":"2026-07-02T18:58:14.160082365Z","close_reason":"Superseded","source_repo":".","compaction_level":0,"comments":[{"id":14,"issue_id":"bf-15hy","author":"cli","text":"Cannot complete as written — bead is stale. (1) VERSION REGRESSION: targets 0.1.354 but repo is at 0.1.357 and deployed mothership at spaxel.ardenone.com/healthz reports 0.1.358 (healthy, ~7.9d uptime); bumping to 0.1.354 would be a downgrade. (2) PREREQUISITE SATISFIED: bf-49fp is CLOSED, merged as commit 301a588 (cache-bust / firmware crash-loop fix) and already deployed in 0.1.358. (3) PHYSICAL STEP UNREACHABLE: ESP32 re-flash needs a human to connect the ESP32-S3 via USB and flash via the browser provision page (Web Serial). No serial device attached (/dev/ttyUSB*, /dev/ttyACM* absent, no USB-serial in lsusb), no firmware .bin in repo, esptool not installed. Deploy intent (get crash-loop fix built and live) is already done by 0.1.357/0.1.358; only the physical reflash remains, which needs a human + connected hardware. Leaving OPEN per retry guidance.","created_at":"2026-07-02T18:53:29.861919590Z"},{"id":15,"issue_id":"bf-15hy","author":"cli","text":"RETRY VERIFICATION (2026-07-02T18:56:56Z): Task is stale and structurally impossible to execute as written. (1) VERSION is 0.1.357 locally; task wants 0.1.354 = a downgrade. (2) Deployed mothership spaxel.ardenone.com/healthz = {status:ok, version:0.1.358, uptime_s:683554 (~7.9d), db:ok, shedding_level:0} -- healthy, NOT crash-looping. (3) Prereq bf-49fp (Kaniko cache-bust fix) is CLOSED, merged as 301a588, already shipped in 0.1.358. The crash-loop fix this chain exists to deliver is already built, deployed, and stable. (4) Only remaining step = physical ESP32-S3 USB re-flash via browser Web Serial, which is unreachable from a shell: no /dev/ttyUSB* or /dev/ttyACM*, no esptool installed, no release spaxel-*.bin in repo. Closing as SUPERSEDED; no code/cluster changes made. Sole outstanding item = manual physical reflash against the already-deployed 0.1.358.","created_at":"2026-07-02T18:56:56.492556342Z"}]} -{"id":"bf-16c1","title":"Strengthen TestDetectionEvents to assert at least one detection event","description":"Split from bf-5jeo (verification capstone). Depends on bf-2330 (assertion primitives). Child 2 of 4.\n\nCONTEXT: mothership/tests/e2e/e2e_test.go:679 TestDetectionEvents currently only checks events != nil. The comment says \"The endpoint must return a valid (possibly empty) events list\" and t.Logf(\"Events API functional: found %d detection events\", ...) — it explicitly ducks the count. This is one of the dodges called out in bf-5jeo.\n\nSCOPE:\n- Replace the nil-only check with a hard assertion that len(events.Events) >= 1, using the helper added in bf-2330.\n- Provide an actionable failure message (include count observed + that the fusion pipeline produced no blobs).\n- Keep the existing 4-node / 2-walker / 15s RunSimulator config and the timeout handling.\n\nACCEPTANCE CRITERIA:\n- TestDetectionEvents asserts >= 1 detection event (no longer accepts an empty list as pass).\n- Failure message names the likely upstream cause (fusion/tracking produced no blobs).\n- Test compiles (go build ./tests/e2e/...).\n- NOTE: this test is expected to be RED until upstream bf-4q5w (fusion SetNodePosition wiring) lands — that strictness is the entire point of this capstone. Do NOT weaken the assertion to make it green.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T18:51:03.114403566Z","updated_at":"2026-07-06T18:51:03.114403566Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-16c1","depends_on_id":"bf-2330","type":"blocks","created_at":"2026-07-06T18:51:13.287924396Z","created_by":"cli","thread_id":""}]} +{"id":"bf-15oi","title":"Capture end-to-end runtime logs and confirm zero undefined-field errors","description":"## Objective\nDecomposed from parent bf-f841 (final step). Run the full identity-blob scenario end-to-end and produce a captured log artifact proving there are NO runtime errors from undefined/missing identity fields — the explicit goal of the parent's last acceptance criterion.\n\n## Scope\n- Run the complete runtime scenario established by the prior steps (blob created + identity populated + projections fed).\n- Capture the runtime log / console output to an artifact (file or quoted log block).\n- Grep/scan the captured log for panic, nil-pointer dereference, or any identity/undefined-field error.\n\n## Acceptance Criteria\n- [ ] Full scenario runs to completion without the process crashing.\n- [ ] A captured runtime log artifact is referenced in the bead.\n- [ ] Scan of the log finds zero panic / nil-deref / undefined-field errors related to identity fields.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T00:10:18.183212356Z","updated_at":"2026-07-07T00:10:18.183212356Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-15oi","depends_on_id":"bf-2v9g","type":"blocks","created_at":"2026-07-07T00:10:18.240851402Z","created_by":"cli","thread_id":""}]} +{"id":"bf-16c1","title":"Strengthen TestDetectionEvents to assert at least one detection event","description":"Split from bf-5jeo (verification capstone). Depends on bf-2330 (assertion primitives). Child 2 of 4.\n\nCONTEXT: mothership/tests/e2e/e2e_test.go:679 TestDetectionEvents currently only checks events != nil. The comment says \"The endpoint must return a valid (possibly empty) events list\" and t.Logf(\"Events API functional: found %d detection events\", ...) — it explicitly ducks the count. This is one of the dodges called out in bf-5jeo.\n\nSCOPE:\n- Replace the nil-only check with a hard assertion that len(events.Events) >= 1, using the helper added in bf-2330.\n- Provide an actionable failure message (include count observed + that the fusion pipeline produced no blobs).\n- Keep the existing 4-node / 2-walker / 15s RunSimulator config and the timeout handling.\n\nACCEPTANCE CRITERIA:\n- TestDetectionEvents asserts >= 1 detection event (no longer accepts an empty list as pass).\n- Failure message names the likely upstream cause (fusion/tracking produced no blobs).\n- Test compiles (go build ./tests/e2e/...).\n- NOTE: this test is expected to be RED until upstream bf-4q5w (fusion SetNodePosition wiring) lands — that strictness is the entire point of this capstone. Do NOT weaken the assertion to make it green.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T18:51:03.114403566Z","updated_at":"2026-07-07T00:34:23.758927308Z","closed_at":"2026-07-07T00:34:23.758927308Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-16c1","depends_on_id":"bf-2330","type":"blocks","created_at":"2026-07-06T18:51:13.287924396Z","created_by":"cli","thread_id":""}],"comments":[{"id":104,"issue_id":"bf-16c1","author":"cli","text":"Implemented: replaced TestDetectionEvents' nil-only events check with a hard assertion that len(events.Events) >= 1, using the AssertDetectionEventsObserved helper from bf-2330. On failure it now t.Fatalf's with an actionable message including the count observed and the likely upstream cause (fusion/tracking produced no blobs from the 4-node/2-walker CSI stream). Preserved the existing 4-node / 2-walker / 20 Hz / 15s RunSimulator config and the duration+2s timeout handling. Verified: go build ./tests/e2e/... OK, go vet ./... clean, assertion-helper unit tests pass. Deliberately strict — expected RED until upstream bf-4q5w (fusion SetNodePosition wiring) lands; assertion was NOT weakened. Commit a8d0759 pushed to main.","created_at":"2026-07-07T00:32:59.442642167Z"}]} {"id":"bf-1837","title":"CI: add idf.py test --target linux firmware gate to spaxel-build WorkflowTemplate","description":"After bf-31bp writes host-based firmware test targets (nvs/csi/serial_prov), wire them into the spaxel-build WorkflowTemplate in declarative-config as a step before the Docker build. firmware/test/ directory does not yet exist and CMakeLists has no host-test targets.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-31T15:43:40.781487343Z","updated_at":"2026-07-06T04:07:57.742934181Z","closed_at":"2026-07-06T04:07:57.742934181Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-18yn","title":"Seed realistic node geometry in spaxel-sim default placement","description":"Split from bf-4q5w (parent umbrella). Depends on the runtime-propagation child.\n\nEven with positions wired, the spaxel-sim default virtual-node placement leaves nodes co-located / at origin, so the Fresnel excess path length |P-T|+|P-R|-|T-R| collapses toward 0 everywhere and the accumulation grid has no meaningful peaks, producing no blobs (the core symptom in bf-4q5w).\n\nScope:\n- In the simulator virtual-node creation path (createVirtualNodes and the simulator package, e.g. internal/simulator/registry_bridge.go where PosX/PosY/PosZ are sourced from node.Position), give the default virtual nodes a realistic spread-out geometry across a default zone instead of co-located/origin positions.\n- Ensure these positions flow into both the registry and the fusion engine via the wiring from prior children.\n- Pick a sane default room/zone size consistent with roomWidth/roomDepth used in main.go for the localization system.\n\nAcceptance criteria:\n- Default sim nodes occupy a spread, non-degenerate geometry (not all at origin, not co-located).\n- A sim or host test shows the fusion accumulation grid produces non-zero peaks with the default placement.\n- Existing simulator tests still pass; go build ./... passes.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T19:22:58.964617777Z","updated_at":"2026-07-06T18:21:40.065868881Z","closed_at":"2026-07-06T18:21:40.065868881Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-18yn","depends_on_id":"bf-3p6g","type":"blocks","created_at":"2026-07-03T19:23:17.356813659Z","created_by":"cli","thread_id":""},{"issue_id":"bf-18yn","depends_on_id":"bf-1kid","type":"blocks","created_at":"2026-07-03T22:10:08.997748955Z","created_by":"cli","thread_id":""},{"issue_id":"bf-18yn","depends_on_id":"bf-4b1c","type":"blocks","created_at":"2026-07-06T04:22:45.618994878Z","created_by":"cli","thread_id":""}],"comments":[{"id":60,"issue_id":"bf-18yn","author":"cli","text":"Closed as complete — verified the full default-placement chain.\n\nbf-18yn is the umbrella; its scope was implemented by the closed children bf-xrej (impl) and bf-1kid (acceptance test), both already on origin/main.\n\nVerification (all green):\n- go build ./... clean across all 3 modules (mothership, cmd/sim, test/acceptance)\n- go test ./... — 50 packages pass\n- go vet ./... clean across all 3 modules\n- Acceptance test TestEngine_DefaultPlacementProducesPeaks passes for default placements of 2/3/4/6 nodes (uses ONLY simulator.DefaultNodePositions, no manual positioning)\n- Counter-example TestEngine_CoLocatedOriginYieldsNoPeaks confirms the assertion is non-trivial (co-located (0,0,1) nodes correctly yield 0 blobs)\n\nScope coverage:\n- Spread geometry: simulator.DefaultNodePositions (node.go) — single node at center, 2 on diagonal, 3+ on expanding grid with mixed Z. registry_bridge.effectivePositions reassigns any default-(0,0,1) origin node a distinct spread slot at sync time (SyncToRegistry/SyncOneNode/ToRegistryRecords).\n- Registry flow: effectivePositions -> SyncToRegistry writes spread positions; bf-24xp persists hello-announced position.\n- Fusion flow: main.go startup seeding loop (main.go:1054-1062) reads registry nodes and calls fusionEngine.SetNodePosition per node; bf-1tsm startup assertion guards distinct/non-(0,0,1) invariant.\n- Room size: DefaultSpace is 6x5x2.5 (plan.md canonical sim example); DefaultNodePositions is parameterized by space.Bounds() so it works for any room incl. main.go's default 10x10x2.5.\n\nNo new code committed — work was already complete and pushed by the children. Nothing left to implement.","created_at":"2026-07-03T23:39:12.940400067Z"},{"id":76,"issue_id":"bf-18yn","author":"cli","text":"Closed out: spread node geometry for spaxel-sim default placement is implemented, committed, and verified against all three acceptance criteria.\n\nImplementation (already committed by prior children of bf-4q5w, verified here):\n- internal/simulator/node.go DefaultNodePositions(space,count) (bf-3fr6): distinct, in-bounds, room-spanning points — center for 1, opposite corners for 2, an expanding ceil(sqrt(n)) grid (>=2 cols & rows, alternating low/high Z band) for >=3. No two nodes co-locate and the geometry is never all-at-origin.\n- internal/simulator/registry_bridge.go effectivePositions (bf-xrej): default-origin (0,0,1) virtual nodes are reassigned deterministic spread slots (sorted-ID order, sized to full node count, collision-free) at sync time so the registry and the fusion engine never see co-located nodes. SyncToRegistry/SyncOneNode/ToRegistryRecords all resolve through it; explicitly-placed nodes are preserved.\n- cmd/sim generateNodePositions: the spaxel-sim CLI binary likewise seeds corners/grid, never origin.\n- Default space 6x5x2.5 (sane residential default; bridge prefers the store's configured room, else DefaultSpace).\n\nAcceptance criteria verified:\n1. Spread non-degenerate geometry — DefaultNodePositions spans both X and Y for count>=2, all positions distinct. node_positions_test.go + registry_bridge_test.go pass.\n2. Host test shows non-zero fusion peaks with default placement — TestEngine_DefaultPlacementProducesPeaks (the bf-18yn acceptance test, nodes=2/3/4/6) passes; TestEngine_GeometryPlacementDrivesFusionPeaks (bf-4b1c differential) shows spread->gridMax=1.0+blobs vs co-located(0,0,1)->gridMax=0.0+no blobs.\n3. go build ./... passes across all three modules (mothership, cmd/sim, test/acceptance); go vet ./... clean; full mothership go test ./... green. Positions flow simulator->registry->fusion via the runtime wiring from bf-u7ds/bf-95tx/bf-3y9r.","created_at":"2026-07-06T18:10:44.606168746Z"},{"id":77,"issue_id":"bf-18yn","author":"cli","text":"Closed bf-18yn: default virtual nodes now receive spread, non-degenerate geometry instead of co-located/origin positions.\n\nThree creation paths covered:\n- cmd/sim createVirtualNodes -> generateNodePositions spreads nodes across corners/grid of the --space room (default 6x5x2.5).\n- simulator.DefaultNodePositions (node.go): room-spanning grid placement, distinct cells, mixed-height Z.\n- registry_bridge.effectivePositions: nodes still at the DB-default origin (0,0,1) are reassigned deterministic spread slots (DefaultNodePositions sized to the full node set) so the registry and downstream fusion engine never see co-located virtual nodes.\n- api AddNode handler (commit 669d379): assigns spread positions for origin nodes, preserving explicitly-set positions.\n\nPositions flow into the registry (SyncToRegistry/SyncOneNode) and into the fusion engine via the prior runtime-propagation child (bf-u7ds) plus main.go's startup SetNodePosition seeding loop.\n\nAcceptance verified:\n- TestEngine_DefaultPlacementProducesPeaks (fusion_test.go) is the explicit bf-18yn acceptance test: seeds nodes via DEFAULT placement only, drives a walker, asserts non-zero peaks (blobs>0 or gridMax>threshold) for node counts 2/3/4/6. Counter-example TestEngine_CoLocatedOriginYieldsNoPeaks confirms co-located nodes yield no peaks, so the assertion is non-trivial.\n- go build ./... passes for mothership, cmd/sim, test/acceptance.\n- go vet ./... clean; go test ./... green across all modules (incl. simulator, fusion, api, tests/e2e).","created_at":"2026-07-06T18:21:30.760397032Z"}]} {"id":"bf-195o","title":"Explore and design default node geometry placement","description":"Child of bf-18yn (split).\n\nResearch the current virtual node creation path and design the approach for realistic node geometry.\n\nScope:\n- Locate createVirtualNodes and simulator package code where PosX/PosY/PosZ are sourced from node.Position\n- Identify the current default placement behavior (likely co-located/origin)\n- Research existing room/zone size constants (roomWidth, roomDepth in main.go)\n- Design a sane geometry layout (e.g., grid placement, random spread with minimum distance, etc.)\n- Document the chosen approach and constants\n\nAcceptance criteria:\n- Document the current virtual node creation flow and where positions are set\n- Identify appropriate room/zone size constants to use\n- Design approach documented (e.g., 'place nodes in a 10x10m grid with 0.5m spacing')\n- No code changes yet — research and design only","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T04:22:13.061882760Z","updated_at":"2026-07-06T04:44:35.213410347Z","closed_at":"2026-07-06T04:44:35.213410347Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-1bmg","title":"Inventory JS and TS dashboard blob creation sites","description":"Scope: all blob creation on the JS/TS dashboard side.\n\nSites to document:\n- dashboard/js/state.js (~:290) — appState.blobs[id] = { id: id } then Object.assign of server payload\n- dashboard/js/websocket.js (~:167) — _blobStates.set(b.id, {x,z,vx,vz,ts}) dead-reckoning cache\n- dashboard/types/spaxel.d.ts — the canonical Blob INTERFACE declaration (personName, personId, assignedColor, identityResolved, etc.) — the single JS identity-field declaration point\n- The 4 unrelated browser new Blob() file-download sites (fleet.js:457, fleet-page.js:1034/1369, fleet.js:1997) — catalogued ONLY to be explicitly skipped (CSV/JSON downloads, not domain blobs)\n- JS test fixtures (ambient.test.js, quick-actions.test.js, replay.test.js) — list for completeness only\n\nReference: notes/bf-3ldj-findings.md sections 2.3-2.5.\n\nAcceptance Criteria:\n- [ ] Every JS blob-creation site listed with file:line + pattern\n- [ ] The spaxel.d.ts Blob interface documented as the canonical JS identity-field declaration\n- [ ] The 4 new Blob() download sites explicitly flagged as OUT OF SCOPE / unrelated browser API\n- [ ] All line numbers verified against current HEAD\n- [ ] Findings written to notes/bf--js-ts.md","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T15:03:46.854905168Z","updated_at":"2026-07-06T19:48:50.158657037Z","closed_at":"2026-07-06T19:48:50.158657037Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1bmg","depends_on_id":"bf-4wwt","type":"blocks","created_at":"2026-07-06T15:04:21.398572664Z","created_by":"cli","thread_id":""}],"comments":[{"id":81,"issue_id":"bf-1bmg","author":"cli","text":"Inventoried every JS/TS dashboard blob-creation site, re-verified against HEAD 68bd308. Findings in notes/bf-1bmg-js-ts.md. Production domain-blob sites: state.js:290 ({id:id} + Object.assign of server payload — canonical creation) and websocket.js:167 (dead-reckoning _blobStates cache). Documented spaxel.d.ts:10-91 Blob interface as the single canonical JS identity-field declaration (personName/personId/assignedColor/identityResolved + deprecated personLabel/personColor) with Go<->JS name map. Flagged the 4 new Blob() file-download sites as OUT OF SCOPE browser API (static/js/fleet.js:457 CSV, fleet-page.js:1034 JSON & :1369 CSV, js/fleet.js:1997 JSON). Listed 12 test fixtures for completeness. Corrected two drifts vs bf-3ldj: the :457 CSV site is in static/js/fleet.js (different file from js/fleet.js), and ambient.test.js literal openings are 124/273/636/653/688/702. Committed 2b44ff6, pushed to main.","created_at":"2026-07-06T19:46:43.072917095Z"}]} {"id":"bf-1h7h","title":"Replace blob markers with humanoid meshes in Three.js","description":"## Objective\nReplace generic 'Blob #2' sphere markers with humanoid figure meshes in the Three.js scene.\n\n## Scope\n- Locate current blob marker rendering code (likely using Three.js SphereGeometry or similar)\n- Design or select a humanoid mesh representation (simple geometric humanoid: cylinder+sphere for body+head, or loaded model)\n- Replace sphere markers with humanoid meshes for identity-resolved blobs\n- Preserve existing scale/position/rotation transforms\n- Ensure humanoid meshes work with existing animation loop\n\n## Dependencies\nDepends on: bf-iner (identity matching must provide personName/assignedColor)\n\n## Acceptance Criteria\n- [ ] Identity-resolved blobs render as humanoid figures, not spheres\n- [ ] Unresolved blobs still render as generic markers (backward compatible)\n- [ ] Humanoid meshes maintain correct position/scale in scene\n- [ ] No console errors from mesh geometry\n- [ ] Scene still renders at 60fps (performance acceptable)\n\n## Notes\nThis adds the humanoid geometry but does NOT yet add per-person colors or name labels.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T04:05:01.330670659Z","updated_at":"2026-07-06T04:05:01.330670659Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1h7h","depends_on_id":"bf-iner","type":"blocks","created_at":"2026-07-06T04:05:04.554395165Z","created_by":"cli","thread_id":""}]} +{"id":"bf-1hcx","title":"File the IO-6 RED-state finding with trace evidence back to bf-4q5w","description":"Split from bf-5312. Child 3 of 4. Depends on child 2 (bf-5k1z, the RED-state trace).\n\nCONTEXT: This is the \"block-back triage gate\" half of bf-5312. The IO-6 gate is RED because upstream bf-4q5w (fusion Engine.SetNodePosition wiring) has not landed. Per bf-5312's CRITICAL TRIAGE GATE: when blobs are zero, do NOT weaken the assertion — instead file the evidence back to bf-4q5w.\n\nSCOPE:\n- Post a comment on bf-4q5w via `br comments add bf-4q5w \"\"` containing: (a) the statement that the IO-6 release hard-gate (TestIO6HardGate_WalkerProducesTrackedBlob) is RED, (b) the captured diagnostic dump from child 2 (distinct announced node positions vs zero tracked blobs / zero detection events), (c) a pointer to the trace under .beads/traces/, (d) the explicit ask: this is blocked on bf-4q5w's fusion Engine.SetNodePosition wiring — do NOT weaken the IO-6 assertion to make it green.\n- Note in the comment that child 4 (the GREEN-flip) is explicitly blocked on bf-4q5w.\n\nACCEPTANCE CRITERIA:\n- A comment with the diagnostic evidence + trace pointer is posted on bf-4q5w.\n- The comment frames the finding as \"blocked on bf-4q5w wiring,\" NOT \"weaken the IO-6 assertion.\"\n- The IO-6 assertion remains strict (untouched).","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T01:24:01.667371622Z","updated_at":"2026-07-07T01:24:01.667371622Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1hcx","depends_on_id":"bf-5k1z","type":"blocks","created_at":"2026-07-07T01:24:29.634932123Z","created_by":"cli","thread_id":""}]} {"id":"bf-1ih7k","title":"TX slot collision detection and adaptive re-stagger","description":"Plan (Component 5 / Fleet Manager) specifies collision monitoring: if CSI frames from two TX nodes arrive within 3ms of each other, log a 'possible slot collision' metric. If collision rate > 5% over a 60-second window, re-randomize stagger assignments (shift one node's slot by half a slot width) and push updated config messages. The fleet manager computes stagger slots but has no collision detection, no re-stagger logic, and no collision rate metric. Needs: (1) per-link-pair collision counter in ingestion/signal processing path, (2) collision rate aggregation in fleet manager, (3) adaptive re-stagger trigger.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":3,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:25:13.899248435Z","updated_at":"2026-05-06T12:29:01.414753570Z","closed_at":"2026-05-06T12:29:01.414753570Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-1k3zg","title":"API: GET /api/doctor — pre-flight configuration diagnostic","description":"## Goal\nAdd a GET /api/doctor endpoint that diagnoses common misconfiguration before the user concludes the system is broken. Complements /healthz (runtime state) with pre-flight checks (configuration correctness).\n\n## Endpoint\n\nGET /api/doctor\n- Requires session cookie (same as all /api/* endpoints)\n- Returns 200 with a JSON report regardless of check results (HTTP status reflects reachability, not check results)\n\n## Checks to run\n\n| Check | Pass condition | Fail message |\n|---|---|---|\n| data_dir_writable | /data is writable and has >100 MB free | 'Data directory not writable' or 'Disk space low: Nf MB free' |\n| db_integrity | PRAGMA integrity_check returns 'ok' | 'SQLite integrity check failed' |\n| firmware_dir | /firmware contains at least one *.bin file | 'No firmware binaries found — OTA updates unavailable' |\n| mdns_binding | mDNS service is registered (or SPAXEL_MDNS_ENABLED=false) | 'mDNS not advertising — nodes cannot auto-discover mothership' |\n| mqtt_reachable | If SPAXEL_MQTT_BROKER is set: TCP connect to broker succeeds within 3s | 'MQTT broker unreachable: ' |\n| ntp_reachable | UDP ping to SPAXEL_NTP_SERVER:123 resolves within 3s | 'NTP server unreachable — node clock sync may fail' |\n| install_secret | install_secret row exists in auth table | 'Installation secret missing — re-run container to regenerate' |\n| pin_configured | pin_bcrypt is non-null in auth table | 'Dashboard PIN not configured — run first-time setup' |\n| node_token_consistency | All nodes in registry have non-null node_token | 'N nodes missing auth tokens — re-provision via Web Serial' |\n\n## Response format\n\n{\n 'checks': [\n {'name': 'db_integrity', 'status': 'ok', 'message': null},\n {'name': 'mqtt_reachable', 'status': 'warn', 'message': 'MQTT broker unreachable: mqtt://ha.local:1883'},\n {'name': 'firmware_dir', 'status': 'error', 'message': 'No firmware binaries found'}\n ],\n 'overall': 'warn', // 'ok' | 'warn' | 'error' (worst of all checks)\n 'checked_at': '2024-03-15T07:00:00Z'\n}\n\nStatus levels: 'ok' (pass), 'warn' (degraded but functional), 'error' (action required).\n\n## Dashboard integration\n- Command palette: 'doctor' → calls /api/doctor, shows results inline\n- Guided troubleshooting (Component 36): 'Node offline' flow links to 'Run diagnostics' which calls /api/doctor\n- /healthz already covers runtime health; /api/doctor covers configuration health — keep them separate\n\n## Acceptance\n- GET /api/doctor returns 200 with all checks when fully configured\n- Reports 'firmware_dir: error' when /firmware is empty\n- Reports 'mqtt_reachable: warn' when MQTT broker env is set but broker is unreachable\n- Unit tests cover each check in isolation with mocked dependencies","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-golf","created_at":"2026-05-02T12:22:51.188946318Z","updated_at":"2026-05-05T12:39:28.566216915Z","closed_at":"2026-05-05T12:39:28.566216915Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-1kid","title":"Add host test asserting default-placement sim produces non-zero fusion peaks","description":"Child of bf-18yn umbrella (final child). Depends on child 3. This is the bf-18yn acceptance-criteria test.\n\nScope:\n- Add a sim/host (or package) test that, using only the DEFAULT node placement (no manual positioning), constructs/seeds the 3D fusion engine and asserts the accumulation grid produces non-zero peaks, i.e. len(blobs) > 0 or grid max above threshold, closing the bf-4q5w symptom. Extend the bf-6s3d fleet->engine invariant test or add a sibling.\n\nAcceptance criteria:\n- Test passes with default placement, and fails (or is skipped with a clear reason) if nodes collapse to (0,0,1).\n- go build ./... passes; existing simulator and fusion tests still pass.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T22:09:38.566010752Z","updated_at":"2026-07-03T23:24:56.503387578Z","closed_at":"2026-07-03T23:24:56.503387578Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1kid","depends_on_id":"bf-xrej","type":"blocks","created_at":"2026-07-03T22:10:08.938736179Z","created_by":"cli","thread_id":""}],"comments":[{"id":58,"issue_id":"bf-1kid","author":"cli","text":"Implemented and pushed (commit 1df0bfa, on origin/main).\n\nAdded two sibling tests to TestEngine_SeedNodePositions (bf-6s3d) in mothership/internal/fusion/fusion_test.go:\n\n1. TestEngine_DefaultPlacementProducesPeaks — the bf-18yn acceptance test / bf-4q5w fix. Seeds the 3D fusion engine using ONLY the default placement (simulator.DefaultNodePositions, the spread geometry a freshly-onboarded virtual/sim fleet gets with no manual positioning — bf-3fr6/bf-xrej), drives a synthetic walker through the room centre, and asserts the accumulation grid produces non-zero peaks (len(blobs) > 0 OR gridMax above threshold) across default placements of 2/3/4/6 nodes. assertPlacementNotCollapsed fails loudly if placement ever collapses to (0,0,1).\n\n2. TestEngine_CoLocatedOriginYieldsNoPeaks — counter-example pinning the bf-4q5w symptom: nodes left at the (0,0,1) DB default are co-located, every link is degenerate, the grid stays at zero, and Fuse emits 0 blobs. Proves the non-zero-peak assertion is meaningful, not trivially satisfiable.\n\nAcceptance criteria met: passes with default placement; co-located (0,0,1) collapse yields no peaks (documented). go build ./... / go vet ./... / go test ./... all green across mothership, cmd/sim, test/acceptance.","created_at":"2026-07-03T23:19:39.087176089Z"},{"id":59,"issue_id":"bf-1kid","author":"cli","text":"Closed bf-18yn acceptance test (commit 1df0bfa, pushed to origin/main).\n\nTestEngine_DefaultPlacementProducesPeaks: seeds the 3D fusion engine using ONLY the default node placement — simulator.DefaultNodePositions, the spread geometry a freshly-onboarded virtual/sim fleet receives with no manual positioning (bf-3fr6/bf-xrej). Runs over 2/3/4/6-node default placements, drives a synthetic walker (buildSyntheticLinks) through the room centre, and asserts len(blobs) > 0 OR gridMax above threshold. assertPlacementNotCollapsed fails loudly if default placement ever collapses to the co-located (0,0,1) DB default (the bf-4q5w root cause).\n\nTestEngine_CoLocatedOriginYieldsNoPeaks: counter-example pinning bf-4q5w — nodes left collapsed at (0,0,1) are co-located, every link is degenerate, the grid stays at zero, and Fuse emits 0 blobs. Proves the non-zero-peak assertion is meaningful, not trivially satisfiable.\n\nSibling to bf-6s3d's TestEngine_SeedNodePositions (which locks the seeding invariant; this locks its downstream consequence).\n\nAcceptance verified: go build/vet/test ./... pass across all three go.work modules (mothership incl. internal/fusion + internal/simulator, cmd/sim, test/acceptance). Targeted run: DefaultPlacement subtests nodes=2/3/4/6 PASS, CoLocatedOriginYieldsNoPeaks PASS, SeedNodePositions PASS.\n\n(implementation was complete in a prior attempt; this attempt verified correctness and closed the bead with the working comment-then-close path — the dispatch footer's --body flag is invalid for this br build)","created_at":"2026-07-03T23:24:47.063692741Z"}]} {"id":"bf-1m2x","title":"Verify blob-creation inventory is current at HEAD","description":"## Context\nbf-4bhd's discovery objective (find all blob creation code paths) is ALREADY DELIVERED by its closed dependency subtree (bf-1q3m, bf-3ldj, bf-55rp, bf-5uzm, bf-4wwt, bf-1bmg) and consolidated in `notes/bf-1q3m-consolidated.md`, which explicitly supersedes `notes/bf-4bhd.md`. This child CLOSES the \"is the inventory actually complete and current?\" loop rather than re-doing discovery from scratch (the anti-pattern that has burned the prior 4 attempts).\n\n## Scope\n- Confirm the consolidated report's 23 production construction sites (5 primary + 16 projection + 2 JS) still match the CURRENT HEAD (report was verified at `1a26c12`).\n- Re-run the completeness greps from report section 1 against current HEAD:\n - `grep -rnE \"Blob\\{|TrackedBlob\\{\" mothership/ --include=*.go | grep -v _test.go` (expect 5 primary)\n - projection-type literals grep (expect 15 non-test projection literals)\n- Record current HEAD SHA + per-category counts; document any site added/removed since `1a26c12`.\n\n## Acceptance Criteria\n- [ ] Short verification note records current HEAD SHA + grep counts (expected: 5 primary / 15 non-test projection / 2 JS production; zero deltas vs `1a26c12`).\n- [ ] Any delta vs `1a26c12` is explicitly documented (expected none — no Go/JS code changed since 1a26c12).\n- [ ] Note cites `notes/bf-1q3m-consolidated.md` as the source of truth and does NOT re-search from scratch.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T20:13:42.925805896Z","updated_at":"2026-07-06T20:20:02.068987123Z","closed_at":"2026-07-06T20:20:02.068987123Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"comments":[{"id":83,"issue_id":"bf-1m2x","author":"cli","text":"Implemented: notes/bf-1q3m-consolidated.md inventory confirmed current at HEAD 0ae0c03. Re-ran the two completeness greps (Blob{|TrackedBlob{ -> 5 primary; BlobSnapshot{|BlobState{|... -> 15 non-test named projection) plus type-def grep (15 types) and JS sites (2) — zero deltas vs the report's verification commit 1a26c12 (no Go/JS source changed in the interval; only notes/ + .beads/). The 16-sites vs 15-literals distinction reconciled (P3 main.go:2288 is an anonymous-struct literal, absent from the named-type grep). Cited notes/bf-1q3m-consolidated.md as source of truth; did NOT re-search from scratch. Verification note at notes/bf-1m2x-verification.md (commit 64a2202, pushed). Notes-only; no source modified.","created_at":"2026-07-06T20:19:10.410924018Z"}]} {"id":"bf-1mwy","title":"Verify all blob-creation sites covered (coverage gate)","description":"## Objective\nProve no blob-creation site is missed: re-run the bf-1q3m inventory greps to confirm every production blob-creation type either carries the three canonical identity fields or is explicitly documented out-of-scope. Build + tests green. This is the coverage gate that closes bf-5151's \"No blob creation code is missed\" acceptance criterion.\n\n## Scope (grounded in notes/bf-1q3m-consolidated.md §1 + §6)\n- Re-run the inventory greps from bf-1q3m §1.1:\n - `grep -rnE \"Blob\\{|TrackedBlob\\{\" mothership/ --include=*.go | grep -v \"_test.go\"`\n - `grep -rnE \"BlobSnapshot\\{|BlobState\\{|BlobPos\\{|BlobUpdate\\{|BlobEvent\\{|BlobResult\\{|BlobExplanation\\{\" mothership/ --include=*.go | grep -v \"_test.go\"`\n- Build a coverage table (write to `notes/bf-5151-coverage.md`) mapping EACH production blob-creation site to one of: (a) has the 3 canonical fields [list the type], or (b) out-of-scope per bf-1q3m §6 with the reason (fusion peak C1 pre-identity; synthetic replay/sim P8-P10; dead api.BlobPos; empty fallbacks P4/P5; etc.).\n- Confirm the in-scope types now all carry PersonName/AssignedColor/IdentityResolved: signal.TrackedBlob, tracker.Blob, tracking.Blob, automation.TrackedBlob (1446ccf); api.Track, dashboard.blobJSON (248d0e0); explainability.BlobSnapshot + volume.BlobPos (children 1 & 2).\n- Run `gofmt -l mothership/`, `go vet ./mothership/...`, `go test ./mothership/...` — all must be clean.\n\n## Acceptance Criteria\n- [ ] notes/bf-5151-coverage.md exists mapping every production site → has-fields / out-of-scope-with-reason\n- [ ] Every in-scope blob-creation type confirmed to carry the 3 canonical fields at zero values\n- [ ] Out-of-scope sites explicitly enumerated (none silently dropped)\n- [ ] gofmt clean, go vet ./mothership/... clean, go test ./mothership/... passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T21:23:54.926143294Z","updated_at":"2026-07-06T22:27:55.636212012Z","closed_at":"2026-07-06T22:27:55.636212012Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-1mwy","depends_on_id":"bf-3wkz","type":"blocks","created_at":"2026-07-06T21:24:06.418876849Z","created_by":"cli","thread_id":""}],"comments":[{"id":97,"issue_id":"bf-1mwy","author":"cli","text":"Coverage gate complete and on origin (commits 7f697a3 + 1871a8a).\n\nRe-ran both bf-1q3m §1.1 inventory greps verbatim at the real code HEAD 908adf2 (confirmed ancestor of HEAD): Grep A = 5 primary sites, Grep B = 15 projection sites + the P3 anon-struct boundary = 21 production sites total, matching bf-1q3m §3.\n\nIndependently verified all 8 in-scope blob types carry the 3 canonical identity fields (PersonName/AssignedColor/IdentityResolved) at the cited defs: signal.TrackedBlob, tracker.Blob, tracking.Blob, automation.TrackedBlob, api.Track (+ api.TrackedBlob alias), dashboard.blobJSON, explainability.BlobSnapshot, volume.BlobPos — all left at Go zero values at construction (omitempty → undefined in JS).\n\nAll 15 out-of-scope sites enumerated by reason in §5 (pre-identity fusion peak C1; synthetic replay/sim P8-P10; dead api.BlobPos; empty fallbacks P4/P5; lifecycle events P11/P12; trigger-state volume.BlobState P13-P16; anon falldetect P3; distinct falldetect.BlobSnapshot P7). None silently dropped.\n\nGates green at HEAD 908adf2 + this bead: gofmt -l mothership/ clean; go vet ./mothership/... exit 0; go test ./mothership/... exit 0 (all packages ok).\n\nNote: the committed doc 7f697a3 originally cited a dangling HEAD ref 37d628e (pre-rebase, same stale-hash class the doc's §6 warns about); corrected to real ancestor 908adf2 in 1871a8a. Doc-only bead — no Go/JS source modified.","created_at":"2026-07-06T22:25:17.010933128Z"}]} +{"id":"bf-1o7qi","title":"Fix the dead token path: bridge the sim token into hello.Token","description":"Second link of the bf-4iewr split (depends on the root-cause finding). The token the sim provisions never reaches the validator — make the token path REAL so a sim node presenting a valid token is accepted even when the migration window is closed.\n\n## Problem (confirmed by the root-cause child)\n- Validator reads only `hello.Token` (`internal/ingestion/server.go:513`).\n- Sim sends its token only as the `X-Spaxel-Token` HTTP header (`cmd/sim/main.go:633`), never in the hello body, and the mothership never reads that header. So `hello.Token` is always \"\" and every node falls through to the migration-window branch.\n\n## Scope\nPick ONE bridging approach and implement it (justify the choice in the PR/comment):\n- (A) Server-side: in the `/ws/node` upgrade handler, read `X-Spaxel-Token` from the WS handshake request and inject it into the parsed hello (`nc.Hello.Token` / the value passed to the validator) before the validation check at server.go:506-530; OR\n- (B) Client-side: have the sim include a `\"token\"` field in the hello JSON body (`cmd/sim/main.go` hello map ~640, and `cmd/sim/scenario.go`).\n\nPrefer the option that keeps real ESP32 firmware behavior unchanged (firmware today sends token in hello — verify against `firmware/main/websocket.c`). Do not weaken the validator.\n\n## Acceptance Criteria\n- [ ] A node presenting a VALID provisioned token is accepted and marked paired (not Unpaired) even when `SPAXEL_MIGRATION_WINDOW_HOURS=0` (window closed) — proven by a test.\n- [ ] A node presenting an INVALID/empty token is still REJECTED when the window is closed (regression guard; do not break rejection).\n- [ ] Test (unit or e2e) exercises the bridge end-to-end through `ValidateToken` (`internal/provisioning/server.go:135`, HMAC-SHA256(installSecret, mac)).","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T09:49:00.897317263Z","updated_at":"2026-07-07T09:49:00.897317263Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1o7qi","depends_on_id":"bf-34lwt","type":"blocks","created_at":"2026-07-07T09:50:16.308082183Z","created_by":"cli","thread_id":""}]} {"id":"bf-1oj7","title":"Node placement UI: TransformControls for dragging live registered nodes in 3D dashboard","description":"Plan Phase 3 item 18 requires the ability to drag actual registered (live) nodes in the 3D Three.js dashboard using TransformControls to adjust their physical placement. The pre-deployment simulator supports this, but dragging live nodes in the operational dashboard is not implemented. Add TransformControls to the live node meshes in the dashboard with position persistence via the mothership API.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-26T22:14:42.792062952Z","updated_at":"2026-07-06T03:47:54.761005897Z","closed_at":"2026-07-06T03:47:54.761005897Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-1q3m","title":"Consolidate and verify all blob-creation findings into single report","description":"Scope: consolidate children 1-4 into a SINGLE verified source-of-truth report and produce the tiered fix-target list the next implementation bead consumes. This is the \"report ready for the next bead\" deliverable.\n\nInputs: notes from children 1-4 (primary types bf-55rp, conversions bf-5uzm, projections bf-4wwt, JS/TS bf-1bmg).\n\nDeliverable: a single consolidated report (notes/bf--consolidated.md) that:\n- Merges the full inventory (primary types + conversions + projections + JS/TS) into one catalogue\n- Re-verifies EVERY file:line against current HEAD (prior reports drifted before — this is the whole reason the parent kept failing)\n- Flags any stale entries found in the sub-bead notes\n- Produces the tiered fix-target list for the identity-leak fix (Tier 1: identity dropped at main.go:2213 automation + :2116 explainability; Tier 2-4 as appropriate)\n- Includes the blob data-flow diagram showing where identity enters and where it leaks\n\nAcceptance Criteria:\n- [ ] Single consolidated report exists at notes/bf--consolidated.md\n- [ ] Every file:line in the catalogue re-verified against current HEAD (run the greps to confirm)\n- [ ] Stale entries from sub-bead notes explicitly flagged\n- [ ] Tiered fix-target list for the identity leak produced\n- [ ] Report explicitly states it supersedes notes/bf-3ldj-findings.md and notes/bf-4bhd.md for the inventory\n- [ ] Report is ready for the implementation bead to consume without re-searching","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T15:04:01.063024261Z","updated_at":"2026-07-06T20:07:06.993145748Z","closed_at":"2026-07-06T20:07:06.993145748Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-1q3m","depends_on_id":"bf-1bmg","type":"blocks","created_at":"2026-07-06T15:04:21.508791539Z","created_by":"cli","thread_id":""}],"comments":[{"id":82,"issue_id":"bf-1q3m","author":"cli","text":"Deliverable complete: notes/bf-1q3m-consolidated.md is the single re-verified source-of-truth report (commit f144aad, pushed to origin/main). Re-verified every file:line against current HEAD by running the greps (did not trust child notes): all 15 blob types, all 5 primary construction sites, and all 4 named leak boundaries are exact -- E2 main.go:2303 (automation.TrackedBlob, no identity fields), E3 main.go:2206 (explainability.BlobSnapshot), volume main.go:2326 (BlobPos PersonID field exists, left empty), E1 main.go:5494. Reference pattern is analytics.TrackUpdate at main.go:2271 which pulls PersonID from identityMatcher.GetMatch at :2267. Completeness sweeps confirm exactly 5 primary + 15 projection non-test literals, so no production site is missed. Stale entries flagged in section 4: bf-3ldj and bf-4bhd are SUPERSEDED (their E2 :2213 / E3 :2116 / E1 :5384 drifted to :2303 / :2206 / :5494; 2D tracking.Blob field list also drifted). Report explicitly supersedes both for the inventory. Tiered fix-target list in section 6: Tier 1 = add identity fields and populate from the ble.IdentityMatcher sidecar at main.go:2303, :2206 (fold in identityMap :2216), and :2326; Tiers 2-4 documented. Report is ready for the implementation bead to consume without re-searching. NOTE: the prior failure-count:1 was the dispatch footer's invalid 'br close --body' flag (no such flag exists) -- closing via br comments add + br close as the tool requires.","created_at":"2026-07-06T20:05:09.702563495Z"}]} {"id":"bf-1rzd","title":"Search TypeScript files for blob-shaped object literals","description":"## Objective\nSearch all TypeScript files for blob-shaped object literals.\n\n## Scope\n- Search only .ts and .tsx files\n- Use the pattern defined in the parent bead\n- Identify all locations with blob structure\n\n## Acceptance Criteria\n- [ ] All TypeScript files searched\n- [ ] Blob literal locations identified with file paths and line numbers\n- [ ] Results saved to notes/bf-26ta-typescript-results.md\n- [ ] Each match includes context snippet showing the blob structure","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T06:01:46.201406389Z","updated_at":"2026-07-06T06:08:54.854421997Z","closed_at":"2026-07-06T06:08:54.854421997Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1rzd","depends_on_id":"bf-3aij","type":"blocks","created_at":"2026-07-06T06:01:58.385955215Z","created_by":"cli","thread_id":""}]} @@ -21,16 +25,18 @@ {"id":"bf-2186","title":"Add TypeScript type definitions for blob identity fields","description":"## Objective\nAdd TypeScript interface/type definitions for the new identity-related fields.\n\n## Scope\n- Extend the blob TypeScript interface with new fields:\n - personName: string (optional)\n - assignedColor: string (hex/rgb format, optional)\n - identityResolved: boolean (optional)\n- Use proper TypeScript optional types to ensure backward compatibility\n\n## Acceptance Criteria\n- [ ] Blob TypeScript interface includes personName, assignedColor, identityResolved fields\n- [ ] All new fields are typed as optional (string | undefined, boolean | undefined)\n- [ ] Interface compiles without TypeScript errors\n- [ ] JSDoc comments added explaining the new fields\n\n## Notes\nType definitions only. No runtime code changes yet.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T04:41:22.516835794Z","updated_at":"2026-07-06T05:05:06.412791638Z","closed_at":"2026-07-06T05:05:06.412791638Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2186","depends_on_id":"bf-13s0","type":"blocks","created_at":"2026-07-06T04:41:35.808332209Z","created_by":"cli","thread_id":""}]} {"id":"bf-21nd","title":"Install and configure axe-core with Playwright","description":"Install @axe-core/playwright package and configure it in the Playwright test suite. Set up the basic axe-core integration pattern that can be reused across tests.\n\n**Acceptance Criteria:**\n- @axe-core/playwright is installed as a dev dependency\n- A shared accessibility test helper is created (e.g., tests/accessibility/helper.ts)\n- The helper exports a function to run axe-core scans with WCAG 2.1 AA tags\n- A minimal test file demonstrates the integration (smoke test)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T03:35:44.192535701Z","updated_at":"2026-07-06T04:26:00.734281351Z","closed_at":"2026-07-06T04:26:00.734281351Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-232u3","title":"GET /api/notifications/preview — rendered test thumbnail endpoint","description":"Plan (Component 30, Renderer spec) specifies a test endpoint: GET /api/notifications/preview?type=fall&person=Alice returns a rendered test image for UI development and QA. The render package (internal/render/floorplan.go) implements thumbnail generation with fogleman/gg, but the preview HTTP endpoint is never registered in main.go. Needs: handler that accepts ?type and ?person query params, calls the appropriate Generate*Thumbnail function, returns the PNG bytes with Content-Type: image/png.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:25:20.897907993Z","updated_at":"2026-05-05T17:25:10.053618969Z","closed_at":"2026-05-05T17:25:10.053618969Z","close_reason":"GET /api/notifications/preview endpoint already implemented and registered in commit 37571ec. The endpoint accepts ?type (fall, anomaly, zone_enter, sleep) and ?person query params, calls the appropriate Generate*Thumbnail function from internal/render/floorplan.go, and returns PNG bytes with Content-Type: image/png. All tests pass.","source_repo":".","compaction_level":0} -{"id":"bf-2330","title":"Add hard-fail blob and detection-event assertion primitives to the e2e harness","description":"Split from bf-5jeo (verification capstone). Foundation child for the e2e assertion work; the other children reuse these primitives.\n\nCONTEXT: mothership/tests/e2e/e2e_test.go captures blob/detection data but never enforces it. AssertDuringRun (line 375) logs \"No detection events during run (fusion pipeline may not have produced blobs)\" and returns nil instead of failing. WatchDashboardWS (line 326) returns blobCounts []int (line 334) that no caller asserts on. The dodge is structural.\n\nSCOPE:\n- Make AssertDuringRun return a non-nil, descriptive error when ZERO blobs AND ZERO detection events are observed across the whole run window (replace the soft log with a hard failure reporting elapsed time + counts observed). Keep the existing health==ok and nodes-online==expected assertions intact.\n- Add a reusable helper to assert max(blobCounts) >= 1 from a WatchDashboardWS result.\n- Add a reusable helper to assert len(events.Events) >= 1 from a GetEvents(ctx, detection, N) result.\n- Both helpers must be usable by TestDetectionEvents / TestFullE2EIntegration / the IO-6 hard-gate test (children 2-4).\n\nACCEPTANCE CRITERIA:\n- New/changed helpers compile (go build ./tests/e2e/... and go vet).\n- AssertDuringRun returns a descriptive error when no blob is produced (no longer silently nil).\n- The health and nodes-online checks are unchanged and still pass.\n- No weakening of any existing assertion.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T18:50:41.863718681Z","updated_at":"2026-07-06T18:50:41.863718681Z","source_repo":".","compaction_level":0,"labels":["split-child"]} +{"id":"bf-2330","title":"Add hard-fail blob and detection-event assertion primitives to the e2e harness","description":"Split from bf-5jeo (verification capstone). Foundation child for the e2e assertion work; the other children reuse these primitives.\n\nCONTEXT: mothership/tests/e2e/e2e_test.go captures blob/detection data but never enforces it. AssertDuringRun (line 375) logs \"No detection events during run (fusion pipeline may not have produced blobs)\" and returns nil instead of failing. WatchDashboardWS (line 326) returns blobCounts []int (line 334) that no caller asserts on. The dodge is structural.\n\nSCOPE:\n- Make AssertDuringRun return a non-nil, descriptive error when ZERO blobs AND ZERO detection events are observed across the whole run window (replace the soft log with a hard failure reporting elapsed time + counts observed). Keep the existing health==ok and nodes-online==expected assertions intact.\n- Add a reusable helper to assert max(blobCounts) >= 1 from a WatchDashboardWS result.\n- Add a reusable helper to assert len(events.Events) >= 1 from a GetEvents(ctx, detection, N) result.\n- Both helpers must be usable by TestDetectionEvents / TestFullE2EIntegration / the IO-6 hard-gate test (children 2-4).\n\nACCEPTANCE CRITERIA:\n- New/changed helpers compile (go build ./tests/e2e/... and go vet).\n- AssertDuringRun returns a descriptive error when no blob is produced (no longer silently nil).\n- The health and nodes-online checks are unchanged and still pass.\n- No weakening of any existing assertion.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T18:50:41.863718681Z","updated_at":"2026-07-07T00:27:13.887369372Z","closed_at":"2026-07-07T00:27:13.887369372Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"comments":[{"id":103,"issue_id":"bf-2330","author":"cli","text":"Implemented hard-fail blob/detection-event assertion primitives in the e2e harness (foundation child of bf-5jeo).\n\n- AssertDuringRun now returns a descriptive error (elapsed time + counts observed) when ZERO blobs AND ZERO detection events are produced across the whole run window, replacing the soft t.Logf that silently returned nil. The health==ok and nodes-online==expected assertions are unchanged.\n- Added reusable AssertBlobObserved(blobCounts []int) -> asserts max(blobCounts)>=1 from a WatchDashboardWS result.\n- Added reusable AssertDetectionEventsObserved(*EventsResponse) -> asserts len(events.Events)>=1 from a GetEvents(ctx,detection,N) result.\n- Added GetBlobCount poller used by AssertDuringRun to track peak concurrent tracked blobs.\n- Table-driven unit tests (assertions_test.go) cover pass/fail/empty/nil cases + the non-nil failure contract.\n\nVerified: go build ./tests/e2e/..., go vet ./tests/e2e/..., full mothership 'go test -short ./...' green, helper unit tests pass. Both helpers are exported and documented for TestDetectionEvents / TestFullE2EIntegration / the IO-6 hard-gate test (children 2-4) to reuse. Committed as 06460fb on main (push initially 502'd from a transient Gitea origin outage; succeeded on retry).","created_at":"2026-07-07T00:26:14.029888090Z"}]} {"id":"bf-24xp","title":"Send node position in spaxel-sim hello and persist it on registration","description":"Child of bf-18yn umbrella. Depends on the geometry helper (child 1).\n\nProblem: cmd/sim/main.go and mothership/cmd/sim/main.go compute good corner geometry in generateNodePositions/createVirtualNodes, but the hello message sent on WebSocket connect (connectNodes) does NOT include position. So the mothership never learns the sim node location and the fleet registry/DB row is left at the schema default (0,0,1), co-located with every other node.\n\nScope:\n- Add the node computed position (X,Y,Z) to the hello JSON message in both cmd/sim/main.go and mothership/cmd/sim/main.go, sourced from createVirtualNodes (or child 1 helper).\n- In the mothership node-registration/hello handler, accept the position and store it via the registry so the node DB row is NOT left at (0,0,1). Ensure it flows to the fusion engine via the existing connect/register sink wired in bf-3p6g (manager.go OnNodeConnected -> nodePositionSink -> fusionEngine.SetNodePosition).\n\nAcceptance criteria:\n- A spaxel-sim node connecting with a position results in a fleet-registry/DB node row whose pos differs from (0,0,1) and matches the sent coordinates.\n- go build ./... passes; registration-path tests updated or added.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T22:09:38.428986890Z","updated_at":"2026-07-03T22:47:49.062186306Z","closed_at":"2026-07-03T22:47:49.062186306Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-24xp","depends_on_id":"bf-3fr6","type":"blocks","created_at":"2026-07-03T22:10:08.812056730Z","created_by":"cli","thread_id":""}],"comments":[{"id":56,"issue_id":"bf-24xp","author":"cli","text":"Implemented and verified: both sim CLIs (cmd/sim and mothership/cmd/sim) announce pos_x/pos_y/pos_z in the hello handshake, sourced from createVirtualNodes corner geometry. HelloMessage gained PosX/PosY/PosZ as *float64 (nil = not announced, so a real ESP32 preserves any user-placed position). The connect/register path (server.HandleNodeWS -> FleetNotifier.OnNodeConnected -> Manager) carries the announced position through; the Manager persists it via registry.SetNodePosition only when all three axes are present, then forwards it to the fusion engine through the bf-3p6g sink (GetNodePosition -> ForwardNodePosition -> fusionEngine.SetNodePosition). Acceptance criteria met: TestManagerOnNodeConnectedPersistsHelloPosition asserts a node connecting with an announced position yields a registry/DB row whose pos differs from (0,0,1) and matches the sent coords, and that the coords reach the fusion engine. go build ./..., go vet ./..., and go test ./... (50 packages) all pass. Commit d048987 pushed to origin/main.","created_at":"2026-07-03T22:47:08.588926475Z"}]} {"id":"bf-25dmx","title":"Guided troubleshooting (Component 36)","description":"## Goal\nWhen system detects that user might be struggling or detection quality has degraded, proactively offer contextual help — but never when things are working well.\n\n## Trigger conditions and responses:\n\nDetection quality drops:\n- Condition: Zone-level detection quality below 60% for >24 hours\n- Banner in timeline and 3D view: 'Detection in the kitchen has been less reliable this week. Want me to help diagnose?'\n- Guided flow: Check node connectivity → show link health with explainability → suggest node repositioning → offer re-baseline → 'Still not right? Try adding a node here [highlighted position]'\n\nRepeated setting changes:\n- Condition: Same settings key modified 3+ times within 60-minute sliding window (qualifying keys: delta_rms_threshold, breathing_sensitivity, tau_s, fresnel_decay, n_subcarriers)\n- Tracking: per-key edit counter in memory, resets after 60 min inactivity\n- Trigger: when counter reaches 3, set hint_pending flag\n- Frontend: show non-intrusive banner: 'You've adjusted the detection threshold several times. Would you like me to show you what the system is seeing?' with [Show me] and [×] dismiss\n- [Show me]: opens time-travel to most recent detection event before first edit, with explainability overlay pre-activated\n- Cooldown: 24 hours after hint is shown\n\nNode offline:\n- Condition: Any node offline for >2 hours\n- Timeline event with expandable troubleshooting steps\n\nFirst-time feature discovery:\n- Condition: User opens feature panel for first time\n- Brief, non-intrusive tooltip (not modal): 'Draw a box around an area, then choose what happens when someone enters or leaves. [Got it]'\n- Shown once, never repeated\n\nAfter false positive feedback:\n- Inline response in timeline: 'Got it. I've slightly raised the detection threshold for the contributing links. If this keeps happening at this time of day, my hourly baseline will adapt within a few days.'\n\nAfter successful calibration:\n- Positive reinforcement: 'Re-baseline complete. Detection quality in the kitchen improved from 64% to 89%.'\n\n## Design principles\n- Reactive, not proactive: help appears only when something seems wrong or when user is clearly exploring\n- Dismissible in one tap: never blocks UI\n- Never repeats after dismissal (stored in localStorage)\n- Always explains what will happen next\n- Never condescending: assumes user is intelligent but may not know CSI physics\n\n## Acceptance\n- Detection quality drop triggers helpful banner\n- Repeated setting changes trigger hint\n- Node offline shows troubleshooting steps\n- First-time feature discovery shows tooltip once\n- Feedback responses are helpful\n- Calibration success shows positive reinforcement","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:06:29.724435180Z","updated_at":"2026-05-06T10:19:23.187979577Z","closed_at":"2026-05-06T10:19:23.187979577Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-26ta","title":"Search for blob-shaped object literals","description":"## Objective\nFind all locations where blob-shaped objects are created via object literals.\n\n## Scope\n- Search for object literals with blob structure (id, type, data fields)\n- Identify TypeScript and JavaScript files\n- Document each literal location\n\n## Acceptance Criteria\n- [ ] All blob-shaped object literals are identified\n- [ ] Each location documented with file path and line number\n- [ ] Blob structure pattern noted for each site","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:31:37.599364650Z","updated_at":"2026-07-06T13:56:30.484444172Z","closed_at":"2026-07-06T13:56:30.484444172Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-26ta","depends_on_id":"bf-5kns","type":"blocks","created_at":"2026-07-06T05:31:56.467390976Z","created_by":"cli","thread_id":""},{"issue_id":"bf-26ta","depends_on_id":"bf-3tlw","type":"blocks","created_at":"2026-07-06T06:02:20.474309914Z","created_by":"cli","thread_id":""}],"comments":[{"id":61,"issue_id":"bf-26ta","author":"cli","text":"Closing as COMPLETE — auto-dispatch re-fired the split on an already-split, already-complete bead. Findings: All 5 children closed (bf-3aij pattern, bf-1rzd TS search, bf-4ly4 JS search, bf-5kns Blob constructor calls, bf-3tlw compiled findings). Deliverables committed: notes/bf-26ta-findings.md (Go 10 types/20+ sites, JS 14 instances/5 files, TS type defs only), bf-26ta-javascript-results.md, bf-26ta-typescript-results.md. All three parent acceptance criteria met (literals identified, file:line documented, structure pattern noted). The failure-count:3 + repeated claims were a re-dispatch loop on an unclosed umbrella; closing breaks the loop and unblocks bf-67ao (blob factory search). NOT re-splitting — duplicate children would misrepresent a done task.","created_at":"2026-07-06T13:55:20.535048726Z"}]} {"id":"bf-28dv","title":"Component 24: Fleet-level sentinel link coordination (adaptive sensing idle mode)","description":"The ratecontrol.go implements per-node adaptive rate (active: 20-50 Hz, idle: 2 Hz) but is missing the fleet-level coordination described in plan.md Component 24.\n\nThe plan specifies:\n- When ALL zones are idle, designate one sentinel link per zone at 5 Hz; all other links drop to 1 Hz\n- When activity detected in one zone, ramp that zone to full rate AND adjacent zones to 5 Hz (adjacency exists but only via OnMotionHint, not zone-level)\n- Prediction engine can preemptively ramp zones before predicted arrivals\n\nWhat exists in ratecontrol.go:\n- Per-node state machine: Active (20 Hz) / Idle (2 Hz)\n- OnMotionHint: preemptively ramps adjacent nodes to full rate\n- OnMotionState: transitions node between active/idle\n- SetAdjacentNodesFn: adjacency function for adjacent-node ramping\n\nWhat is missing:\n1. Zone-level idle detection: when all nodes in a zone have been idle, designate one sentinel link per zone at 5 Hz and drop all others to 1 Hz\n2. Fleet-wide idle state: when all zones are idle, only sentinel links at 5 Hz run\n3. Prediction-engine-driven preemptive zone ramp: RateController needs a RampZone(zoneID) method that the prediction engine can call when P(arrival in zone) > threshold\n4. The 1 Hz floor rate (current code uses 2 Hz idle; plan says 1 Hz for all-idle fleet state)\n\nScope: Add zone-aware idle coordination to RateController. Add RampZone(zoneID) interface for prediction engine wiring. Adjust idle rate to 1 Hz for fully-idle fleet.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-26T04:04:04.870466269Z","updated_at":"2026-07-06T03:19:06.448671235Z","closed_at":"2026-07-06T03:19:06.448671235Z","close_reason":"Completed","source_repo":".","compaction_level":0} +{"id":"bf-29wyl","title":"Confirm sim sends token as header only and mothership ignores it","description":"Second link of the bf-34lwt split. Confirm the sim-side token-sending path and the mothership's failure to read it. This proves the 'supply valid tokens' option is currently DEAD — the core of the root cause.\n\n## Scope (cite file:line)\n3. The sim sends its provisioned token ONLY as the `X-Spaxel-Token` HTTP header (`cmd/sim/main.go`, also `cmd/sim/scenario.go`) and does NOT include a `token` field in the hello JSON body (hello map at cmd/sim/main.go).\n4. The mothership NEVER reads that header — `grep -rn 'X-Spaxel-Token\\|Header.Get' cmd/mothership/ internal/` returns no WS-path hit. Consequence: `hello.Token` is always \"\" for sim nodes → `tokenOK` always false.\n\n## Acceptance Criteria\n- [ ] Quoted file:line(s) showing the sim sets only the `X-Spaxel-Token` header and that the hello JSON map has no `token` field.\n- [ ] Grep output (or equivalent) demonstrating no WS-path read of `X-Spaxel-Token` in `cmd/mothership/` or `internal/`.\n- [ ] Explicit statement: the token-supply path is currently dead (header ignored → hello.Token empty → tokenOK always false).","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-07T10:23:54.721931529Z","updated_at":"2026-07-07T10:57:13.219518369Z","closed_at":"2026-07-07T10:57:13.219518369Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-29wyl","depends_on_id":"bf-5ig3e","type":"blocks","created_at":"2026-07-07T10:24:42.445767431Z","created_by":"cli","thread_id":""}],"comments":[{"id":113,"issue_id":"bf-29wyl","author":"cli","text":"Confirmed (docs/notes/token-supply-path-dead-confirm.md, commit 4dcc06a): the token-supply path is currently DEAD. (1) Sim supplies its token ONLY as the X-Spaxel-Token HTTP header on the WS dial — cmd/sim/main.go:313, mothership/cmd/sim/main.go:633, mothership/cmd/sim/scenario.go:318. (2) The hello JSON map has NO token field — cmd/sim/main.go:348-359, mothership/cmd/sim/main.go:652-665, mothership/cmd/sim/scenario.go:331-342. (3) The mothership never reads that header on the WS path — grep 'X-Spaxel-Token' mothership/ (excluding _test.go and cmd/sim/) returns zero hits; HandleNodeWS (server.go:455-469) passes r straight to Upgrade. Consequence: header ignored -> hello.Token (message.go:22) empty -> tokenOK (server.go:513) always false. Second link of the bf-34lwt split; pairs with bf-5ig3e.","created_at":"2026-07-07T10:56:26.784756657Z"}]} {"id":"bf-2adi","title":"Add accessibility tests for dashboard pages","description":"Create axe-core accessibility tests for all dashboard pages (ambient, live, simple, simulator). Each test should navigate to the page and scan for WCAG 2.1 AA violations.\n\n**Acceptance Criteria:**\n- Test file dashboard/tests/a11y-dashboard.spec.js exists\n- Tests cover all 4 dashboard pages: ambient, live, simple, simulator\n- Each test navigates to the page and runs an axe-core scan\n- Tests fail if any WCAG 2.1 AA violations are found\n- Tests can be run locally with 'npx playwright test a11y-dashboard'","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T03:35:55.473957414Z","updated_at":"2026-07-06T04:27:34.648883970Z","closed_at":"2026-07-06T04:27:34.648883970Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2adi","depends_on_id":"bf-21nd","type":"blocks","created_at":"2026-07-06T03:36:48.664082960Z","created_by":"cli","thread_id":""}]} -{"id":"bf-2aqf","title":"Strengthen TestFullE2EIntegration to assert at least one blob is produced","description":"Split from bf-5jeo (verification capstone). Depends on the TestDetectionEvents child. Child 3 of 4.\n\nCONTEXT: mothership/tests/e2e/e2e_test.go:812 TestFullE2EIntegration captures blobCounts via WatchDashboardWS (line 803) and events via GetEvents (~line 843), but asserts neither count. The comment (~line 843-844) says \"we verify the API responds correctly rather than asserting a minimum count\" — the central dodge called out in bf-5jeo.\n\nSCOPE:\n- Remove the dodge. Assert max(blobCounts) >= 1 from the WatchDashboardWS result (helper from bf-2330).\n- Also assert len(events.Events) >= 1 from GetEvents(ctx, detection, 100) using the bf-2330 helper.\n- Delete/replace the \"verify the API responds correctly rather than asserting a minimum count\" comment.\n- Keep the 4-node / 2-walker / 30s RunSimulator config and AssertDuringRun call (AssertDuringRun itself is hardened by bf-2330).\n\nACCEPTANCE CRITERIA:\n- TestFullE2EIntegration asserts >= 1 blob via BOTH the dashboard WS blobCounts and the events API.\n- The minimum-count dodge comment is gone.\n- Test compiles (go build ./tests/e2e/...).\n- NOTE: expected RED until upstream bf-4q5w lands. Do NOT weaken the assertion to force green; if it cannot pass, block and feed a finding upstream per child 4.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T18:51:03.203633411Z","updated_at":"2026-07-06T18:51:03.203633411Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2aqf","depends_on_id":"bf-16c1","type":"blocks","created_at":"2026-07-06T18:51:13.341776592Z","created_by":"cli","thread_id":""}]} +{"id":"bf-2aqf","title":"Strengthen TestFullE2EIntegration to assert at least one blob is produced","description":"Split from bf-5jeo (verification capstone). Depends on the TestDetectionEvents child. Child 3 of 4.\n\nCONTEXT: mothership/tests/e2e/e2e_test.go:812 TestFullE2EIntegration captures blobCounts via WatchDashboardWS (line 803) and events via GetEvents (~line 843), but asserts neither count. The comment (~line 843-844) says \"we verify the API responds correctly rather than asserting a minimum count\" — the central dodge called out in bf-5jeo.\n\nSCOPE:\n- Remove the dodge. Assert max(blobCounts) >= 1 from the WatchDashboardWS result (helper from bf-2330).\n- Also assert len(events.Events) >= 1 from GetEvents(ctx, detection, 100) using the bf-2330 helper.\n- Delete/replace the \"verify the API responds correctly rather than asserting a minimum count\" comment.\n- Keep the 4-node / 2-walker / 30s RunSimulator config and AssertDuringRun call (AssertDuringRun itself is hardened by bf-2330).\n\nACCEPTANCE CRITERIA:\n- TestFullE2EIntegration asserts >= 1 blob via BOTH the dashboard WS blobCounts and the events API.\n- The minimum-count dodge comment is gone.\n- Test compiles (go build ./tests/e2e/...).\n- NOTE: expected RED until upstream bf-4q5w lands. Do NOT weaken the assertion to force green; if it cannot pass, block and feed a finding upstream per child 4.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T18:51:03.203633411Z","updated_at":"2026-07-07T00:47:33.345600387Z","closed_at":"2026-07-07T00:47:33.345600387Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2aqf","depends_on_id":"bf-16c1","type":"blocks","created_at":"2026-07-06T18:51:13.341776592Z","created_by":"cli","thread_id":""}],"comments":[{"id":105,"issue_id":"bf-2aqf","author":"cli","text":"Implemented and verified in commit 3b00ccf (pushed to origin/main).\n\nTestFullE2EIntegration now hard-asserts at least one blob via BOTH channels, removing the minimum-count dodge:\n- Hard assertion #1: AssertBlobObserved(blobCounts) on the WatchDashboardWS dashboard-WS feed, captured concurrently during the run via a goroutine alongside AssertDuringRun (e2e_test.go:975).\n- Hard assertion #2: AssertDetectionEventsObserved(events) on GetEvents(ctx, \"detection\", 100) from the /api/events stream (e2e_test.go:988).\n\nThe 'verify the API responds correctly rather than asserting a minimum count' dodge comment is deleted; replaced with the bf-5jeo capstone rationale explaining the assertions are deliberately strict.\n\n4-node / 2-walker / 30s RunSimulator config and the AssertDuringRun(ctx, simDuration, 4) call are kept unchanged (AssertDuringRun itself hardened by bf-2330).\n\nVerification:\n- go build ./tests/e2e/... -> exit 0\n- go vet ./tests/e2e/... -> exit 0\n- test binary compiles (go test -run no-match -> ok, no tests to run)\n\nNOTE per acceptance criteria: assertions are deliberately strict and expected RED until upstream bf-4q5w (fusion SetNodePosition wiring) lands. Not weakened to force green.","created_at":"2026-07-07T00:46:25.511243008Z"}]} {"id":"bf-2enwo","title":"Simulator: ray-based propagation engine (internal/sim/propagation.go — direct path + first-order reflections)","description":"","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-06T00:40:16.873911203Z","updated_at":"2026-05-06T01:55:05.181196917Z","closed_at":"2026-05-06T01:55:05.181196917Z","close_reason":"Completed","source_system":"","source_repo":".","deleted_by":"","delete_reason":"","original_type":"","compaction_level":0,"compacted_at_commit":"","sender":""} {"id":"bf-2fz8","title":"Investigate which fusion engine feeds the live blob loop and IO-6 gate","description":"Prerequisite investigation for the bf-3gw1 umbrella. The live 10Hz loop in mothership/cmd/mothership/main.go:1867 calls pm.GetTrackedBlobs(), but grep confirms mothership/internal/fusion.NewEngine (3D Fresnel engine, fusion.go:93) is NEVER constructed in non-test code. Determine definitively which engine produces the blobs consumed by the live loop and the IO-6 hard-gate.\n\nTasks:\n- Trace pm (processing manager) and GetTrackedBlobs() to the engine that actually emits tracked blobs in the live path.\n- Confirm whether internal/fusion.Engine is the intended blob source, or whether blobs come from a different engine (e.g. the localization tracker).\n- Find/confirm any existing non-test construction site of internal/fusion.NewEngine (expected: none).\n- Record the finding as a bead comment: engine package, construction site (or 'none - must be added'), and the exact line where the live loop reads blobs.\n\nAcceptance criteria:\n- A documented determination (bead comment) naming the blob-producing engine with file:line references.\n- States explicitly whether internal/fusion.NewEngine must be newly constructed in non-test code.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T19:53:40.123789285Z","updated_at":"2026-07-03T20:15:51.969894260Z","closed_at":"2026-07-03T20:15:51.969894260Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"comments":[{"id":44,"issue_id":"bf-2fz8","author":"cli","text":"INVESTIGATION COMPLETE (bf-2fz8). Determination of which fusion engine feeds the live blob loop + IO-6 gate:\n\nBLOB SOURCE: NONE. No engine currently produces tracked blobs for the live path.\n- pm.trackedBlobs (internal/signal/processor.go:227) is always nil: SetTrackedBlobs (processor.go:625) has ZERO callers.\n- GetTrackedBlobs() is read by: the live 10Hz loop (cmd/mothership/main.go:1866), /api/blobs (main.go:4056), anomalyPositionAdapter (main.go:4840), and API wrappers (internal/api/status.go:84, internal/api/tracks.go:99) — ALL see zero blobs. IO-6 ('walker produces a tracked blob') cannot pass.\n\nENGINE INVENTORY:\n- internal/fusion.NewEngine (3D Fresnel, internal/fusion/fusion.go:93) = the INTENDED blob producer, but NEVER constructed in non-test code. The package is imported by exactly one file, internal/localizer/fusion/timing_budget_test.go (a test); the only NewEngine call sites are that test and internal/fusion/fusion_test.go.\n- internal/localization.NewEngine (2D Fresnel, internal/localization/fusion.go:53) = IS live, via NewSelfImprovingLocalizer (main.go:1005), runs Fuse (main.go:2593), but the returned *FusionResult is DISCARDED — nothing reads .Peaks, and no signal.TrackedBlob literal exists outside test code.\n\nCONCLUSION: internal/fusion.NewEngine MUST be newly constructed in non-test code and wired (Fuse -> TrackedBlob -> SetTrackedBlobs), OR the existing localization.Engine FusionResult.Peaks must be converted to TrackedBlobs and stored via SetTrackedBlobs. Also recorded in code as a doc comment on SetTrackedBlobs (commit a7ec9d2).","created_at":"2026-07-03T20:10:24.738723116Z"},{"id":45,"issue_id":"bf-2fz8","author":"cli","text":"INVESTIGATION COMPLETE (committed a7ec9d2, on origin/main; go vet + go test green).\n\nFINDING: NO engine currently feeds the live blob loop. pm.trackedBlobs is always nil.\n\n- Blob source determination: SetTrackedBlobs (internal/signal/processor.go:631) is the ONLY writer of pm.trackedBlobs and has ZERO callers -> GetTrackedBlobs() always returns nil -> IO-6 cannot pass.\n- Exact live-loop read line: cmd/mothership/main.go:1866 (blobs := pm.GetTrackedBlobs()). Other readers: /api/blobs main.go:4056, anomalyPositionAdapter main.go:4840, internal/api/status.go:84, internal/api/tracks.go:99.\n- internal/fusion.NewEngine (3D Fresnel, internal/fusion/fusion.go:93): construction site = NONE in non-test code. The internal/fusion package is imported by ZERO non-test files; only NewEngine call sites are internal/localizer/fusion/timing_budget_test.go and internal/fusion/fusion_test.go.\n- internal/localization.NewEngine (2D Fresnel, internal/localization/fusion.go:53) IS constructed live (NewSelfImprovingLocalizer main.go:1005, Fuse main.go:2593) but the returned *FusionResult is DISCARDED -> nothing reads .Peaks; only GetLearnedWeights() consumed (main.go:2599). No signal.TrackedBlob literal outside tests (automation.TrackedBlob at main.go:2111 is a distinct type).\n\nCONCLUSION: internal/fusion.NewEngine MUST be newly constructed in non-test code and wired Fuse->TrackedBlob->SetTrackedBlobs, OR localization.Engine FusionResult.Peaks converted to TrackedBlobs via SetTrackedBlobs. Findings also in doc comments (internal/signal/processor.go:601-630).","created_at":"2026-07-03T20:15:38.634306719Z"}]} {"id":"bf-2gmx","title":"Verify backward compatibility for blob identity fields","description":"## Objective\nEnsure existing blobs without identity info work without errors.\n\n## Scope\n- Test the dashboard with existing blob data that lacks identity fields\n- Verify no console errors from undefined personName, assignedColor, identityResolved\n- Check that rendering code handles missing identity data gracefully\n- Confirm backward compatibility is maintained\n\n## Acceptance Criteria\n- [ ] Dashboard loads with existing blob data without errors\n- [ ] No console errors related to undefined identity fields\n- [ ] Rendering code checks for undefined values before using identity fields\n- [ ] Both identity-resolved and non-identity-resolved blobs display correctly\n\n## Notes\nVerification task. Run the app and check console for errors.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T04:41:53.534620139Z","updated_at":"2026-07-06T04:41:53.534620139Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2gmx","depends_on_id":"bf-1wvm","type":"blocks","created_at":"2026-07-06T04:41:56.600702603Z","created_by":"cli","thread_id":""}]} +{"id":"bf-2hdbg","title":"Confirm migration window is the only thing accepting sim nodes","description":"Third link of the bf-34lwt split. Confirm that the migration window — not tokens — is what accepts tokenless sim nodes, and pin down exactly when REJECT fires. This proves current 'no reject' is a 24h-window MASK, not a real fix.\n\n## Scope (cite file:line)\n5. The only thing accepting sim nodes is the migration window: `internal/ingestion/server.go`. MigrationWindowHours DEFAULTS TO 24 (`internal/config/config.go`), so a fresh boot opens a 24h window → sim nodes accepted as Unpaired, no reject (matches PROGRESS.md bf-3hji).\n6. Therefore REJECT fires ONLY when the window is closed: `SPAXEL_MIGRATION_WINDOW_HOURS=0` (deadline never set → IsZero() → reject branch), OR uptime > 24h. In those cases every sim node logs 'missing token' → `sendReject(conn, 'invalid_token')` → disconnect.\n\n## Acceptance Criteria\n- [ ] Quoted file:line(s) showing MigrationWindowHours default = 24 and the migration-deadline acceptance branch.\n- [ ] Quoted file:line of the reject branch (window closed / IsZero()) and the `sendReject(...,'invalid_token')` call.\n- [ ] Explicit statement: current 'no reject' is a 24h-window MASK, not a real fix — REJECT returns when window=0 or uptime>24h.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-07T10:24:05.653346894Z","updated_at":"2026-07-07T11:26:10.998367210Z","closed_at":"2026-07-07T11:26:10.998367210Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-2hdbg","depends_on_id":"bf-29wyl","type":"blocks","created_at":"2026-07-07T10:24:42.486563321Z","created_by":"cli","thread_id":""}],"comments":[{"id":114,"issue_id":"bf-2hdbg","author":"cli","text":"DONE — committed docs(bf-2hdbg) 8116dc8, pushed to origin main (PROGRESS.md only; unrelated e2e_test.go/.beads bookkeeping left uncommitted).\n\nConfirmed against current source that the migration window — not tokens — is the sole acceptor of tokenless sim nodes:\n\n- config.go:139 MigrationWindowHours defaults to 24 (config.go:43 '0 = disabled'); main.go:4495-4497 installs deadline=now+24h only when >0, so a fresh boot opens the window.\n- server.go:510-518 acceptance branch gates purely on server.go:515 (!deadline.IsZero() && time.Now().Before(deadline)); tokenOK (server.go:513) is irrelevant — tokenless node admitted as Unpaired.\n- server.go:519-526 reject branch (else of 515) → server.go:525 sendReject(conn,\"invalid_token\") + conn.Close(); sendReject emits {\"type\":\"reject\",\"reason\":\"invalid_token\"} (server.go:841-845).\n\nConclusion: current 'no reject' is a 24h-window MASK, not a real fix. REJECT returns when SPAXEL_MIGRATION_WINDOW_HOURS=0 (deadline never set → IsZero() → reject) OR uptime > 24h (Before(deadline) false → reject). Real fix is token/supply side.\n\nAcceptance criteria all met in PROGRESS.md §bf-2hdbg (lines 752-855). go vet + build + internal unit tests clean (docs change touches no Go code).","created_at":"2026-07-07T11:24:59.961980678Z"}]} {"id":"bf-2ibc","title":"Add identity fields to explainability.BlobSnapshot type","description":"## Objective\nAdd the three canonical identity fields — PersonName, AssignedColor, IdentityResolved — to the `explainability.BlobSnapshot` struct type, which currently lacks them. This is the Tier-1 #2 type gap from the bf-1q3m/bf-5cgc handoff.\n\n## Scope (grounded in notes/bf-1q3m-consolidated.md §6 Tier-1 #2)\n- Edit `mothership/internal/explainability/handler.go:95` — `type BlobSnapshot struct` is today `{ID, X, Y, Z, Confidence, Weight}` with NO identity fields (verified: handler.go is absent from the AssignedColor grep set at HEAD).\n- Add `PersonName string`, `AssignedColor string`, `IdentityResolved *bool` with JSON tags matching the dashboard camelCase keys (`person_name`, `assigned_color`, `identity_resolved`), mirroring exactly how commit 1446ccf added them to signal.TrackedBlob / tracker.Blob / tracking.Blob / automation.TrackedBlob.\n- The production creation literal at `cmd/mothership/main.go:2206` needs NO change — Go zero values serialize to undefined in JS via omitempty. Fields-as-undefined, consistent with 1446ccf + 248d0e0.\n- OUT OF SCOPE (do not do here — it is a separate downstream bead): populating these fields from the `ble.IdentityMatcher` sidecar, and folding the parallel `identityMap` (main.go:2216) into the snapshot. This child adds field DECLARATIONS only, left at zero values.\n\n## Acceptance Criteria\n- [ ] PersonName, AssignedColor, IdentityResolved added to explainability.BlobSnapshot (handler.go:95) with correct JSON tags\n- [ ] Fields left at zero values at the main.go:2206 literal — NO populate/sidecar logic added\n- [ ] gofmt clean; `go vet ./internal/explainability/...` clean; `go test ./internal/explainability/...` passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T21:23:30.086543914Z","updated_at":"2026-07-06T21:39:07.645885664Z","closed_at":"2026-07-06T21:39:07.645885664Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"comments":[{"id":93,"issue_id":"bf-2ibc","author":"cli","text":"Implemented in commit c3f4b0d (pushed to origin). Added PersonName, AssignedColor, IdentityResolved to explainability.BlobSnapshot (handler.go) with camelCase JSON tags mirroring 1446ccf and the dashboard Blob type. Left at zero values at the main.go creation literal (declarations only; BLE sidecar populate is a separate downstream bead). Verified gofmt/vet/test/build all clean and zero-values serialize as omitted (undefined). Reconciled a divergent origin (duplicate bf-4bhd commit, identical patch-id) via rebase.","created_at":"2026-07-06T21:38:16.087174010Z"}]} {"id":"bf-2irz","title":"Verify registry bridge receives simulator positions","description":"Verify that registry_bridge.go actually receives position data from the simulator.\n\nScope:\n- Trace the data flow from simulator to registry_bridge.go\n- Add logging/debugging to verify position reception\n- Confirm positions are reaching the bridge\n\nAcceptance criteria:\n- Data flow from simulator to registry_bridge.go is traced\n- Logging confirms positions are received\n- Reception path is documented","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T05:23:44.753973594Z","updated_at":"2026-07-06T05:37:57.313816406Z","closed_at":"2026-07-06T05:37:57.313816406Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2irz","depends_on_id":"bf-2r5o","type":"blocks","created_at":"2026-07-06T05:24:11.877031865Z","created_by":"cli","thread_id":""}]} {"id":"bf-2jyk","title":"Dashboard: fix API path mismatch for crowd flow endpoint","description":"Crowd flow endpoint is served at /api/analytics/flow but plan spec and dashboard expect /api/flow. Fix the route definition in the Go backend or update the dashboard fetch URL to match whichever is canonical. Tracked as bf-3f16 in spaxel bead store but P3 and stale.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-31T15:43:40.808655211Z","updated_at":"2026-07-06T04:10:04.861491826Z","closed_at":"2026-07-06T04:10:04.861491826Z","close_reason":"Completed","source_repo":".","compaction_level":0} @@ -39,11 +45,13 @@ {"id":"bf-2po1","title":"Provision ESP32 node: set WiFi creds + mothership URL","description":"After bf-4gbf (ESP32 flashed and boots successfully):\n\nThe freshly flashed node will start a captive portal AP named spaxel-XXXX (last 4 of MAC).\n\nSteps:\n1. Connect phone or laptop to the \"spaxel-XXXX\" WiFi AP (no password)\n2. Browser redirects to 192.168.4.1 captive portal\n3. Enter WiFi SSID and password for the home network\n4. Enter mothership URL: spaxel.ardenone.com (port 443, WSS)\n The firmware will derive ws://... internally — enter just the hostname.\n5. Submit — node writes creds to NVS, reboots\n6. Node should now connect to home WiFi and then discover mothership via:\n - mDNS: _spaxel._tcp.local (disabled in prod; mothership is remote)\n - Fallback: ms_ip NVS key → use the IP/hostname provisioned in step 4\n7. Verify node appears in spaxel.ardenone.com fleet dashboard as CONNECTED\n (node sends hello: {type:hello, mac, firmware_version, capabilities})\n\nNote: SPAXEL_MIGRATION_WINDOW_HOURS=168 so the node can connect without a\nprovisioned token for 7 days. No token management needed for first node.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":0,"issue_type":"task","created_at":"2026-06-04T03:15:30.668150150Z","updated_at":"2026-07-03T00:03:28.351750089Z","source_repo":".","compaction_level":0,"dependencies":[{"issue_id":"bf-2po1","depends_on_id":"bf-4gbf","type":"blocks","created_at":"2026-07-03T00:01:33.878949841Z","created_by":"cli","thread_id":""}],"comments":[{"id":36,"issue_id":"bf-2po1","author":"cli","text":"Cannot complete via software agent — this is a hardware-validation task.\n\nREQUIREMENTS A SOFTWARE AGENT LACKS:\n- A physical ESP32-S3 dev board, freshly flashed and booting (prerequisite bf-4gbf).\n- A human's phone/laptop to join the spaxel-XXXX captive-portal AP, type the home WiFi SSID/password + hostname 'spaxel.ardenone.com' into the 192.168.4.1 form, submit, and verify the node shows CONNECTED on the dashboard.\n\nBLOCKED BY PREREQUISITE: bf-4gbf (Re-flash ESP32 via Web Serial) is still Status=pending and itself needs a human + USB-connected board. No flashed node exists yet to provision.\n\nNO CODE TO WRITE: The entire provisioning path is already implemented in firmware/:\n- Captive portal: wifi_start_captive_portal() in firmware/main/wifi.c (AP + DNS hijack to 192.168.4.1 + HTML form with ms_ip field, NVS write at wifi.c:399-400)\n- Serial provisioning JSON: firmware/main/provision.c:163-166 (writes NVS_KEY_MS_IP / NVS_KEY_MS_IP_PROV)\n- Boot fallback to provisioned mothership host: firmware/main/main.c:244-245 (ms_ip_prov)\n- Migration window (SPAXEL_MIGRATION_WINDOW_HOURS=168) allows first connect without a token.\n\nSERVER VERIFIED UP: https://spaxel.ardenone.com/healthz returns HTTP 200 (Cloudflare-fronted). Once a board is flashed + provisioned by a human, the node should appear CONNECTED.\n\nACTION: Left OPEN per task rules (do-not-close-when-incomplete). Needs a human + physical ESP32-S3 board. Same class as bf-4gbf / bf-4eal.","created_at":"2026-07-02T23:18:16.021916641Z"},{"id":37,"issue_id":"bf-2po1","author":"cli","text":"Not completable by an autonomous agent on this box — leaving OPEN per the 'do not close if you cannot complete' rule.\n\nBLOCKERS (physical, not code):\n- Prerequisite bf-4gbf is still PENDING — no physical ESP32 has been flashed yet, so there is no spaxel-XXXX captive-portal AP to join. This bead explicitly runs 'after bf-4gbf (ESP32 flashed and boots successfully)'.\n- No ESP32-S3 attached here: /dev/ttyUSB*, /dev/ttyACM* absent; no USB-serial bridge in lsusb.\n- Steps 1-5 are all physical/interactive: join the AP from a phone/laptop, browse 192.168.4.1, enter real home WiFi SSID+password + the mothership hostname.\n- Step 7 verification needs an authenticated browser: dashboard root returns 307 -> Google OAuth (accounts.google.com) — not headless-accessible.\n\nVERIFIED IN PLACE (nothing to build — the code/infra for this bead already exists and is healthy, re-confirmed 2026-07-02):\n- firmware/main/wifi.c: captive portal HTTP server @192.168.4.1:80, DNS hijack for captive detection, GET / form + POST /save handler, wifi_start_captive_portal().\n- firmware/main/provision.c: parses payload, writes wifi_ssid / wifi_pass / ms_ip / ms_ip_prov to NVS — the exact keys steps 5-6 require.\n- https://spaxel.ardenone.com/healthz -> HTTP 200 (0.11s). Mothership reachable; the ms_ip fallback target is healthy.\n\nNEEDED: a human (jedarden) with a physical ESP32-S3 + a phone. Once bf-4gbf flashes a board, the captive portal appears and steps 1-7 are manual. No token management needed (SPAXEL_MIGRATION_WINDOW_HOURS=168).","created_at":"2026-07-02T23:28:09.354382496Z"},{"id":38,"issue_id":"bf-2po1","author":"cli","text":"BLOCKED — not agent-completable (hardware-in-the-loop). This bead is a physical captive-portal provisioning step that requires: (1) a physical ESP32-S3 board connected via USB — NONE present (no /dev/ttyUSB* or /dev/ttyACM* on this host); (2) a human to join the 'spaxel-XXXX' AP from a phone/laptop and enter home WiFi creds + mothership URL at 192.168.4.1; (3) prerequisite bf-4gbf (Web Serial flash) which is still status=pending, so there is no flashed device to provision at all. The captive-portal CODE already exists (firmware/main/provision.c, wifi.c) — there is no code work in this bead. Same category as the tracked HW-validation beads (bf-4gbf, bf-4eal). Leaving OPEN for a human with the physical board; will be retried. NOT closing.","created_at":"2026-07-02T23:32:37.491825838Z"},{"id":39,"issue_id":"bf-2po1","author":"cli","text":"NOT CLOSEABLE by agent (hardware-only). Cannot provision a physical ESP32 from a server: no phone/laptop to join the spaxel-XXXX AP, no browser for the captive portal, no access to the home WiFi creds, no way to physically verify the node in the dashboard. Same category as bf-4gbf/bf-4eal (need a human + physical board). Prereq bf-4gbf (physical re-flash) is still PENDING/unassigned — no booting board exists to provision yet. SW path verified present: captive portal firmware (wifi.c wifi_start_captive_portal) implements AP spaxel-XXXX + DNS hijack to 192.168.4.1 + form (ssid/password/ms_ip) + NVS write + reboot; matches the described flow. GAP that would block step 7 even for a human: firmware hardcodes ws:// (websocket.c:107) with ms_port default 8080 (spaxel.h:13) and has NO TLS/wss support anywhere, but production endpoint per this bead is spaxel.ardenone.com:443 WSS. Provisioning that host via the captive form yields ws://spaxel.ardenone.com:8080 -> cannot reach a TLS-only :443 endpoint, and the form captures no port. Needs a firmware wss/TLS capability (root CA bundle + scheme/port handling) as its own SW bead before provisioning to a remote WSS mothership can succeed.","created_at":"2026-07-02T23:36:57.679696334Z"},{"id":40,"issue_id":"bf-2po1","author":"cli","text":"BLOCKED — not agent-completable (hardware/human step). bf-2po1 is a physical provisioning task: connect a phone to the ESP32's 'spaxel-XXXX' captive-portal AP (192.168.4.1), type the home WiFi SSID+password, enter mothership host (spaxel.ardenone.com), and verify the real node shows CONNECTED on the live dashboard. None of this is doable from the repo: it needs the physical board, the user's home WiFi credentials, and eyes on the dashboard. Additionally the predecessor bf-4gbf (re-flash ESP32 with the 4MB firmware) is still PENDING/unassigned, so the board is not even in the captive-portal state this bead assumes. Software side is already complete and verified: firmware/main/provision.c writes wifi_ssid/wifi_pass/ms_ip/ms_port to NVS and sets provisioned=1; wifi.c advertises the spaxel-%02X AP; the migration window (SPAXEL_MIGRATION_WINDOW_HOURS=168) allows tokenless connect. No code change needed — leaving open for a human with the hardware.","created_at":"2026-07-02T23:44:58.933800032Z"},{"id":41,"issue_id":"bf-2po1","author":"cli","text":"BLOCKED — not agent-completable (physical provisioning task, no code to change).\n\nThree blockers:\n1. Prerequisite bf-4gbf ('ESP32 flashed and boots successfully') is still PENDING — no node has been flashed, so there is no spaxel-XXXX captive-portal AP to provision against.\n2. No ESP32 hardware on this host (no /dev/ttyUSB* or /dev/ttyACM* devices) — this is a remote Hetzner server with no board attached.\n3. Steps 1-3 are inherently physical + require human-only data: a human must join the spaxel-XXXX WiFi AP, open the 192.168.4.1 captive portal in a browser, and type the HOME WiFi SSID/password. Those credentials are not in the repo and must not be guessed.\n\nNeeds: a human with (a) the ESP32-S3 board physically present, (b) bf-4gbf done first so the node boots into its captive portal, and (c) the home WiFi SSID/password. Leaving OPEN per the auto-retry policy — do not close until a human performs the captive-portal provisioning and the node shows CONNECTED at spaxel.ardenone.com.","created_at":"2026-07-02T23:54:25.178566299Z"},{"id":42,"issue_id":"bf-2po1","author":"cli","text":"NOT CLOSEABLE by agent (still needs human + physical ESP32-S3 + home WiFi creds). Two NEW items vs comments 36-41:\n\n(1) NETWORK CORRECTION — comment 39's 'TLS gap blocks step 7' is FALSE. Probed live: ws://spaxel.ardenone.com:8080/ws/node returns HTTP 101 (WebSocket upgrade succeeds through Cloudflare, which proxies :8080 as plain HTTP alongside :443). So the firmware's hardcoded ws://:8080 path (websocket.c:107, ms_port default 8080) works end-to-end. A provisioned node WILL send hello and show CONNECTED — NO firmware wss/TLS work is a prerequisite for physical provisioning to succeed. (The bead's 'port 443 WSS' wording is misleading; in practice Cloudflare exposes the node-facing endpoint on :8080 plain-WS.) healthz on :8080 returns the real mothership JSON: version 0.1.358, nodes_online 0, db ok.\n\n(2) ACTION to stop the retry loop — this bead was dispatched to agents 6x (comments 36-41), each reaching the same not-completable conclusion because prerequisite bf-4gbf (physically flash the board) is still pending. Added a blocking dependency: 'br dep add bf-2po1 bf-4gbf' (bf-2po1 depends-on bf-4gbf). Verified bf-2po1 is now excluded from 'br ready' while bf-4gbf is open. Once a human completes bf-4gbf (board flashed + booting into the spaxel-XXXX captive portal), bf-2po1 becomes ready and the steps are manual: join AP -> 192.168.4.1 -> enter home WiFi SSID/pass + 'spaxel.ardenone.com' -> verify CONNECTED. Left OPEN per do-not-close rule.","created_at":"2026-07-03T00:02:06.795374128Z"}]} {"id":"bf-2povs","title":"CSI simulator (spaxel-sim CLI)","description":"## Goal\nGo CLI tool that opens WebSocket connections as virtual nodes and sends synthetic CSI binary frames for development/testing without hardware.\n\n## CLI Interface\nspaxel-sim --mothership ws://localhost:8080/ws/node --token --nodes 4 --walkers 1 --rate 20 --duration 60s --ble --seed 42 --space '6x5x2.5'\n\n## Synthetic CSI Generation\n- Each virtual node has fixed position (corners, evenly distributed)\n- Each walker follows random walk: Gaussian velocity updates (σ=0.3 m/s per axis per 50ms), reflected at walls\n- For each TX→RX link pair at each tick: compute amplitude and phase using propagation model (path-loss + wall penetration + reflection)\n- Inject Gaussian noise: amplitude_noisy[k] = amplitude × (1 + N(0, 0.05))\n- Serialize into 24-byte binary frame format with n_sub=64\n- rssi = clamp(-30 - path_loss_dB, -90, -30), noise_floor = -95\n\n## Location\ncmd/sim/main.go (new package)\n\n## Acceptance\n- Simulator exits non-zero if it receives {type:'reject'} from mothership\n- Prints per-second frame counts and blob count (from GET /api/blobs poll)\n- Integration test: run simulator for 30s, assert blob count > 0\n- --ble flag also sends simulated BLE advertisements every 5s","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:05:43.376407159Z","updated_at":"2026-05-06T04:27:27.359958280Z","closed_at":"2026-05-06T04:27:27.359958280Z","close_reason":"Implemented CSI simulator CLI tool (spaxel-sim). The simulator connects to a running mothership via WebSocket and streams synthetic CSI data for testing without hardware.\n\nKey features:\n- CLI flags: --mothership, --token, --nodes, --walkers, --rate, --duration, --ble, --seed, --space\n- Synthetic CSI generation using Fresnel zone propagation model\n- Virtual nodes at fixed positions (corners, evenly distributed)\n- Walker random walk with Gaussian velocity updates (σ=0.3 m/s per axis per 50ms)\n- Wall reflection at space boundaries\n- 24-byte binary frame format with n_sub=64\n- RSSI clamped to [-90, -30] dBm, noise_floor=-95\n- Exits non-zero on {type:'reject'} from mothership\n- Prints per-second frame counts and blob count (from GET /api/blobs poll)\n- --ble flag sends simulated BLE advertisements every 5s\n\nBinary built and copied to mothership directory for integration tests.\nAll tests pass (go test ./... && go vet ./...).","source_repo":".","compaction_level":0} {"id":"bf-2r5o","title":"Explore simulator position data structure","description":"Understand what position data the simulator provides and how it's structured.\n\nScope:\n- Examine simulator output to identify position data fields\n- Document the current position data structure\n- Identify where position data originates in the simulator\n\nAcceptance criteria:\n- Position data structure is documented\n- Source of position data in simulator is identified\n- Field names and types are known","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T05:23:39.881193941Z","updated_at":"2026-07-06T05:35:17.257507535Z","closed_at":"2026-07-06T05:35:17.257507535Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} +{"id":"bf-2v9g","title":"Verify identity fields propagate through blob projections at runtime","description":"## Objective\nDecomposed from parent bf-f841. With a runtime blob carrying identity (prior step), this step confirms the identity flows THROUGH every blob projection at runtime — notify, analytics, falldetect — not just inside the unit-test structs that bf-5151 guards.\n\n## Scope\n- Using the identity-bearing runtime blob, observe each projection that consumes blob identity (notify/service.go, analytics/alert_handler.go, falldetect/detector.go).\n- Confirm each projection receives a populated identity value at runtime.\n- Look specifically for undefined/nil/empty identity reaching a projection that previously assumed it was always set.\n\n## Acceptance Criteria\n- [ ] notify projection observes a non-empty identity from the runtime blob.\n- [ ] analytics (alert handler) projection observes a non-empty identity.\n- [ ] falldetect projection observes a non-empty identity (where applicable).\n- [ ] No projection dereferences an unset identity field at runtime.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T00:10:09.042886116Z","updated_at":"2026-07-07T00:10:09.042886116Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-2v9g","depends_on_id":"bf-5h1t","type":"blocks","created_at":"2026-07-07T00:10:09.090341461Z","created_by":"cli","thread_id":""}]} {"id":"bf-2xhp","title":"axe-core accessibility CI gate: must pass before release","description":"Plan Quality Gates item 8 requires an axe-core accessibility check to pass before shipping. No bead (open or closed) covers this. Integrate axe-core into the Playwright E2E suite to scan the dashboard and onboarding UI for WCAG 2.1 AA violations. Wire as a CI step that blocks release.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-26T22:14:42.785566050Z","updated_at":"2026-07-06T12:46:52.964790297Z","source_repo":".","compaction_level":0,"labels":["umbrella"],"dependencies":[{"issue_id":"bf-2xhp","depends_on_id":"bf-6aif","type":"blocks","created_at":"2026-07-06T03:36:48.718374508Z","created_by":"cli","thread_id":""}]} {"id":"bf-2xykf","title":"Time-travel debugging with parameter tuning","description":"## Goal\nImplement pause live view, scrub timeline, replay 3D scene from recorded CSI with parameter tuning overlay.\n\n## Scope\n- Dashboard toolbar: 'Pause Live' button freezes 3D view and reveals timeline scrubber\n- Scrub backward/forward through recorded history (1×, 2×, 5×, or frame-by-frame)\n- 3D scene renders blobs exactly as they were detected at scrubbed time, including trails\n- Parameter tuning overlay: sliders for detection threshold, baseline time constant, Fresnel weight decay, subcarrier selection count, breathing sensitivity\n- Adjusting slider re-runs pipeline on recorded CSI with new parameters\n- 'Apply to Live' button writes tuned parameters to running pipeline\n\n## Location\ndashboard/static/js/timetravel.js (new module)\ninternal/replay/ (package already exists)\n\n## Acceptance\n- 'Pause Live' calls POST /api/replay/start with from=now-60s\n- Scrubber seeks to timestamp via POST /api/replay/seek\n- 3D view shows replay frames with replay:true flag\n- Parameter slider changes trigger PATCH /api/replay/params\n- 3D view immediately shows how detection would have differed\n- 'Apply to Live' calls POST /api/replay/apply-params","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:05:43.329902167Z","updated_at":"2026-05-05T22:34:13.436161012Z","closed_at":"2026-05-05T22:34:13.436161012Z","close_reason":"Implemented time-travel debugging with parameter tuning:\n- Pause Live button freezes 3D view and reveals timeline scrubber\n- Scrub backward/forward through recorded history (1x, 2x, 5x)\n- 3D scene renders blobs exactly as detected at scrubbed time with trails\n- Parameter tuning overlay with sliders for all detection parameters\n- Adjusting slider re-runs pipeline on recorded CSI with new parameters\n- Apply to Live button writes tuned parameters to running pipeline","source_repo":".","compaction_level":0} {"id":"bf-2y6u","title":"Component 5: Self-healing fleet — before/after coverage overlay in dashboard","description":"The fleet manager self-healing logic (fleet/manager.go selfHeal()) correctly re-optimizes roles and pushes new config to nodes when a node goes offline. However the dashboard visualization described in plan.md Component 12 is partially missing.\n\nWhat the plan requires:\n- Dashboard shows a before/after coverage comparison overlay when self-healing fires: Node kitchen-ceiling went offline. Coverage in kitchen corridor degraded from excellent to fair. 4 links lost, 8 remaining.\n- When node reconnects: Node kitchen-ceiling back online. Full coverage restored.\n- Affected areas highlighted in red in the 3D view (not just the node changing to red status dot)\n\nWhat exists:\n- Node status color change (green/yellow/red) on disconnect\n- selfHeal() re-optimizes roles and pushes config\n- Timeline events for node online/offline\n\nWhat is missing:\n- Before/after GDOP comparison when self-heal fires: compute coverage with N nodes vs N-1 nodes, diff\n- Overlay in 3D view highlighting zones with degraded coverage (ground plane zones turn amber/red)\n- WebSocket message type for coverage-degradation event with affected-zones payload\n- Frontend handling in viz3d.js to show the coverage degradation overlay and dismiss after recovery\n\nThis is distinct from the GDOP overlay in setup mode — it fires automatically on node loss, not on manual placement.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":3,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-26T04:04:17.405979993Z","updated_at":"2026-07-06T06:29:20.716432012Z","closed_at":"2026-07-06T06:29:20.716432012Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-31bp","title":"Firmware host-based unit tests: nvs/csi/serial_prov via idf.py test --target linux","description":"Testing Strategy requires 3 firmware modules tested via idf.py test --target linux (no hardware needed): (1) nvs schema migration test, (2) csi binary frame serialization roundtrip, (3) serial_prov JSON parser fuzz. No bead (open or closed) covers this. Implement the host test targets in the ESP-IDF component CMakeLists and wire them into CI.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":1,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-05-26T22:14:42.778447327Z","updated_at":"2026-07-03T17:30:32.207344691Z","closed_at":"2026-07-03T17:30:32.207344691Z","close_reason":"Completed","source_repo":".","compaction_level":0,"comments":[{"id":43,"issue_id":"bf-31bp","author":"cli","text":"Host-test intent satisfied by the committed gcc harness under firmware/test/ (build-out of decision spike bf-21t), NOT idf.py test --target linux. The idf.py --target linux mechanism named in this bead was explicitly REJECTED (decision record docs/notes/firmware-host-test-approach.md): firmware/main builds as ONE idf_component_register whose REQUIRES names esp_wifi/bt/driver -- three components with no host build -- so the whole component is unhostable; even nvs_migration.c (hostable in isolation) is blocked because main is one component. Building the idf.py path would require risky #ifdef-guarding of production firmware and create a second test mechanism (the decision record warns against exactly this). The gcc harness delivers exactly what the Testing Strategy requires, hardware-free: (1) nvs schema migration test_nvs_migration.c (fresh-install v1, no-downgrade guard, forward-migration dispatch, v1->v2 rename+default -- 8 tests); (2) csi binary frame serialization roundtrip test_csi_frame.c (24-byte header roundtrip, little-endian timestamp, signed-RSSI reinterpretation, I/Q payload, n_sub=0 probe, ingestion-side validation -- 7 tests); (3) serial_prov JSON parser fuzz test_serial_prov.c (bounded recursive-descent decoder fuzzed across random byte streams + deep-nesting cap, valid/invalid/minimal payloads -- 12 tests incl. fuzz passes). Result: 29 passed, 0 failed of 29. CI gate: Dockerfile 'RUN make -C test test' (plain gcc, no IDF toolchain; runs before the ESP-IDF build, propagates non-zero exit). Cleanup this bead: gitignored firmware/test_apps/ -- leftover CMake/ninja build output from an abandoned idf.py linux experiment (no source files; root-owned Docker artifacts so left in place, but now gitignored so it cannot be committed). Verification: make -C firmware/test test -> 29/29; cd mothership && go test ./... -> PASS; go vet ./... -> clean. Pushed 4b0eaba..fbb86fb.","created_at":"2026-07-03T17:29:35.954043239Z"}]} {"id":"bf-33za","title":"Component 33: Interactive Onboarding — Teach by Doing (5-step CSI physics wizard)","description":"The existing onboard.js has a basic 3-phase calibration wizard (walk/still/walk_through) but is missing the full Component 33 spec from plan.md.\n\nWhat exists: 3-phase calibration (walk 30s, still 10s, walk-through 15s), live CSI waveform canvas, post-calibration card.\n\nWhat is missing — 5 teaching steps with specific narration:\n\nStep 1 (walk 30s): Copy reads I am listening to your WiFi signal. Walk across the room / See that? Your body just changed the WiFi signal — that is how I detect you. Amplitude spikes highlighted in real-time.\n\nStep 2 (still 10s): A green baseline line fades in on chart. Copy reads This is your rooms baseline — the signal when nothing is moving. Any change from this means someone is here. Baseline captured automatically here, not via separate calibrate trigger.\n\nStep 3 (walk through detection zone 15s): Fresnel zone ellipsoid between node and router lights up as translucent green in 3D scene. Copy reads Walk between your node and the router — through the green zone / That is the Fresnel zone — I am most sensitive along this path. Zone pulses brighter as user crosses.\n\nStep 4 (let me find you 15s): Copy reads Walk somewhere and stop. I will try to locate you. Humanoid figure appears at estimated position with dotted accuracy radius. Copy reads Found you! I estimate you are about here. My accuracy is plus-or-minus 1 meter with this setup.\n\nStep 5 (place your node — interactive): Coverage painting activates on ground plane inside wizard. Copy reads Now drag your node to where it actually is in the room. Watch the green coverage change. After placement shows coverage score percentage. Copy reads Nice! Your coverage score is N percent.\n\nKey constraint: zero jargon — CSI, Fresnel, deltaRMS must never appear in wizard text.\n\nImplementation: Steps 3-4 require viz3d.js integration; step 5 requires placement.js GDOP overlay inside wizard; step 4 polls GET /api/blobs for humanoid placement.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-26T04:03:50.425961066Z","updated_at":"2026-07-06T03:01:11.271309877Z","closed_at":"2026-07-06T03:01:11.271309877Z","close_reason":"Completed","source_repo":".","compaction_level":0} +{"id":"bf-34lwt","title":"Investigate and document the REJECT/token root cause in the WS hello path","description":"First link of the bf-4iewr split. Write the definitive root-cause for the tokenless-sim REJECT failure mode so the remaining children have a precise target. The bead is currently PASSING-BY-ACCIDENT (no reject in a default boot) — this child explains exactly why, and exactly when it breaks.\n\nConfirm and document each of the following against the current source (cite file:line):\n\n## Scope\n1. Validator is wired UNCONDITIONALLY: `cmd/mothership/main.go:4494` calls `ingestSrv.SetTokenValidator(provSrv.ValidateToken)` — there is no config/build gate, so \"validator configured?\" is always YES.\n2. The validator reads `hello.Token` only: `internal/ingestion/server.go:513` (`tokenOK := hello.Token != \"\" && validator(hello.MAC, hello.Token)`).\n3. The sim sends its provisioned token ONLY as the `X-Spaxel-Token` HTTP header (`cmd/sim/main.go:633`, also `cmd/sim/scenario.go:318`) and does NOT include a `token` field in the hello JSON body (hello map at cmd/sim/main.go ~640).\n4. The mothership NEVER reads that header — `grep -rn 'X-Spaxel-Token\\|Header.Get' cmd/mothership/ internal/` returns no WS-path hit. Consequence: `hello.Token` is always \"\" for sim nodes → `tokenOK` always false → the \"supply valid tokens\" option in the parent bead is currently NON-FUNCTIONAL.\n5. The only thing accepting sim nodes is the migration window: `internal/ingestion/server.go:515`. MigrationWindowHours DEFAULTS TO 24 (`internal/config/config.go:139`), so a fresh boot opens a 24h window → sim nodes accepted as Unpaired, no reject (matches PROGRESS.md bf-3hji).\n6. Therefore REJECT fires ONLY when the window is closed: `SPAXEL_MIGRATION_WINDOW_HOURS=0` (deadline never set -> IsZero() -> reject branch), OR uptime > 24h. In those cases every sim node logs \"missing token\" -> sendReject(conn, \"invalid_token\") -> disconnect.\n\n## Acceptance Criteria\n- [ ] A written root-cause finding posted as a comment on bf-4iewr, confirming points 1-6 with file:line citations.\n- [ ] Explicit statement that current \"no reject\" is a 24h-window MASK, not a real fix.\n- [ ] Explicit statement that the token-supply path is currently dead (header ignored, hello.Token empty).","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T09:48:38.581052724Z","updated_at":"2026-07-07T10:25:45.731335737Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-34lwt","depends_on_id":"bf-4th1x","type":"blocks","created_at":"2026-07-07T10:24:42.546648134Z","created_by":"cli","thread_id":""}]} {"id":"bf-3a2py","title":"Acceptance scenario integration tests (AS-1 through AS-6)","description":"## Goal\nImplement the 6 acceptance scenarios from plan §Acceptance Scenarios as verifiable integration tests in test/acceptance/.\n\n## Scenarios to implement\n\nAS-1: First-time setup in under 5 minutes\n - Start fresh mothership container, open /api/auth/setup, set PIN\n - Run spaxel-sim --nodes 1 (simulates provisioned node)\n - Assert: node appears in /api/nodes within 30s\n\nAS-2: Person detected while walking\n - spaxel-sim --nodes 2 --walkers 1 --duration 60s\n - Poll /api/blobs every second\n - Assert: blob count > 0 for >80% of the run\n\nAS-3: Fall alert fires correctly\n - spaxel-sim with a walker that drops Z rapidly (spike downward velocity, then stays at Z<0.5)\n - Assert: events table contains fall_alert within 15s of trigger\n - Assert: webhook endpoint (test HTTP server) receives POST\n\nAS-4: BLE identity resolves to person name\n - Register a BLE device as 'Alice' via POST /api/ble/devices\n - spaxel-sim --ble (sends BLE reports for that address alongside blobs)\n - Assert: /api/blobs returns at least one blob with person='Alice'\n\nAS-5: OTA update succeeds / rollback on bad firmware\n - Already partially covered by existing OTA rollback integration test\n - Extend to verify the VERIFIED badge path (valid firmware + node reconnects with new version)\n\nAS-6: Replay shows recorded history\n - Run 60s of sim data\n - POST /api/replay/start with a 30s window\n - Assert: replay blobs are returned via WebSocket with replay:true flag\n\n## Location\ntest/acceptance/*.go — one file per scenario, parallel to test/integration/\n\n## Acceptance\nAll 6 scenarios pass in the Argo CI workflow using spaxel-sim as the test harness","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"test","assignee":"claude-code-glm-4.7-golf","created_at":"2026-05-02T12:09:24.898852471Z","updated_at":"2026-05-05T11:39:18.097311991Z","closed_at":"2026-05-05T11:39:18.097311991Z","close_reason":"Implemented all 6 acceptance scenarios as verifiable integration tests in test/acceptance/\n\nAS-1: First-time setup in under 5 minutes\n- Fresh mothership container starts successfully\n- PIN setup works via /api/auth/setup\n- spaxel-sim --nodes 1 connects and appears in /api/nodes within 30s\n\nAS-2: Person detected while walking\n- spaxel-sim --nodes 2 --walkers 1 runs for 60 seconds\n- GET /api/blobs returns at least 1 blob during walk\n- Detection ratio > 80% of run duration\n\nAS-3: Fall alert fires correctly\n- spaxel-sim with fall scenario triggers rapid Z descent\n- Fall alert appears in /api/events within 30 seconds\n- Webhook endpoint receives POST with alert payload\n\nAS-4: BLE identity resolves to person name\n- BLE device registered via POST /api/ble/devices\n- spaxel-sim --ble sends BLE advertisements\n- Blob appears with person='Alice' within 15 seconds\n\nAS-5: OTA update succeeds / rollback on bad firmware\n- spaxel-sim --scenario ota simulates successful OTA\n- Node firmware version increments after update\n- Rollback scenario triggers rollback on boot failure\n\nAS-6: Replay shows recorded history\n- 60s of sim data generates CSI buffer\n- POST /api/replay/start creates replay session\n- Replay blobs returned with replay:true flag\n- Seek functionality works within 1 second target\n\nAll tests use spaxel-sim as the test harness for simulating CSI data without hardware.","source_repo":".","compaction_level":0} {"id":"bf-3aij","title":"Define blob object literal search pattern","description":"## Objective\nDefine the search pattern for identifying blob-shaped object literals.\n\n## Scope\n- Define what constitutes a 'blob-shaped' object literal\n- Identify key characteristics: id, type, data fields\n- Create grep/search patterns for finding these literals\n\n## Acceptance Criteria\n- [ ] Blob pattern definition documented\n- [ ] Search patterns created for grep/ripgrep\n- [ ] Example matches documented\n- [ ] Search patterns saved to notes/bf-26ta-pattern.md","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T06:01:42.216826835Z","updated_at":"2026-07-06T06:06:06.848567110Z","closed_at":"2026-07-06T06:06:06.848567110Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-3d55l","title":"Fuzz tests: binary frame parser and JSON protocol","description":"## Goal\nAdd property-based/fuzz tests for the two highest-impact input parsing surfaces, per plan §Testing Strategy → Property-Based / Fuzz Tests.\n\n## Targets\n\n1. FuzzParseBinaryFrame — internal/ingestion/frame_fuzz_test.go\n Seed corpus: valid frame, truncated header, n_sub mismatch, channel=0, n_sub>128\n Property: never panic; drop/parse/error all OK\n\n2. FuzzParseJSONFrame — internal/ingestion/json_fuzz_test.go\n Seed corpus: hello, health, ble, motion_hint, ota_status, unknown type\n Property: never panic; unknown types return typed error\n\n3. Phase sanitization property test — internal/pipeline/phase/phase_property_test.go\n For all valid int8 I/Q pairs, Sanitize output never contains NaN or Inf\n\n## Acceptance\n- go test -fuzz=FuzzParseBinaryFrame ./internal/ingestion/ -fuzztime=60s: no panic found\n- go test -fuzz=FuzzParseJSONFrame ./internal/ingestion/ -fuzztime=60s: no panic found\n- Phase sanitization property test passes for all int8 I/Q corner cases","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"test","assignee":"claude-code-glm-4.7-golf","created_at":"2026-05-02T12:08:47.919183889Z","updated_at":"2026-05-04T10:01:38.755721488Z","closed_at":"2026-05-04T10:01:38.755721488Z","close_reason":"Completed","source_repo":".","compaction_level":0} @@ -55,6 +63,7 @@ {"id":"bf-3gw1","title":"Wire fusion engine node positions at mothership startup","description":"Split from bf-4q5w (parent umbrella). The live 10Hz fusion/blob loop in mothership/cmd/mothership/main.go (~line 1864 onward) produces zero blobs because node positions are never seeded into the blob-producing fusion engine.\n\nFindings:\n- Startup DOES seed positions today, but only into selfImprovingLocalizer (the internal/localization 2D engine) at main.go ~1019-1025. It does NOT seed the internal/fusion 3D Fresnel engine (internal/fusion/fusion.go:93 NewEngine; SetNodePosition at :127) that the IO-6 / blob path depends on.\n- internal/fusion.NewEngine appears to be constructed only in tests (no non-test construction found). The implementer must first confirm which engine actually feeds the live blob/detection loop and the IO-6 gate, then ensure that engine is both constructed AND seeded.\n\nScope of THIS child:\n- Determine definitively which fusion engine produces the blobs observed by tests/e2e and the IO-6 hard-gate (walker produces a tracked blob).\n- Ensure that engine is constructed and integrated into the live loop in cmd/mothership/main.go (construct it if currently test-only).\n- At startup, seed every node position into THAT engine from the fleet registry / DB nodes table (PosX/PosY/PosZ), mirroring the existing selfImprovingLocalizer seeding loop, so nodes are NOT all co-located at the DB default of (0,0,1) per internal/db/migrations.go:143-145.\n\nAcceptance criteria:\n- The blob-producing fusion engine is constructed in non-test code and integrated into the live loop.\n- At startup the engine holds a distinct, non-default position for each node (no all-(0,0,1) collapse).\n- A host/unit test or logged assertion confirms engine positions are populated after startup for N nodes.\n- go build ./... and existing tests pass.\nNote: runtime PATCH/registration propagation and sim geometry are separate children.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T19:22:44.942503675Z","updated_at":"2026-07-03T21:16:16.333632281Z","closed_at":"2026-07-03T21:16:16.333632281Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-3gw1","depends_on_id":"bf-1tsm","type":"blocks","created_at":"2026-07-03T19:54:13.010126760Z","created_by":"cli","thread_id":""}],"comments":[{"id":51,"issue_id":"bf-3gw1","author":"cli","text":"Scope already implemented and verified in the committed tree by sibling beads (bf-3f6q/bf-6s3d/bf-1tsm). No new code needed. DETERMINATION (which engine feeds live blob/IO-6 path): internal/fusion.Engine (3D Fresnel), NOT the 2D selfImprovingLocalizer. Constructed in non-test code (main.go:1028), drives the live 10Hz loop (fusionEngine.Fuse at main.go:1947), publishes peaks via pm.SetTrackedBlobs (main.go:1949). The 2D selfImprovingLocalizer.Fuse runs in a separate slower weight-learning loop (main.go:2676), not the blob path. SEEDING: main.go:1054-1062 iterates fleetReg.GetAllNodes() and calls fusionEngine.SetNodePosition(PosX/PosY/PosZ), mirroring selfImprovingLocalizer; reads actual DB columns (no all-(0,0,1) collapse). ASSERTION+TEST: logged startup assertion main.go:1075-1095 (bf-1tsm); TestEngine_SeedNodePositions internal/fusion/fusion_test.go:329 (bf-6s3d) PASS. GATES: go build ./... exit 0; go vet ./... exit 0; go test ./... 50/50 packages pass. No source changes — did not fabricate redundant seeding/assertion.","created_at":"2026-07-03T21:16:07.321863467Z"}]} {"id":"bf-3h1hk","title":"Playwright e2e acceptance scenarios AS-1 through AS-6","description":"Open bead bf-3a2py tracks acceptance scenario integration tests but they're not implemented. The tests/e2e/run.sh exists as a bash harness skeleton. The plan implies specific acceptance scenarios covering the full happy path: (AS-1) first-node provisioning → presence detection in <30s; (AS-2) multi-node localization accuracy; (AS-3) portal crossing detection; (AS-4) fall detection alert chain; (AS-5) OTA update flow; (AS-6) security mode arm/alert/disarm. These should be Playwright tests against a running mothership+sim stack.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:26:08.138470605Z","updated_at":"2026-05-02T18:26:38.599373498Z","closed_at":"2026-05-02T18:26:38.599373498Z","close_reason":"Duplicate of existing open bead bf-3a2py which already tracks acceptance scenario integration tests AS-1 through AS-6","source_repo":".","compaction_level":0} {"id":"bf-3h5kd","title":"golangci-lint config and CI gate for mothership","description":"The open bead bf-w15bj tracks a CI pipeline gate for golangci-lint, but no .golangci.yml exists in the repo root or mothership/ directory. The sim Makefile references golangci-lint but only for cmd/sim. The main mothership codebase (~50+ packages) has no lint config. Needed: a .golangci.yml at mothership/ level with appropriate linter set (errcheck, staticcheck, unused, govet, etc.) and a CI step in the Argo WorkflowTemplate mta-my-way-build (or a separate lint-only workflow).","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:25:46.332413911Z","updated_at":"2026-05-02T18:26:44.610380579Z","closed_at":"2026-05-02T18:26:44.610380579Z","close_reason":"Duplicate of existing open bead bf-w15bj which already tracks golangci-lint CI gate with full config specification","source_repo":".","compaction_level":0} +{"id":"bf-3hji","title":"Connect spaxel-sim and prove CSI streaming with no reject","description":"Decomposed from bf-40hc. Second link: with the mothership healthy (child bf-3zll), connect spaxel-sim and prove it streams synthetic CSI without being rejected.\n\nSource of truth: e2e TestHarness.RunSimulator in mothership/tests/e2e/e2e_test.go.\n\n## Scope\n- Build the simulator from the mothership module root:\n go build -o /tmp/spaxel-sim ./cmd/sim\n- Run against the healthy mothership:\n ./spaxel-sim --mothership ws://localhost:8080/ws/node --nodes 4 --walkers 1 --rate 20 --duration 30 --ble --seed 42\n- Confirm all 4 nodes connect (no REJECT), frames/s > 0 in sim stats, and GET http://localhost:8080/api/nodes shows the nodes online.\n\n## Acceptance Criteria\n- [ ] spaxel-sim connects all configured nodes; sim logs report frames/s > 0.\n- [ ] No \"REJECT\" message/HTTP 401/403 from the mothership.\n- [ ] /api/nodes lists the sim nodes as online.\n- [ ] Exact run command recorded in the bead body.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T04:07:26.103330575Z","updated_at":"2026-07-07T05:20:51.135659408Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-3hji","depends_on_id":"bf-3zll","type":"blocks","created_at":"2026-07-07T04:07:26.142056834Z","created_by":"cli","thread_id":""},{"issue_id":"bf-3hji","depends_on_id":"bf-vuzie","type":"blocks","created_at":"2026-07-07T05:20:08.484510391Z","created_by":"cli","thread_id":""}]} {"id":"bf-3ifj","title":"Trace simulator-to-registry position wiring","description":"Verify that position data from virtual node creation flows correctly from the simulator to the registry module.\n\nScope:\n- Trace the wiring from prior children that connects simulator positions to registry\n- Identify any breaks or missing connections in the simulator-to-registry path\n- Document the expected data flow\n\nAcceptance criteria:\n- Complete data flow path documented from simulator to registry\n- Any breaks or missing connections identified\n- List of files involved in the simulator-to-registry pipeline","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T04:56:13.049391510Z","updated_at":"2026-07-06T05:14:22.698532733Z","closed_at":"2026-07-06T05:14:22.698532733Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-3j3s","title":"Apply per-person colors to humanoid meshes","description":"## Objective\nApply unique, consistent colors to each person's humanoid mesh based on their assigned color from the BLE identity matching.\n\n## Scope\n- Extend mesh material to use per-person colors (assignedColor field from bf-64h5)\n- Generate or assign colors if not provided (hash-based color generation from name)\n- Ensure colors are visually distinct and readable\n- Add lighting if needed to make colors visible\n\n## Dependencies\nDepends on: bf-1h7h (humanoid meshes must exist first)\n\n## Acceptance Criteria\n- [ ] Each person's humanoid mesh has a unique, consistent color\n- [ ] Alice is always the same color across sessions (consistent assignment)\n- [ ] Colors are visually distinct (no two people with same/similar colors)\n- [ ] Colors are visible in current lighting (not too dark/bright)\n- [ ] Unresolved blobs have default color (e.g., gray)\n\n## Notes\nThis adds color to the humanoid meshes. Name labels come in the next child.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T04:05:17.486156951Z","updated_at":"2026-07-06T04:05:17.486156951Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-3j3s","depends_on_id":"bf-1h7h","type":"blocks","created_at":"2026-07-06T04:05:20.560255186Z","created_by":"cli","thread_id":""}]} {"id":"bf-3jv1x","title":"Complete portal crossings GET /api/portals/:id/crossings endpoint","description":"Plan's REST API spec defines GET /api/portals/:id/crossings with ?limit and ?before cursor pagination, returning [{timestamp, direction, person, blob_id}]. The portals package and CRUD endpoints exist (internal/api/zones.go handles portals), and portal_crossings is in the SQLite schema, but the crossings query endpoint is not implemented. This is needed for the timeline 'crossing log' quick action and for the portal detail view.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:25:57.312746596Z","updated_at":"2026-05-05T19:35:15.900705949Z","closed_at":"2026-05-05T19:35:15.900705949Z","close_reason":"Completed","source_repo":".","compaction_level":0} @@ -62,41 +71,54 @@ {"id":"bf-3p4bj","title":"Command palette (Component 34)","description":"## Goal\nCtrl+K (Cmd+K on Mac) opens universal search and command interface. Invisible to casual users, indispensable for power users.\n\n## Scope\nSearch:\n- 'kitchen' → Kitchen zone, kitchen nodes, kitchen automations, recent kitchen events\n- 'alice' → Alice's current location, today's timeline, sleep report, BLE devices\n- 'node 3' → Node details, diagnostics, link health\n\nNavigate time:\n- 'last night 2am' → timeline jumps there\n- 'yesterday kitchen' → filters timeline to kitchen events yesterday\n- 'this morning' → jumps to first detection today\n\nExecute commands:\n- 'update all nodes', 're-baseline kitchen', 'add node', 'arm security', 'disarm security'\n- 'dark mode'/'light mode', 'export config', 'restart node kitchen-north'\n\nGet help:\n- 'help fall detection', 'why false positive', 'troubleshoot kitchen'\n\nBehavior:\n- Fuzzy matching: 'flr pln' matches 'Floor Plan settings'\n- Recently used commands appear first\n- Results show keyboard shortcut hints where applicable\n- Escape closes, Enter executes top result\n- Works in expert mode only\n\n## Implementation\nFrontend-only component\nCommand registry maps keywords to actions\nSearch runs against: zone names, person names, node names, setting names, help topics\n\n## Acceptance\n- Ctrl+K/Cmd+K opens command palette\n- Search finds zones, people, nodes, settings, help topics\n- Commands execute correctly\n- Time navigation jumps to correct moments\n- Fuzzy matching works\n- Escape closes palette","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:06:11.571696739Z","updated_at":"2026-05-06T08:49:45.284413081Z","closed_at":"2026-05-06T08:49:45.284413081Z","close_reason":"Component 34: Command Palette fully implemented and tested.\n\n- Ctrl+K/Cmd+K opens universal search and command interface\n- Fuzzy matching across zones, people, nodes, settings, help topics\n- Time navigation: @3am, @-30min, @yesterday 11pm, @this morning, @last night\n- Commands execute: arm/disarm security, dark/light mode, export config, re-baseline, add node, etc.\n- Recently used commands appear first (up to 5)\n- Escape closes, Enter executes top result\n- Works in expert mode only (excluded from simple/ambient modes)\n- All 88 tests pass","source_repo":".","compaction_level":0} {"id":"bf-3p6g","title":"Propagate runtime node position changes to the fusion engine","description":"Split from bf-4q5w (parent umbrella). Depends on the startup-seeding child.\n\nToday PATCH /api/nodes/:mac updates only the fleet registry: internal/fleet/handler.go updateNodePosition calls h.mgr.GetRegistry().SetNodePosition (~handler.go:391) and BroadcastRegistry, but never forwards the new position to the fusion engine. Likewise node connect/register events do not push positions to the engine, so a node moved at runtime stays at its stale position in the blob engine.\n\nScope:\n- On PATCH node position (handler.go updateNodePosition), after updating the registry, call the blob-producing fusion engine SetNodePosition(mac,x,y,z) for the engine wired in the prior child.\n- On node connect/register, push the node current position to the engine as well.\n- Decide where the engine reference lives so the handler can reach it (e.g. via the registry/fleet manager or a setter injected at startup), matching existing accessor patterns such as SetNodePositionAccessor in internal/diagnostics and internal/fleet/weather.go.\n\nAcceptance criteria:\n- PATCH /api/nodes/:mac and node registration both update the fusion engine position for that MAC.\n- Existing fleet handler tests (internal/fleet/handler_test.go) still pass.\n- A test asserts the position the engine stores for a MAC changes after a PATCH with new coordinates.\n- go build ./... passes.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T19:22:52.558913759Z","updated_at":"2026-07-03T21:35:49.310935619Z","closed_at":"2026-07-03T21:35:49.310935619Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-3p6g","depends_on_id":"bf-3gw1","type":"blocks","created_at":"2026-07-03T19:23:17.331023175Z","created_by":"cli","thread_id":""}],"comments":[{"id":52,"issue_id":"bf-3p6g","author":"cli","text":"Implemented bf-3p6g: runtime node positions now propagate to the fusion engine. Added Manager.nodePositionSink (write-side mirror of the read-side SetNodePositionAccessor used by diagnostics/weather.go), wired to fusionEngine.SetNodePosition in main.go. Fires from both position paths: PATCH /api/nodes/{mac}/position (handler.updateNodePosition -> ForwardNodePosition) and node connect/register (OnNodeConnected forwards the registry position). Commit a17bdd2. Tests: TestHandlerUpdateNodePositionForwardsToEngine (asserts the engine's stored position for a MAC changes after a PATCH with new coords), TestManagerOnNodeConnectedForwardsPositionToEngine (asserts connect seeds the engine position). Existing fleet handler tests still pass; go build/vet/test ./... all green.","created_at":"2026-07-03T21:31:51.895058799Z"},{"id":53,"issue_id":"bf-3p6g","author":"cli","text":"Implemented and verified (commit a17bdd2, pushed to origin/main).\n\nThe fleet Manager gains a nodePositionSink — the write-side mirror of the read-side SetNodePositionAccessor used by diagnostics/weather.go — wired at startup (main.go) to fusionEngine.SetNodePosition. It fires from both position paths:\n- PATCH /api/nodes/{mac}/position: Handler.updateNodePosition calls ForwardNodePosition after the registry accepts the new position.\n- Node connect/register: Manager.OnNodeConnected forwards the node's current registry position.\n\nTests (internal/fleet/handler_test.go):\n- TestHandlerUpdateNodePositionForwardsToEngine: asserts the engine's stored position for a MAC changes after a PATCH with new coordinates (seeds 1.5,2.5,3.5 then updates to 7,8,9).\n- TestManagerOnNodeConnectedForwardsPositionToEngine: asserts connect pushes the registry position to the engine.\n\nVerification: go build ./... OK, go vet ./... OK, go test ./... all pass (existing fleet handler tests still green).","created_at":"2026-07-03T21:34:59.582248981Z"}]} {"id":"bf-3tlw","title":"Compile blob literal locations documentation","description":"## Objective\nCompile all findings into a comprehensive documentation.\n\n## Scope\n- Merge TypeScript and JavaScript search results\n- Create comprehensive documentation with all locations\n- Note blob structure patterns for each site\n\n## Acceptance Criteria\n- [ ] TypeScript and JavaScript results merged\n- [ ] All blob-shaped object literals documented in single file\n- [ ] Each location includes: file path, line number, blob structure pattern\n- [ ] Final documentation saved to notes/bf-26ta-findings.md","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T06:02:13.129510178Z","updated_at":"2026-07-06T06:20:54.599604029Z","closed_at":"2026-07-06T06:20:54.599604029Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-3tlw","depends_on_id":"bf-4ly4","type":"blocks","created_at":"2026-07-06T06:02:17.268268599Z","created_by":"cli","thread_id":""}]} +{"id":"bf-3ud7e","title":"Build spaxel-sim binary from the mothership module root","description":"First step of the spaxel-sim streaming chain (child of bf-3hji). From the mothership module root (./mothership/, module github.com/spaxel/mothership), build the simulator:\n\n go build -o /tmp/spaxel-sim ./cmd/sim\n\nThis resolves to ./mothership/cmd/sim/ (two levels up from tests/e2e per moduleRoot()). The build/compile step is a frequent failure point for bf-3hji, so it is isolated here.\n\n## Scope\n- Run the build from ./mothership/ (NOT the repo root, which has a separate ./cmd/sim).\n- Confirm the binary is produced and runs.\n\n## Acceptance Criteria\n- [ ] go build -o /tmp/spaxel-sim ./cmd/sim succeeds from ./mothership/ with exit code 0.\n- [ ] /tmp/spaxel-sim --help runs and prints usage without crashing.\n- [ ] Binary path /tmp/spaxel-sim recorded in the bead body.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-07T05:19:35.437601762Z","updated_at":"2026-07-07T05:33:26.519093049Z","closed_at":"2026-07-07T05:33:26.519093049Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"comments":[{"id":108,"issue_id":"bf-3ud7e","author":"cli","text":"DONE — spaxel-sim builds from the mothership module root (module github.com/spaxel/mothership).\n\nAcceptance criteria — all met, run from ./mothership/:\n1. `go build -o /tmp/spaxel-sim ./cmd/sim` → exit 0. Binary produced at /tmp/spaxel-sim (~9.2 MB).\n2. `/tmp/spaxel-sim --help` → exit 0; prints full flag usage (--mothership, --nodes, --walkers, --rate, --duration, --ble, --seed, --space, --scenario, --verify, etc.) without crashing.\n3. Binary path recorded here: /tmp/spaxel-sim.\n\nCompile-step validation (this bead isolates the frequent failure point for parent bf-3hji):\n- `go build ./...` (whole mothership module) → exit 0.\n- `go vet ./...` (whole module) → exit 0.\n- `go test ./cmd/sim/...` → exit 0 (ok, cached).\n\nScope note: `./cmd/sim` from the module root resolves to mothership/cmd/sim — the in-module simulator (supports --ble/--seed/--scenario + WS hello + CSI streaming), NOT the repo-root ./cmd/sim standalone module github.com/spaxel/sim. This is identical to what the e2e harness builds: moduleRoot() in mothership/tests/e2e/e2e_test.go returns mothership/ (two levels up from tests/e2e) and runs `go build -o /tmp/spaxel-sim-test ./cmd/sim` with Dir=mothership.\n\nNo code change was required — the build already passes from the mothership module root; nothing to commit. Full e2e suite intentionally not run: the IO-6 blob hard-gate (bf-4q5w) is deliberately RED per PROGRESS.md and is out of this build-isolation bead's scope.","created_at":"2026-07-07T05:29:06.487886846Z"},{"id":109,"issue_id":"bf-3ud7e","author":"cli","text":"spaxel-sim binary built and verified. Binary path: /tmp/spaxel-sim (9,208,487 bytes). Built from the mothership module root: `go build -C ./mothership -o /tmp/spaxel-sim ./cmd/sim` (resolves to mothership/cmd/sim/, package main). Exit code 0. `/tmp/spaxel-sim --help` prints full usage (--mothership, --nodes, --walkers, --rate, --duration, --ble, --seed, --space, --scenario, etc.) and exits 0. All acceptance criteria met.","created_at":"2026-07-07T05:33:15.317989012Z"}]} {"id":"bf-3ukb","title":"Fix crowd flow API path mismatch: /api/analytics/flow vs plan spec /api/flow","description":"Two open beads (bf-2jyk, bf-3f16) both describe this same bug: the crowd flow analytics endpoint is registered at /api/analytics/flow but the dashboard JS and plan spec reference /api/flow.\n\nFix:\n- Find the crowd flow endpoint registration in mothership/cmd/mothership/main.go\n- Add an alias at /api/flow that delegates to the same handler, OR\n- Update the dashboard JS to use /api/analytics/flow consistently\n- Choose the approach that aligns with the OpenAPI docs\n\nRelated open beads this resolves: bf-2jyk and bf-3f16 (same root cause).","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-04T03:16:28.407609539Z","updated_at":"2026-07-06T04:13:12.269404105Z","closed_at":"2026-07-06T04:13:12.269404105Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-3v39","title":"Capture baseline and verify presence detection with first node","description":"After bf-2po1 (node connected and streaming CSI):\n\nIn passive radar mode (single node, using home AP beacons as TX source):\n1. Ensure room is empty, then POST /api/baseline/capture to capture empty-room baseline\n curl -X POST https://spaxel.ardenone.com/api/baseline/capture (auth required)\n2. Wait 30s for baseline to stabilize (EMA with tau=30s)\n3. Walk through the detection area between the node and the home WiFi AP\n4. In the dashboard, observe: deltaRMS should spike from ~0.02 to ~0.10+\n5. A blob should appear in the 3D visualization at the detected position\n\nWith a single node, only presence detection is reliable (not precise localization).\nFor localization, a second node on the opposite side of the space is needed.\n\nSuccess: deltaRMS > 0.05 when person is present, blob visible in dashboard.\n\nTroubleshooting: if no signal change, check passive_bss NVS key has the AP BSSID,\nor switch node to TX_RX mode and use node-to-node CSI instead of passive radar.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":0,"issue_type":"task","assignee":"","created_at":"2026-06-04T03:15:41.581140007Z","updated_at":"2026-07-03T00:13:34.779002015Z","source_repo":".","compaction_level":0,"dependencies":[{"issue_id":"bf-3v39","depends_on_id":"bf-2po1","type":"blocks","created_at":"2026-07-03T00:12:30.706349666Z","created_by":"cli","thread_id":""}]} {"id":"bf-3wkz","title":"Verify dashboard JS/TS identity-field surface","description":"## Objective\nVerify the dashboard JS/TS blob surface declares and correctly surfaces the three canonical identity fields (personName, assignedColor, identityResolved), aligning field names with the Go JSON tags emitted by the backend projections. This closes the Tier-4 dashboard track of notes/bf-1q3m-consolidated.md §6 (#7/#8/#9).\n\n## Scope (grounded in notes/bf-1q3m-consolidated.md §3.4 + §6 Tier-4)\n- Confirm `dashboard/types/spaxel.d.ts` Blob interface (lines 10-91) declares `personName`, `assignedColor`, `identityResolved` (preliminary grep shows all three present at lines 53/74/90 — verify exact, including the deprecated personLabel/personColor aliases).\n- Cross-check the TS field names against the Go JSON tags emitted by the projections that feed the dashboard: `dashboard.blobJSON` (internal/dashboard/hub.go), `api.Track` (internal/api/tracks.go), and — once children 1 & 2 land — `explainability.BlobSnapshot` and `volume.BlobPos`. Document the mapping; adjust TS or Go tags only if a real mismatch is found.\n- Confirm `dashboard/js/state.js:290` creation literal (`appState.blobs[id] = { id: id }`) correctly surfaces server-supplied identity via the `Object.assign(...)` at state.js:292 — no pre-init needed if the server sends the fields (it does, post-Tier-1).\n- `dashboard/js/websocket.js:167` (J2 dead-reckoning cache) is intentionally identity-free — confirm and leave alone.\n\n## Acceptance Criteria\n- [ ] spaxel.d.ts Blob interface confirmed to declare personName, assignedColor, identityResolved (+ deprecated aliases)\n- [ ] TS field names ↔ Go JSON-tag mapping documented; any mismatch fixed\n- [ ] state.js:290 creation path confirmed to surface server identity via Object.assign; no change needed unless broken","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T21:23:54.879479212Z","updated_at":"2026-07-06T21:59:39.656195409Z","closed_at":"2026-07-06T21:59:39.656195409Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-3wkz","depends_on_id":"bf-5v3q","type":"blocks","created_at":"2026-07-06T21:24:06.395105377Z","created_by":"cli","thread_id":""}],"comments":[{"id":95,"issue_id":"bf-3wkz","author":"cli","text":"Verified the dashboard JS/TS identity-field surface (Tier-4 track of notes/bf-1q3m-consolidated.md §6). spaxel.d.ts Blob interface declares personName/assignedColor/identityResolved (+ deprecated personLabel/personColor aliases). Cross-checked TS vs Go JSON tags across all 8 blob-shaped types: 7 already camelCase; FIXED volume.BlobPos — the sole outlier emitting snake_case (person_name/assigned_color/identity_resolved -> camelCase) to match the canonical convention (bf-5151) and the dashboard. Tags were inert (volume.BlobPos is consumed in Go by Store.Evaluate, which returns fired-trigger IDs; never marshalled to the dashboard) but would have leaked snake_case if ever surfaced. state.js:290 surfaces server identity via Object.assign (no change needed); websocket.js:167 dead-reckoning cache intentionally identity-free (left alone). go vet ./... and go test ./... pass. Mapping documented in notes/bf-3wkz-dashboard-identity-surface.md. Commit 7309564.","created_at":"2026-07-06T21:53:46.343461884Z"},{"id":96,"issue_id":"bf-3wkz","author":"cli","text":"Verified & closed (commit 908adf2, on origin/main).\n\nIndependent re-verification at HEAD 7309564 confirms the Tier-4 dashboard identity surface:\n- spaxel.d.ts Blob interface declares personName(53)/assignedColor(74)/identityResolved(90) + deprecated personLabel/personColor aliases + personId.\n- All 8 Go types emit the canonical camelCase trio: dashboard.blobJSON (hub.go:558-560), api.Track (tracks.go:31-33), explainability.BlobSnapshot (handler.go:109-111), volume.BlobPos (shape.go:1090-1092), signal.TrackedBlob, tracker.Blob (3D), tracking.Blob (2D), automation.TrackedBlob. The lone snake_case outlier (volume.BlobPos) was aligned to camelCase in commit 7309564.\n- state.js:288-294 updateBlob surfaces server identity via Object.assign at :292 (creation literal is {id} only) — no change needed.\n- websocket.js:159-174 dead-reckoning cache is intentionally identity-free ({x,z,vx,vz,ts}) — left alone.\n- go vet ./... exit 0; go test ./... all packages ok.\n\nFull mapping + provenance in notes/bf-3wkz-dashboard-identity-surface.md. No source change required in this pass.","created_at":"2026-07-06T21:58:58.109007041Z"}]} {"id":"bf-3y9r","title":"Verify position propagation through registry and fusion engine","description":"Child of bf-18yn (split). Depends on bf-5yff.\n\nEnsure that the realistic node positions flow through the entire pipeline from simulator to registry to fusion engine.\n\nScope:\n- Trace the wiring from prior children that connects simulator positions to registry\n- Verify positions are correctly propagated to internal/simulator/registry_bridge.go\n- Verify positions reach the fusion engine accumulation grid\n- Add any missing glue if positions don't flow through (should be wired from prior children)\n\nAcceptance criteria:\n- Positions from virtual node creation reach the registry correctly\n- Positions from registry reach the fusion engine correctly\n- If wiring was incomplete, add the missing connections\n- Integration test or log output shows positions flowing through all stages","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T04:22:26.661147812Z","updated_at":"2026-07-06T17:40:04.865168935Z","closed_at":"2026-07-06T17:40:04.865168935Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-3y9r","depends_on_id":"bf-5yff","type":"blocks","created_at":"2026-07-06T04:22:30.785332691Z","created_by":"cli","thread_id":""},{"issue_id":"bf-3y9r","depends_on_id":"bf-95tx","type":"blocks","created_at":"2026-07-06T04:56:36.064193893Z","created_by":"cli","thread_id":""}],"comments":[{"id":72,"issue_id":"bf-3y9r","author":"cli","text":"Closed by verification. Position propagation through the full chain (simulator VirtualNodeStore -> registry_bridge -> fleet.Registry SQLite -> fusion.Engine.nodePos mirror -> accumulation grid) was already wired by prior children (bf-5lii -> bf-69ym -> bf-u7ds -> bf-95tx).\n\nThis bead's acceptance criterion -- 'positions reach the fusion engine accumulation grid and actually drive localization' -- is locked in by TestPositionPropagation_ReachesAccumulationGrid in mothership/internal/api/simulator_test.go. It posts two explicitly-positioned nodes via the simulator REST API, runs one Fuse step with a link between the two MACs the engine now holds, and asserts a blob localizes near the link midpoint (confidence 1.0, 1 active link). That peak is only reachable if BOTH endpoints' positions reached the grid lookup (AddLinkInfluence reads nodePos[mac] for both endpoints; a missing/co-located position yields no peak).\n\nVerified: go test ./... PASS (internal/api incl.), go vet ./... clean. Grid trace under -v:\n [grid] position propagation midpoint=(3.00,1.00) peak=(2.90,0.90,0.90) conf=1.000 activeLinks=1\n\nNo production wiring change needed -- prior children completed it. Committed and pushed to origin/main.","created_at":"2026-07-06T17:32:00.336376744Z"},{"id":73,"issue_id":"bf-3y9r","author":"cli","text":"Verified position propagation through simulator → registry → fusion engine. The wiring from prior children is complete and correct: a posted node position flows VirtualNodeStore → fleetRegistryBridge.SyncToRegistry (SQLite write) → fleetRegistryAdapter forwardPos hook → fleetMgr.ForwardNodePosition → nodePositionSink → fusion.Engine.SetNodePosition, and Fuse reads nodePos[mac] for both endpoints into AddLinkInfluence on the accumulation grid. No production change was needed. Lock-in: TestPositionPropagation_ReachesAccumulationGrid posts two explicitly-positioned nodes through the simulator REST API and asserts one Fuse step localizes a blob near the link midpoint (peak=(2.90,0.90,0.90), conf=1.000, activeLinks=1) — only reachable if BOTH endpoints' positions reached the grid lookup (a missing/co-located position yields no peak, cf. TestEngine_CoLocatedOriginYieldsNoPeaks). go vet ./... clean; go test ./... fully green.","created_at":"2026-07-06T17:39:55.812604919Z"}]} +{"id":"bf-3zll","title":"Build and boot the mothership hardware-free to /healthz","description":"Decomposed from bf-40hc (hardware-free runtime blob path). First link in the chain: prove the mothership can be built and started WITHOUT any ESP32 hardware and reach a healthy state.\n\nThe exact path is already exercised in mothership/tests/e2e/e2e_test.go (TestHarness.Start) — use it as the source of truth.\n\n## Scope\n- From the mothership module root (mothership/, module github.com/spaxel/mothership; go.work declares it as the root), build:\n go build -o /tmp/spaxel-mothership ./cmd/mothership\n- Run with env: SPAXEL_BIND_ADDR=127.0.0.1:8080, SPAXEL_DATA_DIR=, SPAXEL_LOG_LEVEL=info, TZ=UTC\n- Confirm GET http://localhost:8080/healthz returns {\"status\":\"ok\",...} within ~15s\n\n## Acceptance Criteria\n- [ ] Build command documented and succeeds from a clean checkout.\n- [ ] Mothership starts with the documented env and reaches /healthz status=ok with no runtime panic/crash in stderr.\n- [ ] Exact commands written into the bead body for the next child to reuse.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-07T04:07:02.386946384Z","updated_at":"2026-07-07T04:46:43.876136467Z","closed_at":"2026-07-07T04:46:43.876136467Z","close_reason":"Completed — all 3 acceptance criteria verified (build exits 0; /healthz status=ok in ~1s; no panic/crash in stderr). failure-count:3 were 10-min wall-clock timeouts on already-succeeded work, not work failures. Recipe in comment [106] + PROGRESS.md.","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"comments":[{"id":106,"issue_id":"bf-3zll","author":"cli","text":"Hardware-free mothership build + boot to /healthz — DONE (verified 2026-07-07).\n\nEXACT COMMANDS for the next child in the bf-40hc chain (reusable as-is):\n\n# 1. Build (from clean checkout; no -tags=embed needed locally):\ncd mothership # module root: github.com/spaxel/mothership\ngo build -o /tmp/spaxel-mothership ./cmd/mothership\n\n# 2. Boot to /healthz (no ESP32, no Docker):\nDATADIR=$(mktemp -d /tmp/spaxel-data-XXXX)\nSPAXEL_BIND_ADDR=127.0.0.1:8080 SPAXEL_DATA_DIR=$DATADIR SPAXEL_LOG_LEVEL=info TZ=UTC /tmp/spaxel-mothership &\n\n# 3. Verify (reaches ok in <1s; ~15s budget):\ncurl -fsS http://localhost:8080/healthz\n# -> {\"status\":\"ok\",\"uptime_s\":0,\"version\":\"dev\",\"nodes_online\":0,\"db\":\"ok\",\"shedding_level\":0}\nkill %1 # SIGINT -> ordered 30s graceful shutdown\n\nVERIFIED: build exits 0 (25MB binary); all 7 startup phases complete ([READY] All 7 phases completed in ~231ms); /healthz status=ok with process alive; no panic/crash in stderr. Benign WARNs expected: self-probe racing listener bind ('continuing anyway'), and unbundled dashboard without the embed tag. Canonical source of truth: TestHarness.Start in mothership/tests/e2e/e2e_test.go. Recipe also documented in PROGRESS.md ('Hardware-Free Runtime Boot').","created_at":"2026-07-07T04:38:09.446638018Z"},{"id":107,"issue_id":"bf-3zll","author":"cli","text":"INDEPENDENT RE-VERIFICATION (2026-07-07): auto-split dispatch (bf-3zll failure-count:3) was a false alarm. Re-ran the full acceptance path from a clean checkout just now: (1) 'go build -o /tmp/spaxel-mothership ./cmd/mothership' from mothership/ module root -> exit 0, 24M binary; (2) booted with SPAXEL_BIND_ADDR=127.0.0.1:8080 SPAXEL_DATA_DIR= SPAXEL_LOG_LEVEL=info TZ=UTC; (3) curl /healthz returned {\"status\":\"ok\",...} in ~1s with process alive and NO panic/crash/fatal in stderr. All 3 acceptance criteria are satisfied. The '3 failures' recorded in .beads/traces/bf-3zll/metadata.json were 10-min wall-clock TIMEOUTS (exit_code 124, duration 600002ms) on already-succeeded work, NOT work failures. Splitting this bead into child beads would fabricate tasks for completed+verified work. Closing as Completed (recipe already in comment [106] + PROGRESS.md 'Hardware-Free Runtime Boot').","created_at":"2026-07-07T04:44:02.773988092Z"}]} +{"id":"bf-40hc","title":"Establish a hardware-free runtime path that creates a blob","description":"## Objective\nDecomposed from parent bf-f841 (Test blob identity fields at runtime). This first step pins down a reliable, hardware-free way to RUN the mothership and produce a tracked blob, which the original bead never specified — that ambiguity caused the repeated failures.\n\n## Scope\n- Use cmd/sim (spaxel-sim) or a documented local mothership launch to run WITHOUT ESP32 hardware.\n- Drive enough simulated CSI to make the tracker emit at least one tracked blob at runtime.\n- Capture the exact reproduce command(s) so later steps (and reruns) are deterministic.\n\n## Acceptance Criteria\n- [ ] Documented command starts the mothership without a runtime panic/crash.\n- [ ] At least one blob object is observable at runtime (via API, WebSocket, or tracker log).\n- [ ] Reproduce command written into the bead body or a referenced note.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T00:09:36.138791654Z","updated_at":"2026-07-07T04:09:38.897353967Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-40hc","depends_on_id":"bf-5cr3","type":"blocks","created_at":"2026-07-07T04:08:15.170802687Z","created_by":"cli","thread_id":""}]} {"id":"bf-49fp","title":"Dockerfile: bust Kaniko cache for firmware stage to force 4MB rebuild","description":"The 0.1.352 Docker image contains firmware compiled with CONFIG_ESPTOOLPY_FLASHSIZE_16MB (16MB header). The fix was already committed in d837598 (sdkconfig.defaults -> 4MB, partitions.csv redesigned for 4MB). But Kaniko served a cached firmware layer for the 0.1.352 build, ignoring the sdkconfig.defaults change.\n\nSymptom: ESP32-S3 (4MB flash) flashed via Web Serial crashes on every boot with:\n spi_flash: Detected size(4096k) smaller than binary image header(16384k). Probe failed.\n assert failed: do_core_init startup.c:326\n\nFix:\n1. In firmware stage of Dockerfile, add a cache-busting ARG before the COPY:\n ARG FIRMWARE_CACHE_BUST=2026-06-03\n This guarantees Kaniko treats the layer as new on the next build.\n\n2. Also add RUN rm -f sdkconfig sdkconfig.old after COPY and before idf.py set-target\n so any stale tracked sdkconfig cannot survive into the build.\n\nAfter this change, idf.py set-target will regenerate sdkconfig from sdkconfig.defaults\nwhich has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y, producing firmware with a 4MB header.\n\nReference: firmware/sdkconfig.defaults (CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y)\n firmware/partitions.csv (4MB layout: factory+ota_0 fit in 4MB)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":0,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-06-04T03:13:10.895986057Z","updated_at":"2026-07-02T18:50:13.296108997Z","closed_at":"2026-07-02T18:50:13.296108997Z","close_reason":"Fix was already committed in d837598/301a588 (Kaniko cache-bust ARG + sdkconfig cleanup in the firmware Dockerfile stage) weeks ago. Bead was stuck retrying due to a dead ANTHROPIC_BASE_URL in the claude-code-glm-5 adapter (fixed 2026-07-02); the agent correctly verified the fix was in place and closed this itself, but a missing verify-changes.sh hook on lab (also fixed 2026-07-02) reopened it every time. No further action needed.","source_repo":".","compaction_level":0,"comments":[{"id":1,"issue_id":"bf-49fp","author":"cli","text":"Already implemented in commit 301a588 (pushed to origin/main). Firmware stage of Dockerfile now contains both required changes: (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 before 'COPY firmware/ ./' to guarantee a Kaniko cache miss on the next build; (2) RUN rm -f sdkconfig sdkconfig.old after COPY and before 'idf.py set-target' so sdkconfig is regenerated from sdkconfig.defaults (CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y) every build. Verified: firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y, firmware/partitions.csv is a 4MB layout (factory+ota_0 fit), VERSION already at 0.1.357 (past the 0.1.354 bump that accompanied this fix), so a fresh CI build producing the 4MB-header firmware has already run. Resolves the ESP32-S3 boot crash (spi_flash detected 4096k < binary header 16384k).","created_at":"2026-07-02T17:19:36.504115277Z"},{"id":2,"issue_id":"bf-49fp","author":"cli","text":"Already implemented and shipped in commit 301a588 ('fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop'), on origin/main (live since v0.1.354). Verified on re-dispatch: (1) Dockerfile:31 has ARG FIRMWARE_CACHE_BUST=2026-06-03 before the firmware COPY; (2) Dockerfile:38 has 'RUN rm -f sdkconfig sdkconfig.old' after COPY and before 'idf.py set-target'. Confirmed firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y. No new commit needed - working tree had no code changes.","created_at":"2026-07-02T17:30:00.661481731Z"},{"id":3,"issue_id":"bf-49fp","author":"cli","text":"Resolved: this fix was already implemented in commit 301a588 (ancestor of HEAD, pushed to main). Verified both required lines present in current Dockerfile matching spec exactly: line 31 ARG FIRMWARE_CACHE_BUST=2026-06-03 before COPY firmware/ (line 34); line 38 RUN rm -f sdkconfig sdkconfig.old after COPY and before idf.py set-target (line 44). Dockerfile working tree clean (git diff empty) — nothing new to commit/push. go vet ./... clean, go test ./... all pass. Fix shipped in 0.1.354+ (now at 0.1.357); the 0.1.352 16MB-header crash loop is resolved. Did not bump cache-bust value: task explicitly specifies 2026-06-03 and the one-time cache invalidation already served its purpose.","created_at":"2026-07-02T17:37:28.212905891Z"},{"id":4,"issue_id":"bf-49fp","author":"cli","text":"Verified complete — both requested Dockerfile changes are already present in the firmware stage and were committed in 301a588 (pushed to origin/main). (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 before 'COPY firmware/ ./' at Dockerfile:31 busts Kaniko's firmware-layer cache. (2) 'RUN rm -f sdkconfig sdkconfig.old' after COPY and before 'idf.py set-target' at Dockerfile:38 removes stale generated sdkconfig so it regenerates from sdkconfig.defaults. Supporting state verified: sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y; partitions.csv is the 4MB layout (factory 0x10000 + ota_0 0x200000, each 0x1F0000); no stale generated sdkconfig tracked. No code change needed this session; go vet + go test ./... both pass (exit 0). Build/deploy/flash tracked in follow-up bead bf-15hy.","created_at":"2026-07-02T17:42:52.975800256Z"},{"id":5,"issue_id":"bf-49fp","author":"cli","text":"Already implemented in commit 301a588 ('fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop'), in HEAD and pushed to origin/main. Verified: (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 added in firmware stage BEFORE the COPY at Dockerfile:31 — busts Kaniko layer cache; (2) RUN rm -f sdkconfig sdkconfig.old added AFTER COPY and BEFORE idf.py set-target at Dockerfile:38 — clears stale tracked sdkconfig so set-target regenerates from sdkconfig.defaults. Confirmed sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y and partitions.csv is the 4MB layout (factory 0x1F0000 + ota_0 0x1F0000 fits 4MB). Working tree clean; no further changes needed.","created_at":"2026-07-02T17:46:59.138102056Z"},{"id":6,"issue_id":"bf-49fp","author":"cli","text":"Already implemented and committed in 301a588 (in branch history, pushed to origin). Dockerfile firmware stage has ARG FIRMWARE_CACHE_BUST=2026-06-03 before COPY firmware/ (line 31) and RUN rm -f sdkconfig sdkconfig.old after COPY and before idf.py set-target (line 38). Forces Kaniko cache miss + regenerates sdkconfig from sdkconfig.defaults (CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y), producing a 4MB-header firmware. Verified: go vet ./... clean, go test ./... all pass. No new commit needed - working tree Dockerfile is clean.","created_at":"2026-07-02T17:50:11.573756430Z"},{"id":7,"issue_id":"bf-49fp","author":"cli","text":"Implementation already present in commit 301a588 (fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop), verified in current Dockerfile on main: (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 before COPY firmware/ (Dockerfile:31); (2) RUN rm -f sdkconfig sdkconfig.old after COPY and before idf.py set-target (Dockerfile:38). Coherent with firmware/sdkconfig.defaults (CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y) and firmware/partitions.csv (4MB layout). No new code changes were needed — working tree held only dispatcher metadata and build artifacts.","created_at":"2026-07-02T17:55:44.367732542Z"},{"id":8,"issue_id":"bf-49fp","author":"cli","text":"Closed as already-complete: commit 301a588 (fix(firmware): bust Kaniko cache + force sdkconfig regen) already added both requested Dockerfile firmware-stage changes — ARG FIRMWARE_CACHE_BUST=2026-06-03 before the COPY (line 31), and RUN rm -f sdkconfig sdkconfig.old after COPY / before idf.py set-target (line 38). That commit is on origin/main; sdkconfig.defaults carries CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y, partitions.csv is the 4MB layout (factory+ota_0 fit), and VERSION has since advanced to 0.1.357 so the 4MB-header fix is baked into released images. No new commit was needed.","created_at":"2026-07-02T18:03:06.315827627Z"},{"id":9,"issue_id":"bf-49fp","author":"cli","text":"Already implemented and pushed. The exact two changes this bead requests landed in commit 301a588 ('fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop'), which is on origin/main and an ancestor of HEAD. Verified in current Dockerfile: (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 before 'COPY firmware/ ./' at line 31; (2) RUN rm -f sdkconfig sdkconfig.old after COPY and before 'idf.py set-target esp32s3' at line 38. Also confirmed firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y and firmware/partitions.csv is the 4MB layout. No working-tree changes were needed.","created_at":"2026-07-02T18:06:58.975466905Z"},{"id":10,"issue_id":"bf-49fp","author":"cli","text":"Fix already implemented and pushed (commit 301a588, present in origin/main and github/main). Verified the two required Dockerfile changes are in place in the firmware-builder stage, correctly ordered: line 31 ARG FIRMWARE_CACHE_BUST=2026-06-03 (cache-bust ARG before COPY), line 34 COPY firmware/ ./, line 38 RUN rm -f sdkconfig sdkconfig.old (stale sdkconfig removal after COPY, before set-target), line 44 idf.py set-target esp32s3. This guarantees Kaniko treats the firmware layer as new and forces set-target to regenerate sdkconfig from sdkconfig.defaults with CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y, producing firmware with a 4MB header that boots on 4MB-flash ESP32-S3 devices (fixes the spi_flash binary image header 16384k crash loop). No new commit needed; this task is a retry/duplicate of already-shipped work.","created_at":"2026-07-02T18:15:37.119821897Z"},{"id":11,"issue_id":"bf-49fp","author":"cli","text":"Already implemented in commit 301a588 (in main, pushed to origin/main). The Dockerfile firmware stage contains both required changes: (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 before COPY firmware/ (line 31) to bust Kaniko's cached 16MB firmware layer; (2) RUN rm -f sdkconfig sdkconfig.old (line 38) after COPY and before idf.py set-target, so set-target regenerates sdkconfig from sdkconfig.defaults. Verified firmware/sdkconfig.defaults sets CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y and partitions.csv uses a 4MB layout (factory+ota_0 fit in 4MB). No code changes needed - Dockerfile working tree is clean.","created_at":"2026-07-02T18:31:31.018683964Z"},{"id":12,"issue_id":"bf-49fp","author":"cli","text":"Fix already implemented in commit 301a588 (fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop), dated 2026-06-03. Verified present and correct in current Dockerfile: (1) ARG FIRMWARE_CACHE_BUST=2026-06-03 at line 31, immediately before 'COPY firmware/ ./'; (2) RUN rm -f sdkconfig sdkconfig.old at line 38, after COPY and before 'idf.py set-target' at line 44. Reference files confirmed coherent: firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y; firmware/partitions.csv is the 4MB layout (factory 0x10000 + ota_0 0x200000 + otadata 0x3F0000, all within 4MB). No new changes required — already committed and pushed (ancestor of HEAD). No Go code touched, so go test/vet not applicable to this Dockerfile-only fix.","created_at":"2026-07-02T18:37:55.559623529Z"},{"id":13,"issue_id":"bf-49fp","author":"cli","text":"Already implemented and shipped — no change needed.\n\nBoth requested Dockerfile firmware-stage changes are present:\n - Line 31: ARG FIRMWARE_CACHE_BUST=2026-06-03 (before the COPY)\n - Line 38: RUN rm -f sdkconfig sdkconfig.old (after COPY, before idf.py set-target)\n\nIntroduced in commit 301a588 ('fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop'), already on origin/main (verified via git branch -r --contains 301a588). Dockerfile working tree matches HEAD, no uncommitted changes.\n\nEffect: the ARG forces Kaniko to treat the firmware layer as new; the rm ensures idf.py set-target regenerates sdkconfig from sdkconfig.defaults (CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y), yielding a 4MB header — fixing the spi_flash 'binary image header(16384k)' boot crash on 4MB-flash ESP32-S3. Closing as already-complete.","created_at":"2026-07-02T18:45:49.875764400Z"}]} +{"id":"bf-4ads8","title":"Run spaxel-sim and prove frames/s > 0","description":"Third step of the chain (depends on the no-REJECT child). With the binary built and tokenless nodes accepted, run spaxel-sim against the healthy mothership (bf-3zll) and prove it streams synthetic CSI.\n\n## Scope\n- Start the hardware-free mothership (per bf-3zll) and wait for /healthz = ok.\n- Run the documented command:\n /tmp/spaxel-sim --mothership ws://localhost:8080/ws/node --nodes 4 --walkers 1 --rate 20 --duration 30 --ble --seed 42\n- Confirm all 4 nodes connect and the sim streams frames.\n\n## Acceptance Criteria\n- [ ] All 4 nodes connect (sim logs report 4 connected nodes, no REJECT).\n- [ ] Sim end-of-run stats report frames/s > 0.\n- [ ] Exact run command recorded in the bead body.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T05:19:53.269249161Z","updated_at":"2026-07-07T05:19:53.269249161Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4ads8","depends_on_id":"bf-4iewr","type":"blocks","created_at":"2026-07-07T05:20:08.426553463Z","created_by":"cli","thread_id":""}]} {"id":"bf-4b1c","title":"Add test coverage for geometry placement and fusion peaks","description":"Child of bf-18yn (split). Depends on bf-3y9r.\n\nWrite tests that verify the geometry placement produces meaningful fusion results.\n\nScope:\n- Add a sim or host test that creates virtual nodes with default placement\n- Verify the fusion accumulation grid produces non-zero peaks (not all zeros)\n- Ensure test would fail with old co-located placement but passes with new geometry\n- Run existing simulator tests to ensure no regressions\n\nAcceptance criteria:\n- New test passes with realistic geometry\n- Test verifies fusion accumulation grid has non-zero peaks\n- Existing simulator tests still pass\n- go build ./... passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T04:22:35.225244286Z","updated_at":"2026-07-06T17:54:27.194709041Z","closed_at":"2026-07-06T17:54:27.194709041Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4b1c","depends_on_id":"bf-3y9r","type":"blocks","created_at":"2026-07-06T04:22:39.781625261Z","created_by":"cli","thread_id":""}],"comments":[{"id":74,"issue_id":"bf-4b1c","author":"cli","text":"Implemented (commit c4d42e8, pushed to main). Added TestEngine_GeometryPlacementDrivesFusionPeaks to mothership/internal/fusion/fusion_test.go — a differential lock-in that holds node placement as the SOLE variable (engine grid, all-pairs motion link set, and room identical across both legs) and proves the non-zero-peak condition flips with geometry: default (spread) placement accumulates a non-zero fusion grid (gridMax>0; blobs>=1 for count>=4 where crossing links form a true maximum), while the old co-located (0,0,1) collapse makes every link degenerate (<0.1m), so AddLinkInfluence early-returns and the grid stays at zero (gridMax==0, 0 blobs). The trace shows the flip: spread gridMax=1.0 blobs=2 vs colocated gridMax=0.0 blobs=0. Gates: go test ./... , go vet ./... , and go build ./... (mothership + cmd/sim + test/acceptance) all pass.","created_at":"2026-07-06T17:50:18.895413358Z"},{"id":75,"issue_id":"bf-4b1c","author":"cli","text":"Verified and closed (re-dispatch after prior run failed on 'br close --body' which is unsupported). Implementation is commit c4d42e8 on origin/main — TestEngine_GeometryPlacementDrivesFusionPeaks in mothership/internal/fusion/fusion_test.go.\n\nAll bf-4b1c acceptance criteria verified green:\n- go build ./... clean across all 3 modules (mothership, cmd/sim, test/acceptance)\n- go vet ./... clean across all 3 modules\n- go test ./... — all packages pass (mothership incl. internal/fusion + internal/simulator, test/acceptance ok)\n- New test passes with realistic geometry: spread placement accumulates a non-zero grid (gridMax=1.0) and count>=4 crossing links extract >=1 blob (nodes=4 -> blobs=2, conf=0.768)\n- Differential lock-in holds placement as the SOLE variable (identical engine grid, all-pairs motion link set, room): spread gridMax=1.0 blobs=2 vs old co-located (0,0,1) collapse gridMax=0.0 blobs=0 — the non-zero-peak condition flips with geometry, proving the test is genuinely regression-sensitive to the bf-4q5w collapse.","created_at":"2026-07-06T17:53:23.492885933Z"}]} {"id":"bf-4bhd","title":"Find all blob creation code paths","description":"## Objective\nIdentify all locations in the codebase where blob objects are created or constructed.\n\n## Scope\n- Search for blob creation patterns (new Blob, object literals with blob shape, blob factories)\n- Document all blob creation sites\n- Identify which files need updates\n\n## Acceptance Criteria\n- [ ] All blob creation sites are identified and listed\n- [ ] Each site is documented with file path and line number\n- [ ] Creation pattern is noted (constructor, literal, factory, etc.)\n- [ ] Report is ready for the next bead to use","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:11:28.984747608Z","updated_at":"2026-07-06T20:51:27.149808381Z","closed_at":"2026-07-06T20:51:27.149808381Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","umbrella"],"dependencies":[{"issue_id":"bf-4bhd","depends_on_id":"bf-3ldj","type":"blocks","created_at":"2026-07-06T05:32:05.819292341Z","created_by":"cli","thread_id":""},{"issue_id":"bf-4bhd","depends_on_id":"bf-1q3m","type":"blocks","created_at":"2026-07-06T15:04:21.606416708Z","created_by":"cli","thread_id":""},{"issue_id":"bf-4bhd","depends_on_id":"bf-5cgc","type":"blocks","created_at":"2026-07-06T20:13:54.885895615Z","created_by":"cli","thread_id":""}],"comments":[{"id":90,"issue_id":"bf-4bhd","author":"cli","text":"bf-5cgc handoff complete → notes/bf-5cgc-handoff.md. Your acceptance criterion 'Report is ready for the next bead to use' is now satisfied: the re-verified inventory lives in notes/bf-1q3m-consolidated.md (this file supersedes notes/bf-4bhd.md per bf-1q3m §4.1), and the site→bead handoff is formalized in notes/bf-5cgc-handoff.md with concrete owners (bf-5151 primary) + the GetMatch population pattern. bf-4bhd may be closed on this basis.","created_at":"2026-07-06T20:39:00.020455831Z"},{"id":92,"issue_id":"bf-4bhd","author":"cli","text":"Closed via auto re-dispatch. Blob-creation inventory was already delivered by the closed chain (bf-1q3m -> bf-1m2x -> bf-5cgc); this pass re-verifies currency. Empirical greps at reachable commit 21c702f (= 63f0c87 tree before the dispatcher rewrite) return exactly the documented counts: 5 primary literals, 15 non-test projection literals, 15 blob types, 2 JS production sites. Zero Go/JS/firmware source drift across f144aad..HEAD. New finding (provenance): prior notes cite unreachable dangling SHAs (1a26c12 is bf-1bmg not bf-1q3m; 0ae0c03 is amended-away bf-1q3m, real is f144aad) because history was rewritten; inventory unaffected, mapped to real SHAs. Deliverable: notes/bf-4bhd-reverification.md (commit a1e76e0). Source of truth: notes/bf-1q3m-consolidated.md; handoff: notes/bf-5cgc-handoff.md. All four acceptance criteria satisfied.","created_at":"2026-07-06T20:51:18.758949061Z"}]} {"id":"bf-4eal","title":"Update ardenone-cluster deployment to spaxel:0.1.354 after CI","description":"After spaxel-build-manual-bj7l2 (on iad-ci) succeeds and pushes ronaldraygun/spaxel:0.1.354:\n\n1. In ~/declarative-config, open k8s/ardenone-cluster/spaxel/deployment.yml\n2. Change image tag from 0.1.352 to 0.1.354\n3. Commit and push declarative-config\n4. ArgoCD auto-syncs the Deployment (Recreate strategy, ~30s downtime)\n5. Verify: kubectl --server=http://traefik-ardenone-cluster:8001 get pods -n spaxel\n6. Hit spaxel.ardenone.com/healthz to confirm it returns 200\n\nThis gets the 4MB firmware into the running mothership so re-flashing the ESP32 will work.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":0,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-06-04T03:15:07.139700779Z","updated_at":"2026-07-02T19:37:09.459815093Z","closed_at":"2026-07-02T19:37:09.459815093Z","close_reason":"Not executed: stale/obsolete — live cluster already at 0.1.358 (newer than target 0.1.354), healthy; deploying 0.1.354 would be a downtime downgrade. See comment 18.","source_repo":".","compaction_level":0,"comments":[{"id":16,"issue_id":"bf-4eal","author":"cli","text":"NOT DEPLOYED — preconditions unmet & premise stale (agent check 2026-07-02T19:03:12Z).\n\n1) Gating CI build missing: workflow 'spaxel-build-manual-bj7l2' is NotFound on iad-ci (zero spaxel workflows present). The task's hard precondition ('After spaxel-build-manual-bj7l2 succeeds and pushes ronaldraygun/spaxel:0.1.354') has NOT occurred.\n\n2) Premise is stale: task says 'change tag from 0.1.352 to 0.1.354', but declarative-config k8s/ardenone-cluster/spaxel/deployment.yml is ALREADY at 0.1.358. Git history: 0.1.352 -> (pre-staged 0.1.354 @951d408) -> 0.1.356 -> 0.1.358.\n\n3) Deploying 0.1.354 would be a DOWNGRADE (0.1.358 -> 0.1.354) with ~30s Recreate downtime. Live pod spaxel-d76b49496-7l4mj is running 0.1.358, healthy: healthz HTTP 200, uptime ~7.9d, db ok. The stated goal (4MB firmware in the running mothership) is already satisfied by the newer 0.1.358.\n\nPer bead rules, not closing. Recommend: close as obsolete, or re-target to the current VERSION (0.1.357) if a redeploy is actually wanted. No changes made to spaxel or declarative-config.","created_at":"2026-07-02T19:03:12.407349955Z"},{"id":17,"issue_id":"bf-4eal","author":"cli","text":"OBSOLETE — do NOT deploy 0.1.354 (would be a regression). Verified 2026-07-02: production ardenone-cluster is ALREADY running ronaldraygun/spaxel:0.1.358 (1/1 Running, 26d uptime), which is 4 patch versions AHEAD of the task target. The task's stated goal (ship the 4MB firmware / fix the 16MB crash loop) is already met: that fix is commit 301a588, which was VERSION 0.1.354 and is included in the deployed 0.1.358 and all versions since. Additional blockers: (1) prerequisite CI build spaxel-build-manual-bj7l2 does not exist on iad-ci; (2) the bead's own Rules forbid working outside /home/coding/spaxel/ but the task requires editing ~/declarative-config; (3) repo HEAD is 0.1.357 and production 0.1.358 — 0.1.354 was only a transient retry version. Recommend cancelling/re-scoping this bead rather than retrying. No code changes made.","created_at":"2026-07-02T19:25:11.305734462Z"},{"id":18,"issue_id":"bf-4eal","author":"cli","text":"NOT EXECUTED — stale/obsolete, intentionally skipped to avoid a harmful production downgrade. Verified at close time: Live ardenone-cluster Deployment ALREADY running docker.io/ronaldraygun/spaxel:0.1.358 (confirmed via Deployment image AND mothership /healthz: status=ok version=0.1.358 db=ok uptime~686000s). 0.1.358 is healthy (~8d uptime, no load shedding) — no reason to roll back. Target 0.1.354 is OLDER than running 0.1.358; deploying it = downgrade with ~30s downtime (Recreate) losing fixes in 0.1.355-0.1.358 (firmware 16MB crash-loop 301a588, dashboard /fleet 500 9ac200b, provision re-broadcast 2483d36). Precondition CI run spaxel-build-manual-bj7l2 does not exist in iad-ci argo-workflows. spaxel repo VERSION=0.1.357; bead premise (0.1.352 to 0.1.354) is outdated. Bead goal (4MB firmware in mothership for ESP32 re-flash) ALREADY satisfied — 0.1.358 is newer than the firmware-fix version. No action taken; declarative-config untouched, nothing pushed. If a newer-than-0.1.358 deploy is desired, create a fresh bead.","created_at":"2026-07-02T19:36:08.475523656Z"}]} {"id":"bf-4gbf","title":"Re-flash ESP32 via Web Serial after 4MB firmware is deployed","description":"Prerequisites: bf-4eal (deployment to 0.1.354) is complete.\n\nSteps:\n1. Open Chrome or Edge (Web Serial requires Chromium-based browser)\n2. Navigate to spaxel.ardenone.com (auth required — use existing credentials)\n3. Go to the provision / onboard page\n4. Connect the ESP32-S3 dev board via USB-C\n5. Click Flash/Provision — esptool.js will download firmware from mothership\n and write it to the device\n6. Monitor the serial output: should boot, NOT crash with flash size mismatch\n7. Expected: captive portal or WiFi connection attempt (node is unprovisioned)\n\nIf the device still shows the 16MB crash, check /api/firmware/manifest returns\na firmware binary whose image header byte[3] bits 4-7 = 0x4 (4MB), not 0x6 (16MB).\n\nThis is the validation step that confirms the 4MB fix works end-to-end.","design":"","acceptance_criteria":"","notes":"","status":"pending","priority":0,"issue_type":"task","assignee":"","created_at":"2026-06-04T03:15:17.801283106Z","updated_at":"2026-07-02T23:12:44.266969513Z","source_repo":".","compaction_level":0,"comments":[{"id":19,"issue_id":"bf-4gbf","author":"cli","text":"SOFTWARE PRE-CHECK VERIFIED (agent, no hardware): The deployed firmware binary is confirmed 4MB, not 16MB. Pulled ronaldraygun/spaxel:0.1.358 (matches running instance version per /healthz) and extracted /firmware/spaxel-firmware.bin (1.62MB merged binary). APP image header @0x10000 byte[3]=0x2F -> high nibble 0x2 = 4MB (esptool encoding). Bootloader header @0x0 byte[3]=0x2F = 4MB. An old buggy 16MB build would be 0x4F (nibble 0x4). Fix source confirmed in HEAD: commit d837598 (sdkconfig 16MB->4MB, partitions.csv 4MB redesign, onboard.js flashSize 'detect'->'4MB'); Dockerfile builds with idf.py 4MB config + esptool merge_bin --flash_size 4MB. HEADS-UP on this bead's stated nibbles: it says 'bits4-7=0x4(4MB),0x6(16MB)' but that does NOT match esptool's standard mapping (0x2=4MB,0x4=16MB,0x6=64MB). Don't check for literal 0x4 - the deployed 4MB binary is 0x2. REMAINING (needs human + ESP32-S3 + Chrome/Edge): the actual Web Serial flash + serial boot observation (steps 1-7). Agent cannot perform physical USB flashing, so bead NOT closed.","created_at":"2026-07-02T19:46:10.194617767Z"},{"id":20,"issue_id":"bf-4gbf","author":"cli","text":"RE-VERIFIED (independent, this dispatch 2026-07-02) — leaving OPEN.\n\n(1) DEPLOYED FIRMWARE BYTE-VERIFIED 4MB (independent of prior comment #19). Pulled ronaldraygun/spaxel:0.1.358 (exact version running per /healthz: status=ok, uptime~686819s/~7.95d, db=ok, shedding_level=0). Extracted /firmware/spaxel-firmware.bin (1,696,480 B / ~1.62MB, built 2026-06-27). ESP image headers:\n - Bootloader @0x0: E9 04 02 2F -> magic 0xE9 OK; byte[3]=0x2F -> flash-size nibble(bits4-7)=0x2 = 4MB\n - App @0x10000: E9 05 02 2F -> byte[3]=0x2F -> 4MB\n A buggy 16MB build would be 0x4F (nibble 0x4). Deployed binary is genuinely 4MB. CAVEAT (same as #19): the bead's stated nibbles '0x4=4MB, 0x6=16MB' do NOT match esptool's standard map (0x2=4MB, 0x4=16MB, 0x6=64MB); under the real map the deployed 4MB binary is nibble 0x2.\n\n(2) SOURCE-OF-TRUTH corroborates: firmware/sdkconfig CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (16MB not set); partitions.csv = 4MB layout (factory 0x1F0000 + ota_0 0x1F0000 + otadata 0x3F0000); fix commit 301a588 in HEAD; deployed 0.1.358 newer than fix version 0.1.354.\n\n(3) PHYSICAL STEP STILL UNREACHABLE (re-confirmed, permanent): no /dev/ttyUSB* or /dev/ttyACM*; no serial/ESP device in lsusb; esptool not installed; no Chromium; and Web Serial is inherently a human-granted interactive browser permission over a physically-USB-connected device. No agent environment can do steps 1-7.\n\nDECISION: OPEN. All agent-verifiable criteria (the 4MB-header precondition) are satisfied and byte-confirmed, but the bead's actual purpose -- end-to-end validation by physically flashing an ESP32-S3 and observing boot -- has NOT been done by any agent or human. bf-4gbf gates the physical sub-chain (next: bf-2po1 provision the node, also hardware-only); closing on a software-only check would falsely signal 'device flashed & ready' and unblock hardware-dependent beads for a device that does not exist here. Needs a human + USB-C + ESP32-S3 + Chrome/Edge. No code changes; fix already deployed in 0.1.358.","created_at":"2026-07-02T19:54:37.887118038Z"},{"id":21,"issue_id":"bf-4gbf","author":"cli","text":"RE-VERIFIED (3rd dispatch, 2026-07-02) — leaving OPEN. Software precondition fully satisfied; physical step permanently unreachable here.\n\n(1) HARDWARE/BROWSER CONSTRAINT (re-confirmed): no /dev/ttyUSB* or /dev/ttyACM*; no serial/ESP device (lsusb none); esptool/esptool.py not installed; no Chromium/Chrome/Edge on PATH. Web Serial is an interactive, human-granted browser permission over a physically USB-connected device — no agent shell can perform steps 1-7.\n\n(2) NO DEVICE ATTACHED: live https://spaxel.ardenone.com/healthz = {status:ok, version:0.1.358, uptime_s:691176 (~8d), nodes_online:0, db:ok, shedding_level:0}. nodes_online:0 means no ESP32 is connected to the mothership — nothing to flash/observe.\n\n(3) ENTIRE 4MB CHAIN VERIFIED (independent of prior comments): firmware/sdkconfig has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (16MB not set); partitions.csv is the 4MB layout (factory 0x1F0000 + ota_0 0x1F0000 + otadata 0x3F0000); deployed 0.1.358 firmware binary was byte-verified 4MB by prior dispatches (bootloader+app header byte[3]=0x2F -> nibble 0x2 = 4MB per esptool map; a 16MB build would be 0x4F); AND the dashboard Web Serial write path now hard-codes 4MB — dashboard/js/onboard.js:679-692 loader.writeFlash({... flashSize:'4MB', flashMode:'dio', flashFreq:'80m' ...}). The /api/firmware/manifest handler (cmd/mothership/main.go:4231) serves the expected esp-web-tools manifest (behind Google OAuth, matching 'auth required').\n\n(4) BEAD NIBBLE NOTE (carried forward): the task's stated mapping 'byte[3] bits4-7 = 0x4(4MB)/0x6(16MB)' does NOT match esptool's standard map (0x2=4MB, 0x4=16MB, 0x6=64MB). Don't check for literal 0x4 — the real 4MB value is nibble 0x2.\n\nDECISION: OPEN. This is a hardware-validation bead; closing on software-only evidence would falsely signal 'device flashed & ready' and unblock bf-2po1 (node provisioning, also hardware-only) for a device that is not present. Needs a human + USB-C ESP32-S3 + Chrome/Edge. No code changes this session; 4MB fix already shipped in 0.1.358.","created_at":"2026-07-02T21:04:18.395853935Z"},{"id":22,"issue_id":"bf-4gbf","author":"cli","text":"Autonomous agent (bf-4gbf auto-dispatch) could NOT complete this bead — it is a physical hardware validation requiring a human.\n\nWhat this step requires (none achievable by a remote software agent in a terminal):\n- Chrome/Edge Web Serial session (needs a local browser + locally-USB-connected ESP32-S3)\n- Physical ESP32-S3 dev board connected via USB-C\n- Human to click Flash/Provision and read the device serial console\nConfirmed NOT available on the agent host: no /dev/ttyUSB* or /dev/ttyACM* (no board attached), no ESP-IDF toolchain (idf.py / $IDF_PATH absent), and the live mothership's /api/firmware/manifest + /firmware/ are behind the Google OAuth proxy (307), so the deployed binary header can't be inspected remotely either.\n\nSoftware-side prerequisite VERIFIED (the 4MB fix is in place end-to-end in source + deployed image):\n- firmware/sdkconfig: CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y, FLASHSIZE='4MB' (was 16MB before)\n- firmware/sdkconfig.defaults: CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (comment: 'fits in 4MB flash')\n- firmware/partitions.csv fits in 4MB (factory 0x10000 1.9MB + ota_0 0x200000 1.9MB + otadata 0x3F0000)\n- Dockerfile busts Kaniko cache + forces sdkconfig regen (commit 301a588 'fix 16MB crash loop'), so deployed 0.1.357 was rebuilt from the corrected 4MB config\n- Live https://spaxel.ardenone.com/healthz returns HTTP 200\n- NOTE: stale local firmware/build/app-flash_args still says '--flash_size 16MB' but that is a gitignored pre-fix build artifact (Apr 14, pre-301a588), NOT authoritative for what CI deployed.\n\nNo code changes were needed — the fix already shipped via prerequisite bead bf-4eal. Bead left OPEN for a human with hardware to perform the actual Web Serial flash + serial-boot observation. Per dispatch rules, an un-completable bead is not closed.","created_at":"2026-07-02T21:24:09.647935343Z"},{"id":23,"issue_id":"bf-4gbf","author":"cli","text":"Agent pass (claude-code-glm-5-alpha, 2026-07-02): cannot auto-complete — this is a physical hardware validation that needs a human to connect the ESP32-S3 dev board over USB-C and click through the Web Serial / esptool.js provisioning flow in Chrome/Edge at spaxel.ardenone.com, then observe live serial output. Leaving IN_PROGRESS for a human with hardware. Prerequisites verified at source: (1) bf-4eal (deploy 0.1.354) is CLOSED; current VERSION is 0.1.357. (2) 4MB flash fix confirmed in firmware sdkconfig.defaults (CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y / ='4MB') and committed sdkconfig, plus partitions.csv is laid out for 4MB (factory 0x10000+0x1F0000, ota_0 0x200000+0x1F0000, otadata 0x3F0000). Fix commit: 301a588 'fix(firmware): bust Kaniko cache + force sdkconfig regen to fix 16MB crash loop'. No local spaxel.bin built (app binary is built in CI/Kaniko), so byte[3] header nibble of the deployed image can't be inspected from this checkout — confirm at runtime via /api/firmware manifest when flashing. Expected good result on re-flash: node boots cleanly (no 'flash size mismatch' / SPI-size crash) and either shows the captive portal or attempts WiFi as an unprovisioned node.","created_at":"2026-07-02T21:52:41.276935808Z"},{"id":24,"issue_id":"bf-4gbf","author":"cli","text":"Agent verification (bf-4gbf, 2026-07-02): Cannot complete the physical flash step — this is a human-in-the-loop hardware validation.\n\nConfirmed the software/firmware chain is correct end-to-end:\n- 4MB fix at source: firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y / =\"4MB\"\n- Prereq bf-4eal (deploy 0.1.354) is CLOSED; running mothership is now at 0.1.358\n- Deployed mothership healthy: https://spaxel.ardenone.com/healthz -> status:ok, version 0.1.358, db ok\n- /api/firmware and /api/firmware/manifest are behind Google OAuth (can't introspect served binary from a CLI without browser creds)\n\nCannot be done by a coding agent (requires all four):\n- A physical ESP32-S3 dev board (none available)\n- USB-C physical connection\n- Interactive Chromium browser to drive Web Serial (needs user gesture + port picker; no headless path)\n- spaxel.ardenone.com Google OAuth session\n\nLeaving bead OPEN per 'do not close if you cannot complete' rule. Needs a human with the board to: open Chrome/Edge -> spaxel.ardenone.com -> onboard -> connect ESP32-S3 via USB-C -> Flash/Provision -> confirm boot (no flash-size-mismatch crash) -> expect captive portal / WiFi connection attempt.","created_at":"2026-07-02T22:09:20.701829249Z"},{"id":25,"issue_id":"bf-4gbf","author":"cli","text":"Software-side validation (agent, 2026-07-02T22:14:07Z):\n\nCONFIRMED FIX IS DEPLOYED:\n- Source: firmware/sdkconfig.defaults = CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y; generated firmware/sdkconfig also 4MB; partitions.csv fits 4MB.\n- Cache-bust in place (commit 301a588): Kaniko FIRMWARE_CACHE_BUST ARG + 'rm -f sdkconfig sdkconfig.old' so CI regenerates from the 4MB defaults (the root cause of the original 16MB loop).\n- Deployed mothership at spaxel.ardenone.com is healthy, version 0.1.358 (uptime ~8d) -> well past the 0.1.354 fix, so the 4MB image is live.\n- Note: stale local firmware/build/bootloader.bin (dated 2026-04-14, pre-fix) still shows a 16MB header -- it predates the fix and is NOT what shipped.\n\nBLOCKED ON PHYSICAL HARDWARE (needs a human):\nThe actual acceptance of this bead -- open a Chromium browser, OAuth into spaxel.ardenone.com, connect the ESP32-S3 via USB-C, click Flash/Provision (esptool.js), and monitor serial output for a clean boot (no 'Detected size smaller than image header' crash) followed by captive portal / WiFi attempt -- requires a physical board + browser that this coding agent has no access to.\n- I also cannot fetch the deployed /firmware/spaxel-firmware.bin to read byte[3] header bits: the entire site (not just /api/*) is behind the Google OAuth proxy (every non-/healthz path returns 307 to accounts.google.com), and I have no user session.\n- Per esptool's image-header table, byte[3] bits 4-7 = 0x2 means 4MB and 0x4 means 16MB (the task's '0x4=4MB, 0x6=16MB' looks off vs esptool encoding -- whoever does the flash should compare against 0x2 for 4MB).\n\nLeaving OPEN -- the end-to-end physical flash + serial validation remains for a human with the ESP32 board.","created_at":"2026-07-02T22:14:07.684170447Z"},{"id":26,"issue_id":"bf-4gbf","author":"cli","text":"Agent verification (cannot close — needs physical hardware):\n\nBLOCKED ON: human + physical ESP32-S3 board. This Hetzner box has NO USB-serial device (no /dev/ttyUSB* or /dev/ttyACM*), no USB-serial/ESP-JTAG bridge in lsusb, and no esptool on PATH. Web Serial also requires a GUI Chromium browser, which doesn't exist here. An agent cannot perform the physical USB-C flash or observe real boot/serial output.\n\nPREREQUISITES CONFIRMED READY (everything agent/CI-doable is done):\n- bf-4eal (deployment): CLOSED. Live mothership healthy — https://spaxel.ardenone.com/healthz => status ok, version 0.1.358, nodes_online 0.\n- Source 4MB fix in place: firmware/sdkconfig + sdkconfig.defaults both set CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y; partitions.csv is a 4MB layout (factory 0x10000 size 0x1F0000, ota_0 0x200000 size 0x1F0000, otadata 0x3F0000). Commit 301a588 'fix 16MB crash loop'.\n\nACTION FOR A HUMAN: connect an ESP32-S3 via USB-C, open Chrome/Edge, go to spaxel.ardenone.com provision page, Flash/Provision, confirm it boots (no 16MB flash-size-mismatch crash) into captive portal / WiFi-connect attempt. Then close this bead.","created_at":"2026-07-02T22:18:11.875420762Z"},{"id":27,"issue_id":"bf-4gbf","author":"cli","text":"Software/deployment chain verified — only the physical flash remains (requires a human + board):\n\n[PRE-FLIGHT CHECKS — all green]\n- 4MB flash fix at source: firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (+ CONFIG_ESPTOOLPY_FLASHSIZE='4MB'); partitions.csv sized for 4MB (factory 0x1F0000 + ota_0 0x1F0000 + otadata).\n- Prerequisite bf-4eal (deploy 0.1.354) is closed; repo VERSION=0.1.357.\n- Deployed mothership is live+healthy: GET https://spaxel.ardenone.com/healthz -> {status:ok, version:0.1.358, db:ok}. The 4MB firmware is in the running mothership, so a Web Serial flash will pull the correct binary.\n\n[BLOCKER — not agent-completable]\nThis bead's actual validation (open Chrome/Edge Web Serial, plug ESP32-S3 via USB-C, click Flash/Provision, watch serial output for boot-vs-flash-size-mismatch) requires physical hardware + a human. This server has no ESP32 attached and no browser/Web Serial. Leaving OPEN per the bead retry contract; needs a human with the dev board to perform the flash and confirm boot.\n\n[Encoding note for whoever flashes] The diagnostic hint ('byte[3] bits 4-7 = 0x4 = 4MB') does not match esptool's standard image-header encoding: high nibble 0x2 = 4MB, 0x4 = 16MB. Since FLASHSIZE_4MB is set at source, a freshly built/served binary should read high nibble 0x2 (e.g. byte[3]=0x2x). Treat a 0x4 high nibble as still-16MB, not 4MB.","created_at":"2026-07-02T22:36:42.216785943Z"},{"id":28,"issue_id":"bf-4gbf","author":"cli","text":"Autonomous-agent re-attempt (2026-07-02). Cannot complete this step — it requires a physical ESP32-S3 + USB-C and a Chromium browser driving the Web Serial API, neither of which is available on this Hetzner box (no /dev/ttyUSB* or /dev/ttyACM*, no USB-serial bridge in lsusb, no chromium/chrome/edge in PATH). Per the task rule I am leaving this open for a human (jedarden) rather than closing it.","created_at":"2026-07-02T22:39:07.125408227Z"},{"id":29,"issue_id":"bf-4gbf","author":"cli","text":"Source-level verification (2026-07-02): 4MB fix is confirmed at source. firmware/sdkconfig:482 = CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y; firmware/sdkconfig.defaults:10-11 = 4MB; firmware/partitions.csv redesigned for 4MB (factory 0x10000 size 0x1F0000 + single ota_0 0x200000, no 16MB layout; old 16MB config kept as firmware/sdkconfig.old for reference); dashboard/js/onboard.js:681 passes flashSize:'4MB' to esptool. Deploy prerequisite bf-4eal is satisfied: VERSION=0.1.357, well past the 0.1.354 this task references. Do NOT rebuild firmware locally to proxy-verify — it pulls a ~2GB ESP-IDF image and a prior run timed out (~7min) with no gain; source evidence is sufficient.","created_at":"2026-07-02T22:39:22.316277106Z"},{"id":30,"issue_id":"bf-4gbf","author":"cli","text":"IMPORTANT for whoever does the physical flash: the task's stated header test is INVERTED vs the real esptool spec. The task says 'byte[3] bits 4-7 = 0x4 (4MB), not 0x6 (16MB)'. Correct esptool encoding: 4MB -> image-header byte[3] high-nibble 0x2 (full byte 0x20); 16MB -> high-nibble 0x4 (full byte 0x40). So a correctly-built 4MB image is EXPECTED to read 0x2 in that nibble, not 0x4. Verify with: esptool.py --port /dev/ttyUSB0 image_info spaxel-*.bin and check 'Flash size: 4MB', or hexdump byte[3] of the .bin and confirm high nibble is 0x2. If the device still shows a flash-size mismatch crash after flashing, that is the real thing to investigate.","created_at":"2026-07-02T22:39:22.337314455Z"},{"id":31,"issue_id":"bf-4gbf","author":"cli","text":"BLOCKED - requires physical hardware + human, not auto-completable on this server (re-verified 2026-07-02):\n\n- No ESP32 attached: /dev/ttyUSB* and /dev/ttyACM* absent; no USB-serial bridge (CP210x/CH340/FTDI/Espressif) in lsusb.\n- No Chromium/Edge in PATH; Web Serial API cannot be driven headless from the server.\n- spaxel.ardenone.com dashboard is OAuth-gated (307 -> Google accounts), not headless-accessible.\n\n4MB fix IS confirmed correct at source: firmware/sdkconfig AND sdkconfig.defaults have CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (16MB unset); dashboard/js/onboard.js:681 passes flashSize:'4MB'. Deploy is past the 0.1.354 the task references (repo at 0.1.357), so the bf-4eal prereq is satisfied.\n\nHEADER-CHECK CORRECTION (task spec is inverted): per vendored dashboard/js/esptool-bundle.js flash-size table (1MB:0,2MB:16,4MB:32,8MB:48,16MB:64), a valid 4MB image has header byte[3] high-nibble 0x2 (byte 0x20); 16MB is high-nibble 0x4 (byte 0x40). Expect 0x2 for 4MB, NOT 0x4 - the task's 4MB=0x4 is actually the 16MB marker.\n\nLeft open for a human (jedarden) with a Chromium browser + ESP32-S3 via USB-C. Do NOT build firmware locally to proxy-verify: it pulls a ~2GB ESP-IDF Docker image and times out (~7min, prior run killed). Source evidence is sufficient; the physical boot/captive-portal observation is the only remaining step.","created_at":"2026-07-02T22:45:19.450038533Z"},{"id":32,"issue_id":"bf-4gbf","author":"cli","text":"Verification pass (autonomous agent, 2026-07-02): 4MB fix is correct at source level and the deploy prerequisite bf-4eal is closed (4MB image is in the running mothership, past the 0.1.354 the task references). Confirmed: firmware/sdkconfig has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (lines 482/488); firmware/partitions.csv redesigned for 4MB (factory 0x1F0000 + single ota_0 at 0x200000, no 16MB layout); dashboard/js/onboard.js:681 passes flashSize:'4MB' to esptool; the vendored dashboard/js/esptool-bundle.js FLASH_SIZES map sends '4MB'->32 (0x20). Fix landed in commit d837598. PHYSICAL STEP NOT RUN: this Hetzner box has no ESP32-S3 attached (/dev/ttyUSB* and /dev/ttyACM* absent; no CP210x/CH340/FTDI/Espressif USB-serial bridge in lsusb) and no Chromium/Edge in PATH for Web Serial, and spaxel.ardenone.com is OAuth-gated. Needs a human with a Chromium browser + a physical ESP32-S3 board over USB-C. HEAD NOTE for whoever does the physical check: the bead's stated header test is inverted vs the real esptool spec. esptool encodes 4MB as image-header byte[3] high-nibble 0x2 (full byte 0x20) and 16MB as 0x4 (0x40) -- i.e. 0x4 = 16MB, NOT 4MB. So a correct 4MB image reads 0x2 in byte[3], not 0x4. Per task rule (cannot complete -> do not close), leaving OPEN.","created_at":"2026-07-02T22:48:00.612275608Z"},{"id":33,"issue_id":"bf-4gbf","author":"cli","text":"Verification check (autonomous agent on Hetzner box) — NOT closing; this is a physical-hardware validation step that can't be done here.\n\nState confirmed:\n- 4MB fix already correct at source: firmware/sdkconfig CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y (+ FLASHSIZE=\"4MB\"); firmware/partitions.csv redesigned for 4MB (factory 0x1F0000 + single ota_0 0x1F0000, no 16MB layout); dashboard/js/onboard.js:681 passes flashSize:'4MB'.\n- Prerequisite bf-4eal closed; deployed instance live at spaxel.ardenone.com — /healthz returns 200, version 0.1.358 (past the 0.1.354 the task references). So the 4MB firmware is deployed.\n\nWhy this box can't do the physical flash step:\n- No ESP32 attached (no /dev/ttyUSB* or /dev/ttyACM*; no CP210x/CH340/FTDI/Espressif USB-serial bridge in lsusb).\n- No Chromium-based browser in PATH — Web Serial API needs Chrome/Edge.\n- /api/firmware/manifest is OAuth-gated (307 -> accounts.google.com), unreachable headlessly.\n\nHeader-byte note for whoever does the physical check: the task's stated test (byte[3] high-nibble = 0x4 for 4MB, 0x6 for 16MB) is inverted vs the esptool spec. Correct: 4MB -> high-nibble 0x2 (byte 0x20); 16MB -> 0x4 (0x40). So a good 4MB image reads 0x2, not 0x4.\n\nLeaving open for a human with a Chromium browser + physical ESP32-S3 via USB-C.","created_at":"2026-07-02T22:51:34.310104175Z"},{"id":34,"issue_id":"bf-4gbf","author":"cli","text":"Re-attempted by agent (claude-code-glm-5-alpha), still blocked: requires a human + physical ESP32-S3. No ESP32 attached (no /dev/ttyUSB*/ttyACM*, no USB-serial bridge in lsusb), no Chromium browser in PATH, and spaxel.ardenone.com is OAuth-gated — Web Serial can't be driven from this server. Source-level 4MB fix is confirmed in place: sdkconfig FLASHSIZE_4MB=y, partitions.csv redesigned for 4MB (factory 0x1F0000 + single ota_0), onboard.js:681 passes flashSize:'4MB', fix commit d837598. Prereq bf-4eal closed. NOTE for the physical check: the task's header test is inverted vs esptool spec — a 4MB image header byte[3] high-nibble reads 0x2 (byte 0x20), NOT 0x4; 16MB reads 0x4. Leaving open per task rules. See memory: hw-validation-beads-not-agent-completable.","created_at":"2026-07-02T22:57:23.907813868Z"},{"id":35,"issue_id":"bf-4gbf","author":"cli","text":"BLOCKER — requires a human + physical ESP32-S3 board; this server-based coding agent cannot perform it. Not closing per task rule.\n\nSoftware preconditions VERIFIED (no need to re-check):\n- 4MB fix at source: firmware/sdkconfig.defaults has CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y / '4MB'; firmware/partitions.csv is a 4MB layout (factory ~1.9MB + ota_0 ~1.9MB + otadata). The 16MB flash-size-mismatch crash root cause is resolved in the build.\n- Prereq bf-4eal (deploy 0.1.354) is CLOSED. Deployed mothership healthy at v0.1.358: https://spaxel.ardenone.com/healthz -> 200 {status:ok, version:0.1.358}. 4MB firmware is live.\n- Firmware serving endpoints present in code: GET /api/firmware (manifest) + GET /firmware/ (binary), no-auth at app layer.\n\nCould NOT verify headlessly: /api/firmware is fronted by Google OAuth (auth.ardenone.com) at the reverse proxy, so the firmware binary image-header byte[3] flash-size check cannot run from this server without a browser session. A human session passes through.\n\nStill needed (HUMAN ONLY):\n1. Chrome/Edge -> spaxel.ardenone.com -> provision/onboard page.\n2. Plug ESP32-S3 dev board via USB-C.\n3. Click Flash/Provision (esptool.js downloads 4MB firmware + writes).\n4. Watch serial: must boot cleanly, NOT crash with flash-size mismatch; expect captive portal / WiFi connection attempt (node unprovisioned).","created_at":"2026-07-02T23:12:34.936822318Z"}]} +{"id":"bf-4iewr","title":"Resolve the REJECT/token path so tokenless sim nodes are accepted","description":"Second step of the chain (depends on bf-3ud7e build). With the binary built, ensure the hardware-free mothership (bf-3zll) does NOT reject tokenless spaxel-sim nodes.\n\nSource of truth: mothership/internal/ingestion/server.go ~lines 506-525. If a token validator is configured (SetTokenValidator) AND the migration window is closed (SetMigrationDeadline zero or past), every tokenless sim node is rejected via sendReject with reason 'invalid_token' and disconnected — this is the 'REJECT'/401/403 failure mode.\n\n## Scope\n- Determine whether the local/test mothership wires a token validator and what the migration deadline is.\n- If a validator is active for the sim run, disable it (unset validator), open the migration window, or have the sim supply valid tokens — whichever is correct for the hardware-free build.\n- Verify by running the sim (1-2 nodes) and grepping mothership stderr.\n\n## Acceptance Criteria\n- [ ] Root cause of any REJECT identified (validator configured? migration window closed?).\n- [ ] Mothership configured so tokenless sim nodes connect without rejection.\n- [ ] When the sim connects, mothership stderr shows zero occurrences of 'rejected' / 'REJECT' / 'invalid_token' / HTTP 401 / 403.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T05:19:51.181529765Z","updated_at":"2026-07-07T09:52:00.017666181Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-4iewr","depends_on_id":"bf-3ud7e","type":"blocks","created_at":"2026-07-07T05:20:08.389583352Z","created_by":"cli","thread_id":""},{"issue_id":"bf-4iewr","depends_on_id":"bf-601bz","type":"blocks","created_at":"2026-07-07T09:50:16.400577464Z","created_by":"cli","thread_id":""}],"comments":[{"id":110,"issue_id":"bf-4iewr","author":"cli","text":"[bf-34lwt root-cause] TOKENLESS-SIM REJECT PATH — definitive finding (citations are full repo-relative paths; the task body's cmd/* maps to mothership/cmd/*).\n\nTL;DR: current 'no reject' is a 24h MIGRATION-WINDOW MASK, not a fix. The validator is wired unconditionally, but the sim presents its token only as an HTTP header the mothership never reads, so hello.Token is always empty and tokenOK is always false. The only thing keeping sim nodes connected is the default 24h window; the moment it closes, every sim node is sendReject-ed (invalid_token) and disconnected.\n\n1) Validator wired UNCONDITIONALLY: mothership/cmd/mothership/main.go:4494 ingestSrv.SetTokenValidator(provSrv.ValidateToken) — no config/build gate. Only the *deadline* is gated (main.go:4495 if cfg.MigrationWindowHours > 0). So s.tokenValidator is always non-nil. => 'validator configured?' is always YES.\n\n2) Validator reads hello.Token only: mothership/internal/ingestion/server.go:513 tokenOK := hello.Token != \"\" && validator(hello.MAC, hello.Token). validator = ValidateToken(mac,token) (SetTokenValidator at server.go:321-327). hello.Token is JSON 'token,omitempty' (message.go:22).\n\n3) Sim sends token ONLY as X-Spaxel-Token HTTP header, NOT in hello body: mothership/cmd/sim/main.go:633 + mothership/cmd/sim/scenario.go:318 headers.Set(\"X-Spaxel-Token\", token). Hello body map (main.go:652-665 / scenario.go:327-340) has type/mac/firmware_version/capabilities/chip/flash_mb/uptime_ms/wifi_rssi/ip/pos_* — NO 'token' key => hello.Token deserializes to \"\".\n\n4) Mothership NEVER reads that header => token-supply path is DEAD. grep 'X-Spaxel-Token' mothership/ returns ONLY sim files (cmd/sim/*); zero hits in cmd/mothership or internal. The WS upgrade handler reads nothing off the request: server.go:469 s.upgrader.Upgrade(w, r, nil) inside HandleNodeWS (server.go:455) — no r.Header.Get. (All other Header.Get/Set under internal/ are outbound webhook/notify or tests.) Consequence: hello.Token == \"\" for every sim node => tokenOK always false, regardless of the provisioned token. The parent's 'have the sim supply valid tokens' option is currently NON-FUNCTIONAL.\n\n5) Only the migration window accepts sim nodes: server.go:515 if !deadline.IsZero() && time.Now().Before(deadline) => nc.Unpaired = true. MigrationWindowHours DEFAULTS to 24 (config.go:139). Fresh boot sets deadline=now+24h (main.go:4496) => window open => sim nodes accepted as Unpaired, no reject. Matches PROGRESS.md bf-3hji.\n\n6) REJECT fires ONLY when window closed. Two cases: (a) SPAXEL_MIGRATION_WINDOW_HOURS=0 => main.go:4495 false => SetMigrationDeadline never called => deadline IsZero() => server.go:515 !IsZero() false => reject branch; (b) uptime > 24h => Now().Before(deadline) false => reject branch. Either way hello.Token==\"\" => server.go:521 'missing token' => server.go:525 sendReject(conn,\"invalid_token\") => server.go:526 conn.Close() (sendReject at server.go:841-846).\n\nEXPLICIT STATEMENTS (acceptance criteria):\n- Current 'no reject' is a 24h-window MASK, not a real fix: it vanishes under SPAXEL_MIGRATION_WINDOW_HOURS=0 or after 24h uptime.\n- The token-supply path is currently DEAD: header ignored, hello.Token empty, tokenOK always false.\n\nFull write-up + fix options (A keep-mask / B make-token-real via header-read-in-HandleNodeWS-or-body-token / C gate-the-validator): docs/notes/token-reject-root-cause.md (committed with bf-34lwt). The remaining bf-4iewr children should pick exactly one of A/B/C; the two invariants for the final state: 'no reject' must not depend on the 24h mask, and the chosen token contract must be honored end-to-end by both sim and mothership.","created_at":"2026-07-07T09:59:00.629760880Z"},{"id":111,"issue_id":"bf-4iewr","author":"cli","text":"ROOT CAUSE (bf-34lwt, first link of this split) — confirmed against current source. Full write-up: docs/notes/token-reject-root-cause.md (commit 5c6a692).\n\nAll module paths are under mothership/; the task body dropped that prefix.\n\n1. VALIDATOR WIRED UNCONDITIONALLY: mothership/cmd/mothership/main.go:4494 ingestSrv.SetTokenValidator(provSrv.ValidateToken) — no config/build gate, so 'validator configured?' is always YES. Only the deadline is gated (main.go:4495 if cfg.MigrationWindowHours > 0).\n\n2. VALIDATOR READS hello.Token ONLY: mothership/internal/ingestion/server.go:513 tokenOK := hello.Token != \"\" && validator(hello.MAC, hello.Token). ValidateToken (mothership/internal/provisioning/server.go:135) does an HMAC constant-time compare on the token STRING — it never touches any HTTP header. hello.Token maps to JSON field token,omitempty (mothership/internal/ingestion/message.go:22).\n\n3. SIM SENDS TOKEN ONLY AS X-Spaxel-Token HEADER, never in the hello body. Both sim copies: mothership/cmd/sim/main.go:633 and mothership/cmd/sim/scenario.go:318 set headers.Set(\"X-Spaxel-Token\", token) on the WS dial; standalone module cmd/sim/main.go:313 does the same. None of the hello bodies include a 'token' key (mothership/cmd/sim/main.go:652-665; cmd/sim/main.go:348-359; scenario.go:331-341), so hello.Token deserializes to \"\" from either binary.\n\n4. MOTHERSHIP NEVER READS THE HEADER -> TOKEN-SUPPLY PATH IS DEAD. grep 'X-Spaxel-Token' over mothership/ returns only sim files; the WS upgrade handler upgrades UNCONDITIONALLY at server.go:469 s.upgrader.Upgrade(w, r, nil) with no r.Header.Get(...) and no HTTP-401 path (HandleNodeWS, server.go:455). So hello.Token==\"\" -> server.go:513 short-circuits to tokenOK=false regardless of the provisioned token. The 'have the sim supply valid tokens' option in THIS bead is therefore currently NON-FUNCTIONAL — the token goes to a header nobody reads.\n\n5. THE ONLY THING ACCEPTING SIM NODES IS THE MIGRATION WINDOW. server.go:514-519: if !tokenOK, accept as Unpaired only when !deadline.IsZero() && Now().Before(deadline). MigrationWindowHours DEFAULTS TO 24 (mothership/internal/config/config.go:139), so a fresh boot sets deadline=now+24h (main.go:4496) -> every tokenless sim node accepted as Unpaired, no reject (matches PROGRESS.md bf-3hji). Corroborated by the e2e harness itself (mothership/tests/e2e/e2e_test.go:86-94), which pins SPAXEL_MIGRATION_WINDOW_HOURS=24 and documents exactly this.\n\n6. REJECT FIRES ONLY WHEN THE WINDOW IS CLOSED (server.go:519-528): (a) SPAXEL_MIGRATION_WINDOW_HOURS=0 -> SetMigrationDeadline never called (main.go:4495 guard) -> deadline.IsZero() true -> reject; (b) uptime > 24h -> Now().Before(deadline) false -> reject. In both, hello.Token==\"\" logs 'Node rejected: missing token' (server.go:520-521), sendReject(conn,\"invalid_token\") (server.go:525, def 841-846), conn.Close() (526).\n\nBONUS: there is NO HTTP-401 path at all — the upgrade always succeeds (101). So AS-7 quality gate #7 ('node without token must get HTTP 401 and be rejected') is currently UNSATISFIABLE under default config; it's part of the same dead-path/accident.\n\nSUMMARY (for this bead's acceptance criteria):\n- 'No reject' is a 24h-window MASK, not a real fix — it vanishes under SPAXEL_MIGRATION_WINDOW_HOURS=0 or after 24h uptime.\n- The token-supply path is currently DEAD: header ignored (server.go:469), hello.Token empty (sim hello bodies), so tokenOK is always false (server.go:513).\n- Three independent fixes for the sibling children: (A) keep the mask but make it deliberate for sim/test; (B) make the token path real (read X-Spaxel-Token in HandleNodeWS, or have the sim put token in the hello body); (C) gate the validator off for the hardware-free build. See the doc for the trade-offs.","created_at":"2026-07-07T10:20:40.536429191Z"},{"id":115,"issue_id":"bf-4iewr","author":"cli","text":"## Root cause: tokenless-sim REJECT in the WS hello path (bf-34lwt synthesis)\n\nSynthesis of the three bf-34lwt children (bf-5ig3e, bf-29wyl, bf-2hdbg). All six facts re-confirmed against current source (repo-relative paths):\n\n**1. Validator is wired UNCONDITIONALLY** — `mothership/cmd/mothership/main.go:4494`\n`ingestSrv.SetTokenValidator(provSrv.ValidateToken)` runs at every boot with NO env/build guard, so \"is a validator configured?\" is always YES. (The migration deadline at lines 4495-4498 is wired separately, only when `MigrationWindowHours > 0`.)\n\n**2. Validator reads `hello.Token` ONLY** — `mothership/internal/ingestion/server.go:513`\n`tokenOK := hello.Token != \"\" && validator(hello.MAC, hello.Token)`. Accept/reject hinges entirely on the JSON body's `token` field; no HTTP header participates in the check.\n\n**3. Sim sends its token ONLY as the `X-Spaxel-Token` header** — `cmd/sim/main.go:313`\n`reqHeader.Set(\"X-Spaxel-Token\", token)`. The hello map (`cmd/sim/main.go:348-359`) carries type/mac/firmware_version/capabilities/chip/flash_mb/uptime_ms/pos_{x,y,z} and NO `token` field. `cmd/sim/scenario.go` sets no header or body token either.\n\n**4. Mothership NEVER reads that header** — `grep -rn 'X-Spaxel-Token' mothership/cmd/mothership/ mothership/internal/` returns zero hits; every `Header.Get` under `mothership/internal/` is an outbound notification client (webhook/ntfy/pushover/mqtt), none on the WS node path. Consequence: for sim nodes `hello.Token` is always `\"\"` -> `tokenOK` is always false.\n\n**5. The migration window — not tokens — is the only thing accepting sim nodes** — `mothership/internal/config/config.go:139` (`cfg.MigrationWindowHours = 24`, the default), applied at `mothership/cmd/mothership/main.go:4495-4498` to set a deadline 24h out. The acceptance branch is `mothership/internal/ingestion/server.go:515`: `if !deadline.IsZero() && time.Now().Before(deadline)` -> node admitted and flagged `Unpaired` (matches PROGRESS.md bf-3hji).\n\n**6. REJECT fires ONLY when the window is closed** — `mothership/internal/ingestion/server.go:519-527`, the else branch: logs \"missing token\"/\"invalid token\" -> `sendReject(conn, \"invalid_token\")` -> `conn.Close()`. Two triggers: (a) `SPAXEL_MIGRATION_WINDOW_HOURS=0` -> the `main.go:4495` gate is skipped -> deadline stays zero-value -> `deadline.IsZero()` true -> reject; (b) uptime > 24h -> `time.Now().After(deadline)` -> reject.\n\n## Two required takeaways\n\n- **\"No reject\" is a 24h-window MASK, not a real fix.** A default-boot mothership rejects nothing only because the 24h migration window is open. The instant it closes (`SPAXEL_MIGRATION_WINDOW_HOURS=0`, or any process alive >24h), every tokenless sim node hits the `invalid_token` reject branch. The hardware-free build is green by accident of uptime, not by design.\n\n- **The token-supply path is currently DEAD.** The parent bead's \"supply valid tokens\" option is non-functional today: the sim's token lives only in the unread `X-Spaxel-Token` header, while the validator checks only the always-empty `hello.Token` body field — so even a correctly-provisioned token cannot make `tokenOK` true.\n\n## Precise target for the remaining bf-4iewr children\n\nRepair the dead token-supply path (point 4): either (a) populate `hello.Token` in the sim's hello body, or (b) have the mothership read `X-Spaxel-Token` on the WS upgrade. Opening/lengthening the migration window (points 5-6) only hides the bug and must not be treated as the fix.","created_at":"2026-07-07T11:31:28.801356242Z"}]} {"id":"bf-4ly4","title":"Search JavaScript files for blob-shaped object literals","description":"## Objective\nSearch all JavaScript files for blob-shaped object literals.\n\n## Scope\n- Search only .js and .jsx files\n- Use the pattern defined in the parent bead\n- Identify all locations with blob structure\n\n## Acceptance Criteria\n- [ ] All JavaScript files searched\n- [ ] Blob literal locations identified with file paths and line numbers\n- [ ] Results saved to notes/bf-26ta-javascript-results.md\n- [ ] Each match includes context snippet showing the blob structure","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T06:02:03.135320465Z","updated_at":"2026-07-06T06:18:55.359674524Z","closed_at":"2026-07-06T06:18:55.359674524Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4ly4","depends_on_id":"bf-1rzd","type":"blocks","created_at":"2026-07-06T06:02:07.318031216Z","created_by":"cli","thread_id":""}]} +{"id":"bf-4mle6","title":"Make tokenless-sim acceptance durable for the hardware-free build","description":"Third link of the bf-4iewr split (depends on the token-bridge fix). Today the hardware-free sim is accepted only because MigrationWindowHours defaults to 24h — a value that silently EXPIRES (uptime > 24h) and that any `SPAXEL_MIGRATION_WINDOW_HOURS=0` boot disables. Make the HW-free build's auth policy explicit and durable so the sim never regresses.\n\n## Scope\nDecide and implement the supported policy for the hardware-free build (justify the choice; update PROGRESS.md \"Hardware-Free Simulator -> CSI Streaming\" section):\n- (A) Sim ALWAYS supplies a valid token (now functional via the previous child) -> window-independent acceptance. Verify provisionToken() succeeds against /api/provision and the token validates; OR\n- (B) E2E harness / documented boot sets the migration window EXPLICITLY and asserts it is open (no reliance on the implicit 24h default); OR\n- (C) A build/test gate that leaves the validator unwired for the sim path.\n\nEnsure the e2e harness (`mothership/tests/e2e/e2e_test.go`, TestHarness.Start) and the PROGRESS.md human-run commands reflect the chosen policy.\n\n## Acceptance Criteria\n- [ ] A documented, reproducible auth policy for the HW-free build in PROGRESS.md.\n- [ ] Behavior is IDENTICAL whether SPAXEL_MIGRATION_WINDOW_HOURS is 0, default (24), or large — sim nodes connect and stream in all three (i.e., no implicit dependence on the 24h default).\n- [ ] E2E harness no longer silently relies on the implicit 24h window.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T09:49:42.516790738Z","updated_at":"2026-07-07T09:49:42.516790738Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4mle6","depends_on_id":"bf-1o7qi","type":"blocks","created_at":"2026-07-07T09:50:16.344782807Z","created_by":"cli","thread_id":""}]} {"id":"bf-4oiz","title":"Verify and fix registry bridge position handling","description":"Ensure that positions from the simulator are correctly received and processed by internal/simulator/registry_bridge.go.\n\nScope:\n- Verify that registry_bridge.go receives position data from simulator\n- Check that positions are correctly formatted for registry consumption\n- Fix any missing or incorrect wiring\n\nAcceptance criteria:\n- registry_bridge.go receives position data from simulator\n- Positions are correctly formatted for registry\n- Any missing wiring is added\n- Positions flow from simulator to registry_bridge.go correctly","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T04:56:17.285977814Z","updated_at":"2026-07-06T16:23:13.948046549Z","closed_at":"2026-07-06T16:23:13.948046549Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-4oiz","depends_on_id":"bf-3ifj","type":"blocks","created_at":"2026-07-06T04:56:20.008223894Z","created_by":"cli","thread_id":""},{"issue_id":"bf-4oiz","depends_on_id":"bf-5dpu","type":"blocks","created_at":"2026-07-06T05:24:19.551325547Z","created_by":"cli","thread_id":""},{"issue_id":"bf-4oiz","depends_on_id":"bf-69ym","type":"blocks","created_at":"2026-07-06T13:19:39.307840995Z","created_by":"cli","thread_id":""}],"comments":[{"id":69,"issue_id":"bf-4oiz","author":"cli","text":"Verified the registry-bridge position flow end-to-end for close-out. The compile blocker flagged by bf-4pqj (*fleet.Registry not satisfying simulator.RegistryNodeAdapter) was resolved by bf-5lii's fleetRegistryAdapter + immediate SyncToRegistry on AddNode/UpdateNode; bf-69ym added two real-SQLite-registry e2e tests. Re-verified: go build ./cmd/mothership OK, go vet ./... clean, go test ./... all green (simulator + api fresh). The two virtual-node position mutation sites (simulator.go CreateVirtualNode/UpdateNodePosition) are the only writers and both trigger immediate sync; 30s ticker stays as safety net. Acceptance criteria met. Doc-only close-out commit fd7918f.","created_at":"2026-07-06T16:22:53.997318825Z"}]} {"id":"bf-4pqj","title":"Audit and document current registry_bridge position wiring","description":"Read-only verification of the EXISTING registry_bridge position handling (split from bf-4oiz). Prior notes/bf-4oiz-findings.md claimed wiring was missing — that is now STALE: main.go already creates the VirtualNodeStore + FleetRegistryBridge and runs a 30s periodic SyncToRegistry (cmd/mothership/main.go:4269-4301).\n\nScope:\n- Confirm registry_bridge.go receives position data from the simulator via VirtualNodeStore (SyncToRegistry iterates the store's virtual nodes)\n- Confirm positions are correctly formatted for registry consumption (RegistryNodeAdapter.AddVirtualNode/SetNodePosition take x,y,z float64)\n- Update notes/bf-4oiz-findings.md to reflect the real current data flow (periodic sync IS wired)\n\nAcceptance criteria:\n- registry_bridge.go confirmed to receive position data from simulator (via store) — documented\n- Position formatting confirmed correct, backed by registry_bridge_test.go\n- notes/bf-4oiz-findings.md 'Current Data Flow' section corrected to reflect periodic sync wiring exists\n- No production code changes required (verification only)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T13:18:51.748554168Z","updated_at":"2026-07-06T15:21:01.369382114Z","closed_at":"2026-07-06T15:21:01.369382114Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"comments":[{"id":65,"issue_id":"bf-4pqj","author":"cli","text":"Re-verified (2nd dispatch) — audit deliverable in notes/bf-4oiz-findings.md (commit 1499c34) confirmed accurate against current source:\n\nACCEPTANCE CRITERIA — ALL MET:\n1. registry_bridge.go receives simulator positions via store — SyncToRegistry (registry_bridge.go:~147) calls b.store.ListNodes() -> []*VirtualNodeState{.Position}, then spreads pos.X/pos.Y/pos.Z into adapter.AddVirtualNode/SetNodePosition. Data flow store->bridge->adapter CONFIRMED.\n2. Position formatting correct + backed by tests — RegistryNodeAdapter uses float64 XYZ throughout (registry_bridge.go:113-121); fakeRegistry test double asserts exact round-trip; 'go test ./internal/simulator/' -> ok (9 tests, incl. ExplicitPositionsPreserved, SyncOneNode_ExplicitPreserved, OriginNodesGetSpreadGeometry, Idempotent, MatchesFullSync).\n3. notes/bf-4oiz-findings.md 'Current Data Flow' corrected — stale 'wiring missing' claim replaced with the real periodic-sync wiring (main.go:4195-4226: VirtualNodeStore + FleetRegistryBridge + 30s SyncToRegistry ticker).\n4. No production code changes — none made (verification only).\n\nKNOWN OUT-OF-SCOPE BLOCKER (documented, belongs to bf-5dpu / commit e92d5bc): 'go vet ./...' fails on TWO packages — cmd/mothership (main.go:4217: *fleet.Registry.GetAllNodes returns []fleet.NodeRecord, not []simulator.NodeRecord) and internal/api (simulator_test.go:19: NewSimulatorHandler signature changed to (store, bridge), test not updated). The internal/simulator package itself — the subject of THIS audit — builds and tests green. This bead cannot fix the blocker (no-prod-changes mandate); it is flagged for the wiring owner. Closing because all four acceptance criteria are satisfiable and complete; not closing would loop forever on out-of-scope breakage.","created_at":"2026-07-06T15:19:52.397270670Z"}]} {"id":"bf-4q5w","title":"Wire fusion engine SetNodePosition — nodes never positioned, sim/IO-6 produces 0 blobs","description":"Found during investigation of bf-3v39 (presence-detection verification).\n\nSYMPTOM: E2E tests (tests/e2e TestFullE2EIntegration, TestDetectionEvents) and the IO-6 hard-gate scenario produce ZERO detection events / blobs even with 4 nodes + 2 walkers running spaxel-sim. The fusion loop runs at 10Hz but never emits a peak.\n\nROOT CAUSE: internal/fusion/fusion.go Engine.SetNodePosition(mac,x,y,z) is defined but NEVER CALLED in the non-test wiring (grep shows only the definition + unrelated SetPositionProvider helpers). Nodes default to pos=(0,0,1) in the DB (internal/db/migrations.go:143-145). With all nodes co-located, the Fresnel zone excess path length |P-T|+|P-R|-|T-R| collapses to ~0 everywhere, so the accumulation grid has no meaningful peaks and confidence is undefined -> no blobs.\n\nWHY IT MATTERS: This breaks the IO-6 release hard-gate ('walker produces a tracked blob') and means the eventual physical presence-detection validation (bf-3v39) will also show no blobs regardless of hardware, until node positions feed the fusion engine.\n\nLIKELY FIX: In cmd/mothership/main.go, call fusion Engine.SetNodePosition for each node (a) at startup from the DB nodes table, and (b) whenever a node's position changes (PATCH /api/nodes/:mac position handler) or a node connects/registers. The spaxel-sim default node placement (createVirtualNodes) and/or a default zone may also need to seed positions so the simulator path exercises real geometry. Verify by re-running TestFullE2EIntegration and asserting len(blobs) > 0 during the run (the test currently ducks this with 'we verify the API responds correctly rather than asserting a minimum count').","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","created_at":"2026-07-03T00:11:33.217563362Z","updated_at":"2026-07-03T19:23:52.843063709Z","source_repo":".","compaction_level":0,"labels":["bug","deferred","fusion","localization","umbrella"],"dependencies":[{"issue_id":"bf-4q5w","depends_on_id":"bf-5jeo","type":"blocks","created_at":"2026-07-03T19:23:17.400573758Z","created_by":"cli","thread_id":""}]} -{"id":"bf-4qto","title":"Add default values for blob identity fields","description":"## Objective\nEnsure all blob identity fields have appropriate default or undefined values across all contexts.\n\n## Scope\n- Review all updated blob creation code\n- Add proper undefined or default values where needed\n- Ensure consistency in default value handling\n\n## Acceptance Criteria\n- [ ] All personName fields set to undefined or appropriate default\n- [ ] All assignedColor fields set to undefined or appropriate default \n- [ ] All identityResolved fields set to undefined or appropriate default\n- [ ] Default handling is consistent across codebase","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:11:37.452698658Z","updated_at":"2026-07-06T22:54:18.019462186Z","source_repo":".","compaction_level":0,"labels":["failure-count:1","split-child"],"dependencies":[{"issue_id":"bf-4qto","depends_on_id":"bf-5151","type":"blocks","created_at":"2026-07-06T05:11:57.963969215Z","created_by":"cli","thread_id":""}]} +{"id":"bf-4qto","title":"Add default values for blob identity fields","description":"## Objective\nEnsure all blob identity fields have appropriate default or undefined values across all contexts.\n\n## Scope\n- Review all updated blob creation code\n- Add proper undefined or default values where needed\n- Ensure consistency in default value handling\n\n## Acceptance Criteria\n- [ ] All personName fields set to undefined or appropriate default\n- [ ] All assignedColor fields set to undefined or appropriate default \n- [ ] All identityResolved fields set to undefined or appropriate default\n- [ ] Default handling is consistent across codebase","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:11:37.452698658Z","updated_at":"2026-07-06T23:16:24.939887312Z","closed_at":"2026-07-06T23:16:24.939887312Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-4qto","depends_on_id":"bf-5151","type":"blocks","created_at":"2026-07-06T05:11:57.963969215Z","created_by":"cli","thread_id":""}],"comments":[{"id":99,"issue_id":"bf-4qto","author":"cli","text":"Closed: propagated the three canonical identity fields (PersonName, AssignedColor, IdentityResolved) through the three projection sites in the fusion loop that were silently dropping them (explainability.BlobSnapshot, automation.TrackedBlob, volume.BlobPos in cmd/mothership/main.go). api.Track (tracks.go) and dashboard.blobJSON (hub.go) already propagated them, so this makes default-handling consistent across all five projection contexts. Fields stay at Go zero values (empty strings, nil *bool) which omitempty serializes as omitted = undefined in JS — the consistent default the acceptance criteria require. Also committed the four missing companion serialization suites (tracker.Blob, tracking.Blob, volume.BlobPos, explainability.BlobSnapshot) asserting: zero-value omits all three, resolved identity emits camelCase keys, and the *bool tri-state round-trips. BLE sidecar population is a separate follow-up bead. Verified gofmt/vet/test clean. Commit ca7a0f2.","created_at":"2026-07-06T23:13:25.604774018Z"},{"id":100,"issue_id":"bf-4qto","author":"cli","text":"Re-verification (auto-split dispatch refused): the dispatcher fired a forced split on failure-count:3, but two independent checks reject decomposition. (1) The dispatch's own splittability pre-check returned {\"splittable\": false}. (2) The bead's objective is already met in code and committed as ca7a0f2: every identity-bearing blob type (tracker.Blob, tracking.Blob, volume.BlobPos, explainability.BlobSnapshot, api.Track, dashboard.blobJSON, automation.TrackedBlob, signal.TrackedBlob) carries PersonName/AssignedColor as empty string + omitempty and IdentityResolved as nil *bool + omitempty — both serialize as omitted = undefined in JS, the uniform default the AC require. All four identity_fields_test.go suites pass (zero-value omits all three; resolved identity emits camelCase keys; *bool tri-state round-trips). Prior comment [99] already closed this with the same rationale; the close did not persist (known br close flakiness), so re-closing. Splitting would manufacture child beads for already-done work — not done.","created_at":"2026-07-06T23:15:47.700495945Z"}]} {"id":"bf-4qwmy","title":"Simulator: GDOP overlay + shopping-list output (coverage gaps, recommended node count + positions)","description":"","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-06T00:40:16.874114137Z","updated_at":"2026-05-06T03:18:31.808932181Z","closed_at":"2026-05-06T03:18:31.808932181Z","close_reason":"Completed","source_system":"","source_repo":".","deleted_by":"","delete_reason":"","original_type":"","compaction_level":0,"compacted_at_commit":"","sender":""} +{"id":"bf-4th1x","title":"Post synthesized REJECT/token root-cause finding as comment on bf-4iewr","description":"Fourth and final link of the bf-34lwt split. Synthesize the confirmed facts from children 1-3 into the definitive root-cause finding and post it as a comment on the grandparent bf-4iewr, so the remaining bf-4iewr children have a precise target.\n\n## Scope\n- Assemble the confirmed file:line citations for points 1-6 (validator wiring, hello.Token check, sim header-only token, ignored header, 24h default migration window, window-closed reject branch).\n- Post the synthesis as a comment on bf-4iewr using `br comments add bf-4iewr ...`.\n\n## Acceptance Criteria\n- [ ] A written root-cause finding posted as a comment on bf-4iewr, confirming points 1-6 with file:line citations.\n- [ ] Explicit statement in the comment that current 'no reject' is a 24h-window MASK, not a real fix.\n- [ ] Explicit statement in the comment that the token-supply path is currently dead (header ignored, hello.Token empty).","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-07T10:24:19.637847909Z","updated_at":"2026-07-07T11:26:49.132762449Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4th1x","depends_on_id":"bf-2hdbg","type":"blocks","created_at":"2026-07-07T10:24:42.516504023Z","created_by":"cli","thread_id":""}]} {"id":"bf-4truh","title":"Comprehensive notification system tests (open bead spaxel-40tl expansion)","description":"Open bead spaxel-40tl 'Write comprehensive tests for notification system' is open. The notify package (internal/notify/) has ntfy.go, pushover.go, webhook.go but tests are missing or incomplete. Needs tests covering: batching logic (30s dedup window), quiet hours gate (suppress non-critical during quiet window), morning digest aggregation, delivery retry logic, channel enable/disable, test-notification endpoint, and notification history API. The existing service_enhanced.go has complex batching logic that needs coverage.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:26:14.365679205Z","updated_at":"2026-05-02T18:26:51.067231767Z","closed_at":"2026-05-02T18:26:51.067231767Z","close_reason":"Duplicate of existing open bead spaxel-40tl which already comprehensively tracks notification system tests","source_repo":".","compaction_level":0} {"id":"bf-4wwt","title":"Inventory blob projection and derived types with construction sites","description":"Scope: the blob-shaped PROJECTION/derived types — read-only views built FROM a tracked blob for a subsystem (not new tracked entities). Any field added to a tracked blob that a projection copies must be propagated at each construction site.\n\nProjection types to document (definition file:line + every construction boundary site):\n- explainability.BlobSnapshot, explainability.BlobExplanation\n- falldetect.BlobSnapshot (distinct type, shared name)\n- volume.BlobState, volume.BlobPos, api.BlobPos\n- tracking.BlobEvent\n- replay.BlobUpdate\n- simulator.BlobResult\n\nReference (already enumerated — RE-VERIFY, do not re-discover): notes/bf-4bhd.md \"Related Blob-shaped projection types\" table (9 types, ~15 construction sites across handler.go, detector.go, pipeline.go, simulator/engine.go, tracking/tracker.go, volume/shape.go).\n\nAcceptance Criteria:\n- [ ] Each projection type definition recorded with file:line and which tracked-blob type it is built from\n- [ ] Every projection construction boundary site listed (file:line)\n- [ ] Re-locate moved sites with: grep -rn \"BlobSnapshot{|BlobState{|BlobPos{|BlobUpdate{|BlobEvent{|BlobResult{|BlobExplanation{\" mothership/\n- [ ] All line numbers verified against current HEAD\n- [ ] Findings written to notes/bf--projections.md","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T15:03:36.904550067Z","updated_at":"2026-07-06T19:39:55.785656755Z","closed_at":"2026-07-06T19:39:55.785656755Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4wwt","depends_on_id":"bf-5uzm","type":"blocks","created_at":"2026-07-06T15:04:21.277568858Z","created_by":"cli","thread_id":""}],"comments":[{"id":80,"issue_id":"bf-4wwt","author":"cli","text":"Re-verified all 9 blob-shaped projection/derived types and 15 non-test construction sites against current HEAD (68bd308) — every type definition (handler.go:27/95, detector.go:69, shape.go:139/1080, triggers.go:624, tracker.go:52, types.go:303, engine.go:80) and every construction site (grep -rnE of BlobSnapshot{/BlobState{/BlobPos{/BlobUpdate{/BlobEvent{/BlobResult{/BlobExplanation{) matches notes/bf-4wwt-projections.md exactly. Work committed in 68bd308 and pushed to origin/main. Findings written to notes/bf-4wwt-projections.md (companion to bf-4bhd): 9 types, 15 sites + 1 falldetect boundary (main.go:2288), with corrections noting api.BlobPos is test-only/dead, replay.BlobUpdate & simulator.BlobResult are synthetic (not blob-derived), and volume.BlobState's only blob-derived site is shape.go:575.","created_at":"2026-07-06T19:38:49.791215302Z"}]} {"id":"bf-4zio","title":"BLE-to-blob 3D visualization: per-person colored humanoid + name labels in scene","description":"Phase 6 item 30 requires the 3D dashboard to show identified persons as colored humanoid figures with name labels ('Alice is in Kitchen') instead of generic 'Blob #2' markers. The BLE registry (spaxel-2wg) is closed but the frontend visualization using resolved identities is not implemented. Update the Three.js scene to render identity-matched blobs as named humanoid meshes.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-26T22:14:42.798449947Z","updated_at":"2026-07-06T12:46:52.964790297Z","source_repo":".","compaction_level":0,"labels":["umbrella"],"dependencies":[{"issue_id":"bf-4zio","depends_on_id":"bf-3dip","type":"blocks","created_at":"2026-07-06T04:05:42.020469537Z","created_by":"cli","thread_id":""}]} {"id":"bf-5151","title":"Add identity fields to blob creation code","description":"## Objective\nUpdate all blob creation code to include the new identity fields (personName, assignedColor, identityResolved).\n\n## Scope\n- Use the list of blob creation sites from previous bead\n- Add the three identity fields to each blob creation\n- Set fields to undefined for existing blobs\n\n## Acceptance Criteria\n- [ ] All blob creation sites from previous bead are updated\n- [ ] personName, assignedColor, identityResolved added to each\n- [ ] Fields are set to undefined for existing code\n- [ ] No blob creation code is missed","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:11:33.679872377Z","updated_at":"2026-07-06T22:44:51.417873267Z","closed_at":"2026-07-06T22:44:51.417873267Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","umbrella"],"dependencies":[{"issue_id":"bf-5151","depends_on_id":"bf-4bhd","type":"blocks","created_at":"2026-07-06T05:11:54.079027318Z","created_by":"cli","thread_id":""},{"issue_id":"bf-5151","depends_on_id":"bf-1mwy","type":"blocks","created_at":"2026-07-06T21:24:06.442551977Z","created_by":"cli","thread_id":""}],"comments":[{"id":86,"issue_id":"bf-5151","author":"cli","text":"bf-5cgc handoff → notes/bf-5cgc-handoff.md. You are the PRIMARY owner of the three Go Tier-1 identity-leak fixes: (1) automation cmd/mothership/main.go:2303 + automation.TrackedBlob def internal/automation/engine.go:1337 — add PersonID, populate from identityMatcher.GetMatch(b.ID), engine resolves label/color via personProvider (engine.go:952); (2) explainability main.go:2206 + explainability.BlobSnapshot handler.go:95 — add identity fields, FOLD the parallel identityMap (main.go:2216) into the snapshot, drop the side-channel arg from UpdateBlobs (:2236); (3) volume main.go:2326 + volume.BlobPos shape.go:1080 — one-line PersonID populate (field already exists, consumer stubbed at shape.go:624). Mirror the reference pattern analytics.TrackUpdate (main.go:2267-2271) which pulls PersonID via GetMatch. NOTE: despite the sibling beads' TS framing, these are GO backend edits — see scope note §3.","created_at":"2026-07-06T20:38:59.973880259Z"},{"id":98,"issue_id":"bf-5151","author":"cli","text":"Closed by claude-code-glm-5-alpha.\n\n## Summary\n\nAdded the three canonical identity fields (PersonName / AssignedColor / IdentityResolved) to every in-scope blob-creation site, per the bf-1q3m consolidated inventory. The fields surface as undefined (omitted) for existing blobs.\n\n### Field definitions (canonical, camelCase JSON keys match dashboard/types/spaxel.d.ts)\n- PersonName string json:\"personName,omitempty\" (\"\" = unresolved)\n- AssignedColor string json:\"assignedColor,omitempty\" (\"\" = unresolved)\n- IdentityResolved *bool json:\"identityResolved,omitempty\" (tri-state: nil=unattempted, &true=resolved, &false=failed)\n\n### In-scope types now carrying the 3 fields (8 total, verified at HEAD d0ac1b2)\nPrimary (added a612584): signal.TrackedBlob, tracker.Blob (3D), tracking.Blob (2D), automation.TrackedBlob.\nProjections (added 01a415d): api.Track (/api/tracks), dashboard.blobJSON (/ws/dashboard). /api/blobs needs no projection (serializes signal.TrackedBlob directly).\nTier-1 leak targets (added c3f4b0d / 29a114c + 7309564): explainability.BlobSnapshot, volume.BlobPos.\n\n### Construction sites — all 21 production sites accounted for\n5 primary + 16 projection = 21. The 6 in-scope sites' types carry the fields (left at Go zero values at construction → omitempty → undefined in JS). The 15 out-of-scope sites are enumerated with reasons in notes/bf-5151-coverage.md (pre-identity Fresnel peak C1; synthetic sim/replay P8-P10; anon-struct/lifecycle/trigger-state sites; rendering fallbacks; dead api.BlobPos). Zero sites silently dropped.\n\n### Tests added (this commit d0ac1b2)\nTable-driven serialization guards on the 3 types whose fields are reachable via JSON: signal/identity_fields_test.go, automation/identity_fields_test.go, api/tracks_identity_test.go. Each asserts (1) zero-value blobs omit all three (undefined), (2) resolved identity emits camelCase keys, (3) *bool tri-state: &false still emits identityResolved:false.\n\n### Gate\ngofmt clean; go vet ./... exit 0; go test ./... 50 packages ok, 0 failures.\n\nPopulation from the BLE identity sidecar (ble.IdentityMatcher.GetMatch) is intentionally a follow-up bead — this one only adds the type surface as undefined, exactly as scoped.","created_at":"2026-07-06T22:44:43.376331478Z"}]} -{"id":"bf-5312","title":"Add the IO-6 hard-gate scenario test and a strict block-back triage gate","description":"Split from bf-5jeo (verification capstone). Depends on the TestFullE2EIntegration child. Child 4 of 4 (final, blocks the parent).\n\nCONTEXT: The IO-6 release hard-gate is \"walker produces a tracked blob\" with 4 nodes + 2 walkers running spaxel-sim (the config described in bf-4q5w; see also internal/signal/processor.go:607,626 which already documents that no engine feeds the live blob loop). There is currently no dedicated test that exercises this gate end-to-end.\n\nSCOPE:\n- Add a dedicated IO-6 hard-gate scenario (a tagged e2e test or runbook+test) that runs RunSimulator(ctx, 4 nodes, 2 walkers, 20 rate, duration) and asserts a walker produces a tracked blob end-to-end, reusing the assertion helpers from bf-2330.\n- CRITICAL TRIAGE GATE: if blobs are still zero after upstream bf-4q5w lands, DO NOT weaken the assertion. Instead: (a) keep this child blocked, (b) capture a finding as a comment with diagnostic evidence (fusion accumulation grid state, node positions, traces under .beads/traces/), and (c) feed that finding back to upstream bf-4q5w (fusion Engine.SetNodePosition wiring) rather than weakening the assertion.\n\nACCEPTANCE CRITERIA:\n- An IO-6 hard-gate scenario exists that asserts a tracked blob is produced with 4 nodes + 2 walkers running spaxel-sim.\n- When green: the IO-6 release hard-gate (walker produces a tracked blob) is satisfied end-to-end.\n- When zero blobs: the assertion stays strict, and a finding with trace evidence is filed back to bf-4q5w instead of weakening — never silently degrade this test.\n- bf-5jeo (this capstone umbrella) cannot close until this child is green.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T18:51:03.299000490Z","updated_at":"2026-07-06T18:51:03.299000490Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5312","depends_on_id":"bf-2aqf","type":"blocks","created_at":"2026-07-06T18:51:13.389487862Z","created_by":"cli","thread_id":""}]} +{"id":"bf-51fq","title":"Observe at least one tracked blob at runtime via /api/blobs","description":"Decomposed from bf-40hc. Third link: with sim streaming CSI (child bf-3hji), observe at least one tracked blob at runtime.\n\nThe e2e harness already proves this works — bf-2aqf hard-asserts >=1 blob via AssertBlobObserved in mothership/tests/e2e/e2e_test.go. This child reproduces that observation OUTSIDE the test (manual run).\n\n## Scope\n- While spaxel-sim streams, poll GET http://localhost:8080/api/blobs (returns a JSON array of tracked blobs) over the run window; track the peak count.\n- /ws/dashboard (snapshot/delta messages carrying a blobs array) is an acceptable alternative observation channel.\n- If no blob appears within ~30s, tune sim params (walkers/rate/duration) until one does and record the working values.\n\n## Acceptance Criteria\n- [ ] At least one blob observed via /api/blobs (or /ws/dashboard) during a run.\n- [ ] Working sim parameters (nodes/walkers/rate/duration/seed) recorded so the result is reproducible.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T04:07:39.419230396Z","updated_at":"2026-07-07T04:07:39.419230396Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-51fq","depends_on_id":"bf-3hji","type":"blocks","created_at":"2026-07-07T04:07:39.460254019Z","created_by":"cli","thread_id":""}]} +{"id":"bf-5312","title":"Add the IO-6 hard-gate scenario test and a strict block-back triage gate","description":"Split from bf-5jeo (verification capstone). Depends on the TestFullE2EIntegration child. Child 4 of 4 (final, blocks the parent).\n\nCONTEXT: The IO-6 release hard-gate is \"walker produces a tracked blob\" with 4 nodes + 2 walkers running spaxel-sim (the config described in bf-4q5w; see also internal/signal/processor.go:607,626 which already documents that no engine feeds the live blob loop). There is currently no dedicated test that exercises this gate end-to-end.\n\nSCOPE:\n- Add a dedicated IO-6 hard-gate scenario (a tagged e2e test or runbook+test) that runs RunSimulator(ctx, 4 nodes, 2 walkers, 20 rate, duration) and asserts a walker produces a tracked blob end-to-end, reusing the assertion helpers from bf-2330.\n- CRITICAL TRIAGE GATE: if blobs are still zero after upstream bf-4q5w lands, DO NOT weaken the assertion. Instead: (a) keep this child blocked, (b) capture a finding as a comment with diagnostic evidence (fusion accumulation grid state, node positions, traces under .beads/traces/), and (c) feed that finding back to upstream bf-4q5w (fusion Engine.SetNodePosition wiring) rather than weakening the assertion.\n\nACCEPTANCE CRITERIA:\n- An IO-6 hard-gate scenario exists that asserts a tracked blob is produced with 4 nodes + 2 walkers running spaxel-sim.\n- When green: the IO-6 release hard-gate (walker produces a tracked blob) is satisfied end-to-end.\n- When zero blobs: the assertion stays strict, and a finding with trace evidence is filed back to bf-4q5w instead of weakening — never silently degrade this test.\n- bf-5jeo (this capstone umbrella) cannot close until this child is green.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T18:51:03.299000490Z","updated_at":"2026-07-07T01:25:58.403458553Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-5312","depends_on_id":"bf-2aqf","type":"blocks","created_at":"2026-07-06T18:51:13.389487862Z","created_by":"cli","thread_id":""},{"issue_id":"bf-5312","depends_on_id":"bf-5sy8","type":"blocks","created_at":"2026-07-07T01:24:29.772848814Z","created_by":"cli","thread_id":""}]} {"id":"bf-55rp","title":"Inventory primary tracked-blob Go types and direct construction sites","description":"Scope: the FIVE primary tracked-blob Go types that make up the tracked-blob lifecycle, plus the api.TrackedBlob alias. Document each type definition (file:line) and every DIRECT struct-literal construction site.\n\nTypes: tracking.Blob (2D), tracker.Blob (3D, identity-bearing), fusion.Blob (peak), automation.TrackedBlob, signal.TrackedBlob (+ api.TrackedBlob alias).\n\nThis work is mostly DONE in two existing reports — RE-VERIFY, do not re-discover:\n- notes/bf-3ldj-findings.md (sections 1-3)\n- notes/bf-4bhd.md (Blob Type Definitions + Patterns 1-2)\n\nAcceptance Criteria:\n- [ ] Each of the 5 primary types has its struct definition recorded with file:line, verified against current HEAD\n- [ ] Every direct struct-literal construction site listed (file:line + pattern: &Blob{} / Blob{} / TrackedBlob{})\n- [ ] The api.TrackedBlob alias relationship to signal.TrackedBlob is noted\n- [ ] Re-locate moved sites with: grep -rn \"Blob{|&Blob{|TrackedBlob{\" mothership/\n- [ ] Findings written to notes/bf--primary-types.md\n\nOut of scope: projection/derived types (child 3), JS/TS (child 4), test fixtures, the unrelated browser new Blob() download API.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T15:02:58.355494649Z","updated_at":"2026-07-06T18:59:02.545840787Z","closed_at":"2026-07-06T18:59:02.545840787Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"comments":[{"id":78,"issue_id":"bf-55rp","author":"cli","text":"Re-verified all 5 primary tracked-blob Go types + the api.TrackedBlob alias against HEAD c4d42e8. Findings in notes/bf-55rp-primary-types.md (committed d99f42d, pushed). Corrections vs prior reports: (1) two construction sites moved — automation.TrackedBlob main.go:2213->:2303, signal.TrackedBlob main.go:5384->:5494; (2) tracking.Blob (2D) field list drifted — PersonName/AssignedColor/IdentityResolved removed, identity fields now match tracker.Blob (3D) exactly. All 6 type definitions (5 structs + 1 pure = alias) confirmed at their reported file:line. Whole-repo sweep confirms exactly 5 production direct-literal construction sites, one per primary type (fusion:260 Blob{}, tracking:160 &Blob{}, tracker:162 &Blob{}, main.go:2303 automation.TrackedBlob{}, main.go:5494 sigproc.TrackedBlob{}). All 5 acceptance criteria met. Documentation only — no Go source changed.","created_at":"2026-07-06T18:57:23.168436965Z"}]} {"id":"bf-55sg5","title":"Mobile-responsive expert mode","description":"## Goal\nMake expert mode fully functional on mobile devices with touch gestures.\n\n## Scope\n- Touch orbit/pan/zoom: single-finger rotate, two-finger pan, pinch to zoom (already supported by Three.js OrbitControls)\n- Hamburger menu for panels: collapsible sidebar for fleet status, settings, zones, triggers\n- Responsive layout: panels slide in from bottom on mobile, from right on desktop\n- Touch-optimized buttons: minimum 44×44px tap targets\n- No hover-dependent UI: all interactions work with tap\n- Mobile-specific shortcuts: long-press for context menu (replaces right-click)\n\n## Location\ndashboard/static/js/mobile.js (new module)\ndashboard/static/css/mobile.css (new stylesheet)\n\n## Acceptance\n- Three.js scene responds to touch gestures (orbit, pan, zoom)\n- Hamburger menu opens panel navigation\n- Panels slide in from bottom on mobile\n- All buttons are touch-friendly (≥44px)\n- No features require hover\n- Long-press context menu works on mobile","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:06:29.813640292Z","updated_at":"2026-05-06T11:04:43.020442678Z","closed_at":"2026-05-06T11:04:43.020442678Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-5613","title":"Component 25: Sleep monitoring — multi-person bedroom edge case (two blobs assignment)","description":"The sleep analyzer exists in internal/sleep/ with full state machine and breathing analysis. However the plan specifies a multi-person bedroom edge case that should be verified.\n\nPlan (Component 25): If two blobs are tracked in a bedroom zone simultaneously:\n- Assign sleep record to BLE-matched person if available\n- Otherwise create two separate zone-based records (one per occupant slot)\n- Breathing analysis uses blob with strongest stationary signal (lowest smooth_deltaRMS)\n\nCheck internal/sleep/analyzer.go to verify:\n1. Two-blob handling: does it create separate records per occupant or a single merged record?\n2. BLE-first assignment: when BLE identifies one or both blobs, are records attributed to persons?\n3. Lowest-deltaRMS blob selection for breathing analysis in multi-person case\n\nAlso check sleep/integration_test.go for a two-person scenario test. If missing, add a test case.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":3,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-05-26T04:05:59.392510448Z","updated_at":"2026-07-06T06:48:11.772354880Z","closed_at":"2026-07-06T06:48:11.772354880Z","close_reason":"Completed","source_repo":".","compaction_level":0} -{"id":"bf-56uk","title":"Verify blob identity fields TypeScript compliance","description":"## Objective\nEnsure all blob identity field additions are TypeScript-compliant with no compilation errors.\n\n## Scope\n- Run TypeScript compiler to check for errors\n- Fix any type mismatches in blob identity fields\n- Ensure all blob objects satisfy the updated type definition\n\n## Acceptance Criteria\n- [ ] TypeScript compiler runs without errors\n- [ ] All blob objects match the updated Blob interface\n- [ ] No missing field errors\n- [ ] No type mismatch errors for identity fields","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T05:11:41.148471860Z","updated_at":"2026-07-06T05:11:41.148471860Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-56uk","depends_on_id":"bf-4qto","type":"blocks","created_at":"2026-07-06T05:12:01.936893599Z","created_by":"cli","thread_id":""}]} +{"id":"bf-56uk","title":"Verify blob identity fields TypeScript compliance","description":"## Objective\nEnsure all blob identity field additions are TypeScript-compliant with no compilation errors.\n\n## Scope\n- Run TypeScript compiler to check for errors\n- Fix any type mismatches in blob identity fields\n- Ensure all blob objects satisfy the updated type definition\n\n## Acceptance Criteria\n- [ ] TypeScript compiler runs without errors\n- [ ] All blob objects match the updated Blob interface\n- [ ] No missing field errors\n- [ ] No type mismatch errors for identity fields","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:11:41.148471860Z","updated_at":"2026-07-06T23:43:30.934244966Z","closed_at":"2026-07-06T23:43:30.934244966Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-56uk","depends_on_id":"bf-4qto","type":"blocks","created_at":"2026-07-06T05:12:01.936893599Z","created_by":"cli","thread_id":""}],"comments":[{"id":101,"issue_id":"bf-56uk","author":"cli","text":"Closed: blob identity fields verified TypeScript-compliant.\n\nWhat was done (commit 7e0bf66):\n- Added TS tooling: dashboard/tsconfig.json (strict, noEmit), `typescript` devDep + `npm run typecheck` script in package.json.\n- Added dashboard/types/blob-identity.check.ts — a pure compile-time mirror of the Go identity-field suites (bf-5151/bf-2ibc/bf-5v3q/bf-3wkz/bf-4qto). Asserts: zero-value blob (no identity) satisfies Blob; resolved-identity blob (camelCase personName/assignedColor/identityResolved the Go hub.go projection emits) satisfies Blob; identityResolved tri-state (undefined/true/false) is assignable.\n- Fixed the one compile error: the HasField helper used `NonNullable extends NonNullable`, which tsc does not reliably evaluate for an indexed type param in a nested conditional — it produced a spurious TS2344 false for the optional boolean identityResolved. Rewrote as `V extends T[K]`; still catches missing keys and type mismatches.\n\nVerification:\n- `npm run typecheck` -> exit 0 (all blob objects match the Blob interface; no missing-field / no type-mismatch errors).\n- `go vet ./...`, `go build ./...`, `go test ./...` (mothership) all pass — no Go touched.","created_at":"2026-07-06T23:33:13.494518104Z"},{"id":102,"issue_id":"bf-56uk","author":"cli","text":"Verified bf-56uk: blob identity fields are TypeScript-compliant.\n\nAcceptance criteria confirmed:\n- npm run typecheck (tsc --noEmit -p tsconfig.json) exits 0 — TypeScript compiler runs without errors.\n- dashboard/types/blob-identity.check.ts asserts zero-value, resolved-identity (camelCase: personName/assignedColor/identityResolved), and legacy-alias blobs all satisfy the Blob interface — all objects match the updated type, no missing-field errors, no type-mismatch errors for identity fields.\n- Non-vacuous: adversarial mutation (renaming identityResolved on the interface) produced 6 real TS errors (TS2344 + TS2353); restoring returned to exit 0.\n\nImplemented in commit 7e0bf66 (tsconfig.json, package.json typescript devDep + typecheck script, types/blob-identity.check.ts; fixed a spurious TS2344 in the HasField helper). Already pushed (HEAD == origin/main).\n\nNo Go touched; cd mothership: go vet ./... =0, go build ./... =0, go test ./... =0.","created_at":"2026-07-06T23:43:19.724360775Z"}]} {"id":"bf-59me3","title":"GET /api/status and GET /api/occupancy endpoints","description":"Plan's REST API spec defines: (1) GET /api/status returning {version, nodes, blobs, uptime_s, detection_quality} and (2) GET /api/occupancy returning {zones:{:{count, people:[]}}}. Neither endpoint is registered in main.go. /api/blobs exists. These are simple read-only endpoints that dashboard and HA users would expect for quick system checks and occupancy queries without WebSocket.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:25:26.769707158Z","updated_at":"2026-05-05T17:40:16.692260524Z","closed_at":"2026-05-05T17:40:16.692260524Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-5cgc","title":"Hand off blob-creation inventory to identity-field implementation beads","description":"## Context\nbf-4bhd's deliverable exists to be consumed by the downstream identity-field implementation, which is tracked in separate OPEN beads: bf-5151 (add identity fields to blob creation code), bf-64h5 / bf-1wvm (extend blob data structure with identity fields), bf-iner (BLE identity matching), bf-4qto / bf-56uk / bf-f841. The site-to-fix mapping lives in `notes/bf-1q3m-consolidated.md` section 6 (tiered fix-target list) but is not yet formalized as a tracked deliverable linking specific sites to specific beads.\n\n## Scope\n- From consolidated report section 6 Tier-1, produce a concrete site -> fix-target -> bead handoff:\n - automation: `cmd/mothership/main.go:2303` + `internal/automation/engine.go:1337` (automation.TrackedBlob) -> add identity fields, populate from `identityMatcher.GetMatch(b.ID)` -> bf-5151 / bf-64h5.\n - explainability: `cmd/mothership/main.go:2206` + `internal/explainability/handler.go:95` (BlobSnapshot) -> add identity fields, fold parallel `identityMap` (`main.go:2216`) -> bf-5151.\n - volume: `cmd/mothership/main.go:2326` + `internal/volume/shape.go:1080` (volume.BlobPos) -> one-line `PersonID` populate -> bf-5151.\n- Reference population pattern: `analytics.TrackUpdate` (`cmd/mothership/main.go:2271`) pulls `PersonID` from the sidecar via `GetMatch` — mirror it.\n- Record the handoff as a note and/or comments on the linked implementation beads.\n\n## Acceptance Criteria\n- [ ] Handoff artefact (note or bead comments) maps each Tier-1 site to the responsible open implementation bead + the population pattern.\n- [ ] Tier 2 / Tier 3 / out-of-scope sites listed for completeness (tracker wiring decision, E1 no-op, dead `api.BlobPos`).\n- [ ] Closes bf-4bhd acceptance criterion: \"Report is ready for the next bead to use.\"","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T20:13:43.097215550Z","updated_at":"2026-07-06T20:41:56.775925159Z","closed_at":"2026-07-06T20:41:56.775925159Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5cgc","depends_on_id":"bf-5ywk","type":"blocks","created_at":"2026-07-06T20:13:54.838823064Z","created_by":"cli","thread_id":""}],"comments":[{"id":91,"issue_id":"bf-5cgc","author":"cli","text":"Closed: formalized the blob-creation → identity-field handoff in notes/bf-5cgc-handoff.md. Maps each Tier-1 Go site to its owner bead + the GetMatch population pattern: automation (main.go:2303 + engine.go:1337) → bf-5151/bf-64h5; explainability (main.go:2206 + handler.go:95, fold identityMap :2216) → bf-5151; volume (main.go:2326 + shape.go:1080, one-line PersonID) → bf-5151. All mirror analytics.TrackUpdate (main.go:2267-2271). Sites re-verified exact at HEAD 1ecc999. Tier-2/3/out-of-scope listed (tracker wiring decision, E1 no-op, dead api.BlobPos); scope note flags the Go-vs-TS framing mismatch on sibling beads. Comments posted on bf-5151, bf-64h5, bf-1wvm, bf-iner, bf-4bhd. Closes bf-4bhd 'Report is ready for the next bead to use'. Docs/.beads only; go vet passes; pushed as e27fafe.","created_at":"2026-07-06T20:41:10.649205335Z"}]} +{"id":"bf-5cr3","title":"Capture deterministic hardware-free reproduce commands into a checked-in note/script","description":"Decomposed from bf-40hc. Final link: now that the end-to-end hardware-free runtime path works (children bf-3zll -> bf-3hji -> bf-51fq), capture it as a checked-in, deterministic reproduce artifact so later beads (the blob-identity runtime checks under bf-f841) and reruns don't have to re-derive it. This resolves the ambiguity that caused bf-40hc's repeated failures.\n\n## Scope\n- Write the exact command sequence (build mothership, run with env, build sim, run sim, observe /api/blobs) into a repo file — e.g. notes/hardware-free-runtime.md and/or scripts/run-sim-local.sh.\n- Verify the artifact runs from a clean checkout and produces >=1 blob.\n\n## Acceptance Criteria\n- [ ] A runnable script or note checked into the repo containing the full reproduce sequence.\n- [ ] Running it from a clean checkout starts the mothership, streams sim CSI, and yields >=1 tracked blob.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T04:07:47.776550214Z","updated_at":"2026-07-07T04:07:47.776550214Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5cr3","depends_on_id":"bf-51fq","type":"blocks","created_at":"2026-07-07T04:07:47.816005869Z","created_by":"cli","thread_id":""}]} {"id":"bf-5dpu","title":"Fix registry bridge position wiring","description":"Fix any missing or incorrect wiring between simulator and registry_bridge.go to ensure positions flow correctly.\n\nScope:\n- Implement any missing data connections\n- Add required format transformations\n- Fix wiring issues identified in earlier beads\n- Add position routing logic if missing\n\nAcceptance criteria:\n- Positions flow from simulator to registry_bridge.go\n- Positions are correctly formatted for registry\n- All identified wiring issues are resolved\n- Data path is complete and functional","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T05:23:51.211181149Z","updated_at":"2026-07-06T05:51:38.882609058Z","closed_at":"2026-07-06T05:51:38.882609058Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5dpu","depends_on_id":"bf-5occ","type":"blocks","created_at":"2026-07-06T05:24:16.457674105Z","created_by":"cli","thread_id":""}]} {"id":"bf-5fo3h","title":"Node disable/enable API endpoints","description":"Plan's REST API spec defines POST /api/nodes/:mac/disable (sets role to IDLE) and POST /api/nodes/:mac/enable (restores prior role). The fleet handler (internal/fleet/handler.go) has identify, reboot, OTA, position, role endpoints but no dedicated disable/enable. The quick-actions.js context menu exposes 'Disable / Enable' for nodes but there's no corresponding backend route.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:25:37.488896455Z","updated_at":"2026-05-05T18:13:57.459444611Z","closed_at":"2026-05-05T18:13:57.459444611Z","close_reason":"Completed","source_repo":".","compaction_level":0} +{"id":"bf-5h1t","title":"Populate canonical identity fields on a runtime blob","description":"## Objective\nDecomposed from parent bf-f841. Building on the runtime path from the prior step, this step makes a LIVE blob actually carry the canonical identity fields (the work added in bf-4qto/bf-56uk), so the remaining steps have something concrete to assert against.\n\n## Scope\n- Using the established runtime path, drive a scenario that triggers BLE-to-blob matching (or its simulator equivalent) so identity is attached.\n- Inspect the live blob (API/WS/dashboard/tracker log), not just a unit-test struct.\n- Confirm the canonical identity field(s) are present and non-empty on the running blob.\n\n## Acceptance Criteria\n- [ ] A runtime blob is produced whose canonical identity field is populated (non-empty).\n- [ ] Identity is observable from a runtime surface (API response, WS frame, or tracker log line).\n- [ ] No panic when the identity field is read off the live blob.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T00:09:44.949183804Z","updated_at":"2026-07-07T00:09:44.949183804Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5h1t","depends_on_id":"bf-40hc","type":"blocks","created_at":"2026-07-07T00:09:58.542542487Z","created_by":"cli","thread_id":""}]} +{"id":"bf-5ig3e","title":"Confirm validator wiring and hello.Token check against source","description":"First link of the bf-34lwt split. Confirm the validator-side facts behind the tokenless-sim REJECT path against current source. This isolates the 'validator configured? always YES' + 'reads hello.Token only' half of the root cause.\n\n## Scope (cite file:line)\n1. Validator is wired UNCONDITIONALLY: `cmd/mothership/main.go` calls `ingestSrv.SetTokenValidator(provSrv.ValidateToken)` with NO config/build gate — so 'validator configured?' is always YES.\n2. The validator reads `hello.Token` only: `internal/ingestion/server.go` — `tokenOK := hello.Token != \"\" && validator(hello.MAC, hello.Token)`.\n\n## Acceptance Criteria\n- [ ] Quoted file:line showing `SetTokenValidator(...)` is called unconditionally in mothership main (no env/build guard).\n- [ ] Quoted file:line of the `tokenOK := ...` expression showing it depends solely on `hello.Token` (not any HTTP header).\n- [ ] One-line conclusion: validator is always configured; acceptance/rejection hinges entirely on `hello.Token`.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-07T10:23:36.488590711Z","updated_at":"2026-07-07T10:40:20.928235569Z","closed_at":"2026-07-07T10:40:20.928235569Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"comments":[{"id":112,"issue_id":"bf-5ig3e","author":"cli","text":"Confirmed validator-side facts against current source (committed docs/notes/token-validator-wiring-confirm.md, pushed 3c5b79b).\n\nFact 1 — validator wired unconditionally: mothership/cmd/mothership/main.go:4494 `ingestSrv.SetTokenValidator(provSrv.ValidateToken)` is a bare top-level statement with no env/build guard; provSrv is itself created unconditionally at :4492. The `if cfg.MigrationWindowHours > 0` at :4495 gates SetMigrationDeadline (:4497), not the validator. So 'validator configured?' is always YES.\n\nFact 2 — reads hello.Token only: mothership/internal/ingestion/server.go:513 `tokenOK := hello.Token != \"\" && validator(hello.MAC, hello.Token)` depends solely on the deserialized hello JSON body field (message.go:22 token,omitempty), populated by ParseJSONMessage at :489. The validator provSrv.ValidateToken (provisioning/server.go:135) takes only (mac, token) — no *http.Request in scope. grep for X-Spaxel-Token over the ingestion tree returns no reads; the only non-test references are in cmd/sim (the sim sending the header). So no HTTP header can flip tokenOK; acceptance/rejection hinges entirely on hello.Token.\n\nConclusion: validator always configured; accept/reject hinges entirely on hello.Token. Validator-side half of bf-34lwt isolated.","created_at":"2026-07-07T10:39:00.118024518Z"}]} {"id":"bf-5jeo","title":"Assert blob production in e2e tests and the IO-6 hard-gate scenario","description":"Split from bf-4q5w (parent umbrella). Depends on the sim-geometry child. This is the verification capstone.\n\nThe e2e tests currently dodge the real assertion: tests/e2e/e2e_test.go TestFullE2EIntegration (~line 843-844) says we verify the API responds correctly rather than asserting a minimum count, and blobCount is captured but not asserted (line 364). TestDetectionEvents (line 679) is likewise weak.\n\nScope:\n- Replace the dodge with a real assertion that len(blobs) > 0 during a run (assert blobCount >= 1) in TestFullE2EIntegration.\n- Strengthen TestDetectionEvents to assert >=1 detection event.\n- Confirm the IO-6 release hard-gate scenario (walker produces a tracked blob) passes end-to-end with 4 nodes + 2 walkers running spaxel-sim, the config described in bf-4q5w.\n\nAcceptance criteria:\n- TestFullE2EIntegration asserts >=1 blob and passes.\n- TestDetectionEvents asserts >=1 detection event and passes.\n- The IO-6 hard-gate scenario is green (walker produces a tracked blob).\n- If blobs are still zero, this child blocks and feeds a finding back to the relevant upstream child rather than weakening the assertion.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-03T19:23:06.573060103Z","updated_at":"2026-07-06T18:52:33.158201458Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-5jeo","depends_on_id":"bf-18yn","type":"blocks","created_at":"2026-07-03T19:23:17.378033149Z","created_by":"cli","thread_id":""},{"issue_id":"bf-5jeo","depends_on_id":"bf-5312","type":"blocks","created_at":"2026-07-06T18:51:13.434632872Z","created_by":"cli","thread_id":""}]} +{"id":"bf-5k1z","title":"Verify the gated IO-6 test fails RED with actionable diagnostic evidence","description":"Split from bf-5312. Child 2 of 4. Depends on child 1 (bf-11dw, the build-tag move).\n\nCONTEXT: With the IO-6 gate behind the `io6_gate` build tag (child 1), it can be invoked explicitly without poisoning the default `go test ./...` suite. It is expected to be RED until upstream bf-4q5w lands. This child PROVES the RED state is actionable — i.e. the failure dumps the bf-4q5w wiring-gap signature (distinct announced node positions + zero tracked blobs), not a generic timeout or harness error. This is closeable while the test is RED.\n\nSCOPE:\n- Run `cd mothership && go test -tags io6_gate -run TestIO6HardGate_WalkerProducesTrackedBlob ./tests/e2e/... -v` and capture the full failure output.\n- Confirm the failure is the blob assertion (zero tracked blobs), NOT a harness/startup/build error.\n- Confirm CaptureIO6Diagnostics dumps the bf-4q5w signature: spaxel-sim announced DISTINCT node positions (not all collapsed to xy=(0,0)) AND /api/blobs + /api/status report zero tracked blobs / zero detection events.\n- Save a trace under .beads/traces/ (a new bf- dir) containing: the `go test -v` output, the diagnostic dump, and the node-position-vs-blob-state contrast.\n\nACCEPTANCE CRITERIA:\n- The gated test fails on the blob assertion (not on harness startup / build / timeout).\n- The diagnostic dump shows distinct announced node positions + zero blobs (the wiring-gap signature that proves bf-4q5w is the cause).\n- A trace capturing the evidence exists under .beads/traces/.\n- No change to the assertion (still strict).","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T01:23:48.349016558Z","updated_at":"2026-07-07T01:23:48.349016558Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5k1z","depends_on_id":"bf-11dw","type":"blocks","created_at":"2026-07-07T01:24:29.586636503Z","created_by":"cli","thread_id":""}]} {"id":"bf-5kns","title":"Search for direct Blob constructor calls","description":"## Objective\nFind all locations where Blob objects are created via direct constructor calls (new Blob()).\n\n## Scope\n- Search for 'new Blob(' patterns across the codebase\n- Identify TypeScript and JavaScript files\n- Document each constructor call location\n\n## Acceptance Criteria\n- [ ] All 'new Blob()' calls are identified\n- [ ] Each location documented with file path and line number\n- [ ] Code context captured for each site","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T05:31:32.998912911Z","updated_at":"2026-07-06T05:53:06.725660865Z","closed_at":"2026-07-06T05:53:06.725660865Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]} {"id":"bf-5lii","title":"Wire immediate registry sync on simulator node create/update","description":"Fix the incomplete wiring in the simulator API handlers (split from bf-4oiz). AddNode and UpdateNode in mothership/internal/api/simulator.go currently only LOG 'will be synced to registry on next periodic sync' — they hold a registryBridge field but never call SyncToRegistry because the handler has no RegistryNodeAdapter to pass to it. This is the missing wiring: nodes added via the simulator API wait up to 30s (the periodic ticker) before reaching the fleet registry.\n\nScope:\n- Give SimulatorHandler access to a fleet.Registry-backed RegistryNodeAdapter (or a sync callback) so it can drive an immediate SyncToRegistry, in addition to the existing 30s periodic sync in main.go\n- AddNode: call registryBridge.SyncToRegistry() immediately after CreateVirtualNode succeeds, replacing the log-only stub\n- UpdateNode: call registryBridge.SyncToRegistry() immediately after UpdateNodePosition succeeds\n- Keep main.go wiring intact (periodic sync stays as a safety net); thread the adapter from cmd/mothership/main.go:4283/4303 into NewSimulatorHandler\n\nAcceptance criteria:\n- AddNode triggers an immediate SyncToRegistry into the fleet registry (not just a log line)\n- UpdateNode triggers an immediate SyncToRegistry on position change\n- New node appears in the fleet registry promptly after a create, without waiting for the 30s ticker\n- go vet ./internal/api/... ./internal/simulator/... passes\n- Existing simulator + registry_bridge tests still pass","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T13:19:02.660235909Z","updated_at":"2026-07-06T15:49:51.478283740Z","closed_at":"2026-07-06T15:49:51.478283740Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-5lii","depends_on_id":"bf-4pqj","type":"blocks","created_at":"2026-07-06T13:19:39.222892879Z","created_by":"cli","thread_id":""}],"comments":[{"id":66,"issue_id":"bf-5lii","author":"cli","text":"Implemented immediate registry sync on simulator node create/update. SimulatorHandler gains a registryAdapter (simulator.RegistryNodeAdapter) field threaded into NewSimulatorHandler. AddNode calls registryBridge.SyncToRegistry(adapter) right after CreateVirtualNode succeeds; UpdateNode does the same after UpdateNodePosition succeeds (both outside the handler lock). main.go adds fleetRegistryAdapter (adapts *fleet.Registry <-> simulator.NodeRecord), shared by the immediate syncs and the 30s periodic ticker (kept as a safety net). New table-driven TestImmediateSyncOnAddAndUpdate covers the wired (asserts AddVirtualNode + SetNodePosition driven synchronously) and nil-adapter no-op paths. go vet ./... clean; go test ./... all pass. Commit 41e8233 pushed to origin main.","created_at":"2026-07-06T15:48:19.188155951Z"}]} {"id":"bf-5o576","title":"Fuzz tests for binary frame parser and JSON protocol","description":"Open bead bf-3d55l tracks this but has been sitting open with no implementation started. The ingestion frame parser (internal/ingestion/frame.go) and JSON message parser (internal/ingestion/message.go) parse untrusted input from ESP32 nodes. Need Go fuzz tests (testing.F) in frame_fuzz_test.go and message_fuzz_test.go covering: malformed header lengths, n_sub overflow, invalid channel values, truncated payloads, invalid JSON type discriminators, and extra fields.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-02T18:25:51.799073946Z","updated_at":"2026-05-02T18:26:32.910185523Z","closed_at":"2026-05-02T18:26:32.910185523Z","close_reason":"Duplicate of existing open bead bf-3d55l which already tracks fuzz tests for binary frame parser and JSON protocol","source_repo":".","compaction_level":0} {"id":"bf-5occ","title":"Validate position formatting for registry","description":"Check that positions received by registry_bridge.go are correctly formatted for registry consumption.\n\nScope:\n- Examine registry's expected position format\n- Compare with actual format from simulator\n- Identify any format mismatches\n- Document required transformations\n\nAcceptance criteria:\n- Registry's expected position format is documented\n- Actual format from simulator is documented\n- Format differences are identified\n- Required transformations are documented","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T05:23:47.562893475Z","updated_at":"2026-07-06T05:48:18.742639734Z","closed_at":"2026-07-06T05:48:18.742639734Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5occ","depends_on_id":"bf-2irz","type":"blocks","created_at":"2026-07-06T05:24:16.428493592Z","created_by":"cli","thread_id":""}]} {"id":"bf-5rre","title":"Add accessibility tests for onboarding flow","description":"Create axe-core accessibility tests for the onboarding UI flow. Tests should walk through the onboarding steps and scan for WCAG 2.1 AA violations at each step.\n\n**Acceptance Criteria:**\n- Test file dashboard/tests/a11y-onboarding.spec.js exists\n- Tests cover the main onboarding flow steps\n- Each onboarding step page is scanned for accessibility violations\n- Tests fail if any WCAG 2.1 AA violations are found\n- Tests can be run locally with 'npx playwright test a11y-onboarding'","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T03:36:03.687996283Z","updated_at":"2026-07-06T04:29:52.401195679Z","closed_at":"2026-07-06T04:29:52.401195679Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5rre","depends_on_id":"bf-2adi","type":"blocks","created_at":"2026-07-06T03:36:48.688187395Z","created_by":"cli","thread_id":""}]} {"id":"bf-5rulx","title":"Simulator: synthetic walkers + CSI generation (user paths, random walk, propagation model integration)","description":"","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-06T00:40:16.874026391Z","updated_at":"2026-05-06T02:45:17.053495568Z","closed_at":"2026-05-06T02:45:17.053495568Z","close_reason":"Completed","source_system":"","source_repo":".","deleted_by":"","delete_reason":"","original_type":"","compaction_level":0,"compacted_at_commit":"","sender":""} +{"id":"bf-5sy8","title":"Flip the IO-6 hard-gate to GREEN end-to-end once bf-4q5w lands","description":"Split from bf-5312. Child 4 of 4 (FINAL — blocks the parent umbrella bf-5312). Depends on child 3 (bf-1hcx) AND on upstream bf-4q5w.\n\nCONTEXT: This is the \"When green\" acceptance criterion of bf-5312, isolated into its own child so children 1-3 can close NOW. It is the ONLY child that stays blocked — on upstream bf-4q5w (fusion Engine.SetNodePosition wiring). It must NOT close until bf-4q5w lands AND the gated IO-6 test actually passes.\n\nSCOPE:\n- Pre-check that bf-4q5w has landed: grep cmd/mothership/main.go (and the PATCH /api/nodes/:mac position handler / node-register path) for non-test calls to fusion Engine.SetNodePosition. If only the definition exists, bf-4q5w is not done — keep this child open.\n- Once bf-4q5w is landed, run `cd mothership && go test -tags io6_gate -run TestIO6HardGate_WalkerProducesTrackedBlob ./tests/e2e/... -v`.\n- Confirm it PASSES: a walker produces >=1 tracked blob — BOTH the dashboard WS feed (AssertBlobObserved) AND the /api/blobs concurrent count (GetBlobCount).\n- If it is STILL RED after bf-4q5w lands: DO NOT weaken the assertion. Capture fresh diagnostics (CaptureIO6Diagnostics), file a NEW finding back to bf-4q5w (its fix is incomplete), and keep this child open.\n\nACCEPTANCE CRITERIA:\n- `go test -tags io6_gate -run TestIO6HardGate ...` is GREEN with >=1 tracked blob from a 4-node / 2-walker run.\n- The assertion remains strict (unchanged from child 1).\n- This satisfies the \"When green: the IO-6 release hard-gate (walker produces a tracked blob) is met end-to-end\" criterion of bf-5312.\n- bf-5312 (parent umbrella) cannot close until this child is green.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T01:24:13.505591575Z","updated_at":"2026-07-07T01:24:13.505591575Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5sy8","depends_on_id":"bf-1hcx","type":"blocks","created_at":"2026-07-07T01:24:29.680139109Z","created_by":"cli","thread_id":""},{"issue_id":"bf-5sy8","depends_on_id":"bf-4q5w","type":"blocks","created_at":"2026-07-07T01:24:29.728504861Z","created_by":"cli","thread_id":""}]} {"id":"bf-5txbb","title":"Fleet status page","description":"## Goal\nFull table view of all registered nodes with all metrics, bulk actions, camera fly-to on click.\n\n## Scope\nTable columns:\n- Name: user-assigned friendly name\n- MAC: hardware address\n- Role: TX/RX/TX_RX — editable dropdown\n- Position: (x, y, z) — click to highlight node in 3D view and fly camera to it\n- Firmware: version string + 'Update available' badge\n- RSSI: last reported WiFi signal strength\n- Status: ONLINE/STALE/OFFLINE with colored indicator\n- Uptime: time since last boot\n- Actions: Restart, Update, Remove, Identify (blink LED)\n\nGlobal actions:\n- Update All (rolling OTA)\n- Re-baseline All\n- Export Config\n- Import Config\n\n## Location\ndashboard/static/js/fleet.js (new module, extract from existing code)\ninternal/api/fleet.go (already exists)\n\n## Acceptance\n- Table shows all registered nodes\n- Click position → camera flies to node in 3D view\n- Role dropdown changes node role\n- Actions execute correctly\n- Bulk actions work on all nodes\n- Export/import config works","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:06:29.834674580Z","updated_at":"2026-05-06T11:48:29.548615767Z","closed_at":"2026-05-06T11:48:29.548615767Z","close_reason":"Implemented fleet status page with full table view and bulk actions:\n- Added dashboard/static/js/fleet.js: core fleet management module with API functions\n- Added dashboard/static/css/fleet-page.css: complete styling for fleet table\n- Table columns: Name, MAC, Status, Firmware, Uptime, Position, Role, Health, Packet Rate, Temperature, Actions\n- Bulk actions: Update All (rolling OTA), Re-baseline All, Export Config, Import Config\n- Camera fly-to on position click\n- Node actions: identify (LED blink), update firmware, remove, re-assign role\n- CSV export for reporting","source_repo":".","compaction_level":0} {"id":"bf-5uzm","title":"Map cross-package blob conversion boundaries in main.go","description":"Scope: the cross-package conversion boundaries in cmd/mothership/main.go where a tracked blob is converted from one type to another. These are the CRITICAL identity-leak sites — the actual goal of the umbrella effort.\n\nSites to re-verify against HEAD:\n- main.go:5384 — fusion.Blob peak to sigproc.TrackedBlob (E1)\n- main.go:2213 — tracked blob to automation.TrackedBlob, IDENTITY DROPPED (E2)\n- main.go:2116 — tracked blob to explainability.BlobSnapshot, IDENTITY DROPPED (E3)\n- main.go:2236 — tracked blob to volume.BlobPos\n\nFor each: source type, target type, which fields are copied, which identity fields (if any) are dropped, and whether the target type even carries identity fields.\n\nReference: notes/bf-3ldj-findings.md section 5 (Tier 1) + section 6 (data flow), notes/bf-4bhd.md Pattern 4.\n\nAcceptance Criteria:\n- [ ] Every main.go blob-conversion loop enumerated with source type, target type, copied fields\n- [ ] The two identity-dropping boundaries (:2213 automation, :2116 explainability) explicitly flagged as the leak to fix\n- [ ] Whether the target type carries identity fields is noted for each boundary\n- [ ] All line numbers verified against current HEAD\n- [ ] Findings written to notes/bf--conversions.md","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T15:03:09.093500667Z","updated_at":"2026-07-06T19:23:33.753528151Z","closed_at":"2026-07-06T19:23:33.753528151Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-5uzm","depends_on_id":"bf-55rp","type":"blocks","created_at":"2026-07-06T15:04:21.183973883Z","created_by":"cli","thread_id":""}],"comments":[{"id":79,"issue_id":"bf-5uzm","author":"cli","text":"Implemented: findings written to notes/bf-5uzm-conversions.md and pushed (commit 585532f). Re-verified all four named conversion sites against HEAD — task line numbers had drifted +90..+110; corrected to E1 main.go:5494, E2 :2303, E3 :2206, volume :2326 (grep-confirmed against current main.go). Enumerated all 9 blob-projection loops in the live 10 Hz fusion loop with source/target types + copied fields. Flagged E2 (automation.TrackedBlob, no identity field) and E3 (explainability.BlobSnapshot, no identity field) as the two identity-leak boundaries to fix; volume.BlobPos carries PersonID but leaves it unpopulated. Noted per-target whether the type carries identity fields. Recorded a material correction: the live loop runs blobTracker (greedy associator), not tracker.TrackManager/applyIdentity — resolved identity lives in the ble.IdentityMatcher sidecar and is never written onto the blob struct, so every identity-aware boundary must pull from GetMatch(blob.ID) at the conversion site (analytics.TrackUpdate :2267 is the reference pattern). All 5 acceptance criteria met.","created_at":"2026-07-06T19:22:15.401966536Z"}]} {"id":"bf-5v3q","title":"Add identity fields to volume.BlobPos type","description":"## Objective\nAdd the three canonical identity fields — PersonName, AssignedColor, IdentityResolved — to the `volume.BlobPos` struct type, which today carries only `PersonID`. This is the Tier-1 #3 type gap from the bf-1q3m/bf-5cgc handoff.\n\n## Scope (grounded in notes/bf-1q3m-consolidated.md §6 Tier-1 #3)\n- Edit `mothership/internal/volume/shape.go:1080` — `type BlobPos struct` is today `{ID, PersonID, X, Y, Z}` (PersonID already present at :1082, left \"\" at the conversion site). Add `PersonName`, `AssignedColor`, `IdentityResolved *bool` with JSON tags (`person_name`, `assigned_color`, `identity_resolved`), mirroring commit 1446ccf.\n- The production creation literal at `cmd/mothership/main.go:2326` needs NO change for the field declarations — Go zero values = undefined in JS via omitempty. Fields-as-undefined, consistent with 1446ccf + 248d0e0.\n- OUT OF SCOPE (separate downstream bead): populating PersonID/PersonName/etc. from `identityMatcher.GetMatch(blob.ID)` at main.go:2326 (the one-line populate that unblocks person-filtered volume triggers). This child adds field DECLARATIONS only, left at zero. Do NOT add GetMatch calls.\n\n## Acceptance Criteria\n- [ ] PersonName, AssignedColor, IdentityResolved added to volume.BlobPos (shape.go:1080) alongside the existing PersonID, with correct JSON tags\n- [ ] Existing PersonID field retained; no populate/sidecar logic added at main.go:2326\n- [ ] gofmt clean; `go vet ./internal/volume/...` clean; `go test ./internal/volume/...` passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T21:23:54.831218800Z","updated_at":"2026-07-06T21:43:43.622017092Z","closed_at":"2026-07-06T21:43:43.622017092Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5v3q","depends_on_id":"bf-2ibc","type":"blocks","created_at":"2026-07-06T21:24:06.366185817Z","created_by":"cli","thread_id":""}],"comments":[{"id":94,"issue_id":"bf-5v3q","author":"cli","text":"Implemented: added PersonName, AssignedColor, and IdentityResolved (*bool) to volume.BlobPos in mothership/internal/volume/shape.go alongside the existing PersonID. Snake_case JSON tags (person_name, assigned_color, identity_resolved) with omitempty — fields left at zero values (declarations only); production literal at main.go:2326 unchanged since Go zero values serialize as omitted. No GetMatch/populate logic added (out of scope, downstream bead). Mirrors 1446ccf but with snake_case tags per the volume package's internal convention. Verified: gofmt clean, go vet ./... clean, go test ./... passes. Committed 29a114c, pushed to main.","created_at":"2026-07-06T21:42:58.610940269Z"}]} @@ -107,6 +129,7 @@ {"id":"bf-5y8tm","title":"Fresnel zone debug overlay","description":"## Goal\nToggle-able wireframe ellipsoids between active links in the 3D scene for debugging coverage geometry.\n\n## Scope\n- Toggle button in toolbar: 'Fresnel zones'\n- When enabled: render first Fresnel zone ellipsoids as wireframe meshes between active link pairs\n- Helps users understand coverage geometry visually\n- Shows zone 1 (most sensitive) as green wireframe\n- Multiple zones per link can be shown (zones 1-5)\n\n## Location\ndashboard/static/js/viz3d.js (extend existing 3D visualization)\n\n## Acceptance\n- Toggle button shows/hides Fresnel zone ellipsoids\n- Zones render correctly for all active TX→RX links\n- Update in real-time as nodes are moved\n- Performance: <5ms render time for 8-node fleet (28 links)","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:05:43.410156795Z","updated_at":"2026-05-06T05:05:27.767847993Z","closed_at":"2026-05-06T05:05:27.767847993Z","close_reason":"Fixed Fresnel zone debug overlay mouse interaction bugs:\n- Raycast against both wireframe and fill meshes for reliable detection\n- Wireframe is always present even when fill has 0 opacity (zones 2+)\n- Sort intersections by distance to get the closest intersection\n- Fixes hover detection not working properly for multi-zone Fresnel ellipsoids","source_repo":".","compaction_level":0} {"id":"bf-5yff","title":"Implement realistic node geometry in virtual node creation","description":"Child of bf-18yn (split). Depends on bf-195o.\n\nImplement the geometry placement design in the simulator virtual-node creation path.\n\nScope:\n- Modify the virtual node creation code to assign realistic positions instead of co-located/origin\n- Apply the design from bf-195o (e.g., grid layout with specific spacing)\n- Update internal/simulator/registry_bridge.go or wherever PosX/PosY/PosZ are sourced\n- Ensure positions are set during virtual node initialization\n\nAcceptance criteria:\n- Virtual nodes are created with spread-out positions (not all at origin, not co-located)\n- Code uses the constants/layout designed in bf-195o\n- Positions are properly assigned to PosX/PosY/PosZ fields","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-07-06T04:22:17.482482355Z","updated_at":"2026-07-06T04:46:57.705340422Z","closed_at":"2026-07-06T04:46:57.705340422Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5yff","depends_on_id":"bf-195o","type":"blocks","created_at":"2026-07-06T04:22:20.939958629Z","created_by":"cli","thread_id":""}]} {"id":"bf-5ywk","title":"Banner superseded bf-4bhd notes, confirm single inventory source","description":"## Context\nbf-4bhd has failed 4x largely because re-dispatched agents trust the STALE `notes/bf-4bhd.md`. The bf-1q3m consolidated report section 4.1 flagged it SUPERSEDED for two material reasons: (1) 2D field-list drift (it claims `tracking.Blob` has `PersonName/AssignedColor/IdentityResolved` fields that no longer exist), and (2) +90 line drift on E1/E2/E3 boundaries. The file itself carries NO supersession banner, so it still reads as an authoritative \"Analysis Report\" with \"verified line numbers.\" This is the root-cause trap.\n\n## Scope\n- Add an explicit SUPERSEDED banner at the TOP of `notes/bf-4bhd.md` pointing to `notes/bf-1q3m-consolidated.md` as the sole trusted blob inventory. State WHY it is superseded (field-list + line drift, per section 4.1).\n- Confirm `notes/bf-1q3m-consolidated.md` is the single source of truth.\n- Verify `notes/bf-3ldj-findings.md` (also superseded per section 4.1) is clearly secondary/banned or banner it too.\n- Keep both stale files for provenance — do NOT delete.\n\n## Acceptance Criteria\n- [ ] `notes/bf-4bhd.md` top carries a visible SUPERSEDED banner naming `notes/bf-1q3m-consolidated.md` and the reason.\n- [ ] `notes/bf-1q3m-consolidated.md` confirmed as the single live inventory source.\n- [ ] No un-bannered stale inventory note remains that a future agent would mistake for authoritative.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T20:13:43.012182222Z","updated_at":"2026-07-06T20:33:37.078942894Z","closed_at":"2026-07-06T20:33:37.078942894Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5ywk","depends_on_id":"bf-1m2x","type":"blocks","created_at":"2026-07-06T20:13:54.783932850Z","created_by":"cli","thread_id":""}],"comments":[{"id":84,"issue_id":"bf-5ywk","author":"cli","text":"Closed: added SUPERSEDED banners making notes/bf-1q3m-consolidated.md the sole trusted blob inventory.\n\nVerified both staleness claims against HEAD (e698c37; recent commits are docs-only so source lines match the consolidated report's 1a26c12 verification):\n- bf-4bhd 2D field-list drift CONFIRMED: tracking.Blob has PersonID/PersonLabel/PersonColor/IdentityConfidence/IdentitySource/IdentityLastSeen -- no PersonName/AssignedColor/IdentityResolved.\n- bf-4bhd line drift CONFIRMED: E2 :2303 (was :2213, +90), E3 :2206 (was :2116, +90), E1 :5494 (was :5384, +110), volume :2326 (was :2236, +90).\n- bf-3ldj same line drift + the false 'IDENTITY ATTACHED' data-flow claim (tracker.TrackManager not wired into main.go; identity is sidecar-based via ble.IdentityMatcher).\n\nDone (commit 1ecc999, pushed):\n1. notes/bf-4bhd.md -- strong SUPERSEDED banner at top (field-list + line drift, per section 4.1), title marked SUPERSEDED.\n2. notes/bf-3ldj-findings.md -- strong SUPERSEDED banner (line + data-flow drift), title marked SUPERSEDED.\n3. notes/bf-1q3m-consolidated.md -- confirmed single source of truth (header banner + section 9 provenance already declare it); no edit needed.\n4. Proportionate 'secondary -- see consolidated' banners on the bf-3ldj-input notes (bf-26ta, bf-67ao, bf-5kns) and bf-26ta's JS/TS result dumps, so no inventory-titled note remains that a future agent could mistake for authoritative.\n\nAll stale files retained for provenance; none deleted. Docs-only -- no Go/JS source changed. go vet ./... and go test ./... both clean.\nAcceptance criteria all met.","created_at":"2026-07-06T20:29:38.374484316Z"},{"id":85,"issue_id":"bf-5ywk","author":"cli","text":"Closed bf-5ywk. Implementation was completed and pushed in commit 1ecc999 (HEAD == origin/main). Verified at close time:\n\n- notes/bf-4bhd.md: visible 🔴 SUPERSEDED banner at top naming notes/bf-1q3m-consolidated.md as sole trusted blob inventory, with the two material reasons per §4.1 (2D field-list drift: tracking.Blob's claimed PersonName/AssignedColor/IdentityResolved fields no longer exist; +90/+110 line drift on E1/E2/E3 boundaries). Original content retained below for provenance.\n- notes/bf-3ldj-findings.md: same SUPERSEDED banner (line drift + wrong 'IDENTITY ATTACHED' data-flow claim — tracker.TrackManager.UpdateWithIdentity is not wired into main.go at HEAD; identity is sidecar via ble.IdentityMatcher).\n- notes/bf-1q3m-consolidated.md: header already declares itself the single source of truth and explicitly supersedes both stale files. Confirmed unchanged.\n- Proportionate 'Secondary — see consolidated' banners added to the bf-3ldj input notes (bf-26ta, bf-26ta-js/ts, bf-67ao, bf-5kns) so no inventory-titled note reads as authoritative.\n- No stale inventory note left un-bannered. All stale files retained (none deleted) for provenance.\n- Docs-only change (7 markdown files); go vet ./... and go test ./... both clean (exit 0).","created_at":"2026-07-06T20:32:52.639968225Z"}]} +{"id":"bf-601bz","title":"Verify zero-REJECT end-to-end across open- and closed-window boots","description":"Final link of the bf-4iewr split (depends on the durable-policy child). This is the hard acceptance gate: prove the fix is REAL, not masked by the open 24h window, by exercising BOTH window states.\n\n## Scope\nBuild the mothership + sim (per PROGRESS.md \"Hardware-Free Runtime Boot\" and \"Hardware-Free Simulator -> CSI Streaming\"), then run the sim (1-2 nodes) against TWO mothership boots and grep stderr in each:\n\n1. DEFAULT boot (window open via 24h default): `SPAXEL_BIND_ADDR=127.0.0.1:8080 SPAXEL_DATA_DIR= SPAXEL_LOG_LEVEL=info TZ=UTC /tmp/spaxel-mothership`\n2. CLOSED-WINDOW boot (the regression case): same but with `SPAXEL_MIGRATION_WINDOW_HOURS=0`.\n\nFor each, capture mothership stderr to a file, run the sim (`/tmp/spaxel-sim --mothership ws://localhost:8080/ws/node --nodes 2 --walkers 1 --rate 20 --duration 15 --seed 42`), then grep the captured stderr:\n`grep -iE 'reject|invalid_token| 401 | 403 |REJECT'`\n\n## Acceptance Criteria\n- [ ] DEFAULT boot: zero reject/invalid_token/401/403 occurrences in mothership stderr; sim nodes connect and stream (fps > 0).\n- [ ] CLOSED-WINDOW boot (SPAXEL_MIGRATION_WINDOW_HOURS=0): ALSO zero reject/invalid_token/401/403 occurrences — this is the proof that the token/policy fix is real and not a window mask.\n- [ ] Attach the two grep results (counts = 0) as evidence (comment or PROGRESS.md note).","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T09:50:02.447603112Z","updated_at":"2026-07-07T09:50:02.447603112Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-601bz","depends_on_id":"bf-4mle6","type":"blocks","created_at":"2026-07-07T09:50:16.372747556Z","created_by":"cli","thread_id":""}]} {"id":"bf-64h5","title":"Extend blob data structure with identity fields","description":"## Objective\nAdd identity-related fields to the blob/person data structure in the frontend to support name and color rendering.\n\n## Scope\n- Identify where blob data is defined in the 3D dashboard frontend\n- Add fields for: personName (string), assignedColor (hex/rgb), and identityResolved (boolean)\n- Ensure backward compatibility with existing blob data that lacks identity info\n- Add TypeScript types/interfaces if applicable\n\n## Acceptance Criteria\n- [ ] Blob data structure includes new identity fields (personName, assignedColor, identityResolved)\n- [ ] Existing code handles blobs without identity info (backward compatible)\n- [ ] TypeScript types updated if applicable\n- [ ] No console errors from undefined fields on existing blobs\n\n## Notes\nThis is a data structure-only change. No rendering work yet.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T04:04:17.436638406Z","updated_at":"2026-07-06T12:46:52.964790297Z","source_repo":".","compaction_level":0,"labels":["umbrella"],"dependencies":[{"issue_id":"bf-64h5","depends_on_id":"bf-2gmx","type":"blocks","created_at":"2026-07-06T04:41:59.854947777Z","created_by":"cli","thread_id":""}],"comments":[{"id":87,"issue_id":"bf-64h5","author":"cli","text":"bf-5cgc handoff → notes/bf-5cgc-handoff.md §0/§3. The automation Tier-1 leak (cmd/mothership/main.go:2303 + automation.TrackedBlob internal/automation/engine.go:1337) nominally lists this bead, but the fix is a GO backend edit (add PersonID to the Go struct + populate from identityMatcher.GetMatch). Your bead as written is dashboard-TS-types work (spaxel.d.ts Blob interface, per consolidated report Tier-4 #7). Recommend: keep bf-64h5 as the frontend TS track; assign the Go automation edit to bf-5151. See scope note §3 for the Go-vs-TS split.","created_at":"2026-07-06T20:38:59.992275783Z"}]} {"id":"bf-67ao","title":"Search for blob factory functions","description":"## Objective\nFind all functions or methods that create and return blob objects.\n\n## Scope\n- Search for functions returning blob objects\n- Identify blob creation helpers/utilities\n- Document each factory location\n\n## Acceptance Criteria\n- [ ] All blob factory functions are identified\n- [ ] Each location documented with file path and line number\n- [ ] Function signature and blob creation pattern noted","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:31:41.102456169Z","updated_at":"2026-07-06T14:15:08.371357801Z","closed_at":"2026-07-06T14:15:08.371357801Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-67ao","depends_on_id":"bf-26ta","type":"blocks","created_at":"2026-07-06T05:31:59.643168033Z","created_by":"cli","thread_id":""}],"comments":[{"id":62,"issue_id":"bf-67ao","author":"cli","text":"Documented all 15 blob factory functions across 9 blob types in notes/bf-67ao-findings.md (commit 43d3a71). Each entry has function signature + file:line of both the func def and the struct-literal creation site + construction idiom. go vet has pre-existing failures in simulator_test.go/main.go (bf-5dpu, unrelated to this docs-only change).","created_at":"2026-07-06T14:04:25.592569220Z"},{"id":63,"issue_id":"bf-67ao","author":"cli","text":"Completed: documented all blob factory functions in notes/bf-67ao-findings.md. Verified every file:line by direct inspection (all 15 original entries exact). Completeness sweep added 2 gaps: (1) 10th blob type explainability.BlobSnapshot (handler.go:95), distinct from falldetect.BlobSnapshot despite shared name; (2) entry E3 — production conversion loop at main.go:2116 building []explainability.BlobSnapshot, sibling to E2. Also added test-only-factories caveat. Final catalogue: 16 creation sites across 10 blob types. Commits 43d3a71 + 7e89b31 pushed to main. go vet has pre-existing unrelated failures in simulator/main.go (present on HEAD, no .go files changed by this docs bead).","created_at":"2026-07-06T14:14:59.751202810Z"}]} {"id":"bf-69ym","title":"Add end-to-end test for simulator-to-registry position flow","description":"Lock in the full data flow with an integration test (split from bf-4oiz). Today the only coverage is unit tests on registry_bridge.go in isolation; nothing proves a position travels from the simulator path all the way into the fleet registry. After the immediate-sync wiring lands (bf-5lii), add an end-to-end test.\n\nScope:\n- Add a test that exercises the complete path: node create via the simulator handler (or VirtualNodeStore.CreateVirtualNode) -> registryBridge.SyncToRegistry -> adapter.AddVirtualNode / SetNodePosition into a fleet.Registry\n- Assert the node lands in the registry with the SPREAD position assigned at origin (DefaultNodePositions), not (0,0,0)\n- Use the existing fleetRegistryAdapter pattern from cmd/mothership/main.go or a test fake implementing simulator.RegistryNodeAdapter\n\nAcceptance criteria:\n- New test covers create + update (position change) flowing into the registry\n- Test asserts the registry-side position matches the spread position assigned by DefaultNodePositions\n- Test passes: go test ./internal/simulator/... ./internal/api/...\n- No regressions in the existing test suites","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T13:19:16.172808818Z","updated_at":"2026-07-06T16:15:45.746505319Z","closed_at":"2026-07-06T16:15:45.746505319Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-69ym","depends_on_id":"bf-5lii","type":"blocks","created_at":"2026-07-06T13:19:39.265170092Z","created_by":"cli","thread_id":""}],"comments":[{"id":67,"issue_id":"bf-69ym","author":"cli","text":"Closed by end-to-end test for the simulator→fleet-registry position flow (split from bf-4oiz).\n\nAdded two tests in mothership/internal/api/simulator_test.go that wire a real SQLite-backed *fleet.Registry behind the SimulatorHandler via a test-local fleetRegistryTestAdapter (mirrors cmd/mothership/main.go's fleetRegistryAdapter, which lives in package main and isn't importable from tests):\n\n- TestSimulatorHandlerToRegistry_CreateAndUpdate: AddNode (no position) → VirtualNodeStore.CreateVirtualNode → FleetRegistryBridge.SyncToRegistry → adapter.AddVirtualNode into the live registry; asserts the node lands at exactly DefaultNodePositions(space,1) (never (0,0,0)), then UpdateNode → SetNodePosition asserting the new position flows through to the same registry row without duplicating it.\n- TestSimulatorHandlerToRegistry_OriginNodesGetSpreadGeometry: 4 nodes posted at the DB default origin {0,0,1} must land in the registry at the distinct, spread-out DefaultNodePositions(space,4) set — never co-located at the origin (the bf-18yn / bf-4q5w failure mode).\n\nThese exercise the complete path the bridge unit tests (fake adapter) and handler immediate-sync tests (recording fake, explicit positions) did not: a position round-tripped through a real fleet.Registry, with origin-default nodes confirmed spread. go test ./internal/simulator/... ./internal/api/... and full go test/vet ./... green. Commit 1456574.","created_at":"2026-07-06T16:09:52.920594256Z"},{"id":68,"issue_id":"bf-69ym","author":"cli","text":"End-to-end lock-in test added (commit 1456574, pushed to origin/main): TestSimulatorHandlerToRegistry_CreateAndUpdate drives AddNode -> VirtualNodeStore.CreateVirtualNode -> FleetRegistryBridge.SyncToRegistry -> adapter.AddVirtualNode/SetNodePosition into a REAL SQLite-backed fleet.Registry (via a test-local fleetRegistryTestAdapter mirroring cmd/mothership's fleetRegistryAdapter, since the prod adapter lives in package main and is not importable). It asserts the node lands in the live registry at the DefaultNodePositions spread slot (never the degenerate (0,0,0)/DB origin) and that a position change flows through to the same row via SetNodePosition without duplicating. A second test, TestSimulatorHandlerToRegistry_OriginNodesGetSpreadGeometry, asserts all 4 origin nodes map to exactly the DefaultNodePositions set. Verified: go test ./internal/simulator/... ./internal/api/... passes, full go test ./... green, go vet ./... clean. No regressions.","created_at":"2026-07-06T16:15:36.916712607Z"}]} @@ -116,12 +139,13 @@ {"id":"bf-95tx","title":"Add integration test for position propagation","description":"Add integration test or log output that verifies positions flow through all stages: simulator → registry → fusion engine.\n\nScope:\n- Write integration test that traces position data through the full pipeline\n- Or add comprehensive logging that shows position flow at each stage\n- Ensure test/logs can be run to verify the complete data path\n\nAcceptance criteria:\n- Integration test or logging mechanism added\n- Test/logs show positions flowing from simulator to registry\n- Test/logs show positions flowing from registry to fusion engine\n- Full pipeline verification can be demonstrated","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T04:56:29.523616935Z","updated_at":"2026-07-06T17:14:54.756951528Z","closed_at":"2026-07-06T17:14:54.756951528Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-95tx","depends_on_id":"bf-u7ds","type":"blocks","created_at":"2026-07-06T04:56:33.029540394Z","created_by":"cli","thread_id":""}],"comments":[{"id":71,"issue_id":"bf-95tx","author":"cli","text":"Implemented TestPositionPropagation_FullPipeline (mothership/internal/api/simulator_test.go) — an end-to-end lock-in tracing ONE node position through all three runtime stages behind the SimulatorHandler: (1) simulator VirtualNodeStore, (2) fleet.Registry SQLite, (3) fusion.Engine nodePos mirror. Wires the production chain (handler → bridge.SyncToRegistry → adapter → Registry, with adapter forwardPos → fleet.Manager.ForwardNodePosition → sink → fusion.Engine.SetNodePosition), posts a node at AddNode and again at UpdateNode, and asserts every stage holds the same value. A pipelineStages helper logs each stage under go test -v so the full data path is observable: [create] simulator={1.5 2.5 1} registry={1.5 2.5 1} fusion={1.5 2.5 1} / [update] simulator={3.5 4.5 2} registry={3.5 4.5 2} fusion={3.5 4.5 2}. The update leg also asserts no stage retains the stale pre-update position and a single node stays single at every stage. go test ./... + go vet ./... green. Committed c69b0f7, pushed to main.","created_at":"2026-07-06T17:14:08.490298242Z"}]} {"id":"bf-ao8eq","title":"Detection explainability (Component 28)","description":"## Goal\nImplement 'Why is this here?' on any blob/alert that shows exactly why the system made that decision.\n\n## Scope\n- X-ray overlay: non-contributing visual elements dim to 20% opacity\n- Links that contributed to detection glow, brightness proportional to deltaRMS contribution\n- Fresnel zone ellipsoids appear for active links\n- BLE match: dotted line from matched device's strongest node to blob, labeled with RSSI\n- Detail sidebar: per-link contribution table (link name, deltaRMS, threshold, Fresnel zone number, learned weight)\n- Confidence breakdown: spatial confidence + identity confidence with percentages\n\n## Location\ndashboard/static/js/explainability.js (new module)\ninternal/api/explain.go (new package)\n\n## Acceptance\n- Tap/click blob in 3D view → 'Why?' button appears\n- Tap 'Why?' → X-ray overlay activates, showing contributing links\n- Detail sidebar shows per-link breakdown\n- For alerts: specific conditions that triggered with values vs thresholds\n- Makes false positive cause obvious","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:05:43.300430327Z","updated_at":"2026-05-05T22:16:31.461661320Z","closed_at":"2026-05-05T22:16:31.461661320Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-awtza","title":"MQTT command/rebaseline and HA auto-discovery lifecycle management","description":"Plan specifies full HA auto-discovery lifecycle: configs published with retain=true on first connect AND whenever zones/persons are added or renamed; when zone or person is deleted, publish empty retained payload to remove HA entity. Also missing: rebaseline command subscription wiring. Currently mqtt/client.go publishes discovery on connect but has no mechanism to detect zone/person CRUD events and re-publish or un-publish discovery configs. Needs event bus subscription for zone/person changes.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":3,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:26:21.696828674Z","updated_at":"2026-05-06T13:53:20.089856369Z","closed_at":"2026-05-06T13:53:20.089856369Z","source_repo":".","compaction_level":0} -{"id":"bf-f841","title":"Test blob identity fields at runtime","description":"## Objective\nVerify that the blob identity fields work correctly at runtime without errors.\n\n## Scope\n- Run the application and test blob creation\n- Verify no runtime errors from undefined fields\n- Test blob access and identity field usage\n\n## Acceptance Criteria\n- [ ] Application starts without runtime errors\n- [ ] Blob objects can be created successfully\n- [ ] Identity fields can be accessed without errors\n- [ ] No undefined field access errors in console","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T05:11:44.825007401Z","updated_at":"2026-07-06T05:11:44.825007401Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-f841","depends_on_id":"bf-56uk","type":"blocks","created_at":"2026-07-06T05:12:09.557982125Z","created_by":"cli","thread_id":""}]} +{"id":"bf-f841","title":"Test blob identity fields at runtime","description":"## Objective\nVerify that the blob identity fields work correctly at runtime without errors.\n\n## Scope\n- Run the application and test blob creation\n- Verify no runtime errors from undefined fields\n- Test blob access and identity field usage\n\n## Acceptance Criteria\n- [ ] Application starts without runtime errors\n- [ ] Blob objects can be created successfully\n- [ ] Identity fields can be accessed without errors\n- [ ] No undefined field access errors in console","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T05:11:44.825007401Z","updated_at":"2026-07-07T00:10:45.863110085Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child","umbrella"],"dependencies":[{"issue_id":"bf-f841","depends_on_id":"bf-56uk","type":"blocks","created_at":"2026-07-06T05:12:09.557982125Z","created_by":"cli","thread_id":""},{"issue_id":"bf-f841","depends_on_id":"bf-15oi","type":"blocks","created_at":"2026-07-07T00:10:24.224460207Z","created_by":"cli","thread_id":""}]} {"id":"bf-iner","title":"Implement BLE identity matching logic for blobs","description":"## Objective\nImplement frontend logic to match BLE registry identities to blob IDs, so each blob gets its assigned person's name and color.\n\n## Scope\n- Identify where BLE identity data is available in the frontend (from spaxel-2wg work)\n- Write matching logic: given a blob ID, find the corresponding BLE identity and extract name/color\n- Handle unmatched blobs (no BLE identity found)\n- Store matched identity in the blob data structure (from child bf-64h5)\n\n## Dependencies\nDepends on: bf-64h5 (blob data structure must exist first)\n\n## Acceptance Criteria\n- [ ] Identity matching function exists and is callable\n- [ ] Matched blobs have personName and assignedColor populated\n- [ ] Unmatched blobs have identityResolved=false and no name/color\n- [ ] No crashes when BLE identity data is missing or malformed\n\n## Notes\nThis is data-only work. Still no rendering yet.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T04:04:22.989709866Z","updated_at":"2026-07-06T04:04:22.989709866Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-iner","depends_on_id":"bf-64h5","type":"blocks","created_at":"2026-07-06T04:04:51.962111901Z","created_by":"cli","thread_id":""}],"comments":[{"id":89,"issue_id":"bf-iner","author":"cli","text":"bf-5cgc handoff → notes/bf-5cgc-handoff.md §1.1. Server-side BLE→blob matching already EXISTS: ble.IdentityMatcher.GetMatch(blobID) (internal/ble/identity.go:609) returns *IdentityMatch{PersonID,PersonName,PersonColor,DeviceAddr,Confidence,TriangulationPos}. Your bead is the FRONTEND mirror of that match. The Go side already populates the sidecar each tick (main.go:2113 identityMatcher.UpdateBlobs); the Tier-1 fixes under bf-5151 thread that sidecar into automation/explainability/volume. Don't reimplement matching server-side — consume GetMatch.","created_at":"2026-07-06T20:39:00.011414748Z"}]} {"id":"bf-m6f5g","title":"GET /api/baseline and POST /api/baseline/capture endpoints","description":"Plan's REST API spec defines GET /api/baseline (returns [{link_id, snapshot_time, confidence}] for all links) and POST /api/baseline/capture (optional ?links body, starts 60s quiet-room capture). The baselines SQLite table exists (from migrations.go) and the baseline system runs internally, but no HTTP endpoints expose read/capture to the dashboard. The fleet handler has /api/nodes/:mac/rebaseline and /api/nodes/rebaseline-all but no standalone baseline endpoints.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:25:32.710840129Z","updated_at":"2026-05-05T17:55:00.504558262Z","closed_at":"2026-05-05T17:55:00.504558262Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-qonqo","title":"GET /api/zones/:id/history occupancy history endpoint","description":"Plan's REST API spec defines GET /api/zones/:id/history?period=24h returning [{timestamp, count, people:[]}] in hourly buckets. The zones CRUD exists (internal/api/zones.go) but the history sub-endpoint is not implemented. The anomaly/pattern system stores per-zone per-hour data in anomaly_patterns, and zone occupancy is tracked in memory and SQLite. Needed for the 'Zone history' quick action and the occupancy chart in the dashboard sidebar.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-02T18:26:02.249193078Z","updated_at":"2026-05-05T21:44:59.734545703Z","closed_at":"2026-05-05T21:44:59.734545703Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-u7ds","title":"Verify and fix fusion engine position flow","description":"Ensure that positions from the registry correctly reach the fusion engine accumulation grid.\n\nScope:\n- Verify that registry sends positions to fusion engine\n- Check that fusion engine receives and accumulates positions correctly\n- Fix any missing connections in the registry-to-fusion path\n\nAcceptance criteria:\n- Registry sends positions to fusion engine\n- Fusion engine accumulation grid receives positions\n- Any missing wiring is added\n- Positions flow from registry to fusion engine correctly","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T04:56:23.503810524Z","updated_at":"2026-07-06T16:50:28.746899623Z","closed_at":"2026-07-06T16:50:28.746899623Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","split-child"],"dependencies":[{"issue_id":"bf-u7ds","depends_on_id":"bf-4oiz","type":"blocks","created_at":"2026-07-06T04:56:26.193059903Z","created_by":"cli","thread_id":""}],"comments":[{"id":70,"issue_id":"bf-u7ds","author":"cli","text":"Verified and completed the registry→fusion engine position flow.\n\nThe fix (commit dbb5cd0) was committed locally by a prior run but never pushed to origin and the bead was never closed — that is why it re-dispatched. I pushed dbb5cd0 to origin/main and verified the complete path.\n\nAll three registry-write paths now converge on the blob-producing fusion engine's accumulation grid:\n1. REST PATCH /position → fleet.Handler.updateNodePosition → ForwardNodePosition → sink → fusion.Engine.SetNodePosition\n2. Node connect/register → fleet.Manager.OnNodeConnected → ForwardNodePosition → sink → fusion.Engine.SetNodePosition\n3. Simulator sync → fleetRegistryAdapter.forwardPos → ForwardNodePosition → sink → fusion.Engine.SetNodePosition (the path this bead closed; previously landed positions in SQLite only)\n\nPlus startup seeding (main.go reads DB → fusionEngine.SetNodePosition). The engine's Fuse() snapshots nodePos each tick and feeds grid.AddLinkInfluence — so positions reach the accumulation grid.\n\nLock-ins all green:\n- TestSimulatorHandlerToRegistry_PositionsReachFusionEngine (path 3, bf-u7ds)\n- TestHandlerUpdateNodePositionForwardsToEngine (path 1)\n- TestManagerOnNodeConnectedForwardsPositionToEngine (path 2)\n- TestEngine_SeedNodePositions / TestEngine_FourLinks_PositionAccuracy (grid consumes positions)\n\ngo vet ./... clean; go test ./... = 50 packages, exit 0.","created_at":"2026-07-06T16:49:17.319768169Z"}]} {"id":"bf-usafo","title":"Morning briefing (Component 35)","description":"## Goal\nWhen user first opens dashboard each day, brief warm summary appears.\n\n## Content (generated from existing data):\n- Sleep summary (if available): 'You slept 7h 39m — 12 minutes more than your average. Breathing was regular.'\n- Who is home: 'Bob left at 8:15am. The house has been empty since 8:22am.'\n- Overnight anomalies: 'Last night: One unusual event at 2:34am — motion in kitchen for 30 seconds. No BLE match, low-confidence blob. Likely environmental.'\n- System health: 'System health: Excellent (94%). All 6 nodes online. Accuracy improved 2% this week thanks to your 8 corrections.'\n- Today's forecast: 'Based on your Wednesday pattern, you usually return around 5:45pm. Security mode will auto-activate when you leave.'\n\nDisplay:\n- Expert mode: card overlay on first dashboard open, dismissible with tap or 'Got it' button. Slides away after 10s if not interacted\n- Simple mode: morning card is first card in layout, stays visible until dismissed\n- Ambient mode: text fades in over ambient display when first person detected in morning, stays for 30s\n\nDelivery channels:\n- Dashboard (default)\n- Push notification at configured time (e.g., 7am)\n- Webhook to Slack/Discord\n\n## Implementation\nGo function GenerateBriefing(date string, person string) string\nAssembled in priority order: critical alerts → sleep → who's home → anomalies → system health → predictions → learning progress\nStored as daily record in briefings table\n\n## Acceptance\n- Briefing accurately summarizes overnight activity\n- Shows sleep report when available\n- Lists overnight anomalies with context\n- Shows system health\n- Shows today's predictions\n- Dismissible and non-intrusive","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-foxtrot","created_at":"2026-05-05T04:06:11.592787579Z","updated_at":"2026-05-06T10:09:43.916190927Z","closed_at":"2026-05-06T10:09:43.916190927Z","close_reason":"Implemented Component 35: Morning Briefing\n\n- Added briefing dashboard adapter wiring to hub for morning briefing push\n- Morning briefing provides warm summary when user first opens dashboard each day\n- Content includes: sleep summary (if available), who is home, overnight anomalies, system health, predictions, learning progress\n- Briefing generated automatically via GenerateBriefing(date, person) function\n- Stored as daily record in briefings table\n- Delivery channels: dashboard (card overlay on first open), push notification at configured time, webhook to Slack/Discord\n- Display modes: expert mode (dismissible card overlay), simple mode (first card), ambient mode (fade-in text)\n- Briefing scheduler generates daily briefings at configured time and sends push notifications\n- API endpoints: GET/POST /api/briefing/* for managing briefings\n- All tests passing (go test ./internal/briefing/...)\n","source_repo":".","compaction_level":0} +{"id":"bf-vuzie","title":"Verify /api/nodes lists the sim nodes as online","description":"Final step of the chain (depends on the frames/s child). While/after the sim run, confirm the mothership API lists the 4 sim nodes as online. This is the last acceptance gate for the chain and for parent bf-3hji.\n\nThe harness (mothership/tests/e2e/e2e_test.go, GetNodes) treats a node as 'online' when last_seen_at is within the last 30 seconds.\n\n## Scope\n- During a sim run, GET http://localhost:8080/api/nodes.\n- Confirm the 4 sim nodes are present and online.\n\n## Acceptance Criteria\n- [ ] GET http://localhost:8080/api/nodes returns the 4 sim nodes.\n- [ ] Each sim node's last_seen_at is within 30s of now (online per harness logic).\n- [ ] Result (sample JSON / node count) recorded in the bead body.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-07T05:19:57.465795310Z","updated_at":"2026-07-07T05:19:57.465795310Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-vuzie","depends_on_id":"bf-4ads8","type":"blocks","created_at":"2026-07-07T05:20:08.455248674Z","created_by":"cli","thread_id":""}]} {"id":"bf-w15bj","title":"CI: golangci-lint static analysis gate","description":"## Goal\nAdd golangci-lint to the Argo CI workflow as a hard quality gate, per plan §Quality Gates / Definition of Done (item 3).\n\n## Configuration\nFile: .golangci.yml at repo root\n\nEnabled linters (minimum set):\n- errcheck: all errors must be handled or explicitly discarded with _\n- staticcheck: includes S-series (simplifications) and SA-series (bugs)\n- gosimple: simplification suggestions (SA-series overlap)\n- govet: same as go vet but integrated\n- ineffassign: catch dead assignments\n- unused: catch unused exported identifiers\n\nDisabled (too noisy for this codebase):\n- gocyclo, funlen, wsl (style preferences, not correctness)\n\n## CI integration\nAdd to the spaxel-build Argo WorkflowTemplate as a parallel step alongside go test:\n golangci-lint run --timeout 5m ./...\n\n## Acceptance\n- golangci-lint run passes on the current codebase (fix any pre-existing findings before adding the gate)\n- Argo CI fails on new lint violations\n- .golangci.yml committed to repo root","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-golf","created_at":"2026-05-02T12:09:09.633464353Z","updated_at":"2026-05-05T06:49:00.297475830Z","closed_at":"2026-05-05T06:49:00.297475830Z","close_reason":"Completed","source_repo":".","compaction_level":0} {"id":"bf-xrej","title":"Seed default spread geometry in the simulator registry bridge path","description":"Child of bf-18yn umbrella. Depends on child 2.\n\nProblem: the simulator FleetRegistryBridge (internal/simulator package) writes node.Position.X/Y/Z into the fleet registry via AddVirtualNode and SetNodePosition in its SyncToRegistry and SyncOneNode methods. When virtual nodes are created without an explicit position they carry (0,0,1)-style defaults, so the registry and downstream fusion engine see co-located nodes.\n\nScope:\n- In the virtual-node creation path feeding FleetRegistryBridge, and/or in SyncToRegistry/SyncOneNode when a node position is the default origin, assign the spread-out default geometry from child 1 helper (sized to roomWidth/roomDepth/roomHeight) instead of leaving co-located/origin positions.\n- Ensure those positions flow into both the registry and the fusion engine via the existing wiring.\n\nAcceptance criteria:\n- SyncToRegistry writes distinct, non-degenerate positions for default virtual nodes (no two co-located, not all at origin).\n- Existing internal/simulator tests pass; go build ./... passes.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-03T22:10:00.435559247Z","updated_at":"2026-07-03T23:03:03.374990382Z","closed_at":"2026-07-03T23:03:03.374990382Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-xrej","depends_on_id":"bf-24xp","type":"blocks","created_at":"2026-07-03T22:10:08.866035772Z","created_by":"cli","thread_id":""}],"comments":[{"id":57,"issue_id":"bf-xrej","author":"cli","text":"Implemented: virtual nodes still at the DB-default origin (0,0,1) are reassigned distinct, spread-out geometry via a new effectivePositions() helper in registry_bridge.go, sourced from DefaultNodePositions sized to the full node count across the store's space. Slot assignment is keyed by sorted node ID (deterministic across map iteration), and SyncOneNode resolves over the full node set so it writes the same position a full SyncToRegistry would. Explicit (non-origin) placements pass through unchanged; positions flow through SyncToRegistry, SyncOneNode, and ToRegistryRecords into the registry and the fusion-engine seeding. Added registry_bridge_test.go covering isDefaultOrigin, spread geometry for origin fleets (counts 1-9), explicit-position preservation, mixed sets, idempotent re-sync, and SyncOneNode/full-sync parity. go build ./..., go vet ./..., and go test ./... all green. Pushed as 307ab0e.","created_at":"2026-07-03T23:01:11.527240563Z"}]} {"id":"spaxel-05a","title":"Implement calibration GET endpoint","description":"## Task\nImplement GET /api/floorplan/calibrate endpoint.\n\n## Specification\n- Return current calibration from SQLite\n- Return 404 if no calibration exists\n\n## Acceptance\n- Returns calibration data when present\n- Returns 404 when no calibration exists","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"charlie","owner":"","created_at":"2026-04-07T17:55:53.178762085Z","created_by":"coding","updated_at":"2026-04-07T18:58:38.551564957Z","closed_at":"2026-04-07T18:58:38.551463596Z","close_reason":"done","closed_by_session":"","source_system":"","source_repo":".","deleted_by":"","delete_reason":"","original_type":"","compaction_level":0,"original_size":0,"sender":"","labels":["mitosis-child","mitosis-depth:1","parent-spaxel-klk"]}