Commit graph

10 commits

Author SHA1 Message Date
Claude
d0567d7a74
Remove the Perl oracle
Owner's decision: "we need to abandon the perl oracle, this was only useful
when initially porting, but I suspect many of the remaining problems have been
carried in from the perl (such as the weird scoring of outdoor and circulation
space, which definitely needs fixing)".

39 supports that second clause. Every defect the section found is inherited,
not introduced: the two-sided crinkliness gaussian that double-charges surplus
daylight (39.14), quality as a product over a variable number of factors
(39.18), value_supported priced as value_inside so a terrace was worth more per
m2 than a room (39.19), and circulation returning 0.07 per unit cost (hxi).

So parity with the oracle was never a safety net -- it was a commitment to
reproduce those defects. Each of 39.14, 39.18 and 39.19 would have been a
parity failure had parity ever been checked, and keeping the tests would have
meant reverting the fixes or explaining the failures away.

Removed: oracle.py, test_oracle.py, the two parity tests and their fixture
machinery in test_dom_corpus.py, innerloop.OracleEvaluator with its use_native
and urb_root plumbing, the same plumbing through driver, and fourteen
experiments/ scripts that could only run against Perl. Several of those are
cited in earlier DESIGN sections; the citations now point into git history,
which is the honest state -- they had been unrunnable since the oracle root
(/home/bruno/src/urb) stopped being present. run_search is superseded by
run_search_scaled, which does the same job natively.

Kept: dump_areas.pl/.py, which validate GEOMETRY against Urb (4.1) rather than
fitness, and the prose in fitness_cmd.py and dom.py explaining why the
.score/.fails formats are shaped as they are. Provenance is worth keeping; a
dead code path is not.

CLAUDE.md updated: fitness.py is the only evaluator, and "Urb did it this way"
is no longer an argument that a constant is right. 39.16 is the standing
counterweight in the other direction -- the crinkliness target WAS right and
twice looked wrong only because the code reading it was misunderstood.
Inheritance is neither evidence for nor against.

410 passed. The 69 removed cases account exactly: 64 parity (all skipped, since
no oracle .score was ever committed), 4 in test_oracle.py, and the guard test
39.20 added as a stopgap.

Closes homemaker-py-118. Files homemaker-py-bk9 for the re-baseline that 39.19
made necessary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-09-06 07:56:24 +00:00
Claude
03aadec1ba
§39.7: access requirements become a declared usage: attribute (homemaker-py-sel)
Closes the second namespace sharing a first character with programme codes: the
usage prefixes b/t/l/k, under which a room silently inherited another room's
connectivity rules from its spelling.

usage is a plain, MANDATORY attribute of the space definition -- not a lookup
table. An interim design proposed a top-level usage_classes: table binding
author-coined names to behaviour; withdrawn, because an indirect name->behaviour
mapping living apart from the thing it describes is exactly the shape of the
prefix rule §39 exists to remove, it would be the only such table in a schema
where every other space property is a plain attribute, and the need it served
was already met -- "building specific" is about what a room is CALLED, and
name: is already free text.

Rule that settles it: a usage value exists iff the engine treats it differently
somewhere. Config selects among behaviours; it cannot invent them.

- programme.USAGES (living/kitchen/bedroom/toilet/utility/none) plus the
  behaviour groupings PRIVATE_USAGES / PRIVATE_STRIPS / TOILET_STRIPS /
  SOCIABLE_USAGES. Missing or unknown usage is a load error naming the code,
  from BOTH parse paths.
- Code-level, never leaf-level: usage_of(leaf.type) is looked up fresh, so a
  retype changes the class automatically. 51 sites assign leaf.type, and
  share/share_type plus the r5a resurrection are the precedent for why
  leaf-level attributes rot.
- graph.has_circulation takes the usage map and trims on declared class;
  fitness.access and the public-access check likewise. fitness._t0 is DELETED --
  no first-character type test remains anywhere in the codebase.
- utility is distinct from bedroom (same access requirements today) because it
  is a different use and gives derive_interchange_classes an axis to relax on.
- A toilet now keeps its edge to a terminal room -- the Brand adjacency, which
  the old b-before-t loop ordering severed.
- All 107 corpus entries migrated by experiments/migrate_usage_key.py, comments
  and layout preserved.

