Commit graph

88 commits

Author SHA1 Message Date
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
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
Khalim Conn-Kowlessar
ef34f76116 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:34:41 +00:00
Khalim Conn-Kowlessar
e5c112a2d3 Document the fuel-aware electric-CPSU resolution in ADR-0045 🟪
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 16:17:22 +00:00
Khalim Conn-Kowlessar
f253a7b9ba Resolve solid-fuel room-heater overrides off Gas CPSU to their SAP codes 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:58:25 +00:00
Daniel Roth
0488696df8
Merge pull request #1397 from Hestia-Homes/fix/1376-hhrsh-baseline-archetype
Add the HHRSH baseline archetype so it stops scoring as old storage (#1376)
2026-07-01 15:41:52 +01:00
Khalim Conn-Kowlessar
79f48c0564 Resolve a high-heat-retention storage override to SAP code 409 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:26:25 +00:00
Khalim Conn-Kowlessar
c24e465974 Resolve a from-main wood-logs water-heating override to the wood fuel 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:59:56 +00:00
Khalim Conn-Kowlessar
19c1a78613 Record the glazing reconcile collapsing to a MIXED sentinel (ADR-0042 amendment)
Per-window assignment from a landlord aggregate is strictly worse than the cert's
existing per-window sap_windows, so a mixed override always defers — the proportion
comparison only fed a 'flag when far' branch not worth a new schema field. Realized
decision: a GlazingType.MIXED sentinel (LLM classifies mixes to it) resolves to no
overlay, preserving the cert glazing; uniform assertions still apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:00:58 +00:00
Khalim Conn-Kowlessar
cba536eba8 Record flat-roof-thickness and glazing-reconcile decisions (ADR 0041/0042)
ADR-0041: flat roofs ARE scored by insulation thickness via RdSAP Table 16
col (1) ('joists at ceiling level and flat roof'); the RoofType taxonomy lacks
flat-thickness members so the depth is discarded — fix needs new FLAT_*MM members
(FE-owned pgEnum). ADR-0042: a landlord glazing override reconciles against the
cert's per-window sap_windows composition rather than flattening every window to
one type; mixed-and-close defers to the cert, mixed-and-far is flagged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 09:22:13 +00:00
Daniel Roth
0c701ba3de
Merge pull request #1373 from Hestia-Homes/fix/1361-class-b-predicted-null-lodged
Drop the phantom Lodged Performance on predicted Properties (#1361 Class B)
2026-07-01 08:35:58 +01:00
Khalim Conn-Kowlessar
7ecb7eebd1 Record that a predicted Property has no Lodged Performance
Amends ADR-0004's both-halves-always-populated invariant: EPC Prediction
borrows a neighbour's recorded figures, so a predicted Property has no Lodged
Performance — its lodged half is absent (NULL), only the Effective half is
persisted. Sharpens the CONTEXT.md glossary for Lodged / Baseline Performance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 22:07:27 +00:00
Khalim Conn-Kowlessar
56aaace5b1 Default a solid-fuel room heater's natural fuel to house coal 🟩
Solid fuel is ambiguous across coal/wood/smokeless/pellets, so the overlay
defaults to house coal (33, the most common) when no main_fuel override is
given; a specific solid-fuel override still wins by last-wins composition.
Updates the ADR-0041 natural-fuel amendment accordingly (every archetype drags
a natural fuel; solid defaults to coal).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:55:57 +00:00
Khalim Conn-Kowlessar
d5927c68a9 docs: ADR-0041 amendment — natural-fuel coherence
Record the natural-fuel drag-along rule the overlay now implements: drag a fuel
only where the archetype is unambiguous (electric/gas), leave ambiguous
solid/oil room heaters to the main_fuel override, let a present main_fuel
override win by last-wins composition, and LOG (not raise) a landlord fuel that
contradicts the archetype's natural fuel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 16:46:56 +00:00
Khalim Conn-Kowlessar
72e1e67c6d docs: ADR-0041 — landlord-heating classification targets a complete taxonomy
grill-with-docs output for PRD #1361 Class A. The band-G baselines are not a
fabric/rebaseline bug (walls are correct — landlord asserted as-built); they are
a target-vocabulary gap in the landlord-description heating classifier. The LLM
maps unbounded free-text onto a closed internal taxonomy, but that taxonomy had
9 archetypes vs ~13 RdSAP families, so it overflowed into "Gas CPSU"/storage
(the "convector" collision sent direct-acting room heaters to off-peak storage
403 → SAP ~10).

- ADR-0041: fix the taxonomy not the mapper; unmapped input -> None (keep lodged
  EPC, surface to user), never a forced archetype; heat pumps modellable via a
  default Table 4a code (no PCDB id); community heating via codes 301/302/304
  with a documented DLF-1.50 / boiler-source default.
- CONTEXT.md: sharpen Landlord-Description Classification and the four distinct
  vocabularies (landlord / recognised-internal / lodged / SAP code).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:33:54 +00:00
Khalim Conn-Kowlessar
79b86a38c1 docs: ADR-0039 (override-aware rebaseline) + ADR-0040 (nested persistence) 🟪
Record the two load-bearing decisions and correct the CONTEXT.md
'Calculated SAP10 Performance' entry: the lodged figure is kept at >=10.2
only for a pristine lodged cert; overrides/prediction adopt the calculator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 19:25:54 +00:00
Jun-te Kim
a680d65188 fix(overlay): synthesise a coherent gas-boiler heating system on Landlord Override
A Landlord heating-system override was applied as a sparse patch, so the
replaced system's fields bled through. A storage flat reclassified as a gas
combi (property 728513) kept mains_gas=False, heating category 7, the 2401
storage charge control, a Dual meter and an electric-immersion cylinder — an
incoherent record that gated out the gas-boiler-upgrade Measure and made the
heating Generator read the dwelling as off-gas (offering HHRSH storage).

Extend the ADR-0035 drag-along to gas boilers (Table 4b 102/104/120): the
overlay now sets the whole coherent companion set — mains_gas, gas main fuel,
heating category 2, fanned flue, full modern controls (2106), a single-rate
meter, and hot water from the main system with the cylinder set from the boiler
type (combi → none, regular/CPSU → cylinder). The main_fuel overlay also flips
mains_gas=True for a "mains gas" fuel. Non-off-peak archetypes now drag an
explicit Single meter so a system switch never leaves a stale Dual.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:48:33 +00:00
Khalim Conn-Kowlessar
58f6b868a7 docs: ADR-0038 + glossary — bound Solar PV array to the dwelling's own roof
ADR-0026 sized PV at 0.70 x Google maxArrayPanelsCount, assuming Google's roof
is the dwelling's. Medium-imagery footprint conflation (semi-detached/terraced)
returns the whole building's roof -> oversized arrays (property 742003: 55m2
bungalow -> ~16 kWp -> effective E/49.7 -> A/92.4 on solar alone). ADR-0038
bounds the array to the dwelling's own usable roof (roof_area-based budget,
proximity segment selection anchored on the dwelling), subsuming the legacy
GoogleSolarApi guards the rewrite dropped. CONTEXT: add Footprint Conflation +
Dwelling-Roof Cap, extend Solar Potential (per-segment centre + area).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:08:11 +00:00
Khalim Conn-Kowlessar
2d18e9a3b0 docs: ADR-0037 + Full-SAP Assessment term for the baseline-downgrade family
Captures the grill-with-docs decisions for the full-SAP-17.1 mapper completion:
rebaseline-to-10.2 (restores Rebaselining trigger (a)), calc-affecting fields
coupled with the sap_version flip, and growing Elmhurst-anchored validation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 20:24:34 +00:00
Khalim Conn-Kowlessar
eab979e686 Document off-peak as a whole-meter tariff billed day/night (ADR-0014 amendment + glossary)
Resolves the ADR-0014 off-peak deferral: off-peak is a property of the
meter (every electric end use splits day/night by its own High-Rate
Fraction, a calculator output), not a per-end-use fuel. Adds the
Off-Peak Meter and High-Rate Fraction glossary terms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 16:54:41 +00:00
Khalim Conn-Kowlessar
1a059c75bb Document non-separated conservatory handling (terms, ADR, migration) 🟩
CONTEXT.md gains Non-Separated / Separated Conservatory glossary terms; ADR-0036
records the mapper-owned §6.1 split + 1:1 persistence + round-trip contract; the
migration note lists the five new epc_property columns for the FE schema repo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:56:05 +00:00
Khalim Conn-Kowlessar
db3bf00602 fix(synthesis): Coherent Heating System — synthesisers own the whole boundary
A dwelling's heating is one conceptual system, but its fields are scattered
across EpcPropertyData (a gov-API schema mirror): the cluster on sap_heating, the
electricity tariff on sap_energy_source.meter_type, hot-water flags loose at top
level. Three places synthesise a heating system — Measure Options, Landlord
Overrides, EPC Prediction's donor — and each hand-copied a different ad-hoc
subset. The override and donor both dropped meter_type, so an electric-storage
system landed on the template's single-rate meter and billed overnight heat at
the peak rate: property 713406 scored SAP 13 (G) vs ~50 (E), inflating the HHRSH
measure to +45.8 and overshooting the plan to band A.

