UX-10: the last plain-language leftovers: log lines, two table rows, 'Why: Other', red at 0 s
- test_messages.py part 6: every dynamic part of a WARNING+ line is classified as data or as a built text, and every built text is produced by the real code and checked. This is the gap the 2d scan had: it counted `%s` as one word, so reasons built by loop_clamp_note() and friends were never seen. - Quoted reasons rewritten to STYLE.md (red-alarm shape, codes kept): OUTPUT_CLAMP, METER_FROZEN, CIRCULATING_POWER, STALE_READING, the peak guard (codes now PEAK_GUARD*), the migration report, price feed, price history and gate reasons. Plurals via messages.times()/count(). - Top table: Price history "96 prices over 24 hours for 26 Sep", Tariff "one every 15 minutes or every hour", "not final yet" dropped. - Levels and noise: per-cycle shadow line DEBUG, shadow on/off INFO, aiohttp access log only at log level debug, settings warnings once per start, one plain line per Home Assistant outage (hass.Outage). - INFO jargon and error texts: no "§8.3", ISO times, "banked", "keys ARE", "NOT_OBSERVED", "did NOT land", "AND". - Display bugs: WHY_WINNER covers every winner and safety reason (main.why_key(), part 7 finds them in the code); the meter row is amber "waiting for the first reading" inside its limit after a start; the missing-reading age is counted when the page asks. - STYLE.md and DECISIONS/UX-08-short-notifications.md (UX-10 section). No change to safety behaviour, alarm levels, notification ids, triggers, cadence or the banner cascade. One row colour changes (meter at start), justified in DECISIONS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
125b0702a2
commit
23a3a3f5b5
@@ -66,6 +66,7 @@ import sys
|
||||
from named_absence import first, nth
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from app import messages as _msg_fc
|
||||
from app import price as _price
|
||||
from app import price_forecast as _fc
|
||||
from app import static_tariff as _st
|
||||
@@ -668,8 +669,10 @@ check("with neither a published hour nor a yesterday, the result is no_data - "
|
||||
check("...so it has no .points to iterate and no .get() to default out of",
|
||||
raises(lambda: _nothing.points, Absence)
|
||||
and raises(lambda: _nothing.get("points", []), Absence))
|
||||
check("...and its reason names §11.4 for an operator",
|
||||
"§11.4" in _nothing.reason)
|
||||
check("...and its reason says the fixed tariff is used and there is no "
|
||||
"trading (§11.4), in words an operator reads",
|
||||
"fixed tariff is used" in _nothing.reason
|
||||
and "no trading" in _nothing.reason)
|
||||
check("a naive `since` is refused rather than silently interpreted as UTC",
|
||||
raises(lambda: fill_from_yesterday(TODAY_PART, yesterday=YESTERDAY,
|
||||
since=datetime(2026, 1, 15),
|
||||
@@ -745,9 +748,10 @@ check("...and the phase machine did not move: it is still idle, so the "
|
||||
_m.phase == IDLE)
|
||||
check("...and the reason names §8.3, the offending hour, and when the real "
|
||||
"price is expected",
|
||||
"§8.3" in _gate_bad.reason
|
||||
and bool(_first_prov) and _first_prov.isoformat() in _gate_bad.reason
|
||||
and "14-16" in _gate_bad.reason)
|
||||
"is only estimated" in _gate_bad.reason
|
||||
and bool(_first_prov)
|
||||
and _msg_fc.when(brussels(_first_prov)) in _gate_bad.reason
|
||||
and "14:00 to 16:00" in _gate_bad.reason)
|
||||
|
||||
# The positive arm, so the block above is discriminating rather than universal.
|
||||
_firm_long = series_of(hours_between(START,
|
||||
@@ -772,7 +776,8 @@ _gate_none = begin_phase(_m3, START, PriceRead.no_data("the feed timed out"),
|
||||
MAINTENANCE_DRAIN)
|
||||
check("no price data at all BLOCKS an irreversible action too - a missing "
|
||||
"price is not a cheap one (§11.4)",
|
||||
_gate_none.kind == "blocked" and "§11.4" in _gate_none.reason
|
||||
_gate_none.kind == "blocked"
|
||||
and "missing price is not a cheap one" in _gate_none.reason
|
||||
and _m3.phase == IDLE)
|
||||
_short = series_of(DAY[:4], lambda u: t_price(brussels(u).hour),
|
||||
PriceProduct.DAY_AHEAD, "generic_dayahead",
|
||||
|
||||
Reference in New Issue
Block a user