MEASURED -- the connectivity model was ~4x too permissive. `none` is not
neutral: nothing is trimmed, so the graph may route THROUGH the room, and 34 of
52 codes had no class (Dental Surgery, Records Room, Utilities Closet all served
as corridors). Edges trimmed, prefix-inferred vs declared, 3 seeds each:
  harbor-house   18 (9%)  -> 79 (39%)   inaccessible fails 0 -> 4
  health-centre  12 (8%)  -> 59 (40%)   inaccessible fails 2 -> 3
  maple-court    53 (17%) -> 123 (39%)  inaccessible fails 1 -> 5

Re-baseline (seed 1, 20k, harbor): 58 fails (15h/43s) -> 61 (16h/45s), now
reporting 1-inaccessible-usable-space x2 plus level 0 and level 1 not connected.
The count rose because the objective got honest -- those failures were always
true of the layout and the old model could not see them. Every harbor number
before this was measured against a graph crediting routes through store
cupboards.

Sharpens §38.2: the objective pays x60-85 to delete circulation, and until now
the deleted corridors were not missed because storage stood in for them. With
that substitution gone, homemaker-py-2v1 is the remaining half -- and now
measurable, because the fails it should prevent actually fire.

350 passed (+5 new), same 7 pre-existing fixture failures, lint unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 13:39:41 +00:00
Claude
a25dc2cb59
§39.4 completion + §39.5 retraction + §39.6: the usage namespace is NOT clean
Answering "are we clean". Generic namespace: yes. Usage namespace: no.

FINISH §39.4. The first sweep missed sites, found by a full re-grep:
graph.py's free-area budget, operators.py host-preference / keep-type /
repair-candidate, fitness.py's ("l","c","k") public-access test, bubble.py's
generic adjacency reference, and -- the important one -- cpsat.py, which was
still matching adjacency by raw startswith. graph.code_matches_requirement is
now the single public answer to "does this leaf count as the thing the
programme asked to be next to", shared by has_adjacency, has_vertical_connection
and cpsat.

RETRACT §39.5. It concluded 2g7.5's CP-SAT seeder win did not survive the
correction. That was wrong. The cause was the missed cpsat matcher above: the
exact solver was optimising a different relation than the scorer checked, so a
failing test reporting an incomplete sweep was misread as a baseline shift.
Re-measured over 6 seeds, cpsat now wins on both programmes (harbor 102/92,
maple 156/154). xfail removed.

REAL BUG UNDERNEATH: CP-SAT was never deterministic despite
num_search_workers=1 and a comment claiming it. neighbors[slot] is a set of
dom.Node, which hashes by id() -- a memory address -- so raw iteration made the
model-build order vary and CP-SAT returned a different equally-optimal
assignment each run (measured 194/180/171/182 over four identical aggregates).
sorted() on the slot indices fixes it. Also paired the wall-clock cap with
max_deterministic_time (solves run ~124ms against a 2s cap, so nothing was
timing out -- latent hazard, not the cause). solve_room_labels is now
reproducible on every captured instance; constructive_topology on the cpsat
path still is not, filed as homemaker-py-fdp (plausible contributor to b8g).

§39.6 THE SECOND NAMESPACE. Usage prefixes b/t/l/k (bedroom/toilet/living/
kitchen) classify programme codes by first letter and stay prefix-based by
design, but they are not inert: has_circulation deletes graph edges from them.
Four corpus rooms are misclassified by spelling -- la1 "Laundry Room" and li1
"Library Corner" as living, br1 "Staff Room" as bedroom, tr1 "Treatment Room"
as toilet. Measured on a health-centre seed: tr1 loses its edge to the adjacent
O, br1 loses its edge to t10 "Staff WC" -- both feed the connectivity fails §38
found persisting. Filed homemaker-py-sel; an explicit usage: key is the fix,
but it changes fitness for correctly-spelled programmes too so it needs its own
A/B.

DOCS. README gains a "Room codes and reserved names" section; CLAUDE.md and
AGENTS.md gain the same summary for agents. audit_programme_config.py now
reports the usage class each code picks up alongside the namespace and
satisfiability checks. DESIGN §37.2's note calling the c/o/s quirk "existing
product behaviour, not a bug" is annotated as superseded.

