docs: handover post S0380.195 — 6035 OPEN, API-mapper roof/RR over-count lead

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>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-03 10:14:17 +00:00
parent 4a21717de6
commit af477678c2

View file

@ -0,0 +1,147 @@
# Handover — post S0380.195 (gas-combi site-notes + RR/floor bugs; 6035 OPEN)
Point-in-time note. Start from [`AGENT_GUIDE.md`](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.190195 + 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 101119 (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 14 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 14 (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:
1. the **3.82 m² window**: North in case 4, **South** in 6035 (only window diff);
2. **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);
3. **meter type** "Dual" (case4) vs API **2** (6035);
4. 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`, scalar `gable_1/2_length_m=4.65`,
**`roof_construction=4`** → cascade's SCALAR RR path (heat_transmission.py
~363-460 + ~853-875) AND a separate `roof_construction=4` main-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.7329.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:973` treats `wall_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_type` for non-gas/non-104 boilers (only 101119 + 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.8` trailer. AAA tests, `abs(x-y) <= tol` (not `pytest.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.