# requirement_nodes

id: MIDI
title: MIDI Signal Path
description: Low-latency MIDI input, pass-through, and derived signal processing
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:40:35.614338
updated_at: 2026-03-17 04:53:29.853840
acceptance_criteria:
- [MIDI-01] Host receives MIDI note_on/note_off events via USB
- [MIDI-02] MIDI pass-through to USB output completes in <2ms p99 on dedicated thread
- [MIDI-03] Read-only tap captures MIDI events without blocking pass-through
- [MIDI-04] MIDI channel filtering supports omni and single-channel modes
- [MIDI-05] Note On with velocity=0 is converted to Note Off
- [MIDI-06] CC messages throttled to ~30Hz
- [MIDI-07] Ghost notes below min_velocity filtered

test_spec:
GIVEN the MIDI Signal Path pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: MIDI-01
title: Host receives MIDI note_on/note_off events via USB
description: Host receives MIDI note_on/note_off events via USB
priority: critical
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:35.863208
updated_at: 2026-03-17 05:26:45.306259
acceptance_criteria:
- [MIDI-01.1] Open USB MIDI input port via mido/rtmidi
- [MIDI-01.2] Parse note_on and note_off message types
- [MIDI-01.3] Auto-detect first available MIDI port if not specified

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN host receives midi note_on/note_off events via usb
THEN Open USB MIDI input port via mido/rtmidi
AND Parse note_on and note_off message types
AND Auto-detect first available MIDI port if not specified


id: MIDI-01.1
title: Open USB MIDI input port via mido/rtmidi
description: Open USB MIDI input port via mido/rtmidi
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-01
acceptance_criteria: Open USB MIDI input port via mido/rtmidi
test_status: not_written
created_at: 2026-03-17 04:40:36.110893
updated_at: 2026-03-17 04:53:33.959885
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the receiving endpoint is active
THEN Open USB MIDI input port via mido/rtmidi
AND received data matches what was sent


id: MIDI-01.2
title: Parse note_on and note_off message types
description: Parse note_on and note_off message types
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-01
acceptance_criteria: Parse note_on and note_off message types
test_status: not_written
created_at: 2026-03-17 04:40:36.364896
updated_at: 2026-03-17 04:53:35.993232
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the feature is exercised
THEN Parse note_on and note_off message types


id: MIDI-01.3
title: Auto-detect first available MIDI port if not specified
description: Auto-detect first available MIDI port if not specified
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-01
acceptance_criteria: Auto-detect first available MIDI port if not specified
test_status: not_written
created_at: 2026-03-17 04:40:36.613360
updated_at: 2026-03-17 04:53:38.047425
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the detection/scanning process runs
THEN Auto-detect first available MIDI port if not specified


id: MIDI-02
title: MIDI pass-through completes in <2ms p99 on dedicated thread
description: MIDI pass-through to USB output completes in <2ms p99 on dedicated thread
priority: critical
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:36.862474
updated_at: 2026-03-17 04:53:40.081505
acceptance_criteria:
- [MIDI-02.1] Dedicated daemon thread for pass-through (no shared processing)
- [MIDI-02.2] Non-blocking write to USB MIDI output
- [MIDI-02.3] Pass-through thread never waits for LED processing

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN midi pass-through to usb output completes in <2ms p99 on dedicated thread
THEN Dedicated daemon thread for pass-through (no shared processing)
AND Non-blocking write to USB MIDI output
AND Pass-through thread never waits for LED processing


id: MIDI-02.1
title: Dedicated daemon thread for pass-through
description: Dedicated daemon thread for pass-through (no shared processing)
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-02
acceptance_criteria: Dedicated daemon thread for pass-through (no shared processing)
test_status: not_written
created_at: 2026-03-17 04:40:37.105430
updated_at: 2026-03-17 04:53:42.143167
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the feature is exercised
THEN Dedicated daemon thread for pass-through (no shared processing)


id: MIDI-02.2
title: Non-blocking write to USB MIDI output
description: Non-blocking write to USB MIDI output
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-02
acceptance_criteria: Non-blocking write to USB MIDI output
test_status: not_written
created_at: 2026-03-17 04:40:37.355710
updated_at: 2026-03-17 04:53:44.183339
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN a message is dispatched
THEN Non-blocking write to USB MIDI output
AND the recipient receives the correct payload


id: MIDI-02.3
title: Pass-through thread never waits for LED processing
description: Pass-through thread never waits for LED processing
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-02
acceptance_criteria: Pass-through thread never waits for LED processing
test_status: not_written
created_at: 2026-03-17 04:40:37.599250
updated_at: 2026-03-17 04:53:46.240545
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the system is operating under normal conditions
THEN Pass-through thread never waits for LED processing
AND this invariant holds under all tested scenarios


id: MIDI-03
title: Read-only tap captures MIDI events without blocking pass-through
description: Read-only tap captures MIDI events without blocking pass-through
priority: critical
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:37.848929
updated_at: 2026-03-17 04:53:48.282139
acceptance_criteria:
- [MIDI-03.1] Non-blocking copy of MIDI message to processing queue
- [MIDI-03.2] Processing queue decoupled from pass-through thread

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN read-only tap captures midi events without blocking pass-through
THEN Non-blocking copy of MIDI message to processing queue
AND Processing queue decoupled from pass-through thread


id: MIDI-03.1
title: Non-blocking copy of MIDI message to processing queue
description: Non-blocking copy of MIDI message to processing queue
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-03
acceptance_criteria: Non-blocking copy of MIDI message to processing queue
test_status: not_written
created_at: 2026-03-17 04:40:38.097462
updated_at: 2026-03-17 04:53:50.307347
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the feature is exercised
THEN Non-blocking copy of MIDI message to processing queue


id: MIDI-03.2
title: Processing queue decoupled from pass-through thread
description: Processing queue decoupled from pass-through thread
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-03
acceptance_criteria: Processing queue decoupled from pass-through thread
test_status: not_written
created_at: 2026-03-17 04:40:38.345001
updated_at: 2026-03-17 04:53:52.356929
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the feature is exercised
THEN Processing queue decoupled from pass-through thread


id: MIDI-04
title: MIDI channel filtering supports omni and single-channel modes
description: MIDI channel filtering supports omni and single-channel modes
priority: high
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:38.594176
updated_at: 2026-03-17 04:53:54.404534
acceptance_criteria:
- [MIDI-04.1] Omni mode passes all channels (channel_filter = None)
- [MIDI-04.2] Single channel mode filters to one channel (1-16)

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN midi channel filtering supports omni and single-channel modes
THEN Omni mode passes all channels (channel_filter = None)
AND Single channel mode filters to one channel (1-16)


id: MIDI-04.1
title: Omni mode passes all channels (channel_filter = None)
description: Omni mode passes all channels (channel_filter = None)
priority: high
status: draft
node_type: leaf
parent_id: MIDI-04
acceptance_criteria: Omni mode passes all channels (channel_filter = None)
test_status: not_written
created_at: 2026-03-17 04:40:38.839815
updated_at: 2026-03-17 04:53:56.448459
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN input matching the filter criteria is received
THEN Omni mode passes all channels (channel_filter = None)
AND non-matching input passes through unchanged


id: MIDI-04.2
title: Single channel mode filters to one channel (1-16)
description: Single channel mode filters to one channel (1-16)
priority: high
status: draft
node_type: leaf
parent_id: MIDI-04
acceptance_criteria: Single channel mode filters to one channel (1-16)
test_status: not_written
created_at: 2026-03-17 04:40:39.085679
updated_at: 2026-03-17 04:53:58.485677
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN input matching the filter criteria is received
THEN Single channel mode filters to one channel (1-16)
AND non-matching input passes through unchanged


id: MIDI-05
title: Note On with velocity=0 is converted to Note Off
description: Note On with velocity=0 is converted to Note Off
priority: critical
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:39.334506
updated_at: 2026-03-17 04:54:00.535117
acceptance_criteria:
- [MIDI-05.1] Detect velocity=0 in note_on messages
- [MIDI-05.2] Convert to CMD_RELEASE before routing

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN note on with velocity=0 is converted to note off
THEN Detect velocity=0 in note_on messages
AND Convert to CMD_RELEASE before routing


id: MIDI-05.1
title: Detect velocity=0 in note_on messages
description: Detect velocity=0 in note_on messages
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-05
acceptance_criteria: Detect velocity=0 in note_on messages
test_status: not_written
created_at: 2026-03-17 04:40:39.585027
updated_at: 2026-03-17 04:54:02.576208
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the detection/scanning process runs
THEN Detect velocity=0 in note_on messages


id: MIDI-05.2
title: Convert to CMD_RELEASE before routing
description: Convert to CMD_RELEASE before routing
priority: critical
status: draft
node_type: leaf
parent_id: MIDI-05
acceptance_criteria: Convert to CMD_RELEASE before routing
test_status: not_written
created_at: 2026-03-17 04:40:39.839527
updated_at: 2026-03-17 04:54:04.618215
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the input is processed
THEN Convert to CMD_RELEASE before routing


id: MIDI-06
title: CC messages throttled to ~30Hz
description: CC messages throttled to ~30Hz
priority: high
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:40.086095
updated_at: 2026-03-17 04:54:06.668474
acceptance_criteria:
- [MIDI-06.1] Track last CC send time per CC number
- [MIDI-06.2] Skip CC if <33ms since last send for same CC number

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN cc messages throttled to ~30hz
THEN Track last CC send time per CC number
AND Skip CC if <33ms since last send for same CC number


id: MIDI-06.1
title: Track last CC send time per CC number
description: Track last CC send time per CC number
priority: high
status: draft
node_type: leaf
parent_id: MIDI-06
acceptance_criteria: Track last CC send time per CC number
test_status: not_written
created_at: 2026-03-17 04:40:40.342759
updated_at: 2026-03-17 04:54:08.715090
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the relevant event occurs
THEN Track last CC send time per CC number
AND the stored data is retrievable


id: MIDI-06.2
title: Skip CC if <33ms since last send for same CC number
description: Skip CC if <33ms since last send for same CC number
priority: high
status: draft
node_type: leaf
parent_id: MIDI-06
acceptance_criteria: Skip CC if <33ms since last send for same CC number
test_status: not_written
created_at: 2026-03-17 04:40:40.584120
updated_at: 2026-03-17 04:54:10.763437
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the operation is performed under load
THEN Skip CC if <33ms since last send for same CC number
AND the timing constraint is verified via measurement


id: MIDI-07
title: Ghost notes below min_velocity filtered
description: Ghost notes below min_velocity filtered
priority: medium
status: draft
node_type: requirement
parent_id: MIDI
test_status: not_written
created_at: 2026-03-17 04:40:40.837607
updated_at: 2026-03-17 04:54:12.801178
acceptance_criteria:
- [MIDI-07.1] Configurable min_velocity threshold (default 15)
- [MIDI-07.2] Notes below threshold silently dropped

test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN ghost notes below min_velocity filtered
THEN Configurable min_velocity threshold (default 15)
AND Notes below threshold silently dropped


id: MIDI-07.1
title: Configurable min_velocity threshold (default 15)
description: Configurable min_velocity threshold (default 15)
priority: medium
status: draft
node_type: leaf
parent_id: MIDI-07
acceptance_criteria: Configurable min_velocity threshold (default 15)
test_status: not_written
created_at: 2026-03-17 04:40:41.092846
updated_at: 2026-03-17 04:54:14.844187
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN the default configuration is loaded
THEN Configurable min_velocity threshold (default 15)
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: MIDI-07.2
title: Notes below threshold silently dropped
description: Notes below threshold silently dropped
priority: medium
status: draft
node_type: leaf
parent_id: MIDI-07
acceptance_criteria: Notes below threshold silently dropped
test_status: not_written
created_at: 2026-03-17 04:40:41.346125
updated_at: 2026-03-17 04:54:16.889905
test_spec:
GIVEN the MIDI subsystem is initialized with USB input/output ports
WHEN input matching the filter criteria is received
THEN Notes below threshold silently dropped
AND non-matching input passes through unchanged


id: PROTO
title: Transport & Protocol
description: Binary protocol, pluggable transport, and communication layer
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:40:41.599323
updated_at: 2026-03-17 04:54:18.937478
acceptance_criteria:
- [PROTO-01] 5-byte ProtoHeader on all packets
- [PROTO-02] Direction-annotated command registry
- [PROTO-03] LedCommand is 31 bytes
- [PROTO-04] CRC-16-CCITT togglable via FLAG_CRC_PRESENT
- [PROTO-05] Selective 2x redundancy for hit frames
- [PROTO-06] Per-packet sequence numbers for loss detection and dedup
- [PROTO-07] Transport interface abstracts send/receive
- [PROTO-08] UDP transport implementation
- [PROTO-09] Serial transport implementation
- [PROTO-10] Transport auto-detection
- [PROTO-11] Return path from clients to host
- [PROTO-12] ESP-NOW broadcast with 6-byte MAC trailer

test_spec:
GIVEN the Transport & Protocol pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: PROTO-01
title: 5-byte ProtoHeader on all packets
description: 5-byte ProtoHeader on all packets
priority: critical
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:41.852625
updated_at: 2026-03-17 04:54:20.997366
acceptance_criteria:
- [PROTO-01.1] Pack header: version(u8), msg_type(u8), flags(u8), payload_len(u16 LE)
- [PROTO-01.2] Unpack header with validation (version check, length bounds)
- [PROTO-01.3] Python and C++ implementations produce identical bytes

test_spec:
GIVEN the protocol layer is initialized
WHEN 5-byte protoheader on all packets
THEN Pack header: version(u8), msg_type(u8), flags(u8), payload_len(u16 LE)
AND Unpack header with validation (version check, length bounds)
AND Python and C++ implementations produce identical bytes


id: PROTO-01.1
title: Pack header: version(u8), msg_type(u8), flags(u8), payload_len(u16 LE)
description: Pack header: version(u8), msg_type(u8), flags(u8), payload_len(u16 LE)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-01
acceptance_criteria: Pack header: version(u8), msg_type(u8), flags(u8), payload_len(u16 LE)
test_status: not_written
created_at: 2026-03-17 04:40:42.102591
updated_at: 2026-03-17 04:54:23.038675
test_spec:
GIVEN the protocol layer is initialized
WHEN the operation is performed under load
THEN Pack header: version(u8), msg_type(u8), flags(u8), payload_len(u16 LE)
AND the timing constraint is verified via measurement


id: PROTO-01.2
title: Unpack header with validation (version check, length bounds)
description: Unpack header with validation (version check, length bounds)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-01
acceptance_criteria: Unpack header with validation (version check, length bounds)
test_status: not_written
created_at: 2026-03-17 04:40:42.354430
updated_at: 2026-03-17 04:54:25.097265
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN Unpack header with validation (version check, length bounds)
AND round-trip produces identical values


id: PROTO-01.3
title: Python and C++ implementations produce identical bytes
description: Python and C++ implementations produce identical bytes
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-01
acceptance_criteria: Python and C++ implementations produce identical bytes
test_status: not_written
created_at: 2026-03-17 04:40:42.602624
updated_at: 2026-03-17 04:54:27.147060
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN Python and C++ implementations produce identical bytes
AND round-trip produces identical values


id: PROTO-02
title: Direction-annotated command registry
description: Direction-annotated command registry
priority: critical
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:42.855534
updated_at: 2026-03-17 04:54:29.186738
acceptance_criteria:
- [PROTO-02.1] Forward commands: 0x01-0x0F (Piâ†’Gatewayâ†’Client)
- [PROTO-02.2] Return commands: 0x10-0x1F (Clientâ†’Gatewayâ†’Pi)
- [PROTO-02.3] Pairing commands: 0x20-0x2F
- [PROTO-02.4] Gateway commands: 0x30-0x3F
- [PROTO-02.5] OTA commands: 0x40-0x4F

test_spec:
GIVEN the protocol layer is initialized
WHEN direction-annotated command registry
THEN Forward commands: 0x01-0x0F (Piâ†’Gatewayâ†’Client)
AND Return commands: 0x10-0x1F (Clientâ†’Gatewayâ†’Pi)
AND Pairing commands: 0x20-0x2F
AND Gateway commands: 0x30-0x3F
AND OTA commands: 0x40-0x4F


