- commit
- 25fc9feb43365b25f7c85d41083abf9098c5e957
- parent
- f5b5ce820a89ca4d10c8d2ed86504c4673ddaf65
- author
- Dami <dami.etoile@gmail.com>
No content changes.
No content changes.
No content changes.
@@ -15,6 +15,31 @@ The renderer SHALL draw the simulation on the ink-and-signal full-screen canvas
1515 - **WHEN** the engine exposes `state.sentences`1616 - **THEN** the renderer draws maximal horizontal or vertical bars from those physical spans, including exact duplicates1717 18+### Requirement: The app SHALL show active rules and turn feedback19+The presentation SHALL render the current proof HUD, ledger, hints, keycap controls, active/inactive words, and no-YOU state on the stage. It SHALL consume settled engine events for blocked movement, undo, reset, transformations, defeat, victory, and rule changes without changing simulation state.20+21+#### Scenario: Active rules update after a word push22+- **WHEN** a word tile is pushed into or out of a sentence23+- **THEN** the stage updates sentence bars and ledger from the settled event and current state24+25+#### Scenario: Victory is readable26+- **WHEN** the engine enters the won state27+- **THEN** the stage shows the winning sentence, `PROVEN IN N STEPS`, and explicit continue/replay actions28+29+### Requirement: Text effects SHALL support variable-length labels without driving grid geometry30+The app MAY use Pretext or an equivalent text-effect layer for proof names, hints, ledger labels, pack feedback, and scene copy. Text effects SHALL be laid out in their owning HUD or scene region and SHALL not determine integer simulation coordinates or collision geometry.31+32+#### Scenario: A long proof title does not move cells33+- **WHEN** a proof title or hint is longer than its HUD region34+- **THEN** it fits, wraps, or truncates according to the stage copy rules while every grid cell retains its coordinate and size35+36+### Requirement: The presentation SHALL provide accessible control feedback37+The app SHALL expose a polite live region and readable text equivalents for movement, undo, restart, menu, scene, rule, level, pack, and outcome status. Keyboard, gamepad, and touch users SHALL be able to reach every player-facing action, and reduced motion SHALL preserve status without animation.38+39+#### Scenario: Controls are understandable without animation40+- **WHEN** reduced motion is enabled or animation is unavailable41+- **THEN** the board, INDEX, active rules, current proof, pack status, and controls remain understandable from static text and tile labels42+1843 ## ADDED Requirements1944 2045 ### Requirement: Supported character glyphs SHALL use scoped facing state@@ -47,28 +72,3 @@ The presentation SHALL maintain facing per entity ID as visual state separate fr
4772 #### Scenario: Reduced motion skips facing animation4873 - **WHEN** reduced motion is enabled and a qualifying horizontal self-move changes facing4974 - **THEN** the glyph changes facing immediately without the 70ms squash animation50-51-### Requirement: The app SHALL show active rules and turn feedback52-The presentation SHALL render the current proof HUD, ledger, hints, keycap controls, active/inactive words, and no-YOU state on the stage. It SHALL consume settled engine events for blocked movement, undo, reset, transformations, defeat, victory, and rule changes without changing simulation state.53-54-#### Scenario: Active rules update after a word push55-- **WHEN** a word tile is pushed into or out of a sentence56-- **THEN** the stage updates sentence bars and ledger from the settled event and current state57-58-#### Scenario: Victory is readable59-- **WHEN** the engine enters the won state60-- **THEN** the stage shows the winning sentence, `PROVEN IN N STEPS`, and explicit continue/replay actions61-62-### Requirement: Text effects SHALL support variable-length labels without driving grid geometry63-The app MAY use Pretext or an equivalent text-effect layer for proof names, hints, ledger labels, pack feedback, and scene copy. Text effects SHALL be laid out in their owning HUD or scene region and SHALL not determine integer simulation coordinates or collision geometry.64-65-#### Scenario: A long proof title does not move cells66-- **WHEN** a proof title or hint is longer than its HUD region67-- **THEN** it fits, wraps, or truncates according to the stage copy rules while every grid cell retains its coordinate and size68-69-### Requirement: The presentation SHALL provide accessible control feedback70-The app SHALL expose a polite live region and readable text equivalents for movement, undo, restart, menu, scene, rule, level, pack, and outcome status. Keyboard, gamepad, and touch users SHALL be able to reach every player-facing action, and reduced motion SHALL preserve status without animation.71-72-#### Scenario: Controls are understandable without animation73-- **WHEN** reduced motion is enabled or animation is unavailable74-- **THEN** the board, INDEX, active rules, current proof, pack status, and controls remain understandable from static text and tile labelsNo content changes.
@@ -1,6 +1,6 @@
11 ## MODIFIED Requirements22 3-### Requirement: The app SHALL provide a catalog-backed INDEX of proofs3+### Requirement: The app SHALL provide three built-in MIAU teaching puzzles44 The app SHALL expose valid catalog levels through the INDEX scene rather than a flat level list. The original three MIAU teaching proofs SHALL remain in the built-in catalog, while additional curated or installed packs add proof nodes and sheets. Each proof SHALL retain stable `packId` and `levelId` identity and ordered progress metadata.55 66 #### Scenario: The built-in catalog starts at MIAU teaching proofs@@ -19,7 +19,7 @@
1919 - [x] 3.1 Implement and unit-test board geometry, hard-edged slab, no-overlap tile roles, stacks, walls, unknown noun fallback, and no-YOU rendering in `src/render/board.js`.2020 - [x] 3.2 Integrate board rendering into the stage with integer cells, HUD, ledger, hint, keycap controls, and responsive desktop/mobile zones.2121 - [x] 3.3 Consume `state.sentences` for maximal horizontal/vertical sentence bars, including exact duplicates and stacked-word paths; use the 0.13–0.91 gutter bar and Recursive MONO 1.22-- [ ] 3.4 Verify copy deck labels, accessibility live-region announcements, contrast, and static reduced-motion output against the designer reference.22+- [x] 3.4 Verify copy deck labels, accessibility live-region announcements, contrast, and static reduced-motion output against the designer reference.2323 - [x] 3.5 Replace typed middle-dot hints with drawn keycap and verb pairs, use proof terminology and Gift copy, and verify the playable title micro-proof.2424 - [x] 3.6 Add the per-ID presentation facing projection for built-in directional MIAU/KEKE glyphs and explicitly opted-in mod palette glyphs: only accepted horizontal self-moves change facing; pushes, vertical movement, transforms, blocked/idle turns preserve it; undo restores snapshots and reset uses native defaults.2525 - [x] 3.7 Add engine-state/browser fixtures for horizontal self-moves, pushed and vertical movement, transforms, blocked input, non-directional fallbacks, mod palette opt-in, per-ID undo restoration, native level reset, 70ms glyph-only squash, and reduced-motion instant facing without changing simulation coordinates.@@ -5,37 +5,73 @@ Define how the browser presents a fixed-coordinate emoji puzzle board, active
55 word rules, controls, and readable feedback without changing simulation state.66 ## Requirements77 ### Requirement: The app SHALL render the simulation as a readable emoji grid8-The renderer SHALL display each grid cell at a stable coordinate with stacked entities in a consistent visual order. Object nouns SHALL use emoji mappings, word entities SHALL remain legible as text tiles, and the board SHALL expose enough contrast and spacing to distinguish adjacent cells and stacked words.8+The renderer SHALL draw the simulation on the ink-and-signal full-screen canvas with fixed integer cell geometry, hard-edged board and tiles, stable stack order, and no decorative overlap. Object nouns SHALL use emoji mappings or readable text fallbacks, word entities SHALL remain legible, and physical sentence spans SHALL drive sentence bars.99 1010 #### Scenario: Stacked entities remain visible1111 - **WHEN** multiple objects or words occupy one cell12-- **THEN** the renderer preserves their identity and indicates the stack without changing simulation occupancy12+- **THEN** the renderer preserves their identity with the documented stack offset and does not treat decorative elements as simulation occupancy1313 1414 #### Scenario: A modded noun has no emoji fallback1515 - **WHEN** a noun has no declared emoji mapping16-- **THEN** the renderer uses a readable text fallback containing the noun instead of hiding the entity16+- **THEN** the renderer uses a readable text fallback within its fixed cell17+18+#### Scenario: A sentence bar follows state spans19+- **WHEN** the engine exposes `state.sentences`20+- **THEN** the renderer draws maximal horizontal or vertical bars from those physical spans, including exact duplicates1721 1822 ### Requirement: The app SHALL show active rules and turn feedback19-The presentation SHALL display the currently active rules beside or below the board and SHALL update them after each settled turn. It SHALL provide visible feedback for blocked movement, undo, reset, transformations, defeat, and victory without changing engine state.23+The presentation SHALL render the current proof HUD, ledger, hints, keycap controls, active/inactive words, and no-YOU state on the stage. It SHALL consume settled engine events for blocked movement, undo, reset, transformations, defeat, victory, and rule changes without changing simulation state.2024 2125 #### Scenario: Active rules update after a word push2226 - **WHEN** a word tile is pushed into or out of a sentence23-- **THEN** the displayed rule list reflects the engine's new active rule table after the turn27+- **THEN** the stage updates sentence bars and ledger from the settled event and current state2428 2529 #### Scenario: Victory is readable2630 - **WHEN** the engine enters the won state27-- **THEN** the app shows a victory message and a clear continue or reset action31+- **THEN** the stage shows the winning sentence, `PROVEN IN N STEPS`, and explicit continue/replay actions2832 2933 ### Requirement: Text effects SHALL support variable-length labels without driving grid geometry30-The app MAY use Pretext or an equivalent text-effect layer for animated labels, rule emphasis, hints, and feedback. Text effects SHALL be laid out within the UI surface and SHALL not determine the integer coordinates or collision geometry of the simulation grid.34+The app MAY use Pretext or an equivalent text-effect layer for proof names, hints, ledger labels, pack feedback, and scene copy. Text effects SHALL be laid out in their owning HUD or scene region and SHALL not determine integer simulation coordinates or collision geometry.3135 32-#### Scenario: A long hint does not move cells33-- **WHEN** a level hint is longer than the available sidebar width34-- **THEN** the hint wraps or scrolls while every grid cell retains its original coordinate and size36+#### Scenario: A long proof title does not move cells37+- **WHEN** a proof title or hint is longer than its HUD region38+- **THEN** it fits, wraps, or truncates according to the stage copy rules while every grid cell retains its coordinate and size3539 3640 ### Requirement: The presentation SHALL provide accessible control feedback37-The app SHALL expose text labels or accessible names for movement, undo, reset, and continue controls and SHALL communicate rule and level status in a readable text representation in addition to animation and emoji.41+The app SHALL expose a polite live region and readable text equivalents for movement, undo, restart, menu, scene, rule, level, pack, and outcome status. Keyboard, gamepad, and touch users SHALL be able to reach every player-facing action, and reduced motion SHALL preserve status without animation.3842 3943 #### Scenario: Controls are understandable without animation4044 - **WHEN** reduced motion is enabled or animation is unavailable41-- **THEN** the board, active rules, current level, and control actions remain understandable from static text and emoji45+- **THEN** the board, INDEX, active rules, current proof, pack status, and controls remain understandable from static text and tile labels46+47+### Requirement: Supported character glyphs SHALL use scoped facing state48+The presentation SHALL maintain facing per entity ID as visual state separate from simulation coordinates. The built-in MIAU and KEKE glyphs SHALL be directional; a mod palette object SHALL opt into directional treatment explicitly. Only an accepted horizontal self-move SHALL update an entity's facing. Pushed entities, vertical self-moves, transformations, idle turns, and blocked intents SHALL preserve the entity's prior facing. Glyphs without directional treatment SHALL use their unchanged fallback. Undo SHALL restore the per-ID facing snapshot, while loading or resetting a level SHALL use its native declared or neutral facing. A facing change MAY use a 70ms glyph-only squash; reduced motion SHALL apply the facing change instantly.49+50+#### Scenario: A direct horizontal MIAU move updates facing51+- **WHEN** a MIAU entity makes an accepted horizontal self-move52+- **THEN** its supported glyph faces the travel direction after the settled turn, with a 70ms glyph-only squash when motion is enabled and unchanged engine coordinates53+54+#### Scenario: Pushes and vertical movement preserve facing55+- **WHEN** a KEKE entity is pushed or makes an accepted vertical self-move56+- **THEN** its prior facing remains unchanged, regardless of the movement direction or whether it was directly controlled57+58+#### Scenario: A transformation preserves per-ID facing59+- **WHEN** an entity transforms to another noun during a settled turn60+- **THEN** the entity ID retains its prior facing, unless the resulting noun has no directional glyph and uses its normal fallback61+62+#### Scenario: A mod opts a glyph into directional treatment63+- **WHEN** a mod palette explicitly marks an object glyph as directional64+- **THEN** the stage applies the same horizontal self-move facing rules to that glyph; unmarked mod glyphs remain non-directional65+66+#### Scenario: Facing stays stable without a qualifying move67+- **WHEN** an entity is idle or receives a blocked intent68+- **THEN** its prior facing remains unchanged and no direction is inferred from the failed intent69+70+#### Scenario: Undo and reset restore native facing71+- **WHEN** the player undoes a turn or loads/resets a level72+- **THEN** undo restores each entity ID's prior facing snapshot and the new level uses its declared or neutral native facing instead of inheriting the previous level's direction73+74+#### Scenario: Reduced motion skips facing animation75+- **WHEN** reduced motion is enabled and a qualifying horizontal self-move changes facing76+- **THEN** the glyph changes facing immediately without the 70ms squash animation77+@@ -0,0 +1,175 @@
1+# game-stage Specification2+3+## Purpose4+TBD - created by archiving change build-ink-signal-stage. Update Purpose after archive.5+## Requirements6+### Requirement: The app SHALL run normal play in a full-screen stage7+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`.8+9+#### Scenario: The player enters a proof10+- **WHEN** the app opens a proof scene at a desktop or mobile viewport11+- **THEN** the canvas fills the viewport at the capped device pixel ratio and no default sidebar controls are shown12+13+#### Scenario: Developer controls are opt-in14+- **WHEN** the app URL does not include `?dev`15+- **THEN** interim DOM controls are hidden while normal keyboard, pad, touch, and scene controls remain available16+17+### Requirement: The stage SHALL provide the v0.3 scene flow18+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.19+20+#### Scenario: A solved proof returns through the win card21+- **WHEN** a proof reaches the engine's won state22+- **THEN** the stage shows the winning sentence and `PROVEN IN N STEPS`, then waits for Enter before returning to INDEX23+24+#### Scenario: Pause opens from a proof25+- **WHEN** the player presses Escape, P, or the mapped menu action during a proof26+- **THEN** the stage opens Pause without changing the engine state and offers Resume, Restart, INDEX, Workshop, sound, and music actions27+28+#### Scenario: A shared pack opens as a Gift29+- **WHEN** the app opens with a valid `#pack=` payload30+- **THEN** the stage presents a Gift preview with `Keep and play` and `Not now`, and does not install the pack before confirmation31+32+### Requirement: Input SHALL be immediate, repeatable, and never queued33+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 intent SHALL snap an in-flight visual tween before starting the next action.34+35+#### Scenario: Held direction uses owned repeat36+- **WHEN** the player holds Right and no newer direction is held37+- **THEN** one move emits immediately, the first repeat waits 175ms, and subsequent repeats occur 110ms apart38+39+#### Scenario: A newer direction takes over40+- **WHEN** the player holds Right, presses Down, and releases Down41+- **THEN** Down is the active repeating direction while held and Right resumes after Down releases42+43+#### Scenario: Touch and gamepad use the same intent path44+- **WHEN** the player swipes at least 26px or moves the gamepad stick beyond the 0.55 deadzone45+- **THEN** the stage emits the same direction intent path used by keyboard input46+47+### Requirement: INDEX SHALL be data-driven and preserve progress48+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/levelId`; 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.49+50+#### Scenario: A catalog populates INDEX sheets51+- **WHEN** a catalog contains more than fifteen levels52+- **THEN** INDEX creates additional sheets, keeps at most fifteen nodes per sheet, and connects adjacent sheets53+54+#### Scenario: Solved progress unlocks the next proof55+- **WHEN** the player solves `core/intro` and returns to INDEX56+- **THEN** progress marks that node solved and unlocks the next proof under the same stable key57+58+#### Scenario: A locked proof rejects entry59+- **WHEN** the player walks onto a locked proof before its predecessor is solved60+- **THEN** INDEX indicates the lock, emits an error feedback event, and keeps the cursor on the prior proof61+62+#### Scenario: Each pack keeps an open chapter entry63+- **WHEN** a fresh save contains bundled Core, Basic Proofs, and Word Lab packs on one INDEX sheet64+- **THEN** `core/break-the-rule`, `garden-basics/garden-push`, and `word-lab/shared-control` are available, while later proofs lock according to their own pack's `packId/levelId` progress65+66+#### Scenario: Directional input follows visible serpentine neighbors67+- **WHEN** two adjacent proofs on a serpentine row are visually ordered right-to-left, such as 07 and 0868+- **THEN** Left and Right move between the visible neighboring nodes in that visual direction, and input does not jump according to an invisible array order69+70+#### Scenario: Sheet edges follow only visible path connectors71+- **WHEN** the cursor reaches a proof at a sheet edge with a connector to another sheet72+- **THEN** the arrow direction matching that visible connector reaches the connected sheet, while the opposite edge direction keeps the cursor on the current proof73+74+### Requirement: The board SHALL render ink-and-signal geometry without decorative overlap75+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.76+77+#### Scenario: A sentence bar follows a physical span78+- **WHEN** `state.sentences` contains a horizontal or vertical span79+- **THEN** the board draws its bar along that span and does not reconstruct a semantic rule from text80+81+#### Scenario: No-YOU remains recoverable82+- **WHEN** no object has `YOU` after a settled turn83+- **THEN** the board dims, displays `NOTHING IS YOU`, and shows undo/restart recovery hints84+85+#### Scenario: Unknown nouns remain readable86+- **WHEN** an object noun has no emoji mapping87+- **THEN** the renderer displays the noun as text within the fixed cell without changing grid geometry88+89+### Requirement: Juice and audio SHALL consume settled events90+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.91+92+#### Scenario: Settled events drive feedback without changing state93+- **WHEN** the engine returns an accepted or blocked settled event94+- **THEN** juice and audio derive their projections from that event and leave the simulation state unchanged95+96+### Requirement: Juice SHALL animate physical sentence spans and event details97+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.98+99+#### Scenario: A removed sentence keeps its physical trace100+- **WHEN** a settled event removes a rule sentence101+- **THEN** the juice frame reports the sentence by its cell-id key in `removing` phase with frozen pre-move cells until its removal animation completes102+103+#### Scenario: A new input snaps movement without cutting rule motion104+- **WHEN** the player issues a new intent while a movement tween and a rule formation are active105+- **THEN** movement reaches its endpoint immediately while the rule formation remains available to complete106+107+#### Scenario: A pushed entity gets distinct feedback108+- **WHEN** an accepted event contains a movement with non-null `pushedBy`109+- **THEN** juice marks it as pushed for the shorter squash and audio can play the push/clack category110+111+#### Scenario: A blocked move gives reason-specific feedback112+- **WHEN** a settled event reports a `stop` or `bounds` block113+- **THEN** the stage nudges and shakes with the corresponding amplitude and chooses the matching sound category without incrementing moves114+115+#### Scenario: Reduced motion removes tween waits116+- **WHEN** reduced motion is enabled117+- **THEN** movement, rule, contact, and scene visuals settle immediately while state and accessibility feedback remain correct118+119+### Requirement: Workshop and Gift SHALL connect to the pack service120+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.121+122+#### Scenario: Workshop imports a trusted module123+- **WHEN** the player chooses Import or drops a `.cjs` file and confirms the trusted path124+- **THEN** the stage previews and installs it through pack-service hooks, then offers the new pack on INDEX125+126+#### Scenario: Duplicate pack uses explicit choice127+- **WHEN** a pack id already exists during Workshop or Gift installation128+- **THEN** the stage offers Replace, Keep both as a copy, or Cancel and does not silently mutate the registry129+130+#### Scenario: Pack errors are readable131+- **WHEN** validation fails with more than five details132+- **THEN** the stage shows the first five plain-language errors, an `and N more` summary, and a Copy details action133+134+#### Scenario: Gift shows a normalized preview and collision135+- **WHEN** a valid share or local module preview returns a pack that already exists136+- **THEN** Gift shows the pack name, author, level count, first-level preview, and `ALREADY HERE` with Replace, Keep both as a copy, or Not now choices before installation137+138+#### Scenario: Workshop is reachable from Title and INDEX139+- **WHEN** the player is on Title or INDEX and chooses the visible Workshop entry140+- **THEN** the stage opens Workshop directly without requiring a detour through Pause141+142+#### Scenario: Workshop keyboard and pointer actions agree143+- **WHEN** the player activates Workshop, Import, Play, Share, Export, Remove, or Back with either keyboard or pointer input144+- **THEN** both input paths produce the same action and feedback result145+146+#### Scenario: Back returns to the originating scene147+- **WHEN** the player enters Workshop from Title or INDEX and chooses Back148+- **THEN** the stage returns to that originating scene with its current selection and progress intact149+150+#### Scenario: Local Import Gift can return to Workshop151+- **WHEN** the player chooses Import in Workshop, previews a local `.cjs` pack, and chooses Not now or presses Escape in Gift152+- **THEN** the stage returns to Workshop with the existing pack selection and registry unchanged153+154+#### Scenario: Shared URL Gift can return to Title155+- **WHEN** the app opens with a valid shared `#pack=` URL and the player chooses Not now or presses Escape in Gift156+- **THEN** the stage returns to Title without installing the pack or changing the registry157+158+### Requirement: The stage SHALL provide accessible and font-stable output159+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.160+161+#### Scenario: A rule change is announced162+- **WHEN** a settled event adds or removes a rule163+- **THEN** the live region announces the formed or broken sentence and the current rule state in readable text164+165+#### Scenario: A font is available offline166+- **WHEN** the app is loaded without network access167+- **THEN** player-facing stage text uses the bundled Recursive font rather than a remote font request168+169+### Requirement: Stage copy SHALL use drawn keycaps and proof language170+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.171+172+#### Scenario: Reduced motion preserves control copy173+- **WHEN** reduced motion is enabled174+- **THEN** the same drawn keycap and verb hints remain readable without relying on transition animation175+@@ -5,37 +5,45 @@ Define the initial ordered MIAU teaching campaign and the controls needed to
55 play, undo, reset, and advance through its levels.66 ## Requirements77 ### Requirement: The app SHALL provide three built-in MIAU teaching puzzles8-The app SHALL ship exactly three deterministic ordered built-in levels using `MIAU` as the initial controlled noun. The puzzles SHALL cover movement and pushing, `YOU`, `PUSH` and `STOP`, word manipulation, `WIN`, and `AND` subject expansion; each level SHALL include dimensions, entities, and title or hint metadata.8+The app SHALL expose valid catalog levels through the INDEX scene rather than a flat level list. The original three MIAU teaching proofs SHALL remain in the built-in catalog, while additional curated or installed packs add proof nodes and sheets. Each proof SHALL retain stable `packId` and `levelId` identity and ordered progress metadata.99 10-#### Scenario: The first MIAU level is playable without a mod11-- **WHEN** the app is started with no mod selected12-- **THEN** it loads the first MIAU level and displays its board, word tiles, and current rules10+#### Scenario: The built-in catalog starts at MIAU teaching proofs11+- **WHEN** the app opens without imported content12+- **THEN** INDEX selects the first MIAU proof and lays out the rest of the built-in catalog from data1313 14-#### Scenario: Completing a teaching puzzle advances the campaign15-- **WHEN** the active level reaches its win state and the player chooses continue16-- **THEN** the app loads the next built-in level while preserving the campaign order14+#### Scenario: A pack adds an INDEX sheet15+- **WHEN** a valid pack is installed16+- **THEN** its stable proof ids appear on an INDEX sheet without replacing the built-in teaching proofs17+18+#### Scenario: Completing a proof advances only by explicit input19+- **WHEN** the active proof reaches its win state20+- **THEN** the win card waits for Enter before returning to INDEX and marking the proof solved1721 1822 ### Requirement: The app SHALL expose reset, undo, and cardinal controls19-The app SHALL provide reset and undo actions and SHALL accept up, down, left, and right intents from keyboard input and touch controls. Controls SHALL call the same engine intent API and SHALL show the current move count and whether undo is available.23+The app SHALL provide reset and undo actions and SHALL accept up, down, left, and right intents from keyboard, Standard gamepad, and touch input. Controls SHALL use the shared input adapter and SHALL report blocked, undo, reset, and win outcomes through stage feedback without queuing actions behind animation.2024 2125 #### Scenario: Keyboard and touch produce the same move22-- **WHEN** the player uses a keyboard arrow or the corresponding touch direction control26+- **WHEN** the player uses a keyboard arrow or a qualifying touch swipe2327 - **THEN** the engine receives the same cardinal intent and the resulting state is identical2428 29+#### Scenario: Gamepad confirms a proof30+- **WHEN** the player presses Standard gamepad A on an available INDEX node31+- **THEN** the stage opens that proof through the same selection path as Enter32+2533 #### Scenario: Reset is available after a deadlock2634 - **WHEN** the player has made moves that cannot reach the goal27-- **THEN** reset restores the initial level and move count without reloading the page35+- **THEN** restart restores the initial proof and move count without reloading the page2836 2937 #### Scenario: Undo is disabled at the initial state30-- **WHEN** the current level has no successful moves31-- **THEN** the undo control is visibly unavailable and invoking it has no effect38+- **WHEN** the current proof has no successful moves39+- **THEN** undo emits no engine action and its stage hint is unavailable3240 3341 ### Requirement: Level definitions SHALL be validated before play34-The app SHALL reject a built-in level whose dimensions, coordinates, entity ids, word text, or object nouns are invalid, and SHALL report the level id and validation problem in developer-visible diagnostics.42+The app SHALL reject a catalog level whose dimensions, coordinates, entity ids, word text, or object nouns are invalid, and SHALL report the proof identity and validation problem in developer-visible diagnostics.3543 3644 #### Scenario: An out-of-bounds entity is rejected3745 - **WHEN** a level definition places an entity outside its declared width or height38-- **THEN** the level does not start and validation reports the offending entity46+- **THEN** the proof does not start and validation reports its pack/level identity and offending entity3947 4048 ### Requirement: The app SHALL ship curated additional puzzle packs4149 The app SHALL ship additional curated packs alongside the existing three-level MIAU teaching campaign. Each added pack SHALL contain valid levels with stable pack and level ids, dimensions, entities, and title or hint metadata; adding these packs SHALL not change the teaching campaign's identities or order.