diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 41461b0..4a698ca 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -65,7 +65,7 @@ {"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-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-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":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T05:11:28.984747608Z","updated_at":"2026-07-06T20:14:35.102620391Z","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":"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-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":""}]} @@ -77,7 +77,7 @@ {"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":"open","priority":2,"issue_type":"task","created_at":"2026-07-06T05:11:33.679872377Z","updated_at":"2026-07-06T05:11:33.679872377Z","source_repo":".","compaction_level":0,"labels":["split-child"],"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":""}],"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":"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":"in_progress","priority":2,"issue_type":"task","assignee":"claude-code-glm-5-alpha","created_at":"2026-07-06T05:11:33.679872377Z","updated_at":"2026-07-06T21:00:59.097572783Z","source_repo":".","compaction_level":0,"labels":["failure-count:1","split-child"],"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":""}],"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":"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-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} diff --git a/.needle-predispatch-sha b/.needle-predispatch-sha index 97a5a4f..9c1e9eb 100644 --- a/.needle-predispatch-sha +++ b/.needle-predispatch-sha @@ -1 +1 @@ -86b903f090127c4e921e0cd421900f8c616f5818 +1446ccf65fe5366db9a1ac0e1adef608548473d5 diff --git a/mothership/internal/api/tracks.go b/mothership/internal/api/tracks.go index 9728fb5..c4dbb91 100644 --- a/mothership/internal/api/tracks.go +++ b/mothership/internal/api/tracks.go @@ -23,7 +23,15 @@ type Track struct { PersonColor string `json:"person_color,omitempty"` IdentityConfidence float64 `json:"identity_confidence,omitempty"` IdentitySource string `json:"identity_source,omitempty"` - Posture string `json:"posture,omitempty"` + // Canonical identity fields (bf-5151). camelCase JSON keys are the canonical + // API-facing names; the snake_case PersonLabel/PersonColor above are retained + // as deprecated aliases. Propagated from signal.TrackedBlob in listTracks — + // zero today because the source struct is not yet populated (a follow-up bead + // wires the BLE identity sidecar). + PersonName string `json:"personName,omitempty"` + AssignedColor string `json:"assignedColor,omitempty"` + IdentityResolved *bool `json:"identityResolved,omitempty"` // tri-state: nil=unattempted, &true=resolved, &false=failed + Posture string `json:"posture,omitempty"` } // TrackedBlob is an alias for signal.TrackedBlob. @@ -113,6 +121,9 @@ func (h *TracksHandler) listTracks(w http.ResponseWriter, r *http.Request) { PersonColor: b.PersonColor, IdentityConfidence: b.IdentityConfidence, IdentitySource: b.IdentitySource, + PersonName: b.PersonName, + AssignedColor: b.AssignedColor, + IdentityResolved: b.IdentityResolved, Posture: b.Posture, } } diff --git a/mothership/internal/dashboard/hub.go b/mothership/internal/dashboard/hub.go index d12a45f..c839f2d 100644 --- a/mothership/internal/dashboard/hub.go +++ b/mothership/internal/dashboard/hub.go @@ -550,7 +550,15 @@ type blobJSON struct { PersonColor string `json:"person_color,omitempty"` IdentityConfidence float64 `json:"identity_confidence,omitempty"` IdentitySource string `json:"identity_source,omitempty"` - Replay bool `json:"replay"` // true when blob is from time-travel replay + // Canonical identity fields (bf-5151). camelCase JSON keys are the canonical + // dashboard-facing names; the snake_case PersonLabel/PersonColor above are + // retained as deprecated aliases. Propagated from tracking.Blob in + // BroadcastLocUpdate below — zero today because the source struct is not yet + // populated (a follow-up bead wires the BLE identity sidecar). + PersonName string `json:"personName,omitempty"` + AssignedColor string `json:"assignedColor,omitempty"` + IdentityResolved *bool `json:"identityResolved,omitempty"` // tri-state: nil=unattempted, &true=resolved, &false=failed + Replay bool `json:"replay"` // true when blob is from time-travel replay } // BroadcastLocUpdate sends localisation results to all dashboard clients. @@ -576,6 +584,9 @@ func (h *Hub) BroadcastLocUpdate(blobs []tracking.Blob) { PersonColor: b.PersonColor, IdentityConfidence: b.IdentityConfidence, IdentitySource: b.IdentitySource, + PersonName: b.PersonName, + AssignedColor: b.AssignedColor, + IdentityResolved: b.IdentityResolved, } }