Commit Graph

85 Commits

Author SHA1 Message Date
Cal Corum
52c8edcf93 Fix WebSocket game:join to emit game:state event
Backend was returning the state but not emitting the game:state
event that the frontend listens for. Added explicit emit call
to send game:state to the client after successful join.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 12:27:01 -06:00
Cal Corum
50bd3f1591 Add config API endpoint for frontend game settings
Expose game configuration (energy types, card types, rule constants) via
/api/config endpoint so frontend can dynamically load game rules without
hardcoding values.
2026-01-31 15:43:41 -06:00
Cal Corum
aee7ad64b4 Add card images for sets a1 and a1a
Add ~90 card artwork webp files and a MISSING_IMAGES.json tracking file
for cards still needing artwork.
2026-01-31 15:43:29 -06:00
Cal Corum
d1862295e6 Update card JSON data with rarity_tier field
Add rarity_tier to all card definitions in sets a1 and a1a to support
the new pull rate calculation system for booster packs.
2026-01-31 15:43:19 -06:00
Cal Corum
1123d61067 Extend core card models with rarity tiers and card subtypes
Add CardRarityTier enum for pull rate calculations (common through
crown_rare). Add CardSubtype enum for Pokemon classifications (basic,
stage1, stage2, ex, etc.). Update CardDefinition model with new fields
for subtypes and rarity display.
2026-01-31 15:43:07 -06:00
Cal Corum
ca3aca2b38 Add has_starter_deck to user profile API response
The frontend routing guard checks has_starter_deck to decide whether to
redirect users to starter selection. The field was missing from the API
response, causing authenticated users with a starter deck to be
incorrectly redirected to /starter on page refresh.

- Add has_starter_deck computed property to User model
- Add has_starter_deck field to UserResponse schema
- Add unit tests for User model properties
- Add API tests for has_starter_deck in profile response

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:14:04 -06:00
Cal Corum
cd3efcb528 Implement ProfilePage and profanity filter for display names (F1-006)
ProfilePage implementation:
- Full profile page with avatar, editable display name, session count
- LinkedAccountCard and DisplayNameEditor components
- useProfile composable wrapping user store operations
- Support for linking/unlinking OAuth providers
- Logout and logout-all-devices functionality

Profanity service with bypass detection:
- Uses better-profanity library for base detection
- Enhanced to catch common bypass attempts:
  - Number suffixes/prefixes (shit123, 69fuck)
  - Leet-speak substitutions (sh1t, f@ck, $hit)
  - Separator characters (s.h.i.t, f-u-c-k)
- Integrated into PATCH /api/users/me endpoint
- 17 unit tests covering all normalization strategies

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 16:06:42 -06:00
Cal Corum
3cc8d6645e Implement auth composables and starter selection (F1-003, F1-004, F1-005)
Features:
- Add useAuth composable with OAuth flow and token management
- Add useStarter composable with API integration and dev mock fallback
- Implement app auth initialization blocking navigation until ready
- Complete StarterSelectionPage with 5 themed deck options

Bug fixes:
- Fix CORS by adding localhost:3001 to allowed origins
- Fix OAuth URL to include redirect_uri parameter
- Fix emoji rendering in nav components (use actual chars, not escapes)
- Fix requireStarter guard timing by allowing navigation from /starter
- Fix starter "already selected" detection for 400 status code

Documentation:
- Update dev-server skill to use `docker compose` (newer CLI syntax)
- Update .env.example with port 3001 in CORS comment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 15:36:14 -06:00
Cal Corum
f452e69999 Complete Phase 4 implementation files
- TurnTimeoutService with percentage-based warnings (35 tests)
- ConnectionManager enhancements for spectators and reconnection
- GameService with timer integration, spectator support, handle_timeout
- GameNamespace with spectate/leave_spectate handlers, reconnection
- WebSocket message schemas for spectator events
- WinConditionsConfig additions for turn timer thresholds
- 83 GameService tests, 37 ConnectionManager tests, 37 GameNamespace tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 08:03:43 -06:00
Cal Corum
2ae6429798 Mark Phase 4 (Game Service + WebSocket) as complete
- Update status to COMPLETE with completedDate 2026-01-30
- Increment completedPhases from 4 to 5
- Update deliverables list with actual implementations
- 306 new tests added, 1505 total tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 07:35:40 -06:00
Cal Corum
7fcb86ff51 Implement UserRepository pattern with dependency injection
- Add UserRepository and LinkedAccountRepository protocols to protocols.py
- Add UserEntry and LinkedAccountEntry DTOs for service layer decoupling
- Implement PostgresUserRepository and PostgresLinkedAccountRepository
- Refactor UserService to use constructor-injected repositories
- Add get_user_service factory and UserServiceDep to API deps
- Update auth.py and users.py endpoints to use UserServiceDep
- Rewrite tests to use FastAPI dependency overrides (no monkey patching)

