A Landlord heating-system override was applied as a sparse patch, so the replaced system's fields bled through. A storage flat reclassified as a gas combi (property 728513) kept mains_gas=False, heating category 7, the 2401 storage charge control, a Dual meter and an electric-immersion cylinder — an incoherent record that gated out the gas-boiler-upgrade Measure and made the heating Generator read the dwelling as off-gas (offering HHRSH storage). Extend the ADR-0035 drag-along to gas boilers (Table 4b 102/104/120): the overlay now sets the whole coherent companion set — mains_gas, gas main fuel, heating category 2, fanned flue, full modern controls (2106), a single-rate meter, and hot water from the main system with the cylinder set from the boiler type (combi → none, regular/CPSU → cylinder). The main_fuel overlay also flips mains_gas=True for a "mains gas" fuel. Non-off-peak archetypes now drag an explicit Single meter so a system switch never leaves a stale Dual. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.7 KiB
Coherent Heating System is owned by the synthesiser, not the calculator
Status
accepted
Context
A dwelling's heating is one conceptual system, but its fields are physically
scattered across EpcPropertyData because that type mirrors the gov-API / RdSAP
schema: the main cluster lives on sap_heating, the electricity tariff on
sap_energy_source.meter_type, and hot-water flags (has_hot_water_cylinder,
solar_water_heating) on loose top-level fields.
Three places synthesise a heating system — a Measure Option (HHRSH/ASHP), a
Landlord Override, and EPC Prediction's heating donor — and each was hand-copying
a different ad-hoc subset of those fields. The override and the donor both
omitted meter_type. Property 713406 (a predicted property whose landlord
override set "Electric storage heaters") landed on the oil structural template's
single-rate meter, so its overnight storage heat billed at the peak electricity
rate: the calculator scored it SAP 13 (G) instead of ~50 (E), which then
inflated the HHRSH measure to +45.8 SAP and pushed the plan to band A.
Decision
Treat "a heating system" as one named boundary (a Coherent Heating System — see CONTEXT.md). Whoever synthesises a heating system owns covering the whole boundary, with a fill policy suited to its source:
- Measure → set a designed end-state (already did).
- Landlord Override → complete the partial system the landlord named.
Companion fields are derived from the SAP code, not hand-attached per
archetype: the off-peak meter from the calculator's single off-peak
classification (
OFF_PEAK_IMPLYING_HEATING_CODES, SAP §12 Rules 1-2), and — where a field has no logical pairing — a conservative default (storage charge control → manual, SAP Table 4e 2401, the lowest-SAP storage control). So adding a heating archetype is just adding its code; coherent companions fall out and cannot be forgotten. - EPC Prediction donor → carry a real neighbour's whole coherent set
(including its
meter_type), since the donor is already internally coherent.
Coherence is a synthesis-time obligation, never a calculator normalisation.
A real lodged cert is scored exactly as lodged — the calculator must not "fix" a
genuinely single-rate storage dwelling (its existing Unknown-meter inference is
a separate, spec-faithful net for certs that lodged Unknown, and does not fire
on an explicit meter). A contract test guards the override path: every off-peak
code the archetype map can emit must drag a Dual meter, and every other code an
explicit Single meter — a system switch can never leave the previous system's
meter in place.
Amendment: the gas-boiler companion set (property 728513)
The storage-heater drag-along above covered electric systems; a landlord-named
gas boiler was still synthesised as a sparse patch — only the SAP code (its
meter/charge-control companions are None for a boiler). The replaced system's
fields therefore bled through: an electric-storage flat reclassified as a gas
combi kept mains_gas=False, heating category 7, the 2401 storage charge
control, a Dual meter and an electric-immersion cylinder. That incoherent
record gated out the gas-boiler-upgrade Measure and made the heating Generator
read the dwelling as off-gas (offering HHRSH storage to a gas property).
A gas-boiler archetype (Table 4b 102 regular / 104 combi / 120 CPSU) now drags its whole coherent set, code-derived like the storage companions:
mains_gas=True+main_fuel_type=26— a gas boiler implies a mains-gas connection and gas main fuel (so a heating-system-only override is self-coherent, not dependent on a companionmain_fueloverride). Themain_fueloverlay independently setsmains_gas=Truefor a "mains gas" fuel; the two agree (both 26 / True) so composition stays order-independent. A non-gasmain_fueloverride (electricity/LPG/oil) leavesmains_gasNone (unchanged) — an electric main heating does not prove there is no gas hob.main_heating_category=2(gas boiler) andfan_flue_present=True— a modern room-sealed condensing boiler.main_heating_control=2106(programmer + room thermostat + TRVs) — the landlord names the boiler, not its controls. Unlike the storage charge control (where the conservative lowest-SAP default is right because the control is genuinely unknown), a gas boiler installed under modern Building Regs must carry compliant controls, and the overlay already assumes the modern condensing efficiency for the boiler — so assuming modern controls is the coherent, consistent default (the alternative, a conservative "no controls" 2101, was considered and rejected as inconsistent with the assumed-modern boiler). The controls tune-up Measure still adds zone control on top.- Hot water from the main system (
water_heating_code=901,water_heating_fuel=26) with the cylinder set from the boiler type — a combi (104) has no cylinder (has_hot_water_cylinder=False), a regular boiler / CPSU keeps one. (Mirrors Elmhurst: the cylinder choice is offered for a regular condensing boiler and greyed out for a combi.) meter_type="Single"— a boiler is single-rate; the explicit Single meter overwrites any inherited storageDual(the symmetric mirror of storage→Dual).
A mains_gas flag was added to the main_fuel overlay. The companion-fuel and
heating-system overlays now share the mains_gas / main_fuel_type fields
rather than being strictly disjoint — accepted because they only ever agree for
the mains-gas archetypes. The archetype map holds only mains-gas boilers, so a
gas-boiler override always implies mains gas; a contradictory fuel override
(gas boiler + a non-gas fuel) is out-of-scope incoherent landlord input (a
finaliser-level validation, not an overlay concern).
Considered Options
- Reify a
HeatingSystemvalue object all three produce/consume — rejected: overlapsHeatingOverlayand is a large, risky restructure for the value. - A global coherence pass over the assembled effective EPC — rejected: it would corrupt real lodged certs (force storage→dual on a genuine single-rate lodgement). Coherence must be scoped to synthesis points.
- Set
meter_type="Unknown"and lean on the calculator's storage inference — rejected as the primary mechanism: an explicitDualis self-contained, matches the measure overlay, carries display value, and does not depend on a repo extension that could later be tightened toward spec.
This amends ADR-0029 (the heating donor now carries the meter) and extends ADR-0032 (the override overlay derives coherent companions).