SAFETY-60/62: operator tools refuse input they cannot read
checks / unit (pull_request) Successful in 8m31s

predeploy_check.py and rehearse_roster.py now assert a positive property of
their input (a JSON object, most of whose top-level keys are known option or
store keys) and refuse anything else with the shape named. The `ha apps info
--raw-json` wrapper, a truncated or list-wrapped state file and a wrapped
config no longer produce "CLEARED" / "NO FINDINGS" with exit 0.

Also: a clean --no-live-entities rehearsal exits 3, not 0; alloc_sim.py
exits 1 when split-invariance FAILS; decode_bus_capture.py takes several
paths and exits 1 on a file that is mostly not frames.

Graded by the new test_tool_inputs.py; DOCS.md says to pass .data.options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit is contained in:
glenn schrooyen
2026-09-24 23:27:50 +02:00
co-authored by Claude Opus 5.5
parent a45ed06c05
commit f4f72211a9
7 changed files with 390 additions and 25 deletions
+7 -3
View File
@@ -2843,7 +2843,8 @@ check, **before** the roster is declared:
2. **The options blob**, as `--before`. It is not reachable through the Home
Assistant REST API and no entity carries it; export it from the Supervisor,
the same way `tools/predeploy_check.py` needs it. Write the roster you intend
the same way `tools/predeploy_check.py` needs it: the bare options object,
not the `ha apps info` wrapper, which is refused. Write the roster you intend
to deploy into a second file and pass it as `--after`.
3. **The entity list**, as `--live-entities` — one entity id per line:
@@ -2857,7 +2858,8 @@ check, **before** the roster is declared:
hand back a clean bill of health for a half of the transition it did not
check. If you genuinely have to run without it, pass `--no-live-entities`:
the run then carries a standing banner in the report header **and** on the
exit line saying the orphan check was not graded. The asymmetry used to be
exit line saying the orphan check was not graded, and a clean result exits
3, not 0. The asymmetry used to be
the other way round — an unreadable file refused, a missing flag passed with
exit 0 and no disclosure at all — and the owner runs this once, at
commissioning, under time pressure.
@@ -3005,7 +3007,9 @@ Run it inside the container, so the validator half actually runs:
--deployed-version 0.7.0 --options /w/live-options.json
Export `live-options.json` from the Supervisor first — the options blob is not
reachable through the Home Assistant REST API, and no entity carries it.
reachable through the Home Assistant REST API, and no entity carries it. Pass the
bare object (`ha apps info <slug> --raw-json | jq .data.options`); the wrapped
output is refused.
**Exit codes.** `0` cleared · `1` REFUSED, do not deploy · `2` NOT CLEARED,
meaning a check did not run. ⚠️ **2 is not a pass.** On a bare host without the