# Next-agent prompt — PV β-split slices 4-6 Branch: `feature/per-cert-mapper-validation`. HEAD: `beb0db95` (docs commit on top of S0380.46 `5b269f23`). Read `domain/sap10_calculator/docs/HANDOVER_PV_BETA_SPLIT.md` end-to-end before any tool call. It has the full state, the 3 slices shipped (S0380.44 → S0380.46), the residual table showing where each cert sits, and the concrete plans for the 3 remaining slices. ## My directives The PV β-split work is a 6-slice plan; 3/6 are shipped. Continue: 1. **Slice 4 (S0380.47) — cost cascade β wiring.** [fuel_cost.py:182](../worksheet/fuel_cost.py) currently does `pv_credit = -pv_generation × pv_export_credit_gbp_per_kwh` — treats ALL PV as exported at 13.19 p/kWh. Per Appendix M1 §6, onsite-consumed PV should bill at the IMPORT price (Table 12a standard tariff ~18 p, or weighted high/low if off-peak meter). The β infrastructure is already in place (`pv_dwelling_kwh_per_yr` + `pv_exported_kwh_per_yr` on CalculatorInputs from Slice 2). Add a new `pv_dwelling_import_price_gbp_per_kwh` field, wire it in `cert_to_inputs` using the same off-peak meter logic as `_space_heating_fuel_cost_gbp_per_kwh`, and split the credit in fuel_cost.py. This is the riskiest slice because every PV cert's SAP rating shifts. The cohort-1 + cohort-2 chain-test 1e-4 pins will need re-pinning — expect small Δ (~0.02-0.05 SAP per cert) so the new pins will still be tight against worksheets. Re-pin AS PART OF SLICE 4 so the suite stays green between commits. 2. **Slice 5 (S0380.48) — E_PV magnitude audit.** The 7-cert ASHP+5kWh-battery cohort (0350/0380/2225/2636/3800/9285/9418) overshoots PE by +2.7..+8.1 because the cascade computes E_PV ≈ 3× the worksheet's value. For cert 0380: cascade thinks 2570 kWh/yr, worksheet uses 831 kWh/yr. Either `peak_power=3` in the API JSON is in non-kWp units, the cascade's S lookup is wrong, or ZPV is mis-mapped. Concrete probes (in handover §"Slice 5 plan"): - Compare cert 0380's API `peak_power=3` against the Elmhurst Summary PDF Section 19 for the same cert - Compute cascade S for orientation=South, pitch=45°, overshading=1=None — compare to SAP Appendix U3.3 spec value (expected ~1100 kWh/m²/yr UK avg) - Verify Table M1 ZPV[1] = 1.0 against spec - Empirical test: set cert 0380 `peak_power = 1.0` and check if residuals close If it's a kWp interpretation bug, surface via the schema or API mapper. 3. **Slice 6 (S0380.49) — final fixture re-pin + tolerance tightening.** Once Slices 4 + 5 ship, the ASHP cohort residuals should land near zero. Re-pin all affected golden fixtures; if the cluster lands tightly (~0.01 PE / ~0.001 CO2), tighten `_PE_ABS_TOLERANCE_KWH_PER_M2` / `_CO2_ABS_TOLERANCE_TONNES` accordingly per [[feedback-golden-residuals-near-zero]]. ## Conventions preserved (carry forward) - 1e-4 across the board ([[feedback-one-e-minus-4-across-the-board]]) - Worksheet, not API, is the chain-test target ([[feedback-worksheet-not-api-reference]]) - Cross-mapper parity via cascade ([[feedback-cross-mapper-parity-via-cascade]]) - Spec-floor skepticism ([[feedback-spec-floor-skepticism]]) - Bigger slices OK for uniform work ([[feedback-bigger-slices-for-uniform-work]]) - Golden residuals → ~0 ([[feedback-golden-residuals-near-zero]]) - AAA test convention + `abs(diff) <= tol` ([[feedback-aaa-test-convention]], [[feedback-abs-diff-over-pytest-approx]]) - Spec citation in commit messages ([[feedback-spec-citation-in-commits]]) - One slice = one commit; stage by name; re-pin shifted fixtures IN SAME SLICE so suite stays green ([[feedback-commit-per-slice]]) - Pyright net-zero per touched file - Strict-enum raises on unmapped labels ## First concrete actions 1. Re-run the diagnostic baseline at the bottom of `HANDOVER_PV_BETA_SPLIT.md` to confirm **763 pass + 0 fail** at HEAD. 2. Start Slice 4 by reading [fuel_cost.py:182](../worksheet/fuel_cost.py) and the existing `_space_heating_fuel_cost_gbp_per_kwh` in cert_to_inputs.py to understand the off-peak meter price-resolution logic. Mirror that pattern for the dwelling IMPORT price. 3. After Slice 4 lands and chain tests are re-pinned: Slice 5's first probe is comparing cert 0380's API `peak_power` against the Summary PDF lodgement. The golden-fixture cert 0380 is `0380-2471-3250-2596-8761`; its Summary PDF + dr87 worksheet live in `backend/documents_parser/tests/fixtures/` — Section 19 of the Summary carries the PV array lodgement. 4. Slice 6 wraps up — re-pin, verify, document. ## Architecture lessons that landed this session (load-bearing) - **β-split shape is uniform across PE / CO2 / Cost.** Each cascade had the same bug — credit ALL PV at one rate (IMPORT for PE; missing for CO2; EXPORT for cost). The spec-correct fix is uniformly onsite-at-IMPORT + exported-at-EXPORT. `CalculatorInputs.pv_dwelling_kwh_per_yr` + `pv_exported_kwh_per_yr` are shared cross-cascade state; each cascade adds its own factor-pair fields. `None` falls back to legacy single-rate for synthetic test constructions. - **The EXPORT factor is Table 12 code 60** ("electricity sold to grid, PV") at all three cascades — already in `domain/sap10_calculator/tables/table_12.py` for PE (0.501) and CO2 (monthly Table 12d). For Slice 4 cost, you'll reference the same code 60 export-price from Table 12a (typically 13.19 p/kWh for the spec price set). - **Cert 9501 is the validation pin.** It has PV but no battery, and its PE + CO2 residuals both closed to ~0 after Slices 2-3. Any future cascade refactor must keep cert 9501 closed. Good luck. The β-implementation is spec-correct (cert 9501 proves it). Slices 4-5 surface the remaining bugs as forcing functions; Slice 6 finalises the closure.