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>
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>
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>
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>
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>
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>
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>
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>
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>
The heat-network mirror of keep_existing_off_peak_meter: the override's
2313 default applies only when the replaced system's control is
cross-family (stale); an observed community control survives.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per-axis choice (decided with Khalim): the kit axis concedes to the
evidence (community schemes skew modern; lodged community certs are
overwhelmingly thermostatted, none lodge 2301), the charging axis stays
flat-rate — usage-linked billing is scheme paperwork an override
cannot see.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>