Model/backend/documents_parser/tests
Khalim Conn-Kowlessar c60a2ddc17 Slice S0380.139: route _is_off_peak_meter through tariff_from_meter_type canonical dispatch (bare '18 Hour' lodging)
Pre-slice `_is_off_peak_meter` carried its own string-dispatch that
only recognised the RdSAP 10 long form `"off-peak 18 hour"`. The bare
`"18 Hour"` lodging (Elmhurst Summary §14.2 surface form, lodged by
41/41 corpus variants) fell into the catch-all `return False` branch.
That mis-classified every 18-hour cert as non-off-peak for the
secondary / PV cost paths and billed electric secondary heating at
standard 13.19 p/kWh (Table 32 code 30) instead of the 18-hour low
rate 7.41 p/kWh (Table 32 code 40).

The fix routes `_is_off_peak_meter` through `tariff_from_meter_type`
so every lodging form already recognised there (int 1/4/5, `"18 Hour"`,
`"off-peak 18 hour"`, `"Dual"`, `"Dual (24 hour)"`, numeric strings)
is consistently classified. Single (code 2) stays standard; Unknown
(code 3) retains the heuristic "electric end-uses on Unknown meters
typically come from E7-eligible dwellings whose tariff the assessor
couldn't pin down — apply off-peak". Per
[[feedback-zero-error-strict]] the now-dead `_RDSAP_DEFINITELY_OFF_PEAK`
frozenset is deleted (canonical dispatch covers the same codes).

Spec citation per [[feedback-spec-citation-in-commits]]:

> RdSAP 10 §17 page 85 row 10-2 (Electricity meter): "Dual / single /
>  10-hour / 18-hour / 24-hour / unknown"
> RdSAP 10 §12 page 62: "if the meter is dual 18-hour/24-hour it is
>  18-hour/24-hour tariff"

Corpus impact (6 storage-heater / underfloor variants on forced
secondary):

| variant | SAP code | old ΔSAP | new ΔSAP |
|---|---:|---:|---:|
| electric 3 | 401 | -0.10 | +2.42 |
| electric 5 | 402 | -2.48 | -0.06 |
| electric 6 | 404 | -1.14 | +1.19 |
| electric 7 | 408 | -1.08 | +1.14 |
| electric 8 | 409 | -2.54 | -0.41 |
| electric 9 | 421 | -2.76 | -0.24 |

Total absolute SAP residual across the cluster: 10.10 → 5.46.

The 3 sign-flipped variants (electric 3/6/7) surface a separate
cascade bug: `_secondary_heating_fraction_for_category` defaults to
0.10 when the mapper leaves `main_heating_category=None` for electric
storage, but the worksheet for codes 401/402 uses 0.15 = Table 11
Cat 7. Mapper-side fix queued.

Tests:
- new AAA test `test_is_off_peak_meter_recognises_bare_18_hour_lodging`
  covers 7 lodging forms (bare, lowercase, long-form, Single, standard,
  Unknown+electric, Unknown+non-electric)
- 6 corpus pins re-tightened (electric 3/5/6/7/8/9)

Extended handover suite: 882 pass (was 881; +1 new test), 0 fail.
Pyright net-zero on touched files (43 → 43 errors, all pre-existing).

Per [[reference-unmapped-sap-code]] strict-dispatch routing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 16:28:48 +00:00
..
fixtures Slice S0380.52: cert 000565 Elmhurst-only mapper-driven cascade pin + glazing-label coverage 2026-06-01 16:28:47 +00:00
__init__.py Map to RdSapSiteNotes from site notes JSON 🟥 2026-04-16 13:54:03 +00:00
test_elmhurst_end_to_end.py Slice S0380.17: map Elmhurst §11 glazing-type labels to SAP10 codes 2026-06-01 16:28:46 +00:00
test_elmhurst_extractor.py extract window frame details from elmhurst site notes 🟥 2026-04-27 15:50:25 +00:00
test_end_to_end.py P6.1 follow-on: unbox BuildingPartIdentifier at backend boundaries 2026-05-20 09:58:23 +00:00
test_extractor.py Handle wall thickness "Unmeasurable" 🟩 2026-04-30 16:41:16 +00:00
test_heating_systems_corpus.py Slice S0380.139: route _is_off_peak_meter through tariff_from_meter_type canonical dispatch (bare '18 Hour' lodging) 2026-06-01 16:28:48 +00:00
test_pdf.py rename example site notes to PasHub_ and add Elmhurst example 2026-04-24 13:01:51 +00:00
test_summary_pdf_mapper_chain.py Slice S0380.133: derive solid-fuel main fuel from §14.0 EES Code 2026-06-01 16:28:48 +00:00