quest'n'click 4.5.0 · r1

Built for quest'n'click 4.5.0, revision r1. Whether it is published, and which one is current, is recorded at publication.json.

Checking your game

Français · Type: how-to · Audience: author · Context: engine repository, created project · Status: ready · Applies from: 4.3.0

Four commands tell you most of what is wrong with a game before anyone plays it: validate checks the content, solve finds a way to the ending, lint reads the puzzle graph for what a player would trip on, and solve --prove proves that no player can get stuck. Then the game is played for real: by a scripted walkthrough, in a browser, on a phone, and by a person.

What you get

A game whose references all resolve, that can be finished from "New game", whose softlocks are ruled out by an exhaustive proof, and whose remaining warnings you have read and either fixed or kept on purpose. At the end, the checklist Before you share the link.

Context and versions

Prerequisites

Where to run the commands

On your machine, at the root of the folder:

CheckEngine repositoryCreated project
Contentnpm run validatenpx quest-n-click validate
A way to the endingnpm run solvenpx quest-n-click solve
What a player would trip onnpm run lintnpx quest-n-click lint
No softlocknpm run solve -- --provenpx quest-n-click solve --prove
All the game's gatesnpm run verify:gamenpm run verify
The exhaustive gatesnpm run prove:gamepart of npm run release

In the engine repository, options go after -- (npm run solve -- --prove); with npx quest-n-click they follow the subcommand directly.

Steps

  1. Validate the content.

    # in the engine repository
    npm run validate
    # in a created project
    npx quest-n-click validate
    

    Everything referenced exists, every visible thing has a Look line, texts are neither empty nor too long, flags are set and read. Exit 1 on an error. What it checks is listed in What each check looks at.

  1. Find a way to the ending.

    # in the engine repository
    npm run solve
    # in a created project
    npx quest-n-click solve
    

    It prints ✔ The game can be finished, the path, and three warnings to read: dead ends, items obtained but never used, items never obtained (the warnings). A winning path is not a proof.

  1. Lint the content.

    # in the engine repository
    npm run lint
    # in a created project
    npx quest-n-click lint
    

    Conditions nothing can satisfy, rules another rule hides, red herrings, hints that cannot fire, actions the solver never ran. Fix each finding, or keep it on purpose in game.ts (lint.ignore, below).

  1. Prove there is no softlock.

    # in the engine repository: the global proof, the proof by chapters, every Reality world
    npm run prove:game
    # in a created project
    npx quest-n-click solve --prove
    

    Only a solved from --prove rules out softlocks. A truncated result proves nothing: raise the budget (Solver options).

  1. Play it for real. A scripted walkthrough and the browser e2e (Prove it plays), a real phone in landscape (Testing on a phone over the LAN), then a person who has never seen the game (Playtests).
  1. Go through the checklist Before you share the link.

What each check looks at

Keeping a finding on purpose. A red herring you want, or a decorative flag (set, never read), is silenced in game.ts; the same list keeps flag-never-read:<flag> out of validate's warnings:

lint: { ignore: ['item-red-herring:rubber_chicken', 'rule-shadowed:house/on[4]'] },   // a code, `code:<id>` or `code:<room>/<path>`

ui.shareSession names the pause menu's row that sends a playtest from a phone (Playtests).

Commands

Every command below runs in the engine repository with npm run. The second column gives the created project's equivalent when there is one; "—" means the created project has none.

The game's content

