PixelPunk journal, chapter 12 Evidence frozen 24 September 2026. Historical records; not instructions. SOURCE: docs/visual-handoff.md, snapshot lines 5 to 377 **Feel D2a Perspective camera prototype and photo-mode camera, part a (24 September 2026):** Opus 5.5 implementer. Scenery has a new Projection option. Orthographic is the default and is unchanged. Perspective is a narrow lens (24 degrees by default, a Field of view slider from 12 to 60) that frames the same `size` metres at the target, so `--camera`, F and the zoom keep their meaning. Like the view pivot, the projection is a preference of the session: nothing goes into the document, and it is not an edit. Photo mode also gets a camera of its own. It is runtime only, never saved and never in undo: - a click pins the lens's focus on a point, which turns on a depth of field about it (`,` and `.` set the aperture, U unpins); - `-` and `=` change the field of view and keep the eye where it is; - C toggles a free camera (W A S D, Q and E, right drag to look). The photo bar lists these keys. The automated-run arguments are `--projection=perspective`, `--fov=`, `--photo-focus=x,y,z`, `--dof=0..1` and `--free-camera=x,y,z,yaw,pitch`. The photo camera applies to `courtyard.png`. A report carries the camera fields only when they are not the defaults. Audit of the orthographic assumptions and what was done to each: | Assumption | Where | Change | |---|---|---| | Camera at 110 m; clip 0.1/400; distance fog 100 to 320; shadows to 150 | `world_view.gd` | `ViewProjection` (new) gives each as a function of distance. At 110 m the values are exactly the old constants. Under perspective they follow the distance (far at least 400, panel 2 m and mirror cut 10 m inside it). | | Zoom under the cursor | `CameraRig.hold` | A `perspective` argument scales all three offsets about the pivot. Orbit was already the same maths in both. | | Handle ray planes | `main.gd` `_handle_height` | Under perspective the vertical plane faces the eye, not the camera axis. The horizontal-plane handles (coordinate, angle, ridge end) already used real rays. | | Grips keep their screen size | `world_view.gd` `handle_scale_at` | Each grip is sized for its own depth under perspective. Creature picking uses the frame at the target. | | Painted sky panel fills the frame | `backdrop.gdshader` | The perspective branch scales the quad by the panel's depth. | | Height fog rays parallel | `height_fog.gdshader` | Under perspective, rays start at the eye. | | Water and puddle mirror: one screen direction per frame | `mirror.gdshaderinc`, `water.gdshader`, `puddle.gdshader` | New `mirror_march_view`: each fragment's own reflected ray, marched in view space. Fresnel, sky and glitter take the fragment's ray. The panel depth is now a uniform `panel_depth` (default 390). | | Reflection streak placement (`h/tan(pitch)` along the view axis) | `reflections.gdshader` | Under perspective the streak runs along the eye's bearing to the source, with the pitch of the eye-to-mirror-point ray. | | Tilt-shift: ground depth follows screen rows | `tilt_shift.gdshader`, `main.gd` `_apply_lens` | Unchanged outside photo mode. The photo depth of field uses the plane through the focus point seen from the camera (exact per row in both projections, `ViewProjection.lens_model`). The circle of confusion is \|1 - z_f/z\| (perspective) or \|z - z_f\|/z_f (orthographic). | | Light pools and lamp selection | `light_pool*.gdshader`, `_rank_sources` | No change needed: the pools are world geometry, and the selection projects with the camera and uses its real position. | | Contact-shade pass, outlines, creature outlines | shaders | No change beyond the panel depth uniform: they already use the projection matrices. | | Pixel presentation | `main.gd` viewport | Unchanged. Under perspective, texel size varies with depth (see findings). | Files: `scripts/view_projection.gd` (new, `class_name ViewProjection`; editor import `artifacts/feel/15-D2/a-import.log`), `scripts/camera_rig.gd`, `scripts/world_view.gd`, `scripts/main.gd`, `scripts/editor_ui.gd`, `shaders/backdrop.gdshader`, `shaders/height_fog.gdshader`, `shaders/mirror.gdshaderinc`, `shaders/water.gdshader`, `shaders/puddle.gdshader`, `shaders/reflections.gdshader`, `shaders/tilt_shift.gdshader`, `tests/test_camera.gd` (new), `tests/test_wet.gd`, `tools/test.sh`, `tools/Test.ps1`, `README.md` (one passage). The old copies of the last four are in `artifacts/feel/15-D2/*.pre-D2`. Snapshot: `build/backup-feel/15-D2/` (untouched). Diff: `artifacts/feel/15-D2/a-diff.txt`. Tests: - Test first: `artifacts/feel/15-D2/a-testfirst-test_camera.log(.out)`, 13 checks, 13 failures. - `test_camera`: 127 checks, 0 failures (`full-a/test_camera.log.out`). It covers: - the `ViewProjection` numbers, including the orthographic values equal to the old constants; - argument parsing and clamping; - `CameraRig.hold` under perspective; - the shader branches; - in the editor (headless, 960x600 frame): switching projection is no edit and leaves the document alone; the frame is `size` tall at the target; the clip planes, fog, shadows and shader depths follow; switching back restores every orthographic value; - grips sized per depth, the height knob off-centre, zoom and orbit keeping the point under the cursor, the Scenery rows; - photo mode: the focus row and depth, both ground models agreeing with the point's depth, the aperture, the field of view holding the eye, the free camera flying and turning about its eye, and nothing saved or undoable. - Mutation check (`a-mutation-test_camera.log.out`): with the old height plane and the old zoom maths, the height-knob and zoom checks fail. - Full suite in the foreground (`artifacts/feel/15-D2/a-full-test.log`, per suite `full-a/`): 33 suites, 17,367 checks, 0 failures. `--verify` gave `PIXELPUNK_INTEGRATION []`. Changed expectations: one. `tests/test_wet.gd`, "Puddle and water step the same pixels and stop at the same backdrop": - old: the puddle and water shaders have equal `BACKDROP_DEPTH` constants; - new: both declare `uniform float panel_depth = 390.0`. The old test fails against the new shaders (`a-old-test_wet-against-D2.log.out`). Captures (`artifacts/feel-journey/15-D2/pkg/`, 1600x1000 `--smoke`, the journey cameras; `artifacts/feel/15-D2/cap_all.sh`; I looked at every one cited): - `before-*-ortho` (pre-stage tree) and `after-*-ortho` for Harbour (Night), the canal quarter (Night) and Styles (Day). They are the same pictures. - Differing pixels, before against after: 3.1%, 3.0% and 1.2%. Two runs of the pre-stage tree differ on 1.5%, 3.5% and 1.2% (`imgdiff-identity.txt`, `imgdiff-noise.txt`). - Harbour's diff map puts every changed pixel on rain, water and sky (`diffmap-harbour-identity.png`). - Dry Harbour night, pre against live: 1.26%, against 1.94% between two pre runs (`imgdiff-dry.txt`). - Non-timing report fields are equal except `rendered_objects` (+52) and `draw_calls` (+1) in all three (`fields-identity.txt`). I attribute these to the photo bar's new hint line: it has 52 non-space glyphs and is on screen while the report is written in photo mode. I did not confirm this with a run without the line. `courtyard.png` hides the UI. [Orchestrator correction after the handoff check, 24 September 2026: `pool_energy_delivered` and `pool_energy_ratio` also differ in the Harbour and quarter pairs (`fields-identity.txt`, for example Harbour 14.891 → 14.929); two pre-stage Harbour runs move them by as much (14.891 → 14.868, `fields-noise.txt`), and the runbook's journey comparison skips `pool_energy_*`, so that is noise. The Fable review confirmed the +52: the hint string at `scripts/editor_ui.gd:376` has exactly 52 non-space glyphs, and its label batch is the +1 draw call, the same in all three scenes.] - `after-*-persp`, and `sheet-projections.png` (rows Harbour, quarter, Styles; columns before, after ortho, after perspective). - `after-harbour-persp-fov16/-fov36`, the photo views and `sheet-lens-and-photo.png`. Findings from the captures: - Harbour and Styles under perspective read as a model on a table. The near side is larger, walls converge, and the island's silhouette has depth. - On the canal quarter's close camera (size 21), the 24 degree lens stands 49 m off. The foreground quay is roughly 1.5 times larger (an estimate from the distances, not measured), and the round tower's top leaves the frame. The composition changes a lot at close framings. - The near water mirrors less of the town under perspective. The bottom rows are seen about 12 degrees more steeply, so reflected rays clear the houses (a debug probe of the hits showed a narrower band with 72 or 400 steps). This is the physics of a steeper view, but canal night's reflections are weaker. - The photo depth of field (`after-harbour-photo-persp-dof`, `-free-high`) gives the strongest miniature read of anything in this plan so far. - A low free camera (`-free`, `-free-nolens`) shows flat masonry (the brick LOD follows the orbit target) and large texels. The ground-plane depth model blurs walls near the eye as though they were ground. Measurements, same sitting, against the pre-stage tree (`artifacts/feel/15-D2/bench/bench.sh`, `table.txt`). Three alternating rounds, vsync off, 1920x1080, Day 13:00. Mean frame in ms, median (range): | Scene | Pre-stage | D2 orthographic | D2 perspective | |---|---:|---:|---:| | Harbour | 8.36 (8.21 to 8.44) | 7.99 (7.79 to 8.95) | 7.84 (7.73 to 7.97) | | Dense | 13.49 (13.43 to 14.62) | 13.54 (13.49 to 13.71) | 13.09 (13.05 to 13.31) | The orthographic ranges overlap the pre-stage ones. Perspective is no dearer. No drag harness run: D2 is not a track-A part. Recommendation for decision D2 (the owner's call, amendment 8): - Keep orthographic as the editing default. It keeps a constant texel size for the pixel art and the full canal-night reflections, and it frames the close journey views as composed. - Give photo mode the perspective lens with the pinned-focus depth of field now. Pinning a focus point already turns on the depth of field in both projections. - Leave perspective editing as the non-default Scenery option until the owner has used it by hand. If Glade becomes the look, pair it with perspective, where losing reflections matters less. - A perspective default would want three follow-ups: - a field of view that widens as the view closes in, so close framings do not magnify the foreground; - the brick LOD keyed to the eye; - a depth-aware depth of field. Review (orchestrator, 24 September 2026): Fable 5.1 review in run `wf_486a4c5a-41f`, acceptance met, no must-fix, five notes: photo-mode field-of-view keys change the saved camera size and `world.fov` for the rest of the session; `pool_energy_*` also differ, as noise (corrected above); the +52 is confirmed as the hint line; the mirror march can smear with a low free camera over water; the journey was still to run. The Sonnet handoff check flagged only the `pool_energy_*` sentence. Journey `15-D2`: every view differs from `14-D1` only by `rendered_objects` +52 and `draw_calls` +1 (the photo hint line; scene content unchanged). Sheets sent to the owner; decision D2 is pending. Not done: - No hands-on windowed session with the perspective editor. Drag feel, handle use and zoom feel were checked headless only. - The depth of field models only the ground plane. Compatibility has no engine depth of field, and the canvas lens cannot read depth. - Photo settings have keys and a hint line, no sliders. - The Scenery field of view keeps the framing at the target, while photo mode's keeps the eye. These are two meanings, on purpose. - The painted sky strip (`SKY_HORIZON`) stays screen-fixed under perspective. - The texel density and the brick LOD are not adapted. - The six journey views are left to the orchestrator (`tools/feel_journey.sh 15-D2`). With the defaults they should match `14-D1` except `rendered_objects` and `draw_calls`, as above. - Platforms: Mac run; Windows not run (`tools/Test.ps1` gained the suite but was not run). **Feel D1a Glade profile, part a (24 September 2026):** Opus 5.5 implementer. Scenery has a new Look option: Canal night (the default, unchanged) or Glade, a non-default Tiny Glade-like look. Choosing a look is one undoable edit. It writes the optional `environment.look` and the look's starting settings: Glade gives pixel size 1, the tilt-shift lens on, the Day preset and rain 0; Canal night gives pixel size 2, the lens off, Night and rain 0.35. All of these can still be changed afterwards. What has no knob of its own follows the key (`LookProfile`): - pixel size 1 with nothing quantised: no palette step exists in the pipeline, but the foliage's three-tone banding, banded shadow edge and stepped rim are replaced by smooth shading (a `banding` uniform, a uniform branch, so the banded path is untouched); - the AgX mapper (Godot 4.6's neutral, hue-preserving option; exposure 1.5, white 6, contrast 1.25, saturation 1.3 through the environment adjustment); - low dynamic range: no engine glow, no presentation bloom (the pass now skips its 49 taps when `glow_strength` is 0, which canal night never is), glitter at 35%, outline at 30%; - warm soft daylight: the sun pulled 60% toward `ffcc8f` at 80% energy, the ambient 45% toward a warm grey at 130% fill, a warmer haze, sun shadow blur 2.5. Lamps and windows are untouched, so the C3a dusk runs as before; - ambient occlusion and contact shadows from a new pass (`scripts/contact_shade.gd`, `shaders/contact_shade.gdshader`): a full-screen quad after the opaque frame and before the height fog, reading the depth buffer, 16 fixed samples on two rings, radius 0.7 m, darkening creases toward a warm shade. It is drawn only in Glade with the contact-shadow switch on. **Finding:** the engine's SSAO, behind the "Soft contact shadows" switch since the first lighting pass, shows almost nothing here. At canal night it darkens 1.0% of Harbour's pixels by more than 4 levels, and at intensity 8 and radius 3 it changes 0.4%. Two identical runs differ on 1.0 to 2.2% (`artifacts/feel/14-D1/imgdiff-final.txt`, `imgdiff-noise.txt`, `probe/imgdiff-2.log.out`). Its settings are left as they were in both looks. The new pass darkens 11.4% of Harbour's pixels (`ssao-probe/glade-shade-off` against `glade-shade-on`); - a denser meadow: a tuft on every meadow cell, two on three cells in four. Harbour goes from 462 blades on 154 of 768 meadow cells to 3,285 on 638; Styles from 414 to 3,270. The canal rule (one tuft on every fourth cell by its mark) is unchanged and tested. A terrain chunk's fingerprint gains "glade" only under Glade. `--look=glade|canal` does the same for smoke, benchmark and drag runs, before the other atmosphere arguments, and a Glade report carries `look`. Save version stays 2. A scene without the key is canal night, and nothing writes the key for canal. An older build ignores the key, so it opens a Glade scene at pixel size 1 with the lens, in canal colours. Files: `scripts/look_profile.gd` (new, `class_name LookProfile`; editor import `artifacts/feel/14-D1/a-import.log`), `scripts/contact_shade.gd` and `shaders/contact_shade.gdshader` (new), `scripts/lighting_profile.gd` (`profile` applies the look), `scripts/world_view.gd` (`sync_environment`, the pass node, the chunk fingerprint), `scripts/architecture.gd` (`meadow_tufts`, `terrain_chunk`), `scripts/scene_state.gd` (validation), `scripts/main.gd` (`change_environment` "look", `--look=`, report), `scripts/editor_ui.gd` (Look option), `shaders/foliage.gdshader`, `shaders/presentation.gdshader`, `tests/test_look.gd` (new), `tools/test.sh`, `tools/Test.ps1`, `README.md` (old copy `artifacts/feel/14-D1/README.md.pre-D1`). Snapshot: `build/backup-feel/14-D1/` (untouched). Diff: `artifacts/feel/14-D1/a-diff.txt`. Tests: - Test first: `artifacts/feel/14-D1/a-testfirst-test_look.log(.out)`, 3 checks, 3 failures (no `look_profile.gd`, no bloom skip, no banding uniform). - `test_look` 197 checks, 0 failures (`a-final-test_look.log.out`, after the last tuning). It checks: - the key is optional, validated (`canal`/`glade` only) and survives JSON, and the five stock scenes carry and save none; - what choosing each look writes, and that an unknown look changes nothing; - canal lighting identical to `at_hour` at 11 hours and the three presets; Glade warmer, softer, lifted (fill to key more than 1.3 times canal's), no bloom, same lamps and windows; - canal render settings equal to the engine defaults and the old constants; - the meadow counts above; - in the editor (main scene, headless): choosing Glade is one undo step and reaches the mapper, glow, shade pass, shadow blur, foliage banding, bloom, lens and pixel size 1; the meadow geometry grows; undo returns every one and the document exactly; redo returns them; the contact-shadow switch hides the pass; the Look option shows and emits by index. - Full suite in the foreground (`artifacts/feel/14-D1/a-full-test.log`, per-suite `full-a/`): 32 suites, 17,189 checks, 0 failures, `--verify` `PIXELPUNK_INTEGRATION []`. It ran before the last tuning change (Glade exposure 1.25 to 1.5, a constant). `test_look` and `test_lighting` (155 / 0) were re-run after it. Changed expectations: none. Captures (`artifacts/feel-journey/14-D1/pkg/`, 1600x1000 `--smoke`, Harbour at the journey camera, the artwork scene at its own; `artifacts/feel/14-D1/cap_all.sh`; I looked at every one I cite): - `before-{harbour,artwork}-canal-night(-grey)/` (pre-stage tree) and `after-{harbour,artwork}-canal-night(-grey)/`: the same pictures. Every non-timing `performance.json` field is equal except the light pool's delivered energy, which is a fade timing (`artifacts/feel/14-D1/fields-captures.txt`). Dry pairs (`artifacts/feel/14-D1/identity/`) differ on 0.17 to 1.5% of pixels, inside the 1.0 to 2.2% of two runs of one tree (`imgdiff-captures.txt`, `imgdiff-noise.txt`). - `after-*-glade(-grey)/` and, for the same hour in the old look, `after-*-canal-day(-grey)/`. The sheets: `sheet-profiles.png` (rows Harbour and the artwork scene; columns canal night, its greyscale, Glade, its greyscale) and `sheet-day-vs-glade.png` (canal day beside Glade). - My judgement from these (before the review fix below; the `after-*-glade(-grey)/` folders and both sheets have since been re-captured, the old ones are in `artifacts/feel/14-D1/fix/pre-fix-captures/`): - Glade reads as a sunlit miniature. Fine pixels, warm light, lifted shade, creases picked out by the occlusion, grass with visible blades, a clearly blurred top and bottom on the artwork scene. - Greyscale Glade has a lower range than canal day: Harbour luma sd 53 against 62, the artwork scene 66 against 77 (`imgdiff-final.txt`). - Harbour greyscale: the island reads as one mass against calm, bright water. The walls carry the fine detail. The meadow is calm but speckled by the new tufts and their occlusion. There is no single focal point inside the island. - The artwork scene greyscale: the round tower is the darkest vertical and the strongest anchor. The paving is as busy as the walls, so the ground is not calm there. - The lens radius is 4.68 window pixels at pixel size 1 (amount 0.6), half of what the same amount gives at size 2. It is subtle on Harbour's wide view. - Tuning probes are in `artifacts/feel/14-D1/probe/`. Measurements, same sitting, against the pre-stage tree (`tools/feel_snapshot.sh tree 14-D1`), three alternating rounds, vsync off, 1920x1080, `--churn=100000` (`artifacts/feel/14-D1/bench/bench.sh`, `table.txt`, `fields.txt`). Mean frame in ms, median (range): | Scene | Pre-stage, canal day | D1, canal day | D1, Glade | D1, Glade without the pass | |---|---:|---:|---:|---:| | Harbour | 7.80 (7.80 to 7.98) | 7.86 (7.84 to 7.95) | 11.75 (11.44 to 11.84) | 10.71 (10.59 to 11.04) | | Dense | 13.75 (13.51 to 13.79) | 13.49 (13.36 to 13.63) | 18.86 (18.64 to 18.88) | 15.98 (15.85 to 16.01) | Canal is unchanged: the ranges overlap and every non-timing field is equal apart from `light_select_calls`, which counts a 0.25 s timer. Glade costs +3.9 ms on Harbour and +5.4 ms on the dense fixture. The occlusion pass accounts for 1.0 and 2.9 ms of that. The rest is the frame at pixel size 1 (four times the pixels: 1920x1080 against 960x540), the meadow and the lens, not separated further. Draw calls drop in Glade (Harbour 1,007 to 999, dense 2,124 to 1,968) because it has no rain. No drag harness run: D1 is not a track-A part. Review (orchestrator, 24 September 2026): Fable 5.1 review in run `wf_73265282-a42`, one must-fix (the Glade greyscale ground too busy, no single focal point on Harbour), fixed in an Opus pass (see Review fix) and rechecked by Fable: acceptance met, focal-point verdict left to the owner under amendment 8. The Sonnet handoff check could not trace "0.17 to 1.5%"; the orchestrator traced it (0.17% and 0.25% in `imgdiff-captures.txt`, 1.46% for pre-2 against live-2 in `imgdiff-noise.txt`). Journey `14-D1` (default look): counts identical to `13-E1`. Sheets sent to the owner; verdict pending. Review fix (24 September 2026, Opus 5.5). The review had one must-fix finding: the Glade greyscale captures did not meet the acceptance clause "one clear focal point, calm low-frequency ground and detailed walls". Harbour had no single focal point, its meadow was speckled by the denser tufts and their occlusion, and the artwork scene's paving was as busy as its walls (the two judgement bullets above said the same). Everything else in the part was found correct. What I changed, all under Glade only: - A calm ground, from a new `ground_calm` uniform (canal 0, Glade 0.85) in `shaders/masonry_body.gdshaderinc` (paving tops only: the joints, per-stone value, grain and relief give way to the broad weathering) and `shaders/ground.gdshader` (meadow turf clumps, daisies, pebbles and ripples). Each ground is also lifted: the paving by 0.3 and the meadow, earth and sand by 0.4 times the calm, so darker buildings stand on a lighter plane. Walls are untouched. It is a uniform branch, so at 0 the old path runs. - The occlusion pass leaves blades alone. On ground facing up, a neighbour counts only once it rises more than `shade_min_rise` (0.22 m) above it, so tufts and pebbles no longer shade the meadow. A wall foot, a step or a bench still does. A sliver whose neighbours 2 pixels to each side are both behind it (`shade_thin`, a blade or a rail) is skipped. The cost is two extra depth reads. - Glade tufts take the meadow's own colour darkened by 0.3 (`LookProfile.tuft_color`), so a lit blade reads at about the ground's value. Canal keeps `789269`. - Glade meadow cells drop the small per-cell lightness step (`Architecture.ground_color`). Once the turf was calm, that step showed the one-metre cells as a checkerboard (`artifacts/feel/14-D1/fix/look-after-harbour-island.png`, a probe taken before this step, against `look-p5-harbour-island.png`, taken after it). The canal step and all other finishes are unchanged. Files: `scripts/look_profile.gd`, `scripts/contact_shade.gd`, `scripts/world_view.gd`, `scripts/architecture.gd`, `shaders/contact_shade.gdshader`, `shaders/masonry_body.gdshaderinc`, `shaders/ground.gdshader`, `tests/test_look.gd` and `README.md` (one clause). The pre-fix copies are in `artifacts/feel/14-D1/fix/pre-fix/` and the diff is `fix/fix-diff.txt`. Tests: - Test first: `artifacts/feel/14-D1/a-fix-testfirst-test_look.log(.out)`, 242 checks, 30 failures. `a-fix-testfirst-2-test_look.log.out` is a parse error on the missing `Architecture.ground_color`. - `test_look` now has 248 checks and 0 failures (`a-fix-final-test_look.log.out`). The new checks cover the shader switches and their defaults, canal's zeros, Glade's calm, rise and sliver values, both tuft colours, the per-cell step in both looks, and in the editor that the ground materials and their build variants and the pass carry them after choose, undo and redo. - `tools/test.sh` in the foreground (`artifacts/feel/14-D1/a-fix-full-test.log`, per-suite `full-fix/`): 32 suites, 17,240 checks, 0 failures, and `--verify` gave `PIXELPUNK_INTEGRATION []`. It ran before a last refactor of `tuft_color`, which kept the same colour; `test_look` was re-run after it. Changed expectations: none. The earlier checks are unchanged. Captures (same cameras, `artifacts/feel/14-D1/cap_all.sh after-glade`; I looked at each): - `after-{harbour,artwork}-glade(-grey)/`, `sheet-profiles.png` and `sheet-day-vs-glade.png` were re-captured. - Harbour greyscale: the meadow is a calm, lighter plane with faint blades. The house and tall tower make the darkest and largest mass and are the anchor. The second tower and the tea house compete less than before, but they still compete. - The artwork scene greyscale: the paving on the plaza, the quays and the pier is calm and lighter. The brick walls keep their detail, and the round tower is still the anchor. - Colour: Glade's meadow is a brighter, yellower green than before. - High-frequency measure, the mean luma step between neighbouring pixels on ground-only crops, before and after (`fix/measure-ground.txt`, `fix/crop.gd`): | Crop | Before | After | |---|---:|---:| | Harbour meadow | 7.55 | 4.61 | | Harbour lower island | 10.49 | 8.71 | | Artwork plaza | 8.42 | 6.09 | | Artwork pier | 4.62 | 2.03 | | Round tower wall (control) | 5.88 | 5.92 | - Canal night is unchanged. Fresh dry night captures against the part's own (`fix/identity/`, `fix/imgdiff-fix.txt`) differ on 4.2 to 4.3% of pixels with equal mean luma. The difference maps (`fix/diffmap-harbour-night.png`, `diffmap-artwork-night.png`) put every changed pixel on water, sky, stars and swaying leaves, none on buildings or ground. This share is above the 1.0 to 2.2% noise band recorded earlier. I read it as animation phase, but I have not measured it against two fresh runs of one tree. Measurements, same sitting (`fix/bench/bench.sh`: canal day live, Glade in a pre-fix tree built in the scratchpad, Glade live; vsync off, 1920x1080, `--churn=100000`, three alternating rounds; `fix/bench/harbour.txt`, `dense.txt`). Mean frame in ms, median (range): | Scene | Canal day | Glade before fix | Glade after fix | |---|---:|---:|---:| | Harbour | 7.70 (7.57 to 7.74) | 11.22 (11.18 to 11.28) | 11.33 (11.31 to 11.70) | | Dense | 14.68 (14.24 to 14.79) | 19.40 (19.05 to 19.54) | 19.99 (19.16 to 20.63) | The fix costs about +0.1 ms on Harbour and +0.6 ms on the dense fixture, where the ranges overlap. This machine ran slower this sitting than in the part's table above. One dense pre-fix run needed three tries. Not done in the fix: whether the focal point is clear enough is still the owner's call (amendment 8). No vignette or change to the lens amount was tried, because the finding named the ground and the occlusion. Glade tufts still stand at fixed offsets in each cell, so up close they form a regular pattern. Not done: - Toy-photography reference tuning (plan D1 bullet 3). The values were tuned by eye from these captures only, with no reference images. - The occlusion pass is 2.9 ms on the dense fixture. Half resolution or fewer samples would cut that, not tried. - The look does not change the tilt amount, so the blur is half as wide at pixel size 1. - The sky and water colours are the day profile's, only warmed through the haze. Since the review fix, the ground has Glade's calm, lifted tone and the tufts have their own colour. - The engine SSAO finding is not acted on for canal night, whose look must not change. - The six journey views are left to the orchestrator (`tools/feel_journey.sh 14-D1`). With the default look they should be count-identical to `13-E1`. - The owner's verdict on D1 (plan section 5). - Platforms: Mac run; Windows not run (`tools/Test.ps1` gained the suite but was not run). SOURCE: docs/tiny-glade-feel-handoff.md, snapshot lines 23 to 46 and 287 to 290 **C1b result (10:25).** Implemented, Fable review found one must-fix (the new timing checks failed in two of three runs under ordinary load); the fix pass loosened those bounds with retries and added a check that the view mean is within 0.5 ms; the Fable recheck found nothing open; the Sonnet handoff check found nothing unverified. The night light-admit change without `--creatures` was capture-timing noise (14 later runs on both trees all 16/0/0). Accepted with these open (all in the Feel C1b entry): creature CPU on Harbour drag steps is mean 0.36 to 0.39 ms and p95 0.44 to 0.50 ms, but p99 reaches 1.08 ms and sculpt dabs 2.2 ms; a re-plan never finishes during a drag, so arrivals come about 1.2 s after the hand stops; more than six edited parts startle creatures inside the union box of all of them; a forced sync (load, starter, forced undo) leaves the old population over new land for about 1.2 s; the cat is flat gold with no stripes, and the owner has not seen it. **Orchestrator checks (10:25 to 10:40).** Journey `12-C1`: every count identical to `11-C3`. Audio `listens` and `voices` and `p95_frame_ms` differ because the display vsynced at 120 Hz instead of about 180 Hz, so the frame-counted captures ran 0.7 s longer (`elapsed_ms` 3,161 to 3,872 on Harbour day). Whole-stage measurement (`artifacts/feel/12-C1/orch-bench/bench.sh`, `table.txt`; median of three alternating vsync-off 1920x1080 runs against `build/backup-feel/12-C1/`): Harbour 7.79 ms before, 7.78 without creatures, 7.94 with (26 creatures, 1,007 to 1,016 draw calls); dense 13.41, 13.36, 13.46 (18 creatures). Round 1 on Harbour lost its `performance.json` in all nine tries (both trees) and passed when re-run alone (`results-round1-rerun.txt`). `notes.json` and the digest are updated (digest 83 MB). **Found by the orchestrator, not fixed:** `--creatures` is the only switch (`scripts/main.gd:340`), so a normal launch of the app shows no creatures, while plan section C1 asks only that they be frozen or absent in smoke captures. Queued at the head of the polish part (section 8); raised with the owner. **E1 launched 10:45** after snapshot `13-E1`: run `wf_4eb9a71d-ccf` from the canonical `feel-stage.js`, `{stage: 13, date: "24 September 2026"}`, journal under this session's `subagents/workflows/wf_4eb9a71d-ccf/`. **E1 result (about 11:05).** Implemented, Fable review with no must-fix (six notes), Sonnet handoff check clean; suite 31 / 16,992 / 0. Journey `13-E1`: every non-audio count identical to `12-C1`; audio `listens` and `voices` move with `elapsed_ms` in both directions at the same refresh rate, so they are timing, not content. E1 is a single part, so its own frame-cost bench against `13-E1` is the whole-stage one (Harbour 7.80 to 8.05 ms median with overlapping ranges, every non-timing field identical, `artifacts/feel/13-E1/bench/`). Open: the brackets barely read at the demo's own zoom; the stepping-stone quays have no shore; the turn resets each launch; three captures came out oddly framed and were fine re-run (`artifacts/feel/13-E1/odd-framing/`, cause unproven). `notes.json` and digest updated (86 MB). **D1 launched 11:10** after snapshot `14-D1`: run `wf_73265282-a42`, `{stage: 14, date: "24 September 2026"}`. **D1 result (about 12:30).** Run `wf_73265282-a42`, 5 agents, 0.97 M tokens, 1 h 23. Fable review: one must-fix (Glade greyscale ground too busy, no single focal point on Harbour); the Opus fix calmed and lifted ground under Glade only and made the occlusion pass skip grass blades; the Fable recheck met acceptance and left the focal-point verdict to the owner (amendment 8). Suite 32 / 17,240 / 0. The Sonnet handoff check could not trace the D1a entry's "0.17 to 1.5%" dry-pair range; the orchestrator traced it: 0.17 and 0.25% in `artifacts/feel/14-D1/imgdiff-captures.txt` and 1.46% (pre-2 against live-2) in `imgdiff-noise.txt`, so the entry stands. The recheck also notes that the entry's unmeasured 4.2 to 4.3% dry-night difference is now measured as noise in `artifacts/feel/14-D1/review/imgdiff-recheck.txt`. Journey `14-D1` (default look): every non-audio count identical to `13-E1`. Cost of Glade (fix bench): Harbour 11.3 ms against 7.7 canal day, dense 20.0 against 14.7. Sheets sent to the owner 12:35 (`artifacts/feel-journey/14-D1/pkg/sheet-day-vs-glade.png`, `sheet-profiles.png`); **owner verdict pending**: the focal point, the cool cyan sky and water, the tuft grid, the unoptimised occlusion pass. **D2 launched 12:36** after snapshot `15-D2`: run `wf_486a4c5a-41f`, `{stage: 15, date: "24 September 2026"}`. **D2 result (about 13:20).** Run `wf_486a4c5a-41f`, 3 agents, 0.70 M tokens, 44 min. Fable review: acceptance met, no must-fix. Suite 33 / 17,367 / 0. The Sonnet handoff check flagged the entry's "only two fields differ": `pool_energy_*` also move, by no more than between two pre-stage runs; the orchestrator added a correction under that sentence. Journey `15-D2`: every view differs from `14-D1` only by `rendered_objects` +52 and `draw_calls` +1, the photo-mode hint label (52 glyphs, on screen while the report is written); the scene is unchanged. The orchestrator also filled the "Review:" lines of the C1b, E1, D1 and D2 entries in `docs/visual-handoff.md` (backup `build/backup-feel/visual-handoff.md.pre-review-fill-24sep`); older entries keep their placeholders. Sheets and the written recommendation (keep orthographic for editing; perspective lens and depth of field in photo mode now; perspective editing non-default until tried by hand) sent to the owner 13:25. **Owner decisions pending: D1 look, D2 camera, blank-canvas ducks (below).** **Every planned stage is done.** Nothing is running; no Godot is running; the keep-awake timer was stopped at 13:25. Left: the owner's D1 and D2 verdicts, then the polish part (section 8), led by creatures in a normal launch. **Owner question 13:00: creature behaviour.** Answered from the code: creatures are derived from the scene, so they fill in as it is built (roofs and lanterns bring pigeons, bunting, flags, nests and chimneys sparrows, paving walking pigeons, a sunny flat roof or wall top the cat, night fireflies and moths). Ducks are the exception: a blank canvas is all open water, so `Creatures` (`scripts/creatures.gd:854`, homes fall back to any open water when there is no shore) would give it 8 ducks at once (not checked with a run). Offered to the owner: make ducks wait for some land, in the polish part. **Polish stage P1 (from about 13:40).** Owner decisions at about 13:30 (section 7): ducks wait for land; D2 recommendation accepted. The polish is workflow stage 19, step `16-P1`, in a copy of the workflow script, `artifacts/feel/16-P1/feel-stage-polish.js` (auto mode refuses edits under `.claude/`; the copy is the canonical script plus stage 19 and the stage-range check raised to 19). Four parts, each in its own run with its own pre-part snapshot (`16-P1` before a, then `16-P1-b`, `16-P1-c`, `16-P1-d`): **a** creatures on by default in an interactive launch with a Scenery switch saved as a user preference (off in automation unless `--creatures`), ducks wait for land, the cat matched to the sheet; **b** photo mode switches to the perspective lens with a default depth of field and restores the editing camera on leaving, photo field of view runtime-only; **c** release work spread over frames and ivy memoised (Harbour release at most 25 ms, dense at most 120 ms, no spread frame over 8 ms, drag steps within 2 ms); **d** foliage variants merged into fewer MultiMeshes with the picture unchanged, and shader warm-up at start-up (start-up at most +1.5 s). Launch each with `Workflow({scriptPath: ".../artifacts/feel/16-P1/feel-stage-polish.js", args: {stage: 19, parts: [""], date}})`; journey `16-P1` after d, against `15-D2` (d changes draw calls by design). - **24 September 2026, about 13:30:** ducks wait for land (a blank canvas gets no ducks); the D2 recommendation is accepted (orthographic stays the editing default, photo mode gets the perspective lens and pinned-focus depth of field now, perspective editing stays a non-default option). D1 (Glade look) verdict still pending.