Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
The English summary is being prepared. Showing the original description.
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.
The English summary is being prepared. Showing the original description.
Review the instructions the agent will receive before you install.
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.No reviews yet. Tell others what you thought.
No login needed. Share how it worked for you, ask a question, or note where it did not run.
No comments yet. Be the first to write one.
Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases
The English summary is being prepared. Showing the original description.
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
The English summary is being prepared. Showing the original description.
Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal
The English summary is being prepared. Showing the original description.
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
The English summary is being prepared. Showing the original description.
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.
The English summary is being prepared. Showing the original description.
Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here
The English summary is being prepared. Showing the original description.