AI Initiative logoAI Initiative
Reading room / Game Creation Studio

04. Quality gates

Maps static, browser, save-boundary, input, estate and physical-device checks to their limits. Explicitly preserves NOT RUN versus PASS.

Validation and release reference
This is the retained source text, not a new validation of its claims. Numerical results and comparative claims require the accompanying source review before public reuse.

Read the connected case study or article →

Not stated in document opening · Date not established

Provenance, authorship and editorial notes

04. Quality gates

Id

game-platform-docs-04-quality-gates

Project

game-platform

Source path

game-platform/docs/04-quality-gates.md

Source sha256

03b24c67195a5b43b458df08b7a50a70cd6d2801e3ddaf01b7029afc0277e85a

Repo head

adfb7831984715cb220e4c3c8ba55a66fc7c54fa

Date
Not recorded
Date provenance

No date near opening; git date separate

Git last change

2026-10-20T02:34:55+01:00

Author

Not stated in document opening

Document status

Validation and release reference

Summary

Maps static, browser, save-boundary, input, estate and physical-device checks to their limits. Explicitly preserves NOT RUN versus PASS.

Topics
  • games
  • evaluation
  • engineering practice
Related ids
  • game-platform-docs-02-architecture
  • game-platform-docs-11-game-factory
  • game-platform-docs-09-multi-agent-workflow
  • game-platform-proposals-learn-from-play
Evidence concerns
  • Gate coverage excludes fun, deep gameplay correctness and actual live artifact unless tested and published hashes agree.
Word count

2243

Headings
  • 04. Quality gates
  • 1. The pipeline
  • 2. Static rules S01–S19 and H01–H03 (`ci/static-checks.mjs`)
  • 3. The release gate table (all must be green for every shipped game)
  • 4. S19 baseline and expiry policy
  • 5. Browser and hub suites
  • 6. Estate sweep G1–G4 (`ci/estate-game-quality-gates.sh`)
  • 7. Real-device walk-off (movement games)
  • 8. What the gates do not prove
Publication status

Publication or peer-reviewed acceptance not established by discovery

Format

md

Private review scan
Known secret pattern hits

0

Customer or player record sets

None identified in selected narrative/design documents; no gameplay records, account databases or hand datasets copied.

Scope

Pattern scan plus document review; not a formal exhaustive privacy audit.

04. Quality gates

Every gate, what it checks, how to run it, and whether it blocks a release. Source of truth: MM/ci/ and
MM/docs/GAME-DEVELOPMENT-GUIDE.md §3, §10 (decision 36: game quality gates are core focus; releases need them
green). Commands run from MM/. Needs Node 18+, Python 3 (G-sweep helper), Google Chrome (browser suites; set
CHROME=/path off-Mac; Linux without GPU: CHROME_FLAGS="--no-sandbox --use-angle=swiftshader --enable-unsafe-swiftshader").

Blocking = a release does not ship while it is red, unless the operator records a gate exception before
publishing (05 (05-release-process.md)). Advisory = reported; fix it or record a reasoned ci/allow.json entry.
NOT RUN is never PASS.

1. The pipeline

npm run ci = node ci/pipeline.mjs (MM/ci/pipeline.mjs:1-12). It stops at the first red stage:

StageWhat runsFails on
1a gateshtml_meta.py --selftest, quality-gates-selftest.mjs, estate sweep G1–G4any non-zero exit
1 staticstatic-checks.mjs S01–S19, H01–H03any error
2 browserbrowser-checks.mjs B01–B07 (4 sizes + 20 s monkey)any error
2bhub-save-test.mjs HS1–HS6non-zero exit
2csave-boundary-test.mjs SB1–SB4any FAIL
3 packagedist/ + MANIFEST.json (sha256 per file, files changed since ci/published.json, override games)only if 1–2 green
4 reportci/reports/<stamp>/REPORT.md + report.json, incl. the publish plan

Variants: --games a,b (hub always checked), --quick (static + phone smoke; the pre-commit hook), --no-browser,
--mark-published <version> (after a publish). Not in the pipeline (run separately): rotation, pass + touch, the
hub suites in §5, the offline test, the walk-off, the override check.

2. Static rules S01–S19 and H01–H03 (ci/static-checks.mjs)

IDLevelChecks
S01errorevery inline script parses
S02errorexactly one viewport meta
S03errorexternal scripts are https from cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com (ci/pipeline.config.json)
S04errora registered service worker file exists
S05errornew THREE.WebGLRenderer is inside try
S06errora .bak save is read back, not only written
S07errorkeyboard real-time games also have touch/pointer input
S08warnreal-time games pause when hidden (heuristic source scan; a save-only handler does not count)
S09warn3D code that removes objects also disposes
S10warndevicePixelRatio is capped
S11warnno setInterval ≤ 20 ms as a game loop
S12warnno alert/confirm/prompt
S13warnno eval/new Function
S14warn / errorindex.html > 400 KB warns, > 1500 KB fails
S15warnstarts with <!doctype html>
S16error / warnkid-safe answers: every answer/target/prompt word passes games/_shared/kidsafe.js (details: node ci/kidsafe-check.mjs <game>; reasoned exceptions in ci/kidsafe-allow.json)
S17errora real <meta charset> (not a word in a comment/script) in the first 1024 bytes
S18error / warnno undeclared mirror drift: a top-level maneet-games/<game>/ copy is byte-identical or declared in ci/legacy-mirrors.json; nested copies warn
S19error / warnsave writes are versioned ({v:n,…} or a -v<n> key); baseline games warn, new games error (§4)
H01errorevery GAMES entry has games/<id>/index.html
H02warnevery game folder is in GAMES
H03errorhub scripts parse