Corpus audit: zero generic-namespace violations across all ten example
programmes. 346 passed, same 7 pre-existing fixture failures, lint unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 10:09:14 +00:00
cf634ae949 homemaker-py-2g7.5: CP-SAT exact room-code assignment (seeder + reassign op)
Adds src/homemaker_layout/cpsat.py (OR-Tools CP-SAT) as an exact alternative
to operators._assign_adjacency_aware's greedy/beam room-code placement,
wired in as assign_solver="greedy"|"cpsat" (EXPERIMENTAL, default "greedy",
byte-identical to before) through constructive_topology/lift_base_to_storeys/
driver.search, plus a new operators.mutate_reassign in-search repair
operator (driver.search's enable_reassign=False default, mirrors
enable_ruin_recreate). Both found and fixed a resize-fragility bug (a
second CP-SAT pass against settled geometry, operators._cpsat_relabel_settled)
and a CP-SAT symmetry-blowup stall (explicit interchangeable-code grouping).

Seeder-level A/B on harbor-house is a solid, low-noise positive (~13% fewer
real fitness-scored secondary-adjacency fails, 10 seeds). Full driver.search
A/B is only pilot-scale (budget=3000 vs the bead's own 20k target) and
inconclusive -- both flags stay default-off pending a larger-N confirmation.
Full writeup: DESIGN.md §37.7. Bead left in_progress (own acceptance
criteria not fully met); homemaker-py-5bv tracks the deferred post-collapse
repair item.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-04 09:19:36 +01:00
01da0296f0 homemaker-py-koo: add shapecurve.py to CLAUDE.md's Key modules list
Pre-existing gap since the module's creation (§37.4) -- worth closing now
given how much it grew this session (multi-storey support, §37.6).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 23:40:38 +01:00
9c6b1552eb docs: DESIGN.md §26/§27 — backfill 9o5/xi7/b3v (type superposition) and mi7 (bubble-diagram signal)
Two closed, substantive experiments were missing their DESIGN.md write-up
despite being referenced as prior art by later sections:

- 9o5/xi7/b3v (closed 2026-06-30/07-17): multi-use-leaf type superposition,
  a full feature build + real A/B validation (negative — OFF beats ON on
  both programme-house and harbor-house) + a veto-hatch follow-up for the
  one genuine false-positive interchange class found. §17 and §20 both cite
  its verdict directly but it never got its own section.

- mi7 (closed 2026-07-25): 3D bubble-diagram / topological-hop-distance
  fitness signal prototype, tested against real evolved trajectories on two
  programmes, both formulations null. bubble.py was left in the tree
  uncommitted "as documented reference" by the closing session -- committing
  it now (with two trivial ruff fixes: unused import, ambiguous var name) so
  the reference this write-up makes to it is actually resolvable, plus a
  CLAUDE.md module-list entry.

Numbered §26/§27 (appended, not inserted chronologically) to avoid
renumbering every cross-reference in §14-§25.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 23:16:21 +01:00
7c2b9402de docs: add collapse_cmd/homemaker-collapse to module lists (94g)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8566xAxTnwtJTkpXjYNZm
2026-07-18 12:39:23 +01:00
e68bfe53e5 Fix parity gap: oracle.py must run with URB_NO_OCCLUSION=1
Python fitness always pins quality_daylight to 1.0 (URB_NO_OCCLUSION semantics),
but oracle.py was invoking urb-fitness.pl without the flag, causing outside leaves
to receive real sun-model daylight scores and producing a ~9% gap.

Changes:
- oracle.py: add URB_NO_OCCLUSION=1 to score_batch env
- oracle.py: Score.fail_lines now parses structured YAML failures from the
  llm-agent-mcp branch and converts them to plain-text equivalents, so
  parity tests can compare oracle vs native failure sets regardless of format
- Regenerated all 36 corpus .score/.fails files with URB_NO_OCCLUSION=1
  (no .score files are tracked in git; the script generates them locally)
- All 183 tests pass; closes homemaker-py-gpx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 18:40:56 +01:00
fc10466966 Fix stair-fit parity: entrance corners + weighted has_circulation (homemaker-py-w1e/q70)
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>
2026-06-13 20:55:25 +01:00
0366392da4 Scaffold homemaker-py with validated geometry port
Clean-room Python successor to Urb for programme-driven layout search.
This initial commit establishes the .dom bridge format and a faithful
port of Urb's top-down quad geometry, validated byte-identical against
Urb across all 35 programme-house example files (including the wall
inset and multi-storey wall-stacking inheritance).

- dom.py: .dom YAML <-> Node tree, parent/below/position linkage,
  wall_outer inset on load, raw-corner stash for round-tripping
- geometry.py: Coordinate/Coordinate_a/_b/Area/Length + Coordinate_Offset
- experiments/dump_areas.{py,pl}: geometry regression harness
2026-06-10 20:50:20 +01:00