The modelling_e2e Lambda reads the active catalogue once per invocation
(SELECT * FROM material WHERE is_active ORDER BY id). Without an index this
was a full seq scan every time (~23ms), paid by all 32 concurrent Lambdas at
startup. Partial index on id WHERE is_active returns the active rows already
id-ordered — no sort, no filter.
Verified on dev: seq scan 23.2ms -> index scan 4.2ms.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A recursive persistence-fidelity guard surfaced 7 fields the SAP calculator
reads but the epc_property projection had no column for, so they were silently
lost on save->reload (same bug class as the PV arrays / floor flags). All
additive, nullable except the PV-diverter bool; no backfill (defaults match
the domain defaults).
- epc_main_heating_detail: community_heating_boiler_fuel_type,
community_heating_chp_fraction (community-heated CHP).
- epc_building_part: alt_wall_1_is_sheltered, alt_wall_2_is_sheltered, and
wall_insulation_thermal_conductivity (jsonb to preserve Union[int,str]).
- epc_property: energy_pv_diverter_present (NOT NULL DEFAULT false),
heating_cylinder_volume_measured_l, ventilation_air_permeability_ap50_m3_h_m2.
Includes generated migration 0246_minor_lady_bullseye.sql + snapshot/journal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add EPC persistence schema for two round-trip-fidelity gaps so the Model
backend can store the full EpcPropertyData faithfully:
- New child table epc_photovoltaic_array (one row per PV array), mirroring
the epc_window child-table pattern.
- epc_floor_dimension: add is_exposed_floor and is_above_partially_heated_space
boolean flags (NOT NULL DEFAULT false; additive, no backfill).
Includes generated migration 0245_magenta_nomad.sql + snapshot/journal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Display the UPRN beneath the selected address on the remote assessment
page so it's available for quicker debugging. The value is already on
the selected address object from the postcode lookup; it just wasn't
rendered.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a NOT NULL DEFAULT false `marked_for_deletion` boolean to the
`property` table. Hard deletes are expensive (cascading child rows,
indexes), so a row is flagged here and a separate cron job performs the
physical delete later; reads can filter on the flag in the interim.
A constant default makes this a metadata-only ALTER on Postgres 11+ (no
table rewrite); existing rows backfill to false.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a `source` discriminator to `epc_property` so a property can hold a
lodged and a predicted EPC at the same time, per the EPC Prediction
production-wiring branch in the Model repo (docs/MIGRATION_NOTE_predicted_epc_source.md).
- `source` text NOT NULL default 'lodged' — backfills every existing row as
a real lodged EPC.
- Unique index becomes (property_id, portfolio_id, source) so lodged +
predicted rows can coexist for the same property/portfolio.
- New (property_id, source) index — lodged/predicted reads filter on both.
Allowed values: 'lodged' | 'predicted'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>