Model/docs/migrations/property-baseline-performance-table.md
Khalim Conn-Kowlessar 2c8c299fde docs(migration): add the Bill Derivation block to the property_baseline table (ADR-0014)
Slice 5b: update the FE-owned migration spec so the other repo can create the
bill columns in parallel.

- Bill block: per-section delivered kWh + cost (heating, hot water, lighting,
  appliances, cooking, pumps/fans, cooling) + standing_charges_gbp,
  seg_credit_gbp, total_annual_bill_gbp, fuel_rates_period.
- space_heating_kwh / water_heating_kwh (RHI recorded demand) marked SUPERSEDED
  by heating_kwh / hot_water_kwh (calculator delivered fuel); kept until the bill
  populates, then dropped.
- Cooling section kept (mostly 0 but affects the bill, cheap to store).
- Records the calculator-load-bearing posture (effective_* may differ from
  lodged_* for pre-10.2) and that columns are defined now / populated when the
  SapResult->EnergyBreakdown adapter + BillDerivation wiring land.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 10:13:23 +00:00

4.4 KiB
Raw Permalink Blame History

property_baseline_performance table — FE-owned migration

Context: Slice 6 (Hestia-Homes/Model#1135) of the ara_first_run rebuild. The PropertyBaselineOrchestrator establishes a Property's Baseline Performance (ADR-0004) and persists it via a new PropertyBaselineRepository port. This is a brand-new table — no predecessor.

Per ADR-0004's amendment, the lodged/effective pair does not land on property_details_epc (which is being retired as too coupled to the legacy EPC-API schema). It lands here, as its own aggregate's table.

The SQLModel row is defined in infrastructure/postgres/ so the ephemeral-Postgres tests build it via SQLModel.metadata.create_all. The production migration is FE-owned (Drizzle ORM) — a straight lift-and-shift of the columns below.

property_baseline_performance — one row per Property

Column Type Notes
id serial PK
property_id int, FK → property.id, unique one Baseline Performance per Property
lodged_sap_score int Lodged Performance — gov register, off the Effective EPC
lodged_epc_band text the Epc enum, stored as its string value (e.g. "C")
lodged_co2_emissions_t_per_yr float tonnes CO₂/yr (whole dwelling)
lodged_primary_energy_intensity_kwh_per_m2_yr int PEUI (kWh/m²/yr); not "heat demand" — see CONTEXT.md
effective_sap_score int Effective Performance — what modelling scored against
effective_epc_band text
effective_co2_emissions_t_per_yr float tonnes CO₂/yr (whole dwelling)
effective_primary_energy_intensity_kwh_per_m2_yr int kWh/m²/yr
rebaseline_reason text none | pre_sap10 | physical_state_changed | both
space_heating_kwh float EPC renewable_heat_incentive recorded demand. Superseded by heating_kwh (delivered) when the bill block populates; kept until then to avoid an empty-kWh gap, dropped in the population slice.
water_heating_kwh float EPC renewable_heat_incentive; superseded by hot_water_kwh.

Bill block (ADR-0014) — the energy bill, composed per section

Produced by Bill Derivation: the calculator's delivered kWh per end use priced at current Fuel Rates (a committed snapshot, not SAP's standardised prices), per section + the total. Per-section kWh is delivered fuel (demand ÷ efficiency — what the household pays for), distinct from the recorded-demand space_heating_kwh/water_heating_kwh above which it supersedes.

Column Type Notes
fuel_rates_period text which Fuel Rates snapshot priced this bill (e.g. "2026-04 to 2026-06") — provenance
heating_kwh float delivered fuel kWh (main + secondary heating)
heating_cost_gbp float priced at the heating fuel's current rate
hot_water_kwh float
hot_water_cost_gbp float
lighting_kwh float
lighting_cost_gbp float
appliances_kwh float unregulated load — 0 until the appliances/cooking fields land on SapResult (ADR-0014 TODO)
appliances_cost_gbp float
cooking_kwh float unregulated load — 0 until SapResult carries it
cooking_cost_gbp float
pumps_fans_kwh float
pumps_fans_cost_gbp float
cooling_kwh float mostly 0 in UK homes; carried for completeness as it affects the bill
cooling_cost_gbp float
standing_charges_gbp float daily standing charge × 365, once per distinct metered fuel (off-gas fuels have none)
seg_credit_gbp float SEG export credit on PV (subtracted)
total_annual_bill_gbp float Σ section costs + standing charges SEG

The calculator is load-bearing (ADR-0013 amendment): for sap_version < 10.2 the effective_* columns hold the calculator's output (so effective_* != lodged_* legitimately); at/above 10.2 they mirror the lodged figures and divergence is logged. A cert the calculator cannot score aborts the batch rather than persisting a wrong row.

Population timing

The bill columns are defined now so the FE can create them, but are populated only once the SapResultEnergyBreakdown adapter + BillDerivation wiring land (gated on the appliances / cooking SapResult fields). Until then the SQLModel mirror in infrastructure/postgres/ adds these columns as nullable; the Drizzle migration can create them nullable in parallel.