clone

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

main
miau-is-true / openspec / specs / level-packs / spec.md
spec.md 4.6K rendered
1# level-packs Specification23## Purpose4TBD - created by archiving change share-level-packs. Update Purpose after archive.5## Requirements6### Requirement: Packs and levels SHALL have stable identities7Every pack SHALL have a stable lowercase pack id, display name, version, and ordered levels. Every level SHALL have a stable level id unique within its pack. Registry keys SHALL use the pair `{ packId, levelId }`, and display titles SHALL not be used as identity.89#### Scenario: A renamed level keeps its identity10- **WHEN** a pack changes a level title but retains its pack id and level id11- **THEN** the registry treats it as the same level identity and updates only display metadata1213#### Scenario: Duplicate level ids are rejected within a pack14- **WHEN** a pack contains two levels with the same level id15- **THEN** pack validation fails before either level is registered1617### Requirement: The app SHALL ship curated bundled packs18The app SHALL include a curated set of valid bundled packs, including the original three MIAU teaching puzzles as one ordered teaching pack. Bundled packs SHALL be available without a local file or URL import and SHALL pass the same identity and level validation as imported packs.1920#### Scenario: The teaching pack is available on a clean start21- **WHEN** the app starts without imported content22- **THEN** the registry contains the MIAU teaching pack and its three original levels in stable order2324#### Scenario: A bundled pack has invalid data25- **WHEN** a bundled pack fails validation during startup26- **THEN** the app reports the pack id and validation error and keeps other valid bundled packs available2728### Requirement: The registry SHALL retain multiple loaded packs29The registry SHALL retain every valid bundled, URL, and explicitly trusted local pack loaded during the session. Each pack SHALL retain its origin metadata, and selecting one pack or level SHALL not discard other valid registered content.3031#### Scenario: Two local packs remain selectable32- **WHEN** the player loads two valid packs with different pack ids33- **THEN** both packs appear in the pack registry and levels from either pack can be selected3435#### Scenario: A failed pack does not remove earlier content36- **WHEN** a later import fails validation or execution37- **THEN** previously registered packs remain selectable and the failed pack is absent3839### Requirement: The registry SHALL apply an explicit collision policy40The registry SHALL present an explicit choice when a pack id collides: `Replace`, which keeps the authored id and its progress, or `Keep both as a copy`, which creates a new fork id using the next available `-2`, `-3`, and so on suffix, records `forkOf` as the original id, and starts progress under the new id. A level id collision SHALL be scoped to its pack id; identical level ids in different packs SHALL coexist. Both replacement and forking SHALL validate atomically and preserve the existing pack if validation fails.4142#### Scenario: A pack id collision offers explicit choices43- **WHEN** a newly loaded pack uses an existing pack id44- **THEN** the app offers `Replace` and `Keep both as a copy`, and does not change the registry until one choice is made4546#### Scenario: Keep both creates a fresh fork47- **WHEN** the player chooses `Keep both as a copy` for pack `garden-basics`48- **THEN** the registry installs the imported pack under the next available id such as `garden-basics-2`, records `forkOf: garden-basics`, and starts progress under the fork id4950#### Scenario: Replace preserves authored identity51- **WHEN** the player chooses `Replace` for an existing pack id52- **THEN** the new pack keeps that authored id and its existing progress key, subject to complete validation5354#### Scenario: Same level id in different packs is allowed55- **WHEN** two registered packs each contain a level id `intro`56- **THEN** both levels remain addressable by their `{ packId, levelId }` pair5758### Requirement: Pack selection SHALL identify the active content59The app SHALL expose the active pack id and level id to the engine and UI, and SHALL load a selected valid level through the existing engine validation path. A pack selection failure SHALL leave the previously active level playable.6061#### Scenario: Selecting a level changes active identity62- **WHEN** the player selects level `intro` from pack `garden-basics`63- **THEN** the active selection reports both ids and the engine loads that level's validated data6465#### Scenario: Selecting an unknown level is harmless66- **WHEN** a stale link or UI action requests an unknown pack/level pair67- **THEN** the app reports the missing identity and retains the current playable level68