Shape-curve warm-start cannot pay off as wired; no A/B run

With tym landed the DP finally runs on real leaf-sharing searches, so v4s
asked for the search-level A/B. No A/B was run: two structural facts make
the payoff zero before any seed is drawn, and measuring a no-op would have
produced a null that reads like a measurement rather than a fact.

Reach. driver._evaluate gates the warm-start on `x0 is None`, and every
child gets x0 = innerloop.warm_x0(...) from its parent (driver.py:764), so
it fires only for bootstrap-population individuals. Instrumented over a
4000-eval run: 8 DP solves, exactly pop_size. At the corpus baseline's 500k
budget that is 8 evaluations out of 500,000.

Applicability. Constructed-seed feasibility over 6 seeds: harbor-house 0/6
with leaf_sharing on AND off, programme-house 0/6, harbor-house-l0 4-5/6
(75% over a real search). Even those 8 bootstrap individuals get no warm
start on the real programmes; only l0, the reduced programme the DP was
validated on, is feasible.

The infeasibility is correct, checked because it is the dangerous
direction: on 4 harbor topologies the DP rejects, an NM polish minimising
the shape-fail family reaches 14/16/17/16 fails, never 0 -- 0/4 false
negatives. Full harbor is genuinely shape-infeasible per topology where l0
is not. Per topology only: programme-house reaches 0 hard / 1 soft in the
500k baseline while its constructed seeds are DP-infeasible.

shapecurve_prune is separately inert -- it acts only inside the
feasibility_max_shape_fails branch and its exact arm needs best_n_fails<=0.

tym was still worth doing: the DP now models leaf-sharing exactly (38.23)
and fires on real runs, which is what turned an untestable question into a
structural answer.

Filed homemaker-py-ldj for what would have to change: give the DP reach
beyond the bootstrap, and/or use infeasibility as a ranking signal (an
infeasible topology still has a shape-fail floor, which is what a
pre-filter wants) rather than a precondition that discards it.

Closes homemaker-py-v4s.

Lint at parity (46).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
This commit is contained in:
Claude 2026-08-30 07:41:16 +00:00
parent 4c95505907
commit 481e83cdbf
No known key found for this signature in database
2 changed files with 58 additions and 1 deletions

View file

