UX-08 phase 2b review: keep over-rating evidence, pin latch clearing, plainer rows
checks / unit (pull_request) Successful in 8m48s
infra-pin / fresh (pull_request) Successful in 1s

- During a bound episode an earlier over-rating count stays on the page (amber,
  under its own title); test_clamp pins it.
- test_messages pins that every latch row says how it clears.
- pv_entities description no longer claims the page names the entity (en, nl);
  allowlist hash updated for that option-desc line.
- Plainer texts: multi-inverter direction banner, "fine now" after an episode,
  and the e-stop, peak-forecast and peak-guard-waiting rows move to messages.py.
- TEL-12 DECISIONS points to UX-08 item 3 for the retired row rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
glenn schrooyen
2026-09-27 03:10:25 +02:00
co-authored by Claude Opus 5.5
parent 696142bccc
commit 571b884315
9 changed files with 61 additions and 12 deletions
+11 -5
View File
@@ -4669,8 +4669,7 @@ class Controller:
_pg = self.peak_guard
if _pg is None:
out.append({"ok": True, "warn": True, "id": "peak_guard",
"text": "peak guard: no decision yet - the first "
"control cycle has not run"})
"text": messages.row_peak_guard_waiting()})
elif _pg.saturated:
out.append({"ok": False, "warn": False, "id": "peak_guard",
"text": messages.row_peak_saturated(_pg.ceiling_w)})
@@ -4821,6 +4820,12 @@ class Controller:
# SAFETY-40: the integrator bound alone is amber, not red.
out.append({"ok": False, "warn": True, "id": "not_keeping_up",
"text": messages.row_not_keeping_up(_cw.bound)})
# No over-rating in THIS episode (`bug` would be set), so any count
# is from an earlier one: keep that software-fault evidence shown.
if _cw.over_rating:
out.append({"ok": False, "warn": True, "id": "power_too_high",
"text": messages.row_before(
messages.T_OUTPUT_CLAMP, _cw.over_rating)})
else:
if _cw.over_rating:
out.append({"ok": False, "warn": True, "id": "power_too_high",
@@ -4940,13 +4945,14 @@ class Controller:
out.append({"ok": False, "warn": True, "id": "estop",
"text": messages.row_no_estop()})
else:
out.append({"ok": True, "warn": False, "text": "e-stop fitted"})
out.append({"ok": True, "warn": False, "id": "estop",
"text": messages.row_estop_fitted()})
if not o.get("peak_forecast_entity"):
# ok=False + warn=True renders as a caution, not a pass. A check that
# is both is a check nobody reads.
out.append({"ok": False, "warn": True,
"text": "no peak forecast: maintenance charging is not capacity-capped"})
out.append({"ok": False, "warn": True, "id": "peak_forecast",
"text": messages.row_no_peak_forecast()})
# --- TEL-11 ----------------------------------------------------------
# ⚠️ NO ROW AT ALL WHEN THE ROSTER IS EMPTY, AND THAT IS A DELIBERATE
# DIFFERENCE FROM THE PEAK-FORECAST CAUTION DIRECTLY ABOVE. That one
+18 -2
View File
@@ -281,7 +281,7 @@ def banner_site_direction() -> str:
def banner_direction(several: bool) -> str:
if several:
return "Inverter and own power point opposite ways. It is at 0 W."
return "An inverter's own power opposes its command. It is at 0 W."
return "Command and meter point opposite ways. Inverter set to 0 W."
@@ -427,7 +427,7 @@ def row_not_keeping_up(count) -> str:
def row_before(title, count) -> str:
"""After an episode clears: how often it happened, so the evidence stays."""
return f"{title}: {times(count)} since start, not now"
return f"{title}: {times(count)} since start, fine now"
def row_write_refused(unit=None) -> str:
@@ -494,6 +494,19 @@ def row_delivery_check(unit, short_s, hold_s) -> str:
return f"Delivery check for {unit}: on"
def row_estop_fitted() -> str:
return "Emergency stop: fitted"
def row_no_peak_forecast() -> str:
return ("No peak forecast set, so maintenance charging is not kept under "
"the peak limit")
def row_peak_guard_waiting() -> str:
return "Peak guard: no decision yet, control has not run"
def row_no_estop() -> str:
return ("No emergency stop fitted: if this computer fails, the battery "
"keeps its last command")
@@ -625,6 +638,9 @@ def status_samples() -> list:
("row_delivery_check", GREEN, row_delivery_check("inverter-b", 30, 120)),
("row_delivery_check", GREEN, row_delivery_check("inverter-b", 0, 120)),
("row_no_estop", AMBER, row_no_estop()),
("row_estop_fitted", GREEN, row_estop_fitted()),
("row_no_peak_forecast", AMBER, row_no_peak_forecast()),
("row_peak_guard_waiting", AMBER, row_peak_guard_waiting()),
("row_pv", GREEN, row_pv(3200, 2)),
("row_pv", GREEN, row_pv(1800, 1, True)),
("row_pv_none", AMBER, row_pv_none("no unit_of_measurement on the "