Commit graph

7428 commits

Author SHA1 Message Date
Daniel Roth
fc2ba88759 Abri survey creation trigger ignores deals that already have a confirmed_survey_date 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 15:58:41 +00:00
Daniel Roth
697df8ade3 Abri survey creation trigger ignores deals that already have a confirmed_survey_date 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 15:58:09 +00:00
Daniel Roth
51ac5e2f5e Abri survey creation trigger fires when confirmed_survey_date is first set on an Abri Condition deal 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 15:57:26 +00:00
Daniel Roth
bd3c45d81c Abri survey creation trigger fires when confirmed_survey_date is first set on an Abri Condition deal 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 15:56:49 +00:00
Jun-te Kim
cf50c53cad
Merge pull request #1431 from Hestia-Homes/fix/refetch-solar-reuse-stored
Fix refetch_solar=False dropping solar from re-modelled plans (reuse stored Solar instead)
2026-07-02 15:27:44 +01:00
Jun-te Kim
100949073f refetch_solar=False models with stored Solar insights and never calls Google 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:21:46 +00:00
Jun-te Kim
c1a558d2b5 refetch_solar=False models with stored Solar insights and never calls Google 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:19:31 +00:00
Jun-te Kim
e08048244a
Merge pull request #1429 from Hestia-Homes/fix/property-overlay-storage-category
Set electric-storage category (7) on storage property overlays
2026-07-02 12:51:58 +01:00
Jun-te Kim
12e8e52eaa
Merge pull request #1428 from Hestia-Homes/test/booth-close-sap-accuracy-corpus
Add UPRN 100020942571 (8 Booth Close) to the SAP accuracy corpus
2026-07-02 12:51:38 +01:00
Jun-te Kim
4ca3491eb2
Merge pull request #1426 from Hestia-Homes/fix/hhrsh-storage-offpeak-pricing
Fix HHRSH storage heaters priced at peak rate (engine 48 vs accredited Elmhurst 71)
2026-07-02 12:50:32 +01:00
KhalimCK
61321d0d47
Merge pull request #1422 from Hestia-Homes/fix/roof-vaulted-nd-sentinel-eligibility
Roof eligibility resolves sentinel thickness by age band (vaulted joins sloping)
2026-07-02 12:45:51 +01:00
Jun-te Kim
e27ecf4a26 Set electric-storage category (7) on storage property overlays
`_category_for` mapped room heaters / heat pumps / heat networks but
returned None for electric-storage-heater codes (401-409), so a storage
system override emitted `sap_main_heating_code=409` +
`main_heating_category=None` while `_control_for` right beside it correctly
set the storage charge control — an asymmetry. Map storage codes to
category 7 so the overlaid cert is internally consistent (code 401-409 <->
category 7), matching the control/fuel/meter companions the overlay drags.

