Model/domain/sap10_calculator/docs/HANDOVER_POST_S0380_159.md
Khalim Conn-Kowlessar 0261f446f0 docs: handover post S0380.156..159
Captures the per-line walk discipline used to close electric 2 + 5
across four slices (.156 Table 3 WHC=903 primary-loss, .157 Table 2b
note b) WHC=903 ×0.9, .158 Table 4f warm-air heating fans, .159
Table 4a Cat 7 R tariff-aware dispatch). Σ |ΔSAP_c| across the
25-variant heating-systems corpus dropped from 2.87 → 1.21 (58%
reduction). All variants now sit under 0.3 SAP.

Next-slice candidate: the 9-variant cluster at ±0.09..0.12 SAP
(electric 3/5/6/7/8/9 + sf 4/9/10/11) — uniform pattern suggesting
a shared shave-the-residual fix. Worth a per-line walk on one
cluster variant before accepting the prior "Elmhurst-vs-spec quirk"
framing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 22:30:59 +00:00

17 KiB
Raw Blame History

Handover — post Slices S0380.156..159

Branch: feature/per-cert-mapper-validation. HEAD fba45d11. Predecessor: HANDOVER_POST_S0380_155.md.

TL;DR

Four slices landed — three closed the electric 2 (Cat 5 warm-air ASHP code 524) cohort entry point, one closed the electric 5 (Cat 7 slimline storage code 402 + 18-hour tariff) entry point. All four came from the same per-line walk discipline: dump the worksheet section the residual landed in, identify the diverging line ref, look up the spec rule.

Slice Commit Spec rule closed
S0380.156 02092c80 SAP 10.2 Table 3 (p.160) zero-loss list — universal WHC=903 guard at top of _primary_loss_applies. Cat-4 HP branch was falsely returning True when WHC=903 means electric immersion (no primary circuit).
S0380.157 a2a4b682 SAP 10.2 Table 2b note b) (p.159) "×0.9 if separate DHW timing (boiler / warm-air / HP)". Companion WHC=903 guard at top of _separately_timed_dhw. Electric immersion is not in the verbatim system-type list.
S0380.158 8843df1b SAP 10.2 Table 4f (p.174) row "Warm air heating system fans" = SFP × 0.4 × V per footnote e default SFP = 1.5 W/(l/s). New _TABLE_4A_WARM_AIR_SAP_CODES frozenset (22 codes) + leaf helper.
S0380.159 fba45d11 SAP 10.2 Table 4a (p.166) Cat 7 R splits between Off-peak (codes 402/403 R=0.2) and 24-hour heating tariff (R=0.4). Per §12.4.3 the 18-hour tariff has 18h low-rate availability ≈ continuous charging → routes to the 24-hour Table 4a R sub-row for codes 402/403/405/406.

Extended handover suite at HEAD: 903 pass, 0 fail. Pyright net-zero (43 → 43). Σ |ΔSAP_c| across the 25-variant cohort: 2.87 → 1.21 (58% reduction across 4 slices over one session).

Disciplines reinforced this session

  1. Per-line walk → spec → fix. Every closure came from dumping the failing variant's worksheet line-by-line:

    • .156: cascade primary_loss_monthly_kwh_annual = 509.98 vs worksheet (59)m = 0 every month → Table 3 zero-loss line.
    • .157: cascade solar_storage_monthly_kwh_annual = 403.87 vs worksheet (56) = 448.73 → ratio = 0.9 exactly → Table 2b note b.
    • .158: cascade pumps_fans_kwh_per_yr = 0 vs worksheet (249) = 136.35 → Table 4f warm-air fans = 1.5 × 0.4 × 227.25.
    • .159: cascade T_living = 20.12 vs worksheet 19.6519 → all MIT inputs match → back-solve from Tsc formula isolates R as the only divergence → Table 4a Cat 7 24-hour sub-row.
  2. Companion WHC=903 fixes. S0380.156 and .157 both added a guard at the top of a predicate that already had logic for the same case lower down. The Cat-4 HP early-return was masking the downstream electric-immersion / electric-water checks. Pattern: when a predicate has a per-system-category early-return, the data-shape gate (here WHC=903) needs to come first.

  3. Spec docstring flagged the gap before it surfaced. S0380.159 closed a TODO already noted in the source — the existing _RESPONSIVENESS_BY_SAP_CODE dict comment said "promote to (sap_code, tariff) lookup when 24-hour fixture surfaces; until then the off-peak default applies (under-shoots R for the 24-hour case)." The 18-hour fixture surfaces in this corpus.

