clone

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

main
miau-is-true / openspec / specs / world-index-navigation / spec.md
spec.md 7.2K rendered
1# world-index-navigation Specification23## Purpose4TBD - created by archiving change add-world-index-navigation. Update Purpose after archive.5## Requirements6### Requirement: Each registered pack SHALL own a world and INDEX sheet range7The catalog and INDEX layout SHALL create one world record and one or more8pack-owned sheets for every registered pack, including core. Sheets SHALL hold9at most fifteen nodes, and a world with more than fifteen proofs SHALL continue10onto another sheet in the same world. World order SHALL be core first, then11bundled registration order, then imported install order. Layout SHALL NOT infer12world identity from origin or a hard-coded pack ID list.1314#### Scenario: Bundled packs remain separate worlds15- **WHEN** core, crossword, crate-works, and tidewater are registered in that16 order as the approved default bundle17- **THEN** the layout exposes four worlds in that order, with each pack's nodes18 in its own sheet range and no bundled node assigned `worldId: core`1920#### Scenario: Example packs remain available through import21- **WHEN** garden-basics or word-lab is imported22- **THEN** it becomes its own world after the default bundle without changing23 core, crossword, crate-works, or tidewater progress and order2425#### Scenario: A long pack continues within its own world26- **WHEN** an imported pack has sixteen ordered proofs27- **THEN** it receives two sheets with the same world ID, fifteen nodes on the28 first sheet, and one node on the second sheet2930#### Scenario: Registration order survives replacement and forking31- **WHEN** a bundled pack is replaced or an imported pack is forked32- **THEN** replacement keeps the existing world position and the fork appends33 as a new imported world without reordering other worlds3435### Requirement: World identity metadata SHALL use nonblocking W11 validation36An optional `manifest.world` SHALL accept a glyph that is exactly one grapheme37cluster counted with `Intl.Segmenter` at `granularity: 'grapheme'` and no more38than 16 UTF-16 code units. Its tagline SHALL be at most 80 characters, contain39no newline, use sentence case, and contain no `·` (U+00B7). Its accent SHALL40be one of `you`, `push`, `stop`, `win`, `sink`, `defeat`, or `tick`. An invalid41optional identity field SHALL add a nonblocking W11 note and fall back to the42first palette glyph, no tagline, and the default `--fg` path. Invalid gameplay43fields SHALL continue to reject the complete pack atomically.4445#### Scenario: Valid world metadata is exposed46- **WHEN** a pack declares `{ glyph: '🌊', tagline: 'A wet proof.', accent: 'sink' }`47- **THEN** the normalized catalog world exposes those values with the pack's48 stable ID, author, origin, and registration order4950#### Scenario: Invalid world metadata keeps the pack playable51- **WHEN** a pack declares `accent: 'glow'`, a multi-grapheme glyph, or an52 overlong tagline53- **THEN** registration succeeds with a W11 note, uses the fallback world54 identity, and keeps the pack's gameplay and active-level data available5556#### Scenario: World tagline reports the middle-dot correction clearly57- **WHEN** a tagline contains `·`58- **THEN** the pack receives a W11 note saying `Tagline can't use ·; use a59 comma or a dash.` and the normalized world has no tagline6061#### Scenario: Gameplay validation remains atomic62- **WHEN** a pack has valid or invalid `manifest.world` metadata but an invalid63 gameplay field64- **THEN** the pack is rejected by the existing gameplay validator and is not65 added to the catalog6667#### Scenario: A legacy pack receives defaults68- **WHEN** an otherwise valid pack omits `manifest.world`69- **THEN** registration succeeds and its world metadata uses the default glyph,70 no tagline, and the default foreground path accent7172### Requirement: INDEX progress SHALL be scoped per world73The INDEX view model SHALL expose the active world ID/number, world name, world74completion count, total proof count, and current world-strip position. Solved75state SHALL remain keyed by `{ packId, levelId }`. The opener of every world76SHALL be available, later proofs SHALL unlock in order within that world, and a77proof solved in one world SHALL not unlock or count toward another world.7879#### Scenario: Completion counts do not leak between worlds80- **WHEN** one proof is solved in world `alpha` and none is solved in world `beta`81- **THEN** alpha reports `1 / alphaTotal`, beta reports `0 / betaTotal`, and82 beta's second proof remains locked behind beta's opener8384#### Scenario: Entering a world selects its local cursor85- **WHEN** the player leaves world `alpha` after selecting its third proof and86 later returns87- **THEN** the INDEX restores alpha's saved cursor when it still exists, or88 selects alpha's first available node, without changing the active proof in89 another world9091### Requirement: INDEX SHALL navigate between worlds without wraparound92The navigator SHALL map Q/E, gamepad LB/RB, and one two-finger horizontal swipe93per gesture to one-world-left/right actions. Walking forward from a world's94terminal path node SHALL enter the next world's first node; walking backward95from the first node SHALL enter the previous world's terminal node. The first96and last world boundaries SHALL be no-ops with existing blocked-navigation97feedback. Intra-world sheet boundaries SHALL remain distinct from world98boundaries.99100#### Scenario: Q and E switch one adjacent world101- **WHEN** the active world is the second of three and the player presses Q102 once, then E once103- **THEN** the navigator enters world one, then world two, with one transition104 per input and no proof move recorded105106#### Scenario: Outer boundaries do not wrap107- **WHEN** the player presses Q in world one or E in world three108- **THEN** the world and active proof remain unchanged and the navigator returns109 a blocked-navigation indication110111#### Scenario: Path edges cross worlds in travel direction112- **WHEN** the player walks right off the final path node of world one113- **THEN** the cursor enters world two's first node; walking left from world114 two's first node returns to world one's final node, without wrapping past the115 first or last world116117### Requirement: INDEX SHALL provide a stable world presentation seam118The INDEX scene SHALL consume a pure world view model containing world number,119world count, name, glyph, tagline, accent, per-world completion, and strip120position. The first entry into a world and Workshop PLAY SHALL emit a121world-card request with origin `index` or `workshop`, at most once per world per122session. The seam SHALL not change integer board coordinates or simulation123state.124125#### Scenario: First entry requests a world card126- **WHEN** the player enters an unvisited world from INDEX127- **THEN** the scene emits one `world-card` request containing that world's128 normalized identity and `origin: index` before settling on its sheet129130#### Scenario: Workshop PLAY requests the same card seam131- **WHEN** the player chooses PLAY for an unvisited pack in Workshop132- **THEN** the scene emits one `world-card` request with `origin: workshop`,133 preserving the pack's first node and current simulation state134135#### Scenario: Re-entry does not replay the card136- **WHEN** the player returns to a world whose card request was already settled137 during the current session138- **THEN** the world header view model updates without another card request