id: PROTO-02.1
title: Forward commands: 0x01-0x0F (Pi to Gateway to Client)
description: Forward commands: 0x01-0x0F (Piâ†’Gatewayâ†’Client)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-02
acceptance_criteria: Forward commands: 0x01-0x0F (Piâ†’Gatewayâ†’Client)
test_status: not_written
created_at: 2026-03-17 04:40:43.112519
updated_at: 2026-03-17 04:54:31.233897
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN Forward commands: 0x01-0x0F (Piâ†’Gatewayâ†’Client)
AND the recipient receives the correct payload


id: PROTO-02.2
title: Return commands: 0x10-0x1F (Client to Gateway to Pi)
description: Return commands: 0x10-0x1F (Clientâ†’Gatewayâ†’Pi)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-02
acceptance_criteria: Return commands: 0x10-0x1F (Clientâ†’Gatewayâ†’Pi)
test_status: not_written
created_at: 2026-03-17 04:40:43.362719
updated_at: 2026-03-17 04:54:33.277728
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Return commands: 0x10-0x1F (Clientâ†’Gatewayâ†’Pi)


id: PROTO-02.3
title: Pairing commands: 0x20-0x2F
description: Pairing commands: 0x20-0x2F
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-02
acceptance_criteria: Pairing commands: 0x20-0x2F
test_status: not_written
created_at: 2026-03-17 04:40:43.605971
updated_at: 2026-03-17 04:54:35.307944
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Pairing commands: 0x20-0x2F


id: PROTO-02.4
title: Gateway commands: 0x30-0x3F
description: Gateway commands: 0x30-0x3F
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-02
acceptance_criteria: Gateway commands: 0x30-0x3F
test_status: not_written
created_at: 2026-03-17 04:40:43.858035
updated_at: 2026-03-17 04:54:37.335460
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Gateway commands: 0x30-0x3F


id: PROTO-03
title: LedCommand is 31 bytes
description: LedCommand is 31 bytes
priority: critical
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:44.359044
updated_at: 2026-03-17 04:54:41.443808
acceptance_criteria:
- [PROTO-03.1] Pack: header(5) + seq_num(2) + effect_id(1) + note(1) + velocity(1) + channel(1) + rgb(3) + param_count(1) + params(16)
- [PROTO-03.2] param_count=0 means use effect defaults
- [PROTO-03.3] param_count=N means first N params are explicit values

test_spec:
GIVEN the protocol layer is initialized
WHEN ledcommand is 31 bytes
THEN Pack: header(5) + seq_num(2) + effect_id(1) + note(1) + velocity(1) + channel(1) + rgb(3) + param_count(1) + params(16)
AND param_count=0 means use effect defaults
AND param_count=N means first N params are explicit values


id: PROTO-03.1
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-03
test_status: not_written
created_at: 2026-03-17 04:40:44.612512
updated_at: 2026-03-17 04:54:43.500987
title:
Pack: header(5) + seq_num(2) + effect_id(1) + note(1) + velocity(1) + channel(1) + rgb(3) + param_count(1) + params(16)

description:
Pack: header(5) + seq_num(2) + effect_id(1) + note(1) + velocity(1) + channel(1) + rgb(3) + param_count(1) + params(16)

acceptance_criteria:
Pack: header(5) + seq_num(2) + effect_id(1) + note(1) + velocity(1) + channel(1) + rgb(3) + param_count(1) + params(16)

test_spec:
GIVEN the protocol layer is initialized
WHEN the operation is performed under load
THEN Pack: header(5) + seq_num(2) + effect_id(1) + note(1) + velocity(1) + channel(1) + rgb(3) + param_count(1) + params(16)
AND the timing constraint is verified via measurement


id: PROTO-03.2
title: param_count=0 means use effect defaults
description: param_count=0 means use effect defaults
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-03
acceptance_criteria: param_count=0 means use effect defaults
test_status: not_written
created_at: 2026-03-17 04:40:44.868472
updated_at: 2026-03-17 04:54:45.546713
test_spec:
GIVEN the protocol layer is initialized
WHEN the default configuration is loaded
THEN param_count=0 means use effect defaults
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: PROTO-03.3
title: param_count=N means first N params are explicit values
description: param_count=N means first N params are explicit values
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-03
acceptance_criteria: param_count=N means first N params are explicit values
test_status: not_written
created_at: 2026-03-17 04:40:45.122550
updated_at: 2026-03-17 04:54:47.595753
test_spec:
GIVEN the protocol layer is initialized
WHEN the operation is performed under load
THEN param_count=N means first N params are explicit values
AND the timing constraint is verified via measurement


id: PROTO-04
title: CRC-16-CCITT togglable via FLAG_CRC_PRESENT
description: CRC-16-CCITT togglable via FLAG_CRC_PRESENT
priority: high
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:45.377047
updated_at: 2026-03-17 04:54:49.644026
acceptance_criteria:
- [PROTO-04.1] Polynomial 0x1021, init 0xFFFF
- [PROTO-04.2] When enabled, last 2 bytes (LE) are CRC over preceding data
- [PROTO-04.3] Python and C++ CRC implementations produce identical results

test_spec:
GIVEN the protocol layer is initialized
WHEN crc-16-ccitt togglable via flag_crc_present
THEN Polynomial 0x1021, init 0xFFFF
AND When enabled, last 2 bytes (LE) are CRC over preceding data
AND Python and C++ CRC implementations produce identical results


id: PROTO-04.1
title: Polynomial 0x1021, init 0xFFFF
description: Polynomial 0x1021, init 0xFFFF
priority: high
status: draft
node_type: leaf
parent_id: PROTO-04
acceptance_criteria: Polynomial 0x1021, init 0xFFFF
test_status: not_written
created_at: 2026-03-17 04:40:45.632339
updated_at: 2026-03-17 04:54:51.695495
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Polynomial 0x1021, init 0xFFFF


id: PROTO-04.2
title: When enabled, last 2 bytes (LE) are CRC over preceding data
description: When enabled, last 2 bytes (LE) are CRC over preceding data
priority: high
status: draft
node_type: leaf
parent_id: PROTO-04
acceptance_criteria: When enabled, last 2 bytes (LE) are CRC over preceding data
test_status: not_written
created_at: 2026-03-17 04:40:45.883453
updated_at: 2026-03-17 04:54:53.729526
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN When enabled, last 2 bytes (LE) are CRC over preceding data
AND round-trip produces identical values


id: PROTO-04.3
title: Python and C++ CRC implementations produce identical results
description: Python and C++ CRC implementations produce identical results
priority: high
status: draft
node_type: leaf
parent_id: PROTO-04
acceptance_criteria: Python and C++ CRC implementations produce identical results
test_status: not_written
created_at: 2026-03-17 04:40:46.137054
updated_at: 2026-03-17 04:54:55.781532
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Python and C++ CRC implementations produce identical results


id: PROTO-05
title: Selective 2x redundancy for hit frames
description: Selective 2x redundancy for hit frames
priority: medium
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:46.392523
updated_at: 2026-03-17 04:54:57.836682
acceptance_criteria:
- [PROTO-05.1] FLAG_RETRANSMIT bit in header flags
- [PROTO-05.2] Interleaved ordering: 1A,1B,2A,2B pattern

test_spec:
GIVEN the protocol layer is initialized
WHEN selective 2x redundancy for hit frames
THEN FLAG_RETRANSMIT bit in header flags
AND Interleaved ordering: 1A,1B,2A,2B pattern


id: PROTO-05.1
title: FLAG_RETRANSMIT bit in header flags
description: FLAG_RETRANSMIT bit in header flags
priority: medium
status: draft
node_type: leaf
parent_id: PROTO-05
acceptance_criteria: FLAG_RETRANSMIT bit in header flags
test_status: not_written
created_at: 2026-03-17 04:40:46.632486
updated_at: 2026-03-17 04:54:59.896237
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN FLAG_RETRANSMIT bit in header flags


id: PROTO-05.2
title: Interleaved ordering: 1A,1B,2A,2B pattern
description: Interleaved ordering: 1A,1B,2A,2B pattern
priority: medium
status: draft
node_type: leaf
parent_id: PROTO-05
acceptance_criteria: Interleaved ordering: 1A,1B,2A,2B pattern
test_status: not_written
created_at: 2026-03-17 04:40:46.872397
updated_at: 2026-03-17 04:55:01.943562
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Interleaved ordering: 1A,1B,2A,2B pattern


id: PROTO-06
title: Per-packet sequence numbers for loss detection and dedup
description: Per-packet sequence numbers for loss detection and dedup
priority: high
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:47.116055
updated_at: 2026-03-17 04:55:03.987990
acceptance_criteria:
- [PROTO-06.1] u16 LE seq_num increments per sender
- [PROTO-06.2] Client dedup via seq_num comparison
- [PROTO-06.3] Gap counting in client metrics

test_spec:
GIVEN the protocol layer is initialized
WHEN per-packet sequence numbers for loss detection and dedup
THEN u16 LE seq_num increments per sender
AND Client dedup via seq_num comparison
AND Gap counting in client metrics


id: PROTO-06.1
title: u16 LE seq_num increments per sender
description: u16 LE seq_num increments per sender
priority: high
status: draft
node_type: leaf
parent_id: PROTO-06
acceptance_criteria: u16 LE seq_num increments per sender
test_status: not_written
created_at: 2026-03-17 04:40:47.358964
updated_at: 2026-03-17 04:55:06.029972
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN u16 LE seq_num increments per sender
AND the recipient receives the correct payload


id: PROTO-06.2
title: Client dedup via seq_num comparison
description: Client dedup via seq_num comparison
priority: high
status: draft
node_type: leaf
parent_id: PROTO-06
acceptance_criteria: Client dedup via seq_num comparison
test_status: not_written
created_at: 2026-03-17 04:40:47.605381
updated_at: 2026-03-17 04:55:08.065466
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Client dedup via seq_num comparison


id: PROTO-06.3
title: Gap counting in client metrics
description: Gap counting in client metrics
priority: high
status: draft
node_type: leaf
parent_id: PROTO-06
acceptance_criteria: Gap counting in client metrics
test_status: not_written
created_at: 2026-03-17 04:40:47.859229
updated_at: 2026-03-17 04:55:10.111118
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Gap counting in client metrics


id: PROTO-07
title: Transport interface abstracts send/receive
description: Transport interface abstracts send/receive
priority: critical
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:48.102585
updated_at: 2026-03-17 04:55:12.156165
acceptance_criteria:
- [PROTO-07.1] Abstract interface: send(mac, payload), receive(), connect(), disconnect()
- [PROTO-07.2] Interface exposes error semantics (sent vs delivered)
- [PROTO-07.3] Interface exposes capabilities (broadcast, reliable, max_packet_size)

test_spec:
GIVEN the protocol layer is initialized
WHEN transport interface abstracts send/receive
THEN Abstract interface: send(mac, payload), receive(), connect(), disconnect()
AND Interface exposes error semantics (sent vs delivered)
AND Interface exposes capabilities (broadcast, reliable, max_packet_size)


id: PROTO-07.1
title: Abstract interface: send(mac, payload), receive(), connect(), disconnect()
description: Abstract interface: send(mac, payload), receive(), connect(), disconnect()
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-07
acceptance_criteria: Abstract interface: send(mac, payload), receive(), connect(), disconnect()
test_status: not_written
created_at: 2026-03-17 04:40:48.342797
updated_at: 2026-03-17 04:55:14.204765
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN Abstract interface: send(mac, payload), receive(), connect(), disconnect()
AND the recipient receives the correct payload


id: PROTO-07.2
title: Interface exposes error semantics (sent vs delivered)
description: Interface exposes error semantics (sent vs delivered)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-07
acceptance_criteria: Interface exposes error semantics (sent vs delivered)
test_status: not_written
created_at: 2026-03-17 04:40:48.595259
updated_at: 2026-03-17 04:55:16.244081
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Interface exposes error semantics (sent vs delivered)


id: PROTO-07.3
title: Interface exposes capabilities (broadcast, reliable, max_packet_size)
description: Interface exposes capabilities (broadcast, reliable, max_packet_size)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-07
acceptance_criteria: Interface exposes capabilities (broadcast, reliable, max_packet_size)
test_status: not_written
created_at: 2026-03-17 04:40:48.852808
updated_at: 2026-03-17 04:55:18.296049
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN Interface exposes capabilities (broadcast, reliable, max_packet_size)
AND round-trip produces identical values


id: PROTO-08
title: UDP transport implementation
description: UDP transport implementation
priority: critical
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:49.095712
updated_at: 2026-03-17 04:55:20.328365
acceptance_criteria:
- [PROTO-08.1] Send: [6-byte MAC prefix] + [payload] to gateway:9000
- [PROTO-08.2] Bind to gateway subnet for consistent routing
- [PROTO-08.3] Non-blocking send (no stalling MIDI thread)
- [PROTO-08.4] Recreate socket after 5 consecutive send failures

test_spec:
GIVEN the protocol layer is initialized
WHEN udp transport implementation
THEN Send: [6-byte MAC prefix] + [payload] to gateway:9000
AND Bind to gateway subnet for consistent routing
AND Non-blocking send (no stalling MIDI thread)
AND Recreate socket after 5 consecutive send failures


id: PROTO-08.1
title: Send: [6-byte MAC prefix] + [payload] to gateway:9000
description: Send: [6-byte MAC prefix] + [payload] to gateway:9000
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-08
acceptance_criteria: Send: [6-byte MAC prefix] + [payload] to gateway:9000
test_status: not_written
created_at: 2026-03-17 04:40:49.352806
updated_at: 2026-03-17 04:55:22.366136
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN Send: [6-byte MAC prefix] + [payload] to gateway:9000
AND round-trip produces identical values


id: PROTO-08.2
title: Bind to gateway subnet for consistent routing
description: Bind to gateway subnet for consistent routing
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-08
acceptance_criteria: Bind to gateway subnet for consistent routing
test_status: not_written
created_at: 2026-03-17 04:40:49.602555
updated_at: 2026-03-17 04:55:24.424981
test_spec:
GIVEN the protocol layer is initialized
WHEN the receiving endpoint is active
THEN Bind to gateway subnet for consistent routing
AND received data matches what was sent


id: PROTO-08.3
title: Non-blocking send (no stalling MIDI thread)
description: Non-blocking send (no stalling MIDI thread)
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-08
acceptance_criteria: Non-blocking send (no stalling MIDI thread)
test_status: not_written
created_at: 2026-03-17 04:40:49.864534
updated_at: 2026-03-17 04:55:26.471899
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN Non-blocking send (no stalling MIDI thread)
AND the recipient receives the correct payload


id: PROTO-08.4
title: Recreate socket after 5 consecutive send failures
description: Recreate socket after 5 consecutive send failures
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-08
acceptance_criteria: Recreate socket after 5 consecutive send failures
test_status: not_written
created_at: 2026-03-17 04:40:50.119607
updated_at: 2026-03-17 04:55:28.523835
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN Recreate socket after 5 consecutive send failures
AND the recipient receives the correct payload


id: PROTO-09
title: Serial transport implementation
description: Serial transport implementation
priority: high
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:50.373523
updated_at: 2026-03-17 04:55:30.568669
acceptance_criteria:
- [PROTO-09.1] Frame binary packets with length-prefix or COBS encoding
- [PROTO-09.2] Auto-detect ESP32 serial device
- [PROTO-09.3] Handle backpressure (serial is flow-controlled unlike UDP)

test_spec:
GIVEN the protocol layer is initialized
WHEN serial transport implementation
THEN Frame binary packets with length-prefix or COBS encoding
AND Auto-detect ESP32 serial device
AND Handle backpressure (serial is flow-controlled unlike UDP)


id: PROTO-09.1
title: Frame binary packets with length-prefix or COBS encoding
description: Frame binary packets with length-prefix or COBS encoding
priority: high
status: draft
node_type: leaf
parent_id: PROTO-09
acceptance_criteria: Frame binary packets with length-prefix or COBS encoding
test_status: not_written
created_at: 2026-03-17 04:40:50.612786
updated_at: 2026-03-17 04:55:32.614567
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN Frame binary packets with length-prefix or COBS encoding
AND round-trip produces identical values


id: PROTO-09.2
title: Auto-detect ESP32 serial device
description: Auto-detect ESP32 serial device
priority: high
status: draft
node_type: leaf
parent_id: PROTO-09
acceptance_criteria: Auto-detect ESP32 serial device
test_status: not_written
created_at: 2026-03-17 04:40:50.866167
updated_at: 2026-03-17 04:55:34.652782
test_spec:
GIVEN the protocol layer is initialized
WHEN the detection/scanning process runs
THEN Auto-detect ESP32 serial device


