docs(modelling): wall-insulation eligibility + conservation-status ADRs

Captures the grill-with-docs session for solid-wall insulation: CONTEXT.md
gains EWI/IWI Measure Types + the Wall Insulation Eligibility rule (+ a
flagged-ambiguity that the three planning flags stay distinct, never recollapsed
to legacy restricted_measures). ADR-0019 records the eligibility policy (cavity
-> cavity only; brick/system -> IWI+EWI; timber -> IWI only; cob/stone -> none;
conservation/flat block EWI, listed/heritage block both). ADR-0020 records
conservation/listed/heritage as three distinct Property attributes sourced by
extending the geospatial S3 repo (flags co-located with lat/long).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-04 15:11:40 +00:00
parent 68aa80c174
commit da69dc27fd
3 changed files with 59 additions and 0 deletions

View file

@ -234,6 +234,14 @@ _Avoid_: selected measures, default measures, optimal solution, recommended bund
The catalogue classification of a retrofit measure (e.g. `solar_pv`, `loft_insulation`, `ashp`); one or more Recommendations reference the same Measure Type with property-specific cost and impact.
_Avoid_: measure (ambiguous), category
**External Wall Insulation (EWI)** / **Internal Wall Insulation (IWI)**:
The two competing **Measure Options** for insulating a *solid* (non-cavity) main wall — insulation fixed to the outside face (`wall_insulation_type = 1`) or the room side (`wall_insulation_type = 3`), both 100 mm at λ = 0.04 W/m·K; the calculator **derives** the post-insulation U-value from the type + thickness (the lodged cert carries no U-value). IWI additionally lowers the wall's thermal-mass parameter (changing heating demand); EWI does not.
_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).
_Avoid_: restricted measures (legacy collapsed conservation/listed/heritage into one boolean — they now gate different Options, so keep them distinct)
### Valuation
**Property Valuation**:
@ -346,3 +354,4 @@ _Avoid_: API key, auth token, secret
- **"phase"** (sequencing measures into ordered steps within a Scenario/Plan) was a speculative, prospective-client feature and is **deferred — out of scope** (see ADR-0005). It is *not* a current domain term: a **Scenario** carries one set of measures, a **Plan** one **Optimised Package**. The only live use of "phase" is cut-over timeline language in the PRD ("Phase 0 — Status quo"), which is project-management vocabulary and does not enter code.
- **"valuation"** was used for both a Property's current market value and the increase a retrofit produces — resolved into two distinct terms: **Property Valuation** (current value, a Baseline attribute) and **Valuation Uplift** (the plan-conditional, percentage-primary increase). The bare word "valuation" should be qualified to one of these.
- **"stale"** appears in two senses: cache-freshness ("a Repo record is stale and the orchestrator should refetch") — a legitimate operational concept; and as loose shorthand for the EPC's recorded cost fields being unusable. The cost fields are not stale — they are pinned to the inspection-date fuel rates by design. Use "pinned to inspection date" or "pre-SAP10 schema" (whichever applies) instead.
- **"restricted_measures"** (legacy `backend/Property.py`) collapsed `in_conservation_area`, `is_listed_building`, and `is_heritage_building` into one boolean that blocked EWI only. Resolved: the rebuild keeps the three flags **distinct**, because they gate different Options — a **conservation area** blocks EWI but allows IWI, whereas **listed/heritage** block both (see **Wall Insulation Eligibility**). Don't reintroduce a single collapsed flag.

View file