This follows the established repository pattern used by DeckService and
CollectionService, enabling future offline fork support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 07:30:16 -06:00
Cal Corum
f6e8ab5f67 Add integration tests for WebSocket game flow (TEST-002)
Create 16 integration tests across 7 test classes covering:
- JWT authentication (valid/invalid/expired tokens)
- Game join flow with Redis connection tracking
- Action execution and state broadcasting
- Turn timeout Redis operations (start/get/cancel/extend)
- Disconnection cleanup
- Spectator filtered state
- Reconnection tracking

Uses testcontainers for real Redis/Postgres integration. Completes
Phase 4 (Game Service + WebSocket) with all 18 tasks finished.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 00:02:40 -06:00
Cal Corum
cc0254d5ab Implement REST endpoints for game management (API-001)
Add REST API endpoints for game lifecycle operations:
- POST /games - Create new game between two players
- GET /games/{game_id} - Get game info for reconnection
- GET /games/me/active - List user's active games
- POST /games/{game_id}/resign - Resign from game via HTTP

Includes proper reverse proxy support for WebSocket URL generation
(X-Forwarded-* headers -> settings.base_url -> Host header fallback).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 23:09:12 -06:00
Cal Corum
154d466ff1 Implement /game namespace event handlers (WS-005, WS-006)
Add GameNamespaceHandler with full event handling for real-time gameplay:
- handle_join: Join/rejoin games with visibility-filtered state
- handle_action: Execute actions and broadcast state to participants
- handle_resign: Process resignation and end game
- handle_disconnect: Notify opponent of disconnection
- Broadcast helpers for state, game over, and opponent status

Includes 28 unit tests covering all handler methods.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 20:40:06 -06:00
Cal Corum
531d3e1e79 Implement GameService.end_game with history archival (GS-005)
- Add GameEndResult dataclass with winner, loser, final views, duration
- Add _map_end_reason() to map core GameEndReason to DB EndReason
  (raises ValueError for unknown reasons to catch missing enum sync)
- Enhance end_game() to build replay data and return comprehensive result
- Add archive_to_history() to GameStateManager for complete game archival:
  - Creates GameHistory record with replay data
  - Deletes ActiveGame record
  - Clears Redis cache
  - All in single transaction
- Add ArchiveResult dataclass for archive operation metadata
- Add TODO for session_factory DI refactor in GameStateManager
- Update tests: 5 new end_game tests, 6 new archive_to_history tests

Phase 4 progress: 10/18 tasks complete

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 20:10:27 -06:00
Cal Corum
d5460ff418 Enhance GameService.join_game for reconnection support (GS-004)
Add pending forced action and game_over fields to GameJoinResult:
- pending_forced_action: Included when player must complete a forced
  action (e.g., select new active after KO). Essential for reconnection
  so client knows what action is required.
- game_over: Boolean indicating if game has already ended.
- is_your_turn: Now True when player has pending forced action, even if
  it's technically opponent's turn.

The join_game method now handles both initial joins and reconnections
(resume). The last_event_id parameter is accepted for future event
replay support.

Tests: 4 new tests for forced action handling and game_over flag.
Total 51 tests for GameService.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 19:30:44 -06:00
Cal Corum
0a9cd73daf Validate connection data integrity in ConnectionManager
Fix audit warnings about empty string defaults hiding data corruption:

1. get_connection_info(): Validate required fields (user_id, connected_at,
   last_seen) exist before creating ConnectionInfo. Return None and log
   warning for corrupted records instead of returning invalid data.

2. unregister_connection(): Log warning if user_id is missing during
   cleanup. Existing code safely handles this (skips cleanup), but now
   we have visibility into data corruption.

