refactor: move docs/sap-spec/ contents into domain/sap10_calculator/

Locality of reference — SAP-specific docs, specs, and runtime data
now live alongside the calculator that consumes them, mirroring the
prior packages→domain layout moves.

Move targets:

- Narrative MDs → domain/sap10_calculator/docs/
    NEXT_AGENT_PROMPT.md, HANDOVER_NEXT.md, SAP_CALCULATOR.md
- Spec PDFs → domain/sap10_calculator/docs/specs/
    RdSAP 10 Specification 10-06-2025.pdf
    PCDF_Spec_Rev-06b_12_May_2021.pdf
    sap-10-2-full-specification-2025-03-14.pdf
    sap-10-3-full-specification-2026-01-13.pdf
- PCDB runtime data → domain/sap10_calculator/tables/pcdb/data/
    pcdb10.dat (8.3MB) + 7× pcdb_table_*.jsonl (18MB total)

Path code rewrites (load-bearing):

- tables/pcdb/__init__.py: replaced parents[4]/'docs'/'sap-spec' with
  Path(__file__).resolve().parent/'data' for Table 105 JSONL loading.
- tables/pcdb/postcode_weather.py: same rebase for the pcdb10.dat path
  read by _postcode_climate_table().
- tables/pcdb/etl.py __main__: same rebase for the manual ETL invocation
  (source + output_dir both now point inside the package).
- tests/test_pcdb_etl.py: _PCDB_DAT_PATH now derives from
  parents[1]/'tables'/'pcdb'/'data' (was parents[3]/'docs'/'sap-spec').

Citation rewrites:

- 12 .py docstrings and 4 .md docs (ADRs + READMEs + narrative docs)
  had `docs/sap-spec/<file>` strings rewritten to their new locations.
- Two cases where the catch-all sed misfired (an ADR-0009 line about a
  PCDB extract; the pcdb __init__.py docstring about ETL output) were
  hand-corrected to point at tables/pcdb/data/ rather than docs/specs/.

docs/sap-spec/ is now empty (will be removed in a follow-up sweep or
left as a vestigial empty dir for future repurposing). ADRs 0009 and
0010 remain at docs/adr/ — they're part of the chronological
cross-cutting decision log, not calculator-specific narrative.

Verified:

- Calculator's 1e-4 production gate
  (test_api_001479_full_chain_sap_matches_worksheet_pdf_exactly) GREEN.
- Wider sweep (domain/sap10_calculator/ + domain/sap10_ml/): 1654
  passed / 20 failed — exact pre-move baseline. All 20 failures
  pre-existing (10 hand-built skeleton + 4 cohort chain + 6 cohort
  diff).
- Pyright net-zero on the 4 touched runtime/test files (0 errors)
  and unchanged on heat_transmission.py (13) / cert_to_inputs.py (35) /
  mapper.py (33).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-05-26 13:17:18 +00:00
parent 960130b000
commit a7b08a4e8f
32 changed files with 35 additions and 36 deletions

View file

