PIXELPUNK JOURNAL / CHAPTER 02 Editorial evidence captured 23 September 2026. These are historical project notes. Status and next-step instructions in these excerpts belong to their original development session. Performance and test results quoted here are from those sessions, not tests performed by the journal build. ======================================================================== HANDOFF.md | original lines 185–208 ======================================================================== ### Checkpoint All work stopped at a verified and exported checkpoint: `artifacts/phase05-openings/checkpoint.json` records test results, the verify result, smoke reports, source SHA-256 values, the Mac PCK hash and the Windows executable hash. Backups from before this milestone are in `artifacts/phase05-openings/before/` (evidence, not rollback instructions). No usage tool exists in this harness; do not redeem reset credits or create new Codex tasks. #### Design (fixed) - **`Openings` (scripts/openings.gd, new `class_name`, registered with the editor import step)**. Everything is derived, never stored. - **Path sampling**: `runs(a, b, normal, state)` samples a wall face every `SAMPLE` 0.25 m and probes the terrain finish `PROBE` 0.4 m either side; a sample is open when both cells are cobbles (3) or earth (4). Consecutive open samples form a run; runs narrower than `MIN_WIDTH` 0.9 m are widened symmetrically, runs wider than the face minus 0.6 m are ignored (a building standing on a road keeps its walls), runs are clamped 0.15 m from the face ends and merged. - **Kinds** (`face_openings`): the wall must be at least `MIN_WALL` 1.6 m tall and its foot no more than `MAX_RISE` 1.25 m above the ground outside (a lift beyond that has pillars, not doors). Width under `DOOR_WIDTH` 1.4 m on a wall under `LOW_WALL` 3.2 m → `door` (height min 2.1, wall − 0.5). Otherwise `arch`, height `clamp(2.3 + 0.15·width, 1.15, wall − 0.45)`, semicircular head of radius `min(width/2, 0.45·height)`; a run wider than 3 m splits into `ceil(width / ARCH_BAY 2.4)` bays separated by `PIER` 0.36 m of wall, each bay an arch with `pier_after`. A foot 0.12–1.25 m above the ground gets `steps` (0.19 m risers, 0.32 m treads). Holes are `{x0, x1, y0, y1, kind, height, radius, rise, pier_after}` in centred face coordinates (x from the middle of the face, y from the lifted wall foot); an arch hole is cut 0.05 m above its apex so the spandrel never pinches. - **Buildings**: `holes_by_side(o, state)` returns four hole lists (sides 0–3, the canal convention: normal `(sin(side·π/2), 0, cos(side·π/2))`), rectangular buildings only; towers get none. `panel_holes(o, state, face)` maps them onto a cluster panel (nearest side by normal, shifted to the panel's centre and band foot, clipped to its extent, `clipped` flag). - **Windows**: part kind `window` (in `KINDS`, `WALL_PARTS`, `FITTINGS`; params `span` in `WINDOW_SPANS` [0.6, 0.9, 1.5], `dormer` bool). `window_groups` reads each non-dormer window's side from its local rotation (`round(rot / (π/2)) mod 4`), its x from the local position, height 1.4 (1.55 for spans ≥ 1.4); a sill within 0.35 m of the foot makes a `door_window` (y 0 to `DOOR_HEIGHT` 2.1). Along a side, sorted by x, windows whose edges come within `WINDOW_GAP` 0.2 m and whose centres are within 0.3 m vertically fuse, up to `WINDOW_FUSE` 3 lights; doors never fuse. A group is one hole and one frame with mullion boxes at the joins. - **Cutter**: `cut(rects, holes)` subtracts each hole from `[x, y, w, h]` rectangles into left/right columns and above/below rows; the `DAMAGE` 0.35 m strip beside every hole carries `DAMAGE_ALPHA` 0.8 as the vertex colour alpha. `emit_quads` (cluster panels, z 0), `emit_boxes` (single buildings: front offset and depth), `spandrels`/`emit_polygon` (wall left above an arch's springing line, `Geometry2D.triangulate_polygon`, wound for `_append_face`). - **Fittings drawn in the face frame** (x along the wall, y up from the foot, z outward): `portal` (passage of `interior` boxes 0.24 m deep for a door or 0.5–1.3 m for an arch, lit 75 % of the time with a hanging lamp and a pool quad outside; timber door leaf with panels and knob; trim jambs, lintel or segmented arch ring of 10 beams plus keystone; threshold slab or `steps`; a pilaster over each pier; `applied` draws only the surface parts). `window_group` (canal recess window or `ClusterArchitecture.styled_window`, mullions), `tower_windows` (surface-applied on the cylinder at the clicked angle), `dormers` (a `RoofProfile.ray_hit` from above finds the slope; box body span + 0.6 wide, 1.45 tall, 1.4 deep, sunk 0.35 m, gable roof, ridge beam, front light). - **Freehand walls**: `wall_gaps(piece, ground, rise, ring, state)` tests each sample's two sides; runs of up to `MAX_WALL_GAP` 8 samples become gaps (a wall drawn along a path stays whole). Rise under `GATEWAY_RISE` 1.9 → `gate`: body, base and cap absent at the gap samples, posts at the jamb samples, a timber `gate` leaf when the jambs are within `GATE_WIDTH` 1.6 m, merlons skipped over the gap. Otherwise `gateway`: body and base absent, cap kept, a lintel loft (widen 0.025, lightened) from `ground + min(rise − 0.45, 2.3)` to the top over the jamb-to-jamb samples with a keystone, posts at the jambs. - `summary(state)` counts `doors, arches, piers, steps, gates, gateways, windows, groups, fused, door_windows, dormers` for tests and checks. - **Generators**. `Architecture.canal_building`: every bay is a rectangle, bay windows are holes too, `cut` + `emit_boxes` (front 0.01, depth 0.26), spandrels, windows, shop panel, portals and window groups, floor lines and cornice cut around doorways. `Architecture.styled_building`: the solid body box is now a hollow shell (floor slab, ceiling slab under the roof, four walls 0.3 m thick, the z walls shortened 0.3 m at each end so no faces are coplanar) cut by holes; bay windows skip blocked bays; floor lines cut. Towers keep their cylinder (no path openings; placed windows applied). `ClusterArchitecture.facade`: rectangles per bay per row, bay windows as holes, `panel_holes`, quads, spandrels, pilasters skipped inside holes, portals only on the band that holds the hole foot, courses cut. Dormers are drawn after `profile_roof` in both single-building generators (also reached by the cluster roof pass). `Architecture.fittings` skips `window` children (they are cut, not strung around the building; that bug produced string lights on every house with a window until fixed). `Architecture.decoration` has a `window` case for the placement ghost. - **Wall path generator**: gaps are applied to the band arrays before lofting; the lintel band, keystone, jamb posts (added to the pillar `ends` list, deduplicated) and gate leaf follow. - **State**: `finish_ring(o)` (terrain finish of every cell within 0.7 m of a member's footprint or wall samples) joins the cluster signature in `WorldView._sync_clusters`, so painting a path up to a wall regenerates exactly the clusters it touches. `dimensions("window")` = span + 0.3 × 1.6 × 0.3. `validate_document` checks `span` range and `dormer` type. `placement_error`: a dormer needs `parent.kind == "building"` with a pitched roof and a local position within the footprint between eave − 0.3 and eave + `roof_rise` ("Dormers need a pitched roof." / "This resize would leave a dormer off the roof."); other windows use the wall-part checks. - **Editor**: `main.gd` candidate flow lets the `window` tool point at a pitched roof (`hit.roof`) → `params.dormer = true`, position = the hit point, rotation from the horizontal part of the roof normal; toast for `window`; params default `span` 0.9. `editor_ui.gd`: **Width** option (Narrow/Standard/Wide → `WINDOW_SPANS`) and a help label for the window tool; `data/catalog.tres` has the **Window** card in Details. - **Shader**: `shaders/masonry.gdshader` non-top branch reads `damage = clamp((1 − alpha)·5, 0, 1)`: deeper joints and a scatter of bare bricks. Wall colours elsewhere keep alpha 1; top faces are unaffected (alpha 0.8 falls in the flagstone branch like alpha 1). #### Opening diorama "Harbour steps" additions `SampleScene.harbour()` now paints a 3-cell cobbled passage through the fish market (arches with steps on both faces), an earth path through the garden wall (gate), a lane to the low wall by the tea house (gate), and the way out through the battlement, which is now 2.4 m tall with merlons (gateway); the existing earth path at z 2 opens arches through the canal wing house by itself. Placed windows: a fused row of three on the L-house front, a wide door on the wing, two dormers on the hall, one on the tea house. The planter on the battlement moved to x −8.5 and a tree moved off the new path. 136 parts. `tests/test_building_tools.gd` checks `Openings.summary(harbour)`: arches ≥ 4 with steps ≥ 2, fused ≥ 1, door ≥ 1, dormers ≥ 2, gate ≥ 1 and gateway ≥ 1. ======================================================================== HANDOFF.md | original lines 250–265 ======================================================================== ### Checkpoint All work stopped at a verified checkpoint: `artifacts/phase04-supports/checkpoint.json` records test results, smoke reports, source SHA-256 values and, when the exports were rebuilt, the Mac PCK hash and the Windows executable hash. Backups from before this milestone are in `artifacts/phase04-supports/before/` (evidence, not rollback instructions). No usage tool exists in this harness; do not redeem reset credits or create new Codex tasks. #### Design (fixed) - **Site**: a ground building's `pos.y` is `DioramaState.site_height(o)`, the highest ground among `site_samples(o)` (outline every `SITE_STEP` 0.5 m plus every terrain vertex strictly inside the footprint), considering land only; over open water it is `LAND_Y`. `settle(o)` writes it (through `Vector3`, so snapshots round-trip), `settle_all()` runs at the end of every `sculpt` stamp; `paint` (carving) settles the buildings near the brush before every per-cell `all_supported` check. `main.gd` settles after placement (`object_candidate`), parameter edits, rotation and resize. `add()` does **not** settle (tests build terrain-less scenes at y 0). - **Placement**: `footprint_supported` only rejects footprints outside the canvas; land or water are both fine. `placement_error` for a ground building checks `|pos.y − site| ≤ SITE_TOLERANCE` (0.5) with the message "The building must rest on its foundation." `level_foundation` still exists (the windowed verify uses it for a flattened site) but placement no longer calls it. `protected_regions` now holds only ground bridge/stair landings. - **Support ground**: `support_ground(p)` is the terrain over land and `WATER_BED_Y` (−0.15, the quay wall depth) elsewhere. - **`Supports` (scripts/supports.gd)**: `plan(o, state)` returns, in the lifted local frame (y 0 = underside, y −lift = site): `courses` (skirt runs per edge and course: `a`, `b`, `normal`, `top`, `bottom`, `course`, `open_a/b`), `band` (recessed plinth edges), `brackets` (`p`, `normal`), `pillars` and `posts` (`p`, `top`, `bottom`), `struts` (`p`, `anchor`), `carried` (count). Rules: lift ≥ `BRACKET_LIFT` 0.6 → pillar positions (corners and every `PILLAR_SPACING` 3 m, ring for towers) classified against neighbouring bodies whose base lies below the underside: inside a body reaching the underside → carried (nothing drawn); inside a lower body → post to its top; within `STRUT_REACH` 1.2 m outside a body rising to within 0.35 m of the underside → strut anchored on its wall 0.8–1.5 m below the underside (at least 0.25 m above the body's base); otherwise a pillar to the support ground. 0.05 ≤ lift < 0.6 → recessed band around the perimeter plus corbel brackets every `BRACKET_SPACING` 1.1 m (skipped where carried). Any lift below 0.6 on the ground also gets the skirt: per edge, samples every 0.5 m, `COURSE` 0.4 m courses from the site down to `SKIRT_MAX` 2.5 m wherever land lies below the course top; below the cap, and at every pillar position over water, a pillar runs to the ground or bed. Stacked rooms (building parent) and null states are flat: no skirt. `summary(o, state)` gives counts (`courses`, `steps`, `brackets`, `pillars`, `posts`, `struts`, `carried`) for tests and checks. - **Generator**: `Architecture.supports(batch, o, color, state)` replaced `lift_pillars` (same four call sites: massing, canal and styled buildings, cluster members). Skirt courses are boxes along each edge with a growing outset per course (battered look), runs touching a corner extend past it; the band is inset 0.35 m; brackets are two stacked corbel boxes; pillars keep the footing and capital; struts are `beam`s in `Supports.TIMBER` with a pad under the tip and a block at the anchor. - **Cluster signature**: `WorldView` appends `state.support_heights(o)` (land flag and four vertex heights per cell under the footprint) for every ground building, so sculpting or carving under it regenerates the cluster. #### Opening diorama "Harbour steps" (added after the supports milestone) The user asked for a new diorama showcasing the improvements as the default on load, lived in. `SampleScene.harbour()` (scripts/sample_scene.gd, helpers `house`, `settled`) builds a hillside harbour town: two coplanar Victorian row houses on the slope (shared ridge, stone skirts), a medieval hall on the hilltop with a tower through its gable, a gate tower with a lifted gatehouse braced by timber struts over an earth path, a canal L-house with a lifted stacked room on brackets, a terrace with stairs, a fish market lifted 0.3 m on corbels with awning, neon and string lights, a low wall over a plinth and under a lifted Japanese tea house whose pillars stand in the water, a boathouse on stilts in the water with boats, a garden wall over a knoll, a battlement behind the hall, a timber bridge to the far bank, and roads, paths, sand, lanterns, benches, cafe tables, planting and quay fences. Interactive start (`automation_mode` empty) and `--smoke` without `--scene=` load it (`scene_name` "Harbour steps"); `--verify` still starts blank. Header button **Harbour** and the intro button "Explore the harbour" open it (`action("harbour")`, undoable "open harbour"). `tests/test_building_tools.gd` validates it, checks support kinds in use (skirts, brackets, pillars, struts, a house on the water), cluster merging, geometry and JSON round-trip. Render: `artifacts/phase04-supports/harbour/courtyard.png` and `editor.png`. ======================================================================== HANDOFF.md | original lines 303–320 ======================================================================== ## Previous continuation (2026-09-15, phase 3 union session, superseded) The user asked to check this handoff and continue from where the previous model stopped. The phase 0–3 notes below preserve historical design decisions and prior checkpoints; they do not override current implementation or test results. ### Checkpoint **HANDOFF SAVED after the wall-volume and trim milestone.** All work stopped at a verified and exported checkpoint: `artifacts/phase03-union/checkpoint.json` records test results, smoke reports, source SHA-256 values, the Mac PCK hash and the Windows executable hash. No code edits or engine processes are pending. Backups from before this milestone are in `artifacts/phase03-union/before/` (evidence, not rollback instructions). The previous model tracked Codex usage limits in this section and refreshed the file at 98%. This session ran under a different harness with no usage tool; the milestone notes below were written at completion instead. Do not redeem reset credits or create new Codex tasks. #### Implemented this session - **Wall volumes** (`DioramaState.wall_volumes`, `wall_blockers`, `wall_band`, `wall_band_keys`, `wall_events`; constants `WALL_SINK 0.47`, `WALL_CAP 0.12`, `WALL_INSET 0.02`, `WALL_SLIVER 0.03`). Each wall sample has a full vertical interval [ground − sink, ground + rise + cap]. Every building body near the wall subtracts its own world height range from that interval; a body standing on its foundation blocks down to −∞, a lifted body blocks from base + inset. The result is a list of runs, each keyed by [bottom source, top source] (−1 = the wall itself, otherwise the building id), with per-sample `points`, `ground`, `low`, `high`, plus `start_continues`/`end_continues` (another run carries on at that point), `ring`, `top_clipped`, `bottom_clipped`. Runs end exactly at events: outline crossings of the inset footprint and the parameters where the sloping wall bottom or top passes a body's underside or top (solved linearly per segment, then bands are evaluated at sub-segment midpoints). Closed loops merge their first and last runs across the seam when the key matches; an unobstructed loop is one `ring`. `wall_pieces` is now a thin wrapper returning the runs' points, so validation ("entirely inside a building") and older tests are unchanged. - **Wall generator** (`Architecture.wall_path`, new `wall_band`): every course (body, sunken foundation, coping) is lofted per run and clipped to [low, high]; a course thinner than 5 mm ends the loft, so a course cut by a body stops just inside it. Merlons and pillars are only drawn on unclipped runs, and pillars only at ends where no run continues. Ring frames wrap their tangents. - **Trim ownership** (`RoofProfile.trim_segments`, `roof_planes`, `eave_corners`, `subtract_collinear`, `visible_trim`, caches `trim_cache`, `trim_segments_cache`, `plane_cache`). Ridge cap and bargeboard centrelines are generated once per profile in the lifted local frame (bargeboards carry their eave `foot`). `visible_trim` applies: (1) body cutters as before; (2) ownership: the overlap with a lower-ID neighbour's collinear ridge is subtracted, and a bargeboard whose foot and segment coincide with a lower-ID neighbour's board is dropped; (3) coplanar surfaces: any portion lying within ±8 cm of a neighbour roof triangle that is coplanar with one of my planes is removed (exact triangle edges, slightly outset, so adjacent triangles leave no slivers). A ridge ignores slabs from a neighbour whose ridge is collinear, since ownership settles that overlap. Pieces under 5 cm (apex/eave leftovers) are dropped. Broad phases: neighbours need intersecting roof bounds; coplanar checks need parallel ridge axes, rectangular profiles, and a neighbour eave corner lying on one of my eave-row planes; plane groups are matched by the normal's vertical component bin. The cache key describes every body in range (kind, dims, base, roof, ridge, overhang, settings, relative transform, id order) rather than serialising geometry. `Architecture.profile_roof` now emits trim from `visible_trim`; `roof_beam` was removed. - **Terrace landings** (`Architecture.clear_of_landings`, used by `ClusterArchitecture.terraces`): a railing or parapet span is split around every bridge/stair landing circle (radius 1.4 m) instead of being dropped whole; spans under 0.3 m are discarded. - **Tests**: `tests/test_cluster_walls.gd` gained 24 checks (grounded, lifted-clear, lifted-1 m, low, rotated bodies; sloping ground with a mid-span junction; loop opened by a body and wrapping across the seam; ring; coplanar row houses ridge length and 16 boards each; separated houses; different pitch keeps boards but loses the collinear ridge overlap; taller same-shape neighbour keeps all trim; landing spans). `main.gd run_cluster_checks` adds a wall under a lifted body (3 runs, cluster count), lifting it clear via the inspector (1 run, cluster split), undo, and coplanar gables (16 boards), a depth change (32), undo (16). It looks objects up by id after undo because `restore` creates new dictionaries. - README updated (freehand wall paragraph and the limits paragraph).