Establish a single Coherent Heating System boundary (CONTEXT.md) that every
synthesiser must cover, with a source-appropriate fill policy (ADR-0035):

- Override overlay *completes* the partial system the landlord named. Companion
  fields are now DERIVED from the SAP code, not hand-attached per archetype: the
  off-peak meter from the calculator's single off-peak classification (new
  OFF_PEAK_IMPLYING_HEATING_CODES = SAP §12 Rules 1-2), and an unobserved storage
  charge control defaults to the conservative manual control (Table 4e 2401). So
  adding a heating archetype is just adding its code — companions can't be
  forgotten. A contract test guards it (every off-peak code drags a Dual meter).
- Prediction's heating donor now *carries* the donor's meter_type alongside its
  sap_heating cluster — the donor is already coherent.

Coherence is a synthesis-time obligation only; the calculator still scores a real
lodged cert exactly as lodged.

Verified on 713406: baseline 13 -> 47.8 (E), matching its recorded rating; the
phantom HHRSH recommendation is gone and the plan no longer overshoots to A.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:46:11 +00:00
Khalim Conn-Kowlessar
de7fb94ff7 docs(adr): record nearby-postcode broadening (0034) + share HTTP retry primitive
Closes out the cohort-broadening work with its decision record and consolidates
the retry plumbing.

