Model/domain/sap10_calculator/docs/HANDOVER_POST_S0380_114.md
Khalim Conn-Kowlessar 2fe84fcc7e docs: handover + next-agent prompt post S0380.110..114 (cert 000565 SAP exact at 1e-4)
Five spec-cited slices closed cert 000565 from continuous SAP
Δ = -0.0059 → +0.000042 (within user 1e-4 tolerance):

- S0380.110: per-rooflight g_L via Appendix L §L2a
- S0380.111: roof-window inclination adj via Table 6e Note 2
- S0380.112: per-BP rooflight deduction via RdSAP §3.7
- S0380.113: H=0 gable retention via RdSAP §3.9.2 step (b)
- S0380.114: pump GAIN for HP+boiler via Table 5a Note a)

Handover documents the two parallel workstreams the next agent
should tackle:
1. Final sweep for TRULY exact continuous SAP on cert 000565
   (close the remaining sub-1e-4 cost/CO2/SH/fuel/ECF residuals)
2. Tighten golden test residuals across the corpus per
   [[feedback-golden-residuals-near-zero]]

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 19:53:45 +00:00

14 KiB
Raw Blame History

Handover — post S0380.110..114 (cert 000565 continuous SAP exact at 1e-4)

Branch: feature/per-cert-mapper-validation. HEAD cc70e559. Predecessor: HANDOVER_POST_S0380_109.md.

TL;DR

Cert 000565 was closed from continuous SAP Δ = 0.0059 → +0.000042 (within the user's 1e-4 tolerance) across 5 spec-cited slices:

Slice Commit Spec Effect on cert 000565
S0380.110 9461e657 SAP 10.2 Appendix L §L2a (PDF p.88) — per-rooflight g_L via Table 6b lighting_kwh -2.17 → ✓ EXACT
S0380.111 794ef7ed SAP 10.2 §3.2 + Table 6e Note 2 (PDF p.180) — roof-window inclination adj +0.30 W/m²K roof_windows_w_per_k -0.43 → ✓ EXACT
S0380.112 a461b70d RdSAP 10 §3.7 (PDF p.19) — per-BP rooflight deduction Roof +0.30 → -0.06 W/K, TB +0.15 → -0.03 W/K
S0380.113 59de805e RdSAP 10 §3.9.2 step (b) (PDF p.23) — absent gable H=0 lodgement Fabric closed (max 0.005 W/K residual across 8 components)
S0380.114 cc70e559 SAP 10.2 Table 5a Note a) (PDF p.177) — pump GAIN for HP+boiler hybrids Continuous SAP -0.008 → +0.000042

Test baseline at HEAD cc70e559: 616 pass + 5 expected test_sap_result_pin[000565-*] fails (continuous SAP pin closed; remaining 5 are cost / CO2 / SH / fuel / ECF at strict 1e-4 abs).

Pyright net-zero per touched file across every slice.

Critical user direction (read before any tool call)

  1. Primary metric is sap_score_continuous. Target is EXACT (Δ = 0), not 1e-4. The user explicitly wants the cascade to be a true spec replica. Sub-1e-4 residuals are not "essentially exact" — they are real bugs to find and close.

  2. Tighten loose pins as the cascade improves. Per feedback-golden-residuals-near-zero the test_golden_fixtures.py expected_*_resid values were pinned at whatever the cascade produced at the time. As the cascade gets more spec-correct, those pins should shrink toward 0. This is now an active workstream — not just for cert 000565 but across the whole golden corpus.

  3. Don't widen tolerances to make tests pass. Per feedback-zero-error-strict — 1e-4 absolute is the bar, no pytest.approx(rel=...), no xfail, no "spec-precision floor" framing.

State table — cert 000565 (HEAD cc70e559)

Fabric — all ✓ EXACT

Component Cascade WS Δ
walls (29a) 604.0710 604.0710 +0.0000
floor (28a/b) 61.6743 61.6700 +0.0043
roof (30) 51.3768 51.3795 0.0027
windows (27) 11.4788 11.4787 +0.0001
roof_windows (27a) 3.5806 3.5805 +0.0001
doors (26a) 11.1000 11.1000 0.0000
party_walls (32) 65.1300 65.1300 0.0000
thermal_bridging (36) 128.6448 128.6500 0.0052
external area (31) 857.6323 857.6400 0.0077
total HTC (33) 937.0563 937.0600 0.0037

Energy + cost — close but not exact