Test added for corrupted data case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 19:03:17 -06:00
Cal Corum
55e02ceb21 Replace silent error hiding with explicit failures
Three changes to fail fast instead of silently degrading:

1. GameService.create_game: Raise GameCreationError when energy card
   definition not found instead of logging warning and continuing.
   A deck with missing energy cards is fundamentally broken.

2. CardService.load_all: Collect all card file load failures and raise
   CardServiceLoadError at end with comprehensive error report. Prevents
   startup with partial card data that causes cryptic runtime errors.
   New exceptions: CardLoadError, CardServiceLoadError

3. GameStateManager.recover_active_games: Return RecoveryResult dataclass
   with recovered count, failed game IDs with error messages, and total.
   Enables proper monitoring and alerting for corrupted game state.

Tests added for energy card error case. Existing tests updated for
new RecoveryResult return type.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:48:06 -06:00
Cal Corum
cd3cc892f4 Implement GameService.execute_action enhancements (GS-003)
Add forced action handling, turn boundary detection, and DB persistence:
- Check for pending forced actions before allowing regular actions
- Only specified player can act during forced action (except resign)
- Only specified action type allowed during forced action
- Detect turn boundaries (turn number OR current player change)
- Persist to Postgres at turn boundaries for durability
- Include pending_forced_action in GameActionResult for client

New exceptions: ForcedActionRequiredError

Tests: 11 new tests covering forced actions, turn boundaries, and
pending action reporting. Total 47 tests for GameService.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 15:15:34 -06:00
Cal Corum
3c75ee0e00 Implement GameService.create_game (GS-002)
- Add create_game method that loads decks via DeckService, converts
  CardDefinitions to CardInstances, and persists to Redis/Postgres
- Build card registry from only the cards in play (not all cards)
- Add GameCreationError exception and GameCreateResult dataclass
- Add creation_engine_factory for DI-based testing (no monkey patching)
- Add helper methods: _cards_to_instances, _build_card_registry
- Update tests with proper mocks for success, deck failure, engine failure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 14:09:11 -06:00
Cal Corum
f93f5b617a Remove legacy modifier field from WeaknessResistance
Migrate all usages to the proper mode/value fields:
- Weakness: mode=MULTIPLICATIVE, value=2
- Resistance: mode=ADDITIVE, value=-30

Remove backwards compatibility code and legacy test.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:16:51 -06:00
Cal Corum
f512c7b2b3 Refactor to dependency injection pattern - no monkey patching
- ConnectionManager: Add redis_factory constructor parameter
- GameService: Add engine_factory constructor parameter
- AuthHandler: New class replacing standalone functions with
  token_verifier and conn_manager injection
- Update all tests to use constructor DI instead of patch()
- Update CLAUDE.md with factory injection patterns
- Update services README with new patterns
- Add socketio README documenting AuthHandler and events

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:54:57 -06:00
Cal Corum
0c810e5b30 Add Phase 4 WebSocket infrastructure (WS-001 through GS-001)
WebSocket Message Schemas (WS-002):
- Add Pydantic models for all client/server WebSocket messages
- Implement discriminated unions for message type parsing
- Include JoinGame, Action, Resign, Heartbeat client messages
- Include GameState, ActionResult, Error, TurnStart server messages

Connection Manager (WS-003):
- Add Redis-backed WebSocket connection tracking
- Implement user-to-sid mapping with TTL management
- Support game room association and opponent lookup
- Add heartbeat tracking for connection health

Socket.IO Authentication (WS-004):
- Add JWT-based authentication middleware
- Support token extraction from multiple formats
- Implement session setup with ConnectionManager integration
- Add require_auth helper for event handlers

Socket.IO Server Setup (WS-001):
- Configure AsyncServer with ASGI mode
- Register /game namespace with event handlers
- Integrate with FastAPI via ASGIApp wrapper
- Configure CORS from application settings

Game Service (GS-001):
- Add stateless GameService for game lifecycle orchestration
- Create engine per-operation using rules from GameState
- Implement action-based RNG seeding for deterministic replay
- Add rng_seed field to GameState for replay support