id: PROTO-09.3
title: Handle backpressure (serial is flow-controlled unlike UDP)
description: Handle backpressure (serial is flow-controlled unlike UDP)
priority: high
status: draft
node_type: leaf
parent_id: PROTO-09
acceptance_criteria: Handle backpressure (serial is flow-controlled unlike UDP)
test_status: not_written
created_at: 2026-03-17 04:40:51.118095
updated_at: 2026-03-17 04:55:36.683993
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Handle backpressure (serial is flow-controlled unlike UDP)


id: PROTO-10
title: Transport auto-detection
description: Transport auto-detection
priority: medium
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:51.358625
updated_at: 2026-03-17 04:55:38.735482
acceptance_criteria:
- [PROTO-10.1] Probe UDP gateway reachability (timeout 2s)
- [PROTO-10.2] Probe serial device availability
- [PROTO-10.3] Select based on priority: UDP preferred for multi-client

test_spec:
GIVEN the protocol layer is initialized
WHEN transport auto-detection
THEN Probe UDP gateway reachability (timeout 2s)
AND Probe serial device availability
AND Select based on priority: UDP preferred for multi-client


id: PROTO-10.1
title: Probe UDP gateway reachability (timeout 2s)
description: Probe UDP gateway reachability (timeout 2s)
priority: medium
status: draft
node_type: leaf
parent_id: PROTO-10
acceptance_criteria: Probe UDP gateway reachability (timeout 2s)
test_status: not_written
created_at: 2026-03-17 04:40:51.612693
updated_at: 2026-03-17 04:55:40.788648
test_spec:
GIVEN the protocol layer is initialized
WHEN the operation is performed under load
THEN Probe UDP gateway reachability (timeout 2s)
AND the timing constraint is verified via measurement


id: PROTO-10.2
title: Probe serial device availability
description: Probe serial device availability
priority: medium
status: draft
node_type: leaf
parent_id: PROTO-10
acceptance_criteria: Probe serial device availability
test_status: not_written
created_at: 2026-03-17 04:40:51.863666
updated_at: 2026-03-17 04:55:42.843367
test_spec:
GIVEN the protocol layer is initialized
WHEN the detection/scanning process runs
THEN Probe serial device availability


id: PROTO-10.3
title: Select based on priority: UDP preferred for multi-client
description: Select based on priority: UDP preferred for multi-client
priority: medium
status: draft
node_type: leaf
parent_id: PROTO-10
acceptance_criteria: Select based on priority: UDP preferred for multi-client
test_status: not_written
created_at: 2026-03-17 04:40:52.114888
updated_at: 2026-03-17 04:55:44.868620
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Select based on priority: UDP preferred for multi-client


id: PROTO-11
title: Return path from clients to host
description: Return path from clients to host
priority: high
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:52.367684
updated_at: 2026-03-17 04:55:46.932799
acceptance_criteria:
- [PROTO-11.1] Gateway relays client ESP-NOW messages to host
- [PROTO-11.2] UDP return on port 9001 (gateway_port + 1)
- [PROTO-11.3] Serial return via same serial connection

test_spec:
GIVEN the protocol layer is initialized
WHEN return path from clients to host
THEN Gateway relays client ESP-NOW messages to host
AND UDP return on port 9001 (gateway_port + 1)
AND Serial return via same serial connection


id: PROTO-11.1
title: Gateway relays client ESP-NOW messages to host
description: Gateway relays client ESP-NOW messages to host
priority: high
status: draft
node_type: leaf
parent_id: PROTO-11
acceptance_criteria: Gateway relays client ESP-NOW messages to host
test_status: not_written
created_at: 2026-03-17 04:40:52.622626
updated_at: 2026-03-17 04:55:48.980215
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN Gateway relays client ESP-NOW messages to host
AND the recipient receives the correct payload


id: PROTO-11.2
title: UDP return on port 9001 (gateway_port + 1)
description: UDP return on port 9001 (gateway_port + 1)
priority: high
status: draft
node_type: leaf
parent_id: PROTO-11
acceptance_criteria: UDP return on port 9001 (gateway_port + 1)
test_status: not_written
created_at: 2026-03-17 04:40:52.874210
updated_at: 2026-03-17 04:55:51.027074
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN UDP return on port 9001 (gateway_port + 1)


id: PROTO-11.3
title: Serial return via same serial connection
description: Serial return via same serial connection
priority: high
status: draft
node_type: leaf
parent_id: PROTO-11
acceptance_criteria: Serial return via same serial connection
test_status: not_written
created_at: 2026-03-17 04:40:53.117798
updated_at: 2026-03-17 04:55:53.086563
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Serial return via same serial connection


id: PROTO-12
title: ESP-NOW broadcast with 6-byte MAC trailer
description: ESP-NOW broadcast with 6-byte MAC trailer
priority: critical
status: draft
node_type: requirement
parent_id: PROTO
test_status: not_written
created_at: 2026-03-17 04:40:53.372710
updated_at: 2026-03-17 04:55:55.138374
acceptance_criteria:
- [PROTO-12.1] Gateway appends target MAC as last 6 bytes of ESP-NOW frame
- [PROTO-12.2] Client extracts trailer and compares against own MAC
- [PROTO-12.3] Broadcast MAC (FF:FF:FF:FF:FF:FF) accepted by all clients

test_spec:
GIVEN the protocol layer is initialized
WHEN esp-now broadcast with 6-byte mac trailer
THEN Gateway appends target MAC as last 6 bytes of ESP-NOW frame
AND Client extracts trailer and compares against own MAC
AND Broadcast MAC (FF:FF:FF:FF:FF:FF) accepted by all clients


id: PROTO-12.1
title: Gateway appends target MAC as last 6 bytes of ESP-NOW frame
description: Gateway appends target MAC as last 6 bytes of ESP-NOW frame
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-12
acceptance_criteria: Gateway appends target MAC as last 6 bytes of ESP-NOW frame
test_status: not_written
created_at: 2026-03-17 04:40:53.617651
updated_at: 2026-03-17 04:55:57.191759
test_spec:
GIVEN the protocol layer is initialized
WHEN data is serialized and deserialized
THEN Gateway appends target MAC as last 6 bytes of ESP-NOW frame
AND round-trip produces identical values


id: PROTO-12.2
title: Client extracts trailer and compares against own MAC
description: Client extracts trailer and compares against own MAC
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-12
acceptance_criteria: Client extracts trailer and compares against own MAC
test_status: not_written
created_at: 2026-03-17 04:40:53.872709
updated_at: 2026-03-17 04:55:59.230524
test_spec:
GIVEN the protocol layer is initialized
WHEN the feature is exercised
THEN Client extracts trailer and compares against own MAC


id: PROTO-12.3
title: Broadcast MAC (FF:FF:FF:FF:FF:FF) accepted by all clients
description: Broadcast MAC (FF:FF:FF:FF:FF:FF) accepted by all clients
priority: critical
status: draft
node_type: leaf
parent_id: PROTO-12
acceptance_criteria: Broadcast MAC (FF:FF:FF:FF:FF:FF) accepted by all clients
test_status: not_written
created_at: 2026-03-17 04:40:54.126226
updated_at: 2026-03-17 04:56:01.279308
test_spec:
GIVEN the protocol layer is initialized
WHEN a message is dispatched
THEN Broadcast MAC (FF:FF:FF:FF:FF:FF) accepted by all clients
AND the recipient receives the correct payload


id: INST
title: Instrument Model
description: One instrument per ESP32, note collections, configurable layers and blending
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:40:54.366213
updated_at: 2026-03-17 04:56:03.322724
acceptance_criteria:
- [INST-01] Each ESP32 client is one instrument with unique MAC
- [INST-02] Each instrument is a collection of MIDI notes
- [INST-03] Per-note config: effect_id, color, palette_id, start_led, end_led
- [INST-04] Retrigger modes: RESTART, LAYER, MODIFY
- [INST-05] Velocity parameter mapping per note
- [INST-06] Decay modes: NOTE_OFF and FIXED
- [INST-07] Blend modes: additive, max, overlay
- [INST-08] Configurable max layers per instrument (default 3)
- [INST-09] Priority-based layer eviction
- [INST-10] Per-note debounce (default 20ms)

test_spec:
GIVEN the Instrument Model pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: INST-01
title: Each ESP32 client is one instrument with unique MAC
description: Each ESP32 client is one instrument with unique MAC
priority: critical
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:54.622684
updated_at: 2026-03-17 04:56:05.380684
acceptance_criteria:
- [INST-01.1] Instrument-to-MAC mapping in config
- [INST-01.2] MIDI notes route to instrument's MAC address

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the system exercises this capability
THEN Instrument-to-MAC mapping in config
AND MIDI notes route to instrument's MAC address


id: INST-01.1
title: Instrument-to-MAC mapping in config
description: Instrument-to-MAC mapping in config
priority: critical
status: draft
node_type: leaf
parent_id: INST-01
acceptance_criteria: Instrument-to-MAC mapping in config
test_status: not_written
created_at: 2026-03-17 04:40:54.873515
updated_at: 2026-03-17 04:56:07.423185
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the input is processed
THEN Instrument-to-MAC mapping in config


id: INST-01.2
title: MIDI notes route to instrument MAC address
description: MIDI notes route to instrument's MAC address
priority: critical
status: draft
node_type: leaf
parent_id: INST-01
acceptance_criteria: MIDI notes route to instrument's MAC address
test_status: not_written
created_at: 2026-03-17 04:40:55.127724
updated_at: 2026-03-17 04:56:09.472717
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN MIDI notes route to instrument's MAC address


id: INST-02
title: Each instrument is a collection of MIDI notes
description: Each instrument is a collection of MIDI notes
priority: critical
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:55.382417
updated_at: 2026-03-17 04:56:11.524159
acceptance_criteria:
- [INST-02.1] Instrument config contains list of note definitions
- [INST-02.2] Each note has independent effect/color/range/behavior config

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the system exercises this capability
THEN Instrument config contains list of note definitions
AND Each note has independent effect/color/range/behavior config


id: INST-02.1
title: Instrument config contains list of note definitions
description: Instrument config contains list of note definitions
priority: critical
status: draft
node_type: leaf
parent_id: INST-02
acceptance_criteria: Instrument config contains list of note definitions
test_status: not_written
created_at: 2026-03-17 04:40:55.628749
updated_at: 2026-03-17 04:56:13.579857
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN Instrument config contains list of note definitions


id: INST-02.2
title: Each note has independent effect/color/range/behavior config
description: Each note has independent effect/color/range/behavior config
priority: critical
status: draft
node_type: leaf
parent_id: INST-02
acceptance_criteria: Each note has independent effect/color/range/behavior config
test_status: not_written
created_at: 2026-03-17 04:40:55.868220
updated_at: 2026-03-17 04:56:15.628043
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN Each note has independent effect/color/range/behavior config


id: INST-03
title: Per-note config: effect_id, color, palette_id, start_led, end_led
description: Per-note config: effect_id, color, palette_id, start_led, end_led
priority: critical
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:56.112722
updated_at: 2026-03-17 04:56:17.682900
acceptance_criteria:
- [INST-03.1] effect_id selects which effect to trigger
- [INST-03.2] RGB color or palette_id for color resolution
- [INST-03.3] start_led/end_led constrain rendering to LED range

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN per-note config: effect_id, color, palette_id, start_led, end_led
THEN effect_id selects which effect to trigger
AND RGB color or palette_id for color resolution
AND start_led/end_led constrain rendering to LED range


id: INST-03.1
title: effect_id selects which effect to trigger
description: effect_id selects which effect to trigger
priority: critical
status: draft
node_type: leaf
parent_id: INST-03
acceptance_criteria: effect_id selects which effect to trigger
test_status: not_written
created_at: 2026-03-17 04:40:56.352585
updated_at: 2026-03-17 04:56:19.723124
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN effect_id selects which effect to trigger


id: INST-03.2
title: RGB color or palette_id for color resolution
description: RGB color or palette_id for color resolution
priority: critical
status: draft
node_type: leaf
parent_id: INST-03
acceptance_criteria: RGB color or palette_id for color resolution
test_status: not_written
created_at: 2026-03-17 04:40:56.607103
updated_at: 2026-03-17 04:56:21.761501
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN RGB color or palette_id for color resolution


id: INST-03.3
title: start_led/end_led constrain rendering to LED range
description: start_led/end_led constrain rendering to LED range
priority: critical
status: draft
node_type: leaf
parent_id: INST-03
acceptance_criteria: start_led/end_led constrain rendering to LED range
test_status: not_written
created_at: 2026-03-17 04:40:56.862676
updated_at: 2026-03-17 04:56:23.799794
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the UI component renders
THEN start_led/end_led constrain rendering to LED range
AND the display updates correctly on state change


id: INST-04
title: Retrigger modes: RESTART, LAYER, MODIFY
description: Retrigger modes: RESTART, LAYER, MODIFY
priority: high
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:57.115444
updated_at: 2026-03-17 04:56:25.843825
acceptance_criteria:
- [INST-04.1] RESTART: kill existing layer for same note, create new
- [INST-04.2] LAYER: allocate new layer (up to max), existing continues
- [INST-04.3] MODIFY: boost velocity/brightness of existing layer

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN retrigger modes: restart, layer, modify
THEN RESTART: kill existing layer for same note, create new
AND LAYER: allocate new layer (up to max), existing continues
AND MODIFY: boost velocity/brightness of existing layer


id: INST-04.1
title: RESTART: kill existing layer for same note, create new
description: RESTART: kill existing layer for same note, create new
priority: high
status: draft
node_type: leaf
parent_id: INST-04
acceptance_criteria: RESTART: kill existing layer for same note, create new
test_status: not_written
created_at: 2026-03-17 04:40:57.368750
updated_at: 2026-03-17 04:56:27.889628
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN RESTART: kill existing layer for same note, create new


id: INST-04.2
title: LAYER: allocate new layer (up to max), existing continues
description: LAYER: allocate new layer (up to max), existing continues
priority: high
status: draft
node_type: leaf
parent_id: INST-04
acceptance_criteria: LAYER: allocate new layer (up to max), existing continues
test_status: not_written
created_at: 2026-03-17 04:40:57.622663
updated_at: 2026-03-17 04:56:29.939012
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN LAYER: allocate new layer (up to max), existing continues


id: INST-04.3
title: MODIFY: boost velocity/brightness of existing layer
description: MODIFY: boost velocity/brightness of existing layer
priority: high
status: draft
node_type: leaf
parent_id: INST-04
acceptance_criteria: MODIFY: boost velocity/brightness of existing layer
test_status: not_written
created_at: 2026-03-17 04:40:57.876237
updated_at: 2026-03-17 04:56:31.983068
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN MODIFY: boost velocity/brightness of existing layer


id: INST-05
title: Velocity parameter mapping per note
description: Velocity parameter mapping per note
priority: high
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:58.122541
updated_at: 2026-03-17 04:56:34.025124
acceptance_criteria:
- [INST-05.1] Map velocity to one of: speed, decay, density, amplitude, brightness
- [INST-05.2] Configurable velocity curves: linear, quadratic, sqrt

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN velocity parameter mapping per note
THEN Map velocity to one of: speed, decay, density, amplitude, brightness
AND Configurable velocity curves: linear, quadratic, sqrt


id: INST-05.1
title: Map velocity to one of: speed, decay, density, amplitude, brightness
description: Map velocity to one of: speed, decay, density, amplitude, brightness
priority: high
status: draft
node_type: leaf
parent_id: INST-05
acceptance_criteria: Map velocity to one of: speed, decay, density, amplitude, brightness
test_status: not_written
created_at: 2026-03-17 04:40:58.375372
updated_at: 2026-03-17 04:56:36.083334
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the input is processed
THEN Map velocity to one of: speed, decay, density, amplitude, brightness


