Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
日本語の概要は準備中です。原文の説明を表示しています。
Use when working on any browser game (Phaser, PixiJS, three.js, Babylon.js, PlayCanvas, Excalibur, KAPLAY) - requestAnimationFrame with clamped delta and visibility pause, zero-allocation frame loops, measuring in DevTools, bundle and asset size budgets, Vitest with seeded RNG and injected time, and a seeded Playwright smoke test with console capture.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
A browser game shares a thread with the page, a garbage collector with every closure, and a download budget with the player's patience. It is also the easiest kind of game for you to verify end to end on this machine: the build, the unit tests and a real browser run are all available. Use them.
Core principle: No allocations in the frame loop, a measured bundle, rules tested in Vitest with injected time and a seed, and one seeded Playwright smoke test that fails on console errors.
requestAnimationFrame (or the engine's loop built on it); never setInterval.document.visibilitychange, and resume without a catch-up burst.resume() the AudioContext on the first click/key/tap.touch-action: none so gestures do not scroll or zoom the page.// ❌ allocates every frame: new vectors, arrays, closures
function update(dt: number) {
const dir = new THREE.Vector3().subVectors(target.position, enemy.position).normalize();
enemies.filter((e) => e.alive).forEach((e) => e.step(dt));
hud.setText(`Score: ${score}`);
}
// ✅ reuses module-level temporaries; plain loops; updates text only on change
const tmp = new THREE.Vector3();
let shownScore = -1;
function update(dt: number) {
tmp.subVectors(target.position, enemy.position).normalize();
for (let i = 0; i < enemies.length; i++) if (enemies[i].alive) enemies[i].step(dt);
if (score !== shownScore) { shownScore = score; hud.setText(`Score: ${score}`); }
}
Pool bullets, particles and damage numbers; prefer typed arrays (Float32Array) for large particle or boid sets; avoid Array.prototype.map/filter/forEach with fresh closures in hot paths.
Minor GC) in the frame track.renderer.info (three.js), Babylon Inspector statistics, PixiJS devtools, Phaser's debug FPS.performance.mark/performance.measure around a system to get numbers into a test or log.npm run build lists chunk sizes; add rollup-plugin-visualizer only if the repository already has it.import().// src/logic/rng.ts
export function mulberry32(seed: number) {
return () => { seed |= 0; seed = (seed + 0x6d2b79f5) | 0; let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; };
}
// src/logic/spawner.test.ts
import { describe, it, expect } from "vitest";
import { Spawner } from "./spawner";
import { mulberry32 } from "./rng";
describe("Spawner", () => {
it("spawns one wave every 5 seconds regardless of step size", () => {
const a = new Spawner({ waveEverySeconds: 5 }, mulberry32(1));
const b = new Spawner({ waveEverySeconds: 5 }, mulberry32(1));
for (let i = 0; i < 600; i++) a.step(1 / 60);
for (let i = 0; i < 300; i++) b.step(1 / 30);
expect(a.wavesSpawned).toBe(2);
expect(b.wavesSpawned).toBe(2);
});
it("is deterministic for a seed", () => {
const run = () => { const s = new Spawner({ waveEverySeconds: 1 }, mulberry32(42)); for (let i = 0; i < 180; i++) s.step(1 / 60); return s.positions(); };
expect(run()).toEqual(run());
});
});
vi.useFakeTimers() and vi.advanceTimersByTime(ms).NullEngine in Vitest.// e2e/smoke.spec.ts
import { test, expect } from "@playwright/test";
test("boots, takes input, no console errors", async ({ page }) => {
const errors: string[] = [];
page.on("pageerror", (e) => errors.push(e.message));
page.on("console", (m) => { if (m.type() === "error") errors.push(m.text()); });
await page.goto("/?seed=42&scene=Level1&debug=1");
await expect(page.locator("canvas")).toBeVisible();
await expect.poll(() => page.evaluate(() => (window as any).__GAME__?.ready)).toBe(true);
await page.keyboard.press("Space");
await expect.poll(() => page.evaluate(() => (window as any).__GAME__?.player.jumps)).toBe(1);
await page.screenshot({ path: "test-results/level1-after-jump.png" });
expect(errors).toEqual([]);
});
window.__GAME__ read-only debug handle exposed only in debug/dev builds lets tests assert state instead of guessing from pixels.toHaveScreenshot) only with the loop paused on a deterministic frame and a tolerance; GPU differences make exact matches flaky.npx playwright test); if Playwright is not set up and not installed, use the browser tools and say what the smoke run would have covered (game-visual-self-review).setInterval loops; unclamped delta after a background tab.new inside the rAF callback or a system's update.pageerror listener.まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
日本語の概要は準備中です。原文の説明を表示しています。
Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping
日本語の概要は準備中です。原文の説明を表示しています。
Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal
日本語の概要は準備中です。原文の説明を表示しています。
Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task
日本語の概要は準備中です。原文の説明を表示しています。
How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.
日本語の概要は準備中です。原文の説明を表示しています。
Use when deciding whether a request needs an analiz task before implementation - the conditions that require the architect's analysis versus going straight to implementation
日本語の概要は準備中です。原文の説明を表示しています。