mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
HANDOVER_NEXT: rewrite for strict zero-error cascade pin closure
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 <noreply@anthropic.com>
This commit is contained in:
parent
ac68cf88a0
commit
61e369faf7
1 changed files with 327 additions and 159 deletions
|
|
@ -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 <noreply@anthropic.com>` trailer.
|
||||
- **Don't widen ceilings to hide bugs.** Per `feedback-e2e-validation-philosophy` memory: component pins at <abs=1e-4 against U985 line refs; SAP integer = PDF integer is the integration gate.
|
||||
Spec PDFs in `docs/sap-spec/`: SAP 10.2 (14-03-2025), RdSAP 10 (10-06-2025), PCDF.
|
||||
|
||||
---
|
||||
|
||||
## §A — Current state on `ara-backend-design-prd`
|
||||
## §A — Hard rules. Internalise these BEFORE anything else.
|
||||
|
||||
Last commits this session (newest first):
|
||||
### A.1 What this project IS
|
||||
|
||||
```
|
||||
4ac4f7da Cohort residual slice 14: 000477 detailed RR lodgement closes to delta=0
|
||||
1928e5a2 Cohort residual slice 13: Detailed §3.10 RR geometry — per-surface lodgement
|
||||
3ff864bf Cohort residual slice 12: Simplified Type 2 RR geometry (common walls <1.8m)
|
||||
4df05685 Cohort residual slice 11: Simplified Type 1 RR geometry — _part_geometry + heat_transmission
|
||||
0ff81445 Cohort residual slice 10: u_rr_slope / u_rr_flat_ceiling / u_rr_stud_wall — RdSAP10 Table 17
|
||||
82627ebb Cohort residual slice 9: u_rr_default_all_elements — RdSAP10 Table 18 col (4)
|
||||
639b7ee2 Cohort residual slice 8: 000477 xfail re-diagnosed (briefly; un-xfailed in 4ac4f7da)
|
||||
62bbf863 Cohort residual slice 7: PCDB override routes separate_dhw_tests∈{2,3} through Table 3c
|
||||
b01164a2 Cohort residual slice 6: Table 3c row 1 helper + DVF piecewise (M+L / M+S)
|
||||
```
|
||||
This repo replicates the **rdSAP calculation engine** to bit-level fidelity
|
||||
against 6 known test vectors (the U985 Elmhurst worksheets):
|
||||
|
||||
**Test status:** 314 worksheet + rdsap + ml-rdsap_uvalues tests pass, **0 xfails**. Full Elmhurst e2e suite green.
|
||||
- **Inputs**: Summary_NNNNNN.pdf (cert lodgement) for each of 6 fixtures
|
||||
(000474, 000477, 000480, 000487, 000490, 000516).
|
||||
- **Intermediate values**: U985-0001-NNNNNN.{pdf,txt} lodges every
|
||||
worksheet line ref (1) through (258+) to 4 decimal places.
|
||||
- **Final outputs**: SAP rating (continuous + integer), ECF, total fuel cost,
|
||||
CO2, primary energy, per-end-use kWh.
|
||||
|
||||
### SAP integer status (cohort)
|
||||
It is a deterministic numerical function with fully-known test vectors.
|
||||
|
||||
| fixture | actual SAP | PDF | Δ | what closed it / what remains |
|
||||
|---|---|---|---|---|
|
||||
| 000474 | 62 | 62 | **0 ✓** | unchanged this session |
|
||||
| **000477** | **65** | **65** | **0 ✓ NEW** | Table 3c + detailed RR + door_count=1 |
|
||||
| 000480 | 65 | 61 | +4 | needs build_epc lodgement (mirror 000477) |
|
||||
| 000487 | 65 | 62 | +3 | needs build_epc lodgement |
|
||||
| 000490 | 57 | 57 | **0 ✓** | unchanged this session |
|
||||
| 000516 | 67 | 63 | +4 | needs build_epc lodgement |
|
||||
### A.2 The bar: abs=1e-4 on EVERY pin, every fixture, every line ref
|
||||
|
||||
### What landed this session
|
||||
**Every SAP-result field AND every section line ref must pin to PDF at abs=1e-4.**
|
||||
|
||||
**Table 3c two-profile combi loss (slices 6-7):**
|
||||
- `combi_loss_monthly_kwh_table_3c_two_profile_instantaneous` + `_table_3c_dvf` (M+L / M+S piecewise DVF) in [water_heating.py](../../packages/domain/src/domain/sap/worksheet/water_heating.py).
|
||||
- `pcdb_combi_loss_override` (renamed from `_pcdb_table_3b_combi_loss_override`) routes PCDF `separate_dhw_tests ∈ {2, 3}` through Table 3c. Match-statement gate in [cert_to_inputs.py:726-790](../../packages/domain/src/domain/sap/rdsap/cert_to_inputs.py#L726-L790).
|
||||
- Element-wise LINE_61 pin at abs=1e-3 against 000477's U985 PDF.
|
||||
- The PDF lodges 4 d.p. display precision. abs=1e-4 is the floor of "match what
|
||||
the PDF says".
|
||||
- **No `rel=...` tolerances.** Slice 19b removed `rel=0.15` (fuel cost) and
|
||||
`rel=0.05` (fuel cost) precedents. Never re-add these.
|
||||
- **No `<= 0.5` continuous SAP ceilings.** Slice 19a removed these. Never re-add.
|
||||
- **No `xfail` markers on cascade pins.** A failing pin is a calculator bug or
|
||||
fixture defect to fix.
|
||||
- **No "documented widening".** There is no such thing for this project.
|
||||
|
||||
**RdSAP10 Room-in-Roof cascade (slices 9-13):**
|
||||
- Three new public lookups in [rdsap_uvalues.py](../../packages/domain/src/domain/ml/rdsap_uvalues.py): `u_rr_slope` (Table 17 col 1), `u_rr_flat_ceiling` (col 2), `u_rr_stud_wall` (col 3), plus `u_rr_default_all_elements` (Table 18 col 4, "Room-in-roof, all elements" with Scotland age-K override).
|
||||
- `SapRoomInRoof` extended with optional Simplified Type 2 fields (`common_wall_length_m` / `_height_m`, two `gable_*_length_m` / `_height_m` pairs) and a `detailed_surfaces: List[SapRoomInRoofSurface]` for §3.10 Detailed lodgement. Each `SapRoomInRoofSurface` carries `kind` (`"slope"` / `"flat_ceiling"` / `"stud_wall"` / `"gable_wall"`), `area_m2`, optional `insulation_thickness_mm`, `insulation_type`.
|
||||
- `_part_geometry` and `heat_transmission_from_cert` in [heat_transmission.py](../../packages/domain/src/domain/sap/worksheet/heat_transmission.py) extended to route all three RR paths:
|
||||
- **Simplified Type 1** (only `floor_area` lodged): `A_RR = 12.5 × √(A_RR_floor/1.5)` at `u_rr_default_all_elements`. Storey-below roof area deducted by `A_RR_floor` per §3.9.
|
||||
- **Simplified Type 2** (`common_wall_height_m < 1.8`): `A_common_wall = L × (0.25 + H)`, `A_gable = L × (0.25 + H_gable) - Σ((H_gable - H_common_wall)²/2)`. Common walls + gables route to `walls_w_per_k` at `U_main_wall`. `A_RR_final = A_RR - Σ` routes to `roof_w_per_k`.
|
||||
- **Detailed §3.10** (`detailed_surfaces` lodged): each surface contributes A × U via Table 17 / Table 4. Slope+flat_ceiling+stud_wall → `roof_w_per_k`; gable_wall → `party_walls_w_per_k` at U=0.25.
|
||||
If a pin can't be closed in the current slice, **leave it failing**. The failing
|
||||
pin is the next slice's work. Tolerances are NEVER widened to make the suite
|
||||
green. CI red is fine while bugs are being fixed.
|
||||
|
||||
**000477 fixture closure (slice 14):**
|
||||
- `_elmhurst_worksheet_000477.py` updated with detailed RR (6 surfaces from U985 PDF lines 188-198), `roof_insulation_thickness=300`, and `door_count=1` (U985 line 42 lodges single external door).
|
||||
- 000477 e2e SAP integer un-xfailed.
|
||||
### A.3 Past-agent mistakes — DO NOT REPEAT
|
||||
|
||||
**Known parked drift (slice 14):**
|
||||
- Golden cert `0240-0200-5706-2365-8010` (detached, TFA 202, age J) drifted Δ=0 → Δ=-12 because its API response has rich RR lodgement (`room_in_roof_type_1.gable_wall_length_1/2`, description "Roof room(s), insulated (assumed)") that `EpcPropertyDataMapper.from_api_response` doesn't yet extract. `_SAP_TOLERANCE` widened 11 → 13 with documentation. Closes once **Ticket 2** below lands.
|
||||
The user is frustrated with previous agents because:
|
||||
|
||||
1. **Treated SAP integer Δ=0 as "closed"** — that's a weak gate (hides ±0.5
|
||||
continuous drift). The real gate is per-line-ref abs=1e-4.
|
||||
2. **Widened tolerances** to make tests green (`rel=0.15`, `<=0.5`). Every such
|
||||
widening masked a real residual.
|
||||
3. **Tested sections in isolation** using `fixture.LINE_X` PDF values AS INPUTS.
|
||||
That doesn't test the cascade — it tests the section formula given correct
|
||||
inputs. The cascade can still drift.
|
||||
4. **Missed fixture defects** — multiple fixtures had missing or wrong
|
||||
lodgement (bulbs, windows, sap_heating, detailed RR, exposed_floor,
|
||||
door_count, per-window U). When a cascade pin fails, ALWAYS audit the
|
||||
fixture against the PDF first.
|
||||
5. **Labelled code "SAP 10.3"** when implementing SAP 10.2 (mostly cleaned in
|
||||
slice 21a; `tables/table_12.py` retains intentional 10.2-vs-10.3
|
||||
comparison).
|
||||
6. **Diagnosed downstream first**. The cascade is upstream→downstream
|
||||
(§1 → §2 → §3 → §4 → §5 → §6 → §7 → §8 → §9a → §10a → §11a → §12). A
|
||||
downstream pin failure (e.g. `total_fuel_cost_gbp`) is meaningless to
|
||||
diagnose until upstream pins close.
|
||||
|
||||
If you find yourself about to widen a tolerance, add an xfail, or skip a
|
||||
fixture — **stop and ask the user.** Those are anti-patterns for this project.
|
||||
|
||||
### A.4 Workflow rules
|
||||
|
||||
- **Don't scan >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
|
||||
`<section>_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
|
||||
`<section>_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_<NAME>` 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_<thing>_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.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue