skillby starwards

starwards-debugging

Systematic debugging for Starwards - four-phase framework (root cause investigation, pattern analysis, hypothesis testing, implementation) with Colyseus state inspection, Tweakpane debugging, multiplayer sync issues, and monorepo-specific troubleshooting

Installs: 0
Used in: 1 repos
Updated: 2d ago
$npx ai-builder add skill starwards/starwards-debugging

Installs to .claude/skills/starwards-debugging/

# Systematic Debugging for Starwards

## Overview

Random fixes waste time. Quick patches mask underlying issues.

**Core principle:** ALWAYS find root cause before attempting fixes.

**Starwards-specific:** Debug state sync issues, decorator problems, UI/server mismatches, and multiplayer race conditions.

## The Iron Law

```
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
```

If you haven't completed Phase 1, you cannot propose fixes.

## When to Use

**Any technical issue:**
- Test failures (Jest, Playwright)
- State sync problems (Colyseus)
- UI not updating (Tweakpane, React)
- Build failures (webpack, tsup, tsc)
- Decorator issues (@gameField, @range, @tweakable)
- Multiplayer race conditions

## The Four Phases

### Phase 1: Root Cause Investigation

**1. Read Error Messages Carefully**

**TypeScript errors:**
```bash
npm run test:types
# Read FULL error including file:line
# Check @gameField decorator order (must be last)
# Verify @range types match property type
```

**Jest test failures:**
```bash
npm test
# Read expected vs actual values
# Check if float precision issue (use toBeCloseTo)
# Verify async operations completed (await harness.waitForSync())
```

**Playwright failures:**
```bash
npm run test:e2e
# Check screenshot diffs in test-results/
# Verify data-id selectors match actual panel titles
# Check if timing issue (add proper waits, not arbitrary timeouts)
```

**Webpack errors:**
```bash
cd modules/browser && npm start
# Check browser console (F12) for actual error
# Webpack overlay shows [object Object] - this is a known issue
# Look for import/export mismatches
```

**2. Reproduce Consistently**

**For state sync issues:**
```typescript
// Add logging to both client and server
console.log('[CLIENT] Shield strength:', state.shield.strength);
console.log('[SERVER] Shield strength:', manager.state.shield.strength);

// Verify both show same value
// If different → state sync problem
// If same → UI rendering problem
```

**For multiplayer bugs:**
- Test with 1 client (works?)
- Test with 2 clients (breaks?)
- Test with concurrent actions (race condition?)

**3. Check Recent Changes**

```bash
git diff HEAD~1               # Last commit
git log --oneline -5          # Recent commits
git diff --stat origin/master # All changes on branch
```

**Common culprits:**
- @gameField decorator added/removed → rebuild core
- Schema change → restart server
- Widget change → clear browser cache
- Package update → npm ci && npm run build

**4. Gather Evidence in Multi-Component Systems**

**Starwards has 4 layers: Browser → WebSocket → Server → State**

Add diagnostic logging at each boundary:

```typescript
// Layer 1: Browser widget
console.log('[WIDGET] Sending command:', value);
room.send({type: '/Spaceship/ship-0/shield/power', value});

// Layer 2: Network (Chrome DevTools → Network → WS)
// Verify WebSocket message sent

// Layer 3: Server room
this.onMessage((client, message) => {
  console.log('[SERVER] Received:', message);
});

// Layer 4: State manager
console.log('[MANAGER] Shield power updated:', this.state.shield.power);
```

Run once to see WHERE it breaks:
- Widget logs but no WS message → room.send() issue
- WS message sent but server doesn't log → onMessage() not registered
- Server logs but state doesn't change → command handler bug

**5. Trace Data Flow**

**Example: Shield strength not updating**

Trace backward:
```
UI shows 500 ← Where does UI get value?
  ↓
pane.addBinding(shield, 'strength') ← What is shield?
  ↓
shield = ship.state.shield ← Does ship.state exist?
  ↓
ship.state from ShipDriver ← Is driver connected?
  ↓
driver.state from Colyseus room.state ← Is state synced?
```

Find the layer where data stops flowing correctly.

### Phase 2: Pattern Analysis

**1. Find Working Examples**

**For ship systems:**
```bash
# Look at existing systems
modules/core/src/ship/reactor.ts    # Has @gameField
modules/core/src/ship/armor.ts      # Has @range
modules/core/src/ship/warp.ts       # Has commands
```