@ -9,7 +9,7 @@ Seven open questions resolved through a `/grill-with-docs` session before Sessio
| # | Question | Decision |
|---|---|---|
| 0 | Domain placement | **Option B** — new term **Calculated SAP10 Performance**, parallel to Effective Performance (ML) and Lodged Performance (gov register). Effective Performance is **not** retired now; a future ADR may promote Calculated to its current role once parity is confirmed. Process named **SAP10 Calculation**. |
| 1 | PCDB heat-pump COP source for Session A | **Stub-seam.** Define `PcdbLookup` Protocol, ship `NoOpPcdbLookup` returning None, fall back to Table 4a. Session C bundles a CSV PCDB extract under `docs/sap-spec/` and implements the lookup. |
| 1 | PCDB heat-pump COP source for Session A | **Stub-seam.** Define `PcdbLookup` Protocol, ship `NoOpPcdbLookup` returning None, fall back to Table 4a. Session C bundles a CSV PCDB extract under `domain/sap10_calculator/tables/pcdb/data/` and implements the lookup. |
| 2 | MCS installation factors | **Boolean input on calculator inputs, default `False`.** Plumbing in Session A; no behaviour change until the input is populated. Slice 18f (separate, tracked in HANDOFF §7-D0) lifts `mcs_installed_heat_pump` from gov API → `EpcPropertyData.MainHeatingDetail` so calculator can apply the factor on the ~1.5% of HP certs that carry it. |
| 3 | Thermal bridging | **Global y factor** (the path SAP 10.3 specifies for RdSAP-driven assessments). Per-junction Table R2 sum requires junction-count inputs the cert doesn't carry — not available on the RdSAP-driven flow. |
| 4 | Living-area fraction default | **RdSAP 10 Table 27** — direct lookup from `habitable_rooms_count`. Unambiguous, one-line table. |
@ -20,7 +20,7 @@ Seven open questions resolved through a `/grill-with-docs` session before Sessio
## Additional findings from the grill that change Session A scope
- **SAP rating formula belongs to RdSAP, not SAP 10.3.** RdSAP §19 ("RdSAP10-specific SAP rating equations referred to as EER") defines the SAP-score equation used for RdSAP-driven assessments. SAP 10.3 §13 defines the rating for new-build assessments. The cert's `energy_rating_current` was computed by RdSAP §19, so parity validation must compute against RdSAP §19, not SAP 10.3 §13.
- **RdSAP 10 (June 2025) cross-references SAP 10.2 (March 2025) for heating-system identification (Appendix A).** RdSAP was published before SAP 10.3 (Jan 2026). Until BRE updates RdSAP to reference SAP 10.3, the calculator's heating-identification logic reads SAP 10.2 Appendix A while everything else reads SAP 10.3. Keep both PDFs in `docs/sap-spec/`.
- **RdSAP 10 (June 2025) cross-references SAP 10.2 (March 2025) for heating-system identification (Appendix A).** RdSAP was published before SAP 10.3 (Jan 2026). Until BRE updates RdSAP to reference SAP 10.3, the calculator's heating-identification logic reads SAP 10.2 Appendix A while everything else reads SAP 10.3. Keep both PDFs in `domain/sap10_calculator/docs/specs/`.
- **RdSAP Table 29 ("Heating and hot water parameters") is a 20+-entry defaulting table** that the `cascade_defaults.py` module needs to encode. Current scope of `rdsap_uvalues.py` is U-values only; Table 29 extends the cascade pattern to cylinder insulation, primary-pipework insulation, boiler interlock, emitter temperature, underfloor-heating routing, solar-panel parameters, heat-network defaults. Adds ~1-2 hrs to Session A (effective Session A.5 if not split).
- **MCS field exists in gov API** but is dropped by the current mapper. Slice 18f (lift `mcs_installed_heat_pump` into `EpcPropertyData`) is a prerequisite for the MCS-factor path. ~30 min slice; can ship before Session A or in parallel.
@ -35,7 +35,7 @@ These cannot be closed by another tree feature. They require executing the calcu
## Decision
Build a deterministic **`Sap10Calculator`** that reads `EpcPropertyData` and emits the same outputs the certificate's BRE-approved assessor software emits: `sap_score`, `co2_emissions`, `peui_raw`, `peui_ucl`, `space_heating_kwh`, `hot_water_kwh`. Target the SAP 10.3 specification (DESNZ/BRE, 13-01-2026) and the RdSAP 10 specification (BRE, 10-06-2025), both held in `docs/sap-spec/`.
Build a deterministic **`Sap10Calculator`** that reads `EpcPropertyData` and emits the same outputs the certificate's BRE-approved assessor software emits: `sap_score`, `co2_emissions`, `peui_raw`, `peui_ucl`, `space_heating_kwh`, `hot_water_kwh`. Target the SAP 10.3 specification (DESNZ/BRE, 13-01-2026) and the RdSAP 10 specification (BRE, 10-06-2025), both held in `domain/sap10_calculator/docs/specs/`.
The ML model is **not deprecated**. It is repurposed as a **residual learner** against `actual_sap calculator_sap` (and similar deltas for the other five targets). Residual distributions are much narrower than the raw target distributions (calculator is within ~1 SAP-point on 95% of typical certs, per the working hypothesis), so the ML residual head should fit the corrections with far fewer features and reach the MAE ≤ 0.5 target.
@ -150,4 +150,4 @@ Re-derivation work is bounded — a few hundred numbers across tables — and th
- A new top-level domain area `domain.sap10_calculator.*` is introduced; over Sessions B/C it absorbs `domain.sap10_ml.{envelope,demand,ecf,rdsap_uvalues,sap_efficiencies,ventilation}.py`. The ML transform stops shipping those as standalone features once the residual head takes over.
- The codebase carries two SAP outputs: cert-reported `sap_score` (ground truth at training time) and calculator-emitted `sap_score` (ground truth at inference time for any RdSAP cert input). The product layer chooses; for "score this hypothetical post-retrofit state", calculator wins.
- The deterministic calculator is **version-bound to SAP 10.3.** A future SAP 10.4 is a calculator MAJOR bump and an ADR. The ML residual head is SAP-version-agnostic only insofar as the residual distribution it learns stays stationary; in practice a spec bump retrains the residual head.
- Spec PDFs live in `docs/sap-spec/` (this repo). The repo now carries the canonical reference for what the calculator computes. License: SAP 10.3 © Crown copyright 2026; RdSAP 10 © BRE — both are public-interest references for SAP-compliant software, included for traceability.
- Spec PDFs live in `domain/sap10_calculator/docs/specs/` (this repo). The repo now carries the canonical reference for what the calculator computes. License: SAP 10.3 © Crown copyright 2026; RdSAP 10 © BRE — both are public-interest references for SAP-compliant software, included for traceability.

