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.

Writing documentation

Français · Type: how-to + reference · Audience: contributor · Context: engine repository · Status: ready · Applies from: 4.5.0

How the documentation of quest'n'click is written, checked and published: one source, wiki/, in English and French, from which the GitHub wiki, the versioned Pages site, the bundle in the package and the MCP documentation tools are all generated. This page is the procedure for writing or changing a page, then the reference of what a page and the manifest may hold.

What you get

A page, or a change to one, in both languages, that npm run wiki -- check accepts, whose facts are tied to the code it rests on, and that the next release publishes. A page edited in the GitHub wiki itself is not kept: the wiki is generated, and its next publication overwrites it.

Context and versions

The engine repository. The wiki's tooling is new in 4.5.0 (ADR 0030, D50, docs/dev/PLAN-4.5-WIKI.md). Since the 4.5.0 switch-over docs/en and docs/fr are transition pages that npm run wiki -- transition writes from the coverage matrix: never edit them, write in wiki/. Publication happens only from a release, after ship verify, never from main (Releasing).

Prerequisites

Where to run the commands

In the engine repository, on your machine. Every npm run wiki -- … command reads wiki/ by default (--wiki=<dir> reads another folder, as the tests do).

Steps

  1. Find the page's entry in wiki/manifest.json. Every page has one, written or not: its id, type, audience, context, titles, URLs, state. A new page needs its entry first; an entry is never renamed (the id names the file and never moves).
  1. Start from the template of its type, wiki/_templates/<type>.en.md and <type>.fr.md (tutorial, howto, reference, explanation, troubleshooting), copied to wiki/en/<id>.md and wiki/fr/<id>.md. Keep the template's section ids; delete its comments. The header (type, audience, context, versions, statuses, language switch) and the footer are generated: do not write them.
  1. Write each language natively, not word for word: short sentences, the same sections and the same facts. Every command is preceded by the folder and the context it runs in (the engine repository, a created project, a hosted Studio). A figure has a date and a source. A guarantee states its limit and its proof. docs/dev is "public development documentation", never "internal".
  1. Fill the entry: state (draft, then ready), versions.appliesFrom, precise sources, questions for a how-to or troubleshooting page, aliases, keywords, identifiers, related, prerequisites, claims for each [!GUARANTEE], surfaces where a contract applies (ids of docs/dev/contracts.json). The replaces field is generated from the coverage matrix: never typed.
  1. Check.

    # in the engine repository
    npm run wiki -- check
    

    It ends with ✔ wiki (work level): <n> pages consistent, <w> warning(s), <l> link(s) to planned pages, or lists each error (✖) with its file and line. Warnings (⚠) and what is still to write (·) do not fail it.

  1. Record who read what once the two languages were read against each other, and once a claim's test was read against the claim:

    # in the engine repository
    npm run wiki -- translations --ack <id>#<section> --by <name> --kind human|ai --relation author|independent
    npm run wiki -- review-claim <claim-id> --by <name> --kind human|ai --relation author|independent
    
  1. Look at the generated wiki when the layout matters:

    # in the engine repository
    npm run wiki -- build --target wiki --out .cache/wiki --preview
    

    The output folder must be outside the working tree or under .cache/; --preview includes the draft pages.

  1. Open the pull request with both languages together. A branch that touches only wiki/ needs no changes/ fragment (Conventions); CI's fast tier runs the documentation's checks on every change.

Expected result, and how to check it

npm run wiki -- check ends with ✔ and 0 error. npm run wiki -- stats shows the page in its state. When the page answers a real question, npm run wiki -- acceptance finds it in the top three for that question (the commands).

Common errors

SymptomCauseFix
<id> (draft): fr/<id>.md is missing (both languages)Only one language writtenWrite both; a written page always has both files
<id> (planned): planned, yet en/<id>.md existsA file for a page still plannedSet its state to draft (with versions and sources)
the source "<glob>" matches no tracked fileA glob with a typo, or a file not committed yetFix the glob; git add the file
line <n>: links to <path>, which git does not trackA repository link that does not resolveLinks to files are ../../path/in/repo, from wiki/<lang>/
<id>.md#<anchor> is not a section of <lang>/<id>.mdA link to a section id that does not existUse the target page's section id, the same in both languages
the section ids differ between en and frA ready page whose twins do not have the same sectionsSame <a name> ids in both
a howto page asks at least one question in en and frquestions emptyAdd at least one natural question per language
coverage.json …: <id>#<section> is not a section of <id>The coverage matrix sends an old section to a section id the page lacksGive the page that section id
⚠ the source "<glob>" covers <n> files (> 25)A glob too wideNarrow it: an ordinary change must not ask to reread dozens of pages

Files or data changed

wiki/en/<id>.md, wiki/fr/<id>.md, the page's entry in wiki/manifest.json; wiki/translations.json and wiki/reviews.json through their commands; images under wiki/images/ only.

Recovery or going back

Everything is in Git. A published documentation revision is immutable: a correction to a released version's documentation is a new revision r<n> made from a branch docs/<version> and published by the docs-publish workflow, never an edit of what was published.

