diff --git a/CONTEXT.md b/CONTEXT.md index 182959e66..81b2716c3 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -317,7 +317,11 @@ The two competing **Measure Options** for insulating a *solid* (non-cavity) main _Avoid_: solid wall insulation (that names the pair, not one Option), cladding, drylining **Wall Insulation Eligibility**: -The rule fixing which wall Option(s) the main-wall **Recommendation** offers, by wall construction then planning status. By construction: **cavity** → cavity fill only (never solid insulation); **solid brick** / **system-built** → IWI + EWI; **timber-frame** → IWI only (EWI is not constructable); **cob** / **granite or whinstone** / **sandstone or limestone** → none (breathable fabric — standard insulation risks trapping moisture). Then, as planning gates: a **conservation area** or **flat** removes EWI (external-appearance / whole-block constraints), and a **listed** or **heritage** building removes **both** EWI and IWI (protected fabric). +The rule fixing which wall Option(s) the main-wall **Recommendation** offers, by wall construction then planning status. By construction: **cavity** → cavity fill only (never solid insulation); **solid brick** / **system-built** → IWI + EWI; **timber-frame** → IWI only (EWI is not constructable); **cob** / **granite or whinstone** / **sandstone or limestone** → none (breathable fabric — standard insulation risks trapping moisture). Then, as planning gates: a **conservation area** or **flat** removes EWI (external-appearance / whole-block constraints), and a **listed** or **heritage** building removes **both** EWI and IWI (protected fabric). Finally the **Wall U-Value Gate** applies. + +**Wall U-Value Gate**: +The eligibility rule that a wall insulation Option is offered **only when it would lower the wall's derived U-value** (by more than a 0.01 W/m²K float-noise guard) — comparing `u_wall` on the effective wall's current state vs its post-measure state (ADR-0051). Kills the **no-op fabric offer**: on a modern construction age band (e.g. band L, 2012–2022) RdSAP's cascade assigns the as-built wall its at-regs U-value and added insulation resolves to the *same* value, so the measure costs thousands for +0.00 SAP. The gate is physical possibility only — how *much* improvement is worth the cost stays with the Optimiser (ADR-0016/0024). +_Avoid_: no-op filter (it gates generation, not scored output), U-value threshold (there is no absolute target-U; the comparison is before-vs-after) _Avoid_: restricted measures (legacy collapsed conservation/listed/heritage into one boolean — they now gate different Options, so keep them distinct) **Roof Insulation Eligibility**: diff --git a/docs/adr/0050-electric-underfloor-override-drags-category-8-and-conservative-group-7-control.md b/docs/adr/0050-electric-underfloor-override-drags-category-8-and-conservative-group-7-control.md new file mode 100644 index 000000000..1ed2b536c --- /dev/null +++ b/docs/adr/0050-electric-underfloor-override-drags-category-8-and-conservative-group-7-control.md @@ -0,0 +1,84 @@ +# An electric-underfloor override drags category 8 and the conservative Group 7 control + +## Status + +accepted + +## Context + +[ADR-0048](0048-heating-override-forces-category-and-control-defers-fuel-and-meter.md) +established that a system-replacing heating override must emit a **complete, +coherent companion set** — category and control are archetype-forced, never +inherited — and explicitly deferred the category expansion for the still-`None` +archetypes ("electric boiler/CPSU→2, underfloor→8 … flows into the +category-keyed Table 12a resolver and needs a corpus check") to its own change. + +The portfolio-796 audit (PRD #1435, WS2) found the underfloor half of that gap +end-to-end on property **711795** (uprn 10090343115): a lodged **community +heating** cert (SAP 301, category 6, control 2311) overridden to *"Electric +underfloor, in screed above insulation"* (SAP 424, +[ADR-0046](0046-electric-underfloor-scored-by-its-own-codes-meter-deferred-when-tariff-ambiguous.md)). +`_category_for(424)` and `_control_for(424)` both returned `None`, so the +effective cert kept the replaced heat network's **category 6** and community +control. Two independent mis-ratings followed: + +1. **No heating pathway.** `is_heat_network_main` keys on category 6, so the + heating Recommendation Generator excluded both HHRSH and ASHP by the + heat-network topology rule — `recommend_heating` returned `None` and the + flat's whole candidate menu was a no-op wall measure plus lighting. The + dwelling was unliftable from band E (the `plan-stuck-in-low-band` audit + group counts ~7 electric-underfloor dwellings). +2. **Baseline mis-rated.** The calculator scored the electric underfloor as + community heating: baseline SAP **51 (E)** vs **67 (D)** with coherent + companions. With the pathway restored, HHRSH scores **80 (C)** for ~£1k — + the dwelling reaches the scenario goal. + +## Decision + +The three electric-underfloor archetypes (SAP Table 4a **421 / 422 / 424**, +ADR-0046) join the forced-companion branches of `main_heating_system_overlay`: + +1. **Category 8** ("Electric underfloor heating") in `_category_for` — + deterministic from SAP Table 4a, per ADR-0048's category rule. +2. **Control 2701** (SAP Table 4e **Group 7**, "no time or thermostatic control + of room temperature", **+0.3 °C**) in `_control_for` — the conservative + default within the group the category fixes: the largest mean-internal- + temperature adjustment in Group 7, so an unobserved control is never + over-credited. Mirrors the storage manual-charge 2401 + ([ADR-0035](0035-coherent-heating-system-synthesis.md)) and room-heater 2601 + (ADR-0048) precedents. + +Fuel (electricity 29) and meter (Dual for 421/422; **deferred to the cert** for +tariff-ambiguous 424) are already correct per ADR-0046 and are unchanged. + +**Scope**: underfloor only. Electric boiler / CPSU (Group 1) remain +log-and-continue gaps tracked by #1444 — their category (2) feeds the wet-boiler +fallback classification in the generators and needs its own check. + +## Consequences + +- An underfloor override on any lodged system now yields a coherent cert: the + Table 12a resolver and the heat-network gates read the dwelling as electric + underfloor, the baseline re-rates correctly, and the HHRSH bundle is offered + where eligible (a flat's only heating pathway — ASHP stays house/bungalow-only + per ADR-0024). +- The "incomplete companion set" error log no longer fires for 421/422/424. +- **Known deferred gap (pre-existing, unchanged by this ADR):** the Table 12a + space-heating resolver's underfloor rows are wired in the fraction table but + `_table_12a_system_for_main` never returns `UNDERFLOOR_HEATING` ("Underfloor + heating (421-422) — TODO"), so an off-peak-metered underfloor dwelling falls + to the all-low-rate fallback. That affects genuinely-lodged underfloor certs + identically and needs spec sight (SAP 10.2 Table 12a Grid 1, PDF p.191) to + split slab (storage-like) from screed (direct-acting-like); tracked as a + follow-up, not ridden on this override fix. + +### Alternatives rejected + +- **A zero-adjustment Group 7 control (2703/2704).** Rejected: credits a room + thermostat the landlord never reported; the conservative default is the + ADR-0035/0048 pattern. +- **Completing electric boiler / CPSU (category 2) in the same change.** + Rejected: no evidence property in this workstream, and category 2 flows into + the generators' wet-boiler fallback (`_WET_BOILER_CATEGORIES`), which would + make an electric-boiler override newly eligible for tune-up options — a + behaviour change needing its own corpus check (#1444). diff --git a/docs/adr/0051-wall-insulation-offered-only-when-it-lowers-the-derived-wall-u-value.md b/docs/adr/0051-wall-insulation-offered-only-when-it-lowers-the-derived-wall-u-value.md new file mode 100644 index 000000000..90db6f335 --- /dev/null +++ b/docs/adr/0051-wall-insulation-offered-only-when-it-lowers-the-derived-wall-u-value.md @@ -0,0 +1,71 @@ +# Wall insulation is offered only when it lowers the derived wall U-value + +## Status + +accepted + +## Context + +The wall Recommendation Generators trigger on the wall's **insulation state** +alone: `recommend_cavity_wall` offers cavity fill for any cavity wall whose +`wall_insulation_type` is as-built (4), and `recommend_solid_wall` offers +EWI/IWI for any as-built suitable construction +([ADR-0019](0019-solid-wall-insulation-options.md)). Neither asks whether +adding insulation can actually **change the modelled wall**. + +For modern construction age bands it cannot: RdSAP's Table 6-10 cascade +(`u_wall` in `domain/sap10_ml/rdsap_uvalues.py`) assigns an as-built wall its +age band's at-regs U-value, and the post-measure state resolves to the **same +value**. Portfolio-796 evidence (PRD #1435, WS2): property **711795**, timber +frame at age band **L** (2012–2022) — `u_wall` returns **0.280 W/m²K both +as-built and with 100 mm IWI**, so the persisted plan offered +`internal_wall_insulation` at **£4,027 for +0.00 SAP**. Any modern-age-band +dwelling gets the same no-op fabric offer; on an unliftable dwelling it is the +*entire* remedial menu, which reads as "we recommend spending £4k for nothing". + +Scoring already prices the option at +0.00, but the Optimiser keeps +zero-uplift candidates in the persisted menu (they are legitimate for some +measure types, e.g. secondary-heating removal saves bills without SAP), so the +fix belongs at **generation eligibility**, not a blanket score filter. + +## Decision + +Both wall generators consult the **derived wall U-value** before offering: the +measure is offered only when the post-measure state's `u_wall` is **lower than +the current state's by more than 0.01 W/m²K** (a float-noise guard, not a +materiality threshold — economics stay with the Optimiser, ADR-0016/0024). + +- **Current state**: `u_wall` with the effective main wall's construction, age + band, lodged insulation type/thickness — a thin parameter mapping in the + `envelope.py` style, deliberately omitting the calculator's rich extras + (surveyor description, dry-lining, documentary-thickness routes). Omissions + bias the gate toward **offering** (both sides are computed identically, so a + no-op still compares equal), never toward suppressing a real measure. +- **Post-measure state**: the same call with the Option's overlay applied — + filled cavity (`wall_insulation_type=2`) for cavity fill; 100 mm + internal/external insulation for IWI/EWI. + +## Consequences + +- Age-band-L (and any at-regs age band) walls stop polluting candidate menus + with £4k no-ops; genuinely treatable walls (e.g. age-band-D solid brick, + U 2.1 → ~0.3) are offered exactly as before. +- The generators gain a read on `domain/sap10_ml/rdsap_uvalues.u_wall` — same + precedent as the heating generator reading the calculator's PCDB/Table 4b. +- **Deferred:** (a) a cert that lodges an explicit `wall_u_value` overrides the + cascade in the calculator, making fabric measures score +0.00 regardless of + this gate — the measure overlay should adjust the lodged U; (b) roof/floor + generators have the same anti-pattern in principle (roof age-band eligibility + is partly covered by ADR-0047); both tracked as follow-ups. + +### Alternatives rejected + +- **Filter zero-SAP candidates after scoring.** Rejected: zero-uplift options + are legitimate for some measure types (bill-driven), and the pollution would + survive in any consumer that reads Generator output before scoring. +- **Hard-code "age band ≥ K ⇒ no wall offer".** Rejected: duplicates the U + tables coarsely; the tables already encode exactly which + construction × age-band × insulation combinations move. +- **Full-fidelity U parity with the calculator (description, dry-lining, + lodged U).** Rejected for now: heavier coupling for no observed benefit — + the omissions only ever bias toward offering, which is the status quo.