The neighbour SAP-divergence cohort was (postcode, property_type, built_form),
which mixed electric- and gas-heated dwellings. Electricity scores materially
lower in SAP than mains gas for identical fabric, so a mixed-fuel postcode
produced cross-fuel false outliers — an electric dwelling flagged only for being
electric among gas neighbours.
Add the main-heating fuel class to the cohort key (electricity variants — 29/30
+ off-peak 31-40 — collapse to one class; every other code is its own bucket).
Now every flagged divergence is a same-fuel comparison worth investigating. On
portfolio 814 this refined 20 raw divergences to 17 same-fuel ones while keeping
the genuine within-fuel outliers (e.g. the all-electric WC2B 4AW flats at SAP
26/38 vs a cohort median of 67).
Reaches the fuel via epc_main_heating_detail through the indexed property_id —
still never touches the recommendation table. Surfaced by the portfolio-814
recommendation audit (Work item B, #1388).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-time script (dry-run default, --apply in a transaction, idempotent) reusing the
live glazing_mix_guard so the backfill and classifier cannot drift. Maps every
genuine percentage-mix override (neither type >= 90%) off a flattened single type
onto MIXED (no overlay -> the cert's per-window glazing is kept). property_overrides
TEXT updated now; the glazing pgEnum cache write for 'Mixed glazing' is deferred
until the FE adds the member. Dry-run against the audited DB reports 647 rows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A focused sibling to audit-ara-portfolio: that skill audits baselines/plans/SAP;
this one audits the *recommendations themselves* — why a measure was or wasn't
offered. Motivated by the portfolio-814 review (Khalim's HHRSH-on-community-
heating, missing-HHRSH, missing-secondary-heating-removal, and a neighbour split).
Adds:
- .claude/skills/find-weird-recommendations/SKILL.md — scan -> neighbour scan ->
live re-model deep-dive -> root-cause -> codify, with a seeded known-bug
catalogue and the query-safety rules inherited from audit-ara-portfolio.
- scripts/audit/anomalies.py: new `plan-stops-short-of-goal` HIGH check — the
default plan ends below the goal band on an unlimited-budget scenario (the
deterministic worklist for "why didn't this get recommended X"). Adds
scenario_budget to the bundle/query so budget-capped scenarios are excluded.
- scripts/audit/neighbour_divergence.py: groups a portfolio by (postcode,
property_type, built_form) and flags effective-SAP outliers vs the cohort
median. Never touches the 26m-row recommendation table, so it is safe
portfolio-wide.
- Tests for both (12 passing).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-time script (dry-run default, --apply in a transaction, idempotent) mapping a
flat roof's stated depth (flat: Nmm) onto the nearest Table 16 rung member so the
overlay scores it by thickness, not the age-band default. Updates property_overrides
(TEXT) immediately; the roof_type pgEnum cache writes are deferred until the FE adds
the FLAT_*MM members. Dry-run against the audited DB reports 102 rows, deferring
Flat 50/100/150 mm.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-time script (dry-run default, --apply in a transaction, idempotent) that maps
every party-ceiling override (another/same dwelling or premises above) currently
stored as an external roof value onto the party-ceiling member, reusing the live
roof_party_ceiling_guard so the backfill and the classifier cannot drift. Updates
property_overrides (TEXT, the modelling read path) and the roof classifier cache;
dry-run against the audited DB reports 106 rows. No FE migration — the party-
ceiling values already exist in the roof pgEnum.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
One-time script (dry-run default, --apply in a transaction, idempotent) that
NULLs the four lodged_* columns on every predicted-source baseline row — a
predicted EPC exists and no lodged one does — leaving Effective, the bill block,
and rebaseline_reason intact. Dry-run against the audited DB reports 12,236 rows.
Must run after the FE-owned Drizzle ALTER ... DROP NOT NULL lands.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deterministic catalogue remap (no LLM) that re-resolves the stored archetype for
the canonical RdSAP heating descriptions the under-populated taxonomy
mis-classified. Dry-run by default; --apply writes inside a transaction;
idempotent. Updates property_overrides.override_value (TEXT — what the modelling
reads, the actual band-G fix: 3,028 rows incl. 2,210 community boilers + 770
panel/convector room heaters). Cache (the Drizzle-owned main_heating_system
pgEnum) updates are deferred per-value until the FE-repo enum migration adds the
new archetypes — surfaced explicitly by the script.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>