diff --git a/domain/sap10_calculator/docs/HANDOVER_POST_S0380_179.md b/domain/sap10_calculator/docs/HANDOVER_POST_S0380_179.md new file mode 100644 index 00000000..e13f4d56 --- /dev/null +++ b/domain/sap10_calculator/docs/HANDOVER_POST_S0380_179.md @@ -0,0 +1,172 @@ +# Handover — post Slices S0380.177..179 (+ infra/CI work) + +Branch: `feature/per-cert-mapper-validation`. **HEAD `af8e0d94`** +(post merge from main). Predecessor: +[`HANDOVER_POST_S0380_176.md`](HANDOVER_POST_S0380_176.md). + +## TL;DR + +The 41-variant heating-systems corpus is now **36 EXACT + 5 pinned**. +The only remaining residuals are the **5 community-heating (CH) variants** +— all `SAP code 302/301/304` heat-network systems. Everything else +(oil, electric, solid fuel, ASHP/GSHP, PCDB, "no system") is EXACT on +all four metrics (ΔSAP/Δcost/ΔCO2/ΔPE). + +Three closure slices + four infra changes landed this session: + +| Slice / change | HEAD | Scope | +|---|---|---| +| S0380.177 | `5276282d` | **oil 6 boiler interlock from room-thermostat absence.** Control code 2101 ("no thermostatic control of room temperature") ⇒ no room thermostat ⇒ per RdSAP 10 §3 NOT interlocked despite cylinderstat=Yes (P960 "Boiler Interlock: No") ⇒ SAP 10.2 Table 4c(2) −5pp Space+DHW. New `_BOILER_NO_ROOM_THERMOSTAT_CONTROL_CODES={2101,2102}`; `no_interlock` ORs room-thermostat absence with stored-HW cylinderstat absence; Space −5pp leg now fires for Table 4b non-PCDB boilers. | +| S0380.178 | `c054d712` | **oil 6 circulation pump ×1.3 for absent room thermostat.** SAP 10.2 Table 4f footnote a) (PDF p.175) "Multiply by 1.3 if room thermostat is absent" ⇒ 41 × 1.3 = 53.3 kWh = ws (230c). Closes oil 6 FULLY (same root cause as .177). | +| S0380.179 | `f2062a2f` | **RdSAP 10 §10.7 electric-immersion default for "no system".** Cert lodges water code 999 (NON) + "cylinder present: No", but §10.7 substitutes an electric immersion on a Table 28 row-1 110 L cylinder + Table 29 row-1 insulation. New `_apply_rdsap_no_water_heating_system_default(epc)` rebinds the epc at the top of `cert_to_inputs` when `water_heating_code==999`. One fix closed HW (−594 kWh storage loss) AND the downstream space residual (+228, a HW-gains→MIT artifact). Closes "no system" FULLY. | +| appliances+cooking | `2f039aeb` | Threaded `appliances_kwh_per_yr` + `cooking_kwh_per_yr` (Appendix L L13/L14/L16a + L20) onto `SapResult`/`CalculatorInputs` for ADR-0014 BillDerivation. **Output-only, zero rating drift.** | +| test fixes | `0e484aaa` | Fixed 11 pre-existing CI failures from an absorbed PR: `test_appendix_u.py` signature drift + mislabelled "SAP 10.3"→10.2; `test_table_32.py` re-pinned oil(4)=5.44 / FAME(73)=7.64 to the worksheet-canonical values the table actually uses. | +| corpus PDFs | `d1c87d84` | Committed the 82 heating-corpus PDF fixtures (`sap worksheets/heating systems examples/`) so CI can run the residual pins. | +| **test move** | `d7d5084f` | **Moved all 5 calculator test dirs → `tests/domain/sap10_calculator/`** so CI (which collects `tests/`) runs them. SEE "Test layout changed" below — it changes every command. | + +## ⚠ Test layout changed this session — commands are different now + +The calculator tests **moved** out of `domain/sap10_calculator/.../tests` +into `tests/domain/sap10_calculator/{,worksheet,rdsap,climate,validation}`. +Cross-imports were rewritten `domain.sap10_calculator.worksheet.tests` +→ `tests.domain.sap10_calculator.worksheet`. Any old handover command +that references `domain/sap10_calculator/worksheet/tests/...` is STALE. + +**New full verification command** (replaces the old extended suite): + +```bash +PYTHONPATH=/workspaces/model python -m pytest \ + tests/domain/sap10_calculator/ \ + backend/documents_parser/tests/ \ + --no-cov -q -p no:cacheprovider +``` + +Expected at HEAD: **~2221 pass, 1 skipped, 0 fail** (the 1 skip is the +corpus blocked-variant `skipif`). The cascade-pin / golden / e2e +conformance suites are all under `tests/domain/sap10_calculator/`. + +**Two gotchas:** +1. `load_cells` tests (`tests/domain/sap10_calculator/worksheet/test_{dimensions,ventilation,water_heating}.py`) pin against the gitignored `2026-05-19-17-18 RdSap10Worksheet.xlsx` at repo root. `_xlsx_loader.load_cells` `pytest.skip()`s when the xlsx is absent — so they run locally and skip in CI. If you're missing the xlsx locally, those skip (not fail). +2. **Uncommitted `pytest.ini` change** (came in with a main pull) REMOVES `tests/` + `domain/sap10_ml/tests` from `testpaths`. HEAD has them; the working tree strips them. This is NOT a slice change — confirm with the user before committing it, because removing `tests/` would un-collect the moved calculator tests. + +## Current residual state at HEAD `af8e0d94` + +### 36 variants EXACT (all four metrics < tolerance) + +``` +ashp, gshp, +electric 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, +oil 1, oil 2, oil 3, oil 4, oil 5, oil 6, oil pcdb 1, oil pcdb 2, oil pcdb 3, +pcdb 1, pcdb 3, +solid fuel 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +no system +``` + +### 5 community-heating variants pinned + +| Variant | SAP code | ΔSAP_c | Δcost | ΔCO2 | ΔPE | Closure driver | +|---|---:|---:|---:|---:|---:|---| +| CH6 (CHP/Coal) | 302 | −7.4942 | +£172.68 | −2939.67 | +7481.57 | SAP 302 CHP credit + DLF=1.0 P960 quirk | +| CH2 (CHP/Gas) | 302 | +0.5277 | −£12.16 | −1435.09 | +1123.01 | SAP 302 CHP credit (CO2 + PE) | +| CH4 (CHP/Oil) | 302 | +0.5277 | −£12.16 | −4401.85 | +111.58 | SAP 302 CHP credit (CO2) | +| CH3 (HP/Elec) | 304 | +0.0000 | −£0.00 | −98.92 | −457.54 | (372) electrical-distribution + HP COP | +| CH1 (Boilers/Gas) | 301 | +0.0000 | −£0.00 | −23.60 | −208.23 | (372) electrical-distribution factor | + +Blocked tier: **empty**. + +## Open fronts ranked by leverage + +### 1. SAP 302 CHP CO2/PE credit cascade (3 variants — CH2/CH4/CH6) — HIGHEST + +Closes the big CO2/PE residuals on CH2/CH4 AND the −7.49 SAP on CH6 +simultaneously. Spec: block 13b PE (PDF p.153) + 12b CO2 — the +displaced-electricity CHP credit lines (worksheet (363)-(366), +(464)/(466)/(468)): + +``` +Space heating from CHP (307a) × 100 ÷ (362) = ... (363) +less credit emissions −(307a)×(361) ÷ (362) = ... (364) +Water heated by CHP (310a) × 100 ÷ (362) = ... (365) +less credit emissions −(310a)×(361) ÷ (362) = ... (366) +Heat from heat source 2 [(307b)+(310b)] × 100 ÷ (467b) (468) +``` + +RdSAP 10 §C defaults (verified vs CH2/CH4/CH6 worksheet (461)/(462)): +CHP overall eff 75%, heat-to-power 2.0 → heat_eff 50% / electric_eff +25%; boiler eff 80%. The `.172` scaling helper already keys on +`_HEAT_NETWORK_HEAT_SOURCE_EFFICIENCY` — add code 302 there once the +split formula is in place; the `.173` predicate +`_is_community_heating_hw_from_main` auto-activates. + +**⚠ UNRESOLVED per-line caveat — walk before hypothesising.** The +Elmhurst worksheet (463) energy column = `spec_formula × 0.8523` +uniformly across non-CHP heat-network rows (the 0.8523 also shows in +CH1 (467)). It is NOT RdSAP 10 / SAP 10.2 spec-derived. Per +[[feedback-spec-floor-skepticism]] / [[feedback-software-no-special-handling]], +DUMP the worksheet per-line and reconcile 0.8523 before baking any CHP +formula into the cascade. Likely 2-3 slices. + +### 2. CH1/CH3 (372)/(472) electrical-distribution CO2/PE — DEFERRED + +CH1/CH3 are SAP + cost EXACT; only CO2/PE remain. Worksheet (372) CO2 +factor = 0.1994 (block 11a) / 0.2114 (block 11b); PE = 1.7591 / 2.1872. +These don't match ANY Table 12 / 12d / 12e weighting derivable from the +(307) or (307)+(310) heating-demand monthly profile. (313) annual = +0.01 × (307) ONLY (verified across 5 variants, NOT 0.01 × (307+310) as +the spec text says). **Don't guess** — reverse-engineer the 0.1994 +factor from a wider variant set or find BRE documentation first. + +### 3. CH6 DLF=1.0 P960 quirk — architectural, likely pin-forever + +P960 input lodges `Distribution Loss: Two adjoining dwellings...` + +`Distribution Loss Value: 0.0` → ws (306) = 1.0000, but the Summary +doesn't carry anything distinguishing CH6 from CH4. Per §C3.1 the +manual-DLF override is legal but not surfaced by the Summary. +Recommendation: pin + document once the CHP credit lands. + +## Discipline (carried from every prior handover) + +- **Per-line walk worksheet → spec → fix.** All 3 slices this session + landed via per-line P960 dumps. Don't form a spec hypothesis without + per-line data (the 0.8523 + 0.1994 factors are the live examples). +- **Spec-floor skepticism cuts BOTH ways** — a spec-correct fix often + EXPOSES the next residual (oil 6 .177→.178; "no system" HW→space). + Apply the spec uniformly; the surfaced residual is the next target. +- **SAP 10.2 ONLY, never 10.3.** +- **Don't conflate `main_heating_category` and `sap_main_heating_code`** + — the Elmhurst mapper leaves `category=None` on Table 4b liquid-fuel + boilers; cascade gates must check both. +- **Target is < 1e-4 vs worksheet** — ΔSAP=0.07 is NOT closed. Re-pin + smaller; never widen tolerance, never xfail. +- **One slice = one commit**, spec citation in the message, trailer + `Co-Authored-By: Claude Opus 4.8 `. + +## Memories to load (in order) + +``` +project-heating-systems-corpus # HEAD af8e0d94, 36 EXACT + 5 pinned +feedback-sap-10-2-only-never-10-3 +feedback-software-no-special-handling +feedback-spec-floor-skepticism +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-one-e-minus-4-across-the-board +reference-unmapped-sap-code +reference-unmapped-api-code +project-oil-price-spec-divergence +``` + +## Master doc + +Architecture + API + validation: [`SAP_CALCULATOR.md`](SAP_CALCULATOR.md) +(§8 "Elmhurst-mirrored spec divergences" carries .163 HW dual-rate +annual + .164 §12.4.4 summer-immersion). If the CHP 0.8523 multiplier +resolves to an Elmhurst-vs-spec divergence, add §8.3. + +## Good luck.