Snapshot for continuing in Claude Code (which runs locally on the Mac and can build, launch the app, drive BLE, and read the unified log — things the Cowork session could not do from its sandbox).
- Live Muse S streaming confirmed.
--with-brainflowbuild is green; the app connects to a real Muse S (boardID=39),prepare_session status=0, 4 channels @ ~256 Hz steady for minutes. The earlier "synthetic / no signal" was a stale stub binary:build.sh --with-brainflowhad been failing to compile, sorun-*.shrelaunched the last good (stub) binary, and a stub bridge always resolves to synthetic. Fixed now.
34c886cfix(phase-b): plotter subscribes when the stream first becomes available7dba55echore(scripts): add golden-recording runner9f21c38fix(phase-b): compile the debug-window plotter stream wrapper (AsyncThrowingStream Never→any Error)7309db5test: unblock BCICoreTests/BCIEEGTests under Swift 6 strict concurrency611879dfix(eeg-stream): implement true multicast sample fan-outa1b9a4bfeat(phase-b): wire channel-health provider into debug window557c452feat(health): per-channel RMS health classifier and provider
Working tree is clean except intentionally-untracked local files (CLAUDE.md,
requirements-calibration.txt, research artifacts). Recordings/ is gitignored.
AppViewModel.liveSampleStream() is now backed by AsyncMulticastChannel<EEGSample>
(Sources/BCICore/Buffers/), a true one-to-many broadcast: every call returns
an independent subscriber that receives the full stream. The old code shared a
single AsyncStream, so the plotter and the health provider silently split the
samples ~50/50. Both consumers now get everything. BoundedAsyncChannel is
unchanged (single-consumer queue).
- Confirm the plotter fix on device. Open Phase B (Cmd+Shift+D) on the live Muse and verify the trace renders at the same time as the health badges, and that a TP9 electrode lift turns only TP9 red while the plotter keeps drawing. This is the payoff of the multicast + plotter-subscription fixes and is the one thing not yet visually confirmed.
- Capture the golden recording.
./Scripts/record-golden.sh(Muse on, not connected to the Muse phone app). It narrates a timed protocol and writesRecordings/golden/golden_<stamp>.csv+ a.segments.csvtimeline. Enable Record in the app right when you press ENTER so CSV t≈0 aligns with the protocol. - Run the unblocked tests.
swift test—BCICoreTests/BCIEEGTestsnow compile; confirmAsyncMulticastChannelTests, the rewrittenBoundedAsyncChannelTests.testTwoConsumersSplitTheStream, andFanOutHealthValidationTestspass on a real toolchain. - Wire the golden recording into the harness.
FanOutHealthValidationTestscurrently drives synthetic samples; swap the feed loop forPlaybackEEGStreampointed at the golden CSV, and slice by the.segments.csvto assert alpha rises duringeyes_closed_restand only TP9 goes dead duringtp9_electrode_lift. That makes the whole broadcast+health contract a CI regression against real data. - Then proceed to the SceneKit workspace (drive it from the verified stream) before richer visualization (PCA/UMAP/connectivity).
- Must build with
--with-brainflow; a plainbuild.shstub binary → synthetic. Check withotool -L .build/debug/NeuralCompose | grep -i board(should listlibBoardController). - Connect logs go to the unified log, not stdout:
log stream --predicate 'subsystem == "com.neuralcompose"' --level debug. - The MLX
Complex/Float16notes during the build are harmless C-interop notes, not errors.