From 6b4f883bc8d44f75aeb10fdd8f20ac0e9f18ea0d Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 22 Jul 2026 15:34:12 +0000 Subject: [PATCH] =?UTF-8?q?Re-baseline=20epc-prediction=20gates=20for=20th?= =?UTF-8?q?e=20#1669=20ground-truth=20change=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADR-0030 component-accuracy and expired-pairs gates re-derive the *actual* EpcPropertyData through the same mapper the leave-one-out scorer predicts against. #1669 (measurement_type=2 external->internal conversion, RdSAP §3.4 + Table 2) gives the fixture's external certs sharper internal dimensions, which tips the geo-proximity-weighted donor mode on three components by single-target amounts: total_window_area 3.7184 -> 3.7484, door_count 0.3333 -> 0.3737, has_pv floor 0.8929 -> 0.8571 (one pair 25/28 -> 24/28). This is a ground-truth-method change, not a prediction-logic loosening: the fix is spec-correct, corpus MAE improved (0.571 -> 0.570) and the real-cert accuracy pin held. Per the gate convention the affected floors/ceilings are re-baselined to the measured values with the attribution recorded; tighten-only resumes here. Bisected: #1666/#1667 leave the gates green; #1669 introduces all three shifts. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../epc_prediction/test_component_accuracy_gate.py | 13 +++++++++++-- .../epc_prediction/test_expired_pairs_gate.py | 11 ++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/domain/epc_prediction/test_component_accuracy_gate.py b/tests/domain/epc_prediction/test_component_accuracy_gate.py index e4bcd41f6..a9065c926 100644 --- a/tests/domain/epc_prediction/test_component_accuracy_gate.py +++ b/tests/domain/epc_prediction/test_component_accuracy_gate.py @@ -124,11 +124,20 @@ _RATE_FLOORS: dict[str, float] = { # dimensional predictions); floor_area loosened 12.0378 -> 12.0586 as the one # physical residual that fell (1-2 targets picking a new-build donor). See the # _RATE_FLOORS note above. +# total_window_area 3.7184 -> 3.7484 and door_count 0.3333 -> 0.3737 re-baselined +# when the external-measurement conversion landed (#1669: measurement_type=2 certs +# now have their floor area + exposed perimeter converted external->internal per +# RdSAP 10 §3.4 + Table 2). The leave-one-out scorer re-derives the *actual* +# EpcPropertyData through the same mapper, so the sharper (internal) dimensions of +# the fixture's external certs shift the geo-proximity-weighted donor mode — a +# ground-truth-method change, not a prediction-logic loosening (spec-correct, +# corpus MAE 0.571 -> 0.570 and the real-cert pin held). The move is two +# single-target donor tips on the n=36 fixture. Tighten-only resumes from here. _RESIDUAL_CEILINGS: dict[str, float] = { "floor_area": 12.0586, - "total_window_area": 3.7184, + "total_window_area": 3.7484, "building_parts": 0.1212, - "door_count": 0.3333, + "door_count": 0.3737, } _TOLERANCE = 1e-3 diff --git a/tests/domain/epc_prediction/test_expired_pairs_gate.py b/tests/domain/epc_prediction/test_expired_pairs_gate.py index ed037815c..93abebb8b 100644 --- a/tests/domain/epc_prediction/test_expired_pairs_gate.py +++ b/tests/domain/epc_prediction/test_expired_pairs_gate.py @@ -55,6 +55,15 @@ _FIXTURE = ( # 28-scoreable fixture) and residual ceilings — the measured values; tighten, # never loosen. The general (unconditioned) prediction floors live in # test_component_accuracy_gate.py; this gate guards the CONDITIONING path. +# +# has_pv re-baselined 0.8929 -> 0.8571 when the external-measurement conversion +# landed (#1669: measurement_type=2 certs converted external->internal per RdSAP +# 10 §3.4 + Table 2). This gate re-derives the *actual* EpcPropertyData through +# the same mapper, so the sharper internal dimensions of the fixture's external +# certs shift the conditioned donor mode and one pair's has_pv agreement tips +# 25/28 -> 24/28 — a ground-truth-method change, not a prediction-logic loosening +# (spec-correct; corpus MAE improved and the real-cert pin held). Tighten-only +# resumes from here. _CLASSIFICATION_FLOORS: dict[str, float] = { "construction_age_band": 0.5357, "construction_age_band_pm1": 0.8214, @@ -62,7 +71,7 @@ _CLASSIFICATION_FLOORS: dict[str, float] = { "floor_construction": 0.8636, "floor_insulation": 1.0000, "has_hot_water_cylinder": 0.8214, - "has_pv": 0.8929, + "has_pv": 0.8571, "has_room_in_roof": 0.8571, "heating_main_category": 1.0000, "heating_main_control": 0.6071,