Retracts the premature "6035 = lodged divergence" claim (S0380.195 commit msg + fixture docstring). The golden residual SAP -2 / PE +19.16 / CO2 +0.42t is REAL and exceeds the fallback bar. Section-level diff of 6035 (API) vs sim case 4 (site-notes, pins @1e-4) localised it to a cross-mapper parity break: roof W/K 78.33 (site-notes) vs 130.73 (API), a +52 over-count from the API RR scalar path + roof_construction=4. Next agent starts there. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.8 KiB
Handover — post S0380.195 (gas-combi site-notes + RR/floor bugs; 6035 OPEN)
Point-in-time note. Start from AGENT_GUIDE.md for methodology,
accuracy bar, and pipeline — this records what this session did and what is open.
- Branch:
feature/per-cert-mapper-validation - HEAD:
4a21717d(S0380.195) - Baseline:
2341 passed, 1 skipped, 0 failed. Verify with the §4 suite command.
What this session shipped (S0380.190–195 + 1 extractor fix)
| Slice | What | Spec |
|---|---|---|
| .190 | Gas-combi site-notes main_fuel_type derivation. Newer Elmhurst export lodges §14.0 Fuel Type EMPTY + SAP code 104 → MissingMainFuelType blocked ALL gas-combi Summary certs. Derive carrier from §15.0 Water Heating Fuel Type for Table 4b gas-boiler codes 101–119 (NOT "104→mains gas" — Table 4b gas codes span mains gas/LPG/biogas; §15.0 disambiguates). _elmhurst_gas_boiler_main_fuel. |
SAP 10.2 Table 4b p.168 |
| extractor fix | Windows-table header remnant ("value value Proofed Shutters") leaked into the FIRST window's glazing_type (layout before_start=0 reaches the wrapped header). Trim prefix to the glazing-start word. |
— |
| .191 | Promoted sim case 1 (single-part gas combi) to e2e harness 001431, 11 pins @1e-4. Resolved the handover's "+0.0007 SAP" as a display-rounded-ECF target artifact. |
— |
| .192 | Simplified room-in-roof bug. A Simplified RR (assessment "Simplified") lodges PLACEHOLDER slope/ceiling L×H (40 m ceiling height, 32 m slope). Spec derives one timber-framed remaining area A_RR = 12.5√(A_floor/1.5) − Σgables. The cascade already computes this (has_roof_lodgement gate in heat_transmission.py) but _map_elmhurst_rir_surface emitted the placeholder slope/ceiling → 1024+160 m² roof → 7.5× heat-loss explosion (SAP −14.6). Fix: drop roof-going surfaces for Simplified. API path (6035) already correct via scalar gable fields. |
RdSAP 10 §3.9.1 p.21 |
| .193 | Suspended-floor (12) sealed rule. Rule (a) "floor U<0.5 → sealed 0.1" applies only when a U-value is SUPPLIED; an as-built/default U falls to (b)→unsealed 0.2. Cascade fed the computed default U into (a) → wrongly sealed → ~450 kWh space-heat understatement. Fix: gate (a) on floor_u_value_known. |
RdSAP 10 §5 p.29 |
| .194 | Sim case 3 (8 windows, symmetric HLP) → e2e 001431_rr8, 11 pins @1e-4. |
— |
| .195 | Sim case 4 (6035 floor geometry: Main ground HLP 15.99 + first 8.32) → e2e 001431_6035, 11 pins @1e-4. |
— |
Net: 4 new Elmhurst-only e2e fixtures (cases 1–4 of cert 001431), all @1e-4. The
worksheet Summaries are mirrored into backend/documents_parser/tests/fixtures/
(Summary_001431_gas_combi.pdf, _rr_ext, _rr8w, _6035); source Summary +
P960 worksheet tracked under sap worksheets/golden fixture debugging/simulated case {1..4}/.
The .195 commit message and the _elmhurst_worksheet_001431_6035.py docstring
claim 6035's +19 PE is "lodged divergence." THAT CLAIM IS RETRACTED — see below.
OPEN (the priority) — golden cert 6035 residual is REAL, not divergence
tests/.../test_golden_fixtures.py pins cert 6035-7729-2309-0879-2296:
actual_sap=70, expected_sap_resid=-2, expected_pe_resid=+19.16, expected_co2_resid=+0.42 t. All three exceed the ±0.5 SAP / small-CO2 fallback
bar. A −2 SAP is not rounding. 6035 was lodged 2025-11-11 under
RdSAP-Schema-21.0.1 / SAP 10.2 (software 5.02r0328) — the SAME methodology we
target, so it is NOT a version artifact.
What we know
- The cascade reproduces Elmhurst's WORKSHEET engine for this archetype: sim cases 1–4 (Main+Ext+RR+suspended-floor+gas-combi-104) all pin @1e-4 on all 11 Block-1 line refs.
- Case 4 ≈ 6035. Identical: 2 BPs, age A, solid-brick walls (Main ins, Ext
as-built), RR floor 29.75, floor areas, Main floors HLP 15.99/8.32, doors,
heating (104/control 2106), 8 windows by area + BP + 7/8 orientations.
Remaining input diffs case4-vs-6035:
- the 3.82 m² window: North in case 4, South in 6035 (only window diff);
- lighting bulbs: case 4 cascade lighting 262 vs 6035's 364 (6035 lodges 9 low-energy + 2 incandescent; case 4's Summary lighting parsed as None);
- meter type "Dual" (case4) vs API 2 (6035);
- 6035 lodges
cylinder_size=1(case 4 none) — appears immaterial (HW matches).
- Controlled test: flipping case 4's 3.82 window N→S raises SAP only +0.25 (68.19→68.44). Nowhere near +2. So orientation does NOT explain the gap.
- The energy/demand model looks ~right per-end-use. Cascade DEMAND (postcode) costs ≈ 6035's lodged costs: heating £1278 vs lodged £1285, HW £225 vs £217, lighting £103 vs £103. So the −2 SAP lives in the RATING block (UK-avg): cascade rating cost 948.59 → ECF 2.31 → SAP 67.81; register implies cost ~£886 / ECF 2.15 / SAP 70. Plus the CO2 (+0.42 t) is unexplained.
- Neither bug fixed this session touches 6035 (its RR uses the API scalar-field path, already correct; its floor U=0.63 ≥ 0.5 was already "unsealed").
The contradiction to resolve
Elmhurst-worksheet-for-case4-inputs = 68, 6035-register = 70, same methodology, inputs nearly identical, and the known diffs explain only ~+0.25. Either (a) 6035's register was produced from inputs materially different from the golden JSON in a rating-relevant way we can't see, or (b) there's a real cascade bug only 6035's exact combination triggers (the simulated cases didn't hit it).
★ BREAKTHROUGH LEAD (end of session) — API-mapper roof/RR over-count
The user's hypothesis ("something missing from the API mapper") is CONFIRMED.
Diffing 6035 (API path) vs case 4 (site-notes path) at the SECTION level
(heat_transmission_section_from_cert) — with near-identical fabric — exposes a
cross-mapper parity break that should not exist:
| §3 line | case4 (site-notes) | 6035 (API) | Δ |
|---|---|---|---|
| roof W/K | 78.33 | 130.73 | +52.39 |
| party W/K | 36.86 | 0.00 | −36.86 |
| (33) fabric heat loss | 290.72 | 304.66 | +13.94 |
| (31) total ext area | 231.02 | 242.74 | +11.72 |
| walls / floor / windows / doors | — | — | ≈0 |
The roof +52 W/K is the prime suspect for the whole 6035 residual (52 W/K of spurious heat loss ≈ the −2 SAP / +19 PE / +0.42 t CO2). Root cause is the RR/roof representation feeding two DIFFERENT cascade paths:
- case 4 (site-notes):
sap_room_in_roof.detailed_surfaces=[gable_wall_external, gable_wall], scalar gable lengths = None,roof_construction=None→ cascade's Detailed-loop residual path (12.5√(A_floor/1.5) − Σwalls) → roof 78.33. ✓ (pins to case-4 worksheet @1e-4). - 6035 (API):
detailed_surfaces=None, scalargable_1/2_length_m=4.65,roof_construction=4→ cascade's SCALAR RR path (heat_transmission.py ~363-460 + ~853-875) AND a separateroof_construction=4main-roof element → roof 130.73. Likely DOUBLE-COUNTS the main roof over the full footprint with the RR, or the scalar A_RR path over-states the area.
Hand-check: for 6035 the correct roof ≈ RR remaining (12.5√(29.75/1.5) − 2×11.39 = 32.88 × 2.30 = 75.6) + main-loft residual (41.73−29.75=11.98 × 0.14 = 1.68) + ext roof (7.21 × 0.14 = 1.01) ≈ 78.3 (matches case 4). The API path's 130.73 is ~52 too high.
START HERE: instrument the API RR/roof path for 6035. Compare
_api_build_room_in_roof (mapper.py ~2713) output + roof_construction=4
handling vs the site-notes detailed_surfaces path. Find where the extra ~52 W/K
roof comes from (main-roof-area double count with RR, or scalar A_RR over-state).
Fix so the API path matches the site-notes path (cross-mapper parity), then re-pin
6035's golden residual (should collapse toward 0). The party=0 (party_wall_
construction=3) is secondary — verify 3=solid U=0 is correct first.
This is a CALCULATOR/MAPPER bug, not lodged divergence — the byte-exact-worksheet plan below is now a fallback only.
Fallback — byte-exact 6035 worksheet ("simulated case 5")
Ask the user to generate case 5 = case 4 with EVERY remaining input matched to 6035: 3.82 m² window → South, lighting = 9 low-energy + 2 incandescent, meter type matched, cylinder matched. Then:
- If the worksheet SAP = 70 → real cascade bug. Diff cascade vs worksheet line-by-line (start §6 solar gains (74)–(83) for the south window, §8 lighting (232)/Appendix L, then §10a/§12 rating cost/ECF and §12/§13 CO2).
- If the worksheet SAP = 68 → the register's 70 is the anomaly (lodged from different inputs); 6035 becomes a documented register-vs-worksheet divergence.
Parallel angle worth a look NOW (no new worksheet needed): the lighting energy (cascade 364 for 9 LE + 2 inc, TFA 128) — verify against SAP 10.2 Appendix L; and the CO2 (+0.42 t) decomposition by carrier (the demand-cost match suggests the energy is right, so a CO2-FACTOR or rating-block issue is implicated).
Carry-over (lower priority, from the prior handover)
transform.py:973treatswall_construction in (5,6)as timber-frame for the ventilation structural-ACH split, but 6 = system-built (masonry); only 5/7/8 are timber/cob/park. Possible latent ventilation-ACH bug — verify before touching.- Summary-path
main_fuel_typefor non-gas/non-104 boilers (only 101–119 + the existing liquid/solid/electric/community branches are covered).
Process notes
- One slice = one commit, spec citation in the message,
Co-Authored-By: Claude Opus 4.8trailer. AAA tests,abs(x-y) <= tol(notpytest.approx). - The 4 sim-case e2e fixtures pin Block 1 (UK-avg rating) via
Sap10Calculator().calculate(epc)— NOT the postcode demand block. - Window ORIENTATION does NOT change the SAP rating much (+0.25 for 3.82 m²) — do not over-attribute the 6035 gap to it.