Community-heating overrides drag the conservative Group 3 control (2301) 🟩

The last real archetype on the companion-gap log; the log test now
simulates a future incomplete archetype (warm-air heat pump, Group 5
control deliberately unmapped).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-03 14:21:03 +00:00
parent 950ec31d84
commit 61f9a63b66
2 changed files with 23 additions and 7 deletions

View file

@ -188,6 +188,14 @@ _CONSERVATIVE_HEAT_PUMP_CONTROL = 2201
_HEAT_NETWORK_CATEGORY = 6
_HEAT_NETWORK_CODES = frozenset({301, 302, 303, 304})
_COMMUNITY_BOILER_CODES = frozenset({301, 302, 303})
# SAP Table 4e Group 3 heat-network control: 2301 ("flat rate charging, no
# thermostatic control of room temperature") is the conservative default when
# the landlord named only the scheme. It is never-over-crediting on BOTH axes
# the control drives: the largest Group 3 temperature adjustment (+0.3 C, tied
# with 2302) and the worst Table 4c(3) charging factors (space 1.10, DHW 1.05
# — an unobserved charging arrangement is never assumed usage-linked). The
# community mirror of storage 2401 / boiler 2101 / heat-pump 2201 (ADR-0053).
_CONSERVATIVE_HEAT_NETWORK_CONTROL = 2301
_COMMUNITY_GAS_FUEL = 20
# SAP Table 4c full boiler-control code: programmer + room thermostat + TRVs. The
@ -318,6 +326,8 @@ def _control_for(code: int) -> Optional[int]:
return _CONSERVATIVE_BOILER_CONTROL
if code in _WET_HEAT_PUMP_CODES:
return _CONSERVATIVE_HEAT_PUMP_CONTROL
if code in _HEAT_NETWORK_CODES:
return _CONSERVATIVE_HEAT_NETWORK_CONTROL
return None

View file

@ -94,18 +94,24 @@ def test_electric_room_heaters_overlay_the_room_heater_charge_control() -> None:
def test_overlay_logs_when_it_cannot_complete_a_companion_set(
caplog: pytest.LogCaptureFixture,
monkeypatch: pytest.MonkeyPatch,
) -> None:
# An archetype the overlay cannot yet fully companion — here community
# heating, whose Table 4e Group 3 conservative control is not yet mapped —
# is still overlaid (log-and-continue), but the incomplete companion set is
# logged as an error so the known gap is visible (CloudWatch) rather than
# silently inheriting the replaced system's stale value.
# Every real archetype now resolves a complete companion set (ADR-0053),
# so the incomplete case is simulated: a hypothetical warm-air heat-pump
# archetype (SAP 521 — category 4 but its Table 4e Group 5 control is
# deliberately unmapped). The overlay still applies (log-and-continue),
# but the incomplete companion set is reported rather than silently
# inheriting the replaced system's stale value.
from domain.epc.property_overlays import main_heating_system_overlay as mod
monkeypatch.setitem(mod._MAIN_HEATING_CODES, "Warm-air heat pump", 521)
# Act
with caplog.at_level(logging.ERROR):
main_heating_overlay_for("Community heating, boilers", 0)
main_heating_overlay_for("Warm-air heat pump", 0)
# Assert — the gap is reported and names the archetype.
assert "Community heating, boilers" in caplog.text
assert "Warm-air heat pump" in caplog.text
@pytest.mark.parametrize(