{"id":"homemaker-py-ld2","title":"Interior-O courtyard seeding option","description":"_assign_adjacency_aware (operators.py:528) currently places the single O leaf on the MOST PERIPHERAL leaf, where adjacent rooms already have facade. For dense floors (harbor-house ~19 rooms/floor) this wastes the daylight source. Add an option to seed O INTERIOR (as a light well) and to scale O-leaf count with room count, so landlocked rooms get an adjacent uncovered-outside neighbour by construction -\u003e fewer crinkliness fails in the seed. A/B against current peripheral placement.","notes":"Implemented: interior_outside flag + outside_divisor (default 3) threaded through operators.constructive_topology / lift_base_to_storeys, _assign_adjacency_aware (interior light-well placement: most-landlocked leaves first, greedy spread), driver.search/search_staged, run_staged_search.py (INTERIORO/ODIV env). Test test_interior_outside_seeds_landlocked_wells_and_scales_count. A/B script experiments/run_interioro_ab.sh. Seed diagnostic confirmed mechanism (all crinkliness fails landlocked under-exposure) and tuned odiv 6-\u003e3. Full 20k A/B (maple+harbor seeds 0/1/2, control=peripheral must reproduce §13.5 maple 82.3/harbor 40.0) running; DESIGN.md §13.6 verdict pending results.","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-23T20:40:19Z","created_by":"Bruno Postle","updated_at":"2026-06-28T06:19:38Z","started_at":"2026-06-27T20:37:42Z","closed_at":"2026-06-28T06:19:38Z","close_reason":"interior-O light-well seeding implemented + A/B done (§13.6): positive on dense floor (harbor -16.4%, all seeds), marginal/neutral on maple (-2.8%). Default-ON flip tracked as follow-up.","dependencies":[{"issue_id":"homemaker-py-ld2","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T21:49:30Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.4","title":"Experiment: depth-balanced / giant-splitting construction (re-scoped by Diag B)","description":"Attacks the #2 factor (size/undersize 242) via the §12.3 paradox: rooms are undersize while 56% of the plot is empty. The shape floor is computed at TARGET dims, so construction never spends the slack. Scale leaves up to consume available plot area (proportionally, preserving target aspect) so rooms reach/exceed target — bigger leaves are also easier to keep compact, so this may help crinkliness/width too.\n\nBuilds on leu.2 (proportion-aware splits sized FROM target dims) by adding a fill step that scales the whole layout (or per-region) to the plot envelope instead of leaving slack as empty plot. Implementation in operators construction / _size_divisions_from_targets.\n\nNOTE: exact fix-site (construction vs inner loop) is decided by Diagnostic B — if B shows leaves park at target with unused plot, this construction lever is correct; if B shows the inner loop simply lacks an expansion gradient, prefer the inner-loop slack-expansion sibling instead. A/B vs §12.2 baseline, seeds 0/1/2, 20000 evals, staged, default-OFF. Record DESIGN.md §13.4.","notes":"RE-SCOPED by Diagnostic B (§13.2). Original premise (rooms parked at target, scale leaves up into 56%-empty plot) is FALSIFIED: sized rooms already hold 1.4-1.5x aggregate target area; the empty-looking plot is ~46% circulation, not claimable void. Real defect: MALDISTRIBUTION by slicing position — same type/target leaf lands 0.05x..14.7x by binary-tree depth; inner loop cannot fix (frozen topology). NEW SCOPE: construction that balances tree DEPTH so equal-target rooms land at comparable depth and/or splits/caps giant leaves so area tracks target. NOT a uniform scale-to-envelope (that would just inflate the giants further). A/B vs §12.2 baseline, seeds 0/1/2, 20000 evals, staged, default-OFF. Record DESIGN.md §13.4. Synergy with erc.3 (leaf-sharing for the starved tail).","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-22T23:16:19Z","created_by":"Bruno Postle","updated_at":"2026-06-26T06:06:51Z","started_at":"2026-06-24T21:18:57Z","closed_at":"2026-06-26T06:06:51Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-erc.4","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T00:16:19Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-erc.4","depends_on_id":"homemaker-py-erc.2","type":"blocks","created_at":"2026-06-23T00:16:45Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.3","title":"Experiment: leaf-sharing / multi-room leaves in construction","description":"Strongest untried construction lever. §12.3 named 'merge or share leaves across same-class rooms' but c3g never tested it — c3g only coarsened the circulation spine (circ_divisor), trading shape gains for equal access/adjacency damage (null). Leaf-sharing is DIFFERENT: it reduces leaf count by collapsing same-class rooms (e.g. several O/storage, or same-type repeated rooms) into a shared leaf, attacking crinkliness(346)+size(242) directly WITHOUT coarsening circulation — so it should dodge the access penalty that sank c3g.\n\nImplementation sketch: in operators.constructive_topology (+ lift path), allow rooms of the same class/type (and compatible adjacency) to be instantiated as one larger leaf rather than one-leaf-per-room, lowering leaves-per-room from ~1.4 toward 1.0 or below. Honour storey_minimum and required-room presence (a shared leaf must still satisfy each merged room's presence/area in the fitness check, or the merge must be limited to rooms the fitness treats as fungible).\n\nTests the deepest open question: whether 52 rooms simply cannot be well-shaped as 52 leaves at this density. A/B vs §12.2 baseline (maple 136.0, harbor 74.0), seeds 0/1/2, 20000 evals, staged; default-OFF toggle so controls reproduce. Record DESIGN.md §13.3.","notes":"A/B DONE (§13.3): staged 20k, seeds 0/1/2, factor 3. maple 137.0→86.3 (−37%), harbor 74.0→50.3 (−32%). Baseline arm reproduces §12.2 exactly (maple 137 vs 136, harbor 74.0 vs 74.0). Total separation: every share run beats every baseline run same-programme. ~35% faster (fewer leaves). First Phase-8 floor-mover; 5th construction/seed win. Closing.","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-22T23:16:15Z","created_by":"Bruno Postle","updated_at":"2026-06-24T20:51:20Z","started_at":"2026-06-23T21:51:08Z","closed_at":"2026-06-24T20:51:20Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-erc.3","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T00:16:15Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-erc.3","depends_on_id":"homemaker-py-erc.1","type":"blocks","created_at":"2026-06-23T00:16:42Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.2","title":"Diagnostic B: undersize-despite-slack localization (construction-target vs inner-loop-fill)","description":"GATES the plot-fill-construction vs inner-loop-expansion decision. The paradox from §12.3: plot utilisation is 0.44 (56% empty) yet size fails are 242 (rooms UNDERSIZE). Where is the slack stranded, and at which stage should it be spent?\n\nMeasure, on constructive seeds for maple-court + harbor (seeds 0/1/2):\n1. After CONSTRUCTION (before inner loop): per-leaf achieved area vs target area, and total occupied vs plot area. Are leaves parked at target with the slack left as unused plot, or is the slack distributed but mis-shaped?\n2. After the INNER LOOP optimises ratios: did size fails drop — i.e. does the ratio solve already expand leaves into slack, or does it have no gradient/incentive to exceed target? Compare predicted_shape_fails (target geometry) vs achieved size fails (post-optimise).\n\nThe §12.3 calibration (floor at TARGET dims ≈ achieved) already hints the inner loop is NOT filling slack — confirm and quantify, and identify whether the gap is (a) construction targets too-small dims given the plot, or (b) the objective gives no reward for exceeding target area. Output: DESIGN.md §13.2.\n\nDECISION RULE: if rooms are parked at target with unused plot → fix in CONSTRUCTION (plot-fill, erc child). If the inner loop has the room to expand but no objective gradient → fix in the INNER LOOP (slack-expansion term, erc child). Reads only; no behaviour change.","notes":"VERDICT (DESIGN.md §13.2): the '56% empty plot' is a misreading. Sized rooms already occupy ~50-54% of plot and hold 1.4-1.5x their aggregate target area (util\u003etgtFill); ~46% of plot is CIRCULATION, not claimable void (out only 3-4%). Size fails are pure MALDISTRIBUTION set by SLICING POSITION: median room at target (a/t~1.0) but long undersize tail (p25~0.35, min 0.05) starves while a few giants balloon (max 6.8x harbor, 14.7x maple). Same type/target lands at BOTH extremes (harbor r t=10: 68m2 \u0026 2.3m2; maple n t=60: ~target \u0026 2.7m2) =\u003e area dictated by binary-tree depth, not target. Inner loop CANNOT repair it: budget-80 size fails move only -1.6/-3.7, %undersize flat-to-worse; frozen-topology ratio DOF + 0.5^n cliff + symmetric size gaussian. =\u003e FALSIFIES plot-fill-as-claim-void (re-scope erc.4 to depth-balanced/giant-splitting construction), DEPRIORITISE erc.6 (wrong DOF). Reinforces erc.3 leaf-sharing for the starved tail. Script: experiments/diag_slack_localization.py","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-22T23:15:42Z","created_by":"Bruno Postle","updated_at":"2026-06-23T21:46:34Z","started_at":"2026-06-23T21:17:07Z","closed_at":"2026-06-23T21:46:34Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-erc.2","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T00:15:42Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0} {"id":"homemaker-py-erc.1","title":"Diagnostic A: per-leaf shape-fail vs density/granularity profile","description":"GATES the leaf-sharing vs compactness-cuts decision. The open question from §12.3: is the shape floor intrinsic to slicing at this leaf density (→ fewer leaves is the only lever), or fixable by better-shaped cuts at the same leaf count?\n\nMeasure: per-leaf shape-fail rate (crinkliness/size/proportion/width, broken out) as a function of leaves-per-room and plot utilisation, across the existing programmes spanning density — harbor (16 rooms) vs maple-court (52 rooms) — and, if cheap, a synthetic sweep that holds the programme fixed while varying leaf count (e.g. reuse the circ_divisor / construction granularity knob already in place to generate coarser vs finer constructive seeds and score predicted_shape_fails per leaf).\n\nReads, does not change behaviour: use operators.predicted_shape_fails + the per-leaf factor breakdown already in fitness.py (the §12.3 residual table was produced this way). Output: a table of per-leaf shape-fail vs density, written into DESIGN.md §13.1.\n\nDECISION RULE (write it into the verdict): if per-leaf shape-fail is FLAT across densities → floor is intrinsic to slicing density → prioritise leaf-sharing (erc child), deprioritise/close compactness-cuts. If it RISES with density → better cuts can pay → keep compactness-cuts. This is a measurement, not an experiment; no A/B, no baseline reproduction needed.","notes":"VERDICT (DESIGN.md §13.1): per-leaf shape-fail is FLAT vs slicing density in the controlled synthetic sweep (maple-court, room set fixed, circ_divisor 2-\u003e9: leaves 81-\u003e63, per-leaf rate 1.72-1.94 with no trend; TOTAL shape fails track leaf count ~linearly 139-\u003e116). Crinkliness dominates (~0.8/leaf) and is flat. Cuts already squarest (_size_divisions_from_targets) yet still ~1.8 fails/leaf =\u003e little compactness headroom at fixed count. Floor is INTRINSIC to per-leaf slicing. =\u003e prioritise leaf-sharing (erc.3), deprioritise compactness-cuts (erc.5). NOT the c3g null: that removed circulation leaves (access damage cancelled gain); leaf-sharing removes ROOM-leaf count without touching the spine. Script: experiments/diag_leaf_shapefail.py","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-22T23:15:40Z","created_by":"Bruno Postle","updated_at":"2026-06-23T21:00:34Z","started_at":"2026-06-23T20:53:52Z","closed_at":"2026-06-23T21:00:34Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-erc.1","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T00:15:39Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0} {"id":"homemaker-py-erc","title":"Phase 8: lower the geometry/shape floor — construction \u0026 inner-loop levers","description":"Continuation of Phase 7 (leu, closed). Phase 7's decisive finding (§12.3 calibration): predicted_shape_fails at the best achievable geometry ≈ the achieved total fail count (maple floor 121-163 vs achieved 126-148). Therefore SEARCH MACHINERY CANNOT HELP — there is no lower-fail basin for the constructed topologies to reach; the floor IS the result. Scoreboard: 4/4 wins from construction/seed quality (§11.2, §11.6, §11.7, §12.2), 0/3 from search machinery (§11.4, §11.5, §12.3). The only way to lower fails is to lower the geometry FLOOR.\n\nResidual decomposition (maple-court, 6 constructive seeds, §12.3): crinkliness 346 + size 242 (undersize) + proportion 121 + width 102, with plot utilisation only 0.44 (56% of plot empty) yet rooms UNDERSIZE. Diagnosed mechanism: over-granular construction — 73 leaves for 52 rooms — every leaf high perimeter/area (crinkliness) and below target area (size). c3g tested ONE granularity lever (circulation-spine coarsening via circ_divisor) → null (shape gain cancelled by equal access/adjacency damage). The other named levers were never tested.\n\nThis epic runs DIAGNOSTICS FIRST to decide which floor-lowering lever to invest in, then the construction/inner-loop experiments in dependency order. Tier-3 search-machinery bets (island model psk, tournament pressure 6zy) are tracked but LOW prior — do not invest there until something moves the floor.\n\nShared protocol (every experiment): A/B on maple-court + harbor, seeds 0/1/2, 20000 evals, staged; controls MUST reproduce the §12.2 baseline (maple 136.0, harbor 74.0); record verdict in DESIGN.md (new §13.x). Same discipline as every lever in §11-§12.","status":"closed","priority":1,"issue_type":"epic","owner":"bruno@postle.net","created_at":"2026-06-22T23:14:56Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:22:26Z","closed_at":"2026-06-28T13:22:26Z","close_reason":"all steps complete","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-leu.1","title":"Larger-than-house benchmark programme (\u003e16 rooms) + baseline","description":"PREREQUISITE for the whole epic. Harbor (16 rooms) is the biggest real programme in examples/; 9gp's scaling claim ('\u003e16 rooms') and acceptance criterion ('larger-than-house programme') cannot be measured without a bigger one.\n\nBuild a reproducible benchmark programme larger than harbor (target ~24-32 rooms, multi-storey, with a realistic per-level required-room partition and adjacency-to-c load like harbor's). Provide its patterns.config / costs.config (reuse config inheritance, homemaker-py-n5k) and an init.dom, mirroring the examples/harbor-house layout. Wire it into the existing experiment harnesses (run_search_scaled.py / run_staged_search.py) and record a BASELINE total-fail count at a fixed budget for the current default search (adjacency-aware seeding + staged), exactly as §11.6/§11.7 reported harbor. This baseline is the yardstick proportion-seeding and 9gp are measured against.\n\nDeliverable: examples/\u003cnew\u003e/ with configs+init.dom, a documented baseline (seeds 0-2, total fails at budget), recorded in DESIGN.md §12.1 + bead notes.","acceptance_criteria":"A \u003e16-room multi-storey benchmark exists under examples/, runs through the current harness, and has a documented baseline fail count (\u003e=3 seeds) recorded in DESIGN.md.","notes":"Benchmark delivered: examples/maple-court/ (26 entries / 52 rooms / 3 storeys, ~1015 m2 internal, ~790 m2/floor plot). Mirrors harbor's adjacency-to-c load + secondary adjacencies; room codes avoid generic c/o/s leading letters. Baseline (staged adjacency-aware, URB_NO_OCCLUSION=1, 20000 evals): seed0=145, seed1=158, seed2=152, mean=151.7 fails. All native re-score OK. Best (145, seed0) saved as generated.dom. Recorded in DESIGN.md §12.1.","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-19T11:13:59Z","created_by":"Bruno Postle","updated_at":"2026-06-19T12:33:31Z","started_at":"2026-06-19T11:17:25Z","closed_at":"2026-06-19T12:33:31Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-leu.1","depends_on_id":"homemaker-py-leu","type":"parent-child","created_at":"2026-06-19T12:13:59Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0} {"id":"homemaker-py-leu","title":"Phase 7: scaling validation \u0026 residual reduction (post-c4c)","description":"Continuation of the closed c4c epic (Phase 6). Phase 6 evidence is decisive about WHERE leverage lives: the two search-machinery experiments (§11.4 graded high-fail objective, §11.5 niching+restarts) BOTH landed negative and BOTH concluded the high-fail plateau is a REACHABILITY problem (operators+encoding cannot reach low-fail basins), not population-management or objective-shaping. The two wins (§11.6 adjacency-aware seeding, §11.7 adjacency-aware lift) came from CONSTRUCTION/SEED quality. Harbor is now ~85 fails (best 78), down from the 95/105 plateaus; the residual is geometry/shape-bound (size/proportion/crinkliness).\n\nThree gaps block further scaling progress and must be done in order:\n1. There is NO larger-than-house benchmark. Harbor (16 rooms) is the biggest real programme in examples/. 9gp's headline claim is scaling \u003e16 rooms and its acceptance criterion demands 'a larger-than-house programme' to measure on — so a bigger benchmark is a PREREQUISITE, not optional.\n2. Proportion-aware seeding: §11.6 noted the seed uses 0.5 splits -\u003e 'more, smaller leaves' -\u003e geometry fails. Sizing splits from target dims attacks the §11.7 geometry residual directly, in the proven construction direction; cheaper than an encoding rewrite.\n3. 9gp (canonical Polish encoding) must be RE-SCOPED: its 'topology signature for niching' justification is dead (§11.5 falsified niching; genome.signature already exists as the cheap stand-in). The surviving, evidence-supported parts are M1/M2/M3 Wong-Liu moves (reachability) and shape-feasibility pruning (residual + inner-loop budget = scaling).\n\nOrdering rationale: benchmark first (makes scaling measurable for everything downstream), then the cheap proven-direction seeding win (sets the strongest baseline), then the re-scoped canonical-encoding capstone (lands on the best seed, with a benchmark to prove its scaling claim).","design":"Do NOT build search/selection machinery on unmeasured premises — that is exactly what §11.4/§11.5 did and both regressed. Every child lands an experiment with results recorded in DESIGN.md §12.x + bead notes, same discipline as Phase 6. The benchmark child is the root dependency; proportion-seeding depends on it (so the win is measured at scale too); re-scoped 9gp depends on both (best baseline + scaling measurement).","acceptance_criteria":"(1) A reproducible \u003e16-room benchmark exists with a documented baseline fail count; (2) proportion-aware seeding shows a measured fail reduction on harbor AND the new benchmark; (3) re-scoped 9gp lands M1/M2/M3 + shape feasibility and shows measured search improvement on the larger-than-house benchmark.","notes":"EPIC COMPLETE. leu.1 established the \u003e16-room maple-court benchmark (baseline 151.7→ leu.2 136.0). leu.2 proportion-aware seeding: measured win on both larger programmes (harbor -13%, maple -10%), default-on. 9gp (re-scoped): M3 reassociate + shape-feasibility filter landed + measured NEGATIVE — the residual is the geometry/shape floor of the constructed layouts, not reachability/feasibility-bound. Net: Phase 7 reduced the benchmark residual via construction (leu.2) and validated that further search-machinery gains are unavailable (9gp), a 3rd search-machinery negative vs 4 construction wins. See DESIGN.md §12.","status":"closed","priority":1,"issue_type":"epic","owner":"bruno@postle.net","created_at":"2026-06-19T11:13:29Z","created_by":"Bruno Postle","updated_at":"2026-06-21T06:21:30Z","closed_at":"2026-06-21T06:21:01Z","close_reason":"all steps complete","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-c4c.3","title":"Staged per-floor search (curriculum: credible base floor, then upper floors as deltas)","description":"Search the genome in its causal dependency order. The base-floor tree is the master; upper storeys are deltas (Below-inheritance). The programme partitions rooms by required level (harbor: 10 L0, 4 L1, 2 free), so each floor's target room set is known up front. Today the search discovers both floors simultaneously via random typing + the rare/drastic level_add (weighted 0.2) — an uncontrolled, degenerate version of staging.\nStage 1 — base floor: search the single-storey tree over the level-0 room set, dimensionality reduced (one tree, no deltas).\nStage 2 — upper floors as deltas: seed each upper storey with ITS required room set (via the construction op, homemaker-py-c4c.2), search the deltas; keep the base MUTABLE at low probability so it can adapt to upper-floor pressure.\nCRITICAL non-goal: do NOT hard-freeze the base. A base optimised purely as ground floor is a §4.2-style partial objective and can be a bad SUBSTRATE. Stage 1 objective must include (a) a reserved, vertically-alignable circulation core and (b) a substrate-readiness term: enough divisible area/cut structure to host the level-1 room set later.","design":"Premise gated by homemaker-py-c4c.1: only high-value if single-storey construction already reaches low fails. Substrate-readiness proxy candidates: count of base leaves large enough to subdivide for L1 rooms; presence of a core node with vertical continuity. Stage transition: when stage-1 base hits a fails/score threshold or budget fraction, freeze-soft and open the delta genome. Composes with canonical encoding (homemaker-py-9gp) — deltas are where redundancy/coarse moves hurt most.","acceptance_criteria":"Staged search beats single-stage on harbor-house (best fails/score), measured at equal native-fitness budget and recorded in DESIGN.md §11.x + bead notes. Reserved-core + substrate-readiness shown to prevent the bungalow trap (stage-2 does not have to carve a core from scratch — track core-carving moves). No regression on programme-house.","notes":"DONE. Implemented driver.search_staged (Stage 1 single-storey base over level-0 set with substrate-readiness ranking bonus; Stage 2 upper floors lifted as constructed deltas, base mutable at base_p=0.15). New: programme.{n_storeys_required,partition_rooms_by_storey,write_stage1_programme}, graph.substrate_readiness, operators.{lift_base_to_storeys,_pick_weighted_by_storey,base_p}, driver.search rank_bonus_fn/seed_factory/base_p hooks, experiments/run_staged_search.py, tests/test_staging.py. RESULT (harbor, 20000 evals, seed 0): staged 95 fails vs single-stage 105 (-10, -9.5%); gain in crinkliness 27-\u003e18 + edge 12-\u003e8, small access cost +5. Anti-bungalow CONFIRMED: all Stage-2 core_divide/undivide in winning lineage are noops (core inherited, not carved). Regression PASS: programme-house warmstart-2f4 still reaches whole-pop 1-fail. DESIGN.md §11.3 filled. Remaining high-fail plateau is §11.4 (graded objective) territory.","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-17T19:01:01Z","created_by":"Bruno Postle","updated_at":"2026-06-18T05:04:48Z","started_at":"2026-06-18T04:25:07Z","closed_at":"2026-06-18T05:04:48Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-c4c.3","depends_on_id":"homemaker-py-c4c","type":"parent-child","created_at":"2026-06-17T20:01:00Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-c4c.3","depends_on_id":"homemaker-py-c4c.1","type":"blocks","created_at":"2026-06-17T20:01:00Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-c4c.3","depends_on_id":"homemaker-py-c4c.2","type":"blocks","created_at":"2026-06-17T20:01:01Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":2,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-c4c.2","title":"Programme-aware construction + missing-room repair operator","description":"Highest-leverage fix for the epic's diagnosis. Today mutate_divide (operators.py:71) types new leaves at RANDOM, so required programme spaces go missing -\u003e 'missing' stacking dominates fitness on full programmes (harbor: 6 missing-room records stacking critical+size+width+adjacency+level). Make the required room set a constructive invariant rather than something the search must stumble onto.\nTwo parts:\n1. Constructive seeder: generate initial topologies that instantiate each required space (respecting count/level/type) by construction, instead of random divide+retype chains.\n2. Repair operator mutate_place_missing: detect a required-but-absent space and insert it (divide a compatible leaf, type the new leaf to the missing code, prefer a slot satisfying its adjacency). Complements mutate_level_compound_fix (which repairs level, not presence).\nWire the seeder into driver bootstrap and the repair op into mutate() weights.","design":"Seeder must place generic C (circulation/core) and O (outside) too, not just programme codes. Keep it stochastic (diverse population) but biased to cover the required set + correct levels. Repair op should be lex-safe: prefer insertions that don't create more new fails than the missing-stack it removes (cf. the §4.10 deceptive-valley lesson — a naive insert dumps a room into a bad slot and nets worse).","acceptance_criteria":"On harbor-house, 'missing'-type failures collapse to ~0 across the population (record before/after fail histograms); measured net-fail improvement vs current 74-fail out1.dom baseline, recorded in DESIGN.md §11.x + bead notes. No regression on seeded programme-house (still reaches 1-fail optimum, §4.10).","notes":"DONE 2026-06-17. Implemented constructive_topology seeder + mutate_place_missing repair op (operators.py), wired into driver bootstrap + mutate weights. A/B on harbor (20k evals, seed 0, identical config): old random-bootstrap 133 fails (103 missing, 77%) -\u003e new constructive 105 fails (12 missing, 11%); missing-records 22-\u003e2; -21% total. Seed head-start 163-\u003e139. §4.10 regression PASS: warmstart-2f4 still reaches 1-fail population at 50k. Verdict: construction necessary, reframes bottleneck to quality-fail packing (crinkliness/size/access/edge) of complete dense design -\u003e unblocks §11.3 staging, motivates §11.4 graded objective. Follow-up filed: adjacency-aware seeding. Full numbers in DESIGN.md §11.2. 186 tests pass.","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-17T18:51:21Z","created_by":"Bruno Postle","updated_at":"2026-06-17T21:50:34Z","started_at":"2026-06-17T20:19:39Z","closed_at":"2026-06-17T21:50:34Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-c4c.2","depends_on_id":"homemaker-py-c4c","type":"parent-child","created_at":"2026-06-17T19:51:20Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":3,"comment_count":0} {"id":"homemaker-py-c4c.1","title":"Experiment: single-storey harbor premise test (per-floor construction vs multi-storey coupling)","description":"De-risk the staged-search and construction work BEFORE building either. Strip harbor-house to its 10 level-0 rooms as a single-storey programme; run the current memetic search from a bare plot; record best fails/score and the fail-type histogram. This isolates the question: is the bottleneck per-floor CONSTRUCTION (placing the right room set on one floor) or the multi-storey COUPLING (deltas, core alignment, level constraints)?\n- If single-storey 10-room reaches near-zero fails: the difficulty is coupling -\u003e staged per-floor search (homemaker-py-\u003cstaging\u003e) is the high-value lever.\n- If it still stalls at many fails (esp. 'missing'): per-floor construction itself is the bottleneck -\u003e programme-aware construction (homemaker-py-\u003cconstruction\u003e) is required first and staging alone won't rescue it.\nRun from blank-slate (init.dom equivalent) AND from a bootstrap population; report both.","design":"Build examples/harbor-house-l0/ from harbor's level-0 spaces only (drop level: keys or set all to 0; keep adjacency among the retained codes). Reuse experiments/run_search_scaled.py harness. Cheap (~minutes at native-fitness throughput).","acceptance_criteria":"Single-storey 10-room harbor variant created and committed under examples/; current search run and best fails/score + fail histogram recorded in DESIGN.md (new §11.x) and bead notes; explicit verdict on construction-vs-coupling.","notes":"VERDICT: per-floor CONSTRUCTION is the bottleneck, not multi-storey coupling.\nBuilt examples/harbor-house-l0/ (10 explicit level:0 codes = 13 room instances, single-storey constraints), seeded from bare init.dom.\nRun: URB_NO_OCCLUSION=1 python3 experiments/run_search_scaled.py examples/harbor-house-l0 20000 0 examples/harbor-house-l0/init.dom examples/harbor-house-l0/generated.dom\nResult: 20000 evals / 250 topologies / 234s. Best 33 fails (fitness 2.25e-12, deep in 0.5^n regime); whole pop stuck 33-35. 40-\u003e33 over full budget. NOT near zero.\nFail histogram: 13 missing (all 3 m meeting rooms never built) + 6 adjacency + 4 access + 4 size + 2 edge-too-long + 2 crinkliness + 1 proportion + 1 too-few-stairs(single-storey artifact). Missing = 39% — matches the 'still stalls esp. missing' branch.\n=\u003e c4c.2 (programme-aware construction + missing-room repair) is the prerequisite; staging (c4c.3) alone won't rescue it. c4c.3 already correctly depends on both. Full writeup in DESIGN.md §11.1.","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-17T18:49:43Z","created_by":"Bruno Postle","updated_at":"2026-06-17T20:15:36Z","started_at":"2026-06-17T19:25:49Z","closed_at":"2026-06-17T20:15:36Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-c4c.1","depends_on_id":"homemaker-py-c4c","type":"parent-child","created_at":"2026-06-17T19:49:43Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-c4c","title":"Phase 6: topology-search quality for full/multi-storey programmes","description":"Diagnosis (survey 2026-06-17): the delivered speedups (native fitness ~140x, geometry inner loop ~1.6x) landed in the two layers that were never the bottleneck. The geometry inner loop polishes WITHIN a failure tier (DESIGN.md §4.5/§4.7: 0 fail changes, by design — the 0.5^n cliff protects it). But final design quality is dominated by FAILURE COUNT, which is almost entirely a topology property. Topology search on full programmes is the weakness:\n- blank-slate programme-house (init.dom): memetic stalls at 18 fails vs urb-evolve 6 (§7 Phase 2 verdict);\n- harbor-house (16 rooms): out1.dom=74 fails, generated.dom=130 fails, both at ~machine-epsilon score; fails dominated by 'missing' room stacking (each missing room stacks critical+size+width+adjacency+level, §6).\nSmoking gun: operators.mutate_divide (operators.py:71) assigns each new leaf a RANDOM type from programme-codes+C+O. Nothing guarantees the required programme spaces are instantiated, so on a large programme required rooms go missing -\u003e catastrophic 0.5^n stacking, and the search is a random walk over type assignments with a flat/catastrophic gradient in the high-fail regime.\nThis epic groups the topology-search-quality work: programme-aware construction, staged per-floor search, graded high-fail objective, topology diversity, then the canonical-encoding capstone. Everything experiment-driven; results recorded in DESIGN.md sections + bead notes.","design":"Causal frame: base-floor tree is the master genome; upper storeys are divide/undivide deltas (Below-inheritance); the programme partitions rooms by required level (harbor: 10 on L0, 4 on L1, 2 free). So construction and search should follow the genome's dependency order: credible base floor first, upper floors as deltas, with required-room sets known per floor from the programme. Do NOT hard-freeze the base when adding floors — that recreates the §4.2 partial-objective trap at the topology level (a base optimised purely as ground floor can be a bad SUBSTRATE: vertical core must stay aligned, load-bearing walls must stack). Curriculum, not freeze.","acceptance_criteria":"Memetic search reaches a competitive low-fail design on harbor-house (16 rooms, multi-storey) and on blank-slate programme-house, beating the current 74/18-fail plateaus; each child bead lands its experiment with results recorded in DESIGN.md.","status":"closed","priority":1,"issue_type":"epic","owner":"bruno@postle.net","created_at":"2026-06-17T18:45:39Z","created_by":"Bruno Postle","updated_at":"2026-06-18T22:41:59Z","closed_at":"2026-06-18T22:41:59Z","close_reason":"all steps complete","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-mz5","title":"Python native fitness evaluation (port urb-fitness.pl)","description":"We need a Python implementation of the urb-fitness scoring tool that is faithful to the Perl oracle (urb-fitness.pl / ProgrammeDriven.pm). This is the 'native fitness' component identified in DESIGN.md §6 as gating topology search at scale — the oracle requires a subprocess+file roundtrip per eval which is too slow for large populations.\n\nThe native fitness must reproduce all scoring terms from the Perl source:\n- size, width, proportion (per-space Gaussian scoring)\n- adjacency, access/inaccessible, crinkliness, perpendicular\n- level, staircase volume/count, public access\n- circulation \u0026 outside ratios, min internal area\n\nSource of truth: /home/bruno/src/urb/lib/Urb/Dom/Fitness/ProgrammeDriven.pm and the Storey/Building/Leaf/Base submodules.\n\nValidation target: match oracle scores on the programme-house corpus (35+ .dom files) to within the ~3.7% gap documented in homemaker-py-gpx.","status":"closed","priority":1,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-15T22:18:06Z","created_by":"Bruno Postle","updated_at":"2026-06-17T17:51:53Z","closed_at":"2026-06-17T17:51:53Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-40i","title":"Investigate cf0b8a77e8b2325f ~18% raw_value discrepancy (py lower than oracle)","description":"For prefix cf0b8a77e8b2325f: oracle=1.079112e-03 py=9.133243e-04 ratio=0.8464 (python is ~18% too low). debug_nfails shows py n_fails=5 oracle n_fails=5 (same failures), stair_fits=[1.3145] in python, building_factor=0.1104 (vs oracle's implied ~0.1303). The discrepancy is in raw_value (py=11837 vs oracle implied ~13975) or possibly building_factor. Need to check: (1) per-leaf quality values (crinkliness, area_outside, access) via debug_quality.txt; (2) whether the stair corners differ (cf/rl: py=[2,3] perl=[2,3] — SAME, so corners ok); (3) any quality term not yet ported or computed differently. Run debug_quality.py and compare per-leaf contributions.","status":"closed","priority":1,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T18:08:22Z","created_by":"Bruno Postle","updated_at":"2026-06-13T19:54:04Z","started_at":"2026-06-13T18:12:23Z","closed_at":"2026-06-13T19:54:04Z","close_reason":"Investigation complete: traced 18% discrepancy (cf0b8a77) through entrance corner logic and weighted path length bugs, both now fixed in w1e.","dependencies":[{"issue_id":"homemaker-py-40i","depends_on_id":"homemaker-py-hgg","type":"blocks","created_at":"2026-06-13T19:08:30Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-w1e","title":"Port Perl entrance-corner logic into Python stair-fit (ca/cb parity)","description":"Perl's check_stair_fit (Leaf.pm:104-142) adds entrance edge corners to corners_in_use before computing stair_fit. Python's process_storey does not. For ca9e80c5c1502f10 and cb93a2d2de7f5d37 the oracle stair leaf 'lr' has corners [1,2,3] (from perl_bf.pl) but debug_corners.py Perl-compatible trace gives [2,3] — the extra corner 1 comes from Entrances(graph)-\u003eBoundary_Id logic. Need to: (1) port dom.Entrances() — returns {leaf_id: boundary_id} for the best-entrance leaf at ground level (Entrances() returns {} if level\u003e0); (2) port leaf.Boundary_Id(side) — returns the node sharing that edge; (3) in fitness.process_storey, after stack_corners_in_use, add entrance-edge corners before computing stair_fit. Acceptance: ca/cb ratio ≈ 1.0 (currently 1.33).","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T18:08:11Z","created_by":"Bruno Postle","updated_at":"2026-06-13T19:53:40Z","started_at":"2026-06-13T19:09:35Z","closed_at":"2026-06-13T19:53:40Z","close_reason":"Fixed: entrance corner logic via _entrance_bid_for_stair (mirrors Perl Entrances), plus root cause: _avg_path_len_from now uses weighted Dijkstra (centroid distances) matching Perl graph.average_path_length — fixes has_circulation edge removal order. All 4 debug prefixes ratio=1.000, 39 tests pass.","dependencies":[{"issue_id":"homemaker-py-w1e","depends_on_id":"homemaker-py-hgg","type":"blocks","created_at":"2026-06-13T19:08:29Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-q70","title":"Fix corners_in_use _ib(None,None) bug: triple-at-idx=3 always passes in Perl","description":"In graph.py corners_in_use(), the _ib helper returns False when pa=None or pb=None. Perl's is_between_2d(point, undef, undef) returns True (distance_2d(undef,x)=0 so abs(0-0-0)\u003c1e-6). At triple-check idx=3, c1=corners[4]=None and c2=None, so _ib(w, None, None) must return True to match Perl — meaning the triple always succeeds at idx=3. Fix: add 'if pa is None and pb is None: return True' before the existing 'if pa is None or pb is None: return False'. This is already applied to graph.py. Needs: run 35-file corpus parity test to confirm aa0dcab98927d2c9 passes (corners [0,1,3] → stair_fit=0.878 → sf_factor≈0.570 ≈ oracle).","status":"closed","priority":1,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-06-13T18:07:54Z","created_by":"Bruno Postle","updated_at":"2026-06-13T19:53:53Z","closed_at":"2026-06-13T19:53:53Z","close_reason":"Fixed as part of homemaker-py-w1e: the _avg_path_len_from weighted Dijkstra fix corrects has_circulation edge removal ordering, which was the actual cause of wrong stack corner counts. The _ib(None,None)=True fix was already in place but the weighted path length was the remaining blocker.","dependencies":[{"issue_id":"homemaker-py-q70","depends_on_id":"homemaker-py-hgg","type":"blocks","created_at":"2026-06-13T19:08:28Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-gp2","title":"Disable occlusion/daylight in Urb oracle (env flag); re-baseline scores","description":"Strategy decision (Bruno, 2026-06-12): occlusion/daylight is orthogonal to whether a better, scalable optimisation system can be built — disable it in Urb rather than port it. Patch Urb behind an env flag (e.g. URB_NO_OCCLUSION=1): quality_daylight returns 1 for outdoor spaces too, and Crinkliness/Area_Outside pins the CIEsky_vertical illumination factor to 1 (simple crinkliness = unweighted external wall area / floor area). Keep the occlusion object plumbing — it carries the Walls/boundaries cache crinkliness needs (ProgrammeDriven.pm:97). Then re-baseline everything once at this clean boundary: corpus .score files, the DESIGN.md $4.5 gains table, accept_innerloop.py gate bars. Also measure oracle s/dom with the flag on — occlusion sampling may be a real slice of the ~1 s/dom cost. The native Python fitness then ships with simple crinkliness only; full occlusion rebuild is deferred post-Phase-5 (homemaker-py-2g5).","acceptance_criteria":"Env-flagged Urb patch; flag on: corpus re-scored, gate bars re-derived, oracle s/dom re-measured; urb-evolve confirmed to respect the flag for the Phase-2 benchmark","status":"closed","priority":1,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-06-12T07:27:30Z","created_by":"Bruno Postle","updated_at":"2026-06-12T09:31:40Z","closed_at":"2026-06-12T09:31:40Z","close_reason":"URB_NO_OCCLUSION=1 patch in Urb (Leaf.pm quality_daylight -\u003e 1, Dom.pm Area_Outside illumination pinned; flag-off byte-identical, verified). Corpus re-baselined: 35/35 scores shift, one expected crinkliness failure-set change, 0.92 s/dom batched (x1.08). New reference gains recorded in DESIGN §4.7 and accept_innerloop bars (x1.63/x1.70/x1.68, deterministic seed). urb-evolve respects flag by construction. NOTE: Urb working-tree changes left uncommitted in /home/bruno/src/urb for Bruno's review.","dependency_count":0,"dependent_count":3,"comment_count":0} {"id":"homemaker-py-uxz","title":"Native fitness validation: 35-file corpus parity vs oracle; retire oracle (Phase 3 gate)","description":"DESIGN.md §7 Phase 3 gate. Validate the assembled native fitness against urb-fitness.pl across all 35 programme-house .dom files: scores within float tolerance AND identical failure sets. Swap behind the same interface as oracle.score so inner loop and search driver are unchanged; keep the oracle available as validation reference but stop using it in search. Then re-run topology search at scale (separate issue).","acceptance_criteria":"35/35 files: score parity within tolerance, failure sets identical; search runs end-to-end on native fitness with measured speedup vs oracle","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:38:27Z","created_by":"Bruno Postle","updated_at":"2026-06-13T20:45:30Z","started_at":"2026-06-13T19:57:48Z","closed_at":"2026-06-13T20:45:30Z","close_reason":"35/35 score parity + fail-set parity; NativeEvaluator added; optimise() defaults to use_native=True; 23x speedup; one bug fix (_entrance_bid_for_stair via-outdoor case)","dependencies":[{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-3y7","type":"blocks","created_at":"2026-06-12T00:39:40Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-40i","type":"blocks","created_at":"2026-06-13T19:08:33Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-gnw","type":"blocks","created_at":"2026-06-12T00:39:41Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-gp2","type":"blocks","created_at":"2026-06-12T08:27:44Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-hgg","type":"blocks","created_at":"2026-06-12T00:39:43Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-q70","type":"blocks","created_at":"2026-06-13T19:08:31Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-uxz","depends_on_id":"homemaker-py-w1e","type":"blocks","created_at":"2026-06-13T19:08:32Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":7,"dependent_count":3,"comment_count":0} {"id":"homemaker-py-hgg","title":"Native fitness: storey/building checks + missing-space failure stacking","description":"DESIGN.md §6. Port ProgrammeDriven/Storey/Building checks: space-count matching with MISSING-SPACE FAILURE STACKING (2 base failures + 1 per size/width/proportion/adjacency/level requirement, up to ~7 — ProgrammeDriven.pm:192-212; reshaping must preserve this hierarchy), adjacency/level/requires_below checks, staircase fit/volume/min-max, public access, circulation \u0026 outside ratios, min internal area (1.2x programme sum), storey limit/minimum, structural failures (edge too long \u003e8 m both variants, unsupported covered outside, covered outside above ground, level not connected, inaccessible usable space), preprocess_building s-\u003eO conversion, and the 0.5^n penalty over value/cost.","acceptance_criteria":"Failure sets and final scores match the oracle on sample files; failure-stacking counts identical","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:38:26Z","created_by":"Bruno Postle","updated_at":"2026-06-13T18:11:27Z","started_at":"2026-06-13T08:59:14Z","closed_at":"2026-06-13T18:11:27Z","close_reason":"Implementation complete: storey/building checks, failure stacking, staircase logic, public access, circulation ratios, structural checks all ported. 39 tests pass.","dependency_count":0,"dependent_count":4,"comment_count":0} {"id":"homemaker-py-gnw","title":"Native fitness: leaf quality terms + cost model","description":"DESIGN.md §6. Port Leaf.pm quality terms (size, width, proportion, perpendicular, access) with programme-driven parameter lookup (get_space_params fallback chain, generic c/o/s handling, width_inside [4.0,1.0] default), gaussian scoring, FAIL_THRESHOLD=0.1. Also the COST DENOMINATOR — fitness is value/cost: per-leaf area costs, interior/exterior wall edge costs, boundary costs, value rates (Leaf.pm:194-251, Storey.pm:122-147). Cost couples to geometry too.","acceptance_criteria":"Per-leaf quality factors and per-storey cost/value match Perl (float tolerance) on sample corpus files with DEBUG output diffed","notes":"Crinkliness scope (2026-06-12): port SIMPLE crinkliness only — external wall area / floor area with the CIEsky illumination factor pinned to 1 (boundary-overlap geometry from Dom-\u003eWalls stays in scope; the sky model does not). Must match the URB_NO_OCCLUSION-flagged oracle (homemaker-py-gp2), not stock Urb. quality_daylight = 1 for all spaces.","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:38:24Z","created_by":"Bruno Postle","updated_at":"2026-06-13T07:00:07Z","started_at":"2026-06-12T21:07:06Z","closed_at":"2026-06-13T07:00:07Z","close_reason":"0-mismatch parity: 35 files, 407 leaves, 2849 factors","dependencies":[{"issue_id":"homemaker-py-gnw","depends_on_id":"homemaker-py-gp2","type":"blocks","created_at":"2026-06-12T08:27:46Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-3y7","title":"Native fitness: adjacency/connectivity graph build + Merge_Divided semantics","description":"DESIGN.md §6 port scope, §7 Phase 3 (native fitness gates topology search at scale — §4.6). Port the door_width (1.2 m) adjacency graph (Urb Dom Graph), Merge_Divided, and the TWO-PHASE build: adjacency/level/vertical checks run on the UNMERGED tree, graphs rebuilt after Merge_Divided for storey processing (ProgrammeDriven.pm:83-103). Port faithfully — including has_vertical_connection's no-spatial-overlap stub (ProgrammeDriven.pm:399-423) unless the fidelity decision (§8.1) says otherwise; record the decision.","acceptance_criteria":"Graph edges/widths and merged structure match Perl on the 35-file corpus; vertical-connectivity fidelity decision recorded","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:38:23Z","created_by":"Bruno Postle","updated_at":"2026-06-12T20:55:31Z","started_at":"2026-06-12T13:13:09Z","closed_at":"2026-06-12T20:55:31Z","close_reason":"Graph edges/widths match Perl on all 35 corpus files (2 bugs fixed: empty-string boundary excluded by Python 'in' operator substring check, and upper-storey rotation not delegating to below-link). Merge_Divided ported. Vertical-connectivity fidelity decision recorded in graph.py module docstring (faithful stub, no spatial overlap). Tests in test_graph.py.","dependency_count":0,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-1p0","title":"Geometry inner loop: full-objective equal-offset ratio optimiser","description":"DESIGN.md §5.1, §7 Phase 1. Productionise experiments/optimize_fullfitness.py into homemaker: optimise(topology, x0=None) -\u003e (geometry, fitness). DOF = equal-offset division ratios of free branches (solver.free_branches, lowest-storey cut ownership), clipped to [eps, 1-eps]. Objective = full oracle fitness (never a proxy — §4.2 falsified). Must support warm-start x0 (§5.6) and a population/batch evaluation mode so each iteration scores via one batched oracle call (§4.6).","acceptance_criteria":"Reproduces or exceeds §4.5 gains (x1.24–x1.67, no new failures) on 2f45907, candidate-002, c964435; works as a library call on any corpus .dom","status":"closed","priority":1,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:36:58Z","created_by":"Bruno Postle","updated_at":"2026-06-12T08:46:31Z","started_at":"2026-06-12T00:14:19Z","closed_at":"2026-06-12T08:46:31Z","close_reason":"innerloop.optimise() lands: batched CMA-ES sigma ladder (0.05/0.15, IPOP popsize doubling, deterministic seeding) over equal-offset free-branch ratios vs full oracle fitness; warm-start x0 supported. Acceptance vs unprojected originals: x1.65/x1.66/x1.58 against bars x1.24/x1.67/x1.59, no new failures, 46 oracle calls vs NM's 200. Two near-bar results accepted as reproduced-within-noise (1% tol) — draw spread brackets the single-NM-draw bars; approved by Bruno 2026-06-12. Gotchas: equal-offset projection of legacy unequal cuts loses fitness/adds failures (midpoint projection used); pycma seed=0 means clock-seeded.","dependencies":[{"issue_id":"homemaker-py-1p0","depends_on_id":"homemaker-py-av5","type":"blocks","created_at":"2026-06-12T00:39:33Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":3,"comment_count":0} {"id":"homemaker-py-8cs","title":"Experiment: warm-vs-cold start of inner loop (Lamarckian inheritance)","description":"DESIGN.md §5.6, §4.6. Warm-starting a child topology's inner loop from the parent's optimised ratios is the main lever for cutting per-topology cost (~3 min/topology cold). Apply single topology mutations to optimised corpus designs, re-optimise warm (surviving cuts keep values, new cuts get heuristic defaults) vs cold, compare oracle-call counts to convergence at equal final fitness.","acceptance_criteria":"Speedup factor measured across \u003e=10 mutated topologies; decision recorded (expect order-of-magnitude; if \u003c2x, revisit §4.6 Phase-2 scoping)","notes":"Experiment script committed (experiments/warm_vs_cold.py, 1cc86c8) and machinery validated oracle-free; one mutated child scored through the oracle OK. Waiting on homemaker-py-gp2 reference run to finish, then execute under URB_NO_OCCLUSION=1 (3 parents x 400 evals + 12 children x 2 x 200 evals, ~1.5-2 h oracle time). Default budgets: parent 400, child 200; target = evals to 95% of best final.","status":"closed","priority":1,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-06-11T23:36:58Z","created_by":"Bruno Postle","updated_at":"2026-06-12T11:44:45Z","closed_at":"2026-06-12T11:44:45Z","close_reason":"Measured (URB_NO_OCCLUSION=1, parent budget 400, child 200, 12 single mutations across 3 designs): cold start reached 95% of warm final in 0/12 cases within budget — speedup unbounded at practical budgets; warm finals beat cold finals x1.2-x4 in 12/12; 6/12 warm starts were within 95% at 1 eval (near-neutral mutations). Decision: Lamarckian warm-starting is MANDATORY in the memetic driver (homemaker-py-b39), not an optimisation; cold starts produce strictly worse geometry at equal budget. Note: 2 undivides were exactly fitness-neutral (same-type merge == Merge_Divided equivalence) — locality datum for homemaker-py-nyb.","dependencies":[{"issue_id":"homemaker-py-8cs","depends_on_id":"homemaker-py-1p0","type":"blocks","created_at":"2026-06-12T00:39:34Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-av5","title":"Batched oracle: score many .dom files per invocation","description":"oracle.py currently scores one .dom per urb-fitness.pl call (~1.65 s/dom). DESIGN.md §4.6: batching amortises Perl startup to ~0.99 s/dom and is required so population/batch optimisers can score a whole generation in one oracle call. Extend oracle.py with a batch API: write N .dom files, one perl invocation, parse N .score/.fails pairs. Keep the single-file path for compatibility.","acceptance_criteria":"Batch of 35 corpus files scores in one perl invocation; per-file results identical to single-file calls; measured s/dom reported","status":"closed","priority":1,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:36:56Z","created_by":"Bruno Postle","updated_at":"2026-06-12T00:14:06Z","started_at":"2026-06-11T23:50:40Z","closed_at":"2026-06-12T00:14:06Z","close_reason":"score_batch() lands in oracle.py; 35-file corpus parity verified single-vs-batch (1e-12 rel fitness, exact fail sets); 0.98 s/dom batched vs 1.27 single, x1.30","dependency_count":0,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-9wi","title":"Adjacency-aware discrete assignment for finish-time collapse (QAP/CP-SAT)","description":"fitness.collapse_superposition (homemaker-py-9o5/94g family) already does exact optimal relabelling of superposed leaves via brute-force permutation (CLASS_CAP\u003c=4) or Hungarian (linear_sum_assignment) beyond that -- but _best_assignment's docstring is explicit that the objective is deliberately SEPARABLE per leaf (quality_size * quality_width * quality_proportion only); perpendicular/crinkliness/access/adjacency are assumed usage-invariant within a class and left out, because adjacency quality depends on PAIRS of leaf-label assignments, not one leaf at a time, which breaks the exact separable solve.\n\nProposal: extend the collapse step to account for adjacency between candidate labels -- either a quadratic-assignment-style local search (2-opt swaps over the current Hungarian solution, accepting swaps that improve total adjacency satisfaction) or a CP-SAT (OR-Tools) encoding of the labelling problem with pairwise adjacency terms. This directly extends the one search-adjacent technique (exact/near-exact discrete assignment) that has actually paid off in this project, into territory the current separable solve cannot reach.\n\nMeasure against the current Hungarian-only collapse on harbor-house (heavy interchange-class usage: neighborhoods, meeting rooms, individual rooms) where adjacency-blind relabelling is most likely to leave adjacency fails on the table.","status":"open","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-07-25T20:18:24Z","created_by":"Bruno Postle","updated_at":"2026-07-25T20:18:24Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-f1d","title":"Ruin-and-recreate LNS: rebuild wings with the adjacency-aware constructor mid-search","description":"DESIGN.md's own experiment log shows every 'search machinery' change (niching+restarts 11.5, graded objective 11.4, Wong-Liu reassociation+shape-feasibility 12.3, granularity 12.4, island model 14, grain annealing 16, circulation-repair ops 21/22) has come back null-to-negative, while construction/seeding quality (adjacency-aware seeding 11.6/11.7, proportion-aware seeding 12.2) is the only lever that has ever moved the fail count. operators._assign_adjacency_aware currently only runs once, at seeding.\n\nProposal: a large-neighbourhood-search move that periodically un-divides a whole wing/subtree of the CURRENT BEST individual and reconstructs just that region using the same proven adjacency-aware constructive heuristic (seeded from the surviving circulation spine as fixed_circ, same mechanism lift_base_to_storeys already uses for upper floors), instead of relying only on small local mutation operators to find improvements. Reuses the one technique with a real track record, applied repeatedly during search rather than once at initialisation.\n\nA/B against the current baseline on programme-house and harbor-house at a fixed worker count (see 12.4's determinism-fix note about serial vs parallel admission order before trusting sub-±3 effects).","notes":"Implementation landed (uncommitted, pending A/B): operators.mutate_ruin_recreate — picks a divided, live-cut wing of one storey (2..half its leaves), un-divides it, regrows+retypes it via a scope-generalised operators._assign_adjacency_aware (new 'scope' param restricts retyping to a leaf subset while fixed_circ seeds can be border leaves outside that subset), seeded from already-typed circulation leaves bordering the wing. Room-code budget inside the wing is preserved exactly; circ/outside counts rebuilt at circ_divisor=3/outside_divisor=3. Gated like reassociate/bridge_circulation: mutation_weights['ruin_recreate']=0.0 unless driver.search(enable_ruin_recreate=True); CLI flag --ruin-recreate/--no-ruin-recreate added to evolve.py (default off). 297 existing tests pass unchanged; added smoke coverage (200 applications on harbor-house constructive seeds: zero missing-space regressions, all canonical). A/B now running in background: experiments/run_f1d_ab.sh, qpk protocol (harbor-house budget=2500 seeds 1-3, programme-house budget=3000 seeds 1-5, workers=4, both arms finished with standard --collapse), results -\u003e scratch/f1d_ab_results.tsv.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-25T20:18:02Z","created_by":"Bruno Postle","updated_at":"2026-07-26T08:30:13Z","started_at":"2026-07-25T20:20:56Z","closed_at":"2026-07-26T08:30:13Z","close_reason":"DONE (positive, size-dependent). operators.mutate_ruin_recreate landed: un-divides one wing of a storey (2..half its leaves), rebuilds it via a scope-generalised _assign_adjacency_aware seeded from bordering circulation, mirroring lift_base_to_storeys' core-inheritance mechanism. Gated like reassociate/bridge_circulation (enable_ruin_recreate, default off; --ruin-recreate CLI flag). Initial uniform-weight A/B was null but underpowered (op fired ~1/32 children); a weight=3.0 follow-up (_MUTATION_WEIGHTS['ruin_recreate']=3.0, kept in source) showed a statistically significant win on programme-house across 15 seeds (8W/1L/6T, mean fails 7.07-\u003e6.00, Wilcoxon p=0.041, sign-test p=0.020) but no consistent effect on harbor-house across 8 seeds (3W/2L/3T, mean fails 73.0-\u003e74.5, slight negative lean). Kept default OFF pending a size-threshold follow-up (not filed) -- same conservative bar qpk/1ph applied before its own larger-N confirmation. Full writeup: DESIGN.md §23.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-mi7","title":"Prototype: 3D bubble-diagram relaxation of programme adjacency as a fitness signal","description":"Explore building a spring/force relaxation over the programme's required-space adjacency graph (multiple random-restart solutions), then score how well an actual Dom layout's real adjacency-graph distances correlate with a relaxed target's distances. Goal: an additional fitness term / search-guidance signal beyond the existing binary adjacency checks in graph.py. Prototype module: bubble.py. Validate by correlating similarity score against existing fitness .score on examples/programme-house's 36 scored .dom files.","notes":"Harbor-house real trajectory (driver.search, budget=6000, n=75 recorded individuals, fitness 3e-28 -\u003e 3.9e-17, fails 83-\u003e51):\n- embedding similarity(): spearman=0.164 p=0.16 (n.s.)\n- topological_similarity(): spearman=-0.160 p=0.17 (n.s.)\n\nFINAL PICTURE across 4 tests (2 programmes x 2 metric formulations, plus 2 canned-batch tests earlier): no statistically significant correlation anywhere between either the spring/embedding bubble-diagram similarity or the pure topological/abstract-graph-fitting similarity, and existing programme-driven fitness score. programme-house's real-trajectory result (rho=0.05 / rho=-0.06, n=100) is the cleanest data point — that programme has zero multi-count anonymous codes, so matched_leaves' known centroid-order matching heuristic cannot be confounding it, and it's still flat. Harbor-house is noisier (heavy anonymous counts: n x5, m x3, t x6, r x10, of x2 — the fixed centroid-order matching there is a real, uncontrolled confound) but tells the same story.\n\nRecommendation: do not pursue graph-relaxation-derived or pure-topological adjacency-matching as a fitness signal for this project without a fundamentally different formulation — two independent formulations, tested on two programmes with real evolved trajectories (not just static examples), both came back null. If revisited later, the harbor-house confound (anonymous-code instance matching) would need a real assignment solver (Hungarian/brute-force per homemaker-py-9o5's CLASS_CAP pattern) before drawing any conclusion there specifically, but programme-house's clean null already argues against the core idea. bubble.py is left in the repo (uncommitted) as a documented, working prototype/reference — not wired into fitness.py.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-25T12:42:02Z","created_by":"Bruno Postle","updated_at":"2026-07-25T20:12:20Z","started_at":"2026-07-25T12:42:26Z","closed_at":"2026-07-25T20:12:20Z","close_reason":"Two independent formulations (spring/embedding bubble-diagram, pure topological hop-distance) both tested null against real evolve trajectories on programme-house (n=100, clean — no anonymous-code confound) and harbor-house (n=75). No positive correlation with existing fitness score found. bubble.py left in repo, uncommitted, as documented reference; not wired into fitness.py. Consistent with the project's broader pattern (see DESIGN.md §11.4/11.5/12.3/12.4/14/16/21/22): 'search machinery' / fitness-shaping changes have been null-to-negative essentially every time they've been tried; only construction/seeding quality and representation-relaxation changes (leaf-sharing, type superposition, global collapse) have ever moved the needle. This session's result is another data point for that pattern, not an exception.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-qi6","title":"Circulation placement to clear not-connected / access fails","description":"The 94g finish-time collapse cannot touch the \"not-connected\" (level N not\nconnected) and related access/inaccessible fails: they are properties of the\nCIRCULATION skeleton (c/o/s cells), which the collapse deliberately never\nrelabels (they form the structure the room assignment is layered onto). On the\nharbor-house best layout 2 of the 15 fails are not-connected (levels 0 and 1);\nthese are out of scope for any label optimisation.\n\nGOAL: a search/repair step that places or reshapes circulation so every usable\nspace is reachable and each storey's circulation graph is connected. Candidate\nmechanisms: (a) a mutation/operator that inserts a circulation cell to bridge a\ndisconnected component (graph.py already computes connected components +\nconnected_circulation); (b) a finish-time repair that re-types a boundary cell to\ncirculation where it reconnects the graph at least net-fail cost; (c) bias the\nouter search toward connected topologies via the graded signal.\n\nInteracts with 94g: circulation placement changes which cells are skeleton vs\nassignable, so it should run BEFORE the label collapse (collapse then optimises\nlabels over the improved skeleton). Also interacts with the public-access pin\n(94g) — better circulation placement can supply invariant inside public access,\nremoving the need to pin a room provider.\n\nMeasure on the 6 evolved layouts from the 94g sweep (not-connected + access +\ninaccessible fail counts). Related: 94g, homemaker-py-2g5.","notes":"LANDED (2026-07-18) mechanism (c) — graded circulation-connectivity signal (DESIGN §18). graph.circulation_connectivity(G) = largest-circ-component fraction [0,1]; summed over storeys it rides the score_with_grade proximity channel, gated by conf flag conn_grade (replaces the §11.4 leaf-grade on that channel). Secondary comparator key (-n_fails, grade, fitness) only — scalar fitness and fail count byte-identical (verified). Wired conn_grade through driver _overrides_for/_fitness_for/_evaluate/search (enabling it implies the grade key); evolve --conn-grade (HOMEMAKER_CONN_GRADE, default OFF). 9 new tests (tests/test_conn_grade.py), 276 pass; 60-eval CLI smoke confirms plumbing.\n\nA/B VERDICT (2026-07-22, qpk protocol, experiments/run_qi6_ab.sh) — NEGATIVE. conn_grade ON vs OFF, full-budget, both finished with --collapse: harbor-house (budget 2500, seeds 1-3) byte-identical output in every seed — the grade never fired. programme-house (budget 3000, seeds 1-5) 3/5 seeds tie exactly; seeds 1/2 diverge to a different topology with one fewer total fail, but the diff is adjacency/crinkliness/width/access/size, not connectivity. Zero cases (of 4) where a not-connected fail was present and cleared by the grade. Mechanism (b)/(c) (graded proximity as tertiary comparator key) is falsified, not just unconfirmed. Kept default OFF (already was). DESIGN.md §18 updated with full verdict.\n\nRemaining candidate: mechanism (a), an explicit insert/relocate-circulation mutation/repair operator that doesn't depend on the search stumbling onto a fail-count tie. Not started — filing as follow-on if this gets picked up; otherwise low priority (fitness fidelity, not search capability, per 94g framing).","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-18T10:12:27Z","created_by":"Bruno Postle","updated_at":"2026-07-23T17:22:14Z","started_at":"2026-07-18T12:17:08Z","closed_at":"2026-07-23T17:22:14Z","close_reason":"A/B measured negative (see notes + DESIGN.md §18); mechanism (a) follow-on filed as homemaker-py-8sh","dependencies":[{"issue_id":"homemaker-py-qi6","depends_on_id":"homemaker-py-94g","type":"discovered-from","created_at":"2026-07-18T11:12:27Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-7fm","title":"Geometry/topology search for shape-intrinsic fails (long-thin useless cells)","description":"The finish-time collapse (94g) proved that a large share of the harbor-house best\nlayout's residual fails are GEOMETRY-INTRINSIC, not label slack: long-thin cells\nthat are useless whatever room usage is assigned. Their width / proportion /\ncrinkliness fails cannot be cleared by any relabelling (94g searches labels only,\nnever geometry) — confirmed by collapse_global clearing only ~2-3 of 15 fails on\nthe best layout, the rest shape- or building-level bound.\n\nGOAL: a search/repair operator that reshapes such cells so the space becomes\nusable — e.g. re-solving a subtree's division ratios, merging a sliver into a\nneighbour, or a targeted division-ratio mutation biased by the offending factor\n(narrowest-width, aspect, crinkliness). Unlike the collapse this MUST move\ngeometry (division ratios / tree shape), and must be evaluated for net fail-count\neffect (a reshape that fixes width may add size elsewhere — same shuffle risk the\n94g threshold objective addressed for labels).\n\nSCOPE: width/proportion/crinkliness fails on inside room cells whose geometry no\nroom type can satisfy. Explicitly NOT relabelling (that is 94g, done). Candidate\nmechanisms: (a) inner-loop solve already optimises ratios — check why it leaves\ndegenerate cells (local optimum? target-dim conflict?); (b) a finish-time\n\"deslim\" operator + re-solve; (c) an operators.py mutation weighted toward high-\naspect leaves. Measure on the same 6 evolved layouts used for the 94g sweep.\n\nSee bd memory collapse-global-94g-and-any-label-usage-optimisation for the\nlabel-vs-geometry boundary. Related: 94g (label collapse), homemaker-py-2g5\n(occlusion/daylight rebuild, which feeds crinkliness).","design":"DONE (negative) — see DESIGN.md §19 for full writeup.\n\nDiagnosis ruled out mechanism (a): re-running the ratio inner loop with 1500\nevals (vs ~80-200 in real search) on the 12-fail collapsed best layout made\nzero difference. Traced two structural causes instead: (1) area starvation\nseveral levels up the tree, (2) cut orientation parallel to the parent's long\naxis. Neither is a ratio problem.\n\nImplemented mutate_shape_rotate + mutate_deslim (operators.py) targeting each\ncause, gated on a new `fit` argument (mirrors the existing `reqs` gate\npattern). Tested as a finish-time exhaustive hill-climb (all 3 rotations +\ndeslim+reinsert per failing cut, keep-better) on the same 6 harbor-house\nlayouts §17 (94g) swept: 0 improving moves found, anywhere, under any\nvariant. Root cause: on a co-evolved layout the cut that makes a leaf thin is\nalso providing some other leaf's adjacency/public-access — straightening it\nelsewhere isn't free. This is §4.2's lesson (partial-objective repair of a\nco-evolved optimum can't win) confirmed for topology repair, not just ratio\nsolving.\n\nCode landed: operators.py (mutate_shape_rotate, mutate_deslim, _shape_failing,\nMUTATIONS/mutate() wiring), tests/test_operators.py (4 new tests + automatic\ncoverage via test_mutations_yield_canonical_genomes). 282 tests pass. Both\noperators are currently unreachable from driver.search/evolve.py (no `fit`\nthreaded through) since the finish-time evaluation found nothing worth\nwiring up further.\n\nOpen question spun out separately: whether these operators help as in-search\nGA moves, where selection pressure across generations might accept a\nlocally-worse move a later step completes — a different regime from\nsingle-step greedy hill-climbing. See follow-up issue.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-18T10:11:43Z","created_by":"Bruno Postle","updated_at":"2026-07-19T10:06:01Z","started_at":"2026-07-19T07:00:32Z","closed_at":"2026-07-19T10:06:01Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-7fm","depends_on_id":"homemaker-py-94g","type":"discovered-from","created_at":"2026-07-18T11:11:42Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-94g","title":"Global cell↔room collapse: generalise 9o5 matching to all spaces (WFC-style)","description":"Generalise the 9o5 superposition/collapse from interchange-classes to a GLOBAL cell↔room assignment: evolution searches unlabelled floorplans (tree shape + circulation/room/outside), a collapse function optimally LABELS each candidate. Mechanically an assignment problem — N cells (computed area/width/proportion/level/graph-pos) ↔ M required rooms (target dims + level + adjacency) — minimising total fail-cost; reuse Fitness._best_assignment (Hungarian/brute) but over the whole leaf set instead of one class. WFC framing = the constructive algorithm: each cell a distribution over types, PROPAGATE hard constraints exactly (level, requires_below service stacks, must-have adjacency) to prune, OBSERVE lowest-entropy cell, collapse to best-fitting room weighted by fit-quality, backtrack on contradiction.\n\nMOTIVATION (harbor-house evolved-3M-nols-3.dom, best layout, 15 fails): ~11 of 15 are LABEL-RELATIVE — 4 size (0/lrrll,0/rllll,1/lrlr,1/lrrll), 3 width (0/rlrrlr,0/rrllrr,0/rrrr), 2 proportion (0/rlrlr,1/rlrlr), 2 wrong-level (me1 L1-\u003e0, r L0-\u003e1). A cell fails size/width/proportion because the room ASSIGNED to it wants dims the cell lacks; relabel to a room it fits and the fail vanishes. Wrong-level is a HARD constraint a level-respecting collapse honours for free. Only 4 are shape-intrinsic and out of scope: crinkliness x2 (0/llll,0/rlllr) + not-connected x2 (level 0/1). Realistic target: 15 -\u003e ~4-6.\n\nWHY IT MAY SUCCEED WHERE 9o5 WAS FALSIFIED (xi7): 9o5 went negative because (1) auto-derived classes were semantically wrong (harbor-house 8-code chain, see homemaker-py-b3v) and (2) collapse perturbed feasibility (ON ADDS fails, 38v33/48v43). A GLOBAL, hard-constraint-respecting collapse sidesteps both: no fragile similarity classes; never violates level/adjacency/stack so it cannot ADD feasibility fails — only improve or match.\n\nRISK: search-landscape flattening. fitness = max-over-labellings makes the objective flatter/noisier (many topologies collapse to similar best scores), removing the gradient evolution climbs — the likely cause of 9o5's negative verdict, AMPLIFIED at full scope. Mitigations to A/B: (a) collapse only at FINISH (search on committed types, one relabel pass at end — cheap, strictly cannot worsen final score); (b) warm-start collapse from evolved labels as a local polish.\n\nRECOMMENDED FIRST STEP (cheapest, ~1 day, strictly cannot worsen final layout): FINISH-TIME global collapse — after a normal run, one optimal cell-\u003eroom assignment over the full leaf set with hard constraints enforced, then re-score. Measures empirically how many of the 11 label-relative fails are real slack vs already-optimal. If it clears a meaningful chunk, justifies the in-search WFC collapse + the landscape-flattening A/B. Does NOT fix crinkliness/connectivity (need geometry + circulation-placement work, file separately).\n\nFiles: fitness.py (_best_assignment, collapse_superposition), programme.py (constraints), driver.py (finish hook), graph.py (adjacency for propagation).","notes":"WIRED + PUBLIC-ACCESS TERM DONE.\n1. Public-access pin (preserve_public_access=True, default): when the building's\n ONLY street access is an l/k ROOM neighbour of a public outside leaf (no\n circulation fallback — the existential building check the per-leaf objective\n can't see), that room leaf is PINNED (kept + its demand slot decremented) so\n the collapse can't drop \"no outside public access\". On the best layout this\n turns 15-\u003e13 into 15-\u003e12 (the lone regression removed, zero new fails). Sweep\n total 172-\u003e171, still monotone across all 6.\n2. Keep-better wrapper Fitness.collapse_finish(root, **kw) -\u003e (tree, base, coll,\n applied): scores on throwaway copies (score_with_fails merges in place),\n returns collapsed only if fails don't increase. Safety belt (config already\n monotone here, not proven so in general).\n3. Wiring: driver.collapse_best(result, programme_dir, ...) updates result.best\n (lineage +collapse, canonical re-score). evolve.py runs it after the sharing\n polish behind --collapse/--no-collapse (default ON). Standalone CLI\n homemaker-collapse file.dom (pyproject entry) writes \u003cstem\u003e.collapsed.dom;\n flags --adjacency/--public-access/--objective/--keep-better. Verified the\n written dom independently re-scores 15-\u003e12.\n4. Tests: tests/test_collapse_global.py (6) — demand-set relabel, level hard\n constraint, c/o/s exclusion, no-op safety, keep-better/unmerged. 267 pass.\n\nSTILL OPEN (separate work, not label slick): geometry-intrinsic fails — long-thin\nuseless cells (width/proportion/crinkliness) and not-connected — need geometry/\ntopology + circulation-placement search, NOT the collapse (see bd memory\ncollapse-global-94g...). In-search WFC collapse A/B also still open (xi7 risk).","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-17T16:17:12Z","created_by":"Bruno Postle","updated_at":"2026-07-18T10:13:43Z","started_at":"2026-07-17T21:07:05Z","closed_at":"2026-07-18T10:13:43Z","close_reason":"FINISH-TIME global cell→room collapse DELIVERED (commits d52cce6, da18ef7, 880a214):\nFitness.collapse_global (c/o/s partition, level hard constraint, adjacency\nrelaxation, threshold objective, public-access pin) + collapse_finish keep-better\nwrapper + driver.collapse_best + evolve --collapse hook + homemaker-collapse CLI +\n6 tests (267 pass). Best harbor-house layout 15→12 fails; monotone across 6 evolved\nlayouts (sweep 195→171). Label search only — cannot fix geometry/building-intrinsic\nfails.\n\nRemaining scope spun out: homemaker-py-7fm (shape-intrinsic reshape), homemaker-py-qi6\n(circulation placement / not-connected), homemaker-py-qpk (in-search WFC collapse\nA/B, xi7 landscape-flattening risk). Closing 94g as the finish-time thrust is done.","dependencies":[{"issue_id":"homemaker-py-94g","depends_on_id":"homemaker-py-b3v","type":"related","created_at":"2026-07-17T17:23:20Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-94g","depends_on_id":"homemaker-py-xi7","type":"related","created_at":"2026-07-17T17:23:06Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-3l6","title":"Leaf-sharing default makes internal fitness diverge from canonical homemaker-fitness score","description":"Default is --leaf-sharing (on). Leaf sharing is a fitness-evaluation knob (fitness.py:415 quality_size, plus edge cap and count check): a shared leaf of code X is credited as satisfying k programme entries, with its size Gaussian re-centred on k*target. The evolve internal objective therefore rewards genomes that under-materialise the programme. When the winning .dom is re-scored by the canonical homemaker-fitness (leaf_sharing off), those un-materialised copies become 'missing required space ... (critical)' fails.\n\nObserved on examples/harbor-house (init.dom, budget 3M, workers 2):\n - leaf sharing ON (default): internal best 1.03e-05, but canonical score 6.73e-29 with 90 fails (15 critical missing-room).\n - --no-leaf-sharing (warm-started to full budget): internal and canonical agree at 4.19e-06, 15 fails, 0 critical -- ~9500x better than the prior best 3m.dom (4.41e-10).\n\nSo the default silently optimises an objective the canonical scorer does not credit, and writes a catastrophically worse .dom than its reported internal fitness implies.\n\nOptions to consider:\n 1. Make --no-leaf-sharing the default (strict per-leaf baseline agrees with canonical scorer).\n 2. Before writing output, re-score best-so-far with leaf_sharing off and warn (or refuse) if it regresses vs internal fitness.\n 3. Materialise/unfold shared leaves into k distinct rooms when writing the .dom, so the output satisfies the per-room programme.\n 4. Keep sharing as an early-phase relaxation only and anneal leaf_share_factor down to 0 before finishing (see related annealing investigation).","notes":"FIXED (option 3+2 combined, auto-finish): leaf-sharing runs now unfold+polish+rescore before write so output is honest under the canonical scorer.\n\nImplementation:\n- driver.polish_finish(result, programme_dir, polish_budget, ...): deep-copies best, operators.unfold_shared_leaves() to materialise the count deficit, then warm-starts a leaf_sharing=False search (bootstrap=False) from the unfolded genome. polish_budget\u003c=0 -\u003e single rescore eval only (used on interrupt). Stitches evals/topologies/sigs/restarts/history onto the sharing run; history tagged share:/polish: since the two objectives are not comparable. Returned best.fitness is canonical (leaf_sharing off =\u003e internal==canonical).\n- evolve.py: new --polish-budget flag (env HOMEMAKER_POLISH_BUDGET, default -1=auto=budget//2, 0=unfold+rescore only). main() calls polish_finish when --leaf-sharing on; interrupt forces polish_budget=0 for a fast honest output.\n\nVerified end-to-end (harbor-house, budget 3000 + polish 1500): reported polish fitness 4.79788e-27 EXACTLY matches canonical homemaker-fitness, 0 critical fails (was: internal 1.03e-05 vs canonical 6.73e-29 w/ 15 critical). Tiny budget so absolute quality low but honesty restored. Tests: driver.polish_finish x3 (test_driver.py), full suite 254 pass.\n\nDefault kept --leaf-sharing on per decision (sharing's topology-search speed retained; output made honest by the finish). Schedule B in-run annealing remains kpu.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-05T16:24:34Z","created_by":"Bruno Postle","updated_at":"2026-07-15T08:00:26Z","started_at":"2026-07-15T06:56:29Z","closed_at":"2026-07-15T08:00:26Z","close_reason":"Closed","dependency_count":0,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-rq2","title":"Flip share_edge_cap default-ON + rebaseline §13.x floor (hph follow-up)","description":"hph/§13.8 A/B confirmed the share-aware edge-too-long cap is positive and monotone-harmless (maple 80.3→74.0, harbor 34.7→31.0, zero regressions across 6 seeds). The fix shipped behind the SHAREEDGE/share_edge_cap knob (default OFF) so controls reproduce. This issue flips the default ON for leaf-sharing runs — it completes the §13.3 leaf-share objective relaxation on the wall measure, mirroring the pll/interior_outside default flips. Rebaselines the §13.x full-stack floor numbers (harbor 34.7→31.0, maple 80.3→74.0 become the new baseline). Couple with INTERIORO/odiv3 if those are also being default-flipped. Verify the test suite + a control re-score still reproduce post-flip.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-28T20:01:11Z","created_by":"Bruno Postle","updated_at":"2026-06-28T20:39:00Z","started_at":"2026-06-28T20:32:40Z","closed_at":"2026-06-28T20:39:00Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-hph","title":"edge-too-long not share-aware: shared leaves (share\u003e1) penalised for aggregate wall length (§13.7 follow-up)","description":"DESIGN §13.7 flagged edge-too-long as harbor's top fail class (6). Dissection (experiments/diag_edge_too_long.py on the 500k probe best) shows the 6 fails are only 2 distinct locations:\n\n(1) DOMINANT ~4/6: leaf 'lllr' on both levels is a share=3 leaf (one quad = 3 rooms, 247 m2, edges 15-17 m, aspect 1.2 NEARLY SQUARE). Its walls exceed the flat 8 m cap purely because it aggregates 3 rooms — a leaf-sharing REPRESENTATION ARTIFACT, not a design flaw. §13.3 relaxed size/missing for shared leaves (quality_size centres on k*target) but edge_cost (fitness.py:474) and outside_edge_cost (fitness.py:490) still use a flat 8.0 m regardless of leaf.share. So a shared leaf is penalised for being big — the same leak §13.3 closed, on a different measure.\n\n(2) ~2/6: leaf 'llll' is a 1.2 m x 16.7 m sliver (aspect 14) at correct area — a REAL narrow-room pathology, already caught by width/proportion. Its edge-too-long is the wall it shares with lllr.\n\nNo corridors involved.\n\nPROPOSED FIX: make edge-too-long share-aware — exempt or scale the 8 m cap by leaf.share (type-guarded, as graph.leaf_share does) in edge_cost/outside_edge_cost, mirroring quality_size's k*target. Clears the ~4 artifact fails without masking the narrow sliver. Optional separate lever: lift/parametrise the flat 8 m cap for non-domestic programmes (harbor-house) — blunter, lower priority. A/B under §13 protocol (controls reproduce harbor 34.0 / maple 80.3); record verdict. Repro: experiments/diag_edge_too_long.py.","status":"closed","priority":2,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-28T13:51:26Z","created_by":"Bruno Postle","updated_at":"2026-06-28T20:03:00Z","started_at":"2026-06-28T14:46:18Z","closed_at":"2026-06-28T20:03:00Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-71d.1","title":"Diagnostic: high-budget harbor floor on full default stack — does landlocked crinkliness still dominate after interior-O?","description":"71d go/no-go probe. 71d targets landlocked crinkliness (area_outside=0, ratio-invariant) which its named fix (interior O courtyards) addresses. interior_outside now ships default-ON (erc.8), so re-measure: run harbor full default stack at high budget (1M evals, n_workers=4, seed 0) and break down the at-convergence residual — fail-type histogram + landlocked-vs-under-exposed split of crinkliness fails. If landlocked still dominates -\u003e 71d worth it; if interior-O dissolved it -\u003e 71d redundant. Verdict to DESIGN.md.","notes":"VERDICT (DESIGN §13.7): NO-GO on 71d. 500k serial full-stack harbor probe (seed 0) -\u003e 20 fails. Crinkliness collapsed 13-\u003e4, landlocked crinkliness ~13-\u003e2 of 20. Interior-O (now default) IS 71d's named fix (interior O courtyards) and already dissolved the target block. Residual now diffuse (top class edge-too-long 6), no concentrated ratio-invariant block for a targeted operator. Recommend close 71d + 7u5/jrb/u8x as superseded-by-construction.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-28T06:57:44Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:19:08Z","started_at":"2026-06-28T06:58:10Z","closed_at":"2026-06-28T13:19:08Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-71d.1","depends_on_id":"homemaker-py-71d","type":"parent-child","created_at":"2026-06-28T07:57:44Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.8","title":"Flip interior_outside (odiv=3) default to ON","description":"§13.6/ld2 confirmed interior-O light-well seeding positive on dense floors (harbor -16.4%, all seeds improve) and net-neutral on maple (-2.8%, mean improves, no programme regresses on mean). Mirror the pll flip after erc.7: change interior_outside default False-\u003eTrue in driver.search/search_staged and operators.constructive_topology/lift_base_to_storeys (outside_divisor stays 3). No test asserts fail counts so low-risk. Verify control runs still re-score OK.","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-28T06:18:14Z","created_by":"Bruno Postle","updated_at":"2026-06-28T06:29:48Z","started_at":"2026-06-28T06:26:42Z","closed_at":"2026-06-28T06:29:48Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-erc.8","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-28T07:18:13Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-pll","title":"Flip depth_balanced + leaf_sharing (factor 3) defaults to ON","description":"erc.7/§13.5 verdict: depth_balanced + leaf_sharing (factor 3) is the winning Phase-8 stack (harbor -21%, maple -4.6% vs share-alone; factor 3 confirmed optimal). Both default OFF today. Make the bal+share stack the default in driver.search/search_staged (leaf_sharing=True, leaf_share_factor=3, depth_balanced=True) and update the affected tests (the §13.4 note records 214 tests pass with depth_balanced OFF — expect ordering/snapshot churn). Keep env-var overrides (DEPTHBAL/LEAFSHARE/LEAFSHAREFAC) for A/B. leaf_share_max stays 4 (covers factor\u003c=4, no missing-fail leak).","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-27T16:12:53Z","created_by":"Bruno Postle","updated_at":"2026-06-27T20:15:26Z","started_at":"2026-06-27T16:14:52Z","closed_at":"2026-06-27T20:15:26Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-pll","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-27T17:13:34Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-9o5","title":"Multi-use leaves: one leaf satisfies several COMPATIBLE different codes (type superposition)","description":"A leaf that legitimately and simultaneously satisfies several DIFFERENT compatible programme requirements (e.g. study + guest bedroom, or kitchen + dining). Distinct from erc.3 leaf-sharing, which aggregates k instances of the SAME code; this is a strict generalisation across DIFFERENT codes. Idea from Bruno (this corresponds to Stewart Brand's 'How Buildings Learn' — loose-fit / long-life rooms whose use churns over a building's lifetime).\n\nWHY IT MATTERS\n1. Architectural deliverable: adaptable multi-use rooms (Brand loose-fit), not just an optimisation trick.\n2. Generalises the erc.3 floor-lowering lever to the SINGLETON (count:1) long tail that same-type sharing cannot reach: one leaf covering one X AND one Y removes a room-leaf, paying the ~1.8/leaf crinkliness tax (§13.1) once instead of twice. Crinkliness is scale-invariant, so a larger multi-use leaf is not penalised for size.\n\nTWO READINGS\n(a) Superposition as a SEARCH RELAXATION: carry a distribution/set of candidate types per leaf, evaluate a relaxed (expected/best-case) fitness for a smoother landscape, then COLLAPSE (argmax) at the end. Risks: relaxation gap (relaxed optimum need not sit near a good integer solution); collapse is itself a constrained rounding/assignment problem (cannot collapse 5 superposed leaves all to 'kitchen' when 1 is required); and search-machinery bets are 0/3 historically (§11-12) vs construction 4/4 — the floor is geometric, so pure search-easing may fight the wrong battle. LOWER PRIORITY framing.\n(b) Multi-use as the DESIGN GOAL (preferred): the leaf permanently serves a SET of compatible codes; no collapse needed, multi-use survives into the output. Mirrors erc.3's mechanism exactly but with a SET of codes instead of an integer count: stamp leaf with the codes it serves (type-guarded as in erc.3 leaf.share/share_type); fitness count credits each code in the set, size scored against the union/least-upper-bound of targets, width/proportion as today (scale-invariant), adjacency satisfied if the SET satisfies it.\n\nIMPLEMENTATION SKETCH (path b)\n- dom.Node: a set/list of served codes (generalises leaf.share/share_type from erc.3). Survives search via deepcopy; emit in .dom only when non-trivial (as with 'share').\n- graph.check_space_counts: a multi-use leaf credits coverage to EACH code in its set (type-guarded: honoured only while its served set is consistent with its assignment).\n- fitness size/width/proportion: score the multi-use leaf against the combined target (union/LUB) of its served codes; crinkliness/access unchanged.\n- construction: a new constructive option that fuses COMPATIBLE singleton rooms into shared multi-use leaves (analogous to operators._share_rooms but across codes), honouring adjacency/level.\n- default OFF; controls reproduce §12.2 baseline.\n\nKEY OPEN QUESTIONS (Bruno to spec)\n- Who declares type-COMPATIBILITY? A new architectural input, analogous to adjacency (e.g. a 'compatible:' / 'multiuse:' list per space in patterns.config). kitchen+bathroom is nonsensical; study+guestroom is fine.\n- Does the final design COLLAPSE to single uses or stay loose-fit (keep superposition as a deliverable)? Brand argues for keeping it.\n- How exactly to combine size/width/proportion targets for a leaf serving 2+ codes (max? union? a 'dominant use' target?).\n- Interaction with erc.3 same-type sharing and x3b per-code control — composable? (a leaf could be 'k of X' AND 'one Y').\n\nRELATES TO: erc.3 (same-type leaf-sharing, the special case), x3b (per-code shareable flag), erc.7 (factor/synergy sweep), erc epic (lower the geometry floor). Concept only — implement in a future session.","design":"# 9o5 DESIGN SPEC — Multi-use leaves as SUPERPOSITION + COLLAPSE (path a)\n\nDecisions locked with Bruno (2026-06-29):\n- READING: path (a) — superposition as a SEARCH RELAXATION that CONDENSES to a SPECIFIC\n usage at the end. NOT path (b) loose-fit. (This REVERSES the issue body's stated\n \"path b preferred / path a lower priority\" note — superseded by Bruno.)\n- COMPATIBILITY SOURCE: DERIVE AUTOMATICALLY. Interchangeability = SIMILARITY of leaf\n requirements; codes with similar size/width/proportion (and compatible level/adjacency)\n form an INTERCHANGEABLE EQUIVALENCE CLASS. No hand-authored list on the happy path.\n- RELAXED-PHASE SCORING: BEST-CASE per leaf (not the path-b MAX/LUB). During the relaxed\n phase each superposed leaf scores against whichever in-class usage it best fits; the\n COLLAPSE resolves the actual leaf-\u003eusage assignment. (Bruno's earlier MAX/LUB answer was\n given under the wrong path-b framing and is superseded for the relaxed phase.)\n\nDEFAULT OFF. With the feature disabled, every run reproduces the §12.2 / current baseline\nbit-for-bit (no equivalence classes, no relaxation, no collapse; fitness path untouched).\n\nPRIOR / HONESTY: the issue records search-machinery bets at 0/3 (§11-12) vs construction\n4/4, and lists path-(a) risks explicitly: (i) RELAXATION GAP — the relaxed optimum need\nnot sit near a good integer/condensed solution; (ii) COLLAPSE is a constrained assignment,\nnot free rounding — cannot condense 5 superposed leaves all to 'kitchen' when 1 is\nrequired. Treat a NULL result as the likely outcome and report honestly.\n\n------------------------------------------------------------------------------------\n## 1. Core mechanism\n\nEQUIVALENCE CLASS E = a maximal group of codes whose leaf requirements are SIMILAR (sec 2).\nDuring evolution, a leaf typed to any code in E is a SUPERPOSED leaf: it is not committed\nto a specific in-class usage. The set of required usages in E (the multiset of codes with\ntheir counts) is a fixed DEMAND; the leaves currently typed into E are the SUPPLY. The\nleaf\u003c-\u003eusage assignment within E is left FREE during search and resolved by COLLAPSE.\n\nCOLLAPSE / CONDENSE: enumerate the assignments of E's required usages to E's superposed\nleaves and pick the best-scoring condensed layout. Bruno's example: 3 interchangeable\nusages over 3 leaves =\u003e 3! = 6 permutations to check. Small classes =\u003e brute force; if a\nclass ever grows large, fall back to the assignment problem (Hungarian on the\nleaf x usage cost matrix). Output is SPECIFIC (each leaf gets exactly one usage), not\nloose-fit. Collapse runs at OUTPUT; see sec 3 for whether it also runs during search.\n\nWHY it can help: the solver stops being penalised for putting \"study\" where \"guest\" is\nexpected (or vice versa) when the two are interchangeable, smoothing the landscape so\ntopology/adjacency search isn't fighting an arbitrary usage label. The geometric win is\nincidental — this is a SEARCH-EASING bet (note the 0/3 prior above).\n\n------------------------------------------------------------------------------------\n## 2. Equivalence-class derivation (auto, by requirement similarity)\n\nPure function of programme.SpaceReq, computed once at programme load. Conservative:\nfalse-negatives are cheap (miss an interchange), false-positives corrupt the relaxation.\n\nTwo codes a, b are INTERCHANGEABLE iff ALL hold:\n S1. Both SIZED (has_size). Unsized circulation/outside never participate.\n S2. Requirement SIMILARITY: size, width, proportion targets pairwise within bounded\n ratios (max/min), so the leaves are genuinely substitutable, not merely fusible.\n LOCKED DEFAULTS (Bruno 2026-06-29), all tunable via constants:\n R_size = 1.5 (larger area target \u003c= 1.5x smaller; e.g. study 9 / guest 12 = 1.33 OK)\n R_width = 1.3 (clear-width targets vary less than areas; tighter band)\n R_prop = 1.5 (max length/width aspect targets within 1.5x)\n A pair is similar iff ALL THREE ratios hold. Conservative on purpose: a missed\n grouping is cheap, a wrong one corrupts the relaxation.\n S3. Compatible level / requires_below: equal or one None; no requires_below conflict.\n Service codes (requires_below / wet-stack — bathroom, kitchen, wc) only group with\n other service codes of matching stack — this keeps kitchen out of a study class.\n S4. NO direct adjacency edge between a and b (if the programme requires them adjacent\n they are distinct coexisting rooms, not interchangeable for one leaf).\n\nClass = connected component / clique under this relation. v1 may cap class size; the\ncollapse cost is |class|! for brute force, so cap (e.g. \u003c=4 =\u003e \u003c=24 perms) or switch to\nHungarian beyond the cap.\n\nESCAPE HATCH — DEFERRED (Bruno 2026-06-29): the `interchange: false` per-space opt-out\n(or top-level exclude list) is NOT in v1. The similarity (S2) + service-stack (S3) +\nadjacency (S4) guards stand alone for v1; add the veto hatch ONLY IF auto-derivation is\nobserved to misgroup on the real harbor-house / programme-house configs. File a follow-up\nissue at that point rather than pre-building it.\n\n------------------------------------------------------------------------------------\n## 3. Fitness during the relaxed phase (graph.py)\n\nThe CONSTRAINT that makes relaxation admissible (issue's \"can't collapse 5 leaves to\nkitchen\"): per class E, the superposed leaves must be ASSIGNABLE to E's demand. So:\n- COVERAGE (check_space_counts): score E as a CLASS, not per-code. The class is satisfied\n to the extent the supply leaves can cover the demand multiset (a feasibility/matching\n count, not naive per-code leaves_of). A class with 2 leaves and demand {study:1,guest:1}\n is fully covered; 3 leaves and demand {x:1,y:1} over-supplies (slack), 1 leaf under.\n- QUALITY (size/width/proportion): BEST-CASE per leaf — each superposed leaf scores against\n the in-class usage it best matches under the current relaxed (unassigned) state. Because\n S2 keeps in-class targets similar, best-case vs assigned-case differ little, bounding the\n relaxation gap. crinkliness / access / edge-too-long UNCHANGED (scale-invariant).\n\nCOLLAPSE CADENCE — LOCKED (Bruno 2026-06-29): collapse PER FITNESS EVAL, with a\nCLASS-SIZE CAP. Each candidate's fitness inner-loops the in-class assignment and returns\nthe best CONDENSED score, so search optimises the condensed objective directly and the\nrelaxation gap is removed (search and output agree by construction). The per-eval cost\nfactor is bounded because class size is capped AT DERIVATION (sec 2): default cap C=4\n(=\u003e \u003c=4! = 24 assignments/eval, worst case). Output-only collapse is NOT used.\n\n Cost note: the collapse objective is SEPARABLE — each leaf's score given its assigned\n usage (size/width/proportion/crinkliness) is independent of the other leaves'\n assignments; only feasibility (perfect matching of demand multiset to supply leaves)\n couples them. So the collapse is a LINEAR-SUM ASSIGNMENT and is EXACTLY solvable by\n Hungarian in O(n^3) with NO cap needed for optimality. v1 uses brute-force over \u003c=C!\n permutations for simplicity (C is tiny); the cap exists only to bound that brute force.\n If a real programme yields a class larger than C, derivation SPLITS it (or v1 switches\n that class to Hungarian) rather than dropping superposition — note for the build.\n\n------------------------------------------------------------------------------------\n## 4. Data model (dom.Node)\n\n- `class_id: int|None` or `interchange: tuple[str,...]` on a superposed leaf — the\n equivalence class it currently participates in (the set of in-class codes). Type-guarded\n like erc.3 share: honoured only while `type` is in that set (retype out of the class\n invalidates it). Survives deepcopy; emit in .dom only post-collapse as the FINAL specific\n type (superposition is a search-time state, not an output artifact — unlike path b).\n- The COLLAPSED output is an ordinary single-typed leaf, so the .dom format needs NO new\n persisted field for the deliverable (contrast path b, which needed a persisted `serves`).\n A debug/inspection emit of pre-collapse class membership is optional.\n\n------------------------------------------------------------------------------------\n## 5. Construction / operators\n\nUnlike path b, NO new \"fuse rooms\" constructor is required — superposition is about NOT\ncommitting usage, not about merging leaves. Construction stays as today; the relaxation\nlives in (i) equivalence-class derivation at load and (ii) the relaxed fitness + collapse.\nOperators may gain a cheap \"retype within class\" move (swap a leaf's usage to an in-class\nsibling) that is a no-op under relaxed scoring but keeps the genome honest at collapse.\n\n------------------------------------------------------------------------------------\n## 6. Controls / wiring\n\n- programme: derive classes once at load; hang off the parsed programme.\n- driver.search / search_staged: an `interchange`/`superpose` flag, DEFAULT OFF. Mirror\n the leaf_share_factor selector plumbing (x3b/§13.10).\n- fitness: collapse cadence knob (sec 3) behind the same flag.\n- CLI (evolve.py): expose the flag.\n- Golden test: flag OFF reproduces current .score on all fixtures.\n\n------------------------------------------------------------------------------------\n## 7. Validation plan (future BUILD session)\n\n1. Unit: class derivation on harbor-house + programme-house — assert service/non-service\n not grouped; assert a genuinely-similar pair IS grouped; assert the collapse of a\n hand-built 3-usage/3-leaf class enumerates 6 and picks the known best.\n2. Golden: flag OFF reproduces current .score on all fixtures.\n3. A/B at equal budget on a programme with an interchangeable class: superpose ON vs OFF.\n EXPECTED mechanism = smoother landscape lets topology search reach better layouts that\n the rigid usage labels blocked. PRIOR: search-easing bets 0/3 (§11-12) — expect null,\n report honestly. Measure both the relaxed score AND the COLLAPSED (final, specific)\n score; only the collapsed score counts.\n4. Watch the RELAXATION GAP explicitly: log relaxed-best vs collapsed-best divergence; a\n large gap is the diagnostic that path (a) is fighting the wrong battle (§11-12 framing).\n\nOPEN Qs — ALL RESOLVED (Bruno 2026-06-29). Spec is BUILD-READY.\n- [RESOLVED] Collapse cadence: PER-EVAL, class-size cap C=4 CONFIRMED. Sec 3.\n- [RESOLVED] Similarity thresholds: R_size=1.5, R_width=1.3, R_prop=1.5 (S2).\n- [RESOLVED] `interchange:false` veto hatch: DEFERRED — later fix only if auto-derivation\n misgroups on the real configs (file a follow-up then). Not in v1.","notes":"BUILD COMPLETE (path a, per-eval collapse). Implemented:\n- programme.derive_interchange_classes + interchangeable(): S1-S4 relation,\n LOCKED thresholds R_SIZE=1.5/R_WIDTH=1.3/R_PROP=1.5, CLASS_CAP=4. Classes =\n connected components (size\u003e=2). Pure fn of SpaceReq.\n- fitness.Fitness.collapse_superposition(): per-eval COLLAPSE. Re-types each\n superposed leaf to its best in-class usage via an optimal supply-\u003edemand\n assignment (brute force \u003c=C! within cap, scipy Hungarian beyond). Objective =\n usage quality (size*width*proportion) weighted by leaf area (value-faithful,\n rate constant within class). Runs on the UNMERGED tree before all checks, so\n counts/adjacency/quality see the condensed types -\u003e NO changes needed to\n graph.py / dom.py / operators.py. Default OFF is bit-identical (250 tests pass,\n was 233).\n- Plumbing: driver.search/search_staged/_evaluate/_fitness_for take superpose\n bool (folded into conf overrides via _overrides_for); evolve.py exposes\n --superpose/--no-superpose (env HOMEMAKER_SUPERPOSE), default off.\n- tests/test_superposition.py (17): derivation (incl. service/adjacency/level\n guards + real programme-house), assignment (brute + Hungarian + rect), and\n end-to-end collapse re-typing.\n\nKEY DESIGN REALISATION: because collapse re-types at eval time, the Node never\nneeds a persisted class_id/serves field and operators need no \"retype within\nclass\" move -- the genome carries any in-class type and collapse fixes it. This\nrealises the §3 \"search and output agree by construction\" with minimal surface.\n\nOBSERVED: harbor-house derives a chained 8-code component {da1,ef1,k1,la1,m,me1,\nn,ws1} (spans 10..60 m2). Sanctioned (spec: connected-component + Hungarian\nbeyond cap); collapse self-sorts by best-fit so a 60 m2 leaf is not mis-assigned\nto a 10 m2 slot. This is the documented false-positive risk; the deferred\ninterchange:false veto hatch is the fix IF the A/B shows misgrouping hurts.\n\nREMAINING (validation RUN, not build): §7.3 A/B at equal budget superpose ON vs\nOFF on programme-house; §7.4 log relaxed-vs-collapsed gap. See follow-up issue.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-24T21:11:03Z","created_by":"Bruno Postle","updated_at":"2026-06-30T06:08:11Z","started_at":"2026-06-29T22:22:35Z","closed_at":"2026-06-30T06:08:11Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-9o5","depends_on_id":"homemaker-py-erc.3","type":"related","created_at":"2026-06-24T22:11:48Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-x3b","title":"Per-code shareable flag (SpaceReq.share) + homemaker-evolve CLI wiring","description":"Make leaf-sharing (erc.3, §13.3) safe to default-on by giving the programme author per-code control, and expose it on the real CLI (not just the experiment env var).\n\nDesign (agreed with Bruno, open to refinement — he has follow-up questions):\n- patterns.config per-space optional key 'share: N' -\u003e SpaceReq.share (int, default 1 = not shareable). N\u003e=2 means up to N rooms of this code per shared leaf.\n- Master enable stays the 'leaf_sharing' conf/CLI flag (default OFF -\u003e baseline, controls reproduce).\n- Global grain selector 'leaf_share_factor': 0 =\u003e per-code opt-in only (share a code iff it has share:N\u003e=2); F\u003e=2 =\u003e global mode (share all sized multi-instance codes at grain F) with per-code 'share' overriding (share:1 opts a code OUT). This single knob covers both the safe default-on philosophy (0 + per-code keys) and the §13.3 experiment (F=3, reproducible, no example-programme edits).\n- operators._share_rooms picks grain per code accordingly; fitness honours the explicit leaf.share (type-guarded) as today.\n- homemaker-evolve gains --leaf-sharing / --leaf-share-factor, threaded to driver.search/search_staged (already plumbed).\n- Tests: per-code grain, opt-out, default-OFF parity. NOT editing example programmes so §13.3 stays reproducible.\n\nRelates to dyh (productionise). erc.7 covers the factor/max_share sweep + erc.4 synergy.","notes":"DONE (§13.10). Per-code SpaceReq.share + has_share (programme.py). operators._share_grain resolves grain from leaf_share_factor selector: 0=per-code opt-in (share iff share:N\u003e=2), \u003e=2=global with per-code override (share:1 opts OUT, share:N sets grain). End-to-end conf injection productionised (no monkeypatch): load_config(overrides=) merged last; driver.search/innerloop.optimise/NativeEvaluator/_fitness_for thread conf_overrides={leaf_sharing:True}. CLI: homemaker-evolve --leaf-sharing/--no-leaf-sharing + --leaf-share-factor. Example programmes untouched (13.3/13.9 reproducible). Tests added: grain modes, opt-out, default-OFF parity, load_config overrides, programme parse, CLI parse. 233 pass. Smoke: harbor 37 vs 95 fails. Experiment monkeypatches updated to accept overrides=.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-24T21:03:05Z","created_by":"Bruno Postle","updated_at":"2026-06-28T21:02:48Z","started_at":"2026-06-24T21:03:45Z","closed_at":"2026-06-28T21:02:48Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.7","title":"Leaf-sharing × erc.4 depth-balancing synergy + factor/max_share sweep","description":"With the missing-fail leak closed by explicit multiplicity (§13.3), revisit the erc.3↔erc.4 synergy the diagnostics predicted: depth-balanced construction lands shared leaves at their correct absolute k×target area, which should further cut size+crinkliness. Also sweep leaf_share_factor (3 won here; try 2/4) and leaf_share_max (default 4) on maple+harbor, seeds 0/1/2, staged 20k, vs the §13.3 factor-3 result (maple 86.3, harbor 50.3).","notes":"FACTOR SWEEP DONE (§13.5): factor 3 confirmed default under bal+share. maple f2=92.7 f3=82.3 f4=83.3; harbor f2=53.0 f3=40.0 f4=39.7. Factor 2 regresses both; f3/f4 tied within noise (f3 wins maple +1.0, f4 wins harbor +0.3). leaf_share_max=4 covers factor\u003c=4, no missing-fail leak (re-score OK all runs). erc.7 complete.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-24T20:51:43Z","created_by":"Bruno Postle","updated_at":"2026-06-27T09:55:56Z","started_at":"2026-06-26T07:39:54Z","closed_at":"2026-06-27T09:55:56Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-erc.7","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-24T21:51:42Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-dyh","title":"Productionise leaf-sharing: evolve CLI flag + patterns.config key","description":"erc.3 (§13.3) proved leaf-sharing lowers the floor −37% maple / −32% harbor end-to-end, but the flag is only reachable via the LEAFSHARE env in run_staged_search.py. For real runs: (1) expose --leaf-sharing / --leaf-share-factor on homemaker-evolve (evolve.py), threading to driver.search/search_staged (already plumbed); (2) optionally read a leaf_sharing key from patterns.config so the fitness + construction stay consistent without env injection (fitness already reads conf; construction would read it in evolve). Consider whether to default it ON given the decisive win. Also: the genome.signature ignores leaf.share, so a shared vs unshared leaf of the same type/structure collide — assess if niching needs share in the signature.","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-06-24T20:51:41Z","created_by":"Bruno Postle","updated_at":"2026-06-24T21:03:47Z","closed_at":"2026-06-24T21:03:47Z","close_reason":"Superseded by x3b (per-code shareable flag + CLI wiring), which is the concrete implementation of dyh's 'CLI flag + patterns.config key' scope with the per-code opt-in design.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-7u5","title":"Thread parent failure strings onto Individual","description":"Store the sorted .fails tuple on driver.Individual so operators can read which constraints the parent violates. The score is already recomputed per child (driver.py:146 want_grade path / innerloop result); capture score_with_fails output instead of discarding the strings. Near-zero cost. Prereq for the repair operator (homemaker-py-71d).","notes":"Also feeds erc.1 (per-leaf shape-fail vs density/granularity profile): storing the sorted .fails on Individual makes per-leaf fail attribution available to the diagnostic without re-scoring. Cheap, generically useful — promote ahead of the Tier-3 operator work if erc.1 is picked up first.","status":"closed","priority":2,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-06-23T20:40:17Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:21:50Z","closed_at":"2026-06-28T13:21:50Z","close_reason":"Superseded by construction (DESIGN §13.7): interior-O (default-ON, erc.8) is 71d's named fix (interior O courtyards) and collapsed landlocked crinkliness ~13-\u003e2 of 20 in the high-budget probe. Residual now diffuse, no concentrated ratio-invariant block for a targeted repair operator. Reopen/refile if a future floor probe shows a concentrated ratio-invariant class return.","dependencies":[{"issue_id":"homemaker-py-7u5","depends_on_id":"homemaker-py-71d","type":"parent-child","created_at":"2026-06-23T21:49:52Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-xcy","title":"Constructive seeder is nondeterministic across processes (id-based set iteration)","description":"BUG / reproducibility. operators._assign_adjacency_aware builds Python sets of dom.Node objects (circ/dominated/frontier) and iterates them; set iteration order for objects is id()-based, which varies across processes. Result: constructive_topology(seed=0, adjacency_aware=True) yields DIFFERENT topology signatures in separate processes (verified: sig hashes 4480 vs 16064 for maple-court seed 0), so the whole staged search trajectory is non-reproducible run-to-run. Measured single-run noise ~±3 fails (c3g div=3 control 129 vs §12.3 126 for the same maple seed 0). IMPACT: per-seed numbers in the §11/§12 ledger are not reproducible; only multi-seed MEANS are stable, and small effects (±3-4, e.g. the §12.3 negatives) are near the noise floor. FIX: make the dominating-set/assignment iteration deterministic — sort candidate nodes by the existing idx (leaf index) instead of relying on set iteration order, or drive all tie-breaks through idx. Re-establishing determinism will shift baselines slightly; note in DESIGN.md. Files: operators._assign_adjacency_aware (circ set, dominated union, frontier, the for s in circ loops).","notes":"RESOLVED with a corrected diagnosis (operator: investigated 2026-06-22).\n\nMISDIAGNOSIS: the constructive seeder is NOT nondeterministic. _assign_adjacency_aware ends every max/min with a unique idx tiebreak (-idx[L]); its set unions (circ/dominated/frontier) are used only for membership, so iteration order never leaks. Proven: constructive_topology(seed=0, adjacency_aware AND not) gives BYTE-IDENTICAL signatures across processes for all four example programmes (stable sha1, e.g. maple-court aa=e688f744326b in 3 separate processes). The cited '4480 vs 16064' was a MEASUREMENT ARTIFACT: Python's builtin hash() of a str is salted per-process (PYTHONHASHSEED), so hashing an IDENTICAL signature string in two processes yields different ints (reproduced: 51920/5342/59970 for one identical string). Serial search (workers=1) is byte-for-byte reproducible (identical .dom across runs).\n\nREAL BUG (fixed): parallel-only nondeterminism in driver._run_batch. It admitted futures via concurrent.futures.as_completed -\u003e completion order varies run-to-run, and admit() is order-sensitive (accrues n_evals per result; keeps the FIRST individual of an equal-key tie as best). A long parallel run diverged 167 vs 161 fails (maple seed 0) — the real source of the +-3..6 'noise'. FIX: iterate the futures list in SUBMISSION order (block on each f.result() in turn; all still run concurrently), reproducing the serial admission sequence. After fix: two workers=4 runs are byte-identical (162 fails, identical .dom). 211 tests pass.\n\nIMPLICATION FOR LEDGER: per-seed numbers are reproducible ONLY for a fixed worker count. Serial != parallel is EXPECTED (children-per-iteration = 1 vs n_workers changes batch granularity, hence the search), not nondeterminism. Any ledger A/B comparing runs at DIFFERENT worker counts (or pre-fix parallel) conflated this with a real effect — re-run sub-+-3 effects at a fixed worker count.","status":"closed","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-06-21T20:39:09Z","created_by":"Bruno Postle","updated_at":"2026-06-22T22:13:17Z","closed_at":"2026-06-22T22:13:17Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-9gp.2","title":"M3 Wong-Liu re-association reachability move","description":"9gp.2: add mutate_reassociate, the associativity move (a|b)|c \u003c-\u003e a|(b|c) (same-axis tree rotation on owned/live cuts) missing from the swap(M1)/rotate(M2) set. Targets the §11.4/§11.5 reachability bottleneck. Round-trip/invariant tests. MEASURE value on maple-court vs leu.2 baseline — either result is a valid verdict per the re-scoped bead. DESIGN.md §12.3.","notes":"MEASURED — NEGATIVE (DESIGN.md §12.3). M3 reassociate landed + A/B'd: maple 136.0→139.3, harbor 74.0→78.0 (neutral-to-worse, never a win) across seeds 0/1/2. Reaches new tree shapes but they are not better — third independent negative on search machinery (§11.4/§11.5/§12.3). Kept default-OFF. Valid verdict per re-scope.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-20T16:54:07Z","created_by":"Bruno Postle","updated_at":"2026-06-21T06:20:43Z","started_at":"2026-06-20T17:54:15Z","closed_at":"2026-06-21T06:20:43Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-9gp.2","depends_on_id":"homemaker-py-9gp","type":"parent-child","created_at":"2026-06-20T17:54:07Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-9gp.1","title":"Shape-feasibility pre-filter before inner loop","description":"9gp.1: predict per-leaf shape fails (size/width/proportion/crinkliness) at the proportion-aware target geometry, prune clearly-infeasible topologies before the inner loop so budget flows to feasible ones. Reuse operators._size_divisions_from_targets + fitness quality methods. Default OFF; threshold is a measured parameter. Hook in driver._evaluate. Measure on maple-court + harbor vs leu.2 baseline. DESIGN.md §12.3.","notes":"MEASURED — NEGATIVE (DESIGN.md §12.3). Shape-feasibility filter landed + A/B'd: maple 136.0→140.0, harbor 74.0→77.0. Filter DID prune/explore more topologies in several runs, but extra topologies didn't lower fails. Calibration: shape floor ≈ achieved total (geometry-bound residual, confirms §11.7), so no lower-fail basin for saved budget to find. Kept default-OFF.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-20T16:53:48Z","created_by":"Bruno Postle","updated_at":"2026-06-21T06:20:41Z","started_at":"2026-06-20T16:54:15Z","closed_at":"2026-06-21T06:20:41Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-9gp.1","depends_on_id":"homemaker-py-9gp","type":"parent-child","created_at":"2026-06-20T17:53:48Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-cq1","title":"Constructive seeder + staged dispatch ignored storey_minimum","description":"n_storeys_required only reads level: keys, so a programme with storey_minimum\u003emax(level)+1 (e.g. programme-house: storey_minimum:2, all rooms level:0) was seeded one storey short by constructive_topology and routed to plain (non-staged) search. Fitness then fired a 'storey minimum' fail the search had to repair structurally. Surfaced while measuring leu.2 (proportion-aware seeding deepened the basin around the wrong-storey-count seed). Fix: programme.storey_minimum()/n_storeys_for(); driver.search passes min_storeys to constructive_topology; search_staged routes on max(n_storeys_required, storey_minimum). Independent win: programme-house single-stage baseline 8.0 -\u003e 5.0 fails with correct 2-storey seed.","notes":"Fixed. programme.storey_minimum()/n_storeys_for(); driver.search passes min_storeys to constructive_topology; search_staged routes on max(n_storeys_required, storey_minimum). No-op for harbor/maple; programme-house single-stage baseline 8.0-\u003e5.0 with correct 2-storey seed. 204 tests pass.","status":"closed","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-06-20T08:59:31Z","created_by":"Bruno Postle","updated_at":"2026-06-20T12:32:30Z","closed_at":"2026-06-20T12:32:30Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-leu.2","title":"Proportion-aware constructive seeding (size splits from target dims)","description":"Follow-up to §11.6/§11.7. Adjacency-aware seeding cut the topology load (adjacency-to-c / access) but §11.6 explicitly noted the seed still splits at 0.5, producing 'more, smaller leaves' whose size/proportion/crinkliness fails the inner loop then has to recover. With topology fails now cut by seeding, this GEOMETRY residual is the dominant remaining term (§11.7 verdict). Attacking it at the seed is the proven-productive (construction) direction and is far cheaper than the 9gp encoding rewrite.\n\nIdea: when constructive_topology / lift_base_to_storeys place a cut, size the division ratio from the leaves' TARGET dimensions (programme target areas/widths) instead of 0.5, so the raw seed geometry already sits near feasible proportions and the inner loop starts inside (or much closer to) the size/width/proportion basins. Keep adjacency-aware placement (§11.6/§11.7) unchanged; this only changes split RATIOS, not topology or type assignment. Behind a flag for clean A/B, default-on if it wins.\n\nMeasure raw-seed geometry fails (size/width/proportion/crinkliness) before/after AND end-to-end total fails at budget on harbor, programme-house, AND the new leu.1 benchmark, same protocol as §11.6. Record in DESIGN.md §12.2 + bead notes (incl. negative result if it does not win).","acceptance_criteria":"Proportion-aware split sizing implemented behind a flag; raw-seed geometry-fail reduction quantified; end-to-end total-fail change measured on harbor, programme-house, and the leu.1 benchmark (\u003e=3 seeds each); result (positive or negative) recorded in DESIGN.md.","notes":"DONE (positive), default-on. End-to-end (20000 evals, 3 seeds, staged): harbor 85.3-\u003e74.0 (-13%, best 69), maple-court 151.7-\u003e136.0 (-10%, best 126). PROP=0 reproduces 11.7/12.1 baselines exactly. programme-house regresses at fixed budget (deeper-local-optimum: well-fitted seed walls off the undivide restructuring path) but a budget sweep shows it's convergence-SPEED not asymptote (PROP=1 reaches 1 fail at 150k, beating PROP=0's 2; floor is 2). Win requires rotation+ratio sizing from target dims (area-only regressed via slivers). Surfaced + fixed storey_minimum bug (cq1). Default flipped on: driver.search/search_staged seed_proportion_aware=True, harness PROP=1. DESIGN.md 12.2. 204 tests pass. New maple best 126 saved as generated.dom.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-19T11:14:15Z","created_by":"Bruno Postle","updated_at":"2026-06-20T12:32:28Z","started_at":"2026-06-19T13:03:27Z","closed_at":"2026-06-20T12:32:28Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-leu.2","depends_on_id":"homemaker-py-leu","type":"parent-child","created_at":"2026-06-19T12:14:15Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-leu.2","depends_on_id":"homemaker-py-leu.1","type":"blocks","created_at":"2026-06-19T12:14:45Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-s44","title":"Adjacency-aware constructive seeding (cut adjacency/access fails)","description":"Follow-up to homemaker-py-c4c.2. constructive_topology currently assigns room types to leaves at RANDOM, ignoring each space's adjacency requirement. On harbor this leaves 8 adjacency + 13 access fails in the seeded design. Cluster each required room near its required neighbour (esp. circulation c) at construction time — e.g. assign rooms to leaves whose sibling/parent is C, or grow the tree so each room lands adjacent to a circulation spine. Should directly cut the adjacency+access fail load that now dominates the complete-design quality-fail regime (DESIGN.md §11.2 verdict).","notes":"DONE (positive), DESIGN.md §11.6. _assign_adjacency_aware: greedy connected-dominating-set of circulation leaves on the geometric leaf_graph so every room borders a connected circulation spine; rooms on dominated leaves, O peripheral. Default-on via constructive_topology(adjacency_aware=True), threaded driver.search(seed_adjacency_aware). Seed quality (harbor 10 seeds): adjacency 29-\u003e12, access 27-\u003e8. End-to-end single-stage 20000 evals total fails mean: harbor 110.0-\u003e90.7 (-17.5%, ADJ=0 seed0 reproduces §11.2 105 baseline exactly), programme-house 12.3-\u003e9.3 (-24%); adjacency-aware single-stage harbor (mean 90.7, best 85) beats the §11.3 staged 95. Follow-ups filed: lift_base_to_storeys adjacency-awareness + secondary adjacencies.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-17T21:50:01Z","created_by":"Bruno Postle","updated_at":"2026-06-19T08:12:43Z","started_at":"2026-06-18T22:52:25Z","closed_at":"2026-06-19T08:12:43Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-c4c.5","title":"Topology diversity: structural niching + restarts (replace fitness-scalar dedup)","description":"The population dedups on the FITNESS SCALAR (driver.py:174, abs(fitness) within 1e-9) and replaces worst-by-key. There is no structural/topological diversity preservation, no restarts, no islands. On a rugged combinatorial landscape this converges prematurely — and it is the root cause of the blank-slate gap (§7 Phase 2 verdict): a single mutation chain loses to urb-evolve's random-population diversity (init.dom: memetic 18 fails vs urb-evolve 6).\nAdd: (1) a topology signature (canonical tree hash / partition signature) so 'same topology, different geometry' is detectable and niching is by STRUCTURE not score; (2) diversity-preserving replacement (crowding / niching); (3) restarts or a small island model so blank-slate exploration matches urb-evolve's upfront diversity.","design":"A cheap topology-signature hash (string-encode the per-level tree + types) unblocks niching without waiting for the full canonical encoding; the canonical Polish encoding (homemaker-py-9gp) is the principled long-term signature and makes (a|b)|c == a|(b|c) collapse exactly. Wire signature into admit() in place of / alongside the fitness-scalar guard.","acceptance_criteria":"On blank-slate programme-house, memetic reaches \u003c=6 fails (matching/beating urb-evolve) at equal native-fitness budget; population structural diversity quantified (distinct topology signatures over time) before/after; recorded in DESIGN.md §11.x + bead notes.","notes":"DONE (negative), DESIGN.md §11.5. Implemented genome.signature (ratio-invariant structural topology hash), structural niching (niche_by_signature) replacing the fitness-scalar dedup, and soft restarts (restart_patience); SearchResult gained n_distinct_signatures/diversity_history/n_restarts. Diversity criterion MET: final-pop distinct topologies ~5/16 -\u003e 16/16, ~30% more topologies seen with restarts. Gate NOT met: blank-slate programme-house total fails (20000 evals) before/niche/restart = seed0 11/14/12, seed1 11/11/14, seed2 15/13/13 (mean 12.3/12.7/13.0); harbor staged seed0 = 95/94/108 (legacy 95 reproduces §11.3). Niching is a tie within seed noise, restarts strictly worse. Falsifies the epic's premise that the fitness-scalar dedup is the premature-convergence root cause: legacy already holds 14/16 distinct on harbor; the plateau is a reachability (operator/encoding) problem, not population-management. Both flags default-off, kept for reuse; genome.signature is the cheap stand-in for the 9gp canonical Polish encoding.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-17T19:12:54Z","created_by":"Bruno Postle","updated_at":"2026-06-18T22:41:58Z","started_at":"2026-06-18T21:52:37Z","closed_at":"2026-06-18T22:41:58Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-c4c.5","depends_on_id":"homemaker-py-9gp","type":"relates-to","created_at":"2026-06-17T20:14:46Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-c4c.5","depends_on_id":"homemaker-py-c4c","type":"parent-child","created_at":"2026-06-17T20:12:53Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-c4c.5","depends_on_id":"homemaker-py-c4c.2","type":"blocks","created_at":"2026-06-17T20:12:54Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-c4c.4","title":"Graded high-fail objective (gradient in the high-fail regime)","description":"Phase 4 (homemaker-py-yg5) chose lexicographic (-n_fails, fitness) — correct for not being FOOLED by the 0.5^n cliff (§4.9). But lexicographic-by-TOTAL-count gives almost zero selection signal in the high-fail regime: on harbor every candidate sits at ~49-74 fails, so neighbours are indistinguishable and the search has no gradient to climb. There is no partial credit for a size-fail that is nearly in range, nor for covering one more required requirement. §7 predicted penalty reshaping would 'flatten the fail cliff' for blank-slate; lexicographic did not deliver that for high counts.\nAdd a graded objective for the high-fail regime: continuous proximity per unsatisfied constraint (how close a size/width/proportion is to its band) and/or count of DISTINCT unsatisfied requirements with sub-credit, used as a tie/secondary key beneath fail-count. Must preserve: (a) inner-loop 0.5^n cliff protection (§5.4) — inner loop unchanged; (b) the missing-space hierarchy (§6) — must not make dropping a room attractive.","design":"Likely a third comparison key: (-n_fails, -n_distinct_unsatisfied_or_proximity_sum, fitness). Or a soft margin inside fail counting only in the outer comparator. Keep the scalar fitness (with 0.5^n) untouched so the inner loop is unaffected. Extends homemaker-py-yg5; reuse experiments/penalty_reshape.py harness.","acceptance_criteria":"Measured escape from a high-fail plateau on harbor and/or blank-slate programme-house that the current lex comparator cannot escape at equal budget; before/after best-fail trajectory recorded in DESIGN.md §11.x + bead notes. Inner-loop cliff protection verified unchanged (re-run the §4.9 inner-loop 0/9-regression check).","notes":"NEGATIVE RESULT (DESIGN.md §11.4). Implemented graded proximity key (-n_fails, grade, fitness) behind use_grade flag (default off): fitness._leaf_grade / score_with_grade sum f/FAIL_THRESHOLD over failing per-leaf quality factors; scalar fitness + fail count untouched. Inner-loop 0/9 regression: PASS (re-ran §4.9 part 1). Harbor staged A/B, 20000 evals, seeds 0/1/2 total fails: lex 95/96/106 (mean 99.0) vs lex+grade 99/98/102 (mean 99.7). Grade wins 1/3, loses 2/3, slightly worse on mean, NO plateau escape. Acceptance criterion (escape lex cannot achieve) NOT met. Root cause: premise falsified — within a fixed fail-tier 0.5^n is constant so fitness still spans ~6 orders of magnitude (1e-37..1e-31), giving lex's secondary fitness key a strong gradient already; grade above fitness DISPLACES it (stalls fail-reducing restructurings), below fitness is inert. High-fail plateau is a topology-basin problem -\u003e defer to §11.5 niching/restarts + 9gp canonical encoding. Code kept default-off for reproducibility / possible reuse as a §11.5 diversity signal.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-17T19:12:18Z","created_by":"Bruno Postle","updated_at":"2026-06-18T21:20:49Z","started_at":"2026-06-18T05:31:17Z","closed_at":"2026-06-18T21:20:49Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-c4c.4","depends_on_id":"homemaker-py-c4c","type":"parent-child","created_at":"2026-06-17T20:12:18Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-g0b","title":"homemaker-fitness: native Python CLI replacement for urb-fitness.pl","description":"We need a Python CLI tool that replicates the behaviour of urb-fitness.pl so we can score .dom files without shelling out to Perl. The tool should: accept .dom file paths as arguments (or glob *.dom in cwd if none given), load patterns.config and costs.config from cwd and parent dir (local overrides project-level), skip scoring if .score and .fails files are already newer than the .dom (unless FORCE_UPDATE env var is set), score each .dom using fitness.Fitness.score_with_fails(), write the score to \u003cdom\u003e.score (40-digit float format), write the failures to \u003cdom\u003e.fails, print the score to stderr. Expose as homemaker-fitness entry point in pyproject.toml and as python -m homemaker_layout.fitness_cmd module. This replaces the oracle.py shelling-out path for Phase 3 native fitness.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-14T12:32:29Z","created_by":"Bruno Postle","updated_at":"2026-06-14T16:17:21Z","started_at":"2026-06-14T12:32:52Z","closed_at":"2026-06-14T16:17:21Z","close_reason":"Implemented as homemaker_layout/fitness_cmd.py with homemaker-fitness entry point; exact score parity verified against urb-fitness.pl on corpus","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-gpx","title":"Native fitness parity gap on multi-storey designs (~3.7%)","description":"During programme-house cold-start runs with the fixed level_add operator, the generated 2-storey design showed native=1.2388e-04 vs oracle=1.1944e-04 (3.7% gap), exceeding the 0.01% rel_tol in test_native_fitness_score_parity. All existing single-storey corpus files pass parity fine (73/73). Hypothesis: a subtle discrepancy in value or cost computation for multi-level trees — candidates are staircase quality, circulation connectivity, or per-storey cost accumulation. To investigate: score a sweep of known multi-storey corpus files natively vs oracle and identify which term diverges.","status":"closed","priority":2,"issue_type":"bug","owner":"bruno@postle.net","created_at":"2026-06-14T09:35:34Z","created_by":"Bruno Postle","updated_at":"2026-06-17T17:39:25Z","closed_at":"2026-06-17T17:39:25Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-hqw","title":"Make homemaker-py standalone: remove dependency on Perl Urb package","description":"Currently tests and fitness scoring depend on the Perl Urb package (urb-fitness.pl) and corpus files in /home/bruno/src/urb/examples/. The tool should be fully standalone and not require any external Perl packages or local urb corpus paths. This includes: bundling or reimplementing any needed reference data, making the native Python fitness the default path, and ensuring tests pass without /home/bruno/src/urb present.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T22:27:54Z","created_by":"Bruno Postle","updated_at":"2026-06-13T22:39:28Z","started_at":"2026-06-13T22:34:20Z","closed_at":"2026-06-13T22:39:28Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-0px","title":"Blank-slate cold-start initialisation","description":"The outer search stalls when starting from init.dom (Phase 2 gate: 18 fails after 2000 evals vs urb-evolve's 6). The root cause is single-seed topology mutation chaining — building structure one room at a time gives no gradient across the large zero-feasibility region. Fix requires multi-start bootstrap: generate a diverse initial population by random topology sampling, or a greedy room-placement initialiser that satisfies adjacency/level constraints before handing off to the memetic loop. Without this the tool is only useful for refining existing designs, not designing new buildings from scratch.","acceptance_criteria":"Cold-start from init.dom reaches comparable fail count to urb-evolve within equal eval budget; tested on programme-house","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T21:52:15Z","created_by":"Bruno Postle","updated_at":"2026-06-13T22:28:58Z","started_at":"2026-06-13T22:24:02Z","closed_at":"2026-06-13T22:28:58Z","close_reason":"Bootstrap implemented: auto-detect bare-plot seed, generate pop_size random topologies, evaluate each at child_budget before memetic loop; 3 new tests all green","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-9gp","title":"Canonical slicing encoding (normalized Polish expression) + shape feasibility","description":"RE-SCOPED 2026-06-19 under epic homemaker-py-leu. Canonical slicing encoding capstone (DESIGN.md §5.5, §7 Phase 5): normalized Polish expression / skewed slicing tree (Wong-Liu) for redundancy-free, high-locality topology moves; bottom-up shape-feasibility checks to prune infeasible topologies before the inner loop. Goal: scale to larger programmes. Excluded representations stay excluded (§2): no sequence-pair / B*-tree (non-slicing).\n\nSCOPE CHANGE — one of three original justifications is now DEAD. The original bead leaned on 'provides the principled topology SIGNATURE that c4c.5 niching needs ((a|b)|c == a|(b|c) collapse)'. §11.5 (c4c.5) FALSIFIED niching: maximal structural diversity did not lower fails, and genome.signature already exists as the cheap stand-in. So the niching-signature rationale is dropped. The surviving, EVIDENCE-SUPPORTED parts:\n (a) M1/M2/M3 Wong-Liu moves — richer topology operators that attack the REACHABILITY bottleneck §11.4 AND §11.5 both independently fingered (operators+encoding cannot reach low-fail basins). This is the core justification.\n (b) Shape-feasibility pruning before the inner loop — targets the §11.7 geometry/shape residual (size/proportion/crinkliness) AND saves inner-loop budget, which is the part that actually buys SCALING.\nAssociativity collapse for its own sake is unproven at 16 rooms; its value must be MEASURED on the leu.1 \u003e16-room benchmark, not assumed.\n\nSurvey carry-over (still true): current encoding is base-floor slicing tree + per-storey deltas (GNode), not a Polish expression; 11 mutation operators work on decoded Node trees; decode() fixed-point removes intra-encoding redundancy but tree structure is not canonical. Genome: genome.py; operators: operators.py; tests: test_genome.py, test_operators.py.\n\nORDER: lands LAST in the epic — on the strongest seed (after leu.2 proportion-aware seeding) and with the leu.1 benchmark in place to actually measure the scaling claim. Do not build encoding machinery on an unmeasured premise (the §11.4/§11.5 failure mode).","acceptance_criteria":"Encoding round-trips with the genome; M1/M2/M3 moves implemented; shape-feasibility pre-filter prunes infeasible topologies before the inner loop; MEASURED search improvement on the leu.1 larger-than-house benchmark vs its documented baseline; result recorded in DESIGN.md §12.3.","notes":"MEASURED — NEGATIVE, re-scope satisfied (land + measure). Both M3 reassociate and shape-feasibility filter implemented as Node-tree operators (no Polish rewrite), unit-tested, and A/B'd on maple-court+harbor seeds 0/1/2 (DESIGN.md §12.3). Both neutral-to-slightly-worse; controls reproduce §12.2 exactly (maple 136.0, harbor 74.0). Verdict: Phase-7 residual is NOT reachability/feasibility-bound — it is the geometry/shape floor of the constructed slicing layouts (3rd search-machinery negative vs 4 construction wins). Full canonical Polish rewrite NOT justified: its one testable promise (associativity reachability) was tested directly and did not pay. Both kept default-OFF.","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:39:02Z","created_by":"Bruno Postle","updated_at":"2026-06-21T06:21:00Z","started_at":"2026-06-20T13:09:46Z","closed_at":"2026-06-21T06:21:00Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-9gp","depends_on_id":"homemaker-py-c4c.2","type":"blocks","created_at":"2026-06-17T20:14:45Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-9gp","depends_on_id":"homemaker-py-c4c.5","type":"relates-to","created_at":"2026-06-17T20:14:46Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-9gp","depends_on_id":"homemaker-py-ccw","type":"blocks","created_at":"2026-06-12T00:39:48Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-9gp","depends_on_id":"homemaker-py-leu","type":"parent-child","created_at":"2026-06-19T12:14:35Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-9gp","depends_on_id":"homemaker-py-leu.1","type":"blocks","created_at":"2026-06-19T12:14:47Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-9gp","depends_on_id":"homemaker-py-leu.2","type":"blocks","created_at":"2026-06-19T12:14:46Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":4,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-ccw","title":"Scaled topology search on native fitness","description":"DESIGN.md §7 Phase 3 closing step. Once native fitness passes corpus parity, re-run the Phase-2 memetic search at real scale (population/generations comparable to urb-evolve) on the native objective. This is the first point where the §1 scaling question gets a real answer.","acceptance_criteria":"Full-scale run on programme-house beats both urb-evolve and the small-scale Phase-2 result; larger programme attempted","status":"closed","priority":2,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:38:59Z","created_by":"Bruno Postle","updated_at":"2026-06-13T21:11:13Z","started_at":"2026-06-13T20:49:27Z","closed_at":"2026-06-13T21:11:13Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-ccw","depends_on_id":"homemaker-py-uxz","type":"blocks","created_at":"2026-06-12T00:39:44Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-ccw","depends_on_id":"homemaker-py-way","type":"blocks","created_at":"2026-06-12T00:39:45Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-way","title":"Benchmark: memetic loop vs urb-evolve at equal oracle-call budget (Phase 2 gate)","description":"DESIGN.md §7 Phase 2 gate. Compare against urb-evolve from the same seeds/programmes at equal oracle-evaluation budget — NOT generations (urb-evolve has diversity injection/culling baked in, so generations are not comparable). Go/no-go: memetic loop must beat equal-budget urb-evolve. Scaling up waits for native fitness.","acceptance_criteria":"Best-fitness and failure-count comparison at \u003e=2 budgets, \u003e=3 seeds; go/no-go decision recorded in DESIGN.md","status":"closed","priority":2,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:37:28Z","created_by":"Bruno Postle","updated_at":"2026-06-13T08:55:03Z","started_at":"2026-06-12T21:13:20Z","closed_at":"2026-06-13T08:55:03Z","close_reason":"Phase-2 gate run (benchmark_vs_urbevolve.py, 2026-06-13, 2000 evals, URB_NO_OCCLUSION=1): 2/3 seeds → REVIEW. Memetic beats urb-evolve by 1.91x/1.63x on seeded designs; blank-slate init.dom stalls at 18 fails vs urb-evolve's 6 (random-pop init advantage). Fix: patterns.config was missing from re-score cwd (run_search.py), giving false near-zero finals in first run. Results recorded in DESIGN.md §7 Phase 2 gate.","dependencies":[{"issue_id":"homemaker-py-way","depends_on_id":"homemaker-py-b39","type":"blocks","created_at":"2026-06-12T00:39:39Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-way","depends_on_id":"homemaker-py-gp2","type":"blocks","created_at":"2026-06-12T08:27:45Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-b39","title":"Memetic search driver, small-scale (budgets in oracle evaluations)","description":"DESIGN.md §5, §7 Phase 2, §4.6 arithmetic. Memetic EA/SA over topology genomes wrapping the geometry inner loop (warm-started per §5.6); score = best full fitness over the inner loop. Explicitly small-scale on the batched oracle: tens of topologies, budget accounted in oracle evaluations, not generations. Population evaluation batched into single oracle calls.","acceptance_criteria":"End-to-end run on programme-house completes within a stated oracle-call budget and logs evaluations; produces valid .dom output","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-12T21:10:05Z","started_at":"2026-06-12T13:13:53Z","closed_at":"2026-06-12T21:10:05Z","close_reason":"driver.search() lands: steady-state memetic GA, tournament selection, operators + crossover, warm-started inner loop (Lamarckian write-back), budgets accounted in oracle evaluations. Acceptance run (URB_NO_OCCLUSION=1, budget 2000, seed c964435): 2010 evals / 23 topologies, best 0.00765/2 fails via crossover = x1.14 over the geometry-only optimum; output .dom re-scores standalone at exactly the recorded fitness. En route: found + fixed Urb ratio_o/ratio_type first-match nondeterminism (class-sum patch, 35/35 corpus parity) after the search reward-hacked it; operators now emit canonical uppercase generics (Bruno's correction: C=circulation, Is_Covered is a predicate).","dependencies":[{"issue_id":"homemaker-py-b39","depends_on_id":"homemaker-py-1p0","type":"blocks","created_at":"2026-06-12T00:39:37Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-b39","depends_on_id":"homemaker-py-nyb","type":"blocks","created_at":"2026-06-12T00:39:38Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0} {"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} {"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} {"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 (6–7 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} {"id":"homemaker-py-y51","title":"Locate the size threshold where ruin_recreate stops helping (homemaker-py-f1d follow-up)","description":"f1d (DESIGN.md §23) landed operators.mutate_ruin_recreate (LNS wing rebuild via the adjacency-aware\nconstructor) and validated it at weight=3.0: a statistically significant win on programme-house\nacross 15 seeds (8W/1L/6T, mean fails 7.07-\u003e6.00, Wilcoxon p=0.041) but no consistent effect on\nharbor-house across 8 seeds (3W/2L/3T, mean fails 73.0-\u003e74.5, slight negative lean). Kept\nenable_ruin_recreate default OFF because the two example programmes disagree on direction and only\ntwo sizes were tested -- same conservative bar homemaker-py-qpk applied before its own 1ph larger-N\nconfirmation.\n\nProposal: locate the threshold this size split implies rather than inferring it from two data\npoints. Either (a) a third example programme sized between programme-house and harbor-house's room\ncounts, run through the same qpk protocol (weight=3.0 ON vs OFF, both arms finished with\n--collapse), or (b) a synthetic room-count sweep on programme-house's own patterns.config (scaling\nrequired-space counts up) if a natural third example isn't available. Candidate hypothesis from\n§23's interpretation: the wing-rebuild's benefit tracks how large a fraction of the whole floor's\ntopology one wing move samples, which shrinks as room count grows -- worth checking directly against\nroom count rather than just building size.\n\nIf a threshold is found, flip enable_ruin_recreate's default per-programme-size (or document the\ncutoff for users to opt in below it) instead of leaving it a blanket manual flag.","notes":"Measured (2026-07-26): synthetic room-count sweep (10/14/18/22 rooms, programme-house-derived, budget=3000, weight=3.0 ON vs OFF). No clean monotonic threshold found -- n=10 mild win (N=5, ns), n=14 clean null after N=10 confirmation, n=18 strongest trend (7W/2L/1T, +9.3%, p=0.098, N=10) despite sitting between two weaker sizes, n=22 near-null (N=5). Full results + interpretation in DESIGN.md section 24. enable_ruin_recreate stays default OFF; no per-size flip supported. Caveat: sweep scales room count via duplicated interchangeable room codes (same mechanism harbor-house uses to reduce complexity), which may not isolate the same variable as a genuinely diverse third example programme would.","status":"in_progress","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-26T08:36:17Z","created_by":"Bruno Postle","updated_at":"2026-07-26T14:32:43Z","started_at":"2026-07-26T09:34:23Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-2ax","title":"Spike: autodiff/gradient-based inner-loop ratio optimisation","description":"innerloop.py's default ratio optimiser is multi-start Nelder-Mead (nm_search), chosen because it 'outperforms CMA-ES across harbor-house scale' -- both derivative-free, a legacy of the Perl-subprocess oracle era when the fitness function was not differentiable. Fitness is now a native Python port (fitness.py) with geometry (geometry.py) built from ordinary arithmetic (Heron's-formula areas etc.) that is plausibly differentiable end-to-end. Nobody has revisited derivative-based optimisation now that this is possible.\n\nReal risk to test rather than assume: the deliberately-preserved 0.5^n failure-count penalty cliff (DESIGN.md 4.5, kept specifically to protect the inner loop from trading into new failures) is a sharp discontinuity by design, which could make raw gradients unreliable or misleading near failure boundaries.\n\nScope as a SPIKE: implement the ratio-to-fitness path in a differentiable form (JAX or PyTorch, autodiff over cut ratios for a frozen topology), compare convergence speed/quality against nm_search on a handful of frozen topologies from programme-house and harbor-house before deciding whether to invest further. If gradients are usable, the payoff is a much faster inner loop, which frees budget for more topology exploration elsewhere -- but per 12.3's finding that the current residual is NOT search/eval-count bound, this may only pay off in future phases at larger scale, not on the current residual.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-07-25T20:18:27Z","created_by":"Bruno Postle","updated_at":"2026-07-25T20:18:27Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-c94","title":"Beam/best-first search over adjacency-aware construction decisions","description":"operators._assign_adjacency_aware builds a seed topology via a single greedy pass: greedy connected-dominating-set circulation spine, then constraint-hardest-first room placement onto dominated leaves. Since construction quality is the one lever with a consistent positive track record (11.6, 11.7, 12.2), a width-K beam/best-first search over the SAME construction decisions (which leaf a room lands on, tie-broken today by stochastic order) -- keeping several partial constructions alive and expanding the most promising by a cheap proxy (e.g. operators.predicted_shape_fails or partial fail count) -- may find meaningfully better seeds than one greedy pass per bootstrap individual, without touching the outer topology-search loop at all.\n\nScope: prototype as an alternate constructive_topology path (env-flagged, default off, matching the project's existing A/B convention), measure raw-seed quality (adjacency-to-c / access fail counts, per 11.6's own before/after table) and end-to-end fails at budget on programme-house + harbor-house before considering it as a default.","status":"open","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-07-25T20:18:26Z","created_by":"Bruno Postle","updated_at":"2026-07-25T20:18:26Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-lj3","title":"Try higher _MUTATION_WEIGHTS for bridge_circulation to fire more often (homemaker-py-8sh)","description":"homemaker-py-8sh's A/B (DESIGN.md §21) found two of three harbor-house seeds\nnever even fired mutate_bridge_circulation (byte-identical fitness to 6 sig\nfigs ON vs OFF) within a 2500-eval budget -- at the uniform default weight in\ndriver._MUTATION_WEIGHTS the operator is drawn ~1-in-17 times a mutation\nfires, same priority as cosmetic ops like rotate, despite a 'not connected'\nfail being exactly as fatal to the fail-count-first comparator key as a\nmissing required space (which place_missing already gets a 2.0 weight for).\n\nGOAL: re-run the qi6-protocol A/B (experiments/run_8sh_ab.sh) with\nbridge_circulation's _MUTATION_WEIGHTS entry raised (try 2.0, matching\nplace_missing) to see whether firing it more often increases the\nnot-connected clear rate (currently 2/5) without amplifying the\ntrajectory-divergence noise seen on harbor-house seed 1. Depends on / should\nbe run alongside the larger-N confirmation sweep (homemaker-py-qjg) --\nweight and sample-size are separate variables, ideally tested together in\none combined larger-N sweep with the higher weight, not two separate small\nsweeps.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-24T18:38:40Z","created_by":"Bruno Postle","updated_at":"2026-07-25T09:15:54Z","started_at":"2026-07-24T18:59:06Z","closed_at":"2026-07-25T09:15:54Z","close_reason":"Measured negative (DESIGN.md §22): raising bridge_circulation's _MUTATION_WEIGHTS to 2.0 (matching place_missing) tested together with the qjg larger-N sweep. No total-fail benefit (p=0.71 programme-house N=20, p=0.69 harbor-house N=12) and MORE trajectory-divergence-induced new not-connected fails than at uniform weight (3/20 vs 0/5 at the original small-N weight). Reverted to implicit uniform weight.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-qjg","title":"Larger-N seed sweep to confirm bridge_circulation's mixed A/B (homemaker-py-8sh)","description":"homemaker-py-8sh landed operators.mutate_bridge_circulation (qi6 mechanism (a):\nexplicit repair op that bridges disconnected circulation components) gated\nbehind driver.search's enable_bridge_circulation flag, default off. The\nqi6-protocol A/B (N=3 harbor-house seeds, N=5 programme-house seeds,\nexperiments/run_8sh_ab.sh, DESIGN.md §21) was directionally positive --\ntotal fails never regressed (4 wins/4 ties/0 losses) and 2/5 baseline\nnot-connected fails were cleared (vs 0/4 for qi6's graded-signal mechanism)\n-- but one seed (harbor-house seed 1) saw 2 NEW not-connected fails appear\nalongside its biggest fail-count win, attributed to RNG-trajectory\ndivergence rather than the operator itself (it can only ever convert a leaf\nTO circulation, never away).\n\nGOAL: resolve whether the mean fail-count improvement (harbor 72.0-\u003e69.7,\nprogramme 7.8-\u003e6.8) is a true small positive or small-sample noise, the same\nquestion homemaker-py-1ph answered for collapse_insearch/qpk. Re-run the\nprogramme-house arm (and/or harbor-house) at ~4x the sample (e.g. 20 seeds)\nusing the same protocol (experiments/run_8sh_ab.sh, or a copy), paired\nt-test on per-seed fail-count diffs. If confirmed positive, flip\nenable_bridge_circulation's default to on in driver.py/evolve.py, mirroring\n1ph's flip of collapse_insearch.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-24T18:37:15Z","created_by":"Bruno Postle","updated_at":"2026-07-25T09:16:17Z","started_at":"2026-07-24T18:59:28Z","closed_at":"2026-07-25T09:16:17Z","close_reason":"Measured null (DESIGN.md §22), opposite of 8sh's directional signal: larger-N sweep (programme-house N=20, harbor-house N=12, 1ph protocol) found the 8sh A/B's small positive was small-sample noise, not a true effect -- paired t-test p=0.71 (programme-house) and p=0.69 (harbor-house), both indistinguishable from zero. enable_bridge_circulation stays default OFF.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-1ph","title":"Larger-N seed sweep on programme-house for collapse_insearch default","description":"Follow-on from homemaker-py-qpk (DESIGN.md §20). The collapse_insearch A/B was\nmeasured POSITIVE overall (combined head-to-head ON 6, OFF 2) but kept default\nOFF because the programme-house arm was mixed on a small sample: budget 3000,\nseeds 1-5, ON won 3/5 (mean fails 8.4 -\u003e 7.8, s1 8-\u003e5, s2 8-\u003e7, s4 10-\u003e9 win;\ns3 8-\u003e9, s5 8-\u003e9 loss by one fail). harbor-house was a clean 3/3 win (mean\n80.3 -\u003e 72.0).\n\nGOAL: a larger-N seed sweep on programme-house alone (same budget=3000,\n--collapse-insearch vs baseline, both finished with --collapse) to determine\nwhether the mixed 3/5 result is small-sample noise around a true small\npositive, or a genuine size threshold below which in-search collapse doesn't\npay for its ~1.1-1.9x per-eval cost. If the larger sample confirms a net\npositive on programme-house too, flip collapse_insearch's default from OFF to\nON in evolve.py / driver.py.\n\nNot urgent — qpk's opt-in (--collapse-insearch) is already usable and\ndocumented for harbor-house-scale programmes today; this only decides the\ndefault.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-23T22:12:55Z","created_by":"Bruno Postle","updated_at":"2026-07-24T08:54:46Z","started_at":"2026-07-24T08:26:37Z","closed_at":"2026-07-24T08:54:46Z","close_reason":"Larger-N (20 seed) programme-house sweep confirms POSITIVE: mean fails 7.95-\u003e7.10 (~10.7%), 11W/6L/3T, paired t-test p~0.028. Flipped collapse_insearch default OFF-\u003eON in evolve.py/driver.py. See DESIGN.md section20.","dependencies":[{"issue_id":"homemaker-py-1ph","depends_on_id":"homemaker-py-qpk","type":"discovered-from","created_at":"2026-07-23T23:13:53Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-8sh","title":"Insert/relocate-circulation mutation operator for not-connected fails","description":"Follow-on from homemaker-py-qi6, mechanism (a). The graded circulation-connectivity\nsignal (mechanism (b)/(c), DESIGN.md §18) was A/B tested full-budget and measured\nNEGATIVE: it never fired on harbor-house (byte-identical output ON vs OFF across 3\nseeds) and on programme-house it only ever perturbed unrelated fails, never a\nnot-connected fail (0/4 cases cleared). A finish-time repair (bridge cells into\ncirculation) was also measured negative earlier (195-\u003e560 fails, see §18 history).\n\nGOAL: an explicit search-time mutation/repair operator that inserts or relocates a\ncirculation cell to bridge a disconnected component directly, rather than relying\non the outer GA to discover connectivity via a comparator-key gradient (which this\nissue's A/B showed doesn't work) or a finish-time relabel (which the earlier\nprototype showed is too late/costly). graph.py already computes connected\ncomponents + connected_circulation to detect where bridging is needed.\n\nMeasure on the 6 evolved layouts from the 94g sweep (not-connected + access +\ninaccessible fail counts), same protocol as qi6.","notes":"A/B MEASURED (2026-07-24, qi6/qpk protocol, experiments/run_8sh_ab.sh) — MIXED,\ndirectionally positive, kept default off. Full writeup: DESIGN.md §21.\n\nSummary: total fail count never regressed on any of the 8 seed-arms tested\n(harbor-house budget=2500 seeds 1-3, programme-house budget=3000 seeds 1-5;\n4 wins / 4 ties / 0 losses). Of the 5 arms whose OFF baseline had a genuine\n\"level N not connected\" fail, 2/5 cleared it (programme-house seeds 4, 5) --\nbetter than qi6's graded-signal mechanism (b), which cleared 0/4. But\nharbor-house seed 1 saw 2 NEW not-connected fails appear (0-\u003e2) in a run that\nalso landed the sweep's single biggest fail-count win via a different\ntopology -- attributed to RNG-trajectory divergence (adding any live operator\nto operators.mutate's weighted draw perturbs every later draw, not just the\nones that select it), not the operator mechanically causing fragmentation\n(it only ever converts a leaf TO circulation, never away).\n\nSample (N=3/N=5, matching qi6's own protocol) is too small/noisy to resolve\na true small positive from chance. Landed gated OFF by default\n(driver.search/search_staged enable_bridge_circulation=False,\nevolve.py --bridge-circulation / HOMEMAKER_BRIDGE_CIRCULATION).\n\nFollow-ups identified, not filed: (a) 1ph-style larger-N seed sweep to\nconfirm/reject the mean improvement; (b) raise bridge_circulation's\n_MUTATION_WEIGHTS entry above uniform (like place_missing's 2.0) so it fires\nmore often -- a not-connected fail is as fatal as a missing space but the op\nis currently drawn no more eagerly than cosmetic ops like rotate.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-23T17:21:37Z","created_by":"Bruno Postle","updated_at":"2026-07-24T18:33:50Z","started_at":"2026-07-24T15:23:11Z","closed_at":"2026-07-24T18:33:50Z","close_reason":"Landed + measured (DESIGN.md §21): mechanism (a) implemented as operators.mutate_bridge_circulation, gated behind enable_bridge_circulation (default off). A/B mixed/directionally-positive at qi6's N=3/N=5 protocol -- never worse on total fails, clears 2/5 not-connected fails vs qi6's 0/4, but one seed's trajectory-divergence noise adds 2 new not-connected fails. Kept default off pending a larger-N confirmation (not filed as its own bead; noted as a follow-up in the DESIGN.md section).","dependencies":[{"issue_id":"homemaker-py-8sh","depends_on_id":"homemaker-py-qi6","type":"discovered-from","created_at":"2026-07-23T18:22:05Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-161","title":"In-search evaluation of shape_rotate/deslim GA operators (7fm follow-up)","description":"homemaker-py-7fm's finish-time hill-climb found zero improving moves for\nmutate_shape_rotate/mutate_deslim (operators.py) on the 6-layout harbor-house\nsweep — every candidate move traded a shape fail for a new adjacency/access\nfail on the already co-evolved layout. That's a different regime from\nin-search use: a full multi-generation GA run gives selection pressure and\npopulation diversity a chance to accept a locally-worse move that a later\nstep or recombination completes.\n\nTo test: thread `fit` through driver.search (currently only reqs is passed to\noperators.mutate; shape_rotate/deslim need `fit` and currently no-op inside\nthe GA). Gate with an enable_shape_repair-style flag, mirroring how\nenable_reassociate (§12.3) let 9gp.2 do a clean A/B. Run full-budget\nharbor-house search with/without across seeds, compare final fail counts.\n\nIf negative again, the geometry-intrinsic residual on harbor-house-scale\nprogrammes may be a genuine floor for this representation, not a repairable\ninefficiency (consistent with §17/§19's framing). See DESIGN.md §19 and bd\nmemory collapse-global-94g-and-any-label-usage-optimisation for full context.","notes":"RESULT (negative, confirms 7fm): full sweep at budget=1,000,000, pop=16, child_budget=80, workers=4, harbor-house/init.dom cold-start, 4 seeds (0-3):\n\nenable_shape_repair=False (baseline): fails [14,15,12,17] mean=14.50, fitness mean=1.741e-05\nenable_shape_repair=True: fails [17,14,16,12] mean=14.75, fitness mean=1.24e-05\n\nNo improvement from threading fit into the GA and letting shape_rotate/deslim fire in-search — mean fails is marginally WORSE with the operators enabled, and the 0.25 delta is far inside the seed-to-seed spread (12-17) in both arms. Matches the smaller pilot (budget=20000, 3 seeds: off mean=31.33, on mean=32.00) at a different scale, and matches 7fm's finish-time hill-climb finding that these operators trade one fail for another on already-co-evolved harbor-house layouts.\n\nConclusion: in-search selection pressure and population diversity do NOT rescue shape_rotate/deslim on harbor-house-scale programmes. Supports DESIGN.md §19's framing — the residual fails here look like a genuine floor for this representation on this programme, not a repairable inefficiency reachable by richer local operators. Consistent with bd memory collapse-global-94g-and-any-label-usage-optimisation (geometry-intrinsic fails need geometry/topology search, not local repair or relabeling).\n\nCode kept (not reverted): driver.search()/search_staged() gained enable_shape_repair: bool = False, threading a cached fitness.Fitness instance into operators.mutate() only when set, mirroring the enable_reassociate clean-toggle pattern. Default off reproduces prior runs byte-for-byte. Test: test_enable_shape_repair_threads_fit_into_mutate in tests/test_driver.py. Kept for reuse/reproducibility per the enable_reassociate precedent, not because the flag should default on.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-19T10:05:51Z","created_by":"Bruno Postle","updated_at":"2026-07-22T15:54:47Z","started_at":"2026-07-19T19:57:48Z","closed_at":"2026-07-22T15:54:47Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-qpk","title":"In-search WFC collapse: run collapse_global per-eval during search (A/B vs finish-time)","description":"94g landed and validated the FINISH-TIME global cell-\u003eroom collapse (label search\nover a fixed geometry, monotone, best layout 15-\u003e12). The original 94g thrust was\na per-eval IN-SEARCH collapse: evolution searches unlabelled floorplans and the\nfitness collapses (optimally labels) each candidate before scoring, so search\noptimises the condensed objective directly. This issue is that step.\n\nMECHANISM: call collapse_global (or a cheaper incremental variant) inside\n_evaluate_full before the checks, same as the 9o5 collapse_superposition hook\n(fitness.py:_evaluate_full gates on self._superpose). Reuse the 94g substrate:\nc/o/s partition, level hard constraint, adjacency relaxation, public-access pin,\nthreshold objective.\n\nRISK (carried from homemaker-py-xi7, why 9o5 went negative): fitness =\nmax-over-labellings flattens/roughens the landscape — many topologies collapse to\nsimilar best scores, removing the gradient evolution climbs. AMPLIFIED at full\n(global) scope. The finish-time result does NOT de-risk this: finish-time is\nstrictly cannot-worsen by construction; in-search changes the objective every\neval. MUST A/B superpose-global ON vs OFF with a relaxation-gap log, exactly like\nxi7 did for 9o5, before adopting.\n\nCOST: collapse_global builds graphs + an assignment relaxation per eval — far more\nthan 9o5's per-class collapse. Needs an incremental/cheap variant or caching to be\naffordable in the inner loop; profile first.\n\nPrereq ordering: circulation placement (homemaker-py-qi6) and shape repair change\nthe skeleton/geometry the collapse labels over, so ideally sequence those first.\nRelated: 94g (finish-time, done), xi7 (9o5 A/B + relaxation-gap log), 9o5.","notes":"A/B VALIDATION COMPLETE (2026-07-19), xi7 protocol, 4 workers, equal eval\nbudget, both arms finished with standard --collapse (94g finish-time) so\ncomparison is on the final COLLAPSED score:\n\nharbor-house (init.dom, budget=2500, seeds 1-3): ON WINS 3/3.\n mean fails 80.3 -\u003e 72.0 (s1 85-\u003e74, s2 76-\u003e65, s3 80-\u003e77). No losses.\nprogramme-house (init.dom, budget=3000, seeds 1-5): ON wins 3/5.\n mean fails 8.4 -\u003e 7.8 (s1 8-\u003e5, s2 8-\u003e7, s4 10-\u003e9 win; s3 8-\u003e9, s5 8-\u003e9\n loss by 1 fail). Weaker/noisier on this much smaller building (already\n near its geometry floor, see section13/section19).\nCOMBINED head-to-head: ON 6, OFF 2.\n\nVERDICT: POSITIVE, and the OPPOSITE of the 9o5/xi7 prior (which was\nNULL/NEGATIVE for the per-class interchange relaxation). Unlike 9o5, this is\nthe SAME global WFC-style matching section17/94g already proved\nmonotone/positive at finish time -- running it every eval lets the outer\nsearch see the condensed objective instead of discovering it only once, and\nthe gradient survives rather than flattening. Effect scales WITH building\nsize (harbor-house clean 3/3 vs programme-house mixed), opposite of the 9o5\nlandscape-flattening fear.\n\nCOST: harbor-house wall-clock 102.6s(OFF)-\u003e177.8s(ON) ~1.73x;\nprogramme-house 39.0s-\u003e43.7s ~1.12x. Matches the profiled 1.5-1.9x/eval\nfigure.\n\nDECISION: kept default OFF (programme-house sample too mixed/small to flip\ndefault; 9o5/xi7 scar warrants a second larger-budget confirmation first),\nbut --collapse-insearch is a genuine, tested, working opt-in for\nharbor-house-scale-or-larger programmes. DESIGN.md section20 has full\nwriteup + per-seed numbers. Not filing a follow-up issue -- a larger-N\nprogramme-house seed sweep would be the natural next step if this gets\nrevisited, noted in DESIGN.md as a low-priority idea, not a blocker.\n\nRaw run logs/doms: /tmp scratchpad qpk_ab/ (not committed, ephemeral).","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-18T10:12:54Z","created_by":"Bruno Postle","updated_at":"2026-07-19T19:18:44Z","started_at":"2026-07-19T10:11:11Z","closed_at":"2026-07-19T19:18:44Z","close_reason":"A/B validation complete: POSITIVE (opposite of 9o5/xi7 prior). Harbor-house ON wins 3/3 (mean fails 80.3-\u003e72.0); programme-house mixed 3/5. Combined 6/2. Kept default OFF pending a larger programme-house sample, but --collapse-insearch is a working, documented opt-in. See DESIGN.md section20.","dependencies":[{"issue_id":"homemaker-py-qpk","depends_on_id":"homemaker-py-94g","type":"discovered-from","created_at":"2026-07-18T11:12:54Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-kpu","title":"Schedule B: in-run leaf-sharing annealing (ramp grain down, unfold at each step)","description":"Spun out of homemaker-py-yaa, whose investigation is complete. yaa proved Schedule A (two-phase warm-start) works ONLY when shared leaves are unfolded at the sharing-\u003eno-sharing transition: naive warm-start stalls at 8.66e-08/70 fails, but unfold-then-de-share reaches 4.19e-06/15 fails — matching the direct --no-leaf-sharing baseline. operators.unfold_shared_leaves() is built, tested, and proven.\n\nSchedule B is the in-run variant: instead of a manual two-phase chain, anneal leaf_share_factor down within a single driver run (e.g. 4-\u003e3-\u003e2-\u003eoff) at eval thresholds. At each grain transition: (1) rebuild the cached (dir,sharing) evaluator at the new grain, (2) UNFOLD shared leaves that drop below the new grain so the population stays materialised (reuse operators.unfold_shared_leaves), (3) re-evaluate the whole population under the new evaluator, (4) resume local search. Gradual grain ramp = graduated non-convexity: avoids a single fitness cliff, keeps gross topology fixed on the smaller effective problem early, polishes per-room size/proportion/width late.\n\nDriver hooks needed (driver.py): the evaluator is cached per (dir, sharing) at fitness.py:415 and driver caches one per worker; the ramp must rebuild it and re-score the pop at each threshold. Modest change. Compare head-to-head vs (a) direct baseline 5.14e-06 and (b) the manual unfold warm-chain 4.19e-06 from yaa — does a graduated ramp beat a single hard unfold transition?\n\nWants the circulation-aware unfold from homemaker-py-8iv once available.","notes":"A/B DONE — NEGATIVE (2026-07-17). harbor-house 3M (500k/grain x3 + 1.5M polish, workers 4, ~22h): Schedule B = 1.26e-08 / 23 fails (canonical byte-for-byte). Loses decisively to both targets: direct baseline 5.14e-06/15 and warm-chain 4.19e-06/15 (~400x worse, +8 fails). Graduated ramp FALSIFIED: each grain step spikes fails (phase-end 19-\u003e21-\u003e27, final unfold 27-\u003e36); per-phase budget re-polishes partially-materialised states that the next step materialises further, so coarse-grain gains don't carry forward; polish started from a deeper hole (36) than the warm chain's single clean transition and only reached 23. The sharing-phase topology skeleton (yaa) is best cashed in ONCE at full grain, not annealed. Machinery retained (search_annealed, --anneal-grain, unfold above=, seed_pop, max_share override) — correct/tested/honest — but §15 single-transition finish stays the default. DESIGN §16 updated. Closing.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-15T06:48:11Z","created_by":"Bruno Postle","updated_at":"2026-07-17T15:33:01Z","started_at":"2026-07-16T06:47:57Z","closed_at":"2026-07-17T15:33:01Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-kpu","depends_on_id":"homemaker-py-8iv","type":"blocks","created_at":"2026-07-15T07:48:30Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-kpu","depends_on_id":"homemaker-py-yaa","type":"blocks","created_at":"2026-07-15T07:48:28Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":2,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-8iv","title":"unfold: route circulation to interior children (access/adjacency fails)","description":"Follow-up to homemaker-py-yaa. operators.unfold_shared_leaves() materialises a share=k leaf into a BALANCED binary subtree of k equal-target children. This closes the count deficit (all critical missing-room fails) but the balanced split creates interior children with no direct edge onto a corridor, so it introduces access/adjacency polish fails. Measured on harbor-house evolved-3M.dom: after unfold, 0 critical but 59 total fails, of which ~14 access + ~12 adjacency are attributable to the unrouted interior rooms (18 size / 2 width / 2 proportion are the k*target-\u003eper-leaf sizing mismatch, a separate concern).\n\nIdea: make the unfold subdivision circulation-aware instead of purely balanced — bias each cut so every new child retains an edge onto the shared leaf's original access boundary (or onto a sibling circulation leaf), mirroring the adjacency-aware constructive seeder (§11.6). Options: (a) orient/order the k-leaf subtree so children fan off the corridor side rather than nesting inward; (b) reserve a thin circulation spine within the unfolded block; (c) let a few post-unfold local-search evals fix it (cheaper, but that is exactly what the warm-start already does). Compare final endpoint with/without circulation-aware unfold against the 3M direct baseline (5.14e-06).\n\nRelates to the in-run annealing driver change (Schedule B, option B in yaa): if annealing rebuilds+re-evaluates the population at each grain transition, the unfold used there wants the same circulation-aware subdivision.","notes":"A/B VERDICT (seed 0, budget 150k, 4 workers, warm-start no-sharing polish from\nevolved-3M.dom): GRID WINS DECISIVELY. Circulation-aware slice LOSES.\n slice: 41 fails, fitness 3.52e-14\n grid : 25 fails, fitness 2.36e-09 (~5 orders better, 16 fewer fails)\nGrid led at EVERY milestone and the gap widened, not a near-tie:\n ~12k evals slice 67 / grid 49; ~36k slice 56 / grid 39;\n ~85k slice 46 / grid 30; ~130k slice 41 / grid 25.\nSlice never crossed. The thin-slab geometric debt (proportion/long/width) from\nforcing all k rooms onto one corridor wall costs MORE than the access routing\nsaves: local search re-routes access via topology moves (level_retype,\nplace_missing, level_fix) faster than it can widen thin slices (which it can't,\nwithout topology change — k equal slices of a compact leaf are intrinsically\nthin). Grid's squarer children are the better warm-start; yaa already showed grid\nreaches 4.19e-06.\n\nCONCLUSION: circulation-aware slicing is the WRONG trade. Retain the grid unfold.\nThe 8iv hypothesis (route access at unfold time) is falsified for the warm-start\nregime — access is better left to local search on a squarer seed. n=1 but the\ngap is large and monotone across the whole 150k-eval trajectory.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-12T15:38:34Z","created_by":"Bruno Postle","updated_at":"2026-07-16T06:35:46Z","started_at":"2026-07-15T13:46:49Z","closed_at":"2026-07-16T06:35:46Z","close_reason":"Investigated and falsified. Circulation-aware unfold (slice shared leaves perpendicular to their access edge so every child touches the corridor) was implemented + unit-tested, but the warm-start A/B (evolved-3M seed, 150k-eval no-sharing polish) shows it LOSES decisively to the existing balanced grid: slice 41 fails/3.5e-14 vs grid 25 fails/2.4e-09, grid leading monotonically at every milestone. Forcing k rooms onto one wall makes intrinsically thin slices whose geometric debt (proportion/long/width) local search cannot pay down without topology change, whereas grid's squarer children let local search re-route access cheaply via level_retype/place_missing/level_fix. Conclusion: retain grid unfold; access is better left to local search on a squarer seed. Code reverted (operators.py, test_operators.py back to grid). Findings in issue notes; A/B traces in examples/harbor-house/ab-8iv-*.","dependencies":[{"issue_id":"homemaker-py-8iv","depends_on_id":"homemaker-py-yaa","type":"blocks","created_at":"2026-07-12T16:48:16Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-yaa","title":"Investigate leaf-sharing annealing: shared early, materialise-and-de-share later","description":"Follow-up to homemaker-py-3l6. Leaf sharing is a fitness-evaluation knob over an identical genome representation (fitness.py:415; driver caches one evaluator per (dir, sharing)), and leaf_share_factor is a *grain* (0/1=off, N\u003e=2=share at grain N). That makes a coarse-to-fine / continuation schedule feasible: keep sharing on early to fix gross topology (level connectivity, adjacencies, massing) on a smaller effective problem, then reduce sharing to polish per-room size/proportion/width.\n\nTwo schedules to evaluate:\n A. Two-phase warm-start (no code): run sharing to convergence, feed the output .dom as seed to a --no-leaf-sharing run.\n B. In-run annealing (modest driver change): ramp leaf_share_factor down (e.g. 4-\u003e3-\u003e2-\u003eoff) at eval thresholds, rebuilding the cached evaluator and re-evaluating the population at each transition. Gradual grain ramp avoids a single fitness cliff (graduated non-convexity).\n\nKEY IDEA (Bruno): at the sharing-\u003eno-sharing transition, do not rely on place_missing/divide to rediscover the missing rooms. Instead PROGRAMMATICALLY SUBDIVIDE each shared leaf into the correct number of distinct spaces as an explicit 'unfold' operation. This directly pays down the materialisation deficit that otherwise makes a sharing-run seed start deep in the fail hole (evolved-3M.dom was missing ~12 rooms). The unfold turns a shared leaf of code X (share=k) into k sibling leaves of code X splitting its footprint, so the de-shared genome already satisfies the per-room count before local search resumes. This is also option 3 in 3l6 (materialise shared leaves on write) but applied mid-search at the phase change.\n\nRisk to characterise: sharing re-centres size targets on k*target, so the sharing-optimal massing (fewer, larger rooms) is geometrically different from the per-leaf optimum; the transferable value may be the adjacency/topology skeleton, not the sizing. The unfold subdivision needs to produce children with sensible individual proportions/widths, not just area.\n\nBaseline for comparison (harbor-house, init.dom, 3M, niced 1-worker warm chain): direct --no-leaf-sharing reached canonical 4.19e-06, 15 fails, 0 critical, still climbing. A head-to-head warm-start-from-sharing run (seed evolved-3M.dom) is running now (evolved-warmshare.dom) to measure whether the sharing topology, once forced honest, catches the direct route.","notes":"CONCLUSIVE (warm chain complete, ~2.67M total evals): evolved-unfold.dom = 4.19e-06, 15 fails, 0 critical (breakdown: 6 level, 5 size, 1 width). This MATCHES the direct --no-leaf-sharing baseline (evolved-3M-nols-2 4.19e-06/15 fails; -nols-3 5.14e-06/15 fails).\n\nVERDICT for yaa:\n- Schedule A NAIVE (warm-start from raw sharing seed): FAILS — stalls at 8.66e-08, 70 fails; place_missing/divide cannot dig out the ~15-room count deficit.\n- Schedule A + UNFOLD (operators.unfold_shared_leaves at the transition): CATCHES the direct route (4.19e-06, 15 fails). Bruno's key idea validated: the sharing-phase adjacency/topology skeleton is transferable; the materialisation (count) deficit — not the k*target sizing mismatch — was the sole blocker. Unfold pays it down so de-share local search resumes from a competitive genome.\n\nREMAINING: Schedule B (in-run annealing: ramp leaf_share_factor 4-\u003e3-\u003e2-\u003eoff mid-run, rebuilding+re-evaluating the population and unfolding at each grain step) is the still-unimplemented driver change. Now well-motivated: the unfold primitive it needs is built and proven. Recommend spinning Schedule B into its own implementation issue and closing yaa as the investigation it was scoped as. Circulation-routing refinement to unfold tracked in 8iv.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-07-05T16:33:35Z","created_by":"Bruno Postle","updated_at":"2026-07-15T13:37:07Z","started_at":"2026-07-12T14:52:21Z","closed_at":"2026-07-15T13:37:07Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-yaa","depends_on_id":"homemaker-py-3l6","type":"blocks","created_at":"2026-07-05T17:33:56Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0} {"id":"homemaker-py-b3v","title":"9o5 veto hatch: interchange:false to suppress harmful auto-derived classes (harbor-house 8-code chain)","description":"Deferred escape hatch from the 9o5 spec (§2 'escape hatch' / §7.5 open item), now JUSTIFIED by the xi7 validation run. Auto-derivation chains harbor-house into a transitive 8-code class {da1,ef1,k1,la1,m,me1,n,ws1} spanning a 6x size range (Meeting 10 m2 .. Dining/Neighbourhood 60 m2) — semantically nonsensical (Meeting\u003c-\u003eDining\u003c-\u003eKitchen\u003c-\u003eMechanical are not interchangeable). xi7 A/B (3 seeds, budget 2500) shows superpose ON HURTS: OFF wins 2/3 on collapsed score, and ON ADDS fails in both losses (38v33, 48v43), i.e. the collapse re-typing perturbs feasibility. ACTION: add a per-space 'interchange: false' opt-out in patterns.config that removes a code from class derivation (programme.interchangeable / derive_interchange_classes honour the flag). Lets the architect veto a misgroup without disabling superposition globally. NOTE: superpose default stays OFF regardless (xi7 verdict null/negative overall), so this only matters if/when superpose is used on real configs. Lower priority.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-30T07:23:03Z","created_by":"Bruno Postle","updated_at":"2026-07-17T15:53:27Z","started_at":"2026-07-17T15:43:36Z","closed_at":"2026-07-17T15:53:27Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-b3v","depends_on_id":"homemaker-py-xi7","type":"related","created_at":"2026-06-30T08:24:17Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-xi7","title":"9o5 validation run: A/B superpose ON vs OFF + relaxation-gap log","description":"Future RUN (not a build) for homemaker-py-9o5 type superposition. (1) §7.3 A/B at equal budget on programme-house (interchange classes {b1,b2},{t2,t3}) with --superpose vs --no-superpose; measure the COLLAPSED (final, specific) score only. PRIOR: search-easing bets 0/3 -\u003e expect null, report honestly. (2) §7.4 instrument relaxed-best vs collapsed-best divergence; a large gap diagnoses path-(a) fighting the wrong battle. (3) If harbor-house's chained 8-code class is observed to misgroup/hurt, file the deferred interchange:false veto hatch (spec §2 escape hatch). Depends on the 9o5 build (done).","notes":"VALIDATION COMPLETE — verdict NULL/NEGATIVE (as the 0/3 search-easing prior predicted; now effectively 0/4).\n\n§7.3 A/B equal budget, superpose ON vs OFF, measuring the COLLAPSED (final) score:\n programme-house (init.dom, budget 3000, 4 workers, seeds 1-5): OFF wins 4/5, ON wins 1/5.\n s1 OFF 3.35e-5(8f) \u003e ON 1.10e-6(10f); s2 OFF 6.86e-8(11f) \u003e ON 1.92e-8(12f);\n s3 OFF 4.90e-7(10f) \u003e ON 5.14e-8(12f); s4 OFF 3.20e-6(10f) \u003e ON 2.28e-6(10f);\n s5 OFF 1.01e-7(10f) \u003c ON 2.05e-6(8f) [only ON win].\n harbor-house (init.dom, budget 2500, seeds 1-3): OFF wins 2/3, ON wins 1/3.\n s1 ON 3.25e-17(50f) \u003e OFF 3.28e-18(51f); s2 OFF 6.33e-12(33f) \u003e ON 1.58e-14(38f);\n s3 OFF 5.34e-16(43f) \u003e ON 2.26e-17(48f).\n Superposition does NOT reach better layouts; in most seeds ON has \u003e= OFF fails — the\n per-eval collapse re-typing perturbs counts/adjacency rather than smoothing the search.\n\n§7.4 relaxation gap (relaxed unconstrained best-case usage-quality vs constrained collapse,\non the SAME matched leaves, on each ON final layout): TOTAL gap_ratio 1.01–1.23\n (s1 1.05, s2 1.23, s3 1.01, s4 1.07, s5 1.22; per-class peaks 1.39/1.52). SMALL-to-MODERATE.\n Because collapse is PER-EVAL there is no separate relaxed phase to diverge — search already\n optimises the collapsed objective by construction (spec §3). So the feared LARGE relaxation\n gap does not materialise; the modest residual gap is just the assignment-constraint cost on\n the final layout, NOT the failure mode. Conclusion: path (a) underperforms not from a\n relaxation gap but because the geometry floor dominates (§11-12) — type labels are not the\n binding constraint, so easing them buys nothing and the re-typing adds feasibility noise.\n\nPart 3: harbor-house's chained 8-code class {da1,ef1,k1,la1,m,me1,n,ws1} (6x size span,\n10..60 m2; Meeting\u003c-\u003eDining\u003c-\u003eKitchen\u003c-\u003eMechanical) DOES misgroup AND hurt (OFF 2/3, ON adds\nfails in both losses). Filed deferred veto hatch -\u003e homemaker-py-b3v (interchange:false opt-out).\n\nRECOMMENDATION: keep --superpose default OFF (correct as built). No further build on path (a).\nHarness + raw logs in scratchpad (ab.sh / ab_hh.sh / relax_gap.py).","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-30T06:08:06Z","created_by":"Bruno Postle","updated_at":"2026-06-30T07:24:15Z","started_at":"2026-06-30T06:57:37Z","closed_at":"2026-06-30T07:24:15Z","close_reason":"Validation run complete: NULL/negative verdict (OFF wins 4/5 programme-house, 2/3 harbor-house). Relaxation gap small (per-eval collapse removes it by construction); failure mode is geometry-floor dominance not the gap. Veto-hatch follow-up filed as b3v.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-jrb","title":"Bakeoff: repair operator vs baseline on harbor-house","description":"Bake off the failure-directed repair operator against the current baseline on examples/harbor-house (3m.dom config). Seed from the 3M best (3m.dom) and run ~200k evals, multiple seeds. Also sweep child_budget DOWN (e.g. 80 -\u003e 40 -\u003e 20) to test the hypothesis that reallocating evals from ratio-polishing to topology repair lowers fails. Metric: final n_fails and crinkliness/connected/access counts. Reuse experiments/bakeoff_harbor.py pattern.","status":"closed","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-06-23T20:40:21Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:22:12Z","closed_at":"2026-06-28T13:22:12Z","close_reason":"Superseded by construction (DESIGN §13.7): 71d chain closed; interior-O dissolved the landlocked-crinkliness target the bakeoff would have measured.","dependencies":[{"issue_id":"homemaker-py-jrb","depends_on_id":"homemaker-py-71d","type":"parent-child","created_at":"2026-06-23T21:49:55Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-jrb","depends_on_id":"homemaker-py-u8x","type":"blocks","created_at":"2026-06-23T21:40:35Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-u8x","title":"mutate_repair: failure-directed topology repairs","description":"New operator mutate_repair(parent_root, fails, reqs, rng) in operators.py dispatching on failure class, targeting the leaf id named in each fail string. Priority order = ratio-invariant fails first:\n- crinkliness on L -\u003e retype a geometric neighbour of L to O (interior light well) or reassociate/swap L toward facade (attacks 13)\n- 'level N not connected' -\u003e retype a bridging leaf to C to join circulation components (attacks 2)\n- access on L -\u003e retype a neighbour to C (attacks 1)\n- too few stairs -\u003e core_divide to add aligned vertical core (attacks 1)\nReuse leaf-adjacency graph from _assign_adjacency_aware, plus reassociate/core_divide/retype. Wire into operators.mutate weighting and the driver child-generation path (driver.py:452). Depends on fails being available (parent thread task).","status":"closed","priority":3,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-06-23T20:40:18Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:21:55Z","closed_at":"2026-06-28T13:21:55Z","close_reason":"Superseded by construction (DESIGN §13.7): interior-O (default-ON, erc.8) is 71d's named fix (interior O courtyards) and collapsed landlocked crinkliness ~13-\u003e2 of 20 in the high-budget probe. Residual now diffuse, no concentrated ratio-invariant block for a targeted repair operator. Reopen/refile if a future floor probe shows a concentrated ratio-invariant class return.","dependencies":[{"issue_id":"homemaker-py-u8x","depends_on_id":"homemaker-py-71d","type":"parent-child","created_at":"2026-06-23T21:49:53Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-u8x","depends_on_id":"homemaker-py-7u5","type":"blocks","created_at":"2026-06-23T21:40:33Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-71d","title":"Failure-directed topology-repair operator (harbor-house plateau)","description":"harbor-house plateaus at 27 fails under a 3M-eval run. Fail breakdown of the 3M best (3m.dom): 13 crinkliness, 7 size, 2 edge-too-long, 2 level-not-connected, 1 proportion, 1 access, 1 too-few-stairs.\n\nDiagnosis: ~16 of 27 fails (crinkliness 13, not-connected 2, access 1, stairs 1... actually 17 incl stairs) are INVARIANT to split ratios, but the inner loop (child_budget=80 CMA evals/child) spends essentially all eval budget on ratios. The outer comparator only keeps n_fails (driver.py:259) and operators pick targets at random, so the search reaches these discrete adjacency/daylight fails only by luck.\n\nCrinkliness root cause: a landlocked leaf (no facade edge, no adjacent uncovered O) has area_outside=0 -\u003e crink=0 -\u003e quality_uncrinkliness hits the 'if not crink: return 0.0' branch (fitness.py:339) -\u003e guaranteed fail for ALL ratios. Big rooms (cr1 80m2, da1 60m2, n 60m2) are worst. Fix is interior O courtyards / facade access = TOPOLOGY only.\n\nPlan: read the parent's structured .fails (already computed at driver.py:146, just not stored on Individual) and apply targeted, mostly-deterministic topology repairs per failure class, attacking the ratio-invariant fails the inner loop cannot touch. Reuses reassociate, core_divide, retype, and the leaf-adjacency graph.","notes":"Reparented under erc (Phase 8) as a Tier-3 search-machinery bet, LOW prior per erc's thesis ('search machinery cannot help — the floor IS the result', 0/3 wins from grade/niching/feasibility). Honest framing: this is NOT refuted by that scoreboard — those 3 losses were all selection/pruning changes; none added a TARGETED REPAIR OPERATOR, which is a new class. But do not invest here until a construction lever (erc.3/.4/ld2) moves the floor. Must follow erc's shared protocol: A/B maple-court + harbor seeds 0/1/2, 20k evals staged, control reproduces baseline (maple 136.0, harbor 74.0), verdict in DESIGN.md §13.x.","status":"closed","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-23T20:39:34Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:21:46Z","closed_at":"2026-06-28T13:21:46Z","close_reason":"Superseded by construction (DESIGN §13.7): interior-O (default-ON, erc.8) is 71d's named fix (interior O courtyards) and collapsed landlocked crinkliness ~13-\u003e2 of 20 in the high-budget probe. Residual now diffuse, no concentrated ratio-invariant block for a targeted repair operator. Reopen/refile if a future floor probe shows a concentrated ratio-invariant class return.","dependencies":[{"issue_id":"homemaker-py-71d","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T21:49:50Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-psk","title":"Experiment: island model — prime population from N independent seeds, crossover-heavy migration phase","description":"User-proposed lever (2026-06-23): the Perl Urb workflow ran the search many times and kept the best because runs settled into different local minima. The Python tool is deterministic per --seed, so the analog is: run N independent seeds (e.g. 16), then PRIME a fresh population with those N converged elites and run a second, crossover-heavy phase — an island model with synchronous migration.\n\nKEY DISTINCTION from prior negatives: this is NOT the §11.5 (c4c.5) niching/restart experiment. Those injected FRESH constructive/random seeds for raw diversity and landed null. Here the migrants are FULLY-CONVERGED elites (each spent a complete budget), so they are high-quality building blocks, not diversity filler. The §11.5 'diversity does not help' result does not directly refute this; the mechanism is different (recombination of converged basins, not exploration).\n\nHONEST PRIOR (against): this is a SEARCH-MACHINERY bet, and the leu/c4c epics are decisive that search machinery keeps landing neutral-to-negative (§11.4 graded objective, §11.5 niching+restarts, §9gp M3 reachability + shape-feasibility filter = 3 search-machinery negatives) while CONSTRUCTION/SEED quality wins (§11.6 adjacency-aware seeding, §11.7 adjacency-aware lift = 4 construction wins). The residual is diagnosed as geometry/shape-bound (size/proportion/crinkliness), not population-management-bound. So baseline expectation is neutral.\n\nWHY IT MIGHT STILL PAY: the one untested sub-mechanism is whether crossover can stack wins across independent basins (run A solved cluster X, run B solved cluster Y, child inherits both -\u003e lower total fails than either parent). That has never been tested with converged migrants.","design":"Control / baseline: 'best-of-N' — run N=16 seeds, take the single lowest-fail/highest-fitness result. This is essentially free (the N runs happen anyway) and is the legitimate descendant of Urb's multi-run habit. The experiment must BEAT best-of-N to count, on equal TOTAL budget (N short runs + migration phase vs N+ longer independent runs).\n\nPhase A: run search() for seeds 0..N-1 at a per-seed budget, collect each result.best.root (.dom).\nPhase B: prime a population from those N elites and continue evolving with high p_crossover (e.g. 0.5-0.8) to stress recombination. Reuse existing machinery — no new representation:\n - The seed_factory / bootstrap path in driver.search already accepts a custom seed producer; a factory that cycles through the N pre-evolved roots primes the population directly (no fresh construction).\n - Set bootstrap=True so the N elites are evaluated as the initial population, then the memetic loop runs.\n\nALIGNMENT RISK to measure, not assume: operators.crossover (operators.py:1001) is AREA-MATCHED subtree exchange — it pairs a region of A with the area-closest third of B, with no notion of programmatic/spatial role. Two independently-evolved trees encode similar arrangements with different tree structures (the encoding is not canonical — 9gp closed-negative, abandoned), so the same functional cluster sits at a different path/area/orientation per run. Area-matched splice across independent optima may therefore be disruptive rather than synthesizing, and the inner loop re-solves ratios at the splice boundary (spliced quality not preserved). Instrument: track whether any migration child ever beats max(parent fails) reduction; if crossover children are never net-positive, the null is mechanistic (alignment), not budget.\n\nBenchmarks: maple-court + harbor seeds (the §12.x A/B set), so controls reproduce documented baselines (maple 136.0, harbor 74.0). Record in DESIGN.md (new §12.x) per project convention.\n\nNOT gated on canonical encoding: 9gp is CLOSED with a negative verdict (associativity/reachability tested directly, did not pay). Do not revive the Polish rewrite as a prerequisite.","notes":"NULL/negative (DESIGN.md §14). Island model (N=4 converged elites -\u003e crossover-heavy migration) does NOT beat best-of-N at equal total budget. harbor: island 68 vs control 67 (within parallel noise); maple: island 124 vs control 116 (decisive loss). Mechanistic child_probe: crossover across converged elites rarely synthesizes — only 1/65 (harbor) \u0026 3/63 (maple) children beat the BETTER parent, max fail-drop 2-5. Confirms alignment hypothesis (area-matched splice across non-canonical 9gp encoding is disruptive). 3rd search-machinery null. best-of-N at Phase-A budget stays a free habit; dedicated migration phase not worth its budget.","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-22T23:06:30Z","created_by":"Bruno Postle","updated_at":"2026-06-29T05:11:34Z","started_at":"2026-06-28T21:11:49Z","closed_at":"2026-06-29T05:11:34Z","close_reason":"Closed","dependencies":[{"issue_id":"homemaker-py-psk","depends_on_id":"homemaker-py-6zy","type":"related","created_at":"2026-06-23T00:06:59Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-psk","depends_on_id":"homemaker-py-9gp","type":"related","created_at":"2026-06-23T00:07:01Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-psk","depends_on_id":"homemaker-py-c4c.5","type":"related","created_at":"2026-06-23T00:07:02Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-6zy","title":"Experiment: topology diversity x scaled tournament pressure (joint A/B)","description":"Open lever left untested by §11.5 (homemaker-py-c4c.5): structural niching was A/B'd against the legacy fitness-scalar dedup with selection pressure HELD FIXED at a binary tournament (k=2). §11.5's own mechanism note says maximal diversity under fixed pressure just diffuses effort — i.e. diversity and pressure are coupled and were never co-tuned. This issue isolates that coupling: sweep tournament size jointly with niching to test whether sharper selection converts the extra structural diversity into lower fails, rather than diffusing it. Premise from §11.5 is a diagnosis, not a tested result; the project pivoted to the canonical encoding (homemaker-py-9gp) instead. Tracking so the lever is not silently lost.","design":"§11.5 raised structural diversity to 16/16 but held selection pressure FIXED at a\nbinary tournament (driver._tournament, k=2, driver.py:154; never overridden, no\nsearch() parameter, no env var). The §11.5 writeup names the coupling as the\nmechanism behind its own null result: \"Maximal diversity (16/16) with the fixed\ntournament pressure just diffuses effort — the fitness-scalar dedup's smaller\neffective population exploits a basin slightly harder.\" That is, diversity and\npressure were varied as if independent when they are coupled: niching widens the\npopulation, but k=2 was never sharpened to convert the extra exploration back into\nexploitation.\n\nImplementation:\n- Expose tournament size as a parameter: add `tournament_k: int = 2` to search()\n (and search_staged()), thread it into both _tournament call sites\n (driver.py:448 crossover pair, :452 mutation parent). Optionally an env knob\n HOMEMAKER_TOURNAMENT_K mirroring HOMEMAKER_POP for the experiments harness.\n- Reuse the existing genome.signature / niche_by_signature machinery from c4c.5\n unchanged — this issue adds ONLY the pressure knob and the joint A/B.\n\nA/B design (equal native-fitness budget, URB_NO_OCCLUSION=1, 20000 evals):\n- Grid: niche_by_signature ∈ {off, on} × tournament_k ∈ {2, 3, 4}.\n- The (niche=off, k=2) cell is the legacy baseline; (niche=on, k=2) reproduces\n §11.5's \"niche\" column. New cells are the higher-pressure rows.\n- Seeds: programme-house seeds 0/1/2 (reuse §11.5 seeds for direct comparison),\n plus harbor-house staged seed 0. NOTE the §11.5 sample (3+1 seeds) was thin and\n its null sits within seed noise — widen to \u003e=5 programme-house seeds so a real\n effect is distinguishable from noise this time.\n- Reuse experiments/run_search_scaled.py (NICHE env already wired) +\n run_staged_search.py for harbor; add the k knob to both.\n- Report total fails at budget per cell (primary), plus final-pop distinct\n signatures and distinct-seen (confirm niching still bites at higher k).\n","acceptance_criteria":"On blank-slate programme-house at equal native-fitness budget (\u003e=5 seeds), some (niche, k) cell beats the legacy (off, k=2) baseline mean fails by more than seed noise; OR the joint sweep confirms the §11.5 null is robust to selection pressure (no k recovers a win from 16/16 diversity). Either outcome recorded as a DESIGN.md §11.x subsection + bead notes, with the per-cell fails table. Negative result is an acceptable close.","notes":"CORRECTION: first PH sweep accidentally warm-started from c964…dom (finished design) → floored at 3 fails, not a blank-slate test. §11.5 reproduce-cmds seed PH from init.dom (bare plot). Fixed run_6zy_ab.sh PH_SEED_FILE=init.dom; init.dom verified blank-slate (9 fails @3000 evals). Re-running 30 PH cells from init.dom; 6 harbor cells (correctly used init.dom) kept. Harbor result stands: niche=ON uniformly WORSE than OFF at every k (k2 72→83, k3 77→82, k4 67→75); within niche=ON higher k helps monotonically but never catches niche=OFF; best overall n0/k4=67 (1 seed, within noise). pop_distinct confirms niching bites (16/16 vs 7-10).","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-22T22:52:28Z","created_by":"Bruno Postle","updated_at":"2026-06-29T21:55:30Z","started_at":"2026-06-29T05:24:32Z","closed_at":"2026-06-29T21:55:30Z","close_reason":"Negative result (acceptable per acceptance criteria): joint niche×k sweep shows no (niche,k) cell beats the legacy (off,k=2) baseline. Blank-slate programme-house (5 seeds) baseline mean 4.80 fails is best of grid; every k\u003e2 and every niche=on cell is 6.0-7.0 (within ~1 sd, but central tendency strictly worse). Harbor (staged, seed0) niche=on uniformly worse at all k. Niching bites (pop_distinct 16/16 vs 4-11) but sharper pressure does NOT convert it to lower fails — §11.5 'diffuses effort' null is robust to selection pressure. Plateau is reachability-bound (confirms §11.4/§11.5). Knob tournament_k kept default-off-equivalent (k=2). Written up DESIGN.md §11.8.","dependencies":[{"issue_id":"homemaker-py-6zy","depends_on_id":"homemaker-py-9gp","type":"related","created_at":"2026-06-22T23:53:06Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-6zy","depends_on_id":"homemaker-py-c4c.5","type":"related","created_at":"2026-06-22T23:53:04Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-c3g","title":"Construction granularity / leaf-shape lever for the geometry residual","description":"HYPOTHESIS with measured motivation (DESIGN.md §12.3 residual diagnostic), unproven — must be A/B'd vs the §12.2 baseline before adoption (same discipline as §11/§12 levers). Finding: maple-court shape fails are UNIFORM (~68/73 leaves fail), at only 0.44 plot utilisation, dominated by crinkliness (perimeter/area) then size (undersize). So the residual is NOT placement-mismatch (no good leaves to place into) and NOT density/area-bound — it is OVER-GRANULAR construction: 73 small leaves for 52 rooms =\u003e high perimeter/area + below-target sizes. Candidate levers (construction side): fewer/larger leaves, merge or share leaves across same-class rooms, coarser circulation spine, or a granularity that trades adjacency coverage for leaf shape. Cheap first experiment: vary the circulation-per-room ratio and/or a min-leaf-area floor in constructive_topology, measure shape-fail floor (operators.predicted_shape_fails) and end-to-end fails on maple+harbor. Alternative outcome to accept: 52 distinct rooms cannot be well-shaped as 52 leaves at this density (geometry floor of the slicing representation). Files: operators.constructive_topology/_grow_leaves/_assign_adjacency_aware.","notes":"MEASURED — NULL (DESIGN.md §12.4). Cheap raw probe: coarser spine lowers SHAPE floor (maple 135→110, harbor 83→66) but raises access/adj equally → raw TOTAL flat-to-worse; div=3 near the total-floor min. End-to-end A/B (20000 evals, seeds 0/1/2): maple div6 137.0 / div8 134.3 vs baseline 136.0; harbor div6 75.3 vs 74.0 — all within ±1.7, inside the ~±3 noise floor, huge per-seed spread. Coarsening the spine does NOT pay end-to-end (shape gain cancelled by access damage that is not free to repair). Kept circ_divisor=3 default. En route found nondeterminism bug xcy (±3 noise). Residual is the geometry floor of the slicing representation at this density.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-21T19:55:38Z","created_by":"Bruno Postle","updated_at":"2026-06-21T23:49:34Z","started_at":"2026-06-21T19:59:09Z","closed_at":"2026-06-21T23:49:34Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-ld5","title":"Adjacency-aware lift_base_to_storeys + secondary adjacencies","description":"Follow-up to s44 (DESIGN.md §11.6). s44 made constructive_topology cluster rooms around a connected-dominating-set circulation spine (geometric leaf_graph), cutting harbor single-stage fails 110-\u003e90.7 mean and beating the staged §11.3 best of 95. Two gaps remain: (1) lift_base_to_storeys (staged Stage-2 upper floors) still assigns leaf types at RANDOM — port the _assign_adjacency_aware CDS approach to it so staged search benefits too. (2) Secondary adjacencies (k1\u003c-\u003eda1, da1\u003c-\u003eo, etc., ~4 harbor rooms) are not clustered — extend _assign_adjacency_aware to place rooms with non-c adjacency reqs next to their required neighbour after the c-spine is laid.","notes":"DONE positive, DESIGN.md §11.7. Adjacency-aware lift (CDS seeded from inherited core) + secondary-adjacency room placement. Staged harbor 20k evals: ADJ0 mean 99.0 (=§11.4 baseline), ADJ1 mean 85.3 (-14%, best 78). New best harbor overall. operators 22 tests pass.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-19T08:12:11Z","created_by":"Bruno Postle","updated_at":"2026-06-19T10:41:14Z","started_at":"2026-06-19T08:33:43Z","closed_at":"2026-06-19T10:41:14Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-n5k","title":"Config inheritance: load parent patterns.config/costs.config as base layer","description":"urb-evolve.pl walks up one directory level and loads ../patterns.config and ../costs.config as a base configuration before merging the programme directory's own files on top (local keys win). homemaker-evolve and fitness.load_config should replicate this: when loading a programme directory, first check the parent for each config file and load it, then deep-merge the local file over the top. This lets shared defaults live in a project root while individual programmes only override what differs.","status":"closed","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-14T06:22:38Z","created_by":"Bruno Postle","updated_at":"2026-06-14T06:50:27Z","closed_at":"2026-06-14T06:50:27Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-9t6","title":"Package install: pyproject.toml with entry points","description":"The project currently requires PYTHONPATH=/home/bruno/src/homemaker-py/src and is run via 'python3 experiments/...'. There is no installable package. Add a pyproject.toml with: package discovery for src/homemaker/, a [project.scripts] entry point for homemaker-evolve (homemaker-py-2wc), and minimal metadata. After 'pip install -e .' the tool should be on PATH and importable without PYTHONPATH. Keep the existing pyproject.toml if one exists and extend it.","acceptance_criteria":"'pip install -e .' succeeds; 'homemaker-evolve --help' works from any directory; 'import homemaker' works without PYTHONPATH","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T21:52:35Z","created_by":"Bruno Postle","updated_at":"2026-06-14T07:18:42Z","started_at":"2026-06-14T06:52:28Z","closed_at":"2026-06-14T07:18:42Z","close_reason":"pyproject.toml already had entry point; renamed package to homemaker-layout throughout, GitHub repo renamed, pip install -e . verified","dependencies":[{"issue_id":"homemaker-py-9t6","depends_on_id":"homemaker-py-2wc","type":"blocks","created_at":"2026-06-13T22:52:41Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-gug","title":"Test suite","description":"There are no automated tests. Validation has been done entirely through experiment scripts and the 35-file corpus parity check (homemaker-py-uxz). This is acceptable during exploration but fragile as the codebase grows. Need pytest-based unit tests covering: geometry port correctness (vs known values, not just vs oracle), fitness term correctness (size/width/proportion/adjacency/access/crinkliness/stair terms individually), genome operators (mutations preserve tree invariants), inner loop (convergence on known landscape), and a fast corpus smoke test (subset of the 35 files, score within tolerance). The corpus parity experiment can be the integration test baseline.","acceptance_criteria":"pytest runs clean; geometry, fitness terms, operators, and inner loop each have unit tests; corpus smoke test covers at least 5 files","status":"closed","priority":3,"issue_type":"task","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T21:52:31Z","created_by":"Bruno Postle","updated_at":"2026-06-13T22:51:04Z","started_at":"2026-06-13T22:40:56Z","closed_at":"2026-06-13T22:51:04Z","close_reason":"Added test_geometry.py (26 tests) and test_fitness.py (35 tests); full suite now 175 tests, all passing","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-5l6","title":"Parallelise outer search population evaluation","description":"The outer memetic search evaluates topologies sequentially. Each eval runs the inner loop (CMA-ES) to convergence — independent across population members. Native fitness is pure Python with no shared mutable state, so population evaluation is embarrassingly parallel. multiprocessing.Pool or concurrent.futures.ProcessPoolExecutor over the child generation batch would give near-linear speedup with population size. At 71.8 evals/s single-threaded on a seeded programme-house run, parallelisation across available cores would proportionally increase the effective budget within the same wall-clock time.","acceptance_criteria":"Population generation parallelised; throughput scales with core count; verified correct (same result distribution as serial)","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T21:52:29Z","created_by":"Bruno Postle","updated_at":"2026-06-14T05:55:16Z","started_at":"2026-06-14T05:37:13Z","closed_at":"2026-06-14T05:55:16Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-d6d","title":"Revisit Nelder-Mead for inner loop (post-oracle)","description":"The Phase 1 bakeoff (homemaker-py-d0s) chose CMA-ES over Nelder-Mead because CMA batches oracle calls (18 vs 200 per topology) — critical when oracle cost is 1 s/dom. That constraint is gone: native fitness evaluates at 71.8 evals/s with no batching penalty. The bakeoff showed NM wins quality per eval by +15% at budget 200 (x1.56 vs x1.41 gain). NM is also simpler, has no hyperparameters, and is inherently sequential which matches the inner loop's single-topology use. Re-run the bakeoff with native fitness; if NM still wins, swap it in. Also evaluate gradient-based optimisation (autograd through the native fitness functions) as a potential further improvement.","acceptance_criteria":"Bakeoff re-run with native fitness; inner loop updated if NM or gradient method outperforms CMA-ES; gain improvement documented","status":"closed","priority":3,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-13T21:52:27Z","created_by":"Bruno Postle","updated_at":"2026-06-14T07:51:35Z","closed_at":"2026-06-14T07:51:35Z","close_reason":"NM swapped in as default; bakeoff shows wins at all DOF sizes — programme-house +9% at budget 80, harbor-house decisive win (CMA harmful at 35-40 DOF)","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-2wc","title":"CLI tool: homemaker-evolve (equivalent to urb-evolve.pl)","description":"Wrap the existing memetic search driver as a proper command-line tool, analogous to urb-evolve.pl. The tool should: accept a programme directory and optional seed .dom file as positional args; honour env vars for budget/population (MAX_ITERATIONS, MAX_POP or equivalents); write the best .dom found to the programme directory (or stdout); print progress to stderr; handle SIGINT/SIGTERM gracefully (write best-so-far and exit cleanly). The bulk of the logic already exists in driver.py and experiments/run_search_scaled.py — this is a thin wrapper that makes the search usable from the shell and composable with other tools. Install as bin/homemaker-evolve or src/homemaker/bin/homemaker-evolve.","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T21:47:55Z","created_by":"Bruno Postle","updated_at":"2026-06-14T06:50:39Z","started_at":"2026-06-14T06:01:30Z","closed_at":"2026-06-14T06:50:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":1,"comment_count":0} {"id":"homemaker-py-8fe","title":"Fix Urb programme width default (upstream of homemaker-py-can fix)","description":"The native fitness fix in homemaker-py-can derives a sane width from sqrt(size/proportion) when a programme space has no explicit width. The same bug exists upstream in Perl Urb: Fitness/Base.pm and ProgrammeDriven.pm fall back to width_inside [4.0, 1.0] for any programme space without an explicit width key. Fix the Perl oracle to match the native behaviour (same sqrt(size/proportion) formula).","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-13T21:18:19Z","created_by":"Bruno Postle","updated_at":"2026-06-13T22:14:17Z","started_at":"2026-06-13T21:43:33Z","closed_at":"2026-06-13T22:14:17Z","close_reason":"Fixed: get_space_params now derives width from sqrt(size/proportion) when no explicit width key is present. 34/36 corpus files score higher with the fix; all 111 tests pass after rescoring with URB_NO_OCCLUSION=1.","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-can","title":"Programme width defaults: t3 contradiction (impossible width_inside default)","description":"DESIGN.md §8.2, confirmed in source. t3 (3 m2 WC) has no width spec so inherits width_inside [4.0, 1.0] (Fitness/Base.pm:60) — geometrically impossible; designs 'pass' only by failing size instead. Fix AFTER faithful-port validation (port-faithfully-first policy, §8.1): a sane width default scaled to area (e.g. sqrt(area/proportion)) or per-room widths in patterns.config. Applies to native fitness; optionally upstream to Urb.","acceptance_criteria":"No programme space has a default width incompatible with its target area; corpus re-scored and effect documented","status":"closed","priority":3,"issue_type":"bug","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:39:01Z","created_by":"Bruno Postle","updated_at":"2026-06-13T21:21:37Z","started_at":"2026-06-13T21:16:11Z","closed_at":"2026-06-13T21:21:37Z","close_reason":"Fixed in get_space_params: when a programme space has no explicit 'width', derive target from sqrt(size/proportion) instead of falling back to width_inside [4.0, 1.0]. Re-scored 35-file corpus: 32 files improved (+1-121%), 5 files lost spurious width fails. All 109 tests pass. Upstream Perl fix tracked as homemaker-py-8fe.","dependencies":[{"issue_id":"homemaker-py-can","depends_on_id":"homemaker-py-uxz","type":"blocks","created_at":"2026-06-12T00:39:47Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-yg5","title":"Penalty reshaping: replace 0.5^n while preserving inner-loop protection","description":"DESIGN.md §4.7, §5.4, §7 Phase 4, §8.5. The 0.5^n cliff gives the outer search no gradient and rewards flag-count over geometry, but it also PROTECTS the inner loop from trading into new failures (§4.5). One fitness shape cannot naively be both soft outside and cliff-protected inside. Candidates: cliff-inside-inner-loop only, lexicographic (failure count first, score second), additive/soft, multi-objective Pareto. Must preserve the missing-space failure hierarchy (worse to drop a room than to have a poor one). Measure landscape + search outcomes; this helps Urb today too.","acceptance_criteria":"Chosen scheme documented with measurements: search improves while inner loop still never trades into new failures","status":"closed","priority":3,"issue_type":"feature","assignee":"Bruno Postle","owner":"bruno@postle.net","created_at":"2026-06-11T23:39:00Z","created_by":"Bruno Postle","updated_at":"2026-06-14T08:16:14Z","started_at":"2026-06-14T07:55:32Z","closed_at":"2026-06-14T08:16:14Z","close_reason":"Implemented lexicographic outer-search comparison (-n_fails, fitness). Inner loop unchanged (0.5^n cliff protection preserved). Experiment penalty_reshape.py confirms 0/9 fail regressions in inner loop and shows lex avoids the 3-fail trap that scalar hits 1/3 of the time. Fixed stale _CHILD_INNER_KW sigmas entry.","dependencies":[{"issue_id":"homemaker-py-yg5","depends_on_id":"homemaker-py-uxz","type":"blocks","created_at":"2026-06-12T00:39:46Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-1lj","title":"True multi-objective Pareto EA (NSGA-II style) for the outer topology search","description":"DESIGN.md 4.9 explicitly considered 'genuine multi-objective Pareto' as a resolution for the failure-penalty reshaping problem, alongside lexicographic ordering (failure count first, score second) -- lexicographic was chosen and is the current default; true Pareto multi-objective search (maintaining a non-dominated front across e.g. {fail count, cost, value} rather than one scalar/lex key, à la NSGA-II) was never implemented or measured.\n\nLowest priority of the batch: this is another change to outer-loop SEARCH MACHINERY, and every prior change in that category (structural niching+restarts 11.5, graded objective 11.4, Wong-Liu reassociation + shape-feasibility pruning 12.3, construction granularity 12.4, island model 14, in-run grain annealing 16, circulation-repair operators 21/22) has come back null-to-negative. Worth keeping on the board as a genuinely untried technique, but should not be picked up before the higher-conviction construction/assignment-focused items on this board, and should be A/B'd with the same rigour (fixed worker count per 12.4's determinism note, clean baseline control) rather than assumed to help.","status":"open","priority":4,"issue_type":"task","owner":"bruno@postle.net","created_at":"2026-07-25T20:18:29Z","created_by":"Bruno Postle","updated_at":"2026-07-25T20:18:29Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.6","title":"Experiment: inner-loop slack-expansion objective term","description":"Inner-loop counterpart to plot-fill construction. If Diagnostic B shows the inner loop has room to expand leaves into slack but no objective gradient to do so (the scalar rewards hitting target area but not exceeding it where slack exists), add a term/incentive so the ratio optimiser pushes leaf boundaries out to consume neighbouring slack and satisfy size, rather than parking at target.\n\nCONDITIONAL on Diagnostic B: build this only if B localizes the gap to the inner loop (room to expand, no gradient); if B shows construction targets too-small dims, prefer the plot-fill construction sibling. Must preserve the §5.4 inner-loop cliff / §4.9 lexicographic protection — the term sits where it cannot displace the fail-count ordering. A/B vs §12.2 baseline, seeds 0/1/2, 20000 evals, staged, default-OFF. Record DESIGN.md §13.6.","notes":"DEPRIORITISED by Diagnostic B (§13.2). B shows the inner loop CANNOT repair undersize: the slack is depth-driven maldistribution baked into the frozen topology, and the equal-offset ratio DOF cannot shrink a 14x leaf to feed a starved one without trading into shape fails (0.5^n cliff). Wrong DOF and wrong direction — the blocker is slicing POSITION, not a missing expansion reward. Fix belongs upstream in construction/topology (erc.4 re-scoped, erc.3). Keep as a low-priority follow-up only if a depth-balanced construction still leaves a residual size gradient the inner loop could pick up.","status":"closed","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-22T23:16:24Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:22:22Z","closed_at":"2026-06-28T13:22:22Z","close_reason":"wont-fix (DESIGN §13.7): Diag B (§13.2) showed the inner loop cannot repair undersize (wrong DOF — slicing position, frozen-topology ratios). Superseded by depth-balanced construction (erc.4). Condition unmet.","dependencies":[{"issue_id":"homemaker-py-erc.6","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T00:16:23Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-erc.6","depends_on_id":"homemaker-py-erc.2","type":"blocks","created_at":"2026-06-23T00:16:47Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-erc.5","title":"Experiment: compactness-aware cuts (minimize leaf perimeter/area)","description":"Attacks the #1 factor, crinkliness (346) — a per-leaf perimeter/area property DISTINCT from proportion (aspect ratio). Proportion-aware seeding (leu.2) sizes splits but does not bias toward balanced, square-ish subdivision. Add a KD-tree-style 'keep both children compact' cut rule (prefer the cut orientation/position that minimises summed child perimeter/area) in construction.\n\nCONDITIONAL on Diagnostic A: if A shows per-leaf shape-fail is FLAT across densities (floor intrinsic to slicing density), better cuts at the same leaf count will not pay → this should be closed wont-fix in favour of leaf-sharing. Only build if A shows shape-fail RISES with density. A/B vs §12.2 baseline, seeds 0/1/2, 20000 evals, staged, default-OFF. Record DESIGN.md §13.5.","notes":"DEPRIORITISED by erc.1 verdict (§13.1): per-leaf shape-fail flat vs slicing density and cuts already squarest (_size_divisions_from_targets picks squarest rotation) yet still ~1.8 fails/leaf =\u003e little compactness headroom at fixed leaf count. Floor is intrinsic to leaf COUNT, not cut quality. Revisit only if leaf-sharing (erc.3) underdelivers.","status":"closed","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-22T23:16:21Z","created_by":"Bruno Postle","updated_at":"2026-06-28T13:22:17Z","closed_at":"2026-06-28T13:22:17Z","close_reason":"wont-fix (DESIGN §13.7): Diag A (§13.1) showed the floor is intrinsic to leaf COUNT not cut quality; revisit condition was 'only if leaf-sharing underdelivers' but leaf-sharing OVER-delivered (−32…−39%, §13.3). Condition unmet.","dependencies":[{"issue_id":"homemaker-py-erc.5","depends_on_id":"homemaker-py-erc","type":"parent-child","created_at":"2026-06-23T00:16:21Z","created_by":"Bruno Postle","metadata":"{}"},{"issue_id":"homemaker-py-erc.5","depends_on_id":"homemaker-py-erc.1","type":"blocks","created_at":"2026-06-23T00:16:43Z","created_by":"Bruno Postle","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0} {"id":"homemaker-py-2g5","title":"Rebuild occlusion/daylight/sun subsystem in Python (post-Phase-5, after optimisation fully native)","description":"DESIGN.md §6 port scope — a whole subsystem, not a term. quality_daylight (Leaf.pm:281-296) needs Urb::Misc::Sun + Urb::Field::Occlusion (+CIESky); quality_uncrinkliness also takes the occlusion object. Indoor spaces return 1 for daylight; cost is outdoor spaces + crinkliness. Port Sun_horizontal (262980-minute normalisation) and the occlusion wall set from Dom-\u003eWalls.","acceptance_criteria":"Daylight and crinkliness factors match Perl (float tolerance) across the corpus, including multi-storey cases","notes":"Re-scoped 2026-06-12: occlusion disabled in the Urb oracle instead of ported (see homemaker-py-gp2). Native fitness ships with simple crinkliness (illumination factor = 1, in homemaker-py-gnw). This issue is now the eventual Python occlusion rebuild, only after optimisation works entirely in Python. Restores outdoor-daylight and shaded-wall selection pressure.\nReframed 2026-06-17: orthogonal to epic homemaker-py-c4c. This is fitness FIDELITY (restoring daylight + shaded-wall selection pressure to match Perl), not search CAPABILITY — it changes what 'good' means, not the search's ability to find good. It will NOT improve final designs in the sense currently sought. Stays P4, deferred until the topology-search-quality epic lands and optimisation is fully native.","status":"open","priority":4,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-11T23:38:25Z","created_by":"Bruno Postle","updated_at":"2026-06-17T19:14:48Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"memory","key":"island-model-psk-14-is-a-null-priming","value":"Island model (psk, §14) is a NULL: priming a population from N converged independent elites + crossover-heavy migration does not beat best-of-N at equal total budget (maple island 124 vs control 116). The child_probe instrument shows WHY: area-matched crossover across independently-converged elites almost never synthesizes (1-3 of ~64 children beat the better parent, max drop 2-5) because the slicing encoding is non-canonical (9gp), so splices are disruptive not combinatorial. Search-machinery null #3 after graded-objective and niching/restarts; residual stays geometry/shape-bound."} {"_type":"memory","key":"run-to-run-reproducibility-in-homemaker-layout-serial","value":"Run-to-run reproducibility in homemaker-layout: serial search (workers=1) is byte-for-byte deterministic; parallel (workers\u003e1) is now deterministic too AFTER fixing driver._run_batch to admit futures in submission order (was as_completed/completion order, bug xcy). Reproducibility holds only for a FIXED worker count — serial vs parallel differ because children-per-iteration is 1 vs n_workers (different batch granularity), which is expected, not a bug. The constructive seeder was NEVER nondeterministic: _assign_adjacency_aware has unique idx tiebreaks; comparing topologies with Python builtin hash() of the signature STRING is invalid (PYTHONHASHSEED salts str hashing per process) — use a stable hash (sha1) or genome.signature equality."} {"_type":"memory","key":"urb-oracle-nondeterminism-urb-fitness-pl-output-varies","value":"Urb oracle nondeterminism: urb-fitness.pl output varies run-to-run from Perl hash-order randomisation — .fails line ORDER shuffles (compare sorted, use oracle.Score.fail_lines) and the score float can flip by ~1 ULP (compare with math.isclose rel_tol=1e-12, never ==). Not a batching artifact; affects single runs too. Matters for the Phase 3 native-fitness parity gate (homemaker-py-uxz)."} {"_type":"memory","key":"homemaker-py-pythonpath-set-pythonpath-home-bruno-src","value":"homemaker-layout PYTHONPATH: package installed as 'homemaker-layout' via pip install -e . so 'import homemaker_layout' works from anywhere without PYTHONPATH. For running tests use 'python -m pytest' from project root /home/bruno/src/homemaker-layout (pyproject.toml adds src/ automatically). Never try pip show homemaker — that's the old homemaker-addon conflict."} {"_type":"memory","key":"strategy-decision-2026-06-12-bruno-occlusion-daylight","value":"Strategy decision 2026-06-12 (Bruno): occlusion/daylight is ORTHOGONAL to building a scalable optimiser. Disable it in Urb (env flag, homemaker-py-gp2) rather than port it; native fitness uses simple crinkliness (illumination factor = 1); rebuild occlusion in Python only after optimisation is fully native (homemaker-py-2g5, now P4). Consequence: all scores change when the flag flips — re-baseline corpus/.score, DESIGN \\$4.5 gains, gate bars at one clean boundary AFTER homemaker-py-1p0 closes; Phase-2 urb-evolve benchmark must run with the same flag."} {"_type":"memory","key":"warm-x0-initialization-bug-pattern-when-a-topology","value":"warm_x0 initialization bug pattern: when a topology operator explicitly sets division ratios on a newly-created node (e.g. compound_fix sets node.division=[0.25,0.25] for t3), parent.ratios has no entry for that node (it was a leaf). warm_x0 defaults it to 0.5, corrupting the inner loop's starting point and making the operator invisible to lex comparison. Fix: only propagate child ratios for nodes where the parent node was NOT already divided; stale hidden nodes revealed by structural mutations (swap flipping b.below) must NOT contribute their pre-writeback values. See driver.py lines 259-267 (fixed 2026-06-14)."} {"_type":"memory","key":"adjacency-in-binary-slicing-tree-is-structural-not","value":"Adjacency in binary slicing tree is structural, not geometric: the inner-loop NM cannot fix topological adjacency failures. Two paths exist: (1) tree-sibling adjacency — a node is adjacent to its sibling in the tree; (2) cross-zone geometric adjacency — leaves from different subtrees that happen to share a boundary. Staircase/adjacency fails require a topology mutation that changes which nodes are siblings or which zones touch. This was proved empirically on programme-house: staircase fail from rot=0 layout could not be fixed by NM but was fixed by level_retype creating a two-C topology (2026-06-14/15)."} {"_type":"memory","key":"correction-to-urb-fitness-bug-memory-bruno-2026","value":"CORRECTION to urb-fitness-bug memory (Bruno, 2026-06-12): 'C' is NOT a 'covered' type — Is_Covered is a geometric predicate (indoor space above). Urb's generic types are canonically UPPERCASE: C=circulation, O=outside, S=sahn (get_space_types qw/C O S/; corpus is 100% uppercase, never 'c'/'o' leaves). The mixed-case designs that fired the latent ratio_type first-match bug were created by homemaker's own operator type pool emitting lowercase 'c'/'o' — fixed: driver/operators now emit uppercase generics only, and class checks use t[0].lower() in 'cos'. The Urb class-sum patch stays as defensive hardening (zero impact on canonical designs). Native port (3y7/gnw): treat type classes case-insensitively, generics canonically uppercase."} {"_type":"memory","key":"homemaker-py-3l6-fix-leaf-sharing-evolve-runs","value":"homemaker-py-3l6 fix: leaf-sharing evolve runs now auto-finish before write via driver.polish_finish — unfold_shared_leaves() then a warm-started leaf_sharing=False polish search (--polish-budget, default budget//2). Makes the written .dom honest under canonical homemaker-fitness (internal==canonical when leaf_sharing off). Interrupt path forces polish_budget=0 (unfold+rescore only). This is yaa's unfold-then-polish, made automatic; Schedule B annealing is still kpu."} {"_type":"memory","key":"unfold-strategy-for-shared-leaves-homemaker-py-8iv","value":"Unfold strategy for shared leaves (homemaker-py-8iv, resolved 2026-07-16): use the BALANCED GRID (operators._grow_balanced/_size_subtree_equal), NOT circulation-aware slicing. Slicing a shared leaf perpendicular to its access edge so every child touches the corridor was implemented + A/B-tested and LOST decisively (150k-eval warm-start polish from evolved-3M: slice 41 fails/3.5e-14 vs grid 25 fails/2.4e-09, grid ahead at every milestone). Reason: k rooms all touching one wall are intrinsically thin slices; that geometric debt (proportion/long/width) is unfixable without topology change, while the grid's squarer children let local search re-route access cheaply via level_retype/place_missing/level_fix. Lesson: at the sharing-\u003eno-sharing transition, prioritise squarer children and leave access to local search; do not reintroduce slicing in Schedule B (kpu)."} {"_type":"memory","key":"9o5-multi-use-leaves-is-path-a-superposition","value":"9o5 multi-use leaves is path (a) — superposition as SEARCH RELAXATION that COLLAPSES to specific usage at the end, NOT path (b) loose-fit/no-collapse. Bruno's intent: codes with SIMILAR leaf requirements form an interchangeable equivalence class; during evolution the solver doesn't commit which leaf serves which specific usage (smoother landscape, no fighting over exact leaf usage); at the end the layout is CONDENSED to specific usages by brute-forcing the in-class assignment (3 interchangeable usages over 3 leaves = 3! = 6 combinations to check, pick best). 'Derive automatically' compatibility = requirement-similarity grouping. This reverses the issue's stated 'path b preferred' note."} {"_type":"memory","key":"deceptive-valleys-in-topology-search-when-every-single","value":"Deceptive valleys in topology search: when every single-step mutation from a target state passes through a high-fail intermediary (e.g. level_fix displaces a room into 5+ new fails), a compound operator that atomically applies two coordinated changes can escape. Design compound operators to land on the low-fail state directly, bypassing the deceptive gradient. Programme-house example: level_compound_fix atomically moves the level-constrained room AND re-inserts the displaced room adjacent to C in one step (operators.py, 2026-06-14)."} {"_type":"memory","key":"proportion-aware-constructive-seeding-leu-2-12-2","value":"Proportion-aware constructive seeding (leu.2/§12.2): sizing seed cuts from target AREAS only regresses (thin slivers wreck aspect); you must ALSO pick each cut's rotation for child squareness. It is a convergence ACCELERATOR via a deeper local optimum around the constructed topology: wins where that topology is roughly right and budget is scarce (harbor -13%, maple -10% at 20k evals) but DELAYS small programmes where the seed must be restructured by undivide (programme-house regresses at fixed budget, yet reaches the floor given budget - speed, not asymptote). Default-on. Also: n_storeys must honour storey_minimum, not just level: keys (programme-house storey_minimum:2, all rooms level:0 - was seeded 1 storey short; cq1)."} {"_type":"memory","key":"cli-tool-style-prefer-python-m-homemaker-module","value":"CLI tool style: prefer python -m homemaker.module --parameters pattern, installable via pip install -e . with pyproject.toml entry_points. Not standalone bin/ scripts."} {"_type":"memory","key":"experiment-harness-gotcha-the-leaf-sharing-relaxed-objective","value":"Experiment harness gotcha: the leaf-sharing RELAXED objective (§13.3) is injected ONLY by monkeypatching fitness.load_config in the parent process (run_staged_search.py / probe scripts). This is parent-process-only and does NOT propagate into ProcessPoolExecutor workers (n_workers\u003e1), which re-import fitness fresh and score under the STRICT on-disk patterns.config -\u003e r.n_fails MISMATCH (worker strict vs parent relaxed re-score). ALL §13.x floor runs were therefore SERIAL. Any future PARALLEL leaf-sharing experiment will silently mis-score until leaf_sharing lives on disk/CLI (tracked: homemaker-py-x3b). The parallel driver itself is correct; both paths score via load_config(programme_dir)."} {"_type":"memory","key":"ld2-13-6-interior-o-seed-diagnostic-all","value":"ld2/§13.6 interior-O seed diagnostic: ALL crinkliness fails in the constructed bal+share seed are UNDER-exposed (crink\u003c0.62, landlocked rooms with no facade + no uncovered-O neighbour) — zero over-exposed sliver fails. So the erc crinkliness residual is genuine under-daylighting, validating the interior light-well premise. Default outside_divisor=6 was too sparse (null: harbor 147-\u003e142, crinkliness even rose). odiv=3 is the seed-optimal joint setting: harbor seed fails 147-\u003e129 (-18), maple 219-\u003e206 (-14), landlocked fails drop, at cost of more leaves (harbor +4, maple +8). Because it ADDS leaves it carries the §13.4 wash-out risk; A/B to convergence pending."} {"_type":"memory","key":"experiment-seeding-pitfall-run-search-scaled-py-s","value":"Experiment seeding pitfall: run_search_scaled.py's default PH_SEED (c964…dom) is a FINISHED programme-house design — passing it warm-starts and floors at ~3 fails, NOT a blank-slate topology search. For blank-slate runs comparable to §11.5/§11.6 baselines, seed from examples/programme-house/init.dom (a bare undivided plot; driver bootstrap auto-triggers only on bare plots). Bit the 6zy sweep — first pass used c964 and falsely showed 3-fail floor across the whole grid."} {"_type":"memory","key":"user-preference-bruno-this-is-a-fedora-system","value":"User preference (Bruno): this is a Fedora system — NEVER install Python packages via pip without asking first; always ask whether to install the rpm via dnf (e.g. python3-cma) before considering pip. Applies to any dependency additions."} {"_type":"memory","key":"multi-storey-staircase-consistency-when-dividing-or-retyping","value":"Multi-storey staircase consistency: when dividing or retyping a circulation (C) leaf at one level, the same structural change should be propagated to the matching leaf on ALL other storeys so the stair core path is maintained. The optimizer cannot fix staircase disruptions through trial-and-error geometry alone — it requires a synchronized multi-level operator that applies the same topology change to every storey simultaneously."} {"_type":"memory","key":"never-use-corpus-filenames-candidate-001-dom-candidate","value":"Never use corpus filenames (candidate-001.dom, candidate-002.dom, generated.dom, init.dom, etc.) as --output targets when running experiments. These are test fixtures. Always write experimental outputs to scratch/ or a timestamped path. Lesson from 2026-06-14: warm-start runs overwrote candidate-001/002.dom and broke graph tests."} {"_type":"memory","key":"programme-house-optimisation-result-2026-06-14-15","value":"Programme-house optimisation result (2026-06-14/15): best achievable is 1 fail (l1 wrong level, score ~0.005). 0 fails is geometrically impossible: l1 (min 27m²) must occupy ll (~23m²) at level 0, which eliminates the t3-adj-C provider; dividing ll into lll(l1)+llr(C) gives llr proportion ~6:1 (fails). Python memetic optimizer achieves 1 fail in 50k evals vs Perl optimiser's 2-3 fails. Winning topology: TWO C nodes at level 0 — ll(C) for t3-adj-C via geometric contact, rl(C) for staircase via tree-sibling adjacency to rrr(O). Best .dom: scratch/from-warmstart-fixed.dom and scratch/from-compound3-fixed.dom."} {"_type":"memory","key":"collapse-global-94g-and-any-label-usage-optimisation","value":"collapse_global (94g) and any label/usage optimisation CANNOT fix geometry-intrinsic fails. The harbor-house 15-fail best layout contains long-thin cells that are useless whatever room usage is assigned — their width/proportion/crinkliness fails are shape-bound, not label slack. Two consequences: (1) do not over-claim collapse gains — only ~2-3 of that layout's fails are reclaimable relabel slack, the rest are geometry- or building-level bound; (2) the threshold objective must not be tuned to 'pass' a degenerate cell via a permissive room type — a metric-pass on a physically useless space is gaming, not a fix. Real remedies for these are geometry/topology search (cell shape) and circulation placement, filed separately, not the collapse."} {"_type":"memory","key":"urb-fitness-bug-found-fixed-2026-06-12","value":"Urb fitness bug found+fixed 2026-06-12 (patch in /home/bruno/src/urb, uncommitted): ProgrammeDriven.pm ratio_o/ratio_type grepped case-insensitively over the ratios hash and took the FIRST key — nondeterministic (x4.5 score swings) for designs with mixed-case type classes (both 'c' circulation and 'C' covered). Fixed to SUM the class (matches Is_Circulation//Is_Outside semantics); 35/35 corpus scores unchanged. CRITICAL for homemaker-py-3y7/gnw: the native port must implement class-SUM ratios. Building.pm has the same unpatched pattern (site-driven path, not used by our oracle). Also: the memetic search reward-hacked this bug before the fix — search results predating it are noise artifacts."}