ADR-0034 documents broadening the EPC-Prediction cohort to the real unit
postcodes nearest the target (via postcodes.io) when its own postcode holds no
same-type comparable — extending ADR-0031 decision 5. Records why postcodes.io
was chosen over council[] (whole-LA, no property_type in rows), a bulk Code-Point
Open / ONSPD dataset, and the OS Places radius API, and the lazy / nearest-first
early-stop / soft-fail policy. Broadening-specific docstrings now cite 0034.

Retry consolidation: extract the EPC client's call_with_retry into a shared
infrastructure/http_retry.py keyed off a generic TransientHttpError marker, so
the mechanism (exponential backoff, Retry-After) is shared while each client
keeps its own transient policy. EpcRateLimitError now subclasses TransientHttpError
(still an EpcApiError); PostcodesIoClient routes through the same helper, raising
TransientHttpError on 429/5xx and soft-failing to the seed once exhausted (the EPC
client propagates instead). Direct tests for the shared helper; EPC + postcodes.io
suites repointed at the shared sleep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 16:54:06 +00:00
Jun-te Kim
027ba6d89f added landlord overrdies 2026-06-20 12:58:09 +00:00
Jun-te Kim
1c3b4a7b72 Correct ADR-0032 dec-4: property_type affects party-wall calc + eligibility, not just metadata
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 18:07:30 +00:00
Jun-te Kim
7e6974d95e Model the override-folded Effective EPC in the modelling e2e script 🟩
Routes run_modelling through prop.effective_epc and dumps each target's
property_overrides before the run, so a landlord wall override moves the
calculated SAP. Records the overlay design in ADR-0032.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:38:55 +00:00
Khalim Conn-Kowlessar
d1227fd0c6 docs(epc-prediction): ADR-0031 production wiring + CONTEXT gating rule
Resolve the slice-5 design tree (grill-with-docs): estimation runs in Ingestion
(refines ADR-0029 dec-3; drops the #1227 "shift to Modelling" — no surviving
rationale, and stages communicate only via persisted state); predicted EPC is
persisted in a DISTINCT slot (EPC table + source discriminator) so lodged +
predicted coexist (enables EPC Anomaly Flags); provenance is structural (the
slot), not a field on EpcPropertyData; effective_epc/source_path gain a
"predicted" branch; slice-5 is gap-fill only; property_type is a REQUIRED input
(hard cohort filter) from Landlord Overrides, and Properties with unknown type
are gated out (no national defaults). OS postcode_search as a broader type
source is a noted follow-on. CONTEXT EPC Prediction entry gains the gating rule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 03:23:12 +00:00
Khalim Conn-Kowlessar
a622f97d27 docs(adr): ADR-0030 — record S3-hosted Tier-1.5 scale run
Tier-2 (full national bulk streaming) is deferred. The near-term scale
validation is a Tier-1.5: a few-thousand-cert anonymised corpus stored in
S3 (too large to commit, far more stable than the 36-target gate fixture),
pulled to a temp dir and run through the same load_corpus +
evaluate_component_accuracy. Reuses the committed-fixture machinery wholesale
— only the data source differs. One scorer, three data sources (committed
fixture / S3 corpus / bulk stream).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 09:25:58 +00:00
Khalim Conn-Kowlessar
35a7c07812 docs(adr): ADR-0030 — SAP-version-aware, component-first EPC Prediction validation
Records the grilling-session decisions amending ADR-0029's validation:
- Source cohort keeps all cert vintages (components are agnostic of the SAP
  methodology that rated them); only the held-out validation TARGET is
  restricted to SAP 10.2. Amends ADR-0029 decision 5 ("pre-SAP10 dropped").
