diff --git a/CONTEXT.md b/CONTEXT.md index 165b43fc..e5110d99 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -88,6 +88,10 @@ _Avoid_: target (reserve for the EPC band specifically), objective An optional per-Scenario spending cap, denominated **per property (£)** — never a whole-portfolio envelope. Absent budget = no cap. _Avoid_: portfolio budget (a separate concept on the Portfolio row), envelope, cost limit +**Disruptive measure**: +A measure whose installation involves major internal works for residents (internal wall insulation, solid/suspended floor insulation, room-in-roof insulation). Landlords frequently exclude them; the UI sign-posts them and the "Low disruption" quick-start excludes exactly this set. Canonical list: `DISRUPTIVE_MEASURES` in the Scenario domain model. +_Avoid_: invasive (unused here), wet trades (a narrower, overlapping set: plaster-and-screed work) + **Measure exclusion**: A Scenario constraint that takes a measure off the table for the optimiser. The *only* measure-constraint concept on a Scenario — there is no stored inclusion list; "only Solar PV + ASHP" is expressed by excluding everything else. An empty exclusion set is valid and means every measure is available. _Avoid_: inclusions, allowed measures (as a stored concept — UI may present "allowed/excluded" toggles, but what's captured is the exclusion set) diff --git a/src/app/portfolio/[slug]/(portfolio)/scenarios/new/NewScenarioJourney.tsx b/src/app/portfolio/[slug]/(portfolio)/scenarios/new/NewScenarioJourney.tsx index 4bcee7c9..3fc372bc 100644 --- a/src/app/portfolio/[slug]/(portfolio)/scenarios/new/NewScenarioJourney.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/scenarios/new/NewScenarioJourney.tsx @@ -10,7 +10,7 @@ import { measuresList, type MeasureKey, } from "@/app/db/schema/recommendations"; -import { EPC_BANDS, findExactDuplicate, SCENARIO_GOALS } from "@/lib/scenarios/model"; +import { DISRUPTIVE_MEASURES, EPC_BANDS, findExactDuplicate, SCENARIO_GOALS } from "@/lib/scenarios/model"; import { BandChip, EPC_SOFT, GOAL_DESCRIPTIONS, GOAL_SHORT_LABELS, GoalIcon, bandInk, bandTint, formatMoney } from "../ui"; interface ExistingScenario { @@ -36,8 +36,8 @@ const PRESETS: { label: string; hint: string; excl: MeasureKey[] }[] = [ excl: ["internal_wall_insulation", "external_wall_insulation", "solid_floor_insulation"] }, { label: "Fabric only", hint: "Insulation and glazing only — no heating systems or renewables", excl: ["boiler_upgrade", "high_heat_retention_storage_heaters", "air_source_heat_pump", "secondary_heating", "hot_water_tank_insulation", "cylinder_thermostat", "solar_pv", "low_energy_lighting", "fireplace", "ventilation"] }, - { label: "Low disruption", hint: "Nothing that requires residents to move out or clear rooms", - excl: ["internal_wall_insulation", "external_wall_insulation", "solid_floor_insulation", "suspended_floor_insulation", "room_roof_insulation"] }, + { label: "Low disruption", hint: "Excludes every measure marked as disruptive", + excl: Object.keys(DISRUPTIVE_MEASURES) as MeasureKey[] }, ]; const fieldLabel = @@ -356,11 +356,19 @@ export function NewScenarioJourney({
+
Everything is allowed unless you rule it out. Click a measure to exclude it — the engine chooses the best mix from whatever remains.
++ Measures marked{" "} + + disruptive + {" "} + involve major internal works — landlords often exclude them. + Hover one to see why. +