Model/domain/sap10_calculator/docs/HANDOVER_POST_S0380_173.md
Khalim Conn-Kowlessar 6c2053afac docs: handover post S0380.170..173
Captures the 4-slice community-heating closure phase: blocked tier
emptied (.170), CHP cost split (.171), heat-network heat-source-eff
scaling (.172), WHC=901 HW main-fuel routing (.173).

Open fronts ranked: SAP 302 CHP credit cascade (3-variant cohort),
+£12 lighting/standing overage on CH1/CH3, oil 3/4/6 + no-system
follow-ups.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 12:13:34 +00:00

14 KiB
Raw Blame History

Handover — post Slices S0380.170..173

Branch: feature/per-cert-mapper-validation. HEAD e71987c2. Predecessor: HANDOVER_POST_S0380_169.md.

TL;DR

Four community-heating slices landed. The blocked tier emptied for the first time in the corpus's history (.170); cost cascade closed on the CHP cluster (.171); CO2/PE closed on non-CHP variants (.172, .173).

All 41 corpus variants now run end-to-end through the cascade: 36 EXACT + 9 pinned (oil 3/4/6 + no system + 5 community heating). The 5 community-heating variants carry forcing-function residuals scoped to specific Elmhurst-mirror divergences detailed below.

Slice HEAD Scope
S0380.170 9f0d23ad Community heating mapper unblock. New CommunityHeating dataclass on ElmhurstSiteNotes.main_heating; extractor _extract_community_heating() reads §14.1 Heat Source × Fuel Type. Mapper _resolve_community_heating_fuel_code(heat_source, fuel) dispatches per SAP 10.2 Table 12 (PDF p.189): Boilers+Gas→51, CHP→48, HP+Elec→41, Boilers+Oil→53, Boilers+Coal→54. All 5 variants unblocked; 5 forcing-function residuals pinned. Blocked tier tuple emptied.
S0380.171 a4b5f4e7 RdSAP 10 §C CHP heat-fraction cost split. New MainHeatingDetail.community_heating_chp_fraction + community_heating_boiler_fuel_type fields populated by the mapper for SAP code 302. _fuel_cost_gbp_per_kwh returns 0.35 × CHP_price + 0.65 × boiler_price when fields set. CH2/CH4 cost gap £104 → +£0.17 (essentially exact); SAP +4.50 → 0.008. CH6 regressed (-3.52 → -8.03 SAP) — spec-correct fix exposed cert-side DLF=1.0 quirk that only CH6 lodges (in P960 input data, not in Summary).
S0380.172 36d4bf87 Table 4a heat-network heat-source-eff CO2/PE factor scaling. New _HEAT_NETWORK_HEAT_SOURCE_EFFICIENCY dict (301→0.80, 304→3.00 per SAP 10.2 Table 4a PDF p.164). _heat_network_heat_source_efficiency_scaling(main) returns 1/eff. Wired into _main_heating_co2_factor_kg_per_kwh + _main_heating_primary_factor non-electric branches. CH1 CO2/PE -787/-3827 → -126/-967; CH3 CO2/PE +1614/+11879 → +473/+1749. SAP 302 excluded — converges with CHP credit in follow-up.
S0380.173 e71987c2 WHC=901 HW path inherits main fuel for community heating. New _is_community_heating_hw_from_main(epc) predicate (WHC ∈ {901,902,914} + heat-network main + SAP code in heat-source-eff table). _hot_water_fuel_cost_gbp_per_kwh gains inherit_main_for_community_heating kwarg; HW CO2/PE get top-level branches scaled by 1/heat_source_eff. CH1 PE 967 → 9 (essentially closed); CH3 PE +1749 → 387 (~78%); CH3 CO2 +473 → 86 (~82%). Cost/SAP signs flip on CH1/CH3 — HW matches worksheet exactly, exposing +£12 lighting/standing overage.

Extended handover suite at HEAD: 926 pass + 1 skipped, 0 fail. Pyright net-zero on affected files (32 → 32 across the 4 slices).

Current residual state at HEAD e71987c2

Cascade-OK tier (41 variants — all populated corpus folders)

