diff --git a/docs/migrations/epc-property-round-trip-fidelity.md b/docs/migrations/epc-property-round-trip-fidelity.md index f91b8a4f..ce7684f0 100644 --- a/docs/migrations/epc-property-round-trip-fidelity.md +++ b/docs/migrations/epc-property-round-trip-fidelity.md @@ -86,26 +86,29 @@ DB migration: FE branch `feature/epc-pv-and-floor-heatloss-schema` (additive — `NOT NULL DEFAULT false` columns; no backfill). Run `drizzle-kit generate`/migrate **before** deploying this backend code. -### Recursive-guard gaps (newly surfaced) +### Recursive-guard gaps (surfaced AND closed) Generalising the ADR-0036 structural guard to recurse into **nested** dataclasses (it previously saw only `EpcPropertyData`'s top-level fields — which is exactly how the PV-array and floor-flag -drops stayed latent) surfaced **7 pre-existing, calculator-read** nested fields with no FE column. -These are silent-drop gaps of the **same class as the PV bug** and want their own follow-up -(verify scoring impact per field, then add FE columns): +drops stayed latent) surfaced **7 pre-existing, calculator-read** nested fields with no FE column — +silent-drop gaps of the **same class as the PV bug**. All 7 are now **closed**: FE columns added +(additive migration), Python SQLModel + save/`_compose` wired, and a deep-equality round-trip test +(`test_calculator_read_fields_round_trip`) pins them. Their allow-list entries were removed, so the +guard now *enforces* their reconstruction. -| Field | Lives on | -|---|---| -| `community_heating_boiler_fuel_type`, `community_heating_chp_fraction` | `MainHeatingDetail` | -| `is_sheltered` | `SapAlternativeWall` | -| `wall_insulation_thermal_conductivity` | `SapBuildingPart` | -| `pv_diverter_present` | `SapEnergySource` | -| `cylinder_volume_measured_l` | `SapHeating` | -| `air_permeability_ap50_m3_h_m2` | `SapVentilation` | +| Field | Lives on | DB column (table) | +|---|---|---| +| `community_heating_boiler_fuel_type`, `community_heating_chp_fraction` | `MainHeatingDetail` | `epc_main_heating_detail` | +| `is_sheltered` | `SapAlternativeWall` | `epc_building_part.alt_wall_{1,2}_is_sheltered` | +| `wall_insulation_thermal_conductivity` | `SapBuildingPart` | `epc_building_part` (jsonb) | +| `pv_diverter_present` | `SapEnergySource` | `epc_property.energy_pv_diverter_present` | +| `cylinder_volume_measured_l` | `SapHeating` | `epc_property.heating_cylinder_volume_measured_l` | +| `air_permeability_ap50_m3_h_m2` | `SapVentilation` | `epc_property.ventilation_air_permeability_ap50_m3_h_m2` | -(Plus dormant, not-calculator-read nested fields: `SapBuildingPart.{floor_u_value, roof_u_value, -wall_u_value, wall_is_basement, rafter_insulation_thickness}`, `SapAlternativeWall.is_basement`, -`SapHeating.cylinder_heat_loss` — allow-listed as dormant.) +(Still allow-listed as dormant — not calculator-read, no column: `SapBuildingPart.{floor_u_value, +roof_u_value, wall_u_value, wall_is_basement, rafter_insulation_thickness}`, +`SapAlternativeWall.is_basement`, `SapHeating.cylinder_heat_loss`. And `SapAlternativeWall.{u_value, +wall_thickness_mm}` remain FE-column-pending.) ---