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:
@@ -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."
|
||||
}
|
||||
@@ -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)):
|
||||
|
||||
Reference in New Issue
Block a user