Commit graph

5 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
98d2f7aa16 Goal-aligned dispatch reads one enum-keyed table 🟪
Review findings on PR #1527:

- The 'which goals are goal-aligned' set lived in two adjacent if-chains
  (_objective_for and _require_budget_for_goal_aligned) that had to stay in
  sync — a new goal-aligned goal added to one but not the other would slip
  the budget guard. Both now read a single _GOAL_ALIGNED_OBJECTIVES table.
- The goal strings are the canonical PortfolioGoal enum values, not
  re-declared string constants, so goal-value drift can't silently degrade
  a goal to max-SAP; _target_sap reads the enum too.
- _scored_candidate_groups takes objective without a default (its only
  caller passes it).
- scoring.py: 'cached: float | None' -> Optional[float] per the CLAUDE.md
  'Use Optional over | None' rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:17:27 +00:00
Khalim Conn-Kowlessar
c70f6730a3 Remove the superseded role-1 impacts scorer; signals carry the objective currency 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:11:07 +00:00
Khalim Conn-Kowlessar
48d54675c3 A Reducing-CO2 scenario maximises carbon reduction, not SAP 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:10:06 +00:00
Khalim Conn-Kowlessar
e79ffabfc5 refactor(modelling): expose cascade_scores for the role-3 + bill cascade
Pull the cumulative-prefix scoring out of `marginal_impacts` into a reusable
`cascade_scores(scorer, baseline, overlays) -> list[Score]` (index 0 the
baseline, one calculator run per prefix) plus a pure `marginals_from_scores`.
Each Score carries its SapResult, so the next slice's telescoping per-measure
bill cascade can re-bill the same prefixes the role-3 attribution already
scores — no extra `calculate` calls (ADR-0014 / ADR-0016). `marginal_impacts`
now delegates; behaviour unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:54:54 +00:00
Khalim Conn-Kowlessar
84ec6da032 refactor(modelling): group domain/modelling into generators/scoring/optimisation
domain/modelling/ had grown to 15 flat modules. Group the behavioural ones into
subpackages — generators/ (wall/roof/floor Recommendation Generators), scoring/
(overlay applicator, package scorer, role-1/3 scoring), optimisation/ (optimiser
+ measure dependency) — and leave the shared value-object vocabulary
(recommendation, plan, scenario, product, contingencies, simulation) flat at the
top, since it is imported everywhere. Pure move + import-path rewrite across 89
import sites; no behaviour change. 136 pass, pyright strict clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:48:36 +00:00
Renamed from domain/modelling/scoring.py (Browse further)