mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Captures issue status (#1153-#1161), the built compute spine, key facts/gotchas (hand-built 000490 fixture, calculator entry, worktree-vs-main import trap, test/commit conventions), and the two gates (parser fix -> wire Elmhurst cascade pins; #1157 persist-Plan HITL schema review). For picking the work back up in a fresh session. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.4 KiB
8.4 KiB
HANDOVER — Modelling stage rebuild
Branch: feature/bill-derivation (worktree /workspaces/home/hestia-worktrees/model-assemble-new-backend). HEAD: 4c104050.
PRD: GitHub Hestia-Homes/Model#1152, sliced into #1153–#1161.
Issue status
| Issue | What | State |
|---|---|---|
| #1153 | Overlay Applicator + EpcSimulation |
✅ closed (350f4c8e) |
| #1154 | Package Scorer | code done (7a478cff); Elmhurst cascade pin pending parser |
| #1155 | wall Recommendation Generator | ✅ closed (bb2c0068) |
| #1156 | score Options + attribution | ✅ closed (13dd5fe8) |
| #1157 | persist a Plan via ModellingOrchestrator |
not started — HITL (persistence-schema review) |
| #1158 | roof (loft) generator | generator done (3c87be8e); end-to-end + pin pending (#1157 + parser) |
| #1159 | floor generator | generator done (4c104050); end-to-end + pin pending |
| #1160 | Optimiser (knapsack + greedy repair) | not started (blocked by #1157/#1158/#1159) |
| #1161 | Measure Dependency (ventilation) | not started (blocked by #1160) |
Design (already recorded — read these)
- CONTEXT.md terms: Recommendation (a target surface; Recommendations partition the modifiable EPD surface so overlays never collide), Measure Option (bundle-capable; deduped by overlay), Simulation Overlay (
EpcSimulation), Product, Cost, Contingency, Measure Dependency. Targeting: building parts byBuildingPartIdentifier; windows by index; systems direct. - ADR-0016: the three scoring roles (per-Option signal → whole-package re-score → final-package marginal cascade attribution) + warm-start MILP → dependency injection → package re-score → greedy repair. Resolves ADR-0005 §14.
- Governing: ADR-0005 (multi-phase scenarios, per-phase recompute vs rolling Effective EPC), ADR-0011 (composable stage orchestrators), ADR-0012 (one Unit of Work per stage, commit once).
What's built
All in domain/modelling/, domain/building_geometry.py, repositories/product/, infrastructure/postgres/product_table.py. 25 tests green, pyright strict clean, purely additive.
simulation.py—EpcSimulation(building_parts: Mapping[BuildingPartIdentifier, BuildingPartOverlay]);BuildingPartOverlay(all-optional:wall_insulation_type,roof_insulation_thickness,floor_insulation_thickness).overlay_applicator.py—apply_simulations(baseline, simulations) -> EpcPropertyData. Generic field-fold (adding overlay fields needs NO change here — proven by roof/floor), sequential (later overlay wins), deep-copies (baseline never mutated), targets parts by identifier, writes thesap_*fields. Returns a throwaway EPD.recommendation.py—Recommendation(surface, options),MeasureOption(measure_type, description, overlay, cost),Cost(total, contingency_rate).product.py/contingencies.py—Product(measure_type, unit_cost_per_m2, contingency_rate); per-type contingency (cavity 0.10, loft 0.10, suspended floor 0.20, solid floor 0.26).package_scorer.py—PackageScorer(calculator: SapCalculator).score(baseline, simulations) -> Score(sap_continuous, co2_kg_per_yr, primary_energy_kwh_per_yr). The reusable scoring primitive (role 2).scoring.py—marginal_impacts(scorer, baseline, overlays) -> list[MeasureImpact](telescoping cascade, role 3);independent_option_impacts(scorer, baseline, options)(role 1, scores each distinct overlay once).MeasureImpact(sap_points, co2_savings_kg_per_yr, energy_savings_kwh_per_yr).wall_recommendation.py—recommend_cavity_wall(epc, products): detect cavity (wall_construction==4) + uninsulated (wall_insulation_type==4) → overlay setswall_insulation_type=2(Table 6 "Filled cavity").roof_recommendation.py—recommend_loft_insulation(epc, products): detectroof_insulation_thickness==0→ overlayroof_insulation_thickness=270.floor_recommendation.py—recommend_floor_insulation(epc, products): detect uninsulated ground floor + construction (floor_construction_type"Suspended"/"Solid") → overlayfloor_insulation_thickness=100.building_geometry.py—gross_heat_loss_wall_area,roof_area,ground_floor_area(per part, by identifier; party walls excluded; areas are heat-loss/§3.8 quantities, not totals).repositories/product/—ProductRepository(ABC port,get(measure_type)->Product);ProductPostgresRepositoryreads the externally-ownedmaterialtable (defensive SQLModel viewMaterialRow;total_cost → unit_cost_per_m2; joins contingency). AProductJsonRepository(file source, for ETL-gap costs) is intended behind the same port — the one remaining parser-independent AFK task.
Key facts / gotchas
- Hand-built baseline fixture (no PDF):
tests/domain/sap10_calculator/worksheet/_elmhurst_worksheet_000490.build_epc(). Its MAIN is an uninsulated cavity wall + uninsulated suspended ground floor + 300 mm (insulated) loft. Used as the baseline in every generator/scorer test. MAIN gross heat-loss wall area = 45.93 m², roof area = 14.85 m², ground floor = 14.85 m². - Calculator entry:
Sap10Calculator().calculate(epc) -> SapResult(sap_score_continuous,co2_kg_per_yr,primary_energy_kwh_per_yr). Depend on theSapCalculatorabstraction. Filled-cavity wall code = 2 (domain/sap10_ml/rdsap_uvalues.py::u_wall). Calculator reads wall/roof/floor fromSapBuildingPartstructured fields, NOTEnergyElementdescriptions (those are detection-only). - Worktree vs main import trap:
python /tmp/foo.pyimports the repo from/workspaces/model(editable install), NOT this worktree. Run withPYTHONPATH=<worktree>or viapytest(rootdir handles it).pytestalready uses worktree code. - Running tests:
python -m pytest <path> -q. Do NOT pass-p no:cov(pytest.ini injects--covargs that then error). DB repo tests spin up ephemeral Postgres via thedb_enginefixture (tests/conftest.py) — slower; SQLModel tables auto-register on import. - Conventions: commit per TDD slice; conventional-commit message ending
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>; stay onfeature/bill-derivation(user's choice). Tests use literal# Arrange / # Act / # Assert; assert withabs(x - y) <= tol(notpytest.approx); pyright strict, zero errors; annotate call-return locals.
The two gates
- Parser fix (in flight —
feature/per-cert-mapper-validationagent → main). Once cert 001431 parses, wire the Elmhurst before/after cascade pins:- Files (main checkout):
/workspaces/model/sap worksheets/Recommendations Elmhurst Files/<measure>/{before,after}/Summary_*.pdf—cavity_wall_insulation - main wall(001431),loft_insulation - main building,solid_floor/suspended_floor - main building, etc. - Pipeline:
from backend.documents_parser.parser import parse_site_notes_pdf; epd = parse_site_notes_pdf(path). - Pin: parse
before→ apply the measure's overlay (the matchingrecommend_*Option'soverlay) →PackageScorer.score→ compare toafter(either theafterworksheet's SAP/kWh/carbon, orSap10Calculator().calculate(parse(after)))./tmp/spike_diff.pydiffs before/after EPDs to derive/validate the overlay empirically. - The parser bug being fixed:
_extract_windowsreadslocation/orientation/data_sourceas single tokens, but 001431 lodges multi-token values ("External wall", "North West") with a blank Glazing Gap, so'Manufacturer'lands on theu_valuefloat. (ec9ef0e8fixed a different symptom.) - Closing these → #1154 done; #1158/#1159 end-to-end once #1157 exists.
- Files (main checkout):
- #1157 persist a Plan (HITL). Design-review the Plan / Plan Phase / Recommendation persistence schema +
ScenarioRepositorymethod shapes, then buildModellingOrchestrator.run(property_ids, scenario_ids)per ADR-0011/0012 (one UoW, commit once, thread only IDs, read via repos). Template:orchestration/property_baseline_orchestrator.py. Then roof/floor end-to-end + #1160 optimiser + #1161 ventilation dependency.
Relevant memories (auto-loaded)
project_openos_conservation_data_gap— EWI eligibility needs listed/conservation status, not ingested; blocks the solid-wall EWI slice (later), NOT the fabric tracers.project_calculator_geometry_extraction— the calculator holds reusable geometry;building_geometry.pyis the start; DRY the calculator onto it later (coordinate with the calculator branch); don't editheat_transmission.pynow.