Commit graph

8297 commits

Author SHA1 Message Date
KhalimCK
bd2253ee61
Merge pull request #1644 from Hestia-Homes/fix/elmhurst-community-dhw-rate
Bill community-heating DHW at the network fuel rate (not lodged mains-gas)
2026-07-17 14:30:25 +01:00
Daniel Roth
c07c59bb2d
Merge pull request #1642 from Hestia-Homes/1622-sitenotes-mapper-label-blocks
PasHub mapper: close the 14 ticketed label blocks under #1622 (LRHA 45→77, Wythenshawe 149→165 computed)
2026-07-17 13:54:25 +01:00
Khalim Conn-Kowlessar
fac64a90cc Bill community-heating DHW at the network fuel rate 🟩
When DHW is supplied by a community heat network (WHC 901/902/914 on a
heat-network main), the network heat -- space AND water -- bills at the main's
Table-12 heat-network rate. The STANDARD-tariff §10a cost path (_fuel_cost)
re-derived the DHW rate inline from the cert-lodged HW fuel, which Elmhurst
defaults to 'Mains gas' (Table 32 code 26, 3.48 p/kWh) on community certs --
under-costing DHW and over-rating SAP. Now inherits the main heat-network rate
via _fuel_cost_gbp_per_kwh, mirroring the .173 inherit already applied on the
legacy/off-peak HW-rate paths. Scoped to community heating, so all other certs
are unchanged; the RdSAP-21.0.1 corpus gauge improves 78.6%->78.9% within-0.5
(MAE 0.628->0.622) with all 195 Elmhurst worksheet pins green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 18:38:02 +00:00
Khalim Conn-Kowlessar
eb5f52e9c4 Bill community-heating DHW at the network fuel rate 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 18:36:35 +00:00
Daniel Roth
4438cbc4b3 Document LRHA WAVE 3 end-state coverage and residual blocks 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
7c979d60f6 Rate gas room-heater main systems at Table 4a code 603 🟩
#1631: the label blocks in the main room-heater type table (Heating
System (Other) field), not the secondary table the issue cited — same
PCDB row either way. Wythenshawe 164→165 computed. The fixture scores
49.5 vs pre_sap 31: its own 2025 accredited cert
6890-0597-0722-0196-3943 lodges the identical codes (603/26/2601) at
band E agreeing with us, while pre_sap matches the dwelling's
superseded electric configuration (2017/2023 certs, band F) — accepted
divergence, MAE ceiling re-baselined 0.75→0.85 with the citation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
316a112258 Rate gas room-heater main systems at Table 4a code 603 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
f5eb8af534 Bill balanced-flue gas fires at Table 4a code 609 (58%) 🟩
#1629: Wythenshawe 163→164 computed; within-0.5 75.6%, MAE 0.737 —
ratchets hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
4a0f1fe939 Bill balanced-flue gas fires at Table 4a code 609 (58%) 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
6262f6594d Bill title-cased dual-fuel secondary heaters at Table 32 code 10 🟩
#1628: fixture 500796700868 re-blocks on a later label
(first-exception-hit); metrics unchanged, ratchets hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
4af086112a Bill title-cased dual-fuel secondary heaters at Table 32 code 10 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
223edb0066 Bill pre-1980 open-fronted gas fires at Table 4a code 601 (50%) 🟩
#1627: Wythenshawe 162→163 computed; within-0.5 75.5%, MAE 0.741 —
ratchets hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
951913f1de Bill pre-1980 open-fronted gas fires at Table 4a code 601 (50%) 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
e21064dc7a Bill loose-jacket cylinders their Table 2 Note 1 storage-loss factor 🟩
Follow-on surfaced by #1626: fixture 500796646644 progressed past the
2404 conflict onto cylinder insulation 'Loose jacket' → code 2 (gov-API
coding, Elmhurst 'Jacket' sibling). Wythenshawe 161→162 computed
(32.6 vs pre 34); ratchets hold at 75.3%/0.744.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
c17a42afc5 Bill loose-jacket cylinders their Table 2 Note 1 storage-loss factor 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
ea4b7938e7 Resolve HHR-control ticks on manual-entry non-HHR storage heaters to 2402 🟩
#1626: ground truth resolved per the issue's option 3. The accredited
cert 0764-2894-7173-2720-5535 (62 Mayfair Road, UPRN 77066640 — the
blocked Wythenshawe fixture's dwelling) lodges 402 + 2402 for the same
heater description, and RdSAP 10 p.80 (item 7-3) requires a PCDB index
for any HHR lodgement — absent one, the surveyed type field is
authoritative and 2404 downgrades to 2402 (automatic charge control,
preserving the tick's automatic-charging semantics). Applies only to
the mapped non-HHR types (402/404); any other pairing still fails loud.
LRHA fixture 505091686590 (Fan storage heater, same slip) computes
64.0 vs pre 65; LRHA 76→77 computed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
1587db61f2 Resolve HHR-control ticks on manual-entry non-HHR storage heaters to 2402 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
be1f69ccd4 Suppress floor heat loss for internal floors over the same dwelling 🟩
#1625: 'Same dwelling below' → the "(another dwelling below)" party-
floor suppression string, mirroring the gov-API code-8 precedent
(RdSAP 10 §3: an internal floor between heated storeys has no floor
heat loss). Wythenshawe 151→161 computed; 7/10 newly-unblocked land
within 0.5 — ratchets re-baselined 0.76/0.72 → 0.75/0.75 for the two
tail entries (−3.3/−5.5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
6356adb523 Suppress floor heat loss for internal floors over the same dwelling 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
01cacc579a Rate unthermostated room heaters via Table 4e Group 6 code 2601 🟩
#1624: Wythenshawe 149→151 computed. 500767954156 lands within 0.5;
500797465814 is a −8.8 tail entry — MAE ceiling re-baselined 0.67→0.72
per the coverage-growth convention (within-0.5 floor 0.76 holds).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
8b242b7da8 Rate unthermostated room heaters via Table 4e Group 6 code 2601 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
6750e48609 Rate MVHR dwellings via Table 4g and biomass heat networks via Table 12 code 43 🟩
#1637: 'MV - Heat Recovery' → MVHR kind (Table 4g default 46.2% heat
recovery — PasHub lodges no PCDF product/wet-rooms/duct data, so these
land in the accuracy tail as the issue predicts). Unblocking them
surfaced 4 biomass heat networks whose fuel label 'Heat from boilers -
biomass' passed through raw to the calculator's MissingMainFuelType —
now mapped to Table 12 code 43, and any other uncovered community fuel
strict-raises UnmappedPasHubLabel at the boundary (ADR-0015).
LRHA 61→76 computed / 28 blocked / 0 errored, within-0.5 43.4%,
MAE 2.985 — ratchets hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
38e9ebd5c7 Rate MVHR dwellings via Table 4g and biomass heat networks via Table 12 code 43 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
7a81fbfa45 Rate heat-network charging controls 2306/2314/2313 per Table 4e Group 3 🟩
#1636: the 6 single-line-label fixtures unblock and re-block on later
labels (MVHR/cylinder); the 6 line-wrap-truncated '…room thermostat
and' fixtures still need the extractor fix tracked under #1622.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
f135e96e2e Rate heat-network charging controls 2306/2314/2313 per Table 4e Group 3 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
9d761f6ad1 Score heat-pump dwellings via Table 4e Group 2 controls and Appendix N efficiency 🟩
#1635: 'Programmer, room thermostat and TRVs' on a heat pump → 2210
(PCDB heat_pump_controls), and the lodgement now carries Table 4a
category 4 — without it the calculator's Appendix N3.6/N3.7 gate never
fired and the PCDB heat pump was billed as 100%-efficient direct
electric (6.0/18.5 vs pre 62/57). With the category threaded the two
LRHA WAVE 3 fixtures score 52.1/56.9 (the latter within 0.5).
LRHA 59→61 computed, within-0.5 42.6%, MAE 2.830 — ratchets hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
436675d64f Score heat-pump dwellings via Table 4e Group 2 controls and Appendix N efficiency 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
70f5172aa2 Resolve Medium cylinder band to Table 28 fixed 160 L (code 3) 🟩
#1634: LRHA WAVE 3 51→59 computed, within-0.5 42.4%, MAE 2.757 —
ratchets hold.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
7ca02588ab Resolve Medium cylinder band to Table 28 fixed 160 L (code 3) 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
e0b92016a8 Bill secondary water/oil-filled radiators as PCDB 694 electric room heaters 🟩
#1633: fixture 497714783467 re-blocks on its next label (first-exception-hit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
2da0dda9d4 Bill secondary water/oil-filled radiators as PCDB 694 electric room heaters 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
3b2e6e348a Score heating-oil dwellings via SAP main-fuel code 28 🟩
#1632: LRHA WAVE 3 45→51 computed. Newly-unblocked oil configs land
outside 0.5 (coverage growth per module convention) — within-0.5 floor
re-baselined 0.46→0.41 at 41.2%/MAE 2.948.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
bb604d79bf Score heating-oil dwellings via SAP main-fuel code 28 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:32 +00:00
Daniel Roth
2a68fc2a76 Map triple glazing 2002-2021 windows to SAP10 code 9 🟩
#1630: 11 LRHA WAVE 3 fixtures re-block on later labels (first-exception-hit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:31 +00:00
Daniel Roth
cff4095cfd Map triple glazing 2002-2021 windows to SAP10 code 9 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:41:31 +00:00
KhalimCK
cc185e5d73
Merge pull request #1640 from Hestia-Homes/fix/elmhurst-piv-loft-ventilation
Map Elmhurst 'Positive input from loft' ventilation to natural
2026-07-16 17:40:02 +01:00
Khalim Conn-Kowlessar
c533369244 Map Elmhurst 'Positive input from loft' ventilation to natural 🟩
Positive input ventilation sourced from the loft is treated as natural
ventilation under SAP 10.2 §2 (24d) — matching the MechanicalVentilationKind
.NATURAL contract — so the label now resolves instead of raising
UnmappedElmhurstLabel and blocking the whole Elmhurst Summary. Surfaced on
8 WCHG Elmhurst certs (e.g. 29 Piper Hill Avenue, 38 Bucklow Drive).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 16:27:00 +00:00
Khalim Conn-Kowlessar
d301ec3be5 Map Elmhurst 'Positive input from loft' ventilation to natural 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 16:26:15 +00:00
Daniel Roth
8f9b2fe2c5
Merge pull request #1638 from Hestia-Homes/add-herdr-to-devcontainer
Add herdr to backend devcontainer
2026-07-16 17:17:05 +01:00
Daniel Roth
b173fc437a Add herdr to dockerfile 2026-07-16 16:35:58 +01:00
KhalimCK
324397721b
Merge pull request #1623 from Hestia-Homes/feature/sitenotes-mapping-wythenshaw
Wythenshawe Pashub SiteNotes mapping tests
2026-07-16 15:56:59 +01:00
Daniel Roth
d844bc6df2 PasHub SAP accuracy: pin Wythenshawe cohort (149/168 computable) 🟩
Add pashub_accuracy_wythenshawe fixtures + a sibling accuracy module,
built from project_code
[Wythenshawe WH:SHF RAs - WH:SF - 240426 - 494493718748] the same way as
LRHA WAVE 3 (#1620). First run: 149 computed / 19 blocked / 0 errored,
within-0.5 76.5%, MAE 0.665. Ratchets baselined to this cohort; the 19
blocks are separately-ticketed mapper-label gaps (floor 'Same dwelling
below' ×10 dominating), not tuning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 14:49:17 +00:00
Daniel Roth
80bba24598
Merge pull request #1621 from Hestia-Homes/feat/1619-pashub-storage-heaters
PasHub: score electric-storage & room-heater dwellings (Table 4e Group 4/6) — closes #1619
2026-07-16 15:34:52 +01:00
KhalimCK
2d0d04bb89
Merge pull request #1620 from Hestia-Homes/pas-accuracy-lincs-rural
Lincs Rural Pashub SiteNotes mapping tests
2026-07-16 15:32:05 +01:00
Daniel Roth
3e2509fb7d PasHub scores electric-storage & room-heater dwellings (Table 4e Group 4/6) 🟩
Teaches the pashub `from_site_notes` mapper the electric-storage-heater and
room-heater families, following the existing boiler/heat-network split exactly
(fix at the mapper boundary, ADR-0015 — never mis-rate, never strict-raise deep
in the calculator). Adds:

- Table 4e Group 4 storage charge-control codes (2401/2402/2404) and Group 6
  room-heater control codes (2602/2605), each gated on its own system group so a
  control label never takes a wrong-group 2xxx code.
- The Table 4a system code from the surveyed heater type lodged in the "Heating
  System (Other)" field (`community_heat_source`): Fan storage 404, slimline
  402, PCDF-Search product ⇒ HHR 409 (RdSAP 10 p.80), room heaters 691/694.
- A 2404⇔409 coherence guard: high-heat-retention controls on a non-HHR storage
  type is a surveyor contradiction and fails loud rather than rating a mismatch.

All codes taken from the SAP 10.2 spec (14-03-2025 PDF) and already present in
the calculator's inventories — no calculator change.

LRHA WAVE 3 (lincs rural) accuracy: 8 computed / 96 blocked → 45 computed / 59
blocked / 0 errored (within-0.5 46.7%, MAE 2.989); ratchets re-baselined to the
new coverage. Guinness cohort (208) + gov-API corpus unchanged; pyright --strict
adds zero new errors.

Closes #1619

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 13:30:07 +00:00
Daniel Roth
d557e1b0b4 make pashub accuracy test more generic and add files for lincs rural 2026-07-16 11:22:39 +00:00
KhalimCK
e75144afb3
Merge pull request #1617 from Hestia-Homes/fix/pashub-semi-exposed-floor
PasHub SAP accuracy: semi-exposed-unheated floor → Table 20 (+ Walsden/Paxton fixtures)
2026-07-16 02:01:07 +01:00
Khalim Conn-Kowlessar
6c4f73cc91 PasHub SAP accuracy: refresh 5 Paxton fixture to the current V5 (MEV) re-survey 🟩
5 Paxton Place (deal 507639151845, uprn 10070864419) had a STALE fixture.
The same PAS Hub survey (report B74AA5A4-…, inspected 29 Jun 2026) has two
accredited revisions in S3: V2 (uploaded 6 Jul, ventilation "Natural",
SAP 80) and V5 (uploaded 14–15 Jul, ventilation "Mechanical Extract -
Centralised", SAP 76 — the current version). Our committed fixture was the
superseded V2; the portfolio-838 oracle tracks the latest V5 (76). Our
engine faithfully reproduces BOTH (V2 → 80.4, V5 → 75.6) — the +4.38 miss
vs the DB oracle was a fixture-version mismatch, NOT an engine over-rate
(a parallel over-credit sweep found no spec-correct from_site_notes lever;
every channel is faithful).

Refreshed the fixture to the V5 PDF (image-stripped, text layer preserved,
6.7MB→0.27MB, per scripts/build_pashub_accuracy_fixtures.py) and set the
manifest to C76. 5 Paxton flips from the cohort's largest miss to a
within-0.5 hit: DB-oracle gauge 86.9%→87.4% within-0.5, MAE 0.348→0.327.
Pinned via test_paxton_fixture_is_v5_resurvey_sap_76 (asserts a literal 76
against the V5 fixture) — a canary, because hubspot_deal_data.pre_sap is
still stale at C80 and a fixture-builder rerun would revert the manifest.

Confirmed against pashub: the current survey is SAP 76 (Khalim).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 00:52:33 +00:00
Khalim Conn-Kowlessar
d3f36d78ac PasHub SAP accuracy: pin 13 Walsden to its re-survey SAP 64, not the colliding 68 🟩
13 Walsden (deal 499630239947, uprn 77179782) has TWO distinct gov-EPC
assessments: the PAS Hub site-note re-survey we parse (26 May 2026,
accredited SAP 64, backed by rd_sap_site_note files) and a SEPARATE
later lodged cert (24 Jun 2026, SAP 68, registered, no site-note). The
portfolio-838 baseline latched onto the 24-Jun SAP-68 cert; our engine
models the survey it was given and reproduces its own accredited 64
(64.19). 6 of 7 Walsden Street homes have pbp oracle == PAS Hub survey
SAP exactly — #13 is the lone collision.

A durable regression pin (asserts our SAP within 0.5 of a literal 64,
not the manifest value the fixture-builder can revert to a colliding DB
row) documents the provenance so nobody "corrects" the target to 68.
13 Walsden is a known re-survey-diverges outlier, not an engine
under-rate; confirmed against pashub (Khalim, 2026-07-15e).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 00:52:33 +00:00
Khalim Conn-Kowlessar
4c0ccad882 PasHub SAP accuracy: semi-exposed-unheated floor → Table 20, not §5.14 🟩
`_PASHUB_FLOOR_EXPOSURE` mapped the surveyed "Semi Exposed (unheated)"
floor label to `is_above_partially_heated_space` (RdSAP §5.14, constant
U=0.7). A floor semi-exposed to an enclosed UNHEATED space belongs in
§5.13 Table 20 `u_exposed_floor` (band-B as-built U=1.20). §5.14's U=0.7
is reserved for a floor above a *partially* heated space. #1602
transposed the two branches; the too-low U under-counted floor loss on
the 9 label-carrying cohort fixtures → a shared over-credit.

Triangulated against all three siblings + the calculator docstring: the
gov-API code-2 "To unheated space" (mapper.py:4797) and Elmhurst
`_is_floor_exposed_to_unheated_space` (mapper.py:5970) both route to
Table 20; rdsap_uvalues.py:1416 documents Table 20 as collapsing exposed
and semi-exposed-to-unheated into one lookup. One-line data flip,
from_site_notes-only (gov-API RdSAP corpus untouched, still 78.9%).

Adjacent Ronald St mid-terrace pair (the clustered signal): 10 Ronald
60.35→59.02 (oracle 59), 12 Ronald 60.79→59.36 (oracle 59). Cohort vs
DB oracle (portfolio 838 effective_sap) 83.3%→86.9% within-0.5, MAE
0.377→0.348, no built form regresses, all 7 verified dwellings unchanged
(none carry the label). pashub harness 79.5%→82.4%, MAE 0.389→0.367;
ratchets tightened 0.78→0.82 / 0.40→0.37. pyright zero-new.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 00:52:33 +00:00