Commit graph

230 commits

Author SHA1 Message Date
Claude
03c1c5edcf
CP-SAT seeding re-measured deterministically: it loses
39.5 concluded the exact CP-SAT seeder beats greedy (harbor 102 -> 92,
maple 156 -> 154). Re-checked because fdp made the arms deterministic and
3qj made the model 7.5x slower. Three findings.

A live bug in the cap, found on the way. solve_room_labels sets a
deterministic work-unit budget (4.0) and a wall-clock backstop, commented
as "a pathological-case backstop only". At 2.0s it had become THE BINDING
CONSTRAINT: 2 of 24 harbor solves returned FEASIBLE not OPTIMAL, wall time
hit exactly 2010 ms, and the deterministic budget was never reached (max
2.483/4.0). Those labellings were suboptimal AND load-dependent -- the wall
clock is exactly the cap 39.5 added the deterministic one to escape. Cause:
38.14's t -> n adjacency makes the model much harder, and the 2s value
dated from when solves took ~124 ms. Raised to 30s; 24/24 harbor and 36/36
maple now OPTIMAL, deterministic budget still in headroom (3.569/4.0).

The verdict reverses. Deterministic, 12 seeds, scored canonically:
  harbor  greedy 1323 (722h)  0.079 s/seed    cpsat 1548 (908h)  1.623
  maple   greedy 1764 (777h)  0.063 s/seed    cpsat 2256 (1213h) 1.327
cpsat loses on both, +225 and +492 fails at ~21x the seeding time,
concentrated in hard fails.

Time and quality have different causes. Removing t -> n from harbor takes
cpsat 1.623 -> 0.193 s/seed (8.4x faster) but it is still +205 vs greedy
(was +225) -- so the adjacency explains the time blow-up and ~9% of the
quality gap; the regression is otherwise pre-existing.

Squaring with 39.5: that section records cpsat returning 194/180/171/182
over four identical 10-seed aggregates before the determinism work. Its
10-fail harbor margin sits well inside a noise band that wide, and was
measured with fdp's id()-ordered room_slots live. The seeder-level claim
was never established rather than overturned. 39.5 annotated in place.

Absolute totals are ~6x 39.5's because the objective has changed, so they
are not comparable to that table; the within-measurement comparison is
like-for-like and is what the verdict rests on.

No default changes: assign_solver was already greedy for 37.7's independent
reason. What changes is that "cpsat wins the seeder A/B" should no longer
be cited as a reason to pursue it.

The cap fix takes the suite from ~4.5 to ~10 min and the tests cannot opt
out, since constructive_topology does not thread the solver limits through.
Filed as homemaker-py-2xk.

Closes homemaker-py-vjd.

Lint at parity (46); tests 384 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 14:47:36 +00:00
Claude
e9e7060fb4
Re-validate collapse_insearch's default under the current objective
38.18 confirmed the 1ph default-flip was sound for its own era, but that
measurement predates three changes to the objective it was measured
against -- 39.4's namespace fix, 38.10/38.11's per-space crinkliness, and
38.12's missing-space cascade -- and collapse_insearch runs collapse_global
inside every eval, valued against exactly the factors those touched. A
default carried on a superseded measurement is an assumption, not a result.

Re-ran the 1ph protocol as published on the current codebase:

                        N    OFF    ON     W/L/T     diff    t     p
  published 1ph         20   7.95   7.10   11/6/3    +0.85   2.38  0.028
  current objective     20   7.85   7.15   10/7/3    +0.70   1.82  0.069
  current objective     40   7.60   7.03   21/14/5   +0.57   2.01  0.045
  current objective     60   7.58   7.02   29/19/12  +0.57   2.45  0.017

Verdict: the default STANDS. At N=60, mean diff +0.567 fails/seed, paired
t=2.454 (df=59), p=0.0171 exact, 95% CI [+0.105, +1.029] excluding zero;
Wilcoxon signed-rank cross-check agrees (p=0.0138), which matters because
fail counts are small integers and normality is not obvious.

Two caveats. The effect is about a third smaller than published (+0.57 vs
+0.85) -- partly regression from a lucky N=20 draw, partly plausible real
erosion, since several fails collapse_global used to clear have been
redefined out of existence or made harder.

More usefully: the published N=20 can no longer detect its own effect. At
exactly that sample size the current answer is p ~= 0.069, a null by the
conventional threshold. Had I stopped at N=20 the honest report would have
been "the 1ph verdict no longer reproduces" and the default would have
looked unjustified. It took N=60 to resolve. That is the 8sh/1ph/qi6/lj3
pattern this log warns about, now biting the flagship result itself: any
future re-validation of this default needs N >= 40.

20 annotated in place so a reader of the original claim sees the current
figure. Harness takes a seed range now (APPEND=1 to extend a sweep).

Closes homemaker-py-ioe.

Lint at parity (46); tests 384 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 13:57:45 +00:00
Claude
50fb293fa5
Re-verify 1ph: the iio bug could never have touched it
d86 asked for the rigorous version of 35's spot-check -- check out the
codebase at the 1ph commit, backport the iio stale-leaf-share fix, re-run
the historical seed sets.

One constraint had to be worked around: this repository's history begins
2026-07-30, six days AFTER the 1ph commit (2026-07-24), so that checkout
does not exist here. Closest reachable stand-in is 391f510 (2026-07-30), a
genuine ancestor of the iio fix and therefore pre-iio, carrying that era's
examples/ and objective. Measured there, not at the true 1ph commit, and
that is a real limitation of the reproduction.

Protocol as published: programme-house, budget 3000, 4 workers, seeds 1-20,
ON vs OFF, both arms finished with --collapse. Run twice over one worktree,
as-is then with the 22-line iio hunk applied.

                            OFF    ON     W/L/T    mean diff  t(df=19)
  published 1ph             7.95   7.10   11/6/3   +0.85      2.38
  pre-iio 391f510           8.05   7.10   11/6/3   +0.95      2.59
  same + iio fix            8.05   7.10   11/6/3   +0.95      2.59

The published verdict reproduces, and the iio fix changes nothing: 0 of 40
(seed, arm) cells differ, per-seed counts equal cell by cell.

It could not have been otherwise. The bug needs a leaf carrying a STALE
share/share_type, and leaf-sharing only stamps a share where a code has
count > 1 -- programme-house declares count: 1 for all six codes. Over 8
constructed seeds at that commit, programme-house had share_type set on 0
of 56 leaves; harbor-house on 24 of 128. _collapse_value reads
leaf.share_type, so the bug is structurally unreachable on the 1ph
protocol. That is why 35 saw 2/3 harbor seeds diverge by 5-8 fails while
programme-house at N=20 moves not one cell: harbor has codes at counts 10,
6 and 5.

20's retroactive caveat is discharged for the 1ph section and stays live
for harbor-house/qpk, where shares exist and divergence was measured.

Kept experiments/rerun_1ph_protocol.sh and both result TSVs.

Closes homemaker-py-d86.

