Record below-regs loft eligibility as ADR-0063

The roof generator offered loft insulation only when genuinely uninsulated
(ADR-0021), so a loft with a shallow lodged depth (e.g. 724702's 50mm) got
nothing and never entered the optimiser pool. Grilled with Khalim: the loft
branch becomes eligible when the lodged depth is a known numeric value below
the 270mm building-regs compliance gate, topped up to the 300mm install depth.
Numeric-lodged-depths only; sentinels keep their ADR-0047 resolution. Loft
branch only. CONTEXT 'Roof Insulation Eligibility' updated to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-10 15:59:51 +00:00
parent 59954db488
commit ce0940c335
2 changed files with 79 additions and 1 deletions

View file

@ -353,7 +353,7 @@ _Avoid_: no-op filter (it gates generation, not scored output), U-value threshol
_Avoid_: restricted measures (legacy collapsed conservation/listed/heritage into one boolean — they now gate different Options, so keep them distinct)
**Roof Insulation Eligibility**:
The rule fixing which single roof Measure the main-roof **Recommendation** offers, by roof type. One Measure per roof — never a menu the Optimiser chooses between (ADR-0021): **pitched with an accessible loft** (incl. **thatch** — the covering doesn't block insulating the loft floor) → **loft insulation** (laid flat at the ceiling joists, 300 mm); **pitched with a sloping ceiling****sloping-ceiling insulation** (at the rafters, 100 mm); **flat roof****flat-roof insulation** (200 mm); **pitched, no access** → none (can't reach the void). A **room-in-roof** takes neither loft nor sloping-ceiling insulation — it is insulated at its own slopes/stud-walls (rafter-area, not floor-area, quantity) as a distinct **room-in-roof insulation** Measure, currently **deferred** (pending retrofit-specialist examples). A Measure is offered only when the roof is genuinely uninsulated ("As Built" / "None" / 0 mm).
The rule fixing which single roof Measure the main-roof **Recommendation** offers, by roof type. One Measure per roof — never a menu the Optimiser chooses between (ADR-0021): **pitched with an accessible loft** (incl. **thatch** — the covering doesn't block insulating the loft floor) → **loft insulation** (laid flat at the ceiling joists, 300 mm); **pitched with a sloping ceiling****sloping-ceiling insulation** (at the rafters, 100 mm); **flat roof****flat-roof insulation** (200 mm); **pitched, no access** → none (can't reach the void). A **room-in-roof** takes neither loft nor sloping-ceiling insulation — it is insulated at its own slopes/stud-walls (rafter-area, not floor-area, quantity) as a distinct **room-in-roof insulation** Measure, currently **deferred** (pending retrofit-specialist examples). Sloping-ceiling and flat-roof measures are offered only when the roof is genuinely uninsulated ("As Built" / "None" / 0 mm). The **loft** measure is additionally offered when the lodged depth is a known numeric value **below the 270 mm building-regs compliance gate** — a sub-standard loft is topped up to the 300 mm install depth (ADR-0063): the gate (compliance floor, 270 mm) and the target (install depth, 300 mm) are deliberately different numbers. The below-standard gate is **numeric-lodged-depths only**; sentinels keep their [[RdSAP String Sentinels]] resolution (`NI` stays not-eligible; `ND`/`None` resolve by age band).
_Avoid_: "roof insulation" (name the specific Measure — loft / sloping-ceiling / flat-roof / room-in-roof); "joist insulation" (use **loft insulation**, the established Measure Type)
**Glazing Eligibility**:

View file

@ -0,0 +1,78 @@
---
status: accepted (amends ADR-0021; composes with ADR-0047)
---
# A loft is eligible for insulation when its lodged depth is below the building-regs standard, not only when uninsulated
ADR-0021 fixed the roof generator to offer a Measure **only when the roof is
genuinely uninsulated** — for the loft branch, `roof_insulation_thickness ∈
{0, None}` (or a sentinel resolving to as-built-uninsulated per ADR-0047). That
under-serves a large, common case: a loft with a shallow *lodged* depth. Property
724702 (portfolio 796) lodges `roof_insulation_thickness = "50mm"`; the generator
read 50mm as "already insulated → recommend nothing" and returned `None`, so loft
insulation never entered the optimiser's candidate pool — and neither the plain
nor the fabric-first package could select it, on scenario 1292 (Increasing EPC →
C, unlimited budget). The Option's own description already promises to "top up to
recommended depth"; the eligibility gate never let it.
**270 mm is the compliance gate; 300 mm is the install depth — two numbers, two
questions.** Building Regulations Approved Document L pitches the retrofit
loft/ceiling standard at a roof U-value of ≈0.16 W/m²K, which is ≈**270 mm** of
mineral-wool-equivalent insulation: *"is this property at building regs?"*. The
depth we lay when we do the work is the best-practice **300 mm** (ADR-0021;
pinned to the Elmhurst before→after cascade, `test_elmhurst_cascade_pins`):
*"how deep do we install?"*. They are deliberately different values because they
answer different questions — a loft at 270 mm is compliant and left alone; a loft
below it is topped up past the bar, to 300 mm. This is not a 270-vs-300
inconsistency to reconcile.
We gate on **depth** as a practical proxy for the regs **U-value**, because depth
is what the cert lodges. That is exact for mineral-wool lofts and mildly
conservative for higher-performance materials (a thinner high-λ layer could be
compliant yet read as sub-270 mm) — an accepted simplification, not a silent one.
Decided in a grilling session with Khalim, 2026-07-10.
## Decision
**The loft branch is eligible when the MAIN roof's lodged insulation depth is a
known numeric value below the 270 mm building-regs compliance gate**, in addition
to the existing genuinely-uninsulated triggers. The recommended top-up depth is
unchanged at **300 mm**, so the gate (compliance floor) is 270 mm and the target
(install depth) is 300 mm.
- **Numeric lodged values only.** The rule parses an explicit numeric depth from
the lodged `roof_insulation_thickness` — a bare int (`50`), an `"Nmm"` string
(`"50mm"`), or an `"Nmm+"` string (`"400mm+"` → 400). A parsed depth `< 270`
is eligible; `>= 270` is not.
- **Sentinels keep their ADR-0047 resolution, unchanged.** `ND` / `None` /
`"As Built"` still resolve to the construction age band's as-built state
(AD uninsulated → eligible), and `NI` ("insulation present, thickness
unknown") stays **not-eligible** to the generator even though the U-cascade
scores it as 50 mm. The deliberate eligibility-vs-scoring split (ADR-0047,
CONTEXT "RdSAP String Sentinels") is preserved: an unknown depth does not
auto-trigger works. This keeps the change surgical — it fixes the lodged-depth
case without reopening sentinel resolution.
- **Loft branch only.** Sloping-ceiling (100 mm at the rafters) and flat-roof
(200 mm) keep the uninsulated-only gate. Their U-targets and physical
constraints differ from a ceiling-level loft, so a below-standard gate for
them is a separate decision with its own recommended depths — deferred.
## Consequences
- Property 724702 and every like it (a lodged loft depth in `[1, 269]` mm) now
produce a loft Recommendation, priced at the roof area and topped up to 300 mm.
The 50 mm → 300 mm before→after is scored truthfully by the calculator's
existing U-cascade.
- **Unlimited-budget scenarios recommend low-value top-ups.** A loft already at
200250 mm is now eligible for a ~0.1-SAP top-up to 300 mm. Budget-capped
scenarios self-filter (the optimiser will not spend on a negligible gain), but
an unlimited-budget brief — such as the reporting scenario 1292 — will buy it
in fabric-first phase 1. This is the accepted cost of a clean building-regs
gate; if the noise proves material, a minimum-gain or lower-depth floor is the
follow-up lever.
- ADR-0021's dispatch table gains a loft trigger: *uninsulated, **or** a lodged
numeric depth < 270 mm*. The other branches' rows are unchanged.
- CONTEXT.md's "Roof Insulation Eligibility" entry is updated from "offered only
when the roof is genuinely uninsulated" to include the below-standard loft
case, so the ubiquitous language matches the generator.