Model/backend/documents_parser
Khalim Conn-Kowlessar 7f099d986a Slice S0380.13: widen cantilever gate to accept "House" descriptive form
Closes cert 2636 to spec floor (Δ +0.5167 → +0.0323) by accepting
both the EPC schema enum-as-string ("0") AND the Elmhurst Summary
mapper's descriptive form ("House") for the cantilever-detection
property-type gate at `heat_transmission.py:768`.

Root cause: slice 102f-prep.9 (commit 06b4ef3d) added cantilever
detection gated on `epc.property_type == _PROPERTY_TYPE_HOUSE` where
`_PROPERTY_TYPE_HOUSE = "0"`. That matches the API mapper's encoding
(schema enum), but the Summary mapper produces "House" (descriptive)
and the hand-built worksheet fixtures also use "House" — so neither
triggers the gate and the cantilever path silently no-ops on the
Summary path. Cert 2636's worksheet (28b) "Exposed floor Main 3.74
× 1.20 = 4.4880" is the cantilever — without surfacing it the
cascade missed 4.488 W/K of floor heat loss.

Three-encoding origins:
- API mapper:        property_type='0'      (schema enum-as-string)
- Summary mapper:    property_type='House'  (descriptive from §1)
- Hand-built fixtures: property_type='House' (legacy convention)

Fix: replace the equality check with a `_is_house()` helper that
accepts the {"0", "House"} frozenset. Centralised so future
property-type sensitive gates can call the same helper.

Forcing function: cert 2636 first-attempt Summary SAP closes from
Δ +0.5167 (after S0380.12 walls fix) to Δ **+0.0323** — within the
±0.07 ASHP-cohort spec floor. `floor_w_per_k` moves from 19.1982
(ground floor only) to 23.6862 (ground 19.20 + cantilever 4.49 =
worksheet (28a) + (28b) exact match).

Cohort closure status (6 of 7 ASHP certs at spec floor):
  cert  Δ vs worksheet  spec floor?
  0380  +0.0594         ✓
  0350  +0.0458         ✓
  2225  +0.0441         ✓
  2636  +0.0323         ✓  ← this slice
  3800  +0.0442         ✓
  9285  +0.0502         ✓
  9418  +2.5973         ✗  (Daikin EDLQ05CAV3 — final cert)

Boiler hand-built parity verified intact: 5 hand-built cohort certs
(000474, 000477, 000480, 000490, 000516) all use property_type=
"House" and now also fire the cantilever gate, but none have
floor1_area > floor0_area + 1m² (the cantilever-area trigger) so
their cascade output is unchanged. Regression suite 683 pass + 10
fail (= handover baseline 669 + 10 + 17 new GREEN tests across
S0380.2..S0380.13).

Pyright net-zero on edited files:
  domain/sap10_calculator/worksheet/heat_transmission.py: 13
    (baseline; no new errors)
  backend/documents_parser/tests/test_summary_pdf_mapper_chain.py: 0

Spec / precedent refs:
- Slice 102f-prep.9 (commit 06b4ef3d) — RdSAP cantilever-exposed-
  floor detection (originally API-only via `property_type=="0"` gate).
- SAP 10.2 Table 20 — U_exposed_floor (age D + no insulation →
  1.20 W/m²K, the cohort's cantilever U-value).
- Cert 2636 worksheet `dr87-0001-000898.pdf` line refs (28a)+(28b)
  sum 23.6862 W/K (exact cascade match after this slice).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 21:47:56 +00:00
..
handler address JTK review comments 2026-04-20 15:11:17 +00:00
tests Slice S0380.13: widen cantilever gate to accept "House" descriptive form 2026-05-27 21:47:56 +00:00
__init__.py Map to RdSapSiteNotes from site notes JSON 🟥 2026-04-16 13:54:03 +00:00
db_writer.py include updating epc_property_data to pashub to ara workflow 2026-04-29 09:55:14 +00:00
elmhurst_extractor.py Slice S0380.12: parse 'Alternative wall' window-location in pre-data slice 2026-05-27 21:27:47 +00:00
extractor.py Handle wall thickness "Unmeasurable" 🟩 2026-04-30 16:41:16 +00:00
local_runner.py update local runner to work for elmhurst 2026-04-24 14:01:36 +00:00
parser.py load ecmk site notes to db 2026-04-29 11:20:47 +00:00
pdf.py update local runner to work for elmhurst 2026-04-24 14:01:36 +00:00