Current residual state at HEAD fba45d11

Cascade-OK tier (25 variants on pin grid)

Sorted by |ΔSAP_c|:

Variant ΔSAP_c Δcost ΔCO2 ΔPE Notes
oil 1 -0.0000 -0.00 +0.00 +0.00 EXACT
oil pcdb 1 +0.0000 +0.00 -0.00 +0.00 EXACT
oil pcdb 2 +0.0000 +0.00 -0.00 +0.00 EXACT
oil pcdb 3 +0.0000 +0.00 +0.00 -0.00 EXACT
electric 1 -0.0000 -0.00 +11.95 +48.66 SAP/cost exact, lighting-PE quirk
solid fuel 5 +0.0000 +0.00 +11.95 +48.66 same lighting-PE quirk
solid fuel 6 +0.0000 +0.00 +11.95 +48.66 same
solid fuel 7 -0.0000 +0.00 +11.95 +48.66 same
solid fuel 8 -0.0000 +0.00 +11.95 +48.66 same
solid fuel 2 -0.0000 -0.00 -93.10 -1027.51 SAP/cost exact, §12.4.4 blend artifact
solid fuel 3 -0.0000 -0.00 +0.00 -0.00 EXACT
pcdb 1 -0.0108 +£0.24 +1.33 +5.70 basically exact
gshp -0.0178 +£0.41 +7.06 +33.52 basically exact
ashp -0.0240 +£0.55 +7.33 +36.34 basically exact
solid fuel 4 +0.0850 -£1.96 -9.31 -5.78 cluster
solid fuel 11 +0.0912 -£2.10 +10.55 -0.74 cluster
electric 8 +0.0941 -£2.17 +7.92 +6.58 cluster
electric 7 +0.1017 -£2.34 +7.64 +3.10 cluster
electric 5 +0.1081 -£2.49 +7.30 +0.07 CLOSED in .159 — was -1.18
electric 6 +0.1081 -£2.49 +7.32 +0.16 cluster
solid fuel 9 +0.1072 -£2.47 +9.69 -5.07 cluster
electric 2 -0.1087 +£2.50 +16.54 +97.69 CLOSED in .156-.158 — was -0.46
solid fuel 10 +0.1134 -£2.61 +9.31 -13.91 cluster
electric 9 +0.1199 -£2.76 +6.82 -4.51 cluster
electric 3 +0.1215 -£2.80 +6.72 -5.99 cluster

Σ |ΔSAP_c| = 1.21 (was 2.87 at start of S0380.156). Σ |ΔCO2| = 267.67 kg (was ~310). Σ |ΔPE| = 1489.96 kWh (was ~2400 — driven by 5-variant lighting cluster + sf2).

Buckets:

  • EXACT (|Δ|<1e-4): 11/25 (44%)
  • basically exact (|Δ|<0.05): 3/25 (ashp/gshp/pcdb1)
  • mid (0.05..0.3): 11/25 — the 9-variant cluster (electric 3/5/6/7/8/9 + sf 4/9/10/11) + electric 2
  • big (>=0.3): 0/25 — all variants now under 0.3 SAP

Blocked tier (16 variants — MissingMainFuelType)

Unchanged. Community heating × 5, electric storage 11-14, no system, oil 2-6, pcdb 3.

Open fronts ranked by leverage

1. The 9-variant cluster — ±0.09..0.13 SAP / £2..£3 / +£7 CO2 / small PE

electric 3, electric 5, electric 6, electric 7, electric 8, electric 9, solid fuel 4, solid fuel 9, solid fuel 10, solid fuel 11 — plus similar magnitudes on electric 5 post-.159.

The pattern is uniform:

  • ΔSAP +0.085 .. +0.121 (always positive — cascade SAP higher than worksheet)
  • Δcost £1.96 .. £2.80 (cascade cost lower — under-charging by ~38 kWh × 7.41 p/kWh)
  • ΔCO2 +6.72 .. +10.55 kg (cascade over-emitting)
  • ΔPE 13.91 .. +6.58 kWh (small, both signs)

The cost gap is consistent with cascade SH demand being ~38 kWh lower than worksheet. Diagnostic from earlier probing on electric 3 / 6: cascade SH_demand ≈ 11050 vs worksheet (98c) ≈ 11088 (diff 38 kWh per variant).