Consistency follow-up to the modelling HHRSH off-peak-pricing fix (#1426):
that PR's Table 12a resolver already prices any storage-coded system
off-peak regardless of category, so this is NOT a live mispricing — it
removes a latent code<->category inconsistency that other category-keyed
logic (responsiveness, pumps/fans) could trip on.

Tests: storage/HHRSH overlay tests now also assert `main_heating_category==7`.
tests/domain/epc (347) + prediction/override/reclassify (118) green; source
pyright-clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 11:45:49 +00:00
Jun-te Kim
e79e83bbbe Add UPRN 100020942571 (8 Booth Close) to the SAP accuracy corpus
Pins the engine's baseline SAP (54) for this electric-room-heater +
gas-multipoint end-terrace ground-floor maisonette (portfolio 814 pid
742121) against accredited Elmhurst RdSAP-10 (worksheet SAP 53, within
1 point), with the Elmhurst Input Summary + SAP Worksheet PDFs saved as
evidence alongside the cert's epc.json.

Companion to the HHRSH storage-heater off-peak-pricing fix PR: this
baseline pin confirms the calculator is faithful on the as-lodged
dwelling; that PR fixes the HHRSH recommendation overlay (engine 48->73
vs Elmhurst 71).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 11:31:28 +00:00
Jun-te Kim
f5fbeb8e7e Fix HHRSH storage heaters priced at peak rate (engine 48 vs Elmhurst 71)
The high-heat-retention storage-heater (HHRSH) recommendation was computed
as LOWERING SAP and wrongly rejected by the Optimiser, leaving electric-
room-heater flats/maisonettes stuck at band E with no sensible plan.

Root cause: `_HHR_STORAGE_OVERLAY` sets `sap_main_heating_code=409` (HHR
storage) + `meter_type="Dual"` but left `main_heating_category` at the
pre-existing value (10 = electric room heaters). The SAP 10.2 Table 12a
space-heating high-rate-fraction resolver classifies by category, so it
misread the off-peak storage heating as direct-acting electric (1.00
high-rate) and priced 100% at the 7-hour peak rate (15.29p) instead of the
storage off-peak split (0.00, ~5.5p).

Verified against accredited Elmhurst RdSAP-10 on UPRN 100020942571
(8 Booth Close, SE28 8BW): engine HHRSH SAP 48 (peak-priced, cost £1087)
vs Elmhurst 71 (cost ~£611); with the fix the engine gives 73 / £576.

Two-part fix (defence in depth, so no future overlay/mapper can
reintroduce it):
- `_HHR_STORAGE_OVERLAY` now sets `main_heating_category=7`, keeping the
  overlaid EPC internally consistent.
- `_table_12a_system_for_main` now classifies electric storage heaters by
  their Table 4a SAP code (401-409) as well as `category==7`, checked
  before the room-heater branch, so a specific storage code is
  authoritative over a stale/inconsistent category.

Tests: new regression `test_storage_heater_classified_by_sap_code_over_stale_category`;
updated the HHRSH overlay pin. Full accuracy corpus still passes (no regressions).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 11:24:03 +00:00
Jun-te Kim
ca3d4ce84b
Merge pull request #1423 from Hestia-Homes/feat/glazing-secondary-member
Add SECONDARY glazing: GlazingType member, overlay code 5, guard support + 796 backfill
2026-07-02 12:22:58 +01:00
Khalim Conn-Kowlessar
6d812d99c5 An unlodged roof type with ND thickness gets no roof recommendation 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:20:55 +00:00
Khalim Conn-Kowlessar
0efae77fd2 An unlodged roof type with ND thickness gets no roof recommendation 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:18:55 +00:00
Jun-te Kim
7baab9376c Backfill uniform secondary-glazing overrides misfiled as Single glazing
39 Hyde-796 property_overrides rows ('100% secondary glazing (sap 9.94)')
plus the classifier-cache row; the cache phase self-gates on the
'Secondary glazing' pgEnum label landing (assessment-model#345).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:12:25 +00:00
Jun-te Kim
248e230d37 Dominant secondary-glazing split resolves deterministically to SECONDARY 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:10:40 +00:00
Jun-te Kim
787f0066c8 Dominant secondary-glazing split resolves deterministically to SECONDARY 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:09:47 +00:00
Khalim Conn-Kowlessar
63c889bd02 Roof dispatcher docstring and constants reflect the sentinel-aware pitched trigger 🟪
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:09:07 +00:00
Jun-te Kim
f873d5cad3 Landlord 'Secondary glazing' override overlays SAP10 glazing code 5 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:08:16 +00:00
Khalim Conn-Kowlessar
1d13dee38e ND loft roof on a pre-1950 dwelling gets loft insulation 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:05:47 +00:00
Khalim Conn-Kowlessar
989cd61574 ND loft roof on a pre-1950 dwelling gets loft insulation 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:04:40 +00:00
Khalim Conn-Kowlessar
b33735ef30 NI (insulation present) and measured depths keep a vaulted roof ineligible 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:03:38 +00:00
Khalim Conn-Kowlessar
1dd2d19f78 ND on a limited-insulation age band yields no roof recommendation 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:03:01 +00:00
Jun-te Kim
5c9ca727cd Landlord 'Secondary glazing' override overlays SAP10 glazing code 5 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:02:11 +00:00
Khalim Conn-Kowlessar
f3bdf0e884 Vaulted ND roof on a pre-1950 dwelling gets sloping-ceiling insulation 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 11:01:43 +00:00
Khalim Conn-Kowlessar
0d465346c9 Vaulted ND roof on a pre-1950 dwelling gets sloping-ceiling insulation 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:55:58 +00:00
Khalim Conn-Kowlessar
9e1a194b27 Record ADR-0047 roof sentinel eligibility + lodgement-sentinel glossary entry
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:55:57 +00:00
Jun-te Kim
c1169b2d39
Merge pull request #1402 from Hestia-Homes/fix/glazing-dominant-single-guard
Resolve dominant-single glazing deterministically in the mix guard
2026-07-02 10:19:16 +01:00
Jun-te Kim
0f6e6e4936
Merge pull request #1415 from Hestia-Homes/fix/repin-golden-0036-tune-up-after-1399
Re-pin golden cert 0036 after #1399 (its PCDB boiler now fires a zoned tune-up)
2026-07-02 10:01:33 +01:00
Jun-te Kim
0fa47b1047
Merge pull request #1409 from Hestia-Homes/fix/property-type-leading-token-guard
Guard property_type to its leading dwelling-type token
2026-07-02 10:00:03 +01:00
Jun-te Kim
761dcf2a82
Merge pull request #1406 from Hestia-Homes/fix/wood-logs-main-fuel-archetype
Add individual wood-logs main-fuel archetype (API 6 / RdSAP Table 32 code 20)
2026-07-02 09:57:25 +01:00
Jun-te Kim
c1a75fb45b
Merge pull request #1403 from Hestia-Homes/fix/1376-gas-cpsu-dumping-ground
Retire the Gas CPSU dumping ground: solid-fuel room heaters + electric boilers (#1376)
2026-07-02 09:57:15 +01:00
Jun-te Kim
dc868e87a9
Merge pull request #1405 from Hestia-Homes/fix/1376-offpeak-underfloor
Score off-peak electric underfloor on its own codes, not direct-acting single-meter (#1376)
2026-07-02 09:56:57 +01:00
Jun-te Kim
b94c45a392
Merge pull request #1407 from Hestia-Homes/fix/glazing-nd-single-all-schemas
Honour single-glazed ND certs on every reduced-field synthesis seam
2026-07-02 09:21:40 +01:00
Jun-te Kim
deec22302a Resolve dominant double/triple glazing when the era is stated
Addresses PR #1402 review: the guard only claimed dominant-single, leaving
the symmetric bug open — the LLM can flatten a dominant-double/triple split
onto its minority single (e.g. "96% Double glazing 2002 or later, 4% Single").

A dominant double/triple whose era is explicit ("pre-2002" / "2002 or later")
is just as fully determined as era-free single, so the guard now claims it via
_DOMINANT_MEMBER. Only a genuinely ambiguous era ("unknown age", unstated) still
defers to the LLM — the "96% double -> None" contract now holds solely for the
era-unknown case, not the era-stated one.

Backfill script reuses the same guard, so it now corrects any dominant split;
renamed reclassify_dominant_single_glazing.py -> reclassify_dominant_glazing.py
to match. Tests cover double/triple x pre-2002/2002-or-later and the still-
deferred unknown-age case; 14 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 08:07:13 +00:00
KhalimCK
51f1cafb75
Merge pull request #1408 from Hestia-Homes/audit/plan-stuck-in-low-band
Add plan-stuck-in-low-band audit check + refresh weird-recs catalogue
2026-07-02 00:08:06 +01:00
Khalim Conn-Kowlessar
44752344a0 Re-pin golden cert 0036 to expect the zoned tune-up its PCDB boiler now fires (#1399)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 22:56:12 +00:00
Jun-te Kim
88bae2166f Guard property_type to its leading dwelling-type token
The landlord property-type description is a "<dwelling type>: <built form>: <floor>"
split whose leading token IS the dwelling type; the built-form tail is not. The
LLM occasionally over-read the tail and flipped the type — a handful of
"Bungalow: EndTerrace" / "Bungalow: MidTerrace" dwellings were stored as House.

Adds property_type_guard (claims the recognised leading token: House / Bungalow /
Flat / Maisonette / Park home; defers unrecognised phrasings to the LLM) and wires
property_type through a GuardedColumnClassifier, so the built-form tail can never
flip the type and the live path is deterministic.

Applied the scoped backfill to portfolio 796 (Hyde): 3 rows corrected from House
back to Bungalow. No enum migration needed — the targets are original members.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:24:37 +00:00
Jun-te Kim
66d70b6833 Add plan-stuck-in-low-band audit check + refresh weird-recs catalogue
`plan-stops-short-of-goal` fires on anything below the goal band — 322/338
on portfolio 814, mostly flats a single band short of B, which drowns the
real hits. Add a sharper, band-keyed check that isolates the genuinely-
stuck tail: a plan that leaves the dwelling still rated E/F/G with the band
UNCHANGED from baseline on an unlimited-budget scenario — the "E->E, no big
movement" worklist. On portfolio 814 / scenario 1271 it flags just 4 (pids
742121, 742210, 742265, 742347) vs 322. No SAP-points threshold; the
strictly-worse case is left to plan-below-baseline-band so the two
partition cleanly.

Also refresh the find-weird-recommendations skill's Phase 4 catalogue:
register the new check in Phase 1, correct the stale 742121 note (it is an
electric maisonette whose HHRSH IS offered but scores -6.3 SAP, so the
Optimiser correctly drops it — not the old mains-gas story), and add the
742265 community-flat findings (HHRSH/ASHP gated out by heat-network
topology; vaulted-ceiling roof with ND insulation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:22:41 +00:00
Jun-te Kim
0e9bceb2d4 Honour single-glazed ND certs on every reduced-field synthesis seam
A pre-SAP10 RdSAP cert that omits per-window records lodges glazing as a
`multiple_glazing_type` code plus a `window.description` and a
`multiple_glazed_proportion`. When the numeric code is the "ND" (Not
Defined) sentinel, the reduced-field synthesis defaulted to the DG-modal
code 2 (double) — ignoring the two register signals that say otherwise.

`_normalize_sap_schema_16_x` already honours an explicit "Single glazed"
description for 16.x certs (rewriting multiple_glazing_type -> 5), but that
rule was never propagated to the 17.0 / 17.1 / 18.0 / 19.0 / 20.0.0
synthesis seams — they all blindly applied ND -> 2. So a genuinely
single-glazed dwelling was:

  1. scored as double-glazed, overstating its baseline SAP, and
  2. hidden from the glazing generator (which upgrades only single-glazed
     windows), producing no glazing recommendation.

Reproduced on cert 2780-3922-3202-6042-9200 (uprn 10033526327), a
single-glazed community-heated flat lodging multiple_glazing_type="ND",
window "Single glazed", multiple_glazed_proportion 0 — mapped to four
double-glazed windows, no glazing measure offered.

Extend the 16.x rule to all five reduced-field seams via one shared
resolver: on the ND fallback, when the description says "single" or the
multiple-glazed proportion is 0, synthesise single glazing
(`_api_cascade_glazing_type(5) == 1`, SAP10 cascade single, Table 24
U~4.8) — the exact single code the glazing generator's {1, 15} set
matches — else keep each seam's DG-modal default. Refines ADR-0028's ND
handling.

Tests: all five seams synthesise single from a "Single glazed" + 0%
proportion cert; the double-glazed control keeps the DG-modal default;
plus unit pins on the resolver.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:10:58 +00:00
Jun-te Kim
5330bc20d9 Add individual wood-logs main-fuel archetype (API 6 / RdSAP Table 32 code 20)
MainFuelType had no individual wood-logs member — only "biomass (community)" —
so the LLM classifier funnelled "Solid Fuel: Wood Logs" into the community fuel,
inventing a community heat network the dwelling isn't on (and mislabelling the
connection). main_fuel had no deterministic guard at all, so nothing caught it.

Verified against domain/sap10_calculator/docs/specs: RdSAP 10 Specification
Table 32 lists "wood logs" as a solid fuel (code 20, 0.028 kgCO2e/kWh); the
calculator's input scheme (the gov EPC API fuel enum) codes it 6 -> Table 32 20
(sap_efficiencies._API_TO_TABLE32), and water_heating_overlay already pins the
same fuel to 6. So _FUEL_CODES["wood logs"] = 6 is confirmed, not guessed.

Adds MainFuelType.WOOD_LOGS + the _FUEL_CODES entry, a main_fuel_guard mirroring
water_heating_guard (claims the "wood log" token; dual fuel keeps its own member
since it has no "wood log" substring), and wires main_fuel through a
GuardedColumnClassifier so the live path is deterministic.

Applied the scoped backfill to portfolio 796 (Hyde): 21 rows off
"biomass (community)" -> "wood logs". property_overrides (TEXT) only; the
classifier-cache pgEnum member is deferred to the FE Drizzle migration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:00:24 +00:00
Khalim Conn-Kowlessar
f50ee5f957 Reclassify electric-underfloor overrides via the generalized main-heating reclassify 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:51:22 +00:00
Khalim Conn-Kowlessar
9d106d9d6d Deterministically guard the electric-underfloor descriptions 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:48:45 +00:00
Khalim Conn-Kowlessar
6dcb8aee21 Deterministically guard the electric-underfloor descriptions 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:47:59 +00:00
Khalim Conn-Kowlessar
6883864c32 Defer the screed-underfloor meter to the cert while off-peak underfloor asserts Dual 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:46:33 +00:00
Khalim Conn-Kowlessar
1c79b69648 Defer the screed-underfloor meter to the cert while off-peak underfloor asserts Dual 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:36:59 +00:00
Khalim Conn-Kowlessar
749ac3a5ff Resolve electric-underfloor overrides to their own SAP codes dragging electricity 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:36:21 +00:00