docs: handover post S0380.174..176

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>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-02 13:42:42 +00:00 committed by Jun-te Kim
parent ff80fb4b5c
commit a862111795

View file

@ -0,0 +1,277 @@
# Handover — post Slices S0380.174..176
Branch: `feature/per-cert-mapper-validation`. **HEAD `326066ee`**.
Predecessor: [`HANDOVER_POST_S0380_173.md`](HANDOVER_POST_S0380_173.md).
## TL;DR
Three slices landed on the heating-systems corpus:
- **`.174`** closed §4 (62)m HW for all 5 community-heating variants;
CH1 HW EXACT.
- **`.175`** wired §14.1 Community Heating "Heating Controls SAP" into
the mapper; CH1 + CH3 SAP / cost EXACT.
- **`.176`** added 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:
- `.174` probed ws (56)/(57)/(59) and back-solved p=1.0 + TF=0.6 from
the spec literal.
- `.175` traced 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).
- `.176` ran the cascade `_apply_water_efficiency` trace to find
`annual=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)
1. Read spec page + identify rule (or Elmhurst worksheet pattern)
2. Probe one variant; verify diagnosis via monkey-patch / direct walk
3. Write failing AAA test (literal `# Arrange / # Act / # Assert`)
4. Implement helper / dispatch entry / mapper extension
5. Re-pin affected variants (DO NOT widen tolerance)
6. Run extended handover suite (command below)
7. Pyright net-zero check (`git stash` → pyright → `git stash pop` → pyright)
8. 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.
9. Commit with spec citation + `Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>`
10. Update `project-heating-systems-corpus` + `MEMORY.md` index
## Test baseline at HEAD `326066ee`
```bash
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_category` and `sap_main_heating_code`
in cascade gates.** The Elmhurst mapper leaves `category=None` on
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`](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.
## Good luck.