- Component Accuracy (predicted vs API actual components) is the primary,
  calculator-independent signal. calc(predicted) vs calc(actual) rejected
  (circular ground truth, hides calculator error); neighbour-mean-lodged-SAP
  baseline rejected (mixes SAP versions). calc(predicted) vs API-lodged
  SAP/carbon/PE kept as a secondary, calculator-floored guard.
- Two tiers: committed anonymized fixture (ratcheting CI gate) + bulk-export
  national battle-test on harness/epc_bulk.py + harness/cohort.py, emitting
  accuracy + a failure taxonomy, re-baselining the gate floors.

CONTEXT.md: Comparable Properties corrected to all-vintage source; new
Component Accuracy term. ADR-0029 Validation section marked superseded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 08:47:58 +00:00
Khalim Conn-Kowlessar
008a1b2783 docs(adr): EPC Prediction from Comparable Properties (ADR-0029)
Grill-with-docs outcome: deterministic neighbour synthesis (NOT ML) of an
EPC-less Property's EpcPropertyData picture, scored via Sap10Calculator.
Six decisions — predict-components-not-SAP; deterministic k-NN; fetch-phase
fallback behind a pure EpcPrediction service + ComparableProperties port;
hybrid synthesis (cohort-mode categoricals + coherent template structure +
overrides); filter-then-relax cohort weighted geo x recency x similarity;
dual-use gap-fill + anomaly flags. Frozen postcode-clustered corpus backs
leave-one-out validation. CONTEXT.md: new EPC Prediction term, Comparable
Properties refined, ML framing corrected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:36:19 +00:00
Jun-te Kim
77c5f7da49 Merge branch 'feature/bill-derivation' of https://github.com/Hestia-Homes/Model into feature/junte+khalim 2026-06-12 12:52:40 +00:00
Khalim Conn-Kowlessar
f9a89a8e11 docs(adr): secondary heating removal — ADR-0028 + CONTEXT term
Records the four load-bearing design decisions from the grill-with-docs session:
standalone co-selectable rec; eligibility = lodged-only (no effectiveness gate,
electric-storage §A.2.2 no-op is the Optimiser's call); dedicated clearing
SecondaryHeatingOverlay; flat per-dwelling cost (a lodged secondary is fixed per
RdSAP, so a real decommission job, not room-scaled).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 15:32:14 +00:00
Jun-te Kim
cc0e875fd8 Record pre-SAP10 RdSAP family coefficient transfer (ADR-0028)
Documents the inherit-and-validate decision for 18.0: reuse 20.0.0's 0.148 +
band multipliers (the corpus can't self-fit — 958/1000 band-1 with no measured
band-1 windows), validated against 18.0's own band-4 rich certs (0.223 obs vs
0.148 x 1.51 pred). References ADR-0027 one-way (keeps the accepted ADR immutable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:06:03 +00:00
Khalim Conn-Kowlessar
ae7e6a0c42 feat(modelling): composite per-dwelling boiler + tune-up costing (ADR-0027)
Replace the flat placeholder scalars (boiler £3000; tune-up £500/£900) with a
per-dwelling composite cost, mirroring the ASHP architecture (ADR-0025): a
`HeatingRates` table (data, `heating_rates.json`), typed `BoilerCostInputs` /
`TuneUpCostInputs`, pure `Products.boiler_bundle_cost` / `tune_up_cost`, and
modelling-layer interpreters that read the dwelling into those inputs.

The cost mirrors the Simulation Overlay component-for-component, sharing the
controls + cylinder pricing across both options:

- tune-up (standard) = standard controls + cylinder fixes
- tune-up (zone)     = zone controls + cylinder fixes
- boiler upgrade     = £3200 all-in + standard controls (only when the upgrade
  fired a controls change) + cylinder fixes

Standard controls are priced INCREMENTALLY — only the parts missing to reach
SAP 2106 (programmer £120 / room thermostat £150 / TRV £35×radiators), read
from a Table 4e Group-1 feature map so a dwelling that already has a room
thermostat + TRVs is only charged the programmer. Zone controls are a full
smart kit (hub £205 + smart TRV £50×radiators) — the smart TRV is itself the
room sensor, so there is no separate per-room sensor line. Cylinder fixes:
jacket £50 (when under-insulated) + thermostat £150 (when absent). The boiler
is a like-for-like wet swap (no radiators/flue/pipework — eligibility already
requires an existing wet boiler), so those dead-code extras are not modelled.

Figures are research-validated 2025/26 UK installed costs (legacy Costs.py
lineage); fully-loaded totals with one contingency on top (Model B, not the
legacy VAT/preliminaries engine). Contingency: boiler 0.26; tune-ups 0.10
(was a 0.15 placeholder). ADR-0027 records the design; CONTEXT.md's Heating
Eligibility entry updated to cover the partial boiler/tune-up family + composed
cost. Products cost pins (delta<=1e-9) + interpreter tests + generator
composite-cost assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 19:41:06 +00:00
Jun-te Kim
5589a66e7c Document RdSAP 20.0.0 Reduced-Field Synthesis (CONTEXT.md term + ADR-0027)
Sharpen the glossary to decouple deterministic old-schema re-mapping from
neighbour-prediction gap-fill (a separate, unimplemented ML path), and add
the Reduced-Field Synthesis term. ADR-0027 records the pre-SAP10 20.0.0
mapper's best-attempt synthesis (corpus-fit glazing 0.148xTFAxband, 4-way
orientation) and its trade-offs. Grill resume doc captures every resolved branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 14:14:18 +00:00
Khalim Conn-Kowlessar
222704cbc2 docs(modelling): ADR-0026 solar PV eligibility, sizing, overlay, costing
Designed via grill-with-docs. API-first (Google Solar -> typed SolarPotential):
one Solar PV Recommendation, up to 5 conservatively-sized configs x battery
on/off; new PV overlay surface (per-roof-segment arrays, diverter conditional on
cylinder, export-capable ensured); overshading generation-calibrated from the
API's expected generation; eligibility offers conservation areas (only listed/
heritage block); composite costing from EA rates (contingency 0.15). CONTEXT
gains Solar Potential, Solar PV Recommendation, Solar PV Eligibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 09:44:02 +00:00
Khalim Conn-Kowlessar
f182f36802 feat(modelling): ashp_cost_inputs reads a dwelling into AshpCostInputs
Slice 7 of ADR-0025 costing: the modelling-layer interpretation half of the
split. ashp_cost_inputs derives existing system (mains_gas/fuel/SAP-code),
size band (floor area <= 75 m2), design heat loss (floor_area x 0.05 -- the
chosen proxy over HLC, ADR updated), radiator count (habitable + 3, floor-area
fallback) and reusable-wet-system flag. Catalogue math (Products) stays
EPC-free. ADR-0025 updated to record the floor-area pump-sizing choice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 20:59:27 +00:00
Khalim Conn-Kowlessar
a61b6f90c9 docs: heating & hot-water eligibility, bundles, overlay (ADR-0024 + CONTEXT)
Designs the heating Recommendation Generator via /grill-with-docs. Load-bearing
decisions: HW + controls + fuel + meter fold into each competing whole-system
bundle (the legacy heating-vs-HW split double-counted); each bundle is a fixed,
real, contractor-installable end-state (ASHP via PCDB index, HHR storage via
sap_main_heating_code=409), Product stays cost-only; eligibility encodes only
physical/planning installability since the Optimiser owns the economics (the
legacy ASHP built-form / 120 m² rule is dropped — research found no
authoritative basis); the Simulation Overlay is the deepest surface yet,
spanning main_heating_details[0] + sap_heating + top-level EpcPropertyData +
sap_energy_source. Build order HHRSH -> ASHP -> boiler (deferred).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 18:51:33 +00:00
Khalim Conn-Kowlessar
b460f81233 docs: lighting eligibility, overlay + pricing design (ADR-0023 + CONTEXT)
Resolved in a grill-with-docs pass. recommend_lighting converts ALL non-LED
bulbs (incandescent + CFL + low-energy-unknown) to LED — all the way to LED, not
the legacy "fill to low energy", because SAP §12-1 rates LED efficacy (100)
above LEL (80) / CFL (55). A free Optimiser candidate (it improves SAP), unlike
ventilation's forced dependency. Its overlay is the first whole-dwelling,
top-level surface: a LightingOverlay carrying the four bulb-count fields by
their exact EPC names, folded directly onto EpcPropertyData (led=total, others
0). Priced per-bulb x non-LED count, contingency 0.26, measure_type
low_energy_lighting (MEASURE_MAP-aligned; "LED" in the description). Validation:
real before/after cascade pins (zero-existing-LEDs + some-existing-LEDs) at 1e-4,
clean (no fabric coupling). Ground-truth confirmed: 20 incandescent -> 20 LED
drops lighting (232) 783.7 -> 232.7 kWh/yr.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 12:02:40 +00:00
Khalim Conn-Kowlessar
2c36a8e1d6 Merge remote-tracking branch 'origin/main' into feature/bill-derivation
# Conflicts:
#	repositories/property/property_postgres_repository.py
#	tests/orchestration/fakes.py
2026-06-05 11:09:00 +00:00
Khalim Conn-Kowlessar
0846b61304 docs: glazing eligibility, overlay + pricing design (ADR-0022 + CONTEXT)
Grill-with-docs outcome for the glazing generator. Single planning-picked
Measure (double when unrestricted, secondary for conservation/listed/heritage),
upgrading all single-glazed windows together. The overlay writes lodged U-value
+ solar-g directly into WindowTransmissionDetails (our calculator consumes those
as inputs — it does NOT derive them from glazing_type, unlike Elmhurst) plus
glazing_type for the §5 daylight factor; EpcSimulation gains a per-window
`windows` surface. Priced flat average-per-window × single-glazed count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 22:44:13 +00:00
Khalim Conn-Kowlessar
0c7ce634db docs: roof-insulation eligibility design (ADR-0021 + CONTEXT)
Grill-with-docs outcome for the roof-insulation generator. One dispatching
recommend_roof_insulation, one Measure per roof by type (loft 300mm incl.
thatch / sloping-ceiling 100mm / flat-roof 200mm; no-access → none),
MAIN-only, room-in-roof deferred. Detection keys on the roof_construction_type
string (populated on both paths; the calculator already dispatches on it) with
sloping→flat→no-access→loft ordering; the roof_construction-int cross-mapper
parity is the follow-up Hestia-Homes/Model#1178. Thatch is not excluded — it
takes loft insulation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 20:53:21 +00:00
Khalim Conn-Kowlessar
3af6c21ff0 docs: cross-link system-built blocker to Hestia-Homes/Model#1177
Reference the calculator-side wall_construction=6 disambiguation issue from the
strict-xfail tripwire and ADR-0019, so the blocker is traceable both ways.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:29:05 +00:00
Khalim Conn-Kowlessar
e8212544ed docs(adr): record slice 3c persistence + unknown-default decisions (ADR-0020)
Pin the resolution reached in the grill: planning status persists as a
per-UPRN write-through cache in the existing `property_details_spatial` table
(not FE-property columns), read back off the Property in Modelling; unknown
UPRN defaults to unrestricted, matching legacy `empty_spatial_df` (superseding
the earlier "conservative stance" note).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:26:21 +00:00
Khalim Conn-Kowlessar
ea4534f3af feat(modelling): system-built walls take EWI+IWI (blocked on basement-code fix)
System-built (precast/no-fines concrete) takes both solid-wall Options like
solid brick (ADR-0019), keyed on `wall_construction == 6` (WALL_SYSTEM_BUILT,
Elmhurst `SY`). A basement-suitability guard (`main_wall_is_basement`) is added
since a below-ground basement wall is never EWI/IWI-suitable.