id: INST-05.2
title: Configurable velocity curves: linear, quadratic, sqrt
description: Configurable velocity curves: linear, quadratic, sqrt
priority: high
status: draft
node_type: leaf
parent_id: INST-05
acceptance_criteria: Configurable velocity curves: linear, quadratic, sqrt
test_status: not_written
created_at: 2026-03-17 04:40:58.626750
updated_at: 2026-03-17 05:27:18.422798
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the default configuration is loaded
THEN Configurable velocity curves: linear, quadratic, sqrt
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: INST-06
title: Decay modes: NOTE_OFF and FIXED
description: Decay modes: NOTE_OFF and FIXED
priority: high
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:58.882674
updated_at: 2026-03-17 04:56:40.169477
acceptance_criteria:
- [INST-06.1] NOTE_OFF: decay begins on CMD_RELEASE
- [INST-06.2] FIXED: auto-timeout after configurable decay_ms

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN decay modes: note_off and fixed
THEN NOTE_OFF: decay begins on CMD_RELEASE
AND FIXED: auto-timeout after configurable decay_ms


id: INST-06.1
title: NOTE_OFF: decay begins on CMD_RELEASE
description: NOTE_OFF: decay begins on CMD_RELEASE
priority: high
status: draft
node_type: leaf
parent_id: INST-06
acceptance_criteria: NOTE_OFF: decay begins on CMD_RELEASE
test_status: not_written
created_at: 2026-03-17 04:40:59.133788
updated_at: 2026-03-17 04:56:42.213843
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN NOTE_OFF: decay begins on CMD_RELEASE


id: INST-06.2
title: FIXED: auto-timeout after configurable decay_ms
description: FIXED: auto-timeout after configurable decay_ms
priority: high
status: draft
node_type: leaf
parent_id: INST-06
acceptance_criteria: FIXED: auto-timeout after configurable decay_ms
test_status: not_written
created_at: 2026-03-17 04:40:59.385679
updated_at: 2026-03-17 04:56:44.267219
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the default configuration is loaded
THEN FIXED: auto-timeout after configurable decay_ms
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: INST-07
title: Blend modes: additive, max, overlay
description: Blend modes: additive, max, overlay
priority: high
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:40:59.627778
updated_at: 2026-03-17 04:56:46.313038
acceptance_criteria:
- [INST-07.1] Additive (0): pixel values summed, clamped to 255
- [INST-07.2] Max (1): brightest pixel value wins
- [INST-07.3] Overlay (2): top layer overwrites non-black pixels

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN blend modes: additive, max, overlay
THEN Additive (0): pixel values summed, clamped to 255
AND Max (1): brightest pixel value wins
AND Overlay (2): top layer overwrites non-black pixels


id: INST-07.1
title: Additive (0): pixel values summed, clamped to 255
description: Additive (0): pixel values summed, clamped to 255
priority: high
status: draft
node_type: leaf
parent_id: INST-07
acceptance_criteria: Additive (0): pixel values summed, clamped to 255
test_status: not_written
created_at: 2026-03-17 04:40:59.872741
updated_at: 2026-03-17 04:56:48.365127
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN Additive (0): pixel values summed, clamped to 255


id: INST-07.2
title: Max (1): brightest pixel value wins
description: Max (1): brightest pixel value wins
priority: high
status: draft
node_type: leaf
parent_id: INST-07
acceptance_criteria: Max (1): brightest pixel value wins
test_status: not_written
created_at: 2026-03-17 04:41:00.122533
updated_at: 2026-03-17 04:56:50.422863
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN Max (1): brightest pixel value wins


id: INST-07.3
title: Overlay (2): top layer overwrites non-black pixels
description: Overlay (2): top layer overwrites non-black pixels
priority: high
status: draft
node_type: leaf
parent_id: INST-07
acceptance_criteria: Overlay (2): top layer overwrites non-black pixels
test_status: not_written
created_at: 2026-03-17 04:41:00.376684
updated_at: 2026-03-17 04:56:52.458025
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN a message is dispatched
THEN Overlay (2): top layer overwrites non-black pixels
AND the recipient receives the correct payload


id: INST-08
title: Configurable max layers per instrument (default 3)
description: Configurable max layers per instrument (default 3)
priority: high
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:41:00.624305
updated_at: 2026-03-17 04:56:54.503736
acceptance_criteria:
- [INST-08.1] MAX_LAYERS configurable in config (1-8 range)
- [INST-08.2] Memory budget: 256 LEDs Ã— 3 bytes Ã— N layers

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN configurable max layers per instrument (default 3)
THEN MAX_LAYERS configurable in config (1-8 range)
AND Memory budget: 256 LEDs Ã— 3 bytes Ã— N layers


id: INST-08.1
title: MAX_LAYERS configurable in config (1-8 range)
description: MAX_LAYERS configurable in config (1-8 range)
priority: high
status: draft
node_type: leaf
parent_id: INST-08
acceptance_criteria: MAX_LAYERS configurable in config (1-8 range)
test_status: not_written
created_at: 2026-03-17 04:41:00.878686
updated_at: 2026-03-17 04:56:56.557557
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the default configuration is loaded
THEN MAX_LAYERS configurable in config (1-8 range)
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: INST-08.2
title: Memory budget: 256 LEDs x 3 bytes x N layers
description: Memory budget: 256 LEDs Ã— 3 bytes Ã— N layers
priority: high
status: draft
node_type: leaf
parent_id: INST-08
acceptance_criteria: Memory budget: 256 LEDs Ã— 3 bytes Ã— N layers
test_status: not_written
created_at: 2026-03-17 04:41:01.132868
updated_at: 2026-03-17 04:56:58.603866
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN data is serialized and deserialized
THEN Memory budget: 256 LEDs Ã— 3 bytes Ã— N layers
AND round-trip produces identical values


id: INST-09
title: Priority-based layer eviction
description: Priority-based layer eviction
priority: high
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:41:01.385728
updated_at: 2026-03-17 04:57:00.651443
acceptance_criteria:
- [INST-09.1] 4-bit priority (0-15) per layer
- [INST-09.2] Lowest priority evicted first
- [INST-09.3] Tie-break: oldest layer evicted

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN priority-based layer eviction
THEN 4-bit priority (0-15) per layer
AND Lowest priority evicted first
AND Tie-break: oldest layer evicted


id: INST-09.1
title: 4-bit priority (0-15) per layer
description: 4-bit priority (0-15) per layer
priority: high
status: draft
node_type: leaf
parent_id: INST-09
acceptance_criteria: 4-bit priority (0-15) per layer
test_status: not_written
created_at: 2026-03-17 04:41:01.642771
updated_at: 2026-03-17 04:57:02.704063
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN 4-bit priority (0-15) per layer


id: INST-09.2
title: Lowest priority evicted first
description: Lowest priority evicted first
priority: high
status: draft
node_type: leaf
parent_id: INST-09
acceptance_criteria: Lowest priority evicted first
test_status: not_written
created_at: 2026-03-17 04:41:01.896233
updated_at: 2026-03-17 04:57:04.751464
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN Lowest priority evicted first


id: INST-09.3
title: Tie-break: oldest layer evicted
description: Tie-break: oldest layer evicted
priority: high
status: draft
node_type: leaf
parent_id: INST-09
acceptance_criteria: Tie-break: oldest layer evicted
test_status: not_written
created_at: 2026-03-17 04:41:02.138695
updated_at: 2026-03-17 04:57:06.803720
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the feature is exercised
THEN Tie-break: oldest layer evicted


id: INST-10
title: Per-note debounce (default 20ms)
description: Per-note debounce (default 20ms)
priority: medium
status: draft
node_type: requirement
parent_id: INST
test_status: not_written
created_at: 2026-03-17 04:41:02.392835
updated_at: 2026-03-17 04:57:08.876997
acceptance_criteria:
- [INST-10.1] Track last trigger time per note
- [INST-10.2] Skip trigger if within debounce_ms window

test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN per-note debounce (default 20ms)
THEN Track last trigger time per note
AND Skip trigger if within debounce_ms window


id: INST-10.1
title: Track last trigger time per note
description: Track last trigger time per note
priority: medium
status: draft
node_type: leaf
parent_id: INST-10
acceptance_criteria: Track last trigger time per note
test_status: not_written
created_at: 2026-03-17 04:41:02.646752
updated_at: 2026-03-17 04:57:10.923095
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the relevant event occurs
THEN Track last trigger time per note
AND the stored data is retrievable


id: INST-10.2
title: Skip trigger if within debounce_ms window
description: Skip trigger if within debounce_ms window
priority: medium
status: draft
node_type: leaf
parent_id: INST-10
acceptance_criteria: Skip trigger if within debounce_ms window
test_status: not_written
created_at: 2026-03-17 04:41:02.897697
updated_at: 2026-03-17 04:57:12.963714
test_spec:
GIVEN the instrument model is loaded with device configuration
WHEN the operation is performed under load
THEN Skip trigger if within debounce_ms window
AND the timing constraint is verified via measurement


id: FX
title: Effect Engine
description: Plugin architecture, 9 effects, compositor, LED matrix support
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:41:03.152894
updated_at: 2026-03-17 04:57:15.018969
acceptance_criteria:
- [FX-01] Effect plugin interface: onTrigger, onRelease, render, setParam, name
- [FX-02] Zero-touch effect registration
- [FX-03] Per-layer effect instances
- [FX-04] Effects reset all mutable state in onTrigger
- [FX-05] Flash effect
- [FX-06] Ripple effect
- [FX-07] VuMeter effect
- [FX-08] Sparkle effect
- [FX-09] Wave effect
- [FX-10] Chase effect
- [FX-11] Strobe effect
- [FX-12] FillDrain effect
- [FX-13] Fire effect
- [FX-14] LED range clamping per layer
- [FX-15] 2D matrix XY mapping

test_spec:
GIVEN the Effect Engine pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: FX-01
title: Effect plugin interface: onTrigger, onRelease, render, setParam, name
description: Effect plugin interface: onTrigger, onRelease, render, setParam, name
priority: critical
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:03.406224
updated_at: 2026-03-17 04:57:17.068333
acceptance_criteria:
- [FX-01.1] EffectBase abstract class with virtual methods
- [FX-01.2] render() returns bool (true=still active)
- [FX-01.3] All methods receive ITimer for injectable time

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN effect plugin interface: ontrigger, onrelease, render, setparam, name
THEN EffectBase abstract class with virtual methods
AND render() returns bool (true=still active)
AND All methods receive ITimer for injectable time


id: FX-01.1
title: EffectBase abstract class with virtual methods
description: EffectBase abstract class with virtual methods
priority: critical
status: draft
node_type: leaf
parent_id: FX-01
acceptance_criteria: EffectBase abstract class with virtual methods
test_status: not_written
created_at: 2026-03-17 04:41:03.656708
updated_at: 2026-03-17 04:57:19.122985
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN EffectBase abstract class with virtual methods


id: FX-01.2
title: render() returns bool (true=still active)
description: render() returns bool (true=still active)
priority: critical
status: draft
node_type: leaf
parent_id: FX-01
acceptance_criteria: render() returns bool (true=still active)
test_status: not_written
created_at: 2026-03-17 04:41:03.912632
updated_at: 2026-03-17 04:57:21.155771
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the UI component renders
THEN render() returns bool (true=still active)
AND the display updates correctly on state change


id: FX-01.3
title: All methods receive ITimer for injectable time
description: All methods receive ITimer for injectable time
priority: critical
status: draft
node_type: leaf
parent_id: FX-01
acceptance_criteria: All methods receive ITimer for injectable time
test_status: not_written
created_at: 2026-03-17 04:41:04.166298
updated_at: 2026-03-17 04:57:23.205785
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the receiving endpoint is active
THEN All methods receive ITimer for injectable time
AND received data matches what was sent


id: FX-02
title: Zero-touch effect registration
description: Zero-touch effect registration
priority: high
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:04.418217
updated_at: 2026-03-17 04:57:25.255931
acceptance_criteria:
- [FX-02.1] REGISTER_EFFECT macro or factory pattern
- [FX-02.2] New effects require no changes to core code

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN zero-touch effect registration
THEN REGISTER_EFFECT macro or factory pattern
AND New effects require no changes to core code


id: FX-02.1
title: REGISTER_EFFECT macro or factory pattern
description: REGISTER_EFFECT macro or factory pattern
priority: high
status: draft
node_type: leaf
parent_id: FX-02
acceptance_criteria: REGISTER_EFFECT macro or factory pattern
test_status: not_written
created_at: 2026-03-17 04:41:04.662567
updated_at: 2026-03-17 04:57:27.313335
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN REGISTER_EFFECT macro or factory pattern


id: FX-02.2
title: New effects require no changes to core code
description: New effects require no changes to core code
priority: high
status: draft
node_type: leaf
parent_id: FX-02
acceptance_criteria: New effects require no changes to core code
test_status: not_written
created_at: 2026-03-17 04:41:04.912969
updated_at: 2026-03-17 04:57:29.366811
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN New effects require no changes to core code


id: FX-03
title: Per-layer effect instances
description: Per-layer effect instances
priority: critical
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:05.158029
updated_at: 2026-03-17 04:57:31.414695
acceptance_criteria:
- [FX-03.1] Compositor allocates fresh effect instance per layer
- [FX-03.2] No shared mutable state between layers

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN per-layer effect instances
THEN Compositor allocates fresh effect instance per layer
AND No shared mutable state between layers


id: FX-03.1
title: Compositor allocates fresh effect instance per layer
description: Compositor allocates fresh effect instance per layer
priority: critical
status: draft
node_type: leaf
parent_id: FX-03
acceptance_criteria: Compositor allocates fresh effect instance per layer
test_status: not_written
created_at: 2026-03-17 04:41:05.397483
updated_at: 2026-03-17 04:57:33.449250
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Compositor allocates fresh effect instance per layer


id: FX-03.2
title: No shared mutable state between layers
description: No shared mutable state between layers
priority: critical
status: draft
node_type: leaf
parent_id: FX-03
acceptance_criteria: No shared mutable state between layers
test_status: not_written
created_at: 2026-03-17 04:41:05.652731
updated_at: 2026-03-17 04:57:35.496945
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN No shared mutable state between layers


id: FX-04
title: Effects reset all mutable state in onTrigger
description: Effects reset all mutable state in onTrigger
priority: critical
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:05.904474
updated_at: 2026-03-17 04:57:37.542550
acceptance_criteria:
- [FX-04.1] Every member variable reinitialized
- [FX-04.2] Prevents stale state from previous activation

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN effects reset all mutable state in ontrigger
THEN Every member variable reinitialized
AND Prevents stale state from previous activation


id: FX-04.1
title: Every member variable reinitialized
description: Every member variable reinitialized
priority: critical
status: draft
node_type: leaf
parent_id: FX-04
acceptance_criteria: Every member variable reinitialized
test_status: not_written
created_at: 2026-03-17 04:41:06.158233
updated_at: 2026-03-17 04:57:39.590867
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Every member variable reinitialized


id: FX-04.2
title: Prevents stale state from previous activation
description: Prevents stale state from previous activation
priority: critical
status: draft
node_type: leaf
parent_id: FX-04
acceptance_criteria: Prevents stale state from previous activation
test_status: not_written
created_at: 2026-03-17 04:41:06.412750
updated_at: 2026-03-17 04:57:41.623099
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the system is operating under normal conditions
THEN Prevents stale state from previous activation
AND this invariant holds under all tested scenarios


id: FX-05
title: Flash effect
description: Flash effect
priority: critical
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:06.657744
updated_at: 2026-03-17 04:57:43.661029
acceptance_criteria:
- [FX-05.1] Fill buffer with color
- [FX-05.2] Linear brightness decay controlled by decay_rate param

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN flash effect
THEN Fill buffer with color
AND Linear brightness decay controlled by decay_rate param


id: FX-05.1
title: Fill buffer with color
description: Fill buffer with color
priority: critical
status: draft
node_type: leaf
parent_id: FX-05
acceptance_criteria: Fill buffer with color
test_status: not_written
created_at: 2026-03-17 04:41:06.912713
updated_at: 2026-03-17 04:57:45.705982
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Fill buffer with color


id: FX-05.2
title: Linear brightness decay controlled by decay_rate param
description: Linear brightness decay controlled by decay_rate param
priority: critical
status: draft
node_type: leaf
parent_id: FX-05
acceptance_criteria: Linear brightness decay controlled by decay_rate param
test_status: not_written
created_at: 2026-03-17 04:41:07.168128
updated_at: 2026-03-17 04:57:47.761051
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Linear brightness decay controlled by decay_rate param


