PixelPunk journal, chapter 09 Evidence frozen 24 September 2026. Historical records; not instructions. SOURCE: docs/visual-handoff.md, snapshot lines 1114 to 2111 **Feel B4c Nature that notices, part c: ivy, moss and the foliage instance budget (24 September 2026):** Opus 5.5 implementer. Ivy now seeds at wall feet that have climbable wall above them. It spreads through a 2D spatial hash of 0.4 m wall columns toward a seeded target. It stays off windows, doors, holes, signs and wall parts, and it is cut inside arches and gateways. Moss cushions stand at paved wall feet where the shader's damp-and-shelter moss term is strongest. Both are derived at runtime and drawn as MultiMesh instances. They are fully grown on load, and nothing is saved (version 2 and every document are unchanged). The manual `vine` and `ivy_drape` parts stay; they block ivy the way signs do. Files: `scripts/ivy.gd` (new, `class_name Ivy`), `scripts/weathering.gd` (`Weathering.moss`, `MOSS_SHELTER`), `scripts/architecture.gd` (decorations `ivy` and `moss`, `IVY_GREEN`, `MOSS_GREEN`), `scripts/world_view.gd` (`_refresh_ivy`, `_sync_ivy`, `tick_ivy`, `foliage_counts`), `scripts/main.gd` (the performance report's `foliage`), `tests/test_ivy.gd` (new), `tools/test.sh`, `tools/Test.ps1`. Snapshot: `build/backup-feel/09-B4/` (untouched). This part's "before" is the post-part-b tree, saved as `build/backup-feel/09-B4-c-pre/` before the first edit. Working backups are in `build/backup-feel/09-B4-c-work/`. - **Columns** (`Ivy.columns`). Every face of a building, tower and freehand wall is cut into `CELL` 0.4 m columns. Each column's rows count up from its own foot; row 0 is a small leaf at 0.05 m. - Each column carries forbidden rectangles (u, y) from the following: openings, drawn windows, door bays and oriels; boxes of `BLOCKERS` parts (signs, awnings, balconies, lanterns, planters, vine, ivy_drape, ladder, pipes); stairs and bridges (`COVERS`); and faces covered by another body. - `fit` slides a leaf up to half a cell along its face to clear a rectangle. It returns NaN where no slide works. - A jettied storey limits ivy to the ground storey. A block lifted more than 0.05 m, or stacked, is not seedable. - Buildings and towers are memoised per owner (own params, kids, and the terrain and heights within `KEY_REACH` 2.95 m of the bounds). What blocks their columns is memoised on that key plus every blocker and body within 1 m (`_near_key`). Freehand walls are recomputed on every pass. - **Growth** (`Ivy.grow`). - Seeds grow at row 0 when row 1 is also free. Scoring reads meadow, planters or base planting within 1.4 m, water within 1.6 m, and the style. - Chance is `SEED_CHANCE` 0.08. Seeds keep `SEED_GAP` 1.8 m apart. Everything is seeded by the owner seed through a murmur3 finaliser (`_mix`). - Each seed floods with a Dijkstra heap (cost up 0.7, side 1.0, down 1.7, seeded jitter) up to a target from `TARGET` 20 to 70. It takes at most `FOOT_MAX` 5 foot cells. - A leaf's fullness comes from its taken neighbours, and its hue is damp near the foot or water. - **Moss** (`_moss`). `Weathering.moss(h, n, drop) = damp * (1 - 0.75 * shelter)`, the literal term in the masonry and plaster shaders. It is broken by the shader's noise band (smoothstep 0.38 to 0.75), in `MOSS_PATCH` 3-column patches. - A cushion grows where the result reaches `MOSS_MIN` 0.85. It does not grow on water, on meadow, or in a door zone. - Cushions stand `MOSS_OUT` 0.24 m out so they clear shop sills. - **Draw** (`WorldView._sync_ivy`). There is one MultiMesh per kind and variant: at most 6 for ivy (3 fullness × 2 hue, `SHAPES` 1) and 1 for moss. None of them cast shadows. - A newly grown patch appears leaf by leaf over `GROW_SECONDS` 14 s (moss after 0.6 s). - Loaded scenes, settled scenes, and scenes with the switch off appear at once. Tests: - Test-first log: `artifacts/feel/09-B4/c/c-testfirst-test_ivy.log(.out)`, 4 checks with 4 failures (every gate missing). - `test_ivy`: 146 checks, 0 failures (`c/c-run20-test_ivy.log.out`; 148 after the review fix). It covers the moss formula against both shader bodies, and that seeds are deterministic and follow the owner's seed. It checks spread, openings on five styles, the arch and the gateway, target size, moss rules (land, doors, damp), and that the manual parts stay. It checks the budget cap, world drawing and growth ticks, and that the audit scenes stay inside the budget. - `memo_checks` (a warm pass after moving each Harbour body or part equals a cold pass) was written after the memo, not before. - Full set, foreground, one Godot at a time (`c/full2/summary.txt`, via `c/full.sh`): 25 suites, 10,333 checks, 0 failures. `c/full2/verify.log` has `PIXELPUNK_INTEGRATION []`. Changed expectations: none in existing suites. Scenes change by design: - Harbour gains 395 ivy leaves; - garden 313, chemistry 608; - lane 28 leaves and 29 cushions; - dense 774 cushions. Captures: `artifacts/feel-journey/09-B4/pkg/`, taken with `c/cap.sh`. The scene is `c/c-ivy.json` (`c/make_scene.gd`). `diff.png` in each after folder marks the changed pixels in red (`c/probe/diff.gd`). I looked at each one listed. - `c-{before,after}-wall-day`, `c-after-wall-seeds` (seed_1): the tall wall is covered either side of the gateway, with none inside the arch. The low wall has none. Seed 1 grows different patches. - `c-{before,after}-ivy-{day,night}`: the whole scene. The ivy is on the tall wall and the tower base, and moss sits at the yard houses' feet. The five styled houses and the canal house get no ivy; see Not done. - `c-{before,after}-yard-day`, `c-{before,after}-mosseast-day`: moss cushions at the paved feet of both yard houses, one of them clear of a shop sill. - `c-{before,after}-harbour-{day,night}`: ivy on the garden wall, the tower base, one canal-house side and the market's foot. The rest of the diff is water shimmer. - `c-{before,after}-dense-day`: no change is visible. Dense's cushions are out of this view, and the diff is canopy and water noise. - `c-after-ivy-grey`: the ivy reads as dark texture on the wall and does not compete with the houses. Measurements, all in one sitting, alternating three runs of each. "Before" is the post-part-b tree. "Off" is these scripts with `Ivy.BUDGET` 0 (scratchpad `offtree-09B4c`), which isolates the cost of drawing. Run with `c/dense3.sh`, using `EXTRA=--disable-vsync` or not, and `SCENE=harbour`. | Scene | Vsync | Before (ms) | Off (ms) | After (ms) | Draw calls before → after | |---|---|---|---|---|---| | Dense | Off | 12.97 | 12.98 | 13.37 | 2,123 → 2,124 | | Dense | On | 13.65 | 12.95 | 14.14 | 2,123 → 2,124 | | Harbour | Off | 7.43 | 7.46 | 7.75 | 1,001 → 1,007 | | Harbour | On | 7.42 | 7.43 | 7.70 | 1,001 → 1,007 | - **Dense** moves by +0.4 ms, which is beyond noise. The off tree matches before, so the whole change comes from drawing 774 cushions. - On the way there: cushion shadows came off, cushions went from 4 lobes to 3, and `MOSS_MIN` went from 0.6 to 0.85 (1,151 cushions became 774). Ivy shadows and three shapes cost Harbour +0.81 ms (`c/superseded/`). - **Budget** (`Ivy.BUDGET` 2,000 for ivy and moss; `FOLIAGE_BUDGET` 3,000 for all foliage; `MOSS_SHARE` 0.4). The rate is 0.5 to 0.75 µs per instance, so 2,000 is about 1.5 ms at worst. - The audit scenes peak at 608 ivy and 898 foliage (chemistry), and nothing is trimmed. - The worst-case figure is extrapolated, not measured at the cap. - **Release** (`c/probe/release.sh`, median of 9, 0.5 m house move): Harbour went from 62.9 ms to 87.1 ms and dense from 300.6 ms to 344.2 ms. - `c/probe/release_profile2.log.out`: Harbour spends 10 ms on columns (walls, the moved house, keys) and 12 ms on growth. Dense spends 22 ms on columns and 13 ms on moss. - **Cold pass** (`B4C_COST`): Harbour 40 ms, dense 163 ms. The warm pass is under 0.2 ms. Review (Opus 5.5 reviewer, one must-fix): - Found: the per-owner memo key read terrain only 1.5 m past the bounds, but a column reads water up to 2.45 m out (`_water_gap` reach 2 round a point 0.45 m out). On a 7 m house (faces at x = ±3.5), water painted 1.5 to 2 m west of a face left the memo stale: warm pass 0 seeds, cold pass (reload) 3 seeds and 119 leaves (`review/c-memo-terrain-solid.result.txt`). The same gap was on the north face (z = -5). - Fixed: `_owner_key` now reads `KEY_REACH` = 2.45 + 0.5 m past the bounds (`scripts/ivy.gd`). `memo_checks` gained a terrain case: a paved 7x5 medieval house, a one-cell water strip painted in turn at x = ±5..8 and z = ±4..6, warm pass compared with cold (seeds, leaves, moss). - Test first, `c/c-memofix-testfirst-test_ivy.log.out`: 148 checks, 1 failure (strips x = -6 and z = -5 stale). After, `c/c-memofix-after-test_ivy.log.out`: 148 checks, 0 failures. The reviewer's probe rerun, `c/c-memofix-probe.log.out`: every strip SAME, x = -6 now 3 seeds and 119 leaves both ways. - Full set, foreground, one Godot at a time (`c/fix-full/summary.txt`, via `c/full.sh`): 25 suites, 10,335 checks, 0 failures; `c/fix-full/verify.log` has `PIXELPUNK_INTEGRATION []`. - Cold output is unchanged (the `B4C_COST` leaf, moss and seed counts match run 20), so no capture was retaken; the finding names none. Release time was not re-measured; the key now hashes about 140 cells for a 7x5 house instead of about 80. Pre-fix copies: `build/backup-feel/09-B4-c-memofix/`. Not done: - Houses in this kit rarely take ivy. Canal and styled fronts leave no wall wider than a leaf (about 0.66 m) between openings, and their ground floors are mostly glass. In `c-ivy.json` every house got 0 leaves and the tower 18. Ivy lives on freehand walls, towers and blank or wide piers. - A release costs +24 ms on Harbour. Growth and moss are not memoised per owner, and freehand walls are recomputed every pass. - Straight `wall` connections get no ivy or moss, only freehand walls. - The ivy still reads as a grid of leaves on a flat wall (one leaf per 0.4 m cell). - Dense's 774 cushions cost 0.4 ms and none is in the standard dense view. - Growth animation is tested headless (`tick_ivy`) but not in `main.gd --verify`, and there is no filmstrip. - Ivy and moss cannot be picked or hidden one by one. Platforms: Mac run; Windows not run. **Feel B4b Nature that notices, part b: base planting, the flower brush and water edges (24 September 2026):** Opus 5.5 implementer, runbook amendment 16. Meadow now plants itself along building, tower and freehand-wall bases and along meadow banks, by style, and a lifted block loses its planting. A new Landscape paint, Flowers, plants meadow cells; a second stroke thickens them into bushes. It is stored as an optional density per cell. Reeds grow on soft shores and by trees near water, and lily pads float near them. Everything except the density is derived. Files: `scripts/composition.gd`, `scripts/scene_state.gd`, `scripts/world_view.gd`, `scripts/architecture.gd`, `scripts/main.gd`, `scripts/editor_ui.gd`, `scripts/build_audio.gd`, `tests/test_nature.gd`, `tests/test_ui.gd`, `tests/test_build_anim.gd`, `tests/test_clutter.gd`. Snapshot: `build/backup-feel/09-B4/` (untouched). This part's "before" is the post-part-a tree, saved as `build/backup-feel/09-B4-b-pre/` before the first edit, so the comparison shows part b alone. - **Base planting** (`Composition.base_planting`). Spots every 0.9 m round each ground building or tower with a lift of 0.05 m or less, both sides of each freehand wall, and one chance per meadow side onto water (the "quay base": meadow meets water with a plain cut, and stone quays are paving). Only on meadow. - Placement goes through B0. A spot is dropped where the door, opening, stair-foot or landing keep-clear reaches `BASE_KEEP` 0.35, or where it lands on a footprint, a wall, stair or bridge band, a placed prop, door clutter or another piece. - `BASE_STYLES` sets density and species. Canal 0.6: wildflowers, weeds, shrub. Victorian 0.72: flower beds, wildflowers, shrub. Medieval 0.85: weeds, shrub. Japanese 0.35: shrub, weeds. Modern 0.3: weeds only. Block corners get +0.2. - Spots are seeded by the owner's id and seed and their place round it, so lowering a lifted block brings the same planting back, and `--seeds` redraws it. - **Flower brush** (`DioramaState.FLOWER_BRUSH`, tool `flowers`, card "Flowers"). It writes `flowers: {"x,z": 1 or 2}`, and the key appears only when the map is non-empty, so documents without flowers save as before. - A build before B4b ignores the key, and `validate_document` checks it. - Only meadow takes flowers. A stroke thickens only cells that were flowered when it began. - A path hides them and painting the path away restores them. The meadow brush over meadow mows them. Carving water clears them. - Drawn (`brush_planting`): density 1 is wildflowers and sometimes weeds; density 2 is a shrub, wildflowers and weeds. Nothing is drawn under a footprint or a wall. It is recomputed on every sync, so a dab shows at once. - **Water edges** (`water_planting`). Reeds grow along sand banks and along sandy shores where a path runs into the water, and along meadow or earth banks within `TREE_REEDS` 3.5 m of a tree or blossom (owned by the tree). Lily pads grow on water cells with two or more open-water neighbours within `LILY_REACH` 2.5 m of a reed clump (chance 0.3). Quays stay clean, and nothing grows under a footprint, a band or within 1.6 m of a boat. - New derived kinds `weeds`, `wildflowers`, `reeds`, `lily_pad` (`Architecture.decoration`; not placeable, not in `KINDS`). Every piece is drawn as a grown variant: fullness 1, damp hue within 2.5 m of water, one of 3 shapes by seed. The size is 0.6 to 1.35. - **WorldView** (`_refresh_planting`). Base and water pieces are computed on a full sync. On a drag step or a dab they are kept, minus those of an owner that changed. `planting_hash` is part of the decoration signature. Tests: - Test-first: `artifacts/feel/09-B4/b/b-testfirst-test_nature.log(.out)`, 127 checks with 6 failures (every part-b gate missing). - `test_nature`: 259 checks, 0 failures. The new sections are base, brush, reeds, meshes, world and scenes. They cover lift clears and undo restores, style density, the door kept clear, both wall sides, the brush stroke rule, save round-trip byte for byte, validation, EditHistory undo and redo, path hide and restore, the mow, reeds by the tree, and a clean quay canal. - Full set, foreground, one Godot at a time (`b/full2/summary.txt`, run with `b/full.sh`): 24 suites, 10,187 checks, 0 failures. `b/full2/verify.log` has `PIXELPUNK_INTEGRATION []`. - First-run failures, fixed by the expectation changes below: - `b/full/test_composition.first-fail.log.out`: a scene_state comment named `Composition.`; - `b/full/test_clutter.first-fail.log.out`; - test_build_anim, 40 and 39 instances (its first log was overwritten by the rerun). Changed expectations: - `test_ui`: Landscape cards 10 became 11 (Flowers). - `test_build_anim` A2b: decoration instances now subtract `planting_synths.size()`. The props are still 3 and then 2; the fixture's meadow bank adds 37 planting instances. - `test_clutter` `drawn_clutter`: now leaves out planting synths. Before, the "every other opening keeps its clutter" drag check counted the dragged house's base planting. - Existing scenes change by design: Harbour gains 186 pieces (27 bank, 53 base, 15 wall, 33 reeds, 58 lilies), garden 157, chemistry 287, dense 0. Captures: `artifacts/feel-journey/09-B4/pkg/`, taken with `b/cap.sh`. The scene is `b/b-planting.json` (`b/make_scene.gd`). I looked at each one listed. - `b-{before,after}-planting-{day,night}`: base planting round the five styled houses and along the wall, bank flowers round the island, two brush patches (flowers; bushes), and reeds and lilies in the pond. The lifted house has none. Draw calls 348 to 464. - `b-{before,after}-row-day`: victorian pink beds, medieval and japanese shrubs, sparse modern weeds. - `b-{before,after}-pond-day`: reeds on the sand bank, at the road's shore and by the tree. No reeds on the stone quay or the plain north bank. - `b-after-flowers-day`, `b-after-stilts-day`: the brush patches. The lifted house has bare ground and the canal house beside it has shrubs. - `b-after-planting-grey`: the planting reads as small texture and does not compete with the houses. - `b-after-planting-seeds` (seed_1, seed_2 looked at): base planting changes with the seed. The bank, brush and water pieces are ground-seeded and do not. - `b-{before,after}-harbour-{day,night}`, `b-after-harbourfront-day`: reeds and lily pads at the sandy shore by the fish market and by the boat, and flowers along bases and banks. Draw calls 869 to 973. - `b-{before,after}-garden-day`: bank flowers all round and base planting along the court. Measurements (same sitting, alternating, before = post-part-a tree): - **Dense**, `b/dense.sh`, vsync on: mean frame median 13.61 ms before, 13.66 after. p95 is 13.89 on both. Draw calls 2,123 on both (0 planting pieces: dense is paving). - **Harbour**, `SCENE=harbour b/dense.sh`: mean frame median 6.90 to 7.33 ms, p95 6.31 to 8.18, draw calls 893 to 1,001. About 30 new MultiMesh keys (6 kinds, 2 hues, 3 shapes). This is the cost part c's budget has to set. - **Release**, `b/probe/release-{before,after}{1-3}.log.out`, sync after a 0.5 m house move, median of 9: Harbour 50.40 to 61.47 ms, dense 293.31 to 296.61 ms. The planting pass cold (`B4B_COST` in `b/full2/test_nature.log.out`): Harbour 9.5 ms, garden 9.0, chemistry 16.9, dense 3.5. The shared clutter-scene pass is timed separately. Review: . Not done: - the planting pass has no per-owner memo, so one house moved re-plants the scene (about 9.5 ms on Harbour); - straight `wall` connections get no base planting, only freehand walls; - planting pieces cannot be picked, rerolled or hidden one by one; - the flower tool is not in `main.gd --verify`. WorldView and the UI card are tested headless; - no instance or draw-call budget (part c). Harbour's +108 draw calls are the number to budget; - the palette card of Flowers is an icon only; - ivy and moss are part c. Platforms: Mac run; Windows not run. **Feel B4a Nature that notices, part a: openness, no grass under buildings, context-tied variants (24 September 2026):** Opus 5.5 implementer, runbook amendment 16. Plants now read their context: trees and blossoms lean and grow toward open sky, meadow tufts are not drawn under ground buildings, and every tree, blossom, shrub and flower patch is drawn as one of 36 mesh variants: the seed picks one of six shapes and the context picks fullness and hue, with a size from the same context (after the review fix below; the first version had twelve). All of it is derived and none of it stored; save version 2 and every document are unchanged. Files: `scripts/composition.gd`, `scripts/architecture.gd`, `scripts/world_view.gd`, `tests/test_nature.gd` (new), `tools/test.sh`, `tools/Test.ps1`. Snapshot: `build/backup-feel/09-B4/` (untouched). - **Openness proxy** (`Composition.sky_map`, `foliage_context`, a new section of the B0 layer). A 1 m skyline of the canvas holds the ground, building roof tops and freehand wall tops, plus the land mask. Other trees count as crown discs (`foliage_crowns`). Per plant: 16 ways, marched to 13 m from 1.6 m above a tree's foot (0.4 m for a shrub). Openness is the mean of 1 - sin(horizon elevation). Shade is the same read within 3.5 m. Wetness falls from 1 at water to 0 at 6 m, and the canvas edge counts as water. - **Lean.** The sky each way sees, summed as a vector, times `LEAN_GAIN` 0.3, capped at `LEAN_MAX` 0.16 m per metre of height. It is drawn as a shear, so the foot stays put (`foliage_transform`). Trees and blossoms also get a seeded yaw; shrubs and patches do not lean or turn. Probe values (`artifacts/feel/09-B4/a/probe/contexts.log.out`): - alone: openness 1.000, lean 0; - wall 1.5 m away: openness 0.585, lean 0.160 (at the cap); - wall 4.5 m away: openness 0.860, lean 0.080; - in the lane: openness 0.169, no sideways lean; - pair 2.4 m apart: lean 0.093 each, away from each other. - **Variants** (as fixed after review): variant = (full*2 + hue)*6 + shape, where shape = `Composition.foliage_shape(seed)`, a hash of the seed mod `FOLIAGE_SHAPES` 6: - full is 0, 1 or 2, from vigour = 0.55 openness + 0.35 wet + 0.10 (1 - shade); - hue is 0 (damp: wet or shade 0.35 or more) or 1 (dry); - the size is 0.86 to 1.12, from vigour. `Architecture.decoration` draws a variant from an optional `params.grow` that exists only on WorldView's mesh example. Fullness adds or removes one lobe and 6 shells on a tree (4 on a shrub, 8 flowers on a patch). Hue lerps toward blue-green or olive. The mesh seed is 128 + shape (`Architecture.foliage_variant_seed`), so shapes 0 to 3 use the pre-stage mesh seeds and one seed grows one shape in every context. Without `grow` the mesh is the old one. Deco keys are now `__g<0..35>` instead of `seed%4`, built on demand for the variants in use. - **Tufts.** `Architecture.meadow_tufts` and `meadow_fringe` skip blades inside a ground building's footprint (`Composition.under_footprint`). A chunk's fingerprint carries the tufts hidden in it (`hidden_tufts`), so only a house moved over tufts rebuilds ground. - **WorldView** (`_refresh_foliage`) computes contexts on a full sync only. A drag step or a stroke dab keeps them; a moved or new plant reads the last full sync's sky map. `foliage_hash` is part of `_deco_signature`. Clutter's shrubs and patches get contexts too (this path has no test). `foliage_all` memoises each plant on the skyline, land and crowns in its reach. Tests: - Test-first: `artifacts/feel/09-B4/a/a-testfirst-test_nature.log(.out)`, 9 checks, 7 failures. Every section's API was missing; the old-look check passed. - `test_nature`: 112 checks, 0 failures. The old look is 48 no-context meshes byte-identical to fingerprints recorded on the pre-stage tree (`probe/old_look.gd`). Harbour and garden chunks without a footprint over tufts match `probe/old_chunks.gd`. - Full set, foreground, one Godot at a time (`a/full/summary.txt`): 24 suites, 10,040 checks, 0 failures. - `a/full/verify.log`: `PIXELPUNK_INTEGRATION []`. Changed expectations: none. No existing assertion changed. Captures: `artifacts/feel-journey/09-B4/pkg/`, taken with `a/cap.sh`. The scene is `a/a-nature.json` (`a/make_scene.gd`). "Before" is `tools/feel_snapshot.sh tree 09-B4`. I looked at each one listed. - `a-{before,after}-trees-day`: the tree by the tall block leans off it; the tree and blossom pair lean apart. The dry crowns turn olive and the blossom paler, and the lane tree is smaller. - `a-{before,after}-pond-day`: plants at the pond are darker blue-green. Those on dry open ground are olive and fuller. - `a-{before,after}-stilts-day`: the blades under the lifted house are gone. The blades outside it stay. - `a-{before,after}-nature-{day,night}`: draw calls 341 to 348. - `a-{before,after}-harbour-{day,night}` (camera `0.6,0.42,16,-2,3,2`): the blossoms are smaller and deeper pink, and the shaded courtyard shrub and bed are smaller and sparser. Draw calls 642 to 648. - `a-after-nature-grey`: dry plants read lighter than damp ones. - `a-after-nature-seeds` (seed_1, seed_2 looked at): the shapes change with the seed; the lean and hue do not. Measurements: - **Dense**, `a/dense.sh`, 2 sets of 3 alternating runs, vsync on: mean frame median 13.37 ms before and 13.68 after. The spreads are 12.83–13.64 and 13.32–13.79; p95 is 13.89 on both. Draw calls go from 2,087 to 2,078, because all 100 dense trees land in one variant. No per-frame code was added. Those runs came before the release-cost changes below, which change sync work only. - **Release**, `a/probe/release-{before1-3,after6,after7}.log.out`, WorldView.sync_scene after a 0.5 m house move, headless, median of 9: - Harbour 45.5 to 50.4 ms; - dense 273.6 to 292 ms. The foliage pass cold, with the per-plant memo: Harbour 5.5 ms, dense 24.0 ms (`a/full/test_nature.log.out`, B4A_COST lines). Review: . The review (Fable 5.1) had one must-fix finding: only one seed bit reached the mesh (variant = full*4 + hue*2 + seed%2), so one context gave a kind 2 shapes where the pre-stage tree had 4. The dense fixture's 100 trees drew as one variant (`B4A_COST dense ... variants=1`), its draw calls fell 2,087 to 2,078 as four tree MultiMeshes became one, and Harbour trees 84 to 92 used variants 3, 4, 5 and 9 only. **Review fix (24 September 2026, Opus 5.5).** The seed now picks one of six shapes and the context picks fullness and hue: `FOLIAGE_SHAPES` 6, `FOLIAGE_VARIANTS` 36, `foliage_shape`, `foliage_variant` and `foliage_parts` in `scripts/composition.gd`. The shape is a hash of the seed rather than seed % 6, because the dense seeds (23798 + 47514 k) are all 2 mod 6. WorldView decodes a key with `foliage_parts`. `Architecture.foliage_variant_seed` is 128 + shape. Files: `scripts/composition.gd`, `scripts/world_view.gd`, `scripts/architecture.gd`, `tests/test_nature.gd`; backup of the four before the fix in `build/backup-feel/09-B4-a-fix/`. - Test-first: `artifacts/feel/09-B4/a/fix/fix-testfirst-test_nature.log(.out)` stops on the missing `FOLIAGE_SHAPES` (script error) and fails "dense: its trees in one context still use at least 5 shapes (1)". - `test_nature`: 121 checks, 0 failures (`a/fix/test_nature.log.out`). New checks: 100 seeds in steps of 1, 47514 and 4 each reach all 6 shapes; every variant decodes back; the mesh seed is 128 + shape; dense uses at least 5 shapes. B4A_COST: Harbour variants 9 to 18, dense 1 to 6. - `tools/test.sh`, foreground (`a/fix/full-test.log`): 24 suites, 10,049 checks, 0 failures; `a/fix/verify.log` has `PIXELPUNK_INTEGRATION []`. - Changed expectations (`test_nature` only, written in this part): "Twelve mesh variants per foliage kind" became "6 shapes, 36 variants"; "full*4 + hue*2 + seed%2 in 0..11" became "(full*2 + hue)*6 + shape, decoding back"; "the twelve variant meshes are all different" became "the 36 variant meshes are all different". - Harbour trees 84 to 92 now use variants 11, 13, 15, 16, 17 and 28 (`a/fix/contexts.log.out`). - Captures, `pkg/a-fix-after-{nature-day,nature-night,nature-grey,nature-seeds,harbour-day,harbour-night}`, same scenes and cameras as `a-after-*`. I looked at nature-day, nature-grey, seeds 1 and 2, harbour-day and harbour-night. The crowns of the trees, the blossoms and the shrubs change shape against `a-after-*`. Lean, hue and size stay. Across the seed sweep the shapes change and the lean and hue do not. Draw calls: nature 348 to 371, Harbour 648 to 696 (pre-stage 642). - Dense, `a/fix/dense.sh`, 3 alternating runs in one sitting, vsync on: mean frame median 13.54 ms before (pre-stage tree) and 13.64 after. p95 is 13.89 on both. Draw calls go from 2,087 to 2,123: six tree shapes against the pre-stage two (`seed%4` on even seeds). - Release, `a/fix/release-after.log.out`: Harbour 50.36 ms and dense 293.08 ms, the same as `release-after7` (50.23, 293.09). Not done: - no placement scatter in this part (base planting is part b). "Through B0" here means the sky and footprint maps live in the B0 layer. B0's mass map is not used; - the selection box ignores lean and size, so a crown can reach about 0.6 m past it; - the trees-day, pond-day and stilts-day views were not re-captured after the review fix. Their cameras are not recorded in `a/cap.sh`; - the palette cards keep the old look; - no animation of re-leaning; - the plan's line references (`architecture.gd:1748`, `world_view.gd:627`) are stale; - the instance budget is part c. Platforms: Mac run; Windows not run. **Feel B3b Lived-in clutter, part b: links, chimneys, posts, nests and the styled-door fix (23 September 2026):** Opus 5.5 implementer, runbook amendment 16. What is new: - laundry lines between facing upper windows 2 to 6 m apart; - string lights between lanterns and wall lanterns, and bunting when a flag is at either end; - an L-key toggle per object (optional `links` param, undoable); - chimneys that avoid dormers, roof parts and each other, and shrink to a copper vent when there is no room; - a wall lantern set on the ground stands on a post; - a twig nest at the roof junctions against a neighbour's wall; - styled fronts get no door zone. All of it is derived and none of it is stored. It is computed on a full sync only; a drag step or a stroke dab keeps the last result and hides what the moving owner touches. Files: `scripts/composition.gd`, `scripts/architecture.gd`, `scripts/world_view.gd`, `scripts/scene_state.gd`, `scripts/adapt.gd`, `scripts/main.gd`, `tests/test_links.gd` (new), `tests/test_clutter.gd`, `tools/test.sh`, `tools/Test.ps1`. Diff against `build/backup-feel/08-B3b/` (snapshot, untouched). - **Door fix.** `Composition.drawn_door_x` is the canal front's centre ground bay, and only when that bay is open, has ground windows and has no cut hole. Victorian, medieval, japanese and modern fronts draw no door, so they get no door zone. Instead they get window zones at their low drawn ground windows (front side only) and one sill opening at the drawn ground window nearest `front_door_x`. The B0 lane anchor now tries every door site in turn (lanes only). - **Laundry.** Window clutter and laundry follow `drawn_windows()`. Lines pair facing fronts, then windows, one line per storey per pair, and stay clear of footprints. - **Lights.** Range 1.5 to 9 m, rise 3 m or less, at most 2 links per anchor, clear of buildings. Bulbs use the warm material and add no light emitters. - **Chimneys.** Sites are decided in id order and handed to the roofs through the state meta. They are carried in the cluster signatures. Tests: test-first `artifacts/feel/08-B3/b/b-testfirst-test_links.log(.out)`, 25 checks with 14 failures (functions missing, no post, `links` not validated). `b/full2/`: 23 suites, 9,928 checks, 0 failures, including `test_links` at 150. Windowed `--verify`: `b/full2/verify.log`, `PIXELPUNK_INTEGRATION []`. Changed expectations: - `test_clutter` `a2_sill_checks` counts only the upper window's own pieces. The new styled ground sill pieces are near it. - `main.gd` verify: the parent-less wall lantern's source moves from (2.43,3.48,4) to (2.21,4.9,4), because it now stands on a post. - Styled houses have no door zone (`test_composition` lane anchor, via the fallback above). - Part a's door clutter in front of styled fronts is gone. Captures: `artifacts/feel-journey/08-B3/pkg/`, taken with `b/cap.sh`. "Before" is `tools/feel_snapshot.sh tree 08-B3b`. I looked at each one listed. - `b-{before,after}-lane-{day,night}` (`b/b-lane.json`, camera `1.57,0.42,14,1,4,0`): three laundry lines, lights between the wall lanterns, bunting between the flags, and a post lantern. The bulbs glow at night. Part a's benches and bins by the styled fronts are gone, and there are pots on the sills. - `b-{before,after}-roofs-day`, `-roofs-vent-day`, `-roofs-nest-day` (`b/b-roofs.json`): the crowded blue roof's chimney becomes a copper vent beside the antenna. The other chimneys stand apart. There is a nest against the tall wall. - `b-{before,after}-showcase-day` (`a2/door-showcase.json`): the door clutter in front of the four styled fronts is gone, with pots on the ground sills. The canal café is unchanged. - `b-{before,after}-harbour-{day,night}` (camera `0.6,0.42,16,-2,3,2`): lights along the paths and bunting from the market flag to the tea house. Two door pieces by the styled houses are gone. - `b-after-lane-grey`: the laundry and bunting read as light shapes. - `b-after-lane-seeds` (4 seeds): the ground clutter varies; the links do not. Measurements: - **Dense** (`b/dense.sh`, 3 alternating runs): mean frame 13.20 ms before, 13.65 ms after. Draw calls 2,078 before, 2,087 after. The cause is known: the link mesh holds 180 laundry lines and 100 strings. - **Links cold**, from test_links: Harbour 7.9 ms, dense 51.5 ms, warm 0.06 ms or less. - **Drag harness**, 3 sets of 3 alternating runs (`b/drag{,2,3}-{before,after}`), step-total median before/after: - every step except terrain raise is within ±0.5 ms; - terrain raise: 112.2/115.6 (+3.4, over the 2 ms bound) in set 1, 114.4/116.1 (+1.7) in set 2 and 114.5/115.0 (+0.5) in set 3; - all 90 state digests are equal. The dab's own cost is equal on both trees, measured headless and windowed (`b/probe/dab_probe.gd`, `dab*-{before,after}*`), as is a raise-only harness (`probe/raise-*`). The +3.4 in set 1 is not explained. Releases are 4 to 6.5 ms slower, because links and chimneys recompute there. Review: . Not done: - the bulbs cast no light; - drawn-window zones only on the front side; - the canal door check ignores occlusion; - the fence door rule is unchanged; - no inspector field for `links`; - no animation when links appear; - links recompute on every release; - the links ignore the sweep seed. Platforms: Mac run; Windows not run. **Feel B3a2 Lived-in clutter, escalated fix of part a (23 September 2026):** The Opus recheck of the first fix pass left B3a NOT MET; this second fix pass (Opus 5.5, runbook amendment 16) dresses every door keep-clear zone, takes clutter off the drag path, makes the drawn clutter a function of the scene state, places hero sets as one unit, and puts sill pieces on the sill. Files: `scripts/composition.gd`, `scripts/world_view.gd`, `tests/test_clutter.gd` (diff against `build/backup-feel/08-B3a2/`, the tree the first fix pass left). Snapshot: `build/backup-feel/08-B3/` (untouched). Part b not started. - **Every door (recheck finding 1).** `Composition.clutter_all(state)` dresses every opening `clutter_openings()` lists: each ground building's front door, its path doors and arches, doors fused from low windows, tower doors, and one sill opening per window group. A front door and a door hole at the same place are one opening. Probe (`artifacts/feel/08-B3/a2/probe_doors.gd`): the first fix pass's piece strip (±1.6 m along the wall, 0.5 to 2.3 m out) was mostly inside the door's own hard band. Harbour left 10 of 17 door openings empty, even with the openings wired (`probe_doors-strip-before.log.out`). The strip now runs ±2.6 m along the facade, from 0.1 m out to half the lane width (0.8 to 1.8 m). Harbour now has 5 of 17 empty (`probe_doors.log.out`): water right at the door, or neighbouring doors' hard zones. Pieces now keep off water and stepping stones, footprints, wall and stair bands, props already standing, and pieces other openings placed. Drawn, per test: lane.json 9 of 9 door zones (18 pieces), Harbour 14 of 18 (21), dense 100 of 100 (104). The recheck had 1, 0 and 0. - **Off the drag path (finding 2).** Scene maps (keep-clear zones in their existing order, blocked cells, bucketed footprints, bands and props, the opening list) are built once per `hash([objects, terrain, heights])`. Each opening's pieces are cached on a key that hashes its context plus every zone, solid, terrain cell, prop and earlier piece its packing reads. WorldView (`_refresh_clutter`) computes only on a full sync. On `sync_live` and a stroke dab (`whole_next`) it keeps the last result, hides the pieces of any owner or window whose params or transform changed, and lets ground pieces follow the terrain. The release recomputes. - **Not stale (finding 3).** `clutter_hash`, a hash of the drawn clutter, is part of `_deco_signature`. - **Hero sets (finding 4).** `pack()` takes an optional rigid shape per item. `_place_hero` packs the set round its centroid, arrangement unchanged, inside `HERO_RECT` (±4.5 m, 0 to 3 m out), clear of hard keep-clear and solids. It needs a lane of at least 3.5 m and no terrace above. - **Sill (finding 5).** Sill pieces stand at `t.origin.y + base_of + y0` of their window group, inside the window's width. Only kinds with radius 0.3 m or less qualify, so in practice a watering can, or a bonsai on japanese houses; every garden pot is too wide. Tests (`artifacts/feel/08-B3/a2/`): test-first `a2-testfirst-test_clutter.log(.out)`, run against the first fix pass. It fails 19 of 3,189 checks: coverage 1/9, 0/18, 0/100; no clutter counters; water and style edits leave the drawn clutter and the decoration nodes unchanged; 0 of 200 hero sets; the sill piece is drawn on the ground. `a2-run1` found 3 failures. Two came from the narrow strip (Harbour 7 of 18, dense 42 of 100). The third was my own check expecting one opening per zone; it now matches each zone to an opening at its face. `a2-run2-test_clutter.log`: 3,279 checks, 0 failures. Hero rate: 38 of 200 overrides at a canal quay door (22 café corner, 16 boat repair), the same at a victorian one. 20 of 100 building seeds at a front door with no window object (HERO_CHANCE 0.18). Full suite, foreground, one Godot at a time (`a2/full/`): 22 suites, 9,778 checks, 0 failures. Windowed `--verify`: `verify.log` failed once with "Holding the raise brush did not build a hill: 2.05" (floor 2.2, a frame-count hold). `verify2.log` and `verify3.log` both gave `PIXELPUNK_INTEGRATION []`. The cause of the one failure is not proven. Changed expectations: none. The first fix pass's `review_fix_checks()` still pass unchanged. Captures: `artifacts/feel-journey/08-B3/pkg/`, by `artifacts/feel/08-B3/a2/cap.sh`. "Before" is `tools/feel_snapshot.sh tree 08-B3`. I looked at every capture named here. - `a2-{before,after}-showcase-{day,night}`: `a2/door-showcase.json`, five styles on a quay. Before shows no clutter. After shows a café corner by the canal door, boat-repair piles at the victorian and modern houses, a crate stack (medieval), bonsai (japanese), and a watering can on the canal house's upper sill. Crops are in `a2-crops/showcase-canal-{before,after}.png`. - `a2-{before,after}-lane-down-day` (camera `0.0,1.35,12,-3,1,0`): pots, a bike, cans, firewood and a café corner in the lane after, nothing before. `a2-{before,after}-harbour-close-day`: a café corner by a path door, pots and a barrel after. The `lane-close`, `lane-close2`, `lane-top` and `harbour-day` pairs change little: the lane's facades are hidden at those cameras. - **The "+25 is the clutter" claim in the entry below was wrong.** `after-lane-day` (a top-down camera) and `lane-day-reviewfix` (the scene camera, rain on) were taken from different cameras. Same camera, rain off (`a2-plus25-*`): pre-stage 314 rendered objects, first fix pass 316, first fix pass with its clutter block disabled 314, live 361. The first pass's one piece was +2. - Sweep (`a2-sweep/`; `a2/make_sweep.gd`, `.log.out`): lane.json re-seeded as `--seeds` does, with its group composed at s0..s5 and baked, and clutter derived live. Sheets `sheet-lane-{day,grey,down-grey}.png`. Judgement: - **Stair-end bench:** 3 of 6 seeds (s3, s4, s5; composed benches at 1.64 to 2.18 m, none from clutter). This matches B3a's 3 of 6; clutter neither adds to it nor removes it. - **Focal read:** still weak at the play camera, as in 07-B0. In greyscale at the down camera, a café corner's white table is the brightest thing in the lane in s0 and s5. It competes with the group anchor (tree, umbrella) instead of deferring to it. - **Repetition:** pots (34) and watering cans (32) are 66 of 92 pieces over the sweep. The big kinds (bike, bench, stacks) rarely fit the strip. This is a new finding, not tuned here. Measurements (same sitting, pre-stage tree vs live): | | Before | After | |---|---|---| | Harbour drag, step_total median, 3 alternating runs (`a2/drag-{before,after}/README.md`): handle-drag-height | 23.95 ms | 24.58 ms | | wall-node-drag | 20.91 | 21.48 | | slider-scrub-width / -height | 6.56 / 15.75 | 5.85 / 16.17 | | neon-drag / wall-stroke | 5.64 / 8.34 | 5.93 / 8.14 | | terrain-stroke-raise / -paint | 116.39 / 10.50 | 117.90 / 11.04 | | Release: handle-drag-height / slider-scrub-width / undo / terrain-stroke-paint | 52.96 / 60.32 / 37.09 / 14.17 | 62.51 / 80.10 / 47.08 / 32.38 | | Dense fixture mean frame, median of 3 (`a2/dense.txt`) | 13.49 ms (13.14-14.11) | 14.01 ms (13.46-14.31) | | Dense draw calls | 2035 | 2078 | - Every drag step is within 2 ms of the pre-stage tree. The acceptance is met; the recheck had 90.9 / 90.0 / 192.7 ms. Releases cost 10 to 20 ms more, because the clutter recompute and the decoration rebuild moved there. - Dense: +43 draw calls from 104 new pieces. The run ranges overlap; +0.5 ms on the mean median. - One full clutter pass over Harbour (`a2-run2-test_clutter.log.out`, "a2 cost"): 18 openings, 21 pieces. Cold 28.6 ms, warm 0.06 ms. After an edit far from every opening, 8.7 ms: the scene maps are rebuilt and all 18 openings are cache hits. Review: . Not done: - **Styled front doors.** B0's front-door zone sits at `front_door_x` on every ground house. On the showcase, the victorian, medieval, japanese and modern fronts show no door there, so their "door" clutter stands before a ground-floor window. The zones were not changed. - Palette balance (above). There is still no `clutter` override for doors without a window object, and no inspector field. Tower sills are not dressed. The lane capture stays facade-occluded at the play camera. - Journey views: the orchestrator's (`tools/feel_journey.sh 08-B3`). Platforms: Mac run; Windows not run. **Feel B3a review fix (23 September 2026):** An Opus 5 review of the entry below returned seven must-fix findings; all seven are fixed, tested against the pre-fix code (kept failing for the right reason) and against the fix (all green), and re-captured. Fixes, in the review's order: 1. **Stale compose cache** (`Composition._local_signature`): a non-building child (a window, a door, a prop) was matched to the local rect by its own parent-local `o.pos`, treated as if it were a world position — correct only by coincidence when the owning building sits at the origin. Now matched with `state.transform_of(o).origin`. `state.heights` is now hashed into the signature too, so a terrain height edit under a group invalidates its cached layout. 2. **Never wired into a scene.** `world_view.gd`'s `_sync_decorations` now calls `Composition.clutter_context`/`Composition.clutter` for every ground-parented window/door on a building, drawing each piece as a derived (never saved) decoration, batched and animated like any other prop. `main.gd`: a second click on an already-selected window cycles its clutter (mirrors the B0b group-reroll idiom), H toggles it hidden (`Composition.CLUTTER_HIDDEN`). 3. **Lane width always hit the 8 m cap.** The walk now also stops at a blocked cell (`Composition._blocked_cells`: a building footprint or a wall band), not only at water. 4. **Pieces packed into keep-clear zones**, including the opening's own: `clutter_context` now builds a `keep` field from every nearby zone (self included — a door's own dressing must leave it usable) and `clutter()`/`_local_obstacles()` pass it to `pack()` as obstacles, and filter the hero and sill offsets the same way. 5. **`is_door`/`terrace_above` computed but never read.** A window above the sill line (`is_door == false`) now draws only its style's sill-safe (garden/planter) kinds, never a door's ground furniture, hero sets or stacks. `terrace_above` now suppresses hero sets and caps the piece count by one, the way a facing door already did. 6. **Stack offsets non-cumulative and undrawn as a tilt.** `Composition.stack` now accumulates `offset_i = offset_{i-1} + h_{i-1} * tan(lean_i)` (bounded per step, not `y * tan(lean)` from the base with whichever lean a piece happens to be running), clamped to the footprint's own half-width. `architecture.gd` now draws `crate_stack`/`plank_stack` with the lean as a Z tilt. 7. **Bike wheels flat.** `MeshBatch.cylinder` only builds a Y-axis cylinder; wheels are now a `_wheel_ring` (rim + spokes as beam segments in the XY plane), upright. Files: `scripts/composition.gd`, `scripts/architecture.gd`, `scripts/world_view.gd`, `scripts/main.gd`, `tests/test_clutter.gd` (61 checks → 3160; one `unwired_checks()` assertion in `tests/test_composition.gd` flipped, below). Pre-edit backups (made before any edit, since this folder has no Git checkout): `build/backup-feel/08-B3-review/{composition,architecture,world_view, main}.gd.bak` / `.postfix`. Pre-stage snapshot `build/backup-feel/08-B3/` untouched. Tests (logs in `artifacts/feel/08-B3/review-fix/`): all seven findings got a new regression check in `review_fix_checks()`, run test-first against a scratch tree built from the pre-fix backups plus the pre-fix `test_clutter.gd` (`a-testfirst-test_clutter.log(.out)`): 3160 checks, 460 failures, one line per finding (e.g. `no packed clutter piece stands where any keep-clear value ... exceeds KEEP_HARD (42 of 70 over, worst 1.00)` — the same 42-of-70 the review's own probe found) plus the `plank_stack` per-piece jump checks and the bike-wheel checks. Against the fixed code: `a-run3-test_clutter.log`, 3160 checks, 0 failures (`a-run1`/`a-run2` are two earlier passes where my own test fixture had a bug — an offset lane fixture that moved only the buildings, not the terrain, and a keep-clear check that wrongly excluded the door's own zone — corrected before counting these as the test-first evidence, not a weakening of what's asserted). `test_composition.log`/`test_composition2.log`: 155 checks, 0 failures. `test_reroll.log`/ `test_reroll2.log`: 43 checks, 0 failures. Full `tools/test.sh`, foreground, one Godot at a time: 22 suites, 9,659 checks, 0 failures (`a-full-*.log`; up from B3a's 22/6,560 — almost all of it `test_clutter`'s new seed-swept checks), plus the windowed `--verify` (`a-full-verify.log`, `PIXELPUNK_INTEGRATION []`). Changed expectations: `tests/test_composition.gd` `unwired_checks()`. Old: `world_view.gd` and `scene_state.gd` never call `Composition.`. New: `world_view.gd` now does (finding 2's fix); `scene_state.gd` still never does. main.gd's own `Composition.` check now also covers the click-cycle/hide, not only the B0b reroll. Captures: `artifacts/feel-journey/08-B3/pkg/`. Looked at every one. - `bike-closeup/courtyard.png` vs `bike-closeup-before/courtyard.png` (new: a dedicated scene, one bike, close camera, `artifacts/feel/08-B3/review-fix/bike_closeup.gd`, built against the live tree and the pre-fix scratch tree respectively): before, two solid black ellipses lying flat on the ground either side of the frame, matching the review's `bike2.png`; after, two upright spoked wheel rings. `clutter-showcase/courtyard.png` (same scene, same default camera, `--camera=` omitted so it reads the scene's own stored one): the bike now reads as a bike at the showcase's own distance too. - `lane-day-reviewfix/courtyard.png` (`lane.json`, live tree, same omitted-camera convention as `before-/after-lane-day`): `performance.json` against the original, pre-fix `after-lane-day` (same scene, same camera): `rendered_objects` 306 → 331, `draw_calls` 281 → 306, masonry `detail.total` unchanged at 2791 — the +25 is the new automatic clutter now drawing, with no brick/tile cost. *(Corrected 23 September 2026, B3a2: not so. The two captures used different cameras; at one camera the clutter was +2 objects. See the Feel B3a2 entry, `a2-plus25-*`.)* `lane-day-reviewfix-top/courtyard.png`, a closer top-down camera on the same scene, was looked at for a qualitative check of the lane; roof overhangs occlude most door-level detail from directly above, so this view mainly confirms nothing crashed or looked broken, not the per-door dressing itself — the object-count delta above is the real evidence for that. Measurements: dense fixture (`artifacts/weather-pass9/dense.json`), two alternating runs, same sitting, live tree vs. a scratch tree built from the pre-fix backups (not `tools/feel_snapshot.sh tree`, since this pass predates its own snapshot step — see Not done): before 13.34/12.93 ms mean, after 12.88/13.13 ms mean; draw calls and rendered objects unchanged (2035/2060) on this fixture — within run-to-run noise, which is the expected result since `dense.json`'s buildings evidently carry few or no door/window children for the new per-opening pass to act on. Not a 3-run median (time budget); see Not done. Drag harness not re-measured this pass (see Not done). Review: . Not done: - **No formal pre-fix snapshot via `tools/feel_snapshot.sh`.** The official `08-B3` snapshot predates B3a itself, not this fix, and re-running `save` would refuse (it never overwrites). The before/after evidence instead comes from backups made by hand before the first edit (`build/backup-feel/08-B3-review/*.bak`, confirmed byte-identical to the review's own state) and a scratch tree assembled from them, the same shape `tools/feel_snapshot.sh tree` builds. - **Drag harness** not re-run: none of the seven findings touch the drag path, and the cache fix only changes behaviour when an edit inside a group's own reach is a child object, which the Harbour drag script does not exercise. - **Dense fixture measured twice, not three times** (time budget); both runs on each side agree with each other within about half a millisecond, which is the kind of spread already on record for this fixture. - **Towers** still not covered by context clutter (`clutter_context` reads a `window`'s parent as a `building`); carried over from the original entry's Not done, untouched by this pass. - **Inspector exposure of the `clutter` param** (an explicit field, not only click-cycle/H) is still not built; click-cycle and hide cover the plan's "cycle on click" and "can be hidden" wording, but a numeric override typed in the inspector is not exposed. - Everything else the original entry listed as not done (hero sets/hidden state tested as pure functions only) is unchanged by this pass. Escalation (23 September 2026): the Opus recheck (`artifacts/feel/08-B3/review/recheck/`) left this pass NOT MET. Clutter drew only at window objects: lane.json 1 of 9 door zones, Harbour 0 of 18, dense 0 of 100. It ran whole-scene maps on every drag step: Harbour handle-drag-height step_total went from 24.1 to 90.9 ms, wall-node-drag from 21.1 to 90.0, terrain-stroke-raise from 118 to 193. It went stale after water and style edits, placed no hero set (0 of 800), and put upper-window sill pieces on the ground. The second fix pass (Feel B3a2, above) fixed all five. The same drag steps now measure 24.58 / 21.48 / 117.90 ms against 23.95 / 20.91 / 116.39 ms pre-stage. Doors dressed: 9 of 9, 14 of 18, 100 of 100. **Feel B3a Lived-in clutter — part a (23 September 2026):** Three of the 07-B0 handoff's open items, plus a tested-but-unwired clutter engine. `Composition.groups()` and `Composition.compose()` are now memoised (whole-scene for groups, per-group local signature for compose), `DOOR_ZONE` is narrower so it no longer floods a 4 m lane, and `Composition.clutter`/`Composition.stack` are new, fully tested library functions for door/window clutter and the firewood/crate/plank stacking generator — standing alone, the way B0a's library stood before B0b wired it in. **Nothing renders automatically from a door or window yet**: `world_view.gd` never calls the composition layer, so no existing scene's live rendering changes (confirmed below). The stacking generator and the context clutter's own new object kinds (nine of them) *are* placeable and drawn, through `architecture.gd`'s `reference_decoration`, the way any other prop is. - **Caching** (`scripts/composition.gd`): `groups()` is cached on a whole-scene signature (`hash([objects, terrain])`, the same idea as `DioramaState.mounts()`); any edit invalidates it, but a selection click that changes nothing does not recompute. `compose()` is cached per group on `_local_signature()`, a hash of only the objects and terrain within the rect `_context()` itself reads (the group plus `MARGIN` plus the keep-clear reach) — an edit outside that rect leaves the group's cached layout alone. `Composition.cache_stats()`/`reset_cache()` let a test or a script tell a hit from a miss without timing. - **DOOR_ZONE** (`scripts/composition.gd`): `[1.2, 0.8, 1.5, 1.0]` → `[1.0, 0.6, 0.9, 1.0]`. A door zone's perpendicular reach (half-width + falloff) was 0.8 + 1.5 = 2.3 m — two facing doors on a 4 m lane overlapped in the middle; it is now 0.6 + 0.9 = 1.5 m. - **Clutter** (`Composition.clutter_context`, `Composition.clutter`): context read for a placed `window` object (a low one is a door): water and a garden within 5 m, the lane's width along the face normal, a facing door across it, and a terrace stacked above. `STYLE_CLUTTER` maps each of the five building styles to water/garden/base kinds (canal crates and a bike, medieval firewood and a barrel, japanese lantern and bonsai, victorian bench, modern planters). `clutter()` reads the optional `clutter` param on the window (absent = automatic and seeded from the object; -1 hides it; any other integer overrides the seed, which is what a click-to-cycle would step through once wired), narrows the palette when a door faces it, and packs 1-3 pieces beside the opening through `Composition.pack()` so they never overlap. Two hand-authored hero sets (`cafe_corner`, `boat_repair`) are mixed in at `HERO_CHANCE`. - **Stacks** (`Composition.stack`): firewood, crates and planks. Each piece's lean is clamped within `STACK_LEAN_MAX` of the running lean (never an unbounded total), about 30% of pieces copy the previous piece's lean instead of drifting, and about 12% face the other way (rotated near π). Wired as three real placeable/drawn kinds, `firewood_stack`/`crate_stack`/`plank_stack`, with an optional `count` param. - **New kinds** (`scene_state.gd` `KINDS`/`dimensions`, `architecture.gd` `reference_decoration`): `mooring_rope`, `fishing_crate`, `watering_can`, `barrel`, `bike`, `bonsai`, `firewood_stack`, `crate_stack`, `plank_stack`. Save version stays 2; `clutter` is optional on a `window` object and validated (-1 or an integer seed); `count` is validated only on the three stack kinds. Files: `scripts/composition.gd`, `scripts/scene_state.gd`, `scripts/architecture.gd`, `tests/test_clutter.gd` (new), `tests/test_composition.gd` (two assertions changed, below), `tools/test.sh` and `tools/Test.ps1` (suite added). Pre-edit copies: `artifacts/feel/08-B3/a-bak/`. Snapshot: `build/backup-feel/08-B3/` (pre-existing, not overwritten). Tests (logs in `artifacts/feel/08-B3/`): - Test first, against the pre-stage tree (`tools/feel_snapshot.sh tree 08-B3`, since the new functions do not exist there): `a-testfirst-test_clutter.log(.out)`, 16 checks reached before the DOOR_ZONE and cache assertions failed and later sections hit `SCRIPT ERROR: Nonexistent function` for `clutter_context`/`clutter`/`hero_set`/`stack`/`reset_cache` and stopped (the run still exits cleanly, code 1, not a hang). - After: `a-run4-test_clutter.log`, 61 checks, 0 failures (three earlier fixture bugs in the test itself along the way: a water rectangle painted behind the door instead of in front of it, a facing-door fixture with a house's local side numbered wrong, and a coverage assertion that read `_context()`'s coarse "allowed" list, which DOOR_ZONE does not touch, instead of `ctx.keep`, which it does — none weaken what is asserted, `a-run1..3-test_clutter.log` keep the intermediate runs). - Full `tools/test.sh`, 22 suites in the foreground one at a time (`test_clutter` added), plus the windowed `--verify`: `a-full-test.log`, `a-full-counts.txt` — 22 suites, 6,560 checks, 0 failures (07-B0 was 21 suites / 6,499; +61 from `test_clutter`); `a-full-verify.log`, `PIXELPUNK_INTEGRATION []`. Changed expectations: - `tests/test_composition.gd` `unwired_checks()`. Old: `world_view.gd`, `architecture.gd` and `scene_state.gd` never contain `Composition.`; `main.gd` does (B0b, the reroll). New: `architecture.gd` also now does (the stacking generator's rendering); `world_view.gd` and `scene_state.gd` still never do (B3a's automatic per-door clutter is not wired into the renderer, so `scene_state.gd`'s `clutter` param validation is worded to avoid the literal substring the test greps for, since it genuinely never calls in). - `tests/test_composition.gd` `compose_checks()`, the "new mass lands opposite the heavy west side" check on the courtyard fixture. Old: the new mass's centroid x strictly greater than the group centre's x. New: no more than 0.34 m west of it. DOOR_ZONE narrowed, more of this fixture's door-adjacent cells are free, and the winning candidate's new mass now centres at x -0.02 instead of a positive value — still essentially centred, not pulled onto the heavy side, so the qualitative claim holds; the strict-inequality version doesn't. Captures: `artifacts/feel-journey/08-B3/pkg/`, made by `artifacts/feel/08-B3/a-cap.sh` (`before-`/`after-lane-day`, 1600x1000, day) and `artifacts/visual-audit/make_composition_maps.gd` (`maps/lane-lane-5_-10.png`, tuned DOOR_ZONE, seeds 0-5). - `before-lane-day` vs `after-lane-day` `courtyard.png`: looked at both; identical, and `performance.json` confirms it field for field (parts, draw calls, rendered objects, terrain cells, detail counts all equal) apart from timing — expected, since nothing yet renders clutter. - `maps/lane-lane-5_-10.png`: looked at it. The red keep-clear blobs sit in tight pools at each door rather than the continuous band the 07-B0 map showed (its own words: "the lane's door zones cover most of its 4 m width"); more pale, placeable lane surface shows between them. - `clutter-showcase/courtyard.png` (`artifacts/feel/08-B3/a-make-clutter-showcase.gd`, a small audit scene, not one of the plan's named ones): looked at it. All nine new kinds place and draw without error — a mooring-rope coil, a slatted fishing crate, a watering can, a barrel, a bike, a bonsai in its dish, and the three stacks (firewood upright, crates two-high, planks leaning) — at a glance each reads as what it is meant to be. - No night or greyscale capture of the lane this part (time budget); see Not done. Measurements (same sitting, live tree against `tools/feel_snapshot.sh tree 08-B3`): | | Before | After | |---|---|---| | Dense fixture mean frame, 3 alternating runs, median (`a-dense.txt`) | 13.04 ms (12.41-13.13) | 12.43 ms (12.39-13.13) | | Dense fixture p95, median | 13.33 ms | 12.20 ms | | Dense fixture draw calls | 2035 | 2035 | | Harbour handle drag, step_total median (1 run, not 3 — see Not done) | 19.505 ms | 19.485 ms | - Dense and the drag harness move by run-to-run noise, not by design: nothing calls the composition layer during either today, so this is the expected null result for a library that is not wired into rendering or input yet, not evidence the caching does nothing (next row). - Placement cost, headless, cold (first call after `reset_cache()`) vs warm (repeat call, same state) vs after an edit 40 m away from the group (`a-cost.log`, `a-cost.gd`): - `groups()`: cold 3.3-16.6 ms (matches the 07-B0 baseline of 3.4-17 ms), warm 0.01-0.09 ms — a part selection that changes nothing now costs about 1% of what it did. - `compose()`: cold 24.5-112.6 ms (07-B0: 24-105 ms; dense's is a caller-made window, not a real group, so a little higher); after an edit 40 m away, every group measured stayed within noise of its own warm-repeat cost (lane 0.54 ms, garden 0.83-0.91, harbour's four groups 0.93-1.21, dense's one 80-cell group 12.0 ms) — nowhere near its cold cost. That is the ask met: an edit outside a group's own reach does not recompose it. - Remaining cost centre: `_local_signature()` itself scans every object in the scene to find which are near the group's rect, so its own cost scales with total object count — on the 600-object dense fixture the "warm" cost is 12 ms, not "a few ms". Lane/garden/harbour (9-136 objects) are all under 1.3 ms. Not fixed this part; a spatial index (the terrain-chunk grid the plan mentions) would remove the remaining scan. - Lane seed sweep, DOOR_ZONE judged against the 07-B0 handoff's own findings (`a-bench-check.gd`, `a-maps.log`, seeds 0-5, `compose()` not `bake()`, so nothing is written to a scene): - **Repeated bench.** 07-B0 recorded a bench at the lane's stair end in 5 of 6 seeds. With the narrower DOOR_ZONE it is 3 of 6 (`d < 3 m` of the stair foot at (13.5, 1.5)): seeds 3 and 4 keep one close (1.64 m, 2.18 m), seed 5 adds a second nearer bench (8.47 m) alongside two far ones; seeds 0, 1, 2 keep every bench 19 m or more away. Fewer, not zero — the door zones opening up freed room elsewhere in the piazza and alley, but the stair end is still one of the few low-keep-clear pockets near a door. I did not re-derive the "5 of 6" figure myself against the old code: the same script pointed at the pre-stage tree (`tools/feel_snapshot.sh tree` does not copy `artifacts/`, so `res://artifacts/visual-audit/lane.json` does not exist there) failed to parse the missing file, hit a `SCRIPT ERROR` before its own `quit()`, and sat as an idle headless process rather than exiting or hanging (`a-bench-before.log`); it was killed rather than left running two Godot processes, and not retried with an absolute path given the time left. The "5 of 6" is quoted from the existing 07-B0 entry above, which the same lane.json and the same stair-foot coordinates make a fair comparison. - **Focal read.** Not re-judged by eye this part (no new day/night/greyscale sweep sheet was captured — see Not done); the map sheet shows the anchor (gold) still landing at the piazza mouth or mid-lane across seeds, as in 07-B0. Review: . Not done: - **Not wired into rendering or input.** `Composition.clutter` is not called from `world_view.gd`, so no door or window draws clutter in the game; there is no click-to-cycle or hide UI in `main.gd`/`editor_ui.gd`, and the `clutter` param, though validated, is not exposed in the inspector. This was a deliberate scope cut (see the session's own reasoning, not reproduced here) to avoid modifying `Architecture.styled_building` — the shared hot path every building's walls draw through — without the time left in this part to verify it thoroughly; wiring it in is real remaining work, not a rounding error, and is the natural next B3 part. - **Hero sets and hidden state are untested in the renderer** for the same reason; they are tested as pure functions only. - **Towers.** `clutter_context` reads a `window` object's parent as a `building`; a tower's doors and windows (`Openings.tower_openings`) are not covered. - **DOOR_ZONE's focal-read claim** is carried over from 07-B0's map sheet, not re-judged with a fresh greyscale sweep this part. - **Drag harness** measured 1 run each side, not the methodology's 3 alternating runs (time budget); the numbers above are single samples, not medians, and are within the kind of run-to-run spread `drag-baseline-v2` itself shows for `handle` (harbour p95 was 41.79 ms against a median of 25.978 ms there). - **`_local_signature`'s own O(objects) scan** on a large scene is the remaining cost centre for `compose()`'s cache (see Measurements). - Hero sets: only two authored (`cafe_corner`, `boat_repair`); the plan names both as examples, not a target count. - Journey views are the orchestrator's (`tools/feel_journey.sh 08-B3`). Platforms: Mac run; Windows not run (`tools/Test.ps1` gained the suite but was not executed). Escalation (23 September 2026): the review fix and the recheck are recorded in the two entries above. After the second fix pass (Feel B3a2), clutter is drawn at every door zone and computed off the drag path; every Harbour drag step is within 2 ms of the pre-stage tree. **Feel B0b Shared composition layer — part b (23 September 2026):** Any derived group (a lane or a courtyard) can now be rerolled, from the panel of a house or wall that belongs to it or by clicking the group's own ground, and the only thing stored is an optional seed override on the group's owner. Nothing is drawn from the layer yet (B3 and B4 will), so while a group is picked its layout is shown on the ground as editing furniture; no existing scene changes. - **Storage** (`DioramaState.SEED_OVERRIDE` = `"seed_override"`): an optional `{group kind: seed}` dictionary on the group's owner, its lowest-id member. Validated (a dictionary of at most 8 entries, integers 0 to 2^32−1), made integer again on restore, save version 2. A scene that never rerolls carries no key and saves as before; a build older than B0b ignores the key. Deleting the owner deletes the curation. A move keeps it (`object_candidate`); a copy, an Alt-dragged wall and a split wall's second half start without one. `DioramaState.drawn()` strips the key from the cluster signatures, so a reroll rebuilds no building or wall (tested). - **Library** (`Composition`): groups gain `owner`, `derived_seed` and `override`, and read the stored seed. `reroll()` walks the group's sweep (`sweep_seed(key, k)`, the seeds part a's composed variants and review maps already used), skips any seed whose layout equals the one on show, and writes the override. Five rerolls of each audit group walk steps 1 to 5, so `composed/{lane,garden}-s1..s5.json` are exactly what one to five rerolls give. `group_at()` and `groups_with()` find a group from a point or a member. A `--seeds` sweep leaves a rerolled group on its chosen seed. - **Editor** (`main.gd`, `editor_ui.gd`, `world_view.gd`): - with the Select tool, a click on a group's ground picks it (panel "Derived group", its anchor and piece count, a Reroll button) and a second click rerolls it; Esc, another tool or open ground lets it go; a part tool still places on it; - a selected house or wall lists "Reroll the lane (164 cells)" style buttons under *Surroundings*, one per group, each rerolling its own group only; - each reroll is one undo step, `reroll lane` / `reroll courtyard`; - `world.composition` draws the layout (gold anchor, white pieces, pale outline), depth-tested and double-sided; hidden in photo mode, in clean captures and on undo; - `--group=,`, `--reroll=` and `--select=` drive it for captures (README). Files: `scripts/composition.gd`, `scripts/scene_state.gd`, `scripts/main.gd`, `scripts/editor_ui.gd`, `scripts/world_view.gd`, `tests/test_composition.gd`, `tests/test_reroll.gd` (new), `tools/test.sh` and `tools/Test.ps1` (suite added), `README.md`. Pre-edit copies: `artifacts/feel/07-B0/b-bak/`. Snapshot: `build/backup-feel/07-B0/`. Tests (logs in `artifacts/feel/07-B0/`): - Test first: `b-testfirst-test_composition.log(.out)`, 113 checks, 7 failures (no owner, no reroll, main unwired); `b-testfirst-test_reroll.log(.out)`, 1 check, 1 failure (no picked group or preview in the editor). - After: `b-run2-test_composition.log`, 155 checks, 0 failures; `b-run2-test_reroll.log`, 43 checks, 0 failures. The first reroll-suite run (`b-run1-test_reroll.log.out`) stopped on a parse error in `editor_ui.gd` (a duplicate loop variable), fixed before run 2. - Full `tools/test.sh` in one call (`b-full-test.log`, `b-full-counts.txt`): 21 suites, 6,499 checks, 0 failures (6,408 plus 48 composition and 43 reroll checks), and `PIXELPUNK_INTEGRATION []` (`b-full-verify.log`). - One fixture correction after test-first, not a weakening: the owner-deleted check moved from the yard fixture to the lane fixture, because taking the yard's west house away opens the yard onto the meadow and there is no group left to test. Changed expectations: `unwired_checks` in `tests/test_composition.gd`. Old: `main.gd`, `world_view.gd`, `architecture.gd` and `scene_state.gd` never contain `Composition.`. New: the last three still never do, and `main.gd` does (the reroll). No other assertion moved. Captures: `artifacts/feel-journey/07-B0/pkg/`, 1600x1000, rain off, made by `artifacts/feel/07-B0/b-cap.sh`. Lane camera `0.62,1.05,26,-1,1.5,-2`; garden uses its scene camera. - `after-reroll-lane-day`, `-r1-day`, `-r2-day`, `-r1-night`, `after-reroll-garden-day`, `-r1-day` (`editor.png`): the panel and the layout on the ground. The lane goes from a tree to a café table after one reroll; the courtyard from a tree to a pergola. `after-select-garden-3-day`: at this size the Surroundings buttons are below the panel's fold (the suite checks them). - `before-/after-reroll-{lane,garden}-day` `courtyard.png`: the preview never reaches a clean capture. 1.1 % and 3.1 % of pixels differ (`b-sheets.log`, `diff-reroll-*.png`), all of it on the water and the sky; nothing on land changed. - Seed sweep, `sweep-{lane,garden}-s0..s5-{day,night,grey}`; sheets `sheets/sweep-*.png` (s0 top left, s5 bottom right; `b-sheets.gd`). The review: - **Focal hierarchy.** Every one of the 18 sweep layouts has one anchor, and no piece carries more than half its mass (tested). In greyscale the garden courtyard reads: the pergola (s1, s3) is the brightest thing in it, the tree and blossom less so. The ring lawn's adopted tree is the focal point in all six. The lane gives no strong focal read at the play camera. Its café table and umbrella are small, and the tree (s0, s2, s4) stands at the piazza mouth, half behind the eaves. - **Repetition.** The six layouts of each group are all different (tested). The anchor kinds change (lane: tree, café table, umbrella; courtyard: tree, pergola, blossom), and the anchor moves 2.87 m on average in the lane (0.40 of its radius) and 1.72 m in the courtyard (0.32). The test's floor is 0.3. One repeat: the lane puts a bench at the stair end in five of six seeds. Its door zones leave that end, the alley and the piazza as the only room (part a's `DOOR_ZONE` note). Measurements (same sitting): | | Before | After | |---|---|---| | Dense fixture mean frame, 3 alternating runs, median (`b-dense.txt`, `b-dense-*`) | 13.32 ms (13.31–13.33) | 13.35 ms (13.31–13.69) | | Dense fixture p95, median | 14.97 ms | 14.87 ms | | Dense fixture draw calls | 2035 | 2035 | - Nothing runs per frame, so the dense difference is run-to-run noise. - Placement cost, headless, medians of 3 (`b-cost.log`, `b-cost.gd`; no before, since the pre-stage tree has no layer): - `groups()`, which selecting a house or wall and a click on ground now pay: lane.json 3.4 ms, garden 6.6, Harbour 7.6, dense 17.0; - `compose()`: 24 to 67 ms per derived group; - `reroll()` (shown layout plus the new one): 81 to 133 ms; - dense.json derives no group, so a caller-made 80-cell window at its centre was used: 105 ms per compose. - From the inspector on lane.json, a reroll with its preview took 166 ms (`b-run2-test_reroll`). - Two Harbour groups (`lane:-2,-7`, `courtyard:7,6`) find no room for an anchor. Their reroll tries all 8 seeds (220 to 266 ms) and reports "No other arrangement fits". - No drag-harness run: this is a track B part. Review: . Not done: - Nothing is placed in the game from the layer (B3, B4), so outside the preview a reroll is not visible. - The Surroundings buttons sit below the fold of a tall building panel. - `groups()` is not cached. Selecting a part on the dense fixture pays 17 ms for it. - A rerolled group's key follows its first cell, so an edit that moves that cell starts its sweep again from step 1 (the stored seed still applies). - The lane focal read and the stair-end bench are left for B3's palettes and `DOOR_ZONE`. - Journey views are the orchestrator's (`tools/feel_journey.sh 07-B0`). Platforms: Mac run; Windows not run (`tools/Test.ps1` gained the suite but was not executed).