Reference: the wiki/ folder

PathWhat it holds
wiki/manifest.jsonThe map of the documentation: one entry per page, written or not
wiki/en/<id>.md, wiki/fr/<id>.mdThe pages, one file per language
wiki/_templates/*.{en,fr}.mdThe five page templates
wiki/images/The only images a page may show (../images/<file> from a page)
wiki/coverage.jsonThe coverage matrix: every section of the old documentation and its destinations
wiki/legacy.jsonFor each of the eight read_doc { name } pages, the ordered sections that rebuild it
wiki/translations.jsonWhich revision of each language was read against the other, and by whom
wiki/reviews.jsonWhich claims' scope was read against their test, and by whom
wiki/data/env.json, wiki/data/glossary.jsonThe environment variable register and the bilingual glossary
wiki/acceptance/questions.jsonThe real questions the documentation must answer, with counter-examples

Reference: the manifest

Form

{ "format": "quest-n-click-docs-manifest", "version": 1, "pages": [...] }. The schema is strict: a field it does not name is refused, so a misspelt key is an error.

Values

FieldRequiredMeaning
idyes<domain>.<slug> in lowercase kebab case; names the file, never moves
domainyesindex, start, play, install, create, engine, studio, team, clever, bridge, contribute, reference, help
type, alsoTypestypetutorial, howto, reference, explanation, troubleshooting, index
audienceyesplayer, author, admin, contributor
contextyesengine-repo, created-project, studio-local, studio-demo, studio-hosted, bridge, player
title.{en,fr}, url.{en,fr}yesThe natural titles; the public URLs (below)
stateyesplanned, draft, ready (below)
versionsfrom draftappliesFrom (the first version for which what the page says is true and works), appliesUntil, featureSince
sourcesnon-empty from draftGlobs of the tracked files the page rests on: its drift sources
replacesgeneratedThe old sections the page takes over, from the coverage matrix
questions.{en,fr}how-to and troubleshooting: at least one eachNatural questions a reader asks
aliases, keywordsoptionalOther names (an old name, a synonym), other search words
identifiersyes (may be empty)Exact identifiers the page documents: a command, an npm script, a variable, a route, a code
related, prerequisitesyes (may be empty)Other page ids
claimswith each [!GUARANTEE]{ id, test: "file.test.ts › describe › it" }
surfacesoptionalIds of docs/dev/contracts.json; the header shows their statuses
lifecycleoptionaldeprecated or archived, a page kept for older versions
owner, reviewedoptional@wanoo by default; the day of the last review

URLs are mechanical: EN-<Domain>-<Topic> and FR-<Domaine>-<Sujet>, ASCII words each capitalised, joined by hyphens, the domain's fixed word (Start/Demarrer, Create/Creer, Engine/Moteur, Team/Equipe, Contribute/Contribuer, Help/Aide; the others the same in both), acronyms in capitals (EN-Reference-NPM-Scripts, never Npm). URLs are unique without case.

Constraints

stateMeaningPublished
plannedDestination decided, no file (a file is an error)No
draftFiles in both languages, checks incompleteNo: previews only, with a "draft" banner
readyComplete, checks passed, translation pairedYes

ready says the work is complete, not that a person reviewed it: that is recorded apart (Reviews). The four indexes (index.by-task, index.by-symptom, index.identifiers, index.glossary) are generated: true, have no file, and are built from the data. wiki -- check warns when a glob covers more than 25 files, or when one source file feeds more than 10 pages.

Reference: a page

Section ids. A plain HTML anchor on its own line before each ## or ### heading, lowercase kebab case, the same id in both languages; a link to a section survives a translated heading:

<a name="walkable-zones"></a>

## Walk zones

Links. Another page: create.rooms.md#walkable-zones (relative, same language folder). A repository file: ../../src/engine/core/save.ts, from wiki/<lang>/; a bare src/x.ts is refused. An image: ../images/<file>. The build rewrites each link for each output (wiki, Pages, the bundle). A link to a planned page is listed, not an error, at the work level; a published page may only link to ready pages.

Steps. Each essential step of a procedure carries <!-- step: <id> --> on the line before it; the set of step ids is the same in both languages.

Section versions. A section true for fewer versions than its page says so on the line right after its anchor, inside the page's range and the same in both languages: <!-- applies: 4.2.2.. --> (from), <!-- applies: 4.2.2..4.4.0 --> (both inclusive), <!-- applies: ..4.4.0 --> (until), with since <v> for the version the feature appeared in and a last deprecated or archived for a section kept for older versions. A malformed marker is an error, never ignored.

Reference: structured blocks

Blockquotes with an id, paired by id between the languages, and rendered by the build:

> [!LIMIT id=local-only]
> Available on the local Studio only, not on a hosted Studio.

> [!GUARANTEE id=restore-empty-db-only]
> A restore goes into an empty database only; any other is refused.

> [!MEASURE id=restore-time]
> 30 to 60 s, observed: an observation, never a promise.
> - date: 2026-10-19
> - version: 4.2.3
> - volume: 508 journal lines
> - conditions: the smallest plans
> - source: https://github.com/wanoo/quest-n-click/blob/<40-hex sha>/docs/dev/QUALIF-4.2.3-REFERENCE.md

Reference: checked examples

A fenced block tagged room-example holds JSON: a room as defineRoom takes it, or { "room", "layout", "items", "characters", "verbs", "hero" } when it needs its geometry, items, actors, the game's own verbs or another hero. It is put into a one-room game (schema 3) and run through the engine's validator: any error fails wiki -- check. A block tagged studio-config holds a Studio Server configuration, loaded by the server's own loader (loadConfig): what it refuses, the check refuses. Use plain json or ts blocks for anything that must not be validated. The parser never reads inside a fenced block, so syntax shown in a fence (like the examples on this page) is not taken for a real anchor or block.

Reference: French and English parity

There is no source language. For a ready page, wiki -- check compares the two languages by meaning, not by words: the same section ids, the same link destinations (id#section, never URLs), the same structured blocks by kind and id, the same step ids, the same section versions, and a measure's same date, version and source. Translatable text (lines, labels, comments in code blocks) may differ; shell commands stay identical, comments aside.

Reference: translations and reviews

wiki/translations.json records, for each section <id>#<section> of a ready page (<id>#_lead for the text before the first section), the hash of each language that was read against the other and its reviewers. When either text, or an image the section shows, changes, the pair is "to review": a warning at the work level, an error for check --publish. wiki/reviews.json records the same for a claim's scope (the hashes of the claim's text and of its test file).

Each reviewer is { name, kind: human|ai, relation: author|independent, date }, and both commands require --by, --kind and --relation. The same text read again adds a reviewer; a changed text starts over. A published page needs a review by a person (kind: human) for every translation pair and every claim; until then the page says "self-checked by the author (AI), not reviewed" (or "checked by an independent AI, not reviewed by a person"), never "reviewed".

Limit · ai-review-is-not-review An AI's check of what it wrote is recorded as --kind ai --relation author and is never shown as a review. Only a record with --kind human makes a pair or a claim "reviewed".

Reference: the commands

All run in the engine repository as npm run wiki -- <command>.

CommandDoes
checkThe work level of every pull request: schema, files present for draft and ready pages, links, parity of ready pages, structured blocks, examples, claims referenced, the coverage matrix against docs/, the real questions' file. Fails on errors only
check --publish --product-ref <tag|sha> [--doc-ref <sha>] [--released] [--state preview|candidate|published] [--test-report <file>]… [--candidate <run id>]The publication level: published pages link to ready pages only, no translation to review, every claim executed on the product, the source set the product's plus documentation only; --state candidate|published needs --candidate
stats [--json]Pages by state, domain, type and language, and the coverage counters
build --target wiki --out <dir> [--preview]The folder a publication pushes to the GitHub wiki; ready pages only, drafts too with --preview; refuses a folder inside the working tree other than .cache/
bundle --out <dir> [--build-id <id>] [--allow-dirty]The documentation an installed engine carries (scripts/pack.mjs runs it); refuses sources that differ from HEAD unless --allow-dirty, which marks it -dirty
translations --ack <id#section> --by <name> --kind human|ai --relation author|independentRecords that a pair was read
review-claim <id> --by … --kind … --relation …Records that a claim's scope was read
coverage [--ack <key>], coverage --init [--force]Merges a new inventory of the old docs into the matrix, regenerates legacy.json and the manifest's replaces; --init takes the first inventory (--force replaces an existing matrix, losing every decision)
acceptance [--lang en|fr] [--json] [--strict]Runs the real questions through search_docs; exits 0 (reported) unless --strict; --export-prompts <dir> writes the prompts of an AI reader's test instead
envRegenerates the environment variable reference from wiki/data/env.json and the code
archive, publish, pages, transition, next-revision, attestThe publication chain, run by the release and docs-publish workflows

npm run docs:truth checks what the old documentation states against the code (schemas, npm run scripts, files, the current release in the READMEs); npm run docs:links asks every external link once, by hand before a release, never in CI.

Reference: the coverage matrix and the freeze

wiki/coverage.json lists every section of the old documentation (docs/en, docs/fr, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, CREDITS, both READMEs) with its destinations id#section and two counters, "destinations planned" and "content migrated and reviewed". No content may be lost: every fact, command, example, limit and warning of a replaced section lands in one of its destination pages. Until the switch-over, wiki -- check refused a matrix out of step with docs/ (the freeze). Since it (4.5.0) the matrix is final: the old pages are the transition pages it makes (npm run wiki -- transition), and wiki -- check refuses one edited by hand or one that lost an old anchor.

Reference: when the code moves under a page

npm run ci:plan reads a branch's diff and names the wiki pages whose sources it touched ("wiki pages to reread"), and CI's docs family runs on them. A page's sources are therefore its precise drift sources: keep them narrow.

Limit · no-drift-ack-yet The plan's wiki -- ack <id> --reason command, which would record "reviewed, nothing to change" for a drift notice, is not implemented in 4.5.0: ci:plan names the pages, and the reader decides.

Decisions and ADRs


Edit the source of this page · Report an error