id: FX-06
title: Ripple effect
description: Ripple effect
priority: high
status: draft
node_type: requirement
parent_id: FX
acceptance_criteria: - [FX-06.1] Expanding ring from configurable origin - [FX-06.2] Speed, direction, and decay params
test_status: not_written
created_at: 2026-03-17 04:41:07.426319
updated_at: 2026-03-17 04:57:49.808581
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN ripple effect
THEN Expanding ring from configurable origin
AND Speed, direction, and decay params


id: FX-06.1
title: Expanding ring from configurable origin
description: Expanding ring from configurable origin
priority: high
status: draft
node_type: leaf
parent_id: FX-06
acceptance_criteria: Expanding ring from configurable origin
test_status: not_written
created_at: 2026-03-17 04:41:07.682578
updated_at: 2026-03-17 04:57:51.855135
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the default configuration is loaded
THEN Expanding ring from configurable origin
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: FX-06.2
title: Speed, direction, and decay params
description: Speed, direction, and decay params
priority: high
status: draft
node_type: leaf
parent_id: FX-06
acceptance_criteria: Speed, direction, and decay params
test_status: not_written
created_at: 2026-03-17 04:41:07.933773
updated_at: 2026-03-17 04:57:53.897401
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the operation is performed under load
THEN Speed, direction, and decay params
AND the timing constraint is verified via measurement


id: FX-07
title: VuMeter effect
description: VuMeter effect
priority: high
status: draft
node_type: requirement
parent_id: FX
acceptance_criteria: - [FX-07.1] Vertical bars proportional to velocity - [FX-07.2] Direction and decay params
test_status: not_written
created_at: 2026-03-17 04:41:08.174599
updated_at: 2026-03-17 04:57:55.936764
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN vumeter effect
THEN Vertical bars proportional to velocity
AND Direction and decay params


id: FX-07.1
title: Vertical bars proportional to velocity
description: Vertical bars proportional to velocity
priority: high
status: draft
node_type: leaf
parent_id: FX-07
acceptance_criteria: Vertical bars proportional to velocity
test_status: not_written
created_at: 2026-03-17 04:41:08.418726
updated_at: 2026-03-17 04:57:57.990299
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Vertical bars proportional to velocity


id: FX-07.2
title: Direction and decay params
description: Direction and decay params
priority: high
status: draft
node_type: leaf
parent_id: FX-07
acceptance_criteria: Direction and decay params
test_status: not_written
created_at: 2026-03-17 04:41:08.672880
updated_at: 2026-03-17 04:58:00.030735
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the operation is performed under load
THEN Direction and decay params
AND the timing constraint is verified via measurement


id: FX-08
title: Sparkle effect
description: Sparkle effect
priority: high
status: draft
node_type: requirement
parent_id: FX
acceptance_criteria: - [FX-08.1] Random sparkles at configurable density - [FX-08.2] Fade on release
test_status: not_written
created_at: 2026-03-17 04:41:08.926335
updated_at: 2026-03-17 04:58:02.066555
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN sparkle effect
THEN Random sparkles at configurable density
AND Fade on release


id: FX-08.1
title: Random sparkles at configurable density
description: Random sparkles at configurable density
priority: high
status: draft
node_type: leaf
parent_id: FX-08
acceptance_criteria: Random sparkles at configurable density
test_status: not_written
created_at: 2026-03-17 04:41:09.169338
updated_at: 2026-03-17 04:58:04.116757
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the default configuration is loaded
THEN Random sparkles at configurable density
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: FX-08.2
title: Fade on release
description: Fade on release
priority: high
status: draft
node_type: leaf
parent_id: FX-08
acceptance_criteria: Fade on release
test_status: not_written
created_at: 2026-03-17 04:41:09.427865
updated_at: 2026-03-17 04:58:06.157955
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Fade on release


id: FX-09
title: Wave effect
description: Wave effect
priority: high
status: draft
node_type: requirement
parent_id: FX
acceptance_criteria: - [FX-09.1] Scrolling sine wave using beatsin8 (drift-free) - [FX-09.2] Speed maps to BPM 15-120
test_status: not_written
created_at: 2026-03-17 04:41:09.674627
updated_at: 2026-03-17 04:58:08.202773
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN wave effect
THEN Scrolling sine wave using beatsin8 (drift-free)
AND Speed maps to BPM 15-120


id: FX-09.1
title: Scrolling sine wave using beatsin8 (drift-free)
description: Scrolling sine wave using beatsin8 (drift-free)
priority: high
status: draft
node_type: leaf
parent_id: FX-09
acceptance_criteria: Scrolling sine wave using beatsin8 (drift-free)
test_status: not_written
created_at: 2026-03-17 04:41:09.926843
updated_at: 2026-03-17 04:58:10.258413
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Scrolling sine wave using beatsin8 (drift-free)


id: FX-09.2
title: Speed maps to BPM 15-120
description: Speed maps to BPM 15-120
priority: high
status: draft
node_type: leaf
parent_id: FX-09
acceptance_criteria: Speed maps to BPM 15-120
test_status: not_written
created_at: 2026-03-17 04:41:10.165811
updated_at: 2026-03-17 04:58:12.304716
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the input is processed
THEN Speed maps to BPM 15-120


id: FX-10
title: Chase effect
description: Chase effect
priority: high
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:10.404513
updated_at: 2026-03-17 04:58:14.359016
acceptance_criteria:
- [FX-10.1] Moving lit segment with exponential fade trail
- [FX-10.2] Speed, direction, tail length params

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN chase effect
THEN Moving lit segment with exponential fade trail
AND Speed, direction, tail length params


id: FX-10.1
title: Moving lit segment with exponential fade trail
description: Moving lit segment with exponential fade trail
priority: high
status: draft
node_type: leaf
parent_id: FX-10
acceptance_criteria: Moving lit segment with exponential fade trail
test_status: not_written
created_at: 2026-03-17 04:41:10.658148
updated_at: 2026-03-17 04:58:16.406251
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Moving lit segment with exponential fade trail


id: FX-10.2
title: Speed, direction, tail length params
description: Speed, direction, tail length params
priority: high
status: draft
node_type: leaf
parent_id: FX-10
acceptance_criteria: Speed, direction, tail length params
test_status: not_written
created_at: 2026-03-17 04:41:10.912870
updated_at: 2026-03-17 04:58:18.454752
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the operation is performed under load
THEN Speed, direction, tail length params
AND the timing constraint is verified via measurement


id: FX-11
title: Strobe effect
description: Strobe effect
priority: high
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:11.167396
updated_at: 2026-03-17 04:58:20.496246
acceptance_criteria:
- [FX-11.1] On/off flash at configurable period
- [FX-11.2] Hard cap at 10Hz (100ms minimum period) for seizure safety

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN strobe effect
THEN On/off flash at configurable period
AND Hard cap at 10Hz (100ms minimum period) for seizure safety


id: FX-11.1
title: On/off flash at configurable period
description: On/off flash at configurable period
priority: high
status: draft
node_type: leaf
parent_id: FX-11
acceptance_criteria: On/off flash at configurable period
test_status: not_written
created_at: 2026-03-17 04:41:11.412549
updated_at: 2026-03-17 04:58:22.536538
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the default configuration is loaded
THEN On/off flash at configurable period
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: FX-11.2
title: Hard cap at 10Hz (100ms minimum period) for seizure safety
description: Hard cap at 10Hz (100ms minimum period) for seizure safety
priority: high
status: draft
node_type: leaf
parent_id: FX-11
acceptance_criteria: Hard cap at 10Hz (100ms minimum period) for seizure safety
test_status: not_written
created_at: 2026-03-17 04:41:11.652898
updated_at: 2026-03-17 04:58:24.580990
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the operation is performed under load
THEN Hard cap at 10Hz (100ms minimum period) for seizure safety
AND the timing constraint is verified via measurement


id: FX-12
title: FillDrain effect
description: FillDrain effect
priority: high
status: draft
node_type: requirement
parent_id: FX
acceptance_criteria: - [FX-12.1] Fill columns proportional to velocity - [FX-12.2] Speed and direction params
test_status: not_written
created_at: 2026-03-17 04:41:11.909439
updated_at: 2026-03-17 04:58:26.623644
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN filldrain effect
THEN Fill columns proportional to velocity
AND Speed and direction params


id: FX-12.1
title: Fill columns proportional to velocity
description: Fill columns proportional to velocity
priority: high
status: draft
node_type: leaf
parent_id: FX-12
acceptance_criteria: Fill columns proportional to velocity
test_status: not_written
created_at: 2026-03-17 04:41:12.162628
updated_at: 2026-03-17 04:58:28.662219
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Fill columns proportional to velocity


id: FX-12.2
title: Speed and direction params
description: Speed and direction params
priority: high
status: draft
node_type: leaf
parent_id: FX-12
acceptance_criteria: Speed and direction params
test_status: not_written
created_at: 2026-03-17 04:41:12.415549
updated_at: 2026-03-17 04:58:30.712747
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the operation is performed under load
THEN Speed and direction params
AND the timing constraint is verified via measurement


id: FX-13
title: Fire effect
description: Fire effect
priority: high
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:12.669602
updated_at: 2026-03-17 04:58:32.752231
acceptance_criteria:
- [FX-13.1] Per-pixel heat array simulation
- [FX-13.2] Cooling (default 55) and sparking (default 120) params

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN fire effect
THEN Per-pixel heat array simulation
AND Cooling (default 55) and sparking (default 120) params


id: FX-13.1
title: Per-pixel heat array simulation
description: Per-pixel heat array simulation
priority: high
status: draft
node_type: leaf
parent_id: FX-13
acceptance_criteria: Per-pixel heat array simulation
test_status: not_written
created_at: 2026-03-17 04:41:12.922734
updated_at: 2026-03-17 04:58:34.790377
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Per-pixel heat array simulation


id: FX-13.2
title: Cooling (default 55) and sparking (default 120) params
description: Cooling (default 55) and sparking (default 120) params
priority: high
status: draft
node_type: leaf
parent_id: FX-13
acceptance_criteria: Cooling (default 55) and sparking (default 120) params
test_status: not_written
created_at: 2026-03-17 04:41:13.177759
updated_at: 2026-03-17 04:58:36.831313
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the default configuration is loaded
THEN Cooling (default 55) and sparking (default 120) params
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: FX-14
title: LED range clamping per layer
description: LED range clamping per layer
priority: high
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:13.417788
updated_at: 2026-03-17 04:58:38.883805
acceptance_criteria:
- [FX-14.1] start_led and end_led from params[10-11]
- [FX-14.2] Compositor clamps render output to range

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN led range clamping per layer
THEN start_led and end_led from params[10-11]
AND Compositor clamps render output to range


id: FX-14.1
title: start_led and end_led from params[10-11]
description: start_led and end_led from params[10-11]
priority: high
status: draft
node_type: leaf
parent_id: FX-14
acceptance_criteria: start_led and end_led from params[10-11]
test_status: not_written
created_at: 2026-03-17 04:41:13.662715
updated_at: 2026-03-17 04:58:40.929202
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the operation is performed under load
THEN start_led and end_led from params[10-11]
AND the timing constraint is verified via measurement


id: FX-14.2
title: Compositor clamps render output to range
description: Compositor clamps render output to range
priority: high
status: draft
node_type: leaf
parent_id: FX-14
acceptance_criteria: Compositor clamps render output to range
test_status: not_written
created_at: 2026-03-17 04:41:13.904782
updated_at: 2026-03-17 04:58:42.981890
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the UI component renders
THEN Compositor clamps render output to range
AND the display updates correctly on state change


id: FX-15
title: 2D matrix XY mapping
description: 2D matrix XY mapping
priority: medium
status: draft
node_type: requirement
parent_id: FX
test_status: not_written
created_at: 2026-03-17 04:41:14.158802
updated_at: 2026-03-17 04:58:45.024103
acceptance_criteria:
- [FX-15.1] XY() function maps (x,y) to LED index
- [FX-15.2] Serpentine wiring: even rows Lâ†’R, odd rows Râ†’L
- [FX-15.3] MATRIX_WIDTH=32, MATRIX_HEIGHT=8 configurable

test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN 2d matrix xy mapping
THEN XY() function maps (x,y) to LED index
AND Serpentine wiring: even rows Lâ†’R, odd rows Râ†’L
AND MATRIX_WIDTH=32, MATRIX_HEIGHT=8 configurable


id: FX-15.1
title: XY() function maps (x,y) to LED index
description: XY() function maps (x,y) to LED index
priority: medium
status: draft
node_type: leaf
parent_id: FX-15
acceptance_criteria: XY() function maps (x,y) to LED index
test_status: not_written
created_at: 2026-03-17 04:41:14.407200
updated_at: 2026-03-17 04:58:47.078466
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the input is processed
THEN XY() function maps (x,y) to LED index


id: FX-15.2
title: Serpentine wiring: even rows L-R, odd rows R-L
description: Serpentine wiring: even rows Lâ†’R, odd rows Râ†’L
priority: medium
status: draft
node_type: leaf
parent_id: FX-15
acceptance_criteria: Serpentine wiring: even rows Lâ†’R, odd rows Râ†’L
test_status: not_written
created_at: 2026-03-17 04:41:14.662997
updated_at: 2026-03-17 04:58:49.114020
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the feature is exercised
THEN Serpentine wiring: even rows Lâ†’R, odd rows Râ†’L


id: FX-15.3
title: MATRIX_WIDTH=32, MATRIX_HEIGHT=8 configurable
description: MATRIX_WIDTH=32, MATRIX_HEIGHT=8 configurable
priority: medium
status: draft
node_type: leaf
parent_id: FX-15
acceptance_criteria: MATRIX_WIDTH=32, MATRIX_HEIGHT=8 configurable
test_status: not_written
created_at: 2026-03-17 04:41:14.914470
updated_at: 2026-03-17 04:58:51.148330
test_spec:
GIVEN the effect engine is initialized with HAL interfaces
WHEN the default configuration is loaded
THEN MATRIX_WIDTH=32, MATRIX_HEIGHT=8 configurable
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: FLEET
title: Fleet Management
description: Gateway, pairing, OTA, health monitoring
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:41:15.168004
updated_at: 2026-03-17 04:58:53.188733
acceptance_criteria:
- [FLEET-01] Gateway as WiFi AP
- [FLEET-02] Gateway UDP-to-ESP-NOW bridge
- [FLEET-03] Client auto-pairing state machine
- [FLEET-04] NVS fast-pair caching
- [FLEET-05] Gateway peer management
- [FLEET-06] Gateway heartbeat broadcast every 500ms
- [FLEET-07] Client heartbeat echo and metrics reporting every 5s
- [FLEET-08] Graceful degradation on gateway disconnect
- [FLEET-09] OTA firmware update
- [FLEET-10] Sequential fleet deployment
- [FLEET-11] WiFi channel scanning
- [FLEET-12] WiFi channel change

test_spec:
GIVEN the Fleet Management pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: FLEET-01
title: Gateway as WiFi AP
description: Gateway as WiFi AP
priority: critical
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:15.422855
updated_at: 2026-03-17 04:58:55.244865
acceptance_criteria:
- [FLEET-01.1] SSID and password configurable
- [FLEET-01.2] Static IP 192.168.4.1/24
- [FLEET-01.3] Disable WiFi power save (WIFI_PS_NONE)
- [FLEET-01.4] Auto-channel selection on startup

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN gateway as wifi ap
THEN SSID and password configurable
AND Static IP 192.168.4.1/24
AND Disable WiFi power save (WIFI_PS_NONE)
AND Auto-channel selection on startup


id: FLEET-01.1
title: SSID and password configurable
description: SSID and password configurable
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-01
acceptance_criteria: SSID and password configurable
test_status: not_written
created_at: 2026-03-17 04:41:15.664903
updated_at: 2026-03-17 04:58:57.289284
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the default configuration is loaded
THEN SSID and password configurable
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: FLEET-01.2
title: Static IP 192.168.4.1/24
description: Static IP 192.168.4.1/24
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-01
acceptance_criteria: Static IP 192.168.4.1/24
test_status: not_written
created_at: 2026-03-17 04:41:15.917840
updated_at: 2026-03-17 04:58:59.344880
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Static IP 192.168.4.1/24


id: FLEET-01.3
title: Disable WiFi power save (WIFI_PS_NONE)
description: Disable WiFi power save (WIFI_PS_NONE)
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-01
acceptance_criteria: Disable WiFi power save (WIFI_PS_NONE)
test_status: not_written
created_at: 2026-03-17 04:41:16.169728
updated_at: 2026-03-17 04:59:01.384009
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the relevant event occurs
THEN Disable WiFi power save (WIFI_PS_NONE)
AND the stored data is retrievable


