clone

a browser grid puzzle game where you push words around to rewrite the rules of each level

main
miau-is-true / openspec / changes / archive / 2026-09-24-add-world-index-navigation / specs / world-index-navigation / spec.md
spec.md 7.1K rendered
1## ADDED Requirements23### Requirement: Each registered pack SHALL own a world and INDEX sheet range4The catalog and INDEX layout SHALL create one world record and one or more5pack-owned sheets for every registered pack, including core. Sheets SHALL hold6at most fifteen nodes, and a world with more than fifteen proofs SHALL continue7onto another sheet in the same world. World order SHALL be core first, then8bundled registration order, then imported install order. Layout SHALL NOT infer9world identity from origin or a hard-coded pack ID list.1011#### Scenario: Bundled packs remain separate worlds12- **WHEN** core, crossword, crate-works, and tidewater are registered in that13 order as the approved default bundle14- **THEN** the layout exposes four worlds in that order, with each pack's nodes15 in its own sheet range and no bundled node assigned `worldId: core`1617#### Scenario: Example packs remain available through import18- **WHEN** garden-basics or word-lab is imported19- **THEN** it becomes its own world after the default bundle without changing20 core, crossword, crate-works, or tidewater progress and order2122#### Scenario: A long pack continues within its own world23- **WHEN** an imported pack has sixteen ordered proofs24- **THEN** it receives two sheets with the same world ID, fifteen nodes on the25 first sheet, and one node on the second sheet2627#### Scenario: Registration order survives replacement and forking28- **WHEN** a bundled pack is replaced or an imported pack is forked29- **THEN** replacement keeps the existing world position and the fork appends30 as a new imported world without reordering other worlds3132### Requirement: World identity metadata SHALL use nonblocking W11 validation33An optional `manifest.world` SHALL accept a glyph that is exactly one grapheme34cluster counted with `Intl.Segmenter` at `granularity: 'grapheme'` and no more35than 16 UTF-16 code units. Its tagline SHALL be at most 80 characters, contain36no newline, use sentence case, and contain no `·` (U+00B7). Its accent SHALL37be one of `you`, `push`, `stop`, `win`, `sink`, `defeat`, or `tick`. An invalid38optional identity field SHALL add a nonblocking W11 note and fall back to the39first palette glyph, no tagline, and the default `--fg` path. Invalid gameplay40fields SHALL continue to reject the complete pack atomically.4142#### Scenario: Valid world metadata is exposed43- **WHEN** a pack declares `{ glyph: '🌊', tagline: 'A wet proof.', accent: 'sink' }`44- **THEN** the normalized catalog world exposes those values with the pack's45 stable ID, author, origin, and registration order4647#### Scenario: Invalid world metadata keeps the pack playable48- **WHEN** a pack declares `accent: 'glow'`, a multi-grapheme glyph, or an49 overlong tagline50- **THEN** registration succeeds with a W11 note, uses the fallback world51 identity, and keeps the pack's gameplay and active-level data available5253#### Scenario: World tagline reports the middle-dot correction clearly54- **WHEN** a tagline contains `·`55- **THEN** the pack receives a W11 note saying `Tagline can't use ·; use a56 comma or a dash.` and the normalized world has no tagline5758#### Scenario: Gameplay validation remains atomic59- **WHEN** a pack has valid or invalid `manifest.world` metadata but an invalid60 gameplay field61- **THEN** the pack is rejected by the existing gameplay validator and is not62 added to the catalog6364#### Scenario: A legacy pack receives defaults65- **WHEN** an otherwise valid pack omits `manifest.world`66- **THEN** registration succeeds and its world metadata uses the default glyph,67 no tagline, and the default foreground path accent6869### Requirement: INDEX progress SHALL be scoped per world70The INDEX view model SHALL expose the active world ID/number, world name, world71completion count, total proof count, and current world-strip position. Solved72state SHALL remain keyed by `{ packId, levelId }`. The opener of every world73SHALL be available, later proofs SHALL unlock in order within that world, and a74proof solved in one world SHALL not unlock or count toward another world.7576#### Scenario: Completion counts do not leak between worlds77- **WHEN** one proof is solved in world `alpha` and none is solved in world `beta`78- **THEN** alpha reports `1 / alphaTotal`, beta reports `0 / betaTotal`, and79 beta's second proof remains locked behind beta's opener8081#### Scenario: Entering a world selects its local cursor82- **WHEN** the player leaves world `alpha` after selecting its third proof and83 later returns84- **THEN** the INDEX restores alpha's saved cursor when it still exists, or85 selects alpha's first available node, without changing the active proof in86 another world8788### Requirement: INDEX SHALL navigate between worlds without wraparound89The navigator SHALL map Q/E, gamepad LB/RB, and one two-finger horizontal swipe90per gesture to one-world-left/right actions. Walking forward from a world's91terminal path node SHALL enter the next world's first node; walking backward92from the first node SHALL enter the previous world's terminal node. The first93and last world boundaries SHALL be no-ops with existing blocked-navigation94feedback. Intra-world sheet boundaries SHALL remain distinct from world95boundaries.9697#### Scenario: Q and E switch one adjacent world98- **WHEN** the active world is the second of three and the player presses Q99 once, then E once100- **THEN** the navigator enters world one, then world two, with one transition101 per input and no proof move recorded102103#### Scenario: Outer boundaries do not wrap104- **WHEN** the player presses Q in world one or E in world three105- **THEN** the world and active proof remain unchanged and the navigator returns106 a blocked-navigation indication107108#### Scenario: Path edges cross worlds in travel direction109- **WHEN** the player walks right off the final path node of world one110- **THEN** the cursor enters world two's first node; walking left from world111 two's first node returns to world one's final node, without wrapping past the112 first or last world113114### Requirement: INDEX SHALL provide a stable world presentation seam115The INDEX scene SHALL consume a pure world view model containing world number,116world count, name, glyph, tagline, accent, per-world completion, and strip117position. The first entry into a world and Workshop PLAY SHALL emit a118world-card request with origin `index` or `workshop`, at most once per world per119session. The seam SHALL not change integer board coordinates or simulation120state.121122#### Scenario: First entry requests a world card123- **WHEN** the player enters an unvisited world from INDEX124- **THEN** the scene emits one `world-card` request containing that world's125 normalized identity and `origin: index` before settling on its sheet126127#### Scenario: Workshop PLAY requests the same card seam128- **WHEN** the player chooses PLAY for an unvisited pack in Workshop129- **THEN** the scene emits one `world-card` request with `origin: workshop`,130 preserving the pack's first node and current simulation state131132#### Scenario: Re-entry does not replay the card133- **WHEN** the player returns to a world whose card request was already settled134 during the current session135- **THEN** the world header view model updates without another card request