forgecraft-mcp 1.2.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +525 -525
- package/dist/cli/help.js +44 -44
- package/dist/registry/renderer-skeletons.js +92 -92
- package/dist/shared/gs-score-logger.js +6 -6
- package/dist/tools/add-module.js +123 -123
- package/dist/tools/advice-registry.js +18 -18
- package/dist/tools/check-cascade-report.js +64 -64
- package/dist/tools/configure-mcp.d.ts +3 -0
- package/dist/tools/configure-mcp.d.ts.map +1 -1
- package/dist/tools/configure-mcp.js +10 -0
- package/dist/tools/configure-mcp.js.map +1 -1
- package/dist/tools/forgecraft-dispatch.d.ts.map +1 -1
- package/dist/tools/forgecraft-dispatch.js +3 -0
- package/dist/tools/forgecraft-dispatch.js.map +1 -1
- package/dist/tools/forgecraft-schema-params.d.ts +9 -0
- package/dist/tools/forgecraft-schema-params.d.ts.map +1 -1
- package/dist/tools/forgecraft-schema-params.js +21 -0
- package/dist/tools/forgecraft-schema-params.js.map +1 -1
- package/dist/tools/forgecraft-schema.d.ts +9 -0
- package/dist/tools/forgecraft-schema.d.ts.map +1 -1
- package/dist/tools/refresh-output.js +14 -14
- package/dist/tools/scaffold-spec-stubs.js +115 -115
- package/dist/tools/scaffold-templates.js +62 -62
- package/dist/tools/setup-artifact-writers.d.ts +30 -0
- package/dist/tools/setup-artifact-writers.d.ts.map +1 -1
- package/dist/tools/setup-artifact-writers.js +120 -8
- package/dist/tools/setup-artifact-writers.js.map +1 -1
- package/dist/tools/setup-phase1.d.ts +3 -0
- package/dist/tools/setup-phase1.d.ts.map +1 -1
- package/dist/tools/setup-phase1.js +79 -35
- package/dist/tools/setup-phase1.js.map +1 -1
- package/dist/tools/setup-phase2.d.ts +2 -0
- package/dist/tools/setup-phase2.d.ts.map +1 -1
- package/dist/tools/setup-phase2.js +10 -1
- package/dist/tools/setup-phase2.js.map +1 -1
- package/dist/tools/setup-project.d.ts +18 -0
- package/dist/tools/setup-project.d.ts.map +1 -1
- package/dist/tools/setup-project.js +77 -1
- package/dist/tools/setup-project.js.map +1 -1
- package/dist/tools/spec-parser-tags.d.ts +9 -0
- package/dist/tools/spec-parser-tags.d.ts.map +1 -1
- package/dist/tools/spec-parser-tags.js +92 -0
- package/dist/tools/spec-parser-tags.js.map +1 -1
- package/package.json +89 -86
- package/templates/analytics/instructions.yaml +37 -37
- package/templates/analytics/mcp-servers.yaml +11 -11
- package/templates/analytics/structure.yaml +25 -25
- package/templates/api/instructions.yaml +231 -231
- package/templates/api/mcp-servers.yaml +22 -13
- package/templates/api/nfr.yaml +23 -23
- package/templates/api/review.yaml +103 -103
- package/templates/api/structure.yaml +34 -34
- package/templates/api/verification.yaml +132 -132
- package/templates/cli/instructions.yaml +31 -31
- package/templates/cli/mcp-servers.yaml +11 -11
- package/templates/cli/review.yaml +53 -53
- package/templates/cli/structure.yaml +16 -16
- package/templates/data-lineage/instructions.yaml +28 -28
- package/templates/data-lineage/mcp-servers.yaml +22 -22
- package/templates/data-pipeline/instructions.yaml +84 -84
- package/templates/data-pipeline/mcp-servers.yaml +13 -13
- package/templates/data-pipeline/nfr.yaml +39 -39
- package/templates/data-pipeline/structure.yaml +23 -23
- package/templates/fintech/hooks.yaml +55 -55
- package/templates/fintech/instructions.yaml +112 -112
- package/templates/fintech/mcp-servers.yaml +13 -13
- package/templates/fintech/nfr.yaml +46 -46
- package/templates/fintech/playbook.yaml +210 -210
- package/templates/fintech/verification.yaml +239 -239
- package/templates/game/instructions.yaml +289 -289
- package/templates/game/mcp-servers.yaml +38 -38
- package/templates/game/nfr.yaml +64 -64
- package/templates/game/playbook.yaml +214 -214
- package/templates/game/review.yaml +97 -97
- package/templates/game/structure.yaml +67 -67
- package/templates/game/verification.yaml +174 -174
- package/templates/healthcare/instructions.yaml +42 -42
- package/templates/healthcare/mcp-servers.yaml +13 -13
- package/templates/healthcare/nfr.yaml +47 -47
- package/templates/hipaa/instructions.yaml +41 -41
- package/templates/hipaa/mcp-servers.yaml +13 -13
- package/templates/infra/instructions.yaml +104 -104
- package/templates/infra/mcp-servers.yaml +20 -20
- package/templates/infra/nfr.yaml +46 -46
- package/templates/infra/review.yaml +65 -65
- package/templates/infra/structure.yaml +25 -25
- package/templates/library/instructions.yaml +36 -36
- package/templates/library/mcp-servers.yaml +20 -20
- package/templates/library/review.yaml +56 -56
- package/templates/library/structure.yaml +19 -19
- package/templates/medallion-architecture/instructions.yaml +41 -41
- package/templates/medallion-architecture/mcp-servers.yaml +22 -22
- package/templates/ml/instructions.yaml +85 -85
- package/templates/ml/mcp-servers.yaml +11 -11
- package/templates/ml/nfr.yaml +39 -39
- package/templates/ml/structure.yaml +25 -25
- package/templates/ml/verification.yaml +156 -156
- package/templates/mobile/instructions.yaml +44 -44
- package/templates/mobile/mcp-servers.yaml +11 -11
- package/templates/mobile/nfr.yaml +49 -49
- package/templates/mobile/structure.yaml +27 -27
- package/templates/mobile/verification.yaml +121 -121
- package/templates/observability-xray/instructions.yaml +40 -40
- package/templates/observability-xray/mcp-servers.yaml +15 -15
- package/templates/realtime/instructions.yaml +42 -42
- package/templates/realtime/mcp-servers.yaml +13 -13
- package/templates/soc2/instructions.yaml +41 -41
- package/templates/soc2/mcp-servers.yaml +24 -24
- package/templates/social/instructions.yaml +43 -43
- package/templates/social/mcp-servers.yaml +24 -24
- package/templates/state-machine/instructions.yaml +42 -42
- package/templates/state-machine/mcp-servers.yaml +11 -11
- package/templates/tools-registry.yaml +164 -164
- package/templates/universal/hooks.yaml +531 -531
- package/templates/universal/instructions.yaml +1692 -1692
- package/templates/universal/mcp-servers.yaml +50 -50
- package/templates/universal/nfr.yaml +197 -197
- package/templates/universal/reference.yaml +326 -326
- package/templates/universal/review.yaml +204 -204
- package/templates/universal/skills.yaml +262 -262
- package/templates/universal/structure.yaml +67 -67
- package/templates/universal/verification.yaml +416 -416
- package/templates/web-react/hooks.yaml +44 -44
- package/templates/web-react/instructions.yaml +207 -207
- package/templates/web-react/mcp-servers.yaml +20 -20
- package/templates/web-react/nfr.yaml +27 -27
- package/templates/web-react/review.yaml +94 -94
- package/templates/web-react/structure.yaml +46 -46
- package/templates/web-react/verification.yaml +126 -126
- package/templates/web-static/instructions.yaml +115 -115
- package/templates/web-static/mcp-servers.yaml +20 -20
- package/templates/web3/instructions.yaml +44 -44
- package/templates/web3/mcp-servers.yaml +11 -11
- package/templates/web3/verification.yaml +159 -159
- package/templates/zero-trust/instructions.yaml +41 -41
- package/templates/zero-trust/mcp-servers.yaml +15 -15
|
@@ -1,289 +1,289 @@
|
|
|
1
|
-
tag: GAME
|
|
2
|
-
section: instructions
|
|
3
|
-
blocks:
|
|
4
|
-
- id: game-loop-timing
|
|
5
|
-
tier: recommended
|
|
6
|
-
title: "Game Loop & Frame Timing"
|
|
7
|
-
content: |
|
|
8
|
-
## Game Loop & Frame Timing
|
|
9
|
-
|
|
10
|
-
- Use a fixed timestep for game logic updates (e.g., 60 Hz / 16.67ms) decoupled from the render frame rate. This ensures deterministic simulation regardless of display refresh rate.
|
|
11
|
-
- Accumulate elapsed time and consume it in fixed-size steps. Interpolate visual state between the previous and current simulation state for smooth rendering on variable-rate displays.
|
|
12
|
-
- Never tie game logic to `requestAnimationFrame` or vsync directly. Use `deltaTime` from a fixed update loop for physics, AI, and gameplay—use `requestAnimationFrame` only for rendering.
|
|
13
|
-
- Profile frame budgets rigorously. At 60 FPS, the total frame budget is ~16ms. Allocate budgets per system (e.g., physics 3ms, AI 2ms, rendering 8ms, overhead 3ms) and alert on overruns.
|
|
14
|
-
- Implement a frame rate limiter and graceful degradation: reduce visual quality (particle counts, draw distance, shadow resolution) before dropping simulation fidelity.
|
|
15
|
-
- Use a time scale factor to support pause, slow motion, and fast-forward without modifying core loop logic.
|
|
16
|
-
|
|
17
|
-
- id: ecs-architecture
|
|
18
|
-
tier: recommended
|
|
19
|
-
title: "Entity-Component-System Architecture"
|
|
20
|
-
content: |
|
|
21
|
-
## Entity-Component-System (ECS) Architecture
|
|
22
|
-
|
|
23
|
-
- Separate identity (Entity), data (Component), and behavior (System). Entities are lightweight IDs; components are plain data structs; systems operate on sets of components.
|
|
24
|
-
- Organize components for data locality: store components of the same type in contiguous arrays (Struct of Arrays) to maximize CPU cache efficiency during system iteration.
|
|
25
|
-
- Systems should have no state of their own. They query the world for entities matching a component archetype and process them in a tight loop.
|
|
26
|
-
- Use component composition over inheritance. A `PlayerCharacter` is an entity with `Position`, `Velocity`, `Sprite`, `Health`, and `Input` components—not a deep class hierarchy.
|
|
27
|
-
- Implement an event bus or command buffer for deferred entity creation/destruction. Never modify the entity list while iterating over it.
|
|
28
|
-
- Tag components (zero-size marker components) are useful for filtering: `IsPlayer`, `IsEnemy`, `IsProjectile` let systems query efficiently without data overhead.
|
|
29
|
-
|
|
30
|
-
- id: asset-input-management
|
|
31
|
-
tier: recommended
|
|
32
|
-
title: "Asset & Input Management"
|
|
33
|
-
content: |
|
|
34
|
-
## Asset Management & Input Handling
|
|
35
|
-
|
|
36
|
-
- Preload and cache assets (textures, audio, meshes, fonts) during loading screens. Use an asset manifest to declare all required assets and track loading progress.
|
|
37
|
-
- Implement reference counting or handle-based asset management. Share loaded assets across entities and unload them only when no references remain.
|
|
38
|
-
- Use asset atlases (sprite sheets, texture atlases) to reduce draw calls. Batch rendering by texture to minimize GPU state changes.
|
|
39
|
-
- Abstract input handling behind an action mapping layer. Map physical inputs (keyboard keys, gamepad buttons, touch gestures) to logical actions (`Jump`, `Attack`, `MoveLeft`) defined in a rebindable configuration.
|
|
40
|
-
- Support input buffering: queue player inputs for a short window (2-5 frames) so that slightly early inputs still register. This dramatically improves perceived responsiveness.
|
|
41
|
-
- Handle input device hotplugging gracefully. Detect controller connect/disconnect events and update the UI prompts (keyboard glyphs vs. gamepad glyphs) accordingly.
|
|
42
|
-
- Implement an input replay/recording system for debugging and automated testing. Serialize input streams with frame timestamps for deterministic playback.
|
|
43
|
-
|
|
44
|
-
- id: phaser3-setup
|
|
45
|
-
tier: recommended
|
|
46
|
-
title: "Phaser 3 Project Setup"
|
|
47
|
-
content: |
|
|
48
|
-
## Phaser 3 Project Setup
|
|
49
|
-
|
|
50
|
-
### Bootstrap
|
|
51
|
-
- Initialize Phaser with an explicit `GameConfig`: set `type: Phaser.AUTO` so Phaser chooses WebGL then falls back to Canvas. Declare `width`, `height`, `scale.mode`, `physics`, and an explicit `scene` array.
|
|
52
|
-
- Use Vite (or Webpack 5) as the bundler. Configure asset handling for textures, audio, and tilemaps via `assetsDir` and `publicDir` so raw files remain accessible during development without being inlined.
|
|
53
|
-
- Enable `pixelArt: true` and `antialias: false` for pixel-art games to prevent bilinear blurring. Use `roundPixels: true` to snap sprites to whole pixels.
|
|
54
|
-
|
|
55
|
-
### Scene Architecture
|
|
56
|
-
- Divide the game into discrete Scenes: `BootScene` (config/registry), `PreloadScene` (asset loading), `MenuScene`, `GameScene`, `UIScene` (HUD overlay), `PauseScene`, `GameOverScene`.
|
|
57
|
-
- Run the HUD as a parallel Scene (`this.scene.launch('UIScene')`) so it renders on top without being destroyed when the game scene restarts.
|
|
58
|
-
- Avoid god-object Scenes. Extract game logic into plain TypeScript systems/services injected via the Phaser registry (`this.registry.set('myService', instance)`).
|
|
59
|
-
- Use Phaser's `EventEmitter` (`this.events`) for intra-scene communication. Use `this.game.events` for cross-scene messaging. Never let scenes reference each other's instances directly.
|
|
60
|
-
|
|
61
|
-
### Physics
|
|
62
|
-
- Prefer Arcade Physics for simple axis-aligned collision. Use Matter.js integration only when complex polygon shapes are required — it has a significant overhead.
|
|
63
|
-
- Define physics bodies on `create()`, not inside `update()`. Destroying and re-creating bodies every frame is expensive.
|
|
64
|
-
- Use `setCollideWorldBounds(true)` and `setMaxVelocity()` to prevent tunneling and runaway acceleration.
|
|
65
|
-
|
|
66
|
-
### Asset Pipeline
|
|
67
|
-
- Use `this.load.atlas()` for sprite sheets; minimise the number of texture atlases to 1–2 per scene to keep GPU texture switches low.
|
|
68
|
-
- Pack atlases with TexturePacker or Shoebox. Export as JSON (Array or Hash format compatible with Phaser 3).
|
|
69
|
-
- Audio: load both `.ogg` and `.mp3` variants; Phaser picks the supported format at runtime. Use `this.sound.add()` and pool frequently played SFX.
|
|
70
|
-
|
|
71
|
-
- id: pixijs-setup
|
|
72
|
-
tier: recommended
|
|
73
|
-
title: "PixiJS Project Setup"
|
|
74
|
-
content: |
|
|
75
|
-
## PixiJS Project Setup
|
|
76
|
-
|
|
77
|
-
### Renderer Initialization
|
|
78
|
-
- Use `Application.init()` (PixiJS v8 async API) with an explicit config: `{ width, height, backgroundColor, antialias, resolution: window.devicePixelRatio }`. Append `app.canvas` to the DOM rather than using auto-append to keep layout control.
|
|
79
|
-
- For pixel art, set `antialias: false` and `SCALE_MODE.NEAREST` via `TextureStyle.defaultOptions.scaleMode`.
|
|
80
|
-
- Prefer `ResizePlugin` with `resizeTo: window` for full-viewport games. For fixed-resolution games, scale a `Container` root and letterbox.
|
|
81
|
-
|
|
82
|
-
### Scene Graph
|
|
83
|
-
- Structure the display list as layered containers: `worldContainer` (tiles + entities), `fxContainer` (particles, lights), `uiContainer` (HUD, menus). Add all three to `app.stage` in order.
|
|
84
|
-
- Wrap game entities in typed display-object classes that extend `Container`. Avoid putting game logic directly in display objects — use a separate system that operates on them.
|
|
85
|
-
- Use `Container.sortableChildren = true` and manage `zIndex` only where needed. Unnecessary sorting is expensive.
|
|
86
|
-
|
|
87
|
-
### Rendering Performance
|
|
88
|
-
- Batch identical sprites by sharing a `Texture` or `SpriteSheet`. PixiJS auto-batches draw calls when textures are identical — atlas packing is essential.
|
|
89
|
-
- Use `ParticleContainer` for large sprite populations (e.g., bullets, sparks). It bypasses the general-purpose display-object pipeline for maximum throughput.
|
|
90
|
-
- Use `RenderTexture` for expensive static backgrounds and composited UI panels — render once, display as a sprite each frame.
|
|
91
|
-
- Profile with the PixiJS DevTools browser extension. Watch `drawCalls` per frame — target < 50 for smooth 60 FPS on mid-range mobile.
|
|
92
|
-
|
|
93
|
-
### Asset Loading
|
|
94
|
-
- Centralise all asset definitions in a manifest (`assets.ts`) and load via `Assets.loadBundle()`. Use bundles to scope assets per scene and unload on scene exit.
|
|
95
|
-
- Use `Assets.backgroundLoadBundle()` to stream assets while showing a menu. Avoid blocking the main thread during level transitions.
|
|
96
|
-
|
|
97
|
-
- id: threejs-webgl-setup
|
|
98
|
-
tier: recommended
|
|
99
|
-
title: "Three.js / WebGL Setup"
|
|
100
|
-
content: |
|
|
101
|
-
## Three.js / WebGL Setup
|
|
102
|
-
|
|
103
|
-
### Renderer & Scene Bootstrap
|
|
104
|
-
- Create a single `WebGLRenderer` with `{ antialias: true, powerPreference: 'high-performance' }`. Reuse it across scenes — creating multiple renderers leaks GPU contexts (browser limit is typically 8–16).
|
|
105
|
-
- Set `renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))` to cap resolution on high-DPI displays and avoid bandwidth-bound scenarios.
|
|
106
|
-
- Enable shadow maps only when needed: `renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap`. Limit shadow-casting lights to 1–2 per scene.
|
|
107
|
-
- Use `renderer.setAnimationLoop(tick)` for the render loop — it integrates with XR and handles tab visibility correctly, unlike a manual `requestAnimationFrame`.
|
|
108
|
-
|
|
109
|
-
### Scene & Camera
|
|
110
|
-
- Keep one active `Scene` object per game level. On level transition, dispose all geometries, materials, and textures in the outgoing scene before replacing it.
|
|
111
|
-
- Use `PerspectiveCamera` for 3D games. Tune `near`/`far` clipping planes tightly — a near/far ratio > 10,000 causes z-fighting. A ratio of 1/1000 is a safe starting-point.
|
|
112
|
-
- For 2.5D or UI overlays, maintain a separate orthographic camera and render pass.
|
|
113
|
-
|
|
114
|
-
### Geometry & Material Management
|
|
115
|
-
- Reuse `Geometry` and `Material` instances across `Mesh` objects via `instancedMesh` when drawing > 100 identical objects. `InstancedMesh` reduces draw calls from N to 1.
|
|
116
|
-
- Dispose resources explicitly: `geometry.dispose()`, `material.dispose()`, `texture.dispose()`. Three.js does not garbage-collect GPU resources automatically.
|
|
117
|
-
- Use `GLTFLoader` for 3D assets. Compress assets with `gltf-transform` using Draco (geometry) and KTX2/Basis (textures) — typically 70–90% smaller than raw GLTF.
|
|
118
|
-
- Share a single `TextureLoader` (or `LoadingManager`) across the app. Cache loaded textures in a `Map<string, THREE.Texture>` keyed by URL.
|
|
119
|
-
|
|
120
|
-
### Lighting & Post-Processing
|
|
121
|
-
- Prefer baked lightmaps for static environments to eliminate costly real-time light calculations.
|
|
122
|
-
- Use `@react-three/postprocessing` (r3f) or `three/examples/jsm/postprocessing/EffectComposer` for bloom, SSAO, and tonemapping. Chain passes on a single composer to minimize render targets.
|
|
123
|
-
- HDR environment maps via `RGBELoader` + `PMREMGenerator` provide physically accurate image-based lighting at low runtime cost.
|
|
124
|
-
|
|
125
|
-
### Raw Canvas / WebGL (No Framework)
|
|
126
|
-
- When authoring raw WebGL, manage shader programs as ES module string literals compiled once and cached in a `Map<string, WebGLProgram>`.
|
|
127
|
-
- Use Vertex Array Objects (VAOs) to encapsulate buffer bindings. Bind the VAO at draw time, not per-attribute setup.
|
|
128
|
-
- Use `ANGLE_instanced_arrays` (WebGL 1) or native instancing (WebGL 2) for repeated geometry.
|
|
129
|
-
- Consider `wgpu`/WebGPU for new projects targeting modern browsers — it exposes compute shaders and reduces CPU-side overhead significantly.
|
|
130
|
-
|
|
131
|
-
- id: web-game-performance
|
|
132
|
-
tier: recommended
|
|
133
|
-
title: "Web Game Performance"
|
|
134
|
-
content: |
|
|
135
|
-
## Web Game Performance
|
|
136
|
-
|
|
137
|
-
### JavaScript Thread Budget
|
|
138
|
-
- Target < 6ms of JavaScript CPU time per 16ms frame. Profile with Chrome DevTools Performance panel. The flame chart pinpoints expensive `update()` call stacks.
|
|
139
|
-
- Move physics calculations, pathfinding, and procedural generation to a Web Worker. Communicate via `postMessage` with `SharedArrayBuffer` or `Transferable` objects to avoid serialisation overhead.
|
|
140
|
-
- Avoid per-frame allocations: object pools for bullets, particles, and audio nodes. GC pauses at inopportune moments cause visible stutter.
|
|
141
|
-
|
|
142
|
-
### Memory
|
|
143
|
-
- Audit GPU memory with `WEBGL_debug_renderer_info` + `getParameter`. GPU memory exhaustion silently degrades performance before crashing.
|
|
144
|
-
- Unload atlas textures on scene exit. GL textures live on the GPU until explicitly deleted — `gl.deleteTexture(tex)` or the framework's `.destroy()` / `.dispose()` equivalents.
|
|
145
|
-
- Set a target: < 256 MB JS heap for mobile web games. Use `performance.measureUserAgentSpecificMemory()` in development to track heap growth.
|
|
146
|
-
|
|
147
|
-
### Asset Delivery
|
|
148
|
-
- Serve assets from a CDN with immutable cache headers (`Cache-Control: public, max-age=31536000, immutable`) using content-hashed filenames.
|
|
149
|
-
- Use the Compression Streams API or serve assets pre-compressed (Brotli > gzip). Atlas textures in KTX2 format (Basis Universal) reduce GPU upload time and GPU memory by 4–8× over PNG.
|
|
150
|
-
- Prefetch assets for the next level during gameplay idle periods with `fetch()` + `Cache API` rather than embedding loading screens in critical path.
|
|
151
|
-
|
|
152
|
-
### Mobile Web
|
|
153
|
-
- Test on real mid-range Android (Snapdragon 665 class) — not just desktop Chrome DevTools emulation. GPU drivers differ significantly.
|
|
154
|
-
- Reduce canvas resolution on low-power devices: detect `navigator.hardwareConcurrency < 4` or battery level via Battery API as a proxy for device capability.
|
|
155
|
-
- Touch input: use `pointer` events (`pointerdown`, `pointermove`, `pointerup`) instead of separate mouse/touch event handlers. Add `touch-action: none` to the canvas CSS to prevent scroll hijacking.
|
|
156
|
-
|
|
157
|
-
- id: game-testing
|
|
158
|
-
tier: recommended
|
|
159
|
-
title: "Game & Interactive Testing Patterns"
|
|
160
|
-
content: |
|
|
161
|
-
## Game & Interactive Testing Patterns
|
|
162
|
-
|
|
163
|
-
### Expose-Store-to-Window (E2E Layer)
|
|
164
|
-
In the test environment, expose the application state store to `window`:
|
|
165
|
-
```typescript
|
|
166
|
-
if (import.meta.env.TEST) window.__STORE__ = store;
|
|
167
|
-
```
|
|
168
|
-
The Playwright driver then asserts not only what renders but what the application believes is true — the store's internal state. Catches failures that render correctly but corrupt state silently. Required on all game and real-time UI projects.
|
|
169
|
-
|
|
170
|
-
### Vertical Chain Test Pattern
|
|
171
|
-
A single UI action triggers Playwright, which sequentially queries:
|
|
172
|
-
1. Service layer response (API or game service object)
|
|
173
|
-
2. Store / state manager internal state
|
|
174
|
-
3. Affected database records or in-memory structures
|
|
175
|
-
4. Rendered UI output
|
|
176
|
-
|
|
177
|
-
This is not a unit test and not a flow test — it is a chain verification. One trigger, observed at every boundary it crosses. Specify in CLAUDE.md which critical flows (combat resolution, save/load, transaction) receive this treatment.
|
|
178
|
-
|
|
179
|
-
### Generative Asset Quality Gates — Visual
|
|
180
|
-
When visual assets are produced by AI pipelines, assert before bundle inclusion:
|
|
181
|
-
|
|
182
|
-
| Check | Mechanism | Default Threshold |
|
|
183
|
-
|---|---|---|
|
|
184
|
-
| **Geometric orientation** | PCA on sprite silhouette pixels; extract principal axis angle from vertical | ≤ 15° |
|
|
185
|
-
| **Pixel coverage ratio** | Sprite fills acceptable proportion of bounding box | ≥ 0.40 |
|
|
186
|
-
| **Symmetry** | Pixel-level left/right half similarity after horizontal flip; normalized 0–1 | ≥ 0.80 |
|
|
187
|
-
| **Background cleanliness** | Non-background pixel ratio in border region | ≤ 0.30 |
|
|
188
|
-
| **Color palette compliance** | Generated asset matches scene-defined palette range (histogram comparison) | Per spec |
|
|
189
|
-
| **Alpha mask correctness** | No opaque pixels in defined transparent zones | Zero violations |
|
|
190
|
-
| **Dimension compliance** | Output matches required sprite sheet grid dimensions exactly | ± 0 px |
|
|
191
|
-
|
|
192
|
-
Assets failing any gate trigger regeneration (up to N retries). Accepted assets are logged to a preservation list so re-runs skip them. Rejection thresholds must be stated in the spec, not left implicit.
|
|
193
|
-
|
|
194
|
-
### Generative Asset Quality Gates — Audio
|
|
195
|
-
When audio assets are produced by AI models, assert before bundle inclusion:
|
|
196
|
-
|
|
197
|
-
| Check | Mechanism |
|
|
198
|
-
|---|---|
|
|
199
|
-
| **Loudness normalization** | LUFS target compliance (e.g., -14 LUFS for game audio); measured with ffmpeg-normalize or pyloudnorm |
|
|
200
|
-
| **Frequency profile** | No asset competes with dialogue in 2–4 kHz presence range unless specified |
|
|
201
|
-
| **Tempo consistency** | BPM within ± tolerance for scene-matched music; measured with librosa or essentia |
|
|
202
|
-
| **Silence detection** | Leading/trailing silence below threshold; catches generation artifacts |
|
|
203
|
-
| **Duration compliance** | Generated clip matches required length ± tolerance |
|
|
204
|
-
| **Clipping detection** | No samples at 0 dBFS unless intentionally distorted |
|
|
205
|
-
|
|
206
|
-
### MCP-Mediated Visual / Scene Inspection
|
|
207
|
-
For projects with an MCP server exposing instrumented game state: add a test layer where a language model is given a scene description and acceptance criteria, loads the live state through the MCP interface, and reports whether the scene satisfies them.
|
|
208
|
-
|
|
209
|
-
This is not a replacement for the structured suite — it closes the gap between what a pixel diff catches and what a brief human review would flag. Schedule as a pre-release gate, not a per-commit trigger. Document the scene invariants and acceptance criteria in the spec before implementing the gate.
|
|
210
|
-
|
|
211
|
-
- id: game-smoke-testing
|
|
212
|
-
tier: recommended
|
|
213
|
-
title: "Game Smoke Testing (Three-Tier)"
|
|
214
|
-
content: |
|
|
215
|
-
## Game Smoke Testing
|
|
216
|
-
|
|
217
|
-
### Tier 1 — Headless Unit (Pre-Commit, No Browser)
|
|
218
|
-
Pure game logic with no renderer dependency:
|
|
219
|
-
- **State machines**: all valid transitions, all invalid-transition rejections
|
|
220
|
-
- **Physics / collision**: core formulas, edge cases (zero velocity, exact boundary contact)
|
|
221
|
-
- **Scoring and economy**: accumulation, overflow bounds, negative prevention
|
|
222
|
-
- **Save / load**: round-trip serialization of game state loses no data
|
|
223
|
-
|
|
224
|
-
Tools: Vitest / Jest / pytest. Fast — runs pre-commit.
|
|
225
|
-
|
|
226
|
-
### Tier 2 — Browser Smoke (Post-Deploy, Playwright)
|
|
227
|
-
Confirms the game bootstraps correctly in a real browser:
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
// tests/smoke/game.smoke.ts
|
|
231
|
-
import { test, expect } from '@playwright/test';
|
|
232
|
-
|
|
233
|
-
test('@smoke canvas is visible and sized', async ({ page }) => {
|
|
234
|
-
await page.goto('/');
|
|
235
|
-
const canvas = page.locator('canvas');
|
|
236
|
-
await expect(canvas).toBeVisible({ timeout: 10_000 });
|
|
237
|
-
const box = await canvas.boundingBox();
|
|
238
|
-
expect(box?.width).toBeGreaterThan(0);
|
|
239
|
-
expect(box?.height).toBeGreaterThan(0);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('@smoke no JS errors during game init', async ({ page }) => {
|
|
243
|
-
const errors: string[] = [];
|
|
244
|
-
page.on('pageerror', e => errors.push(e.message));
|
|
245
|
-
await page.goto('/');
|
|
246
|
-
await page.waitForTimeout(3_000); // allow game boot
|
|
247
|
-
expect(errors).toHaveLength(0);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
test('@smoke WebGL context created', async ({ page }) => {
|
|
251
|
-
await page.goto('/');
|
|
252
|
-
const hasWebGL = await page.evaluate(() =>
|
|
253
|
-
!!document.querySelector('canvas')?.getContext('webgl2') ||
|
|
254
|
-
!!document.querySelector('canvas')?.getContext('webgl')
|
|
255
|
-
);
|
|
256
|
-
expect(hasWebGL).toBe(true);
|
|
257
|
-
});
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Tier 3 — Performance Smoke (FPS Floor, Playwright + CDP)
|
|
261
|
-
Assert minimum sustained frame rate after game warms up:
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
// tests/smoke/game-perf.smoke.ts
|
|
265
|
-
import { test, expect, chromium } from '@playwright/test';
|
|
266
|
-
|
|
267
|
-
const FPS_FLOOR = Number(process.env['GAME_SMOKE_FPS_FLOOR'] ?? '30');
|
|
268
|
-
|
|
269
|
-
test('@smoke FPS above floor after 5s', async () => {
|
|
270
|
-
const browser = await chromium.launch();
|
|
271
|
-
const context = await browser.newContext();
|
|
272
|
-
const page = await context.newPage();
|
|
273
|
-
const cdp = await context.newCDPSession(page);
|
|
274
|
-
|
|
275
|
-
await page.goto('/');
|
|
276
|
-
await page.waitForTimeout(5_000); // warm-up
|
|
277
|
-
const { metrics } = await cdp.send('Performance.getMetrics');
|
|
278
|
-
const frames = metrics.find(m => m.name === 'Frames');
|
|
279
|
-
const duration = metrics.find(m => m.name === 'TaskDuration');
|
|
280
|
-
if (frames && duration && duration.value > 0) {
|
|
281
|
-
const fps = frames.value / duration.value;
|
|
282
|
-
expect(fps).toBeGreaterThanOrEqual(FPS_FLOOR);
|
|
283
|
-
}
|
|
284
|
-
await browser.close();
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
The FPS floor (default 30) must be declared in `spec.md` under Performance Requirements.
|
|
289
|
-
In CI, use `--use-gl=swiftshader` for consistent software-rendering results across agents.
|
|
1
|
+
tag: GAME
|
|
2
|
+
section: instructions
|
|
3
|
+
blocks:
|
|
4
|
+
- id: game-loop-timing
|
|
5
|
+
tier: recommended
|
|
6
|
+
title: "Game Loop & Frame Timing"
|
|
7
|
+
content: |
|
|
8
|
+
## Game Loop & Frame Timing
|
|
9
|
+
|
|
10
|
+
- Use a fixed timestep for game logic updates (e.g., 60 Hz / 16.67ms) decoupled from the render frame rate. This ensures deterministic simulation regardless of display refresh rate.
|
|
11
|
+
- Accumulate elapsed time and consume it in fixed-size steps. Interpolate visual state between the previous and current simulation state for smooth rendering on variable-rate displays.
|
|
12
|
+
- Never tie game logic to `requestAnimationFrame` or vsync directly. Use `deltaTime` from a fixed update loop for physics, AI, and gameplay—use `requestAnimationFrame` only for rendering.
|
|
13
|
+
- Profile frame budgets rigorously. At 60 FPS, the total frame budget is ~16ms. Allocate budgets per system (e.g., physics 3ms, AI 2ms, rendering 8ms, overhead 3ms) and alert on overruns.
|
|
14
|
+
- Implement a frame rate limiter and graceful degradation: reduce visual quality (particle counts, draw distance, shadow resolution) before dropping simulation fidelity.
|
|
15
|
+
- Use a time scale factor to support pause, slow motion, and fast-forward without modifying core loop logic.
|
|
16
|
+
|
|
17
|
+
- id: ecs-architecture
|
|
18
|
+
tier: recommended
|
|
19
|
+
title: "Entity-Component-System Architecture"
|
|
20
|
+
content: |
|
|
21
|
+
## Entity-Component-System (ECS) Architecture
|
|
22
|
+
|
|
23
|
+
- Separate identity (Entity), data (Component), and behavior (System). Entities are lightweight IDs; components are plain data structs; systems operate on sets of components.
|
|
24
|
+
- Organize components for data locality: store components of the same type in contiguous arrays (Struct of Arrays) to maximize CPU cache efficiency during system iteration.
|
|
25
|
+
- Systems should have no state of their own. They query the world for entities matching a component archetype and process them in a tight loop.
|
|
26
|
+
- Use component composition over inheritance. A `PlayerCharacter` is an entity with `Position`, `Velocity`, `Sprite`, `Health`, and `Input` components—not a deep class hierarchy.
|
|
27
|
+
- Implement an event bus or command buffer for deferred entity creation/destruction. Never modify the entity list while iterating over it.
|
|
28
|
+
- Tag components (zero-size marker components) are useful for filtering: `IsPlayer`, `IsEnemy`, `IsProjectile` let systems query efficiently without data overhead.
|
|
29
|
+
|
|
30
|
+
- id: asset-input-management
|
|
31
|
+
tier: recommended
|
|
32
|
+
title: "Asset & Input Management"
|
|
33
|
+
content: |
|
|
34
|
+
## Asset Management & Input Handling
|
|
35
|
+
|
|
36
|
+
- Preload and cache assets (textures, audio, meshes, fonts) during loading screens. Use an asset manifest to declare all required assets and track loading progress.
|
|
37
|
+
- Implement reference counting or handle-based asset management. Share loaded assets across entities and unload them only when no references remain.
|
|
38
|
+
- Use asset atlases (sprite sheets, texture atlases) to reduce draw calls. Batch rendering by texture to minimize GPU state changes.
|
|
39
|
+
- Abstract input handling behind an action mapping layer. Map physical inputs (keyboard keys, gamepad buttons, touch gestures) to logical actions (`Jump`, `Attack`, `MoveLeft`) defined in a rebindable configuration.
|
|
40
|
+
- Support input buffering: queue player inputs for a short window (2-5 frames) so that slightly early inputs still register. This dramatically improves perceived responsiveness.
|
|
41
|
+
- Handle input device hotplugging gracefully. Detect controller connect/disconnect events and update the UI prompts (keyboard glyphs vs. gamepad glyphs) accordingly.
|
|
42
|
+
- Implement an input replay/recording system for debugging and automated testing. Serialize input streams with frame timestamps for deterministic playback.
|
|
43
|
+
|
|
44
|
+
- id: phaser3-setup
|
|
45
|
+
tier: recommended
|
|
46
|
+
title: "Phaser 3 Project Setup"
|
|
47
|
+
content: |
|
|
48
|
+
## Phaser 3 Project Setup
|
|
49
|
+
|
|
50
|
+
### Bootstrap
|
|
51
|
+
- Initialize Phaser with an explicit `GameConfig`: set `type: Phaser.AUTO` so Phaser chooses WebGL then falls back to Canvas. Declare `width`, `height`, `scale.mode`, `physics`, and an explicit `scene` array.
|
|
52
|
+
- Use Vite (or Webpack 5) as the bundler. Configure asset handling for textures, audio, and tilemaps via `assetsDir` and `publicDir` so raw files remain accessible during development without being inlined.
|
|
53
|
+
- Enable `pixelArt: true` and `antialias: false` for pixel-art games to prevent bilinear blurring. Use `roundPixels: true` to snap sprites to whole pixels.
|
|
54
|
+
|
|
55
|
+
### Scene Architecture
|
|
56
|
+
- Divide the game into discrete Scenes: `BootScene` (config/registry), `PreloadScene` (asset loading), `MenuScene`, `GameScene`, `UIScene` (HUD overlay), `PauseScene`, `GameOverScene`.
|
|
57
|
+
- Run the HUD as a parallel Scene (`this.scene.launch('UIScene')`) so it renders on top without being destroyed when the game scene restarts.
|
|
58
|
+
- Avoid god-object Scenes. Extract game logic into plain TypeScript systems/services injected via the Phaser registry (`this.registry.set('myService', instance)`).
|
|
59
|
+
- Use Phaser's `EventEmitter` (`this.events`) for intra-scene communication. Use `this.game.events` for cross-scene messaging. Never let scenes reference each other's instances directly.
|
|
60
|
+
|
|
61
|
+
### Physics
|
|
62
|
+
- Prefer Arcade Physics for simple axis-aligned collision. Use Matter.js integration only when complex polygon shapes are required — it has a significant overhead.
|
|
63
|
+
- Define physics bodies on `create()`, not inside `update()`. Destroying and re-creating bodies every frame is expensive.
|
|
64
|
+
- Use `setCollideWorldBounds(true)` and `setMaxVelocity()` to prevent tunneling and runaway acceleration.
|
|
65
|
+
|
|
66
|
+
### Asset Pipeline
|
|
67
|
+
- Use `this.load.atlas()` for sprite sheets; minimise the number of texture atlases to 1–2 per scene to keep GPU texture switches low.
|
|
68
|
+
- Pack atlases with TexturePacker or Shoebox. Export as JSON (Array or Hash format compatible with Phaser 3).
|
|
69
|
+
- Audio: load both `.ogg` and `.mp3` variants; Phaser picks the supported format at runtime. Use `this.sound.add()` and pool frequently played SFX.
|
|
70
|
+
|
|
71
|
+
- id: pixijs-setup
|
|
72
|
+
tier: recommended
|
|
73
|
+
title: "PixiJS Project Setup"
|
|
74
|
+
content: |
|
|
75
|
+
## PixiJS Project Setup
|
|
76
|
+
|
|
77
|
+
### Renderer Initialization
|
|
78
|
+
- Use `Application.init()` (PixiJS v8 async API) with an explicit config: `{ width, height, backgroundColor, antialias, resolution: window.devicePixelRatio }`. Append `app.canvas` to the DOM rather than using auto-append to keep layout control.
|
|
79
|
+
- For pixel art, set `antialias: false` and `SCALE_MODE.NEAREST` via `TextureStyle.defaultOptions.scaleMode`.
|
|
80
|
+
- Prefer `ResizePlugin` with `resizeTo: window` for full-viewport games. For fixed-resolution games, scale a `Container` root and letterbox.
|
|
81
|
+
|
|
82
|
+
### Scene Graph
|
|
83
|
+
- Structure the display list as layered containers: `worldContainer` (tiles + entities), `fxContainer` (particles, lights), `uiContainer` (HUD, menus). Add all three to `app.stage` in order.
|
|
84
|
+
- Wrap game entities in typed display-object classes that extend `Container`. Avoid putting game logic directly in display objects — use a separate system that operates on them.
|
|
85
|
+
- Use `Container.sortableChildren = true` and manage `zIndex` only where needed. Unnecessary sorting is expensive.
|
|
86
|
+
|
|
87
|
+
### Rendering Performance
|
|
88
|
+
- Batch identical sprites by sharing a `Texture` or `SpriteSheet`. PixiJS auto-batches draw calls when textures are identical — atlas packing is essential.
|
|
89
|
+
- Use `ParticleContainer` for large sprite populations (e.g., bullets, sparks). It bypasses the general-purpose display-object pipeline for maximum throughput.
|
|
90
|
+
- Use `RenderTexture` for expensive static backgrounds and composited UI panels — render once, display as a sprite each frame.
|
|
91
|
+
- Profile with the PixiJS DevTools browser extension. Watch `drawCalls` per frame — target < 50 for smooth 60 FPS on mid-range mobile.
|
|
92
|
+
|
|
93
|
+
### Asset Loading
|
|
94
|
+
- Centralise all asset definitions in a manifest (`assets.ts`) and load via `Assets.loadBundle()`. Use bundles to scope assets per scene and unload on scene exit.
|
|
95
|
+
- Use `Assets.backgroundLoadBundle()` to stream assets while showing a menu. Avoid blocking the main thread during level transitions.
|
|
96
|
+
|
|
97
|
+
- id: threejs-webgl-setup
|
|
98
|
+
tier: recommended
|
|
99
|
+
title: "Three.js / WebGL Setup"
|
|
100
|
+
content: |
|
|
101
|
+
## Three.js / WebGL Setup
|
|
102
|
+
|
|
103
|
+
### Renderer & Scene Bootstrap
|
|
104
|
+
- Create a single `WebGLRenderer` with `{ antialias: true, powerPreference: 'high-performance' }`. Reuse it across scenes — creating multiple renderers leaks GPU contexts (browser limit is typically 8–16).
|
|
105
|
+
- Set `renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))` to cap resolution on high-DPI displays and avoid bandwidth-bound scenarios.
|
|
106
|
+
- Enable shadow maps only when needed: `renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap`. Limit shadow-casting lights to 1–2 per scene.
|
|
107
|
+
- Use `renderer.setAnimationLoop(tick)` for the render loop — it integrates with XR and handles tab visibility correctly, unlike a manual `requestAnimationFrame`.
|
|
108
|
+
|
|
109
|
+
### Scene & Camera
|
|
110
|
+
- Keep one active `Scene` object per game level. On level transition, dispose all geometries, materials, and textures in the outgoing scene before replacing it.
|
|
111
|
+
- Use `PerspectiveCamera` for 3D games. Tune `near`/`far` clipping planes tightly — a near/far ratio > 10,000 causes z-fighting. A ratio of 1/1000 is a safe starting-point.
|
|
112
|
+
- For 2.5D or UI overlays, maintain a separate orthographic camera and render pass.
|
|
113
|
+
|
|
114
|
+
### Geometry & Material Management
|
|
115
|
+
- Reuse `Geometry` and `Material` instances across `Mesh` objects via `instancedMesh` when drawing > 100 identical objects. `InstancedMesh` reduces draw calls from N to 1.
|
|
116
|
+
- Dispose resources explicitly: `geometry.dispose()`, `material.dispose()`, `texture.dispose()`. Three.js does not garbage-collect GPU resources automatically.
|
|
117
|
+
- Use `GLTFLoader` for 3D assets. Compress assets with `gltf-transform` using Draco (geometry) and KTX2/Basis (textures) — typically 70–90% smaller than raw GLTF.
|
|
118
|
+
- Share a single `TextureLoader` (or `LoadingManager`) across the app. Cache loaded textures in a `Map<string, THREE.Texture>` keyed by URL.
|
|
119
|
+
|
|
120
|
+
### Lighting & Post-Processing
|
|
121
|
+
- Prefer baked lightmaps for static environments to eliminate costly real-time light calculations.
|
|
122
|
+
- Use `@react-three/postprocessing` (r3f) or `three/examples/jsm/postprocessing/EffectComposer` for bloom, SSAO, and tonemapping. Chain passes on a single composer to minimize render targets.
|
|
123
|
+
- HDR environment maps via `RGBELoader` + `PMREMGenerator` provide physically accurate image-based lighting at low runtime cost.
|
|
124
|
+
|
|
125
|
+
### Raw Canvas / WebGL (No Framework)
|
|
126
|
+
- When authoring raw WebGL, manage shader programs as ES module string literals compiled once and cached in a `Map<string, WebGLProgram>`.
|
|
127
|
+
- Use Vertex Array Objects (VAOs) to encapsulate buffer bindings. Bind the VAO at draw time, not per-attribute setup.
|
|
128
|
+
- Use `ANGLE_instanced_arrays` (WebGL 1) or native instancing (WebGL 2) for repeated geometry.
|
|
129
|
+
- Consider `wgpu`/WebGPU for new projects targeting modern browsers — it exposes compute shaders and reduces CPU-side overhead significantly.
|
|
130
|
+
|
|
131
|
+
- id: web-game-performance
|
|
132
|
+
tier: recommended
|
|
133
|
+
title: "Web Game Performance"
|
|
134
|
+
content: |
|
|
135
|
+
## Web Game Performance
|
|
136
|
+
|
|
137
|
+
### JavaScript Thread Budget
|
|
138
|
+
- Target < 6ms of JavaScript CPU time per 16ms frame. Profile with Chrome DevTools Performance panel. The flame chart pinpoints expensive `update()` call stacks.
|
|
139
|
+
- Move physics calculations, pathfinding, and procedural generation to a Web Worker. Communicate via `postMessage` with `SharedArrayBuffer` or `Transferable` objects to avoid serialisation overhead.
|
|
140
|
+
- Avoid per-frame allocations: object pools for bullets, particles, and audio nodes. GC pauses at inopportune moments cause visible stutter.
|
|
141
|
+
|
|
142
|
+
### Memory
|
|
143
|
+
- Audit GPU memory with `WEBGL_debug_renderer_info` + `getParameter`. GPU memory exhaustion silently degrades performance before crashing.
|
|
144
|
+
- Unload atlas textures on scene exit. GL textures live on the GPU until explicitly deleted — `gl.deleteTexture(tex)` or the framework's `.destroy()` / `.dispose()` equivalents.
|
|
145
|
+
- Set a target: < 256 MB JS heap for mobile web games. Use `performance.measureUserAgentSpecificMemory()` in development to track heap growth.
|
|
146
|
+
|
|
147
|
+
### Asset Delivery
|
|
148
|
+
- Serve assets from a CDN with immutable cache headers (`Cache-Control: public, max-age=31536000, immutable`) using content-hashed filenames.
|
|
149
|
+
- Use the Compression Streams API or serve assets pre-compressed (Brotli > gzip). Atlas textures in KTX2 format (Basis Universal) reduce GPU upload time and GPU memory by 4–8× over PNG.
|
|
150
|
+
- Prefetch assets for the next level during gameplay idle periods with `fetch()` + `Cache API` rather than embedding loading screens in critical path.
|
|
151
|
+
|
|
152
|
+
### Mobile Web
|
|
153
|
+
- Test on real mid-range Android (Snapdragon 665 class) — not just desktop Chrome DevTools emulation. GPU drivers differ significantly.
|
|
154
|
+
- Reduce canvas resolution on low-power devices: detect `navigator.hardwareConcurrency < 4` or battery level via Battery API as a proxy for device capability.
|
|
155
|
+
- Touch input: use `pointer` events (`pointerdown`, `pointermove`, `pointerup`) instead of separate mouse/touch event handlers. Add `touch-action: none` to the canvas CSS to prevent scroll hijacking.
|
|
156
|
+
|
|
157
|
+
- id: game-testing
|
|
158
|
+
tier: recommended
|
|
159
|
+
title: "Game & Interactive Testing Patterns"
|
|
160
|
+
content: |
|
|
161
|
+
## Game & Interactive Testing Patterns
|
|
162
|
+
|
|
163
|
+
### Expose-Store-to-Window (E2E Layer)
|
|
164
|
+
In the test environment, expose the application state store to `window`:
|
|
165
|
+
```typescript
|
|
166
|
+
if (import.meta.env.TEST) window.__STORE__ = store;
|
|
167
|
+
```
|
|
168
|
+
The Playwright driver then asserts not only what renders but what the application believes is true — the store's internal state. Catches failures that render correctly but corrupt state silently. Required on all game and real-time UI projects.
|
|
169
|
+
|
|
170
|
+
### Vertical Chain Test Pattern
|
|
171
|
+
A single UI action triggers Playwright, which sequentially queries:
|
|
172
|
+
1. Service layer response (API or game service object)
|
|
173
|
+
2. Store / state manager internal state
|
|
174
|
+
3. Affected database records or in-memory structures
|
|
175
|
+
4. Rendered UI output
|
|
176
|
+
|
|
177
|
+
This is not a unit test and not a flow test — it is a chain verification. One trigger, observed at every boundary it crosses. Specify in CLAUDE.md which critical flows (combat resolution, save/load, transaction) receive this treatment.
|
|
178
|
+
|
|
179
|
+
### Generative Asset Quality Gates — Visual
|
|
180
|
+
When visual assets are produced by AI pipelines, assert before bundle inclusion:
|
|
181
|
+
|
|
182
|
+
| Check | Mechanism | Default Threshold |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| **Geometric orientation** | PCA on sprite silhouette pixels; extract principal axis angle from vertical | ≤ 15° |
|
|
185
|
+
| **Pixel coverage ratio** | Sprite fills acceptable proportion of bounding box | ≥ 0.40 |
|
|
186
|
+
| **Symmetry** | Pixel-level left/right half similarity after horizontal flip; normalized 0–1 | ≥ 0.80 |
|
|
187
|
+
| **Background cleanliness** | Non-background pixel ratio in border region | ≤ 0.30 |
|
|
188
|
+
| **Color palette compliance** | Generated asset matches scene-defined palette range (histogram comparison) | Per spec |
|
|
189
|
+
| **Alpha mask correctness** | No opaque pixels in defined transparent zones | Zero violations |
|
|
190
|
+
| **Dimension compliance** | Output matches required sprite sheet grid dimensions exactly | ± 0 px |
|
|
191
|
+
|
|
192
|
+
Assets failing any gate trigger regeneration (up to N retries). Accepted assets are logged to a preservation list so re-runs skip them. Rejection thresholds must be stated in the spec, not left implicit.
|
|
193
|
+
|
|
194
|
+
### Generative Asset Quality Gates — Audio
|
|
195
|
+
When audio assets are produced by AI models, assert before bundle inclusion:
|
|
196
|
+
|
|
197
|
+
| Check | Mechanism |
|
|
198
|
+
|---|---|
|
|
199
|
+
| **Loudness normalization** | LUFS target compliance (e.g., -14 LUFS for game audio); measured with ffmpeg-normalize or pyloudnorm |
|
|
200
|
+
| **Frequency profile** | No asset competes with dialogue in 2–4 kHz presence range unless specified |
|
|
201
|
+
| **Tempo consistency** | BPM within ± tolerance for scene-matched music; measured with librosa or essentia |
|
|
202
|
+
| **Silence detection** | Leading/trailing silence below threshold; catches generation artifacts |
|
|
203
|
+
| **Duration compliance** | Generated clip matches required length ± tolerance |
|
|
204
|
+
| **Clipping detection** | No samples at 0 dBFS unless intentionally distorted |
|
|
205
|
+
|
|
206
|
+
### MCP-Mediated Visual / Scene Inspection
|
|
207
|
+
For projects with an MCP server exposing instrumented game state: add a test layer where a language model is given a scene description and acceptance criteria, loads the live state through the MCP interface, and reports whether the scene satisfies them.
|
|
208
|
+
|
|
209
|
+
This is not a replacement for the structured suite — it closes the gap between what a pixel diff catches and what a brief human review would flag. Schedule as a pre-release gate, not a per-commit trigger. Document the scene invariants and acceptance criteria in the spec before implementing the gate.
|
|
210
|
+
|
|
211
|
+
- id: game-smoke-testing
|
|
212
|
+
tier: recommended
|
|
213
|
+
title: "Game Smoke Testing (Three-Tier)"
|
|
214
|
+
content: |
|
|
215
|
+
## Game Smoke Testing
|
|
216
|
+
|
|
217
|
+
### Tier 1 — Headless Unit (Pre-Commit, No Browser)
|
|
218
|
+
Pure game logic with no renderer dependency:
|
|
219
|
+
- **State machines**: all valid transitions, all invalid-transition rejections
|
|
220
|
+
- **Physics / collision**: core formulas, edge cases (zero velocity, exact boundary contact)
|
|
221
|
+
- **Scoring and economy**: accumulation, overflow bounds, negative prevention
|
|
222
|
+
- **Save / load**: round-trip serialization of game state loses no data
|
|
223
|
+
|
|
224
|
+
Tools: Vitest / Jest / pytest. Fast — runs pre-commit.
|
|
225
|
+
|
|
226
|
+
### Tier 2 — Browser Smoke (Post-Deploy, Playwright)
|
|
227
|
+
Confirms the game bootstraps correctly in a real browser:
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// tests/smoke/game.smoke.ts
|
|
231
|
+
import { test, expect } from '@playwright/test';
|
|
232
|
+
|
|
233
|
+
test('@smoke canvas is visible and sized', async ({ page }) => {
|
|
234
|
+
await page.goto('/');
|
|
235
|
+
const canvas = page.locator('canvas');
|
|
236
|
+
await expect(canvas).toBeVisible({ timeout: 10_000 });
|
|
237
|
+
const box = await canvas.boundingBox();
|
|
238
|
+
expect(box?.width).toBeGreaterThan(0);
|
|
239
|
+
expect(box?.height).toBeGreaterThan(0);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('@smoke no JS errors during game init', async ({ page }) => {
|
|
243
|
+
const errors: string[] = [];
|
|
244
|
+
page.on('pageerror', e => errors.push(e.message));
|
|
245
|
+
await page.goto('/');
|
|
246
|
+
await page.waitForTimeout(3_000); // allow game boot
|
|
247
|
+
expect(errors).toHaveLength(0);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('@smoke WebGL context created', async ({ page }) => {
|
|
251
|
+
await page.goto('/');
|
|
252
|
+
const hasWebGL = await page.evaluate(() =>
|
|
253
|
+
!!document.querySelector('canvas')?.getContext('webgl2') ||
|
|
254
|
+
!!document.querySelector('canvas')?.getContext('webgl')
|
|
255
|
+
);
|
|
256
|
+
expect(hasWebGL).toBe(true);
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Tier 3 — Performance Smoke (FPS Floor, Playwright + CDP)
|
|
261
|
+
Assert minimum sustained frame rate after game warms up:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// tests/smoke/game-perf.smoke.ts
|
|
265
|
+
import { test, expect, chromium } from '@playwright/test';
|
|
266
|
+
|
|
267
|
+
const FPS_FLOOR = Number(process.env['GAME_SMOKE_FPS_FLOOR'] ?? '30');
|
|
268
|
+
|
|
269
|
+
test('@smoke FPS above floor after 5s', async () => {
|
|
270
|
+
const browser = await chromium.launch();
|
|
271
|
+
const context = await browser.newContext();
|
|
272
|
+
const page = await context.newPage();
|
|
273
|
+
const cdp = await context.newCDPSession(page);
|
|
274
|
+
|
|
275
|
+
await page.goto('/');
|
|
276
|
+
await page.waitForTimeout(5_000); // warm-up
|
|
277
|
+
const { metrics } = await cdp.send('Performance.getMetrics');
|
|
278
|
+
const frames = metrics.find(m => m.name === 'Frames');
|
|
279
|
+
const duration = metrics.find(m => m.name === 'TaskDuration');
|
|
280
|
+
if (frames && duration && duration.value > 0) {
|
|
281
|
+
const fps = frames.value / duration.value;
|
|
282
|
+
expect(fps).toBeGreaterThanOrEqual(FPS_FLOOR);
|
|
283
|
+
}
|
|
284
|
+
await browser.close();
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The FPS floor (default 30) must be declared in `spec.md` under Performance Requirements.
|
|
289
|
+
In CI, use `--use-gl=swiftshader` for consistent software-rendering results across agents.
|