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
68 lines
2.7 KiB
Python
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) == []
|