This is currently inert: `B Basement wall` also maps to 6 (mapper.py:2100) and
`main_wall_is_basement` is derived as `wall_construction == 6`, so every code-6
wall reads as basement and is guarded out — the live cohort is unchanged. The
system-built EWI/IWI cascade pin is committed as a strict-xfail tripwire that
flips green the moment the calculator disambiguates system-built from basement
(MAIN wall_construction==6 with main_wall_is_basement False). `wall_construction
== 8` is Park home, not system-built — not keyed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:26:08 +00:00
Khalim Conn-Kowlessar
bb1029c0d8 feat(baseline): SAP fuel-code -> Fuel mapping for billing (ADR-0014)
Slice 3 of Bill Derivation. sap_code_to_fuel(code) maps a SAP 10.2 / Table 32
fuel code to the canonical billing Fuel — bounded to the ~47 Table 32 codes (the
carrier, orthogonal to the PCDB product index, so all PCDB heat pumps share one
electricity code). Mains gas / LPG / oil+bioliquids / coal / smokeless / wood /
electricity (standard + off-peak) / heat-network groupings; an unmapped code
(dual fuel, grid-export) raises UnmappedSapCode rather than guessing.

Also: ADR-0014 deferred/TODO section records the stubbed appliances+cooking
(pending the SapResult fields), the off-peak day/night split, the heat-network
rate gap, and regional rates / ETL.

