RdSAP 10 Specification (10-06-2025) §5.2.5: "If a dwelling or part of a
dwelling has commercial premises above record as another dwelling
above" — so code 9 ("another premises above") must get the same
zero-heat-loss treatment as code 7 ("another dwelling above"), per the
spec's own "there is no heat loss through the roof of a building part
that has the same dwelling above or another dwelling above."
The previous fix mapped both to None, which only avoided the mapper
crash — it didn't achieve zero heat loss. heat_transmission.py's
per-part suppression (part_roof_is_party) only fires when
roof_construction_type CONTAINS the literal string "another dwelling
above"; that string was only ever set on the Elmhurst ingestion path.
The API path mapped 7 (and, until now, 9) to None, so the suppression
never fired for any API-derived cert — has_exposed_roof (the
dwelling_type-label-only fallback) was the sole gate, with no way to
know about a per-part party-ceiling lodgement. Both codes now map to
"(another dwelling above)" so the existing suppression logic fires on
both ingestion paths, matching what
domain/epc/property_overrides/roof_party_ceiling_guard.py already does
independently for the landlord-override pipeline (same spec citation).
Verified against the motivating cert (uprn 10013320122): roof_w_per_k
now correctly computes to 0.0. Full RdSAP-21.0.1 corpus (1000 certs)
re-run: within-0.5 unchanged at 77.8%, MAE improves marginally
(0.6364 vs 0.637 ceiling) — no regression.
Same party-ceiling semantics as the already-mapped code 7 ("another
dwelling above" / "same dwelling above") — the unit has no exposed
roof, so there's nothing for the cascade to compute a U-value for.
Confirmed against the live cert (uprn 10013320122): roofs[].description
= "(another premises above)", roof_insulation_location = "ND" (the
codebase's established "no exposed roof" signal).
Surfaced by 1 failed modelling_e2e subtask on portfolio 817
(task_source='modelling_e2e', job_completed > '2026-07-03 12:50:37.555+01:00').
Some lodged certs omit the sap_heating.has_fixed_air_conditioning key,
which the generic from_dict loader treats as a required field and
rejects outright. Two prior commits (26651ca71, 8074f4152) fixed the
duplicate top-level field of the same name but left this nested copy
untouched; a third pass (44991bed0) missed it too. The mapper already
reads it defensively (== "true", which is False on None), so this is
a safe default.
Surfaced by 7 failed modelling_e2e subtasks on portfolio 817.
Golden cert 0330 now reaches band C on the dwelling-sized ASHP alone
(ADR-0049), dropping its solid_floor_insulation companion; cert 0390
still fires three measures, so the per-measure trigger-attribute
assertions move there (and gain the lighting triggers).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`from_sap_schema_17_1` hardcoded is_dwelling_export_capable=False, so
SAP 10.2 Appendix M1 zeroed the PV export credit on every export-capable
full-SAP cert. Cert 0380-3044-6070-2305-5925 (property 741840, PRD #1435
WS3) now reproduces its lodged 92 exactly (was 89); the existing
19.1.0 pin 10096028301 closes its documented -1 residual (84 -> 85 =
lodged). The full-SAP top-level pv_connection is deliberately NOT
carried: its enum disagrees with the RdSAP one the calculator gates on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>