World model
Create one world record for each catalog pack, including the core pack:
{
id: 'tidewater',
number: 4,
packId: 'tidewater',
name: 'Tidewater',
author: '...',
glyph: '๐',
tagline: 'Everything floats until it does not.',
accent: 'sink',
origin: 'bundled',
registrationIndex: 3,
sheets: ['tidewater:sheet-1'],
levelCount: 9,
completedCount: 2,
}
number is one-based in display and follows the final world order. The world
record is derived from the catalog, not duplicated in level data. A pack may
have multiple sheet records, but every sheet has exactly one worldId/packId.
The core pack is world 1 and owns its own sheet range. No bundled pack is
implicitly folded into the core world.
The layout API should expose both worlds and sheets, plus lookup helpers for
world by pack ID, sheet by ID, and node by global level ID. Each sheet keeps the
existing fifteen-cell serpentine auto layout and links to the previous and next
sheet in the same world. A world boundary is represented separately from an
intra-world sheet boundary so callers can animate or announce it later.
Ordering and registration
The catalog order is the source of truth:
- core;
- bundled packs in registration order;
- local and URL/imported packs in install order.
The currently approved default bundle is core, Crossword, Crate Works, then Tidewater. Clockwork joins only after W12 custom-property presentation review; Garden Basics and Word Lab remain importable examples outside the default INDEX. This is a registration choice, not an INDEX hard-coded ID rule.
Replacing a pack preserves its position. Forking an imported pack appends the fork as a new imported world. Removing an imported pack removes its world and its sheets while preserving the relative order of all remaining worlds.
The layout must not infer world identity from origin, a hard-coded ID set, or
the active level. This prevents a newly bundled or imported pack from silently
joining core.
Manifest world metadata
manifest.world is optional. When present it is an object with these fields:
glyph: exactly one grapheme cluster, counted withIntl.Segmenterusinggranularity: 'grapheme', and no more than 16 UTF-16 code units. This permits visible emoji such asโ๏ธ, ZWJ sequences, and flags while restricting the card to one glyph;tagline: at most 80 characters, one source line with no newline, sentence case, and noยท(U+00B7). The diagnostic for the middle dot is explicit:Tagline can't use ยท; use a comma or a dash.;accent: an optional token fromyou,push,stop,win,sink,defeat, ortick.
World identity is presentation metadata, so an invalid optional field produces
a nonblocking W11 note and does not reject the pack. The world falls back to
the first palette glyph, no tagline, and the default --fg path. Missing
manifest.world uses the same defaults. Invalid gameplay fields continue to
reject the complete pack atomically through the existing pack validator.
Normalized metadata and notes are available through catalog pack records and
pack-service views without changing share identity or level IDs.
Progress and chapter unlocking
Solved state remains keyed by { packId, levelId }. World completion is the
count of solved levels whose packId matches that world, over that world's
level count. Unlocking is evaluated in world order within each world: its
opener is always available and each later proof requires the previous proof in
the same world. Solving or selecting a proof in one world cannot unlock a
proof in another world.
The active INDEX state must carry worldId, worldIndex, sheetId, and
nodeId. World switching selects the first available node in the destination
world, preserving that world's current cursor when one exists. If no proof in a
world is available, its opener remains the selectable boundary target and the
existing locked-node indication applies.
Navigation contract
Q and E switch one world left or right. Gamepad LB/RB map to the same
actions. Touch uses a two-finger horizontal swipe and must resolve one switch
per gesture. Switching at the first/last world is a no-op with the existing
blocked-navigation feedback and does not alter progress or the active proof.
Walking from the terminal node of a world's final sheet in its path-forward direction enters the first node of the next world. Walking backward from the first node enters the final node of the previous world. There is no wraparound. Intra-world sheet crossings continue to use the current path boundary behavior and must not be mistaken for a world change.
Presentation seam
The INDEX scene receives a pure view model containing:
worldNumber,worldCount,worldName,worldGlyph,worldTagline,worldAccent;completedCount,levelCount, and the current world-strip position;enteredWorldBefore,worldCardRequested, andcardOrigin(indexorworkshop).
The first entry into a world and Workshop PLAY request a card once per session and then settle on the world sheet. The card displays the glyph, name, tagline, author, and an ENTER action. The follow-up presentation change owns its exact geometry, copy fitting, and 280ms camera ease; the engine must only emit stable world transition and card-request state.
Compatibility and migration
Existing progress keys remain valid. Catalog callers that only ask for flat
levels continue to receive the same ordered level records. The layout migration
updates callers that assumed built-ins shared a core sheet range. Imported
packs without manifest.world remain valid and receive normalized default
metadata.
Verification and designer handoff
Physical keyboard Q/E is handled by the stage action router; gamepad LB/RB and two-finger gestures enter through the shared input intent path. The local and public world-index browser fixtures exercised Q/E, first/last boundary feedback, Core โ Crossword โ Crate Works โ Tidewater order, Workshop PLAY, mobile touch, and reduced motion without page errors. Focused map/input tests cover world and sheet edges, cursor restoration after removal, independent opener locks and completion counts, and stable pack/local progress keys.
The designer accepted the header, strip, world cards, and four-world bundle on
2026-09-24 in docs/. The separate presentation change has
been archived. The complete 30-proof sequential world gate, 373-test Node
suite, production build, and local Gift/Workshop/share browser flow passed
before public revision 13 at https:/. The live
game-presentation and level-packs specs contain no obsolete built-in
sheet-1 grouping rule; this change's merged spec now owns per-pack worlds.