id: FLEET-01.4
title: Auto-channel selection on startup
description: Auto-channel selection on startup
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-01
acceptance_criteria: Auto-channel selection on startup
test_status: not_written
created_at: 2026-03-17 04:41:16.414697
updated_at: 2026-03-17 04:59:03.424332
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Auto-channel selection on startup


id: FLEET-02
title: Gateway UDP-to-ESP-NOW bridge
description: Gateway UDP-to-ESP-NOW bridge
priority: critical
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:16.654452
updated_at: 2026-03-17 04:59:05.486943
acceptance_criteria:
- [FLEET-02.1] UDP listener on port 9000
- [FLEET-02.2] Drain UDP into ring buffer (4 slots, newest kept)
- [FLEET-02.3] Forward via ESP-NOW broadcast with MAC trailer

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN gateway udp-to-esp-now bridge
THEN UDP listener on port 9000
AND Drain UDP into ring buffer (4 slots, newest kept)
AND Forward via ESP-NOW broadcast with MAC trailer


id: FLEET-02.1
title: UDP listener on port 9000
description: UDP listener on port 9000
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-02
acceptance_criteria: UDP listener on port 9000
test_status: not_written
created_at: 2026-03-17 04:41:16.907621
updated_at: 2026-03-17 04:59:07.533242
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the receiving endpoint is active
THEN UDP listener on port 9000
AND received data matches what was sent


id: FLEET-02.2
title: Drain UDP into ring buffer (4 slots, newest kept)
description: Drain UDP into ring buffer (4 slots, newest kept)
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-02
acceptance_criteria: Drain UDP into ring buffer (4 slots, newest kept)
test_status: not_written
created_at: 2026-03-17 04:41:17.163088
updated_at: 2026-03-17 04:59:09.579888
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Drain UDP into ring buffer (4 slots, newest kept)


id: FLEET-02.3
title: Forward via ESP-NOW broadcast with MAC trailer
description: Forward via ESP-NOW broadcast with MAC trailer
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-02
acceptance_criteria: Forward via ESP-NOW broadcast with MAC trailer
test_status: not_written
created_at: 2026-03-17 04:41:17.418220
updated_at: 2026-03-17 04:59:11.633709
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN a message is dispatched
THEN Forward via ESP-NOW broadcast with MAC trailer
AND the recipient receives the correct payload


id: FLEET-03
title: Client auto-pairing state machine
description: Client auto-pairing state machine
priority: critical
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:17.672945
updated_at: 2026-03-17 04:59:13.686061
acceptance_criteria:
- [FLEET-03.1] PAIR_SCANNING: iterate channels 1-13, broadcast CMD_PAIR_REQ every 250ms
- [FLEET-03.2] PAIR_REQUESTED: waiting for CMD_PAIR_RESP
- [FLEET-03.3] PAIR_PAIRED: normal operation
- [FLEET-03.4] Never call esp_wifi_set_channel() after pairing (P3)

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN client auto-pairing state machine
THEN PAIR_SCANNING: iterate channels 1-13, broadcast CMD_PAIR_REQ every 250ms
AND PAIR_REQUESTED: waiting for CMD_PAIR_RESP
AND PAIR_PAIRED: normal operation
AND Never call esp_wifi_set_channel() after pairing (P3)


id: FLEET-03.1
title: PAIR_SCANNING: iterate channels 1-13, broadcast CMD_PAIR_REQ every 250ms
description: PAIR_SCANNING: iterate channels 1-13, broadcast CMD_PAIR_REQ every 250ms
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-03
acceptance_criteria: PAIR_SCANNING: iterate channels 1-13, broadcast CMD_PAIR_REQ every 250ms
test_status: not_written
created_at: 2026-03-17 04:41:17.922931
updated_at: 2026-03-17 04:59:15.732636
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN PAIR_SCANNING: iterate channels 1-13, broadcast CMD_PAIR_REQ every 250ms
AND the timing constraint is verified via measurement


id: FLEET-03.2
title: PAIR_REQUESTED: waiting for CMD_PAIR_RESP
description: PAIR_REQUESTED: waiting for CMD_PAIR_RESP
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-03
acceptance_criteria: PAIR_REQUESTED: waiting for CMD_PAIR_RESP
test_status: not_written
created_at: 2026-03-17 04:41:18.176342
updated_at: 2026-03-17 04:59:17.792607
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN PAIR_REQUESTED: waiting for CMD_PAIR_RESP


id: FLEET-03.3
title: PAIR_PAIRED: normal operation
description: PAIR_PAIRED: normal operation
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-03
acceptance_criteria: PAIR_PAIRED: normal operation
test_status: not_written
created_at: 2026-03-17 04:41:18.422674
updated_at: 2026-03-17 04:59:19.834273
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN PAIR_PAIRED: normal operation


id: FLEET-03.4
title: Never call esp_wifi_set_channel() after pairing (P3)
description: Never call esp_wifi_set_channel() after pairing (P3)
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-03
acceptance_criteria: Never call esp_wifi_set_channel() after pairing (P3)
test_status: not_written
created_at: 2026-03-17 04:41:18.683136
updated_at: 2026-03-17 04:59:21.893920
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the system is operating under normal conditions
THEN Never call esp_wifi_set_channel() after pairing (P3)
AND this invariant holds under all tested scenarios


id: FLEET-04
title: NVS fast-pair caching
description: NVS fast-pair caching
priority: high
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:18.942885
updated_at: 2026-03-17 04:59:23.946580
acceptance_criteria:
- [FLEET-04.1] Store gateway MAC + channel in NVS on successful pair
- [FLEET-04.2] On boot: try cached values first (500ms timeout)
- [FLEET-04.3] Fall back to full 1-13 scan if cached pair fails

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN nvs fast-pair caching
THEN Store gateway MAC + channel in NVS on successful pair
AND On boot: try cached values first (500ms timeout)
AND Fall back to full 1-13 scan if cached pair fails


id: FLEET-04.1
title: Store gateway MAC + channel in NVS on successful pair
description: Store gateway MAC + channel in NVS on successful pair
priority: high
status: draft
node_type: leaf
parent_id: FLEET-04
acceptance_criteria: Store gateway MAC + channel in NVS on successful pair
test_status: not_written
created_at: 2026-03-17 04:41:19.194111
updated_at: 2026-03-17 04:59:25.987536
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the relevant event occurs
THEN Store gateway MAC + channel in NVS on successful pair
AND the stored data is retrievable


id: FLEET-04.2
title: On boot: try cached values first (500ms timeout)
description: On boot: try cached values first (500ms timeout)
priority: high
status: draft
node_type: leaf
parent_id: FLEET-04
acceptance_criteria: On boot: try cached values first (500ms timeout)
test_status: not_written
created_at: 2026-03-17 04:41:19.448957
updated_at: 2026-03-17 04:59:28.038211
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN On boot: try cached values first (500ms timeout)
AND the timing constraint is verified via measurement


id: FLEET-04.3
title: Fall back to full 1-13 scan if cached pair fails
description: Fall back to full 1-13 scan if cached pair fails
priority: high
status: draft
node_type: leaf
parent_id: FLEET-04
acceptance_criteria: Fall back to full 1-13 scan if cached pair fails
test_status: not_written
created_at: 2026-03-17 04:41:19.702854
updated_at: 2026-03-17 04:59:30.074561
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the detection/scanning process runs
THEN Fall back to full 1-13 scan if cached pair fails


id: FLEET-05
title: Gateway peer management
description: Gateway peer management
priority: critical
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:19.957359
updated_at: 2026-03-17 04:59:32.111907
acceptance_criteria:
- [FLEET-05.1] Track peers: mac, last_seen_ms, send_failures, paired
- [FLEET-05.2] Timeout: remove peers not seen in 3000ms
- [FLEET-05.3] Auto-register peers from return-path data
- [FLEET-05.4] MAX_CLIENTS = 20

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN gateway peer management
THEN Track peers: mac, last_seen_ms, send_failures, paired
AND Timeout: remove peers not seen in 3000ms
AND Auto-register peers from return-path data
AND MAX_CLIENTS = 20


id: FLEET-05.1
title: Track peers: mac, last_seen_ms, send_failures, paired
description: Track peers: mac, last_seen_ms, send_failures, paired
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-05
acceptance_criteria: Track peers: mac, last_seen_ms, send_failures, paired
test_status: not_written
created_at: 2026-03-17 04:41:20.213049
updated_at: 2026-03-17 04:59:34.148063
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN Track peers: mac, last_seen_ms, send_failures, paired
AND the timing constraint is verified via measurement


id: FLEET-05.2
title: Timeout: remove peers not seen in 3000ms
description: Timeout: remove peers not seen in 3000ms
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-05
acceptance_criteria: Timeout: remove peers not seen in 3000ms
test_status: not_written
created_at: 2026-03-17 04:41:20.457354
updated_at: 2026-03-17 04:59:36.190776
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN Timeout: remove peers not seen in 3000ms
AND the timing constraint is verified via measurement


id: FLEET-05.3
title: Auto-register peers from return-path data
description: Auto-register peers from return-path data
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-05
acceptance_criteria: Auto-register peers from return-path data
test_status: not_written
created_at: 2026-03-17 04:41:20.702872
updated_at: 2026-03-17 04:59:38.236996
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Auto-register peers from return-path data


id: FLEET-05.4
title: MAX_CLIENTS = 20
description: MAX_CLIENTS = 20
priority: critical
status: draft
node_type: leaf
parent_id: FLEET-05
acceptance_criteria: MAX_CLIENTS = 20
test_status: not_written
created_at: 2026-03-17 04:41:20.955851
updated_at: 2026-03-17 04:59:40.289738
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN MAX_CLIENTS = 20


id: FLEET-06
title: Gateway heartbeat broadcast every 500ms
description: Gateway heartbeat broadcast every 500ms
priority: high
status: draft
node_type: requirement
parent_id: FLEET
acceptance_criteria: Gateway heartbeat broadcast every 500ms
test_status: not_written
created_at: 2026-03-17 04:41:21.209797
updated_at: 2026-03-17 04:59:42.329148
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Gateway heartbeat broadcast every 500ms


id: FLEET-07
title: Client heartbeat echo and metrics reporting every 5s
description: Client heartbeat echo and metrics reporting every 5s
priority: high
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:21.466875
updated_at: 2026-03-17 04:59:44.375484
acceptance_criteria:
- [FLEET-07.1] MetricsReport: free_heap, min_heap, uptime, fps, dropped_cmds, crc_fails, seq_gaps, error_count, fw_version, rssi, wifi_channel, frame_time_us, mac_filter stats

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN client heartbeat echo and metrics reporting every 5s
THEN MetricsReport: free_heap, min_heap, uptime, fps, dropped_cmds, crc_fails, seq_gaps, error_count, fw_version, rssi, wifi_channel, frame_time_us, mac_filter stats


id: FLEET-07.1
priority: high
status: draft
node_type: leaf
parent_id: FLEET-07
test_status: not_written
created_at: 2026-03-17 04:41:21.718251
updated_at: 2026-03-17 04:59:46.412829
title:
MetricsReport: free_heap, min_heap, uptime, fps, dropped_cmds, crc_fails, seq_gaps, error_count, fw_version, rssi, wifi_channel, frame_time_us, mac_filter stats

description:
MetricsReport: free_heap, min_heap, uptime, fps, dropped_cmds, crc_fails, seq_gaps, error_count, fw_version, rssi, wifi_channel, frame_time_us, mac_filter stats

acceptance_criteria:
MetricsReport: free_heap, min_heap, uptime, fps, dropped_cmds, crc_fails, seq_gaps, error_count, fw_version, rssi, wifi_channel, frame_time_us, mac_filter stats

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN input matching the filter criteria is received
THEN MetricsReport: free_heap, min_heap, uptime, fps, dropped_cmds, crc_fails, seq_gaps, error_count, fw_version, rssi, wifi_channel, frame_time_us, mac_filter stats
AND non-matching input passes through unchanged


id: FLEET-08
title: Graceful degradation on gateway disconnect
description: Graceful degradation on gateway disconnect
priority: high
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:21.957368
updated_at: 2026-03-17 04:59:48.471781
acceptance_criteria:
- [FLEET-08.1] Detect disconnect via 3s heartbeat timeout
- [FLEET-08.2] Dim brightness by 1 level every 5s
- [FLEET-08.3] Never go below MIN_BRIGHTNESS=16
- [FLEET-08.4] Restore brightness on reconnect

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN graceful degradation on gateway disconnect
THEN Detect disconnect via 3s heartbeat timeout
AND Dim brightness by 1 level every 5s
AND Never go below MIN_BRIGHTNESS=16
AND Restore brightness on reconnect


id: FLEET-08.1
title: Detect disconnect via 3s heartbeat timeout
description: Detect disconnect via 3s heartbeat timeout
priority: high
status: draft
node_type: leaf
parent_id: FLEET-08
acceptance_criteria: Detect disconnect via 3s heartbeat timeout
test_status: not_written
created_at: 2026-03-17 04:41:22.213035
updated_at: 2026-03-17 04:59:50.522244
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN Detect disconnect via 3s heartbeat timeout
AND the timing constraint is verified via measurement


id: FLEET-08.2
title: Dim brightness by 1 level every 5s
description: Dim brightness by 1 level every 5s
priority: high
status: draft
node_type: leaf
parent_id: FLEET-08
acceptance_criteria: Dim brightness by 1 level every 5s
test_status: not_written
created_at: 2026-03-17 04:41:22.464014
updated_at: 2026-03-17 04:59:52.555692
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Dim brightness by 1 level every 5s


id: FLEET-08.3
title: Never go below MIN_BRIGHTNESS=16
description: Never go below MIN_BRIGHTNESS=16
priority: high
status: draft
node_type: leaf
parent_id: FLEET-08
acceptance_criteria: Never go below MIN_BRIGHTNESS=16
test_status: not_written
created_at: 2026-03-17 04:41:22.706700
updated_at: 2026-03-17 04:59:54.595580
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the system is operating under normal conditions
THEN Never go below MIN_BRIGHTNESS=16
AND this invariant holds under all tested scenarios


id: FLEET-08.4
title: Restore brightness on reconnect
description: Restore brightness on reconnect
priority: high
status: draft
node_type: leaf
parent_id: FLEET-08
acceptance_criteria: Restore brightness on reconnect
test_status: not_written
created_at: 2026-03-17 04:41:22.946470
updated_at: 2026-03-17 04:59:56.634826
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the relevant event occurs
THEN Restore brightness on reconnect
AND the stored data is retrievable


id: FLEET-09
title: OTA firmware update
description: OTA firmware update
priority: high
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:23.199885
updated_at: 2026-03-17 04:59:58.674807
acceptance_criteria:
- [FLEET-09.1] CMD_OTA_BEGIN triggers deferred OTA (needs 8KB stack)
- [FLEET-09.2] Client connects to gateway WiFi AP for HTTP download
- [FLEET-09.3] Dual OTA partitions with bootloader rollback
- [FLEET-09.4] validateFirmwareOnBoot() marks partition valid

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN ota firmware update
THEN CMD_OTA_BEGIN triggers deferred OTA (needs 8KB stack)
AND Client connects to gateway WiFi AP for HTTP download
AND Dual OTA partitions with bootloader rollback
AND validateFirmwareOnBoot() marks partition valid


id: FLEET-09.1
title: CMD_OTA_BEGIN triggers deferred OTA (needs 8KB stack)
description: CMD_OTA_BEGIN triggers deferred OTA (needs 8KB stack)
priority: high
status: draft
node_type: leaf
parent_id: FLEET-09
acceptance_criteria: CMD_OTA_BEGIN triggers deferred OTA (needs 8KB stack)
test_status: not_written
created_at: 2026-03-17 04:41:23.452984
updated_at: 2026-03-17 05:00:00.735596
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN CMD_OTA_BEGIN triggers deferred OTA (needs 8KB stack)


id: FLEET-09.2
title: Client connects to gateway WiFi AP for HTTP download
description: Client connects to gateway WiFi AP for HTTP download
priority: high
status: draft
node_type: leaf
parent_id: FLEET-09
acceptance_criteria: Client connects to gateway WiFi AP for HTTP download
test_status: not_written
created_at: 2026-03-17 04:41:23.705261
updated_at: 2026-03-17 05:00:02.778061
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Client connects to gateway WiFi AP for HTTP download


