Commit graph

7672 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
13b75bcb0a Raw-JSON EPC disk cache + roof_construction co-occurrence sweep 🟩
JsonCachingEpcClient caches at the raw layer (cert payloads verbatim,
search rows as JSON) so warm re-runs replay through retry+mapper+parsing
in minutes AND the cache doubles as integration-test fixture material —
the ADR-0030 corpus pattern (anonymised raw JSON through the mapper),
which pickled domain objects could never be. Wired into the pairs
harness (--cache-dir). roof_construction_code_sweep pins the RdSAP roof
code table the basement-code-6 way: single-building-part certs only,
code x description-prefix purity table. Also fixes the tfa telemetry key
rename that left the agreement row empty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:35:51 +00:00
Khalim Conn-Kowlessar
006c1317ac ADR-0054 amendment: band widths set by the 439-pair harness evidence
Age band equality -> ±1-band neighbourhood; TFA ±5% -> ±20%; whitelist
core validated (fuel 95% / type 91% / wall 79% stable); roof-insulation
collateral flagged for follow-up. CONTEXT.md term updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:09:15 +00:00
Khalim Conn-Kowlessar
38661c6bd7 Age band conditions within one band; the floor-area band widens to ±20% 🟩
Evidence (439-pair harness, PR #1466): historic-vs-new age band agrees
52% exactly but 90% within one band (assessors re-band, skewing newer);
TFA agrees 45% within ±5% but 82% within ±20%. Equality/±5% steered the
cohort toward stale values where they engaged and relaxed everywhere
else. Band definitions are public so the harness's ladder replay shares
one source of truth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:08:03 +00:00
Khalim Conn-Kowlessar
355c1e7d2f Age band conditions within one band; the floor-area band widens to ±20% 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 11:06:45 +00:00
Khalim Conn-Kowlessar
1a11d0b900 Legacy register fuel descriptions resolve to modern main_fuel codes 🟩
Strip the pre-RdSAP-17 deprecation rider and alias the SAP-prefixed
forms before the code-table lookup. Raises the historic fuel resolution
rate from ~22% of pairs to ~92% of dump rows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:03:50 +00:00
Khalim Conn-Kowlessar
c79cd777e5 Legacy register fuel descriptions resolve to modern main_fuel codes 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:03:09 +00:00
Khalim Conn-Kowlessar
2903a069ea Pairs harness survives unmappable certs and appends telemetry incrementally 🟩
The 2000-postcode sweep died at ~884 on a strict-raising RdSAP-17.1 cert
(missing multiple_glazed_proportion) inside the pair-check, losing the
whole run's output. get_by_uprn and the cohort fetch now skip-and-log per
item; telemetry rows append as produced and carry raw values (age band,
TFA, fuel text) so band-width questions are answerable post hoc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:01:57 +00:00
Khalim Conn-Kowlessar
ee5bb6c4f2 Pairs harness telemetry: filter engagement + historic-vs-new agreement 🟩
Per pair, replay the age->fuel->TFA conditioning ladder over the plain
arm's cohort to record ENGAGED vs RELAXED per filter, and record whether
each historic stable attribute still agrees with the newly lodged cert
(the direct staleness measurement). Emitted as JSONL (--telemetry) and
aggregated into two diagnosis tables appended to the report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:24:46 +00:00
Khalim Conn-Kowlessar
1d83637afa Pairs harness reports the full Component Accuracy suite per arm 🟩
Classification hit-rates for every compare_prediction component, all five
numeric residuals, and the secondary calculator-floored SAP residual
(calc(predicted) − lodged), plain vs conditioned side by side — the same
metric shape as the prediction-corpus gate (ADR-0030). Pair-check now
precedes the cohort fetch so a national postcode sweep only pays the
expensive search-by-postcode for shards that actually hold a pair.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 09:05:59 +00:00
Khalim Conn-Kowlessar
d9576db429 Pairs harness: score plain vs historic-conditioned prediction per attribute 🟩
Finds pre-2012 (S3 backup) x SAP-10.2 (new API) cert pairs per postcode,
predicts each from its leave-one-out cohort with and without ADR-0054
conditioning, and reports compare_prediction hit rates side by side.
Evidence for the stable-attribute whitelist, not a CI gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:46:16 +00:00
Khalim Conn-Kowlessar
64f7d7ad8b Thread the historic-EPC reader through the first-run composition root 🟩
Off by default like the other two prediction collaborators; the pipeline
turns on Expired-Enhanced Prediction by passing a resolver over the
historic S3 backup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:41:56 +00:00
Khalim Conn-Kowlessar
9204228366 An expired historic cert conditions ingestion's prediction and labels it 🟩
Ingestion's lookup chain becomes: new EPC API -> historic backup by exact
UPRN -> plain prediction. A found record's stable attributes fill the
gaps Landlord Overrides left (overrides always win) and enrich the target
with age band / fuel / TFA band; the persisted predicted-slot row carries
source="expired". Reader unwired or shard miss -> behaviour unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:38:07 +00:00
Khalim Conn-Kowlessar
9e1c1b71a4 An expired historic cert conditions ingestion's prediction and labels it 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:34:54 +00:00
Khalim Conn-Kowlessar
f32b0d8405 An expired-source EPC occupies the predicted slot without stranding rows 🟩
EpcSource widens to "expired" (ADR-0054; the column is TEXT — no
migration). "predicted"/"expired" form one slot family: _slot_sources
routes every slot read and slot-clearing delete through the family, so a
re-ingestion flipping the flavour replaces the row instead of stranding
its sibling. FakeEpcRepo mirrors the family.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:33:16 +00:00
Khalim Conn-Kowlessar
592f924761 An expired-source EPC occupies the predicted slot without stranding rows 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:28:40 +00:00
Khalim Conn-Kowlessar
d2d696a1c5 Historic stable attributes condition cohort selection on the relax ladder 🟩
Age band (Table S1 letter), main fuel code, and a ±5% floor-area band —
the first numeric-tolerance filter — each ride _maybe_filter, so an
unresolved attribute (None) is inactive and a starving filter relaxes.
Existing callers pass no new fields and are behaviourally untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:26:00 +00:00
Khalim Conn-Kowlessar
248aa5ad6a Historic stable attributes condition cohort selection on the relax ladder 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:24:36 +00:00
Khalim Conn-Kowlessar
bbfcf1385b Resolve a historic EPC's stable attributes into cohort code spaces 🟩
property_type/built_form reuse the override code mapping; wall material
prefix -> RdSAP wall_construction; E&W age-band strings -> Table S1
letters; main_fuel display text (stripping " (not community)") -> modern
RdSAP-20/21 codes; TFA -> float. Unresolvable -> None (filter inactive).
Roof construction is deferred: its RdSAP code table isn't pinned in-repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:22:31 +00:00
Khalim Conn-Kowlessar
992950bd9f Resolve a historic EPC's stable attributes into cohort code spaces 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:21:41 +00:00
Khalim Conn-Kowlessar
ece4b736d9 A re-lodged UPRN resolves to its latest historic certificate 🟩
Pins behaviour the exact-match slice already carried (max by
lodgement_date; ISO dates sort lexicographically).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:18:41 +00:00
Khalim Conn-Kowlessar
f9f80f7184 Resolve a UPRN to its historic EPC record by exact match 🟩
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:17:54 +00:00
Khalim Conn-Kowlessar
4bbcdb6a61 Resolve a UPRN to its historic EPC record by exact match 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:17:07 +00:00
Khalim Conn-Kowlessar
2788849e16 ADR-0054 + glossary: expired historic EPC conditions prediction with stable attributes
Settled in a grilling session with Khalim: the pre-2012 backup feeds EPC
Prediction landlord-override-style (stable attrs as relax-ladder filters,
TFA as a ±5% band, exact-UPRN lookup, source="expired"), never trusted as
current state; scoped to the historic source; validated by a pairs harness.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:15:43 +00:00
Khalim Conn-Kowlessar
cbffae07b8 Annotate locals assigned from cross-module calls in the historic-EPC stack 🟪
Review ask (dancafc): resolver/repository locals now carry explicit types
(matches: list[ScoredHistoricEpc], records: list[HistoricEpc], df:
pd.DataFrame, ...) so the flow reads without chasing callee signatures.
CLAUDE.md's Type Safety section gains the rule so future sessions enforce it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 14:59:13 +00:00
Khalim Conn-Kowlessar
246834fac0 Map shard rows to HistoricEpc field-by-field so every column is pyright-checked 🟩
The row→domain mapper now names all 93 constructor arguments explicitly
instead of splatting a lowercased dict, takes a plain Mapping (a
DataFrame.to_dict("records") row) instead of a pandas Series, and ignores
columns the domain type doesn't know. A missing/renamed CSV column fails
loudly as a KeyError at the row. Both iterrows() call sites move to
to_dict("records") — pandas-stubs types iterrows' Series unparameterized,
which strict mode rejects. pandas-stubs + boto3-stubs[s3] make the stack
check clean: pyright strict is now 0 errors across the PR's files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 14:58:09 +00:00
Khalim Conn-Kowlessar
55284a1807 A shard with an unexpected extra column still maps to HistoricEpc 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 14:43:47 +00:00
Khalim Conn-Kowlessar
ab245de68d Merge remote-tracking branch 'origin/main' into feature/historic-epc-repository 2026-07-04 11:19:59 +00:00
KhalimCK
6dfaecc352
Merge pull request #1460 from Hestia-Homes/fix/table-12a-underfloor-rows
Electric underfloor codes resolve their Table 12a Grid 1 rows (ADR-0050 deferred follow-up)
2026-07-03 17:53:59 +01:00
KhalimCK
adcdce4271
Merge pull request #1462 from Hestia-Homes/fix/floor-u-value-gate
Floor insulation gated on the derived floor U-value (ADR-0051 floor arm)
2026-07-03 17:53:34 +01:00
Khalim Conn-Kowlessar
2a0cf05fdd Amend ADR-0051: the floor U-value gate landed; roof stays evidence-gated 🟪
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 16:08:57 +00:00
Khalim Conn-Kowlessar
fb3516ee95 Floor insulation is withheld when it cannot lower the derived floor U 🟩
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 16:06:45 +00:00
Khalim Conn-Kowlessar
128981289a Floor insulation is withheld when it cannot lower the derived floor U 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 16:05:04 +00:00
Khalim Conn-Kowlessar
6c6be1a432 Electric underfloor codes resolve their SAP 10.2 Table 12a Grid 1 rows 🟩
421 slab -> Other storage heaters (0.00); 422/423 integrated -> 0.20;
424/425 screed/timber -> 0.90/0.50 — per the Grid 1 row labels that name
the codes (PDF p.184). Closes the None all-low-rate fallback that
over-credited 7-hour-metered underfloor dwellings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 16:00:40 +00:00
Khalim Conn-Kowlessar
f604672ae0 Electric underfloor codes resolve their SAP 10.2 Table 12a Grid 1 rows 🟥
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 15:57:37 +00:00
Daniel Roth
c0d36d1e67
Merge pull request #1458 from Hestia-Homes/feature/abri-api-integration
Abri DomnaRelay client — foundation + LogJob vertical (strictly typed)
2026-07-03 16:49:46 +01:00
Jun-te Kim
838668bfdb
Merge pull request #1451 from Hestia-Homes/fix/sap-heating-optional-air-conditioning
Make nested SapHeating.has_fixed_air_conditioning optional across all RdSAP schemas
2026-07-03 16:34:27 +01:00
Jun-te Kim
6172f5d30d
Merge pull request #1452 from Hestia-Homes/fix/roof-construction-another-premises-above
Map API roof_construction code 9 ("another premises above") to None
2026-07-03 16:34:15 +01:00
Daniel Roth
ff868156d1 Abri job logging trigger is named after the DomnaRelay route it fires 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:26:41 +00:00
Daniel Roth
a86607e420 Abri relay exchange reads as serialise, post, parse, then dispatch on the failure document 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:12:44 +00:00
Jun-te Kim
d769f4488a Suppress roof heat loss for API roof_construction codes 7 and 9
RdSAP 10 Specification (10-06-2025) §5.2.5: "If a dwelling or part of a
dwelling has commercial premises above record as another dwelling
above" — so code 9 ("another premises above") must get the same
zero-heat-loss treatment as code 7 ("another dwelling above"), per the
spec's own "there is no heat loss through the roof of a building part
that has the same dwelling above or another dwelling above."

The previous fix mapped both to None, which only avoided the mapper
crash — it didn't achieve zero heat loss. heat_transmission.py's
per-part suppression (part_roof_is_party) only fires when
roof_construction_type CONTAINS the literal string "another dwelling
above"; that string was only ever set on the Elmhurst ingestion path.
The API path mapped 7 (and, until now, 9) to None, so the suppression
never fired for any API-derived cert — has_exposed_roof (the
dwelling_type-label-only fallback) was the sole gate, with no way to
know about a per-part party-ceiling lodgement. Both codes now map to
"(another dwelling above)" so the existing suppression logic fires on
both ingestion paths, matching what
domain/epc/property_overrides/roof_party_ceiling_guard.py already does
independently for the landlord-override pipeline (same spec citation).

Verified against the motivating cert (uprn 10013320122): roof_w_per_k
now correctly computes to 0.0. Full RdSAP-21.0.1 corpus (1000 certs)
re-run: within-0.5 unchanged at 77.8%, MAE improves marginally
(0.6364 vs 0.637 ceiling) — no regression.
2026-07-03 14:44:01 +00:00
Daniel Roth
32b56050b0 Abri relay requests share one envelope, transport and rejection path across request types 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:37:40 +00:00
Daniel Roth
c23c74be6b Abri log_job treats a logged-job response missing its job number as retriable 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:36:55 +00:00
Daniel Roth
c1c2a1947e Abri log_job treats a logged-job response missing its job number as retriable 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:35:35 +00:00
Daniel Roth
62ad53df5a Abri relay credentials and defaults hydrate from environment configuration 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:34:53 +00:00
Daniel Roth
39055bafed Abri relay credentials and defaults hydrate from environment configuration 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:34:27 +00:00
Daniel Roth
f5fefd4d93 Abri job descriptions identify the visit as a Domna condition survey 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:34:05 +00:00
Daniel Roth
caffa5c4e1 Abri job descriptions identify the visit as a Domna condition survey 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:33:40 +00:00
Daniel Roth
2c660bdb5d Abri unparseable confirmed time fails loudly rather than silently booking all day 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:33:12 +00:00
Daniel Roth
eccc03f9f2 Abri confirmed time at or after midday books the afternoon slot 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:32:49 +00:00
Daniel Roth
cd29cbb0b8 Abri confirmed time before midday books the morning slot 🟩
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 14:32:15 +00:00