Nothing had ever run the real `Controller` through the transition inverter B's commissioning performs on 2026-09-12. `tools/rehearse_roster.py` does, over a COPY of a state file given as an input path, and reports the difference: every store key that moved, appeared, vanished or was STRANDED under a name no machine reads; every entity id before and after with orphans named; and whether the maintenance history MAINT-05 moved once survived being moved again. ⭐ WHAT IT FOUND, AND IT IS SAFETY-44's ANSWER. A store that ran pre-DEV-02 flat and declares a multi-inverter roster comes up with its SAFETY-02 latch DISARMED unless the first roster entry happens to be called `inverter-a`. `Controller.__init__` hands `migrate_unit_keys()` the hardcoded `UNIT_A`, so the latch lands in `unit/inverter-a/sign_tripped`; a roster of ["garage","shed"] reads `unit/garage/sign_tripped`, which is absent. And `_grade_store_migration()` - the guard written for exactly this - sees the migration's OWN misdirected key satisfy its `namespaced_latched` test and returns without re-arming. Measured, not argued: the file says `sign_tripped: true`, no inverter is latched, nothing is held at 0 W. The same state file onto a roster whose first entry IS `inverter-a` comes up latched, so the finding is attributable to the name. The staged upgrade reaches it too: the flat start stamps `schema_version`, and the roster start's migration then takes its early return. ⚠️ NO RUNTIME CODE IS CHANGED HERE. The fix to `migrate_unit_keys()` belongs to SAFETY-44 reopened as a defect, shown red before it; this commit is the instrument that shows it red. THE CHECK CAN FAIL, AND WAS SEEN TO. `test_rehearse.py` establishes two CLEAN CONTROLS first - two transitions the shipped code gets right, on which the harness must say nothing - and then breaks the shipped migration and the entity-id composition six ways in a private copy of the tree, requiring each to turn its control from green to red WITH THE RIGHT FINDING NAMED. One mutation is required to SURVIVE, with the reason written down. The synthetic state carries maintenance history and a legacy-shaped latched `sign_tripped` with no `schema_version` beside it, because state built by the happy path cannot detect a migration that only mishandles legacy shapes. ⚠️ THE FIRST DRAFT OF THE HARNESS HAD HABIT 4 IN IT and its own battery caught it - MUTATION.md's thirteenth instance, third in an instrument. The entity diff composed both sides with the same build, so renaming the MQTT device moved every id equally and it reported "no orphans" about a change that orphans everything. `--live-entities` is the fix: a reference that does not move with the code. THE RETREAT is supported and does not corrupt state - the flat rollback view keeps the latch and the first pack's history live - but it orphans eight HA entities, and the ROUND TRIP is not supported: a maintenance cycle completed while rolled back is discarded on re-advance. Stated in DOCS.md as a decision. Fetching /data/state.json, the options blob and the entity list are OWNER STEPS and are written down. ⚠️ Nothing here was run against real state and nothing here claims to have been. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
12 lines
744 B
JSON
12 lines
744 B
JSON
{
|
|
"_comment": "ARCH-02 SYNTHETIC FIXTURE - a store that has ALREADY run the flat config under 0.7.0+, so DEV-02's `schema_version` is stamped and the SAFETY-02 latch is not tripped. NOT a copy of the live box's /data/state.json. This is the CLEAN CONTROL for the MAINT-05 half of the rehearsal: the flat->roster transition over it must come out with NO findings, which is what makes each deliberate mutation's RED mean something. A harness that is red on every input has not measured anything.",
|
|
"schema_version": 1,
|
|
"phase": "idle",
|
|
"phase_started": "2026-08-07T10:00:00+00:00",
|
|
"last_completed": "2026-08-07T18:42:00+00:00",
|
|
"last_start_attempt": "2026-08-07T10:00:00+00:00",
|
|
"sign_tripped": false,
|
|
"sign_reason": "",
|
|
"auto": true
|
|
}
|