Model/domain/sap10_calculator/docs/HANDOVER_POST_S0380_149.md
Khalim Conn-Kowlessar f20d96369f docs: handover post S0380.146..149
Captures the four slices that closed the oil-cohort Table 4f gap:
.146 primary loss for Table 4b regular boilers, .147 Eq D1 for
non-PCDB Table 4b, .148 liquid fuel boiler aux 100 kWh, .149
per-pump-age circulation + wet-boiler gate.

Documents the cohort-wide ~-£10/yr cost residual that S0380.149's
spec correctness exposed — the new next-slice front. Highlights the
user directive [[feedback-software-no-special-handling]] that
surfaced during S0380.147 and continued to apply through .149.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 09:18:38 +00:00

13 KiB
Raw Blame History

Handover — post Slices S0380.146..149

Branch: feature/per-cert-mapper-validation. HEAD 35ea664d. Predecessor: HANDOVER_POST_S0380_147.md.

TL;DR

Four slices landed on top of 1636cfbc (the predecessor handover commit). The session closed the oil cohort Table 4f auxiliary energy gap: oil 1 SAP +2.66 → +0.40, oil pcdb 3 SAP +1.16 → +0.39, pcdb 1 +0.57 → +0.50, oil pcdb 1/2 +0.42 → +0.36. Cascade HW fuel cascade is now exact at the worksheet line ref for oil 1 (3638.99 kWh/yr).

The session also applied spec-correct dispatch uniformly across the entire cohort per the user's mid-session directive (feedback-software-no-special-handling): "The software doesn't have special non-spec handling." This unmasked offsetting cascade gaps that the pre-fix _DEFAULT_PUMPS_FANS_KWH_PER_YR = 130 and _PUMPS_FANS_KWH_BY_MAIN_CATEGORY[2] = 160 hardcodes had been masking — solid fuel 2 regressed +2.64 → +3.15, electric storage cohort moved from ~zero to +0.45..+0.66 SAP, etc.

Slice Commit Scope
S0380.146 bd193e06 SAP 10.2 Table 3 row 1 — primary loss for Table 4b non-PCDB regular boilers + cylinder. New _TABLE_4B_COMBI_OR_CPSU_CODES zero-loss exclusion set.
S0380.147 7dceeff2 SAP 10.2 Appendix D §D2.1 (2) Eq D1 — wire monthly winter/summer cascade for non-PCDB Table 4b boilers. New tables/table_4b.py carries 41-row (winter, summer) dict verbatim from spec p.168. _apply_water_efficiency refactored to eq_d1_winter_summer_pct: Optional[tuple[float, float]].
S0380.148 1b1f45b6 SAP 10.2 Table 4f "Liquid fuel boiler flue fan and fuel pump" 100 kWh/yr — added for Main 1 + Main 2 per Note c). New is_liquid_fuel_code in tables/table_32.py.
S0380.149 35ea664d SAP 10.2 Table 4f circulation pump per pump age (41 / 165 / 115) + new _is_wet_boiler_main gate (Table 4a/4b code 101-141/151-161/191-196 + PCDB Table 322 + cat {1,2} fallback). Removes _PUMPS_FANS_KWH_BY_MAIN_CATEGORY + _DEFAULT_PUMPS_FANS_KWH_PER_YR. Mapper fix: "2012 or earlier" → int 1 (was silently 2).

Extended handover suite at HEAD: 892 pass, 0 fail. Pyright net-zero / net-improved.

Critical user directive (read first)

feedback-software-no-special-handling: "The software doesn't have special non-spec handling." The BRE-approved Elmhurst lodging software follows spec exactly. When a spec-correct fix shifts a cohort cert pin, the pre-fix near-zero state was masking offsetting cascade gaps — NOT a deliberate non-spec rule. Apply spec uniformly + re-pin + document the unmasked gap as a follow-up.

S0380.147 was initially scoped narrowly ("only fire Eq D1 when cylinder is present") to avoid shifting cert 0240/6035. The user pushed back; the cylinder gate was removed; cert 0240/6035 were re-pinned. Same discipline applies to S0380.149's broader cohort shift.

Current residual state at HEAD 35ea664d

Cascade-OK tier (25 variants on pin grid) — sorted by |ΔSAP_c|