The SapResult -> EnergyBreakdown adapter (next slice) is gated on the
appliances/cooking fields landing on SapResult.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:40:26 +00:00
Khalim Conn-Kowlessar
8c2bc08df3 docs(adr): Bill Derivation (ADR-0014) + calculator goes load-bearing (ADR-0013 amend)
Pin the bills design from a /grill-with-docs session:
- ADR-0014: whole-home annual bill from SAP10 Calculation's delivered kWh per
  end use, re-priced at real Fuel Rates (NOT the calculator's SAP-notional
  total_fuel_cost_gbp, which is RdSAP Table 32 standardised prices ~half real
  electricity). Fuel enum + FuelRates + FuelRatesRepository static snapshot;
  per-section + total flat columns; raise on unpriced fuel (house coal /
  heat network are the named gaps).
- ADR-0013 amendment: the shadow stepping-stone is collapsed — the calculator
  is load-bearing now. effective=calculated for sap_version<10.2 (StubRebaseliner
  floor 10.0->10.2); >=10.2 keeps lodged + logs divergence; a strict-raise
  aborts the batch (load-bearing for bills regardless of version).
- CONTEXT: EPC Energy Derivation -> Bill Derivation (no "service" suffix);
  Baseline Performance energy block = per-end-use kWh + per-section bill + total;
  Fuel Rates = committed static snapshot; Rebaselining trigger threshold 10.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:40:26 +00:00