Heuristic caution: several rules (S12, S16, S19 at least) also match text inside HTML comments. Observed
2026-10-19 while validating templates/game/index.html: a comment mentioning the dialog function and the store API
produced S12/S16/S19 findings. Keep API names out of comments rather than allow-listing.

Exceptions (ci/allow.json): { "<game>": { "<RULE>": { "reason": "…", "until": "YYYY-MM-DD" } } }. No reason →
ignored; past until → stops counting (MM/ci/lib.mjs, allowFor). Exceptions show as allowed in every report.

3. The release gate table (all must be green for every shipped game)

From GAME-DEVELOPMENT-GUIDE.md §10.4, extended with the suites used in the 19 Oct releases.

#GateCommandRequiredBlocks?
1Estate sweep G1–G4npm run ci:gates (= sh ci/estate-game-quality-gates.sh --checkout ..)exit 0yes
2Gate self-testsnpm run ci:gates-selftestall PASSyes
3Static S01–S19, H01–H03npm run ci:static0 errors; warnings listedyes (errors)
4Browser B01–B07npm run ci:browser -- <games>0 errorsyes
5Hub save HS1–HS6node ci/hub-save-test.mjs6/6yes
6Save boundary SB1–SB4node ci/save-boundary-test.mjs <games>no FAIL; N/A only with its stated reasonyes
7Rotation R1–R8node ci/rotate-test.mjs <games>no failureyes
8Pass + touchnode ci/pass-touch-test.mjs <games>no failureyes
9Full pipelinenode ci/pipeline.mjs --games <games>PIPELINE PASSEDyes
10Kid-safenode ci/kidsafe-check.mjs <game>no erroryes
11Real-device walk-off (movement games)§7 tableboth rows filledyes; headless never closes it
12Override gamesCode Editor db version checked, not only the filestated in the noteyes
13Hub suites (when the hub, _sdk or a bridge op changed)§5all pass, or a pre-recorded exceptionyes
14Display sink (D40)static scan: no dynamic text written outside the one fail-closed sink (06 (06-security.md))0 hits outside the allowlistyes
15Pack budgets (D41, games with content packs)validator + pack runtime test (08 (08-content-packs.md) §4)≤ 150 KB per pack, ≤ 300 ms load, LRU caches dispose on evictionyes

| 16 | Factory static F01–F08 (factory games) | node ci/factory-static.mjs <g> | 0 errors | yes (F01 is the D40 sink rule, stricter than S20) |

| 17 | Factory probe P01–P36 + the game's test stub (factory games) | node ci/factory-probe.mjs <g> | no failure | yes |

Gates 16–17 are new (2026-10-19, 11-game-factory.md §4). node ci/game-check.mjs <g> runs
gates 1, 3, 6–8, 10, 14 (the …or Die sink suite), 16, 17 and the pipeline in one command; --full adds 2 and the full
pipeline (4, 5, 9). For a factory game the source heuristics S07, S08, S10 and S11 read only the game file and pass
vacuously (the loop, input and DPR cap live in games/_factory/kit2.js): their runtime proof is P04, P06–P08 and P14.

Gates 14 and 15 were made blocking by the operator on 2026-10-19 but do not exist in MM/ci yet. Until they
are built, they are NOT RUN for every release, which is not PASS: a release needs either the check's result or a gate
exception recorded before publishing (05 (05-release-process.md) step 5). Building them is the first platform task
this implies (owner: CI/hub builder, to be confirmed by the coordinator).

4. S19 baseline and expiry policy

only while its entry has a non-empty owner, a non-empty remediation and an expires date (YYYY-MM-DD) not
yet passed; otherwise S19 is an error for it (MM/ci/static-checks.mjs:193-197, S19_BASE).

builder for hub games, otherwise the pack or game builder. The coordinator confirms them. On 2026-10-19 the file
still read "not named in the repo; coordinator assigns" (s19-baseline.json); replace that text with the
confirmed name before 2026-11-15.

5. Browser and hub suites

