From edb1e6b8923a548ed120b3cb1da56bc95799e678 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Sat, 30 May 2026 09:52:35 +0000 Subject: [PATCH] docs: handover + next-agent prompt post S0380.85..90 (BP main-wall closure + SH-channel discovery + strict-raise series) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../docs/HANDOVER_POST_S0380_90.md | 251 ++++++++++++++++++ .../docs/NEXT_AGENT_PROMPT_POST_S0380_90.md | 200 ++++++++++++++ 2 files changed, 451 insertions(+) create mode 100644 domain/sap10_calculator/docs/HANDOVER_POST_S0380_90.md create mode 100644 domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_90.md diff --git a/domain/sap10_calculator/docs/HANDOVER_POST_S0380_90.md b/domain/sap10_calculator/docs/HANDOVER_POST_S0380_90.md new file mode 100644 index 00000000..7d7f3096 --- /dev/null +++ b/domain/sap10_calculator/docs/HANDOVER_POST_S0380_90.md @@ -0,0 +1,251 @@ +# 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`](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: + + 1. `_control_type` (S0380.87 → .88) + 2. `_responsiveness` (S0380.89 — fixed bug) + 3. `_pv_pitch_deg` (S0380.90) + 4. `_pv_overshading_factor` (S0380.90) + 5. `tariff_from_meter_type` (S0380.90 — fixed bug) + 6. `_tariff_high_low_rates_p_per_kwh` (S0380.90) + 7. `_heat_network_dlf` (S0380.90) + 8. `_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`](../../sap10_ml/rdsap_uvalues.py) +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_wall` +> # only 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:** +1. Need a way to distinguish CF from CU in `u_party_wall` — currently + the function takes a single `party_wall_construction` int. The + Elmhurst mapper collapses both to code 4. Need either: + - New `party_wall_insulation_type` parameter (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. + +2. 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 + +```bash +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 diagnosis +- `reference_unmapped_sap_code` — **NEW** memory documenting the + strict-raise pattern + which dispatch sites are now closed +- `project_sap10_ml_deprecation` — **NEW** memory recording the + user-requested folder deprecation (any new cascade helpers should + land under `domain/sap10_calculator/` not `domain/sap10_ml/`) +- `MEMORY.md` — index refreshed at HEAD `9bfb8524` diff --git a/domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_90.md b/domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_90.md new file mode 100644 index 00000000..0a8a0cf8 --- /dev/null +++ b/domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_90.md @@ -0,0 +1,200 @@ +# Next-agent prompt — post S0380.85..90 + +Branch: `feature/per-cert-mapper-validation`. +HEAD: `9bfb8524`. + +Read these in order before any tool call: + +1. [`HANDOVER_POST_S0380_90.md`](HANDOVER_POST_S0380_90.md) — full state +2. [`HANDOVER_POST_S0380_84.md`](HANDOVER_POST_S0380_84.md) — predecessor (background) + +Also load these memories before starting: + +- `project_cert_000565_recovery_state` — slice history + per-pin state +- `reference_unmapped_sap_code` — calculator strict-raise pattern +- `project_sap10_ml_deprecation` — `domain/sap10_ml/` is marked for + migration; new cascade helpers should land under + `domain/sap10_calculator/` +- `feedback_sap_10_2_only_never_10_3` — **CRITICAL** — never reference + SAP 10.3 spec +- `feedback_spec_citation_in_commits` — quote spec text + page in + commit messages +- `feedback_verify_handover_claims` — verify spec citations + numeric + claims before implementing the prescribed fix +- `feedback_zero_error_strict` — pyright net-zero per touched file +- `feedback_commit_per_slice` — one slice = one commit +- `feedback_aaa_test_convention` — every new test uses literal + `# Arrange / # Act / # Assert` headers +- `feedback_e2e_validation_philosophy` — component pins at <1e-3; + SAP integer delta=0; no adaptive ceilings +- `feedback_no_misleading_insulation_type` — field names should reflect + what they semantically contain + +## State summary + +This session shipped **S0380.85..90** — six spec-cited slices that: + +1. Closed both named BP main-wall gaps (Curtain Wall §5.18 + thin-wall + stone §5.6/§5.8). Cascade walls 443 → 602.40 W/K (worksheet 604.07; + 0.27% residual). +2. **Surfaced and fixed the dominant SH-channel over-count** via + S0380.87 — HP control code 2207 silently routed to control type 2 + instead of spec type 3. SH residual closed +7924 → **+1460 kWh + (82%)**, sap_score 23 → 27. +3. **Forecloses the silent-fallback bug pattern across the calculator** + via S0380.88-90 strict-raise series. 8 cascade-dispatch sites now + raise `UnmappedSapCode` on unmapped codes instead of silently + defaulting. Two latent bugs were surfaced as a side-effect: + - emitter=2 (Underfloor in screed) was returning R=0.25 instead of + spec R=0.75 + - GOV.UK API digit-string `meter_type='2'` silently fell through to + STANDARD on 125 golden certs + +**Cert 000565 state at HEAD `9bfb8524`:** + +| Pin | Cascade | Worksheet | Δ | +|---|---:|---:|---:| +| sap_score | 27 | 29 | −2 | +| sap_score_continuous | 27.3534 | 28.5087 | −1.16 | +| space_heating_kwh | 60468.18 | 59008.35 | **+1460** | +| hot_water_kwh | 3755.03 | 3755.03 | ✓ 0 EXACT | +| (others) | various | various | downstream | + +## Recommended next slice — S0380.91 Party-wall CF 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 + +### Audit steps + +1. Read RdSAP 10 §5.10 Table 15 (PDF p.42) for the spec text + footnote. +2. Read `u_party_wall` at + [`domain/sap10_ml/rdsap_uvalues.py:1018`](../../sap10_ml/rdsap_uvalues.py). + Current signature takes `(party_wall_construction, is_flat=False)`. + For code 4 (Cavity) it returns U=0.5 (unfilled). No CF branch. +3. Read the comment at + [`datatypes/epc/domain/mapper.py:2196`](../../../datatypes/epc/domain/mapper.py) + (Elmhurst mapper) which already flags this as a known approximation + since S0380.64. CF currently routes to SAP10 code 4 (same as CU). +4. Probe cert 000565 Ext1's `party_wall_construction` value in the + built EPC — confirm it's lodged as code 4. + +### Suggested implementation + +The cleanest fix introduces a way to distinguish CF from CU. Options: + +**Option A** (recommended): new wall_construction int specifically for +CF — e.g. `WALL_CAVITY_FILLED_PARTY = 11`. Add to `u_party_wall` +dispatch returning 0.2. Update both Elmhurst mapper +(`_ELMHURST_PARTY_WALL_CODE_TO_SAP10["CF"]: 11`) and API mapper +(`_API_PARTY_WALL_CONSTRUCTION_TO_SAP10[3]: 11`). + +**Option B**: add a new optional parameter `is_cavity_filled: bool` to +`u_party_wall`. Plumb through `heat_transmission.py`. More invasive +but doesn't add a new code. + +Per [[reference-unmapped-sap-code]] strict-raise pattern, the new code +must be added to any party-wall dispatch dict (e.g. if other helpers +gate on `party_wall_construction`). + +### Slice span + +1. `domain/sap10_ml/rdsap_uvalues.py:u_party_wall` — add CF branch + (Option A: new code 11; Option B: new param) +2. `datatypes/epc/domain/mapper.py` — update both mapper dispatches + to route "CF" / API "Filled" to the new representation +3. New tests in + `domain/sap10_ml/tests/test_rdsap_uvalues.py` — + - `u_party_wall_cavity_masonry_filled_returns_0p2_per_rdsap_10_table_15_row_3` + - `u_party_wall_cavity_masonry_unfilled_returns_0p5_per_rdsap_10_table_15_row_2` +4. Cascade pin test in + `backend/documents_parser/tests/test_summary_pdf_mapper_chain.py` — + - `test_summary_000565_ext1_party_wall_cf_routes_to_u_value_0p2` + +### Expected outcome + +- cert 000565 cascade party_walls: 93.26 → ~65 W/K (worksheet 65.13) +- cert 000565 SH residual: +1460 → ~+460 kWh +- sap_score 27 → 28 (possibly 29 depending on continuous SAP rounding) +- Continuous SAP residual: −1.16 → ~−0.4 + +### Audit risk + +- Cohort + golden audit: only cert 000565 Ext1 lodges CF in the cohort. +- Need to scan corpus for `party_wall_construction` values 3 ("Filled + cavity" in API enum) on golden + corpus to catch any cert that + changes when CF→0.2. + +## Beyond S0380.91 — remaining cert 000565 work-items + +After S0380.91 the cert 000565 cascade should be within ~+500 kWh SH. +Open items: + +- Ventilation infiltration +27 W/K over worksheet (~+900 kWh SH) — + RdSAP 10 §5.15 / SAP 10.2 §3 line refs (24)..(25) +- Doors cascade missing entirely (cascade 0 vs worksheet ~21 W/K) — + check `door_count` plumbing for cert 000565 +- Lighting +2.19, pumps_fans +2.48 — sub-spec / MEV PCDB gap (deferred) + +## Standard workflow per slice + +1. Read SAP 10.2 / RdSAP 10 spec page for the change — quote it in commit +2. Probe current cascade output; identify exact spec-vs-cascade gap +3. Write failing test FIRST (AAA structure) +4. Implement helper / change +5. Verify test passes +6. Run full handover suite (command in handover) +7. Check pyright on touched files — net-zero from baseline +8. Commit with spec citation +9. Update relevant memory if state changed + +## How to run the baseline + +```bash +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**. + +After S0380.91 lands the expected fail count should drop (sap_score +likely closes to EXACT or Δ−1; SH residual closes by ~1000 kWh; other +downstream pins move with it). + +## What NOT to do + +- **Don't reference SAP 10.3** ([[feedback-sap-10-2-only-never-10-3]]). +- **Don't widen pin tolerances or xfail residual gaps** + ([[feedback-zero-error-strict]]). The 9 cert 000565 fails are the work queue. +- **Don't re-investigate Curtain Wall (§5.18), thin-wall stone (§5.6/§5.8), + control codes (Table 4e), emitter codes (Table 4d)**, or any of the + 6 strict-raise dispatches. All closed in S0380.85..90. +- **Don't add new helpers to `domain/sap10_ml/`** — that folder is on + the deprecation path per [[project-sap10_ml-deprecation]]. New cascade + helpers should land under `domain/sap10_calculator/`. +- **Don't chase the 12 gas-combi PV certs or the 5 SAP-residual certs + without worksheets** — user has explicitly de-prioritised. + +## Memory hygiene + +After the next slice, update: +- `project_cert_000565_recovery_state` — append S0380.91 closure + + refresh the open work-items table + +Good luck.