Commit graph

8 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
4d5504fa10 feat: thread physical-state-change signal into rebaselining 🟩
Add Property.physical_state_changed (true on Site Notes / Landlord Overrides
/ Prediction — trigger (b)/(c)) and pass it from the
PropertyBaselineOrchestrator into the Rebaseliner. So an overridden or
predicted SAP>=10.2 property now stores calc(effective) as its Effective
baseline instead of echoing the lodged headline — closing the "81 in the DB"
divergence between the displayed baseline and the modelled plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 19:15:11 +00:00
Jun-te Kim
6679d8c621 Fall back to computed kWh for predicted-EPC baselines (no lodged RHI)
16 modelling_e2e properties failed with "Effective EPC is missing
renewable_heat_incentive; cannot read baseline space-heating / hot-water kWh".

Baseline runs for predicted properties too (ADR-0031), reading space/water-
heating kWh off the EPC's lodged RHI block. Predicted EPCs deep-copy a neighbour
template that may carry no RHI, so `_require_rhi` hard-failed the whole subtask.

Fix: when the EPC has no RHI, fall back to the property's OWN computed figures
from the scored SapResult (space_heating_kwh_per_yr / hot_water_kwh_per_yr) —
more representative than a neighbour's lodged numbers. Only when there is also no
SapResult (the rebaseliner scored nothing) is there genuinely no demand to
record, and we still fail noisily. Lodged certs are unchanged (RHI still wins).

Regression tests: fallback-to-computed, and the no-RHI/no-result raise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 08:58:24 +00:00
Khalim Conn-Kowlessar
ced6287baa refactor(billing): relocate Bill Derivation to domain/billing/ (cross-stage)
Bill / EnergyBreakdown / BillDerivation / sap_fuel were under
domain/property_baseline/ only because Baseline was built first. The Modelling
stage now needs them too, so move them (and their tests) to a neutral
domain/billing/ — Fuel/FuelRates already live in the shared domain/fuel_rates/.
Avoids a modelling -> property_baseline cross-stage import and a package name
that wrongly implies ownership (ADR-0011, ADR-0014 amendment). Pure git mv +
import rewrite across 10 files; 40 billing/baseline/repo tests pass, pyright
strict clean. CONTEXT.md Bill Derivation location updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:19:23 +00:00
Khalim Conn-Kowlessar
f179950519 feat(baseline): wire BillDerivation into the orchestrator and persist the Bill (ADR-0014)
The PropertyBaselineOrchestrator now reads the current Fuel Rates snapshot
once per batch, builds a BillDerivation, and prices each scored property's
SapResult -> EnergyBreakdown into a Bill carried on PropertyBaselinePerformance
(None only on the stub no-calculator path). The Bill is flattened onto nullable
bill_* flat columns (per-section kwh+cost, standing charges, SEG credit, total)
on the postgres table, with bill_total_annual_bill_gbp as the not-null
discriminator on read-back. Section absent from the bill stays None, not 0.

Updated all four orchestrator construction sites to inject the FuelRatesRepository
port (handler + three test sites), and the FE migration doc to reflect the
prefixed columns and that they are now populated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:51:18 +00:00
Khalim Conn-Kowlessar
f7dc9dbccb feat(baseline): Rebaseliner returns RebaselineResult carrying the SapResult
The Rebaseliner is the assemble-and-score step (ADR-0013 amendment); its
SapResult is the scored picture that Bill Derivation also prices (ADR-0014),
so rebaseline() now returns a RebaselineResult{effective, reason, sap_result}
instead of (Performance, reason). CalculatorRebaseliner sets sap_result on
both branches (the bill prices it whether lodged or calculated figures win);
StubRebaseliner returns sap_result=None (runs no calculator). Orchestrator
unpacks the result; the bill wiring lands in the next slice.

Also refreshes the stale ML-era docstrings in rebaseliner.py to the
assemble-and-score model (the calculator, not ML, is the rebaseliner
mechanism per ADR-0013).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:37:13 +00:00
Khalim Conn-Kowlessar
15da2d3970 feat(baseline): CalculatorRebaseliner — calculator goes load-bearing (ADR-0013 amend)
Slice 5a: the promotion. Replaces StubRebaseliner in production and collapses the
shadow runner into the rebaseliner (ADR-0013 amendment).