SuiteIDsChecksCommand
browser-checks.mjsB01–B07headless Chrome, game in an exact-size iframe like the hub, 4 sizes, 20 s seeded monkey (monkeySeed 42); uncaught errors, hangs, sideways scroll, console.error, dialogs, no-GPU 3D, failed loads. --selftest proves it catches a crash, a too-wide page and a blank 3D screennpm run ci:browser -- <g>; npm run ci:selftest
hub-save-test.mjsHS1–HS6 (T1–T6 in the file)real hub, two Chrome profiles as devices, fake db: legacy migration, progress reaches the doc, merge on sign-in, no blind overwrite, conflict rule, account list mergenode ci/hub-save-test.mjs
save-boundary-test.mjsSB1–SB4each SDK game in a fake hub: SB1 round-trip incl. an owner marker (a dropped marker FAILS, and since D42 that is a product defect: saves MUST preserve unknown fields); SB2 live player switch, B sees none of A's keys; SB3 fresh boot as B; SB4 XP banking (N/A: no hub XP ledger). --selftest proves it catches a leak. Boots, does not play: progress written deep in play is not exercisednode ci/save-boundary-test.mjs [--json f] <g>
rotate-test.mjsR1–R8portrait ↔ landscape at 375×667 and 390×844, title and mid-game: no sideways scroll, frame resized, nothing cut off, canvas not stretched/blurred, still fits, turning back restores, no errorsnode ci/rotate-test.mjs [--pairs …] <g>
pass-touch-test.mjsPASS + T1–T6pass/code rewards (none/vip/premium/expired/expiring) lock and unlock and render; touch at 360×640 and 390×844: no sideways scroll, targets ≥ 44 px (probed with elementFromPoint), touch-action on the play surface, no hover-only UI, a swipe does not scroll the pagenode ci/pass-touch-test.mjs [--pass] [--touch] <g>
kidsafe-check.mjsS16answer words vs the shared list; --discover finds new lists; kidsafe-selftest.mjsnode ci/kidsafe-check.mjs <g>
phase0-test.mjsA, B, C, Dbridge scopes, Fish It preset chat, Studio publishing approvalnode ci/phase0-test.mjs
hub-f1-test.mjsR, P, G, W, C, V, B, S, L, Qcross-page save race, atomic play counts, uid gifts, spend ledger, quest day clock, bridge allowlist scannode ci/hub-f1-test.mjs
hub-bundle2-test.mjsN, Q, A, T, J, S, I, Kboards, coin limits, telemetry, judge bad_reply, offline import plan, cards/questsnode ci/hub-bundle2-test.mjs
hub-ai-test.mjsN, S, B, RAI only for GOAT/hacker, every AI path through aiAllowed, fail closednode ci/hub-ai-test.mjs
hub-r4-test.mjsX, G, C, T, PGOAT bound to the Claude uid, cross-tab revocation, marker not trustednode ci/hub-r4-test.mjs
hub-xplayer-test.mjsX, C, Pcross-player save leak scenariosnode ci/hub-xplayer-test.mjs
hub-pl21-test.mjsC, F, NAI judge reply contractnode ci/hub-pl21-test.mjs
hub-avatar-test.mjs, hub-addons-test.mjsA/W/B, H/Iavatar builder + wallet; HBAY code date, no import controlnode ci/<file>
studio-save-test.mjsS1–S8Game Studio saves and shared boardnode ci/studio-save-test.mjs
offline-test.mjsA, Boffline host op coverage and playnode ci/offline-test.mjs

6. Estate sweep G1–G4 (ci/estate-game-quality-gates.sh)

Adopted from muse-alpine-libra's default-check pack v4 (channel ann-modular-20261002 seq 8179; helper
html_meta.py v3, seq 8159). Read-only; sh + python3.

GateLevelChecks
G1errora real <meta charset> on every served entry (comment/script-proof parser)
G2errorexactly one real viewport meta per entry
G3errorno undeclared canonical-vs-mirror drift (both modes)
G3bwarnnested same-name copies (arcade/, learning-lab/) that differ
G4warndoctype present

Modes: sh ci/estate-game-quality-gates.sh --checkout <maneet-games root> or … ESTATE_ROOT. A wrong or empty
root, zero inventory or a traversal error is an error, never a silent PASS. Declared-legacy mirrors
(ci/legacy-mirrors.json, decision 37) are skipped by G3 and downgraded to [LEGACY] warnings; an entry needs a
reason and an archive field.

7. Real-device walk-off (movement games)

Headless input is not proof of movement (surf-island, Oct 2026: avatar did not move under headless keys/touch while
rAF ran; recorded inconclusive, never as a game bug). Before releasing any title where an avatar moves:

DeviceBrowserInputFPSDistance movedResultWho / when
a phoneSafari/Chrometouch joystick, on-screen d-pad
a desktopChromeWASD, arrows

At least one phone and one desktop; every input the game offers; distance observed in game (crossing a landmark).
Until both rows exist the scorecard says NOT DONE and the game does not release.

Also do the 5-minute phone check on any change (GAME-DEVELOPMENT-GUIDE.md §4 step 10): starts and plays by touch;
nothing cut off either way round; hide the tab and return (paused, no time lost); close and reopen (progress kept);
restart (no double speed, no ghost game-over).

8. What the gates do not prove

Fun, correctness of deep levels, real-device feel and performance, real tab switches, sound, gameplay-written saves
(SB boots only; 13 SDK games had no save exercised on 2026-10-19, master-mind-games seq 798), XP banking, and
anything about the live artifact unless the tested file's hash equals the published one.

Continue exploring