When a programme space has no explicit 'width' key, the fallback to
width_inside [4.0, 1.0] is geometrically impossible for small spaces
(e.g. t3 WC at 3 m²). Now compute target = sqrt(size/proportion),
sigma = max(0.1, target * size_sigma / (2 * size_target)).
Effect on 35-file corpus: 32 files score +1–307% (width quality improves
for correctly-sized small rooms); 5 files lose spurious width fail lines.
Upstream Perl fix tracked as homemaker-py-8fe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bug fix: _entrance_bid_for_stair now returns None when the stair leaf has an
outdoor neighbour with public access — Perl's Entrances function picks the
via-outdoor priority (3.5 > 3) which maps the stair to a leaf id rather than
a boundary id, so Boundary_Id(edge) eq leaf_id never matches and no entrance
corners are added. Without this fix 7 files had an extra 'staircase volume'
failure from corners [3,1,2] giving stair_fit=0.718 instead of [3]→1.095.
New: Fitness._evaluate_full() extracts the shared pipeline so evaluate()
and score_with_fails() both use it. NativeEvaluator added to innerloop.py
as a drop-in for OracleEvaluator; optimise() defaults to use_native=True.
Gate results: 35/35 score parity (rel_tol=1e-4), 35/35 fail-set identity,
native speed ~45ms/eval vs oracle ~1000ms/eval batched = 23x speedup.
OracleEvaluator kept for validation; oracle.score_batch unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two root causes found for ca/cb corpus parity failures:
1. _avg_path_len_from used unweighted BFS (hop count) but Perl's
Graph::average_path_length uses weighted Dijkstra with centroid-to-centroid
edge distances. This caused wrong edge removal in has_circulation, giving
wrong stack corner counts (2 instead of 3 for lr in ca9e80c5).
2. Entrance corner logic used _public_access (any street boundary) but Perl's
Entrances() picks the best entrance route — a stair only gets entrance corners
if no higher-priority non-stair C leaf has public access.
Also includes homemaker-py-hgg storey/building checks previously uncommitted:
stair fit, circ connectivity, roof-garden, public-access tracking, has_circulation,
corners_in_use, stack_corners_in_use, check_space_counts with failure stacking.
All 4 debug corpus prefixes: ratio=1.000000. 39 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Port Urb's programme-driven fitness leaf quality factors (perpendicular,
proportion, size, width, crinkliness, daylight, access), value rates,
and cost model (per-leaf area costs, interior/exterior wall edge costs,
boundary costs) to Python. Passes 0-mismatch parity against the Urb
oracle across all 35 corpus files (407 leaves, 2849 factors), using
URB_NO_OCCLUSION=1 simple crinkliness (illumination factor pinned to 1).
Key fixes: _dist must use math.sqrt not math.hypot (1-ULP difference
flips boundary overlap predicates); leaf-scope fail regex requires ^\d+/
prefix to exclude building-level failure messages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>