Project-specific development guidance for Claude Code, including package management rules, git workflow, development commands, architecture overview, and known patterns/solutions.
Follow these instructions when working with code in this repository.
1. **Read CHANGELOG.md first** for project history and recent changes
2. **Check tasks/todo.md** for current priorities and next steps
1. Open `tasks/todo.md` to see the current task list
2. Work through tasks in order
3. Ask user if ready to move to next task section before proceeding
Before committing, **always** run quality checks:
1. Run `pnpm lint`
2. If linting passes, run `pnpm tsc`
3. If TypeScript compilation succeeds, run `pnpm build`
4. If build succeeds:
- Update CHANGELOG.md with changes
- Cross off completed tasks in tasks/todo.md
- Stage all changes: `git add .`
- Commit with descriptive message
- Push to remote
**Never commit without passing all three checks.**
This is a **multiplayer board game application** built with Next.js 15 and Y.js real-time collaboration.
**Problem**: Client B receives Yjs update from Client A, triggers Zustand set(), which fires subscription and calls syncToYjs(), potentially overwriting A's fresh data with B's stale copy.
**Solution** (implemented in `gameStore.ts:552-578`):
1. **Lock Pattern**: Use `isUpdatingFromYjs` flag
```typescript
// Set flag BEFORE calling set() in observer
isUpdatingFromYjs = true
set(newState)
isUpdatingFromYjs = false
// Check flag in syncToYjs
if (isUpdatingFromYjs) return
```
2. **Shallow Equality Check**: Compare previous vs current state with JSON.stringify
3. **Debounced Sync**: Use setTimeout(50ms) to batch rapid changes
4. **Debug Logging**: Track update sources
**When to Apply**: Always implement this pattern when adding new Yjs synchronization.
**Problem**: Boolean `false` values incorrectly overridden by local fallback in multiplayer sync.
**Wrong**:
```typescript
energyTaxPaid: yjsState.energyTaxPaid || get().energyTaxPaid // ❌ false becomes true
```
**Correct**:
```typescript
energyTaxPaid: yjsState.energyTaxPaid ?? get().energyTaxPaid // ✅ only null/undefined fallback
```
**Rule**: Use nullish coalescing (`??`) instead of logical OR (`||`) for boolean and numeric fields in Yjs observers.
**Problem**: Local UI changes (e.g., energy tax payment) not immediately visible on current player's screen, but visible to other players.
**Root Cause**: Double synchronization to Yjs:
1. Action methods manually synced with `gameStateMap.set()`
2. Store subscription also synced with 50ms debounce
3. Remote Yjs updates could override local changes
**Solution**: Remove all manual `gameStateMap.set()` calls from action methods. Let the store subscription handle ALL Yjs synchronization.
**Fixed Methods**:
**Rule**: Never manually sync to Yjs in action methods. Always rely on the store subscription.
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/claude-code-project-guide/raw