Model/domain/sap10_calculator/docs/HANDOVER_POST_S0380_84.md
Khalim Conn-Kowlessar b0fef688da docs: handover + next-agent prompt post S0380.81..84 (Table 32 default + Table 12a Grid 2 CO2 + RR fold-in)
Captures the 4-slice arc this session — Table 32 default prices (sap_score
28 → 29 EXACT), Table 12a Grid 2 dual-rate CO2 (CO2 65% closed), extractor
gable_type recognition + mapper preservation of cert 9501, and the full
RR mapper + cascade fix per RdSAP 10 §3.9.2 + §3.10 + Table 4 (11
per-BP RR surface areas EXACT vs worksheet PDF).

Documents the BP main-wall residual −161 W/K diagnostic localising the
next slice block to two spec-cited cascade gaps: Curtain Wall (−112
W/K, no _ENG_WALL entry for WALL_CURTAIN=9) + thin-wall stone granite
alt (−47 W/K, _insulation_bucket short-circuit + thin-wall §6.6/§6.7
formula). Each spans extractor → datatype → mapper → cascade and is a
single coherent slice when the spec lookup is tractable.

NEXT_AGENT_PROMPT_POST_S0380_84.md prescribes S0380.85 (Curtain Wall)
as the highest-leverage next slice with audit + implementation +
expected-outcome details.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 23:33:18 +00:00

16 KiB
Raw Blame History

Handover — post S0380.81..84 (Table 32 default + Table 12a Grid 2 CO2 + RR fold-in)

Branch: feature/per-cert-mapper-validation. HEAD 49622f55. Predecessor: HANDOVER_POST_S0380_80.md.

Slices committed this session (S0380.81..84)

Four spec-cited slices, two clean closures + one extractor data-completion

  • one structural RR fix that surfaces the next named gap.
Slice Commit Spec Cert 000565 outcome
S0380.81 9338914f RdSAP 10 §19.1 (PDF p.80-81) — "use Table 32 prices (not Table 12) for §10a/§10b" sap_score 28 → 29 EXACT at the (28.5) rounding boundary. Cost residual £+3.62 → £2.39.
S0380.82 27ead127 SAP 10.2 Table 12a Grid 2 (p.191) + Table 12d/12e (p.194-195) — "All other uses" off-peak dual-rate CO2 residual 8.92 → 3.08 kg/yr (65% closed). Lighting + pumps_fans + electric_shower CO2/PE factors now blend Table 12d/12e high-rate × low-rate codes per Grid 2 fraction on off-peak certs.
S0380.83 ed8fdc6a RdSAP 10 §3.10 + Summary PDF §8.1 schema Extractor recognises "Exposed" + "Connected" gable_type (was Party / Sheltered / "Connected to heated space" only). Mapper elif extended to preserve cert 9501. Pure data-extraction completion; zero cascade impact.
S0380.84 49622f55 RdSAP 10 §3.9.2 + §3.10 + Table 4 (p.22) Mapper drops Connected gables, routes Exposed → gable_wall_external with lodged U, surfaces Common Walls, applies spec area formula L × (0.25 + H) and Σ per-common-wall gable correction. Cascade adds common_wall kind handler. 11 per-BP RR surface areas EXACT vs worksheet PDF. Cascade walls 322 → 443 W/K (43% closer to worksheet 604); party 153 → 93 (68% closer to worksheet 65). cert 000565 sap_score temporarily regressed 29 → 26 — see §"Why the regression is the correct signal" below.

Test baseline at HEAD 49622f55: 555 pass + 9 expected test_sap_result_pin[000565-*] cascade-gap fails. Pyright net-zero on every touched file. Cohort + golden + cert 9501 unaffected.

Cert 000565 state (HEAD 49622f55)

Pin Cascade Worksheet Δ Cause
sap_score (int) 26 29 3 RR fold-in (S0380.84) exposed BP main-wall gap; see §"BP main-wall residual 161 W/K diagnostic"
sap_score_continuous 26.4972 28.5087 2.01 Downstream of HTC over-count via space_heating +2591
ecf 5.5970 5.3866 +0.21 Downstream of cost +£182.6
total_fuel_cost_gbp 4862.88 4680.26 +182.6 Downstream of space_heating fuel cost
co2_kg_per_yr 6684.52 6447.63 +236.9 Downstream of HP electricity over-fuel-use
space_heating_kwh 61599.61 59008.35 +2591.3 BP main-wall cascade gap (Curtain Wall 112 W/K + thin-wall alt 47 W/K — see diagnostic below)
main_heating_fuel 36235.07 34710.79 +1524.3 Follows space_heating via 1/COP
hot_water_kwh 3755.03 3755.03 ✓ 0 EXACT §4 cascade fully closed (S0380.77..80)
lighting 1387.02 1384.84 +2.19 Sub-spec
pumps_fans 255.00 252.52 +2.48 MEV PCDB record missing (external data)