id: FLEET-09.3
title: Dual OTA partitions with bootloader rollback
description: Dual OTA partitions with bootloader rollback
priority: high
status: draft
node_type: leaf
parent_id: FLEET-09
acceptance_criteria: Dual OTA partitions with bootloader rollback
test_status: not_written
created_at: 2026-03-17 04:41:23.958910
updated_at: 2026-03-17 05:00:04.821857
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Dual OTA partitions with bootloader rollback


id: FLEET-09.4
title: validateFirmwareOnBoot() marks partition valid
description: validateFirmwareOnBoot() marks partition valid
priority: high
status: draft
node_type: leaf
parent_id: FLEET-09
acceptance_criteria: validateFirmwareOnBoot() marks partition valid
test_status: not_written
created_at: 2026-03-17 04:41:24.212661
updated_at: 2026-03-17 05:00:06.865182
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN validateFirmwareOnBoot() marks partition valid


id: FLEET-10
title: Sequential fleet deployment
description: Sequential fleet deployment
priority: high
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:24.456417
updated_at: 2026-03-17 05:00:08.903237
acceptance_criteria:
- [FLEET-10.1] Deploy one device at a time
- [FLEET-10.2] Per-device status: queued/deploying/success/failed
- [FLEET-10.3] Verify firmware version changed after OTA

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN sequential fleet deployment
THEN Deploy one device at a time
AND Per-device status: queued/deploying/success/failed
AND Verify firmware version changed after OTA


id: FLEET-10.1
title: Deploy one device at a time
description: Deploy one device at a time
priority: high
status: draft
node_type: leaf
parent_id: FLEET-10
acceptance_criteria: Deploy one device at a time
test_status: not_written
created_at: 2026-03-17 04:41:24.702716
updated_at: 2026-03-17 05:00:10.942425
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Deploy one device at a time


id: FLEET-10.2
title: Per-device status: queued/deploying/success/failed
description: Per-device status: queued/deploying/success/failed
priority: high
status: draft
node_type: leaf
parent_id: FLEET-10
acceptance_criteria: Per-device status: queued/deploying/success/failed
test_status: not_written
created_at: 2026-03-17 04:41:24.953041
updated_at: 2026-03-17 05:00:12.982119
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Per-device status: queued/deploying/success/failed


id: FLEET-10.3
title: Verify firmware version changed after OTA
description: Verify firmware version changed after OTA
priority: high
status: draft
node_type: leaf
parent_id: FLEET-10
acceptance_criteria: Verify firmware version changed after OTA
test_status: not_written
created_at: 2026-03-17 04:41:25.207717
updated_at: 2026-03-17 05:00:15.030264
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Verify firmware version changed after OTA


id: FLEET-11
title: WiFi channel scanning
description: WiFi channel scanning
priority: medium
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:25.446068
updated_at: 2026-03-17 05:00:17.082867
acceptance_criteria:
- [FLEET-11.1] Per-channel passive scan (300ms each)
- [FLEET-11.2] Score: ap_count Ã— 1000 + rssi_factor - preferred_bonus
- [FLEET-11.3] Preferred channels: 1, 6, 11

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN wifi channel scanning
THEN Per-channel passive scan (300ms each)
AND Score: ap_count Ã— 1000 + rssi_factor - preferred_bonus
AND Preferred channels: 1, 6, 11


id: FLEET-11.1
title: Per-channel passive scan (300ms each)
description: Per-channel passive scan (300ms each)
priority: medium
status: draft
node_type: leaf
parent_id: FLEET-11
acceptance_criteria: Per-channel passive scan (300ms each)
test_status: not_written
created_at: 2026-03-17 04:41:25.702967
updated_at: 2026-03-17 05:00:19.137216
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN Per-channel passive scan (300ms each)
AND the timing constraint is verified via measurement


id: FLEET-11.2
title: Score: ap_count x 1000 + rssi_factor - preferred_bonus
description: Score: ap_count Ã— 1000 + rssi_factor - preferred_bonus
priority: medium
status: draft
node_type: leaf
parent_id: FLEET-11
acceptance_criteria: Score: ap_count Ã— 1000 + rssi_factor - preferred_bonus
test_status: not_written
created_at: 2026-03-17 04:41:25.954508
updated_at: 2026-03-17 05:00:21.186347
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Score: ap_count Ã— 1000 + rssi_factor - preferred_bonus


id: FLEET-11.3
title: Preferred channels: 1, 6, 11
description: Preferred channels: 1, 6, 11
priority: medium
status: draft
node_type: leaf
parent_id: FLEET-11
acceptance_criteria: Preferred channels: 1, 6, 11
test_status: not_written
created_at: 2026-03-17 04:41:26.211687
updated_at: 2026-03-17 05:00:23.232021
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Preferred channels: 1, 6, 11


id: FLEET-12
title: WiFi channel change
description: WiFi channel change
priority: medium
status: draft
node_type: requirement
parent_id: FLEET
test_status: not_written
created_at: 2026-03-17 04:41:26.462925
updated_at: 2026-03-17 05:00:25.268570
acceptance_criteria:
- [FLEET-12.1] Stop AP, wait 100ms, reconfigure, restart
- [FLEET-12.2] Clear all peers (clients must re-pair)

test_spec:
GIVEN the fleet management system is running on ESP32
WHEN wifi channel change
THEN Stop AP, wait 100ms, reconfigure, restart
AND Clear all peers (clients must re-pair)


id: FLEET-12.1
title: Stop AP, wait 100ms, reconfigure, restart
description: Stop AP, wait 100ms, reconfigure, restart
priority: medium
status: draft
node_type: leaf
parent_id: FLEET-12
acceptance_criteria: Stop AP, wait 100ms, reconfigure, restart
test_status: not_written
created_at: 2026-03-17 04:41:26.717919
updated_at: 2026-03-17 05:00:27.299507
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the operation is performed under load
THEN Stop AP, wait 100ms, reconfigure, restart
AND the timing constraint is verified via measurement


id: FLEET-12.2
title: Clear all peers (clients must re-pair)
description: Clear all peers (clients must re-pair)
priority: medium
status: draft
node_type: leaf
parent_id: FLEET-12
acceptance_criteria: Clear all peers (clients must re-pair)
test_status: not_written
created_at: 2026-03-17 04:41:26.972764
updated_at: 2026-03-17 05:00:29.343732
test_spec:
GIVEN the fleet management system is running on ESP32
WHEN the feature is exercised
THEN Clear all peers (clients must re-pair)


id: CFG
title: Configuration
description: Config model, persistence, hot-reload, migration
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:41:27.225942
updated_at: 2026-03-17 05:00:31.390011
acceptance_criteria:
- [CFG-01] SystemConfig validated by Pydantic v2
- [CFG-02] Atomic persistence via tmp + rename
- [CFG-03] Hot-reload via atomic reference swap
- [CFG-04] Schema versioning with auto-migration
- [CFG-05] Config export/import
- [CFG-06] Global and per-client brightness
- [CFG-07] Color palette system
- [CFG-08] CC mappings

test_spec:
GIVEN the Configuration pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: CFG-01
title: SystemConfig validated by Pydantic v2
description: SystemConfig validated by Pydantic v2
priority: critical
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:27.472963
updated_at: 2026-03-17 05:00:33.437052
acceptance_criteria:
- [CFG-01.1] Pydantic BaseModel with field validators
- [CFG-01.2] Validation on load and on API update

test_spec:
GIVEN the configuration system is initialized
WHEN systemconfig validated by pydantic v2
THEN Pydantic BaseModel with field validators
AND Validation on load and on API update


id: CFG-01.1
title: Pydantic BaseModel with field validators
description: Pydantic BaseModel with field validators
priority: critical
status: draft
node_type: leaf
parent_id: CFG-01
acceptance_criteria: Pydantic BaseModel with field validators
test_status: not_written
created_at: 2026-03-17 04:41:27.717432
updated_at: 2026-03-17 05:00:35.486145
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Pydantic BaseModel with field validators


id: CFG-01.2
title: Validation on load and on API update
description: Validation on load and on API update
priority: critical
status: draft
node_type: leaf
parent_id: CFG-01
acceptance_criteria: Validation on load and on API update
test_status: not_written
created_at: 2026-03-17 04:41:27.973041
updated_at: 2026-03-17 05:00:37.532411
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Validation on load and on API update


id: CFG-02
title: Atomic persistence via tmp + rename
description: Atomic persistence via tmp + rename
priority: critical
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:28.226506
updated_at: 2026-03-17 05:00:39.569904
acceptance_criteria:
- [CFG-02.1] Write to .tmp file first
- [CFG-02.2] Atomic rename to config.json
- [CFG-02.3] Survives power loss during save

test_spec:
GIVEN the configuration system is initialized
WHEN atomic persistence via tmp + rename
THEN Write to .tmp file first
AND Atomic rename to config.json
AND Survives power loss during save


id: CFG-02.1
title: Write to .tmp file first
description: Write to .tmp file first
priority: critical
status: draft
node_type: leaf
parent_id: CFG-02
acceptance_criteria: Write to .tmp file first
test_status: not_written
created_at: 2026-03-17 04:41:28.482922
updated_at: 2026-03-17 05:00:41.605316
test_spec:
GIVEN the configuration system is initialized
WHEN a message is dispatched
THEN Write to .tmp file first
AND the recipient receives the correct payload


id: CFG-02.2
title: Atomic rename to config.json
description: Atomic rename to config.json
priority: critical
status: draft
node_type: leaf
parent_id: CFG-02
acceptance_criteria: Atomic rename to config.json
test_status: not_written
created_at: 2026-03-17 04:41:28.734461
updated_at: 2026-03-17 05:00:43.653490
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Atomic rename to config.json


id: CFG-02.3
title: Survives power loss during save
description: Survives power loss during save
priority: critical
status: draft
node_type: leaf
parent_id: CFG-02
acceptance_criteria: Survives power loss during save
test_status: not_written
created_at: 2026-03-17 04:41:28.973996
updated_at: 2026-03-17 05:00:45.690609
test_spec:
GIVEN the configuration system is initialized
WHEN the relevant event occurs
THEN Survives power loss during save
AND the stored data is retrievable


id: CFG-03
title: Hot-reload via atomic reference swap
description: Hot-reload via atomic reference swap
priority: high
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:29.218120
updated_at: 2026-03-17 05:00:47.736261
acceptance_criteria:
- [CFG-03.1] Rebuild entire config object
- [CFG-03.2] Single reference assignment (GIL-safe)
- [CFG-03.3] MIDI thread sees new config on next message

test_spec:
GIVEN the configuration system is initialized
WHEN hot-reload via atomic reference swap
THEN Rebuild entire config object
AND Single reference assignment (GIL-safe)
AND MIDI thread sees new config on next message


id: CFG-03.1
title: Rebuild entire config object
description: Rebuild entire config object
priority: high
status: draft
node_type: leaf
parent_id: CFG-03
acceptance_criteria: Rebuild entire config object
test_status: not_written
created_at: 2026-03-17 04:41:29.464817
updated_at: 2026-03-17 05:00:49.781204
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Rebuild entire config object


id: CFG-03.2
title: Single reference assignment (GIL-safe)
description: Single reference assignment (GIL-safe)
priority: high
status: draft
node_type: leaf
parent_id: CFG-03
acceptance_criteria: Single reference assignment (GIL-safe)
test_status: not_written
created_at: 2026-03-17 04:41:29.712753
updated_at: 2026-03-17 05:00:51.830510
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Single reference assignment (GIL-safe)


id: CFG-03.3
title: MIDI thread sees new config on next message
description: MIDI thread sees new config on next message
priority: high
status: draft
node_type: leaf
parent_id: CFG-03
acceptance_criteria: MIDI thread sees new config on next message
test_status: not_written
created_at: 2026-03-17 04:41:29.963197
updated_at: 2026-03-17 05:00:53.885827
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN MIDI thread sees new config on next message


id: CFG-04
title: Schema versioning with auto-migration
description: Schema versioning with auto-migration
priority: high
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:30.215894
updated_at: 2026-03-17 05:00:55.937610
acceptance_criteria:
- [CFG-04.1] Version field in config JSON
- [CFG-04.2] Migration functions: v1â†’v2â†’v3
- [CFG-04.3] Auto-migrate on load

test_spec:
GIVEN the configuration system is initialized
WHEN schema versioning with auto-migration
THEN Version field in config JSON
AND Migration functions: v1â†’v2â†’v3
AND Auto-migrate on load


id: CFG-04.1
title: Version field in config JSON
description: Version field in config JSON
priority: high
status: draft
node_type: leaf
parent_id: CFG-04
acceptance_criteria: Version field in config JSON
test_status: not_written
created_at: 2026-03-17 04:41:30.473133
updated_at: 2026-03-17 05:00:57.982171
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Version field in config JSON


id: CFG-04.2
title: Migration functions: v1 to v2 to v3
description: Migration functions: v1â†’v2â†’v3
priority: high
status: draft
node_type: leaf
parent_id: CFG-04
acceptance_criteria: Migration functions: v1â†’v2â†’v3
test_status: not_written
created_at: 2026-03-17 04:41:30.724535
updated_at: 2026-03-17 05:01:00.028903
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Migration functions: v1â†’v2â†’v3


id: CFG-04.3
title: Auto-migrate on load
description: Auto-migrate on load
priority: high
status: draft
node_type: leaf
parent_id: CFG-04
acceptance_criteria: Auto-migrate on load
test_status: not_written
created_at: 2026-03-17 04:41:30.964277
updated_at: 2026-03-17 05:01:02.066895
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Auto-migrate on load


id: CFG-05
title: Config export/import
description: Config export/import
priority: medium
status: draft
node_type: requirement
parent_id: CFG
acceptance_criteria: - [CFG-05.1] JSON download from API - [CFG-05.2] JSON upload with schema validation
test_status: not_written
created_at: 2026-03-17 04:41:31.205524
updated_at: 2026-03-17 05:01:04.109556
test_spec:
GIVEN the configuration system is initialized
WHEN config export/import
THEN JSON download from API
AND JSON upload with schema validation


id: CFG-05.1
title: JSON download from API
description: JSON download from API
priority: medium
status: draft
node_type: leaf
parent_id: CFG-05
acceptance_criteria: JSON download from API
test_status: not_written
created_at: 2026-03-17 04:41:31.450031
updated_at: 2026-03-17 05:01:06.154939
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN JSON download from API


id: CFG-05.2
title: JSON upload with schema validation
description: JSON upload with schema validation
priority: medium
status: draft
node_type: leaf
parent_id: CFG-05
acceptance_criteria: JSON upload with schema validation
test_status: not_written
created_at: 2026-03-17 04:41:31.704955
updated_at: 2026-03-17 05:01:08.204480
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN JSON upload with schema validation


id: CFG-06
title: Global and per-client brightness
description: Global and per-client brightness
priority: high
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:31.958959
updated_at: 2026-03-17 05:01:10.258857
acceptance_criteria:
- [CFG-06.1] global_brightness: 0-255
- [CFG-06.2] client_brightness map: MAC â†’ brightness override
- [CFG-06.3] Send brightness on startup

test_spec:
GIVEN the configuration system is initialized
WHEN global and per-client brightness
THEN global_brightness: 0-255
AND client_brightness map: MAC â†’ brightness override
AND Send brightness on startup


id: CFG-06.1
title: global_brightness: 0-255
description: global_brightness: 0-255
priority: high
status: draft
node_type: leaf
parent_id: CFG-06
acceptance_criteria: global_brightness: 0-255
test_status: not_written
created_at: 2026-03-17 04:41:32.198963
updated_at: 2026-03-17 05:01:12.312329
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN global_brightness: 0-255


id: CFG-06.2
title: client_brightness map: MAC to brightness override
description: client_brightness map: MAC â†’ brightness override
priority: high
status: draft
node_type: leaf
parent_id: CFG-06
acceptance_criteria: client_brightness map: MAC â†’ brightness override
test_status: not_written
created_at: 2026-03-17 04:41:32.453835
updated_at: 2026-03-17 05:01:14.348372
test_spec:
GIVEN the configuration system is initialized
WHEN the input is processed
THEN client_brightness map: MAC â†’ brightness override