36 variants EXACT (|Δ| < 1e-3) on all 4 metrics. 9 variants carry pinned non-zero residuals (forcing functions, ranked by total magnitude):

Variant ΔSAP_c Δcost ΔCO2 ΔPE Closure driver
CH6 (CHP/Coal) 8.03 +£185 2935 +7865 DLF=1.0 in P960 + CHP credit
CH4 (CHP/Oil) 0.008 +£0.17 4397 +495 CHP credit (CO2)
CH2 (CHP/Gas) 0.008 +£0.17 1430 +1506 CHP credit (CO2 + PE)
oil 6 (B30K) +3.05 £70 241 1113 Table 4b code 126 SH+HW kWh gap
oil 3 (FAME) +2.59 £62 15 967 Table 4b code 128 HW kWh gap
oil 4 (FAME) +2.56 £57 13 885 Table 4b code 129 HW kWh gap
CH3 (HP/Elec) 0.53 +£12 86 387 Lighting/standing + 0.8523 multiplier
CH1 (Boilers/Gas) 0.53 +£12 +52 9 Lighting/standing (PE essentially closed)
no system +1.18 £27 50 562 §A.2.2 portable-electric defaults

Blocked tier (0 variants)

Empty for the first time. All previously blocked variants (community heating 1/2/3/4/6, electric 11-14, oil 2-6, no system, pcdb 3) now cascade-execute. The _BLOCKED_BY_MISSING_MAIN_FUEL_TYPE tuple in test_heating_systems_corpus.py is empty; the parametrized raise-test is pytest.mark.skipif'd with reason "all blocked variants have been unblocked (latest: S0380.170)".

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 CH2 / CH4 / CH6 + their large CO2 / PE residuals simultaneously.

Per spec block 13b PE (PDF p.153) + 12b CO2:

Space heating from CHP    (307a) × 100 ÷ (462) = ...   (463)
less credit emissions     (307a)×(461) ÷ (462) = ...  (464)
Water heated by CHP       (310a) × 100 ÷ (462) = ...   (465)
less credit emissions     (310a)×(461) ÷ (462) = ...  (466)
Heat from heat source 2   [(307b)+(310b)] × 100 ÷
                          (467b) = ...                  (468)

Per RdSAP 10 §C (PDF p.58) 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 worksheet caveat. The Elmhurst worksheet (463) energy column = spec_formula × 0.8523 uniformly across non-CHP heat- network rows. This 0.8523 multiplier appears in CH1 (467) too (= spec (307+310) × 100/80 × 0.8523 → 16717.79 instead of 19614.94). Mechanism unidentified; not RdSAP 10 / SAP 10.2 spec-derived as far as the spec PDFs document. Do per-line walks before forming hypotheses per feedback-spec-floor-skepticism. This may need a SAP_CALCULATOR.md §8 row.

Implementation sketch: add CHP credit factor + boiler-fuel-code fields to MainHeatingDetail; 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.

Likely 2-3 slices: (a) CHP credit + boiler-side eff for SH; (b) mirror for HW path; (c) Elmhurst 0.8523 multiplier if it turns out to be load-bearing.

2. CH1 / CH3 lighting / standing overage (+£12 cost)

Surfaced by S0380.173 closing the HW path. Cascade cost matches worksheet exactly on SH + HW, leaves +£12 over on lighting + standing.

Probable mechanisms (in rank order):

  1. Standing charge double-count. Worksheet (351) = £120 for code 51 (heat-network). Cascade may also apply the Mains-gas standing even though water_heating_fuel still lodges code 26 → API code 1.
  2. Lighting kWh rate mismatch. Cascade uses other_fuel_cost_gbp_ per_kwh = 0.1367 (18-hour high) — verify against worksheet (350) = 282 × 0.1367.
  3. (313) electricity-for-heat-distribution kWh stream billed at wrong rate. Worksheet uses heat-network rate 4.24 for this; check cascade.

Probably 1 slice once diagnosed via per-line walk. Closes CH1 + CH3 fully.

3. CH6 DLF=1.0 lodging in P960 (cert-side architecture gap)

CH6 P960 input data lodges Distribution Loss: Two adjoining dwellings sharing a single heating system + Distribution Loss Value: 0.0, producing worksheet (306) = 1.0000. CH4 with the same §14 Summary shape lodges Distribution Loss: Calculated + Value: 1.5, producing (306) = 1.4500.

