dev deploys have failed since PR #1231 (2026-06-15) with
InvalidParameterValueException: Unzipped size must be smaller than
262144000 bytes. The lambda zip is built from the whole repo root
(deployment/terraform/modules/lambda_with_api_gateway) and only
excluded tests/deployment/pycache — so .git (206MB packed), the
per-schema corpus.jsonl fixtures under backend/epc_api/json_samples/
(~50MB), and the sap10_calculator PDF specs (~13MB) were all being
zipped up even though none of them are read at runtime.
Also add scripts/check_lambda_zip_size.py, which mirrors the
terraform module's pip install + zip-with-excludes behaviour to
report the projected unzipped size without mutating the repo, and
wire it into deploy_terraform.yml as a fast_api_lambda_zip_size_check
job that gates the real deploy so this fails fast with a clear
message instead of surfacing as an opaque Terraform/AWS error.
Co-Authored-By: Claude Sonnet 5 <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>
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.