ARCH-02: the harness was red on every fresh install, and a third control says so

A store the add-on has never written has no latch and no history. The DEV-02
migration still runs on it - `schema_version` is absent, so `from_version` is 0 -
and it writes `sign_tripped = False` and `sign_reason = ""` under `inverter-a`
before `build_roster()` exists. On a declared roster nothing reads either of
them, so the first draft called both STRANDED and exited 1. Every fresh install
that grows a second inverter would have been reported as a failed transition.

⚠️ THAT IS A DEFECT IN THE ALARM, NOT A CASE TO SUPPRESS, and store.py already
carries the distinction it needed: `MigrationReport.lost` reports a dropped
`sign_tripped=True` and NOT a dropped `sign_tripped=False`, because "absent" and
"never tripped" are the same statement - and because an alarm that fires on the
harmless case is one an operator learns to dismiss before the real one arrives.
The stranded check now makes the same split, on the VALUE and not on the key
name: a key stranded at its default is printed under its own heading, visibly,
and is not a finding; the same key stranded at a non-default still is. SAFETY-44
turns on `sign_tripped = True`, so it is untouched, and a check pins both
halves against each other so the softening cannot widen into a hole.

CONTROL-C is the fresh install, and it is a third clean control rather than a
line in a comment: the shipped code is right about it and the harness must say
nothing. It is the control that found this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit is contained in:
Claude Opus 5
2026-09-08 13:20:49 +02:00
parent 018da00b74
commit cb8f800180
3 changed files with 58 additions and 4 deletions
+25
View File
@@ -142,9 +142,18 @@ print("2. THE CLEAN CONTROLS: two transitions on which the shipped code is "
# CONTROL-B a store that has already run flat under 0.7.0 (schema stamped,
# no latch), onto a roster named `garage`/`shed`. This is the
# MAINT-05 half, and the names are deliberately not `inverter-a`.
# CONTROL-C a FRESH INSTALL - an empty state file - onto `garage`/`shed`.
# ⚠️ THIS ONE FOUND A DEFECT IN THE HARNESS. A fresh store's
# migration writes `sign_tripped = False` and `sign_reason = ""`
# under `inverter-a` before any roster exists, and the first draft
# reported both as STRANDED - red on every fresh install. Losing a
# DEFAULT loses nothing; that is `MigrationReport.lost`'s own
# distinction, and an alarm that fires on the harmless case is one
# an operator learns to dismiss before the real one arrives.
CONTROLS = {
"CONTROL-A": ("state-legacy.json", "roster-inverter-a.yaml"),
"CONTROL-B": ("state-flat-070.json", "roster.yaml"),
"CONTROL-C": ("state-fresh.json", "roster.yaml"),
}
@@ -379,6 +388,22 @@ print("6. THE REVERSE DIRECTION: the retreat, and the return from it")
# --------------------------------------------------------------------------- #
code_r, out_r = run(HERE, "state-flat-070.json", "roster.yaml",
"--reverse", "--round-trip")
# ⚠️ AND THE SOFTENING IS NOT A HOLE. The SAME key stranded with a NON-default
# value is still a finding - that is the whole of part 5 - so the two cases are
# separated by the VALUE and not by the key name.
_fresh_code, _fresh_out = run(HERE, "state-fresh.json", "roster.yaml")
check("a fresh install DOES strand `unit/inverter-a/sign_tripped` - the key is "
"written and nothing on the roster reads it - and the report SAYS SO",
_fresh_code == RR.EXIT_OK
and "unit/inverter-a/sign_tripped = False" in _fresh_out)
check("...and does NOT call it a finding, because the value is the default; "
"the same key at True is the finding part 5 turns on, so the two cases "
"are separated by the VALUE and not by the key name",
"STRANDED STORE KEY: unit/inverter-a/sign_tripped" not in _fresh_out
and "STRANDED STORE KEY: unit/inverter-a/sign_tripped = True"
in "\n".join(l.strip() for l in
run(HERE, "state-legacy.json", "roster.yaml")[1].splitlines()))
_orphaned = set(re.findall(r"ORPHANED ENTITY: (sensor\.\S+)", out_r))
print(f" the retreat orphans: {sorted(_orphaned)}")
check("the RETREAT is rehearsed at all, and it reports the entities it "
@@ -0,0 +1,3 @@
{
"_comment": "ARCH-02 SYNTHETIC FIXTURE - a FRESH INSTALL. No maintenance history, no latch, no schema markers: what /data/state.json looks like before the add-on has ever completed a cycle. It is a clean control, and it earned its place: the first draft of the harness reported this transition RED, because the DEV-02 migration writes both SAFETY-02 defaults under `inverter-a` before a roster exists and the tool called them stranded. Losing a default loses nothing."
}
+30 -4
View File
@@ -371,6 +371,9 @@ class Snapshot:
self.store_migration = controller.store_migration
self.maint_migration = controller.maint_migration
self.machine_read = machine_reads(controller, store)
# Keys no machine reads whose value is the default `Store.load()` would
# have supplied. Reported, never a finding: see `diff()`.
self.stranded_at_default = []
@property
def unit_keys(self):
@@ -451,14 +454,27 @@ def diff(before: Snapshot, after: Snapshot) -> list:
# ever look under. `MigrationReport.misdirected` says the same thing about
# the keys the migration itself wrote; this says it about the WHOLE FILE,
# including keys written by a version that is no longer installed.
#
# ⚠️ ONLY WHERE THE VALUE IS NOT THE DEFAULT, WHICH IS `MigrationReport`'s
# OWN DISTINCTION AND NOT A SOFTENING OF THIS ONE. A stranded
# `sign_tripped = False` has lost the DEFAULT - the same statement "absent"
# already makes - so nothing is gone; a stranded `sign_tripped = True` has
# lost A LATCH. Reporting the first would make this tool red on a FRESH
# INSTALL, whose migration writes both defaults under `inverter-a` before
# any roster exists, and an alarm that fires on the harmless case is an
# alarm an operator learns to dismiss before the real one arrives - which is
# the argument `MigrationReport.lost` already carries in store.py.
for key in sorted(after.data):
if not key.startswith("unit/"):
if not key.startswith("unit/") or key in after.machine_read:
continue
if key in after.machine_read:
value = after.data[key]
bare = key.rsplit("/", 1)[-1]
if value in (None, DEFAULTS.get(bare)):
after.stranded_at_default.append(key)
continue
findings.append(Finding(
"STRANDED STORE KEY",
f"{key} = {after.data[key]!r} is in the store and NO machine on "
f"{key} = {value!r} is in the store and NO machine on "
f"the roster {after.units!r} read it during a full start. Its "
f"value is not information."))
@@ -511,7 +527,8 @@ def _latch_finding(key, before, after):
"LATCH KEY NOT UNDER A ROSTER NAME",
f"the flat `{key}` = {was!r} did not reach {target}, which is the "
f"key `Inverter({unit!r})` restores from"))
elif target in after.data and target not in after.machine_read:
elif (target in after.data and target not in after.machine_read
and after.data[target] not in (None, DEFAULTS.get(key))):
out.append(Finding(
"LATCH KEY NOT READ BY ANY MACHINE",
f"{target} = {after.data[target]!r} exists but no machine on the "
@@ -660,6 +677,15 @@ def report_transition(name, before, after, findings) -> None:
mark = " " if key in after.data else " (absent, defaulted) "
print(f" {key}{mark}")
if after.stranded_at_default:
print()
print(" keys no machine reads whose value IS the default, so "
"nothing is lost by")
print(" stranding them - reported rather than hidden, not counted "
"as findings:")
for key in after.stranded_at_default:
print(f" {key} = {after.data[key]!r}")
print()
print(" -- MAINTENANCE HISTORY (MAINT-05: a second move must not drop it) -")
for label, hist in (("before", before.history), ("after", after.history)):