homemaker-layout/tests/test_evolve_preflight.py
Claude f5612ce06c
Pre-flight: check the ground floor's frontage, not the per-storey average
39.16 relocated the crinkliness residual to a plan-form question. Four
measurements answer it, and two of them refute the premises 773 was filed on.

The search DOES build courtyards -- harbor 8 (273 m2), maple 16 (404 m2),
health-centre 13 (107 m2) over three seeds -- and they work: of 524 lit edges
44% come from the plot wall, 30% from a courtyard, 26% from a perimeter void,
and a courtyard supplies at least one side for 53% of the two-aspect leaves.
No operator is missing. (The shape-curve DP does NOT model exposure --
shapecurve.py:25 -- but per 38.24 it fires ~8 times in 500k evals, so that gap
is not what is costing anything.)

The answer is per-storey. Comparing each storey's demand, sum A_i/(1.6202*h),
with the lit wall its leaves actually hold: every harbor and maple ground floor
is below 1.0 and every top floor above 1.2, and the ratio predicts the fail
rate almost exactly -- above ~1.2 near-zero fails, below 1.0 40-55% of the
storey. health-centre and programme-house sit at 1.6-4.2 throughout and fail
essentially nothing.

That corrects 39.11, which divided demand evenly across storeys and concluded
harbor and maple were frontage-feasible "with room to spare". Programmes pin
rooms to level 0 and the ground floor cannot set itself back to buy perimeter:
harbor's pinned 347 m2 needs 71.4 m against the plot's 53.0 m, maple's 414 m2
needs 85.2 m against 55.0 m, while health-centre and programme-house have 51.0
and 14.2 m spare. Same ordering as the corpus fail counts, and fixed before any
search runs. The averaged check is not just weaker: on maple it asks for a
22 m2 courtyard where the ground floor needs 57 m2.

New third _preflight check, advisory like the others, silent on the two
programmes with slack. tests/test_evolve_preflight.py covers all three checks
and asserts the ground-floor figure exceeds the averaged one -- if they ever
agree, one has stopped earning its place. 39.11 annotated in place.

Also recorded, not acted on: the open space is on the wrong storey (harbor puts
50 m2 of courtyard on the starved ground floor and 223 m2 on the surplus first
floor), because value_rate pays an outside leaf above ground value_supported =
300 -- a room's rate -- against a cost of 110, with nothing tying its value to
whether it illuminates anything. Filed as homemaker-py-ecx.

411 passed, 72 skipped.

Refs homemaker-py-773.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-09-05 17:46:52 +00:00

68 lines
2.7 KiB
Python

"""`evolve._preflight` — the advisory pre-run feasibility warnings.
DESIGN.md §39.11 shipped two checks (does the demand fit the plot; is there
enough daylit wall for it). §39.17 adds the third, and the reason it is a
separate check is the whole point: check 2 divides demand evenly across
storeys, but a programme pins rooms to level 0 and the ground floor is the one
storey that cannot set itself back to buy more perimeter. Averaging hides a
ground floor that is short.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from homemaker_layout import evolve
EXAMPLES = Path(__file__).resolve().parent.parent / "examples"
pytestmark = pytest.mark.skipif(not (EXAMPLES / "harbor-house").is_dir(),
reason="examples absent")
def _warnings(progdir, capsys) -> list[str]:
evolve._preflight(str(progdir))
return [ln for ln in capsys.readouterr().err.splitlines() if ln.startswith("WARNING")]
def _ground(lines):
return [ln for ln in lines if "pinned to level 0" in ln]
@pytest.mark.parametrize("name", ["harbor-house", "maple-court"])
def test_ground_floor_shortfall_is_reported(name, capsys):
"""Both plateau programmes pass the averaged check and fail the ground-floor
one -- which is the case §39.17 exists to catch."""
ground = _ground(_warnings(EXAMPLES / name, capsys))
assert len(ground) == 1, f"{name} should warn about its pinned ground floor"
assert "courtyard" in ground[0]
assert "§39.17" in ground[0]
@pytest.mark.parametrize("name", ["health-centre", "programme-house"])
def test_programmes_with_a_slack_ground_floor_stay_quiet(name, capsys):
"""The two programmes that reach near-zero fails have ground-floor frontage
to spare, and must not be warned about it."""
assert _ground(_warnings(EXAMPLES / name, capsys)) == []
def test_the_ground_floor_check_is_stricter_than_the_averaged_one(capsys):
"""maple-court is the case that shows why averaging is not enough: the
per-storey average asks for a far smaller courtyard than the ground floor
actually needs. If these ever agree, one of the two checks is redundant."""
lines = _warnings(EXAMPLES / "maple-court", capsys)
averaged = [ln for ln in lines if "per storey needs" in ln]
assert averaged and _ground(lines)
def m2(line, after):
tail = line.split(after, 1)[1]
return float("".join(c for c in tail.split("m2")[0] if c.isdigit() or c == "."))
assert m2(_ground(lines)[0], "roughly") > m2(averaged[0], "Roughly")
def test_preflight_never_raises_on_a_directory_it_cannot_read(tmp_path, capsys):
"""Advisory only: it must never be able to stop a run."""
evolve._preflight(str(tmp_path))
assert _warnings(tmp_path, capsys) == []