Summary
The STATUS response header spreads the firmware version over two lines:
FW Version:MCU_Main V1.5.0/Web_GUI V1.4.0a
DSP V1.5.9
Every parser captures only the first line, so the DSP version is silently dropped. status (JSON and human output) reports firmware_version: "MCU_Main V1.5.0/Web_GUI V1.4.0a" with no DSP component. The DSP version is real, independently-revved firmware (fixtures show DSP V1.5.7 and V1.5.9 against the same MCU V1.5.0), so under-reporting it loses meaningful device state.
Observed against a live device on firmware MCU_Main V1.5.0 / Web_GUI V1.4.0a / DSP V1.5.9; reproducible offline with the committed fixtures.
Affected code
The single-line assumption is replicated in three parsers and asserted by the shared drift-gate vector, so this is a protocol-contract fix, not a one-file fix:
blustream/devices/dmp168/status_parser.py:70-81 — _parse_firmware returns the FW Version: line only; the indented DSP Vx.y.z continuation line matches nothing.
blustream/devices/dmp168/parser.py:130-137 — legacy parser, same logic (re.search(r"FW Version:([^\n]+)"), then break).
control4/dmp168/src/status_parser.lua:75-81 — Lua parse_firmware, same single-line capture.
spec/vectors/fixtures/status_full_routing.txt:11-12 vs status_full_routing.expected.yaml:9 — the input fixture contains DSP V1.5.7, but the expected output pins firmware_version: "MCU_Main V1.5.0/Web_GUI V1.5.0", encoding the bug into the cross-implementation contract (ADR-0007).
Repro: uv run python -c "from pathlib import Path; from blustream.devices.dmp168.status_parser import parse_status; print(parse_status(Path('tests/fixtures/status_live_full.txt').read_text())['firmware_version'])" → no DSP component, while the fixture's header (lines 3–4) carries DSP V1.5.9.
Open design decision
How to surface the DSP version — append to the existing string (e.g. MCU_Main V1.5.0/Web_GUI V1.4.0a/DSP V1.5.9) or add a separate field (e.g. dsp_version) to SystemStatus (blustream/devices/dmp168/models.py:105) and the Lua result table. A separate field is cleaner but changes the model/JSON shape consumers see; appending keeps the shape but overloads the string.
Acceptance criteria
Summary
The
STATUSresponse header spreads the firmware version over two lines:Every parser captures only the first line, so the DSP version is silently dropped.
status(JSON and human output) reportsfirmware_version: "MCU_Main V1.5.0/Web_GUI V1.4.0a"with no DSP component. The DSP version is real, independently-revved firmware (fixtures show DSP V1.5.7 and V1.5.9 against the same MCU V1.5.0), so under-reporting it loses meaningful device state.Observed against a live device on firmware MCU_Main V1.5.0 / Web_GUI V1.4.0a / DSP V1.5.9; reproducible offline with the committed fixtures.
Affected code
The single-line assumption is replicated in three parsers and asserted by the shared drift-gate vector, so this is a protocol-contract fix, not a one-file fix:
blustream/devices/dmp168/status_parser.py:70-81—_parse_firmwarereturns theFW Version:line only; the indentedDSP Vx.y.zcontinuation line matches nothing.blustream/devices/dmp168/parser.py:130-137— legacy parser, same logic (re.search(r"FW Version:([^\n]+)"), thenbreak).control4/dmp168/src/status_parser.lua:75-81— Luaparse_firmware, same single-line capture.spec/vectors/fixtures/status_full_routing.txt:11-12vsstatus_full_routing.expected.yaml:9— the input fixture containsDSP V1.5.7, but the expected output pinsfirmware_version: "MCU_Main V1.5.0/Web_GUI V1.5.0", encoding the bug into the cross-implementation contract (ADR-0007).Repro:
uv run python -c "from pathlib import Path; from blustream.devices.dmp168.status_parser import parse_status; print(parse_status(Path('tests/fixtures/status_live_full.txt').read_text())['firmware_version'])"→ no DSP component, while the fixture's header (lines 3–4) carriesDSP V1.5.9.Open design decision
How to surface the DSP version — append to the existing string (e.g.
MCU_Main V1.5.0/Web_GUI V1.4.0a/DSP V1.5.9) or add a separate field (e.g.dsp_version) toSystemStatus(blustream/devices/dmp168/models.py:105) and the Lua result table. A separate field is cleaner but changes the model/JSON shape consumers see; appending keeps the shape but overloads the string.Acceptance criteria
status_parser.pycaptures the DSP version from the continuation lineparser.py(legacy) agrees withstatus_parser.pycontrol4/dmp168/src/status_parser.luahas parity with the Python parsersspec/vectors/fixtures/status_full_routing.expected.yamlupdated so the shared vectors enforce the new contract in both runtimesSystemStatusmodel /to_dict/ formatter output expose the DSP version (per the design decision above)firmware_version)