Add Phase 19 Starter Scene Composition UI/UX Specification and Tests

- Introduced a new UI/UX specification document for the Phase 19 Starter Scene Composition, detailing user paths, composition rules for Town Street and Living Room, camera framing, and observable acceptance outcomes.
- Implemented a visual capture test for different sky states in the sandbox stage, ensuring proper rendering and visual consistency.
- Created a comprehensive test suite for the Phase 19 starter scene composition, validating scene loading, geometry, navigation, and interaction behaviors for both Town Street and Living Room.
- Added checks for visual alignment, object placements, and scene transitions to ensure adherence to the new UI/UX specifications.
This commit is contained in:
2026-10-21 17:46:58 -04:00
parent c1758efd3b
commit 0b1dda2e2f
18 changed files with 1574 additions and 141 deletions
@@ -85,6 +85,8 @@ This document tracks known technical debt, optimization opportunities, and minor
| 73 | **Whole-set transform requires cross-system invariants** — Phase 19's temporary group transform covers only inert Scenery pieces: wall panels, artwork, and the planned Background ground. | Medium | Future feature | Moving/scaling walkable Ground, characters, interactive Props, Sky, camera composition, waypoints, and rules together needs separate physics, navigation, event-target, and save contracts. Do not expand the Scenery group tool into a whole-set transform without that design. See `docs/specs/phase_19_scenery_backgrounds_surface_finishes.md` and `docs/specs/phase_19_background_ground_and_wall_color.md`. (2026-10-17) |
| 74 | **Background surface motifs need a shared zoom-consistent draw budget** — a pattern detail scale derived from each panel's size can shift seams or texture density where adjacent pieces meet, while uncapped large rectangles can issue excessive draw calls. | Medium | Planned | Phase 19 follow-up uses world-cell coordinates, visible-region culling, and any level-of-detail choice derived from camera zoom for all pieces in the same view. Keep Grass/Concrete/Carpet detail deterministic and bounded; consider a texture cache only if measured scenes remain expensive. See `docs/specs/phase_19_background_ground_and_wall_color.md`. (2026-10-17) |
| 75 | **Old Brick/Wood wall colors were saved but not rendered** — v1.5 walls can carry the old default green while their brick and wood palettes were hard-coded. | Low | Planned migration | Scene v1.6 migration replaces only the old green placeholder on pre-v1.6 Brick/Wood entries with natural finish defaults; Paint and non-placeholder colors remain. The exact placeholder could have been intentionally authored in an older file, which cannot be distinguished retrospectively. New v1.6 colors are authoritative, including green Brick/Wood. See `docs/specs/phase_19_background_ground_and_wall_color.md`. (2026-10-17) |
| 76 | **Camera-aware Sky redraws vector motifs while framing changes** — the former static Sky drew motifs only when the preset or viewport changed. | Low | Planned | The Phase 19 Sky addition keeps one screen-space `StageSkyView`, redraws only when a bound camera's effective framing changes, and caches reusable cloud/moon curve samples where practical. Measure sustained pan/zoom cost in a rendered Stage before adding a raster or shader path. The fixed reference frame and bounded response can saturate at distant camera positions; that is an accepted composition trade-off, not scene state to persist. See `docs/specs/phase_19_scenery_backgrounds_surface_finishes.md#phase-19-addition-camera-aware-sky-details`. (2026-10-21) |
| 77 | **Starter surfaces guarantee a finite pan envelope, not arbitrary camera framing** — the two bundled sets use a fixed authored width to keep their scene data simple. | Low | Implemented; visual review pending | The authored envelope is a 1,280-logical-pixel viewport at zoom 0.5, ±1,000 world units of travel, and 100 units of margin per side. User review prompted an opaque Town Grass Background ground and separate scale-1 tile rows. Twenty-five 192-unit tiles cover the 4,760-unit minimum; Asphalt has 27 tiles (5,184 units) so the road extends one tile beyond the sidewalk at both ends. Navigation merges contiguous tile tops and routes the 32-unit curb step at the exposed ends without merging Ground objects or colliders. The focused suite passes 515 assertions, including the curb step and PLAY landing. The camera remains unrestricted, so wider viewports, farther pans, and zoomed-out shots can reveal finite ends. Rendered seam review remains pending. See docs/specs/phase_19_starter_scene_composition.md. (2026-10-21)
---
@@ -181,5 +183,9 @@ This document tracks known technical debt, optimization opportunities, and minor
| 2026-10-20 | Approved the scenery bitmap tile cache architecture (`docs/specs/scenery_bitmap_tile_cache.md`). Lightweight `StageSurfaceCache` handles share immutable bitmap tiles through one `StageSurfaceBakePool` under the current scene, with a 64 MiB resident bitmap budget and one reusable offscreen baker. Camera-visible tiles use four fixed zoom levels (0.125, 0.5, 1, 2); the coarsest tile bakes first and refined detail overlays it. At maximum 6× zoom, the 2× bitmap may look softer because it is magnified threefold. Erased fragments clip shared tiles only when their complete pattern sampling phase matches. The previous per-surface viewport cache retained vector commands and had no scene-wide resource limit, a likely contributor to `Element limit reached`; the exact RID pool remains unconfirmed. GPU readback during bitmap capture and regeneration after budget eviction are accepted costs to measure during implementation. |
| 2026-10-20 | Replaced the positional scenery tile design after the user observed continuous buffering when panning after zoom. The accepted revision in `docs/specs/scenery_bitmap_tile_cache.md` bakes one seamless, 2× bitmap per finish/tint period and uses GPU repeat plus generated mipmaps for all camera positions and zooms. Erased fragments share the style texture and crop it using their existing sample coordinate. The scene-wide 64 MiB limit includes mipmaps; inactive style variants may be evicted, while recently used styles stay warm when space allows. This trades unique random detail across an unlimited wall for finite repeated variation and keeps the existing possible 3× bitmap enlargement at maximum zoom. A style change can bake once; repeated camera movement must not increment build or readback counts after styles are ready. |
| 2026-10-21 | Drafted the camera-aware Sky architecture addendum in `docs/specs/phase_19_scenery_backgrounds_surface_finishes.md` for user sign-off. Accepted a fixed built-in camera reference, one bounded response profile per preset, and normalized motif anchors with uniform scaling. The gradient remains a full-screen `CanvasLayer` draw; motifs derive deterministic bounded pan/zoom response from absolute `Camera2D` framing. Chooser and starter previews stay unbound and still; existing Sky/camera scene fields suffice. The trade-offs are response saturation far from the reference and more frequent vector redraws during camera movement; logged #76 for measured performance follow-up. No runtime code or tests changed. |
| 2026-10-21 | Wrote the Phase 19 starter-scene composition UI/UX and architecture specifications (`docs/specs/phase_19_starter_scene_composition_ui_ux.md`, `docs/specs/phase_19_starter_scene_composition.md`) after approval of the Everyday Storybook brief and the 1,280-pixel / ±1,000-world-unit pan defaults. The edited starter snapshots use current scene v1.6 and existing Ground, wall, Background ground, Scenery, and Prop types, and keep the camera unrestricted; the starter card derives a central opening preview without new scene metadata. No code or art changed. Added #77 for the finite authored-envelope and broad-selection trade-off; the architecture spec is approved and implementation/agent verification are underway.
| 2026-10-21 | Implemented the approved starter-scene composition spec. Town Street and Living Room now use continuous 4,760-unit support surfaces, aligned bases, and camera-derived starter-card framing; both edited scene files use current v1.6. The focused production-load/save-reopen/PLAY composition test passed 177 assertions, with measured visible-contact geometry within 0.225 logical pixels. The broader scenery suite exits 1 on nine other background-card and wall-erasure/capture-bar assertions, while the new surface assertions pass. GUI capture exited 134 and headless capture produced no image, so rendered-pixel checks, pan-endpoint seam inspection, narrow viewport/zoom-in framing, and user/editor verification remain pending. Updated #77 from planned to implemented with the finite-camera limitation accepted. See `docs/specs/phase_19_starter_scene_composition.md`.
| 2026-10-21 | Revised the starters after the user's Town Street screenshot exposed Sky clouds below the skyline and the user rejected oversized scaled Ground blocks. Added an opaque Grass Background ground, replaced the sidewalk and both starter floors with scale-1 tile rows, and extended Asphalt by one tile at each end so the road is accessible. The focused production-path test passed 515 assertions, including navigation across tile seams, sidewalk-to-road routing, and a PLAY actor landing grounded on Asphalt. The broader scenery suite still exits 1 on the same nine Sky-preview/gallery and Scenery-edit UI assertions observed before this revision; its new surface assertions pass. A bounded GUI capture exited 134 without an image, so rendered pixels, pan endpoints, card previews, narrow viewport/zoom-in states, and user/editor review remain pending. See `docs/specs/phase_19_starter_scene_composition.md`.
_End of Log_
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
# Phase 19 — Scenery, Backgrounds, and Vector Surface Finishes
> **Status:** Phase 19 implementation, including Stage-relative asset sizing and the selected-artwork depth inspector, is integrated; automated and render-capable checks pass. Full user/editor verification remains pending. See the [Phase 19 handoff](phase_19_handoff.md) for continuation context and verification entry points.
> **Status:** The original Phase 19 implementation, including Stage-relative asset sizing and the selected-artwork depth inspector, is integrated; automated and render-capable checks pass. The camera-aware Sky details addendum below is accepted design work awaiting implementation and user/editor verification. See the [Phase 19 handoff](phase_19_handoff.md) for continuation context and verification entry points.
> **Audience:** UI/UX, architecture, implementation, and art contributors.
> **Purpose:** Define the child experience and the content required for indoor and outdoor sets. Rendering, file contracts, and GDScript APIs belong in later specifications.
@@ -53,10 +53,24 @@ The existing vector Prop Editor and `.prop` assets supply user-created artwork.
### Choose a Sky
**Backgrounds → Sky** shows four designed thumbnail cards: **Clear Day**, **Cloudy Day**, **Sunset**, and **Starry Night**. One tap changes the scene's Sky, and choosing another card changes it again. A Sky fills every camera view without an exposed edge; its clouds, sun, moon, and stars remain visually stable and crisp during pan and zoom. Each preset should be a considered composition rather than a flat color with an obvious repeated icon. The Sky remains fixed throughout PLAY.
**Backgrounds → Sky** shows four designed thumbnail cards: **Clear Day**, **Cloudy Day**, **Sunset**, and **Starry Night**. One tap changes the scene's Sky, and choosing another card changes it again. A Sky fills every camera view without an exposed edge. Its gradient stays fixed to the screen while its clouds, sun, moon, and stars respond gently to camera pan and zoom as described in the [camera-aware Sky details addendum](#camera-aware-sky-details-addendum). Each preset should be a considered composition rather than a flat color with an obvious repeated icon.
Changing Sky changes **only the sky**. It never tints or recolors Ground, wall panels, scenery artwork, characters, or Props. Sky is selected through its chooser, not by clicking a giant rectangle on the Stage.
### Camera-aware Sky details addendum
**Goal and audience.** A child making an outdoor scene should see a Sky that feels distant without making their Ground and stickman appear to float among oversized clouds. This applies when a child chooses any of the four Phase 19 Sky presets, frames a shot in EDIT or DIRECT, and watches it in PLAY.
**Child flow.** The child chooses a Sky as before, places walkable Ground and a stickman, then pans or zooms the Stage camera. The Sky gradient continues to fill the entire view. Clouds and celestial details shift gently with camera movement and change size modestly with camera zoom, so the composition feels connected to the shot while staying distant. The child does not set a motion mode, slider, or per-scene option. Switching presets applies that preset's tuned response automatically. The same view is reproduced after saving and reopening the scene at its saved camera framing.
**Visual behavior.** Each preset keeps its distinctive color and motifs, with detail size and placement tuned to leave clear space around characters and Ground at ordinary framing. Camera response is bounded and smooth: no visible Sky edge, abrupt jump, distracting sweep, or cloud that grows to dominate the action during normal pan and zoom. The gradient remains screen-filling and fixed; only Sky details respond. EDIT, DIRECT, and PLAY use the same camera-relative composition. The chosen Sky remains decorative and never changes the color, collision, or movement of any world object.
**Horizon and Ground.** The Sky does not draw a horizon or a ground line. A child who wants land behind the playable area uses the existing world-anchored **Background ground** scenery surface, placed behind walkable Ground. This keeps a visible landscape edge aligned with the scene as the camera moves. Softer contours, automatic edge blending, and easier horizon construction remain the separate [backdrop horizon and edge treatment idea](../ideas/feature_backlog.md); this addendum does not define or ship those tools.
**Success criteria.** At ordinary and representative close/wide camera views, a character on Ground reads as being in front of the Sky rather than inside a cloud. Panning and zooming each preset gives a perceptible but subtle response without revealing an edge or obscuring the action. The same camera framing reproduces the same Sky composition after a mode change, PLAY/stop cycle, or save/reopen, and the composition adapts coherently to a resized viewport. A Background ground surface and walkable Ground stay aligned with each other while Sky details respond behind them. Sky chooser previews still identify the four looks and do not imply that the child must configure camera behavior.
**Scope and handoff.** This is a Phase 19 visual-behavior addition for the four existing Sky presets. It preserves the existing Sky choice and screen-filling gradient. It excludes autonomous cloud/weather animation, a child-facing motion control, a Sky-ground horizon, multi-layer parallax scenery, and changes to terrain physics or Background ground placement. UI/UX and architecture should resolve how much pan/zoom response is comfortable for each preset and how the view stays composed at viewport extremes. Relevant current references are `scripts/stage/scenery/stage_sky_view.gd`, `scripts/editors/stage/sandbox_stage.gd`, `scripts/stage/scenery/stage_background_ground.gd`, the [Phase 19 Sky UI/UX specification](../specs/phase_19_scenery_backgrounds_ui_ux.md), and the [Background Ground and Wall Color plan](phase_19_background_ground_and_wall_color.md). Those earlier specifications describe the original static behavior and need a focused follow-up before implementation.
### Draw a wall panel
**Backgrounds → Wall panel** offers **Paint**, **Brick**, and **Wood** finishes with a color choice. The child chooses a finish, drags from one corner to the other, sees a translucent preview of the intended size and pattern, and releases to place a finite rectangular panel. Snap may help edges meet. A selected panel can later be moved, resized, recolored, restyled, or erased; changing one panel does not silently alter others.
@@ -89,13 +103,13 @@ The existing **Ground** palette entry gains a visual finish chooser. Picture swa
Ground remains solid and walkable. This phase gives finishes only to **Ground**; it does not redesign or restyle Ramp and Step. Children can use Sidewalk and Asphalt Ground in Town Street and Wood Ground in Living Room. A Scenery wall panel or road marking never becomes a movement surface.
In **PLAY**, edit handles and placement ghosts disappear. Sky and Scenery stay still; characters move on Ground; Props follow their own saved behavior settings. Stopping PLAY restores the authored arrangement for another take. Story playback and camera movement should preserve the intended visual layer order and the child's view of the action.
In **PLAY**, edit handles and placement ghosts disappear. Scenery stays put; Sky details respond gently to camera pan and zoom under the same rules as EDIT and DIRECT. Characters move on Ground; Props follow their own saved behavior settings. Stopping PLAY restores the authored arrangement for another take. Story playback and camera movement should preserve the intended visual layer order and the child's view of the action.
## Included scope and exclusions
Included in Phase 19: one fixed scene-wide Sky preset; four initial Sky designs; finite editable wall panels with manually erasable rectangular openings; inert `.prop` Scenery placements with Far, Near, and On the floor depth; built-in multi-select browse tags; Ground-only visual finishes; editable Blank/Town Street/Living Room New Scene choices; Scenery-safe selection; scene save/reopen and legacy defaults; built-in vector content listed below; and a stage-relative size-authoring guide/workflow shared by Props and Scenery artwork, with single Scenery artwork placements resizable independently in Edit Scenery. The new authoring convention does not silently rescale existing assets or saved placements.
Included in Phase 19: one chosen scene-wide Sky with a screen-filling fixed gradient and gentle automatic camera-aware details; four initial Sky designs; finite editable wall panels with manually erasable rectangular openings; inert `.prop` Scenery placements with Far, Near, and On the floor depth; built-in multi-select browse tags; Ground-only visual finishes; editable Blank/Town Street/Living Room New Scene choices; Scenery-safe selection; scene save/reopen and legacy defaults; built-in vector content listed below; and a stage-relative size-authoring guide/workflow shared by Props and Scenery artwork, with single Scenery artwork placements resizable independently in Edit Scenery. The new authoring convention does not silently rescale existing assets or saved placements.
Excluded from Phase 19: a new Scenery Editor or `.scenery` file; child-authored repeatable finish patterns or reusable whole-Sky themes; animated weather, parallax, or directed Sky transitions; whole-scene filters such as sepia or black-and-white; functional doors or scene travel; automatic window cutouts or glass; Ramp/Step finishes or terrain redesign; inserting a starter into an existing scene; user-authored reusable set templates; and functional stairs or multi-level travel. [Multi-level walkable scenes](../ideas/feature_backlog.md) and [whole-scene visual filters](../ideas/feature_backlog.md) are separate future ideas, not Phase 19 work.
Excluded from Phase 19: a new Scenery Editor or `.scenery` file; child-authored repeatable finish patterns or reusable whole-Sky themes; autonomous weather animation, multi-layer parallax scenery, or directed Sky transitions; whole-scene filters such as sepia or black-and-white; functional doors or scene travel; automatic window cutouts or glass; Ramp/Step finishes or terrain redesign; inserting a starter into an existing scene; user-authored reusable set templates; and functional stairs or multi-level travel. [Multi-level walkable scenes](../ideas/feature_backlog.md) and [whole-scene visual filters](../ideas/feature_backlog.md) are separate future ideas, not Phase 19 work.
## Asset production manifest
@@ -105,12 +119,14 @@ In the tables, **TBD** in Source/rights means the creator, reference provenance,
### Adaptive Sky themes
The **Integrated** statuses below describe the original Sky artwork and picker. Camera-aware detail behavior is accepted in the addendum above and remains to be implemented and verified.
| Stable ID | Class / final format | Tags / default layer | Starter use | Behavior / collision | Status | Source / rights | Visual acceptance | Final location / definition | Validation evidence |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| `sky_clear_day` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Living Room; picker | Scene-wide, static; none | Integrated | Original vector concept | Appealing blue depth and varied light cloud/sun detail; window gap reads clearly; no edge on pan. | `scripts/stage/scenery/stage_sky_view.gd` | Production card and Living Room Stage render inspected: `tests/phase19_visual_background_gallery.png`, `tests/phase19_visual_living_room.png`; user pan/zoom check pending. |
| `sky_cloudy_day` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Town Street; picker | Scene-wide, static; none | Integrated | Original vector concept | Layered, varied clouds leave town silhouettes readable; no obvious tile or edge. | `scripts/stage/scenery/stage_sky_view.gd` | Production card and Town Street Stage render inspected: `tests/phase19_visual_background_gallery.png`, `tests/phase19_visual_town_street.png`; user pan/zoom check pending. |
| `sky_sunset` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Picker | Scene-wide, static; none | Integrated | Original vector concept | Warm atmospheric color and sun/cloud composition remain distinct from Clear Day without tinting scene objects. | `scripts/stage/scenery/stage_sky_view.gd` | Production card and Stage render inspected: `tests/phase19_visual_background_gallery.png`, `tests/phase19_visual_finishes_and_repair.png`; user pan/zoom check pending. |
| `sky_starry_night` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Picker | Scene-wide, static; none | Integrated | Original vector concept | Dark graduated sky, varied stars, and moon remain legible while foreground contrast survives. | `scripts/stage/scenery/stage_sky_view.gd` | Distinct production gallery preview inspected: `tests/phase19_visual_background_gallery.png`; full Stage user check pending. |
| `sky_clear_day` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Living Room; picker | Scene-wide; camera-aware details; none | Integrated | Original vector concept | Appealing blue depth and varied light cloud/sun detail; window gap reads clearly; no edge on pan. | `scripts/stage/scenery/stage_sky_view.gd` | Production card and Living Room Stage render inspected: `tests/phase19_visual_background_gallery.png`, `tests/phase19_visual_living_room.png`; user pan/zoom check pending. |
| `sky_cloudy_day` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Town Street; picker | Scene-wide; camera-aware details; none | Integrated | Original vector concept | Layered, varied clouds leave town silhouettes readable; no obvious tile or edge. | `scripts/stage/scenery/stage_sky_view.gd` | Production card and Town Street Stage render inspected: `tests/phase19_visual_background_gallery.png`, `tests/phase19_visual_town_street.png`; user pan/zoom check pending. |
| `sky_sunset` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Picker | Scene-wide; camera-aware details; none | Integrated | Original vector concept | Warm atmospheric color and sun/cloud composition remain distinct from Clear Day without tinting scene objects. | `scripts/stage/scenery/stage_sky_view.gd` | Production card and Stage render inspected: `tests/phase19_visual_background_gallery.png`, `tests/phase19_visual_finishes_and_repair.png`; user pan/zoom check pending. |
| `sky_starry_night` | Adaptive vector Sky theme; not `.prop` | N/A; Sky | Picker | Scene-wide; camera-aware details; none | Integrated | Original vector concept | Dark graduated sky, varied stars, and moon remain legible while foreground contrast survives. | `scripts/stage/scenery/stage_sky_view.gd` | Distinct production gallery preview inspected: `tests/phase19_visual_background_gallery.png`; full Stage user check pending. |
### Repeatable vector surface finishes
@@ -168,8 +184,9 @@ Existing built-in crate and ball artwork may be used in Town Street if an intera
5. Ordinary EDIT selection still reaches foreground characters, Props, and Ground over broad panels. Edit Scenery makes every cutout and panel discoverable and correctable; placement and an active resize gesture can be canceled before commit, while a single-piece resize commits on release and group resize retains Keep/Cancel. Labels, icons, outlines, and focus reveal state without depending on color alone.
6. Wall panel and Ground finish choices preview correctly; wall patterns do not stretch as panels resize, and adjacent Ground pieces do not show distracting pattern restarts. Changing a palette finish does not repaint existing pieces; a selected piece can be changed deliberately.
7. Older `.scene` documents retain their prior neutral background and Ground appearance when opened. Older untagged `.prop` assets remain visible in All artwork, and a missing referenced Scenery asset yields a visible recovery cue rather than silently removing the child's placement.
8. Every required manifest item reaches **Verified**, with source/rights and final location recorded. A raster concept alone cannot satisfy a final vector content requirement.
9. New and revised `.prop` artwork is authored at its intended Stage size relative to the standard approximately 940-world-unit standing stickman, avoiding a mandatory resize on ordinary placement. The Prop Editor's Size for Stage guide is visible over artwork and above a floor/contact cue. Loading performs no automatic normalization; deliberate asset overwrites retain the usual live-reference behavior and existing Save workflow, without new copy/version prompts. Individual Scenery pieces resize independently, preserve proportions, and save/reopen at the committed scale.
8. Each chosen Sky keeps its gradient screen-filling while its details respond gently and predictably to camera pan and zoom in EDIT, DIRECT, and PLAY, as specified in the [camera-aware Sky details addendum](#camera-aware-sky-details-addendum). The action remains readable, no edge appears, and the same framing survives save/reopen.
9. Every required manifest item reaches **Verified**, with source/rights and final location recorded. A raster concept alone cannot satisfy a final vector content requirement.
10. New and revised `.prop` artwork is authored at its intended Stage size relative to the standard approximately 940-world-unit standing stickman, avoiding a mandatory resize on ordinary placement. The Prop Editor's Size for Stage guide is visible over artwork and above a floor/contact cue. Loading performs no automatic normalization; deliberate asset overwrites retain the usual live-reference behavior and existing Save workflow, without new copy/version prompts. Individual Scenery pieces resize independently, preserve proportions, and save/reopen at the committed scale.
## Current project context, risks, and dependencies
@@ -186,7 +203,7 @@ Existing built-in crate and ball artwork may be used in Town Street if an intera
These are design and architecture tasks for the specification stage, not unresolved user choices about Phase 19's product goal:
1. What exact UI presents Backgrounds, Artwork tags, placement layer, Ground finish, and Edit Scenery without crowding the existing Stage palette? Define keyboard, touch, narrow-viewport, dismissal, and cancellation behavior using the established Stage panel patterns; Stage Undo remains out of scope for Phase 19 and is tracked separately.
2. How will an adaptive Sky fill unbounded camera views while keeping motifs stable and detailed, and how will wall and Ground finishes keep repeatable pattern scale and alignment through resizing, drag painting, and zoom?
2. How will an adaptive Sky fill unbounded camera views while keeping its gradient fixed and its details gently camera-aware as described in the addendum, and how will wall and Ground finishes keep repeatable pattern scale and alignment through resizing, drag painting, and zoom?
3. What saved scene and optional `.prop` tag fields, validation, migration defaults, missing-art recovery, and starter creation flow preserve existing documents and the same artwork's independent Scenery/Prop placements?
4. How will selected panels, cutouts, rugs, Ground, and ordinary objects remain reachable where bounds overlap, including the window gap and a room-sized wall?
5. Which asset production workflow turns concepts into final vector content, records provenance, checks collision only for interactive Props, and advances each manifest row through integration and verification?
@@ -0,0 +1,63 @@
# Phase 19 — Starter Scene Composition
> **Status:** Accepted product design for a focused Phase 19 starter-scene polish. This is a design handoff to the spec-pipeline, not a final technical specification.
## Goal and audience
Make **Town Street** and **Living Room** look like complete, usable story sets when a child opens them, moves the camera, places a stickman, and presses PLAY. The audience is a child starting from **File → New** who should see a clear place to stage action and be able to change its individual pieces.
The selected visual direction is the upper-left **Everyday Storybook** composition from the outdoor and indoor concept boards. It favors friendly shapes, a calm backdrop, a clear floor line, and open space for characters and speech.
## Desired child outcome
- **Town Street:** A child immediately recognizes a neighborhood street. Shops, houses, and trees stand on the same sidewalk line; a subdued skyline gives distance; the center has room for a character to walk or speak. The sidewalk, curb, and road remain visible as a continuous set as the child pans through the intended starter area.
- **Living Room:** A child immediately recognizes a room with a complete wall and wood floor. The window is the only opening to the Sky. Furniture rests on the floor, and a centered rug gives the room warmth while leaving a clear action lane.
- In either starter, the child can select, move, resize, restyle, or remove ordinary pieces with the existing Stage tools, then save and reopen the revised scene.
## Core behavior and composition
**Opening and editing.** Keep the existing Blank Scene, Town Street, and Living Room choices and the current unsaved-scene flow. The two revised starters should open at a useful camera frame with no setup step. They remain editable scenes assembled from the existing Sky, Scenery, wall/background surfaces, walkable Ground, and Props. Do not flatten a starter into a single picture or merge independently useful objects merely to hide seams.
**Outdoor set.** Use the current Cloudy Day Sky and a quiet Far skyline. Arrange the existing house, storefront, and tree artwork so their visible bases meet one consistent street datum. Preserve a generous, readable opening near the initial center for a stickman and speech. Carry Sidewalk and Asphalt as left-to-right rows of ordinary-size Ground tiles across the visible frame and agreed pan range. Add an opaque, non-walkable Background ground layer below the skyline datum to cover clouds showing through the lower scene. Buildings and trees may repeat or vary in size and spacing to support a longer street, while remaining individual editable placements.
**Indoor set.** Carry the painted wall and a left-to-right row of ordinary-size Wood Ground tiles across the initial frame and agreed pan range. Make the wall and floor meet cleanly with a visible baseboard or equivalent visual edge. Keep the intentional opening beneath the window frame as the only place the selected Sky shows through; no Sky should leak at outer wall edges, below the floor, or through unintended seams. Position the couch, armchair, and coffee table so they visually rest on the floor. Center the existing rug in the room, show it above the wood finish and below action, and leave enough uncluttered floor for a character to move and be seen. Wall decoration remains secondary to the window and action area.
**Camera envelope.** The approved implementation target is a **4,760-world-unit** minimum for each walkable span: a 1,280-logical-pixel viewport at saved zoom 0.5, ±1,000 world units of pan from the saved camera, and 100 world units of margin at each end. Twenty-five standard 192-unit tiles span 4,800 units and satisfy the target without oversized scaled blocks. Town Asphalt adds one normal-size tile at each end (27 total) so the lower road is physically reachable beyond the sidewalk ends. Outside the defined starter envelope, finite set edges may appear naturally because the camera remains unrestricted. The architecture spec records the exact formula and accepted trade-offs.
## Story and kid-facing feedback
The default shot should communicate where action can happen without explanatory text. A child placing a stickman should see feet on a walkable surface, room in the center for a short walk or conversation, and enough contrast to read the character and a speech bubble. Panning should reveal more of the same coherent set, with no sudden floating building, missing floor, or stray Sky gap. Existing Scenery and Ground editing affordances provide recovery if a child moves a piece; this polish adds no new child-facing controls.
## Success criteria
1. At the saved starting camera, both starters read as complete places at normal Stage size, with a clear central action area and understandable walkable surfaces.
2. In Town Street, every near building and tree appears grounded on the sidewalk/curb datum. Sidewalk and road continue across the initial view and the defined horizontal pan envelope without an exposed end or distracting join. A character can step from the sidewalk onto the road at either exposed end. The Far skyline remains subdued behind the action.
3. In Living Room, the wall, baseboard, and wood floor continue across the initial view and pan envelope. The selected Sky appears through the window opening only. Couch, chair, and table visibly contact the floor; the rug remains centered and does not consume the character's main movement lane.
4. At normal framing, a placed stickman and speech remain easy to see in both starters. PLAY preserves the intended walking/support behavior, visual order, and composition; saving and reopening preserves the revised set and camera frame.
5. The important pieces remain separately editable through their established tools. Moving or removing one house, tree, wall panel, Ground piece, rug, or furniture Prop does not require replacing a baked image.
The pre-implementation design review found the outdoor artwork bases roughly **128 screen pixels above Ground**, the **700-world-unit Ground run** ending inside a roughly **917-pixel viewport**, and the indoor couch/chair roughly **24 screen pixels above the floor**. Those measurements motivated the composition work; they are not final placement coordinates.
## Scope and exclusions
**Included:** Composition and placement polish for the existing Town Street and Living Room starter scenes; continuity of existing surfaces and backdrops over a defined pan range; alignment of visible artwork and furniture with their shared ground/floor datum; initial camera framing; preservation of scene editability and existing Prop/Scenery roles.
**Excluded:** New story mechanics, sit behavior, new controls or authoring tools, Stage-wide Undo/Redo, camera restrictions, endless scenery generation, and changes to terrain collision or a general navigation system. The existing navigation bake receives only the tile-seam and exposed 32-unit curb connectors required by these authored surfaces. New art is outside this pass unless the existing library cannot make the selected composition legible; identify any specific missing visual element before proposing an asset request.
## Current project behavior and references
The Phase 19 [scenery product plan](phase_19_scenery_backgrounds_surface_finishes.md) defines the starter choices, editable scenery layers, and the distinction between visual Scenery and walkable Ground. The [Background Ground and Wall Color brief](phase_19_background_ground_and_wall_color.md) defines decorative Background ground and editable wall finishes. The [Phase 19 handoff](phase_19_handoff.md) says overall user/editor verification is still pending.
The accepted visual direction is detailed in the [starter-scene UI/UX specification](../specs/phase_19_starter_scene_composition_ui_ux.md). The implementation contract is in the [approved starter-scene architecture specification](../specs/phase_19_starter_scene_composition.md).
Before this composition pass, the [Town Street scene](../../content/stages/starters/town_street.scene) used one Far skyline, separate Near trees/buildings, and short Sidewalk/Asphalt Ground runs. The [Living Room scene](../../content/stages/starters/living_room.scene) used three wall panels, seven Wood Ground pieces, separate decorations, and three Props. Both saved zoom 0.5. The implementation now promotes these starters to current scene format v1.6, extends required surfaces to the approved envelope, and frames starter previews from the saved camera. See the focused [architecture specification](../specs/phase_19_starter_scene_composition.md) for exact placements and verification results. The existing artwork and furniture remain in [content/props](../../content/props/), with each placement independently editable.
## Verification status and remaining checks
- The focused composition suite passes 515 assertions, including tile bounds, seam navigation, a physical PLAY step from sidewalk to road, and Living Room floor support.
- A render-capable capture route is not available in this run: the bounded GUI capture exited 134 without producing an image. Visual checks remain for the opening frames, pan endpoints, tile/background joins, starter cards, narrower viewport, and zoom-in framing.
- The broader scenery suite still exits 1 on nine Sky-preview/gallery and Scenery-edit interaction assertions; Town/Living Room surface assertions pass and those nine failures were also present before the latest tile and curb changes.
## Implementation status
The initial focused composition test passed 177 assertions, but user visual review found clouds visible through the Town Street below the skyline and rejected the oversized scaled walkable blocks. The revision adds opaque Grass Background ground, ordinary-size tile rows, and a one-tile Asphalt extension at each end so characters can reach the road. The focused composition suite now passes 515 assertions, including routing across tile seams and a grounded PLAY drop onto Asphalt. Rendered review and user/editor checks remain pending. The [architecture specification](../specs/phase_19_starter_scene_composition.md) records the measurements and test limitations.
@@ -1,11 +1,11 @@
# Phase 19 — Scenery, backgrounds, and surface finishes: architecture and implementation specification
> **Status:** Original Phase 19 scenery architecture and the approved Stage-relative sizing amendment are integrated; automated and render-capable checks pass. This specification follows the [accepted product brief](../plans/phase_19_scenery_backgrounds_surface_finishes.md), the [approved UI/UX specification](phase_19_scenery_backgrounds_ui_ux.md), and the [Stage panel UI system](stage_panel_ui_system.md). Full Phase 19 user/editor verification remains pending. The earlier sections describe the original implementation contract; the sizing amendment starts from the current `.scene` v1.5, `.prop` v1.1, Stage controller, and shared UI.
> **Status:** Original Phase 19 scenery architecture and the approved Stage-relative sizing amendment are integrated; automated and render-capable checks pass. The camera-aware Sky architecture addendum below is specified for user sign-off and is not yet implemented. This specification follows the [accepted product brief](../plans/phase_19_scenery_backgrounds_surface_finishes.md), the [approved UI/UX specification](phase_19_scenery_backgrounds_ui_ux.md), and the [Stage panel UI system](stage_panel_ui_system.md). Full Phase 19 user/editor verification remains pending. The camera-aware Sky addendum supersedes the original static-detail statements in this document.
## Decisions and scope
1. Use independent, inert `Node2D` placements for wall panels and `.prop` Scenery artwork. The source `.prop` is a **live reference**: its current vector art is used on reopen. Missing art remains a saved, visible, repairable placement. A given `.prop` may be used as inert Scenery and, when it has valid collision geometry, as a functional Prop.
2. Sky is one adaptive, viewport-filling, screen-space vector drawing behind the world. Its details stay fixed during camera movement; it does not tint other objects. Existing scenes and Blank Scene use an internal neutral Sky. The four child-facing Sky presets are Clear Day, Cloudy Day, Sunset, and Starry Night.
2. Sky is one adaptive, viewport-filling, screen-space vector drawing behind the world. Its gradient stays fixed while its details respond gently to camera pan and zoom under the [camera-aware Sky addendum](#phase-19-addition-camera-aware-sky-details); it does not tint other objects. Existing scenes and Blank Scene use an internal neutral Sky. The four child-facing Sky presets are Clear Day, Cloudy Day, Sunset, and Starry Night.
3. The existing Prop Editor opens **inside the live Stage session** for Make artwork. The Stage and its unsaved document remain in memory, with Stage input suspended until the editor returns. The standalone F6 route remains valid.
4. Select several creates a temporary Scenery selection and snapshot-based transform preview. It does not create a persistent group or a parent that changes the saved hierarchy. Sky, Ground, characters, and functional Props are outside this transform.
5. Phase 19 adds neither Stage Undo/Redo nor a whole-set transform. Group preview has its own exact Cancel/Keep boundary. Stage-wide Undo/Redo is a separately planned phase and is logged in [technical debt](../architecture/tech_debt_and_optimizations.md).
@@ -31,7 +31,7 @@ flowchart TD
| Owner / location | Typed contract or change | Boundary |
| --- | --- | --- |
| `scripts/stage/stage_draw_order.gd`, `class_name StageDrawOrder extends RefCounted` | Constants `WALL := -40`, `FAR := -30`, `NEAR := -20`, `TERRAIN := -10`, `FLOOR := -5`; `static func scenery_z(depth: StringName) -> int`; `static func apply_terrain(node: TerrainBlock) -> void` | One drawing policy. Leave the existing Prop `-1/0/1` and character relative order intact; rebase terrain drawing only. Never serialize raw `z_index` in place of a semantic depth/finish. |
| `scripts/stage/scenery/stage_sky_view.gd`, `class_name StageSkyView extends Control` | `func set_sky(id: StringName) -> void`; `func get_sky() -> StringName`; `func apply_theme(theme: SandboxTheme) -> void` | Full-rect child of a dedicated `CanvasLayer` with `layer = -1`, mouse ignored. Deterministic vector `_draw()` on viewport resize/theme/choice only. No world hit test or physics. |
| `scripts/stage/scenery/stage_sky_view.gd`, `class_name StageSkyView extends Control` | `func set_sky(id: StringName) -> void`; `func get_sky() -> StringName`; `func apply_theme(theme: SandboxTheme) -> void`; camera binding specified in the [Sky addendum](#phase-19-addition-camera-aware-sky-details) | Full-rect child of a dedicated `CanvasLayer` with `layer = -1`, mouse ignored. Deterministic vector drawing on preset, viewport, or effective camera-framing changes. No world hit test or physics. |
| `scripts/stage/scenery/stage_wall_panel.gd`, `class_name StageWallPanel extends Node2D` | `var finish_id: StringName`; `var tint: Color`; `var panel_size: Vector2`; optional `var motif_origin_offset: Vector2`; `func configure(size: Vector2, finish: StringName, color: Color) -> void`; `func get_local_bounds() -> Rect2` | Finite world rectangle centered at `position`. No `CollisionObject2D`, navigation, support, or interaction registration. Resize changes `panel_size`, never root scale, so motifs retain their world pitch. The renderer lays the pattern out from each panel's current world transform, so eraser-created fragments keep continuous brick courses; the optional offset preserves source-wall lineage in saved data. |
| `scripts/stage/scenery/stage_scenery_artwork.gd`, `class_name StageSceneryArtwork extends Node2D` | `var asset_path: String`; `var display_name: String`; `var depth: StringName`; `var last_known_local_bounds: Rect2`; `var missing: bool`; `func load_visual(path: String) -> bool`; `func set_depth(value: StringName) -> void`; `func get_visual_world_bounds() -> Rect2`; `func get_saved_footprint() -> Rect2` | Build a `Visuals` child via `PropVisuals.build()` from the complete shape stack. No `PropBlock`, collision hull, physics, Use spots, anchors, or event identity. If loading fails, render a neutral marker/known footprint from saved data and preserve path/name/transform. |
| `scripts/stage/scenery/stage_surface_renderer.gd`, `class_name StageSurfaceRenderer extends RefCounted` | `static func is_wall_finish(id: StringName) -> bool`; `static func is_ground_finish(id: StringName) -> bool`; `static func draw_wall(canvas: CanvasItem, rect: Rect2, finish: StringName, tint: Color, world_origin: Vector2) -> void`; `static func draw_ground(canvas: CanvasItem, rect: Rect2, finish: StringName, tint: Color, world_origin: Vector2) -> void` | Shared vector definitions for Paint/Brick/Wood, with Grass/Dirt/Sidewalk/Asphalt for Ground. One motif vocabulary and world-coordinate phase; no raster scale blur. Drawing helpers clip motifs to finite panel/block bounds and bound work to visible tiles. |
@@ -75,7 +75,7 @@ The manual **Erase wall area** tool adds one optional wall field, `motif_origin_
The semantic draw order is Sky → wall panels → Far art → Near art → terrain → floor art → characters and Props. `StageDrawOrder` applies bands to direct `World` children. Set all existing terrain varieties to the terrain band for consistent physical-surface drawing; their collision and input remain unchanged. Prop `-1/0/1` choices and World child order among legacy depth-zero Props and stickmen are preserved. Reapply the policy on spawn, load, depth change, and reset. Selection must use the same semantic tier order plus smaller actual art footprint preference, rather than assuming child insertion order expresses depth.
`StageSkyView` lives in its own `CanvasLayer` behind `World`, not under the camera or a wall. Its draw rectangle follows viewport size, so panning/zooming cannot reveal an edge. Use deterministic vector gradient bands, stars, clouds, sun/moon and shapes with bounded point counts. The preset controls only this view. Rendering should be static between sky or viewport changes; no per-frame random placement, animation, parallax, or tint propagation. Sky thumbnails use the same drawing definitions at card size so the choice predicts the Stage.
`StageSkyView` lives in its own `CanvasLayer` behind `World`, not under the camera or a wall. Its draw rectangle follows viewport size, so panning/zooming cannot reveal an edge. Use deterministic vector gradient bands, stars, clouds, sun/moon and shapes with bounded point counts. The preset controls only this view. The gradient stays screen-fixed; the [camera-aware Sky addendum](#phase-19-addition-camera-aware-sky-details) defines bounded, camera-responsive details and redraw invalidation. There is no per-frame random placement, autonomous animation, or tint propagation. Sky thumbnails use the same drawing definitions at card size in their unbound default composition.
`StageSurfaceRenderer` defines finite vector motifs with a world-aligned origin and fixed world-unit pitch. Wall and Ground may share Brick/Wood definitions but use context-aware fill/outline colors and scale. `StageWallPanel._draw()` uses its current finite rectangle as a clip and asks the renderer for only intersecting motif cells. For brick walls, cull to the camera-visible local rectangle; if that visible area exceeds 12,000 cells, increase the brick pitch uniformly for all visible courses instead of truncating rows or columns. This keeps large walls fully patterned and fragments continuous without unbounded draw work. Ground renders within its existing rectangle geometry; existing `CollisionPolygon2D` and navigation surfaces are never rebuilt merely for a finish change. The selected **New Ground look** is passed into both `StageTerrainPainter` ghost creation and each committed Ground block; it never repaints earlier blocks. During group resize, wall motif lines retain their pitch and phase. Ground blocks adjacent on a shared edge use the same world phase; shared same-finish interior borders are suppressed or visually joined so a painted run reads as one surface. The neighbor check refreshes on place, delete, move, load, and per-piece finish change, and considers only nearby Ground rectangles; it does not change occupancy/collision. Other motif types remain bounded by the camera-visible region or a maximum cell count per finite panel so `_draw()` work stays limited.
@@ -185,6 +185,8 @@ On scene open, load every live artwork reference. A missing/invalid source leave
Keep versioned starter documents under `content/stages/starters/` with stable IDs for Blank, Town Street, and Living Room. They are ordinary `.scene` snapshots, not locked nested instances. Blank may be assembled from the current neutral empty-scene defaults; Town Street and Living Room data use the same wall, artwork, terrain, Prop, Sky, and camera schema as a saved child scene. The bundled files reference only explicitly approved production art. A window is a genuine gap between wall rectangles with a transparent frame and Sky behind it; no cutting or glass behavior is inferred. Furniture Props receive deliberate still/block/support roles from the normal Prop contract, without promising Sit.
The focused [Phase 19 starter-scene composition specification](phase_19_starter_scene_composition.md) defines the approved pan-safe Town Street and Living Room surfaces and the opening-shot bounds used by starter-card previews. It keeps the ordinary v1.5 scene records and camera free of limits.
The current `PendingFileAction.NEW_SCENE` unsaved Save/Discard/Cancel gate remains authoritative. After it resolves, show the shared starter gallery **before** replacing the current document. Canceling the gallery leaves the existing scene and post-gate dirty state. Selecting a starter preflights its scene data, loads ordinary editable objects, resets `current_path` to empty, stamps a fresh creation time, proposes its editable name, and marks the new document unsaved. Failed starter validation leaves the prior scene intact and shows one themed error. No starter is inserted into another scene. A first-save path follows the existing Save As flow. CLI scene playback continues to load `.scene` directly, with no starter-specific runtime branch.
## UI component and theme reuse
@@ -237,7 +239,7 @@ Each row names a child path and a repeatable observation. Automated checks may c
| Edit Scenery → select two copies of one artwork; drag a corner on one, release; then cancel another drag with Esc/RMB/touch Cancel and release the pointer | The opposite visible corner stays at the same world position; only the edited placement changes, and it commits on release without Keep. A canceled drag restores its snapshot and the late release makes no change. With Snap on, a moved artwork or wall's visible top-left lands on a grid intersection. Smaller then Bigger returns near the initial bounds. Save/reopen and check the two copies retain distinct scales, while Props/Ground/PLAY behavior remains unchanged. A missing artwork without known bounds shows no resize handle. |
| Open a pre-amendment `.prop` and `.scene`, then explicitly overwrite one referenced `.prop` through ordinary Save | Opening alone does not rescale artwork or placement values. The explicit source overwrite changes every live reference to that source on reopen, while each scene placement retains its saved scale; there is no hidden compensation, copy prompt, or version migration. |
| Move a referenced `.prop` aside, reopen, repair selected missing artwork; cancel once then confirm | One neutral marker remains at saved position/depth, summary appears once, ordinary EDIT targets are not stolen, cancel keeps path/dirty state, and confirmed repair changes only that placement. Missing footprint/no-footprint group resize rules and PLAY marker presentation hold. Restore source and reopen to confirm live-reference visual update. |
| Select each Sky, pan/zoom, PLAY/stop, reopen | Sky fills screen without an edge or flicker; foreground pixel colors are unchanged by Sky choice. Layer order remains Sky/wall/Far/Near/Ground/floor/actors through camera movement, simulation, and reload; ghosts/handles/capture bars disappear in PLAY. |
| Select each Sky, pan/zoom, PLAY/stop, reopen | The fixed gradient fills the screen without an edge or flicker; details follow the bounded response in the [Sky addendum](#phase-19-addition-camera-aware-sky-details). Foreground pixel colors are unchanged by Sky choice. Layer order remains Sky/wall/Far/Near/Ground/floor/actors through camera movement, simulation, and reload; ghosts/handles/capture bars disappear in PLAY. |
Visual acceptance requires opening the affected scene in a render-capable Godot GUI, capturing the named chooser, normal/zoomed Stage, missing-art, group-preview, **Prop Editor sizing guide at Small/Big and narrow width, active single-piece handles/capture bar**, and PLAY states, and inspecting the pixels. Record capture location, scene/state, and observation. Interactive acceptance requires exercising the actual controls and input events, including RMB/Esc/touch cancellation, button sizing, grid-aligned move, commit on release, and a late release after cancellation. If GUI or input automation is unavailable after an attempted route, name the unverified outcomes and hand the user a focused reproducible check; a headless parse or screenshot file alone is insufficient. Phase 19 user/editor verification remains open until the child checks the integrated behavior.
@@ -250,3 +252,58 @@ Visual acceptance requires opening the affected scene in a render-capable Godot
- Rebased terrain drawing enables the required floor-art tier without changing Prop-to-character depth semantics. It changes terrain's relative visual position beneath Props; physics and navigation stay unchanged and must be regression checked.
- Vector motifs retain clarity and world alignment without repeated raster textures, at the cost of careful clipping/culling and seam management. Keep motif definitions centralized so wall/Ground previews match runtime drawing.
- Stage-wide Undo/Redo deserves one command history across placement, deletion, style, repair, and group commits. The Phase 19 Keep/Cancel preview is a local transaction only. Whole-set transform needs its own design because Ground physics, character/Prop placement, rules, and camera all have different transform invariants.
## Phase 19 addition: camera-aware Sky details
This focused addition implements the accepted [product addendum](../plans/phase_19_scenery_backgrounds_surface_finishes.md#camera-aware-sky-details-addendum) and [UI/UX contract](phase_19_scenery_backgrounds_ui_ux.md#camera-aware-sky-details). The original clauses above that describe Sky details as static are superseded here. **Only the four existing child-facing Sky presets gain automatic camera response.** The internal neutral Sky may use a zero-response profile. There is no motion setting, additional Sky card, theme token, scene field, or change to Background ground behavior.
### Ownership, nodes, and typed contract
```mermaid
flowchart LR
Camera[SandboxStage/Camera2D\nworld position + zoom] -->|bind once; sample framing| Sky[StageSkyLayer: CanvasLayer -1\nStageSkyView: Control]
Sky --> Gradient[full-rect gradient\nscreen fixed]
Sky --> Motifs[clouds / sun / moon / stars\nbounded camera response]
World[World: Background ground + walkable Ground + actors] --> Camera
Cards[Sky chooser and starter card previews] -->|SkyView without camera binding| Still[default still composition]
```
`SandboxStage` (`class_name SandboxStage extends Node2D`) owns the one live `Camera2D` and passes it to the already-built `StageSkyView`; it does not dispatch a separate Sky update from every mouse, touchpad, scene-load, mode, or PLAY path. `StageSkyView` (`class_name StageSkyView extends Control`) owns all Sky drawing and the response calculation. Its narrow typed API is:
```gdscript
func bind_camera(camera: Camera2D) -> void # A null camera restores the default still composition.
func set_sky(id: StringName) -> void
func get_sky() -> StringName
func apply_theme(theme: SandboxTheme) -> void
```
`StageSkyView` samples the bound camera's finite `global_position` and positive uniform `zoom.x` in its own `_process(delta: float) -> void`, compares the **effective** framing against the last drawn values, and calls `queue_redraw()` only when the visible composition changes. Its `NOTIFICATION_RESIZED` path also invalidates layout. Invalid or unavailable camera data falls back to the fixed default composition rather than propagating NaN into drawing. The first binding, preset change, and scene load invalidate immediately so a newly shown view does not display a previous frame's motifs. No signal or runtime state is serialized; the last sampled values exist only to avoid redundant redraws. The chooser's `StageBackgroundCardPreview` and starter's `StageStarterCardPreview` create unbound `StageSkyView` instances and remain representative still images. No new UI component, Stage inspector control, or `SandboxTheme.stage_panels` token is needed.
### Deterministic response and layout
Use the built-in Stage camera framing, `REF_POSITION := Vector2(0.0, -400.0)` and `REF_ZOOM := 0.5`, as the fixed reference for **every** scene. These values match `SandboxStage`'s current defaults and `sandbox_stage.tscn`. Evaluate each frame from absolute camera position and zoom; never accumulate input deltas, seed from elapsed time, or capture a per-session origin. Thus identical `{sky_id, camera.position, camera.zoom, viewport.size}` yields identical drawing after mode changes, PLAY/stop, and save/reopen. The existing `.scene` camera position/zoom and `sky_id` fields are sufficient; scene version and migration stay unchanged.
Each preset has one internal response profile, shared by all its motifs. A profile contains bounded horizontal/vertical pan limits in viewport fractions, a pan response span, and a bounded logarithmic zoom strength/span. These are renderer constants, not saved data or child-facing controls. A suitable starting envelope is at most about **5% of viewport width and 3% of height** for motif displacement and about **0.85–1.15×** for uniform motif size over the Stage's supported zoom range; tune each preset by rendered comparison at ordinary, close, and wide shots. Derive pan from the camera-position difference expressed in reference-frame screen pixels and pass it through a smooth odd saturating function (for example `tanh`). Derive zoom from `log(camera_zoom / REF_ZOOM)` through a smooth bounded function, then exponentiate to keep the size positive and reciprocal around the reference zoom. Avoid hard clamps in the visible response range, which can create a conspicuous change in motion at the limit. Camera movement of world objects must visibly exceed Sky-detail movement at ordinary framing. At the reference camera framing, displacement is zero and scale is one.
For clarity, one possible pure calculation is `shift.x = -W * limit_x * tanh(((camera_x - REF_POSITION.x) * REF_ZOOM / W) / span_x)` (and likewise for y), with `size_factor = exp(gain * tanh(log(camera_zoom / REF_ZOOM) / zoom_span))`; choose `limit/span` and `gain` so the detail response remains gentle. The final spec requirement is bounded, continuous, deterministic behavior, not these exact coefficients. Apply the same profile to all motifs of a preset as one visual group; individual stars may retain different authored sizes and positions, but do not acquire independent motion layers. Keep `sky_neutral` deterministic; a zero-response profile is acceptable for that internal default.
The gradient remains a full `Rect2(Vector2.ZERO, size)` draw in the screen-space `CanvasLayer`; **do not apply the motif transform to the gradient**. Place motif centers using normalized viewport anchors and scale their authored vector shapes **uniformly** with a single viewport-derived scalar, rather than separate x/y stretch. Apply the camera-induced size factor about the motif's own anchor, then add the bounded shift. Let the viewport clip any motif portion outside the visible rectangle; the gradient still fills those pixels, so no Sky edge appears. On resize, recompute anchors and the scalar from the new dimensions and reevaluate the same absolute camera frame. Retune the current low and oversized clouds, especially Cloudy Day and Sunset, so the ordinary lower action area remains comparatively open. This is art composition within the renderer, not a hard mask that hides clouds over arbitrary character positions.
The current star loop uses `fposmod(..., size.x/y)`, so changing viewport size can wrap a star abruptly to a distant position. Replace that layout with a fixed, deterministic set of normalized star anchors (or a fixed seeded list mapped to normalized coordinates); never seed it from the frame clock or call `randf()` while drawing. Existing cloud/moon curves should be sampled or cached once per shape/preset where practical, because camera movement now invalidates the drawing more often. Keep motif count and vector point count bounded; retain the gradient texture until the preset actually changes. There is no autonomous cloud drift, weather animation, horizon line, selection geometry, collision, or world tint.
`StageBackgroundGround` remains a world-anchored `Node2D` surface behind walkable Ground. Its landscape edge and the walkable Ground therefore move and scale together through the normal `Camera2D` transform. The Sky makes no ground-line claim and does not inspect terrain or character positions.
### Data flow and observable acceptance
1. The child chooses Clear Day, Cloudy Day, Sunset, or Starry Night. `SandboxStage` calls the existing `set_sky(id)` path; the Sky draws its fixed gradient and reference-frame motif composition. The chooser card remains a still preview.
2. A pan or zoom changes `Camera2D` through any existing input route. The bound `StageSkyView` samples the resulting absolute framing, derives its bounded motif transform, and redraws only when that framing changes. No mode or PLAY-specific Sky branch runs.
3. Save writes the existing `sky_id` and camera fields. Reopen loads those values; the next Sky draw derives the same composition from them and the current viewport size. A resize derives a coherent composition at the new size without changing saved state.
Repeat this in a rendered Stage: choose each preset; place a stickman on walkable Ground with a broad Background ground panel behind it; frame an ordinary shot, a close shot, and a wide shot; pan in EDIT and DIRECT, switch to PLAY and pan/zoom again, then stop. At an unchanged camera framing, motif anchors and apparent size must match across those mode and PLAY transitions. Resize the viewport twice and verify the gradient remains full-screen, motif shapes keep their proportions, and stars do not jump across the screen. Save at a nondefault camera framing, reopen at the same viewport size, and compare the motif composition; it must match without a separate Sky setting. Throughout, Sky displacement and scaling are perceptible but restrained, no edge or flicker is exposed, the lower ordinary action area remains usable, Ground and Background ground stay aligned, and foreground colors/physics remain unchanged. A screenshot or headless parse alone cannot establish the camera-response and composition outcomes; inspect rendered frames at the named camera states.
### Architectural trade-offs
- Keeping one `CanvasLayer` and one `StageSkyView` reuses current drawing and preview paths. The renderer must now redraw vector motifs while the camera moves; bounded geometry, geometry caching, and change-only invalidation keep that cost proportional to visible camera changes.
- Fixed reference framing avoids a new `.scene` field and makes save/reopen exact. Very distant camera positions can sit near the response limit, so additional panning there may change the Sky little; that is the accepted cost of a bounded composition.
- One profile per preset is simple to tune and preserves visual unity. It does not simulate separate cloud distances. True multilayer parallax and autonomous Sky animation remain outside this Phase 19 addition.
- Normalized anchors and uniform motif size preserve proportions across viewport shapes. Wide or narrow aspect ratios change the available spacing between details, requiring visual checks on both shapes; exact pixel placement across resizes is not promised.
@@ -1,6 +1,6 @@
# Phase 19 — Scenery, backgrounds, and surface finishes: UI/UX specification
> **Status:** Approved and implemented, including the Stage-relative asset-sizing addition (2026-10-16). Based on the [accepted product brief](../plans/phase_19_scenery_backgrounds_surface_finishes.md). Full Phase 19 user/editor verification remains pending.
> **Status:** The original Phase 19 UI/UX is approved and implemented, including the Stage-relative asset-sizing addition (2026-10-16). The accepted camera-aware Sky behavior is being specified as a focused Phase 19 addition. Based on the [accepted product brief](../plans/phase_19_scenery_backgrounds_surface_finishes.md). Full Phase 19 user/editor verification remains pending.
>
> **Decision:** Use a responsive Scenery chooser followed by canvas tools. Placement stays active for repeated copies. Right-click exits placement. Scenery artwork keeps live `.prop` references, with a visible, repairable placeholder if a referenced file cannot be found. The Phase 19 addition standardizes new artwork around a standing-stickman size guide and adds single-placement Scenery resizing; direct single-piece transforms commit on release, while the existing group preview keeps its Keep/Cancel boundary. Phase 19 does not add Undo; Stage Undo is deferred to its own future phase and should be recorded in tech debt during architecture planning.
@@ -114,7 +114,17 @@ These are relationship examples, not fixed pixel geometry. On a narrow viewport
- **Backgrounds → Sky** opens four designed thumbnail cards: **Clear Day**, **Cloudy Day**, **Sunset**, **Starry Night**. The currently selected card has a strong border, filled selected state, and the text **“Current Sky”**; keyboard focus remains a separate outline.
- One tap or Enter/Space changes the Sky immediately; the chooser stays open so another card can be tried. Close means “stop choosing”, not “revert”. With no Undo in Phase 19, the child can restore the previous look by choosing its card again. Changing Sky never recolors or tints other pieces. Sky has no Stage selection handles or overlap entry.
- The thumbnail shows enough clouds, sun/moon, and color variation to predict the Stage result. A chosen Sky is static during PLAY and stable through camera movement.
- The thumbnail shows enough clouds, sun/moon, and color variation to predict the Stage result. It remains a representative still image; the Stage applies its automatic camera response without asking the child to configure motion.
### Camera-aware Sky details
- The four existing Sky cards and their immediate selection flow stay unchanged. The selected card still says **Current Sky**. Do not add a motion toggle, slider, new label, tutorial cue, cursor, or theme token.
- Keep the vertical gradient fixed to and filling the full viewport. Clouds, sun, moon, and stars respond automatically and subtly to Stage camera pan and zoom in **EDIT**, **DIRECT**, and **PLAY**. They move less and change size less than the stickman and Ground, so they read as distant details while connecting to a dramatic shot.
- Preset-specific composition keeps the main action area clear at ordinary framing. During close and wide views, details remain recognizable and do not grow across the character or walkable Ground. Camera response stays continuous and bounded; it does not expose an edge, pop, sweep distractingly, or reverse abruptly.
- At unchanged camera framing, switching modes, starting/stopping PLAY, and reopening a saved scene reproduce the same Sky composition. On viewport resize, the gradient continues to cover the view and the details recompose coherently.
- Keep landscape edges on world-anchored **Background ground** and scenery. Sky details do not draw a horizon, gain selection handles, enter selection, or alter the colors or behavior of world pieces.
- The visible feedback is the Sky details' restrained response to the same pan/zoom gesture already used on the Stage. No additional status message or control feedback is needed.
- Acceptance path: choose each Sky, then pan and zoom through ordinary, close, and wide framing in EDIT/DIRECT/PLAY; resize the viewport; start/stop PLAY; save/reopen with a saved camera view; and place Background ground behind walkable Ground. Confirm a subtle but perceptible Sky response, clear action area, no edge/flicker, consistent mode transitions, and Ground alignment.
### Wall panel
@@ -0,0 +1,157 @@
# Phase 19 — Starter Scene Composition: Architecture and Implementation Specification
> **Status:** Implemented; focused behavior verification passed. Rendered-pixel and user/editor verification remain pending because the available Godot capture route exited before producing an image. The user requested an opaque Town Street Background ground layer and rows of ordinary-size walkable Ground tiles in both starters instead of oversized terrain blocks.
>
> **Approvals:** Everyday Storybook direction approved; architecture defaults approved: **1,280×720 logical-pixel reference viewport (16:9)**, saved zoom **0.5**, horizontal pan guarantee **±1,000 world units**, and **100 world units** of end margin.
>
> **Related documents:** [Accepted composition brief](../plans/phase_19_starter_scene_composition.md), [UI/UX specification](phase_19_starter_scene_composition_ui_ux.md), [Phase 19 scenery specification](phase_19_scenery_backgrounds_surface_finishes.md), and [Background Ground and Wall Color brief](../plans/phase_19_background_ground_and_wall_color.md).
## Decisions and scope
1. Revise the existing Town Street and Living Room `.scene` snapshots using the existing editable node types. Promote the edited starter snapshots to the existing current scene format v1.6; their bundled source snapshots are v1.5. Preserve current starter IDs, File → New flow, and ordinary save/reopen behavior. The existing 1.5→1.6 migration and `background_ground` object type are already supported; this feature adds no schema field and does not bump beyond v1.6.
2. At saved zoom 0.5, author a horizontal surface span of at least **4,760 world units** for each surface that must remain present during pan. Use adjacent ordinary-size terrain tiles rather than scaling one walkable block across the whole span. The current Ground template snaps to a 192×32-world-unit polygon at scale 1; 25 adjacent tiles produce a 4,800-unit run from x=−2,400 to +2,400, covering a **1,280-logical-pixel rendered viewport**, camera center travel from the saved x-position by **±1,000 world units**, and at least **100 world units** of margin at either end:
`required_width = viewport_width / saved_zoom + 2 * pan_travel + 2 * margin`
`= 1,280 / 0.5 + 2 * 1,000 + 2 * 100 = 4,760 world units`
Each tile remains a normal-size, independently selectable Ground object. Adjacent edges must meet exactly: preserve one level collision top, avoid gaps/overlaps, and let the repeated tile edges read as intentional joints.
With the current saved camera center x=0, 25 tiles span x=−2,400 to +2,400. At camera x=±1,000, the viewport reaches ±2,280, leaving a 120-unit margin. Town Asphalt uses two additional normal-size tiles, one at each end, so a character can step down from the sidewalk onto the road at an exposed curb; its 27 tiles span 5,184 units.
3. Keep camera panning unrestricted. The authored guarantee is finite and applies at the saved zoom over the stated envelope; scene edges can appear beyond that envelope or in a wider zoomed-out shot. Zooming in continues to show the same continuous surface across the viewport.
4. Keep camera and placement state as ordinary saved `.scene` fields. Adjust the saved camera y only as needed to meet the approved opening composition; do not add camera limits or runtime surface extension.
5. Reframe `StageStarterCardPreview` around the saved opening composition. Its image must not become panoramic merely because pan-only Ground or wall extents are longer. Derive the preview focus from the saved camera and visible central composition; do not add preview metadata to `.scene`.
6. Reuse existing production artwork, surface finishes, Sky presets, and child-facing choices. This pass introduces no new controls, theme tokens, scene object types, signals, or graphical assets.
**In scope:** `content/stages/starters/town_street.scene`, `content/stages/starters/living_room.scene`, `scripts/stage/scenery/stage_starter_card_preview.gd`, and targeted navigation-ribbon coalescing plus the exposed Town curb connector in `scripts/editors/stage/sandbox_stage.gd` needed for tile seams and the 32-unit sidewalk-to-road step.
**Out of scope:** camera restrictions; runtime-generated or auto-extending scenery; a new starter or template system; new art; changes to Ground physics/collision, navigation behavior beyond adjacent tile seams and the exposed 32-unit Town curb step, wall collision, Prop roles, Sky response, chooser controls, or scene serialization; zoom-out edge guarantees beyond the stated envelope.
## Current state and architecture fit
Both bundled starter files currently use scene v1.5 with `sky_id`, saved camera `{position, zoom}`, and independently editable object records. `StageSceneIO` migrates them to the current v1.6 format on read. Both currently save zoom 0.5 and camera x=0. Town Street saves a Cloudy Day Sky, one Far skyline, four Near artworks, and 1,400-world-unit Sidewalk and Asphalt runs. The buildings and trees do not share one visible base line, and their current Ground ends inside the opening view. Living Room saves a Clear Day Sky, three painted wall panels around a real window gap, seven 200-unit Wood Ground pieces, separate wall art and rug, and three functional Props. The floor run is short; the couch and armchair are visibly above its support line.
`StageSceneIO` and `StageSceneAdapter` already persist and reload these records. `StageSpawner` creates ordinary terrain and Props. `TerrainBlock` remains the walkable surface; `StageBackgroundGround`, `StageWallPanel`, and `StageSceneryArtwork` remain inert Scenery. `StageSurfaceRenderer` already draws world-aligned surface motifs with viewport culling, and `StageRectSurface` caches wall patterns. These are existing contracts to reuse, not new abstractions.
The source art's measured visual bottoms are useful placement references: Town house about 128 local units below its origin, storefront 119, tree 113; couch 111, armchair 107, coffee table 94. These bounds are starting measurements only. Final alignment uses rendered visible contact at Stage scale, not saved origins or selection AABBs alone.
## Scene and rendering flow
```mermaid
flowchart LR
FileNew[File → New → Town Street or Living Room] --> Read[StageSceneIO migrates v1.5 input to current v1.6]
Read --> Apply[StageSceneAdapter + StageSpawner create ordinary World children]
Apply --> Camera[Saved Camera2D position and zoom]
Camera --> Sky[Screen-space StageSkyView]
Camera --> World[World surfaces, scenery, Props, actors]
World --> Opening[Everyday Storybook opening composition]
Pan[Unrestricted camera pan] --> Camera
Extent[Authored 4,760-unit surfaces] --> World
Read --> Preview[StageStarterCardPreview]
Camera --> Preview
Preview --> Card[Opening-shot preview; ignores pan-only bounds]
Opening --> Save[Existing scene save/reopen path]
Save --> Read
```
The live Sky remains the screen-space background. World surfaces and objects keep their existing semantic draw order: Sky, Background ground, wall/Far scenery, Near scenery, walkable Ground and floor scenery, then action objects and actors. Preserve the existing Sky response and keep the chooser card's Sky unbound to the live camera.
## Starter composition contracts
### Town Street
- Keep the Cloudy Day Sky, muted Far skyline, existing house/storefront/tree artwork, and normal Ground palette. Use the Far skyline and the placement/scale of existing Near art to imply more street while preserving a clear center.
- Align the visible bottoms of each building and tree to one sidewalk top line. Use source-art visible bounds to seed placement, then adjust by rendered inspection. At saved zoom 0.5, the final visible gap from an asset base to the sidewalk must be no more than 3 logical screen pixels.
- Give the initial camera frame a calm center lane at walking height: at least its middle third remains clear of Near artwork, leaving room for a standard standing stickman and a short speech bubble.
- Add an opaque `StageBackgroundGround` behind the skyline, near artwork, and walkable tiles. It begins at the skyline's lower visible datum and extends below the opening viewport, covering the clouds that otherwise show through the gap between the skyline and street. Use the existing Grass finish. It is visual only and does not create another walking line.
- Build Sidewalk as a horizontal row of 25 default-size Ground tiles, with centers from x=−2,304 to +2,304 and bounds x=−2,400…+2,400. Build Asphalt as a separate row of 27 default-size tiles, with one extra tile at either end and bounds x=−2,592…+2,592. Place Sidewalk at y=0 and Asphalt at y=32 so their edges meet and the Asphalt begins below the Sidewalk. The exposed 192-unit road ends provide a physically reachable 32-unit curb step; route navigation at those endpoints without merging the Ground blocks or colliders. Keep both rows flat and continuous; no single oversized block or deep scaled Asphalt piece.
- Keep skyline and Near artwork as separate editable placements. The Far skyline should not reveal a distracting cut edge within the pan envelope; reuse/reposition or duplicate existing skyline art only if its actual bounds require it.
### Living Room
- Keep the Clear Day Sky and the existing window frame and room artwork. Extend the painted wall from side to side across x=−2,380…+2,380, with a clean floor boundary and editable baseboard or equivalent edge.
- Build the wall as opaque editable panels around one deliberate window aperture. The opening is the only visible route to Sky. Extend the top and side panel bounds far enough to cover the full rendered viewport at pan endpoints; use slight opaque overlap at non-window joins to prevent subpixel Sky leaks. Do not overlap the aperture.
- Preserve the window as a real gap between wall panels with the existing transparent frame in front of the Sky. Do not add fake glass, collision, or a new wall-cutout behavior.
- Build `finish_ground_wood` as one horizontal row of 25 default-size Ground tiles. Centers are spaced 192 world units apart from x=−2,304 to +2,304 at y=200; at scale 1 the row's top is y=184 and its combined bounds are x=−2,400…+2,400. Keep one level collision top and the existing wood pattern scale; do not stretch one block across the room.
- Place the couch, armchair, coffee table, and floor art so visible supports/base edges sit at the wood floor line within 3 logical screen pixels at saved zoom. Preserve Prop roles and their normal collision/support. Keep the rug centered, visually above the wood, below actors, and outside the central movement lane.
- Keep the wall decorations separate and secondary to the window and action. No individual decoration or furniture piece may be merged into a backdrop image.
### Shared camera and child readability
- Retain saved zoom 0.5 unless rendered comparison shows the approved composition cannot fit. Save the chosen camera y/center in each scene so normal File → New opens directly to the intended shot.
- At the supported 1,280×720-logical-pixel reference viewport, test the saved center and camera x at −1,000 and +1,000, at zoom 0.5. At all three positions, the applicable sidewalk/road, wall/baseboard, and floor cover the full rendered viewport width with no panel or terrain end, accidental Sky seam, or distracting join.
- Tune vertical framing against the safe Stage area in a render-capable run: put the walking line about 70–80% down from the top of the usable Stage area. A standing stickman must fit from feet to head; a two-line speech bubble must remain readable with about 24 logical pixels of clearance from visible Stage chrome.
- A narrower viewport is covered by the same surfaces and must retain actor/bubble legibility. Do not assert the 1,280-pixel guarantee for a wider logical viewport without recalculating the extent formula.
## Persistence and typed contracts
No new API, signal, or serialized field is introduced.
| Target | Existing contract and required update |
| --- | --- |
| `content/stages/starters/town_street.scene` | Write current v1.6 with the existing `sky_id`, camera, and ordinary object dictionaries. Add the Grass Background ground object, and use unique `file_id`s for each default-size Sidewalk (25) and Asphalt (27) terrain tile. Keep stable IDs for retained artwork and preserve the camera. |
| `content/stages/starters/living_room.scene` | Write current v1.6. Update wall panel `position`/`size`, use unique `file_id`s for each default-size Wood Ground tile, and preserve artwork/Prop transforms and camera. Keep the window aperture represented by the spatial gap between wall panels. |
| `StageSceneIO` (`class_name StageSceneIO`) | Read v1.5 legacy snapshots and write current v1.6 documents. Do not bump beyond v1.6 or add camera-envelope/preview fields. |
| `StageSceneAdapter` (`class_name StageSceneAdapter`) | Continue mapping `terrain`, `background_ground`, `wall_panel`, `scenery_artwork`, and `prop` dictionaries into their existing node classes. No starter-specific loader branch. |
| `SandboxStage` (`class_name SandboxStage extends Node2D`) and `Camera2D` | Load the saved camera normally. Do not clamp position to starter bounds. Coalesce adjacent collinear terrain top edges into continuous navigation ribbons so pathfinding crosses tile joints; connect Town's 32-unit curb step only where the lower Asphalt extends past the upper Sidewalk. Keep the terrain objects/colliders separate. Existing zoom limits and user input remain authoritative. |
| `StageStarterCardPreview` (`class_name StageStarterCardPreview extends SubViewportContainer`) | Replace the all-object-bounds fit as the preview focus rule. Keep its private preview setup and local `_world.scale`/`_world.position`; derive an opening-shot focus from scene `camera.position`/`camera.zoom` and a stable reference aspect. Ignore bounds added solely to cover the pan envelope. Do not mutate the loaded document or live camera. No public signal or caller change. |
| `TerrainBlock`, `StageBackgroundGround`, `StageWallPanel`, `StageSceneryArtwork`, `PropBlock`, `StageSurfaceRenderer`, `StageRectSurface` | Reuse existing typed configure/save/render/collision contracts. Modify one only if visual validation finds a concrete rendering, scaling, or hit-selection defect in the required bounds. |
Representative existing object shapes (not new schema):
```json
{"file_id":"ground_sidewalk_13","type":"terrain","asset_id":"ground","position":{"x":0,"y":0},"rotation":0,"scale":{"x":1,"y":1},"finish_id":"finish_ground_sidewalk"}
{"file_id":"ground_asphalt_13","type":"terrain","asset_id":"ground","position":{"x":0,"y":32},"rotation":0,"scale":{"x":1,"y":1},"finish_id":"finish_ground_asphalt"}
{"file_id":"town_backdrop","type":"background_ground","position":{"x":0,"y":Y},"size":{"x":4800,"y":H},"finish_id":"finish_background_grass","color":"#72A84C"}
{"file_id":"wall_left","type":"wall_panel","position":{"x":X,"y":Y},"rotation":0,"size":{"x":W,"y":H},"finish_id":"finish_wall_paint","color":"#5A9D70"}
```
`N`, `X`, `Y`, `W`, and `H` are scene composition values to tune from rendered visible bounds and camera framing. They do not denote new runtime fields. Prefer scene objects whose visible geometry meets at common edges rather than hiding incorrect positions with z-order.
## Starter-card framing
Today `_fit_scene_to_preview()` unions every loaded object's world AABB and fits that union into 256×256. The 4,800-unit rows of tiles or wall would dominate that union. The replacement should:
1. Read the saved camera center and zoom from the already-loaded scene data.
2. Build a square preview-only focus centered on the saved camera position. Use the 16:9 reference viewport height and saved zoom for the square world extent: at 1,280×720 and zoom 0.5, the crop is 1,440×1,440 world units. This keeps the town's central buildings/trees or the room's window/furniture in the chooser card while ignoring long pan-only bounds. Fit the square crop into the existing 220×220 usable area of the 256×256 preview.
3. Fit that focus region inside the preview's usable bounds. Do not include pan-only surface tails when computing the frame; the live scene still renders those full surfaces.
4. Leave the Sky preview still and full-rect. Apply preview pan/scale only to the preview `_world` node; never modify saved camera data.
5. Inspect both cards at their actual chooser size. Each should show its identity and central composition without shrinking the actor/set to a tiny strip or clipping the window/road datum.
Do not add per-starter preview bounds to `.scene` unless a shared, camera-derived crop demonstrably cannot keep both cards recognizable. That would require a separately approved data/authoring need.
## User paths and observable acceptance
| Child path / repeatable scenario | Required observation |
| --- | --- |
| File → New → Town Street | At the saved shot, the child sees a readable neighborhood, opaque land behind the skyline, continuous tiled sidewalk/road, a clear center lane, and the existing cloudy Sky above the skyline. Near artwork bases rest on the same walkable line within 3 screen pixels. |
| Town Street at saved zoom; pan to camera x=−1,000, 0, +1,000 | The full rendered width remains covered by sidewalk and Asphalt tile rows. Tile joints are intentional and edges have no collision or navigation gaps; no exposed row end or Sky/clouds show through below the skyline. A PLAY actor can walk off the sidewalk end and land on the exposed road, and navigation connects both walk lines there. The decorative backdrop does not support a stickman. |
| File → New → Living Room | The child sees a complete painted room with visible baseboard and wood floor. Sky appears through the window opening only. Furniture supports meet the floor within 3 screen pixels; rug is centered below actors, with an open action lane. |
| Living Room at saved zoom; pan to camera x=−1,000, 0, +1,000 | Wall, baseboard, and floor cover the full rendered width. No Sky leak appears at panel edges, under the floor, or outside the window. Furniture stays separate and the ground remains one support level. |
| Place a standard standing stickman and a two-line speech bubble in the central lane; inspect EDIT and PLAY | Feet contact the intended surface. Head, body, and bubble are legible with the specified chrome clearance. PLAY preserves Ground support and existing Prop behavior; no decorative Background ground or wall panel gains collision. |
| Open New Scene chooser; inspect Town Street and Living Room cards | Each card remains a recognizable opening-shot preview after the live-world pan surfaces are lengthened. The camera framing of the preview does not move the saved/live scene. |
| Save each revised starter as an ordinary scene, close/reopen it | Existing object types, finish IDs, individual placements, roles, Sky ID, and saved camera framing round-trip through the current scene v1.6 format. The loaded scene remains editable and has no starter-only runtime behavior. |
| Resize to a narrower Stage viewport; zoom in for a close shot | Continuous surfaces still cover the visible width. The action lane and window remain legible. This pass does not promise full-width coverage for zoom-out views wider than the reference envelope. |
Phase 2 must inspect rendered captures from a render-capable Godot Stage at opening frame, both pan endpoints, PLAY, both starter cards, and a narrower viewport; record the capture paths and observed pixel gaps/seams. Exercise actual camera pan, save/reopen, Ground support and navigation across tile joints, and normal selection/edit routes. Bake adjacent collinear tile tops into continuous navigation ribbons and connect the exposed Town curb step without merging separate terrain objects or colliders. A successful import or headless parse alone does not verify composition. If an interactive rendering/input route is unavailable after trying it, list each unverified outcome and provide a focused user check.
### Implementation and verification record (2026-10-21)
- The initial pass promoted both snapshots to v1.6 and aligned visible bases, but used oversized scaled walkable blocks and omitted Town Background ground. The user identified those issues; that surface implementation was superseded by the tile/backdrop revision below.
- `StageStarterCardPreview` now derives a square crop from the saved camera position and zoom. At zoom 0.5, it frames a 1,440 × 1,440 world-unit opening crop in the existing 220 × 220 usable preview area.
- `/Applications/Godot.app/Contents/MacOS/Godot --path /Users/ryan/Projects/stickman --headless --rendering-method gl_compatibility --script res://tests/test_phase19_starter_scene_composition.gd` passed **515 assertions** on Godot 4.7.1. It covers production scene loading, save/reopen, surface bounds, window opening, preview transforms, base contacts, PLAY support, navigation across each tile row, and sidewalk-to-road access. Town Asphalt has 27 scale-1 tiles spanning 5,184 units; a PLAY actor moved from x=2,280 to x=2,500 and landed grounded on the road at feet y=15.31. The navigation query from center to the opposite walk line follows the exposed right curb through `(2400, -4)` and `(2400, 12)`. Measured contact gaps at the saved zoom were 0.0 logical px for Town artwork; 0.0–0.123 px for Living Room floor artwork; and 0.025–0.225 px for the three Props. These are production-loaded geometry measurements, not pixel samples from rendered captures.
- `/Applications/Godot.app/Contents/MacOS/Godot --path /Users/ryan/Projects/stickman --headless --rendering-method gl_compatibility --script res://tests/test_phase19_scenery.gd` exits 1. The Town/Living starter surface, count, scale, bounds, seam, pan-margin, and camera assertions pass; the nine remaining failures concern the Sky/surface preview card count, a wall-erasure document-change signal, and Edit Scenery erase/Cancel capture-bar behavior. These same failures were present before the latest tile and curb changes. The environment also reports a Stage autosave warning during that run.
- Rendered-pixel inspection could not be completed here: a bounded GUI capture attempt exited 134 without output or an image, and an earlier headless compatibility capture produced no image before it was stopped after 30 seconds. The default headless Vulkan test path also crashed in shader translation. The focused compatibility test exited successfully; the environment printed a macOS CA-certificate lookup warning.
- Remaining user/editor checks: opening frames, ±1,000 camera pan endpoints, the Town background and tile appearance, both starter cards, a narrower viewport, dramatic zoom-in, and rendered seams/Sky inspection. User-supplied Town Street imagery exposed the missing background; this revised pass still needs a fresh rendered visual review. Automated geometry and PLAY checks cover support across tile seams and the curb step.
### User visual follow-up (2026-10-21)
The user's Town Street capture showed Sky clouds visible below the skyline because the initial implementation omitted Background ground. The user also rejected the oversized scaled TerrainBlocks used for walkable surfaces and requested left-to-right rows of ordinary Ground tiles in both templates. The current revision adds opaque non-walkable Grass Background ground behind Town Street and uses adjacent scale-1 tiles for Sidewalk, Asphalt, and Living Room Wood. Sidewalk uses 25 tiles; Asphalt uses 27, extending one tile beyond each sidewalk end so the road is physically reachable. The focused production-path test passed 515 assertions, including seam routing and a grounded PLAY step onto Asphalt. Fresh rendered review and user/editor checks remain pending.
## Trade-offs and deferred work
- Ordinary-scale tile rows avoid oversized Ground selection bounds and keep each walkable piece independently editable. Their joints should remain intentional, with adjacent collision and visible edges meeting without gaps. The navigation bake merges contiguous coplanar tile tops for routing while the Ground pieces remain separate, and connects the Town curb step only at exposed road ends.
- The 4,800-unit sidewalk/floor rows of ordinary-size Ground tiles cover the specified 4,760-unit minimum envelope and remain individually editable. Town Asphalt extends to 5,184 units for curb access. These finite surfaces do not track arbitrary viewport resizing, very wide zoom-out shots, or unbounded camera travel. The camera remains free; no edge clamp or auto-extension obscures that finite set boundary.
- Preview framing is derived from the saved opening camera instead of saved preview metadata. This avoids schema/round-trip work but gives every card the same generic crop policy; if that cannot show a particular starter clearly, document the concrete limitation before proposing metadata.
- No new art is required based on current library bounds. A curb/baseboard visual gap that cannot be composed from existing terrain finishes and wall panels should be described as one specific missing asset and sent through the existing asset-approval workflow.
- Stage-wide Undo/Redo remains deferred. Starter polishing edits saved object data through existing flows and adds no new transaction system.
@@ -0,0 +1,59 @@
# Phase 19 — Starter Scene Composition UI/UX Specification
> **Status:** Approved UI/UX specification. The separate architecture specification is approved for implementation. This defines scene presentation, not new controls.
>
> **Source:** [Phase 19 — Starter Scene Composition brief](../plans/phase_19_starter_scene_composition.md).
## User path
From **File → New**, the child chooses **Town Street** or **Living Room** and immediately sees a coherent set with a clear walkable area. At the saved camera frame, a standing stickman and a short speech bubble fit comfortably in the action area. The same visual relationships remain legible in EDIT, DIRECT, PLAY, and after save/reopen. A deliberate horizontal pan reveals more of the same set.
## Composition rules
### Town Street
- Keep the Cloudy Day Sky and a subdued Far skyline behind individual Near buildings and trees.
- Add an opaque, non-walkable Background ground layer below the skyline datum. It must cover the lower opening view behind the street and prevent Sky clouds from showing through the gap between the skyline and the walkable surface.
- Align all visible house, storefront, and tree bases to one sidewalk datum. Keep seams at or below 3 logical screen pixels at saved zoom.
- Carry the Sidewalk and Asphalt across the usable stage frame as horizontal rows of ordinary-size, walkable Ground tiles. Keep their tops level and their edges adjacent so the tile joints are intentional, with no collision or visual gaps.
- Leave at least the middle third of the initial frame visually calm at walking height. Near artwork may frame the action, but should not cover a character or speech placed in this area.
- Keep Background ground behind the skyline, artwork, and terrain; distinguish it from the walkable sidewalk/road. It does not create another walking line.
### Living Room
- Continue the painted wall and a horizontal row of ordinary-size, walkable Wood Ground tiles across the initial frame and agreed pan envelope. Keep adjacent tile edges level, with intentional joints and no collision gaps. A visible baseboard or equivalent edge marks where wall meets floor.
- Construct the window as a deliberate opening between editable wall pieces. The selected Sky is visible through that opening only; no Sky gaps appear at the room edges, below the floor, or along panel seams.
- Place the couch, armchair, and coffee table so their visible supports meet the floor within 3 logical screen pixels at saved zoom. Keep the rug centered, above the floor finish, below actors, and outside the main movement lane.
- Keep the middle third of the frame at walking height visually calm enough for a stickman and short speech bubble. Wall decoration stays secondary to the window and action.
## Camera framing and pan continuity
- Preserve the existing starter-choice flow and use a saved camera framing that shows the complete scene identity, walking datum, and clear action area at the actual safe Stage viewport size.
- Place the walking line around 70–80% of usable viewport height measured from the top. Show a default standing stickman from feet to head and leave about 24 logical pixels between a two-line speech bubble and visible Stage chrome.
- Define a horizontal pan envelope for each starter. At the saved zoom, the appropriate continuous surfaces must cover the full rendered viewport width at the envelope's left and right limits, with a small safety margin. Camera panning remains unrestricted beyond this authored envelope; finite set edges may appear there.
- Compute surface width from the reference viewport: `rendered_viewport_width / saved_zoom + 2 * pan_travel + 2 * safety_margin`. For a 1,280-pixel reference width, zoom 0.5, ±1,000-world-unit travel, and 100 world units of margin per side, surfaces need at least 4,760 world units. Use 25 standard 192-unit Ground tiles for the Town sidewalk and Living Room floor. Town Asphalt uses 27 standard tiles, extending one tile beyond each sidewalk end so the road is physically reachable. Do not scale one walkable block across the scene.
- Keep set parts independently editable. Tile rows use ordinary-scale pieces with adjacent edges, no collision gaps, and visible joints that read as regular surface tiles.
- Do not stretch one walkable Ground block across the full pan envelope. Use a left-to-right line of default-size terrain tiles so each surface remains an ordinary, individually editable piece.
## Starter chooser preview
The starter card should show a representative opening composition, not fit the full pan-only extent. The current preview fits the combined bounds of every object; extending the ground and walls could make the important scene too small. Bound preview framing around the saved opening shot (or an explicit authored preview frame) while still showing the Sky, walking datum, and recognizable key artwork. This is a preview-only framing decision: it must not crop or constrain the loaded scene.
## Controls, copy, and theme
- Add no controls, filters, dialogs, inspectors, cursor modes, placement overlays, or status messages.
- Keep the existing **Blank Scene**, **Town Street**, and **Living Room** choice labels.
- Add no `SandboxTheme` colors or `stage_panels` tokens. Use the existing Sky, wall, Ground, Scenery, and Prop visual styles; tune scene-art contrast so a character and speech remain easy to read.
- Reuse the existing EDIT, DIRECT, and PLAY mode treatments without adding mode-specific starter behavior.
## Observable UI/UX acceptance outcomes
1. Opening either starter at its saved frame reveals the intended place, a single readable walking datum, a calm central action area, and room for a default stickman plus short speech.
2. At saved zoom, there is no visible (>3 logical screen-pixel) gap between Near artwork and the street datum or between furniture supports and the room floor.
3. At the center and both ends of the specified pan envelope, the sidewalk/road or wall/baseboard/floor fills the usable Stage width without exposed ends, distracting seams, or stray Sky. In Living Room, Sky appears through the window opening only.
4. The chooser card remains a recognizable view of the opening composition after the world surfaces are extended for panning.
5. The same composition and visual ordering remain in EDIT, DIRECT, PLAY, and after save/reopen. Normal child edits can still move or remove individual pieces.
## Not applicable
There is no new interaction flow, so UI node/layout additions, focus behavior, cursor feedback, child-facing copy changes, dialogs, and inspector-token changes do not apply. Architecture will identify only the scene data, preview framing, or rendering changes needed to meet the observable outcomes.