Engine repositoryCreated projectWhat it does
npm run validatenpx quest-n-click validateBroken references, empty or too-long text, missing Look lines, flags never set or never read. Exit 1 on an error.
npm run validate -- --reportnpx quest-n-click validate --reportThe content profiler (Markdown): rooms, items, characters, what is thin.
npm run validate -- --releasenpx quest-n-click validate --releaseAlso provenance, placeholders, and a stable id on every line of a game shipped in another language than its own or voiced.
npm run validate -- --commercial --errorsnpx quest-n-click validate --commercial --errorsA release that may be sold: no exception, no placeholder, no non-commercial licence, every source checkable; --errors prints the errors only.
npm run solve …npx quest-n-click solve …The solver: Solver options.
npm run lint [-- --prove | --static | --json]npx quest-n-click lint …The content lint, above. lint:content is the same command (alias since 4.1.0).
npm run i18n -- extract [--lang xx]npx quest-n-click i18n extract …Translation tables (locales/<xx>.json); status for the coverage. Translations.
npm run playtests …npx quest-n-click playtests …Players' sessions replayed and summed up: Playtests.
npm run page:world—The map of the world as a page: exits, gotos, unreachable rooms, DOT source.
npm run page:puzzles—The puzzle graph as a page: what every rule needs and changes, a card per item and flag.
npm run replay -- session.json— (the Studio's Play tab)A session played on the real engine: Sessions.

The game's gates

Engine repositoryCreated projectWhat it does
npm run verify:gamenpm run verifyEngine repository: validate, the witness, the witness by chapters, the variants (verify:variants), the translation status, the lint, the playtests. Created project: validate, solve, i18n status, lint, playtests.
npm run prove:game— (npm run release runs solve --prove and solve-reality)The global proof, the proof by chapters and every Reality world; fails on a softlock or a truncation.
npm run build:gamenpm run buildThe game's gates, the bundle, verify:dist, then (engine repository) the spoiler check and the asset audit. No type-check, no unit suite.
npm run buildnpm run buildEngine repository: check (type-check and Node tests), then build:game.
npm run verify:releasepart of npm run releasevalidate --release, weight --release, i18n status, voices check --release, strict playtests.
npm run verify:commercialnpm run release -- --commercialverify:release, then no exception, no placeholder, no NC or ND licence, every source checkable, then verify:dist.
npm run verify:distpart of buildEvery file of dist/ accounted for: code, locked assets (their reviewed bytes), named data, fonts, icons, licenses/.
npm run verify:field—verify:commercial, then the playtest quotas of a release tested by players (not a step of release-check, D12).
npm run provenance [-- --lock]npx quest-n-click provenance [--lock]The shipped assets by licence, what changed since the reviewed lock; --lock records the files after a review. Asset provenance.
npm run weight [-- --release --json --stems]npx quest-n-click weight …What a phone downloads before the first room, per room and per chapter, against assetBudgets.
npm run voices -- status | export | import | checknpx quest-n-click voices …Recorded lines per language. status [--lang xx]: lines with an id by status (draft, record, recorded, approved), clips, orphans; a line with text switches has one row per value, <id>~form=f. export --lang xx [--json] > t.csv: the actors' table (id, who, text, status, file, actor, note). import t.csv --lang xx: statuses, actors and notes back into voices.json (unknown lines refused). check [--lang xx] [--release]: every clip measured with ffmpeg (codec, rate, length for its text, loudness about −16 LUFS, peak); --release: an approved line needs its clip. Voices.
npm run i18n -- voicesnpx quest-n-click i18n voicesThe lines with an id and no clip, the clips no line claims.
npm run ids [-- --write --map], npm run ids -- --linesnpx quest-n-click ids …Stable ids (schema 3) written into the sources, locales renamed, the save migration step. --lines: an id on every say, toast, guide object, list line, hint and kind reaction (--lines=all: plain strings too, required for a translated or voiced release); translations and voices are keyed by it. State and saves.

The engine repository's own checks

CommandWhat it does
npm testThe Node tests of the engine, the tools and the selected game, without the heavy solver tests.
npm run test:heavyThe CPU-bound solver tests: every night, minutes each (Solver options).
npm run test:coverageThe suite under V8 coverage against the floors of vite.config.ts; then npx tsx tools/coverage-ratchet.ts --strict fails on a floor at least three points behind what the tests reach (The coverage ratchet).
npm run test:assetsThe Python-backed image and asset-pipeline tests.
npm run checkType-check and Node tests.
npm run tsc -- …The TypeScript 7 compiler itself; check and quality call it.
npm run qualityBiome formatting and lint, knip, tsconfig.json and tsconfig.strictest.json, then the content lint.
npm run quality:baseline -- --check [--dist]The behaviour of 4.0.0 kept: witnesses, proofs, golden saves, public surface, first visit (The quality baseline).
npm run doctor [-- --release]Node, Python modules, ffmpeg, Playwright browsers (doctor).
npm run release-checkWhat CI runs, in one go: doctor --release, quality, build, coverage and its strict ratchet, verify:release, prove:game, solve --audit-abstractions, the Rust cross-check, mutation of the core, the packages with npm publish --dry-run, the dependency audits. release-check:ci is the same without the mutation sets (Releasing).
npm run field:init, field:check, field:report, field:bundle, field:verify-bundle, field:carryThe Field Kit: the people's passes as JSON reports bound to a candidate run (Field passes).

In a browser (engine repository; the dev server or a production build already running at <url>)

CommandWhat it does
npm run e2e -- <url>A playthrough in Chromium, phone landscape, along the solver's path (Prove it plays).
npm run e2e -- <url> --renderer canvasThe whole game drawn by the Canvas painter.
npm run e2e -- <url> --lang frThe whole game in that language; fails on any visible English default of the engine.
npm run e2e:visual -- <url> [--update]Every room, still, against tests/visual/<game>/*.png (0.5 % of the pixels at most).
npm run e2e:a11y -- <url> [--only=axe,keys,storage] [--allow-skip]axe on the conversation, map, slots, confirmations and every minigame; every minigame won at the keyboard; an older save upgraded (E2E_BROWSER=chromium|webkit; exit 3: a check the browser cannot automate).
npm run e2e:perf -- <url> [--renderer=canvas|dom --cpu=4 --min=30 --room=<id>]Frames per second while the hero walks, CPU slowed (the phone stand-in).
npm run e2e:music -- <url> [--only=offline|live|game|reference …]The music director: 30 min offline without drift, 100 changes without a click, real-time jitter (--live=chromium|webkit), the theme's stems in the game; --only=reference [--browser=webkit] with GAME=reference: two scores, bridges, a restore and a stop mid-transition, the decoded peak (Music).

Documentation and repository tooling (engine repository)

CommandWhat it does
npm run docs:screenshots [-- --only=game|studio --keep-png]The README images from the production bundle and the Studio, as WebP (needs Python with Pillow).
npm run docs:truthWhat the docs state against the code: the save and .wsrun schemas, every npm script and tools/ or scripts/ file they cite, no release link newer than the version. CI's check job.
npm run docs:links [-- --timeout=10000]Every external link of the docs asked once; a person runs it before a release, CI never does.
npm run changes -- --check | --assembleThe CHANGELOG and LOG fragments of a branch (Conventions).
npm run ci:plan [-- --base=origin/main | --files=a,b | --all]Which of CI's heavier jobs a change needs, one line of JSON; since 4.5.0 also docs and docsPages, the wiki pages whose sources the change touches (CI explained).
npx tsx tools/api-doc.ts [--check], npx tsx tools/dsl-doc.ts [--check]The generated API and DSL references (Public API, DSL reference).

Prove it plays

In the engine repository, npm test runs the engine tests and the game's walkthrough: a scripted full playthrough on the real engine (tests/demo-walkthrough.test.ts for the demo). npm run e2e plays the whole game by touch in a phone-sized Chromium and saves a screenshot of every step: look at them, or have your assistant look, to catch what the solver cannot see (a character standing on a table, a line cut by the screen edge).

# in the engine repository: start the dev server, then in another terminal
npm run dev
npm run e2e -- http://localhost:5173/

npm run e2e takes the solver's path (npm run solve -- --json, its steps) and taps through it. It passes only when the solver's run is solved (its exit code, status and steps are checked) and the engine itself reports the ending (state.done): a truncated or unsolved game is a failure, never "the best path replayed anyway". games/demo/e2e.mjs is the example of a game-specific e2e script: it starts from the title screen, plays the pipes and pick minigames and the sealed ending's scratch ticket for real instead of skipping them, then checks the final card.

Limit · created-project-no-e2e A created project has no test or e2e command: its checks are verify, build and release. The browser e2e and the visual, accessibility, performance and music checks run in the engine repository.

In CI (the engine repository's .github/workflows/ci.yml): every push to main, every tag and every pull request runs npm run build:game (the game's gates, the bundle, the spoiler and asset audits) and npm run prove:game on the sample game; the heavier jobs (the production e2e in several browsers, the keyboard and French playthroughs, a second game made from the template…) run when ci:plan says the change needs them. main deploys to Pages; a tag releases. The weekly prove workflow (Mondays, or by hand) runs prove:game, the strict playtests and the bench on a generated 100-room game. Dependabot proposes npm and Actions updates weekly, pip ones monthly (Bot PRs).

Before you share the link

Then build and ship it: Build and release a game.

Expected result, and how to check it

The exit code is the verdict: 0 passed, 1 a problem to fix, 2 the search was truncated and nothing is proved.

Common errors

SymptomCauseFix
solve exits 2, truncated: the search stopped at N states, nothing is proved (raise --max)The budget is too small for the gameRaise --max, --time or --mem, prove by chapters, read the profile (Solver options).
softlocks: N reachable state(s) can no longer reach the endingA consumed item, a closing flag, a hidden clueRead the causes and replay their path (What the solver proves).
Dead ends (n): …A state where no action changes anythingGive the item back, or loosen the condition (the warnings).
broken: N invariant(s) broken on a reachable stateA "never again" bug came backThe path is printed; fix the rule it names.
lint exits 1 with cond-never-true or hint-stuckA condition or hint waits for something nothing setsSet it somewhere, or change the condition; or ignore it in lint.ignore if intended.
fallback-unplayedA required signal's fallback is declared on a rule the closed witness never playsA game with the outside world.
The result says (from the proof cache, key …) and you doubt itThe cache answered because nothing changed--no-cache or PROOF_CACHE=0 runs it again (the proof cache).

Files or data changed

The checks only read your game. What they write: .cache/proofs/ (the proof cache), and the folders you name with --out. npm run e2e writes its screenshots to E2E_OUT (default /tmp/e2e). validate --report prints to the terminal; page:world and page:puzzles write their page under dist-pages/ (or --out <dir|file.html>). ids --write and provenance --lock are the exceptions that change files you commit: they are not checks, and they say so.

Recovery or going back

Nothing to undo: a check does not change the game. To start from a clean slate, delete .cache/proofs/.

References


Edit the source of this page · Report an error