Commit graph

6 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
f33bb9d52d feat(modelling): room-in-roof safety guard defers the roof generator
A room-in-roof carries its insulation on its own sloping/stud/gable surfaces
(RdSAP 10 §3.10, Table 17/18), which the roof overlay's flat
roof_insulation_thickness bump cannot model. Without a guard a RR with an
uninsulated loft fell through to the loft fallback and mis-recommended 300 mm
loft insulation. Return None when the main part lodges a sap_room_in_roof,
deferring until a dedicated RR branch lands (ADR-0021).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 10:06:08 +00:00
Khalim Conn-Kowlessar
13b18ce9fb feat(modelling): roof dispatcher insulates a flat roof
Slice 3 (ADR-0021). The dispatcher gains a flat-roof branch: a "flat"
roof_construction_type with no lodged thickness (uninsulated → None on the
Elmhurst path) gets a single flat_roof_insulation Option whose overlay raises
roof_insulation_thickness to 200 mm — tested before the loft fallback so a flat
roof's None doesn't trip the loft trigger. Pinned against the Elmhurst
before→after cert at 1e-4. Golden cohort roof firing unchanged (none across 57).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:16:00 +00:00
Khalim Conn-Kowlessar
7d40cddf3b feat(modelling): fold loft into the roof dispatcher; thatch routes to loft
Slice 2 (ADR-0021). `recommend_roof_insulation` now owns the loft branch as the
fallback — a plain pitched loft, a thatched roof (the covering doesn't block
insulating the loft floor), or an unlodged roof type all take loft (joist)
insulation at 300 mm when `roof_insulation_thickness == 0`. Sloping is tested
first; a no-access roof gets nothing. Retired the standalone
`recommend_loft_insulation`; the orchestrator and its tests now call the
dispatcher.

Pinned: thatch before→after (None→300) reproduces at 1e-4; the existing loft pin
still holds through the dispatcher. Behaviour-preserving on the golden cohort
(roof measure unchanged: none across all 57) — the dispatch is strictly more
precise (won't fire loft on a sloping/no-access roof).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:12:01 +00:00
Khalim Conn-Kowlessar
6484610b6c feat(modelling): recommend_roof_insulation insulates a sloping ceiling
Slice 1 of the roof-insulation generator (ADR-0021). New `recommend_roof_insulation`
dispatcher keys on the `roof_construction_type` string: a "sloping ceiling" roof
that is uninsulated (roof_insulation_thickness 0/None) gets a single
`sloping_ceiling_insulation` Option whose overlay raises roof_insulation_thickness
to 100 mm. Pinned against the Elmhurst before→after cert 001431 at 1e-4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 21:02:06 +00:00
Khalim Conn-Kowlessar
31da90f5eb feat(modelling): persist recommendation.material_id from the catalogue
Expand half of the recommendation_materials retirement (ADR-0017). A
Plan Measure installs a single Product, so thread its catalogue id end to
end — Product.id -> MeasureOption.material_id -> PlanMeasure.material_id
-> recommendation.material_id — replacing the per-material BOM child
table with one nullable column on the row. ProductPostgresRepository
reads the id from MaterialRow; the four fabric generators set it on their
Option; the orchestrator carries it onto the Plan Measure; the mirror
declares + maps the column. Optional throughout (the JSON stopgap
catalogue carries no ids -> NULL).

The multi-measure integration test now pins each persisted measure's
material_id to its seeded MaterialRow id. Migration spec (live column
must be added before this deploys; contraction is the owner's next step)
in docs/migrations/recommendation-material-id.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 08:26:58 +00:00
Khalim Conn-Kowlessar
84ec6da032 refactor(modelling): group domain/modelling into generators/scoring/optimisation
domain/modelling/ had grown to 15 flat modules. Group the behavioural ones into
subpackages — generators/ (wall/roof/floor Recommendation Generators), scoring/
(overlay applicator, package scorer, role-1/3 scoring), optimisation/ (optimiser
+ measure dependency) — and leave the shared value-object vocabulary
(recommendation, plan, scenario, product, contingencies, simulation) flat at the
top, since it is imported everywhere. Pure move + import-path rewrite across 89
import sites; no behaviour change. 136 pass, pyright strict clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:48:36 +00:00
Renamed from domain/modelling/roof_recommendation.py (Browse further)