Khalim Conn-Kowlessar
741993270e 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-04 15:40:26 +00:00
Khalim Conn-Kowlessar
1c7997c471 feat(modelling): solid-wall generator offers EWI+IWI for solid brick
Slice 2a. New recommend_solid_wall emits one Main-wall Recommendation carrying
External + Internal wall-insulation Options for an uninsulated (wall_insulation
_type=4) solid-brick (wall_construction=3) main wall, each priced at the heat-
loss wall area. Cascade pin: the generator's EWI and IWI Options reproduce
their respective re-lodged afters at abs(diff) <= 1e-4.

Detection keys on wall_construction code, not description (ADR-0019 note
corrected): the Elmhurst ingestion path leaves walls[].description empty, so
the code is the only cross-path signal; codes 1-5 are consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:28:15 +00:00
Khalim Conn-Kowlessar
da69dc27fd docs(modelling): wall-insulation eligibility + conservation-status ADRs
Captures the grill-with-docs session for solid-wall insulation: CONTEXT.md
gains EWI/IWI Measure Types + the Wall Insulation Eligibility rule (+ a
flagged-ambiguity that the three planning flags stay distinct, never recollapsed
to legacy restricted_measures). ADR-0019 records the eligibility policy (cavity
-> cavity only; brick/system -> IWI+EWI; timber -> IWI only; cob/stone -> none;
conservation/flat block EWI, listed/heritage block both). ADR-0020 records
conservation/listed/heritage as three distinct Property attributes sourced by
extending the geospatial S3 repo (flags co-located with lat/long).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:11:40 +00:00