The DLF distinguisher is NOT in the Summary PDF — only the P960 worksheet input data block. The current architecture only reads Summary; routing through P960 inverts that.

Two paths forward:

  • (a) Extend the Elmhurst Summary extractor to look for any §17 Additional Information line — currently neither CH4 nor CH6 Summaries lodge anything here, but if Elmhurst adds it the gap closes.
  • (b) Accept CH6 as a pinned forcing function. Spec-correct cascade applies DLF=1.45 for age G per Table 12c; CH6's manual override (per spec §C3.1: "For design-stage SAP assessments, a DLF of >= 1 can be manually entered") is unmodelable without P960 access.

Recommend (b) — pin and document.

4. oil 3 / oil 4 (FAME) HW kWh gap

Carried over from S0380.168. ΔSAP +2.59/+2.56. Cascade HW kWh ~900 less than worksheet on FAME boilers (Table 4b codes 128/129). Per-line walk on _apply_water_efficiency vs (219)m. Probably 1 slice.

5. oil 6 (B30K) SH + HW kWh gap

Carried over from S0380.168. ΔSAP +3.05. Likely Table 4b code-126 path differs. Probably 1 slice.

6. "no system" §A.2.2 portable-electric defaults

Carried over from S0380.169. ΔSAP +1.18. Cascade thinks dwelling more efficient than worksheet. Probable §A.2.2 portable-electric defaults gap (responsiveness/control/Table 11). Probably 1 slice.

Critical discipline reinforced last session

Per-line walk worksheet → spec → fix. S0380.171 + .172 + .173 each landed via per-line worksheet dumps confirming the spec rule before implementation. S0380.173 in particular: probing CH3 HW factors revealed the cascade was billing HW at Mains-gas (Elmhurst §15.0 placeholder) rather than heat-network rate; per-line walk on worksheet (342) confirmed the fix direction.

Spec-floor skepticism cuts BOTH ways. S0380.171 was framed by the prior handover as a single closure for CH2/CH4/CH6 ("biggest leverage by spec-coherent grouping"). The actual implementation closed CH2/CH4 exactly but REGRESSED CH6 — exposing the cert-side DLF=1.0 quirk that was previously masked by offsetting bugs. Per feedback-software-no-special-handling applied uniformly; documented as forcing function rather than gated out.

Gate carefully across SH and HW paths. S0380.172 + .173 use the same _HEAT_NETWORK_HEAT_SOURCE_EFFICIENCY table to gate the heat-source-eff scaling. SAP 302 is intentionally absent — when the CHP credit slice lands, ADD 302 to that table and both SH (via .172's wiring) and HW (via .173's predicate) auto-activate.

Cost-side and CO2/PE-side need different efficiencies for heat networks. Cost uses heat-network unit price × network_input (metered at the dwelling boundary). CO2/PE uses Table 12 factor × fuel_input = network_input / heat_source_eff. The .172 + .173 scaling helpers express this by pre-scaling the Table 12 factor at lookup time, leaving the cost path unaffected.

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 (S0380.164 added §8.2 under this exception with a single-cert flag).
  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 e71987c2

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: 926 pass + 1 skipped, 0 fail.

Memories to load (in order)

project-heating-systems-corpus            # HEAD e71987c2
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..173 — 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 — walk the worksheet first. The Elmhurst 0.8523 multiplier on heat-network rows (CH1 (467), CH3 (467), CH2/CH4/CH6 (468)) is unexplained and may be load-bearing for the CHP credit slice.
  • Don't gate SH and HW paths separately. The .172 + .173 wiring shares _HEAT_NETWORK_HEAT_SOURCE_EFFICIENCY membership; adding SAP 302 to that table auto-activates both paths.

Master doc

The canonical architecture + API + validation doc lives at domain/sap10_calculator/docs/SAP_CALCULATOR.md (§8.1 + §8.2 documented). The next CHP-credit slice may add §8.3 if the Elmhurst 0.8523 multiplier or block-13b PE/CO2 line formulas turn out to diverge from spec literal.

Good luck.