@ -0,0 +1,31 @@
# Wall Insulation Eligibility (cavity vs IWI vs EWI)
The solid-wall Recommendation Generator must decide, per Property, which wall-insulation Option(s) to offer. We decided eligibility is fixed first by **wall construction**, then narrowed by **planning status**, and that **External (EWI)** and **Internal (IWI)** wall insulation are two competing **Measure Options** under one "Main wall" **Recommendation** (the Optimiser picks at most one), consistent with ADR-0016 and the "cavity-fill vs EWI" exclusivity already described in CONTEXT.md.
## Decision
**By construction (detected from the wall *description* string, not the numeric `wall_construction` code — see Consequences):**
| Construction | Cavity fill | IWI | EWI |
|---|---|---|---|
| Cavity | ✅ only | ❌ | ❌ |
| Solid brick | — | ✅ | ✅ |
| System built | — | ✅ | ✅ |
| Timber frame | — | ✅ | ❌ (not constructable) |
| Cob / Granite-whinstone / Sandstone-limestone | — | ❌ | ❌ |
**Planning gates (on top), using three *distinct* flags (see ADR-0020):**
- **Conservation area** or **Flat** → remove **EWI** (external-appearance / whole-block constraints); IWI still offered.
- **Listed** or **Heritage** → remove **both** EWI and IWI (protected fabric).
A Recommendation is produced only when the wall is genuinely uninsulated (description contains "no insulation"), at a fixed **100 mm** insulation depth.
## Considered options
- **Mirror the legacy `is_suitable_for_solid_insulation` / `ewi_valid` rules verbatim.** Rejected in part: legacy collapsed all three planning flags into one `restricted_measures` boolean that blocked EWI only. We keep the flags distinct so listed/heritage can block IWI too — a deliberate strengthening.
- **Offer solid-wall insulation on cob/stone** (the calculator *can* model it — Elmhurst produces valid after-certs). Rejected: recommending standard EWI/IWI on breathable cob/rubble-stone fabric risks trapping moisture; we do not auto-suggest it.
## Consequences
- Cob and both stone types get **no** wall-insulation recommendation at all, even though the SAP calculator scores them fine — this is a deliberate building-pathology safeguard, not a gap.
- The conservation/listed/heritage gate depends on Property data not yet ingested (ADR-0020); until that lands the gate is an explicit generator input defaulting to "unrestricted", so the offline generator over-offers EWI in the interim. Not production-exposed.

View file

@ -0,0 +1,19 @@
# Conservation / Listed / Heritage as distinct Property attributes
Wall Insulation Eligibility (ADR-0019) — and later Solar-PV and Windows generators — gate on a Property's planning status. That status is **not** on the EPC and **not** in the OS Open-UPRN parquet the geospatial Repo reads today (it only yields coordinates); legacy derives it by spatial-joining separate conservation/listed/heritage layers (`OpenUprnClient.set_spatial_data``property_spatial` table). We decided to model it as **three distinct boolean Property attributes**`in_conservation_area`, `is_listed`, `is_heritage` — resolved through the **geospatial layer** during Ingestion and read off the **Property** (not the EPC), because they describe the building's location/protection, not its energy fabric.
## Decision
- Keep the three flags **separate**, not legacy's collapsed `restricted_measures` boolean — they gate different Measure Options (conservation blocks EWI only; listed/heritage block both — ADR-0019).
- Surface them via the geospatial Repo (a `GeospatialRepository` method returning a planning-status record, alongside `coordinates_for`), persist onto the Property in Ingestion, and pass them into the generator as an explicit input.
- Build this as the **final integrating slice** of the solid-wall feature (build order A), after the calculator mechanics and generator are pinned — it also unlocks the PV/Windows gates.
## Source (decided)
The conservation/listed/heritage flags are **co-located with longitude/latitude in the same S3 partition** the geospatial Repo already reads — so we **extend the existing `GeospatialS3Repository`** to surface those extra columns alongside the coordinates, rather than porting a separate spatial-join or reading the legacy `property_spatial` table. A further deep-dive into the exact S3 columns/shape precedes slice 3.
## Consequences
- Generators that need planning status take it as an input or read it off the Property; it never lands on `EpcPropertyData`.
- Until this slice ships, the ADR-0019 gate defaults to "unrestricted" (offline only).
- Mirrors legacy's conservative stance where missing data implied restriction — the source slice should decide the "unknown" default explicitly (block EWI vs allow) rather than silently allowing.