Lint at parity (46); tests 384 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 13:21:29 +00:00
Claude
f6eeee7191
n_workers is an algorithm parameter, not noise
14 recorded "harbor seed 2 scored 71 then 73 on byte-identical re-runs --
parallel/BLAS non-determinism", and b8g carried that forward as noise
widening the error bars on every A/B run at n_workers>1. The premise does
not survive measurement. Nothing is non-deterministic:

  score a frozen .dom, 20 repeats in one process   bit-identical
  same .dom, 8 processes, varied PYTHONHASHSEED    bit-identical
  full search, harbor seeds 0-3, n_workers 1..4,
    repeated across processes                      bit-identical PER count
  the same with OMP/OPENBLAS/MKL_NUM_THREADS=1     IDENTICAL to unpinned

The last line matters most: b8g proposed "likely a one-line env pin in the
worker pool initializer". Pinning BLAS threads changes nothing bit-for-bit,
so shipping that would have looked like a fix, done nothing, and retired
the issue.

What is real is not noise: the trajectory is a deterministic function of
n_workers. harbor seed 3, budget 1500 -- w=1/2/3 all give 64 fails with
identical bits, w=4 gives 65. Each stable across processes. The mechanism
is batch_n = min(n_workers, ...) children bred from ONE population snapshot
before any is admitted, with the shared rng consumed in a different
pattern; at w=1 each child sees the population its predecessor updated. A
4-worker run is partly generational, a 1-worker run steady-state -- same
seed, different search. Divergence is occasional (seeds 0/1/2 agreed, seed
3 did not), which is how it reads as noise when sampled.

14's observation was most likely homemaker-py-xcy, the as_completed
admission-ordering bug, which WAS non-deterministic and is fixed.

Shipped instead of a no-op env pin: driver.search's docstring states the
contract; test_search_is_reproducible_at_a_fixed_worker_count parametrises
over 2/3/4 workers, asserting each is internally stable and deliberately
NOT that they agree; test_scoring_a_frozen_design_is_deterministic guards
the floor.

The run_*_ab.sh harnesses already pin WORKERS=4, so arms inside one harness
are sound. The exposure is comparing across harnesses, or against a
historical figure whose worker count was never recorded.

Closes homemaker-py-b8g.

Lint at parity (46); tests 384 passed (3 new), 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 12:52:33 +00:00
Claude
225e673ae3
Staged harness re-scored under a different objective than it searched
run_staged_search.py reported MISMATCH on its BASELINE arm -- the
LEAFSHARE=0/MULTIUSE=0 control every A/B compares against. Two facts
combined: driver.search_staged had no collapse_insearch parameter at all,
so every inner search() call inherited search()'s True default
unconditionally; and no example patterns.config sets the key, so the final
_native_score rescore got False from a bare load_config. Search optimised
one objective, the rescore graded another.

The 7ua fix pinned the key inside a fitness.load_config monkeypatch, but
that patch was installed only `if leaf_share or multi_use` -- so it fixed
every arm except the control.

Fixed in the right place: search_staged now HAS the parameter (default
True, byte-identical to the inherited default), threaded into all three
internal search() calls. The harness chooses the arm explicitly (COLLAPSE,
default 1), passes it to the search, and passes the SAME value to
_native_score, which overrides the key rather than hoping the config
carries it. The rescore mirrors the search by construction.

Verified on programme-house, budget 150:
  baseline    MISMATCH 1.56663e-08 vs 1.51708e-08  ->  OK
  COLLAPSE=0  (knob did not exist)                 ->  OK 1.66216e-08
  LEAFSHARE=1 / MULTIUSE=1                         ->  OK

COLLAPSE=0 scoring differently confirms the knob is not a no-op, and the
default arm's search result is unchanged, so no prior staged number moves.

Audited the other three search_staged callers: run_and_capture_91f.py
already pins collapse_insearch: True; run_island_ab.py never re-scores;
probe_harbor_floor.py did NOT pin it and had the same bug -- now fixed, and
that is the harness which produced every 13.x floor number.

The recorded mitigating factor -- only the continuous score moved, the fail
count matched, and the run_*_ab.sh greps read only the count -- is true and
is exactly what made it dangerous: a harness that reports MISMATCH on its
own control, invisibly to the metric of record, trains everyone to ignore
the warning.

Closes homemaker-py-4ok.

Lint at parity (46); tests 381 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 12:11:38 +00:00
Claude
148988df14
constructive_topology was ordered by memory address on the cpsat path
assign_solver="cpsat" gave a different leaf-type signature on every run
from an identical seed, in the same process. One line:

  assignable = scope if scope is not None else set(leaves)
  noncirc = [L for L in assignable if L not in circ]      # id() order

assignable is a set of dom.Node, and Node hashes by id() -- a memory
address -- so iterating it ordered noncirc, and hence room_slots, by where
the objects happened to land in memory. That shifts between calls within
one process as allocation patterns change, with no seed involved.

Only cpsat showed it. The greedy path re-sorts every slot list with -idx[L]
as a unique tiebreak and is immune to the incoming order; CP-SAT consumes
room_slots order as its model's variable order, and the labelling problem
has many equally-optimal solutions. Greedy was not more correct, it was
masking a defect that had been there all along.

Fix: iterate the tree-ordered list, use the set only for membership.

Verified on programme-house, harbor-house and maple-court: 1 distinct
signature over 5 runs on both solvers, and 1 across 4 processes started
with different PYTHONHASHSEED, so context_types' string sets are not a
second source. test_constructive_topology_is_bit_reproducible guards both.

Method: rather than guess which set was at fault, instrument
solve_room_labels with an id-free fingerprint of inputs and outputs and
isolate the FIRST call, since later calls legitimately depend on earlier
ones through leaf types. Five runs gave five distinct first-call inputs,
placing the fault upstream of the solver in one step.

Every A/B on the cpsat path was comparing arms that differed partly by
memory layout -- 39.5's cpsat-vs-greedy verdict included, already down for
re-measurement under homemaker-py-vjd. Same id()-keying hazard as the
documented geometry._cache issue and a plausible contributor to
homemaker-py-b8g, which stays open: n_workers>1 has its own BLAS mechanism
and is not addressed here.

Closes homemaker-py-fdp.

Lint at parity (46); tests 381 passed (2 new), 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 11:45:19 +00:00
Claude
ee050532d6
Declare toilet-to-sleeping adjacency where the brief supports it
A toilet next to a sleeping room is a positive even with no door between
them (Brand): the adjacency is what makes a later knock-through possible.
The engine already scores it -- check_adjacency runs against the unfiltered
graph_base_pre -- but only where a programme declares it, and only
programme-house did.

Declared:
  harbor-house  t -> n    bathrooms serve the Neighborhoods (communal
                          sleeping); both unpinned, 6 t / 5 n
  maple-court   tt -> r   Upper Bathrooms among Individual Rooms, both
                          level 2, already 62% adjacent at seed time

