Model/packages
Khalim Conn-Kowlessar 015144361a Slice 25a: 000487 §3 full closure — RR detailed surfaces + gable_wall_external + roof-area-as-max + half-up rounding
§3 cascade pins now close at abs=1e-4 for all 6 fixtures (was 5 of 6 with
000487 the holdout). Five spec-grounded changes:

1. SapRoomInRoofSurface gains optional `u_value` override + new kind
   `gable_wall_external` per RdSAP10 Table 4 (p.22) row 1 (exposed gable,
   U "as common wall" with assessor-lodged override). Routes to (29a)
   walls + LINE_31 external area.

2. SapAlternativeWall gains optional `u_value` override — assessor-lodged
   measured U bypasses the Table 6 cascade. 000487 Ext1 has a 9-mm
   TimberWallOneLayer at U=1.90 outside the Table 6 buckets.

3. _part_geometry uses MAX of floor areas (not top) for roof area, per
   RdSAP10 §3.8 (p.20): "Roof area is the greatest of the floor areas
   on each level". Fixes 000487 Ext1 where ground=7.13 m² > first=5.63.

4. Replace Python `round()` (banker's) with `_round_half_up` for §15
   element-area rounding. Banker's rounds 17.125 → 17.12; SAP convention
   rounds half-up → 17.13. Boundary case appears in 000487 Ext1 party
   wall area (party_length 6.25 × height 2.74 = 17.125).

5. 000487 fixture lodges 5 detailed RR surfaces (party gable, external
   gable @ U=0.86, flat ceiling, stud wall, slope), roof_insulation_
   thickness=300 (both parts → U=0.14), is_exposed_floor=True on Ext1
   floor 0, and u_value=1.90 on the Ext1 alt wall.

§3 cascade per-fixture:
  field    | 474 | 477 | 480 | 487 | 490 | 516
  LINE_31  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓
  LINE_33  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓
  LINE_36  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓
  LINE_37  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓  |  ✓

Scoreboard:
  section_cascade_pins: 274 → 279 PASS (+5: §3 +4 for 000487, §7 +1
    cascade)
  e2e SapResult:         32 →  32 PASS (unchanged — downstream §8-§12
    pins not yet asserted)

§4 (000487) deferred to slice 25b — needs has_electric_shower routing
through the §4 cascade so Nbath uses the "0.13N+0.19" branch when only
electric showers are present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 22:32:41 +00:00
..
domain Slice 25a: 000487 §3 full closure — RR detailed surfaces + gable_wall_external + roof-area-as-max + half-up rounding 2026-05-23 22:32:41 +00:00
fetchers added potential file scaffolding: 2026-05-15 10:56:53 +00:00
repos added potential file scaffolding: 2026-05-15 10:56:53 +00:00
utils added potential file scaffolding: 2026-05-15 10:56:53 +00:00
README.md added potential file scaffolding: 2026-05-15 10:56:53 +00:00

Shared packages

Workspace packages consumed by services/*. Each package is its own Python distribution with its own pyproject.toml; services import via the workspace dependency mechanism ({ workspace = true }).

Package Purpose
domain/ Shared domain types — Property, BaselinePerformance, Plan, Scenario, EpcPropertyData, etc. No persistence, no IO, no business logic.
repos/ Persistence layer — one repo per aggregate. Owns the SQL. Depends on domain.
fetchers/ External API clients (gov EPC, Ofgem, Google Solar, etc.). Depend on domain for response shapes.
utils/ Cross-cutting infra — logging, S3, CloudWatch URL builders, SQS task helpers.

Adding a new shared package

Only when a real second consumer materialises. Don't pre-shatter (repos-epc, repos-property, ...) — split when a deployment needs to drop a dep, not before.

See ../ara_backend_design.md §11 for the broader monorepo layout and ../CONTEXT.md for the domain glossary that names the types living in domain/.