Three slices landed: §4 storage+primary loss for community heating
(.174), §14.1 heating_controls_sap extraction (.175), Table 4b combi
sub-row dispatch (.176). Cohort moves from 36 EXACT + 5 pinned to 34
EXACT + 7 pinned — net 2 new full-EXACT closures (oil 3 + oil 4) +
2 reshape (CH1/CH3 SAP/cost EXACT, CO2/PE pinned on the (372)
electrical-distribution Elmhurst-factor mystery).
933 pass + 0 fail at HEAD 326066ee. Pyright net-zero.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
15 KiB
Handover — post Slices S0380.174..176
Branch: feature/per-cert-mapper-validation. HEAD 326066ee.
Predecessor: HANDOVER_POST_S0380_173.md.
TL;DR
Three slices landed on the heating-systems corpus:
.174closed §4 (62)m HW for all 5 community-heating variants; CH1 HW EXACT..175wired §14.1 Community Heating "Heating Controls SAP" into the mapper; CH1 + CH3 SAP / cost EXACT..176added Table 4b combi sub-row fall-through to the (61)m default gate; oil 3 + oil 4 FULLY EXACT on all four metrics.
41 variants total → 34 EXACT + 7 pinned (was 36 + 5 pre-.174).
Pinned count drops from 9 (in .173 handover) to 7 because oil 3 +
oil 4 fully closed; CH1 + CH3 reshape from "SAP/cost-pinned" to
"SAP/cost EXACT + CO2/PE pinned" pending the (372) electrical-
distribution Elmhurst factor mystery.
| Slice | HEAD | Scope |
|---|---|---|
| S0380.174 | 4876140a |
§4 storage + primary loss for community heating. SAP 10.2 §4 line 1482 "Heat networks": primary circuit loss for insulated pipework + cylinderstat applies. Table 2b note b verbatim system-type list ("boiler / warm air / heat pump") OMITS community heating — ×0.9 multiplier doesn't apply. Three changes: new _HEAT_NETWORK_PIPEWORK_INSULATION_FRACTION = 1.0, new branch in _primary_loss_applies (heat-network main + WHC ∈ {901, 902, 914} → True), new _table_2b_note_b_multiplier_applies predicate gating ×0.9 by system type. CH1 HW useful (62)m closes 2339.24 → 2658.01 EXACT vs ws; HW fuel kWh closes 3391.90 → 3854.12 EXACT; (65)m heat gains 793.51 → 1221.62 EXACT. Cost/SAP signs flipped — exposed pre-existing §7 MIT +0.46 K over-count. |
| S0380.175 | eda07d12 |
§14.1 Community Heating heating_controls_sap extraction. All 5 CH variants lodge "Heating Controls SAP: 2306" in §14.1 (bare 4-digit form), not in §14.0 "Main Heating Controls Sap" (which is empty for CH certs). Pre-slice mapper read only §14.0 → main_heating_control='' → cascade defaulted to control_type=2 (off-hours (7, 8)). Code 2306 (Table 4e Group 3) → control_type=3 (off-hours (9, 8)), which closes the +0.46 K MIT (92)m residual that .174 surfaced. Two changes: _elmhurst_sap_control_code accepts bare integer form, _map_elmhurst_sap_heating falls through to mh.community_heating.heating_controls_sap when §14.0 is empty. CH1 + CH3 ΔSAP_c -1.0572 → +0.0000 EXACT; Δcost +£24.36 → -£0.00 EXACT. |
| S0380.176 | 326066ee |
Table 4b combi sub-row dispatch for (61)m default. SAP 10.2 §4 line 7702 + Table 4b row names: codes 128/129/130 are explicit combi sub-rows ("Combi oil boiler, ..."). Pre-slice _table_3a_combi_loss_default_applies gated only on main_heating_category ∈ {1, 2, 3, 6}; Elmhurst mapper leaves the category None on Table 4b liquid-fuel boilers so the cascade fell through to (61)m=0. Added _TABLE_4B_COMBI_OR_CPSU_CODES fall-through (set already exists in symmetric _primary_loss_applies Table 4b branch — see .146). oil 3 + oil 4 ALL FOUR METRICS EXACT (ΔSAP +2.5863/+2.5603 → ±0.0000, Δcost -£62/-£57 → ±0.00, ΔPE -967/-885 → ±0.00). Cohort 9 → 7 pinned. |
Extended handover suite at HEAD: 933 pass + 1 skipped, 0 fail. Pyright net-zero on all affected files.
Current residual state at HEAD 326066ee
Cascade-OK tier (41 variants — all populated corpus folders)
34 variants EXACT on all four metrics (|ΔSAP| < 1e-3, |Δcost| < £0.01, |ΔCO2| < 0.1 kg, |ΔPE| < 0.1 kWh):
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 pcdb 1, oil pcdb 2, oil pcdb 3,
pcdb 1, pcdb 3,
solid fuel 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
7 variants pinned:
| Variant | SAP code | ΔSAP_c | Δcost | ΔCO2 | ΔPE | Closure driver |
|---|---|---|---|---|---|---|
| CH6 (CHP/Coal) | 302 | −7.49 | +£172.68 | −2939.67 | +7481.57 | DLF=1.0 P960 quirk + CHP credit |
| oil 6 (B30K) | 126 | +3.05 | −£69.79 | −240.66 | −1112.66 | -5pp interlock penalty on non-combi |
| no system | 699 | +1.18 | −£27.15 | −49.83 | −562.44 | §A.2.2 portable-electric defaults |
| CH4 (CHP/Oil) | 302 | +0.53 | −£12.16 | −4401.85 | +111.58 | SAP 302 CHP credit (CO2) |
| CH2 (CHP/Gas) | 302 | +0.53 | −£12.16 | −1435.09 | +1123.01 | SAP 302 CHP credit (CO2 + PE) |
| CH3 (HP/Elec) | 304 | +0.0000 | −£0.00 | −98.92 | −457.54 | (372) electrical-distribution CO2/PE + (367) HP scaling |
| CH1 (Boilers/Gas) | 301 | +0.0000 | −£0.00 | −23.60 | −208.23 | (372) electrical-distribution CO2/PE Elmhurst factor |
Blocked tier (0 variants)
Empty (since .170). _BLOCKED_BY_MISSING_MAIN_FUEL_TYPE tuple
in test_heating_systems_corpus.py remains empty; the parametrised
raise-test is pytest.mark.skipif'd.
Open fronts ranked by leverage
1. SAP 302 CHP CO2/PE credit cascade (3 variants — CH2, CH4, CH6)
Highest cohort leverage: closes ~−8 SAP-equivalent across CH6 + the big CO2 / PE residuals on CH2 / CH4 simultaneously. Spec block 13b PE (PDF p.153) + 12b CO2:
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: CHP overall eff 75%, heat-to-power ratio 2.0 → heat_eff 50% + electric_eff 25%; boiler eff 80%. Verified against CH2/CH4/CH6 worksheet (461)/(462) = 25% / 50% exactly.
Per-line walk caveat (unresolved). The Elmhurst worksheet (463) energy column = spec_formula × 0.8523 uniformly across non-CHP heat-network rows. The 0.8523 multiplier appears in CH1 (467) too. Mechanism unidentified; not RdSAP 10 / SAP 10.2 spec-derived. Walk the worksheet per-line before forming hypotheses.
Likely 2-3 slices: (a) CHP credit + boiler-side eff for SH; (b)
mirror for HW path; (c) the 0.8523 multiplier if load-bearing. The
.172 scaling helper already keys on
_HEAT_NETWORK_HEAT_SOURCE_EFFICIENCY — add 302 there with weighted
overall eff once the split formula is in place. The .173 predicate
_is_community_heating_hw_from_main also gates on table membership
and will pick up SAP 302 automatically.
2. oil 6 (B30K) −5pp interlock cascade (1 variant, ΔSAP +3.05)
Per-line walk in this session: cascade Eq D1 outputs (winter, summer) = (80, 68) → annual eff ~73% at Jan; ws (217)m Jan = 73.07. Cascade gives HW kWh 3823.38 vs ws 4099.59. Back-solving the worksheet: applying -5pp interlock penalty to BOTH winter and summer ((75, 63)) reproduces ws (217)m Jan = 73.07 EXACTLY.
Cascade eq_d1_interlock_penalty_pp is gated on no_interlock =
"cylinder thermostat absent". For oil 6 the cert lodges
cylinder_thermostat = 'Y' so cascade sets penalty=0. But the
worksheet applies -5pp anyway — likely a different gate for non-PCDB
Table 4b regular boilers vs PCDB Table 105 boilers.
Probable 1-slice fix: extend the interlock-penalty gate to fire for Table 4b non-combi boiler codes (124-127) when... [need spec citation on the exact rule — investigate SAP 10.2 §9.4.11 interlock conditions for Elmhurst's interpretation]. ΔSAP_c +3.05 → ±0.0000 expected; closes 4 metrics on a single variant.
3. "no system" §A.2.2 portable-electric defaults (1 variant, ΔSAP +1.18)
Carried over from .169. Cascade thinks dwelling more efficient than
worksheet. Probable §A.2.2 portable-electric defaults gap
(responsiveness / control / Table 11). Probably 1 slice.
4. CH1 / CH3 (372)/(472) electrical-distribution CO2/PE (deferred)
Worksheet (372) CO2 factor = 0.1994 (block 11a, rating cascade) and 0.2114 (block 11b, demand cascade). PE factor = 1.7591 / 2.1872. These don't match any Table 12 / 12d / 12e weighting I could derive from the SH (307) or (307)+(310) heating-demand monthly profile.
(313) annual = 0.01 × (307) only — confirmed across all 5 CH variants (NOT 0.01 × ((307)+(310)) as spec text says). Once a factor source is identified, cascade should add an electricity-for-heat-distribution contribution to CO2/PE for heat-network mains.
Deferred this session. Either reverse-engineer the Elmhurst formula from a wider set of variants or find BRE documentation on the (372) factor convention.
5. CH6 DLF=1.0 lodging in P960 (architectural — pinned forever)
P960 input lodges Distribution Loss: Two adjoining dwellings sharing a single heating system + Distribution Loss Value: 0.0 → ws (306)
= 1.0000. Summary lodges nothing distinguishing CH6 from CH4. Per
spec §C3.1 the manual-DLF override is legal but the Summary doesn't
carry it. Two paths: (a) extend extractor to surface §17 Additional
Information when Elmhurst eventually lodges it; (b) accept as pinned.
Recommendation: (b) — pin and document.
Critical discipline reinforced this session
Per-line walk worksheet → spec → fix. All three slices landed via per-line worksheet dumps confirming the spec rule before implementation:
.174probed ws (56)/(57)/(59) and back-solved p=1.0 + TF=0.6 from the spec literal..175traced the cascade's MIT divergence to control_type=2 vs expected 3, then back-solved from worksheet (89) util_rest = 0.9898- (90) T_rest = 16.11 confirming off-hours (9, 8).
.176ran the cascade_apply_water_efficiencytrace to findannual=1935.37(= (45)) being passed when ws uses (62) = 2535.37 (= (45) + (46) + (61)) — exposing the missing combi_loss.
Spec-floor skepticism cuts BOTH ways. .174's spec-correct fix
EXPOSED the §7 MIT bug that pre-slice offsetting bugs had masked. The
chain .174 → .175 followed feedback-software-no-special-handling:
apply spec-correct fix uniformly; the surfaced residual is the next
slice's target, not a regression.
Pin diagnoses before forming hypotheses. The (372) Elmhurst factor 0.1994 doesn't match any Table 12 derivation. Rather than guess, session pivoted to the next-tractable front (oil 3/4 combi loss) which closed cleanly. The (372) deferred entry documents what's known and what's tried.
Don't conflate main_heating_category and sap_main_heating_code.
Two .176 slices ago, similar Elmhurst mapper artifact: the FAME oil
boilers lodge sap_main_heating_code=128/129 but the mapper leaves
main_heating_category=None. Cascade dispatch helpers that gate on
either field must check BOTH. The _TABLE_4B_COMBI_OR_CPSU_CODES set
already existed for the symmetric _primary_loss_applies branch
(per .146); adding the same fall-through to
_table_3a_combi_loss_default_applies was a 4-line change with
exact closure on 8 metrics (oil 3 + oil 4 × SAP/cost/CO2/PE).
Standard slice workflow (unchanged)
- Read spec page + identify rule (or Elmhurst worksheet pattern)
- Probe one variant; verify diagnosis via monkey-patch / direct walk
- Write failing AAA test (literal
# Arrange / # Act / # Assert) - Implement helper / dispatch entry / mapper extension
- Re-pin affected variants (DO NOT widen tolerance)
- Run extended handover suite (command below)
- Pyright net-zero check (
git stash→ pyright →git stash pop→ pyright) - If mirroring Elmhurst against spec literal: add a row to
SAP_CALCULATOR.md §8 "Elmhurst-mirrored spec divergences". The ≥2-cert rule applies unless the new divergence shares its shape with an already-documented row. - Commit with spec citation +
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> - Update
project-heating-systems-corpus+MEMORY.mdindex
Test baseline at HEAD 326066ee
PYTHONPATH=/workspaces/model python -m pytest \
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py \
backend/documents_parser/tests/test_heating_systems_corpus.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_heat_transmission.py \
domain/sap10_calculator/worksheet/tests/test_internal_gains.py \
domain/sap10_calculator/worksheet/tests/test_solar_gains.py \
domain/sap10_calculator/worksheet/tests/test_dimensions.py \
domain/sap10_calculator/worksheet/tests/test_rating.py \
domain/sap10_calculator/worksheet/tests/test_ventilation.py \
domain/sap10_calculator/worksheet/tests/test_appendix_h_solar.py \
domain/sap10_calculator/worksheet/tests/test_mev.py \
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \
domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
domain/sap10_calculator/tests/test_pcdb_table_322_lookup.py \
domain/sap10_calculator/tests/test_pcdb_table_329_lookup.py \
domain/sap10_calculator/tests/test_table_12a.py \
--no-cov -q
Expected: 933 pass + 1 skipped, 0 fail.
Memories to load (in order)
project-heating-systems-corpus # HEAD 326066ee
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-golden-residuals-near-zero
feedback-one-e-minus-4-across-the-board
feedback-bigger-slices-for-uniform-work
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 — re-pin smaller or find the spec gap.
- Don't add empirical gates to keep cohort pins stable.
- Don't re-investigate Slices .91..176 — all settled.
- Don't add new helpers to
domain/sap10_ml/— on deprecation path. - Don't treat ΔSAP=0.07 as "closed" — target is <1e-4 vs worksheet.
- Don't form a spec hypothesis without per-line data. The (372) Elmhurst factor 0.1994 is unexplained; don't bake guesses into the cascade. Reverse-engineer with more variants first, or find BRE documentation.
- Don't conflate
main_heating_categoryandsap_main_heating_codein cascade gates. The Elmhurst mapper leavescategory=Noneon Table 4b liquid-fuel boilers; gates must check both fields.
Master doc
The canonical architecture + API + validation doc lives at
domain/sap10_calculator/docs/SAP_CALCULATOR.md
(§8.1 + §8.2 documented). If the (372) Elmhurst-factor mystery
resolves and the formula turns out to be an Elmhurst-vs-spec
divergence, add §8.3.