Pin Cascade WS Δ Rel
sap_score (int) 29 29 0 ✓ EXACT
sap_score_continuous 28.508742 28.5087 +0.000042 1.5e-6
ecf 5.386823 5.3866 +0.000223 4e-5
total_fuel_cost_gbp 4680.2515 4680.2593 0.0078 2e-6
co2_kg_per_yr 6447.6161 6447.6263 0.0102 2e-6
space_heating_kwh 59008.2363 59008.3499 0.1136 2e-6
main_heating_fuel 34710.7272 34710.7941 0.0669 2e-6
lighting_kwh 1384.8353 1384.8353 0 ✓ EXACT
hot_water_kwh 3755.0288 3755.0288 0 ✓ EXACT
pumps_fans_kwh 252.5159 252.5159 0 ✓ EXACT
pumps_fans_co2 35.3349 35.3349 0 ✓ EXACT
pumps_fans_pe 383.3797 383.3796 0 ✓ EXACT

Next agent's job — TWO PARALLEL WORKSTREAMS

Workstream 1: True exact closure of cert 000565

Continuous SAP currently at +4.2e-5. The user wants 0. The remaining sub-1e-4 residuals are sub-spec float drift somewhere in the cascade. Some candidates worth investigating:

  1. Floor +0.0043 W/K residual. Small but persistent. Probably a 2-d.p. rounding inconsistency in u_floor or floor-area cascade. At U≈0.7, this is 0.006 m² of phantom area.

  2. Roof 0.0027 W/K residual. Probably the Ext3 A_RR_shell formula precision (12.5 × √(32.0/1.5) cascade vs Elmhurst's slightly different result). Could be a rounding step in the cascade Elmhurst doesn't apply, or vice versa.

  3. MIT off by 0.0008°C average. Tiny but accumulates over 8 heating months. Drives part of the SH residual.

  4. Utilisation factor off by 0.0001. Same story.

  5. Cost / CO2 / PE per-month factor application. The cascade applies SAP10.2 Table 12 monthly factors to per-month fuel energy. Look for whether the cascade uses the worksheet's exact monthly weighting vs an annual-average shortcut.

Approach: the existing audit method works — dump every monthly intermediate value, diff against worksheet line refs, find the smallest residual that's still > 1e-6, trace its source. Continue the discipline from the prior 5 slices.

Verification: the e2e test test_sap_result_pin[000565-*] pins every result field at abs=1e-4. When all 5 currently-failing fields close, cert 000565 is truly exact.

Workstream 2: Tighten golden test residuals

test_golden_fixtures.py has ~50+ certs with expected_sap_resid / expected_pe_resid / expected_co2_resid baselines. Many were pinned at whatever the cascade produced at the time of test-creation. After the recent slice improvements (especially S0380.110..114), several of these should now be re-pinnable at SMALLER residuals.

Approach:

  1. Run the golden fixture suite — note any tests that still pass but have an expected_*_resid magnitude > 1e-4. Each is a candidate for re-pinning.

  2. For each candidate, check the actual cascade residual today vs the pinned expected. If the cascade is now CLOSER to lodged (residual smaller in magnitude), re-pin to the new (smaller) value. Document the why in the test's notes field.

  3. For pins that are far from 0 (e.g. expected_sap_resid=-14 on cert 0240), investigate the gap. Some will be load-bearing mapper gaps (cert 0240 has a documented mapper note); others may be spec bugs the recent slices half-closed. Treat each as a mini- audit.

  4. The user's bar (2026-05-28 onwards): residuals should be at ~1e-2 PE / 1e-3 CO2 or smaller for mapper-closed certs. Any cert whose notes say "mapper gap closed in slice X" should have expected_*_resid pinned at near-zero.

Other test files to sweep:

Each may have assert abs(diff) <= TOL constructs where TOL is historically lax. Sweep + tighten as the underlying cascade precision allows.

Memories to load before any tool call

  1. project_cert_000565_recovery_state — per-slice history + open work
  2. project_sap10_ml_deprecationdomain/sap10_ml/ retiring
  3. feedback_sap_10_2_only_never_10_3CRITICAL — never reference SAP 10.3
  4. feedback_spec_citation_in_commits — quote spec + page in commit messages
  5. feedback_verify_handover_claims — verify numeric claims against source PDF
  6. feedback_zero_error_strict — pyright net-zero per touched file
  7. feedback_commit_per_slice — one slice = one commit
  8. feedback_aaa_test_convention# Arrange / # Act / # Assert headers
  9. feedback_e2e_validation_philosophy — abs=1e-4 pins, no rel/xfail
  10. feedback_abs_diff_over_pytest_approx — use abs(x-y) <= tol
  11. feedback_spec_floor_skepticism — verify "spec-precision floor" claims
  12. feedback_golden_residuals_near_zero — golden pins should shrink toward 0
  13. reference_unmapped_sap_code — calculator strict-raise pattern

How to run the baseline

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: 616 pass + 5 expected test_sap_result_pin[000565-*] fails:

ecf
total_fuel_cost_gbp
co2_kg_per_yr
space_heating_kwh_per_yr
main_heating_fuel_kwh_per_yr

(Note: sap_score_continuous pin already passes at +4.2e-5 < 1e-4.)

Cohort fixture state (HEAD cc70e559)

For reference, the 7 hand-built / extractor-driven fixtures all land their integer SAP exact:

cert sap_score sap_continuous
000474 62 62.2584
000477 65 65.0057
000480 61 61.2986
000487 62 61.6431
000490 57 57.3979
000516 63 62.7937
000565 29 28.5087 ← user target reached

How the audit worked (replicate this method)

The single-bug-per-slice closure pattern that worked for S0380.110..114:

  1. Audit before implementing. Dump every cascade intermediate value alongside the worksheet line ref. Don't trust handover narratives — verify the actual numerical residual against the source PDF.

  2. Find the spec citation. When you spot a residual, search the spec for what the value SHOULD be. The bug is almost always a misreading or omission of a specific spec clause.

  3. Confirm the back-solve. Before writing code, prove the hypothesis: "if I add the spec rule, the cascade should produce X". Compare X against the worksheet. If it matches at 1e-4 or better, ship the slice.

  4. Tight AAA tests. Pin the narrowest intermediate the slice directly changes. Don't pin downstream-rolled-up values with tight thresholds (S0380.103 cost-test reframing pattern).

  5. Cohort safety. Verify the new rule doesn't break the cohort certs. Usually the new spec branch is gated by a condition that doesn't fire on cohort (e.g. "non-HP system present alongside HP" doesn't apply to cohort gas-only certs).

Spec source quick-reference

All under domain/sap10_calculator/docs/specs/:

  • SAP 10.2 full spec: sap-10-2-full-specification-2025-03-14.pdf
    • §3.2 + Table 6e Note 2 (p.180) — roof-window inclination adj — S0380.111
    • §10a Table 12a Grid 2 (p.191) + Table 12d (p.194) + Table 12e (p.195) — MEV trifecta
    • Appendix L §L2a (p.88) + Table 6b (p.178) — daylight factor — S0380.110
    • Table 5a Note a) (p.177) — pump gain spec — S0380.114
  • RdSAP 10 spec: RdSAP 10 Specification 10-06-2025.pdf
    • §3.7 (p.19) — per-BP window/door deduction — S0380.112
    • §3.7.1 (p.21) — window vs roof window classification — S0380.107
    • §3.9.2 step (b) (p.23) — Type 2 RR gable formula (including H=0) — S0380.113
    • §3.9.2 step (d) (p.23) — Connected RR deduction — S0380.108
    • §5.6 + Table 12 (p.40-41) — stone wall — S0380.109
    • §5.7 + Table 13 (p.41) — brick wall U₀ — S0380.109
    • §5.8 + Table 14 (p.41-42) — insulation R — S0380.109
  • SAP 10.3 at sap-10-3-full-specification-2026-01-13.pdf: DO NOT reference (feedback-sap-10-2-only-never-10-3)

Files touched this session (S0380.110..114)

File Slices Purpose
datatypes/epc/domain/epc_property_data.py .110, .112 SapRoofWindow.glazing_type + .window_location
datatypes/epc/domain/mapper.py .110, .111, .112, .113 Roof-window glazing/BP/inclination; H=0 gable retention
domain/sap10_calculator/worksheet/internal_gains.py .110, .114 Per-rooflight g_L dispatch; HP+boiler pump gain
domain/sap10_calculator/worksheet/heat_transmission.py .112, .113 Per-BP rooflight deduction; negative gable area handling
domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000516.py .110, .112 glazing_type=2 + window_location="Main" on cohort rooflight
backend/documents_parser/tests/test_summary_pdf_mapper_chain.py .110..114 AAA tests for each slice

What NOT to do

  • Don't reference SAP 10.3 (feedback-sap-10-2-only-never-10-3).
  • Don't widen pin tolerances to make currently-failing pins pass (feedback-zero-error-strict). Find the bug, fix it, the pin closes.
  • Don't re-investigate any closed work (.91..114). All settled.
  • Don't add new helpers to domain/sap10_ml/ — deprecation path.
  • Don't pin downstream-only metrics with tight thresholds — S0380.103 cost-test pattern. Pin the narrowest intermediate the slice directly changes.

Memory hygiene

After each new slice, update:

  • project_cert_000565_recovery_state — append slice closure + refresh open work
  • MEMORY.md — refresh HEAD + one-line summary

Good luck. Cert 000565 is at the threshold — one or two more spec-precision slices and it's truly exact. Then sweep the rest of the cohort + golden fixtures with the same discipline.