3.4 KiB
| status |
|---|
| accepted (extends ADR-0016; composes with ADR-0061) |
Goal-aligned Optimiser objectives: each goal maximises its own metric
Every Scenario goal used to optimise SAP. The legacy engine returned no
target for Energy Savings / Reducing CO2 (optimiser_functions.calculate_gain
→ None) and maximised SAP gain within budget regardless of the goal, and the
new engine inherited that: the goal label changed nothing but the words on the
brief. The scorer already computes each package's carbon and (via SapResult →
EnergyBreakdown → BillDerivation) its annual bill, so aligning the objective
is a selection change, not a calculator change.
Decided in a grilling session with Khalim, 2026-07-09.
Decision
The Optimiser maximises the Scenario goal's own metric, as a pluggable
objective: Callable[[Score], float] (higher is better), with no target:
goal-aligned briefs are "reduce as much as possible within this budget".
- Reducing CO2 emissions maximises annual kg CO2 saved
(
-score.co2_kg_per_yr). - Energy Savings maximises the annual bill £ saved, priced at the live Fuel Rates snapshot (ADR-0014), not SAP's internal tariff book — that difference is the point of the goal. SAP is itself a cost-shaped rating, so the two frequently agree; they diverge exactly when current tariffs disagree with SAP's assumptions (e.g. the gas/electricity price ratio).
- Increasing EPC keeps its SAP objective and band-target semantics (least-cost-to-target, repair, max-gain fallback) unchanged.
- Valuation Improvement / None stay max-SAP-within-budget — SAP is a
defensible valuation proxy and
Nonehas no semantics to encode. goal_valueis ignored for the goal-aligned goals — no percentage or absolute target exists yet. If targets arrive later they slot into the existing target machinery on the objective's scale.- A budget is mandatory for the goal-aligned goals: unconstrained
"as much as possible" would recommend every beneficial measure. A
budget-less Energy/CO2 Scenario raises a
ValueErrornaming the scenario and goal — a loud misconfiguration, not a maximal plan. - One currency everywhere: the role-1 group signals
(
independent_option_signals), the forced Measure Dependency pricing, the greedy-repair marginals, and Fabric First's phase-2 re-scoring (ADR-0061) are all measured by the same objective, so a ventilation that costs SAP but is carbon-neutral cannot sink a carbon-improving wall, and a fabric-first phase 2 picks its heating on post-fabric carbon, not post-fabric SAP.
Consequences
- Selection changes; truth-telling does not. The Plan's persisted Scores, Bills, and role-3 SAP attribution are computed exactly as before — only which package is chosen responds to the goal.
- At a £16,000 budget on the uninsulated solid-brick corpus dwelling (001431), the SAP objective buys wall + floor + gas boiler (SAP 72.9, 2,069 kg CO2/yr, £2,088/yr) while the carbon objective buys wall + floor + storage heaters (SAP 69.2, 1,098 kg CO2/yr, £2,635/yr) — goals now trade SAP, carbon and bills against each other visibly.
- The Energy Savings objective inherits the Fuel Rates snapshot's staleness characteristics (quarterly Ofgem-cap cadence, ADR-0014).
independent_option_impacts(role-1 SAP/CO2/kWh triple) is removed — superseded byindependent_option_signalsin the objective's currency.