mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Flag works bought for no real SAP gain 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8c2c19495c
commit
08d2b874d5
2 changed files with 73 additions and 0 deletions
|
|
@ -187,6 +187,11 @@ def _already_meets_goal_with_works(a: PropertyAudit) -> Optional[str]:
|
|||
return f"already {a.effective_band} >= goal {a.scenario_goal_band} but cost_of_works £{a.cost_of_works:.0f}"
|
||||
|
||||
|
||||
@check("costed-plan-without-real-gain", Severity.HIGH)
|
||||
def _costed_plan_without_real_gain(a: PropertyAudit) -> Optional[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@check("plan-stops-short-of-goal", Severity.HIGH)
|
||||
def _plan_stops_short_of_goal(a: PropertyAudit) -> Optional[str]:
|
||||
"""The default plan ends BELOW the scenario's goal band on a scenario that is
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from scripts.audit.anomalies import (
|
|||
PropertyAudit,
|
||||
Severity,
|
||||
_already_meets_goal_with_works, # pyright: ignore[reportPrivateUsage]
|
||||
_costed_plan_without_real_gain, # pyright: ignore[reportPrivateUsage]
|
||||
_effective_lodged_divergence, # pyright: ignore[reportPrivateUsage]
|
||||
_implausible_lodged_score, # pyright: ignore[reportPrivateUsage]
|
||||
_plan_stops_short_of_goal, # pyright: ignore[reportPrivateUsage]
|
||||
|
|
@ -213,3 +214,70 @@ class TestAlreadyMeetsGoalWithWorks:
|
|||
|
||||
# Assert
|
||||
assert registered["already-meets-goal-with-works"] is Severity.HIGH
|
||||
|
||||
|
||||
class TestCostedPlanWithoutRealGain:
|
||||
"""Money spent for no meaningful SAP movement (#1652 / #1655).
|
||||
|
||||
The coverage hole the #1652 diagnosis found: `plan-score-below-baseline`
|
||||
only fires on a *drop* beyond 0.5 and `zero-works-post-differs` only on £0
|
||||
plans, so a costed plan landing flat against the effective baseline was
|
||||
caught by neither — including the 112 homes whose modelled post-retrofit SAP
|
||||
sat ~0.2 *below* their effective baseline.
|
||||
"""
|
||||
|
||||
def test_fires_when_works_are_bought_for_no_real_gain(self) -> None:
|
||||
# Arrange — £6,240 of works moving the dwelling 0.2 SAP.
|
||||
audit = _make_audit(
|
||||
effective_sap=63.0, post_sap=63.2, cost_of_works=6240.0
|
||||
)
|
||||
|
||||
# Act
|
||||
result = _costed_plan_without_real_gain(audit)
|
||||
|
||||
# Assert
|
||||
assert result is not None
|
||||
assert "6240" in result or "6,240" in result
|
||||
|
||||
def test_fires_when_the_post_score_lands_below_the_baseline(self) -> None:
|
||||
# Arrange — the likely-downgrade case: paid works, post *below* baseline.
|
||||
audit = _make_audit(
|
||||
effective_sap=63.0, post_sap=62.8, cost_of_works=1029.0
|
||||
)
|
||||
|
||||
# Act
|
||||
result = _costed_plan_without_real_gain(audit)
|
||||
|
||||
# Assert
|
||||
assert result is not None
|
||||
|
||||
def test_silent_when_the_works_deliver_a_real_uplift(self) -> None:
|
||||
# Arrange — £6,240 buying 9 SAP points: what a retrofit should look like.
|
||||
audit = _make_audit(
|
||||
effective_sap=63.0, post_sap=72.0, cost_of_works=6240.0
|
||||
)
|
||||
|
||||
# Act
|
||||
result = _costed_plan_without_real_gain(audit)
|
||||
|
||||
# Assert
|
||||
assert result is None
|
||||
|
||||
def test_silent_on_a_zero_cost_plan(self) -> None:
|
||||
# Arrange — no money spent, so there is nothing to justify; a £0 plan
|
||||
# that moves nothing is the correct outcome for an already-compliant
|
||||
# dwelling, and is covered by its own check.
|
||||
audit = _make_audit(effective_sap=63.0, post_sap=63.0, cost_of_works=0.0)
|
||||
|
||||
# Act
|
||||
result = _costed_plan_without_real_gain(audit)
|
||||
|
||||
# Assert
|
||||
assert result is None
|
||||
|
||||
def test_is_registered_at_high_severity(self) -> None:
|
||||
# Act
|
||||
registered = {name: severity for name, severity, _ in _REGISTRY}
|
||||
|
||||
# Assert
|
||||
assert registered["costed-plan-without-real-gain"] is Severity.HIGH
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue