Owner: "maybe plot_ratio is having unintended consequences, Alexander simply
says that all levels should have accessible outside space, he doesn't say how
much."
Both halves are right, and the codebase had the two rules the wrong way round.
ratio_outside is a gaussian on the outdoor FRACTION, applied as a whole-building
multiplier, and its declared targets have no stated basis and contradict each
other: health-centre targets 0.06 and sits at 0.096-0.129, so it is penalised
x0.39-x0.77 for having too MUCH; programme-house targets 0.30 and sits at
0.098-0.293, penalised x0.40-x0.999 for having too LITTLE. Penalties as large as
the ratio_circulation ones 39.24 removed, pulling two programmes in opposite
directions on the same quantity.
force_roof_garden already implements the rule Alexander actually states -- per
level, no outdoor space at all is a hard fail, no quantity attached. It has
existed all along and was switched OFF in every corpus config.
Near-miss worth recording: measuring first, I found zero "no outside space"
fails across the twelve baseline runs and briefly read that as the requirement
being met everywhere. It meant the check never ran. Same shape as 39.20's parity
tests -- no failures from a test that is not executing looks exactly like no
failures from a test that passes, and the tell was again the config, not the
code.
Enabled, it bites on 4 of 25 baseline levels: maple s0 level 1, and
programme-house level 0 in all three seeds. A house with no outdoor space on its
own ground floor is a fair criticism of the layout, and exactly what a
building-level fraction cannot catch, since 22% outdoor concentrated on one
storey satisfies it perfectly.
The upper side ratio_outside used to provide is covered in a better currency by
the minimum-internal-area factor (internal area >= 1.2x the programme's declared
room area), which is live -- binding on harbor s0 x0.920 and programme-house s0
x0.787.
This is the one change in 39.22-39.25 whose risk is NOT measured: nothing here
proves the outdoor fraction will not drift up once the search is free to raise
it, and outdoor space is profitable (1.64 return against a room's 0.66). The
re-baseline (bk9) is what shows it, and 39.25 asks for the fraction to be
recorded there.
4 hard fails added, none removed. 426 passed.
Refs homemaker-py-hxi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
Owner: "I think a corridor could be worth a sixth of a room, this is ok. maybe
we should dump the ratio_circulation altogether if there is already a pressure
in circulation caused by the cost benefit ratio per msq. this is the kind of
thing we want to root out of the scoring model: anything that is double
counting, or using a gaussian where a linear ramp is appropriate, etc."
value_circulation = 50 stands; hxi's rate question is closed.
The duplication argument is stronger than it first looks. score = value/cost is
already a ratio, so the per-m2 economics (50 against a build cost of 200) is not
merely an absolute pressure -- adding corridor moves value/cost by an amount
that depends on how much of the building is already corridor. It is ALREADY
proportional. ratio_circulation said the same thing again as a whole-building
multiplier, on a curve where twice the corridor is far more than twice as bad.
Correction to my own first measurement: I overrode ratio_circulation and got
scores going DOWN when a <=1 multiplier was removed, which is impossible. All
four corpus programmes DECLARE ratio_circulation, so the CONF_DEFAULTS value I
had changed was never in play and the two arms differed only in sigma. Same
trap as value_supported in 39.19.
Against the keep-it case, recorded because it is the one real argument: three of
four declare a POSITIVE target (harbor/maple 0.08, health-centre 0.10), making
the term formally two-sided rather than "less is better". It does not survive
the numbers -- the lower side is worth at most 13.3% on the large programmes
against 99% on the upper side, and "a building needs some circulation" is
enforced structurally by access and connectivity, which no amount of value can
buy off.
Disabled in CONF_DEFAULTS and the four corpus configs, each with the reason
inline and a note that a [target, sigma] pair re-enables it. Fail sets
unchanged; it was always a value multiplier. Scores +42% to +7712%.
39.24 also sweeps every remaining term against the owner's two tests. Verdicts:
perpendicular, proportion, width, crinkliness, access, size's lower side,
ratio_outside, staircase volume and the count/limit fails are all sound.
Filed as homemaker-py-dpt: size's UPPER side (cost already charges area; 82% of
size fails are over-target), the minimum-internal-area factor (a third
statement of "build the rooms"), the 0.5**n_fails curve (a ruling, not a
measurement), and two dead paths -- ratio_public/private_outside, which no
config declares, and the daylight factor pinned to 1.0 since the descope.
Also updated a test I added last turn which asserted ratio_circulation was the
second charge; it now pins that the linear ramp is the ONLY one.
419 passed.
Closes homemaker-py-hxi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
Owner's ruling. Measured over the twelve baseline layouts as realised value per
m2 (rate x quality, not the rate alone):
as shipped before this room 67.0 terrace 294.6 violates, 4.39x
value_supported=100 only room 67.0 terrace 98.2 violates, 1.46x
geometric mean only room 132.9 terrace 296.8 violates, 2.23x
both room 132.9 terrace 98.9 satisfies
The 4.4x is roughly 2.2x aggregation and 2.0x rate, so neither half alone is
enough. That is why 39.18's geometric-mean aggregation moves from default-OFF
to default-ON here rather than waiting on its own A/B: it is not an optional
improvement, it is half of a ruling.
value_supported 300 -> 100, and set to value_outside rather than to a number
that makes the inequality come out -- back-solving from the corpus's measured
mean room quality would rot the moment either changed. Outdoor space is worth
the same to an occupant whatever level it sits on; the real difference between
a ground garden and a roof terrace is what it takes to BUILD, and cost already
says that (outside 10.0 vs outside_supported 110.0). Value describes worth,
cost describes structure, and the level belongs in the second.
Changed in CONF_DEFAULTS and the four corpus patterns.config files, which all
declared 300.0 explicitly. NOT changed in harbor-house-l0 (a shape-curve test
fixture) or y51-sweep-* (historical fixtures that exist to reproduce past
measurements) -- repricing those would destroy what they are for.
Neither change can move a fail, structurally rather than luckily: value rates
never enter fail emission, and evaluate_leaf emits each fail from its factor
before anything is combined. Verified corpus-wide: identical fail sets, scores
+11% to +169% (and -5% once, on a layout that is mostly terrace).
tests/test_terrace_value_ruling.py pins the ruling as an invariant of the
objective, and asserts that reverting the aggregation breaks it again, so
neither half can be quietly dropped.
The 500k cold-start baseline (39.12) is superseded -- this changes what "good"
means. The layouts stay valid and their fail counts are unchanged, but a fresh
corpus run is needed before any new number is compared with them.
Still untouched: circulation returns 0.07 per unit cost against a room's 0.66,
by far the worst thing a building can contain. That is homemaker-py-hxi.
Refs homemaker-py-ecx.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
Ruling: corridors need daylight. Only rooms not occupied from day to day --
a cupboard, a store, a plant room -- do without it.
That overturns the classification 38.8 and 38.9 rested on. Those sections
exempted internal circulation, covered courtyards, WCs, laundries and
reception on my inference rather than any ruling. None are exempt: a
corridor is occupied all day every day, a waiting room likewise, a laundry
is a room people spend time in, a sterilisation room is a workplace.
Re-measured with the classification READ FROM THE CORPUS (a space is exempt
exactly when its own patterns.config declares crinkliness: none) instead of
inferred: 24 of 271 crinkliness fails (9%) were not defects -- not 136
(50%), not 61%. Overstated by about six times.
- uncrinkliness_circulation stays at [5/6, 1.1/3]. 38.10 called its
equality with the habitable target the purest case of an untuned value;
it is not a bug, it is the correct answer reached by default, since
corridors want daylight on the same terms as rooms.
- corpus declarations narrowed 18 -> 14. usage: utility was too coarse and
swept in Laundry Rooms and a Sterilisation Room. What remains is
storage, plant/mechanical, utilities closets and a records room.
- audit_crinkliness_truth.py rebuilt to read the classification from the
configs rather than infer it from usage:, so it cannot drift from the
ruling again.
- 38.8/38.9's inflated figures flagged in place, with 38.11 carrying the
correction.
What survives, none of it dependent on the classification: the 38.6
critique of the three modes; usage_daylight being mis-keyed; the 38.8 A/B
yardstick being wrong; and the per-space crinkliness mechanism itself.
ssz's opening measurement was right but its reading was wrong -- under this
ruling a buried leaf usually IS a defect, so scoring it zero is largely
correct. The narrowed remnant is that the score is FLAT rather than low, so
two equally-buried layouts cannot be ranked. Filed as homemaker-py-9gj; it
is a search-mechanics problem, not a calibration one.
Lint at parity (46); tests 369 passed, 2 pre-existing failures (ut5's stale
baseline and 1ue), both failing identically before this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
Owner's ruling, and it corrects the design not just the classification: the
daylight requirement is already defined in the crinkliness. The gaussian's
compact side IS "too little exposed wall per unit floor"; its exposed side
is envelope cost. 38.9's proposed daylight: axis was redundant, and keying
it off usage: was worse than redundant.
What was actually missing: crinkliness is the only leaf quality factor with
no per-space target. size, width and proportion are all declared by the
space; crinkliness was one global number for every room in every building.
crinkliness: none -> no minimum-exposure requirement, may be buried
crinkliness: [t, s] -> this space's own target
key absent -> the global uncrinkliness target, as today
`none` clips the factor on the compact side, it does not switch it off:
over-exposure is still penalised, because a crinkly leaf costs envelope
whatever it holds. A store may be buried; a store may not be a starfish.
The mechanism is backward compatible -- an absent key resolves to the
global target, so shipping it changes no score. Behaviour changes only
where a config declares something, which keeps the objective change
visible per programme in config rather than hidden in a default.
Owner's classification: everything a person occupies wants a window, WCs
and reception/waiting/foyer included; only stores, plant, records and
laundry do not. migrate_crinkliness_key.py declared crinkliness: none on 18
corpus spaces. Crinkliness fails 271 -> 243, of which not-defects 136 (50%)
-> 108 (44%); the 28 that went are exactly the utility fails.
usage_daylight and needs_daylight are removed as mis-keyed, and
DAYLIGHT_USAGES with them -- a vocabulary value should exist only where the
engine treats it differently. The historical crinkliness_mode modes stay,
default off, so 38.6/38.8 remain reproducible.
uncrinkliness_circulation is now settable to none like any space, but its
default is left unchanged pending a ruling: corridors were not among the
groups ruled on and are 63% of the remaining phantom fails.
Lint at parity (46); tests 364 passed, same 7 pre-existing fixture failures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
Closes the second namespace sharing a first character with programme codes: the
usage prefixes b/t/l/k, under which a room silently inherited another room's
connectivity rules from its spelling.
usage is a plain, MANDATORY attribute of the space definition -- not a lookup
table. An interim design proposed a top-level usage_classes: table binding
author-coined names to behaviour; withdrawn, because an indirect name->behaviour
mapping living apart from the thing it describes is exactly the shape of the
prefix rule §39 exists to remove, it would be the only such table in a schema
where every other space property is a plain attribute, and the need it served
was already met -- "building specific" is about what a room is CALLED, and
name: is already free text.
Rule that settles it: a usage value exists iff the engine treats it differently
somewhere. Config selects among behaviours; it cannot invent them.
- programme.USAGES (living/kitchen/bedroom/toilet/utility/none) plus the
behaviour groupings PRIVATE_USAGES / PRIVATE_STRIPS / TOILET_STRIPS /
SOCIABLE_USAGES. Missing or unknown usage is a load error naming the code,
from BOTH parse paths.
- Code-level, never leaf-level: usage_of(leaf.type) is looked up fresh, so a
retype changes the class automatically. 51 sites assign leaf.type, and
share/share_type plus the r5a resurrection are the precedent for why
leaf-level attributes rot.
- graph.has_circulation takes the usage map and trims on declared class;
fitness.access and the public-access check likewise. fitness._t0 is DELETED --
no first-character type test remains anywhere in the codebase.
- utility is distinct from bedroom (same access requirements today) because it
is a different use and gives derive_interchange_classes an axis to relax on.
- A toilet now keeps its edge to a terminal room -- the Brand adjacency, which
the old b-before-t loop ordering severed.
- All 107 corpus entries migrated by experiments/migrate_usage_key.py, comments
and layout preserved.
MEASURED -- the connectivity model was ~4x too permissive. `none` is not
neutral: nothing is trimmed, so the graph may route THROUGH the room, and 34 of
52 codes had no class (Dental Surgery, Records Room, Utilities Closet all served
as corridors). Edges trimmed, prefix-inferred vs declared, 3 seeds each:
harbor-house 18 (9%) -> 79 (39%) inaccessible fails 0 -> 4
health-centre 12 (8%) -> 59 (40%) inaccessible fails 2 -> 3
maple-court 53 (17%) -> 123 (39%) inaccessible fails 1 -> 5
Re-baseline (seed 1, 20k, harbor): 58 fails (15h/43s) -> 61 (16h/45s), now
reporting 1-inaccessible-usable-space x2 plus level 0 and level 1 not connected.
The count rose because the objective got honest -- those failures were always
true of the layout and the old model could not see them. Every harbor number
before this was measured against a graph crediting routes through store
cupboards.
Sharpens §38.2: the objective pays x60-85 to delete circulation, and until now
the deleted corridors were not missed because storage stood in for them. With
that substitution gone, homemaker-py-2v1 is the remaining half -- and now
measurable, because the fails it should prevent actually fire.
350 passed (+5 new), same 7 pre-existing fixture failures, lint unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MJ84Feep79Hhm3E4zZJmnB
Builds path (b) from §26 -- a leaf permanently serving two DIFFERENT
compatible programme codes at once, extending leaf-sharing's same-code
multiplicity mechanism to different-but-compatible codes. Architect-declared
`co_locate` pairs (validated against interchangeable()'s S1-S4 bounds, no
transitive closure so the b3v chain problem can't recur), threaded through
graph.py's checks via a new leaf_codes() resolver and fitness.py's quality
terms (additive size, stricter-of-both width/proportion). Construction-time
only, gated behind `multi_use` (default OFF, bit-identical when off).
End-to-end A/B (20k evals x 3 seeds x 2 programmes) came back net negative:
harbor-house -4.0% but health-centre +24.5% worse (3/3 seeds), because
fusing different codes' shape targets via stricter-of-both can impose a
tighter joint constraint than either code needed alone, which the tightly-
packed health-centre programme can't absorb. Written up as DESIGN.md §33;
multi_use stays default OFF, no default-flip recommended.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R8agJBT2ZpmF3ErW7wi2wY
A small primary-care health centre: 19 distinct, individually-sized room
codes at n=20 room instances (only one deliberate duplication: two public
WCs), filling the gap between programme-house's duplicated-count sweep
sizes and harbor-house's out-of-range 37 real-diversity instances. Widths
are deliberately tiered (>1.3x gaps at three boundaries) so the auto-derived
interchange relation resolves to three bounded utility/office/clinical
classes instead of one whole-building chain, which a first pass produced.
experiments/run_9yx_sweep.sh repeats xyu's ruin_recreate ON/OFF protocol
(budget 3000, 4 workers, N=15 seeds) against this programme.