Model/docs/adr/0004-baseline-performance-lodged-effective-pair.md
2026-05-16 14:15:56 +00:00

2 KiB

BaselinePerformance stores both lodged and effective values

A Property's current performance has two states we care about: the rating that was lodged on the government register (the "lodged" SAP / band / carbon / heat) and the rating produced by the modelling pipeline against the current Effective EPC (the "effective" values, which may have been rebaselined by ML when the EPC was pre-SAP10 or when Landlord Overrides / Site Notes changed physical state). We considered storing a single set of values — the rebaselined-if-needed-otherwise-lodged figures — and rejected that. Both are stored as a pair on every BaselinePerformance, equal when no rebaselining trigger fires.

The pair lets the FE show "this is what the gov register says vs this is the SAP10-equivalent we modelled against" side by side without a second query, and keeps the audit trail clean: a user looking at a property's plan can see exactly which figure drove the recommendation pipeline. Storing only one set forces a downstream consumer to recompute the missing one from raw EPC fields when it needs both, which is the kind of derivation creep we want to keep out of the FE.

The cost is a wider row + the discipline that every BaselinePerformance populates both halves, even when they're equal. Annual kWh, fuel split and bills are not paired — they are always derived deterministically by EpcEnergyDerivationService against the Effective state, because the EPC's recorded cost fields use fuel rates pinned to the inspection date and the UCL correction depends on the modelled band.

Consequences

  • Schema migration: property_details_epc (or its successor) carries 8 fields instead of 4 for the SAP-equivalent block.
  • Reversing this means rewriting every consumer that has learned to read both values. Hard to roll back once the FE depends on the pair.
  • The rebaseline trigger has two reasons (pre_sap10, physical_state_changed, or both) — store the reason alongside so we know why a property was rebaselined when debugging.