View file

@ -58,7 +58,7 @@ Each `domain/sap/worksheet/*.py` module must mirror the SAP 10.2 worksheet struc
- ADR-0009's "MAE ≤ 1.0 SAP-point on typical subset" success criterion is restated against the Validation Cohort (not the full corpus). The "typical subset" exclusions in ADR-0009 (sap_score ≤ 5, ≥ 100, multi-heating, conservatory, RIR) still apply on top of the cohort filter.
- The training parquet schema bumps when `inspection_date` is added — a non-breaking MINOR addition under [ADR-0008](0008-physics-as-feature.md)'s `Feature Schema Version` discipline.
- The handover document `docs/sap-spec/HANDOVER_SYSTEMATIC_REVIEW.md` is rewritten in lockstep: §3 (diagnosis), §4 (scope), §7 (state-A-vs-state-B framing deleted), §7b (findings re-framed), §10 (fixture strategy), and a new §2.5 listing the five prerequisites.
- The handover document `domain/sap10_calculator/docs/HANDOVER_SYSTEMATIC_REVIEW.md` is rewritten in lockstep: §3 (diagnosis), §4 (scope), §7 (state-A-vs-state-B framing deleted), §7b (findings re-framed), §10 (fixture strategy), and a new §2.5 listing the five prerequisites.
- Sessions A/B/C from ADR-0009 collapse into a single sequence: prerequisites land, then the section sweep runs against a clean probe with PCDB available.
## Considered alternatives

View file

