From d4817ccdc74ed144a6d7ed0ff2869c43f61b77cb Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 3 Jun 2026 17:05:58 +0000 Subject: [PATCH] docs: handover for closing golden cert 0240 to 1e-4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records why case 6 (worksheet-validated dual-oil archetype) did not close 0240's residual: 0240 is API-only with an INTEGER-rounded register target (PE 122, CO2 6.0), so 0 residual at 1e-4 is not well-posed without a worksheet. 0240's unvalidated path vs case 6 is the condensing-combi (code 130) + no-cylinder HW (Table 3a keep-hot 600 kWh) — case 6 used a regular boiler + cylinder. Recommends generating an exact-0240 worksheet (or a 'case 7' = case 6 with the combi swapped in) to get a 1e-4 target. Notes the lodged RHI water_heating 2842.82 already matches the cascade HW output exactly (HW demand is right; any residual is in efficiency). Co-Authored-By: Claude Opus 4.8 --- .../docs/HANDOVER_0240_CLOSURE.md | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 domain/sap10_calculator/docs/HANDOVER_0240_CLOSURE.md diff --git a/domain/sap10_calculator/docs/HANDOVER_0240_CLOSURE.md b/domain/sap10_calculator/docs/HANDOVER_0240_CLOSURE.md new file mode 100644 index 00000000..190f9593 --- /dev/null +++ b/domain/sap10_calculator/docs/HANDOVER_0240_CLOSURE.md @@ -0,0 +1,129 @@ +# Handover — closing golden cert 0240-0200-5706-2365-8010 to 1e-4 + +Point-in-time note. Start from [`AGENT_GUIDE.md`](AGENT_GUIDE.md) for methodology, +accuracy bar, and pipeline. This records the state of cert **0240** and the +concrete path to driving its residual to zero. + +- **Branch:** `feature/per-cert-mapper-validation` +- **HEAD:** `7344f600` (S0380.207). Confirm with `git rev-parse HEAD`. +- **Baseline:** `2372 passed, 1 skipped, 0 failed` (AGENT_GUIDE §4 suite command). + +--- + +## What the last session shipped (S0380.201–207) + +Closed **simulated case 6** (`001431_case6`) to 1e-4 on the full SapResult and +promoted it to an e2e fixture. It is the worksheet-backed proxy for 0240's +**dual-oil, different-parts** archetype (Main 1 rads/2106 + Main 2 UFH/2110, +51/49, 6 "Roof of Room" rooflights, no boiler interlock). Slices: + +| slice | spec | what | +|---|---|---| +| S0380.201 | Table 4f note c) | 2nd-main circulation pump → (231) | +| S0380.202 | Table 5a note a) | 2nd-main pump **gain** → (70) | +| S0380.203 | RdSAP §3.7 | "Roof of Room" rooflights deduct from the §3.10.1 RR residual → (30) | +| S0380.204 | extractor/mapper | capture Main 2's §14.1 emitter + control | +| S0380.205 | SAP 10.2 **p.186** | two-systems-different-parts MIT: weighted R + elsewhere two-control blend → (87)/(90)/(98c) | +| S0380.206 | Appendix D Eq D1 | Q_space = DHW boiler's own (204) share, not (202) → (219) | +| S0380.207 | test | promote case 6 to a full e2e fixture | + +**0240 was re-pinned at each step** (it shares the archetype) and its residual +improved on PE/CO2 but its SAP integer dropped 73→72. The boiler-interlock −5pp +the *previous* handover called the priority was already implemented — see +[[project_case6_interlock_already_done]]. + +--- + +## The 0240 problem — and why case 6 did NOT close it + +### ⚠️ Critical: 0240 is API-only and its register target is INTEGER-rounded + +0240 has **no worksheet**. The golden test pins the cascade against the lodged +EPC register: +- `energy_consumption_current = 122` — **an integer** (1 kWh/m² resolution). +- `co2_emissions_current = 6.0` — **1 d.p.** (tonnes). +- `current_energy_efficiency = None` — the SAP isn't even in the JSON + (`actual_sap=73` in the test was carried from the original lodgement). + +**You cannot drive 0240 to "0 residual" at 1e-4 against these.** The register +rounds PE to the nearest whole kWh/m², so any cascade value in `[121.5, 122.5)` +*is* 122, and the true (unrounded) Elmhurst value could sit anywhere in that band +— or itself carry residual vs the rounded lodgement. Matching a rounded integer +to 1e-4 is not a well-posed target. **The only 1e-4 ground truth is a worksheet** +(the per-line `(1)..(286)` Elmhurst output), which is exactly why case 5/6 were +generated. + +Current 0240 cascade vs lodged: **PE 123.8687 vs 122 (resid +1.8687)**, **CO2 +6.0907 vs 6.0 (+0.0907)**, SAP cont 72.39 (integer 72 vs lodged 73, resid −1). + +### Why case 6 didn't close 0240 + +Case 6 validated the dual-main **structure** (MIT p.186, pumps, rooflights, +Eq-D1 fraction). But 0240 differs in cert-specific features case 6 does **not** +exercise: + +| feature | case 6 (worksheet-validated) | **0240** (unvalidated) | +|---|---|---| +| SAP code | 127 regular oil boiler | **130 condensing oil combi** (Table 4b 82/73) | +| DHW path | regular boiler **+ 110 L cylinder** → primary/storage loss | **combi, NO cylinder** → Table 3a keep-hot **600 kWh** (`combi_loss`), primary_loss 0 | +| TFA | (case-6 dwelling) | **201.53 m²** (different fabric/dimensions) | +| PV | none | **none** (the golden note's "+ PV" is STALE — `solar_water_heating=N`, no PV field) | + +So 0240's *remaining* residual lives in the parts case 6 never touched — +**the condensing-combi (130) + no-cylinder HW path** and the cert's own fabric. +The combi Eq-D1 / Table 3a keep-hot path has never been pinned against a +worksheet in the dual-main context. + +### Partial ground truth already in the 0240 JSON + +The lodged `renewable_heat_incentive` block gives two deemed-demand figures: +- `water_heating = 2842.82` — **exactly equals** the cascade's §4 HW output + `(64)` (2842.82). So the **HW demand is right**; any HW residual is in the + *efficiency* (Eq D1 combi blend), not the demand. +- `space_heating_existing_dwelling = 13254.52` vs cascade `(98c)` 12760.9 — + differ ~494 kWh (~3.7%). RHI uses its own deemed methodology so this is **not** + a clean 1e-4 check, but it's a hint the space-heat demand or the combi figures + are worth scrutinising. + +--- + +## What to do next — generate the right example + +To close 0240 properly you need a **worksheet** that exercises its +combi-HW path. Two options, best first: + +1. **Exact 0240 replica worksheet (gold standard).** Re-enter 0240's lodged data + into Elmhurst and export the worksheet PDF. Then build a mapper-driven fixture + (mirror `_elmhurst_worksheet_001431_case6.py`) and pin every line `(1)..(286)` + at 1e-4. The first diverging line localises the residual exactly. This is the + only way to get a true 1e-4 target for 0240. + +2. **"Case 7" — case 6 with 0240's combi swapped in.** If generating an exact + 0240 replica is hard, generate a `001431` variant that changes case 6's + heating to **0240's**: + - **Condensing oil combi, SAP code 130** (not 127 regular boiler). + - **NO hot water cylinder** — combi instantaneous DHW → WHC 901, Table 3a/3b + combi keep-hot loss, no primary/storage loss. + - Keep the validated dual-main rads(2106)+UFH(2110) 51/49 + RR rooflights. + This pins the **combi Eq-D1 + Table 3 keep-hot** path (the biggest unvalidated + piece) against a worksheet. Whatever it reveals applies directly to 0240. + +**The single most important differentiator to change vs case 6: regular +boiler + cylinder → condensing combi (130) with no cylinder.** That is the one +HW path 0240 uses that has never seen a worksheet in this archetype. + +### Reframing the goal +If a worksheet is genuinely unavailable, "0 residual vs the lodged register" is +not achievable at 1e-4 (integer rounding). The realistic target then is the +**±0.5 SAP fallback** (AGENT_GUIDE §1) — and 0240's continuous SAP 72.39 vs +lodged 73 is ~0.6 off, just outside it. Closing that last 0.6 still requires +knowing the true (worksheet) value, so the worksheet is the unblocker either way. + +--- + +## Pointers +- Golden pin + full slice history: `tests/domain/sap10_calculator/rdsap/test_golden_fixtures.py` (cert `0240-0200-5706-2365-8010`, line ~83). +- Case-6 fixture to mirror: `tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_001431_case6.py` + its e2e pins in `test_e2e_elmhurst_sap_score.py::_FIXTURE_PINS["001431_case6"]`. +- Memories: [[project_case6_mit_two_system_rootcause]] (the p.186 MIT, now CLOSED), [[project_case6_interlock_already_done]], [[feedback-worksheet-not-api-reference]] (API matches the worksheet, not the lodged register), [[feedback-software-no-special-handling]]. +- Repro 0240: `EpcPropertyDataMapper.from_api_response(json.load(...0240.json))` → `cert_to_inputs` / `cert_to_demand_inputs` → `calculate_sap_from_inputs`. The §2.4 section helpers are UNFAITHFUL (skip the interlock penalty + two-system MIT params) — diagnose against the real `cert_to_inputs` cascade. +- Process: one slice = one commit, spec citation (page+line), `Co-Authored-By: Claude Opus 4.8 `. SAP 10.2 only. No tolerance widening. mapper.py + cert_to_inputs.py each carry 32 pre-existing pyright errors (baseline-compare with `git stash`).