Worked examples — the catalogue contract
Five named examples span the design space the library is meant to cover.
Each is a grammar module under examples/, each exports the same
interface, and each is reproducible from the command line at a pinned
seed.
The axes
| puzzle-free | puzzle-inclusive | |
|---|---|---|
| DAG hypertext | dag-plain |
dag-locked |
| bidirectional hypertext | maze-plain |
maze-locked |
Plus mystery-daily: the murder-mystery track, bidirectional and
puzzle-inclusive, whose locks are social rather than physical.
The catalogue
| id | topology | puzzles | what it demonstrates |
|---|---|---|---|
dag-plain |
dag | no | Pure branching narrative. Fork/join structure, no state, no backtracking. The "hello world" of the export pipeline. |
dag-locked |
dag | yes | Key-lock inside an acyclic graph: the key sits on one arm of a fork, both arms rejoin, and the join's forward edge is locked. Choosing the keyless arm is a real (and recoverable-by-replay) failure. |
maze-plain |
bidirectional | no | Two-way midpoint rooms, dead ends, parallel paths, cycle-closing returns. Every passage revisitable, so text.repeat earns its keep. |
maze-locked |
bidirectional | yes | The full dungeon: key/door side-branches, nested locks, monster and puzzle minigames, health potions, gated returns. |
mystery-daily |
bidirectional | yes | Suspects, secrets, and social locks. Nested key-lock structure wearing the clothes of a logic puzzle. Seed derives from the date. |
Module interface
// examples/<id>.js
module.exports = {
id: 'maze-locked',
title: 'The Cindermoor Vault',
topology: 'bidirectional', // matches meta.topology
puzzles: true,
defaultSeeds: [42, 1729, 8675309],
budget: { rooms: 12, keys: 3, nestingDepth: 2, npcs: 0, minigames: 2 },
// Build the grammar JSON. `opts` carries { theme, budget, debug }.
grammar (opts) { return { name: ..., start: 'START', stages: [ ... ] } }
}
defaultSeeds[0] is the canonical seed: it is the one the docs site
ships as playable, the one golden-file tests pin, and the one
make examples builds by default.
Command line
bin/story.js --example maze-locked --seed 42 --format twine --out out/maze-locked.42.twee
bin/story.js --example maze-locked --seed 42 --format choicescript --out out/maze-locked.42/
bin/story.js --example maze-locked --seed 42 --format inform7 --out out/maze-locked.42.ni
bin/story.js --example maze-locked --seed 42 --format ir --out out/maze-locked.42.ir.json
bin/story.js --example maze-locked --seed 42 --format play --out docs/play/stories/maze-locked.42.js
bin/story.js --list
--format values: ir, twine, choicescript, inform7, play, dot.
choicescript writes a directory (a ChoiceScript project needs several
files); every other format writes a single file. --out - writes to
stdout for the single-file formats.
Other flags mirror bin/transform.js where they overlap: --theme,
--placeholder, --no-llm, --sonnet, --quiet, --verbose.
Reproducibility contract
For a fixed library version:
--example E --seed Sproduces byte-identical output on every machine, in every format, for as long as the grammar forEis unchanged.
This holds because the only entropy source is the seeded Mersenne
Twister, the theme is derived from the seed, meta carries no timestamp,
and IR ordering is defined (see story-ir.md §1). The
--sonnet path is exempt — it calls a model — but it is cached on disk
by prompt hash, so a warm cache reproduces too.