From 7df3fef8bb7a6b618cb7815c71dac68ec8827d75 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Sat, 30 May 2026 16:13:33 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20handover=20+=20next-agent=20prompt=20po?= =?UTF-8?q?st=20S0380.96..103=20(RIR=20Unknown=20+=20=C2=A79=20floor=20+?= =?UTF-8?q?=20MEV=20PCDB=20arc=20+=20HP-on-E7=20cost=20split)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8 slices shipped this session: S0380.96 RIR Unknown insulation (RdSAP 10 §3.10.1) S0380.97 Floor §9 insulation thickness (RdSAP 10 §5.13 Table 20) S0380.98 PCDB Table 322 ETL+parser (PCDF Spec §A.19) S0380.99 PCDB Table 329 ETL+parser (PCDF Spec §A.20) S0380.100 MEV SFPav + (230a) helpers (SAP 10.2 §2.6.4) S0380.101 HP SAP code → cat=4 mapper (SAP 10.2 Table 4a) S0380.102 Wire MEV into pumps_fans (SAP 10.2 Table 4f 230a) S0380.103 MEV-fan cost split (SAP 10.2 Table 12a Grid 2) Cert 000565 at HEAD `e3abe9b2`: sap_score (int) ✓ EXACT pumps_fans_kwh_per_yr ✓ EXACT (was +2.48 over) hot_water_kwh_per_yr ✓ 0 EXACT sap_score_continuous Δ +0.0182 (SH cascade-driven) 7 expected fails (was 9) Next slice candidate: S0380.104 investigate §3-§8 space-heating cascade -27 kWh under-count (cert-000565-specific; cohort certs pass at 1e-4). Alternative: S0380.105 CO2 MEV split (mirror of .103 for Table 12d monthly factors). Co-Authored-By: Claude Opus 4.7 --- .../docs/HANDOVER_POST_S0380_103.md | 303 ++++++++++++++++++ .../docs/NEXT_AGENT_PROMPT_POST_S0380_103.md | 237 ++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 domain/sap10_calculator/docs/HANDOVER_POST_S0380_103.md create mode 100644 domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_103.md diff --git a/domain/sap10_calculator/docs/HANDOVER_POST_S0380_103.md b/domain/sap10_calculator/docs/HANDOVER_POST_S0380_103.md new file mode 100644 index 00000000..e39d489c --- /dev/null +++ b/domain/sap10_calculator/docs/HANDOVER_POST_S0380_103.md @@ -0,0 +1,303 @@ +# Handover — post S0380.96..103 (RIR Unknown + §9 floor extractor + MEV PCDB arc + HP-on-E7 cost split) + +Branch: `feature/per-cert-mapper-validation`. **HEAD `e3abe9b2`**. +Predecessor: [`HANDOVER_POST_S0380_95.md`](HANDOVER_POST_S0380_95.md). + +## Slices committed this session (S0380.96..103) + +Eight spec-cited slices. The first two closed remaining cert 000565 +extractor/mapper gaps (RIR "Unknown" insulation + floor §9 +"Insulation Thickness"). Slices .98..102 built the MEV PCDB +decentralised cascade arc end-to-end (Tables 322 + 329 + SFPav +formula + HP-category mapper + wiring). The final slice .103 closed +the Table 12a Grid 2 MEV-fan cost split, completing the HP-on-E7 +cost cascade. + +| Slice | Commit | Spec | Cert 000565 outcome | +|---|---|---|---| +| **S0380.96** | `32a4cf20` | RdSAP 10 §3.10.1 (PDF p.24) — "Unknown" insulation → Table 18 col 4 age-band default | BP[4] FC1 cascade U: 2.30 → **0.15 ✓ EXACT**. roof_w_per_k Δ +12.34 → +1.59 (closed -10.75). Continuous SAP Δ -0.44 → -0.20. | +| **S0380.97** | `7121a86b` | RdSAP 10 §5.13 Table 20 (PDF p.47) — exposed/semi-exposed floor U by age × thickness | BP[2] Ext2 floor U: 0.51 → **0.22 ✓ EXACT**. floor_w_per_k **✓ EXACT**. **sap_score 28 → 29 ✓ EXACT**. Continuous SAP Δ -0.0001 (within 1e-4). | +| **S0380.98** | `b3330821` | PCDF Spec Rev 6b §A.19 — PCDB Table 322 Format 427/428 | Foundation only. Typed parser + ETL + `decentralised_mev_record(pcdb_id)` lookup. 48 records ingested. No cascade integration. | +| **S0380.99** | `433f4a49` | PCDF Spec Rev 6b §A.20 — PCDB Table 329 Format 430/432 | Foundation only. Typed parser + ETL + `mv_in_use_factors_record(system_type)`. 5 records ingested. No cascade integration. | +| **S0380.100** | `44fb8c07` | SAP 10.2 §2.6.4 equation (1) + Table 4f line (230a) | New module `worksheet/mev.py` — `mev_sfp_av` + `mev_decentralised_kwh_per_yr` pure helpers. AAA tests pin cert 000565 worksheet values. No cascade integration. | +| **S0380.101** | `1b183f9c` | SAP 10.2 Table 4a (PDF p.165) — Heat-pump category 4 | HP SAP codes 211-227 / 521-527 → `main_heating_category=4` in `_elmhurst_main_heating_category` (Elmhurst path). Cert 000565 Main 1 (SAP 224) flipped None→4. Transient regression: pumps_fans 255 → 125 (offset bug exposed). | +| **S0380.102** | `a0413155` | SAP 10.2 §2.6.4 + Table 4f (230a) — Wire MEV cascade into `_table_4f_additive_components` | **pumps_fans_kwh_per_yr 255 → 252.5159 ✓ EXACT**. Schema + extractor + mapper for MV PCDF index / wet rooms / duct type. Elmhurst fan-count convention reverse-engineered from cert 000565 (TODO: validate on a 2nd MEV cert). | +| **S0380.103** | `e3abe9b2` | SAP 10.2 Table 12a Grid 2 (PDF p.191) — `FANS_FOR_MECH_VENT` blended rate on off-peak | MEV-fan cost weighting: 127.5 kWh at 11.6644 p/kWh + 125 kWh at 13.2440 p/kWh → effective 12.4467 p/kWh. cost Δ +£0.39 → -£1.62 (sign flipped; SH cascade residual exposed). | + +**Test baseline at HEAD `e3abe9b2`:** 597 pass + 7 expected `000565` +fails (was 585 + 9 at start of session, with .96+.97 closing the +sap_score integer fail and .102 closing the pumps_fans fail). The +ETL test count grew by ~25 with the new PCDB tables. + +Pyright net-zero per touched file across every slice. + +## Cert 000565 state (HEAD `e3abe9b2`) + +### Fabric subtotals + +| Component | Cascade W/K | Worksheet W/K | Δ | Status | +|---|---:|---:|---:|---| +| walls | 601.22 | 604.07 | -2.85 | sub-spec | +| **party_walls** | **65.13** | 65.13 | ✓ EXACT | S0380.91 | +| **floor** | **61.67** | 61.67 | ✓ EXACT | S0380.97 | +| roof | 52.97 | 51.38 | +1.59 | residual +1.29 BP[1] formula | +| 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 | +| **total external area** | **862.34** | 857.64 | +4.70 | S0380.95 | + +### SapResult pins (HEAD `e3abe9b2`) + +| Pin | Cascade | Worksheet | Δ | Status | +|---|---:|---:|---:|---| +| **sap_score (int)** | **29** | 29 | **✓ EXACT** | S0380.97 | +| sap_score_continuous | 28.5269 | 28.5087 | +0.0182 | SH cascade-driven | +| ecf | 5.3850 | 5.3866 | -0.0016 | SH cascade-driven | +| total_fuel_cost_gbp | 4678.6372 | 4680.2593 | -1.6221 | SH cascade-driven | +| co2_kg_per_yr | 6445.8198 | 6447.6263 | -1.8065 | mix: CO2 MEV split + SH | +| space_heating_kwh_per_yr | 58980.8225 | 59008.3499 | -27.5274 | §3-§8 cascade gap | +| main_heating_fuel_kwh_per_yr | 34694.6015 | 34710.7941 | -16.1926 | downstream of SH | +| **hot_water_kwh_per_yr** | 3755.0288 | 3755.0288 | ✓ 0 EXACT | unchanged | +| lighting_kwh_per_yr | 1387.0237 | 1384.8353 | +2.1884 | sub-spec | +| **pumps_fans_kwh_per_yr** | **252.5159** | 252.5159 | **✓ 0 EXACT** | S0380.102 | + +### Continuous SAP journey across this session + +| Slice | sap_score (int) | sap_score_continuous | Δ vs ws | +|---|---:|---:|---:| +| Pre-S0380.96 | 28 | 28.07 | -0.44 | +| S0380.96 | 28 | 28.31 | -0.20 | +| **S0380.97** | **29** | **28.5086** | **-0.0001** (within 1e-4!) | +| S0380.98..100 | 29 | 28.5086 | -0.0001 (no cascade change) | +| S0380.101 | 29 | 28.6942 | +0.1855 (transient — HP cat=4 only, MEV not yet wired) | +| S0380.102 | 29 | 28.5043 | -0.0044 (MEV wired, restored balance) | +| **S0380.103** | 29 | **28.5269** | **+0.0182** (MEV cost split exposed pre-existing SH residual) | + +Per user direction [[feedback-spec-floor-skepticism]] + +[[feedback-spec-floor-skepticism]]: each slice closed a true spec- +correct intermediate-value bug. The continuous-SAP residual is now +driven by a §3-§8 SH cascade under-count (main_heating_fuel -16 kWh) +that was previously masked by the +£2.01 pumps_fans cost over-count. + +## Open work — prioritised next slices + +### S0380.104 — Investigate §3-§8 space-heating cascade -27 kWh + +**The current biggest residual driver.** main_heating_fuel_kwh is +-16.19 kWh under ws (34694.60 vs ws 34710.79) → SH cost £1.58 under +ws → continuous-SAP +0.0182 OVER ws. + +Possible causes: +1. **Heat transmission HLC residual** — fabric subtotals net to net + ~+29 W/K (post-S0380.95 fabric snapshot). Walls -2.85, roof + +1.59, thermal_bridging +0.70, total_external_area +4.70. + Roof BP[1] residual formula gap (+1.29 W/K, deferred from + S0380.95) is the largest single localised item. +2. **Internal gains** — pumps_fans gains contribution changes with + HP cat=4 path; verify against ws line (70) by month. +3. **Solar gains / utilization factor** — sub-spec window U-values + leak into solar gains too. +4. **Mean internal temperature / per-month solve** — possible + convergence-loop tolerance issue on this multi-BP cert. + +**Approach:** probe per-month `space_heat_requirement_kwh` vs ws +line (98c)m to localise. The cohort certs (000474..000516) hit SH +at 1e-4 so the §8 orchestrator IS correct on simpler dwellings — +something cert-000565-specific (Detailed-RR + multi-BP + HP + MEV ++ FGHRS + solar HW + draught lobby) is the differentiator. + +Expected closure: continuous SAP +0.0182 → within 1e-4. + +### S0380.105 — CO2 cascade MEV split (Table 12d monthly factors) + +Mirror of S0380.103 for CO2. Cert 000565 worksheet line (267): + + Pumps, fans and electric keep-hot 252.5159 × 0.1412 = 35.3349 + +Cascade `pumps_fans_co2_factor_kg_per_kwh = 0.14116` (kWh-weighted +Table 12d monthly factor for code 30) → 35.6453 kg → +0.31 over ws. + +Cause: cascade uses a single Table 12d profile across all pumps_fans +kWh. MEV fans have a different MONTHLY DISTRIBUTION than central- +heating pumps + flue fans (MEV runs year-round at 0.5 ach; pumps +run heating season only). The worksheet integrates separately. + +**Slice scope:** add `MevFanEntry`-style CO2 helper + new +`pumps_fans_co2_factor_kg_per_kwh` resolution that weights the two +streams. + +Impact: -0.31 kg/yr → continuous SAP downstream. + +### S0380.106 — PE cascade MEV split (Table 12e monthly factors) + +Mirror of S0380.105 for primary energy. Analogous structure. + +### S0380.107 — BP[1] residual formula refinement (roof) + +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). + +### S0380.108 — Lighting +2.19 kWh trace residual + +Cascade 1387.02 vs ws 1384.84. Sub-spec but breaks 1e-4 strict pin. + +§5 Appendix L lighting cascade. Likely a per-cert-lodging gap +(bulb count, fixed/non-fixed lighting fraction). + +### Deferred (unchanged from earlier handovers) + +- 12 gas-combi PV certs at +0.5..+1.6 PE (no worksheets) +- 5 SAP-residual API-only certs (no worksheets) + +## MEV PCDB arc — architecture summary + +The S0380.98..103 arc landed the entire MEV decentralised cascade +end-to-end. Architecture (in dependency order): + +``` +PCDB pcdb10.dat + ↓ ETL (etl.py, parser.py) +Table 322 (per-fan SFP) ←→ Table 329 (per-ducting IUF) + ↓ runtime lookups (__init__.py) +decentralised_mev_record(pcdb_id) + mv_in_use_factors_record(system_type) + ↓ +worksheet/mev.py — pure helpers + mev_sfp_av(fan_entries) → §2.6.4 equation (1) avg SFP + mev_decentralised_kwh_per_yr(sfp_av, V) → Table 4f line (230a) kWh + ↓ +cert_to_inputs.py + _mev_decentralised_kwh_per_yr_from_cert(epc) — composer + reads epc.mechanical_ventilation_index_number, .wet_rooms_count, + .mechanical_vent_duct_type + builds Elmhurst per-fan count distribution + invokes mev.py helpers + ↓ +_table_4f_additive_components(epc) adds the MEV contribution + ↓ +pumps_fans_kwh_per_yr final cascade output + +For COST (S0380.103): + _pumps_fans_fuel_cost_gbp_per_kwh(tariff, mev_kwh, total_pumps_fans_kwh) + → kWh-weighted blended rate (FANS_FOR_MECH_VENT vs ALL_OTHER_USES) + CalculatorInputs.pumps_fans_fuel_cost_gbp_per_kwh: Optional[float] + Calculator legacy path uses it via `or other_fuel_cost_gbp_per_kwh`. +``` + +**Open question for the next agent:** the Elmhurst per-fan-count +convention in `_mev_decentralised_kwh_per_yr_from_cert` was reverse- +engineered from cert 000565 alone: +- Each PCDB-defined config (1..6) gets baseline count = 1 +- Through-wall kitchen (5): wet_rooms_count fans total +- Through-wall other wet (6): wet_rooms_count + 1 fans total + +When a 2nd MEV cert lands, validate this. May need to consult +Elmhurst's documentation or the BRE-published SAP 10.2 +implementation guide. + +## 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/worksheet/tests/test_mev.py \ + domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \ + domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \ + domain/sap10_calculator/tests/test_pcdb_table_322_lookup.py \ + domain/sap10_calculator/tests/test_pcdb_table_329_lookup.py \ + --no-cov -q +``` + +Expected: **597 pass + 7 expected `test_sap_result_pin[000565-*]` fails**. + +The 7 expected fails (verbatim): +``` +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 +``` + +All driven by the §3-§8 SH cascade residual + lighting trace + CO2 +MEV-split gap. + +## Files touched this session + +| File | Slices | Change | +|---|---|---| +| `backend/documents_parser/elmhurst_extractor.py` | .96, .97, .102 | "Unknown" insulation token; §9 "Insulation Thickness" cell; §12.1 MV PCDF/Wet-Rooms/Duct-Type fields | +| `backend/documents_parser/tests/test_summary_pdf_mapper_chain.py` | .96, .97, .101, .103 | AAA tests for cert 000565 closures | +| `datatypes/epc/domain/mapper.py` | .96, .97, .101, .102 | `_elmhurst_rir_insulation_thickness_mm` → `Optional[int]`; floor `insulation_thickness_mm` plumbing; HP SAP-code → category 4; MV duct-type mapper + PCDF plumbing | +| `datatypes/epc/surveys/elmhurst_site_notes.py` | .97, .102 | `FloorDetails.insulation_thickness_mm`; `VentilationAndCooling.mechanical_ventilation_pcdf_reference` + `.wet_rooms_count` + `.duct_type` + `.approved_installation` | +| `domain/sap10_calculator/tables/pcdb/__init__.py` | .98, .99 | `decentralised_mev_record` + `mv_in_use_factors_record` lookups | +| `domain/sap10_calculator/tables/pcdb/etl.py` | .98, .99 | Table 322 + 329 typed ETL | +| `domain/sap10_calculator/tables/pcdb/parser.py` | .98, .99 | `DecentralisedMevRecord` + `MvInUseFactorsRecord` + parsers | +| `domain/sap10_calculator/tables/pcdb/data/pcdb_table_322_decentralised_mev.jsonl` | .98 | New file — 48 records | +| `domain/sap10_calculator/tables/pcdb/data/pcdb_table_329_mv_in_use_factors.jsonl` | .99 | New file — 5 records | +| `domain/sap10_calculator/worksheet/mev.py` | .100 | New module — `mev_sfp_av` + `mev_decentralised_kwh_per_yr` helpers | +| `domain/sap10_calculator/worksheet/tests/test_mev.py` | .100 | AAA tests pinning cert 000565 SFPav | +| `domain/sap10_calculator/rdsap/cert_to_inputs.py` | .102, .103 | `_mev_decentralised_kwh_per_yr_from_cert` composer; `_pumps_fans_fuel_cost_gbp_per_kwh` helper | +| `domain/sap10_calculator/calculator.py` | .103 | `CalculatorInputs.pumps_fans_fuel_cost_gbp_per_kwh` field + legacy cost path | +| `domain/sap10_calculator/tests/test_pcdb_etl.py` | .98, .99 | Added Tables 322, 329 to file list | +| `domain/sap10_calculator/tests/test_pcdb_table_322_lookup.py` | .98 | New file — 3 tests | +| `domain/sap10_calculator/tests/test_pcdb_table_329_lookup.py` | .99 | New file — 4 tests | + +## Spec source quick-reference + +- **SAP 10.2 full specification**: `domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf` + - §2.6.4 (p.16) — Decentralised MEV SFPav equation (1) — S0380.100 + - §5 Table 4a (p.165) — Heat-pump category 4 — S0380.101 + - §5 Table 4f (p.174) — Annual electricity for fans / pumps — S0380.100, .102 + - §5 Table 4g (p.176) — Default SFP for MV systems — S0380.99 + - §10a Table 12a (p.191) — High-rate fractions on off-peak tariffs — S0380.103 +- **RdSAP 10 specification**: `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf` + - §3.10.1 (p.24) — Unknown insulation → Table 18 default — S0380.96 + - §5.13 + Table 20 (p.47) — Exposed/semi-exposed floor U-values — S0380.97 +- **PCDF Spec Rev 6b**: `domain/sap10_calculator/docs/specs/PCDF_Spec_Rev-06b_12_May_2021.pdf` + - §A.19 Format 427 (Decentralised MEV) — S0380.98 + - §A.20 Format 430 (MV In-Use Factors) — S0380.99 +- **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 .96..103 + open-work analysis +- `MEMORY.md` — index entry refreshed at HEAD `e3abe9b2` + +## 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 7 cert 000565 fails are the + work queue. +- **Don't re-investigate any closed work** (.91..103). All settled. +- **Don't add new helpers to `domain/sap10_ml/`** — deprecation path + per [[project-sap10_ml-deprecation]]. New cascade helpers belong + under `domain/sap10_calculator/`. +- **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. diff --git a/domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_103.md b/domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_103.md new file mode 100644 index 00000000..9310f8c8 --- /dev/null +++ b/domain/sap10_calculator/docs/NEXT_AGENT_PROMPT_POST_S0380_103.md @@ -0,0 +1,237 @@ +# Next-agent prompt — post S0380.96..103 + +Branch: `feature/per-cert-mapper-validation`. +HEAD: `e3abe9b2`. + +Read these in order before any tool call: + +1. [`HANDOVER_POST_S0380_103.md`](HANDOVER_POST_S0380_103.md) — full state +2. [`HANDOVER_POST_S0380_95.md`](HANDOVER_POST_S0380_95.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_spec_floor_skepticism` — skeptical of "spec-precision + floor" claims; verify the spec citation against the PDF first + +## 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.96..103** — eight spec-cited slices. +The first two (.96, .97) closed remaining cert 000565 extractor / +mapper gaps; .98..102 built the entire MEV PCDB decentralised +cascade arc; .103 closed the Table 12a Grid 2 MEV-fan cost split. + +1. **S0380.96** (`32a4cf20`) — RIR "Unknown" insulation → Table 18 + col 4 default (RdSAP 10 §3.10.1). BP[4] FC1 U: 2.30→**0.15 ✓**. +2. **S0380.97** (`7121a86b`) — Floor §9 "Insulation Thickness" + extractor (RdSAP 10 §5.13 Table 20). BP[2] floor U: + 0.51→**0.22 ✓ EXACT**. **sap_score 28→29 ✓ EXACT**. Continuous + SAP Δ -0.0001 (within 1e-4 strict floor). +3. **S0380.98** (`b3330821`) — PCDB Table 322 (Decentralised MEV) + ETL + parser + lookup foundation (PCDF Spec §A.19). +4. **S0380.99** (`433f4a49`) — PCDB Table 329 (MV In-Use Factors) + ETL + parser + lookup foundation (PCDF Spec §A.20). +5. **S0380.100** (`44fb8c07`) — SFPav + Table 4f (230a) cascade + helpers in `worksheet/mev.py` (SAP 10.2 §2.6.4). +6. **S0380.101** (`1b183f9c`) — HP SAP code 211-227 / 521-527 → + `main_heating_category=4` (SAP 10.2 Table 4a). +7. **S0380.102** (`a0413155`) — Wire MEV cascade into + `_table_4f_additive_components`. **pumps_fans_kwh_per_yr ✓ + EXACT** (was +2.48 over). Schema + extractor + mapper for MV + PCDF index / wet rooms / duct type. +8. **S0380.103** (`e3abe9b2`) — MEV-fan cost split via Table 12a + Grid 2 `FANS_FOR_MECH_VENT` rate. cost residual Δ +£0.39 → + -£1.62 (sign flipped; SH cascade residual now exposed). + +**Cert 000565 state at HEAD `e3abe9b2`:** + +| Pin | Cascade | Worksheet | Δ | +|---|---:|---:|---:| +| **sap_score (int)** | **29** | 29 | **✓ EXACT** | +| sap_score_continuous | 28.5269 | 28.5087 | +0.0182 | +| ecf | 5.3850 | 5.3866 | -0.0016 | +| total_fuel_cost_gbp | 4678.6372 | 4680.2593 | -1.6221 | +| co2_kg_per_yr | 6445.8198 | 6447.6263 | -1.8065 | +| space_heating_kwh_per_yr | 58980.8225 | 59008.3499 | -27.5274 | +| main_heating_fuel_kwh_per_yr | 34694.6015 | 34710.7941 | -16.1926 | +| **pumps_fans_kwh_per_yr** | **252.5159** | 252.5159 | **✓ 0 EXACT** | +| **hot_water_kwh_per_yr** | 3755.0288 | 3755.0288 | **✓ 0 EXACT** | +| lighting_kwh_per_yr | 1387.0237 | 1384.8353 | +2.1884 | + +## Recommended next slice — S0380.104 § Investigate §3-§8 SH cascade -27 kWh + +**The current biggest residual driver.** Cert 000565 cascade +space_heating_kwh = 58980.82 vs ws 59008.35 → Δ -27.53 kWh under. +This propagates downstream to main_heating_fuel (-16.19 kWh under) +and total_fuel_cost (-£1.62 under). It is the dominant cause of +continuous-SAP residual +0.0182 OVER ws. + +### Why it's now exposed + +S0380.103 closed the +£2.01 MEV-cost over-count (Table 12a Grid 2 +split). Pre-slice that over-count nearly cancelled the SH under- +count → cost looked +£0.39 over. Post-slice the SH under-count +shows through to cost / co2 / continuous SAP. + +The SH cascade IS correct on the cohort fixtures (000474..000516 at +1e-4) so this is **cert-000565-specific**. The differentiators are: +- 5 building parts (Main + 4 extensions) +- Heat pump + gas combi WHC 914 +- Detailed-RR with residual area (S0380.95 closure) +- MEV decentralised +- FGHRS, solar HW, draught lobby, basement walls (Ext3/Ext4), + Curtain Wall Post-2023 (Ext2), CF + CU party walls + +### Investigation approach + +1. **Probe per-month `space_heat_requirement_kwh`** vs ws line + (98c)m to identify which month(s) carry the residual: + + ```python + from domain.sap10_calculator.worksheet.tests._elmhurst_worksheet_000565 import build_epc + from domain.sap10_calculator.rdsap.cert_to_inputs import cert_to_inputs + epc = build_epc() + inputs = cert_to_inputs(epc) + print("monthly SH:", inputs.space_heating_monthly_kwh) + # Compare to ws (98c)m line refs from U985-0001-000565.pdf + ``` + +2. **Check the fabric subtotals** — net cascade HTC is +29.45 W/K + over ws. Closing roof BP[1] residual (+1.29 W/K, deferred) + + thermal_bridging (+0.70) brings it to +27.5. Walls are -2.85 + under and windows/roof_windows offset. + + Big +29 W/K HTC should DRIVE space_heating UP, but cascade SH is + -27 kWh UNDER. That means the cascade is OVER-counting heat + GAINS somewhere, or UNDER-counting demand by an offsetting factor. + +3. **Check internal gains** — pumps_fans gains (line 70) changed + between cohort certs and cert 000565 (HP cat=4 → 0W heating- + season pump). Verify against ws line (70)m by month. + +4. **Check solar gains** (line 74-83) — sub-spec window U could + propagate to gain magnitude. + +5. **Check utilisation factor / mean-internal-temp solve** — multi- + BP cert with mixed age bands might hit a corner case. + +Expected closure: continuous SAP +0.0182 → within 1e-4. + +## Alternative next slice — S0380.105 § CO2 cascade MEV split + +Mirror of S0380.103 for CO2. Cert 000565 worksheet line (267): + +``` +Pumps, fans and electric keep-hot 252.5159 × 0.1412 = 35.3349 +``` + +Cascade `pumps_fans_co2_factor_kg_per_kwh = 0.14116` (kWh-weighted +Table 12d monthly factor for code 30) → 35.6453 kg → +0.31 over ws. + +The cascade applies a single Table 12d profile across all +pumps_fans. The worksheet integrates MEV (year-round) separately +from heating-season pumps + flue fans. + +**Slice scope:** add an MEV-weighted CO2 factor analogous to +`_pumps_fans_fuel_cost_gbp_per_kwh`. Add field +`CalculatorInputs.pumps_fans_co2_factor_kg_per_kwh` resolution that +weights two streams. + +Impact: -0.31 kg/yr → continuous SAP downstream marginal change. + +This is the **lower-leverage** of the two open options. S0380.104 +SH investigation is higher leverage. + +## 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/worksheet/tests/test_mev.py \ + domain/sap10_calculator/rdsap/tests/test_cert_to_inputs.py \ + domain/sap10_calculator/rdsap/tests/test_golden_fixtures.py \ + domain/sap10_calculator/tests/test_pcdb_table_322_lookup.py \ + domain/sap10_calculator/tests/test_pcdb_table_329_lookup.py \ + --no-cov -q +``` + +Expected: **597 pass + 7 expected `test_sap_result_pin[000565-*]` fails**. + +After S0380.104 lands the expected fail count should drop by 5-7 +(sap_score_continuous, ecf, total_fuel_cost_gbp, co2_kg_per_yr, +space_heating_kwh_per_yr, main_heating_fuel_kwh_per_yr) if the SH +cascade closes. Lighting (+2.19 kWh) is unrelated and survives as +its own slice. + +## 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 7 cert 000565 fails are the + work queue. +- **Don't re-investigate any closed work** (.91..103). All settled. +- **Don't add new helpers to `domain/sap10_ml/`** — deprecation path + per [[project-sap10_ml-deprecation]]. New cascade helpers belong + under `domain/sap10_calculator/`. +- **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 closure + open work- + items refresh +- `MEMORY.md` index — refresh HEAD + one-line summary + +Good luck.