NOT declared, and checking before declaring is what caught these:

  maple t -> n is IMPOSSIBLE. Adjacency is evaluated per level, and maple
  pins t to level 0, n to level 1. Declaring it would have added six
  permanently unsatisfiable fails; the 0% seed-time rate was a hard
  impossibility, not search difficulty. maple's ground floor has six
  bathrooms and one sleeping room (Clinic Room x1) -- a ground-floor WC in
  a communal building is public, so Brand does not apply anyway.

  health-centre has no dedicated WC. The ruling was that a treatment room
  "may give access to a toilet, but this would be a dedicated toilet"; t9
  is a Public WC and t10 a Staff WC. Earning the credit needs a WC added to
  the brief -- programme authoring, filed as homemaker-py-5nw.

Both declarations are reachable (best of 8 seeds 2/3 harbor, 2/2 maple), so
the search gets a gradient not a permanent penalty. evolved-3M-nols-3
84 -> 89 fails, all five the new requirement.

Cost: cpsat assignment ~7.5x slower on harbor (0.28 -> 2.11s per seed);
greedy, the default, unchanged at 0.06s. Ordinary runs pay nothing, but
39.5's cpsat-vs-greedy verdict was measured on a cheaper problem than the
corpus now poses -- filed as homemaker-py-vjd.

Two tests were over-fitted to the old seeds and are repaired to assert
their intent, not relaxed to pass: reassign now sweeps six constructive
seeds (seed 0's better-seeded design legitimately has nothing to improve,
5 of 6 others fire), and repair_circulation asserts that repair strictly
helps plus a >=85% bar rather than a sampled 100% hardened into a
guarantee (measured 25% -> 92%, stable over 6 and 12 seeds).

Closes homemaker-py-3qj.

Lint at parity (46); tests 379 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 10:57:44 +00:00
Claude
98d403b966
health-centre: enlarge the plot for a courtyard typology
39.11 found health-centre demanding 240 m2 of floor on a 183 m2 plot --
131%, single storey, nowhere for the overflow to go. Every room came out at
0.60x its declared target, 100% undersized, uniformly, and no amount of
searching could fix it.

Owner's ruling: enlarge the plot, sized assuming the building has a
courtyard. The plot is a quadrilateral here, so the courtyard is interior
space the search carves out, not a hole in the site. Sizing must therefore
allow for rooms (240, declared), circulation (~65, ~27%, NOT in demand --
the search creates C leaves) and courtyard (~36).

