| name | gd-validation-playtest |
| description | Playwright-based game playtesting and design validation. Use when validating implementation against GDD, testing gameplay mechanics, capturing screenshot evidence, performing game state detection via Vision MCP, or conducting visual GDD compliance validation. |
Playtest Validation
When Playtest Is NOT Required
Skip playtest for:
- Test infrastructure bugfixes (unit tests, E2E tests, build fixes)
- Non-gameplay tasks (CI/CD, tooling, documentation)
- Backend-only changes without visual impact
Playtest IS required for:
- Gameplay mechanics (movement, shooting, physics)
- Visual features (shaders, materials, effects)
- UI/UX changes (HUD, menus, interactions)
- Character/weapon behavior
- Multiplayer features
Playtest Initiation
Triggers (any of these):
.claude/session/retrospective.txt contains "[ ] Request playtest"
prd.json.session.currentTask.status = "playtest_phase"
- PM sends
playtest_session_request message
When ANY trigger is true, initiate playtest flow:
⚠️ CRITICAL RULES:
- Playwright MCP REQUIRED - NO manual testing alternatives
- Screenshot Evidence - At least 3: start, during, end
- Vision MCP Analysis - Game state detection, GDD compliance validation
- Send
playtest_report - MUST be sent to PM FIRST
Non-negotiable evidence in playtest_report:
If Playwright MCP unavailable:
- Send
question to PM immediately: "Playwright MCP unavailable - cannot playtest"
- DO NOT attempt manual testing workaround
Playtest Process
Step 1: Setup
Bash("npm run dev:all:sh")
Step 2: Launch Game via Playwright
await page.goto('http://localhost:3000');
await page.waitForLoadState('networkidle');
await page.screenshot({ path: 'screenshots/playtest-start.png' });
Step 3: Test Core Mechanics
For each mechanic in GDD:
await page.keyboard.down('KeyW');
await page.waitForTimeout(1000);
await page.keyboard.up('KeyW');
await page.click('[data-testid="interact-button"]');
await page.click('[data-testid="attack-button"]');
await page.screenshot({ path: 'screenshots/mechanic-tested.png' });
Step 4: Validate vs GDD
For each GDD requirement:
Step 5: Document Findings
Create playtest report:
{
"taskId": "feat-001",
"playtestedAt": "2025-01-21T12:00:00Z",
"gddCompliance": {
"mechanic-name": {
"status": "matches|deviates|missing",
"notes": "Description"
}
},
"deviations": [
{
"feature": "Mechanic name",
"expected": "GDD description",
"actual": "What happens in game",
"severity": "low|medium|high",
"screenshot": "path/to/evidence"
}
],
"issues": [
{
"type"
Playwright MCP Usage
Starting the Game
Bash("npm run dev:all:sh")
await page.goto('http://localhost:3000');
await page.waitForLoadState('networkidle');
Testing Controls
await page.keyboard.press('KeyW');
await page.keyboard.up('KeyW');
await page.mouse.click(x, y);
await page.mouse.down();
await page.mouse.up();
await page.touchscreen.tap(x, y);
Continuous Movement (Critical for Games)
await page.keyboard.down('KeyW');
await page.waitForTimeout(1000);
await page.keyboard.up('KeyW');
await page.keyboard.down('KeyW');
await page.keyboard.down('KeyD');
await page.keyboard.down('ShiftLeft');
await page.waitForTimeout(2000);
await page.keyboard.up('ShiftLeft');
await page.keyboard.up('KeyD');
await page.keyboard.up('KeyW');
async function executeCombo(page, sequence) {
for (const action of sequence) {
page..(action.);
page.(action.);
page..(action.);
page.();
}
}
(page, [
{ : , : },
{ : , : },
{ : , : },
]);
Game State Detection (Vision MCP)
Use Vision MCP to analyze screenshots and determine current game state:
async function detectGameState(screenshotPath) {
const analysis = await visionAnalyze(screenshotPath, {
prompt: `Analyze this game screenshot and determine:
1. Is this a menu screen, gameplay, game over, victory, or loading?
2. What UI elements are visible? (HUD, health bar, minimap, inventory)
3. Is the player character visible?
4. Are there any error messages?
Respond in JSON:
{
"state": "menu|playing|gameover|win|loading|error",
"uiElements": ["hud", "healthBar", ...],
"playerVisible": true|false,
"details": "description"
}`,
});
return JSON.parse(analysis);
}
await page.screenshot({ path: 'playtest/state-1.png' });
const state = await detectGameState('playtest/state-1.png');
console.log('Current state:', state.state);
Visual GDD Compliance Validation
async function validateVisualGDD(screenshotPath, gddRequirement) {
const analysis = await visionAnalyze(screenshotPath, {
prompt: `According to this GDD requirement:
"${gddRequirement}"
Does the screenshot match? Check:
1. Required elements are present
2. Visual style is correct
3. Colors/theme match specification
4. Layout is as described
Return {
"matches": true|false,
"deviations": [
{ "element": "name", "expected": "spec", "actual": "observed" }
],
"severity": "low|medium|high"
}`,
});
return JSON.parse(analysis);
}
const characterGDD = 'A knight in silver armor with blue cape, holding sword';
const result = await validateVisualGDD('playtest/character.png', characterGDD);
Screenshot Comparison Analysis
async function comparePlaytestStates(beforePath, afterPath) {
const comparison = await visionAnalyze([beforePath, afterPath], {
prompt: `Compare these two gameplay screenshots.
Image 1 is BEFORE the action.
Image 2 is AFTER the action.
What changed?
1. Did player position change?
2. Did UI elements change (health, score, ammo)?
3. Are there new visual effects?
4. Any bugs or glitches visible?
Return {
"playerMoved": true|false,
"uiChanges": ["health decreased", "score increased", ...],
"newEffects": ["explosion", "particle", ...],
"issues": ["list of visual problems"]
}`,
});
return JSON.parse(comparison);
}
Monitoring State
page.on('console', (msg) => {
console.log(msg.text());
});
const content = await page.content();
Capturing Evidence
await page.screenshot({
path: 'screenshots/evidence.png',
fullPage: true,
});
await page.pdf({
path: 'report.pdf',
});
Validation Categories
Functional Validation
Does the feature work as intended?
Design Validation
Does it match the GDD?
Experience Validation
Is it fun?
Common Issues to Check
| Issue | Check Method |
|---|
| Console errors | Check browser console |
| Visual glitches | Compare to reference |
| Input lag | Test responsiveness |
| Performance | Monitor FPS |
| Crashes | Try stress scenarios |
Playtest Report Template
# Playtest Report - [Task Name]
**Date:** YYYY-MM-DD
**Tester:** Game Designer Agent
**GDD Version:** X.X.X
## Summary
[Overall assessment]
## GDD Compliance
| Mechanic | Status | Notes |
| -------- | -------- | --------- |
| [Name] | ✅/❌/⚠️ | [Details] |
## Deviations Found
| Feature | Expected | Actual | Severity |
| ------- | -------- | -------- | -------------- |
| [Name] | [GDD] | [Actual] | [High/Med/Low] |
## Issues Found
| ID | Type | Description | Severity | Status |
| --- | ------ | ------------- | -------- | ------ |
| 1 | [Type] | [Description] | [Level] | [Open] |
## Recommendations
1. [Improvement 1]
2. [Improvement 2]
3. [Improvement 3]
## Screenshots


Sending Playtest Report
After completing playtest, use Write tool to send message to PM's inbox:
Write(
'.claude/session/messages/pm/msg-playtest-{timestamp}.json',
JSON.stringify({
id: 'msg-playtest-{timestamp}',
from: 'gamedesigner',
to: 'pm',
type: 'playtest_report',
priority: 'high',
payload: {
},
timestamp: '{UTC-timestamp}',
status: 'pending',
})
);
Retrospective Participation
When retrospective initiated:
- Play the game - Full playthrough if possible
- Test each mechanic - Systematic validation
- Capture evidence - Screenshots of key moments
- Compare vs GDD - Note all deviations
- Document findings - Comprehensive report
- Send report - To PM via message
- Write to retrospective.txt - Team contribution
Playtest Checklist
Before completing playtest:
Visual Quality Assessment Criteria (Added: ui-001 Playtest)
Learned from ui-001 playtest: Functionally complete UI can still be visually inadequate for shipping.
Visual Quality Assessment Matrix
| Category | Pass Criteria | Weight |
|---|
| Aspect Ratio | 16:9 enforced, letterbox on non-16:9 | High |
| Design System | Tokens, consistent styling, reusable components | High |
| Typography | Gaming fonts, readable, appropriate scale | Medium |
| Button Polish | Hover/active states, feedback animations | High |
| Color Palette | Theme-appropriate, accessible contrast | Medium |
| Animations | Smooth, custom easing, not default/linear | Medium |
| Professional Appearance | Not prototype-like, shipping quality | High |
Visual Quality Levels
| Level | Description | Action |
|---|
| SHIPPABLE | All visual criteria met, professional appearance | PASS |
| CONDITIONAL | Functional but needs polish | CONDITIONAL_PASS |
| PROTOTYPE | Looks like prototype, not shipping game | FAIL - Create redesign task |
When to Issue CONDITIONAL_PASS
Use CONDITIONAL_PASS when:
- All functional requirements work correctly
- Core mechanics are playable
- Visual design is functional but lacks polish
- UI looks prototype-like, not production-ready
Mandatory Actions on CONDITIONAL_PASS:
- Document specific visual gaps in report
- Create dedicated visual redesign task (TIER_0_BLOCKER if UI is primary feature)
- Create specification document for redesign
- Block original task completion until visual redesign done
Example:
{
"result": "CONDITIONAL_PASS",
"functionalStatus": "PASS",
"visualStatus": "NEEDS_REDESIGN",
"gaps": [
"No 16:9 aspect ratio enforcement",
"Basic Tailwind styling instead of custom design",
"Generic fonts instead of gaming typography",
"No custom easing curves for animations"
],
"recommendation": "BLOCK until visual design addressed",
"newTask": {
"id": "ui-002",
"title": "Professional UI/UX Redesign",
"priority": "TIER_0_BLOCKER"
}
}