Architecture verified:
- Core module independence (no forbidden imports)
- Config from request pattern (rules in GameState)
- Dependency injection (constructor deps, method config)
- All 1090 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:21:20 -06:00
Cal Corum
c00ee87f25 Switch to testcontainers for automatic test container management
- Create tests/conftest.py with testcontainers for Postgres and Redis
- Auto-detect Docker Desktop socket and disable Ryuk for compatibility
- Update tests/db/conftest.py and tests/services/conftest.py to use shared fixtures
- Fix test_resolve_effect_logs_exceptions: logger was disabled by pytest
- Fix test_save_and_load_with_real_redis: use redis_url fixture
- Minor lint fix in engine_validation.py

Tests now auto-start containers on run - no need for `docker compose up`
All 1199 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:49:11 -06:00
Cal Corum
0a7c35c262 Add detailed Phase 4 (Game Service + WebSocket) project plan
18 tasks covering:
- WebSocket setup with python-socketio
- GameService lifecycle (create, join, execute, resume, end)
- Message protocol specification
- Connection management with Redis
- Turn timeout system
- Reconnection handling
- REST endpoints for game management
- Unit and integration tests
- Spectator mode (stretch goal)

Includes architecture decisions, sequence diagrams, and acceptance criteria.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:50:57 -06:00
Cal Corum
9e14ab906f Add services layer README documentation
Documents:
- Architecture principles (stateless, DI, repository protocol)
- Services overview table
- Key patterns (config from request, UNSET sentinel, repository injection)
- Service details with usage examples
- Testing approach with examples

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:40:56 -06:00
Cal Corum
ebe776d54d Update project plans and documentation for Phase 3 completion
Project plan updates:
- Mark all 14 Phase 3 tasks as completed
- Update acceptance criteria to met
- Update master plan status to Phase 4 next
- Add detailed deliverables list for Phase 3

Documentation updates:
- Add UNSET sentinel pattern to CLAUDE.md
- Document when to use UNSET vs None for nullable fields

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:26:01 -06:00
Cal Corum
7d397a2e22 Fix medium priority issues from code review
UNSET sentinel pattern:
- Add UNSET sentinel in protocols.py for nullable field updates
- Fix inability to clear deck description (UNSET=keep, None=clear)
- Fix repository inability to clear validation_errors

Starter deck improvements:
- Remove unused has_starter_deck from CollectionService
- Add deprecation notes to old starter deck methods

Validation improvements:
- Add energy type validation in deck_validator.py
- Add energy type validation in deck schemas
- Add VALID_ENERGY_TYPES constant

Game loading fix:
- Fix get_deck_for_game silently skipping invalid cards
- Now raises ValueError with clear error message

Tests:
- Add TestEnergyTypeValidation test class
- Add TestGetDeckForGame test class
- Add tests for validate_energy_types utility function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 14:32:08 -06:00
Cal Corum
3ec670753b Fix security and validation issues from code review
Critical fixes:
- Add admin API key authentication for admin endpoints
- Add race condition protection via unique partial index for starter decks
- Make starter deck selection atomic with combined method

Moderate fixes:
- Fix DI pattern violation in validate_deck_endpoint
- Add card ID format validation (regex pattern)
- Add card quantity validation (1-99 range)
- Fix exception chaining with from None (B904)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 14:16:07 -06:00
Cal Corum
58349c126a Phase 3: Collections + Decks - Services and DI architecture
Implemented with Repository Protocol pattern for offline fork support:
- CollectionService with PostgresCollectionRepository
- DeckService with PostgresDeckRepository
- DeckValidator with DeckConfig + CardService injection
- Starter deck definitions (5 types: grass, fire, water, psychic, lightning)
- Pydantic schemas for collection and deck APIs
- Unit tests for DeckValidator (32 tests passing)

Architecture follows pure dependency injection - no service locator patterns.
Added CLAUDE.md documenting DI requirements and patterns.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 11:27:14 -06:00
Cal Corum
4859b2a9cb Add detailed Phase 3 (Collections + Decks) project plan
14 tasks covering card ownership, deck building, validation, and
starter deck selection. 29 estimated hours total.

