game-stage Specification
Purpose
TBD - created by archiving change build-ink-signal-stage. Update Purpose after archive.
Requirements
Requirement: The app SHALL run normal play in a full-screen stage
The player-facing app SHALL use one full-viewport canvas sized for the device pixel ratio capped at two. During normal play the stage SHALL hide the interim header, sidebar, footer, scroll, selection, and context menu behavior; the remaining DOM SHALL be limited to an accessibility live region, the hidden Workshop file input, and developer tools behind ?dev.
Scenario: The player enters a proof
- WHEN the app opens a proof scene at a desktop or mobile viewport
- THEN the canvas fills the viewport at the capped device pixel ratio and no default sidebar controls are shown
Scenario: Developer controls are opt-in
- WHEN the app URL does not include
?dev - THEN interim DOM controls are hidden while normal keyboard, pad, touch, and scene controls remain available
Requirement: The stage SHALL provide the v0.3 scene flow
The stage SHALL support Title, INDEX, Proof, Pause, Win card, Workshop, and pending Gift scenes. Scene changes SHALL be driven by explicit input and SHALL use a cat-centered iris transition, with a straight cut under reduced motion. A win SHALL wait for explicit continue or replay input. Pause and other menu overlays SHALL use the D22 entry, navigation, and exit phases; the Win card SHALL use the D22 ring and card choreography. ?slowmo=N SHALL scale presentation timing for review without changing engine turns or held-input repeat cadence.
Scenario: Pause enters and exits before its action
- WHEN the player opens Pause and then selects a non-toggle action
- THEN the shutter, title, and staggered items enter in order; the selected item presses, items leave in reverse order, the shutter closes, and the action runs after the exit
Scenario: Repeated input during menu exit is ignored
- WHEN a second key or tap arrives while Pause is exiting
- THEN only the first captured action runs and navigation does not resume until the destination scene is ready
Scenario: An external import finishes during menu exit
- WHEN a file import finishes while a menu is exiting and requests Gift or Workshop
- THEN the current exit completes and the latest externally requested scene opens, while player menu input remains ignored during the exit
Scenario: Toggle remains in Pause
- WHEN the player selects Sound or Music
- THEN that item presses and updates in place while Pause remains open
Scenario: Win entrance can be skipped
- WHEN the player presses Enter before the Win card has settled
- THEN the entire card reaches its final legible state; a later Enter exits to INDEX
Scenario: An authored proof is already won on entry
- WHEN the player enters an initially won proof from INDEX while its exit animation is in progress
- THEN the proof handoff completes before the Win card opens for that same proof
Scenario: Reduced motion uses settled scenes
- WHEN reduced motion is requested
- THEN Pause and Win controls and text are immediately visible in their final positions without waiting for choreography
Requirement: Input SHALL be immediate, repeatable, and never queued
The stage SHALL use the shared input adapter for keyboard, Standard gamepad, and touch. A press SHALL emit on the same frame, browser repeat events SHALL be ignored, only the most recently held direction SHALL repeat after 175ms and every 110ms, undo SHALL accelerate from 240ms toward a 55ms floor, and a new gameplay intent SHALL snap an in-flight visual tween before starting the next action. A menu exit SHALL capture one selected action and ignore later menu intents until the exit completes; this is an explicit scene phase, not a gameplay input queue.
Scenario: Slow-motion review preserves input cadence
- WHEN
?slowmo=8is enabled and Right is held during a proof - THEN the engine accepts the same immediate and repeated movement cadence while presentation effects advance at one eighth visual speed
Requirement: INDEX SHALL be data-driven and preserve progress
The INDEX map SHALL lay out levels from catalog data on a serpentine path with at most five nodes per row and three rows per sheet. Progress SHALL be stored by packId/; locks SHALL be evaluated within each pack, and the first proof of every pack SHALL remain available even when bundled packs share one sheet. The next unsolved proof within a pack SHALL lock until its predecessor is solved, while additional sheets remain reachable through pack path connections.
Scenario: A catalog populates INDEX sheets
- WHEN a catalog contains more than fifteen levels
- THEN INDEX creates additional sheets, keeps at most fifteen nodes per sheet, and connects adjacent sheets
Scenario: Solved progress unlocks the next proof
- WHEN the player solves
core/and returns to INDEXintro - THEN progress marks that node solved and unlocks the next proof under the same stable key
Scenario: A locked proof rejects entry
- WHEN the player walks onto a locked proof before its predecessor is solved
- THEN INDEX indicates the lock, emits an error feedback event, and keeps the cursor on the prior proof
Scenario: Each pack keeps an open chapter entry
- WHEN a fresh save contains bundled Core, Basic Proofs, and Word Lab packs on one INDEX sheet
- THEN
core/,break-the-rule garden-basics/, andgarden-push word-lab/are available, while later proofs lock according to their own pack'sshared-control packId/progresslevelId
Scenario: Directional input follows visible serpentine neighbors
- WHEN two adjacent proofs on a serpentine row are visually ordered right-to-left, such as 07 and 08
- THEN Left and Right move between the visible neighboring nodes in that visual direction, and input does not jump according to an invisible array order
Scenario: Sheet edges follow only visible path connectors
- WHEN the cursor reaches a proof at a sheet edge with a connector to another sheet
- THEN the arrow direction matching that visible connector reaches the connected sheet, while the opposite edge direction keeps the cursor on the current proof
Requirement: The board SHALL render ink-and-signal geometry without decorative overlap
The board renderer SHALL draw a flat near-black slab with hard offset lip, corner dots, merged walls, fixed cell geometry, and stable draw order. Word tiles SHALL use 74% cells, emoji things 60%, operators outline pills, properties chamfered tiles, and sentence bars SHALL come from physical spans in state.sentences in the tile gutters. The horizontal sentence bar SHALL span from 0.13 to 0.91 of each cell, and player-facing board text SHALL use Recursive MONO 1. Only real engine stacks may overlap.
Scenario: A sentence bar follows a physical span
- WHEN
state.sentencescontains a horizontal or vertical span - THEN the board draws its bar along that span and does not reconstruct a semantic rule from text
Scenario: No-YOU remains recoverable
- WHEN no object has
YOUafter a settled turn - THEN the board dims, displays
NOTHING IS YOU, and shows undo/restart recovery hints
Scenario: Unknown nouns remain readable
- WHEN an object noun has no emoji mapping
- THEN the renderer displays the noun as text within the fixed cell without changing grid geometry
Requirement: Juice and audio SHALL consume settled events
Visual motion and audio SHALL be derived from settled engine results and SHALL never mutate simulation state. Movement, pushes, blocks, rule formation/removal, transformations, sink, defeat, win, undo, restart, and scene changes SHALL have deterministic timing categories. Reduced motion SHALL snap animations or use a one-frame outline. Audio SHALL preload buffers, unlock on first gesture, keep music off by default, and persist mute state.
Scenario: Settled events drive feedback without changing state
- WHEN the engine returns an accepted or blocked settled event
- THEN juice and audio derive their projections from that event and leave the simulation state unchanged
Requirement: Juice SHALL animate physical sentence spans and event details
Juice SHALL expose sentence frames keyed by the physical cell ids joined with >, with phase values forming, active, or removing, progress, and frozen cells for a removed span. Snapping SHALL settle movement only and SHALL allow rule bars and particles to finish. The frame SHALL expose source tile bounce at 1.14x for 200ms, transform or restore pop from 0.55x to 1x with a 240ms back ease, 4px sink and defeat shake, 140ms undo tint, 260ms restart wipe, a 260ms win beat, and rule sparks containing text, cell, color role, and start time alongside destruction letters.
Scenario: A removed sentence keeps its physical trace
- WHEN a settled event removes a rule sentence
- THEN the juice frame reports the sentence by its cell-id key in
removingphase with frozen pre-move cells until its removal animation completes
Scenario: A new input snaps movement without cutting rule motion
- WHEN the player issues a new intent while a movement tween and a rule formation are active
- THEN movement reaches its endpoint immediately while the rule formation remains available to complete
Scenario: A pushed entity gets distinct feedback
- WHEN an accepted event contains a movement with non-null
pushedBy - THEN juice marks it as pushed for the shorter squash and audio can play the push/clack category
Scenario: A blocked move gives reason-specific feedback
- WHEN a settled event reports a
stoporboundsblock - THEN the stage nudges and shakes with the corresponding amplitude and chooses the matching sound category without incrementing moves
Scenario: Reduced motion removes tween waits
- WHEN reduced motion is enabled
- THEN movement, rule, contact, and scene visuals settle immediately while state and accessibility feedback remain correct
Requirement: Workshop and Gift SHALL connect to the pack service
The stage SHALL connect Workshop actions to the presentation-independent pack service hooks, support drag-and-drop .cjs preview/import, keep URL intake pending until Gift confirmation, and expose in-canvas feedback for success, validation errors, collisions, copy, export, and removal. Workshop SHALL have a prominent player-facing entry from Title and INDEX in addition to any Pause route, and entering it SHALL record the originating scene so Back returns there. Keyboard and pointer activation SHALL expose the same Workshop actions. Workshop and Gift layout SHALL follow the designer reference and remain separate from pack validation logic. Gift SHALL reserve a distinct action region after fitting its preview content, and every action SHALL remain visible, inside the card, and separated from the preview at desktop and mobile widths.
Scenario: Workshop imports a trusted module
- WHEN the player chooses Import or drops a
.cjsfile and confirms the trusted path - THEN the stage previews and installs it through pack-service hooks, then offers the new pack on INDEX
Scenario: Duplicate pack uses explicit choice
- WHEN a pack id already exists during Workshop or Gift installation
- THEN the stage offers Replace, Keep both as a copy, or Cancel and does not silently mutate the registry
Scenario: Pack errors are readable
- WHEN validation fails with more than five details
- THEN the stage shows the first five plain-language errors, an
and N moresummary, and a Copy details action
Scenario: Gift shows a normalized preview and collision
- WHEN a valid share or local module preview returns a pack that already exists
- THEN Gift shows the pack name, author, level count, first-level preview, and
ALREADY HEREwith Replace, Keep both as a copy, or Not now choices before installation
Scenario: Gift actions do not overlap the desktop preview
- WHEN a shared pack Gift card is drawn at a desktop viewport
- THEN the preview board and metadata stay within the content region, every action rectangle stays within the card's reserved action region, and no preview pixels or labels overlap an action hit target
Scenario: Gift actions remain reachable on mobile
- WHEN a shared pack Gift card is drawn at a narrow mobile viewport
- THEN preview content scales or clips within its region, actions stack in order with at least 44 CSS pixels of height, and each action remains inside the safe card margins and viewport
Scenario: Collision actions use the same safe layout
- WHEN Gift enters
ALREADY HEREcollision mode at desktop or mobile width - THEN Replace, Keep Both as a Copy, and Cancel use distinct visible hit rectangles that do not overlap the preview or one another
Scenario: Keyboard and pointer Gift actions agree after responsive layout
- WHEN the player selects an action with keyboard input or activates its pointer rectangle
- THEN both paths produce the same keep, later, replace, copy, or cancel result and preserve the existing confirmation behavior
Scenario: Workshop is reachable from Title and INDEX
- WHEN the player is on Title or INDEX and chooses the visible Workshop entry
- THEN the stage opens Workshop directly without requiring a detour through Pause
Scenario: Workshop keyboard and pointer actions agree
- WHEN the player activates Workshop, Import, Play, Share, Export, Remove, or Back with either keyboard or pointer input
- THEN both input paths produce the same action and feedback result
Scenario: Back returns to the originating scene
- WHEN the player enters Workshop from Title or INDEX and chooses Back
- THEN the stage returns to that originating scene with its current selection and progress intact
Scenario: Local Import Gift can return to Workshop
- WHEN the player chooses Import in Workshop, previews a local
.cjspack, and chooses Not now or presses Escape in Gift - THEN the stage returns to Workshop with the existing pack selection and registry unchanged
Scenario: Shared URL Gift can return to Title
- WHEN the app opens with a valid shared
#pack=URL and the player chooses Not now or presses Escape in Gift - THEN the stage returns to Title without installing the pack or changing the registry
Requirement: The stage SHALL provide accessible and font-stable output
The stage SHALL keep a polite live region for rule, level, no-YOU, win, and pack status; all functions SHALL be reachable from keyboard, pad, and touch; and the player-facing font SHALL be the self-hosted Recursive instance with MONO 1 and CASL 0. Reduced-motion and contrast behavior SHALL preserve readable text independent of animation or color.
Scenario: A rule change is announced
- WHEN a settled event adds or removes a rule
- THEN the live region announces the formed or broken sentence and the current rule state in readable text
Scenario: A font is available offline
- WHEN the app is loaded without network access
- THEN player-facing stage text uses the bundled Recursive font rather than a remote font request
Requirement: Stage copy SHALL use drawn keycaps and proof language
Player-facing control hints SHALL render keycaps with adjacent verbs, such as [↑↓] choose [ENTER] select [ESC] not now, rather than typed middle-dot separators. Gift copy SHALL use A PACK FOR YOU, sentence-case bylines such as by Dami, and proofs; scene metadata and headings SHALL not call the content a garden. The title scene SHALL be playable by walking the cat into DOOR IS WIN; Enter may skip to INDEX.
Scenario: Reduced motion preserves control copy
- WHEN reduced motion is enabled
- THEN the same drawn keycap and verb hints remain readable without relying on transition animation