Variant SAP code ΔSAP_c Δcost ΔPE Notes
ashp 214 +0.24 -£5.57 -12 (closed)
oil pcdb 1/2 (PCDB) +0.36 -£8.32 -67
oil pcdb 3 (PCDB) +0.39 -£8.91 -67
oil 1 127 +0.40 -£9.31 -71
solid fuel 4 633 +0.45 -£10.42 -107 room heater
electric 1 191 +0.45 -£10.42 -40 electric boiler
electric 8 409 +0.49 -£11.23 -71
solid fuel 11 634 +0.48 -£11.08 -92 room heater
pcdb 1 (PCDB) +0.50 -£11.10 -93
electric 7 408 +0.54 -£12.44 -84
solid fuel 6 160 +0.54 -£12.39 -90
solid fuel 9 636 +0.55 -£12.64 -104 room heater
electric 6 404 +0.57 -£13.24 -93
solid fuel 10 634 +0.58 -£13.45 -130 room heater
solid fuel 7 160 +0.60 -£14.07 -118
electric 9 421 +0.63 -£14.43 -105
electric 3 401 +0.66 -£15.13 -115
electric 5 402 -0.68 +£15.70 +339 regressed by .145
gshp 211 +1.15 -£26.48 -455 open
solid fuel 3 160 +1.83 -£42.19 -1069 PE outlier
solid fuel 2 158 +3.15 -£72.53 -1346 PE outlier (regressed by .149)
solid fuel 5 153 +0.34 -£7.93 -42
solid fuel 8 160 +0.43 -£9.89 -89
electric 2 524 -0.18 +£4.24 +393 warm-air ASHP
solid fuel 4 633 +0.45 -£10.42 -107 room heater

Σ |ΔSAP_c| across 25 variants ≈ 15.4 SAP points (was 10.7 pre- session — Note: appearance of "regression" is misleading because the pre-session pins on solid fuel + electric were masking offsetting bugs via the 130 kWh default. The new pins reflect the actual underlying cascade-vs-worksheet gap.)

Blocked tier (16 variants — MissingMainFuelType)

Unchanged from previous handover. Categories: community heating × 5, electric storage 11-14, no system, oil 2-6, pcdb 3.

Pattern observed across the cohort

After S0380.149's spec-correct dispatch, MANY variants share a ~-£10 to -£14 cost residual (cascade UNDER worksheet by ~£10-14). This is a cohort-wide signal: there's a systematic gap somewhere producing ~£10/yr of cost the cascade is missing. Candidates:

  • Space heating fuel kWh under-count: cascade SH useful kWh tends to be slightly above worksheet (e.g. oil 1 +87 kWh useful = +103 fuel = +£5.60 cost), but the cost residual is -£10 (cascade UNDER). So SH fuel isn't the driver of the under-count.
  • A possible (45) energy content or (62) HW demand under-count.
  • Standing charges (Table 12 footnote) — cascade may not be including off-peak / gas standing charges that the worksheet adds.
  • Table 4f component I'm still missing — keep-hot facility (600 kWh combi gas), warm-air heating fans (SFP × 0.4 × V), or solar HW pump on certs with solar.

This is the next-slice front: identify the cohort-wide cost deficit and close it.

Next-slice candidates ranked by leverage

1. Cohort-wide ~-£10/yr cost under-count — highest leverage

Affects ~15+ variants simultaneously. Probe a variant with high fidelity (oil 1, oil pcdb 1) line-by-line against the worksheet (240) SH cost / (247) HW cost / (249) pumps cost / (250) lighting cost / (251) standing charges → total (255). One of these line refs is under-counting.

2. solid fuel 2 +3.15 / 3 +1.83 PE outliers — anthracite

Both Table 4a codes 158/160. PE residuals -1346 / -1069 kWh/yr are huge. Likely Table 4b solid-fuel efficiency, Table 4f, or §9 anthracite-specific secondary fraction.

3. electric 5 -0.68 — still open from S0380.145 regression

Pre-S0380.145 was +0.07 (offsetting bugs). Post-slice with +0.4 K Table 4e adjustment applied: cascade now OVER worksheet SH by ~248 kWh. Likely §9 MIT calc for fan-assisted storage heater R=0.40 (code 402) OR Table 9b Tsc formula divergence.

4. gshp +1.15 — heat pump cascade

PCDB Table 362 dispatch. Separate from the boiler cohort.

5. Community heating unblocking (5 variants) — extractor work

Extend extractor to capture §14.1 Community Heating block (heat- network codes 41-58).

6. Electric storage unblocking (variants 11-14)

Extend _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE for EES codes WEA, REA, OEA.

7. Cert 0240 dual-main Q_space split