Key components:
- CollectionService for card ownership CRUD
- DeckService with slot limits and validation
- DeckValidator for rule enforcement
- 5 starter deck definitions
- REST API endpoints for collections and decks
- ~80-90 tests planned

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 00:40:25 -06:00
Cal Corum
f82bc8aa1f Fix OAuth callback redirect and deps import
- Discord OAuth callback now redirects to frontend with tokens in URL fragment
  (more secure than query params - fragment not sent to server)
- Error responses also redirect with error in query params
- Fix deps.py import: get_session_dependency -> get_session

Part of Phase 2 Authentication completion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 00:17:27 -06:00
Cal Corum
5cf2198542 Add engine validation script with attack_coin_status effect handler
- Add attack_coin_status effect handler for coin-flip status conditions
  (e.g., Thunder Shock paralysis on heads)
- Create comprehensive engine_validation.py script (~1250 lines) that
  validates game engine behavior with 29 test cases:
  - Illegal moves (attack without energy, wrong turn, evolution rules)
  - Energy mechanics (attachment limits, cost validation)
  - Weakness calculation (+20 additive mode)
  - Status conditions (paralysis blocks actions, poison damage)
  - Knockout flow (points, forced actions, state cleanup)
  - Win conditions (4 points triggers game over)
- Update game_walkthrough.py Thunder Shock to use new effect handler
- Interactive prompts between sections (Enter to continue, q to quit)
- Uses seed=42 for deterministic, reproducible coin flips

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 00:15:12 -06:00
Cal Corum
3ad79a4860 Fix OAuth absolute URLs and add account linking endpoints
- Add base_url config setting for OAuth callback URLs
- Change OAuth callbacks from relative to absolute URLs
- Add account linking OAuth flow (GET /auth/link/{provider})
- Add unlink endpoint (DELETE /users/me/link/{provider})
- Add AccountLinkingError and service methods for linking
- Add 14 new tests for linking functionality
- Update Phase 2 plan to mark complete (1072 tests passing)
2026-01-27 22:06:22 -06:00
Cal Corum
996c43fbd9 Implement Phase 2: Authentication system
Complete OAuth-based authentication with JWT session management:

Core Services:
- JWT service for access/refresh token creation and verification
- Token store with Redis-backed refresh token revocation
- User service for CRUD operations and OAuth-based creation
- Google and Discord OAuth services with full flow support

API Endpoints:
- GET /api/auth/{google,discord} - Start OAuth flows
- GET /api/auth/{google,discord}/callback - Handle OAuth callbacks
- POST /api/auth/refresh - Exchange refresh token for new access token
- POST /api/auth/logout - Revoke single refresh token
- POST /api/auth/logout-all - Revoke all user sessions
- GET/PATCH /api/users/me - User profile management
- GET /api/users/me/linked-accounts - List OAuth providers
- GET /api/users/me/sessions - Count active sessions

Infrastructure:
- Pydantic schemas for auth/user request/response models
- FastAPI dependencies (get_current_user, get_current_premium_user)
- OAuthLinkedAccount model for multi-provider support
- Alembic migration for oauth_linked_accounts table

Dependencies added: email-validator, fakeredis (dev), respx (dev)

84 new tests, 1058 total passing
2026-01-27 21:49:59 -06:00
Cal Corum
4ddc9b8c30 Add detailed Phase 2 (Authentication) project plan
Defines 15 tasks covering OAuth login (Google/Discord), JWT session
management, user services, and API endpoints for player authentication
at play.mantimon.com.

Key components:
- JWT utilities with access/refresh token pattern
- Redis-backed refresh token storage for revocation
- Google and Discord OAuth services
- FastAPI auth dependencies (get_current_user, etc.)
- Account linking support (multiple OAuth providers per user)
- Premium subscription tracking

Estimated: 24 hours across 1-2 weeks
2026-01-27 16:25:57 -06:00
Cal Corum
b78236ac49 Simplify card image URLs to use set-based paths
- Derive image_path from card ID instead of raw data image_file field
- Use simplified CDN paths: /<set>/<card>.webp (e.g., a1/033-charmander.webp)
- Energy cards use basic/<type>.webp paths
- Fix undefined variable bug in transform_trainer_card
- Update tests to match new path format
- Regenerate all 382 card definitions with correct image_url fields
2026-01-27 16:16:51 -06:00
Cal Corum
e237a0deb6 Add image_url to card definitions from source data
The converter now falls back to using the source image_url from raw
data when image_file is not available (images not downloaded locally).

