mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
docs: handover + next-agent prompt post S0380.91..95 (party-wall + AP4/MEV + §5.14 floor + RIR insulation + Detailed-RR residual)
This commit is contained in:
parent
a8d6568cbf
commit
25f3af9eba
2 changed files with 512 additions and 0 deletions
258
domain/sap10_calculator/docs/HANDOVER_POST_S0380_95.md
Normal file
258
domain/sap10_calculator/docs/HANDOVER_POST_S0380_95.md
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
# Handover — post S0380.91..95 (party-wall + AP4/MEV + §5.14 floor + RIR insulation + Detailed-RR residual)
|
||||
|
||||
Branch: `feature/per-cert-mapper-validation`. **HEAD `fa6974bd`**.
|
||||
Predecessor: [`HANDOVER_POST_S0380_90.md`](HANDOVER_POST_S0380_90.md).
|
||||
|
||||
## Slices committed this session (S0380.91..95)
|
||||
|
||||
Five spec-cited slices targeting cert 000565 closure. The user
|
||||
clarified their primary metric is **`sap_score_continuous`** (not
|
||||
just integer `sap_score`), but is OK with temporary continuous-SAP
|
||||
drift as long as each slice closes a true spec-correct sub-component
|
||||
gap. Zero error is the eventual goal; achievable only when every
|
||||
component is spec-correct.
|
||||
|
||||
| Slice | Commit | Spec | Cert 000565 outcome |
|
||||
|---|---|---|---|
|
||||
| **S0380.91** | `83218630` | RdSAP 10 §5.10 Table 15 row 3 (PDF p.42) — "Cavity masonry filled: 0.2 W/m²K" | party_walls 93.26 → **65.13 ✓ EXACT**. sap_score 27 → 28 (Δ-2 → -1). SH +1460 → +631 (57% closed). Continuous SAP Δ-1.16 → -0.52. New synthetic SAP10 code `WALL_CAVITY_FILLED_PARTY=11`. |
|
||||
| **S0380.92** | `a7894b11` | SAP 10.2 §2 (17a)/(18)/(23a)/(24c) AP4 + MEV | **sap_score 28 → 29 ✓ EXACT**. cascade (18) pressure_test_ach 2.4037 → **2.0287 ✓ EXACT** vs ws 2.0287. SH +631 → -367 (~75% closed). 5-layer slice: AP4 + MEV-decentralised plumbing across schema / extractor / mapper / cert_to_inputs. Coupling-aware bundling. |
|
||||
| **S0380.93** | `23aaa4fa` | RdSAP 10 §5.14 (PDF p.47) — "Floor above partially heated: 0.7 W/m²K" | BP[1] floor U: 0.76 → **0.70 ✓ EXACT**. floor_w_per_k 72.41 → 70.37 (Δ+10.74 → +8.70). sap_score 29 ✓ EXACT unchanged. Continuous SAP +0.26 → +0.30 (small drift). |
|
||||
| **S0380.94** | `78c57c0d` | RdSAP 10 Table 17 col 3b (PDF p.42-43) — "Stud wall PUR or PIR 400mm: 0.10 W/m²K" + extractor regex fixes | BP[2] Stud Wall 2 cascade U: 2.30 → **0.10 ✓ EXACT**. 4-layer slice: extractor regex `^\d+\+?\s*mm$` + mapper allow-list ("PUR or PIR") + canonical insulation_type "rigid_foam" + cascade `_is_rigid_foam`. roof_w_per_k 43.44 → 34.64 (closed 8.80 over-count). Continuous SAP Δ+0.26 → +0.51 (drift). |
|
||||
| **S0380.95** | `fa6974bd` | RdSAP 10 §3.10.1 + §3.9.1 (PDF p.21-22, p.24) — Detailed-RR residual area cascade | **thermal_bridging 116.89 → 129.35 ✓** vs ws 128.65 (Δ-11.76 → +0.70). **total_external_area 779.27 → 862.34 ✓** vs ws 857.64 (Δ-78.37 → +4.70). Big-leverage closure of the cascade -78 m² area gap. sap_score 29 → 28 transient regression (continuous crossed 28.5 → 28.07). Continuous SAP Δ+0.51 → -0.44 (absolute residual slightly closer to zero). |
|
||||
|
||||
**Test baseline at HEAD `fa6974bd`:** 585 pass + 9 expected `000565`
|
||||
fails (was 574 + 9 at start of session). Cohort (000474/000477/000480/
|
||||
000487/000490/000516) + 9 golden API + 38 cohort-2 API all
|
||||
unaffected via discriminators (S0380.91: scoped to new code 11;
|
||||
S0380.92: defaulted None for cohort certs without AP4/MEV; S0380.93:
|
||||
floor type "Above partially heated" only on cert 000565 Ext1;
|
||||
S0380.94: cohort uses "Mineral or EPS" not "PUR or PIR"; S0380.95:
|
||||
discriminator filters out true Detailed-mode lodgements with full
|
||||
shell enumeration).
|
||||
|
||||
Pyright net-zero per touched file across every slice.
|
||||
|
||||
## Cert 000565 state (HEAD `fa6974bd`)
|
||||
|
||||
### Fabric subtotals (post-S0380.95)
|
||||
|
||||
| Component | Cascade W/K | Worksheet W/K | Δ | Notes |
|
||||
|---|---:|---:|---:|---|
|
||||
| walls | 601.22 | 604.07 | -2.85 | sub-spec |
|
||||
| **party_walls** | **65.13** | 65.13 | ✓ EXACT | S0380.91 |
|
||||
| floor | 70.37 | 61.67 | +8.70 | BP[2] Ext2 200mm insulation extractor gap |
|
||||
| roof | 63.72 | 51.38 | +12.34 | BP[4] FC1 + BP[1] residual (see below) |
|
||||
| windows | 9.60 | 11.48 | -1.88 | sub-spec |
|
||||
| roof_windows | 5.02 | 3.58 | +1.44 | sub-spec |
|
||||
| **doors** | **11.10** | 11.10 | ✓ EXACT | full pipeline plumbing |
|
||||
| **thermal_bridging** | **129.35** | 128.65 | +0.70 | S0380.95 |
|
||||
| **HTC fabric** | **966.51** | 937.06 | +29.45 | Net cascade over by ~29 W/K |
|
||||
|
||||
### Ventilation subtotals (post-S0380.92)
|
||||
|
||||
| Line | Cascade | Worksheet | Δ |
|
||||
|---|---:|---:|---:|
|
||||
| (18) pressure_test_ach | 2.0287 | 2.0287 | ✓ EXACT |
|
||||
| (21) shelter-adj ach | 1.7244 | 1.7244 | ✓ EXACT |
|
||||
| mean (25)m | 2.1360 | 2.1360 | ✓ EXACT (was +0.149 over) |
|
||||
| mv_kind | EXTRACT_OR_PIV_OUTSIDE | (24c) | ✓ correct |
|
||||
| mv_system_ach | 0.5 | (23a) 0.5 | ✓ EXACT |
|
||||
|
||||
### SapResult pins (HEAD `fa6974bd`)
|
||||
|
||||
| Pin | Cascade | Worksheet | Δ | Cause |
|
||||
|---|---:|---:|---:|---|
|
||||
| **sap_score (int)** | **28** | 29 | -1 | Continuous below 28.5 threshold |
|
||||
| sap_score_continuous | 28.07 | 28.51 | -0.44 | Cascade SH over-count drives lower SAP |
|
||||
| ecf | 5.43 | 5.39 | +0.05 | Downstream |
|
||||
| total_fuel_cost_gbp | 4720.79 | 4680.26 | +40.53 | Downstream |
|
||||
| co2_kg_per_yr | 6497.82 | 6447.63 | +50.20 | Downstream |
|
||||
| space_heating_kwh | 59541.61 | 59008.35 | **+533.26** | Cascade HTC over |
|
||||
| main_heating_fuel | 35031.76 | 34710.79 | +320.96 | SH × 1/COP=1.70 |
|
||||
| **hot_water_kwh** | 3755.03 | 3755.03 | ✓ 0 EXACT | unchanged |
|
||||
| lighting | 1387.02 | 1384.84 | +2.19 | sub-spec |
|
||||
| pumps_fans | 255.00 | 252.52 | +2.48 | MEV PCDB external data |
|
||||
|
||||
### Continuous SAP journey across this session
|
||||
|
||||
| Slice | sap_score (int) | sap_score_continuous | Δ vs ws |
|
||||
|---|---:|---:|---:|
|
||||
| Pre-S0380.91 | 27 | 27.35 | -1.16 |
|
||||
| S0380.91 | 28 | 27.99 | -0.52 |
|
||||
| S0380.92 | 29 | 28.77 | +0.26 |
|
||||
| S0380.93 | 29 | 28.81 | +0.30 |
|
||||
| S0380.94 | 29 | 29.02 | +0.51 |
|
||||
| **S0380.95** | **28** | **28.07** | **-0.44** |
|
||||
|
||||
User direction: continuous SAP residual is the primary metric.
|
||||
Temporary drift is OK when fixing real spec gaps; zero error
|
||||
achievable only when every component is spec-correct.
|
||||
|
||||
## Open work — prioritised next slices
|
||||
|
||||
### S0380.96 — BP[4] Flat Ceiling 1 "Unknown PUR or PIR" lodgement (highest leverage)
|
||||
|
||||
**Spec-divergence question:** worksheet shows U=0.15 for the lodgement
|
||||
"Unknown thickness, PUR or PIR" → matches Table 17 col 4 (flat ceiling,
|
||||
PUR/PIR) at 200mm row. So Elmhurst applies a convention "Unknown
|
||||
thickness + known material → assumed 200mm".
|
||||
|
||||
Per RdSAP 10 spec literal: `_u_rr_table_17` with `insulation_thickness
|
||||
_mm=None` falls back to `u_rr_default_all_elements` (Table 18 col 4).
|
||||
For age band M = 0.15. Coincidence? Or is the spec text consistent?
|
||||
|
||||
Investigation needed:
|
||||
1. Verify Elmhurst's 200mm convention against RdSAP spec edge cases
|
||||
2. If "Unknown + known material" should fall back to Table 18 col 4
|
||||
default, then U=0.15 for age M IS correct
|
||||
3. Cert 000565 BP[4] rir_age=M → `u_rr_default_all_elements(ENG, M)`
|
||||
returns 0.15 (per probe). So if the cascade routes Flat Ceiling 1
|
||||
through `insulation_thickness_mm=None` (not 0), it would return
|
||||
0.15 ✓
|
||||
|
||||
Current fixture state: BP[4] Flat Ceiling 1 has `insulation_thickness
|
||||
_mm=0` (extractor stores "" → mapper returns 0). Worksheet expects
|
||||
the Table 18 col 4 fallback path (`None` → 0.15).
|
||||
|
||||
**Cleanest fix:** when extractor sees "Unknown" in insulation cell,
|
||||
store it. Mapper translates "Unknown" → `insulation_thickness_mm=None`
|
||||
(not 0). Cascade's existing `_u_rr_table_17` handles None →
|
||||
`u_rr_default_all_elements`.
|
||||
|
||||
Expected closure:
|
||||
- BP[4] Flat Ceiling 1 cascade U: 2.30 → 0.15 ✓
|
||||
- roof_w_per_k: 63.72 → 53.97 (closes -10.75)
|
||||
- Then BP[1] residual +1.29 W/K remains → roof Δ ~+1.6
|
||||
- Continuous SAP: -0.44 → ~ -0.10 (much closer to zero)
|
||||
- Integer sap_score may flip back to 29
|
||||
|
||||
### S0380.97 — BP[2] Ext2 floor 200mm insulation thickness extractor
|
||||
|
||||
**Spec citation:** RdSAP 10 §5.13 Table 20 (PDF p.47) — exposed/semi-
|
||||
exposed floor U-value by age band + insulation thickness. Cert 000565
|
||||
Ext2 Summary §9 lodges "Insulation Thickness: 200 mm" but extractor's
|
||||
`_floor_details_from_lines` doesn't read it. Fixture: BP[2] ground
|
||||
floor `floor_insulation_thickness=None` → cascade returns 0.51 vs ws
|
||||
0.22.
|
||||
|
||||
**Slice span (multi-layer):**
|
||||
1. Extractor: parse "Insulation Thickness" inside each §9 extension
|
||||
block
|
||||
2. Schema: `FloorDetails.insulation_thickness_mm: Optional[int]`
|
||||
(currently has `insulation: str` only)
|
||||
3. Mapper: plumb to `SapBuildingPart.floor_insulation_thickness`
|
||||
4. Cascade: already reads `floor_ins_thickness` and dispatches via
|
||||
`u_exposed_floor` (Table 20) — no cascade change needed.
|
||||
|
||||
Expected closure:
|
||||
- BP[2] floor U: 0.51 → 0.22 ✓
|
||||
- floor_w_per_k: 70.37 → 61.67 ✓ EXACT vs ws (closes +8.70)
|
||||
- Continuous SAP: cascade SH would DROP (less heat loss) → continuous
|
||||
SAP UP — drifts AWAY from worksheet. Per user direction OK if
|
||||
spec-correct.
|
||||
|
||||
### S0380.98 — BP[1] residual formula refinement (lower priority)
|
||||
|
||||
BP[1] Ext1 currently has residual +3.68 m² over worksheet (cascade
|
||||
21.93 vs ws 18.25). The Simplified A_RR formula `12.5 × √(34/1.5)`
|
||||
gives 59.51 — minus 37.58 lodged walls = 21.93. Worksheet uses 18.25.
|
||||
|
||||
Hypothesis: Ext1's RR height = 3.0 m (not 2.45 m assumed by formula).
|
||||
A height-aware formula like `A_RR = perimeter × actual_RR_height`
|
||||
might match. Need investigation against multiple Detailed-mode certs
|
||||
with non-2.45 RR heights.
|
||||
|
||||
Impact if closed: roof -1.29 W/K (residual drops by 3.68 × 0.35).
|
||||
|
||||
### Deferred (unchanged from earlier handovers)
|
||||
|
||||
- MEV PCDB Table 4f component for pumps_fans +2.5 (external data)
|
||||
- HP SAP code → main_heating_category=4 mapper extension
|
||||
- 12 gas-combi PV certs at +0.5..+1.6 PE (no worksheets)
|
||||
- 5 SAP-residual API-only certs (no worksheets)
|
||||
|
||||
## How to run the baseline
|
||||
|
||||
```bash
|
||||
PYTHONPATH=/workspaces/model python -m pytest \
|
||||
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py \
|
||||
backend/documents_parser/tests/test_elmhurst_extractor.py \
|
||||
backend/documents_parser/tests/test_elmhurst_end_to_end.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_appendix_h_solar.py \
|
||||
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \
|
||||
domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
|
||||
--no-cov -q
|
||||
```
|
||||
|
||||
Expected: **585 pass + 9 expected `test_sap_result_pin[000565-*]` fails**.
|
||||
|
||||
The 9 expected fails (verbatim):
|
||||
```
|
||||
sap_score
|
||||
sap_score_continuous
|
||||
ecf
|
||||
total_fuel_cost_gbp
|
||||
co2_kg_per_yr
|
||||
space_heating_kwh_per_yr
|
||||
main_heating_fuel_kwh_per_yr
|
||||
lighting_kwh_per_yr
|
||||
pumps_fans_kwh_per_yr
|
||||
```
|
||||
|
||||
## Files touched this session
|
||||
|
||||
| File | Slices | Change |
|
||||
|---|---|---|
|
||||
| `datatypes/epc/domain/epc_property_data.py` | .92, .93 | `SapVentilation.air_permeability_ap4_m3_h_m2` + `SapVentilation.mechanical_ventilation_kind`; `SapFloorDimension.is_above_partially_heated_space` |
|
||||
| `datatypes/epc/surveys/elmhurst_site_notes.py` | .92 | `VentilationAndCooling.air_permeability_ap4_m3_h_m2` + `.mechanical_ventilation_type` |
|
||||
| `backend/documents_parser/elmhurst_extractor.py` | .92, .94 | §12.1/12.2 AP4 + MV-type parsing; `_RIR_INSULATION_THICKNESS_RE` extended to `^\d+\+?\s*mm$`; allow-list adds "PUR or PIR" |
|
||||
| `datatypes/epc/domain/mapper.py` | .91, .92, .93, .94 | CF→11 mapper entry; AP4 + MV-kind plumbing + `_ELMHURST_MV_TYPE_TO_KIND` + strict-raise; `_is_floor_above_partially_heated_space` helper; `_RIR_INSULATION_TYPE_TO_SAP10` adds "PUR or PIR"/"PUR"/"PIR" → "rigid_foam"; `_elmhurst_rir_insulation_thickness_mm` regex extended |
|
||||
| `domain/sap10_calculator/rdsap/cert_to_inputs.py` | .92 | `ventilation_from_cert` reads `air_permeability_ap4_m3_h_m2` + resolves `mechanical_ventilation_kind` name → enum; MEV sets `mv_system_ach=0.5` |
|
||||
| `domain/sap10_ml/rdsap_uvalues.py` | .91, .93, .94 | `WALL_CAVITY_FILLED_PARTY=11` constant + `u_party_wall` CF branch (0.2); `u_floor_above_partially_heated_space()` helper (0.7); `_RR_RIGID_FOAM_INSULATION_TYPES` adds "rigid_foam" |
|
||||
| `domain/sap10_calculator/worksheet/heat_transmission.py` | .93, .95 | Floor dispatch adds `is_above_partial → u_floor_above_partially_heated_space()` branch; Detailed-RR branch adds §3.10.1 residual area computation with `has_roof_lodgement` discriminator |
|
||||
|
||||
## Spec source quick-reference
|
||||
|
||||
- **SAP 10.2 full specification**: `domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf`
|
||||
- §2 (p.12-13) — Infiltration / pressure test (AP50/AP4) — S0380.92
|
||||
- §2 (p.13, 133) — MEV (23a/24c) — S0380.92
|
||||
- **RdSAP 10 specification**: `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf`
|
||||
- §3.9.1 (p.21-22) — Simplified A_RR formula — S0380.95
|
||||
- §3.10.1 (p.24) — Detailed RR residual area — S0380.95
|
||||
- §5.10 Table 15 (p.42) — Party-wall U-values — S0380.91
|
||||
- §5.14 (p.47) — Floor above partially heated — S0380.93
|
||||
- Table 17 col 3b (p.42-43) — Stud wall PUR/PIR — S0380.94
|
||||
- **SAP 10.3 at** `sap-10-3-full-specification-2026-01-13.pdf`: **DO NOT reference** ([[feedback-sap-10-2-only-never-10-3]])
|
||||
|
||||
## Memory updated this session
|
||||
|
||||
- `project_cert_000565_recovery_state` — slice-by-slice closure table
|
||||
for .91..95 + remaining open-work analysis
|
||||
- `MEMORY.md` — index entry refreshed at HEAD `fa6974bd`
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **Don't reference SAP 10.3** ([[feedback-sap-10-2-only-never-10-3]]).
|
||||
- **Don't widen pin tolerances or xfail residual gaps**
|
||||
([[feedback-zero-error-strict]]). The 9 cert 000565 fails are the
|
||||
work queue.
|
||||
- **Don't re-investigate any closed work**: party-wall CF (.91),
|
||||
AP4/MEV (.92), §5.14 partially-heated (.93), RIR PUR or PIR (.94),
|
||||
§3.10.1 residual area (.95). All settled.
|
||||
- **Don't add new helpers to `domain/sap10_ml/`** — that folder is on
|
||||
the deprecation path per [[project-sap10_ml-deprecation]]. New
|
||||
cascade helpers should land under `domain/sap10_calculator/`.
|
||||
(Editing existing sap10_ml files for incremental fixes is fine.)
|
||||
- **Don't avoid spec-correct closures because continuous SAP drifts
|
||||
away** — user explicitly OK'd transient drift. Zero error
|
||||
achievable only when every component is spec-correct.
|
||||
|
||||
## Memory hygiene
|
||||
|
||||
After the next slice, update:
|
||||
- `project_cert_000565_recovery_state` — append slice closure +
|
||||
refresh the open work-items table
|
||||
- `MEMORY.md` — refresh HEAD + one-line summary
|
||||
|
||||
Good luck.
|
||||
254
domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_95.md
Normal file
254
domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_95.md
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
# Next-agent prompt — post S0380.91..95
|
||||
|
||||
Branch: `feature/per-cert-mapper-validation`.
|
||||
HEAD: `fa6974bd`.
|
||||
|
||||
Read these in order before any tool call:
|
||||
|
||||
1. [`HANDOVER_POST_S0380_95.md`](HANDOVER_POST_S0380_95.md) — full state
|
||||
2. [`HANDOVER_POST_S0380_90.md`](HANDOVER_POST_S0380_90.md) — predecessor (background)
|
||||
|
||||
Also load these memories before starting:
|
||||
|
||||
- `project_cert_000565_recovery_state` — slice history + per-pin state
|
||||
- `reference_unmapped_sap_code` — calculator strict-raise pattern
|
||||
- `project_sap10_ml_deprecation` — `domain/sap10_ml/` is on the
|
||||
deprecation path; new cascade helpers should land under
|
||||
`domain/sap10_calculator/`
|
||||
- `feedback_sap_10_2_only_never_10_3` — **CRITICAL** — never reference
|
||||
SAP 10.3 spec
|
||||
- `feedback_spec_citation_in_commits` — quote spec text + page in
|
||||
commit messages
|
||||
- `feedback_verify_handover_claims` — verify spec citations + numeric
|
||||
claims before implementing the prescribed fix
|
||||
- `feedback_zero_error_strict` — pyright net-zero per touched file
|
||||
- `feedback_commit_per_slice` — one slice = one commit
|
||||
- `feedback_aaa_test_convention` — every new test uses literal
|
||||
`# Arrange / # Act / # Assert` headers
|
||||
- `feedback_e2e_validation_philosophy` — component pins at <1e-3;
|
||||
SAP integer delta=0; no adaptive ceilings
|
||||
- `feedback_abs_diff_over_pytest_approx` — use `abs(x - y) <= tol`
|
||||
instead of `pytest.approx` to keep pyright net-zero
|
||||
- `feedback_no_misleading_insulation_type` — field names should reflect
|
||||
what they semantically contain
|
||||
|
||||
## Critical user direction
|
||||
|
||||
The user's **primary metric is `sap_score_continuous`** (not just
|
||||
integer `sap_score`). However the user has explicitly stated:
|
||||
|
||||
> "It's okay if we temp drift away from continuous SAP, as long as we
|
||||
> are actually fixing true problems with the intermediate values.
|
||||
> Eventually, I expect the error of continuous SAP to be zero but
|
||||
> that is only possible if we fix all of the sub components and
|
||||
> remain true to spec."
|
||||
|
||||
**Implication:** ship spec-correct slices even when they cause
|
||||
transient continuous-SAP drift. Closing real intermediate-value bugs
|
||||
is the path to zero error.
|
||||
|
||||
## State summary
|
||||
|
||||
This session shipped **S0380.91..95** — five spec-cited slices that
|
||||
closed major fabric + ventilation gaps for cert 000565:
|
||||
|
||||
1. **S0380.91** (`83218630`) — party-wall CF U=0.2 (RdSAP 10 Table
|
||||
15 row 3). party_walls ✓ EXACT. sap_score 27 → 28.
|
||||
2. **S0380.92** (`a7894b11`) — AP4 + MEV decentralised plumbing
|
||||
(SAP 10.2 §2 (17a)/(18)/(23a)/(24c)). sap_score 28 → **29 ✓
|
||||
EXACT**. (18) + (21) + (25)m all ✓ EXACT.
|
||||
3. **S0380.93** (`23aaa4fa`) — Floor above partially-heated U=0.7
|
||||
(RdSAP 10 §5.14). BP[1] floor ✓ EXACT.
|
||||
4. **S0380.94** (`78c57c0d`) — RIR "400+ mm PUR or PIR" extractor +
|
||||
mapper + cascade fixes (RdSAP 10 Table 17 col 3b). BP[2] Stud
|
||||
Wall 2 ✓ EXACT.
|
||||
5. **S0380.95** (`fa6974bd`) — Detailed-RR residual area cascade
|
||||
(RdSAP 10 §3.10.1). **thermal_bridging ✓** + **total_external
|
||||
_area ✓ close**. sap_score 29 → 28 transient (continuous crossed
|
||||
28.5).
|
||||
|
||||
**Cert 000565 state at HEAD `fa6974bd`:**
|
||||
|
||||
| Pin | Cascade | Worksheet | Δ |
|
||||
|---|---:|---:|---:|
|
||||
| sap_score (int) | 28 | 29 | -1 |
|
||||
| **sap_score_continuous** | **28.07** | 28.51 | **-0.44** |
|
||||
| space_heating_kwh | 59541.61 | 59008.35 | +533.26 |
|
||||
| **hot_water_kwh** | 3755.03 | 3755.03 | ✓ 0 EXACT |
|
||||
| **party_walls W/K** | 65.13 | 65.13 | ✓ EXACT |
|
||||
| **thermal_bridging W/K** | 129.35 | 128.65 | +0.70 |
|
||||
| **doors W/K** | 11.10 | 11.10 | ✓ EXACT |
|
||||
| **total external area** | 862.34 | 857.64 | +4.70 |
|
||||
| floor W/K | 70.37 | 61.67 | +8.70 |
|
||||
| roof W/K | 63.72 | 51.38 | +12.34 |
|
||||
| walls W/K | 601.22 | 604.07 | -2.85 |
|
||||
|
||||
## Recommended next slice — S0380.96 BP[4] Flat Ceiling 1 "Unknown PUR or PIR"
|
||||
|
||||
**Highest-leverage remaining single-cause closure for cert 000565.**
|
||||
|
||||
Cert 000565 BP[4] Ext4 Summary §8.1 lodges:
|
||||
```
|
||||
Flat Ceiling 1 5.00 × 1.00 Unknown PUR or PIR Default U=0.15
|
||||
```
|
||||
|
||||
Worksheet line (30): `Roof room Ext4 Flat Ceiling 1: 5 × 0.15 = 0.75 W/K`
|
||||
|
||||
Pre-slice the extractor reads "Unknown" as a non-thickness token and
|
||||
drops it. The mapper sees `insulation = ""` → `_elmhurst_rir_
|
||||
insulation_thickness_mm("")` returns 0 (uninsulated). Cascade hits
|
||||
Table 17 row 0 → U=2.30. Cascade over-counts by (2.30 - 0.15) × 5 =
|
||||
**+10.75 W/K**.
|
||||
|
||||
### Audit steps
|
||||
|
||||
1. Read RdSAP 10 §3.10.1 (PDF p.24) for "Unknown" thickness fallback.
|
||||
Worksheet U=0.15 matches `u_rr_default_all_elements(ENG, M)` (probed
|
||||
earlier) — so the cascade's existing None → Table 18 col 4 fallback
|
||||
IS the spec-correct path.
|
||||
2. Read the BP[4] rir_age (= M for cert 000565). Verify `u_rr_default
|
||||
_all_elements(ENG, "M")` returns 0.15.
|
||||
3. Probe `domain/sap10_ml/rdsap_uvalues.py:_u_rr_table_17` to confirm
|
||||
the `insulation_thickness_mm is None` → `u_rr_default_all_elements`
|
||||
path.
|
||||
4. Read the Elmhurst extractor + mapper code paths for "Unknown"
|
||||
thickness token.
|
||||
|
||||
### Suggested implementation
|
||||
|
||||
The cleanest path treats "Unknown thickness with known insulation
|
||||
material" as `insulation_thickness_mm=None` (not 0). The cascade's
|
||||
existing path handles None → Table 18 col 4 default.
|
||||
|
||||
**Slice span:**
|
||||
1. Extractor (`elmhurst_extractor.py:_parse_rir_surface_row`):
|
||||
recognise "Unknown" as a valid `insulation` value (currently the
|
||||
allow-list checks `_RIR_INSULATION_THICKNESS_RE` or
|
||||
`("As Built", "None")` — extend to include `"Unknown"`).
|
||||
2. Mapper (`datatypes/epc/domain/mapper.py:_elmhurst_rir_insulation
|
||||
_thickness_mm`): translate `"Unknown"` → `None` (not 0). Today the
|
||||
function returns int (always), so the schema change requires
|
||||
`Optional[int]` return type + downstream updates.
|
||||
|
||||
Alternative: change to `Optional[int]` return; update
|
||||
`SapRoomInRoofSurface.insulation_thickness_mm` consumers to handle
|
||||
None.
|
||||
|
||||
3. Cascade: no change needed — existing None → Table 18 col 4
|
||||
fallback is spec-correct.
|
||||
|
||||
4. Tests (AAA):
|
||||
- Extractor: `test_summary_000565_bp4_flat_ceiling_1_extracts_
|
||||
unknown_thickness_lodgement`
|
||||
- Mapper: `test_summary_000565_bp4_flat_ceiling_1_maps_unknown_
|
||||
to_none_thickness_per_rdsap_10_section_3_10_1`
|
||||
- Cascade pin: BP[4] FC1 cascade U = 0.15 vs ws 0.15
|
||||
|
||||
### Expected outcome
|
||||
|
||||
- cert 000565 BP[4] FC1 cascade U: 2.30 → **0.15 ✓ EXACT**
|
||||
- roof_w_per_k: 63.72 → 53.97 (Δ+12.34 → +2.59)
|
||||
- Continuous SAP: −0.44 → ~ −0.10 (much closer to zero)
|
||||
- Integer sap_score: 28 → potentially 29 (if continuous crosses 28.5
|
||||
back up)
|
||||
- SH: +533 → ~+230 kWh
|
||||
|
||||
### Cohort safety
|
||||
|
||||
- Cohort fixtures (000474..000516) all lodge concrete insulation values
|
||||
for Flat Ceiling lodgements; "Unknown" is cert 000565-specific.
|
||||
- Need to verify cohort: any other Summary cert with "Unknown" in RIR
|
||||
insulation cell? Run: `grep -l "Unknown" $(ls backend/documents_
|
||||
parser/tests/fixtures/Summary_*.pdf | xargs -I {} sh -c 'pdftotext
|
||||
-layout {} - | grep -lq Unknown')` (or similar audit).
|
||||
|
||||
## Beyond S0380.96 — remaining cert 000565 work-items
|
||||
|
||||
### S0380.97 — BP[2] Ext2 floor 200 mm insulation extractor
|
||||
|
||||
Summary §9 lodges "Insulation Thickness: 200 mm" + "Insulation Type:
|
||||
Retro-fitted" for Ext2. Extractor's `_floor_details_from_lines`
|
||||
doesn't read it. Worksheet U=0.22, cascade currently 0.51 (Δ +0.29 ×
|
||||
30 m² = +8.70 W/K).
|
||||
|
||||
**Slice span:**
|
||||
- Extract per-extension "Insulation Thickness" inside §9 block (scoped)
|
||||
- Schema: `FloorDetails.insulation_thickness_mm: Optional[int]`
|
||||
- Mapper: plumb to `SapBuildingPart.floor_insulation_thickness`
|
||||
- Cascade: no change (existing `u_exposed_floor` reads thickness)
|
||||
|
||||
Per user direction OK to drift continuous SAP further (this fix LOWERS
|
||||
heat loss → SAP UP → drifts away from worksheet). Spec-correct.
|
||||
|
||||
### S0380.98 — BP[1] Ext1 residual formula refinement
|
||||
|
||||
BP[1] residual +3.68 m² over. Simplified A_RR formula `12.5 × √(34/
|
||||
1.5) = 59.51` minus 37.58 lodged walls = 21.93 vs ws 18.25.
|
||||
|
||||
Hypothesis: Ext1's RR height = 3.0 m (not 2.45 m). Formula may need
|
||||
to be height-aware. Investigation needed.
|
||||
|
||||
Impact: roof -1.29 W/K. Small.
|
||||
|
||||
### Deferred (unchanged)
|
||||
|
||||
- MEV PCDB Table 4f component for pumps_fans +2.5 (external data)
|
||||
- HP SAP code → main_heating_category=4 mapper extension
|
||||
- 12 gas-combi PV certs at +0.5..+1.6 PE (no worksheets)
|
||||
- 5 SAP-residual API-only certs (no worksheets)
|
||||
|
||||
## Standard workflow per slice
|
||||
|
||||
1. Read SAP 10.2 / RdSAP 10 spec page for the change — quote it in commit
|
||||
2. Probe current cascade output; identify exact spec-vs-cascade gap
|
||||
3. Write failing test FIRST (AAA structure)
|
||||
4. Implement helper / change
|
||||
5. Verify test passes
|
||||
6. Run full handover suite (command below)
|
||||
7. Check pyright on touched files — net-zero from baseline
|
||||
(use `git stash` + re-run pyright to compute baseline)
|
||||
8. Commit with spec citation + verbatim quote
|
||||
9. Update relevant memory if state changed
|
||||
|
||||
## How to run the baseline
|
||||
|
||||
```bash
|
||||
PYTHONPATH=/workspaces/model python -m pytest \
|
||||
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py \
|
||||
backend/documents_parser/tests/test_elmhurst_extractor.py \
|
||||
backend/documents_parser/tests/test_elmhurst_end_to_end.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_e2e_elmhurst_sap_score.py \
|
||||
domain/sap10_calculator/worksheet/tests/test_appendix_h_solar.py \
|
||||
domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \
|
||||
domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \
|
||||
--no-cov -q
|
||||
```
|
||||
|
||||
Expected: **585 pass + 9 expected `test_sap_result_pin[000565-*]` fails**.
|
||||
|
||||
After S0380.96 lands the expected fail count likely drops by 1
|
||||
(sap_score back to EXACT) and continuous-SAP residual closes to
|
||||
~−0.1. Other downstream pins (SH, fuel, cost, CO2) move with SH.
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **Don't reference SAP 10.3** ([[feedback-sap-10-2-only-never-10-3]]).
|
||||
- **Don't widen pin tolerances or xfail residual gaps**
|
||||
([[feedback-zero-error-strict]]). The 9 cert 000565 fails are the
|
||||
work queue.
|
||||
- **Don't re-investigate any of the .85..95 closures.** All settled.
|
||||
- **Don't add new helpers to `domain/sap10_ml/`** — deprecation path
|
||||
per [[project-sap10_ml-deprecation]].
|
||||
- **Don't chase the 12 gas-combi PV certs or the 5 SAP-residual certs
|
||||
without worksheets** — user has explicitly de-prioritised.
|
||||
- **Don't avoid spec-correct closures because continuous SAP drifts
|
||||
transiently** — user explicitly OK'd it.
|
||||
|
||||
## Memory hygiene
|
||||
|
||||
After the next slice, update:
|
||||
- `project_cert_000565_recovery_state` — append closure + open work-
|
||||
items refresh
|
||||
- `MEMORY.md` index — refresh HEAD + one-line summary
|
||||
|
||||
Good luck.
|
||||
Loading…
Add table
Reference in a new issue