RdSAP 10 §5.11.4: a pitched roof lodging thickness "NI" with description
"...insulated (assumed)" was billed at the §5.11.4 observed-retrofit
50 mm row (U 0.68) while the SAME description lodged with "ND" fell
through to the Table 18 age-band default (0.40 for bands A-G). The
ND-vs-NI sentinel is lodging-software noise; "(assumed)" means the
insulation PRESENCE is an age-band assumption, not an observed
retrofit, so Table 18's "thickness cannot be determined" clause
governs both forms. The bare "insulated" description (observed
retrofit, no qualifier) keeps the 50 mm row.
Evidence: the 27-cert loc4+NI corpus cohort was systematically
under-rated (~-1 SAP; zero positive movers among "(assumed)" certs),
vs the 102-cert loc4+ND cohort already on Table 18 at ~0 bias.
Gauge: within-0.5 74.2% -> 75.5%, SAP MAE 0.721 -> 0.708, CO2 MAE
0.09 -> 0.074, PE MAE 3.5 -> 3.2 (floors ratcheted). Unit-pinned in
test_rdsap_uvalues (assumed -> Table 18; bare-insulated -> 0.68
regression guard); integration pin in test_heat_transmission updated;
RealCertExpectation pinned for 10094975827 (66 = lodged, was -1.87).
Ledger: C002 103001004 deferred pending Elmhurst arbitration
(single-immersion off-peak pricing + declared cylinder loss vs
Table-2); C006 quadruplet marked ⚠ (LIG-21.0 software honours lodged
0 extract fans literally vs the Elmhurst-validated 0=unknown->default
convention, worksheet case 46).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Setup for the RdSAP-21.0.1 corpus gauge campaign (74.2% within-0.5,
SAP MAE 0.721 at HEAD, 1000/1000 computed):
- scripts/corpus_1000/build_worklist.py — runs all 1000 corpus certs
through the gauge's own path (from_api_response -> Sap10Calculator),
ranks |engine - lodged| descending, clusters by dwelling signature,
and (re)writes scripts/corpus_1000/worklist.md preserving per-cert
statuses and notes. 258 certs outside 0.5 in 249 clusters.
- C001 (worst cert, Δ +23.9): uprn 4510053280, ground-floor flat 47 m²
on ASHP PCDB 100053 (Mitsubishi Ecodan 5 kW, PSR table 0.2-2.0).
Dwelling PSR 2.031 -> SAP 10.2 N2 footnotes 44/45 heat-pump extension
(reciprocal interpolation toward 100% at 2x largest PSR) = 305%
space efficiency, which accredited Elmhurst also applies (golden
case 56, record 100061). The LODGED software instead treated the
out-of-range record as invalid and billed 100% direct electric +
standard schedule (reproduces lodged CO2 to 1%). Lodged-software
methodology gap, worklist ⚠, engine untouched; observed engine 75
pinned in test_real_cert_sap_accuracy.
Gauge floors unchanged (no engine change).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PropertyAudit gained three required cylinder fields; the pre-existing
divergence/goal-shortfall tests construct it via _make_audit, which
wasn't updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Portfolio 814 / scenario 1271 audit (2026-07-03): all 26 properties lodging
has_hot_water_cylinder with insulation type 0 (none) or NULL over-rate their
effective baseline by up to +18 SAP (avg +12.7) because
_cylinder_storage_loss_override returns None for any insulation label it
can't resolve and the worksheet keeps the zero-storage-loss combi default.
Same-lodged neighbours split by 19 effective points (WD5 0DP pids
742215/742216, both lodged 54, effective 50 vs 69) and the inflated members
distorted neighbour-divergence cohort medians (E17 6EB pid 742355 Δ+18).
New HIGH check cylinder-storage-loss-dropped fires on the deterministic
input pattern (cylinder present, insulation type ∉ {factory, loose jacket}
or thickness missing) — 28/338 on the motivating portfolio (the 26 above
plus 2 same-pattern rows with no lodged score). The calculator fix
(uninsulated Table 2 loss, or RdSAP Table 29 age-band default) is separate
follow-up work.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`_category_for` mapped room heaters / heat pumps / heat networks but
returned None for electric-storage-heater codes (401-409), so a storage
system override emitted `sap_main_heating_code=409` +
`main_heating_category=None` while `_control_for` right beside it correctly
set the storage charge control — an asymmetry. Map storage codes to
category 7 so the overlaid cert is internally consistent (code 401-409 <->
category 7), matching the control/fuel/meter companions the overlay drags.
Consistency follow-up to the modelling HHRSH off-peak-pricing fix (#1426):
that PR's Table 12a resolver already prices any storage-coded system
off-peak regardless of category, so this is NOT a live mispricing — it
removes a latent code<->category inconsistency that other category-keyed
logic (responsiveness, pumps/fans) could trip on.
Tests: storage/HHRSH overlay tests now also assert `main_heating_category==7`.
tests/domain/epc (347) + prediction/override/reclassify (118) green; source
pyright-clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>