From ec4916b5a7645a447da97eddc568bb51eb42eeb3 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Sun, 24 May 2026 20:21:27 +0000 Subject: [PATCH] Handover: 2/6 Elmhurst chains closed at 1e-4; per-cert diagnoses for remaining 4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates NEXT_AGENT_PROMPT.md after Slices 47/48/49. State at hand-off: 000474 Δ=0.0000 ✓ Slice 47 000477 Δ=2.6555 Room-in-Roof support needed (15.06 m² 3rd storey) 000480 Δ=4.1955 diagnosis pending 000487 Δ=4.4553 extractor drops most §11 windows on this layout 000490 Δ=0.0000 ✓ Slice 49 000516 Δ=1.5162 roof-window separation (1 of 6 extracted windows is actually a roof window per handbuilt fixture) Each remaining cert needs its own schema/extractor/mapper extension — documented with file/method pointers and recommended slice ordering. Co-Authored-By: Claude Opus 4.7 --- docs/sap-spec/NEXT_AGENT_PROMPT.md | 164 +++++++++++++++-------------- 1 file changed, 85 insertions(+), 79 deletions(-) diff --git a/docs/sap-spec/NEXT_AGENT_PROMPT.md b/docs/sap-spec/NEXT_AGENT_PROMPT.md index 266fe437..eadfaf07 100644 --- a/docs/sap-spec/NEXT_AGENT_PROMPT.md +++ b/docs/sap-spec/NEXT_AGENT_PROMPT.md @@ -1,78 +1,88 @@ -# Handover — close the remaining 5 Elmhurst Summary→SAP chains to 1e-4 +# Handover — close the remaining 4 Elmhurst Summary→SAP chains to 1e-4 -You are picking up branch `ara-backend-design-prd` mid-stream. The -previous chain of work closed **Summary_000474** to 1e-4 (Slice 47) -and landed extractor infrastructure that helps the other 5 fixtures -(Slice 48), but each of the 5 remaining certs still has its own diff -to close. This handover captures the exact per-cert state so you -don't have to rediscover it. +You are picking up branch `ara-backend-design-prd` mid-stream. Two +of six fixtures are now closed at 1e-4 (000474 + 000490). The other +four still have substantive diffs that each need their own mapper +or extractor extension to close. ## The 30-second picture ``` -Cert Mapped SAP Target SAP Δ Notes -000474 62.2584 62.2584 0.0000 ✓ CLOSED (Slice 47) -000477 71.3712 65.0057 +6.3655 secondary heating + lighting -000480 69.5681 61.2986 +8.2695 unknown (largest gap) -000487 69.7864 61.6431 +8.1433 window count 1/many (extractor) + others -000490 63.0530 57.3979 +5.6551 unknown -000516 68.7749 62.7937 +5.9812 roof window separation +Cert Mapped SAP Target SAP Δ State +000474 62.2584 62.2584 0.0000 ✓ Slice 47 +000477 67.6612 65.0057 +2.6555 Room-in-Roof support (15.06 m² 3rd storey) +000480 65.4941 61.2986 +4.1955 diagnosis pending +000487 66.0984 61.6431 +4.4553 extractor drops most §11 windows +000490 57.3979 57.3979 0.0000 ✓ Slice 49 +000516 64.3099 62.7937 +1.5162 roof-window separation ``` -All Δ are positive — the mapper is computing SAP too HIGH, which -typically means missing cost contributions (secondary heating fuel, -lighting, etc.) or under-counted heat-loss area. - -Forcing function for the 000474 case is in: -`backend/documents_parser/tests/test_summary_pdf_mapper_chain.py::test_summary_000474_full_chain_sap_matches_worksheet_pdf_exactly` -(green at 1e-4). When you close each remaining cert, add a mirror -test next to it. +All Δ are positive — mapper computes SAP too HIGH. Forcing functions +in `backend/documents_parser/tests/test_summary_pdf_mapper_chain.py`: +2 chain tests green (000474, 000490), 2 structural tests green (BPs, +windows for 000474). 4 mirror tests to add as each closes. ## What landed already | Slice | Commit | What | |---|---|---| -| 47 | `29ab80b0` | `main_heating_category=2` in mapper → pumps_fans 130→160; window-gap partitioned on glazing-type-start marker → fixes W4/W5 orientation mis-classification | -| 48 | (this slice) | Extractor handles combined `Wood 0.70` frame line; data anchor allows trailing glazing-type (`1.22 1.76 2.15 Double pre 2002`); partition falls back to second-orient-token when no glazing marker in gap; 5 fixture PDFs copied to `backend/documents_parser/tests/fixtures/` | +| 47 | `29ab80b0` | `main_heating_category=2` in mapper → pumps_fans 130→160; window-gap partitioned on glazing-type-start marker → fixes W4/W5 orientation. Closed 000474. | +| 48 | `00a27efd` | Extractor handles `Wood 0.70` combined frame-line; data anchor allows trailing glazing-type (`1.22 1.76 2.15 Double pre 2002`); partition falls back to second-orient-token when no glazing marker in gap. 5 fixture PDFs copied to `backend/documents_parser/tests/fixtures/`. | +| 49 | `7f17de84` | Elmhurst `MainHeating.secondary_heating_sap_code` extracted from §14.1 Main Heating2 sub-section; mapper threads through to `SapHeating.secondary_heating_type`. RdSAP §S5 sheltered-sides defaults derived from built_form. Closed 000490. | ## Per-cert diff diagnoses (already done — don't re-discover) -### 000477 — Δ +6.37 — single-BP cert -Run `python /tmp/diff_cert.py 000477` to reproduce. Scalar input diffs: -- `secondary_heating_fraction`: mapped=**0.0** vs handbuilt=**0.1** ← root cause. Handbuilt has `secondary_heating_type=691` (Electric panel heaters) lodged on `SapHeating`. Mapper doesn't extract from §10 "Secondary heating" Summary PDF section. -- `lighting_kwh_per_yr`: mapped=160.88 vs handbuilt=201.68 — bulb count mismatch -- `secondary_heating_co2_factor_kg_per_kwh`, `secondary_heating_primary_factor`: None vs populated — downstream of the missing secondary_heating_type +### 000477 — Δ +2.66 — Room-in-Roof support needed +Summary PDF §4.0 lodges `Room(s) in Roof: 15.06` (m²). Mapper drops +this entirely — `total_floor_area_m2=62.52` vs handbuilt 77.58 +(exactly +15.06 m²); `storey_count=2` vs 3; `roof_w_per_k=4.38` vs +35.97. Look at: -**Fix**: Extend `ElmhurstSiteNotesExtractor` to extract §10 Secondary heating fields, and `_map_elmhurst_sap_heating` to populate `secondary_heating_type` + `secondary_fuel_type` on the `SapHeating`. +- `ElmhurstSiteNotes.BuildingPartDimensions` — needs a + `room_in_roof_area_m2` field (and probably room-in-roof construction + + insulation details too). +- `ElmhurstSiteNotesExtractor._extract_dimensions` (or wherever §4 is + handled) — parse the "Room(s) in Roof" line. +- `from_elmhurst_site_notes` — build a `SapRoomInRoof` and attach to + the building part. See existing API paths like + `datatypes/epc/domain/mapper.py:749-760` for the API-side analog. +- Handbuilt: see `_elmhurst_worksheet_000477.py` `sap_room_in_roof=...`. -### 000487 — Δ +8.14 — only 1 window extracted (should be many) +Once Room-in-Roof lands, the other diffs (party_walls, infiltration, +lighting kWh) may shrink as the dwelling shape becomes correct. + +### 000487 — Δ +4.46 — only 1 of many windows extracted Despite Slice 48's frame-line + data-anchor improvements, the §11 table layout of this cert still drops most windows. Run -`python /tmp/dump_one.py 000487` to see the parsed window. Inspect -the raw layout via `python /tmp/dump_section.py 000487` — the table -has a different cell-joining pattern that the current parser hasn't -yet covered. +`python /tmp/dump_section.py 000487` to inspect — this PDF has yet +another layout pattern (see lines 14-50 in the section dump). Look +at the actual fields: `'1st'` ordinal in prefix WITHOUT a preceding +glazing-type line, etc. -### 000516 — Δ +5.98 — roof window contamination -6 vertical windows extracted (correct: 5) — the 6th is actually a -**roof window** (`1.05×1.12=1.18 m² U=3.10 NE`, pitch 45°) being -treated as vertical. Handbuilt 000516 separates this into -`sap_roof_windows=[SapRoofWindow(area_m2=1.18, u_value_raw=3.40, -orientation=2 NE, pitch_deg=45.0, ...)]`. +### 000516 — Δ +1.52 — roof-window contamination +6 vertical windows extracted (correct: 5 vertical + 1 roof). The 6th +is a roof window (`1.05×1.12=1.18 m² U=3.10` lodged → U=2.99 in +worksheet line 27a, but handbuilt uses `u_value_raw=3.40` — needs +investigation of how Elmhurst computes the in-situ value). Handbuilt: -**Fix**: Add roof-window extraction in `ElmhurstSiteNotesExtractor` -(likely a "Roof Windows" subsection of §11). Map to `SapRoofWindow` -not `SapWindow`. +```python +sap_roof_windows=[SapRoofWindow( + area_m2=1.18, u_value_raw=3.40, orientation=2 NE, + pitch_deg=45.0, g_perpendicular=0.76, frame_factor=0.70, +)] +``` -### 000480 — Δ +8.27 — diagnosis pending -Run `python /tmp/diff_cert.py 000480` to start. Two BPs (Main + ext) -extracted correctly, 7 windows extracted. Largest gap of the 5 — may -have multiple compounding issues. +The Summary PDF doesn't have an explicit "roof window" type marker — +the discrimination is by **U-value range** (>3.0 W/m²K characteristic +of roof window) or by some other heuristic. Investigate which signal +Elmhurst uses; may also need a `Roof Windows` subsection if a later +fixture has one. -### 000490 — Δ +5.66 — diagnosis pending -Same: `python /tmp/diff_cert.py 000490`. +### 000480 — Δ +4.20 — diagnosis pending +Run `python /tmp/diff_objects.py 000480` to start. Already has 2 BPs ++ 7 windows correctly extracted post-Slice 48. -## Probe scripts (already in `/tmp`) +## Probe scripts (in `/tmp`) ```bash # Probe all 6 fixtures' SAP delta @@ -84,35 +94,31 @@ python /tmp/dump_one.py 000516 # Dump the §11 Windows section layout (debug extractor) python /tmp/dump_section.py 000487 -# Field-by-field input diff mapped vs handbuilt -python /tmp/diff_cert.py 000477 +# Field-by-field input diff mapped vs handbuilt (scalars + objects) +python /tmp/diff_objects.py 000477 + +# Verify secondary heating + sheltered-sides plumbing +python /tmp/check_secondary.py ``` -If `/tmp` got wiped, the contents are: -- `probe_all.py`: iterates 6 fixtures, mapper-cascades each, prints - Δ and BPs/windows counts. -- `dump_one.py `: prints raw `ElmhurstSiteNotes.windows` for - the given cert. -- `dump_section.py `: prints the line-by-line `§11 Windows` - section from the layout-preprocessed pages. -- `diff_cert.py `: cascades both mapped and handbuilt EPCs - through `cert_to_inputs`, diffs the scalar input fields. +If `/tmp` got wiped, the contents are documented in commit `7f17de84`'s +diff (the prior handover writeup). Re-create them by following the +patterns there. ## Suggested next slices | Slice | Cert | Effort | Why next | |---|---|---|---| -| 49 | 000477 | Medium | 1 BP topology → smallest moving parts; secondary-heating extraction unlocks the largest single fix | -| 50 | 000516 | Medium | Roof-window separation — adds new schema field, generalisable | -| 51 | 000487 | High | Extractor parser improvements (the §11 layout is uncovered terrain) | -| 52 | 000490 | Unknown | Diagnose then close | -| 53 | 000480 | Unknown | Largest Δ; do last after others reveal patterns | +| 50 | 000516 | Medium | Roof-window separation by U-value threshold; smallest remaining Δ | +| 51 | 000477 | High | Room-in-Roof schema + extractor + mapper plumbing; biggest unlock | +| 52 | 000487 | Medium-High | Extractor §11 layout — third PDF variant; may share work with 000477's room-in-roof if §11 layout matches | +| 53 | 000480 | Unknown | Diagnose then close after others reveal patterns | -## Definition of done (unchanged from before) +## Definition of done (unchanged) - All 6 `test_summary_pdf_mapper_chain.py` chain tests pin at 1e-4. -- Wider regression stays green (currently 754 pass). -- Pyright net-zero on every commit (strict mode). +- Wider regression stays green (currently 755 pass). +- Pyright net-zero on every commit (strict mode; baseline 35 errors). - One slice = one commit. - No widening, no xfail (project memory `feedback_zero_error_strict`). @@ -120,20 +126,20 @@ If `/tmp` got wiped, the contents are: - `docs/sap-spec/HANDOVER_NEXT.md` — original cascade-conventions handover. - `docs/sap-spec/SAP_CALCULATOR.md` — public API + two-cascade architecture. -- Hand-built worksheet fixtures: `packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_*.py` — the calculator-equivalent EpcPropertyData each mapped chain must reproduce. -- Untracked source PDFs: `sap worksheets/` (`Summary_NNNNNN.pdf` + `U985-0001-NNNNNN.pdf` worksheets — the unrounded SAP target lives next to "SAP value" in the U985 PDF). +- Hand-built worksheet fixtures: `packages/domain/src/domain/sap/worksheet/tests/_elmhurst_worksheet_*.py`. +- Untracked source PDFs: `sap worksheets/` (`Summary_NNNNNN.pdf` + `U985-0001-NNNNNN.pdf` worksheets). ## Branch state at handover ``` $ git log --oneline -7 - Slice 48: Elmhurst extractor handles 5 new layout quirks; 5 fixture PDFs added -29ab80b0 Slice 47: Summary_000474 chain pins SAP at 1e-4 vs worksheet PDF -b6544e1c Handover: tighten Summary→SAP chain pin to 1e-4 + brief next agent -256a5afe Slice 46c: Elmhurst mapper produces calculator-equivalent EpcPropertyData — Summary_000474 SAP within 0.5 of worksheet PDF -066dce19 Slice 46b: Elmhurst extractor parses windows from layout-style Summary PDFs -36f2c7bb Slice 46a: Elmhurst mapper handles multi-bp Summary PDFs — Summary_000474 chain test flips green -ccf7aa21 Scaffold: end-to-end Summary→EpcPropertyData chain test for 000474 (xfail) +7f17de84 Slice 49: Summary_000490 chain pins SAP at 1e-4; secondary heating + RdSAP sheltered-sides +00a27efd Slice 48: Elmhurst extractor handles 3 new layout quirks; 5 fixture PDFs added +29ab80b0 Slice 47: Summary_000474 chain pins SAP at 1e-4 vs worksheet PDF +b6544e1c Handover: tighten Summary→SAP chain pin to 1e-4 + brief next agent +256a5afe Slice 46c: Elmhurst mapper produces calculator-equivalent EpcPropertyData — Summary_000474 SAP within 0.5 of worksheet PDF +066dce19 Slice 46b: Elmhurst extractor parses windows from layout-style Summary PDFs +36f2c7bb Slice 46a: Elmhurst mapper handles multi-bp Summary PDFs — Summary_000474 chain test flips green ``` Good luck.