§4 HW cascade line refs (all EXACT, unchanged)

Line Cascade Worksheet Δ
(45)m sum energy_content 1286.3266 1286.3266 ✓ 0
(46)m sum distribution_loss 192.9490 192.95 ✓ <1e-3
(57)m sum solar_storage 596.9725 596.9725 ✓ <1e-4
(59)m sum primary_loss 1176.77 1174.79 +1.98
(61)m combi_loss 0.00 0.00 ✓ 0
(62)m sum total_demand 3060.07 3060.07 ✓ <1e-3
(64)m sum (after solar) 2778.72 2778.7213 ✓ <1e-4
(64a)m electric shower 702.94 702.94 ✓ <1e-4
(217)m water-heater eff 0.74 0.74 ✓ EXACT
(219) HW fuel kWh 3755.03 3755.0288 ✓ <1e-3

Per-BP RR surface areas (all EXACT after S0380.84)

Verified against the cert 000565 U985 worksheet "External Walls" + "Party Walls" sections at 4 d.p. precision:

BP Surface Spec formula Worksheet Cascade
0 Main GW1 Exposed 4 × 2.45 (Simplified, no CW) 9.80 9.80 ✓
0 Main GW2 Sheltered 6 × 2.45 14.70 14.70 ✓
1 Ext1 CW1 9 × (0.25 + 1.0) (Simplified + CW) 11.25 11.25 ✓
1 Ext1 CW2 5 × (0.25 + 1.8) 10.25 10.25 ✓
1 Ext1 GW2 Exposed 8 × (0.25+9) ((91)²+(91.8)²)/2 16.08 16.08 ✓
2 Ext2 GW2 Exposed 3 × 8 (Detailed) 24.00 24.00 ✓
3 Ext3 CW1 5 × (0.25 + 1.5) (Simplified + CW) 8.75 8.75 ✓
3 Ext3 CW2 7.5 × (0.25 + 0.3) 4.13 4.13 ✓
3 Ext3 GW1 Exposed 9 × (0.25+7) ((71.5)²+(70.3)²)/2 27.68 27.68 ✓
4 Ext4 CW1 4 × 1 (Detailed) 4.00 4.00 ✓
4 Ext4 CW2 3.5 × 0.6 (Detailed) 2.10 2.10 ✓

Cumulative cert 000565 closure (S0380.77 → .84)

Pin .77→ .78→ .79→ .80→ .81→ .82→ .83→ .84
hot_water_kwh +1399 +260 238 ✓0 ✓0 ✓0 ✓0 ✓0
sap_score (int) +1 1 0 1 ✓0 ✓0 ✓0 3
sap_score_continuous +0.60 0.04 +0.06 0.04 +0.03 +0.03 +0.03 2.01 ⚠
ecf 0.06 +0.00 0.01 +0.00 0.00 0.00 0.00 +0.21 ⚠
total_fuel_cost_gbp 53 +3 5 +4 2 2 2 +183 ⚠
co2_kg_per_yr (n/a) (n/a) 58 9 9 3 3 +237 ⚠