Cert 0240 has main_heating_fraction = 51%/49%. Spec Eq D1 says Q_space = (98c)m × (204) per Main 1's fraction. Cascade currently uses full (98c)m. Closing this might close the +£11 cost gap on cert 0240 too.

Important diagnostic findings from this session

  1. Cohort-wide spec correctness exposes the underlying cascade gaps. Pre-fix near-zero pins on solid fuel / electric were coincidental — the broken 130 kWh default cancelled real cascade gaps. Now that the pumps_fans dispatch is spec-correct, the cascade-vs-worksheet diff is visible for the first time.

  2. Pre-existing default fallbacks are landmines. The 130 kWh and 160 kWh hardcodes silently mis-classified ~25 cohort variants — each shift looked like a regression but was actually the truth becoming visible.

  3. PCDB-listed certs need a separate wet-boiler discriminator. sap_main_heating_code is None on PCDB-listed mains; the _is_wet_boiler_main helper had to add a Table 322 lookup to correctly identify them as wet.

  4. The "next oil property" pattern: focus on closing one variant at a time, but the spec fix typically applies cohort-wide. Two slices (one spec rule each) closed five oil variants together.

Standard slice workflow

  1. Read spec page + identify rule
  2. Probe one cluster variant; verify diagnosis via monkey-patch
  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; or stripping line numbers from diff to find genuinely new errors after a refactor)
  8. Commit with spec citation + Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
  9. Update project-heating-systems-corpus + MEMORY.md index

Test baseline at HEAD 35ea664d

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: 892 pass, 0 fail.

Memories to load (in order)

project-heating-systems-corpus            # HEAD 35ea664d
feedback-sap-10-2-only-never-10-3         # CRITICAL — never reference SAP 10.3
feedback-software-no-special-handling     # CRITICAL — apply spec uniformly, no empirical gates
feedback-worksheet-not-api-reference
feedback-spec-citation-in-commits
feedback-verify-handover-claims
feedback-zero-error-strict                # TARGET: ΔSAP_c < 1e-4 vs worksheet
feedback-commit-per-slice
feedback-aaa-test-convention
feedback-e2e-validation-philosophy
feedback-abs-diff-over-pytest-approx
feedback-spec-floor-skepticism
feedback-golden-residuals-near-zero
feedback-one-e-minus-4-across-the-board
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 when a spec rule clearly applies. The cohort-wide ~-£10 cost shift after S0380.149 is NOT a regression — it's spec correctness unmasking offsetting bugs. Don't reintroduce the 130 default to "fix" it.
  • Don't re-investigate Slices .91..149 — all settled
  • Don't add new helpers to domain/sap10_ml/ — on deprecation path; domain/sap10_calculator/tables/ is the canonical home
  • Don't treat ΔSAP=0.07 as "closed" — target is <1e-4 vs worksheet

Spec source quick-reference

All under domain/sap10_calculator/docs/specs/:

  • SAP 10.2 full spec: sap-10-2-full-specification-2025-03-14.pdf
    • §4 (p.135-137) — water heating worksheet (45..65)
    • §9 (p.155+) — MIT calc, Tables 9/9a/9b/9c
    • §9.4.11 (p.30) — Boiler interlock: -5pp to BOTH SH and DHW
    • §A.2.2 (~p.189) — Forced-secondary set
    • Table 3 (p.160) — Primary circuit loss; zero-loss list
    • Table 4a (p.163-170) — heating systems incl. R column
    • Table 4b (p.168) — gas/liquid boilers seasonal efficiency
    • Table 4c (p.169-170) — Efficiency adjustments
    • Table 4d (p.170) — heat-emitter R
    • Table 4e (p.170-173) — heating system controls + temp adjustment
    • Table 4f (p.174) — pumps + fans (S0380.148..149 territory)
    • Table 9c (p.184) — heating requirement (step 8 Table 4e adj)
    • Table 11 (p.188) — secondary heating fraction
    • Table 12 (p.191) — SAP rating fuel prices + standing charges
    • Table 12a (p.191) — high/low-rate fraction by system × tariff
    • Appendix D §D2.1 (2) (p.57) — Eq D1 monthly water eff cascade
  • RdSAP 10 spec: RdSAP 10 Specification 10-06-2025.pdf
    • §10.11 Table 29 (p.56) — Heating/HW parameters; inaccessible cylinder
    • §19 Table 32 (p.95) — RdSAP10 fuel prices / CO2 / PE

Good luck.