Hypothesis (per feedback-spec-floor-skepticism — verify before accepting): prior handover ranked this cluster as "lowest leverage — probably Elmhurst-vs-spec PE/CO2 monthly factor pattern". After .159 closed electric 5 from ALSO being in this pattern, that hypothesis looks weaker — the same cluster shape may have a clean spec citation. Suggested probe:

  • Pick one cluster variant (electric 3 or solid fuel 4) and run a per-line walk against the worksheet (98c)/(211)/(255).
  • The £2.50 cost suggests 38 kWh SH demand difference. Where?
  • Check §8 step 10 Qheat = 0.024 × (Lm η·Gm) × nm line refs vs worksheet (98a). If cascade Qheat is off, look upstream at (97).
  • Closing this cluster would shave Σ|ΔSAP| from 1.21 → ~0.10 in one slice. Highest leverage today.

2. Electric 2 follow-up — 0.11 SAP / +£2.50 cost / +£16 CO2 / +£98 PE

Cascade now overshoots after .156-.158 wired the missing primary-loss / storage-loss / warm-air-fan components correctly. Likely a small upstream SH cascade gap (cascade SH demand +57 kWh over worksheet — Cat 5 warm-air HP specific). The +136 kWh warm-air fan electricity bills at 18-hour high rate → adds £18.64 + 18.54 kg CO2 + 204 kWh PE. The over-shoot of +£2.50 / +£16 CO2 / +£98 PE roughly matches the magnitude → suggests one of:

  • The warm-air fan should bill at a different rate (Table 12a fraction for ALL_OTHER_USES on 18-hour vs the Appendix F2 rate).
  • The fan power should be lower for Cat 5 HPs (different SFP than the default 1.5 W/(l/s)).
  • Some other small Cat 5 / warm-air HP specific rule.

Suggested probe: dump the worksheet (249) cost line carefully — worksheet shows 136.35 × 13.67 = £18.64. Cascade should compute the same. If Δcost ≠ +£18.64, then the fan kWh is right but the rate is not. If Δcost = +£18.64, the fan kWh shouldn't be there at all on this row (maybe it goes to a different (249)b line).

3. Lighting-only PE +48.66 cohort — 5 variants — DEFERRED

electric 1, solid fuel 5/6/7/8. SAP/cost EXACT; PE +48.66 / CO2 +11.95 from Elmhurst using Table 12 ANNUAL factor for off-peak HW immersion split. Spec Table 12d/12e header mandates MONTHLY factors. Closing it violates spec.

4. Mapper-extension unblocking (16 blocked variants)

  • Community heating × 5 — extend extractor for §14.1 block.
  • Electric storage 11-14 — extend _ELMHURST_MAIN_HEATING_EES_TO_FUEL_CODE for EES codes WEA, REA, OEA.
  • "No system" — spec-assumed direct electric.
  • Oil 2-6 — Table 4b non-oil liquid fuels (HVO/FAME/B30K/bioethanol).
  • pcdb 3 — "Bulk LPG" mapper dict gap.

Separate from cascade closure work. Each unblock = one mapper slice.

Slice history (this session)

Slice HEAD Scope
S0380.156 02092c80 SAP 10.2 Table 3 (PDF p.160) zero-loss list — universal WHC=903 guard at the top of _primary_loss_applies. New _WHC_ELECTRIC_IMMERSION: Final[int] = 903 constant. Pre-slice the Cat 4 HP branch returned True unconditionally when no PCDB record was lodged → electric 2 cascade falsely added ~510 kWh/yr primary loss. Closures electric 2: HW kWh 2849 → 2339, ΔSAP 0.46 → +0.81 (residual swung past — exposed offsetting bugs). Δcost +£10.56 → £18.71, ΔPE +443 → 162.
S0380.157 a2a4b682 SAP 10.2 Table 2b note b) (PDF p.159) — companion WHC=903 guard at top of _separately_timed_dhw. Pre-slice the Cat 4 HP branch fired before the existing _is_electric_water check could route to False; cascade applied ×0.9 to (53) Temperature Factor on an immersion-fed cylinder. Reuses _WHC_ELECTRIC_IMMERSION. Closures electric 2: storage loss 403.87 → 448.73 EXACT, HW kWh 2339 → 2384.12 EXACT match worksheet, ΔSAP +0.81 → +0.70.
S0380.158 8843df1b SAP 10.2 Table 4f (PDF p.174) row "Warm air heating system fans" = SFP × 0.4 × V per footnote e default SFP = 1.5 W/(l/s). New _TABLE_4A_WARM_AIR_SAP_CODES frozenset (22 codes: Cat 5 HPs 521/523-527 + Cat 9 warm-air 501-515/520) + leaf helper _table_4f_warm_air_heating_fans_kwh(main, dwelling_volume_m3, has_balanced_mv). Footnote-e balanced-MV omission via _has_balanced_mechanical_ventilation predicate. Closures electric 2: pumps_fans 0 → 136.35 EXACT, ΔSAP +0.70 → 0.11, Δcost £16.14 → +£2.50.
S0380.159 fba45d11 SAP 10.2 Table 4a (PDF p.166) Cat 7 R splits Off-peak vs 24-hour heating tariff sub-rows. Per §12.4.3 the 18-hour tariff has 18h low-rate availability ≈ continuous charging. New _CONTINUOUS_CHARGING_TARIFFS = {EIGHTEEN_HOUR, TWENTY_FOUR_HOUR} + _RESPONSIVENESS_24_HOUR_OVERRIDE_BY_SAP_CODE (codes 402/403/405/406). tariff: Optional[Tariff] parameter added to _responsiveness; threaded through both MIT cascade call sites. Closures electric 5: ΔSAP 1.18 → +0.11 (91% reduction), Δcost +£27.09 → £2.49, ΔPE +438 → +0.07 EXACT. Electric 5 now joins the 9-variant cluster pattern.