- Pokemon cards: use source image_url when image_file is null
- Trainer cards: same fallback behavior
- Regenerated all 382 card definitions with image URLs

Images point to pokemon-zone.com assets for now. When we host our
own images, cards with image_file will use our CDN instead.
2026-01-27 15:55:10 -06:00
Cal Corum
b95453569b Update Phase 1 plan with gap fixes and Phase 2 prerequisites 2026-01-27 15:38:49 -06:00
Cal Corum
2a95316f04 Add FastAPI lifespan hooks and fix Phase 1 gaps
- Add lifespan context manager to app/main.py with startup/shutdown hooks
- Wire startup: init_db(), init_redis(), CardService.load_all()
- Wire shutdown: close_db(), close_redis()
- Add /health/ready endpoint for readiness checks
- Add CORS middleware with configurable origins
- Disable docs in production (only available in dev)

- Export get_session_dependency from app/db/__init__.py for FastAPI DI
- Add game_cache_ttl_seconds to Settings (configurable, was hardcoded)
- Fix datetime.utcnow() deprecation (4 occurrences) -> datetime.now(UTC)
- Update test to match S3 image URL (was placeholder CDN)

All 974 tests passing.
2026-01-27 15:37:19 -06:00
Cal Corum
c3c0a310a7 Mark Phase 1 Database complete - all 18 tasks done 2026-01-27 15:22:43 -06:00
Cal Corum
adb55dec12 Fix scraper to preserve energy types in effect text
The source website uses <span class='energy-text energy-text--type-fire'>
to render inline energy icons. BeautifulSoup's get_text() was stripping
these spans, losing the energy type information and causing merged text
like 'Discard aEnergy' instead of 'Discard a Fire Energy'.

Changes:
- Add ENERGY_TEXT_TYPES mapping for inline energy references
- Add replace_energy_text_spans() to convert spans to text before extraction
- Add extract_effect_text() helper with proper text joining (separator=' ')
- Update parse_attack(), parse_ability(), _parse_trainer_details() to use it
- Fix JSON encoding in convert_cards.py to use UTF-8 (ensure_ascii=False)

Before: 'Discard an Energy from this Pokémon'
After:  'Discard a Fire Energy from this Pokémon'

Re-scraped all 372 cards and regenerated 382 definitions.
2026-01-27 15:10:02 -06:00
Cal Corum
c6e3695760 Fix card data pipeline: fossil cards and text artifacts
Scraper fixes:
- Detect fossil cards (Helix/Dome Fossil, Old Amber) as Trainer/Item cards
- Add text artifact cleaning for stripped energy icons:
  - 'aEnergy' -> 'an Energy'
  - 'extraEnergy' -> 'extra Energy'
  - 'BenchedPokémon' -> 'Benched Pokémon'
  - And 20+ other common patterns

Converter improvements:
- Add evolution chain validation to detect broken evolves_from references
- Track conversion errors and validation warnings in _index.json
- Return errors from convert_set() for better debugging

Data fixes:
- Fixed 4 fossil cards (now correctly typed as trainer/item)
- Fixed text artifacts in 46 raw card files
- Regenerated all 382 card definitions
- All evolution chains now valid

Added fix_raw_text.py utility script for batch text cleanup.
2026-01-27 14:37:03 -06:00
Cal Corum
934aa4c443 Add CardService and card data conversion pipeline
- Rename data/cards/ to data/raw/ for scraped data
- Add data/definitions/ as authoritative card data source
- Add convert_cards.py script to transform raw -> definitions
- Generate 378 card definitions (344 Pokemon, 24 Trainers, 10 Energy)
- Add CardService for loading and querying card definitions
  - In-memory indexes for fast lookups by type, set, pokemon_type
  - search() with multiple filter criteria
  - get_all_cards() for GameEngine integration
- Add SetInfo model for set metadata
- Update Attack model with damage_display field for variable damage
- Update CardDefinition with image_path, illustrator, flavor_text
- Add 45 tests (21 converter + 24 CardService)
- Update scraper output path to data/raw/