**For widgets:**
```bash
# Look at working widgets
modules/browser/src/widgets/armor.ts       # Tweakpane panel
modules/browser/src/widgets/targeting.ts   # Command sending
```

**For tests:**
```bash
# Look at test patterns
modules/core/test/ship-test-harness.ts     # Multiplayer testing
modules/e2e/test/pilot-screen.spec.ts      # E2E testing
```

**2. Compare Against References**

Read docs COMPLETELY:
- `docs/PATTERNS.md` - Common patterns and gotchas
- `docs/TECHNICAL_REFERENCE.md` - @gameField, JSON Pointer, decorators
- `docs/testing/UTILITIES.md` - Test harness usage

**3. Identify Differences**

Use diff tools:
```bash
# Compare your new system to existing one
diff modules/core/src/ship/shield.ts modules/core/src/ship/armor.ts
```

Look for:
- Missing @gameField decorator
- Wrong decorator order (@gameField must be last)
- Missing MapSchema/ArraySchema for collections
- Type mismatches (float32 vs number)

**4. Understand Dependencies**

**For new ship systems:**
- Needs: @gameField decorator, added to ShipState, manager update loop
- Optional: Command handler, widget, E2E test

**For new widgets:**
- Needs: createPane() call, ship.state binding, registered in Dashboard
- Optional: data-id attribute for E2E tests

**For multiplayer features:**
- Needs: State sync via @gameField, command handler in room, client-side sending
- Optional: Typed command vs JSON Pointer

### Phase 3: Hypothesis and Testing

**1. Form Single Hypothesis**

**Good:**
- "Shield strength doesn't sync because @gameField decorator is missing"
- "Widget doesn't update because onChange listener not set up"
- "Test fails because float32 precision loss, need toBeCloseTo()"

**Bad:**
- "State sync issue" (too vague)
- "Something wrong with Colyseus" (blaming framework)
- "Might be a race condition" (guessing)

**2. Test Minimally**

**Example: Test @gameField hypothesis**

```typescript
// Before: (no decorator)
strength = 1000;

// After: (add decorator)
@gameField('float32') strength = 1000;

// Rebuild core, restart server, verify
```

ONE change at a time.

**3. Verify Before Continuing**

```bash
# Did it work?
npm test
npm run test:e2e

# If NO: Form NEW hypothesis
# If YES: Proceed to Phase 4
```

**4. When You Don't Know**

Say: "I don't understand why Colyseus state isn't syncing"

Don't say: "Let me try adding more decorators and see what happens"

Ask for help or research in:
- `docs/PATTERNS.md`
- Colyseus documentation
- Existing codebase examples

### Phase 4: Implementation

**1. Create Failing Test**

Use **starwards-tdd** skill to write proper test.

**For state sync bug:**
```typescript
test('shield strength syncs to client', async () => {
  const harness = new ShipTestHarness();
  await harness.connect();

  harness.shipManager.state.shield.strength = 750;
  await harness.waitForSync();

  expect(harness.shipDriver.state.shield.strength).toBe(750);

  await harness.cleanup();
});
```

**2. Implement Single Fix**

Address root cause only.

```typescript
// If root cause is: @gameField missing
@gameField('float32') strength = 1000;
```

NO "while I'm here" improvements.

**3. Verify Fix**

```bash
# Run the specific test
npm test -- shield-sync.spec.ts

# Run full suite
npm test
npm run test:e2e
npm run test:types
```

**4. If 3+ Fixes Failed**

**Pattern:**
- Each fix reveals new shared state issue
- "Need massive refactoring" for simple feature
- Fixes create new symptoms elsewhere

**STOP and question:**
- Is this architecture sound?
- Should we refactor instead of patching?
- Discuss with team before attempting fix #4

## Starwards-Specific Debugging Tools

### 1. Colyseus Monitor

```bash
# Access at http://localhost:2567/colyseus-monitor
# Login: admin / admin
# View: Active rooms, connected clients, state tree
```

**Use for:**
- Inspecting live state values
- Seeing connected clients
- Monitoring room creation/disposal

### 2. Chrome DevTools

**Network Tab → WS:**
- See WebSocket messages
- Verify commands sent
- Check state patches received

**Console:**
- Client-side errors
- Webpack HMR logs
- State change logging

**Sources:**
- Source maps enabled
- Breakpoints in TypeScript
- Step through React render

### 3. VSCode Debugger

**Terminal 3:**
```bash
# Instead of: node -r ts-node/register/transpile-only modules/server/src/dev.ts
# Use VSCode: F5 → "Run Server" (launches with debugger)
```

