From 87d309771ecd73f38c37b7e1ec6f0d377727a38a Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Mon, 29 Jun 2026 23:20:59 +0100 Subject: [PATCH] =?UTF-8?q?9o5:=20lock=20similarity=20thresholds,=20confir?= =?UTF-8?q?m=20C=3D4,=20defer=20veto=20hatch=20=E2=80=94=20spec=20build-re?= =?UTF-8?q?ady?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R_size=1.5 / R_width=1.3 / R_prop=1.5 for the interchangeable-class similarity gate (S2); class-size cap C=4 confirmed; interchange:false veto hatch deferred to a later fix only if auto-derivation misgroups on real configs. All open questions resolved. Co-Authored-By: Claude Opus 4.8 --- .beads/issues.jsonl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index e7f51a9..285d17b 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -27,7 +27,7 @@ {"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 (e.g. max/min size \u003c= R_size, default ~1.5; tighter than path-b because\n these leaves must be genuinely substitutable, not merely fusible). Tunable.\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 (kept, since pure similarity can't know semantics): optional per-space\n`interchange: false` opt-out or top-level exclude list to veto a bad derived grouping.\nBruno to confirm whether the similarity+service heuristic is sufficient on the\nharbor-house / programme-house configs, or whether the veto hatch ships in v1.\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 LEFT FOR BRUNO BEFORE BUILD:\n- [RESOLVED] Collapse cadence: PER-EVAL with class-size cap C (default 4). Sec 3.\n- R_size / similarity thresholds (S2) defaults; confirm class-size cap C=4 and the\n split-vs-Hungarian behaviour beyond the cap.\n- Whether the `interchange:false` veto hatch ships in v1 or only if the auto heuristic\n misgroups on the real configs.","status":"open","priority":2,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-24T21:11:03Z","created_by":"Bruno Postle","updated_at":"2026-06-29T22:18:09Z","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-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.","status":"open","priority":2,"issue_type":"feature","owner":"bruno@postle.net","created_at":"2026-06-24T21:11:03Z","created_by":"Bruno Postle","updated_at":"2026-06-29T22:20:58Z","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} @@ -70,23 +70,23 @@ {"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":"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."} {"_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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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-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."} -{"_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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"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."}