ContributeHow-toFor contributorsApplies from 4.1.11
The dev loop
Cette page en françaisThis page on the GitHub wiki
How to change the engine one small step at a time, and how to carry a change through every place that reads it. The worked case is the hardest common one: adding or changing a command of the DSL.
What you get
A change that compiles, whose own tests pass, that the validator, the solver, the Studio and the MCP all understand, and that you have looked at in the browser, before you run the full list of Before a pull request.
Context and versions
The engine repository, on your machine. The command table (HANDLERS, src/engine/core/command-handlers.ts) exists
since 4.1.5; the catalogue src/engine/core/cmds.ts since 4.1.0. This page describes the code as of 4.4.0.
Prerequisites
- A prepared repository: Developer setup.
- The map of the code: Architecture; the walk through it: Code tour.
Where to run the commands
In the engine repository, on your machine. GAME=<id> picks another game than the current one
(Developer setup).
Steps
-
Run the tests closest to what you change, not the whole suite, while you work.
# in the engine repository npx vitest run tests/core.test.ts # one file npx vitest run tests/core.test.ts -t "fixture game" # one test by name npx vitest tests/cmds.test.ts # watch mode: re-runs on saveWhich tests run when says which file tests what.
-
Type-check as you go: the catalogue and the command table refuse a command you forgot, at compile time.
# in the engine repository npm run -s tsc -- --noEmit npm run -s tsc -- --noEmit -p tsconfig.strictest.json # every index access checked in src/
-
Ask the game. After a change that a game can feel, the truth commands answer in seconds:
# in the engine repository npm run validate && npm run solve && npm run lintvalidatenames broken ids and values,solveproves the game can still be finished from New Game,lint(the content lint) finds conditions nothing sets, hidden rules and actions never run.
- Look at it.
npm run devserves the game athttp://localhost:5173/;npm run studioopens the local Studio, which shows each room rendered by the real engine and reloads when a file changes. A visual change is looked at, not assumed:?dev&at=<checkpoint>starts the game at a checkpoint (Dev URL parameters).
Changing a command, end to end
-
Changing a command, end to end. A command (
Cmd) is read by the engine, the validator, the solver, the puzzle graph, the texts, the Studio and the MCP. Touch each in this order; the type check or a test refuses what you forget:- Type. Its variant of
Cmdinsrc/engine/core/types/content.ts, with a doc comment. - Catalogue. Its key in
CMD_KEYS(src/engine/core/cmds.ts): a variant missing from it, or a key no variant has, does not compile. Add it toCHANGESif it changes the state (the solver and the no-op memo read that set), toCONTAINERSandsubListsif it holds command lists. - Engine. Its handler in
HANDLERS(src/engine/core/command-handlers.ts), whichstep(src/engine/core/command-runtime.ts) looks up by key; the type of the table refuses a missing handler. The screen is reached through aPresentermethod (src/engine/core/ports.ts), implemented byDomPresenter(src/engine/dom/presenter.ts) and byFakePresenter. If it touches the state, what it reads and writes is recorded (Engine.readsandEngine.writes). - Validator. Its references and values checked in
src/engine/tools/validate.ts. - Solver and puzzle graph. Nothing to do if the catalogue is right; a command that branches or waits needs a
look at
src/engine/tools/puzzle.tsandsrc/engine/tools/solve/expansion.ts. - Texts. A command that carries a line needs its branch in the texts walker,
src/engine/tools/i18n.ts(extraction, locales), besidesay,toast,guideandchoice. - Studio. Its form in
src/studio/schema.ts; the MCP reads the same schemas (MCP tools). - Tests. An example in the table of
tests/cmds.test.ts(every key must have one), a behaviour test intests/core.test.ts, and a validator case. - Docs. The wiki pages of the command, in both languages (Game commands,
DSL reference), and the branch's fragment under
changes/(Conventions).
A review of such a change reads these files in this order.
- Type. Its variant of
-
Worked example:
{ shake: ms }, the screen shaking for a moment.- The type is
| { shake: number }incore/types/content.ts. 'shake'is inCMD_KEYSbut not inCHANGES: it changes nothing the solver keys on, so a shake is a no-op for a proof.- Its handler in
command-handlers.tscallseng.ui.shake(c.shake)unless a cutscene is being skipped (ctx.fast).DomPresenter.shakeadds the scene'sshakeCSS class for that many milliseconds;FakePresenterignores it. - The validator accepts it inside a looping animation's frame events, with
sfx, and nothing else (tools/validate.ts: "a looping animation only plays sounds and shakes at a frame"). - The Studio offers it as "Shake (ms)", from 0 to 5000 in steps of 50 (
src/studio/schema.ts). tests/cmds.test.tsholdsshake: { shake: 2 }.
- The type is
Expected result, and how to check it
npm run -s tsc -- --noEmitis silent; the test files you touched pass.npm run validate && npm run solveend green on the sample game (and onGAME=referenceif your change reaches staging or several characters).- What you changed looks right in the browser or the Studio.
Then go through Before a pull request.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
'a Cmd variant is missing from CMD_KEYS' at compile time | The variant was added to Cmd, not to the catalogue | Add its key to CMD_KEYS in core/cmds.ts |
'CMD_KEYS names a key no Cmd has' | The key was added to the catalogue only | Add the variant to Cmd |
A type error on HANDLERS | No handler for the new key | Add it to the table in core/command-handlers.ts |
tests/cmds.test.ts fails on the new key | No example in its table | Add one |
quality:baseline -- --check reports a moved digest | The change altered what the engine does on a frozen game | Intended: rewrite the baseline and say so in the fragment; not intended: find the regression (The quality baseline) |
Files or data changed
The source files above; tests/; the wiki pages of the command; changes/<slug>.md. A command that the solver reads
can move tests/quality-baseline.json, which is then rewritten on purpose.
Recovery or going back
Everything is in Git: git restore <file> or git stash undoes a local change. GAME=demo npm run game puts the .cache/game
link back when a GAME= experiment left it elsewhere.
References
- Architecture, Code tour, Import boundaries.
- Which tests run when, Before a pull request.
- ADR 0002, declarative content; ADR 0003, the solver runs the engine.