From 077e3a39473996b527158ae2bac21c0775a7fe7b Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 15 Jun 2026 13:46:22 +0000 Subject: [PATCH] test(orchestration): re-pin multi-measure plan to the gain-maximising package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optimiser-package expectation was stale: it predated the optimiser folding a triggered measure's forced dependency into its candidate gain (ADR-0016). The run considers ALL measures (considered_measures defaults to None — no restriction), so once the ASHP bundle became SAP-beneficial (ADR-0025) the gain-maximising package shifted. Verified the new package is CORRECT, not a regression: on the test EPC, cavity-wall insulation earns +2.9 SAP alone but its forced fabric→ ventilation dependency (ADR-0016) drags the wall+ventilation pair to a NET −1.8 SAP (−0.9 on top of the ASHP package), so the gain-maximising Optimiser correctly excludes the wall and its forced ventilation. Update the expected set to {air_source_heat_pump, suspended_floor_insulation, low_energy_lighting, secondary_heating_removal} and drop the wall/vent- specific assertions — the forced wall→ventilation edge is covered by test_measure_dependency / test_optimiser; this integration test keeps its end-to-end optimise→persist→telescope coverage on the chosen package. Pre-existing failure (present before this branch's recent commits), outside the handover regression gate. Co-Authored-By: Claude Opus 4.8 --- ...test_ara_first_run_pipeline_integration.py | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/tests/orchestration/test_ara_first_run_pipeline_integration.py b/tests/orchestration/test_ara_first_run_pipeline_integration.py index 7caffe6f..192f07a5 100644 --- a/tests/orchestration/test_ara_first_run_pipeline_integration.py +++ b/tests/orchestration/test_ara_first_run_pipeline_integration.py @@ -384,29 +384,29 @@ def test_modelling_optimises_and_persists_a_multi_measure_plan( assert plan.energy_consumption_savings > 0.0 by_type = {rec.type: rec for rec in rec_rows} + # The gain-maximising package: the efficient representative heat pump + # (Vaillant aroTHERM plus 5 kW, ADR-0025) now raises SAP even on this gas + # dwelling, plus the cheap positive-SAP fabric/lighting/secondary measures. + # CAVITY-WALL INSULATION is NOT selected: it earns +2.9 SAP alone, but the + # fabric→ventilation forced dependency (ADR-0016) drags the wall+ventilation + # pair to a NET −1.8 SAP (−0.9 on top of the ASHP package), so the Optimiser + # correctly leaves the wall — and therefore its forced ventilation — out. + # (The forced wall→ventilation edge itself is covered by + # test_measure_dependency / test_optimiser; here we prove the end-to-end + # optimise→persist→telescope pipeline on the package the Optimiser keeps.) + # The sample EPC lodges 8 low-energy-unknown bulbs (LED upgrade, ADR-0023) + # and an electric secondary heater (SAP 691, removal offered per ADR-0028). assert set(by_type) == { - "cavity_wall_insulation", "suspended_floor_insulation", - "mechanical_ventilation", - # The sample EPC lodges 8 low-energy-unknown bulbs, so the LED upgrade is - # a cheap positive-SAP candidate the Optimiser also keeps (ADR-0023). "low_energy_lighting", - # The efficient representative heat pump (Vaillant aroTHERM plus 5 kW, - # ADR-0025) now raises SAP even on this gas dwelling, so the Optimiser - # also keeps the ASHP bundle in the least-cost-to-band package (ADR-0024). "air_source_heat_pump", - # The sample lodges an electric secondary (SAP 691), so removal is offered - # (ADR-0028); the Optimiser keeps it in its all-beneficial-measures package - # — its SAP gain is 0 once the ASHP (category 4) ignores the secondary, but - # the heater is still physically removed at its own cost. "secondary_heating_removal", } # Each persisted measure carries the catalogue id of the Product it installs # (the MaterialRow ids seeded above), replacing the retired # recommendation_materials BOM with a single material_id on the row. - assert by_type["cavity_wall_insulation"].material_id == 1 + assert by_type["air_source_heat_pump"].material_id == 5 assert by_type["suspended_floor_insulation"].material_id == 2 - assert by_type["mechanical_ventilation"].material_id == 3 assert by_type["low_energy_lighting"].material_id == 4 assert by_type["secondary_heating_removal"].material_id == 9 for rec in rec_rows: @@ -414,27 +414,12 @@ def test_modelling_optimises_and_persists_a_multi_measure_plan( assert rec.already_installed is False assert rec.sap_points is not None assert rec.estimated_cost is not None - # The forced ventilation costs two £450 units and is priced even though it - # was never a free choice in the pool. - vent_cost: float | None = by_type["mechanical_ventilation"].estimated_cost - assert vent_cost is not None - assert abs(vent_cost - 900.0) <= 1e-6 - # The insulation measures earn positive SAP; ventilation's contribution is - # not positive (it only ever costs SAP — ADR-0016). - wall_sap: float | None = by_type["cavity_wall_insulation"].sap_points - vent_sap: float | None = by_type["mechanical_ventilation"].sap_points - assert wall_sap is not None and vent_sap is not None - assert wall_sap > 0.0 - assert vent_sap <= 0.0 # Per-measure bill savings (telescoping cascade, ADR-0014 amendment): each # measure carries its delivered-kWh and £ saving, and they telescope exactly - # to the Plan's headline savings. Ventilation increases energy, so its - # savings are negative — and the telescoping still holds. + # to the Plan's headline savings. for rec in rec_rows: assert rec.kwh_savings is not None assert rec.energy_cost_savings is not None - vent_kwh: float | None = by_type["mechanical_ventilation"].kwh_savings - assert vent_kwh is not None and vent_kwh < 0.0 kwh_total: float = sum(rec.kwh_savings or 0.0 for rec in rec_rows) cost_total: float = sum(rec.energy_cost_savings or 0.0 for rec in rec_rows) assert plan.energy_consumption_savings is not None