id: CFG-06.3
title: Send brightness on startup
description: Send brightness on startup
priority: high
status: draft
node_type: leaf
parent_id: CFG-06
acceptance_criteria: Send brightness on startup
test_status: not_written
created_at: 2026-03-17 04:41:32.707937
updated_at: 2026-03-17 05:01:16.386712
test_spec:
GIVEN the configuration system is initialized
WHEN a message is dispatched
THEN Send brightness on startup
AND the recipient receives the correct payload


id: CFG-07
title: Color palette system
description: Color palette system
priority: medium
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:32.962994
updated_at: 2026-03-17 05:01:18.437302
acceptance_criteria:
- [CFG-07.1] Named palettes with color arrays
- [CFG-07.2] Palette resolution: palette_id â†’ colors

test_spec:
GIVEN the configuration system is initialized
WHEN color palette system
THEN Named palettes with color arrays
AND Palette resolution: palette_id â†’ colors


id: CFG-07.1
title: Named palettes with color arrays
description: Named palettes with color arrays
priority: medium
status: draft
node_type: leaf
parent_id: CFG-07
acceptance_criteria: Named palettes with color arrays
test_status: not_written
created_at: 2026-03-17 04:41:33.217980
updated_at: 2026-03-17 05:01:20.487351
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Named palettes with color arrays


id: CFG-07.2
title: Palette resolution: palette_id to colors
description: Palette resolution: palette_id â†’ colors
priority: medium
status: draft
node_type: leaf
parent_id: CFG-07
acceptance_criteria: Palette resolution: palette_id â†’ colors
test_status: not_written
created_at: 2026-03-17 04:41:33.466574
updated_at: 2026-03-17 05:01:22.524060
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN Palette resolution: palette_id â†’ colors


id: CFG-08
title: CC mappings
description: CC mappings
priority: high
status: draft
node_type: requirement
parent_id: CFG
test_status: not_written
created_at: 2026-03-17 04:41:33.712868
updated_at: 2026-03-17 05:01:24.561384
acceptance_criteria:
- [CFG-08.1] CC number â†’ target_param + target_note + min/max range
- [CFG-08.2] CC value scaled to min_value..max_value range

test_spec:
GIVEN the configuration system is initialized
WHEN cc mappings
THEN CC number â†’ target_param + target_note + min/max range
AND CC value scaled to min_value..max_value range


id: CFG-08.1
title: CC number to target_param + target_note + min/max range
description: CC number â†’ target_param + target_note + min/max range
priority: high
status: draft
node_type: leaf
parent_id: CFG-08
acceptance_criteria: CC number â†’ target_param + target_note + min/max range
test_status: not_written
created_at: 2026-03-17 04:41:33.963994
updated_at: 2026-03-17 05:01:26.595971
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN CC number â†’ target_param + target_note + min/max range


id: CFG-08.2
title: CC value scaled to min_value..max_value range
description: CC value scaled to min_value..max_value range
priority: high
status: draft
node_type: leaf
parent_id: CFG-08
acceptance_criteria: CC value scaled to min_value..max_value range
test_status: not_written
created_at: 2026-03-17 04:41:34.217983
updated_at: 2026-03-17 05:01:28.652073
test_spec:
GIVEN the configuration system is initialized
WHEN the feature is exercised
THEN CC value scaled to min_value..max_value range


id: WEBUI
title: Web UI
description: Angular dashboard, config editor, monitoring
priority: high
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:41:34.473130
updated_at: 2026-03-17 05:01:30.695265
acceptance_criteria:
- [UI-01] Angular 19 with standalone components
- [UI-02] Monitor dashboard with per-client cards
- [UI-03] Per-client brightness sliders
- [UI-04] Instrument group editor
- [UI-05] Effect configuration panel
- [UI-06] WiFi channel management
- [UI-07] Build controls
- [UI-08] Deploy controls
- [UI-09] WebSocket service
- [UI-10] Authentication
- [UI-11] Dark mode
- [UI-12] Responsive layout

test_spec:
GIVEN the Web UI pillar is fully implemented
WHEN all child requirements are verified
THEN every requirement listed in the acceptance criteria passes its own test spec


id: UI-01
title: Angular 19 with standalone components
description: Angular 19 with standalone components
priority: critical
status: draft
node_type: requirement
parent_id: WEBUI
test_status: not_written
created_at: 2026-03-17 04:41:34.728596
updated_at: 2026-03-17 05:01:32.747173
acceptance_criteria:
- [UI-01.1] Standalone components (no NgModules)
- [UI-01.2] Lazy-loaded feature routes
- [UI-01.3] OnPush change detection on all components

test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN angular 19 with standalone components
THEN Standalone components (no NgModules)
AND Lazy-loaded feature routes
AND OnPush change detection on all components


id: UI-01.1
title: Standalone components (no NgModules)
description: Standalone components (no NgModules)
priority: critical
status: draft
node_type: leaf
parent_id: UI-01
acceptance_criteria: Standalone components (no NgModules)
test_status: not_written
created_at: 2026-03-17 04:41:34.973080
updated_at: 2026-03-17 05:01:34.810314
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Standalone components (no NgModules)


id: UI-01.2
title: Lazy-loaded feature routes
description: Lazy-loaded feature routes
priority: critical
status: draft
node_type: leaf
parent_id: UI-01
acceptance_criteria: Lazy-loaded feature routes
test_status: not_written
created_at: 2026-03-17 04:41:35.225930
updated_at: 2026-03-17 05:01:36.840179
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Lazy-loaded feature routes


id: UI-01.3
title: OnPush change detection on all components
description: OnPush change detection on all components
priority: critical
status: draft
node_type: leaf
parent_id: UI-01
acceptance_criteria: OnPush change detection on all components
test_status: not_written
created_at: 2026-03-17 04:41:35.468783
updated_at: 2026-03-17 05:01:38.873460
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the detection/scanning process runs
THEN OnPush change detection on all components


id: UI-02
title: Monitor dashboard with per-client cards
description: Monitor dashboard with per-client cards
priority: critical
status: draft
node_type: requirement
parent_id: WEBUI
test_status: not_written
created_at: 2026-03-17 04:41:35.714195
updated_at: 2026-03-17 05:01:40.929705
acceptance_criteria:
- [UI-02.1] Card per connected client
- [UI-02.2] Display: health, RSSI, firmware version, FPS, heap
- [UI-02.3] Real-time updates via WebSocket

test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN monitor dashboard with per-client cards
THEN Card per connected client
AND Display: health, RSSI, firmware version, FPS, heap
AND Real-time updates via WebSocket


id: UI-02.1
title: Card per connected client
description: Card per connected client
priority: critical
status: draft
node_type: leaf
parent_id: UI-02
acceptance_criteria: Card per connected client
test_status: not_written
created_at: 2026-03-17 04:41:35.966599
updated_at: 2026-03-17 05:01:42.974069
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Card per connected client


id: UI-02.2
title: Display: health, RSSI, firmware version, FPS, heap
description: Display: health, RSSI, firmware version, FPS, heap
priority: critical
status: draft
node_type: leaf
parent_id: UI-02
acceptance_criteria: Display: health, RSSI, firmware version, FPS, heap
test_status: not_written
created_at: 2026-03-17 04:41:36.223009
updated_at: 2026-03-17 05:01:45.027222
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the UI component renders
THEN Display: health, RSSI, firmware version, FPS, heap
AND the display updates correctly on state change


id: UI-02.3
title: Real-time updates via WebSocket
description: Real-time updates via WebSocket
priority: critical
status: draft
node_type: leaf
parent_id: UI-02
acceptance_criteria: Real-time updates via WebSocket
test_status: not_written
created_at: 2026-03-17 04:41:36.467627
updated_at: 2026-03-17 05:01:47.074738
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Real-time updates via WebSocket


id: UI-03
title: Per-client brightness sliders
description: Per-client brightness sliders
priority: high
status: draft
node_type: requirement
parent_id: WEBUI
acceptance_criteria: Per-client brightness sliders
test_status: not_written
created_at: 2026-03-17 04:41:36.713141
updated_at: 2026-03-17 05:01:49.112088
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Per-client brightness sliders


id: UI-04
title: Instrument group editor
description: Instrument group editor
priority: high
status: draft
node_type: requirement
parent_id: WEBUI
test_status: not_written
created_at: 2026-03-17 04:41:36.963301
updated_at: 2026-03-17 05:01:51.143763
acceptance_criteria:
- [UI-04.1] Create/edit/delete instrument groups
- [UI-04.2] Per-note configuration within group
- [UI-04.3] Visual feedback for LED range assignment

test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN instrument group editor
THEN Create/edit/delete instrument groups
AND Per-note configuration within group
AND Visual feedback for LED range assignment


id: UI-04.1
title: Create/edit/delete instrument groups
description: Create/edit/delete instrument groups
priority: high
status: draft
node_type: leaf
parent_id: UI-04
acceptance_criteria: Create/edit/delete instrument groups
test_status: not_written
created_at: 2026-03-17 04:41:37.218313
updated_at: 2026-03-17 05:01:53.190122
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Create/edit/delete instrument groups


id: UI-04.2
title: Per-note configuration within group
description: Per-note configuration within group
priority: high
status: draft
node_type: leaf
parent_id: UI-04
acceptance_criteria: Per-note configuration within group
test_status: not_written
created_at: 2026-03-17 04:41:37.470094
updated_at: 2026-03-17 05:01:55.221639
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the operation is performed under load
THEN Per-note configuration within group
AND the timing constraint is verified via measurement


id: UI-04.3
title: Visual feedback for LED range assignment
description: Visual feedback for LED range assignment
priority: high
status: draft
node_type: leaf
parent_id: UI-04
acceptance_criteria: Visual feedback for LED range assignment
test_status: not_written
created_at: 2026-03-17 04:41:37.725071
updated_at: 2026-03-17 05:01:57.256790
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the UI component renders
THEN Visual feedback for LED range assignment
AND the display updates correctly on state change


id: UI-05
title: Effect configuration panel
description: Effect configuration panel
priority: high
status: draft
node_type: requirement
parent_id: WEBUI
acceptance_criteria: - [UI-05.1] 16-param sliders per effect - [UI-05.2] Default values from effect registration
test_status: not_written
created_at: 2026-03-17 04:41:37.978514
updated_at: 2026-03-17 05:01:59.289337
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN effect configuration panel
THEN 16-param sliders per effect
AND Default values from effect registration


id: UI-05.1
title: 16-param sliders per effect
description: 16-param sliders per effect
priority: high
status: draft
node_type: leaf
parent_id: UI-05
acceptance_criteria: 16-param sliders per effect
test_status: not_written
created_at: 2026-03-17 04:41:38.233590
updated_at: 2026-03-17 05:02:01.342047
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN 16-param sliders per effect


id: UI-05.2
title: Default values from effect registration
description: Default values from effect registration
priority: high
status: draft
node_type: leaf
parent_id: UI-05
acceptance_criteria: Default values from effect registration
test_status: not_written
created_at: 2026-03-17 04:41:38.476520
updated_at: 2026-03-17 05:02:03.392547
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the default configuration is loaded
THEN Default values from effect registration
AND WHEN a custom value is configured
THEN the custom value is used instead of the default


id: UI-06
title: WiFi channel management
description: WiFi channel management
priority: medium
status: draft
node_type: requirement
parent_id: WEBUI
test_status: not_written
created_at: 2026-03-17 04:41:38.725823
updated_at: 2026-03-17 05:02:05.430410
acceptance_criteria:
- [UI-06.1] Trigger channel scan
- [UI-06.2] Display scan results with signal strength
- [UI-06.3] Switch channel with confirmation

test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN wifi channel management
THEN Trigger channel scan
AND Display scan results with signal strength
AND Switch channel with confirmation


id: UI-06.1
title: Trigger channel scan
description: Trigger channel scan
priority: medium
status: draft
node_type: leaf
parent_id: UI-06
acceptance_criteria: Trigger channel scan
test_status: not_written
created_at: 2026-03-17 04:41:38.979031
updated_at: 2026-03-17 05:02:07.466750
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the detection/scanning process runs
THEN Trigger channel scan


id: UI-06.2
title: Display scan results with signal strength
description: Display scan results with signal strength
priority: medium
status: draft
node_type: leaf
parent_id: UI-06
acceptance_criteria: Display scan results with signal strength
test_status: not_written
created_at: 2026-03-17 04:41:39.218681
updated_at: 2026-03-17 05:02:09.520264
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the detection/scanning process runs
THEN Display scan results with signal strength


id: UI-06.3
title: Switch channel with confirmation
description: Switch channel with confirmation
priority: medium
status: draft
node_type: leaf
parent_id: UI-06
acceptance_criteria: Switch channel with confirmation
test_status: not_written
created_at: 2026-03-17 04:41:39.472877
updated_at: 2026-03-17 05:02:11.574948
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Switch channel with confirmation


id: UI-07
title: Build controls
description: Build controls
priority: high
status: draft
node_type: requirement
parent_id: WEBUI
acceptance_criteria: - [UI-07.1] Trigger PlatformIO build - [UI-07.2] Stream build output via WebSocket
test_status: not_written
created_at: 2026-03-17 04:41:39.717032
updated_at: 2026-03-17 05:02:13.608462
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN build controls
THEN Trigger PlatformIO build
AND Stream build output via WebSocket


id: UI-07.1
title: Trigger PlatformIO build
description: Trigger PlatformIO build
priority: high
status: draft
node_type: leaf
parent_id: UI-07
acceptance_criteria: Trigger PlatformIO build
test_status: not_written
created_at: 2026-03-17 04:41:39.973132
updated_at: 2026-03-17 05:02:15.667940
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Trigger PlatformIO build


id: UI-07.2
title: Stream build output via WebSocket
description: Stream build output via WebSocket
priority: high
status: draft
node_type: leaf
parent_id: UI-07
acceptance_criteria: Stream build output via WebSocket
test_status: not_written
created_at: 2026-03-17 04:41:40.224694
updated_at: 2026-03-17 05:02:17.716079
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Stream build output via WebSocket


id: UI-08
title: Deploy controls
description: Deploy controls
priority: high
status: draft
node_type: requirement
parent_id: WEBUI
acceptance_criteria: - [UI-08.1] Trigger OTA deploy (all or single device) - [UI-08.2] Per-device status tracking in UI
test_status: not_written
created_at: 2026-03-17 04:41:40.479031
updated_at: 2026-03-17 05:02:19.766390
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN deploy controls
THEN Trigger OTA deploy (all or single device)
AND Per-device status tracking in UI


id: UI-08.1
title: Trigger OTA deploy (all or single device)
description: Trigger OTA deploy (all or single device)
priority: high
status: draft
node_type: leaf
parent_id: UI-08
acceptance_criteria: Trigger OTA deploy (all or single device)
test_status: not_written
created_at: 2026-03-17 04:41:40.743013
updated_at: 2026-03-17 05:02:21.820904
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the feature is exercised
THEN Trigger OTA deploy (all or single device)


id: UI-08.2
title: Per-device status tracking in UI
description: Per-device status tracking in UI
priority: high
status: draft
node_type: leaf
parent_id: UI-08
acceptance_criteria: Per-device status tracking in UI
test_status: not_written
created_at: 2026-03-17 04:41:40.997660
updated_at: 2026-03-17 05:02:23.852422
test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN the relevant event occurs
THEN Per-device status tracking in UI
AND the stored data is retrievable


id: UI-09
title: WebSocket service
description: WebSocket service
priority: critical
status: draft
node_type: requirement
parent_id: WEBUI
test_status: not_written
created_at: 2026-03-17 04:41:41.237992
updated_at: 2026-03-17 05:02:25.900250
acceptance_criteria:
- [UI-09.1] Singleton service with typed observables
- [UI-09.2] Auto-reconnect with exponential backoff
- [UI-09.3] State refresh on reconnect

test_spec:
GIVEN the Angular web application is loaded in the browser
WHEN websocket service
THEN Singleton service with typed observables
AND Auto-reconnect with exponential backoff
AND State refresh on reconnect


id: TEST
title: Testability
description: TDD, test pyramid, HAL, traceability
priority: critical
status: draft
node_type: pillar
test_status: not_written
created_at: 2026-03-17 04:41:43.753191
updated_at: 2026-03-17 04:41:43.753191
acceptance_criteria:
HAL interfaces for all hardware, mock implementations, CI pipeline with coverage gating, requirements traceability


