diff --git a/.gitignore b/.gitignore index 053c70f..cdf03ca 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,5 @@ poetry.lock # Game state persistence file game_state.json +coverage.xml + diff --git a/CLAUDE.md b/CLAUDE.md index 5357635..84a99a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -374,39 +374,152 @@ if 'key' in app.storage.general: - Responsive design classes - Clear visual feedback -## MCP Tools Used +## MCP Tools & Memory Management + +### 🛑 STOP! SAVE TO MEMORY NOW - NOT LATER! + +#### THROUGHOUT EVERY SESSION: +- ✅ Just solved something? → SAVE NOW +- ✅ Created a script? → SAVE NOW +- ✅ Fixed an error? → SAVE NOW +- ✅ Made a decision? → SAVE NOW +- ✅ Discovered a pattern? → SAVE NOW + +**DON'T WAIT UNTIL THE END OF THE SESSION!** + +### Memory-First Protocol (CRITICAL) +- **ALWAYS** search memory BEFORE starting any work: `mcp__mcp-memory__searchMCPMemory "bingo [topic]"` +- **ALWAYS** save solutions after fixing issues: `mcp__mcp-memory__addToMCPMemory "bingo: Problem: X, Solution: Y"` +- **Save context switches**: When interrupted or switching tasks, save current state +- **Capture train of thought**: Document reasoning and decision paths + +### Memory Save Triggers (DO IMMEDIATELY) +- After creating any script → Save its purpose and usage +- After fixing any error → Save problem + solution +- After file reorganization → Save what moved where +- After discovering pattern → Save the insight +- After making decision → Save the rationale +- After solving problem → Save approach + result + +### Session Start Protocol for Bingo +```bash +# 1. Restore context from last session +mcp__mcp-memory__searchMCPMemory "bingo last session state" +mcp__mcp-memory__searchMCPMemory "bingo open tasks TODO" +mcp__mcp-memory__searchMCPMemory "Jonathan workflow guidelines best practices" + +# 2. Rebuild mental model +tree . -I 'node_modules' -L 2 +cat CLAUDE.md + +# 3. Load comprehensive project memory +mcp__mcp-memory__searchMCPMemory "bingo current state" +mcp__mcp-memory__searchMCPMemory "bingo where I left off" +mcp__mcp-memory__searchMCPMemory "bingo blockers questions" +mcp__mcp-memory__searchMCPMemory "bingo solutions patterns" +mcp__mcp-memory__searchMCPMemory "bingo nicegui patterns" +mcp__mcp-memory__searchMCPMemory "bingo state persistence" +mcp__mcp-memory__searchMCPMemory "bingo testing infrastructure" + +# 4. Check work state +git status +git diff +git log --oneline -10 +gh pr list --assignee @me --state open +``` + +### MCP Tools Available -### For This Project -When working on the Bingo project, the following MCP tools are particularly useful: +1. **mcp-memory** (Always Active - External Brain) + - `mcp__mcp-memory__searchMCPMemory "[query]"` - Search stored knowledge + - `mcp__mcp-memory__addToMCPMemory "content"` - Save new knowledge + - Always prefix with "bingo:" for project isolation -1. **mcp-memory** (Long-term memory) - - `mcp__mcp-memory__searchMCPMemory`: Search for past solutions and patterns - - `mcp__mcp-memory__addToMCPMemory`: Save solutions and learnings - - Always prefix entries with "bingo:" for project isolation +2. **sequentialthinking** - For complex reasoning (especially with Sonnet 4) + - Use for architectural decisions and complex debugging + - Saves reasoning process to memory automatically -2. **context7** (Documentation lookup) +3. **context7** (Documentation lookup) - `mcp__context7__resolve-library-id`: Find library IDs (e.g., NiceGUI) - `mcp__context7__get-library-docs`: Get library documentation - - Used to research NiceGUI storage patterns - -3. **sequentialthinking** (Complex reasoning) - - `mcp__sequentialthinking__sequentialthinking`: Break down complex problems - - Useful for architectural decisions and debugging 4. **serena** (Code intelligence) - - Activate with: `mcp__serena__activate_project` + - Activate with: `mcp__serena__activate_project "bingo"` - Provides symbol search, refactoring, and code analysis - - Manages project-specific memories -### Example Usage -```python -# Search for past solutions -mcp__mcp-memory__searchMCPMemory("bingo state persistence") +### Memory Templates for Bingo Project -# Save new solution -mcp__mcp-memory__addToMCPMemory("bingo: Fixed state persistence using StateManager pattern...") +#### Error Solutions +``` +Project: bingo +Error: [exact error message] +Context: [NiceGUI version, test environment, etc.] +Solution: [step-by-step fix] +Code Before: [relevant code showing issue] +Code After: [corrected code] +Validation: [how verified it worked] +Tags: bingo, error-fix, [component], [technology] +``` + +#### Testing Infrastructure +``` +Project: bingo +Component: [StateManager/UI/Testing] +Issue: [what needed testing/fixing] +Approach: [testing strategy used] +Implementation: [specific test code/patterns] +Results: [coverage/performance metrics] +Patterns: [reusable testing patterns discovered] +Tags: bingo, testing, [unit/integration/e2e], [component] +``` + +#### NiceGUI Specific Patterns +``` +Project: bingo +NiceGUI Pattern: [state management/UI/timers/etc.] +Problem: [what was challenging] +Solution: [NiceGUI-specific approach] +Code Example: [working implementation] +Gotchas: [things to watch out for] +Performance: [any performance considerations] +Tags: bingo, nicegui, [pattern-type], [version] +``` + +#### StateManager Architecture +``` +Project: bingo +Architecture Decision: [what was decided] +Previous Approach: [old way - app.storage.general] +New Approach: [StateManager pattern] +Implementation: [key code/patterns] +Benefits: [persistence, thread-safety, etc.] +Testing Strategy: [how we verified it works] +Tags: bingo, architecture, state-persistence, statemanager +``` + +### Common Search Patterns for Bingo +```bash +# Starting work +mcp__mcp-memory__searchMCPMemory "bingo session startup protocol" +mcp__mcp-memory__searchMCPMemory "bingo current priorities" + +# Debugging +mcp__mcp-memory__searchMCPMemory "bingo [error-type] solutions" +mcp__mcp-memory__searchMCPMemory "bingo nicegui [issue-type]" +mcp__mcp-memory__searchMCPMemory "bingo testing [test-type] patterns" + +# Development +mcp__mcp-memory__searchMCPMemory "bingo statemanager patterns" +mcp__mcp-memory__searchMCPMemory "bingo ci optimization" +mcp__mcp-memory__searchMCPMemory "bingo deployment troubleshooting" +``` -# Get NiceGUI docs -mcp__context7__resolve-library-id("nicegui") -mcp__context7__get-library-docs("/zauberzeug/nicegui", "storage persistence") -``` \ No newline at end of file +### What to Save for Bingo (SAVE IMMEDIATELY) +- **Every StateManager fix/enhancement** with before/after code +- **NiceGUI UI patterns** that work well for this app +- **Testing strategies** that prove effective (unit/integration/e2e) +- **CI/CD optimizations** and performance improvements +- **Docker/Helm deployment issues** and their solutions +- **Performance bottlenecks** and optimization approaches +- **User experience improvements** and their impact +- **Architecture decisions** with reasoning and alternatives considered \ No newline at end of file diff --git a/tests/test_hot_reload_integration.py b/tests/test_hot_reload_integration.py index 155a609..33b051c 100644 --- a/tests/test_hot_reload_integration.py +++ b/tests/test_hot_reload_integration.py @@ -21,73 +21,73 @@ class TestHotReloadIntegration: @pytest.mark.asyncio async def test_state_persists_on_page_reload(self): - """Test that game state persists when page is reloaded.""" - async with async_playwright() as p: - # Launch browser - browser = await p.chromium.launch(headless=True) - page = await browser.new_page() - - try: - # Navigate to the app - await page.goto("http://localhost:8080") - await page.wait_for_load_state("networkidle") - - # Get initial board state - count clickable tile divs - initial_tiles = await page.locator("[style*='cursor: pointer']").count() - assert initial_tiles >= 25, f"Should have at least 25 tiles, got {initial_tiles}" - - # Get the actual tile texts from the page to click the first few - # Since tiles might have text split across multiple elements, - # we'll click by index position instead - tile_elements = await page.locator("[style*='cursor: pointer']").all() - - # Click tiles at specific indices (avoiding the center FREE MEAT tile at index 12) - tiles_to_click_indices = [0, 1, 5] # Top-left, second in first row, first in second row - - for index in tiles_to_click_indices: - await tile_elements[index].click() - await asyncio.sleep(0.2) # Wait for state save - - # Take screenshot before reload - await page.screenshot(path="before_reload.png") - - # Check state file exists and has correct data - state_file = Path("game_state.json") - assert state_file.exists(), "State file should exist" - - with open(state_file, 'r') as f: - state = json.load(f) - - # Verify clicked tiles are saved (including FREE MEAT) - assert len(state['clicked_tiles']) >= 4, f"Should have at least 4 clicked tiles, got {len(state['clicked_tiles'])}" - - # Store clicked positions for verification - clicked_positions = set(tuple(pos) for pos in state['clicked_tiles']) - - # Reload the page - await page.reload() - await page.wait_for_load_state("networkidle") - - # Take screenshot after reload - await page.screenshot(path="after_reload.png") - - # Verify the board is restored - restored_tiles = await page.locator("[style*='cursor: pointer']").count() - assert restored_tiles >= 25, f"Should still have at least 25 tiles after reload, got {restored_tiles}" - - # Read state file again to verify it still has the same data - with open(state_file, 'r') as f: - restored_state = json.load(f) - - restored_positions = set(tuple(pos) for pos in restored_state['clicked_tiles']) - assert clicked_positions == restored_positions, "Clicked tiles should be preserved" - - # Verify we have the expected number of tiles clicked - # Should be 3 tiles we clicked + 1 FREE MEAT tile = 4 total - assert len(restored_positions) == 4, f"Should have exactly 4 clicked tiles after reload, got {len(restored_positions)}" - - finally: - await browser.close() + """Test that game state persists when page is reloaded.""" + async with async_playwright() as p: + # Launch browser + browser = await p.chromium.launch(headless=True) + page = await browser.new_page() + + try: + # Navigate to the app + await page.goto("http://localhost:8080") + await page.wait_for_load_state("networkidle") + + # Get all clickable tiles + tiles = await page.locator("[style*='cursor: pointer']").all() + assert len(tiles) == 25, f"Should have exactly 25 tiles, got {len(tiles)}" + + # Click tiles by position (avoiding FREE MEAT at position 12) + # Map positions to board coordinates for verification + tiles_to_click = [ + 0, # (0,0) - top-left + 4, # (0,4) - top-right + 6, # (1,1) - second row, second col + ] + + for tile_index in tiles_to_click: + await tiles[tile_index].click() + await asyncio.sleep(0.2) # Wait for state save + + # Take screenshot before reload + await page.screenshot(path="before_reload.png") + + # Check state file exists and has correct data + state_file = Path("game_state.json") + assert state_file.exists(), "State file should exist" + + with open(state_file, 'r') as f: + state = json.load(f) + + # Verify clicked tiles are saved (our 3 clicks + FREE MEAT) + assert len(state['clicked_tiles']) == 4, f"Should have 4 clicked tiles, got {len(state['clicked_tiles'])}" + + # Store clicked positions for verification + clicked_positions = set(tuple(pos) for pos in state['clicked_tiles']) + + # Reload the page + await page.reload() + await page.wait_for_load_state("networkidle") + + # Take screenshot after reload + await page.screenshot(path="after_reload.png") + + # Verify the board is restored + restored_tiles = await page.locator("[style*='cursor: pointer']").all() + assert len(restored_tiles) == 25, f"Should still have 25 tiles after reload, got {len(restored_tiles)}" + + # Read state file again to verify it still has the same data + with open(state_file, 'r') as f: + restored_state = json.load(f) + + restored_positions = set(tuple(pos) for pos in restored_state['clicked_tiles']) + assert clicked_positions == restored_positions, "Clicked tiles should be preserved" + + # Verify we have the expected number of tiles clicked + assert len(restored_positions) == 4, f"Should have exactly 4 clicked tiles after reload, got {len(restored_positions)}" + + finally: + await browser.close() + @pytest.mark.asyncio async def test_state_persists_across_sessions(self):