Make both failing tests assert their intent, not stale artefacts

The suite is green for the first time this session: 376 passed, 0 failed.

test_collapse_insearch_reproduces_94g_finish_time_result hard-coded both
endpoints of the 17 result -- 15 fails before collapse, 12 after. Those
were measured before 39.4, when harbor's effective programme was silently
32 instances because codes like cr1 were read as generic circulation; the
same layout now scores 82. But the guarantee the test exists to protect,
per its own docstring, is that in-search collapse reaches the SAME layout
as finish-time collapse on fixed geometry -- and two independent constants
never checked that. They can both drift and stay equal, or both hold and
mask an inequality.

Rewritten to compute both sides live and assert they agree, plus that
collapse does not make the layout worse. Measured: 82 -> 58 in-search, and
finish-time collapse independently reaches 58 at iters=3 and iters=6. The
invariant holds; only the constants were stale. Restating the reference
figure itself remains homemaker-py-ut5.

test_classify_fail_tier_covers_full_corpus globbed examples/**/*.fails and
asserted checked > 0. Git tracks ZERO .fails -- they are artefacts the
scorer writes beside a .dom -- so its docstring described files that by
design never exist in the repo, and it passed only on a machine that had
already run the scorer. Split into: a test that GENERATES fails by scoring
three corpus layouts picked for breadth (requiring >= 8 distinct kinds so
it cannot silently narrow); a test that an unclassifiable string actually
raises; and an opportunistic .fails sweep that never requires them.
Verified by moving every .fails out of the tree and re-running.

Closes homemaker-py-1ue.

Lint at parity (46).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
This commit is contained in:
Claude 2026-08-29 07:11:25 +00:00
parent e5eb397b52
commit f5286dde3d
No known key found for this signature in database
3 changed files with 95 additions and 25 deletions

File diff suppressed because one or more lines are too long

View file

@ -95,21 +95,41 @@ def test_evaluate_full_does_not_call_collapse_global_when_off():
# --------------------------------------------------------------------------- #
@pytest.mark.skipif(not HARBOR.is_dir(), reason="harbor-house example absent")
def test_collapse_insearch_reproduces_94g_finish_time_result():
# DESIGN.md §17: the finish-time collapse takes this layout 15 -> 12 fails.
# collapse_insearch runs the SAME collapse_global earlier in the SAME
# pipeline (before Phase-1 checks instead of after the whole search), so it
# must reach the identical fail count on this fixed-geometry layout.
def test_collapse_insearch_matches_finish_time_collapse():
"""in-search collapse must reach the SAME layout as finish-time collapse.
That equality is the actual guarantee: `collapse_insearch` runs the same
`collapse_global` earlier in the same pipeline (before the Phase-1 checks
rather than after the whole search), so on a FIXED geometry the two must
agree. Both sides are computed here rather than hard-coded.
This test previously asserted the §17 constants directly -- `15` fails
before collapse and `12` after. Those were measured before §39.4, when
harbor's effective programme was silently 32 instances because codes like
`cr1` were being read as generic circulation; the same layout now scores 82
-> 58. Pinning the endpoints made a live invariant fail whenever the
programme or the objective legitimately changed, while not actually
checking the invariant at all (two independent constants can both drift and
still be equal, or both hold and mask an inequality). See
`homemaker-py-ut5` for restating the reference figure itself.
"""
conf, cost = load_config(HARBOR)
conf_ci, _ = load_config(HARBOR, overrides={"collapse_insearch": True})
fit, fit_ci = Fitness(conf, cost), Fitness(conf_ci, cost)
root = dom_mod.load(str(HARBOR / "evolved-3M-nols-3.dom"))
_, f_base = fit.score_with_fails(copy.deepcopy(root))
_, f_ci = fit_ci.score_with_fails(copy.deepcopy(root))
_, f_base = Fitness(conf, cost).score_with_fails(copy.deepcopy(root))
_, f_ci = Fitness(conf_ci, cost).score_with_fails(copy.deepcopy(root))
assert len(f_base) == 15
assert len(f_ci) == 12
# the same collapse, applied once at finish time, scored canonically
finished = copy.deepcopy(root)
Fitness(conf, cost).collapse_global(
finished, adjacency=True, objective="threshold",
preserve_public_access=True, iters=3)
_, f_finish = Fitness(conf, cost).score_with_fails(finished)
assert len(f_ci) == len(f_finish), (
"in-search collapse diverged from finish-time collapse on fixed geometry")
assert len(f_ci) < len(f_base), "collapse must not make the layout worse"
@pytest.mark.skipif(not HARBOR.is_dir(), reason="harbor-house example absent")

