Model/domain/sap10_calculator/docs/HANDOVER_POST_S0380_179.md
Khalim Conn-Kowlessar ba2d6e1cbb docs: handover post S0380.177..179 + CI/test-move infra
Captures the corpus state (36 EXACT + 5 pinned community-heating
variants), the SAP 302 CHP credit cluster as the highest-leverage
remaining front, the unresolved 0.8523 / 0.1994 worksheet-factor
mysteries to per-line-walk before hypothesising, and — importantly —
the new test layout (tests/domain/sap10_calculator/) that changes every
verification command.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 17:21:50 +00:00

9.5 KiB
Raw Permalink Blame History

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.

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.teststests.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):

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 <noreply@anthropic.com>.

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 (§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.