Model/docs/adr/0005-multi-phase-scenarios-per-phase-recompute.md
Khalim Conn-Kowlessar 772cdd4f5a docs(modelling): #1157 Plan-persistence design review
Outcome of the /grill-with-docs session scoping #1157.

- CONTEXT.md: add **Plan Measure** (the persisted selected Option +
  role-3 attribution + cost); Recommendation stays the candidate.
  Remove Scenario Phase / Plan Phase / Rolled-over Options — multi-phase
  is deferred. Reshape Scenario + Plan to single-phase; fix relationships,
  dialogue, and the "phase" ambiguity note.
- ADR-0005: rewritten to Deferred (multi-phase was speculative
  prospective-client work; single-phase now; future plan_phase back-fill
  path preserved). Stray phase refs cleaned in ADR-0016 / ADR-0009.
- ADR-0017 (new): Plan persistence — reuse the live plan/recommendation
  tables via SQLModel mirrors + a PlanRepository on the UoW; add
  recommendation.plan_id, retire the plan_recommendations m2m; flat
  post-retrofit on plan; idempotent replace; CO2 in tonnes. Unselected
  alternatives + bills noted as deferred directions.
- docs/migrations/recommendation-plan-id.md: the FE-owned Drizzle change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 11:12:54 +00:00

3.7 KiB
Raw Blame History

Multi-phase scenarios — deferred (speculative)

Status: Deferred / Out of scope. Superseded by the single-phase decision taken in a /grill-with-docs session (2026-06-03) while scoping the #1157 Plan persistence schema. This ADR previously proposed an Accepted multi-phase Scenario aggregate with per-phase recompute against rolling state; that design is not being built now. The original proposal is preserved below under "Deferred design" for the day the requirement returns.

Why deferred

Multi-phase sequencing — letting a user split a Scenario into ordered phases ("fabric this year, heat pump next, solar after"), each with its own measure allowlist / budget / target, and producing Plans shaped to match — came from a prospective (not current) client. It is entirely speculative: we may never build it. Baking it into the core domain as an accepted decision made the model "too strong" — it forced a first-class Scenario Phase / Plan Phase / Rolled-over Options vocabulary and a plan_phase table into a live product that has no consumer for any of it.

The current goal is to replicate and improve the existing pipeline, which is single-phase. So:

  • A Scenario carries one set of permitted measure types (no ordered phases).
  • A Plan holds one Optimised Package of Plan Measures plus the Property's flat post-retrofit figures (the legacy plan columns). There is no plan_phase table and no phase column.
  • The terms Scenario Phase, Plan Phase, and Rolled-over Options are removed from CONTEXT.md.

This is cheap to reverse: re-introducing phases is additive, and the ADR-0016 scoring split (per-Option signal → whole-package re-score → marginal-cascade attribution) already works against a single package and generalises to per-phase rolling state unchanged.

Future migration path (when/if the requirement returns)

Scope it properly as a feature in its own right — do not retrofit it implicitly. The migration shape we expect:

  1. Add a plan_phase table; give each existing live Plan exactly one Plan Phase and back-fill its current Optimised Package + post-retrofit figures into that single phase.
  2. Add ordered phases to the Scenario aggregate (allowlist / budget / target per phase).
  3. Generalise the Optimiser to run per phase against the rolling Effective EPC (phase 1 = baseline; phase 2 = post-phase-1 state; …), so phase ordering becomes load-bearing in the optimisation rather than decorative.

This back-fill keeps every live single-phase Plan valid as a degenerate one-phase case.

Deferred design (original proposal, for reference)

The Scenario aggregate becomes ordered phases: each phase has a measure-type allowlist, an optional budget, and an optional goal. The pipeline walks the phases in order; for each phase it (1) generates candidate recommendations restricted to the phase's measure types, (2) re-runs scoring against the rolling Effective EPC state (baseline for phase 1; post-phase-1 for phase 2; etc.), (3) optimises within the phase's budget/goal, (4) applies the selected package and rolls the state forward.

The rationale was that per-phase recompute makes phase ordering load-bearing in the optimisation, not decorative: installing fabric measures before a heat pump materially changes the heat pump's SAP impact. The cost is ML/calculator calls scaling with N_phases × N_scenarios × N_candidate_measures per property. A single-phase Scenario was modelled as phases: [<one ScenarioPhase>] with all measure types allowed — i.e. exactly the single-phase product we are now building directly, without the phase machinery.