View file

@ -1,5 +1,7 @@
"""Unit tests for fitness.py quality terms and helpers (oracle-free)."""
from pathlib import Path
import pytest
from _helpers import with_usage
@ -12,6 +14,7 @@ from homemaker_layout.fitness import (
Fitness,
_leaf_grade,
classify_fail_tier,
load_config,
gaussian,
tier_counts,
)
@ -449,26 +452,73 @@ def test_tier_counts_empty():
assert tier_counts(()) == (0, 0)
def test_classify_fail_tier_covers_full_corpus():
"""Regression guard: every fail string ever emitted into a checked-in
native (non-YAML) .fails file must still classify without error."""
import glob
from pathlib import Path
# Layouts chosen for BREADTH of failure kinds, not for being good designs --
# between them these emit size/width/proportion/crinkliness/access/adjacency,
# missing-space cascades, connectivity and volume fails.
_CORPUS_LAYOUTS = [
("harbor-house", "evolved-3M-nols-3.dom"),
("harbor-house", "generated.dom"),
("maple-court", "generated.dom"),
]
def test_classify_fail_tier_covers_every_fail_the_evaluator_emits():
"""Every fail string the evaluator can produce must classify into a tier.
Fails are GENERATED here by scoring corpus layouts. The previous version
globbed `examples/**/*.fails` and asserted it had checked something -- but
those are generated artefacts that `homemaker-fitness` writes beside a
`.dom`, absent from a clean checkout. So it passed only on a machine that
had already run the scorer, and in a fresh clone failed with `assert 0 > 0`:
it was asserting on the state of the developer's working tree, not on the
code (`homemaker-py-1ue`).
"""
import copy
from homemaker_layout import dom as dom_mod
repo_root = Path(__file__).resolve().parent.parent
checked = 0
for path in glob.glob(str(repo_root / "examples" / "**" / "*.fails"), recursive=True):
checked = kinds = 0
seen: set[str] = set()
for prog, name in _CORPUS_LAYOUTS:
path = repo_root / "examples" / prog / name
if not path.is_file():
continue
conf, cost = load_config(repo_root / "examples" / prog)
_, fails = Fitness(conf, cost).score_with_fails(
copy.deepcopy(dom_mod.load(str(path))))
for fail in fails:
classify_fail_tier(fail) # raises on an unclassified string
checked += 1
seen.add(fail.split()[-1])
kinds = len(seen)
assert checked > 0, "no corpus layout could be scored -- fixtures missing?"
assert kinds >= 8, f"only {kinds} distinct fail kinds exercised; too narrow"
def test_classify_fail_tier_rejects_an_unknown_fail_string():
"""The guard above is only worth anything if an unclassifiable string
actually raises."""
with pytest.raises(ValueError, match="unclassified fail string"):
classify_fail_tier("0/lr something nobody has ever emitted")
def test_classify_fail_tier_checks_any_native_fails_artefacts_present():
"""If a working tree happens to carry .fails artefacts, check them too --
but never require them to exist."""
import glob
repo_root = Path(__file__).resolve().parent.parent
for path in glob.glob(str(repo_root / "examples" / "**" / "*.fails"),
recursive=True):
with open(path) as f:
first = f.readline()
if first.startswith("---"):
continue # legacy Perl-oracle YAML .fails, not this evaluator's output
continue # legacy Perl-oracle YAML, not this evaluator
lines = [first.rstrip("\n")] + [ln.rstrip("\n") for ln in f]
for line in lines:
if not line:
continue
classify_fail_tier(line) # raises on failure
checked += 1
assert checked > 0
if line:
classify_fail_tier(line)
# --------------------------------------------------------------------------- #