# Handover — post Slices S0380.138..140 Branch: `feature/per-cert-mapper-validation`. **HEAD `068088bc`**. Predecessor: [`HANDOVER_POST_S0380_137.md`](HANDOVER_POST_S0380_137.md). ## TL;DR Three slices landed on top of `3542186f` this session, all concentrated on the §10a fuel-cost + §4 cylinder-storage-loss cascades. Each slice surfaced 1-2 spec-citable bugs hidden behind silent default fallbacks; together they shifted ~24 corpus residuals substantially. | Slice | Commit | Scope | |---|---|---| | **S0380.138** | `a830e855` | New `_off_peak_low_rate_gbp_per_kwh(tariff)` helper routing every off-peak callsite (`_space_heating_fuel_cost_gbp_per_kwh`, `_hot_water_fuel_cost_gbp_per_kwh`, `_secondary_fuel_cost_gbp_per_kwh`, `_pv_dwelling_import_price_gbp_per_kwh`) through the per-tariff Table 32 low-rate (codes 31/33/35/40) instead of hardcoded `prices.e7_low_rate_p_per_kwh = 5.50`. Companion `_off_peak_low_rate_gbp_per_kwh_via_meter_heuristic` covers the Unknown-meter path. `PriceTable.e7_low_rate_p_per_kwh` field deleted (dead code). | | **S0380.139** | `c4db37db` | `_is_off_peak_meter` routed through canonical `tariff_from_meter_type` (the bare `"18 Hour"` lodging — all 41 corpus variants' surface form — is now recognised as off-peak; pre-slice only the long form `"off-peak 18 hour"` matched). Dead `_RDSAP_DEFINITELY_OFF_PEAK` frozenset deleted. | | **S0380.140** | `068088bc` | §4 (56)m cylinder storage loss cascade closed via two compounding fixes: (a) extractor parses §16 `"Cylinder thermostat (Already installed)"` recommendation line (previously only §15.1 "Cylinder Thermostat" label was checked, so `cylinder_thermostat=None` for every variant on property 001431); (b) `_separately_timed_dhw` now excludes electric immersion per SAP 10.2 Table 2b note b (which restricts the ×0.9 multiplier to "boiler systems, warm air systems and heat pump systems"). Combined, cascade TF closes from 0.702 → 0.60 (matching worksheet); HW kWh closes to ±1e-3 of worksheet (2384.116 vs 2384.12). | Extended handover suite at HEAD: **883 pass, 0 fail.** ## Current residual state at HEAD `068088bc` ### Cascade-OK tier (25 variants on pin grid) | Variant | ΔSAP_c | Δcost | ΔPE | |---|---:|---:|---:| | ashp | +0.24 | -£5.57 | -12 | | electric 1 | -0.06 | +£1.32 | +94 | | electric 2 | +0.47 | -£10.92 | +101 | | **electric 3** | **+2.55** | **-£58.65** | **-1122** | | electric 5 | +0.07 | -£1.72 | -161 | | **electric 6** | **+1.33** | **-£30.60** | **-563** | | **electric 7** | **+1.29** | **-£29.73** | **-498** | | electric 8 | -0.26 | +£5.92 | +126 | | electric 9 | -0.12 | +£2.72 | +91 | | gshp | +1.15 | -£26.48 | -455 | | **oil 1** | **+2.66** | **-£61.24** | **-1050** | | oil pcdb 1/2 | +0.42 | -£9.77 | -84 | | oil pcdb 3 | +1.16 | -£26.72 | -271 | | **pcdb 1** | **+6.95** | **-£157.61** | **-3135** | | **solid fuel 2** | **+2.55** | **-£60.79** | **-1211** | | **solid fuel 3** | **+1.24** | **-£28.31** | **-935** | | solid fuel 4-11 (×8) | -0.29..+0.10 | small | ±100 | ### Blocked tier (16 variants) Unchanged from previous handover — community heating × 5, electric storage 11/12/13/14, no system, oil 2-6, pcdb 3. ## Next-slice candidates ranked by leverage ### 1. **+2.5 SAP cluster (electric 3, oil 1, solid fuel 2)** — open, HETEROGENEOUS These three variants share `ΔSAP_c ≈ +2.55` with `Δcost ≈ −£60`, but **probing shows the residuals trace to DIFFERENT causes — not a single shared cascade gap**. Slice attempted in this thread, abandoned after diagnosis showed each variant needs a different fix: | variant | SAP code | SH+Sec demand gap | HW kWh gap | Driver | |---|---|---:|---:|---| | electric 3 | 401 | cascade UNDER by 1005 kWh | exact | §9 MIT for storage heaters | | oil 1 | 127 | cascade OVER by 104 kWh (small) | cascade UNDER by 854 kWh | HW efficiency 86% vs worksheet 65% | | solid fuel 2 | 158 | cascade OVER by 337 kWh | unclear (different lodging) | TBD | **Combined-R hypothesis tested and rejected.** SAP 10.2 §9b defines `effective_R = (1−sec_frac) × R_main + sec_frac × R_sec`, which the cascade is NOT applying (`mean_internal_temperature_section_from_cert` at L2543 and main orchestrator at L4490 both call `mean_internal_temperature_monthly` without passing `secondary_fraction`/`secondary_responsiveness`). A monkey-patch to inject these REGRESSES electric 3 (+2.55 → +3.17) because raising effective_R LOWERS cascade demand — the wrong direction. The cascade is "compensating by not using combined R" — fixing this in isolation will require fixing the demand undercount simultaneously. **Per-variant fix plans:** - **electric 3**: cascade total useful demand (10046 kWh) is ~10% below worksheet (11088 kWh). Not driven by R alone — even with combined-R fix it gets worse. Likely the cascade's Table 9 heating- hours-per-day for ctrl=3 storage heaters differs from worksheet (worksheet may assume 24-hr "always on" for off-peak storage; cascade uses the standard ctrl=3 pattern). - **oil 1**: cascade water_efficiency for Table 4b oil boiler (code 127, eff=84%) produces HW kWh 2785; worksheet 3639. Worksheet effective HW eff ≈ 65% (suggests summer/winter blend or different Appendix D path). Per SAP 10.2 Appendix D §D2.1 — the cascade may not apply the right summer-eff override for non-PCDB oil boilers. - **solid fuel 2**: anthracite (eff=65%). Cascade HW is 3599 kWh; worksheet HW likely lodged in a different line ref (the probe regex returned 0). Re-probe needed. **Recommended approach**: take these as 3 separate per-variant slices in a fresh session. Each is its own diagnosis. The "+2.5" magnitude similarity is coincidence, not signal. ### 2. **pcdb 1 PE -3135 (single variant, biggest residual)** — open Diagnosed during this session: cascade water_efficiency for PCDB boiler 716 (Potterton KOA 90/26) uses summer efficiency 53% → HW kWh 4269. Worksheet effective HW efficiency ≈ 34% → HW kWh 7064. The diff is +2794 kWh HW × 5.44 p/kWh = +£152 cost gap. **Likely root**: PCDB Appendix D §D2.1 Equation D1 monthly cascade isn't being invoked for this record. The record has both winter (65%) and summer (53%) but the cascade may default to summer scalar. The spec wants monthly weighted blend via Eq D1. **Suggested slice plan:** 1. Check why Eq D1 monthly cascade isn't firing for PCDB 716 2. Spec citation: SAP 10.2 Appendix D §D2.1 3. Fix the dispatch + re-pin pcdb 1 ### 3. **solid fuel 2/3 PE -935..-1211** — open Both anthracite. Same fuel + R as variants that closed (solid fuel 4-11 all ±170 PE). Distinct cause from #1 above. Different `main_heating_efficiency` per cert lodgement? Different `main_heating_control`? Per-variant probe required. ### 4. **Lighting/pumps rate 13.19 vs 13.67 on 18-hour** — uniform but tiny Worksheet bills lighting/pumps at 18-hour HIGH rate (Table 32 code 38 = 13.67 p/kWh). Cascade falls back to standard 13.19 because Table 12a Grid 2 has no EIGHTEEN_HOUR row. Fix: add `(OtherUse.ALL_OTHER_USES, Tariff.EIGHTEEN_HOUR): 1.0` to `_OTHER_USE_HIGH_RATE_FRACTION`. Empirical citation (no spec PDF verification). Impact: ~+£2 cost / -0.086 SAP per variant × 25 variants = uniform small shift. Doesn't strongly close any single variant but cleans up cohort-wide. ### 5. **Pumps overcount for electric storage** — wrong direction Cascade applies 130 kWh pumps default for any non-gas/non-HP main. Worksheet has 0 pumps for electric storage / underfloor / direct- acting (no wet pump). But the cascade is already UNDER-counting cost for these variants, so removing pumps would make residuals MORE negative. Defer until SH+Sec demand cluster (#1) closes. ### 6. **Community heating unblocking (5 variants)** — sizeable Extend extractor to capture §14.1 Community Heating block (heat-network codes 41-58). Each cert maps to a Table 32 heat-network code via the lodged heat source type. ### 7. **Electric storage unblocking (variants 11-14)** — small Extend `_ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE` for EES codes WEA, REA, OEA. ## Standard slice workflow 1. Read spec page + identify rule 2. Probe one cluster variant; verify diagnosis via monkey-patch 3. Write failing AAA test (literal `# Arrange / # Act / # Assert`) 4. Implement helper / dispatch entry / mapper extension 5. Re-pin affected variants 6. Run extended handover suite (command in previous handover) 7. Pyright net-zero check (`git stash` → pyright → `git stash pop` → pyright) 8. Commit with spec citation + `Co-Authored-By: Claude Opus 4.7 ` 9. Update `project-heating-systems-corpus` + `MEMORY.md` index ## Memories to load (in order) ``` project-heating-systems-corpus # HEAD 068088bc feedback-sap-10-2-only-never-10-3 # CRITICAL — never reference SAP 10.3 feedback-worksheet-not-api-reference feedback-spec-citation-in-commits feedback-verify-handover-claims feedback-zero-error-strict feedback-commit-per-slice feedback-aaa-test-convention feedback-e2e-validation-philosophy feedback-abs-diff-over-pytest-approx feedback-spec-floor-skepticism feedback-golden-residuals-near-zero feedback-one-e-minus-4-across-the-board reference-unmapped-sap-code reference-unmapped-api-code project-oil-price-spec-divergence ``` ## What NOT to do - **Don't reference SAP 10.3** — track 10.2 deliberately - **Don't widen pin tolerances** to make pins pass — re-pin smaller or find the spec gap - **Don't re-investigate Slices .91..140** — all settled - **Don't add new helpers to `domain/sap10_ml/`** — on deprecation path - **Don't accept "spec-precision floor" framing** without spec-citation verification (the +2.5 SAP cluster is NOT a precision floor; it's a diagnosable shared cascade gap) ## Spec source quick-reference All under `domain/sap10_calculator/docs/specs/`: - **SAP 10.2 full spec**: `sap-10-2-full-specification-2025-03-14.pdf` - **§4** (p.135-137) — water heating worksheet (45..65), Tables 2/2a/2b - **§9** (p.155+) — MIT calc, Tables 9/9a/9b - **Table 4a/4b/4d** — heating systems + emitter responsiveness - **Table 4f** (p.174) — pumps + fans - **Table 11** — secondary heating fraction by category - **Table 12** (p.191) — SAP rating fuel prices - **Table 12a** (p.191) — high/low-rate fraction by system × tariff - **RdSAP 10 spec**: `RdSAP 10 Specification 10-06-2025.pdf` - **§19 Table 32** (p.95) — RdSAP10 fuel prices / CO2 / PE ## Good luck.