From f476a5849e164d7d225366d6711bddb39e25e0db Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 26 Aug 2026 10:32:14 +0000 Subject: [PATCH] =?UTF-8?q?beads:=20revise=20homemaker-py-sel=20design=20?= =?UTF-8?q?=E2=80=94=20mandatory=20code-level=20usage,=20no=20fallback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records the outcome of the "how does a parallel usage key work when the leaf name can mutate?" review. Two problems were conflated: 1. Mutation staleness dissolves if usage is CODE-level: usage_of(leaf.type) is looked up fresh, exactly as size/width/proportion/adjacency already are. Verified every corpus leaf type resolves to a declared code or a generic. The broken design is usage stamped ON THE LEAF — 51 sites assign leaf.type, and this project already hit that class twice (share needed a companion share_type; r5a was a real stale-stamp resurrection even with the guard). 2. Name/key divergence is real and is NOT fixed by (1). Resolved by making usage mandatory on every declared code with no prefix fallback, closed vocabulary {bedroom, toilet, living, kitchen, none}, missing key = load error. One source of truth; room codes become free-form identifiers. Measured migration: 10 files, 107 entries, 100 (93%) pure annotation; only 7 change behaviour (la1 x3, li1 x2, br1, tr1) and those are the fix. write_stage1_programme already propagates the key. Left open as the programme author's call, not mechanical: what those four rooms should declare — li1 "Library Corner" as living is arguably already correct, and br1 "Staff Room" may want living rather than none. Also noted: mandatory breaks out-of-repo configs until annotated (mitigate with an error naming each code and its prefix-implied usage, plus a migration script), and the 7 changed entries need their own A/B and re-baseline. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB --- .beads/issues.jsonl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index b87e4c9..e8fd56c 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,6 +1,6 @@ {"_type":"issue","id":"homemaker-py-ju3","title":"Programme codes share a namespace with the generic c/o/s type prefixes: 14% of harbor-house is silently optional and cr1's declared targets are all discarded","description":"Urb's type system is prefix-based (a type starting with 'c' is circulation, 'o'/'s' is outside) and programme room codes live in the SAME namespace, so any code whose name happens to start with c, o or s is silently reinterpreted as a generic type. Three separate consequences, none announced anywhere in the output:\\n\\n1. graph.check_space_counts line ~530 does 'if code[0].lower() in (\"c\",\"o\",\"s\"): continue' -- the code is SKIPPED ENTIRELY. Never required, never counted, no missing fail, no too-many fail.\\n2. Fitness.get_space_params returns the generic *_circulation / *_outside params BEFORE consulting self.spaces, so declared size/width/proportion are overridden.\\n3. dom.is_circulation / is_outside become true, changing the leaf's value rate, exempting it from crinkliness, and making it supply daylight to neighbours.\\n\\nharbor-house is affected; maple-court, health-centre and programme-house are namespace-clean.\\n\\n cr1 'Common Room with Fireplace' (c): size 80.0 -\u003e 0.0/14.0, width 6.0 -\u003e 2.4, proportion 2.0 -\u003e 1.5, ALL THREE overridden; is_circulation=True so value_rate 50 not 300.\\n of 'Staff Office' x2 (o): width/proportion overridden; is_outside=True; value_rate 100.\\n st1/st2 'Storage' (s): width/proportion overridden; is_outside AND is_circulation True; value_rate 100.\\n\\n5 of 37 room instances (14%) are silently optional. MEASURED CONSEQUENCE: in a 20k-eval run the two cr1 leaves converged to 32.9 and 17.1 m2 against a declared 80 m2, and produced no too-many-spaces fail despite count:1; of/st1/st2 are absent from the result entirely with zero fails, because nothing ever asked for them. Compounds with homemaker-py-2v1: cr1 is the single largest room in the programme and is classified circulation, so the x6 value gap pays the search to shrink it.","design":"Separate the namespaces. Cleanest is an explicit per-space 'class:' key in patterns.config (inside/circulation/outside) defaulting to inside, with the prefix rule used ONLY for untyped generic leaves the search creates -- programme codes then never collide regardless of spelling. A cheaper stopgap is a load-time validation error in programme.load_programme_dir that refuses a programme code starting with c/o/s, which at least converts a silent misread into a loud one. Renaming harbor's four codes would fix that one programme but leaves the trap armed for the next author.","acceptance_criteria":"A programme declaring a code starting with c/o/s either honours its declared params and count, or fails loudly at load. harbor-house re-baselined against its real 37-instance programme, and every DESIGN.md harbor fail count re-stated or annotated as measured against the 32-instance effective programme.","status":"closed","priority":0,"issue_type":"bug","assignee":"Claude","owner":"noreply@anthropic.com","created_at":"2026-08-26T08:26:37Z","created_by":"Claude","updated_at":"2026-08-26T09:05:11Z","started_at":"2026-08-26T08:55:30Z","closed_at":"2026-08-26T09:05:11Z","close_reason":"Shipped as loud validation + harbor rename rather than the class: key (DESIGN.md §39.3). The class: key was deliberately not built: auditing the prefix rule showed l/k/b/t carry adjacency semantics too, so re-plumbing the type system would invalidate the whole corpus for a problem whose damage is the silence, not the convention. programme.validate_codes raises on c/o/s codes from both parse paths; harbor cr1/of/st1/st2 renamed to fr1/ao/gs1/gs2 (neutral unused prefixes, prefix-sharing preserved); migrate_ju3_rename.py migrates pre-rename .dom files. Re-baselined at seed 1/20k: 57 fails on the 32-instance effective programme -\u003e 55 on the real 37-instance one, with all five previously-lost room instances now placed inside their declared sigma bands and zero fails naming them.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-2v1","title":"Connectivity is under-priced ~3x against the circulation-\u003ehabitable value gap: the objective is net-positive on severing the spine","description":"Sharper root cause than homemaker-py-ssz, isolated by the 38.6 A/B (which showed none of the three crinkliness modes removes the deletion incentive). Deleting a circulation leaf merges it into its sibling, converting corridor into habitable area. value_circulation=50 vs value_inside=300, so that is a flat 6x value gain. The only counter-pressure is the 'level N not connected' fail, worth 0.5x under value *= 0.5**len(failures). Break-even needs 0.5^k \u003c 50/300, i.e. k \u003e 2.58 -- severing must cost AT LEAST 3 fails to be net-negative, and it costs 1. Net incentive to sever = 6 * 0.5 = 3.0x in favour; measured 4.06x on a well-lit (q_crink=0.736) circulation leaf, so this is NOT the zero-exposure effect and is not fixable inside quality_uncrinkliness. This is the cleanest explanation of why 'level 0 not connected' and 'level 1 not connected' are still present in evolved-3M-nols-3, the best layout found after 1.7M evals: the search is being paid 3-4x to create them.","design":"Options: (a) emit connectivity fails with a multiplicity \u003e= 3 (cheapest, but stacks with the 1i8 cascade-weighting problem and is a magic number); (b) make the connectivity penalty multiplicative and explicit rather than riding the generic 0.5^n (a dedicated building_factor term, sized from the value-rate gap so it tracks value_circulation/value_inside instead of being hard-coded); (c) revalue circulation as infrastructure -- its worth is that it makes other rooms reachable, which the current per-leaf value rate cannot express; the principled version credits circulation with the access it provides rather than its own floor area. (c) is the architecturally correct one and the biggest change. Recommend measuring (a) first purely to confirm the mechanism (does the 3x threshold flip the deletion test?), then designing (b) or (c) properly.","acceptance_criteria":"Deletion test (experiments/diag_exposure_frontage.py value + the ssz A/B harness) shows lit and buried C/O deletions are no longer rewarded; then harbor-house reaches the 15-fail floor in materially fewer than 1.7M evals AND without 'level 0/1 not connected' in the result.","status":"open","priority":0,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-26T07:37:39Z","created_by":"Claude","updated_at":"2026-08-26T07:37:39Z","dependency_count":0,"dependent_count":2,"comment_count":0} -{"_type":"issue","id":"homemaker-py-sel","title":"Usage prefixes (b/t/l/k) are a second implicit namespace: 4 corpus rooms get another room's connectivity rules from their spelling","description":"§39.4 separated programme codes from the GENERIC structural types (C/O/S). It did not touch the other namespace sharing the first character: the USAGE prefixes b=bedroom, t=toilet, l=living, k=kitchen. These classify programme codes by first letter and are still prefix-based BY DESIGN (it is how Urb encodes room usage, and unlike the generic rule they never discard a requirement) -- but they are not inert.\\n\\ngraph.has_circulation deletes graph edges from them: a 'bedroom' loses its edges to living/kitchen/bedroom/toilet, a 'toilet' loses its edges to outside/living/kitchen/toilet, and b/t keep their LEAST popular circulation neighbour while l/k keep their MOST popular. fitness.access and the public-access check read them too. So a code that picks one up by accident is silently given another room's connectivity rules -- and connectivity is exactly where §38 located the residual.\\n\\nFour corpus rooms are misclassified by spelling alone:\\n la1 'Laundry Room' -\u003e living (harbor-house, harbor-house-l0, maple-court)\\n li1 'Library Corner' -\u003e living (harbor-house, maple-court)\\n br1 'Staff Room' -\u003e bedroom (health-centre)\\n tr1 'Treatment Room' -\u003e toilet (health-centre)\\n\\nMeasured on a constructed health-centre seed: tr1 (as a toilet) has its edge to the adjacent outside space O stripped from the circulation graph; br1 (as a bedroom) has its edge to t10 'Staff WC' stripped. Both feed has_circulation and therefore the 'N inaccessible usable space' / 'level N not connected' fails.\\n\\nReport it with: python experiments/audit_programme_config.py (usage-prefix section).","design":"Same shape as §39.4: an explicit 'usage:' key in patterns.config (bedroom/toilet/living/kitchen/none), defaulting to the current prefix rule so nothing changes until a programme opts in. UNLIKE §39.4 this alters fitness for programmes that are currently spelled correctly too (any code whose usage is inferred), so it needs its own A/B and re-baseline rather than being folded into §39. Consider also whether the four misclassified corpus rooms should simply declare their real usage once the key exists -- that is the point of the key.","acceptance_criteria":"A programme can declare a room's usage explicitly; the prefix rule applies only where nothing is declared; audit_programme_config reports no unintended usage classification across the corpus; re-baselined with an A/B.","status":"open","priority":1,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-26T10:06:54Z","created_by":"Claude","updated_at":"2026-08-26T10:06:54Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"homemaker-py-sel","title":"Usage prefixes (b/t/l/k) are a second implicit namespace: 4 corpus rooms get another room's connectivity rules from their spelling","description":"§39.4 separated programme codes from the GENERIC structural types (C/O/S). It did not touch the other namespace sharing the first character: the USAGE prefixes b=bedroom, t=toilet, l=living, k=kitchen. These classify programme codes by first letter and are still prefix-based BY DESIGN (it is how Urb encodes room usage, and unlike the generic rule they never discard a requirement) -- but they are not inert.\\n\\ngraph.has_circulation deletes graph edges from them: a 'bedroom' loses its edges to living/kitchen/bedroom/toilet, a 'toilet' loses its edges to outside/living/kitchen/toilet, and b/t keep their LEAST popular circulation neighbour while l/k keep their MOST popular. fitness.access and the public-access check read them too. So a code that picks one up by accident is silently given another room's connectivity rules -- and connectivity is exactly where §38 located the residual.\\n\\nFour corpus rooms are misclassified by spelling alone:\\n la1 'Laundry Room' -\u003e living (harbor-house, harbor-house-l0, maple-court)\\n li1 'Library Corner' -\u003e living (harbor-house, maple-court)\\n br1 'Staff Room' -\u003e bedroom (health-centre)\\n tr1 'Treatment Room' -\u003e toilet (health-centre)\\n\\nMeasured on a constructed health-centre seed: tr1 (as a toilet) has its edge to the adjacent outside space O stripped from the circulation graph; br1 (as a bedroom) has its edge to t10 'Staff WC' stripped. Both feed has_circulation and therefore the 'N inaccessible usable space' / 'level N not connected' fails.\\n\\nReport it with: python experiments/audit_programme_config.py (usage-prefix section).","design":"REVISED after review (the 'parallel key vs mutating leaf name' objection).\n\nTWO DISTINCT PROBLEMS, only one of which the key creates:\n\n1. Mutation staleness — DISSOLVES if usage is CODE-level, not leaf-level. mutate_retype sets leaf.type='b1'; usage is usage_of('b1'), looked up fresh from patterns.config on every read, exactly as size/width/proportion/adjacency/level/count already are. Verified: every leaf type in the corpus resolves to a declared code or a generic, so the lookup never dangles. The broken design is usage STAMPED ON THE LEAF: there are 51 sites assigning leaf.type, and this project has been bitten by that class twice already — 'share' needed a companion 'share_type' so a retype invalidates it, and even with that guard homemaker-py-r5a was a real bug where a stale stamp got RESURRECTED by a relabel restoring the old code. Never stamp usage on a leaf.\n\n2. Name/key divergence — REAL, and not fixed by (1). If b1 declares usage:living, the name says bedroom and the key says living: two sources of truth for one fact, the exact bug class §39 removed.\n\nRESOLUTION (supersedes the earlier per-code-override design): make usage MANDATORY on every declared space code, with NO prefix fallback. The prefix rule is retired as a semantic mechanism; room codes become free-form identifiers. A missing usage is a load error naming the code (loud, like validate_codes), not a silent 'none'. Closed vocabulary {bedroom, toilet, living, kitchen, none} — exactly what the engine reads (graph.has_circulation edge stripping, fitness.access, public-access) — so an unknown value errors rather than silently no-opping. Generic C/O/S are unaffected: they are never declared in spaces:, and their behaviour comes from the generic rule.\n\nMEASURED MIGRATION COST: 10 corpus files, 107 code entries. 100 of 107 (93%) are pure annotation reproducing current behaviour. Declarations would be none=60, toilet=23, bedroom=12, living=8, kitchen=4. Only 7 entries change behaviour (la1 x3, li1 x2, br1, tr1) and those ARE the fix. write_stage1_programme already does dict(spec) so derived programmes carry usage through.\n\nOPEN — ARCHITECTURAL, NOT MECHANICAL: what the four flagged rooms should actually declare. la1 'Laundry Room' (currently living) and tr1 'Treatment Room' (currently toilet) look plainly wrong. But li1 'Library Corner' as 'living' is arguably CORRECT — a reading corner is a sitting space — and br1 'Staff Room' is a break room, so 'living' may fit better than 'none'. These are the programme author's calls; the audit tool's 'name disagrees' flag is a prompt, not a verdict.\n\nCOST TO OUT-OF-REPO PROGRAMMES: mandatory means any config outside this repo fails to load until annotated. Mitigate with an error listing each missing code and its prefix-implied usage so migration is copy-paste, plus a one-shot migration script.\n\nStill needs its own A/B and re-baseline (the 7 changed entries move harbor/maple/health-centre).","acceptance_criteria":"A programme can declare a room's usage explicitly; the prefix rule applies only where nothing is declared; audit_programme_config reports no unintended usage classification across the corpus; re-baselined with an A/B.","status":"open","priority":1,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-26T10:06:54Z","created_by":"Claude","updated_at":"2026-08-26T10:31:31Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-ut5","title":"Update the 2v1 acceptance target: harbor's 15-fail reference was measured pre-ju3 against the 32-instance effective programme","description":"DESIGN.md 38.7 fixes the acceptance test for homemaker-py-2v1 as 'harbor-house reaches its known 15-fail floor in materially fewer than 1.7M evals, and without level 0/1 not connected'. That 15-fail figure comes from evolved-3M-nols-3, measured before homemaker-py-ju3 against the 32-instance EFFECTIVE programme (cr1/of/st1/st2 silently dropped or mis-parameterised). Against the real 37-instance programme the number will differ, so the target as written is not measurable any more.\\n\\nNeeded: migrate evolved-3M*.dom with experiments/migrate_ju3_rename.py, rescore against the renamed programme, and restate the 2v1 acceptance figure. The 39.3 re-baseline (55 fails, seed 1, 20k evals) is the new near-term reference but is not the long-budget floor.","acceptance_criteria":"evolved-3M*.dom committed and migrated; its post-ju3 fail count recorded in DESIGN.md; the 2v1 acceptance figure restated against it.","status":"open","priority":1,"issue_type":"task","owner":"noreply@anthropic.com","created_at":"2026-08-26T09:05:23Z","created_by":"Claude","updated_at":"2026-08-26T09:05:23Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-hxi","title":"Buried circulation and outside space are negative-value: search is rewarded for deleting the circulation spine","description":"Direct consequence of the zero-exposure bug. Measured on a constructed harbor-house seed: deleting a buried O leaf improved the raw score 85x and removed 7 fails; deleting a buried C leaf improved it 62x and removed 6 fails. Programme rooms are held in place ONLY by the missing-space fail cascade, not by contributing value -- deleting a buried k1/da1/m costs +15 fails, so they stay, but nothing positive keeps them. Circulation and outside leaves carry no missing-space requirement, so nothing keeps them at all. Observed live: in a 20k-eval harbor-house run, undivide/core_undivide account for a large share of recorded improvements (16 occurrences in the log) -- the search is literally deleting circulation to score better. This explains three prior negative results as a single mechanism: 18 graded circulation-connectivity (a tie-break signal cannot beat a 60x scalar gradient), 21/22 bridge_circulation (the operator inserts corridor leaves the objective immediately punishes), and the 'level N not connected' hard fails surviving \u003e1M evals in the 3M run.","notes":"Depends on the zero-exposure fix; may need no separate fix if (a)/(c) there restores a value gradient for circulation. Worth re-running the 18 and 21/22 A/Bs afterwards -- both may have been measuring a broken gradient rather than a bad idea.","status":"open","priority":1,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-25T23:14:40Z","created_by":"Claude","updated_at":"2026-08-25T23:14:40Z","dependencies":[{"issue_id":"homemaker-py-hxi","depends_on_id":"homemaker-py-ssz","type":"blocks","created_at":"2026-08-25T23:15:12Z","created_by":"Claude","metadata":"{}"},{"issue_id":"homemaker-py-hxi","depends_on_id":"homemaker-py-2v1","type":"blocks","created_at":"2026-08-26T07:37:47Z","created_by":"Claude","metadata":"{}"}],"dependency_count":2,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"homemaker-py-ssz","title":"Zero-exposure leaves score hard quality=0: the objective assigns no value to any interior room","description":"fitness.quality_uncrinkliness returns a hard 0.0 when a leaf has no daylit wall (area_outside==0 =\u003e crink==0 =\u003e 'if not crink: return 0.0'). Because evaluate_leaf MULTIPLIES factors into quality, and process_storey accumulates 'value += quality * rate * area', such a leaf contributes EXACTLY ZERO value while still adding cost. Measured on the full default construction stack (leaf_sharing, depth_balanced, interior_outside, collapse_insearch), 3 seeds each: harbor-house 46% of interior leaves, health-centre 45%, maple-court 56% are zero-exposure. On a converged 20k-eval harbor-house run (seed 1, 57 fails) 14 of 17 crinkliness fails are zero-exposure, and ~470 m2 of the 721 m2 ground floor plate sits at zero value. This is the mathematically consistent limit of the gaussian (1/crink -\u003e inf), so it is a faithful port, not a porting bug -- but it means the objective's gradient does not describe a good building.","design":"Options, none yet chosen: (a) floor the factor at a small epsilon instead of 0 so buried leaves keep a value gradient and remain rankable; (b) make the gaussian one-sided (clip to 1.0 on the compact side) so being LESS exposed than target is not punished as hard as being over-exposed -- architecturally, a compact well-insulated room is not a defect; (c) exempt circulation/store types from the daylight requirement entirely (uncrinkliness_circulation currently uses the same [5/6, 1.1/3] as habitable rooms, so internal corridors -- completely normal architecture -- are guaranteed failures). Any change here invalidates prior fail-count baselines, so it needs its own A/B and a DESIGN.md section.","acceptance_criteria":"A/B on harbor-house + maple-court at fixed budget showing the chosen variant lowers hard-fail count without inflating soft; DESIGN.md section recording the result; prior baselines re-stated under the new objective.","status":"open","priority":1,"issue_type":"bug","owner":"noreply@anthropic.com","created_at":"2026-08-25T23:14:40Z","created_by":"Claude","updated_at":"2026-08-25T23:14:40Z","dependencies":[{"issue_id":"homemaker-py-ssz","depends_on_id":"homemaker-py-2v1","type":"blocks","created_at":"2026-08-26T07:37:47Z","created_by":"Claude","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}