mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
docs: handover for §6 Solar gains agent
Captures the §5 implementation pattern (slice-per-test/impl/commit,
ALL_FIXTURES e2e conformance, frozen Result dataclass, calculator.py
wiring) and the SAP10.2 / Table 6d gotchas that cost time during §5
(Z_solar vs Z_L columns, rooflight Z=1.0, existing modules untrusted).
Hard constraints documented for the next agent:
- 6-fixture conformance ≤5e-3 W on every line (do not loosen tests).
- Stop and ask the user after ~15 min of unsuccessful reconciliation
or before scanning more than ~50 lines of spec PDF.
- Don't touch the untracked `sap worksheets/` folder.
Surfaces the pre-grilling unknowns the §6 agent should propose
recommended answers for during `/grill-me`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
52a11f5e74
commit
29feee7869
1 changed files with 148 additions and 0 deletions
148
docs/sap-spec/HANDOVER_SECTION_6.md
Normal file
148
docs/sap-spec/HANDOVER_SECTION_6.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# Handover — SAP 10.2 §6 Solar gains
|
||||
|
||||
**For the agent picking up §6.** Read this BEFORE invoking `/grill-me`. Read all of it.
|
||||
|
||||
Owner: `khalim@domna.homes`. Branch: `ara-backend-design-prd`.
|
||||
|
||||
## 1. Mission
|
||||
|
||||
Implement §6 Solar gains (xlsx rows ~332-371) to **6-fixture worksheet conformance** with the same shape and tolerances we landed for §4 and §5.
|
||||
|
||||
Worksheet lines you must close:
|
||||
- **(74)** Z access factor (Table 6d, FIRST column — solar heating; NOT the lighting Z_L from §5)
|
||||
- **(75)-(82)** per-orientation solar gain per month (N, NE, E, SE, S, SW, W, NW)
|
||||
- **(83)** Total solar gains per month = Σ (75)..(82)
|
||||
- **(84)** Total gains = (73) internal + (83) solar
|
||||
|
||||
**Success criterion (non-negotiable):** all 6 Elmhurst U985 fixtures conform to **≤5e-3 W on every line**, every month. Tolerances in the parametrised tests must be tight — `abs=5e-3` or tighter. **Do not loosen tests to make them pass.** If a fixture won't close to that tolerance, pause and ask the user.
|
||||
|
||||
The user expects "0 error". Anything looser means we missed a Table/formula.
|
||||
|
||||
## 2. Codebase state (read first)
|
||||
|
||||
- **Existing §6 file:** [packages/domain/src/domain/sap/worksheet/solar_gains.py](../../packages/domain/src/domain/sap/worksheet/solar_gains.py) — already has `Orientation` enum + `surface_solar_flux_w_per_m2()` leaf + `window_solar_gain_w()` leaf. **DO NOT assume these are correct or complete.** §5 had the same "looks done" appearance and was a 4-of-8 stub. Audit before reusing.
|
||||
- **Existing §6 tests:** [test_solar_gains.py](../../packages/domain/src/domain/sap/worksheet/tests/test_solar_gains.py) — 7 leaf tests, **no per-fixture conformance**. Likely needs rewrite.
|
||||
- **Existing cert→inputs window mapping:** [cert_to_inputs._window_inputs](../../packages/domain/src/domain/sap/rdsap/cert_to_inputs.py) at L379 already aggregates windows with `pitch_deg=90`, `g_perpendicular` from Table 6b, `frame_factor` from Table 6c, `overshading_factor=0.77` hardcoded (AVERAGE solar Z). Reuse the table lookups.
|
||||
- **6 Elmhurst fixtures:** `_elmhurst_worksheet_NNNNNN.py` — each carries `LINE_4_TFA_M2`, `LINE_5_VOLUME_M3`, and worksheet outputs through §5. You will append:
|
||||
- `SECTION_6_WINDOW_AREAS_BY_ORIENTATION_M2: dict[Orientation, tuple[float, ...]]`
|
||||
- `SECTION_6_ROOFLIGHT_AREAS_M2: tuple[float, ...]` (or merge into the dict at `Orientation.HORIZONTAL`)
|
||||
- `LINE_74_Z_ACCESS_FACTOR: float = 0.77` (default AVERAGE)
|
||||
- `LINE_75_M_*` through `LINE_84_M_TOTAL_GAINS_W` 12-tuples
|
||||
- **Calculator wiring:** [calculator.py:_solar_gains_w](../../packages/domain/src/domain/sap/calculator.py) currently calls the leaf `_solar_gains_w` per-month inside `_solve_month`. After §6 rebuild, the orchestrator should produce a 12-tuple that `CalculatorInputs.solar_gains_monthly_w` carries forward (mirror the §5 wiring pattern).
|
||||
|
||||
## 3. Implementation pattern — mirror §5 EXACTLY
|
||||
|
||||
Follow the §4/§5 slice precedent. **One slice = one test → one impl → one commit.** Slice commit message format: `§6 slice N: <line_ref> <name> — <one-line summary>`.
|
||||
|
||||
Suggested slice ordering (build in this order, tracer first):
|
||||
|
||||
1. **Tracer:** simplest line. Probably `(74) z_access_factor` lookup from `OvershadingCategory` (mirror `_Z_L_BY_OVERSHADING` from internal_gains).
|
||||
2. **Per-orientation flux leaves:** revalidate existing `surface_solar_flux_w_per_m2` against one worksheet's (75)..(82) column values. If wrong, fix; if right, keep.
|
||||
3. **Per-window gain leaf:** `window_solar_gain_w` — formula `(75)m = A_w × S × g_⊥ × FF × Z`. Validate against worksheet to 4 d.p.
|
||||
4. **Sum per orientation per month:** Σ over windows of the same orientation.
|
||||
5. **(83) total solar gains:** Σ across orientations for each month.
|
||||
6. **`SolarGainsResult` frozen dataclass** holding 9 line tuples ((75)..(83)).
|
||||
7. **`solar_gains_from_cert` orchestrator** — signature mirrors `internal_gains_from_cert`:
|
||||
```python
|
||||
def solar_gains_from_cert(
|
||||
*,
|
||||
epc: EpcPropertyData,
|
||||
region: int,
|
||||
overshading: OvershadingCategory = OvershadingCategory.AVERAGE,
|
||||
rooflight_windows: tuple[RooflightInput, ...] = (), # if needed
|
||||
) -> SolarGainsResult: ...
|
||||
```
|
||||
8. **Extract LINE_74_*..LINE_84_* from all 6 U985 PDFs** via a `/tmp/extract_section6.py` script. Mirror the `/tmp/extract_section4.py` / `extract_section5.py` precedent.
|
||||
9. **Populate fixtures + add ALL_FIXTURES-parametrized e2e test** with abs=5e-3 tolerance.
|
||||
10. **Wire calculator.py** — add `solar_gains_monthly_w: tuple[float, ...]` to `CalculatorInputs`, replace per-month `_solar_gains_w` call with index lookup, update `cert_to_inputs` to call `solar_gains_from_cert`. Update synthetic test constructors.
|
||||
11. **Delete legacy `_solar_gains_w` from calculator.py** + any superseded leaf fns.
|
||||
12. **Update [SPEC_COVERAGE.md](SPEC_COVERAGE.md)** §6 row + add `## §6 — slice progress` table.
|
||||
|
||||
## 4. Lessons from §5 that the §6 agent must absorb
|
||||
|
||||
These cost time during §5. Don't repeat.
|
||||
|
||||
1. **Table 6d has THREE columns.** First = solar heating Z (0.77 AVERAGE), second = solar cooling Z (0.9 AVERAGE), third = lighting Z_L (0.83 AVERAGE). For §6 you use the FIRST column. (§5 used the third — different value.)
|
||||
2. **Table 6d note 2: rooflights use Z = 1.0** regardless of overshading bucket. Handle this in your orchestrator; do not assume a single Z applies to every window. 000516 has a 1.18 m² rooflight.
|
||||
3. **Worksheet rounds intermediate values at 4 d.p.** When your formula gives `52.0001` and the worksheet shows `52.0000`, that's worksheet display rounding — your code is right.
|
||||
4. **Existing modules are not trusted.** §5's was labelled "Full" in SPEC_COVERAGE but was 4-of-8 lines + a broken lighting fallback. Audit the existing `solar_gains.py` against worksheet (75)..(83) for 000490 before reusing it.
|
||||
5. **`cert_to_inputs._window_inputs` hardcodes Z_solar=0.77 (AVERAGE).** Replace with the overshading enum in the orchestrator. The hardcode is a known shortcut.
|
||||
|
||||
## 5. **CRITICAL: stop and ask, do not scan**
|
||||
|
||||
§5 wasted ~30-60 min scanning the SAP10.2 spec PDF looking for the missing reconciliation factor on lighting. The factor was a single table column the agent kept missing.
|
||||
|
||||
**Hard rule for §6:** if a formula or table lookup doesn't reconcile within **15 minutes** of normal debugging, **STOP** and ask the user (`AskUserQuestion`) for a worked example or pointer to the right table/page. The user has the worksheets, the spec PDFs, and domain knowledge. Asking is cheap; rabbit-holing is expensive.
|
||||
|
||||
**Specific triggers to ask the user, not scan:**
|
||||
- A formula gives the wrong value and you've checked the obvious inputs.
|
||||
- You're not sure which Table or column applies (e.g. "Z for solar heating vs cooling vs lighting").
|
||||
- A cert field's value doesn't map cleanly to a SAP code.
|
||||
- You're considering reading more than ~50 lines of spec text.
|
||||
|
||||
Format the question with the data: "I'm getting X, worksheet says Y, for fixture 000NNN. The formula I'm using is Z. Which table/factor am I missing?"
|
||||
|
||||
## 6. Pre-grilling — unknowns the agent must surface
|
||||
|
||||
The user will run `/grill-me` before implementation. The grilling session should tease out at least these branches; the agent should propose recommended answers for each:
|
||||
|
||||
1. **Scope of rebuild.** Audit existing `solar_gains.py` against 000490 worksheet (75)..(83). If it's exact, extend with orchestrator + fixture wiring. If it's wrong, rebuild like §5. (Likely rebuild; see lesson 4.)
|
||||
2. **Orchestrator API shape.** Mirror §5 — takes `epc`, `region`, `overshading`, optional rooflight metadata. Returns `SolarGainsResult` (frozen dataclass with 9 line tuples).
|
||||
3. **Multi-orientation handling.** Worksheet shows (75)..(82) per cardinal+inter-cardinal direction. How does the orchestrator group/aggregate windows? Likely via `Orientation` enum already in solar_gains.py.
|
||||
4. **Region / Table U3 solar flux.** Each fixture has a region code (000490 = East Pennines). Confirm `region` field exists on EPC and feeds into surface_solar_flux_w_per_m2.
|
||||
5. **Rooflight handling.** 000516 has a rooflight. Per Table 6d note 2: Z=1.0 + horizontal pitch → Table U3 horizontal flux. Decide: separate `rooflight_windows` arg or `pitch_deg=0` in the SapWindow.
|
||||
6. **`cert_to_inputs._window_inputs` reconciliation.** That helper already maps SapWindow → WindowInput at pitch_deg=90 with Z=0.77. Decide: reuse + extend, or replace.
|
||||
7. **Calculator.py wiring scope.** Same as §5: add `solar_gains_monthly_w` 12-tuple to `CalculatorInputs`, replace inline `_solar_gains_w` call.
|
||||
8. **E2e SAP-score test impact.** Wiring §6 may shift 000490 / 000474 SAP scores. Decide whether to update expected values or pin to current.
|
||||
9. **Column A vs Column B (Table 5 analogue).** §6 uses the solar HEATING column always for rating + DPER/TPER. The COOLING column (second) only applies to cooling-load calcs, which we're not doing. Document explicitly.
|
||||
10. **Tolerance.** 5e-3 W on every line. Do not propose looser.
|
||||
|
||||
## 7. References — bounded, do not browse
|
||||
|
||||
Use these. **Do not scan more than ~50 lines per reference without checking with the user first.**
|
||||
|
||||
- **SAP 10.2 spec PDF**: [docs/sap-spec/sap-10-2-full-specification-2025-03-14.pdf](sap-10-2-full-specification-2025-03-14.pdf). §6 prose is around page 12-15; Table 6b/6c/6d around page 178-181; Appendix U (solar flux Tables U3-U5) is the climate-data section.
|
||||
- **RdSAP 10 spec PDF**: [rdsap-10-specification-2025-06-10.pdf](rdsap-10-specification-2025-06-10.pdf). Window data lodging conventions.
|
||||
- **Canonical worked example xlsx**: [`2026-05-19-17-18 RdSap10Worksheet.xlsx`](../../2026-05-19-17-18%20RdSap10Worksheet.xlsx) at repo root. Cells in sheet `NonRegionalWeather` cover (74)..(84).
|
||||
- **6 Elmhurst U985 worksheets**: `/workspaces/model/sap worksheets/U985-0001-NNNNNN.pdf`. These are the ground truth for conformance.
|
||||
- **6 Elmhurst Summary PDFs**: `/workspaces/model/sap worksheets/Summary_NNNNNN.pdf`. Cert-input source.
|
||||
- **§5 implementation as exemplar**: `packages/domain/src/domain/sap/worksheet/internal_gains.py` + `test_internal_gains.py` + `_elmhurst_worksheet_*.py` (search for `SECTION_5_*` and `LINE_66_M`..`LINE_73_M`). **This is the pattern to clone.**
|
||||
- **§5 commit chain** (clone the shape): `git log --oneline --grep '§5 slice'` → 13 commits from `3ec56216` through `380115e2`.
|
||||
|
||||
## 8. Test commands
|
||||
|
||||
```bash
|
||||
# §6 alone
|
||||
python -m pytest packages/domain/src/domain/sap/worksheet/tests/test_solar_gains.py --no-header --no-cov
|
||||
|
||||
# Full SAP suite (regression check)
|
||||
python -m pytest packages/domain/src/domain/sap/ --no-header --no-cov -q
|
||||
|
||||
# Per-fixture error diagnostic (write a temp script like the §5 one I used)
|
||||
# Expect every line ≤5e-3 W on every fixture.
|
||||
```
|
||||
|
||||
## 9. Commit conventions
|
||||
|
||||
- Stage by name, never `git add -A` (user has untracked `sap worksheets/` PDFs that must not be committed).
|
||||
- AAA test convention (`# Arrange / # Act / # Assert` literal headers).
|
||||
- `Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>` trailer on every slice commit.
|
||||
- One slice = one commit. Commit messages: `§6 slice N: ...`.
|
||||
|
||||
## 10. When you finish
|
||||
|
||||
- 6 fixtures conforming end-to-end on §6 to ≤5e-3 W.
|
||||
- `solar_gains_from_cert` orchestrator wired into `cert_to_inputs` + `calculator.py`.
|
||||
- Legacy per-month `_solar_gains_w` deleted.
|
||||
- SPEC_COVERAGE.md §6 row flipped + slice progress table added.
|
||||
- E2e SAP-score tests still passing (may need to update expected values; flag this to the user before changing them).
|
||||
- A handover doc for §7 if you stop mid-flight.
|
||||
|
||||
## 11. Definitely do NOT
|
||||
|
||||
- **Do not** delete or modify the user's untracked `sap worksheets/` folder.
|
||||
- **Do not** loosen test tolerances to make tests pass. Ask the user.
|
||||
- **Do not** scan more than ~50 lines of a spec PDF before asking the user for the specific table/page.
|
||||
- **Do not** modify existing fixture `build_epc()` functions unless asked — they're pinned for §1-§5 conformance and the e2e SAP-score regression.
|
||||
- **Do not** assume the existing `solar_gains.py` is correct. Verify against the 000490 worksheet first.
|
||||
- **Do not** invoke `/ultrareview` yourself (user-triggered, billed).
|
||||
Loading…
Add table
Reference in a new issue