Record goal-aligned Optimiser objectives as ADR-0062

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-09 13:19:56 +00:00
parent c70f6730a3
commit b71bc788a2

View file

@ -0,0 +1,62 @@
---
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 `None` has no semantics to encode.
- **`goal_value` is 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 `ValueError` naming 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 by `independent_option_signals` in the objective's currency.