Repository navigation
fix(sound): refuse the unpublished bare WHO 22 power frame in the send layer; deprecate turn_on / turn_off - #163
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Live on the F461 2.0.11 test plant (2026-11-04):
|
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Thanks for investigating the F461 020011 / F454 020051 sound-daemon crash on bare *22*0*WHERE## / *22*1*WHERE## frames.
Preventing proven gateway-daemon crashes in the send layer (reason="destructive_frame", while keeping OWNCommand.parse permissive) fits the same safety exception as is_who4_dimension_22_crash_form and #157's 52#YYYY#M guard. However, under our standing rule — if we don't have real traces (or committed, reproducible oracle crash records for crash guards), we don't change behaviour — three changes are needed before this guard can land:
| # | Area / Symbol | Issue | Required action |
|---|---|---|---|
| 1 | Branch base & replacement references (sound.py, command_session.py, test_who22_bare_power_guard.py) |
Stacked on #158 and references turn_on_atomic in DESTRUCTIVE_WHO22_REASON, docstrings, and tests. #158's runtime WHO 22 / WHO 16 changes (including turn_on_atomic and area-derived turn_off_atomic) lack physical sound-plant traces and have changes requested. |
Rebase directly onto master so the crash guard stands on its own, and reference existing master builders (turn_on_with_source / route_source / turn_off_atomic) in DESTRUCTIVE_WHO22_REASON and docstrings rather than turn_on_atomic. |
| 2 | Missing committed oracle crash provenance (firmware_oracle.json / own-firmware-oracle) |
lookup_firmware_verdict currently has no committed rows for *22*1*2#1##, *22*0*2#1##, *22*1*3#1#1##, *22*0*4#1##, *22*1*6##, or *22*0*6## in own-firmware-oracle (mcp_index.json) or OWNd/tests/golden/firmware_oracle.json. |
Commit the F461 020011 / F454 020051 crash replay rows to own-firmware-oracle and pin them in OWNd/tests/golden/firmware_oracle.json so the destructive_frame refusal is backed by reproducible, hash-verified oracle evidence in-repo. |
| 3 | Runtime DeprecationWarning in OWNSoundDiffusionCommand.turn_on / turn_off (OWNd/message/sound.py) |
Emits warnings.warn(..., DeprecationWarning), breaking callers under filterwarnings = ["error"] (forcing pytest.warns(DeprecationWarning) edits in tests/test_sound_diffusion.py) without physical sound-plant traces settling the replacement power flow. |
Remove warnings.warn(..., DeprecationWarning) from turn_on and turn_off; document the F461 / F454 daemon crash and send-layer refusal in their docstrings while is_who22_bare_power_frame protects the send path. |
…und-daemon crash on 8 of 12 gateways) The bare `*22*1*WHERE##` / `*22*0*WHERE##` (WHAT 0 / 1 without the published `#mmtype#area` parameters) crashes the sound daemon of F453, F453AV, F454, F459, F460, F461, MH200N and MyHomeServer1 on every target class (2#1, 3#1#1, 4#1, 6) in the firmware oracle (suite sound-who22-bare-power, OpenWebNet-HA/own-firmware-oracle#78, two byte-identical runs each); the published `*22*1#4#1*WHERE##` goes out on the bus on the same eight and on MH202; F450, H4684 and MH200 drop both. `is_who22_bare_power_frame` detects the form and the command session, send_to_gateway and the serial transport refuse it with reason="destructive_frame", the pattern of the WHO 4 dimension 22 guard. `OWNSoundDiffusionCommand.turn_on` / `turn_off` build the same frames as before (their docstrings point to the published `turn_on_with_source` / `route_source` / `turn_off_atomic`), `OWNCommand.parse` stays permissive, no decoded value or log line changes. tests/golden/firmware_oracle.json pins the suite's 144 rows (12 frames x 12 emulated gateways): 520 unique inputs, 6,644 rows, 23 suites per gateway, digest recomputed; the conformance test pins the 64 crash rows to exactly that suite, frame shape and gateway set (no other crash, no timeout) and the golden README numbers follow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3dad2df to
28bfafc
Compare
|
Applied in
The suite widened the finding: the bare form crashes the sound daemon on eight of the twelve emulated gateways (F453, F453AV, F454, F459, F460, F461, MH200N, MyHomeServer1), whatever the WHERE class; the published One question for you: the golden pin now carries crash rows for the first time, so the conformance test's "zero crashes" invariant became "exactly these 64 rows of this suite". If you would rather keep the golden crash-free and point the guard at the oracle repo only (as |
Requested by A
Review follow-up (GreenGrassBlueOcean, 2026-11-04) — rebuilt as one commit on
mastermaster; referencemasterbuildersmaster(no #158 history).DESTRUCTIVE_WHO22_REASON, theturn_on/turn_offdocstrings and the tests nameturn_on_with_source/route_source/turn_off_atomic;turn_on_atomicis gone.oracle/cases/sound-who22-bare-power.caseswith result tables for all twelve emulated gateways (two byte-identical runs) and the regeneratedmcp_index.json;tests/golden/firmware_oracle.jsonhere pins the same 144 rows (520 inputs, 6,644 rows, 23 suites, digest recomputed) and the conformance test pins the 64 crash rows to exactly that suite, frame shape and gateway set.DeprecationWarningturn_on/turn_offbuild the same frames as onmasterand document the crash and the send-layer refusal in their docstrings.What the suite showed, wider than the first run: the bare form crashes the sound daemon on eight of the twelve emulated gateways (F453, F453AV, F454, F459, F460, F461, MH200N, MyHomeServer1), whatever the WHERE class; the published
*22*1#4#1*WHERE##goes out on the bus on the same eight and on MH202; F450, H4684 and MH200 drop both. The bare form was never sent to physical hardware; the published form was ACKed by the live F461 2.0.11 (comment above). The guard also covers the serial transport, as the WHO 4 dimension 22 guard does.Before:
OWNSoundDiffusionCommand.turn_on/turn_offbuilt the bare WHO 22 power frame, WHAT0/1with no#mmtype#areaparameters (*22*1*WHERE##,*22*0*WHERE##), and every send path let it through.After: every send path refuses the bare frame with
reason="destructive_frame", exactly as the WHO 4 dimension 22 and WHO 18 dimension 52 crash frames are refused;turn_on/turn_offstill build it for callers that pin the frame but issue aDeprecationWarningpointing toturn_on_atomic/turn_off_atomic. No new builder is added.OWNCommand.parsestays permissive (decision D1).Why: the bare form is not published. WHO 22 PDF 3.1.1 / 3.1.2 give
*22*0#<mmtype>#<area>*2#<sourceID>##/*22*1#<mmtype>#<area>*2#<sourceID>##and 3.2.1 gives*22*0#<mmtype>#<area>*3#<area><point>##(printed in the PDF without the#between the parameters; the tested form has it), with<area>the output area1..9. In the firmware oracle (2026-11-04, two byte-identical runs each) the bare form crashes the sound daemon on F461020011and F454020051on every tested target type — source2#S, amplifier3#A#P, area4#A, all sources6(6is "All sources" in the WHO 22 WHERE table; General is5#SENDER) — while MH202 refuses it without crashing, and the published forms go out (ack/out) on every target. The PDF also confirms the reading #158 already takes: the second parameter is the area, not the source, which is why no*_with_sourceOFF twin is added.MyHOME is unaffected: it powers audio zones through WHO 16 (
OWNSoundCommand.turn_on→*16*3*WHERE##,turn_off→*16*13*WHERE##) and never builds the WHO 22 form; its conformance suite passes in full against this branch.How:
is_who22_bare_power_frame()detector insound.py(exported), the refusal helper and error take a reason string, both send entry points check it after the dimension 22 detector. Tests: detector on every target, published forms not detected and reaching the write through both send paths (connection mocked), deprecated builders warn and keep their frame, both session entry points refusing before any connect. Full suite 2878 passed, mypy clean. Second opinion (DeepSeek) taken twice; its area-vs-source catch is what removed the OFF builder.🤖 Generated with Claude Code