Standard slice workflow (unchanged)

  1. Read spec page + identify rule
  2. Probe one cluster 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. 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 fba45d11

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

Memories to load (in order)

project-heating-systems-corpus            # HEAD fba45d11
feedback-sap-10-2-only-never-10-3         # CRITICAL — never reference SAP 10.3
feedback-software-no-special-handling     # CRITICAL — apply spec uniformly
feedback-spec-floor-skepticism            # CUTS BOTH WAYS — skeptical of your OWN audit narrative
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-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.
  • Don't re-investigate Slices .91..159 — 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.
  • Don't try to close the lighting-PE +48.66 cluster — it's an Elmhurst-vs-spec quirk that closing would violate spec.
  • Don't form a spec hypothesis without per-line data — walk the worksheet line-by-line for the failing variant first.

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)
    • §7 (p.26) — Mean internal temperature
    • §9.2.4 (p.27) — Solid fuel boiler systems
    • §9.4.11 (p.30) — Boiler interlock: -5pp to BOTH SH and DHW
    • §9.4.19 (p.34-35) — Storage heater controls
    • §12.4.3 (p.36) — Electric tariff types (7-hour / 10-hour / 18-hour / 24-hour heating). Slice .159.
    • §12.4.4 (p.36-37) — Solid fuel back-boiler summer immersion. Slice .154.
    • §A.2.2 (~p.189) — Forced-secondary set
    • Appendix D §D2.1 (2) (p.57) — Eq D1 monthly water eff cascade
    • Appendix F2 (p.63) — 18-hour CPSU
    • Appendix N3 (p.107-109) — Heat pump DHW efficiency cascade
    • Table 2b (p.159) — Cylinder temperature factor + note b ×0.9 rule for boiler/warm-air/HP. Slice .157.
    • Table 3 (p.160) — Primary circuit loss; zero-loss list incl. electric immersion. Slices .152 / .153 / .156.
    • Table 4a (p.163-170) — heating systems + R splits between Off-peak and 24-hour heating tariff for Cat 7 electric storage. Slices .155 / .159.
    • Table 4b (p.168) — gas/liquid boilers seasonal efficiency
    • Table 4e (p.171-173) — heating system controls + temperature adjustment column. Group 4 storage controls 2401/2402/2403.
    • Table 4f (p.174) — pumps + fans (incl. warm-air row). Slice .158.
    • Tables 9 / 9a / 9b / 9c (p.182-184) — heating periods, MIT cascade, T_sc formula. Used in .159 back-solve.
    • 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
    • Table 12d/12e (p.195-196) — monthly variation in CO2/PE factors
    • Table 13 (p.197) — high-rate fraction for electric DHW
  • RdSAP 10 spec: RdSAP 10 Specification 10-06-2025.pdf
    • §4.1 Table 5 (p.28) — Ventilation parameters
    • §5 (p.29) — Floor infiltration spec rule
    • §10.11 Table 29 (p.56) — Heating/HW parameters
    • §19 Table 32 (p.95) — RdSAP10 fuel prices / CO2 / PE

Good luck.