S0380.84 ⚠ rows are the documented BP main-wall surfacing (NOT a regression of S0380.84's RR fix itself).

Why the regression is the correct signal

S0380.84 closed the RR cascade routing to spec correctness. The 11 per-BP RR surface areas pin EXACT vs worksheet at 4 d.p. The cascade walls subtotal moved 322 → 443 W/K (worksheet 604, 43% closed); party 153 → 93 (worksheet 65, 68% closed).

Pre-S0380.84 the cascade had two ~equal-magnitude bugs of opposite sign that mostly cancelled at the SH-pin level:

  • RR cascade: Exposed gables wrongly routed to party_walls at U=0.25 (cascade over-counts party_walls by ~88 W/K)
  • BP main-wall cascade: Curtain Wall + thin-wall alt missing (cascade under-counts walls by ~161 W/K)

S0380.84 closed the first one. The second is now exposed as a +2591 kWh space_heating residual. Per [[feedback-spec-citation-in-commits]] and [[feedback-spec-floor-skepticism]] the spec-correct fix ships even when the test pin temporarily regresses; the diagnostic signal is sharper now.

BP main-wall residual 161 W/K diagnostic

Probed per-BP at HEAD 49622f55:

BP Cascade U Worksheet U Δ contribution Spec gap
0 Main 0.32 0.35 1.6 W/K sub-spec (Solid Brick A age, 75mm External insulation)
0 Main alt1 0.32 2.34 46.5 W/K _insulation_bucket(thk=120, ins_present=False) returns 100 not 0 (docstring intent vs current code) + thin-wall §6.6/§6.7 for U=2.34
1 Ext1 1.70 1.70 ✓ 0 ✓ Spec-correct (Stone Granite E age, Unknown insulation)
2 Ext2 (Curtain Wall) 0.60 1.40 112.2 W/K WALL_CURTAIN=9 defined rdsap_uvalues.py:116 but no _ENG_WALL table entry; u_wall falls through to default Cavity table
3 Ext3 (basement) 0.45 0.45 ✓ 0 ✓ Spec-correct
4 Ext4 (basement) 0.35 0.35 ✓ 0 ✓ Spec-correct

Total: 160.3 W/K (matches the observed 161 W/K).

Gap #1 — Curtain Wall (largest, 112 W/K)

Where: domain/sap10_ml/rdsap_uvalues.py:116WALL_CURTAIN: Final[int] = 9 is defined but has no entry in _ENG_WALL and is not in the known_types set at u_wall:373-376. When the cert lodges wall_construction=9, u_wall falls through to _DEFAULT_WALL_BY_AGE (default cavity) and returns the cavity-wall U for that age band.

Cert 000565 BP[2] Ext2 lodges Type: CW Curtain Wall + Curtain Wall Age: Post 2023 per Summary PDF §7. The "Curtain Wall Age" is a separate per-BP attribute from the dwelling-wide construction_age_band — the BP is age H (1991-1995) but the curtain wall itself was installed Post-2023. Worksheet uses Curtain Wall Post-2023 U=1.40.

Slice span:

  1. Extractor (backend/documents_parser/elmhurst_extractor.py) — currently doesn't surface "Curtain Wall Age" from Summary §7
  2. datatypes/epc/surveys/elmhurst_site_notes.py — add curtain_wall_age to WallDetails
  3. datatypes/epc/domain/epc_property_data.py — add curtain_wall_age to SapBuildingPart
  4. datatypes/epc/domain/mapper.py — thread through both API + Elmhurst paths
  5. domain/sap10_ml/rdsap_uvalues.py — Curtain Wall U-value lookup by age; add WALL_CURTAIN to known_types

Spec citation needed: RdSAP 10 Table 6 or related — locate the canonical Curtain Wall U-values per age category. The worksheet says 1.40 for Post-2023; need to verify the full table.

Gap #2 — Thin-wall alt stone granite (47 W/K)

Where: Two coupled bugs.

  1. domain/sap10_ml/rdsap_uvalues.py:160 _insulation_bucket ignores insulation_present=False when thickness_mm > 0. Docstring says "when not present, the as-built (bucket 0) row applies regardless" but the code falls through to thickness-bucket selection. For BP[0] alt1 with wall_insulation_type=4 (None) but wall_insulation_thickness='120', bucket returns 100 not 0.

  2. The wall_insulation_thickness='120' on SapAlternativeWall is actually the WALL thickness lodged in Summary §7 ("Alternative Wall 1 Thickness: 120 mm"), NOT an insulation thickness. Per feedback-no-misleading-insulation-type this should land on a new SapAlternativeWall.wall_thickness_mm field (mirror of SapBuildingPart.wall_thickness_mm).

  3. Even with bucket 0, _TYPICAL_STONE_UNINSULATED[0] = 1.7 + dry-lined adjustment = 1.32. Worksheet wants 2.34. This is the RdSAP 10 §6.6/§6.7 "thin-wall" formula for stone walls below typical thickness — needs implementing in u_wall.

Slice span:

  1. Extractor — surface "Alt 1 Thickness" as wall thickness (currently mapped to wall_insulation_thickness)
  2. datatypes/epc/domain/epc_property_data.pySapAlternativeWall.wall_thickness_mm: Optional[int]
  3. datatypes/epc/domain/mapper.py — populate wall_thickness_mm from Elmhurst extractor's alt-wall-thickness field
  4. domain/sap10_ml/rdsap_uvalues.py:160 _insulation_bucket — short-circuit if not insulation_present: return 0 per docstring intent (audit cohort for any cert with insulation_present=False AND thickness>0)
  5. domain/sap10_ml/rdsap_uvalues.py:329 u_wall — RdSAP §6.6/§6.7 thin-wall formula keyed on wall_thickness_mm for stone constructions

Open work — prioritised next slices

S0380.85 — Curtain Wall (highest impact, 112 W/K of 161)

Extract curtain_wall_age per BP + add Curtain Wall U-value lookup keyed on age. Multi-layer (extractor + datatypes + mapper + cascade); a single slice if the spec lookup is tractable.

Spec citation candidate: RdSAP 10 Table 6 row for "CW Curtain Wall" across age categories. The worksheet (cert 000565) gives Post-2023 → U=1.40 as the empirical ground truth.

Expected impact: cert 000565 cascade walls 443 → 555 (worksheet 604). HTC fabric 795 → 907. SH residual +2591 → +~800 kWh. sap_score should move 26 → ~28 (still 1-2 short of 29 due to remaining alt1 gap).

S0380.86 — Thin-wall alt stone granite (47 W/K)

Two coupled bugs in rdsap_uvalues.py:

  1. _insulation_bucket short-circuit on not insulation_present
  2. Thin-wall §6.6/§6.7 formula keyed on a new wall_thickness_mm field on SapAlternativeWall

Plus extractor + datatype + mapper plumbing for the wall_thickness field. After both gaps close: cascade walls 555 → ~610 (matches worksheet 604). HTC → ~960. SH should close to ~72 (or smaller — the original residual pre-S0380.84).

Deferred (unchanged from post-S0380.80 handover)

  • MEV PCDB Table 4f component for pumps_fans +2.5 (blocked on external data acquisition; PCDF 500755 record needed)
  • HP SAP code → main_heating_category=4 mapper extension (couples with MEV; ship after MEV record acquired)
  • 12 gas-combi PV certs at +0.5..+1.6 PE (no worksheets)
  • 5 SAP-residual API-only certs (no worksheets)

How to run the baseline

PYTHONPATH=/workspaces/model python -m pytest \
    backend/documents_parser/tests/test_summary_pdf_mapper_chain.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_appendix_h_solar.py \
    domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \
    domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
    --no-cov -q

Expected: 555 pass + 9 expected test_sap_result_pin[000565-*] fails.

The 9 expected fails (verbatim):

sap_score
sap_score_continuous
ecf
total_fuel_cost_gbp
co2_kg_per_yr
space_heating_kwh_per_yr
main_heating_fuel_kwh_per_yr
lighting_kwh_per_yr
pumps_fans_kwh_per_yr

(was 8 + sap_score EXACT at HEAD 27ead127; sap_score moved into the fail list at HEAD 49622f55 per "Why the regression is the correct signal" above).

Files touched this session

File Slices Change
domain/sap10_calculator/rdsap/cert_to_inputs.py S0380.81, .82 Added RDSAP_10_TABLE_32_PRICES; switched default; new _other_use_co2_factor_kg_per_kwh + _other_use_primary_factor helpers; wired into pumps_fans / lighting / electric_shower CO2 + PE fields
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py S0380.81, .82 2 new tests + 3 re-pinned scalar assertions to Table 32
backend/documents_parser/elmhurst_extractor.py S0380.83 Added "Exposed" + "Connected" to gable_type recognition set
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py S0380.83, .84 2 new tests (extractor gable_type + mapper RR routing/areas)
datatypes/epc/domain/mapper.py S0380.83, .84 Mapper RR routing per §3.10 Table 4; drops Connected, routes Exposed external, surfaces Common Walls with §3.9.2 spec area formula
datatypes/epc/domain/epc_property_data.py S0380.84 SapRoomInRoofSurface.kind docstring extended with common_wall
domain/sap10_calculator/worksheet/heat_transmission.py S0380.84 Added common_wall kind handler (walls += area × U)

Spec source quick-reference

  • SAP 10.2 full specification: domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf
    • Table 12a (p.191) — Grid 1 SH + WH + Grid 2 "All other uses" high-rate fractions
    • Table 12d (p.194) — monthly CO2 factors for electricity
    • Table 12e (p.195) — monthly PE factors for electricity
  • RdSAP 10 specification: domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf
    • §3.9 + §3.10 (p.30-35) — Simplified Type 1/2 + Detailed RR
    • Table 4 (p.22) — RR surface variants (gable_wall U-value rules)
    • §19.1 (p.80-81) — Table 32 prices for §10a/§10b
    • Table 32 (p.95) — RdSAP unit prices + standing charges
    • Table 6 — wall U-values by construction + insulation bucket (Curtain Wall entry needed for S0380.85)
    • §6.6 / §6.7 — thin-wall stone formula (S0380.86)
  • SAP 10.3 at domain/sap10_calculator/docs/specs/sap-10-3-full-specification-2026-01-13.pdf: DO NOT reference (feedback-sap-10-2-only-never-10-3)

Memory updated this session

  • project_cert_000565_recovery_state — S0380.81/.82/.83/.84 entries
    • post-S0380.84 BP main-wall diagnostic table localising the 161 W/K residual to Curtain Wall + thin-wall alt
  • MEMORY.md — index entry refreshed at HEAD 49622f55