CI-17 round 2: stale/dead fixtures take rejected_since from the pinned instant; comment made true
checks / unit (pull_request) Successful in 36m34s
infra-pin / fresh (pull_request) Successful in 1s

The two fixtures ran their cycles at _SRC_NOW but set rejected_since from the
real clock, so the cycle saw inverter-b live while live_units() saw it stale.
Both now use _SRC_NOW, and a check asserts both clocks agree. run_cycle's
comment no longer claims ctl_cycle.py:49 is the only clock read.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
glenn schrooyen
2026-11-14 23:39:49 +01:00
co-authored by Claude
parent 39ca1df232
commit 63043ea219
+11 -7
View File
@@ -2789,9 +2789,9 @@ def cycle_claims(run):
def run_cycle(ctl, at=None):
"""One `Controller.cycle()`. `at` pins the instant `cycle()` reads as "now"
(ctl_cycle.py:49, its only clock read), so a fixture laid up to `at` is the
same fixture however slowly the host runs the cycles after it. CI-17."""
"""One `Controller.cycle()`. `at` pins the `now` that cycle() takes
(ctl_cycle.py:49) so a fixture laid up to `at` does not age with host load.
Other modules still read the real clock. CI-17."""
real_dt = _ctl_cycle.datetime
if at is not None:
class _Pinned(datetime):
@@ -3400,11 +3400,16 @@ check("...and the 0 W is NOT what the site target happened to be - the same "
# method is asked, which is what makes this a check on `live_units()` instead of
# a check on a lambda this file wrote.
_bs, _restore_bs = second_inverter(c_src)
_bs.rejected_since = datetime.now(timezone.utc) - timedelta(
seconds=app_main.LINK_STALE_S + 5)
# CI-17: from `_SRC_NOW`, the instant the pinned cycles run at; the real clock
# is later, so `live_units()` (real clock) and the cycle (pinned) agree.
_bs.rejected_since = _SRC_NOW - timedelta(seconds=app_main.LINK_STALE_S + 5)
_bs.last_write_ok = False
_stale_writes_before = len(c_src.hass.per_entity)
try:
check("CI-17: the pinned clock and the real clock agree that inverter B's "
"link is stale, so the cycle and `live_units()` see the same roster",
not _bs.is_live(_SRC_NOW)[0]
and not _bs.is_live(datetime.now(timezone.utc))[0])
check("PRECONDITION: the SHIPPED `live_units()` - not a lambda - drops the "
"inverter whose writes have been rejected for a whole layer-1 window, "
"and keeps the healthy one",
@@ -3451,8 +3456,7 @@ check("⚠️ AND IT DOES NOT ABSORB IT SILENTLY, which is the whole of AC 6 and
# state `live_units()`'s own docstring warns about - the sole inverter dropping
# takes the SITE to 0 W - so it is driven here rather than left as a sentence.
_bd, _restore_bd = second_inverter(c_src)
_gone = datetime.now(timezone.utc) - timedelta(
seconds=app_main.LINK_STALE_S + 5)
_gone = _SRC_NOW - timedelta(seconds=app_main.LINK_STALE_S + 5) # CI-17: as above
_bd.rejected_since, _bd.last_write_ok = _gone, False
c_src.inverters[0].rejected_since = _gone
c_src.inverters[0].last_write_ok = False