From 61e369faf7cf0c594d05bfad6b89c2fc3569c389 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Sat, 23 May 2026 07:35:25 +0000 Subject: [PATCH] HANDOVER_NEXT: rewrite for strict zero-error cascade pin closure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the previous handover. The previous one framed the work as "close three tickets to integer Δ=0" — a weak gate. The user has since made clear the real requirement is **abs=1e-4 on every line ref of every output for every fixture**, and that previous agents have repeatedly made the following mistakes: 1. Treated SAP integer Δ=0 as "closed" (it hides ±0.5 continuous drift). 2. Widened tolerances (rel=0.15 / rel=0.05 / <=0.5) to make tests green — masking real residuals. 3. Tested sections in isolation using PDF values as INPUTS — that verifies the section formula but not the cascade. 4. Diagnosed downstream first when upstream sections still drift. 5. Missed fixture-lodgement defects (bulbs / windows / sap_heating / detailed RR / exposed_floor / door_count / per-window u_value) — the cascade pin failure was the fixture, not the calculator. 6. Labelled code "SAP 10.3" when implementing 10.2. The new handover front-loads these anti-patterns (§A.3), then states the current cascade-pin scoreboard, the work queue in priority order (rooflight, 000487 RR + U=0.86 gable, then §5/§6/§7/§8/§9a/§10a/§11a/ §12 pins in worksheet order), the diagnostic loop, and the spec page anchors the user has already given. Three new memories were also written: - feedback-zero-error-strict (abs=1e-4, no widening) - feedback-cascade-pin-methodology (test the cascade, not isolation) - feedback-fixture-defects-common (audit fixture first) Co-Authored-By: Claude Opus 4.7 --- docs/sap-spec/HANDOVER_NEXT.md | 486 ++++++++++++++++++++++----------- 1 file changed, 327 insertions(+), 159 deletions(-) diff --git a/docs/sap-spec/HANDOVER_NEXT.md b/docs/sap-spec/HANDOVER_NEXT.md index 510110ee..315d5c93 100644 --- a/docs/sap-spec/HANDOVER_NEXT.md +++ b/docs/sap-spec/HANDOVER_NEXT.md @@ -1,230 +1,398 @@ -# Handover — close 000480 / 000487 / 000516 to delta=0 + mapper RR extraction + windows/doors residual +# Handover — strict zero-error cascade pin closure for the 6 Elmhurst fixtures -**For the agent picking up the next chunk of work.** Read this BEFORE invoking `/grill-me`. Read all of it. Caveman mode is the house style — terse, technical, no filler. +**For the agent picking up the next chunk of work.** Read this BEFORE any tool call. +Read it in full. The previous agents' errors are catalogued here so you don't +repeat them. Owner: `khalim@domna.homes`. Branch: `ara-backend-design-prd`. - -Three tickets in priority order: - -1. **Build_epc lodgement on 000480 / 000487 / 000516** (currently Δ=+4 / +3 / +4). Mirror the 000477 pattern from slice 14 (commit `4ac4f7da`) — detailed RR surfaces from each U985 worksheet PDF + door_count fix + roof_insulation_thickness. Should close all three to Δ=0 if no other residual. -2. **`EpcPropertyDataMapper` extends to extract RR-detailed lodgement** from API responses. The gov-EPC API carries `sap_building_parts[i].sap_room_in_roof.room_in_roof_type_1` with gable lengths/types, and the `epc.roofs[*].description` flags retrofit insulation ("Roof room(s), insulated (assumed)"). Once extracted, golden cert `0240-0200-5706-2365-8010` returns to Δ=0 and `_SAP_TOLERANCE` tightens 13 → 11. -3. **Windows / doors over-count residual**. After RR closure, 000480/487/516 still show (37) overshooting PDF by ~9-40 W/K — dominated by windows being computed at higher effective U than the U985 worksheet shows. Likely curtain-resistance / per-window-U handling gap. - -Hard rules (unchanged): -- **Caveman mode** house style. -- **Tolerance**: don't loosen test tolerances to mask drift. If a refactor can't hit the locked tolerance, pause and ask. -- **Spec PDFs**: don't scan more than ~50 lines without checking with the user. -- **Commit per slice**, AAA test convention (`# Arrange / # Act / # Assert`), `Co-Authored-By: Claude Opus 4.7 ` trailer. -- **Don't widen ceilings to hide bugs.** Per `feedback-e2e-validation-philosophy` memory: component pins at 50 lines of spec PDF without checking with the user** for the + specific page/table range. Spec PDFs are big and the user has the page + anchors. (Table 11 = page 188, Table 12 = 189, Table 12a = 191, Table 3a/b/c + = 160/161/162 already given.) +- **One slice = one commit**. AAA test convention (`# Arrange / # Act / + # Assert`). Co-Authored-By trailer. +- **Don't touch SAP rating constants in `worksheet/rating.py`** — + `ENERGY_COST_DEFLATOR=0.42`, `ECF_LOG_THRESHOLD=3.5`, `SAP_LOG_COEFF=113.7`, + `SAP_LOG_CONSTANT=117.0`. SAP 10.2 (14-03-2025) per ADR-0010. Pinned by 8+ + tests. +- **Don't auto-update unrelated git status changes** — see deletions/new files + in `git status` that aren't from your work? Don't touch them without asking. +- **Don't invoke `/ultrareview`** — user-triggered only. +- **Caveman mode** for prose. Terse. Technical. No filler. --- -## §B — Ticket 1: build_epc lodgement on 000480 / 000487 / 000516 +## §B — Current state (as of 2026-05-23) -### B.1 Mission +### B.1 Cascade pin scoreboard -Mirror the 000477 closure recipe (commit `4ac4f7da`) on the three remaining Elmhurst fixtures. Each needs its U985 worksheet PDF read to extract: +Two test files contain the strict pins: -- **Detailed RR surfaces** (slope / stud_wall / flat_ceiling / gable_wall) — areas + insulation thicknesses + types from U985 §3 lines (30)/(32). Lodge as `SapRoomInRoofSurface` entries on `SapRoomInRoof.detailed_surfaces`. -- **Storey-below roof insulation thickness** — usually the "External roof Main 16.20" line gives U × A; back-solve the thickness from Table 16 row. Lodge as `SapBuildingPart.roof_insulation_thickness`. -- **`door_count`** — likely 1 (single external door per U985 line 42); double-check each PDF. -- Anything else missing (windows, secondary heating, lighting bulbs, PCDB id) — compare against the existing build_epc to find lodgement gaps. +1. **`test_e2e_elmhurst_sap_score.py::test_sap_result_pin[fixture-field]`** — + top-level SapResult fields. 66 cases (11 fields × 6 fixtures). Currently + **18 PASS / 48 FAIL** at abs=1e-4. +2. **`test_section_cascade_pins.py`** — per-section line refs walking + `
_from_cert(epc)` against PDF. Currently **151 PASS / 35 FAIL**: + - §1 (dimensions): 12 PASS / 0 FAIL ✓ + - §2 (ventilation): 96 PASS / 0 FAIL ✓ + - §3 (heat losses): 1 PASS / 23 FAIL + - §4 (water heating): 42 PASS / 12 FAIL + - §5-§12: not yet pinned -### B.2 PDFs +Total: **169 PASS / 83 FAIL** across the strict pins. 4 of 6 fixtures fully +close §1+§2+§4. 000487 is the worst (RR fixture defect propagates everywhere). + +### B.2 SapResult pin matrix (post-slice-22/23) ``` -sap worksheets/U985-0001-000480.pdf -sap worksheets/U985-0001-000487.pdf -sap worksheets/U985-0001-000516.pdf +field | 474 | 477 | 480 | 487 | 490 | 516 +-----------------------------------|-----|-----|-----|-----|-----|----- +sap_score (int) | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ +sap_score_continuous | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ +ecf | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ +total_fuel_cost_gbp | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ +co2_kg_per_yr | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ +space_heating_kwh_per_yr | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ +main_heating_fuel_kwh_per_yr | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ +secondary_heating_fuel_kwh_per_yr | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ +hot_water_kwh_per_yr | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ +lighting_kwh_per_yr | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ +pumps_fans_kwh_per_yr | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ ``` -Plus the `.txt` dumps alongside each — those are the fastest path to the §3 line items. Same format as `U985-0001-000477.txt` which already informed slice 14. +5 of 6 fixtures hit SAP integer Δ=0 (000487 is the holdout). But continuous SAP +is still off by sub-SAP-point amounts on every fixture — none of `sap_score_ +continuous` is closed at abs=1e-4. -### B.3 Slice plan (proposed) +### B.3 §3 residuals after slice 22 (window curtain) + slice 23 (000516 RR) ``` -S16 — 000480 build_epc lodgement + detailed RR + roof_insulation_thickness. - Target: Δ → 0. -S17 — 000487 build_epc lodgement (same). -S18 — 000516 build_epc lodgement (same). -S19 — Tighten _SAP_TOLERANCE (and other ceilings) once the three new fixtures - are clean and the golden recalibration in S20 lands. +fixture | LINE_31 Δ | LINE_33 Δ | LINE_36 Δ | LINE_37 Δ +000474 | 0.0014 | 0.0296 | 0.0002 | 0.0294 +000477 | 0.0004 | 0.1246 | ✓ | 0.1244 +000480 | 0.0060 | 0.0168 | 0.0009 | 0.0177 +000487 | 8.82 | 37.88 | 1.32 | 39.21 +000490 | 0.0010 | 0.0282 | 0.0002 | 0.0284 +000516 | 0.0025 | 0.8215 | 0.0004 | 0.8219 ``` -Per the [feedback_commit_per_slice memory](../../home/vscode/.claude/projects/-workspaces-model/memory/feedback_commit_per_slice.md): one slice = one commit. +5 of 6 fixtures have §3 residuals under 0.2 W/K. 000516's 0.82 W/K is the +rooflight (line 27a) not yet wired into the §3 cascade. 000487's huge gaps are +the RR fixture defect + the U=0.86 external-gable variant our `gable_wall` +enum doesn't handle. -### B.4 Risks +### B.4 §4 residuals -- Each fixture might surface a NEW residual (different from 000477's). Diagnose at the LINE_33/LINE_37 component level first; only un-xfail when SAP integer hits 0. -- 000480 / 000487 PDFs may exercise §3.9.2 Type 2 or different RR geometry that the current code path doesn't handle correctly. The slice 12 implementation is unit-tested but no real fixture exercises it. +``` +fixture | section §4 pin status +000474 | 9/9 ✓ +000477 | 5/9 (combi loss LINE_61m diverges → cascades to 62/64/65) +000480 | 9/9 ✓ +000487 | 1/9 (LINE_43 + every monthly fails — HW lodgement defect) +000490 | 9/9 ✓ +000516 | 9/9 ✓ +``` + +### B.5 Recent slices (in reverse order — newest first) + +``` +ac68cf88 Slice 23: 000516 detailed RR + exposed_floor + door_count fixture lodgement +6be8fdb7 Slice 22: per-window curtain resistance fix (mixed glazing) +024244ec Slice 21d: §3 cascade pins + heat_transmission_section_from_cert helper +778b150c Slice 21e: §4 water heating cascade pins (42/54 PASS) +5b7dbe2c Slice 21c: §2 cascade pins + ventilation_from_cert helper (96 PASS) +c1472330 Slice 21b: §1 cascade pins (12/12 PASS) +20424a2d Slice 21a: relabel SAP 10.3 → SAP 10.2 in calculator docstrings +4c2f37f6 Slice 19b: drop loose-tolerance fuel cost tests (rel=0.15, rel=0.05) +6bfb0614 Slice 19a: strict cascade-pin scoreboard for SapResult vs U985 PDFs +e2d9f77d Slice 20: lodge per-window u_value on mixed-glazing fixtures +5e34594d Slice 18a: sap_heating lodgement on 000480 / 487 / 516 +8786b907 Slice 17: wire Appendix L inputs into 000480 / 487 / 516 +``` --- -## §C — Ticket 2: `EpcPropertyDataMapper` extracts RR detailed lodgement +## §C — Work queue (in priority order) -### C.1 Mission +### C.1 Slice 24 — Rooflight (line 27a) heat transmission, for 000516 -Extend [datatypes/epc/domain/mapper.py](../../datatypes/epc/domain/mapper.py) so `from_api_response` populates `SapRoomInRoof.detailed_surfaces` (and any Type 2 fields) from the API JSON. Two main signals to map: +000516 PDF lodges a 1.18 m² rooflight on line (27a) at U_eff=2.9930 → 3.5317 +W/K. Our §3 cascade doesn't include roof windows in heat transmission (only +solar gains via SECTION_6_ROOF_WINDOWS). -1. **`sap_room_in_roof.room_in_roof_type_1`** sub-block. Carries `gable_wall_type_1`, `gable_wall_type_2` (Table 4 codes — 0=exposed gable, party/sheltered/connected as applicable) plus `gable_wall_length_1`, `gable_wall_length_2`. Map to detailed gable surfaces (or Simplified Type 2 gable lengths if no per-surface lodgement). -2. **`epc.roofs[i].description`** flags. Patterns observed in cert JSON: - - `"Roof room(s), insulated (assumed)"` → retrofit RR insulation, unknown thickness → 50 mm per §5.11.4. - - `"Roof room(s), no insulation"` → 0 mm row (U=2.30, Table 17 none row). - - Specific thickness in description (rare): regex-extract per existing `_parse_thickness_mm` patterns. +Closes 000516 §3 LINE_33 residual (0.82 W/K → ~0). Likely fixture lodgement + +small calc change to iterate roof windows alongside vertical windows when +applying curtain-resistance + summing. -The existing `_described_as_insulated` / `_ROOF_NO_INSULATION_MARKERS` / `_ROOF_LIMITED_INSULATION_MARKERS` patterns in [rdsap_uvalues.py](../../packages/domain/src/domain/ml/rdsap_uvalues.py) are the precedent — same regex shape, applied to RR descriptions. +Spec source: SAP 10.2 §3 (page 17-22), worksheet line (27a). -### C.2 Acceptance gate +### C.2 Slice 25 — 000487 RR + HW + external gable variant -- Golden cert `0240-0200-5706-2365-8010` returns from Δ=-12 → Δ≈0. -- `_SAP_TOLERANCE` tightens 13 → 11 (back to where it was before slice 14). -- The other 5 golden certs stay inside tolerance. +000487 is the worst remaining fixture. PDF lodges: +- **Detailed §3.10 RR with one gable as EXTERNAL** at U=0.86 (line 29a), not + party at U=0.25. Our `SapRoomInRoofSurface.kind="gable_wall"` enum only + routes to party. New variant needed. +- Specific HW lodgement (1 bath, but PDF (43) annual avg HW diverges from + what fixture currently produces — likely shower flow rate or bath count). +- 000487 also still fails SAP integer (60 vs PDF 62) — the only fixture with + Δ_int ≠ 0. -### C.3 Spec anchors +**Needs spec input from user**: RdSAP 10 Table 4 / Table 6 page reference for +the "RR gable as external" routing. The user has given Table 11 (p 188), +Table 12 (189), Table 12a (191), Tables 3a/b/c (160-162). Ask for Table 4 + +Table 6 pages. -- RdSAP 10 §3.9.1 page 21-22 (Simplified Type 1 + Table 4 wall categories). -- RdSAP 10 §3.9.2 page 22-23 (Simplified Type 2 with common walls < 1.8 m). -- RdSAP 10 §3.10 page 24-25 (Detailed measurements). -- RdSAP 10 §5.11.3 page 44 + Table 17 (RR U-values when insulation thickness is known). -- RdSAP 10 §5.11.4 + Table 18 column (4) page 45 (RR as-built / unknown defaults). -- BRE PCDF Spec Rev 6b — already in repo at `docs/sap-spec/PCDF_Spec_Rev-06b_12_May_2021.pdf` (pp. 14-15 for the gas-and-oil combi field layout — relevant to Tickets 1/2 if those certs lodge combi-boiler RR variants). +### C.3 Slice 26+ — §5 / §6 / §7 / §8 / §9a / §10a / §11a / §12 cascade pins ---- +The cascade pin work continues in worksheet order. For each section: -## §D — Ticket 3: windows + doors over-count residual +1. Identify the cert→inputs cascade entry point. May need to extract a + `
_from_cert(epc)` helper from `cert_to_inputs` (mirroring slice + 21c's `ventilation_from_cert`, 21d's `heat_transmission_section_from_cert`, + 21e's `water_heating_section_from_cert`). +2. Map fixture `LINE_X_` constants to result struct attributes. +3. Add scalar + monthly pin tests at abs=1e-4 to `test_section_cascade_pins.py`. +4. Run, see failures, diagnose. Fixture defect or calculator bug — fix in place, + no widening. -### D.1 Mission - -After RR closure on the cohort, the remaining (37) overshoot on 000480/000487/000516 is dominated by: - -- **Windows**: our calculator computes ~23 W/K for 000477 but the U985 worksheet lodges 9.21 W/K (sum across all per-window A×U entries). That's ~14 W/K too high — roughly 2.5× over. -- **Doors**: pre-slice-14 we counted 2 doors when the worksheet lodges 1. Fixed for 000477 in slice 14 (door_count=1). Same delta likely on the other three. - -Diagnose: - -1. Walk a single 000477 window through the calculator. Compare the effective U (post curtain-resistance + frame factor) against the worksheet's per-window value. The U985 lodges raw U-values; our calculator applies the SAP10.2 §3.2 curtain-resistance transform `U_eff = 1 / (1/U_raw + 0.04)` — verify it's applied consistently with the spec convention. -2. Check whether `WindowTransmissionDetails.u_value` is the raw or effective U-value when sourced from the API (the `data_source` field's encoding matters). -3. Spot-check `door_count=1` lands across the 4 RR fixtures (it should — they're all single-entry mid-terraces or detached). - -### D.2 Slice plan (proposed) +Sections still to pin: +- **§5 internal gains** (lines 66-73 + 232 lighting kWh). 6 monthly + 1 annual. +- **§6 solar gains** (lines 83-84). 2 monthly tuples. +- **§7 mean internal temperature** (lines 85-94). 10 line refs, mostly monthly. +- **§8 space heating** (lines 95-99). 4 monthly + 2 annual. +- **§9a energy requirements** (lines 201, 206-208, 211-215, 219). 5 scalar + 2 + monthly. Currently only the annual aggregates show on `SapResult` — may need + monthly exposure. +- **§10a fuel costs** (lines 240-255). 17+ line refs. +- **§11a SAP rating** (lines 256-258). 3 line refs. +- **§12 environmental** (lines 261-282). CO2 + primary energy + EI rating. +Some fixtures' constants for these sections may be missing — check first. PDF +extraction commands (sample for §9a): ``` -S20 — diagnose window U-value cascade. Single-fixture trace + LINE_27 pin. -S21 — fix window cascade if needed. Re-run cohort. -S22 — doors lodgement parity sweep across fixtures. +awk '/^9a\. Energy requirements/,/^10a\./' "sap worksheets/U985-0001-NNNNNN.txt" ``` -### D.3 Spec anchors +### C.4 Slice 27 — Floor-U precision (≈0.04 W/m²K drift on 4 fixtures) -- SAP 10.2 §3 + Table 6e (window U-values + curtain resistance). -- RdSAP 10 §3.7 page 20 (door + window area conventions). -- RdSAP 10 §5 + Table 24 / Table 26 (window / door U-value defaults). +After slices 22 + 23 closed window + RR contributions, 4 fixtures +(000474/477/480/490) have residual ~0.03-0.13 W/K on LINE_33 traced to floor U +drift: +- 000477: calc 0.5261 vs PDF 0.5300 for suspended timber + age B + 31.26 m² + ground floor. +- Likely the BS EN ISO 13370 ground-contact formula vs PDF Table 19 lookup. + +Diagnose what the PDF uses for these (probably a tabulated value, not a +formula) and align the calc. + +### C.5 Slice 28 — Continuous SAP / fuel cost / CO2 closure + +Once §1-§9a all close at abs=1e-4, the downstream pins +(`total_fuel_cost_gbp`, `ecf`, `sap_score_continuous`, `co2_kg_per_yr`) tighten +mechanically. Re-run the SapResult pin matrix; whatever still fails has a +section-specific residual to chase. --- -## §E — Useful-space-heating residual (now mostly resolved) +## §D — How to work (toolbox) -The §9/§10 useful_space_heating undershoot diagnosed in slice 8 (`useful_space_heating_kwh_per_yr = 9156 vs PDF 10111`) was **NOT** a §9/§10 cascade bug. It was the missing RR contribution to (33). Now resolved by slices 11-14. No followup needed. +### D.1 Cascade pin diagnostic loop + +When a pin fails: +1. Add a TEMP diagnostic test in `packages/domain/src/domain/sap/worksheet/tests/test__diag_TEMP.py` that dumps the cascade output alongside the PDF expected. +2. Compare element-by-element against the PDF block (use `awk` to extract the relevant §X PDF block). +3. Identify the drift source — fixture defect or calc bug. +4. Fix. Re-run the pin test. +5. **Delete the TEMP file before committing.** Never commit `_TEMP.py` files. + +### D.2 Spec lookups + +User has given these page anchors: +- Table 11 (secondary heating fraction): p 188 +- Table 12 (fuel prices/CO2/PEF): p 189 +- Table 12a (standing charges, off-peak): p 191 +- Table 3a (water heating single-system): p 160 +- Table 3b (water heating combi PCDB): p 161 +- Table 3c (water heating two-profile): p 162 + +For other pages, **ask the user.** Don't scan more than ~50 lines of spec PDF +without permission. + +### D.3 PDF extraction + +Worksheet PDFs are in `sap worksheets/` (note the space — quote in shell). +Each fixture has `U985-0001-NNNNNN.{pdf,txt}` (intermediate values) and +`Summary_NNNNNN.pdf` (cert lodgement). + +PDF blocks for sections (sample for §3): +```bash +awk '/^3\. Heat losses/,/Thermal mass parameter/' "sap worksheets/U985-0001-000474.txt" +``` + +### D.4 Section helpers (cascade-pin enablers) + +Already extracted in `domain.sap.rdsap.cert_to_inputs`: +- `dimensions_from_cert(epc) -> Dimensions` (§1) +- `ventilation_from_cert(epc) -> VentilationResult` (§2, slice 21c) +- `heat_transmission_section_from_cert(epc) -> HeatTransmission` (§3, slice 21d) +- `water_heating_section_from_cert(epc) -> WaterHeatingResult` (§4, slice 21e) + +For §5/§6/§7/§8/§9a/§10a/§11a/§12 you may need to extract similar helpers. +The existing `internal_gains_from_cert`, `solar_gains_from_cert`, etc. mostly +exist already — check whether they're already public on the worksheet/* module. + +### D.5 Hard rules summary card + +| do | don't | +|----|-------| +| `pytest.approx(..., abs=1e-4)` | `rel=…` | +| Audit fixture against PDF first | Diagnose downstream first | +| Leave failing pins, fix one at a time | Widen tolerance / add xfail | +| Quote PDF page when asking for spec | Scan >50 lines of PDF without asking | +| `[[reference-style]]` cross-links in memory | Bare prose references | +| Delete `_TEMP.py` before commit | Commit diagnostic scripts | --- -## §F — Known follow-ups (named on prior deferred lists) +## §E — Key files -Same list as the previous handover, with the following items now closed: +``` +docs/sap-spec/sap-10-2-full-specification-2025-03-14.pdf Spec PDF +docs/sap-spec/HANDOVER_NEXT.md This file +docs/sap-spec/PARITY_FINDINGS.md Older findings +sap worksheets/ U985 + Summary PDFs -- ✓ **Table 3c two-profile combi loss** (slice 6-7). -- ✓ **RR cascade via RdSAP §3.9 / §3.10** (slices 9-13). -- ✓ **000477 closure** (slice 14). +packages/domain/src/domain/sap/calculator.py Top-level SAP10.2 orchestrator +packages/domain/src/domain/sap/rdsap/cert_to_inputs.py Cert→CalculatorInputs + + section_from_cert helpers +packages/domain/src/domain/sap/tables/table_12.py SAP 10.2 Table 12 (price/CO2/PEF) +packages/domain/src/domain/sap/tables/table_12a.py Off-peak high-rate fraction +packages/domain/src/domain/sap/tables/table_32.py RdSAP 10 Table 32 (cost prices) -Still deferred (in approximate priority): +packages/domain/src/domain/sap/worksheet/ + dimensions.py §1 + ventilation.py §2 + VentilationResult + heat_transmission.py §3 + HeatTransmission + water_heating.py §4 + WaterHeatingResult + water_heating_from_cert + internal_gains.py §5 + InternalGainsResult + internal_gains_from_cert + solar_gains.py §6 + solar_gains_from_cert + mean_internal_temperature.py §7 + space_heating.py §8 + SpaceHeatingResult + fabric_energy_efficiency.py §8f + space_cooling.py §8c + fuel_cost.py §10a + FuelCostResult + rating.py §11/§13 SAP rating equations (10.2 constants — DO NOT TOUCH) -### Worksheet / heat transmission -- **Windows/doors residual** — Ticket 3 above. -- Tables 3b + 3c rows 2-5 (storage / FGHRS variants) — no fixture exercises. -- Table 3b storage / FGHRS rows + Electric CPSU Appendix F. -- (247a) Instant electric shower kWh routing. -- (252) per-row Appendix M/N split. -- (253)/(254) Appendix Q routes. - -### Heating -- **Appendix N heat-pump cascade via PCDB Table 362** (replaces SCOP 2.30 Table 4a fallback for `main_category=4`). -- **Table D1/D2/D3 Ecodesign condensing-boiler control-class corrections**. -- Two-main heating system §9a (213) — single-main currently default. - -### Pumps/fans Table 4f -- Currently only category 2 (gas combi) is keyed to 160 kWh/yr; categories 3 (oil), 4 (heat pump), 5 (warm-air), 7 (electric storage), 12 (micro-CHP) all fall back to the legacy 130 sentinel. - -### Cooling -- Table 10c SEER → cooling fuel kWh — all 6 Elmhurst have `has_fixed_air_conditioning=False`. - -### Mapper -- **Ticket 2** above: `EpcPropertyDataMapper` → `SapRoomInRoof.detailed_surfaces` + Type 2 fields. - -### Infra -- Drop legacy scalar fuel-cost fields from `CalculatorInputs` once synthetic test corpus migrates to `fuel_cost=...` composite. +packages/domain/src/domain/sap/worksheet/tests/ + test_section_cascade_pins.py Strict per-section line-ref pins (THE work) + test_e2e_elmhurst_sap_score.py SapResult-field pins + monthly_infiltration_ach pin + _elmhurst_worksheet_NNNNNN.py The 6 fixture modules (1 per fixture) + _elmhurst_fixtures.py ALL_FIXTURES registry + test_dimensions.py / _ventilation.py / _heat_transmission.py / ... + ← LEGACY per-section isolation tests; use PDF values as INPUTS. + Keep them but understand they don't test the cascade. +``` --- -## §G — Definitely do NOT +## §F — Definitely do NOT -- Do **not** loosen the existing component pins to mask drift. Tickets 1-3 are real engine fixes; closure tightens, not loosens. -- Do **not** scan more than ~50 lines of spec PDF without asking the user for the specific page/table range. -- Do **not** touch the SAP rating constants in `worksheet/rating.py` — they're SAP 10.2 (per `a41ac6bd`) and pinned by 8+ tests. -- Do **not** invoke `/ultrareview` yourself — user-triggered only. +- Do **not** widen any tolerance, ever. +- Do **not** add xfail to cascade pins. +- Do **not** "investigate later" by widening — fix it or leave it failing. +- Do **not** assume the calculator is wrong before auditing the fixture. +- Do **not** touch `rating.py` constants. +- Do **not** scan unread spec PDF pages without asking the user. +- Do **not** invoke `/ultrareview`. +- Do **not** auto-update unrelated `git status` items (deletions / new files + that aren't from your work). --- -End of handover. Read in full before `/grill-me`. +## §G — Quick orient + +```bash +# Run full cohort pin matrix +python -m pytest \ + packages/domain/src/domain/sap/worksheet/tests/test_section_cascade_pins.py \ + packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py \ + --no-header --no-cov --tb=no -q + +# Run §3 pins only +python -m pytest packages/domain/src/domain/sap/worksheet/tests/test_section_cascade_pins.py::test_section_3_line_refs_match_pdf -v --no-header --no-cov --tb=no + +# Run a single SapResult pin to see numeric diff +python -m pytest \ + "packages/domain/src/domain/sap/worksheet/tests/test_e2e_elmhurst_sap_score.py::test_sap_result_pin[000477-space_heating_kwh_per_yr]" \ + --no-cov 2>&1 | grep AssertionError + +# PDF §X block +awk '/^X\. Section/,/^Y\./' "sap worksheets/U985-0001-NNNNNN.txt" +``` + +End of handover. Read §A again before starting.