@ -19,9 +19,9 @@ sap/
└── tables/ # Table U2 wind, Table 6 walls, Table 21 bridging, …
```
Spec references: `docs/sap-spec/sap-10-2-full-specification-2025-03-14.pdf` (SAP 10.2, the active target per ADR-0010), `docs/sap-spec/RdSAP 10 Specification 10-06-2025.pdf` (RdSAP cascade). Canonical worked example: `2026-05-19-17-18 RdSap10Worksheet.xlsx` at repo root — loaded by `_xlsx_loader.py`.
Spec references: `domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf` (SAP 10.2, the active target per ADR-0010), `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf` (RdSAP cascade). Canonical worked example: `2026-05-19-17-18 RdSap10Worksheet.xlsx` at repo root — loaded by `_xlsx_loader.py`.
**Validation contract.** Per `[[feedback-zero-error-strict]]` the 6 Elmhurst U985 fixtures are deterministic test vectors: every line ref of every output must pin against the U985 PDF at `abs=1e-4`. See `worksheet/tests/test_section_cascade_pins.py` (per-section line refs, 768 rating + 90 demand pins) and `test_e2e_elmhurst_sap_score.py::test_sap_result_pin` (top-level SapResult fields). Tolerances are never widened. **Current state: 930/930 pins green.** The public API + architecture overview lives in `docs/sap-spec/SAP_CALCULATOR.md`.
**Validation contract.** Per `[[feedback-zero-error-strict]]` the 6 Elmhurst U985 fixtures are deterministic test vectors: every line ref of every output must pin against the U985 PDF at `abs=1e-4`. See `worksheet/tests/test_section_cascade_pins.py` (per-section line refs, 768 rating + 90 demand pins) and `test_e2e_elmhurst_sap_score.py::test_sap_result_pin` (top-level SapResult fields). Tolerances are never widened. **Current state: 930/930 pins green.** The public API + architecture overview lives in `domain/sap10_calculator/docs/SAP_CALCULATOR.md`.
## Adding a new Elmhurst conformance fixture

View file

@ -131,7 +131,7 @@ expanding.
| File | Why |
|---|---|
| [`docs/sap-spec/SAP_CALCULATOR.md`](./SAP_CALCULATOR.md) | Module API + architecture (you're heading there) |
| [`domain/sap10_calculator/docs/SAP_CALCULATOR.md`](./SAP_CALCULATOR.md) | Module API + architecture (you're heading there) |
| [`domain/sap10_calculator/calculator.py`](../../domain/sap10_calculator/calculator.py) | `SapResult` fields you'll assert against |
| [`domain/sap10_calculator/rdsap/cert_to_inputs.py`](../../domain/sap10_calculator/rdsap/cert_to_inputs.py) | The 3 public entry points + the section helpers |
| [`domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py`](../../domain/sap10_calculator/worksheet/tests/_elmhurst_worksheet_000474.py) | A reference fixture — `build_epc()` shows the EpcPropertyData shape |

View file

@ -44,7 +44,7 @@ Layer 4: API mapper cascade SAP = worksheet SAP at 1e-4 (production goal)
The big breakthrough: implementing the RdSAP 10 §5 (12) spec rule
(`Floor infiltration (suspended timber ground floor only)` — page 29
of `docs/sap-spec/RdSAP 10 Specification 10-06-2025.pdf`) revealed a
of `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf`) revealed a
series of API-mapper coverage gaps that all needed fixing for the
spec rule's premise to be met. Each slice closed one gap:
@ -262,7 +262,7 @@ f863598d Slice 85: bulk-update cohort 000516 hand-built for Cat A diff parity
```
Earlier slice context (71-86 closed cohort Layer 2) is in the prior
handover at commit `86eff23f` (`docs/sap-spec/NEXT_AGENT_PROMPT.md`
handover at commit `86eff23f` (`domain/sap10_calculator/docs/NEXT_AGENT_PROMPT.md`
before this rewrite).
## First action

View file

@ -228,7 +228,7 @@ domain/sap10_calculator/
├── parser.py # PCDB row parsers
└── (other PCDB tables)
docs/sap-spec/
domain/sap10_calculator/docs/specs/
├── sap-10-2-full-specification-2025-03-14.pdf # SAP 10.2 spec
├── RdSAP 10 Specification 10-06-2025.pdf # RdSAP 10 spec
├── pcdb10.dat # PCDB raw data (Table 172 + others)
@ -370,6 +370,6 @@ RdSAP 10 (10-06-2025):
Table 12a (standing/off-peak) p.191
PCDB10:
Table 105 (gas/oil boilers) docs/sap-spec/pcdb_table_105_...
Table 172 (postcode-district weather) docs/sap-spec/pcdb10.dat
Table 105 (gas/oil boilers) domain/sap10_calculator/docs/specs/pcdb_table_105_...
Table 172 (postcode-district weather) domain/sap10_calculator/tables/pcdb/data/pcdb10.dat
```

View file

@ -15,7 +15,7 @@ Public surface:
- `parser.py`: per-table row parsers (Table 105 typed; raw walker for the
other 7 tables).
- `etl.py`: walks the multi-table `pcdb10.dat` source and writes one
newline-delimited JSON file per table under `docs/sap-spec/`.
newline-delimited JSON file per table under `domain/sap10_calculator/tables/pcdb/data/`.
Reference: BRE PCDB pcdb10.dat (April 2026 revision); SAP 10.2
specification (14-03-2025) Appendix D2.1.
@ -32,11 +32,9 @@ from domain.sap10_calculator.tables.pcdb.parser import GasOilBoilerRecord
__all__ = ["GasOilBoilerRecord", "gas_oil_boiler_record"]
_REPO_SAP_SPEC_DIR: Final[Path] = (
Path(__file__).resolve().parents[4] / "docs" / "sap-spec"
)
_PCDB_DATA_DIR: Final[Path] = Path(__file__).resolve().parent / "data"
_TABLE_105_JSONL: Final[Path] = (
_REPO_SAP_SPEC_DIR / "pcdb_table_105_gas_oil_boilers.jsonl"
_PCDB_DATA_DIR / "pcdb_table_105_gas_oil_boilers.jsonl"
)

View file

@ -75,8 +75,8 @@ def run_etl(*, source: Path, output_dir: Path) -> None:
if __name__ == "__main__": # pragma: no cover — manual ETL invocation
repo_root = Path(__file__).resolve().parents[4]
data_dir = Path(__file__).resolve().parent / "data"
run_etl(
source=repo_root / "docs" / "sap-spec" / "pcdb10.dat",
output_dir=repo_root / "docs" / "sap-spec",
source=data_dir / "pcdb10.dat",
output_dir=data_dir,
)

View file

@ -66,7 +66,7 @@ class GasOilBoilerRecord:
final_year_of_manufacture: Optional[int]
# SAP10.2 Appendix J Table 3b/3c — combi-loss fields per BRE PCDF Spec
# Rev 6b (12 May 2021), Gas and Oil Boiler Table, fields 48 / 51 / 52
# / 56 / 57 (see `docs/sap-spec/PCDF_Spec_Rev-06b_12_May_2021.pdf`
# / 56 / 57 (see `domain/sap10_calculator/docs/specs/PCDF_Spec_Rev-06b_12_May_2021.pdf`
# pp. 14-15). Populated only for boilers EN 13203-2 / OPS 26 tested;
# SAP-default boilers leave them all blank → `separate_dhw_tests=0`
# and (61)m falls back to Table 3a. Field 48 encodes the test

View file

@ -9,7 +9,7 @@ The "rating" cascade (SAP rating, EI rating) uses UK-average climate per
Appendix U; the "demand" cascade (EPC emissions, primary energy, fuel
cost) uses the postcode-specific climate from this table.
Reference: PCDB10 data file `docs/sap-spec/pcdb10.dat`.
Reference: PCDB10 data file `domain/sap10_calculator/tables/pcdb/data/pcdb10.dat`.
"""
from __future__ import annotations
@ -21,7 +21,7 @@ from typing import Final, Optional
_PCDB_DAT_PATH: Final[Path] = (
Path(__file__).resolve().parents[4] / "docs" / "sap-spec" / "pcdb10.dat"
Path(__file__).resolve().parent / "data" / "pcdb10.dat"
)
_TABLE_172_TAG: Final[str] = "$172"

View file

@ -1,6 +1,6 @@
"""SAP 10.2 Table 12a — high-rate fractions for off-peak tariffs.
Sourced verbatim from `docs/sap-spec/sap-10-2-full-specification-2025-
Sourced verbatim from `domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-
03-14.pdf`, page 191 (Table 12a). RdSAP10 §19.1 cross-references this
table from RdSAP10 §10a/§10b the table is not duplicated in the
RdSAP10 PDF.

View file

@ -1,6 +1,6 @@
"""RdSAP10 Table 32 — fuel prices, standing charges, PV export credit.
Sourced verbatim from `docs/sap-spec/RdSAP 10 Specification 10-06-2025.pdf`,
Sourced verbatim from `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf`,
page 95 (Table 32). RdSAP10 §19.1: SAP rating for RdSAP10 is calculated
using Table 32 prices (not Table 12) for §10a and §10b. The calculator
targets RdSAP10 cost per ADR-0010 amendment.

View file

@ -2,14 +2,14 @@
synthetic baseline dwelling.
**Provenance.** The SAP 10.2 worksheet template (pages 131148 of
docs/sap-spec/sap-10-2-full-specification-2025-03-14.pdf) is the canonical
domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf) is the canonical
calculation form every SAP implementation must mirror, using the item
reference numbers (1a), (4), (33), (39), (40), (91), (92), (93), (257),
(272), (286) etc. The PDF's form fields are non-functional, so this test
is **spec-formula-derived** each expected value is computed independently
from the worksheet formulas applied to the baseline inputs below, not from
BRE-published worked-example tables. BRE worked-example values were not
located in any of the three SAP-spec PDFs in docs/sap-spec/; if they
located in any of the three SAP-spec PDFs in domain/sap10_calculator/docs/specs/; if they
surface later, only the expected numbers need updating, not this file's
structure.

View file

@ -23,8 +23,9 @@ from domain.sap10_calculator.tables.pcdb.parser import (
)
_REPO_ROOT: Path = Path(__file__).resolve().parents[3]
_PCDB_DAT_PATH: Path = _REPO_ROOT / "docs" / "sap-spec" / "pcdb10.dat"
_PCDB_DAT_PATH: Path = (
Path(__file__).resolve().parents[1] / "tables" / "pcdb" / "data" / "pcdb10.dat"
)
# Verified by user against ncm-pcdb.org.uk: Baxi Heating Wm 20/3rs.

View file

@ -2,7 +2,7 @@
Locks the CO2 emission factors and primary energy factors against the
published SAP 10.2 specification at
`docs/sap-spec/sap-10-2-full-specification-2025-03-14.pdf`, page 189.
`domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf`, page 189.
The price column (`UNIT_PRICE_P_PER_KWH`) was already SAP 10.2-correct
when the calculator code was authored; the CO2 column was authored

View file

@ -3,7 +3,7 @@
Locks the `Tariff` enum, the `tariff_from_meter_type` cert resolver,
and the per-system / per-use high-rate-fraction lookups against the
published SAP10.2 specification at
`docs/sap-spec/sap-10-2-full-specification-2025-03-14.pdf`, page 191.
`domain/sap10_calculator/docs/specs/sap-10-2-full-specification-2025-03-14.pdf`, page 191.
RdSAP10 §19.1 cross-references Table 12a in SAP10.2 for off-peak
splitting the table itself is not duplicated in the RdSAP10 PDF.

View file

@ -2,7 +2,7 @@
Locks unit prices, standing charges, PV export credit, and the Table 12
note (a) standing-charge gating against the published RdSAP10
specification at `docs/sap-spec/RdSAP 10 Specification 10-06-2025.pdf`,
specification at `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf`,
page 95 (Table 32).
RdSAP10 §19.1: "The SAP rating for RdSAP 10 is to be calculated using

View file

@ -6,7 +6,7 @@ extension). Reuses the existing fixtures from the ML test pack so tests
match the shape `transform.py` already sees in production.
SAP 10.3 specification (13-01-2026), §1 reference at
docs/sap-spec/sap-10-3-full-specification-2026-01-13.pdf pages 11-12.
domain/sap10_calculator/docs/specs/sap-10-3-full-specification-2026-01-13.pdf pages 11-12.
"""
import json
@ -468,7 +468,7 @@ def test_all_rir_shapes_apply_section_1_2_45m_convention_uniformly(
) -> None:
"""RdSAP §3.9.2 wall-area formulas and §3.10 detailed measurements
are for §3 heat-loss U-value calculation, **not** §1 dimensions
confirmed at `docs/sap-spec/RdSAP 10 Specification 10-06-2025.pdf`
confirmed at `domain/sap10_calculator/docs/specs/RdSAP 10 Specification 10-06-2025.pdf`
pages 22-24. The §1 storey-height convention of 2.45 m from §3.9.1
extends uniformly to every RR shape: each contributes exactly
`floor_area` to TFA, `floor_area × 2.45` to volume, and +1 storey.

View file

@ -210,7 +210,7 @@ def test_u_wall_filled_cavity_england_age_band_e_returns_table6_value() -> None:
# Arrange — RdSAP 10 Table 6 (England) row "Filled cavity", age band E
# (1967-1975) -> 0.7 W/m^2K. The cert records this as the triple
# (wall_construction=4 cavity, wall_insulation_type=2 filled,
# wall_insulation_thickness="NI"). Spec: docs/sap-spec/rdsap-10-
# wall_insulation_thickness="NI"). Spec: domain/sap10_calculator/docs/specs/rdsap-10-
# specification-2025-06-10.pdf page 33.
# Act