From 611d3dbcdfc67d46f7f95a221dbd44f5b0446837 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Sun, 30 Aug 2026 17:05:28 +0200 Subject: [PATCH] TEST-06 review follow-ups: two notification surfaces, the real ordering boundary, the schema bounds 1. notes_of() could not tell this guard's alarm from anybody else's. It selected by SERVICE, so against #12's app/ (which adds goodwe_output_clamp) eight assertions went red, AC1's core check among them. Blanket id-keying is not the repair and was measured, not guessed: selector vs #12's app/ id renamed by service (as committed) 8 red 1 red by id alone 3 red 6 red by id or title (shipped) 3 red 1 red all_notes_of() is the whole surface; notes_of() is this guard's, recognised by the id it should carry OR the title it writes - the `or` is what keeps the one assertion whose subject IS the id able to fail on its own. The three surface-wide claims stay surface-wide, each with a comment naming which surface it is about and why the next guard's author must decide rather than narrow them. They are the three that go red against #12, which is the correct signal. 2. The ordering comment named the wrong boundary. N3 - moving the call to immediately before write_setpoint() - is green and correctly so: self.target is assigned two lines earlier, write_setpoint() clamps only a local and can skip the write entirely. So the invariant is "after `self.target = resolution.target_w`", and the detector grades pre-clamp INTENT, not the wire. Corrected in test_freeze.py and in main.py's own note above the call (comment-only; I merge second and own the conflict). N8 - moving the call above the assignment - is RED 4. 3. The schema range's own bounds are now pinned. int(60,3600) -> int(1,86400) survived green: an operator could set meter_freeze_s: 86400 and the guard is dead with nothing red. Both bounds are re-derived rather than restated. 4. find_hwsim() crashed the whole suite on a checkout one level below the root (HERE.parents[1] -> IndexError, reproduced at C:/goodwe_controller). Guarded. And part 3 now names its transport in the summary: the rig hwsim and the stub are not equivalent - the rig path failed 1 run in 3 - so a green log has to say which one backed it. Also: the "revived transport re-rails" check is labelled a PRECONDITION like its sibling; freeze_rows() records that flipping p1_enabled exercises the row's content and never its gate; and the note about the deleted assertion is corrected - it was REDUNDANT, not undecidable. The reviewer falsified it in one line (move the hass.call above `if not fired: return`, RED 9). "I could not construct a falsifier, therefore it cannot fail" was exactly the inference this file exists to distrust. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015VXjA3LWccN1rJsk3okyPC --- goodwe_controller/app/main.py | 22 ++- goodwe_controller/test_freeze.py | 234 +++++++++++++++++++++++------ goodwe_controller/test_validate.py | 30 ++++ 3 files changed, 233 insertions(+), 53 deletions(-) diff --git a/goodwe_controller/app/main.py b/goodwe_controller/app/main.py index 7d6f1ab..f4c6b86 100644 --- a/goodwe_controller/app/main.py +++ b/goodwe_controller/app/main.py @@ -406,12 +406,22 @@ class Controller: self.reason = resolution.explain() await self.write_setpoint(self.target) - # --- SAFETY-18, and it runs LAST on purpose -------------------------- - # Both terms of the conjunction describe the state that now exists: the - # command that has just gone on the wire, and how long the meter that - # chose it has been standing still. Evaluated before write_setpoint() - # it would be reasoning about the PREVIOUS command against THIS cycle's - # meter - the one combination that is true of neither moment. + # --- SAFETY-18, and it runs AFTER `self.target` is assigned ---------- + # ⚠️ THE BOUNDARY IS THE ASSIGNMENT ABOVE, NOT write_setpoint(). This + # comment used to say "the command that has just gone on the wire", + # which is wrong twice over and was corrected by TEST-06's reviewer: + # `check_meter_frozen()` grades `self.target` - the arbiter's resolved + # intent - and `write_setpoint()` clamps only a local `value` and can + # skip the write entirely, so that number may never have gone anywhere. + # + # What both terms of the conjunction must describe is the SAME moment: + # the command this cycle resolved, against how long the meter that + # chose it has been standing still. Evaluated before + # `self.target = resolution.target_w` it would grade the PREVIOUS + # cycle's command against THIS cycle's meter - the one combination that + # was true of neither moment. Moving it between the assignment and + # write_setpoint() is therefore harmless; moving it above the + # assignment is not, and test_freeze.py part 5 fails on exactly that. await self.check_meter_frozen() async def check_meter_frozen(self) -> None: diff --git a/goodwe_controller/test_freeze.py b/goodwe_controller/test_freeze.py index 89838fa..a6bc24c 100644 --- a/goodwe_controller/test_freeze.py +++ b/goodwe_controller/test_freeze.py @@ -349,11 +349,19 @@ check("...and the same replay DOES alarm once both terms are made reachable", # 3. a genuinely frozen meter, over real HTTP, through the real transport # --------------------------------------------------------------------------- # def find_hwsim(): - """The rig's own fake meter, if the infra repo is reachable.""" + """The rig's own fake meter, if the infra repo is reachable. + + ⚠️ `HERE.parents[1]` only exists when this package sits at least two levels + below the filesystem root - it raised IndexError and took the WHOLE suite + down on a checkout at `C:/goodwe_controller`, which is a failure of the + lookup, not of the thing being tested. Guarded, so a shallow checkout falls + through to the stub like any other missing sibling repo. + """ cands = [] if os.environ.get("GOODWE_SIM"): cands.append(Path(os.environ["GOODWE_SIM"]) / "hwsim.py") - cands.append(HERE.parents[1] / "goodwe" / "sim" / "hwsim.py") + if len(HERE.parents) > 1: + cands.append(HERE.parents[1] / "goodwe" / "sim" / "hwsim.py") for c in cands: if c.exists(): return c @@ -451,7 +459,15 @@ def with_server(argv, port, fn): hw = find_hwsim() -print(f"frozen meter over HTTP - using {'the rig hwsim at ' + str(hw) if hw else 'the local stub'}") +# ⚠️ WHICH TRANSPORT BACKED PART 3 IS PART OF THE RESULT, and it is repeated in +# the summary at the end of the run. The two are not equivalent: pointed at the +# real `sim/hwsim.py` the rig path failed 1 run in 3 (a 16 s window against a +# 10 s hold, when hwsim.serve() is slow to start), while the stub is tuned never +# to fail. A green log that does not say which one ran is a green log that can +# have quietly substituted the weaker stand-in - the same shape as an assertion +# that cannot fail, from the other end. +PART3_TRANSPORT = f"the rig hwsim at {hw}" if hw else "the local stub" +print(f"frozen meter over HTTP - using {PART3_TRANSPORT}") if hw: def argv_for(mode): return [HWSIM_DRIVER, "8791" if mode == "freeze" else "8792", mode, @@ -574,12 +590,65 @@ def _opts(**over): return o -def notes_of(hass): - """The HA persistent notifications that have left the process so far.""" +# ⚠️ TWO SURFACES, AND EVERY ASSERTION BELOW SAYS WHICH ONE IT IS ABOUT. +# The first cut of this helper selected by SERVICE, which measured "any guard's +# notification" while every assertion built on it was named for THIS guard's. +# That was true only because `check_meter_frozen()` was the sole +# `persistent_notification.create` in `app/`; SAFETY-03 (#12) adds a second one +# (`goodwe_output_clamp`) raised by scenarios that rail the command on purpose, +# and eight assertions here went red against it - AC1's core check included. +# +# Blanket id-keying is NOT the repair. Three of those eight are claims about the +# WHOLE notification surface, and narrowing them to this guard's id would have +# silently shrunk a claim rather than fixed a conflation - which is the defect +# class this card exists to close. So both surfaces are available by name, and +# each assertion picks one deliberately. +FREEZE_NOTE_ID = "goodwe_meter_frozen" + + +def all_notes_of(hass): + """EVERY HA persistent notification raised this run, whoever raised it.""" return [d for dom, svc, d in hass.calls if (dom, svc) == ("persistent_notification", "create")] +def _is_freeze_note(d): + """Is this notification the freeze detector's? + + ⚠️ THE ID **OR** THE TITLE, and the `or` is measured rather than tidy. + Keyed on the id alone, renaming the id empties every list in this file and + SIX assertions collapse together - so the one check whose subject IS the id + could no longer fail on its own, in the file about assertions that cannot + fail. Recognising the guard by what it says as well leaves the rename + exactly one red: the id's own check. Measured both ways: + + selector vs #12's app/ id renamed + by service (the first cut) 8 red 1 red + by id alone 3 red 6 red + by id or title (this) 3 red 1 red + + The union cannot re-admit another guard's alarm unless that guard also + calls itself frozen, which would be a confusable alarm on its own account. + """ + return (d.get("notification_id") == FREEZE_NOTE_ID + or "frozen" in str(d.get("title", "")).lower()) + + +def notes_of(hass): + """Only THIS guard's - see _is_freeze_note().""" + return [d for d in all_notes_of(hass) if _is_freeze_note(d)] + + +def notes_by_text_of(hass): + """This guard's, recognised by what it SAYS and never by its id. + + Exists for the two assertions whose subject IS the id: selected by the id, + both would be true by construction. + """ + return [d for d in all_notes_of(hass) + if "frozen" in str(d.get("title", "")).lower()] + + def wired(unchanged_s, grid, cycles=60, **over): """Returns (hass, controller, trace). @@ -604,7 +673,8 @@ def wired(unchanged_s, grid, cycles=60, **over): hass_f, ctl_f, trace_f = wired(3600.0, 4000.0) -notes = notes_of(hass_f) +notes = notes_of(hass_f) # THIS guard's +all_notes = all_notes_of(hass_f) # the whole surface # The status row lives behind `p1_enabled`, which is what a homewizard_local # install has. Flipped here rather than plumbing a whole P1 transport in - the # row's own content is what is under test. @@ -612,9 +682,22 @@ ctl_f.p1_enabled = True rows = [c for c in ctl_f.checks() if "METER MAY BE FROZEN" in c["text"]] print(f" frozen at +4000 W: command ran to {ctl_f.target:.0f} W, " f"{len(notes)} notification(s), {len(rows)} red status row(s)") -check("the shipped cycle() raises exactly one HA notification", len(notes) == 1) +# SURFACE-WIDE, deliberately. "The controller alarmed once" is a different and +# stronger claim than "the freeze guard alarmed once", and it is the one this +# row has always made. When a second guard lands (SAFETY-03's +# `goodwe_output_clamp` is the first), this assertion is where somebody decides +# whether the scenario is still meant to raise exactly one alarm in total - a +# decision, made here, rather than a claim quietly narrowed to keep it green. +check("the whole notification surface carried exactly one alarm this run", + len(all_notes) == 1) +# THIS GUARD'S, keyed on the id. +check("...and the freeze detector is the one that raised it", len(notes) == 1) +# ⚠️ Selected by TEXT, not by id - see notes_by_text_of(). This is the only +# assertion whose subject is the id, so it is the one that must survive being +# the only thing a rename breaks. check("...with a fixed notification_id so repeats replace, not stack", - notes and notes[0].get("notification_id") == "goodwe_meter_frozen") + [d.get("notification_id") for d in notes_by_text_of(hass_f)] + == [FREEZE_NOTE_ID]) check("...and it says the controller is still running", bool(notes) and "STILL RUNNING" in notes[0]["message"]) check("the status page carries a failed row saying so", @@ -625,39 +708,66 @@ check("the event log names it", any("METER FROZEN" in e for e in ctl_f.events)) check("and it does NOT command 0 W - this cut is an alarm only", abs(ctl_f.target) >= rail_w(LIVE_MAX_W)) +# ⚠️ THESE THREE ARE SURFACE-WIDE ON PURPOSE, and they are the three that go red +# against #12's `app/`. That is the correct signal and not a bug in them: each +# says "this scenario is quiet", which is a stronger claim than "this guard is +# quiet", and it is the claim a reader of a converged install wants. Whoever +# lands a second notifying guard has to come here and decide, per scenario, +# whether the new alarm belongs in it - `notes_of()` is one word away for the +# narrower reading, and choosing it must be a decision with a reason. +# (They also used to match `svc == "create"` in ANY domain, which was looser +# still.) hass_n, ctl_n, _ = wired(3600.0, -14.2) -check("wired up, TEL-05's converged case still raises nothing", - not [1 for dom, svc, _ in hass_n.calls if svc == "create"]) +check("wired up, TEL-05's converged case leaves the whole surface silent", + not all_notes_of(hass_n)) hass_m, ctl_m, _ = wired(2.0, 8000.0) -check("wired up, a real railed load against a moving meter raises nothing", - not [1 for dom, svc, _ in hass_m.calls if svc == "create"] - and abs(ctl_m.target) >= rail_w(LIVE_MAX_W)) -check("an uncovered transport (no unchanged_s) raises no notification", - not [1 for dom, svc, _ in wired(None, 4000.0)[0].calls if svc == "create"]) +check("wired up, a real railed load against a moving meter leaves the whole " + "surface silent", + not all_notes_of(hass_m) and abs(ctl_m.target) >= rail_w(LIVE_MAX_W)) +check("an uncovered transport (no unchanged_s) leaves the whole surface silent", + not all_notes_of(wired(None, 4000.0)[0])) # --------------------------------------------------------------------------- # # 5. WHERE in cycle() the detector is evaluated (TEST-06 / AC1) # --------------------------------------------------------------------------- # -# ⚠️ WHY THIS IS NOT STYLE. `check_meter_frozen()` runs LAST, after -# `write_setpoint()`, and moving it to the top of `cycle()` used to leave this -# whole file green. It is not a tidiness preference, it is the reason two -# separate compositions are safe, and each is asserted here by an OBSERVABLE -# CONSEQUENCE rather than by the source order - so a refactor that keeps the -# semantics (renaming it, inlining it, calling it from a helper that runs at the -# end) stays green, and one that changes WHICH command it judges goes red: +# ⚠️ WHERE THE BOUNDARY ACTUALLY IS - AND IT IS NOT write_setpoint(). +# The first version of this comment said the detector runs "after +# write_setpoint()" and grades "the command that has just gone on the wire". +# TEST-06's reviewer disproved both halves by mutation: +# +# N3 move check_meter_frozen() to immediately BEFORE write_setpoint() +# -> green, and CORRECTLY green +# +# because `self.target = resolution.target_w` is assigned two lines above the +# write, `write_setpoint()` clamps only a local `value`, and it can skip the +# write entirely (`if not (changed or force): return`). So what the detector +# grades is `self.target` - the arbiter's resolved intent, PRE device clamp and +# possibly never emitted - and the invariant these checks pin is: +# +# check_meter_frozen() runs AFTER `self.target = resolution.target_w` +# +# That distinction is not pedantry. A check more robust than its own +# explanation is how the next person moves the call ABOVE the assignment +# believing they had preserved the stated rule. +# +# Both properties below are asserted by an OBSERVABLE CONSEQUENCE rather than by +# source order, so semantic-preserving refactors stay green (measured by the +# reviewer: renaming the method, and calling it from a helper that itself runs +# last, both survive - the suite drives `await c.cycle()` and never names the +# method): # # (a) BOTH TERMS MUST DESCRIBE THE SAME MOMENT. The command it grades has to -# be the one that just went on the wire, judged against how long the meter -# that chose it has stood still. Evaluated at the top it grades the +# be the one THIS cycle resolved, judged against how long the meter that +# chose it has stood still. Evaluated above the assignment it grades the # PREVIOUS cycle's command against THIS cycle's meter - a pairing that was # never true of any moment - and the alarm arrives a whole cycle late. # (b) THE DEAD-POLL-TASK COMPOSITION. When the poll task dies the inputs go -# stale, the arbiter commands 0 W, and the detector - evaluated after that -# write - correctly sees an unrailed command and CLEARS. At the top of the -# cycle it would still be looking at the railed command from before the -# failsafe, and would keep the alarm standing over a controller that is -# already commanding 0 W. +# stale, the arbiter resolves 0 W, and the detector - evaluated after that +# resolution lands in self.target - correctly sees an unrailed command and +# CLEARS. Evaluated above the assignment it would still be looking at the +# railed command from before the failsafe, and would keep the alarm +# standing over a controller that is already commanding 0 W. print("WHERE the detector is evaluated in cycle() - the ordering AC1 protects") _railed = [i for i, (t, _, _) in enumerate(trace_f) @@ -674,9 +784,9 @@ check("the command takes several cycles to reach the rail, so 'same cycle' and " _first_railed is not None and _first_railed >= 1 and len(trace_f) > _first_railed + 1) check("the alarm lands on the SAME cycle the command reaches the rail - " - "evaluated after write_setpoint() it grades the command now on the wire; " - "moved to the top of cycle() it would grade the previous one and arrive a " - "cycle late", + "evaluated after `self.target = resolution.target_w` it grades the " + "command THIS cycle resolved; evaluated above that assignment it grades " + "the previous one and arrives a cycle late", _first_alarm is not None and _first_alarm == _first_railed) @@ -684,7 +794,7 @@ def dead_poll(after_cycles=20, dead_cycles=4, revived_cycles=20): """Alarm on a railed command, then kill the poll task and keep cycling. The real thing: the transport task dies, every input reads None, and after - `stale_input_s` the arbiter's P_SAFETY stop commands 0 W. `inputs_bad_since` + `stale_input_s` the arbiter's P_SAFETY stop resolves 0 W. `inputs_bad_since` is backdated rather than slept through - the clock is wall clock, and the mechanism rather than the wait is what is under test. """ @@ -716,11 +826,14 @@ def dead_poll(after_cycles=20, dead_cycles=4, revived_cycles=20): await c.read_inputs() await c.cycle() revived.append((c.target, c.freeze_wd.active, len(notes_of(hass)))) - return before, trace, revived, notes_of(hass), c + # ⚠️ by TEXT: "the second episode REPLACED the first" is a claim about + # the id, so the list it is checked against must not be selected by the + # id - that would make it true by construction. + return before, trace, revived, notes_by_text_of(hass), c return asyncio.run(go()) -_before, _dead, _revived, _all_notes, _ctl_d = dead_poll() +_before, _dead, _revived, _freeze_notes, _ctl_d = dead_poll() _zeroed = [i for i, (t, _, _) in enumerate(_dead) if t == 0.0] print(f" poll task killed: alarming at {_before[0]:+.0f} W " f"(active={_before[1]}, {_before[2]} notification(s)) -> " @@ -732,15 +845,21 @@ check("the episode really was standing before the poll task died - otherwise " check("a dead poll task drives the command to 0 W", bool(_zeroed) and _zeroed[0] == 0) check("...and on THAT SAME cycle the alarm is already clear, because the " - "detector ran after the 0 W write and not before it", + "detector ran after 0 W was resolved into self.target, not before it", bool(_zeroed) and not _dead[_zeroed[0]][1]) check("...and it stays clear for as long as the failsafe holds it there", _dead and not any(a for _, a, _ in _dead)) -# ⚠️ "and it raises nothing new while parked" is NOT asserted here, and that is -# deliberate: with the alarm cleared there is no mutation of this code that -# could make a notification appear during those cycles, so the assertion could -# not fail and would have been decoration. The real claim - that the outage did -# not add notifications - is carried by the exact count below, which CAN fail. +# ⚠️ "and it raises nothing new while parked" is NOT asserted here, and the +# reason matters more than the deletion. It was dropped as REDUNDANT, not as +# undecidable: TEST-06's reviewer falsified it in one line - move the +# `hass.call` above `if not fired: return` so the detector notifies every cycle, +# and notifications appear in exactly these parked cycles (that mutation takes +# this file RED 9). My first note here claimed no mutation could break it, which +# was the very inference this file exists to distrust, made about this file. +# The deletion stands because the exact count below catches that same mutation +# nine times over - no coverage was lost - and the honest form of the claim is +# "I tried to break it and here is what happened", never "I could not, so it +# cannot". # ⚠️ AND THE OTHER HALF, or "no second notification" is a claim no mutation can # break: when the transport comes back against a meter that is STILL frozen the @@ -750,17 +869,28 @@ _re_railed = [i for i, (t, _, _) in enumerate(_revived) if abs(t) >= rail_w(LIVE_MAX_W)] print(f" transport revived: command re-rails on cycle " f"{_re_railed[0] if _re_railed else None}, " - f"{_ctl_d.freeze_wd.episodes} episode(s), {len(_all_notes)} notification(s), " - f"ids {sorted({n.get('notification_id') for n in _all_notes})}") -check("a revived transport against a still-frozen meter re-rails the command", + f"{_ctl_d.freeze_wd.episodes} episode(s), {len(_freeze_notes)} " + f"notification(s) from this guard, " + f"ids {sorted({n.get('notification_id') for n in _freeze_notes})}") +# A PRECONDITION, not a discriminating check - the same role as "the command +# takes several cycles to reach the rail" above, and labelled the same way: +# without it the two checks below are satisfied by a controller that never came +# back. The crudest falsifier (never file the loop claim) takes most of this +# file red, which is the signature of a precondition rather than a check; the +# narrowest one measured is a stale-input stop that never releases. +check("PRECONDITION: a revived transport against a still-frozen meter re-rails " + "the command", bool(_re_railed)) check("...which is a SECOND episode, and it alarms again on the cycle it rails", _ctl_d.freeze_wd.episodes == 2 and bool(_re_railed) and _revived[_re_railed[0]][2] == _before[2] + 1) +# ⚠️ STABILITY, not the value - the value is pinned once, above. Renaming the +# id must fail that check and NOT this one; the two say different things and +# each has to be able to go red on its own. check("...on the same notification_id, so the second REPLACES the first " "instead of stacking a second one beside it", - len(_all_notes) == 2 - and len({n.get("notification_id") for n in _all_notes}) == 1) + len(_freeze_notes) == 2 + and len({n.get("notification_id") for n in _freeze_notes}) == 1) # --------------------------------------------------------------------------- # @@ -776,7 +906,16 @@ print("what the status page SAYS about coverage") def freeze_rows(unchanged_s, grid, **over): - """The freeze-detector rows of the real checks(), on a p1 install.""" + """The freeze-detector rows of the real checks(), on a p1 install. + + ⚠️ WHAT THIS DOES NOT COVER. The rows live behind `if self.p1_enabled:`, and + the flag is set here by hand rather than by building a real P1 transport - + inherited from part 4's fixture. So everything below proves "WHEN the row is + produced it says the right thing", and nothing below proves "the row is + produced on homewizard_local and not otherwise". That gate is uncovered; + AC2 is about the string, and widening the fixture belongs to whoever covers + the gate. + """ _h, ctl, _t = wired(unchanged_s, grid, cycles=3, **over) # p1_enabled is what a homewizard_local install has; the row's own content # is what is under test, not the transport that feeds it. @@ -829,6 +968,7 @@ check("a Controller built with the key absent lands on the SHIPPED hold, so " _ctl_c.freeze_wd.cfg.hold_s == SHIPPED_HOLD_S) print() +print(f"part 3 (the live HTTP transport) ran against: {PART3_TRANSPORT}") if fails: print(f"{len(fails)} FAILED: {', '.join(fails)}") sys.exit(1) diff --git a/goodwe_controller/test_validate.py b/goodwe_controller/test_validate.py index b7faa3f..4645058 100644 --- a/goodwe_controller/test_validate.py +++ b/goodwe_controller/test_validate.py @@ -308,6 +308,36 @@ check("...and a healthy meter that looks unchanged for it is still inside the " _slowest_healthy_w is not None and _db is not None and _slowest_healthy_w < _db) +# ⚠️ AND THE RANGE'S OWN BOUNDS. "300 sits inside the range" is worth nothing +# while the range itself is unpinned: widened to `int(1,86400)` an operator can +# configure `meter_freeze_s: 86400` and the guard is dead for a day at a time, +# with every check in this repo still green. That mutation survived the first +# cut of this PR and is what this block closes. Both numbers are argued for in +# config.yaml's own comment and in freeze.py, and each is re-derived here: +# +# 60 s the floor freeze.py calls NOT A RECOMMENDATION - there a healthy +# meter can look unchanged at 60 W, four times the shipped deadband, so +# the conjunction's two terms stop being mutually exclusive and only +# the margin is left; +# 3600 s the ceiling - already four times the billing quarter, so the top of +# the range has given up detecting a freeze inside the quarter that +# bills it. It is the last bound between a supported configuration and +# a disabled guard, and moving it is a decision rather than a tidy-up. +print(f" schema range {_lo}-{_hi} s: at the floor a healthy meter that looks " + f"unchanged is " + + (f"{3600.0 / _lo:.0f} W" if _lo else "?") + + f" against a {_db} W deadband; the ceiling is " + + (f"{_hi / _QUARTER_S:.1f}x" if _hi else "?") + + " the billing quarter") +check(f"the schema range itself is still the argued 60-3600 s (found " + f"{_lo}-{_hi})", (_lo, _hi) == (60.0, 3600.0)) +check("...a floor that gives up the mutual exclusion and keeps only the " + "margin, which is why config.yaml calls it not a recommendation", + None not in (_lo, _db) and 3600.0 / _lo > _db) +check("...and a ceiling already past the billing quarter, so no value the " + "schema still accepts can alarm inside the quarter that bills it", + _hi is not None and _hi > _QUARTER_S) + # --------------------------------------------------------------------------- # print("the KNOWN cross-file default divergence, pinned rather than commented") # ⚠️ WHAT THIS GUARDS, AND WHY IT ASSERTS THE BUG RATHER THAN THE FIX.