@ -98,6 +98,7 @@
{"_type":"issue","id":"homemaker-py-nyb","title":"High-locality topology operators (mutation + subtree crossover)","description":"DESIGN.md §5, §7 Phase 2, §8.4. Mutation moves: divide/undivide leaf, swap children, rotate cut, retype leaf, per-floor delta edits, storey add/delete (cf. Urb Mutate.pm — but geometry sliding belongs to the inner loop, not the operator set). Crossover: area-matched subtree exchange (a subtree = a contiguous region, so crossover is meaningful — Crossover.pm). Operators must be high-locality: small genome change =\u003e small phenotype change, so warm-started inner loops stay cheap.","acceptance_criteria":"Each operator produces valid genomes (oracle scores them without error); locality measured (mean fitness/geometry perturbation per operator)","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:37:27Z","created_by":"Bruno Postle","updated_at":"2026-06-12T13:07:37Z","started_at":"2026-06-12T12:54:23Z","closed_at":"2026-06-12T13:07:37Z","close_reason":"operators.py lands: 7 mutations + area-matched crossover, valid-by-construction via genome.encode repair. 115/115 oracle-valid children; locality measured: geom-pert 0.07-0.33 per op, fitness-pert 0.68-0.99 (0.5^n cliff flags raw moves — warm restart + penalty reshaping confirmed load-bearing). Also fixed dom._link stale below-links on structural mutation.","dependencies":[{"issue_id":"homemaker-py-nyb","depends_on_id":"homemaker-py-k2g","type":"blocks","created_at":"2026-06-12T00:39:36Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"_type":"issue","id":"homemaker-py-nyb","title":"High-locality topology operators (mutation + subtree crossover)","description":"DESIGN.md §5, §7 Phase 2, §8.4. Mutation moves: divide/undivide leaf, swap children, rotate cut, retype leaf, per-floor delta edits, storey add/delete (cf. Urb Mutate.pm — but geometry sliding belongs to the inner loop, not the operator set). Crossover: area-matched subtree exchange (a subtree = a contiguous region, so crossover is meaningful — Crossover.pm). Operators must be high-locality: small genome change =\u003e small phenotype change, so warm-started inner loops stay cheap.","acceptance_criteria":"Each operator produces valid genomes (oracle scores them without error); locality measured (mean fitness/geometry perturbation per operator)","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:37:27Z","created_by":"Bruno Postle","updated_at":"2026-06-12T13:07:37Z","started_at":"2026-06-12T12:54:23Z","closed_at":"2026-06-12T13:07:37Z","close_reason":"operators.py lands: 7 mutations + area-matched crossover, valid-by-construction via genome.encode repair. 115/115 oracle-valid children; locality measured: geom-pert 0.07-0.33 per op, fitness-pert 0.68-0.99 (0.5^n cliff flags raw moves — warm restart + penalty reshaping confirmed load-bearing). Also fixed dom._link stale below-links on structural mutation.","dependencies":[{"issue_id":"homemaker-py-nyb","depends_on_id":"homemaker-py-k2g","type":"blocks","created_at":"2026-06-12T00:39:36Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"homemaker-py-k2g","title":"Topology genome: base-floor tree + per-floor deltas + type assignment","description":"DESIGN.md §5.2, §7 Phase 2. Genome = base-floor slicing topology (primary) + per-leaf type assignment + per-floor divide/undivide deltas (Below-inheritance as regulariser; cut owned by lowest storey where its path is divided — §10). Must round-trip to/from dom.py Node trees so the oracle and inner loop consume it directly. Includes storey count and per-floor type overrides.","acceptance_criteria":"Genome \u003c-\u003e .dom round-trip on all 35 corpus files preserves fitness; multi-storey wall stacking preserved","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:37:26Z","created_by":"Bruno Postle","updated_at":"2026-06-12T12:52:34Z","started_at":"2026-06-12T10:55:21Z","closed_at":"2026-06-12T12:52:34Z","close_reason":"genome.py encode/decode lands. 35/35 oracle fitness parity after round-trip (flag-on); genome fixed-point + owned-projection tests. Dead-field discovery: corpus upper storeys carry drifted dead divisions (97) and rotations (187) — canonicalised by decode, validated fitness-neutral.","dependency_count":0,"dependent_count":1,"comment_count":0} {"_type":"issue","id":"homemaker-py-k2g","title":"Topology genome: base-floor tree + per-floor deltas + type assignment","description":"DESIGN.md §5.2, §7 Phase 2. Genome = base-floor slicing topology (primary) + per-leaf type assignment + per-floor divide/undivide deltas (Below-inheritance as regulariser; cut owned by lowest storey where its path is divided — §10). Must round-trip to/from dom.py Node trees so the oracle and inner loop consume it directly. Includes storey count and per-floor type overrides.","acceptance_criteria":"Genome \u003c-\u003e .dom round-trip on all 35 corpus files preserves fitness; multi-storey wall stacking preserved","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:37:26Z","created_by":"Bruno Postle","updated_at":"2026-06-12T12:52:34Z","started_at":"2026-06-12T10:55:21Z","closed_at":"2026-06-12T12:52:34Z","close_reason":"genome.py encode/decode lands. 35/35 oracle fitness parity after round-trip (flag-on); genome fixed-point + owned-projection tests. Dead-field discovery: corpus upper storeys carry drifted dead divisions (97) and rotations (187) — canonicalised by decode, validated fitness-neutral.","dependency_count":0,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"homemaker-py-d0s","title":"Experiment: inner-loop optimiser bake-off at equal oracle budgets","description":"DESIGN.md §7 Phase 1, §8.3. DOF is only ~rooms-1 (67 on corpus). Compare Nelder-Mead vs CMA-ES vs batched multi-start pattern search at equal oracle-call budgets, measuring fitness gained per oracle call and wall-clock (batch-friendliness matters — §4.6). Measure, don't commit blind.","acceptance_criteria":"Table of fitness-per-budget across \u003e=3 candidates; one optimiser chosen and recorded in DESIGN.md","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:36:59Z","created_by":"Bruno Postle","updated_at":"2026-06-13T08:48:13Z","started_at":"2026-06-12T21:22:15Z","closed_at":"2026-06-13T08:48:13Z","close_reason":"Bake-off complete: CMA-ES confirmed as Phase 1/2 optimiser. NM wins quality per eval but sequential architecture incompatible with batching (§4.6). Compass stalls on narrow valleys. Results in DESIGN.md §8.3 and experiments/bakeoff_innerloop.*","dependencies":[{"issue_id":"homemaker-py-d0s","depends_on_id":"homemaker-py-1p0","type":"blocks","created_at":"2026-06-12T00:39:35Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-d0s","title":"Experiment: inner-loop optimiser bake-off at equal oracle budgets","description":"DESIGN.md §7 Phase 1, §8.3. DOF is only ~rooms-1 (67 on corpus). Compare Nelder-Mead vs CMA-ES vs batched multi-start pattern search at equal oracle-call budgets, measuring fitness gained per oracle call and wall-clock (batch-friendliness matters — §4.6). Measure, don't commit blind.","acceptance_criteria":"Table of fitness-per-budget across \u003e=3 candidates; one optimiser chosen and recorded in DESIGN.md","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:36:59Z","created_by":"Bruno Postle","updated_at":"2026-06-13T08:48:13Z","started_at":"2026-06-12T21:22:15Z","closed_at":"2026-06-13T08:48:13Z","close_reason":"Bake-off complete: CMA-ES confirmed as Phase 1/2 optimiser. NM wins quality per eval but sequential architecture incompatible with batching (§4.6). Compass stalls on narrow valleys. Results in DESIGN.md §8.3 and experiments/bakeoff_innerloop.*","dependencies":[{"issue_id":"homemaker-py-d0s","depends_on_id":"homemaker-py-1p0","type":"blocks","created_at":"2026-06-12T00:39:35Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-ldj","title":"shapecurve warm-start reaches only the bootstrap population, and DP-infeasibility is used as a precondition rather than a signal","description":"Split out of homemaker-py-v4s (DESIGN.md §38.24), which found the shape-curve\nwarm-start cannot pay off as currently wired. Two independent limits, either of\nwhich alone makes it a no-op on the real corpus:\n\n1. REACH. driver._evaluate gates the warm-start on `x0 is None`, and every child\n receives x0 = innerloop.warm_x0(child_root, ratios) from its parent\n (driver.py:764). So it fires only for bootstrap-population individuals --\n instrumented at 8 DP solves in a 4000-eval run, exactly pop_size. At the 500k\n budget the corpus baseline uses, that is 8 evaluations out of 500,000.\n\n2. APPLICABILITY. On harbor-house and programme-house the DP finds 0/6\n constructed seeds feasible (with leaf_sharing on OR off), so even those 8 get\n no warm start. Only harbor-house-l0, the reduced programme it was validated\n on, is feasible (4-5/6, and 75% over a real search). The infeasibility is\n CORRECT -- 0/4 false negatives, NM reaches only 14-17 shape fails on\n topologies the DP rejects -- so this is not a bug to fix but a fact about the\n full programmes.\n\nTwo directions, either of which would make the feature testable:\n\n a) run the DP for children too, not only where x0 is None, so its reach is the\n search rather than the bootstrap. Cost check needed: DP time was 0.05s per\n 8 solves, so it is cheap per call, but per-child would be ~pop_size x more\n often.\n\n b) use DP-infeasibility as a SIGNAL rather than a precondition. An infeasible\n topology still has a best-achievable shape-fail floor, and that floor is\n precisely what a pre-filter wants to rank candidates on -- currently an\n infeasible verdict just falls through to the cold start and the information\n is discarded.\n\n(b) looks the more valuable: it turns a binary gate that almost always says \"no\"\ninto a continuous ranking signal, and it would apply to the programmes that\nmatter rather than only to l0.\n","status":"open","priority":3,"issue_type":"task","owner":"noreply@anthropic.com","created_at":"2026-08-30T07:40:49Z","created_by":"Claude","updated_at":"2026-08-30T07:40:49Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-7t1","title":"cpsat solver limits are not threadable from constructive_topology, so tests pay full solve cost","description":"homemaker-py-vjd raised solve_room_labels' wall-clock backstop from 2.0s to 30.0s so the deterministic budget governs (it had silently become the binding, load-dependent cap -- 2 of 24 harbor solves returned FEASIBLE not OPTIMAL). Correct, but the full test suite went from ~4.5 min to ~10 min: the three assign_cpsat tests now dominate it, because the solves that used to be truncated at 2s run to optimality.\\n\\nThe tests cannot opt out. operators.constructive_topology takes assign_solver but does not thread time_limit_s / deterministic_limit through to cpsat.solve_room_labels, so there is no way to ask for a cheaper solve in a test.\\n\\nThread the two limits through constructive_topology (and _assign_adjacency_aware) as optional parameters defaulting to the current values, then have the assign_cpsat tests pass a smaller deterministic_limit. The DEFAULT must not change -- the point is that production solves run to optimality deterministically; only the tests should economise.","acceptance_criteria":"constructive_topology accepts and forwards the solver limits; the assign_cpsat tests use a reduced deterministic_limit and the suite returns to roughly its previous runtime; default limits unchanged and all solves still OPTIMAL in the vjd measurement.","status":"closed","priority":3,"issue_type":"task","assignee":"Claude","owner":"noreply@anthropic.com","created_at":"2026-08-29T14:47:36Z","created_by":"Claude","updated_at":"2026-08-29T18:55:52Z","started_at":"2026-08-29T18:34:05Z","closed_at":"2026-08-29T18:55:52Z","close_reason":"Done. Suite ~10 min -\u003e ~6.8 min, with the remaining cost identified as genuine.\n\nTwo recoveries, and one deliberate non-recovery:\n\n1. test_assign_cpsat_matches_or_beats_greedy_secondary_adjacency ran the cpsat\n arm THREE times and averaged. Its own comment gave the reason: the cpsat path\n \"is not yet bit-reproducible (homemaker-py-fdp)\", so a single 10-seed\n aggregate could straddle greedy's deterministic value and the test was flaky\n by construction. fdp is fixed (§38.15), so one pass says exactly what three\n did. That was work spent papering over a bug that no longer exists -- worth\n more than the threading itself.\n\n2. constructive_topology and _assign_adjacency_aware now forward an optional\n cpsat_limits=(time_limit_s, deterministic_limit); default None keeps\n solve_room_labels' own defaults, so PRODUCTION IS UNCHANGED (verified: 24/24\n harbor solves still OPTIMAL at the defaults). It is not a tuning knob -- it\n exists so a test whose claim does not depend on optimality can economise.\n test_construction_assign_cpsat_yields_valid_seed asserts invariants only and\n uses it: 91s -\u003e 53s.\n\n Guarded a real trap there: too small a budget makes solve_room_labels return\n None, _assign_adjacency_aware falls back to GREEDY, and the test would pass\n while exercising nothing. It now counts fallbacks and fails if any occur.\n\n3. The two QUALITY comparisons keep the full budget deliberately. Their claims\n are about the optimum; cheapening them would weaken what they assert. That is\n why the suite does not return all the way to 4.5 min -- the residue is the\n honest price of solves reaching optimality deterministically, and should not\n be optimised away.\n\nAlso corrected a stale claim in the secondary-adjacency test's comment: it now\nnotes it measures ONLY \"not adjacent to\" fails, and is not a claim that cpsat\nseeds better overall -- §38.20 measured it markedly worse on total fails.\n","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-7t1","title":"cpsat solver limits are not threadable from constructive_topology, so tests pay full solve cost","description":"homemaker-py-vjd raised solve_room_labels' wall-clock backstop from 2.0s to 30.0s so the deterministic budget governs (it had silently become the binding, load-dependent cap -- 2 of 24 harbor solves returned FEASIBLE not OPTIMAL). Correct, but the full test suite went from ~4.5 min to ~10 min: the three assign_cpsat tests now dominate it, because the solves that used to be truncated at 2s run to optimality.\\n\\nThe tests cannot opt out. operators.constructive_topology takes assign_solver but does not thread time_limit_s / deterministic_limit through to cpsat.solve_room_labels, so there is no way to ask for a cheaper solve in a test.\\n\\nThread the two limits through constructive_topology (and _assign_adjacency_aware) as optional parameters defaulting to the current values, then have the assign_cpsat tests pass a smaller deterministic_limit. The DEFAULT must not change -- the point is that production solves run to optimality deterministically; only the tests should economise.","acceptance_criteria":"constructive_topology accepts and forwards the solver limits; the assign_cpsat tests use a reduced deterministic_limit and the suite returns to roughly its previous runtime; default limits unchanged and all solves still OPTIMAL in the vjd measurement.","status":"closed","priority":3,"issue_type":"task","assignee":"Claude","owner":"noreply@anthropic.com","created_at":"2026-08-29T14:47:36Z","created_by":"Claude","updated_at":"2026-08-29T18:55:52Z","started_at":"2026-08-29T18:34:05Z","closed_at":"2026-08-29T18:55:52Z","close_reason":"Done. Suite ~10 min -\u003e ~6.8 min, with the remaining cost identified as genuine.\n\nTwo recoveries, and one deliberate non-recovery:\n\n1. test_assign_cpsat_matches_or_beats_greedy_secondary_adjacency ran the cpsat\n arm THREE times and averaged. Its own comment gave the reason: the cpsat path\n \"is not yet bit-reproducible (homemaker-py-fdp)\", so a single 10-seed\n aggregate could straddle greedy's deterministic value and the test was flaky\n by construction. fdp is fixed (§38.15), so one pass says exactly what three\n did. That was work spent papering over a bug that no longer exists -- worth\n more than the threading itself.\n\n2. constructive_topology and _assign_adjacency_aware now forward an optional\n cpsat_limits=(time_limit_s, deterministic_limit); default None keeps\n solve_room_labels' own defaults, so PRODUCTION IS UNCHANGED (verified: 24/24\n harbor solves still OPTIMAL at the defaults). It is not a tuning knob -- it\n exists so a test whose claim does not depend on optimality can economise.\n test_construction_assign_cpsat_yields_valid_seed asserts invariants only and\n uses it: 91s -\u003e 53s.\n\n Guarded a real trap there: too small a budget makes solve_room_labels return\n None, _assign_adjacency_aware falls back to GREEDY, and the test would pass\n while exercising nothing. It now counts fallbacks and fails if any occur.\n\n3. The two QUALITY comparisons keep the full budget deliberately. Their claims\n are about the optimum; cheapening them would weaken what they assert. That is\n why the suite does not return all the way to 4.5 min -- the residue is the\n honest price of solves reaching optimality deterministically, and should not\n be optimised away.\n\nAlso corrected a stale claim in the secondary-adjacency test's comment: it now\nnotes it measures ONLY \"not adjacent to\" fails, and is not a claim that cpsat\nseeds better overall -- §38.20 measured it markedly worse on total fails.\n","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-5nw","title":"health-centre has no dedicated WC for the treatment room, so the Brand credit cannot be declared","description":"Split out of homemaker-py-3qj (DESIGN.md §38.14). The owner ruled that a treatment room 'may give access to a toilet, but this would be a dedicated toilet'. health-centre offers only t9 Public WC (count 2) and t10 Staff WC; declaring either adjacent to tr1 would make a public or staff WC dedicated to the treatment room, contradicting the ruling.\\n\\nEarning the adaptability credit here means ADDING a dedicated WC to the brief -- a new space with its own size/usage/adjacency -- which is programme authoring, not a config tweak, and wants the owner's decision on whether the building should have one.\\n\\nRelated: maple-court's level 1 carries four Neighborhoods and no toilet on that level at all, which may be a genuine gap in that brief too.","acceptance_criteria":"Either a dedicated WC is added to health-centre's programme and declared adjacent to tr1, or it is recorded as a deliberate omission; same question answered for maple-court's level 1.","status":"open","priority":3,"issue_type":"task","owner":"noreply@anthropic.com","created_at":"2026-08-29T10:57:27Z","created_by":"Claude","updated_at":"2026-08-29T10:57:27Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-5nw","title":"health-centre has no dedicated WC for the treatment room, so the Brand credit cannot be declared","description":"Split out of homemaker-py-3qj (DESIGN.md §38.14). The owner ruled that a treatment room 'may give access to a toilet, but this would be a dedicated toilet'. health-centre offers only t9 Public WC (count 2) and t10 Staff WC; declaring either adjacent to tr1 would make a public or staff WC dedicated to the treatment room, contradicting the ruling.\\n\\nEarning the adaptability credit here means ADDING a dedicated WC to the brief -- a new space with its own size/usage/adjacency -- which is programme authoring, not a config tweak, and wants the owner's decision on whether the building should have one.\\n\\nRelated: maple-court's level 1 carries four Neighborhoods and no toilet on that level at all, which may be a genuine gap in that brief too.","acceptance_criteria":"Either a dedicated WC is added to health-centre's programme and declared adjacent to tr1, or it is recorded as a deliberate omission; same question answered for maple-court's level 1.","status":"open","priority":3,"issue_type":"task","owner":"noreply@anthropic.com","created_at":"2026-08-29T10:57:27Z","created_by":"Claude","updated_at":"2026-08-29T10:57:27Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-3i3","title":"Is a missing required room correctly weighted at 1/32 of fitness?","description":"Split out of homemaker-py-1i8 (DESIGN.md §38.12), which deliberately fixed only the verbosity dependence and left the magnitude untouched.\n\nA missing required room now costs a fixed 5 fails, so under value *= 0.5 ** len(failures) it is a 1/32 penalty -- 32x the weight of a single crinkliness or proportion fail. Nobody has ever checked that ratio against intent. It may well be right (a missing room is a brief not met, arguably the most serious defect available), but it is currently an accident of how the cascade was built rather than a decision.\n\nThe question is what the objective should SAY, so it wants an owner ruling first and a measurement second: does the cascade's weight crowd out geometry-quality signal in the region where the search actually operates? Note most corpus layouts carry several missing instances at once, so the compounding is steep -- three missing rooms is 1/32768.","acceptance_criteria":"A recorded decision on the intended weight of a missing required room relative to a quality fail, with the cascade's fixed count set to match it; if the answer is 'as-is', that is recorded as a decision rather than left as an accident.","status":"open","priority":3,"issue_type":"task","owner":"noreply@anthropic.com","created_at":"2026-08-29T09:33:24Z","created_by":"Claude","updated_at":"2026-08-29T09:33:24Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-3i3","title":"Is a missing required room correctly weighted at 1/32 of fitness?","description":"Split out of homemaker-py-1i8 (DESIGN.md §38.12), which deliberately fixed only the verbosity dependence and left the magnitude untouched.\n\nA missing required room now costs a fixed 5 fails, so under value *= 0.5 ** len(failures) it is a 1/32 penalty -- 32x the weight of a single crinkliness or proportion fail. Nobody has ever checked that ratio against intent. It may well be right (a missing room is a brief not met, arguably the most serious defect available), but it is currently an accident of how the cascade was built rather than a decision.\n\nThe question is what the objective should SAY, so it wants an owner ruling first and a measurement second: does the cascade's weight crowd out geometry-quality signal in the region where the search actually operates? Note most corpus layouts carry several missing instances at once, so the compounding is steep -- three missing rooms is 1/32768.","acceptance_criteria":"A recorded decision on the intended weight of a missing required room relative to a quality fail, with the cascade's fixed count set to match it; if the answer is 'as-is', that is recorded as a decision rather than left as an accident.","status":"open","priority":3,"issue_type":"task","owner":"noreply@anthropic.com","created_at":"2026-08-29T09:33:24Z","created_by":"Claude","updated_at":"2026-08-29T09:33:24Z","dependency_count":0,"dependent_count":0,"comment_count":0}
@ -105,7 +106,7 @@
{"_type":"issue","id":"homemaker-py-bdf","title":"7 tests fail in a fresh clone: evolved-3M*.dom fixtures are referenced but not checked in","description":"On a clean checkout (no local scratch), 'pytest' gives 7 failed / 331 passed / 72 skipped. All 7 trace to missing .dom fixtures that tests reference but the repo does not contain: examples/harbor-house/evolved-3M.dom and evolved-3M-nols-3.dom (the 3M-eval run outputs cited throughout DESIGN.md). tests/test_conn_grade.py raises FileNotFoundError on them (4 tests); tests/test_collapse_insearch.py fails for the same reason (2 tests); and tests/test_fitness.py::test_classify_fail_tier_covers_full_corpus asserts 'checked \u003e 0' but finds zero corpus files to walk, so it fails with 'assert 0 \u003e 0' -- i.e. it is a vacuous test that only appeared to pass on a machine that happened to have the artefacts. Confirmed pre-existing by stashing all local changes and re-running on clean HEAD.","design":"Either commit the two .dom artefacts (they are small YAML), regenerate them from a documented command and commit that, or skip the affected tests via pytest.importorskip-style guards when the fixture is absent. The corpus-walk test should skip rather than assert when it finds nothing, so a fresh clone does not report a false failure -- but only after the fixture question is settled, since making it skip silently would hide real coverage loss.","acceptance_criteria":"pytest is green on a fresh clone with no local artefacts, and test_classify_fail_tier_covers_full_corpus either checks a real corpus or skips explicitly.","status":"closed","priority":3,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-25T23:18:02Z","created_by":"Claude","updated_at":"2026-08-28T22:41:24Z","closed_at":"2026-08-28T22:41:24Z","close_reason":"Fixed by the owner: 'Add missing test files' (0d4ae7a) checked in the evolved-3M*.dom fixtures. Fresh-clone failures went 7 -\u003e 2, and the 4 conn_grade tests plus one collapse_insearch test now pass.\n\nThe 2 that remain are NOT fixture problems and are tracked separately:\n - test_collapse_insearch_reproduces_94g_finish_time_result -- stale numeric baseline, see homemaker-py-ut5\n - test_classify_fail_tier_covers_full_corpus -- asserts checked \u003e 0 over examples/**/*.fails, which are generated artefacts absent from a clean tree, so the test passes only on a machine that has run the scorer. Filed separately.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-bdf","title":"7 tests fail in a fresh clone: evolved-3M*.dom fixtures are referenced but not checked in","description":"On a clean checkout (no local scratch), 'pytest' gives 7 failed / 331 passed / 72 skipped. All 7 trace to missing .dom fixtures that tests reference but the repo does not contain: examples/harbor-house/evolved-3M.dom and evolved-3M-nols-3.dom (the 3M-eval run outputs cited throughout DESIGN.md). tests/test_conn_grade.py raises FileNotFoundError on them (4 tests); tests/test_collapse_insearch.py fails for the same reason (2 tests); and tests/test_fitness.py::test_classify_fail_tier_covers_full_corpus asserts 'checked \u003e 0' but finds zero corpus files to walk, so it fails with 'assert 0 \u003e 0' -- i.e. it is a vacuous test that only appeared to pass on a machine that happened to have the artefacts. Confirmed pre-existing by stashing all local changes and re-running on clean HEAD.","design":"Either commit the two .dom artefacts (they are small YAML), regenerate them from a documented command and commit that, or skip the affected tests via pytest.importorskip-style guards when the fixture is absent. The corpus-walk test should skip rather than assert when it finds nothing, so a fresh clone does not report a false failure -- but only after the fixture question is settled, since making it skip silently would hide real coverage loss.","acceptance_criteria":"pytest is green on a fresh clone with no local artefacts, and test_classify_fail_tier_covers_full_corpus either checks a real corpus or skips explicitly.","status":"closed","priority":3,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-25T23:18:02Z","created_by":"Claude","updated_at":"2026-08-28T22:41:24Z","closed_at":"2026-08-28T22:41:24Z","close_reason":"Fixed by the owner: 'Add missing test files' (0d4ae7a) checked in the evolved-3M*.dom fixtures. Fresh-clone failures went 7 -\u003e 2, and the 4 conn_grade tests plus one collapse_insearch test now pass.\n\nThe 2 that remain are NOT fixture problems and are tracked separately:\n - test_collapse_insearch_reproduces_94g_finish_time_result -- stale numeric baseline, see homemaker-py-ut5\n - test_classify_fail_tier_covers_full_corpus -- asserts checked \u003e 0 over examples/**/*.fails, which are generated artefacts absent from a clean tree, so the test passes only on a machine that has run the scorer. Filed separately.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-4ok","title":"run_staged_search.py rescore still MISMATCHes on baseline (LEAFSHARE=0/MULTIUSE=0) arms","description":"Follow-up to homemaker-py-7ua. That fix added collapse_insearch=True to\nrun_staged_search.py's monkeypatched fitness.load_config, but the monkeypatch\nis only installed `if leaf_share or multi_use:`. driver.search_staged has NO\ncollapse_insearch parameter at all -- every internal search() call it makes\nalways inherits collapse_insearch=True unconditionally, regardless of the\nleaf_sharing/multi_use flags. So on a baseline run (LEAFSHARE=0, MULTIUSE=0,\nthe default), fitness.load_config is never patched, and _native_score's\nrescore still silently uses collapse_insearch=False (no example\npatterns.config sets it) -- the exact same MISMATCH the 7ua fix was meant to\neliminate, just on the arm the fix doesn't touch.\n\nConfirmed empirically: `URB_NO_OCCLUSION=1 python3 experiments/run_staged_search.py\nexamples/programme-house 150 0 ...` (no env vars) reports MISMATCH before AND\nafter the 7ua fix, since the patched _load_with_flags function is never\ninstalled for that arm.\n\nMitigating factor: in the small-budget cases tested so far, the fail COUNT\n(what the various run_*_ab.sh harnesses grep as their \"authoritative metric\")\nwas identical between collapse_insearch=True and False rescoring -- only the\ncontinuous score shifted in a low decimal place, which is invisible to those\ngreps. But the underlying mechanism (collapse_global relabels leaf types\nin-place before any check runs, so downstream adjacency/missing/level checks\nsee genuinely different trees) means fail count CAN diverge in principle --\n7ua's own cited historical example (91f: 38 vs 34 fails on a WORKERS=4\nharbor-house run) showed count divergence, albeit confounded with known\nWORKERS parallel non-determinism (homemaker-py-b8g).\n\nFix: make run_staged_search.py's rescore always pin collapse_insearch=True\n(not gated behind leaf_share/multi_use), since search_staged's inner\nevaluator always runs with it regardless of those flags. Simplest: hoist the\n`conf[\"collapse_insearch\"] = True` line out of the `if leaf_share or\nmulti_use:` gate into an unconditional monkeypatch install (or an\nunconditional post-load override in _native_score itself).\n\nAlso worth: audit whether any of run_leafshare_ab.sh / run_9gp_ab.sh /\nrun_c3g_ab.sh / run_depthbal_ab.sh / run_1s3_confirm_staged.sh's BASELINE\narms (which don't set LEAFSHARE/MULTIUSE) had their reported fail counts\nactually affected historically, not just theoretically -- e.g. by rescoring\na few archived .dom outputs (if kept) with the corrected conf and diffing\nfail counts against the published numbers.","status":"closed","priority":3,"issue_type":"bug","assignee":"Claude","owner":"bruno@postle.net","created_at":"2026-08-05T11:25:30Z","created_by":"Bruno Postle","updated_at":"2026-08-29T12:11:37Z","started_at":"2026-08-29T12:03:00Z","closed_at":"2026-08-29T12:11:37Z","close_reason":"Fixed at the root rather than by extending the monkeypatch (DESIGN.md §38.16).\n\ndriver.search_staged now HAS a collapse_insearch parameter (default True,\nbyte-identical to the value it previously inherited from search()), threaded\ninto all three internal search() calls. run_staged_search.py chooses the arm\nexplicitly via COLLAPSE (default 1), passes it to the search, and passes the\nSAME value to _native_score, which now overrides the key instead of hoping the\nconfig carries it. The rescore mirrors the search by construction, not by\ncoincidence of which monkeypatch happened to be installed.\n\nVerified, programme-house budget 150:\n baseline (no env) MISMATCH 1.56663e-08 vs 1.51708e-08 -\u003e OK\n COLLAPSE=0 (knob did not exist) -\u003e OK 1.66216e-08\n LEAFSHARE=1 OK -\u003e OK\n MULTIUSE=1 OK -\u003e OK\n\nCOLLAPSE=0 scoring differently from COLLAPSE=1 confirms the knob does real work.\nThe search result on the default arm is unchanged, so no prior staged number\nmoves.\n\nAudited the other three search_staged callers while here:\n run_and_capture_91f.py already pins collapse_insearch: True -- correct\n run_island_ab.py never re-scores -- cannot mismatch\n probe_harbor_floor.py DID NOT pin it, same bug -- now fixed. That is the\n harness which produced every §13.x floor number.\n\nOn the \"mitigating factor\" that only the continuous score moved while the fail\ncount matched: true, and precisely what made it dangerous. A harness that\nreports MISMATCH on its own control, in a way the metric-of-record cannot see,\ntrains everyone to ignore the warning.\n","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-4ok","title":"run_staged_search.py rescore still MISMATCHes on baseline (LEAFSHARE=0/MULTIUSE=0) arms","description":"Follow-up to homemaker-py-7ua. That fix added collapse_insearch=True to\nrun_staged_search.py's monkeypatched fitness.load_config, but the monkeypatch\nis only installed `if leaf_share or multi_use:`. driver.search_staged has NO\ncollapse_insearch parameter at all -- every internal search() call it makes\nalways inherits collapse_insearch=True unconditionally, regardless of the\nleaf_sharing/multi_use flags. So on a baseline run (LEAFSHARE=0, MULTIUSE=0,\nthe default), fitness.load_config is never patched, and _native_score's\nrescore still silently uses collapse_insearch=False (no example\npatterns.config sets it) -- the exact same MISMATCH the 7ua fix was meant to\neliminate, just on the arm the fix doesn't touch.\n\nConfirmed empirically: `URB_NO_OCCLUSION=1 python3 experiments/run_staged_search.py\nexamples/programme-house 150 0 ...` (no env vars) reports MISMATCH before AND\nafter the 7ua fix, since the patched _load_with_flags function is never\ninstalled for that arm.\n\nMitigating factor: in the small-budget cases tested so far, the fail COUNT\n(what the various run_*_ab.sh harnesses grep as their \"authoritative metric\")\nwas identical between collapse_insearch=True and False rescoring -- only the\ncontinuous score shifted in a low decimal place, which is invisible to those\ngreps. But the underlying mechanism (collapse_global relabels leaf types\nin-place before any check runs, so downstream adjacency/missing/level checks\nsee genuinely different trees) means fail count CAN diverge in principle --\n7ua's own cited historical example (91f: 38 vs 34 fails on a WORKERS=4\nharbor-house run) showed count divergence, albeit confounded with known\nWORKERS parallel non-determinism (homemaker-py-b8g).\n\nFix: make run_staged_search.py's rescore always pin collapse_insearch=True\n(not gated behind leaf_share/multi_use), since search_staged's inner\nevaluator always runs with it regardless of those flags. Simplest: hoist the\n`conf[\"collapse_insearch\"] = True` line out of the `if leaf_share or\nmulti_use:` gate into an unconditional monkeypatch install (or an\nunconditional post-load override in _native_score itself).\n\nAlso worth: audit whether any of run_leafshare_ab.sh / run_9gp_ab.sh /\nrun_c3g_ab.sh / run_depthbal_ab.sh / run_1s3_confirm_staged.sh's BASELINE\narms (which don't set LEAFSHARE/MULTIUSE) had their reported fail counts\nactually affected historically, not just theoretically -- e.g. by rescoring\na few archived .dom outputs (if kept) with the corrected conf and diffing\nfail counts against the published numbers.","status":"closed","priority":3,"issue_type":"bug","assignee":"Claude","owner":"bruno@postle.net","created_at":"2026-08-05T11:25:30Z","created_by":"Bruno Postle","updated_at":"2026-08-29T12:11:37Z","started_at":"2026-08-29T12:03:00Z","closed_at":"2026-08-29T12:11:37Z","close_reason":"Fixed at the root rather than by extending the monkeypatch (DESIGN.md §38.16).\n\ndriver.search_staged now HAS a collapse_insearch parameter (default True,\nbyte-identical to the value it previously inherited from search()), threaded\ninto all three internal search() calls. run_staged_search.py chooses the arm\nexplicitly via COLLAPSE (default 1), passes it to the search, and passes the\nSAME value to _native_score, which now overrides the key instead of hoping the\nconfig carries it. The rescore mirrors the search by construction, not by\ncoincidence of which monkeypatch happened to be installed.\n\nVerified, programme-house budget 150:\n baseline (no env) MISMATCH 1.56663e-08 vs 1.51708e-08 -\u003e OK\n COLLAPSE=0 (knob did not exist) -\u003e OK 1.66216e-08\n LEAFSHARE=1 OK -\u003e OK\n MULTIUSE=1 OK -\u003e OK\n\nCOLLAPSE=0 scoring differently from COLLAPSE=1 confirms the knob does real work.\nThe search result on the default arm is unchanged, so no prior staged number\nmoves.\n\nAudited the other three search_staged callers while here:\n run_and_capture_91f.py already pins collapse_insearch: True -- correct\n run_island_ab.py never re-scores -- cannot mismatch\n probe_harbor_floor.py DID NOT pin it, same bug -- now fixed. That is the\n harness which produced every §13.x floor number.\n\nOn the \"mitigating factor\" that only the continuous score moved while the fail\ncount matched: true, and precisely what made it dangerous. A harness that\nreports MISMATCH on its own control, in a way the metric-of-record cannot see,\ntrains everyone to ignore the warning.\n","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-5bv","title":"CP-SAT post-collapse repair (Fitness.collapse_global's Jacobi+2-opt QAP relaxation)","description":"homemaker-py-2g7.5 item (c), deferred (DESIGN.md §37.7). Fitness.collapse_global (fitness.py:655-847) approximates a finish-time cell\u003c-\u003eroom relabelling QAP with a Jacobi-style fixpoint iteration (_best_assignment, linear_sum_assignment warm-started each round from the previous round's neighbour labels) plus _two_opt_adjacency_polish to escape 2-cycle plateaus. DESIGN.md §25 explicitly considered and rejected OR-Tools for this exact problem 'because the project has no ortools' -- 2g7.5 has now added that dependency (for a simpler, different problem: assignment on a FIXED topology, not this finish-time relabel). This bead: replace or augment collapse_global's Jacobi+2-opt loop with an exact CP-SAT solve of the same cell\u003c-\u003eroom assignment (reusing _collapse_value's per-(leaf,code) value function so both stay consistent), verified safe against the 94g keep-better guard. Riskier than 2g7.5's seeder/reassign work since collapse_global is delicate, heavily tested, and runs inside every in-search eval when collapse_insearch=True (driver.py default) -- correctness and wall-clock regressions would be felt everywhere, not just in an opt-in flag.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-08-04T07:43:33Z","created_by":"Bruno Postle","updated_at":"2026-08-04T07:43:33Z","dependencies":[{"issue_id":"homemaker-py-5bv","depends_on_id":"homemaker-py-2g7.5","type":"parent-child","created_at":"2026-08-04T08:44:03Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-5bv","depends_on_id":"homemaker-py-2g7","type":"parent-child","created_at":"2026-08-04T08:44:25Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-5bv","title":"CP-SAT post-collapse repair (Fitness.collapse_global's Jacobi+2-opt QAP relaxation)","description":"homemaker-py-2g7.5 item (c), deferred (DESIGN.md §37.7). Fitness.collapse_global (fitness.py:655-847) approximates a finish-time cell\u003c-\u003eroom relabelling QAP with a Jacobi-style fixpoint iteration (_best_assignment, linear_sum_assignment warm-started each round from the previous round's neighbour labels) plus _two_opt_adjacency_polish to escape 2-cycle plateaus. DESIGN.md §25 explicitly considered and rejected OR-Tools for this exact problem 'because the project has no ortools' -- 2g7.5 has now added that dependency (for a simpler, different problem: assignment on a FIXED topology, not this finish-time relabel). This bead: replace or augment collapse_global's Jacobi+2-opt loop with an exact CP-SAT solve of the same cell\u003c-\u003eroom assignment (reusing _collapse_value's per-(leaf,code) value function so both stay consistent), verified safe against the 94g keep-better guard. Riskier than 2g7.5's seeder/reassign work since collapse_global is delicate, heavily tested, and runs inside every in-search eval when collapse_insearch=True (driver.py default) -- correctness and wall-clock regressions would be felt everywhere, not just in an opt-in flag.","status":"open","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-08-04T07:43:33Z","created_by":"Bruno Postle","updated_at":"2026-08-04T07:43:33Z","dependencies":[{"issue_id":"homemaker-py-5bv","depends_on_id":"homemaker-py-2g7.5","type":"parent-child","created_at":"2026-08-04T08:44:03Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-5bv","depends_on_id":"homemaker-py-2g7","type":"parent-child","created_at":"2026-08-04T08:44:25Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-v4s","title":"driver.search A/B for shapecurve warm-start/prune on real multi-storey programmes","description":"Follow-up from homemaker-py-koo (DESIGN.md §37.6): koo generalised shapecurve.py's DP to handle below-inherited multi-storey trees and validated it (DP-vs-NM agreement/false-negative bar, 200 topologies on the real examples/harbor-house, 99.5% agreement, 0 false negatives, 117.7x speedup, DESIGN.md §37.6). Not measured: the search-level payoff of shapecurve_warmstart/shapecurve_prune on a real multi-storey programme at the 6xh/wkh A/B protocol (budget=2000, seeds 0-4, driver.search mean hard/soft/fitness fails, off vs on). Best sized as a single A/B once homemaker-py-tym (leaf_sharing/co_type modelling in shapecurve.leaf_constraints) also lands, since leaf_sharing defaults True in driver.search and both programme-house and harbor-house require it by default -- measuring the combined win (multi-storey + leaf_sharing) in one pass avoids two partial A/Bs that each only apply with a flag most real runs don't use.","notes":"Depends on homemaker-py-tym landing first for the combined measurement to be meaningful.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-08-03T22:23:50Z","created_by":"Bruno Postle","updated_at":"2026-08-03T22:24:36Z","dependencies":[{"issue_id":"homemaker-py-v4s","depends_on_id":"homemaker-py-tym","type":"blocks","created_at":"2026-08-03T23:24:37Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-v4s","title":"driver.search A/B for shapecurve warm-start/prune on real multi-storey programmes","description":"Follow-up from homemaker-py-koo (DESIGN.md §37.6): koo generalised shapecurve.py's DP to handle below-inherited multi-storey trees and validated it (DP-vs-NM agreement/false-negative bar, 200 topologies on the real examples/harbor-house, 99.5% agreement, 0 false negatives, 117.7x speedup, DESIGN.md §37.6). Not measured: the search-level payoff of shapecurve_warmstart/shapecurve_prune on a real multi-storey programme at the 6xh/wkh A/B protocol (budget=2000, seeds 0-4, driver.search mean hard/soft/fitness fails, off vs on). Best sized as a single A/B once homemaker-py-tym (leaf_sharing/co_type modelling in shapecurve.leaf_constraints) also lands, since leaf_sharing defaults True in driver.search and both programme-house and harbor-house require it by default -- measuring the combined win (multi-storey + leaf_sharing) in one pass avoids two partial A/Bs that each only apply with a flag most real runs don't use.","notes":"Depends on homemaker-py-tym landing first for the combined measurement to be meaningful.","status":"closed","priority":3,"issue_type":"task","assignee":"Claude","owner":"bruno@postle.net","created_at":"2026-08-03T22:23:50Z","created_by":"Bruno Postle","updated_at":"2026-08-30T07:41:15Z","started_at":"2026-08-30T07:35:58Z","closed_at":"2026-08-30T07:41:15Z","close_reason":"Answered without running the A/B, because two structural facts make the payoff\nzero before any seed is drawn (DESIGN.md §38.24). Measuring a no-op would have\nproduced a null that reads like a measurement rather than a fact -- the exact\npattern §38.19/§38.21 spent this session retracting.\n\n1. REACH. driver._evaluate gates the warm-start on `x0 is None`, and every child\ngets x0 = innerloop.warm_x0(child_root, ratios) from its parent (driver.py:764).\nSo it fires only for bootstrap-population individuals. Instrumented over a\n4000-eval run: 8 DP solves, exactly pop_size. At the 500k budget the corpus\nbaseline uses, that is 8 evaluations out of 500,000.\n\n2. APPLICABILITY. Constructed-seed feasibility, 6 seeds each:\n\n harbor-house 0/6 (leaf_sharing on) 0/6 (off)\n programme-house 0/6\n harbor-house-l0 4/6 5/6 (75% over a real search)\n\nSo even those 8 bootstrap individuals get no warm start on the real programmes.\nOnly l0 -- the reduced programme the DP was originally validated on -- is\nfeasible.\n\nTHE INFEASIBILITY IS CORRECT, checked because it is the dangerous direction: on\n4 harbor topologies the DP rejects, an NM polish minimising the shape-fail family\nreaches 14/16/17/16 fails, never 0. 0/4 false negatives. The full harbor\nprogramme is genuinely shape-infeasible per topology where l0 is not. (Per\nTOPOLOGY -- it says nothing about whether a good design exists; programme-house\nreaches 0 hard / 1 soft in the 500k baseline while its constructed seeds are\nDP-infeasible.)\n\nshapecurve_prune is separately inert: it only acts inside the\nfeasibility_max_shape_fails branch, and its exact-prune arm additionally needs\nbest_n_fails \u003c= 0.\n\ntym was still worth doing -- the DP now models leaf-sharing exactly (§38.23) and\nfires on real runs, which is what turned an untestable question into a structural\nanswer.\n\nWhat would have to change is filed as homemaker-py-ldj: give the DP reach beyond\nthe bootstrap, and/or use infeasibility as a ranking SIGNAL (an infeasible\ntopology still has a shape-fail floor, which is what a pre-filter wants) rather\nthan as a precondition that discards the information.\n","dependencies":[{"issue_id":"homemaker-py-v4s","depends_on_id":"homemaker-py-tym","type":"blocks","created_at":"2026-08-03T23:24:37Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-ekc","title":"True skew-quad polygon algebra for the shape-curve DP leaf region (remove ~7-12% rectangle approximation error)","description":"homemaker-py-6xh item (DESIGN.md §37.2, 'Remaining approximation error, root-caused'). src/homemaker_layout/shapecurve.py approximates every quad (leaf or internal) as a rectangle with edge-length-derived (w,h) = ((edge0+edge2)/2, (edge1+edge3)/2) -- exact only for a true rectangle/parallelogram. DESIGN.md §37.2's 200-topology harbor-house-l0 validation root-caused both measured false positives to this approximation specifically (not to global rotation or to the rotation-parity composition rule, both already fixed/verified exact): the DP's own realised point had a leaf whose edge-length-approximated area was comfortably inside its feasible bound but whose true geometry.area (a real, slightly non-parallelogram quad) fell just below the true lower bound -- an ~8-12% gap, the same magnitude as harbor-house-l0's own plot-level residual skew. Needs: either (a) replace the rectangle approximation with true skew-quad polygon algebra (a harder closed-form derivation, or a numerically-solved per-leaf feasible region), or (b) at minimum re-characterise the error's magnitude on a LESS rectangular plot than harbor-house-l0's near-rectangular trapezoid (§37.2 flagged this as untested and likely worse elsewhere) so shapecurve_warmstart's real-world false-positive rate is known before wider rollout.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-08-03T17:30:23Z","created_by":"Bruno Postle","updated_at":"2026-08-03T17:30:23Z","dependencies":[{"issue_id":"homemaker-py-ekc","depends_on_id":"homemaker-py-2g7","type":"parent-child","created_at":"2026-08-03T18:31:51Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-ekc","title":"True skew-quad polygon algebra for the shape-curve DP leaf region (remove ~7-12% rectangle approximation error)","description":"homemaker-py-6xh item (DESIGN.md §37.2, 'Remaining approximation error, root-caused'). src/homemaker_layout/shapecurve.py approximates every quad (leaf or internal) as a rectangle with edge-length-derived (w,h) = ((edge0+edge2)/2, (edge1+edge3)/2) -- exact only for a true rectangle/parallelogram. DESIGN.md §37.2's 200-topology harbor-house-l0 validation root-caused both measured false positives to this approximation specifically (not to global rotation or to the rotation-parity composition rule, both already fixed/verified exact): the DP's own realised point had a leaf whose edge-length-approximated area was comfortably inside its feasible bound but whose true geometry.area (a real, slightly non-parallelogram quad) fell just below the true lower bound -- an ~8-12% gap, the same magnitude as harbor-house-l0's own plot-level residual skew. Needs: either (a) replace the rectangle approximation with true skew-quad polygon algebra (a harder closed-form derivation, or a numerically-solved per-leaf feasible region), or (b) at minimum re-characterise the error's magnitude on a LESS rectangular plot than harbor-house-l0's near-rectangular trapezoid (§37.2 flagged this as untested and likely worse elsewhere) so shapecurve_warmstart's real-world false-positive rate is known before wider rollout.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-08-03T17:30:23Z","created_by":"Bruno Postle","updated_at":"2026-08-03T17:30:23Z","dependencies":[{"issue_id":"homemaker-py-ekc","depends_on_id":"homemaker-py-2g7","type":"parent-child","created_at":"2026-08-03T18:31:51Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"homemaker-py-tym","title":"leaf_sharing/co_type target-adjustment modelling in shapecurve.leaf_constraints","description":"homemaker-py-6xh item 4 (DESIGN.md §37.2/§37.4). src/homemaker_layout/shapecurve.py's leaf_constraints() uses each leaf's own type's base (target, sigma) params only -- it does not model the leaf-sharing/co_type k-scaling (target*=k, sigma adjustment) that fitness.py's quality_size applies for shared/multi-use leaves. shapecurve.eligible() currently guards this by excluding any run with leaf_sharing/superpose/max_share/multi_use on, so the DP warm-start never fires for those runs -- but leaf_sharing defaults to True in driver.search(), so most real runs are excluded today. Needs: read fitness.py's actual k-scaling formula (quality_size's leaf-sharing branch) and mirror it in leaf_constraints so (amin, amax) reflects a shared leaf's k-multiplied target, then relax shapecurve.eligible's leaf_sharing/max_share guards accordingly (superpose/multi_use may need separate analysis -- check whether either changes the per-leaf target formula the same way share does, or a different one).","status":"closed","priority":3,"issue_type":"feature","assignee":"Claude","owner":"bruno@postle.net","created_at":"2026-08-03T17:30:01Z","created_by":"Bruno Postle","updated_at":"2026-08-29T21:36:44Z","started_at":"2026-08-29T21:22:42Z","closed_at":"2026-08-29T21:36:44Z","close_reason":"Done (DESIGN.md §38.23).\n\nleaf_constraints derived each leaf's feasible area from its own type's base\n(target, sigma). quality_size does not: a leaf holding k same-code rooms is\ncentred on k*target with sigma*k, and a co-typed leaf adds both codes' targets.\nThe DP modelled neither, so eligible() excluded leaf_sharing/max_share/multi_use\n-- and leaf_sharing defaults True in driver.search, so the guard excluded\nessentially every real run. The DP was correct and unreachable.\n\nWhy the guard could not just be dropped, measured before touching it: on 6\nharbor constructed seeds, 24 of 24 shared leaves (100%) have a real area OUTSIDE\nthe unscaled single-room bounds. Relaxing eligible without modelling k would\nhave made the DP call every one of those topologies infeasible -- false\nnegatives that prune feasible topologies and misdirect the NM warm-start. The\nguard was load-bearing.\n\nFix: mirror quality_size by asking the SAME Fitness object -- k =\ngraph.leaf_share(leaf, fit._max_share) when fit._leaf_sharing, then target*k /\nsigma*k, else fit._leaf_co_type for the additive case. Same object, same flags,\nsame branch order. Deliberately not re-derived: §39.5's cpsat._matches bug was a\nsolver optimising a relation the scorer had moved, and this is the same hazard\nclass.\n\nVerified as an EXACT inversion, not an approximation: for every shared leaf in a\nreal seed, quality_size evaluated at the DP's amin and amax returns\nFAIL_THRESHOLD to 1e-9 (k=3 n-leaf: bounds [128.50, 231.50], both 0.100000).\n\nsuperpose stays excluded, for a different reason than the others: it does not\nrescale a target, it changes WHICH TYPE the leaf is scored as, and that collapse\nhappens after the DP has read leaf.type. Bounding the wrong code is not fixable\nby a rescale.\n\nshapecurve_warmstart/shapecurve_prune remain default off, so no current run\nchanges -- including the cold-start baseline in progress. What changes is that\nthey are now applicable, which unblocks homemaker-py-v4s.\n\nThree new tests plus an updated legacy one (test_eligible_guards_sharing_not_\nstorey_count asserted the old guard; rewritten to the new contract rather than\ndeleted).\n","dependencies":[{"issue_id":"homemaker-py-tym","depends_on_id":"homemaker-py-2g7","type":"parent-child","created_at":"2026-08-03T18:31:49Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":1,"comment_count":0} {"_type":"issue","id":"homemaker-py-tym","title":"leaf_sharing/co_type target-adjustment modelling in shapecurve.leaf_constraints","description":"homemaker-py-6xh item 4 (DESIGN.md §37.2/§37.4). src/homemaker_layout/shapecurve.py's leaf_constraints() uses each leaf's own type's base (target, sigma) params only -- it does not model the leaf-sharing/co_type k-scaling (target*=k, sigma adjustment) that fitness.py's quality_size applies for shared/multi-use leaves. shapecurve.eligible() currently guards this by excluding any run with leaf_sharing/superpose/max_share/multi_use on, so the DP warm-start never fires for those runs -- but leaf_sharing defaults to True in driver.search(), so most real runs are excluded today. Needs: read fitness.py's actual k-scaling formula (quality_size's leaf-sharing branch) and mirror it in leaf_constraints so (amin, amax) reflects a shared leaf's k-multiplied target, then relax shapecurve.eligible's leaf_sharing/max_share guards accordingly (superpose/multi_use may need separate analysis -- check whether either changes the per-leaf target formula the same way share does, or a different one).","status":"closed","priority":3,"issue_type":"feature","assignee":"Claude","owner":"bruno@postle.net","created_at":"2026-08-03T17:30:01Z","created_by":"Bruno Postle","updated_at":"2026-08-29T21:36:44Z","started_at":"2026-08-29T21:22:42Z","closed_at":"2026-08-29T21:36:44Z","close_reason":"Done (DESIGN.md §38.23).\n\nleaf_constraints derived each leaf's feasible area from its own type's base\n(target, sigma). quality_size does not: a leaf holding k same-code rooms is\ncentred on k*target with sigma*k, and a co-typed leaf adds both codes' targets.\nThe DP modelled neither, so eligible() excluded leaf_sharing/max_share/multi_use\n-- and leaf_sharing defaults True in driver.search, so the guard excluded\nessentially every real run. The DP was correct and unreachable.\n\nWhy the guard could not just be dropped, measured before touching it: on 6\nharbor constructed seeds, 24 of 24 shared leaves (100%) have a real area OUTSIDE\nthe unscaled single-room bounds. Relaxing eligible without modelling k would\nhave made the DP call every one of those topologies infeasible -- false\nnegatives that prune feasible topologies and misdirect the NM warm-start. The\nguard was load-bearing.\n\nFix: mirror quality_size by asking the SAME Fitness object -- k =\ngraph.leaf_share(leaf, fit._max_share) when fit._leaf_sharing, then target*k /\nsigma*k, else fit._leaf_co_type for the additive case. Same object, same flags,\nsame branch order. Deliberately not re-derived: §39.5's cpsat._matches bug was a\nsolver optimising a relation the scorer had moved, and this is the same hazard\nclass.\n\nVerified as an EXACT inversion, not an approximation: for every shared leaf in a\nreal seed, quality_size evaluated at the DP's amin and amax returns\nFAIL_THRESHOLD to 1e-9 (k=3 n-leaf: bounds [128.50, 231.50], both 0.100000).\n\nsuperpose stays excluded, for a different reason than the others: it does not\nrescale a target, it changes WHICH TYPE the leaf is scored as, and that collapse\nhappens after the DP has read leaf.type. Bounding the wrong code is not fixable\nby a rescale.\n\nshapecurve_warmstart/shapecurve_prune remain default off, so no current run\nchanges -- including the cold-start baseline in progress. What changes is that\nthey are now applicable, which unblocks homemaker-py-v4s.\n\nThree new tests plus an updated legacy one (test_eligible_guards_sharing_not_\nstorey_count asserted the old guard; rewritten to the new contract rather than\ndeleted).\n","dependencies":[{"issue_id":"homemaker-py-tym","depends_on_id":"homemaker-py-2g7","type":"parent-child","created_at":"2026-08-03T18:31:49Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"homemaker-py-p6t","title":"Convergence-speed A/B for tiered comparator: evals to 0 hard fails, tiered vs flat","description":"homemaker-py-2g7.3 (DESIGN.md §37.1) validated that tiered search (-n_hard,-n_soft,fitness) reaches a strictly lower mean hard-fail count than flat (-n_fails,fitness) at a FIXED budget (20k evals) on harbor-house and maple-court. That measures fail composition at a snapshot, not time-to-solved. The natural follow-up: race the two comparators to '0 hard fails' (or a hard-fail floor) and compare evals/wall-clock to get there, ideally after 2g7.1/2g7.2 ground truth lands so there is a real target to race to instead of an arbitrary floor.","design":"Reuse experiments/tier_ab_2g7_3.py's harness; instead of a fixed budget, run until n_hard==0 or a budget cap, log evals-to-target per seed/scheme, same programmes (harbor-house, maple-court), same 3-seed protocol.","acceptance_criteria":"Report showing evals-to-0-hard-fails (or evals-to-floor) for tiered vs flat, both programmes, 3 seeds; verdict on whether tiering also wins on convergence speed, not just fixed-budget composition.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-08-02T17:51:38Z","created_by":"Bruno Postle","updated_at":"2026-08-02T17:51:38Z","comments":[{"id":"01a04f66-b19a-7e1f-b14c-e6a592452e7d","issue_id":"homemaker-py-p6t","author":"Claude","text":"NOT RUNNABLE AS SPECIFIED -- checked before spending compute. Two blockers.\n\n1. THE TARGET IS UNREACHABLE ON THE PROGRAMMES IT NAMES. p6t races to \"0 hard\nfails\" on harbor-house and maple-court. Under the current objective:\n\n harbor evolved-3M-nols-3.dom (3M evals, our best-ever) 44 hard / 45 soft\n harbor coldstart-500000-s0.dom (partial run) 29 hard / 56 soft\n maple coldstart-500000-s0.dom (partial run) 36 hard / 90 soft\n programme-house coldstart-500000-s0.dom 0 hard / 1 soft\n\n0 hard has never been achieved on harbor or maple and is not close. Every arm\nwould hit the budget cap with the metric undefined -- a fully censored race\nproducing no data. Only programme-house reaches the target, and it is not in\np6t's protocol.\n\n2. THE 3-SEED PROTOCOL CANNOT RESOLVE ANYTHING. §38.21: harbor's paired sd is\n6.19 fails, so n=3 resolves nothing finer than ~15.4 fails, and 25% of 3-seed\nsubsets show a clean sweep by chance. p6t's design says \"same 3-seed protocol\"\n-- it would generate exactly the kind of verdict this log has spent the session\nretracting.\n\nREDESIGN NEEDED before this is worth running:\n - race to a REACHABLE target: best-known hard-fail count per programme, or\n \"evals to reach within k of it\", rather than an absolute 0;\n - size N from the observed sd via experiments/ab_report.py (homemaker-py-tco)\n and state the minimum detectable difference BEFORE running;\n - note the prerequisite p6t itself flags is still unmet: 2g7.1/2g7.2 ground\n truth would give a principled target instead of an arbitrary floor.\n\nLeft open -- the question is still worth answering, the protocol is not.","created_at":"2026-08-29T21:22:05Z"}],"dependency_count":0,"dependent_count":0,"comment_count":1} {"_type":"issue","id":"homemaker-py-p6t","title":"Convergence-speed A/B for tiered comparator: evals to 0 hard fails, tiered vs flat","description":"homemaker-py-2g7.3 (DESIGN.md §37.1) validated that tiered search (-n_hard,-n_soft,fitness) reaches a strictly lower mean hard-fail count than flat (-n_fails,fitness) at a FIXED budget (20k evals) on harbor-house and maple-court. That measures fail composition at a snapshot, not time-to-solved. The natural follow-up: race the two comparators to '0 hard fails' (or a hard-fail floor) and compare evals/wall-clock to get there, ideally after 2g7.1/2g7.2 ground truth lands so there is a real target to race to instead of an arbitrary floor.","design":"Reuse experiments/tier_ab_2g7_3.py's harness; instead of a fixed budget, run until n_hard==0 or a budget cap, log evals-to-target per seed/scheme, same programmes (harbor-house, maple-court), same 3-seed protocol.","acceptance_criteria":"Report showing evals-to-0-hard-fails (or evals-to-floor) for tiered vs flat, both programmes, 3 seeds; verdict on whether tiering also wins on convergence speed, not just fixed-budget composition.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-08-02T17:51:38Z","created_by":"Bruno Postle","updated_at":"2026-08-02T17:51:38Z","comments":[{"id":"01a04f66-b19a-7e1f-b14c-e6a592452e7d","issue_id":"homemaker-py-p6t","author":"Claude","text":"NOT RUNNABLE AS SPECIFIED -- checked before spending compute. Two blockers.\n\n1. THE TARGET IS UNREACHABLE ON THE PROGRAMMES IT NAMES. p6t races to \"0 hard\nfails\" on harbor-house and maple-court. Under the current objective:\n\n harbor evolved-3M-nols-3.dom (3M evals, our best-ever) 44 hard / 45 soft\n harbor coldstart-500000-s0.dom (partial run) 29 hard / 56 soft\n maple coldstart-500000-s0.dom (partial run) 36 hard / 90 soft\n programme-house coldstart-500000-s0.dom 0 hard / 1 soft\n\n0 hard has never been achieved on harbor or maple and is not close. Every arm\nwould hit the budget cap with the metric undefined -- a fully censored race\nproducing no data. Only programme-house reaches the target, and it is not in\np6t's protocol.\n\n2. THE 3-SEED PROTOCOL CANNOT RESOLVE ANYTHING. §38.21: harbor's paired sd is\n6.19 fails, so n=3 resolves nothing finer than ~15.4 fails, and 25% of 3-seed\nsubsets show a clean sweep by chance. p6t's design says \"same 3-seed protocol\"\n-- it would generate exactly the kind of verdict this log has spent the session\nretracting.\n\nREDESIGN NEEDED before this is worth running:\n - race to a REACHABLE target: best-known hard-fail count per programme, or\n \"evals to reach within k of it\", rather than an absolute 0;\n - size N from the observed sd via experiments/ab_report.py (homemaker-py-tco)\n and state the minimum detectable difference BEFORE running;\n - note the prerequisite p6t itself flags is still unmet: 2g7.1/2g7.2 ground\n truth would give a principled target instead of an arbitrary floor.\n\nLeft open -- the question is still worth answering, the protocol is not.","created_at":"2026-08-29T21:22:05Z"}],"dependency_count":0,"dependent_count":0,"comment_count":1}

View file

@ -6130,6 +6130,62 @@ changes no current run. What it changes is that they are now *applicable*:
`homemaker-py-v4s`'s A/B, which its own issue said to defer until this landed, `homemaker-py-v4s`'s A/B, which its own issue said to defer until this landed,
is unblocked. is unblocked.
### 38.24 The shape-curve warm-start cannot pay off as wired (`homemaker-py-v4s`)
With `tym` landed the DP finally *runs* on real (leaf-sharing) searches, so
`v4s` asked for the search-level A/B: `shapecurve_warmstart`/`_prune` off vs on,
budget 2000, seeds 04. **No A/B was run, because two structural facts make the
payoff zero before any seed is drawn**, and measuring a no-op would have
produced a null that reads like a measurement rather than a fact.
**1. Reach: the warm-start only ever touches the bootstrap population.**
`driver._evaluate` gates it on `x0 is None`, and every child gets
`x0 = innerloop.warm_x0(child_root, ratios)` from its parent (`driver.py:764`).
So `x0 is None` holds only for seed-population individuals. Instrumented over a
4000-eval run: **8 DP solves**, exactly `pop_size`. At the 500k budget the
corpus baseline uses that is 8 evaluations out of 500,000.
**2. Applicability: on the real programmes the DP finds nothing feasible.**
Feasibility of constructed seeds, 6 seeds each:
| programme | leaf_sharing on | off |
|---|---|---|
| harbor-house | **0/6** | **0/6** |
| programme-house | 0/6 | — |
| harbor-house-l0 | 4/6 | 5/6 |
So even those 8 bootstrap individuals get no warm start on harbor or
programme-house. Only `harbor-house-l0` — the reduced programme the DP was
originally validated on (§37.2) — is feasible, at 75% over a real search.
**The infeasibility is correct, not a bug.** This is the dangerous direction, so
it was checked: on 4 harbor topologies the DP calls infeasible, an NM polish
minimising the shape-fail family reaches **14, 16, 17, 16** fails — never 0.
**0/4 false negatives.** The DP is right that these topologies admit no ratio
assignment satisfying every size/width/proportion bound at once; the full harbor
programme is simply shape-infeasible where `l0` is not. Note this is per
*topology*, and says nothing about whether a good design exists — programme-house
reaches 0 hard / 1 soft in the 500k baseline while its constructed seeds are
DP-infeasible.
**`shapecurve_prune` is separately inert.** It only acts inside the
`feasibility_max_shape_fails is not None` branch, and its exact-prune arm
additionally requires `best_n_fails <= 0` — an incumbent with zero total fails.
On these programmes that combination effectively never arises.
**What would have to change** for the feature to be worth an A/B — filed as its
own issue rather than smuggled in here:
- let the DP run for children too, not only where `x0 is None`, so its reach is
the search rather than the bootstrap; and/or
- treat DP-infeasibility as a *signal* rather than a precondition — an infeasible
topology still has a best-achievable shape-fail floor, and that floor is
exactly what a pre-filter wants to rank on.
`tym` was still worth doing: the DP now models leaf-sharing exactly (§38.23) and
fires on real runs, which is what turned an untestable question into a
structural answer.
## 39. Config audit: requirements that actively fight the engine (`homemaker-py-ju3`) — measured 2026-08-25 ## 39. Config audit: requirements that actively fight the engine (`homemaker-py-ju3`) — measured 2026-08-25
The corpus `patterns.config` targets and `costs.config` values were estimated The corpus `patterns.config` targets and `costs.config` values were estimated