The landlord property-type description is a "<dwelling type>: <built form>: <floor>"
split whose leading token IS the dwelling type; the built-form tail is not. The
LLM occasionally over-read the tail and flipped the type — a handful of
"Bungalow: EndTerrace" / "Bungalow: MidTerrace" dwellings were stored as House.
Adds property_type_guard (claims the recognised leading token: House / Bungalow /
Flat / Maisonette / Park home; defers unrecognised phrasings to the LLM) and wires
property_type through a GuardedColumnClassifier, so the built-form tail can never
flip the type and the live path is deterministic.
Applied the scoped backfill to portfolio 796 (Hyde): 3 rows corrected from House
back to Bungalow. No enum migration needed — the targets are original members.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MainFuelType had no individual wood-logs member — only "biomass (community)" —
so the LLM classifier funnelled "Solid Fuel: Wood Logs" into the community fuel,
inventing a community heat network the dwelling isn't on (and mislabelling the
connection). main_fuel had no deterministic guard at all, so nothing caught it.
Verified against domain/sap10_calculator/docs/specs: RdSAP 10 Specification
Table 32 lists "wood logs" as a solid fuel (code 20, 0.028 kgCO2e/kWh); the
calculator's input scheme (the gov EPC API fuel enum) codes it 6 -> Table 32 20
(sap_efficiencies._API_TO_TABLE32), and water_heating_overlay already pins the
same fuel to 6. So _FUEL_CODES["wood logs"] = 6 is confirmed, not guessed.
Adds MainFuelType.WOOD_LOGS + the _FUEL_CODES entry, a main_fuel_guard mirroring
water_heating_guard (claims the "wood log" token; dual fuel keeps its own member
since it has no "wood log" substring), and wires main_fuel through a
GuardedColumnClassifier so the live path is deterministic.
Applied the scoped backfill to portfolio 796 (Hyde): 21 rows off
"biomass (community)" -> "wood logs". property_overrides (TEXT) only; the
classifier-cache pgEnum member is deferred to the FE Drizzle migration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gov-API certs that lodge a gas boiler as main_heating_category=2 + a PCDB
main_heating_index_number but omit sap_main_heating_code were carried with
a null code, so the heating generators (which gated on sap_main_heating_code
alone) never evaluated them for a boiler upgrade or a system tune-up — they
silently got nothing (£0 works, plateau at D). The SAP calculator already
handles this shape (cert_to_inputs._is_wet_boiler_main), so the calculator
modelled these as wet boilers while the recommenders ignored them.
Mirror the calculator's classification in the generator:
- _is_wet_boiler / _is_electric_boiler fall back, when no SAP code is
lodged, to a PCDB Table 105 boiler index or main_heating_category in
{1,2} (heat pumps excluded); electric boilers stay excluded by the code
range when lodged, else by an electricity main fuel.
- _already_condensing reads the boiler's declared PCDB winter efficiency
when there is no Table 4b code, so a modern condensing boiler lodged this
way still isn't offered a pointless like-for-like swap (gets a tune-up).
Verified read-only on portfolio 814 / scenario 1271: pids 742304, 742306,
742322, 742325, 742336 now get a gas_boiler_upgrade (two cross D->C on the
boiler alone) where they had £0 works; predicted properties whose heating
donor is a PCDB-shape boiler (e.g. 742095/742102/742105) also recover a
tune-up. Fixes the A#2 slice of #1388 (and unblocks 742302/742367).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PropertyBaselineOrchestrator now reads no Lodged Performance off a predicted
Property's neighbour-synthesised EPC: lodged is None when source_path is
'predicted', so no phantom lodged figure is manufactured. The Effective half is
unchanged. Rebaseliner port + PropertyBaselinePerformance.lodged widen to
Optional; the pristine-cert paths assert non-None (lodged is None only for a
predicted Property, which is always physical_state_changed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers the 10 audited "community CHP and boilers" properties (302) alongside the
2,210 "community boilers" (301) — both were mis-bucketed to Gas CPSU.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>