- CalculatorRebaseliner runs Sap10Calculator on every Property:
  * sap_version < 10.2 -> Effective Performance IS the calculator output
    (band via Epc.from_sap_score, CO2 kg->t, PEUI rounded), reason "pre_sap10".
  * sap_version >= 10.2 -> Effective = lodged (API figures on-target), and the
    calculator only logs divergence (SAP>0.5, PEUI/CO2 1%) as a validation signal.
  * a calculator raise propagates -> batch aborts (ADR-0012); fix the cert at once.
- Rebaseliner.rebaseline gains property_id (for the divergence log).
- LoggingCalculatorShadow / the calculator_shadow seam removed from the
  orchestrator; its divergence-comparison logic now lives in the rebaseliner.
- StubRebaseliner kept (signature updated) for orchestrator/repo unit tests.

The SapResult->EnergyBreakdown adapter + BillDerivation wiring (to populate the
bill block) follow once the appliances/cooking SapResult fields land.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 10:04:24 +00:00
Khalim Conn-Kowlessar
561e1b8b49 feat(baseline): run Sap10Calculator in shadow on Property Baseline (ADR-0013)
Wire Sap10Calculator into PropertyBaselineOrchestrator as a non-load-bearing
shadow runner. For each property it scores the Effective EPC beside the
load-bearing Lodged/Effective write, catches any strict-raise -> log.error
(never aborts the batch), and on success log.warning's divergence from Lodged:
SAP |continuous - lodged| > 0.5; PEUI/CO2 > 1% relative (CO2 after kg->tonnes).
Every line is tagged with sap_version so SAP-10.2 signal separates from
older-spec drift (ADR-0010 Validation Cohort).

Per ADR-0013, Calculated SAP10 Performance is not a persisted third value-set:
effective = calculated in every baselining scenario, so the calculator IS the
mechanism that produces Effective Performance (the Rebaseliner). It runs in
shadow only while being hardened; when overrides/estimation land it is promoted
to drive Effective and the failure posture flips to abort (ADR-0012, calculator
now load-bearing). No table change.

- ADR-0013 + CONTEXT (Calculated SAP10 Performance / Effective Performance /
  Rebaselining) record the decision.
- CalculatorShadow port + LoggingCalculatorShadow + Calculator protocol.
- FakeCalculatorShadow for orchestrator unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 08:01:47 +00:00
Khalim Conn-Kowlessar
c3691d9af2 refactor(property-baseline): rename baseline → property_baseline aggregate (PR #1139 review)
Wholesale rename of the Baseline aggregate to PropertyBaseline for clarity /
to disambiguate from baselines that appear elsewhere in Modelling. Scoped to
this aggregate only — the distinct Rebaselining term (rebaseline_reason,
StubRebaseliner, RebaselineNotImplemented) is deliberately untouched.

- domain/baseline → domain/property_baseline; BaselinePerformance →
  PropertyBaselinePerformance.
- repositories/baseline → repositories/property_baseline; BaselineRepository
  / BaselinePostgresRepository → PropertyBaseline*.
- orchestration/baseline_orchestrator.py → property_baseline_orchestrator.py;
  BaselineOrchestrator → PropertyBaselineOrchestrator. BaselineStage →
  PropertyBaselineStage.
- infrastructure/postgres: baseline_performance_table.py →
  property_baseline_performance_table.py; table `baseline_performance` →
  `property_baseline_performance`; Model renamed.
- UnitOfWork attribute `.baseline` → `.property_baseline`.
- Docs: ADR-0004 references + migration doc (renamed to
  property-baseline-performance-table.md) updated.

CONTEXT.md glossary term ("Baseline Performance") left as-is pending a
ubiquitous-language call (raised on the PR). 123 tests pass; pyright strict
clean (only the unrelated pre-existing moto import errors remain).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 14:54:59 +00:00
Renamed from orchestration/baseline_orchestrator.py (Browse further)