hsl GUI objects don't initialize in -nogui mode, causing
cutoff at 0 Hz and volume at 0 → no audio output.
Use loadbang + float for headless-safe initialization.
- scripts/diagnostics/test-pipeline.py: full end-to-end test
- just diag-full: run pipeline diagnostic
- just viz-log/midi-log: watch service logs live
- just viz-stats/midi-stats: check counters
- just reset-mcu: reset MCU + reopen serial via router RPC
- just test-chord: send test MIDI chord locally
- just fix-perms: restore +x bits on all scripts
Background thread stack (500 bytes) overflows after ~50 draw_frame
calls. __loopHook (called after loop() returns) is the fallback.
With delay(1000), it ran once per second — too slow.
Reducing to delay(10) keeps RPC processing responsive.
RouterBridge on MCU ignores NOTIFICATION (type 2) messages.
Must use REQUEST (type 0) for draw_frame, then drain the
response to prevent router buffer buildup.
Viz was sending REQUEST (type 0) but never reading responses.
Router response buffer filled up, blocking all subsequent RPCs.
NOTIFICATION (type 2) expects no response — no buffer buildup.
Without Bridge.run() in loop(), the MCU never reads incoming
draw_frame requests from the router. First frame goes through
during setup handshake, but subsequent frames fill the router's
send buffer and the viz stalls.
- just deploy: stash local board changes before git pull
- install.sh: configure git alias 'pull-clean' for manual use
- Board-side changes from test edits are discarded on deploy
- Permanent rename: led-matrix-bridge.ino → sketch.ino (Arduino CLI convention)
- Add just flash-sketch recipe for compiling/flashing MCU via board's arduino-cli
- Run via post-merge hooks for auto-deploy
Rewrite led-matrix-viz.py to eliminate latency:
- Render and pack frame immediately on each MIDI event
- Send on next poll cycle (no POLLOUT dependency, no frame loop)
- Notes on/off purely from MIDI data — zero decay
- Background 5 FPS refresh for router drop recovery
- Clean stats: FPS, active_notes, midi_events, bytes_received, raw_packets
- DECAY_FRAMES default 8→3 (100ms visual trail)
- MAX_NOTE_AGE safety cap: force-expire notes after 15 frames
- Track note age separately from decay to prevent stale notes
- Add FPS and active_notes to viz stats for diagnostics
- Fix router reconnect: unregister old fd, not new one
- .githooks/pre-push: validate Pd patches via python3 pd-validator
- .githooks/post-merge: auto-restart services when systemd/scripts change
- install.sh: configure core.hooksPath at install time
- justfile deploy: set hooksPath after git pull
- Create skills: rego-pd-validator, usb-gadget-configfs, led-matrix-msgpack-rpc
- Update pd-audio-synthesis skill with Uno Q specifics
- Add Pd version check (min 0.54.0) to start-synth.sh
- Add DO NOT EDIT headers to generated pd patches
- Add Gitea Actions CI workflow for patch validation
- Add ConditionPathExists guards to legacy systemd services
- Add 'just ci' recipe for CI
Root cause: synth-diagnostic uses systemctl start to start pipeline
services, but those services had After=synth-diagnostic — causing
deadlock (they waited for synth-diagnostic to exit, which was waiting
for them).
Fix: pipeline services no longer list synth-diagnostic in After=.
synth-diagnostic has Before= them for boot ordering, and starts them
directly via systemctl start in run-tests.sh.
Also fixed prometheus-exporter: had After=synth-diagnostic backwards
— should start before synth-diagnostic (synth-diagnostic has
After=prometheus-exporter).
- dashboard.html: standalone HTML, fetches :9090/metrics, auto-refresh
- Exporter serves dashboard at /dashboard and /dashboard.html
- Cards: Pipeline, Audio, Mixer, MIDI Bridge, LED Viz
- Live gauges, counters, service status dots, last-activity timestamps
midi-bridge: removed HTTP server on :9091, now writes /tmp/midi-stats.json
prometheus-exporter: reads /tmp/midi-stats.json, exposes all metrics on :9090/metrics
Single scrape endpoint for all pipeline components