Session summary documents covering 6 slices:
- S0380.85 — Curtain Wall §5.18 dispatch (cascade walls 443 → 555.93 W/K)
- S0380.86 — §5.6 thin-wall stone + §5.8 dry-line (555.93 → 602.40,
worksheet 604.07, 0.27% residual)
- S0380.87 — Table 4e GROUP 2 HP control codes — single-line spec
dispatch bug; SH residual +7924 → +1460 (82%), sap_score
23 → 27, largest single-slice movement of session
- S0380.88 — Full Table 4e Groups 0-7 + UnmappedSapCode strict-raise
- S0380.89 — Table 4d responsiveness + screed-UFH bug fix + strict raise
(latent bug found via strict-raise rollout)
- S0380.90 — 6 dispatch sites strict-raise + UnmappedSapCode shared
module + GOV.UK API digit-string meter_type bug fix
HANDOVER_POST_S0380_90.md covers full state, cumulative-closure table,
strict-raise philosophy, and which 6 dispatch sites were closed.
NEXT_AGENT_PROMPT_POST_S0380_90.md briefs the next-slice work:
S0380.91 (RdSAP 10 Table 15 row 3 "Cavity masonry filled" U=0.2 in
u_party_wall — closes ~+1000 kWh of the remaining +1460 SH residual
on cert 000565 Ext1).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
14 KiB
Handover — post S0380.85..90 (BP main-wall closure + SH-channel discovery + strict-raise series)
Branch: feature/per-cert-mapper-validation. HEAD 9bfb8524.
Predecessor: HANDOVER_POST_S0380_84.md.
Slices committed this session (S0380.85..90)
Six spec-cited slices: two BP main-wall closures (Curtain Wall + thin- wall stone) followed by SH-channel investigation that surfaced the S0380.87 single-line bug (the dominant SH residual driver), then a 3-slice strict-raise series that closed the calculator's cascade- dispatch silent-fallback inventory.
| Slice | Commit | Spec | Cert 000565 outcome |
|---|---|---|---|
| S0380.85 | 647c1aad |
RdSAP 10 §5.18 (PDF p.48) — "U= 2.0 W/m²K for pre-2023 curtain walls; for post-2023, U-values as for windows below Table 24" | Cascade walls 443 → 555.93 W/K (+112). BP[2] Ext2 Curtain Wall U: 0.60 → 1.40 per worksheet (29a). |
| S0380.86 | 6c8bbbc9 |
RdSAP 10 §5.6 Table 12 (PDF p.40) thin-wall stone formula + §5.8 + Table 14 dry-line | Cascade walls 555.93 → 602.40 W/K (worksheet 604.07; 0.27% residual). BP[0] alt1 U: 0.32 → 2.34 EXACT vs worksheet. Plus SapAlternativeWall.wall_thickness_mm field added (per feedback-no-misleading-insulation-type). |
| S0380.87 | c0328f4e |
SAP 10.2 Table 4e GROUP 2 (PDF p.172-173) — HP control code 2207 → control type 3 | Largest single-slice movement of the session: sap_score 23 → 27; SH residual +7924 → +1460 kWh (82% closed); sap_score_continuous +4.71 closer. Mechanism: wrong control type → wrong elsewhere off-hours per Table 9 → MIT_elsewhere over by +0.5 °C → over-counted SH. |
| S0380.88 | 1b3bbbf7 |
SAP 10.2 Table 4e (PDF p.171-174) all 8 groups | Introduced UnmappedSapCode(ValueError) strict-raise. Extended _CONTROL_TYPE_BY_CODE to all 40 codes (Groups 0-7). Corpus audit closed 3 silent mis-classifications (codes 2307, 2401, 2603). |
| S0380.89 | 6d02d205 |
SAP 10.2 Table 4d (PDF p.170) | Fixed a latent bug: _responsiveness had if emitter == 2: return 0.25 — treating screed UFH as concrete-slab UFH. Spec is R=0.75 (3× under-spec). Bug was latent because _first_main_heating picks main[0] and all cohort+golden certs lodge radiators (emitter=1) on main[0]. |
| S0380.90 | 9bfb8524 |
Bundled 6 dispatch-site closures | _pv_pitch_deg, _pv_overshading_factor, tariff_from_meter_type, _tariff_high_low_rates_p_per_kwh, _heat_network_dlf, _secondary_heating_fraction_for_category all flipped to strict-raise. UnmappedSapCode promoted to shared domain/sap10_calculator/exceptions.py. Fixed a second latent bug: GOV.UK API lodges meter_type='2' as digit-string on 125 golden certs — silently fell through to STANDARD via the str-dict miss; now routes via int-cast short-circuit. |
Test baseline at HEAD 9bfb8524: 574 pass + 9 expected
test_sap_result_pin[000565-*] fails. Pyright net-zero on every
touched file. Cohort + golden + cert 9501 unaffected.
Cert 000565 state (HEAD 9bfb8524)
| Pin | Cascade | Worksheet | Δ | Cause |
|---|---|---|---|---|
| sap_score (int) | 27 | 29 | −2 | Remaining +1460 SH residual; closes when party-wall CF U=0.2 + ventilation gap close |
| sap_score_continuous | 27.3534 | 28.5087 | −1.16 | Downstream of SH residual |
| ecf | 5.5066 | 5.3866 | +0.12 | Downstream |
| total_fuel_cost_gbp | 4784.29 | 4680.26 | +104 | Downstream |
| co2_kg_per_yr | 6581.12 | 6447.63 | +133 | Downstream |
| space_heating_kwh | 60468.18 | 59008.35 | +1460 | Party-wall CF over-count + ventilation +27 W/K |
| main_heating_fuel | 35569.52 | 34710.79 | +859 | Follows SH via 1/COP |
| hot_water_kwh | 3755.03 | 3755.03 | ✓ 0 EXACT | §4 cascade closed S0380.77..80 |
| lighting | 1387.02 | 1384.84 | +2.19 | Sub-spec |
| pumps_fans | 255.00 | 252.52 | +2.48 | MEV PCDB record missing |
Cumulative closure across this session
| Pin | post-.84 | .85 | .86 | .87 | .88 | .89 | .90 |
|---|---|---|---|---|---|---|---|
| sap_score | 26 | 24 | 23 | 27 | 27 | 27 | 27 |
| sap_score_continuous | 26.50 | 23.94 | 22.64 | 27.35 | 27.35 | 27.35 | 27.35 |
| space_heating_kwh | +2591 | +6348 | +7924 | +1460 | +1460 | +1460 | +1460 |
| cascade walls W/K | 443 | 555.93 | 602.40 | 602.40 | 602.40 | 602.40 | 602.40 |
S0380.85+.86 closed the BP main-wall cascade gap (walls 443 → 602 W/K, worksheet 604). Per feedback-verify-handover-claims the predicted SH closure didn't materialise — instead exposed a separate +8 k kWh SH-channel over-count that S0380.87 traced to a single-line spec dispatch bug. S0380.88-90 forecloses that bug pattern across the calculator.
Why the BP-wall slices made SH "worse" before S0380.87 fixed it
Per the diagnosis at the end of S0380.86:
- Pre-S0380.84: cascade walls 322 (under by 282) + party 153 (over by 88) ≈ cancelled at HTC level. SH residual ~0.
- Post-S0380.84: party fixed, walls still under. Cascade HTC under. But SH cascade was OVER worksheet — meaning a separate non-fabric SH-channel over-count was masked by the wall under-count.
- Each spec-correct +1 W/K of cascade walls added ~33.5 kWh of cascade SH (consistent ratio across .85/.86/.87). Closing wall under-count exposed the SH-channel over-count fully.
- S0380.87 traced it: cert 000565 main_heating_control=2207 (HP zone control) silently mapped to type 2 instead of type 3 → wrong elsewhere off-hours → MIT_elsewhere +0.5 °C → SH +4500 kWh.
Strict-raise series (S0380.88..90) — calculator philosophy change
User mandate ("we keep debugging silent fallbacks") prompted the strict-raise rollout. New module:
domain/sap10_calculator/exceptions.py — UnmappedSapCode(ValueError).
Mirror of mapper-side UnmappedApiCode / UnmappedElmhurstLabel.
Principle: distinguish "lodging absent" (None / 0 / "" — cascade
default OK per RdSAP §6.2.3 "assume as-built") from "lodging present
but unmapped" (raise so spec-coverage gap surfaces at test time).
Strict-raise applies to CODE DISPATCH sites; VALUE defaults (u_wall,
u_floor, ...) remain total per cascade-helper docstring.
Eight dispatch sites flipped:
_control_type(S0380.87 → .88)_responsiveness(S0380.89 — fixed bug)_pv_pitch_deg(S0380.90)_pv_overshading_factor(S0380.90)tariff_from_meter_type(S0380.90 — fixed bug)_tariff_high_low_rates_p_per_kwh(S0380.90)_heat_network_dlf(S0380.90)_secondary_heating_fraction_for_category(S0380.90)
Both latent bugs (S0380.89 + S0380.90) were exclusively surfaced by the strict-raise rollout — corpus audit caught the dispatch coverage gaps that would otherwise have stayed silent.
Open work — prioritised next slices
S0380.91 — u_party_wall Table 15 row 3 "Cavity masonry filled"
Highest-leverage remaining single-cause closure for cert 000565.
Per RdSAP 10 §5.10 Table 15 (PDF p.42) "U-values of party walls":
Party wall type U
Solid masonry / timber / system 0.0 Cavity masonry unfilled 0.5 Cavity masonry filled 0.2 ← cert 000565 Ext1 lodges CF Unknown, house 0.25 Unknown, flat / maisonette 0.0
The current u_party_wall at
domain/sap10_ml/rdsap_uvalues.py:1022
does not have a CF (Cavity masonry filled) branch — both CU (unfilled)
and CF (filled) currently route to U=0.5. Spec value for CF is 0.2.
Cert 000565 Ext1 lodges Party Wall Type CF Cavity masonry filled;
the cascade over-counts party_wall by (0.5 - 0.2) × Ext1_party_area ≈ +28 W/K. At the cascade rate of 33.5 kWh per W/K, this maps to
**+1000 kWh of the remaining +1460 SH residual**.
The existing mapper at datatypes/epc/domain/mapper.py:2196 already
maps "CF" → SAP10 code 4 (Cavity) — same as CU — and the comment
already flags this as a known approximation since S0380.64:
CF: 4, # Cavity masonry filled (cert 000565 Ext1) — RdSAP 10
Table 15 row 3 spec U=0.20. The cascade's
u_party_wallonly returns 0.0 / 0.5 / 0.25 for code 4 today, so CF
rounds up to the conservative cavity-unfilled U=0.5 —
matches the existing `_API_PARTY_WALL_CONSTRUCTION_TO
_SAP10[3]` approximation until u_party_wall gains the
filled-cavity branch (TODO).
Slice span:
-
Need a way to distinguish CF from CU in
u_party_wall— currently the function takes a singleparty_wall_constructionint. The Elmhurst mapper collapses both to code 4. Need either:- New
party_wall_insulation_typeparameter (filled / unfilled) - Or a new wall_construction int specifically for CF (e.g. 11)
- Or a separate cert-side "party_wall_filled: bool" field
The cleanest is option 2 (new wall_construction int) since it parallels the existing WALL_CAVITY=4 convention.
- New
-
Cohort + golden audit for party-wall CF lodgings — only cert 000565 Ext1 in the cohort has CF; golden API enum has its own party_wall _construction enum at
_API_PARTY_WALL_CONSTRUCTION_TO_SAP10[3]which is also CF-aware per the comment (currently rounds to U=0.5).
Expected outcome: cert 000565 SH residual +1460 → ~+460 kWh; sap_score 27 → 28 (or 29 depending on continuous SAP rounding).
S0380.92+ — remaining smaller residuals
After S0380.91 the cascade should be within ~+500 kWh SH. Open work-items per project-cert-000565-recovery-state memory:
- Ventilation infiltration +27 W/K over worksheet (~+900 kWh SH) — RdSAP 10 §5.15 / SAP 10.2 §3 line refs (24)..(25)
- Doors 0 vs worksheet ~21 W/K (cascade missing doors entirely?)
- Lighting +2.19 / pumps_fans +2.48 (sub-spec / MEV PCDB gap)
Deferred (unchanged from earlier handovers)
- MEV PCDB Table 4f component for pumps_fans +2.5 (blocked on external data acquisition)
- HP SAP code → main_heating_category=4 mapper extension
- 12 gas-combi PV certs at +0.5..+1.6 PE (no worksheets)
- 5 SAP-residual API-only certs (no worksheets)
How to run the baseline
PYTHONPATH=/workspaces/model python -m pytest \
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py \
backend/documents_parser/tests/test_elmhurst_extractor.py \
backend/documents_parser/tests/test_elmhurst_end_to_end.py \
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
domain/sap10_calculator/worksheet/tests/test_appendix_h_solar.py \
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \
domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
--no-cov -q
Expected: 574 pass + 9 expected test_sap_result_pin[000565-*] fails.
The 9 expected fails (verbatim):
sap_score
sap_score_continuous
ecf
total_fuel_cost_gbp
co2_kg_per_yr
space_heating_kwh_per_yr
main_heating_fuel_kwh_per_yr
lighting_kwh_per_yr
pumps_fans_kwh_per_yr
Files touched this session
| File | Slices | Change |
|---|---|---|
backend/documents_parser/elmhurst_extractor.py |
.85 | Curtain Wall Age extraction in _wall_details_from_lines |
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py |
.85, .86 | 5 new tests (extractor + mapper + cascade pins) |
datatypes/epc/surveys/elmhurst_site_notes.py:WallDetails |
.85 | curtain_wall_age: Optional[str] = None |
datatypes/epc/domain/epc_property_data.py |
.85, .86 | SapBuildingPart.curtain_wall_age; SapAlternativeWall.wall_thickness_mm |
datatypes/epc/domain/mapper.py |
.85, .86 | Plumb new fields through _map_elmhurst_building_part + _map_elmhurst_alternative_wall (also rename wall_insulation_thickness mis-route → wall_thickness_mm) |
domain/sap10_ml/rdsap_uvalues.py |
.85, .86 | _u_curtain_wall helper (§5.18); _u_stone_thin_wall_age_a_to_e helper (§5.6); u_wall dispatch extended with both branches |
domain/sap10_calculator/worksheet/heat_transmission.py |
.85, .86 | Pass curtain_wall_age=part.curtain_wall_age and wall_thickness_mm=alt_wall.wall_thickness_mm to u_wall |
domain/sap10_calculator/rdsap/cert_to_inputs.py |
.87..90 | Full Table 4e GROUP 2 dispatch (.87) + full Groups 0-7 + strict raise (.88) + Table 4d screed-UFH bug fix + strict raise (.89) + 5 dispatch helpers strict-raise (.90); UnmappedSapCode import from shared module |
domain/sap10_calculator/exceptions.py |
.90 | NEW — UnmappedSapCode(ValueError) shared exception class |
domain/sap10_calculator/tables/table_12a.py |
.90 | tariff_from_meter_type strict-raise + digit-string int-cast fix |
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py |
.87..90 | 13 new tests across the strict-raise series |
domain/sap10_ml/tests/test_rdsap_uvalues.py |
.85, .86 | 8 new tests (Curtain Wall 3-test set + thin-wall stone 5-test set) |
Spec source quick-reference
- SAP 10.2 full specification:
domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf- Table 4d (p.170) — heat emitter responsiveness R
- Table 4e (p.171-174) — heating system controls (Groups 0-7)
- Table 9 (p.182) — heating periods + temperatures
- Table 9b (p.183) — off-period temperature reduction formula
- Table 11 (p.188) — secondary-heating fraction by main category
- Table 12c (p.193) — heat-network distribution loss factor
- Table M1 — PV overshading factor ZPV
- RdSAP 10 specification:
domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf- §5.6 Table 12 (p.40) — uninsulated stone wall thin-wall formula
- §5.8 + Table 14 (p.40-41) — dry-line + insulation R-value
- §5.10 Table 15 (p.42) — party-wall U-values (S0380.91 target)
- §5.18 (p.48) — curtain wall U-values
- §11.1 — PV pitch enum
- SAP 10.3 at
domain/sap10_calculator/docs/specs/sap-10-3-full-specification-2026-01-13.pdf: DO NOT reference (feedback-sap-10-2-only-never-10-3)
Memory updated this session
project_cert_000565_recovery_state— slice-by-slice closure table for .85..87 + SH-channel diagnosisreference_unmapped_sap_code— NEW memory documenting the strict-raise pattern + which dispatch sites are now closedproject_sap10_ml_deprecation— NEW memory recording the user-requested folder deprecation (any new cascade helpers should land underdomain/sap10_calculator/notdomain/sap10_ml/)MEMORY.md— index refreshed at HEAD9bfb8524