Closes homemaker-py-ju3. DESIGN.md §39.3. The class: key from the bead's design was deliberately NOT built. Auditing every use of the prefix rule first showed it runs deeper than c/o/s -- l/k/b/t carry real adjacency semantics (graph.py builds bedroom<->toilet and kitchen<->living relations from first characters) -- so re-plumbing the type system would invalidate the whole corpus and every baseline, for a problem whose damage is the silence, not the convention. Two findings made the smaller fix sufficient: no corpus programme has ever declared a bare c/o/s code, so check_space_counts' skip only ever discarded declared rooms; and nothing references harbor's four codes in any adjacency or co_locate list. - programme.validate_codes raises on a reserved-prefix code, with the full explanation. Called from BOTH parse paths (programme._parse_spaces and fitness.Fitness._load_programme parse conf["spaces"] independently, so validating one would leave the other door open). l/k/b/t stay unreserved. - harbor-house and harbor-house-l0 renamed: cr1->fr1, of->ao, st1->gs1, st2->gs2. New prefixes are unused in harbor and semantically neutral, and the two storage codes still share a prefix, preserving the structure evaluate_building's per-code plot-ratio term depends on. name: unchanged. - experiments/migrate_ju3_rename.py migrates .dom files written before the rename (--check dry-runs). Pre-rename artefacts, notably evolved-3M*.dom, must be migrated or their leaves read as unmatched generics. - test_collapse_global's c/o/s exclusion test now uses a generic C leaf, which is what the exclusion is actually for; it previously relied on a programme code colliding, which is no longer possible. Re-baseline (seed 1, 20k evals, same settings as §38's run): 57 fails against the 32-instance effective programme -> 55 against the real 37-instance one, with all five previously-lost room instances now placed inside their declared sigma bands (fr1 87.2 vs declared 80, was 32.9/17.1; ao/gs1/gs2 were absent entirely) and no failure naming any of the four codes. At one seed each, 57 vs 55 is within noise -- the robust result is the room placement, not the count. Historical harbor numbers are not comparable to post-ju3 ones; filed homemaker-py-t3s to restate 2v1's acceptance figure once evolved-3M is migrated. 346 passed (+10 new), same 7 pre-existing fixture failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
79 lines
2.8 KiB
Python
79 lines
2.8 KiB
Python
"""Rename harbor-house's colliding room codes in existing `.dom` files.
|
|
|
|
`homemaker-py-ju3` (DESIGN.md §39.2) renamed four harbor-house programme codes
|
|
that collided with Urb's reserved generic type prefixes (``c`` = circulation,
|
|
``o``/``s`` = outside). Any `.dom` produced **before** that change still carries
|
|
the old leaf types, and will score differently against the renamed programme —
|
|
the old types are no longer declared, so every affected leaf reads as an
|
|
unmatched generic instead of the room it was.
|
|
|
|
This matters for artefacts not checked in at the time, notably the
|
|
``evolved-3M*.dom`` harbor-house runs.
|
|
|
|
Mapping (chosen so each new prefix is unused in harbor-house *and* carries no
|
|
adjacency semantics of its own — ``l``/``k``/``b``/``t`` do, ``f``/``a``/``g``
|
|
do not — while preserving the original prefix-sharing structure, i.e. the two
|
|
storage codes still share one prefix):
|
|
|
|
cr1 -> fr1 Common Room with Fireplace
|
|
of -> ao Staff Office
|
|
st1 -> gs1 Ground Floor Storage
|
|
st2 -> gs2 First Floor Storage
|
|
|
|
Usage::
|
|
|
|
python experiments/migrate_ju3_rename.py path/to/evolved-3M.dom [...]
|
|
python experiments/migrate_ju3_rename.py --check path/to/*.dom
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
RENAMES = {"cr1": "fr1", "of": "ao", "st1": "gs1", "st2": "gs2"}
|
|
|
|
|
|
def migrate(path: Path, check_only: bool) -> int:
|
|
"""Rewrite ``type:`` lines in place; return the number of leaves changed."""
|
|
text = path.read_text()
|
|
total = 0
|
|
for old, new in RENAMES.items():
|
|
# Anchored to a whole `type: <code>` line so a code appearing inside a
|
|
# name, comment or unrelated scalar is never touched.
|
|
text, n = re.subn(rf"^(\s*type: ){re.escape(old)}\s*$", rf"\g<1>{new}",
|
|
text, flags=re.M)
|
|
total += n
|
|
if total and not check_only:
|
|
path.write_text(text)
|
|
return total
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser(description=__doc__,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
ap.add_argument("paths", nargs="+", type=Path)
|
|
ap.add_argument("--check", action="store_true",
|
|
help="report what would change without writing")
|
|
args = ap.parse_args()
|
|
|
|
stale = 0
|
|
for path in args.paths:
|
|
if not path.is_file():
|
|
print(f" {path}: NOT FOUND", file=sys.stderr)
|
|
continue
|
|
n = migrate(path, args.check)
|
|
stale += bool(n)
|
|
verb = "would rename" if args.check else "renamed"
|
|
print(f" {path}: {verb} {n} leaf type(s)"
|
|
if n else f" {path}: already current")
|
|
if args.check and stale:
|
|
print(f"\n{stale} file(s) still carry pre-ju3 codes.")
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|