Scaled about the polygon centroid by k = 1.4606, chosen so the INSET area
lands on 400 m2. Centroid scaling preserves the site's irregular shape and
its one private edge: same site, larger.

  plot          183.2 -> 400.0 m2
  rooms/plot    131%  -> 60%      (harbor's ratio exactly, and harbor works)
  daylit perim  41.5  -> 61.3 m   (49.4 needed)
  median room area / (target x share)  0.60x -> 1.00x

The last figure is the one that matters -- 0.60x was the geometric
signature of the infeasibility, now level with harbor/maple's 1.01x.

The courtyard stays REQUIRED by the geometry, not merely permitted: a room
is at most 4.86 m deep before failing crinkliness, so a ~20x20 plot with a
daylit ring leaves a 106 m2 core that cannot reach an external wall.
Enlarging made room for a courtyard; it did not remove the pressure that
produces one.

evolve._preflight is now silent on health-centre.

Closes homemaker-py-7b7, unblocks homemaker-py-7xb.

Lint at parity (46); tests 379 passed, 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 10:20:01 +00:00
Claude
df84bfe1e6
Missing-space cascade no longer weighted by YAML verbosity
check_space_counts emitted, per missing room instance, two base failures
plus one placeholder for each optional key the author happened to type --
has_size/has_width/has_proportion are literally "size" in c from the YAML.
So a missing room cost 3, 4 or 5 fails depending on nothing but how
verbosely its space was written, and under value *= 0.5 ** len(failures)
that is a 4x difference in penalty between two single rooms. The tiered
comparator inherits it directly, since n_hard is dominated by these
cascades -- the search's primary key was partly a measure of config style.

The two paths disagreed about the same room. A PRESENT room is checked on
all three qualities regardless of declaration: get_space_params fills width
and proportion from defaults, deriving width from size when absent, so
programme-house's t2 declares size: alone and still gets a real width
target of 1.633 it can fail on. Missing, it emitted one placeholder where
b1 emitted three. The cascade stands in for the checks that could not run,
and it stood in for the wrong number of them.

Fix: emit all three placeholders always -- a fixed 5 per missing instance,
mirroring the present-room path. 36 of 67 corpus codes were under-counted.
Max weight ratio between two single rooms 4x -> 1x (programme-house),
2x -> 1x (harbor, maple).

This makes fail counts LARGER and that is the point; it is a correctness
fix, not an improvement. harbor evolved-3M-nols-3 82 -> 84, generated
155 -> 174, evolved-3M 131 -> 144; maple generated unchanged (no missing
instances).

NOT taken: 1i8's other option, one fail per instance with the placeholders
informational. It fixes the verbosity dependence too but silently rescales
a missing room from 1/32 to 1/2, the same weight as one crinkliness fail.
Whether it SHOULD cost 1/32 is a real and separate question; bundling it
here would change the objective's priorities under cover of a bug fix.
Magnitude left exactly where it was, filed as homemaker-py-3i3.

Every historical corpus fail count is invalidated again, on top of 39.4 and
38.10/38.11 -- which is why the cold-start re-baseline belongs after the
objective work, not before it.

Closes homemaker-py-1i8.

Lint at parity (46); tests 379 passed (3 new), 0 failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-29 09:33:40 +00:00
Claude
f5286dde3d
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
2026-08-29 07:11:25 +00:00
Claude
45eff37f96
Records Room ruled a store; stays exempt
No config change -- health-centre re1 already declares crinkliness: none.
This settles the corpus classification at 14 spaces: storage,
plant/mechanical, utilities closets, records room. Replaces the hedge in
38.11 with the ruling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-28 23:22:02 +00:00
Claude
c9b9eafef6
Owner's daylight ruling: corridors need it; retract the 61% headline
Ruling: corridors need daylight. Only rooms not occupied from day to day --
a cupboard, a store, a plant room -- do without it.

That overturns the classification 38.8 and 38.9 rested on. Those sections
exempted internal circulation, covered courtyards, WCs, laundries and
reception on my inference rather than any ruling. None are exempt: a
corridor is occupied all day every day, a waiting room likewise, a laundry
is a room people spend time in, a sterilisation room is a workplace.

Re-measured with the classification READ FROM THE CORPUS (a space is exempt
exactly when its own patterns.config declares crinkliness: none) instead of
inferred: 24 of 271 crinkliness fails (9%) were not defects -- not 136
(50%), not 61%. Overstated by about six times.

  - uncrinkliness_circulation stays at [5/6, 1.1/3]. 38.10 called its
    equality with the habitable target the purest case of an untuned value;
    it is not a bug, it is the correct answer reached by default, since
    corridors want daylight on the same terms as rooms.
  - corpus declarations narrowed 18 -> 14. usage: utility was too coarse and
    swept in Laundry Rooms and a Sterilisation Room. What remains is
    storage, plant/mechanical, utilities closets and a records room.
  - audit_crinkliness_truth.py rebuilt to read the classification from the
    configs rather than infer it from usage:, so it cannot drift from the
    ruling again.
  - 38.8/38.9's inflated figures flagged in place, with 38.11 carrying the
    correction.

What survives, none of it dependent on the classification: the 38.6
critique of the three modes; usage_daylight being mis-keyed; the 38.8 A/B
yardstick being wrong; and the per-space crinkliness mechanism itself.

ssz's opening measurement was right but its reading was wrong -- under this
ruling a buried leaf usually IS a defect, so scoring it zero is largely
correct. The narrowed remnant is that the score is FLAT rather than low, so
two equally-buried layouts cannot be ranked. Filed as homemaker-py-9gj; it
is a search-mechanics problem, not a calibration one.

Lint at parity (46); tests 369 passed, 2 pre-existing failures (ut5's stale
baseline and 1ue), both failing identically before this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-28 23:14:21 +00:00
Claude
31d50ba72b
beads: correct a mangled ssz comment
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-28 22:42:12 +00:00
Claude
335dd9c1ff
beads: close bdf, attach ut5's failing test, file 1ue
The owner's fixture commit (0d4ae7a) took fresh-clone failures 7 -> 2, so
bdf is closed. The 2 remaining are different bugs:

- ut5 now has a failing test attached. test_collapse_insearch hard-codes
  15 fails for evolved-3M.dom; measured 86 at 0d4ae7a and 81 after the ssz
  crinkliness change. The gap is the 39.4 namespace fix (the 15 dates from
  when harbor's effective programme was 32 instances because cr1 and
  friends were read as generic circulation); the 5 is this session's
  change, in the expected direction.

- 1ue (new): test_classify_fail_tier_covers_full_corpus globs
  examples/**/*.fails and asserts checked > 0, but those are generated
  artefacts absent from a clean tree, so it asserts on the developer's
  working tree rather than on the code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-28 22:41:42 +00:00
Claude
8522576467
Frontage feasibility as a pre-flight check; retract the "3x short" claim
Closes homemaker-py-tdp. The bound it was built on (every interior leaf
needs L >= A/(1.6202*h) of daylit wall) is sound, but tdp applied it to a
FULLY BUILT plot. None of these programmes ask for a fully built plot.

Recomputed against the area each programme actually demands, harbor-house
and maple-court are not frontage-infeasible: they need 49 m2 and 22 m2 of
courtyard against 277 m2 and 424 m2 of spare plot. The "2.7x / 2.9x short"
figures are withdrawn, and with them the claim that the plateau programmes
are unsatisfiable as specified -- the plateau remains unexplained.

One corpus programme is genuinely infeasible, for a much cruder reason:
health-centre demands 240 m2 of floor on a 183 m2 plot (131%), single
storey. Every room lands at 0.60x its declared target, 100% undersized,
uniformly. Filed as homemaker-py-7b7, blocking homemaker-py-7xb.

  - evolve._preflight: two closed-form checks at startup (does the demand
    fit the plot; is there enough daylit wall for it). Advisory only, it
    never blocks a run -- an author may be exploring an over-tight brief
    deliberately. Silent on programme-house.
  - diag_exposure_frontage.frontage_budget reports the full budget.
  - DESIGN.md 39.11 with the corrected corpus table; 38.3 marked PARTLY
    RETRACTED and cross-referenced.

Both measure plot area and frontage through geometry rather than the raw
init.dom corners, so they carry the wall_outer inset and plot rotation,
and "daylit" means what Fitness.area_outside means by it. A hand-rolled
first version skipped the inset and read ~1 m / ~14 m2 optimistic per
plot; 39.11 carries the corrected numbers.

Lint unchanged at 46 pre-existing findings; tests unchanged at 7
pre-existing failures (the uncommitted evolved-3M*.dom fixtures,
homemaker-py-bdf).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 16:20:52 +00:00
Claude
024311a2c2
§39.10: preserving constructed connectivity is NULL — and it reframes §39.9
§39.9 named the upstream fix: keep circulation connected DURING the resize
rather than rebuilding it after. Built and measured. It does not help, and the
reason matters more than the lever.

Both halves of the re-cut do damage, in different proportions per programme.
Freezing rotations and letting only ratios move (% levels connected, 12 seeds):
harbor 100 -> 71 -> 50, health-centre 100 -> 8 -> 8, maple 100 -> 92 -> 67. So
health-centre is destroyed entirely by the ratio and maple mostly by the
rotation; a fix must be able to give back either.

operators._size_divisions_preserving_circulation snapshots every cut, resizes,
then reverts the cuts on the tree path between each circulation pair the resize
broke -- programme fully intact, no retyping, only geometry given back. It works
on connectivity (harbor 50->92%, maple 67->97%, health-centre 8->17%) and costs
area accuracy: constructed-seed fails harbor 96.6->141.5, maple 141.8->175.8,
size fails roughly double. (A greedy single-cut revert barely moved -- it stalls
where no ONE revert helps though two would. Targeting the broken pairs is what
made connectivity work.)

The obvious defence -- raw constructed seeds understate it, the resize is only a
warm start, the inner loop should recover -- was TESTED AND FAILS. Full search,
harbor-house, 12000 evals, seed 1:
  OFF  43 fails,  9 hard, 3 connectivity
  ON   65 fails, 26 hard, 4 connectivity
Worse on every axis, including connectivity itself.

REFRAMING: §39.9's fact stands (the resize destroys 41 of 49 circulation edges)
but is NOT ACTIONABLE, because construction-time connectivity does not determine
final connectivity. The search discards and rebuilds the seeder's circulation
either way, and constraining the seed only spends area quality the search cannot
recover. Together with §39.8 (not an incentive problem) that retires the framing
this thread inherited from §38: connectivity is neither a construction problem
nor an incentive one.

Both flags (repair_circulation, preserve_circulation) stay default off with the
numbers recorded, plus byte-identical-default tests. Do not revisit either
without a new formulation -- the standing this document gives bubble.py.

356 passed (+1 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 15:11:46 +00:00
Claude
7c41069226
§39.9: level-not-connected is destroyed by the resize, not by the search
Answers homemaker-py-yql. §39.8 established the search is not PAID to sever
circulation; this establishes where connectivity actually goes.

CONSTRUCTED, THEN LOST -- at construction time, in the resize.
_assign_adjacency_aware picks circulation as a CONNECTED dominating set and
succeeds every time. _size_divisions_from_targets then moves every wall to hit
the programme's area targets and destroys it.

Measured over 20 constructed seeds per programme, fully-connected seeds:
harbor-house 1/20, health-centre 1/20, maple-court 0/20. The control -- same
seeds with proportion_aware=False, i.e. no resize -- is 100% connected on all
three. Mechanism confirmed on health-centre: 41 of 49 circulation-to-circulation
edges destroyed by the resize, surviving shared walls squeezed to 0.54-1.11 m
against door_width=1.2, so they stop counting as edges. This is the failure mode
§37.7 recorded for CP-SAT assignment, never looked for in connectivity, where it
costs 35-95 points.

§39.7 COST CHECK: zero. Identical rates under prefix-inferred vs declared
usages -- has_circulation never trims C-C edges, so last commit's usage change
could not and did not make connectivity harder to achieve.

REPAIR MEASURED NEGATIVE. operators.repair_circulation_settled applies §37.7's
own alternating-minimisation fix (re-connect against the settled geometry by
retyping the cheapest bridging leaves to C). It restores 100% connectivity on
all three programmes -- and is still the wrong trade: connectivity fails fall
0.8-1.7 per seed while missing-room fails rise 5.0-8.5, because every retyped
leaf displaces a required room at a 3-5 fail cascade (§38.5). Kept default off
with the write-up, per house style for a null lever, plus a byte-identical
default test and a test asserting it does reconnect every storey.

NEXT LEVER, FILED: preserve the connection during the resize (constrain
_size_divisions_from_targets so a shared C-C boundary cannot fall below
door_width) rather than rebuild it afterwards at the programme's expense --
a constraint on an existing solve, not a new repair pass. solver.py's existing
min_width_generic is the same idea applied to leaf width rather than to a shared
boundary, so it may belong beside it.

Adds experiments/diag_connectivity_yql.py (construct / cost / survive reports).
355 passed (+2 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 14:39:07 +00:00
Claude
3aee813ccd
§39.8: homemaker-py-2v1 connectivity weighting — MEASURED NULL, premise retracted
§38.2 concluded the objective is net-positive on severing a level's
circulation: merging a corridor into a habitable sibling gains x6
(value_inside/value_circulation), while "level N not connected" costs x0.5, so
break-even needs 0.5^w < 50/300, w > 2.58 -- "severing must cost at least 3
fails and costs 1". The arithmetic is right. The premise is wrong.

Shipped anyway, EXPERIMENTAL and default off (byte-identical):
fitness.connectivity_weight_for(value_inside, value_circulation) returns the
smallest weight making severing net-negative -- 3.0 at the defaults, DERIVED
from the rates rather than hard-coded so it tracks them if either is retuned.
conf["connectivity_weight"] takes 1.0 / "auto" / a number and counts each
connectivity failure as w failures in the 0.5^n penalty.

MEASUREMENT: at auto (=3) the §38.2 deletion test does not move at all -- 5/25
rewarded either way, median x0.26 vs x0.27. Reason: the connectivity fail count
is UNCHANGED in every rewarded deletion (115->107 fails but 5->5 connectivity;
107->99 but 3->3; 78->71 but 3->3). Weighting a fail that never fires changes
nothing.

And when a deletion DOES break connectivity, it is already punished. Every such
case, 4 seeds per programme: harbor-house 2 of 32 sampled deletions, both
punished (x0.00, x0.01); maple-court 5 of 32, all punished (x0.58 .. x0.07).
Severing costs 1-2 connectivity fails PLUS the cascade after them, which
already outweighs the x6 gain. The flat rule was never the problem.

Where §38.2 went wrong: the x4.06 "well-daylit circulation leaf" that motivated
the bead was a deletion that did NOT change the connectivity fail count. It was
rewarded for removing the leaf's own quality failures -- §38.1's zero-value
finding -- and I misread it as a pricing mechanism. §38.2 now carries the
retraction inline. Two lessons recorded: a plausible closed-form arithmetic is
not a measurement, and when a fix produces exactly no effect, suspect the
premise before the implementation.

Still standing from §38: §38.1 (buried leaves score zero quality and contribute
no value) and §38.3 (frontage budget) are direct measurements. §39.7 remains
the better lever on the same symptom -- it made the connectivity fails FIRE,
where this would only have made them cost more.

Re-opened as homemaker-py-yql: why level-not-connected persists in the best
layout when severing is already punished. Evidence now points at reachability,
not incentive, and it is newly measurable because §39.7 stopped store cupboards
standing in for corridors.

353 passed (+3 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 14:15:22 +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
72673a2ebf
homemaker-py-sel: usage is a plain space attribute — drop the mapping table
The proposed top-level `usage_classes:` table (binding author-coined names to
engine behaviour, e.g. craft -> living) is withdrawn. Wrong three ways:

a) Same bug in another costume. An indirect name -> behaviour mapping living
   apart from the thing it describes is exactly the shape of the
   first-character prefix rule. §39 exists to remove that shape, not relocate
   it into a table.

b) Inconsistent with the schema. Every other space property is a plain
   attribute -- name, size, width, proportion, adjacency, level,
   requires_below, count, share, interchange, co_locate -- and no key anywhere
   in patterns.config is an indirection table. Verified across the corpus.

c) The need was already met. "Programmes are building specific" is about what a
   room is CALLED, and name: is already free text. usage: records ACCESS
   REQUIREMENTS, which are not building-specific -- a craft room and a lounge
   have the same ones, which is the entire reason the key exists. The earlier
   draft conflated the two.

So: spaces: ws1: {name: Workshop Space, usage: living} -- one place, no
binding step, no unbound-name error class.

Rule that falls out and is worth keeping: A USAGE VALUE EXISTS IF AND ONLY IF
THE ENGINE TREATS IT DIFFERENTLY SOMEWHERE. Config selects among behaviours; it
cannot invent them. An alias produces no behaviour that the aliased value does
not. `utility` passes the test -- not on edge-stripping, which is identical to
bedroom today, but because it is intended to drive derive_interchange_classes.

No src changes; proposal file only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 12:56:12 +00:00
Claude
d5e10172c5
homemaker-py-sel: all 52 corpus codes assigned; utility distinct; open vocabulary
Rulings settle every previously-unresolved code: zt1 Sterilisation -> utility;
ph1 Pharmacy -> bedroom (a private room, not public, like an office); n
"Neighborhood" -> bedroom (a communal sleeping area, much like a dormitory);
gh1 Greenhouse -> living (grows plants, but a living area like a conservatory);
ws1 Workshop -> living conditionally, since it is building-specific (communal
craft room vs maintenance area) and both corpus programmes carrying it are
communal living facilities.

Final: living 12, bedroom 17, utility 11, toilet 7, kitchen 2, none 3. Zero
unresolved, zero missing.

DESIGN 1 — `utility` is a distinct value, not an alias for `bedroom`. Same
edge-stripping today (identical access requirements) but separately declared,
because they are very different uses AND because it gives
programme.derive_interchange_classes (§13/§26, homemaker-py-9o5) a natural
axis: bedroom- and utility-class leaves could be interchangeable DURING search
and collapse to their real use at scoring time, which is exactly the
superposition relaxation that machinery already implements.

DESIGN 2 — vocabulary controlled but NOT closed. The earlier "closed enum,
unknown value errors" recommendation is withdrawn. The six known names drive
behaviour; a programme may coin its own (`usage: craft`) provided it binds it
in the same file via `usage_classes: {craft: living}`. An unbound name is a
load error, so naming stays building-specific without an unrecognised usage
ever falling through to a silent default.

VERIFIED — the Brand adjacency point is not an engine bug. check_adjacency runs
against graph_base_pre, the UNFILTERED graph, so a declared adjacency is
satisfied by a neighbouring room regardless of what has_circulation strips; the
stripping only denies routing, which is correct. "A toilet adjacent to a
bedroom is a positive even if there is no door" is therefore already
expressible -- but only where DECLARED, and just one corpus programme declares
it (programme-house t1 -> [b1]). harbor-house, maple-court and health-centre
leave that adaptability credit on the table. Filed separately; it is programme
authoring and can land before or after the usage key.

No src changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 12:34:03 +00:00
Claude
6bed7cc535
homemaker-py-sel: propose usage for all 52 corpus codes; scope was understated
Bruno's rulings settle the framing: usage is an ACCESS-REQUIREMENT class, not a
room-name category. li1 "Library Corner" and br1 "Staff Room" are living rooms
(same access requirements); tr1 "Treatment Room" has a bedroom's requirements
(terminal, not a circulation route); la1 "Laundry Room" is utility. Ground-floor
external cases (fuel stores, bins) need no class — level: 0 plus adjacency: [o]
already expresses that.

SCOPE CORRECTION. My "93% pure annotation, 7 entries change" estimate was
mechanically true but misleading: `none` is not neutral. Nothing is stripped, so
has_circulation MAY ROUTE THROUGH the room. 34 of 52 distinct corpus codes
currently have no usage class and are therefore modelled as permeable corridors
-- including Dental Surgery, GP Consulting, Minor Surgery, Physiotherapy,
Counselling, Records, Sterilisation, six storage rooms, four offices and
"Individual Room" (a resident's own room).

Consequence for §38: the model OVERSTATES connectivity, since a level can be
found connected through rooms that are dead ends in reality. "level N not
connected" is therefore rarer than it should be, compounding §38.2 -- the
objective already pays x60-85 to delete circulation, and the corridors it
deletes are then not missed because store cupboards stand in for them.

Adds experiments/usage_map_proposal.yaml: all 52 codes covered (47 assigned, 5
unresolved, 0 missing) -- living 10, kitchen 2, bedroom 15, toilet 7, utility
10, none 3. Nothing reads it yet; it is the review artifact and, once agreed,
the migration input.

Also verified and recorded (Q2): a dedicated toilet off a bedroom-class room
does NOT survive scoring. has_circulation runs its bedroom loop before its
toilet loop and the bedroom loop strips bedroom->toilet, so the ensuite is
severed -- for real bedrooms too. Existing Urb behaviour, neither caused nor
fixed by the usage key.

No src changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 10:47:11 +00:00
Claude
f476a5849e
beads: revise homemaker-py-sel design — mandatory code-level usage, no fallback
Records the outcome of the "how does a parallel usage key work when the leaf
name can mutate?" review. Two problems were conflated:

1. Mutation staleness dissolves if usage is CODE-level: usage_of(leaf.type) is
   looked up fresh, exactly as size/width/proportion/adjacency already are.
   Verified every corpus leaf type resolves to a declared code or a generic.
   The broken design is usage stamped ON THE LEAF — 51 sites assign leaf.type,
   and this project already hit that class twice (share needed a companion
   share_type; r5a was a real stale-stamp resurrection even with the guard).

2. Name/key divergence is real and is NOT fixed by (1). Resolved by making
   usage mandatory on every declared code with no prefix fallback, closed
   vocabulary {bedroom, toilet, living, kitchen, none}, missing key = load
   error. One source of truth; room codes become free-form identifiers.

Measured migration: 10 files, 107 entries, 100 (93%) pure annotation; only 7
change behaviour (la1 x3, li1 x2, br1, tr1) and those are the fix.
write_stage1_programme already propagates the key.

Left open as the programme author's call, not mechanical: what those four
rooms should declare — li1 "Library Corner" as living is arguably already
correct, and br1 "Staff Room" may want living rather than none. Also noted:
mandatory breaks out-of-repo configs until annotated (mitigate with an error
naming each code and its prefix-implied usage, plus a migration script), and
the 7 changed entries need their own A/B and re-baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 10:32:14 +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
Claude
7ec4e5d121
§39.4: tighten generic-type matching, reverting the harbor rename
Supersedes the previous commit's approach. Renaming harbor's four colliding
codes fixed one programme; tightening the matching rule fixes the rule, so a
room may be called anything. cr1/of/st1/st2 are restored and the examples are
byte-identical to their pre-§39 state -- which also means existing .dom
artefacts (evolved-3M*) stay valid, so migrate_ju3_rename.py is deleted.

The rule: Urb has exactly three GENERIC structural types (get_space_types:
qw/C O S/), the leaves the search creates. Measured across the corpus: 154 C,
110 O, 1 S, not one lowercase generic -- while every programme code is
lowercase, including single-character ones (r, t, m, n). Case is the
discriminator, not length. Every generic test was type[0].lower() in (...), a
case-insensitive PREFIX that swept up any programme code starting with those
letters; they now match the generic set exactly. 30 sites across dom, fitness,
graph, operators, programme, shapecurve and bubble.

NOT applied to the SEMANTIC prefixes: l/k/b/t classify programme codes by first
letter (graph.py builds bedroom<->toilet and kitchen<->living relations from
them) and stay prefix-based. Where the namespaces were mixed in one expression
they were split -- has_circulation's ("b","l","k","c") is three semantic
prefixes plus dom.is_circulation; access()'s ("l","c","s") is semantic l plus
the generic circulation set.

New: dom.GENERIC_{CIRCULATION,OUTSIDE,TYPES} + is_generic(); fitness.
_generic_class(), replacing the _t0 dispatch in quality_size/quality_width/
quality_proportion/value_rate -- the four terms that mattered most and that a
first sweep missed, since they dispatch through a t0 variable rather than an
inline test. graph._adjacency_target resolves a generic adjacency requirement
(programmes write "adjacency: [c, o]") to the generic set while every other
requirement keeps Perl's prefix semantics.

Two subtleties: S is in both generic sets but takes the OUTSIDE parameter
families -- a first translation tested circulation first and silently gave S
the circulation params, caught by test_get_space_params_sahn_proportion. And
validate_codes survives, narrowed to a code spelled exactly C/O/S, which is a
genuine ambiguity; merely starting with c/o/s is now fine.

Invariant asserted as a test: test_scoring_is_invariant_under_programme_code_
spelling relabels one tree and its config together and re-scores. Bit-identical
across 12 comparisons (6 seeds x collapse on/off).

Re-baseline (seed 1, 20k, original names): 58 fails (15 hard / 43 soft) against
the real 37-instance programme, with cr1 at 79.1 m2 vs declared 80 (was 32.9
and 17.1), of/st1/st2 all present and in band, and one fail naming any of them.
57 -> 58 on a 5-instance-harder programme is within noise: "did not regress".

Fallout (§39.5): 2g7.5's CP-SAT seeder win does not survive. Over 6 seeds --
harbor real 102/114 (cpsat loses), harbor old-effective 98/99 (tie, so the win
was already marginal), maple-court 156/144 (cpsat wins). maple is the control:
the solver did not regress, harbor's programme changed. Test xfail'd with that
reason plus a maple companion; both assign_solver flags stay default off.
Filed homemaker-py-w6x to re-check other narrow-margin harbor A/Bs.

345 passed, 1 xfailed, same 7 pre-existing fixture failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 09:45:28 +00:00
Claude
697914fac8
ju3: reject programme codes colliding with the c/o/s generic type prefixes
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
2026-08-26 09:06:08 +00:00
Claude
fd9802e499
DESIGN.md §39: config audit — programme codes collide with the c/o/s type namespace
Answers "are any config requirements actively fighting the engine". One is.

§39.1 NEGATIVE (recorded): no room spec in any corpus programme is internally
contradictory. Using shapecurve.leaf_constraints' validated FAIL_THRESHOLD
inversions, every code admits an (area, aspect) satisfying size, width,
proportion and crinkliness at once, and none needs more than one exposed side.
The "estimated targets are mutually unsatisfiable" hypothesis is falsified.

§39.2 SEVERE: Urb's type system is prefix-based (c = circulation, o/s =
outside) and programme codes share that namespace. A code starting with those
letters is silently reinterpreted, with three unannounced consequences:
check_space_counts SKIPS it outright (never required, no missing or too-many
fail); get_space_params returns generic *_circulation/*_outside params before
consulting self.spaces; and is_circulation/is_outside flip, changing value
rate, crinkliness exemption, and whether it supplies daylight to neighbours.

harbor-house is affected (maple-court, health-centre, programme-house are
clean): cr1 "Common Room with Fireplace" has all three declared targets
overridden (size 80.0 -> 0.0/14.0) and is valued at 50/m2 not 300; of x2 and
st1/st2 lose width/proportion and are treated as outside space. 5 of 37 room
instances (14%) are silently optional.

Measured: the two cr1 leaves converged to 32.9 and 17.1 m2 against a declared
80, with no too-many-spaces fail despite count:1; of/st1/st2 are absent from
the result with zero fails. Compounds with §38.2 -- the largest room in the
programme sits on the wrong side of the x6 circulation value gap, so the
objective is paid to shrink it.

Benchmark validity: every harbor-house fail count in this document was
measured against a 32-instance effective programme, not the 37 its config
declares.

Adds experiments/audit_programme_config.py (namespace + satisfiability
reports). Filed homemaker-py-ju3 (P0). No src changes; 336 passed, same 7
pre-existing fixture failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 08:27:49 +00:00
Claude
1b3a0adc2e
beads: file homemaker-py-2v1 (connectivity mispricing, P0)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-26 07:40:37 +00:00
Claude
753356e47e
beads: file §38 objective-gradient findings
homemaker-py-ssz  (P1) zero-exposure leaves score hard quality=0
homemaker-py-hxi  (P1) buried circulation/outside are negative-value
homemaker-py-tdp  (P1) frontage-budget feasibility bound
homemaker-py-gvb  (P2) crinkliness mis-tiered as SOFT
homemaker-py-1i8  (P2) missing-space cascade weighted by config verbosity
homemaker-py-bdf  (P3) fresh-clone test failures from absent evolved-3M fixtures

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-25 23:18:47 +00:00
Claude
7046c7bd76
Ignore beads auto-import tracking marker
.auto-import-issues.jsonl is a per-machine mtime/size cache bd writes
when auto-importing issues.jsonl; it shouldn't be versioned.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
2026-08-25 23:01:40 +00:00
25e0503ddc bd: sync issues.jsonl export after 8oq/57z creation
Created homemaker-py-8oq (review the 2g7.7 LLM-repair plan with a more
capable model) and homemaker-py-57z (live acceptance-benchmark follow-up,
blocked on ANTHROPIC_API_KEY availability), linked as blockers/dependents
of homemaker-py-2g7.7 per the planning session on 2026-08-05.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-06 08:08:07 +01:00
91f7626a77 bd: close homemaker-py-pek
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-05 16:59:25 +01:00
5f5e640906 DESIGN.md sec 13.11: correct stale 7ua note, cross-ref follow-up 4ok
homemaker-py-7ua is fixed but only covers run_staged_search.py's
LEAFSHARE/MULTIUSE-gated rescore path. Baseline (LEAFSHARE=0/MULTIUSE=0)
runs still MISMATCH for the same underlying reason -- driver.search_staged
has no collapse_insearch param and always runs with it on -- tracked as
the new homemaker-py-4ok.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-05 13:54:42 +01:00
88f36177bb homemaker-py-7ua: pin collapse_insearch=True in run_staged_search.py rescore
driver.search_staged has no param to disable collapse_insearch, so its
inner evaluator always runs with search()'s collapse_insearch=True
default. The final _native_score() sanity rescore loaded patterns.config
fresh with no override, so it silently diverged from search-time conf
whenever leaf_sharing was on, producing a false MISMATCH. Verified with
a smoke run: pre-fix MISMATCH, post-fix OK with identical scores.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-05 10:08:04 +01:00
5af8827a10 bd: sync issues.jsonl export after sd3 close
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-05 07:55:02 +01:00
35adcd4b4d homemaker-py-sd3: fix vacuous 94g keep-better guard (collapse_insearch leak)
driver.collapse_best built its evaluator with _fitness_for's default
collapse_insearch=True, so collapse_finish's base_fails/cand_fails were
both measured through score_with_fails' own auto-collapse pass -- base
silently equalled collapsed on 5/5 probed files, making the "keep only
if fails don't increase" safety guard vacuous and understating 94g's
real effect in logs. fitness.collapse_finish now forces canonical
(collapse_insearch=False) scoring for its own measurement regardless of
self's config; collapse_best now builds its evaluator canonically too
(matching what homemaker-fitness reports for the written .dom) and
threads max_share/conn_grade through. Same-family fix in
search_annealed's no-polish-budget rescore branch, which silently
defaulted to collapse_insearch=True via _evaluate's default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-05 07:52:44 +01:00
30adbf4948 homemaker-py-2g7.6: NO-GO on graph-first slicing-tree construction via rectangular dualization
Research spike (DESIGN.md §37.8): classical rectangular dualization assumes
one-vertex-one-rectangle, which breaks on harbor's circulation hub (an
emergent-shape multi-leaf region, not a fixed single module) and is overkill
on the room-only adjacency graph (a trivial 3-edge matching already fully
satisfied by §11.7's seeding). No literature precedent for the multi-storey
stacking constraint either. Not prototyping; no code changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uCyZJCh5mBuA2yEFxgQpo
2026-08-05 00:25:23 +01:00
eb1248d3a5 bd: sync issues.jsonl export after 2g7.5 close
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-05 00:02:49 +01:00
ade7f9a131 homemaker-py-2g7.5: full acceptance-criteria A/B (harbor+maple, 3 seeds, 20k budget)
CP-SAT assignment does not clearly beat greedy end-to-end: worse on
harbor-house hard fails, roughly a wash on maple-court, and the reassign
operator fired in only 1 of 18 runs. Keeps assign_solver="greedy" and
enable_reassign=False as defaults; closes the bead's remaining acceptance
criterion. Raw results in experiments/results/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-05 00:01:00 +01: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
b2eded5641 bd: sync issues.jsonl export after koo close
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 23:33:51 +01:00
3aa08ee6ff homemaker-py-koo: multi-storey (below-link) support for the shape-curve DP
Generalise shapecurve.py's DP to process dom.levels(root) bottom-up per
storey instead of assuming a single free tree. A divided node's split is
free only per solver.free_branches' own criterion (below is None or
undivided there) -- geometry.coordinate always mirrors a below-linked
node's corners from the storey below regardless of whether that storey's
counterpart is divided, so every free region at any storey reduces to the
exact same single-region problem the pre-existing _check/realise already
solved. New _region_roots finds below-fixed leaves (checked directly,
gridless) and below-fixed-box/free-split fringe nodes per storey;
_solve_all_levels realises each storey before checking the one above and
snapshots+restores on any infeasibility, preserving solve()'s all-or-nothing
and is_feasible()'s never-writes contracts across the whole tree.
eligible() now allows any storey count.

Validated on the real (non-de-risked) examples/harbor-house: 200 random
2-storey topologies, DP-vs-NM agreement 99.5%, 0 false negatives, 117.7x
speedup (DESIGN.md §37.6). Full suite 397 passed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 23:30:18 +01:00
deb12bc726 bd: sync issues.jsonl export after wkh close
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 21:19:22 +01:00
467c732260 homemaker-py-wkh: DP-exact hard pre-filter for driver.py's shape-feasibility prune
Adds shapecurve.is_feasible() (a non-mutating refactor of solve()'s check
phase) and a shapecurve_prune flag composing the DP's exact feasible/
infeasible verdict with operators.predicted_shape_fails' existing heuristic
prune: DP-feasible vetoes a heuristic prune outright; DP-infeasible only
hard-prunes when the incumbent already has zero total fails (exact, since
infeasible proves the shape-fail floor is >=1); otherwise defers unchanged
to today's heuristic threshold. Conservative by design since a wrong prune
is unrecoverable.

Validated 0/400 false negatives across two structurally distinct plots
(harbor-house-l0 + a newly-added programme-house sweep, the first genuinely
non-rectangular plot this DP has been checked against). The real
driver.search A/B on harbor-house-l0 measured NULL (byte-identical off/on)
for a root-caused, pre-existing reason: predicted_shape_fails rarely
triggers organically at this scale, so neither new branch had an opening to
fire -- not a defect in this change. Full writeup: DESIGN.md §37.5.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 21:10:28 +01:00
229c3c6933 homemaker-py-6xh: wire shape-curve DP into driver.py as an NM warm-start
Promotes the validated shape-curve DP (experiments/shapecurve_spike.py,
2g7.4, DESIGN.md §37.2) from a reference-only spike into
src/homemaker_layout/shapecurve.py, and wires it into driver._evaluate as a
warm-start for innerloop.optimise: when eligible (single storey, no
leaf_sharing/superpose/max_share/multi_use) and no caller-supplied x0, the
DP's exact shape-feasible ratio point is written onto the tree before NM
runs, off by default (shapecurve_warmstart=/--shapecurve-warmstart).

Caught and fixed a latent bug promoting the spike: realise() could leave
numpy.float64 in `division`, which yaml.safe_dump can't serialise — the
original spike never round-tripped through dom.dumps so this was never hit.

A/B on harbor-house-l0 (experiments/ab_shapecurve_warmstart.py, budget=2000,
5 seeds): mean total fails 16.6 (on) vs 19.6 (off), ~3.5x mean fitness
improvement; mean hard-fail count alone was a noise-level wash at this
sample size. Full writeup in DESIGN.md §37.4.

Deliberately deferred to new tracked beads (children of 2g7): DP-exact hard
pre-filter (wkh), multi-storey below-link support (koo), leaf_sharing/
co_type modelling (tym), true skew-quad polygon algebra (ekc) — 6xh stays
in_progress pending those.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 18:43:28 +01:00
9296f75cdb homemaker-py-2g7.1: plan->dom composer (SVG trace -> slicing-tree .dom)
Composer half of the ground-truth bead: examples/harbor-house/drawings/
harbor-house 1.svg turned out to be a Bonsai/Blender render of 3m.dom's own
IFC (32 IfcSpace paths == 3m.dom's upper-storey leaf count), not a human
trace, so no usable reference exists yet -- this builds and tests the
pipeline that will consume one once traced. compose.py parses storey-N
Inkscape layers of cut-lines + labels against a boundary-stub .dom (plot/
height/elevation only, no room shapes to keep aligned across storeys) and
recursively detects guillotine cuts, mirroring geometry.py's own
division-line algebra; non-slicible regions and label mismatches are
reported by location rather than guessed at. homemaker-compose CLI added.
Renamed dom._link to public dom.link since compose.py needs to re-link from
outside dom.py. Full design writeup in DESIGN.md sec 37.3; actual human
tracing of harbor-house/programme-house is tracked as follow-up under
2g7.1, still open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 11:03:02 +01:00
eb1d2fc7f0 bd: sync issues.jsonl export after 2g7.4 correction comment
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-03 07:21:27 +01:00
85c1183d4c homemaker-py-2g7.4: shape-curve DP prototype (Otten/Stockmeyer) — PASS
Prototype + validation for an exact size/width/proportion feasibility DP
over a frozen slicing topology, replacing the ~80-200 eval Nelder-Mead
inner loop's approximate answer to the same question with one bottom-up
pass (experiments/shapecurve_spike.py). Leaf feasible regions are exact
FAIL_THRESHOLD-inversions of fitness.py's quality_size/width/proportion;
internal-node composition runs on a shared discretised grid.

Validated on harbor-house-l0 (experiments/validate_shapecurve.py, 200
random topologies vs NM minimising shape-fail-count directly): 99.0%
agreement (0 false negatives), 93.6x speedup at grid_n=150, plot-level
bbox approximation error quantified at +7.5% (root-causing both observed
false positives). All three acceptance criteria cleared -- see DESIGN.md
§37.2 for full results and the caveats/scope not covered (multi-storey,
leaf_sharing/co_type, true skew-quad regions). Kept as a reference spike,
same status as experiments/autodiff_spike.py (§34); production wiring
into driver.py filed as homemaker-py-6xh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-02 23:43:30 +01:00
3f2438797f bd: sync issues.jsonl export after 2g7.3 close + p6t create
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-02 19:20:08 +01:00
c644f279eb homemaker-py-2g7.3: record A/B acceptance result, close bead
DESIGN.md §37.1: hard/soft tiering A/B (harbor-house + maple-court, 3 seeds,
20k evals/run) shows hard-fail mean strictly better under the tiered
comparator on both programmes (harbor 11.67->5.33, maple 19.33->14.00) at
the cost of higher soft/total fails — the intended trade. ACCEPTANCE: PASS.

Filed homemaker-py-p6t as a non-blocking follow-up: race tiered vs flat to
0 hard fails (convergence speed) rather than composition at a fixed budget.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LSwQwpEaHFBkeVSDDWd75S
2026-08-02 19:00:48 +01:00