Card data is JSON-authoritative (no database) to support offline fork goal.
2026-01-27 14:16:40 -06:00
Cal Corum
29ab0b3d84 Add GameStateManager service with Redis/Postgres dual storage
- Implement GameStateManager with Redis-primary, Postgres-backup pattern
- Cache operations: save_to_cache, load_from_cache, delete_from_cache
- DB operations: persist_to_db, load_from_db, delete_from_db
- High-level: load_state (cache-first), delete_game, recover_active_games
- Query helpers: get_active_game_count, get_player_active_games
- Add 22 tests for GameStateManager (87% coverage)
- Add 6 __repr__ tests for all DB models (100% model coverage)
2026-01-27 10:59:58 -06:00
Cal Corum
50684a1b11 Add database infrastructure with SQLAlchemy models and test suite
Phase 1 Database Implementation (DB-001 through DB-012):

Models:
- User: OAuth support (Google/Discord), premium subscriptions
- Collection: Card ownership with CardSource enum
- Deck: JSONB cards/energy_cards, validation state
- CampaignProgress: One-to-one with User, medals/NPCs as JSONB
- ActiveGame: In-progress games with GameType enum
- GameHistory: Completed games with EndReason enum, replay data

Infrastructure:
- Alembic migrations with sync psycopg2 (avoids async issues)
- Docker Compose for Postgres (5433) and Redis (6380)
- App config with Pydantic settings
- Redis client helper

Test Infrastructure:
- 68 database tests (47 model + 21 relationship)
- Async factory pattern for test data creation
- Sync TRUNCATE cleanup (solves pytest-asyncio event loop mismatch)
- Uses dev containers instead of testcontainers for reliability

Key technical decisions:
- passive_deletes=True for ON DELETE SET NULL relationships
- NullPool for test sessions (no connection reuse)
- expire_on_commit=False with manual expire() for relationship tests
2026-01-27 10:17:30 -06:00
Cal Corum
e82b204849 Complete card scrape for Genetic Apex (A1) and Mythical Island (A1a)
- Fix is_ex detection to only match ' ex' suffix (fixes Exeggutor false positive)
- Fix trainer card detection with specific 'Trainer | Type' pattern
- Improve trainer effect text extraction from content body
- Scrape full A1 set: 286 cards
- Scrape full A1a set: 86 cards
- Total: 372 cards with images (some images failed due to rate limiting)
- Re-scraped affected cards to fix is_ex and trainer type issues
2026-01-26 23:21:08 -06:00
Cal Corum
5e1229aa7c Add image download support to card scraper
- Add --images flag to download images during scraping
- Add --download-images command to fetch images for existing card data
- Images saved to data/images/{set}/ directory
- Card JSON updated with image_file field (relative path)
- Uses Referer header for asset server compatibility
- Supports skip-if-exists for incremental downloads
2026-01-26 22:55:13 -06:00
Cal Corum
2517d241ac Add Pokemon Pocket card scraper for offline card data
- Add scrape_pokemon_pocket.py script to fetch card data from pokemon-zone.com
- Scrapes Pokemon, Trainer, and Energy cards with full metadata
- Includes image URLs for offline caching support
- Supports --set, --card, --limit, and --reindex CLI options
- Add beautifulsoup4 and requests as dev dependencies
- Create data/cards/ directory structure for card JSON files
2026-01-26 22:52:20 -06:00
Cal Corum
3ed67ea16b Add Active Effects system design and module README files
Design Documentation:
- docs/ACTIVE_EFFECTS_DESIGN.md: Comprehensive design for persistent effect system
  - Data model (ActiveEffect, EffectTrigger, EffectScope, StackingMode)
  - Core operations (register, remove, query effects)
  - Integration points (damage calc, energy counting, retreat, lifecycle)
  - Effect categories from Pokemon Pocket card research (~372 cards)
  - Example implementations (Serperior, Greninja, Mr. Mime, Victreebel)
  - Post-launch TODO for generic modifier system

Module README Files:
- backend/app/core/README.md: Core engine overview and key classes
- backend/app/core/effects/README.md: Effects module index and quick reference
- backend/app/core/models/README.md: Models module with relationship diagram

Minor cleanup:
- Revert Bulbasaur weakness to Fire (was test change for Lightning)
- Clean up debug output in game walkthrough
2026-01-26 22:39:02 -06:00