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.

CI explained

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

What GitHub Actions runs on the engine repository, why a job ran or said "not needed by the plan", and what to do when one is red. The workflows are in .github/workflows/; the plan that decides what a pull request runs is tools/ci-plan.ts. Which test suite runs in which job is Which tests run when.

The workflows

WorkflowStarts onDoesGates
cia pull request; a push to main; a v* tag; by hand (pages-only)the three tiers below, then Pages on mainthe pull request (pr-gate); a release (the tag's run)
candidateby hand on main for a SHA (gh workflow run candidate --ref main -f sha=<full sha>); a pull request labelled full-ci (information only)every heavy family on one exact SHA, then candidate-gate, which writes and attests candidate-manifest.jsona tag must name one green run of it (Releasing)
releasethe completion of ci on a v* tagthe mutation sets, release-check:ci, the release made from the candidate's files, ship verify; then docs-publish (4.5.0)publication
nightlyevery day at 03:17 UTC; by handnpm names, the heavy suite, the connectors' fuzz (a minute each), the abstraction corpus (500 seeds, four shards), every mutation set from scratch, the Bridge's loads on SQLite and Postgres, the proof matrix, cross-runtime with 500 Remix seeds, the worker in a containernothing directly; a red night is a bug to fix before the next release
proveevery Monday at 04:00 UTC; by hand (game, rooms)prove:game, the committed playtests (--strict), and the bench on a 100-room generated game (uploaded as bench)never a gate
mutationby hand: gh workflow run mutation --ref main -f set=<set> -f ref=<branch or SHA> [-f file=<path>]one mutation set, measured on a runner, its report kept as an artifactnothing
qualifya pull request or a push to main touching ops/, scripts/ops-qualify*, scripts/pack.mjs, bridge/, package.json or the lockfile; by handnpm run ops:qualify: the Bridge's qualification profile on Dockerthe profile does not rot between candidates
docs-publishcalled by release after ship verify; by hand (revision, rollback, promote, preview)the wiki and the publication record (4.5.0)publication of the documentation

The three tiers of ci

feature since 4.1.9

The fast tier, on every change. plan reads the diff; check and coverage run beside it:

The second tier, what the change can break. Each job needs plan (most also check) and reads its gate in the plan: it runs its real work, or one step that says "not needed by the plan" and succeeds.

JobGateRuns
node-24node24the unit suite on Node 24
windowswindowsthe doctor, the type checks, the unit suite without the files that assume POSIX, build:game, the production server answering
e2e (six rows)e2ethe production build played by touch: Chromium full, WebKit generic, Chromium and WebKit at the keyboard, Chromium in French, Chromium with the Canvas painter; with weight, music, visual, perf, Studio, accessibility and PWA steps on some rows
pwa-firefoxpwaFirefoxthe PWA scenario in Firefox (install, update, interrupted update, reinstall, offline)
reference (Chromium, WebKit), reference-fr, heroesreferencethe reference chapter built, proved, played at the keyboard, against its visual references, at 30 fps or more with the CPU slowed 4×; reference-fr plays it in French and with the generic verbs; heroes builds, proves and plays the heroes example
second-gamesecondGamea game made by npm run new-game, through every gate the sample passes
fresh-installfreshInstallthe engine packed, a game created outside the repository, installed, built and played
external-consumer (Ubuntu, macOS, Windows)freshInstallthe archives installed outside the repository on three systems
upgradeupgradea game of the previous release moved to this engine, its old save played to the end
reality-xcheck, connectors, bridge-postgres, reality (Chromium, WebKit)realityBiscuit's samples in JavaScript and Rust, the connectors against a real Bridge with their fuzz, the Bridge on Postgres 16, the signals game end to end
studio-hostedstudioHostedthe Studio Server on Postgres and S3 (SeaweedFS) with a real Keycloak, then the hosted Studio in Chromium and Firefox
cross-runtimenone (every change)the same canonical text, speedrun proofs and Remix worlds in Node, Chromium, WebKit and Firefox

The third tier, off the pull request path: the mutation sets on a pull request labelled full-ci, the nightly, the candidate and the release.

pr-gate reads every job's result: each must be success; the mutation jobs may be skipped where they do not run. It is the one check that sums a pull request up.

pages, on main only, after a green pr-gate: deploys the game, the Studio demo and /docs/ to GitHub Pages.

The plan (npm run ci:plan)

feature since 4.1.9

# in the engine repository, on your branch
npm run ci:plan                    # against origin/main
npm run ci:plan -- --base=<ref>
npm run ci:plan -- --files=src/engine/dom/room.ts,games/demo/rooms/garden.ts

It prints, on stderr, one line per changed file with the rule that matched and the gates it opened, then the plan; on stdout, one line of JSON ({"auditDeps":…,"node24":…,…,"all":…,"docs":…,"docsPages":[…],"pagesOnly":…}), which the plan job writes to its output. The first rule that matches a path wins, and a change runs the union of what its files ask:

A change toOpens
.github/workflows/ci.yml, tools/ci-plan.ts, package.json, the lockfile, a shared configuration (tsconfig*.json, vite.config.ts, vitest*.ts, biome.json, knip.json, requirements.txt, .nvmrc, .gitattributes), the app shell, tools/vite/everything
docs/, changes/, wiki/, any .md, LICENSE*, .claude/, .cursor/, clevercloud/, another .github/ filethe fast tier only
the Reality runtimereality, e2e, node24, windows
the painters, the music directore2e, reference, node24, windows
the PWA and the storage (boot.ts, dom/update, offline, save-store, storage)e2e, pwaFirefox, upgrade, node24, windows
any other file of src/engine/ (the core)everything
src/studio/e2e, studioHosted, node24, windows
bridge/reality, freshInstall, node24, windows (and studioHosted for its SQL layer)
games/demo/e2e, reference, node24, windows
games/reference/, games/heroes/reference, node24
games/_template/, cli/, scripts/{pack,fresh-install,external-consumer,new-game}.mjssecondGame, freshInstall, upgrade, node24, windows
studio-server/the authoring gates and studioHosted
other tools/ and scripts/the authoring gates (node24, windows, secondGame, freshInstall, upgrade, reference)
tests/node24, windows
a path no rule knowseverything

A git error runs everything too: the plan never decides that it does not need to be checked. docs is true when the change touches the documentation, its generators, or a file a wiki page rests on (a sources glob of wiki/manifest.json); docsPages names those pages, to reread. It opens no job: the fast tier runs the doc tests on every change.

Everything on a pull request: the full-ci label

On main, on a tag and on a pull request labelled full-ci, the plan is --all: every gate opens. The label also starts the mutation jobs of ci and a candidate run for information. Add the label, then push (the label applies to the next run).

pages-only runs

Applies from 4.5.0 · feature since 4.5.0

docs-publish ends by gh workflow run ci.yml --ref main -f pages-only=true. The plan is then --pages-only: no gate opens, and check, coverage and cross-runtime skip everything but the Pages build, /docs/ and the upload. A dispatched run cancels no running run: it waits. GitHub keeps one pending run per concurrency group whatever cancel-in-progress says, so a dispatch queued while a push's run on main is pending replaces it (that commit is then deployed by the dispatch without its own checks), and a later push replaces a pending dispatch.

Limit · pages-fail-closed If /docs/ cannot be assembled from the released archives (a network failure, an archive refused), the check job fails on main and Pages is not deployed: never a site without /docs/ nor with an unchecked one. A rerun of the job is the recovery.

A job failed on my pull request

  1. Read the plan's summary in the plan job (or run npm run ci:plan locally): it says why each job ran.
  1. Reproduce locally, in the engine repository, with the job's own command:

    Red jobRun locally
    checkthe failing step: npm run quality, npm run docs:truth, npm run build:game, npm run quality:baseline -- --check --dist (after npm run build:web), npm run prove:game, npm run changes -- --check
    coveragenpm run test:coverage (a test, or a floor of vite.config.ts not met)
    node-24npm run test:node under Node 24
    e2e, reference, second-gamenpm run build:game, npm run preview -- --port 5173 &, then the row's npm run e2e -- http://127.0.0.1:5173/ --game demo --prod … (E2E_BROWSER=webkit for a WebKit row)
    fresh-install, external-consumer, upgradenpm run fresh-install, npm run external-consumer, npm run upgrade-check -- --from=previous
    mutation (<set>)npm run test:mutation:core -- --set=<set> (Mutation testing)

    A failed e2e row uploads its screenshots as an artifact: read them first.

  1. Check the known flakes below before rerunning. A rerun of a red job that is not a known flake hides a bug.
  1. Fix, push, read pr-gate again.

Known flakes and their handling

SymptomWhereWhat is doneSince
WebKit's Context is stopped (or Cache API operation failed: Context is stopped) after every check passede2e:pwa and e2e on WebKit, during the teardownReported as a warning ("reported, not counted"), only on WebKit, only in that exact form and only after every check passed; any other browser error still fails. Seen on #111, #118 (13 Oct 2026) and #139 (18 Oct 2026)4.1.20 (e2e:pwa), 4.2.1 (e2e)
The Firefox pass of e2e:studio-hosted timed out at step 6d (Prepare assets)studio-hostedA real bug (a late status poll overwrote the panel), fixed in 4.3.0 (#210). If it comes back, it is a regression, not a flakefixed in 4.3.0
apt-get update hangs for many minutes on the runner's Ubuntu mirrorevery job that installs ffmpegscripts/ci-ffmpeg.sh: apt bounded to four minutes an attempt, then a static ffmpeg from GitHub's CDN (BtbN/FFmpeg-Builds); release.yml carries the same logic inline. Install steps have 20 minutes. Seen on 7 Oct 20264.1.15
toomanyrequests pulling an image from Docker Hubbridge-postgres, mutation, the nightly and the candidate's servicesImages pulled from mirror.gcr.io at the same digest. Seen on two jobs on 9 Oct 20264.1.18
npm ci or pip install fails on a network errorany jobEvery install step is tried three times, 15 s apart—
WebKit cannot navigate offline under Playwrighte2e:pwa WebKit, reality WebKit--allow-skip: that step reports "skipped", never counted as a proof; a person checks Safari offline before each release (Field passes)3.1.1

Limit · windows-not-required The windows job runs the unit suite without the files that assume POSIX; it is not a required check of the ruleset until the maintainer adds it.


Edit the source of this page · Report an error