**Set breakpoints in:**
- `modules/server/src/ship/room.ts` - Command handlers
- `modules/core/src/ship/ship-manager.ts` - Update loops
- `modules/core/src/logic/space-manager.ts` - Physics

### 4. Tweakpane Debugging

Add temporary debug panel:

```typescript
const debugPane = createPane({title: 'DEBUG', container});

// Monitor live values
debugPane.addBinding(shield, 'strength', {readonly: true});
debugPane.addBinding(shield, 'power');
debugPane.addBinding(shield, 'effectiveness', {readonly: true});
```

### 5. Test Infrastructure

**ShipTestHarness:**
```typescript
const harness = new ShipTestHarness();
harness.shipManager.state // Server state
harness.shipDriver.state  // Client state
await harness.waitForSync(); // Wait for replication
```

**MultiClientDriver:**
```typescript
const driver = new MultiClientDriver();
await driver.start();
const [c1, c2] = await Promise.all([
  driver.joinShip('ship-1'),
  driver.joinShip('ship-1')
]);
// Test multiplayer scenarios
```

See `docs/testing/UTILITIES.md` for full API.

### 6. Logging Best Practices

```typescript
// GOOD: Scoped, structured
console.log('[ShieldManager.update] strength:', this.state.shield.strength, 'rate:', rechargeRate);

// BAD: Generic, unclear
console.log('shield', shield);
```

Use prefixes: `[CLIENT]`, `[SERVER]`, `[MANAGER]`, `[WIDGET]`

## Common Starwards Issues

| Symptom | Root Cause | Solution |
|---------|-----------|----------|
| State doesn't sync | Missing @gameField | Add decorator, rebuild core |
| Widget doesn't update | No onChange listener | Add state.onChange(() => update()) |
| Test fails with close values | Float32 precision | Use toBeCloseTo(expected, 1) |
| Angle values wrong | Not wrapped to [0, 360] | Use toPositiveDegreesDelta() |
| Effectiveness always 0 | System broken or no power | Check broken flag, power level |
| Webpack overlay shows [object Object] | Error wrapped by webpack | Check browser console (F12) |
| E2E test can't find panel | Wrong data-id | Use exact panel title in data-id |
| Build fails after core change | Core not rebuilt | npm run build:core or build:watch |
| Server doesn't see changes | Not restarted | Restart Terminal 3 (server) |

## Red Flags - STOP and Follow Process

- "Just try adding @gameField everywhere"
- "Let me restart everything and see if it works"
- "Probably a Colyseus bug"
- "I'll add setTimeout() to fix the race condition"
- Proposing fixes before checking browser console
- Blaming TypeScript strict mode
- "One more decorator tweak" (after 2+ failures)

## Integration with Other Skills

- **starwards-tdd** - Write failing test for bug before fixing
- **starwards-verification** - Verify fix with full test suite
- **starwards-monorepo** - Understand build dependencies
- **starwards-ci-debugging** - Debug GitHub Actions failures when tests pass locally but fail in CI

## Quick Debugging Checklist

```bash
# 1. Verify build is fresh
npm run build

# 2. Check types
npm run test:types

# 3. Run tests
npm test

# 4. Check browser console
# Open http://localhost:3000, press F12

# 5. Check server logs
# Look at Terminal 3 output

# 6. Check network
# Chrome DevTools → Network → WS

# 7. Check Colyseus monitor
# http://localhost:2567/colyseus-monitor
```

## Real-World Debugging Flow

**Example: "Shield widget shows wrong value"**

**Phase 1: Investigate**
1. Browser console: No errors ✓
2. Colyseus monitor: Server shows strength=800
3. Widget shows: 500
4. Hypothesis: State not syncing OR UI not updating

**Phase 2: Pattern**
1. Check working widget: armor.ts uses addBinding
2. My widget: Also uses addBinding
3. Difference: Armor has onChange listener, mine doesn't!

**Phase 3: Hypothesis**
"Widget doesn't update because Tweakpane binding isn't reactive to Colyseus state changes"

**Test minimally:**
```typescript
shield.state.onChange(() => {
  pane.refresh(); // Force Tweakpane update
});
```

**Phase 4: Implement**
1. Write E2E test that fails
2. Add onChange listener
3. Verify test passes
4. Verify manually in browser

Done!

Quick Install

$npx ai-builder add skill starwards/starwards-debugging

Details

Type
skill
Author
starwards
Slug
starwards/starwards-debugging
Created
6d ago