Model/docs/adr/0035-coherent-heating-system-synthesis.md
Khalim Conn-Kowlessar db3bf00602 fix(synthesis): Coherent Heating System — synthesisers own the whole boundary
A dwelling's heating is one conceptual system, but its fields are scattered
across EpcPropertyData (a gov-API schema mirror): the cluster on sap_heating, the
electricity tariff on sap_energy_source.meter_type, hot-water flags loose at top
level. Three places synthesise a heating system — Measure Options, Landlord
Overrides, EPC Prediction's donor — and each hand-copied a different ad-hoc
subset. The override and donor both dropped meter_type, so an electric-storage
system landed on the template's single-rate meter and billed overnight heat at
the peak rate: property 713406 scored SAP 13 (G) vs ~50 (E), inflating the HHRSH
measure to +45.8 and overshooting the plan to band A.

Establish a single Coherent Heating System boundary (CONTEXT.md) that every
synthesiser must cover, with a source-appropriate fill policy (ADR-0035):

- Override overlay *completes* the partial system the landlord named. Companion
  fields are now DERIVED from the SAP code, not hand-attached per archetype: the
  off-peak meter from the calculator's single off-peak classification (new
  OFF_PEAK_IMPLYING_HEATING_CODES = SAP §12 Rules 1-2), and an unobserved storage
  charge control defaults to the conservative manual control (Table 4e 2401). So
  adding a heating archetype is just adding its code — companions can't be
  forgotten. A contract test guards it (every off-peak code drags a Dual meter).
- Prediction's heating donor now *carries* the donor's meter_type alongside its
  sap_heating cluster — the donor is already coherent.

Coherence is a synthesis-time obligation only; the calculator still scores a real
lodged cert exactly as lodged.

Verified on 713406: baseline 13 -> 47.8 (E), matching its recorded rating; the
phantom HHRSH recommendation is gone and the plan no longer overshoots to A.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:46:11 +00:00

3.5 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 Overridecomplete 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 donorcarry 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.

Considered Options

  • Reify a HeatingSystem value object all three produce/consume — rejected: overlaps HeatingOverlay and 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 explicit Dual is 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).