ARCH-02 round 2: the pin could not tell one red leg from two, and the tool went
checks / unit (pull_request) Successful in 6m57s
checks / unit (pull_request) Successful in 6m57s
quiet when a flag was forgotten Both blocking findings were reproduced before anything was changed, and both are the class this card exists to guard against. B1. THE PIN WAS A COUNT, AND A COUNT CANNOT SEPARATE TWO ROUTES. The rehearsal reaches the disarmed latch by two independent paths and prints every finding TWICE per red leg - once in the leg's block, once in the closing summary - so ONE red leg satisfied `count(...) >= 2`. Measured: shipped code 4; with `migrate_unit_keys()` handed the roster's own name (SAME-STEP fixed) 2, PASSES; with a guard-side fix (both fixed) 0. The check named "NOT THE SAME-STEP PATH ALONE" reported ok on the build whose only remaining failure IS the staged path. The tool now prints `LEG <id>` / `END LEG <id>` and the pin requires the finding in EACH leg by name. ⚠️ AND THE FIRST SPLITTER CARRIED THE SAME DEFECT ONE LEVEL DOWN - it opened on the marker and never closed, so the last leg absorbed the closing summary and read red whenever ANY leg was red. Caught by building the one fixture that separates them. Part 5b is now that fixture, run every time: a tree in which SAME-STEP is genuinely clean and STAGED is not. A per-thing check that has never seen the things disagree is a total with extra steps. ⚠️ AND IT CORRECTS A CLAIM THIS CARD MADE. `main.py:250` is NOT the whole fix. Measured on that exact change: LEG SAME-STEP clean, LEG STAGED still DISARMED - the staged path stamps `schema_version` on the flat start and the roster start's migration returns early before the name it is handed matters. DOCS.md now says so, with the measurement. B2. A FORGOTTEN FLAG WAS A CLEAN BILL OF HEALTH. Omitting `--live-entities` printed "nothing orphaned" and exited 0, with `grep -ci` for the words returning 0 - while an UNREADABLE file exited 2. The asymmetry was backwards. Omitting it now refuses (exit 2); `--no-live-entities` is the deliberate opt-out and carries a standing banner in the header AND on the exit line, because a report is skimmed from the bottom. N1. THE REFERENCE DID NOT CARRY WHAT HA KEYS ON. It held entity_ids only, so renaming `mqtt.BASE` (every unique_id moves) and changing `DEVICE["identifiers"]` (HA sees a new device) both came back exit 0 - real orphanings with every composed id byte-identical. The reference is now three columns (`entity_id unique_id device`), is loaded on EVERY run including a degraded one, and M7/M8 pin both. `object_id` is deliberately not a column and its rename is required to SURVIVE, which is what bounds the four mqtt.py mutations. Also found while fixing the above, and fixed with its own paired positive: the VANISHED check did not carry the default/non-default rule the STRANDED check does, so a pair of migrated DEFAULTS moving namespace read as two findings and CONTROL-C went red on a CORRECT build. M9 drops non-default keys and must still be a finding. N2/N3. DOCS.md withdraws the unmeasured "not recoverable by advancing again" - its own reasoning cut the other way and nothing here tested a re-advance; the measured part (eight rows stop updating, history gaps, automations stop firing) stands. And the byte-identical-input property is `test_rehearse.py`'s assertion, not the tool's. FRAMING. SAFETY-44 is ALREADY RECORDED on the merge base - `test_maintenance.py` section 13, with the "guard is defeated by the very key the defect wrote" sentence, in a file this card does not touch. DOCS.md now says this is independent re-confirmation through a second instrument, reachable from an owner-supplied state file rather than only from a fixture, and not a discovery. app/ is still untouched. 59 checks in test_rehearse.py, 9 mutations red, 2 required survivors, 3 clean controls. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit is contained in:
@@ -251,6 +251,38 @@ same place twice, the check is decoration.
|
||||
this comparison come from* — and if a mutation of the subject moves both
|
||||
sides, the instrument is decoration.
|
||||
|
||||
⚠️ **AND REVIEW FOUND TWO MORE IN THE SAME CARD, ONE OF THEM INSIDE THE FIX
|
||||
FOR THE OTHER — the FOURTEENTH and FIFTEENTH.** Both are the pin, not the
|
||||
tool, and both are the same sentence in a new costume: *a check whose data
|
||||
cannot reach the failure it names.*
|
||||
|
||||
* **Fourteenth — a COUNT cannot tell one red leg from two.** The rehearsal
|
||||
reports two independent routes to the disarmed latch (`SAME-STEP` and
|
||||
`STAGED`) and prints every finding **twice per red leg**: once in that
|
||||
leg's own block and once in the closing summary. The check named *"NOT THE
|
||||
SAME-STEP PATH ALONE"* was written `out.count("LATCH CAME UP DISARMED")
|
||||
>= 2`, so **one** red leg always satisfied it. Measured on three builds:
|
||||
shipped code (both legs red) → 4; `migrate_unit_keys()` handed the roster
|
||||
name (SAME-STEP fixed) → 2, **passes**; a guard-side fix (both fixed) → 0.
|
||||
The check reported `ok` on the build whose only remaining failure *is* the
|
||||
path it says is not the only one. The fix is to parse the legs and require
|
||||
the finding in **each**, which is habit 2 again: grade the thing, not a
|
||||
total over the thing.
|
||||
|
||||
* **Fifteenth — and it is INSIDE THAT FIX, which is the third time this file
|
||||
has had to write that sentence.** The first leg-splitter opened on a
|
||||
`LEG <id>` marker and never closed, so every line after the *last* marker
|
||||
was attributed to that leg — including the closing summary, which repeats
|
||||
every finding from every leg. The last leg therefore read red whenever
|
||||
**any** leg was red, and the new per-leg check was the old count with extra
|
||||
steps. It was caught by building the one fixture that separates them: a
|
||||
tree in which `SAME-STEP` is genuinely clean and `STAGED` is not. The tool
|
||||
now prints `END LEG <id>` and the splitter closes on it.
|
||||
|
||||
The rule that falls out, and it is the cheap one: **a per-thing check that
|
||||
has never seen the things DISAGREE is a total.** Part 5b exists to make
|
||||
them disagree on every run.
|
||||
|
||||
* **CFG-04's `tools/supervisor_shape_probe.py` §B** — and this one is the rule
|
||||
reaching outside the suite. The probe is the committed evidence that
|
||||
`config.yaml`'s `inverters: []` default is load-bearing, and four documents
|
||||
|
||||
Reference in New Issue
Block a user