# 13. Implementation Phases and Checklists [< Back to Index](README.md) | [Next: Risks and Open Questions >](14-risks.md) --- ## Phase 0 — Render Pipeline Optimization (Week 0, Pre-Requisite) Focus: Persistent browser, self-hosted fonts, concurrent upload pipeline. This phase is **independent of evolution** and benefits all existing card generation workflows immediately. Should be implemented before any evolution work begins. **Deliverables:** - Persistent Chromium browser instance in API server (replaces per-request browser launch) - FastAPI lifespan hooks for browser startup/shutdown with auto-reconnect - Self-hosted fonts: download Source Sans 3 + Open Sans WOFF2 files, base64-embed in template - Remove Google Fonts CDN `@import` from `style.html` - Concurrent upload pipeline in `pd_cards/core/upload.py` using `asyncio.Semaphore(8)` + `asyncio.gather` - Increase `fetch_card_image` timeout from 6s to 10s to account for render queue contention - Benchmark: measure per-card render time before/after, total run time for full cardset **Implementation details:** See [02-architecture.md § Card Render Pipeline Optimization](02-architecture.md#card-render-pipeline-optimization) **Success Criteria:** Per-card render time drops from ~3s to ~0.6-1.0s. Full 800-card upload run completes in under 5 minutes (down from ~40 minutes). No external font CDN dependency. **Files changed:** | File | Change | |---|---| | `database/app/routers_v2/players.py` | Persistent browser, page-per-request pattern | | `database/app/main.py` | FastAPI lifespan hooks | | `database/storage/templates/style.html` | `@font-face` with base64 WOFF2 | | `database/storage/fonts/` | Font files (new directory) | | `pd_cards/core/upload.py` | `asyncio.gather` with semaphore | | `check_cards_and_upload.py` | Same concurrency pattern (legacy script) | --- ## 13.1 Phase 1 — Foundation (Weeks 1-3) Focus: Database schema, lazy evaluation engine, tier badge display, basic Discord commands. **Deliverables:** - `player_season_stats` materialized aggregate table created (reference Major Domo implementation) - Incremental update logic in post-game callback for `player_season_stats` - Backfill script to populate `player_season_stats` from existing `stratplay`/`decision` data - All evolution database tables created with migrations - `evolution_card_state` initialization on card acquisition (pack opening hook): create if no `(player_id, team_id)` state exists, otherwise inherit existing state - API endpoints: track catalog, card state GET, evaluate POST - Post-game callback integration: evaluate participating cards after each game - Milestone evaluation engine for all stratplay/decision/stratgame milestone types - Bot command: `!evo status` (roster-wide view and per-card view) - Basic Discord embed: tier badge on card name - No rating boosts yet — tier advances are tracked but boosts deferred to Phase 2 - Three universal evolution tracks seeded in database (Batter, Starting Pitcher, Relief Pitcher) - Backfill job: initialize `evolution_card_state` for all unique `(player_id, team_id)` pairs with `progress_since = earliest card.created_at` **Success Criteria:** All cards have evolution state. Players can see tier progress. Milestones complete automatically after games. ## 13.2 Phase 2 — Rating Boosts (Weeks 4-6) Focus: Apply rating boosts on tier completion, variant card API. **Deliverables:** - `apply_evolution_boosts()` function in card-creation repo - Batter and pitcher player profile detection from existing ratings - Tier boost application on tier completion: writes new variant rows to battingcardratings/pitchingcardratings - `evolution_tier_boost` audit table populated - Updated card API: `card_id` param for variant resolution - Discord notification on milestone and tier completion (with boost summary) - T4 rarity upgrade (universal, capped at HoF) - Evolved card display name (`[Team]'s Evolved [Player]`) **Success Criteria:** Tier completion triggers rating boost. Evolved cards show higher ratings in card embed. ## 13.3 Phase 3 — Static Cosmetics (Weeks 7-10) Focus: Premium static cosmetics, visual differentiation, economy tuning. **Deliverables:** - Cosmetics catalog seeded in database (static cosmetics only: frames, themes, badges) - Cosmetics purchase and display endpoints - Cosmetic CSS injection in card HTML template (frame overlays, background colors, badges) - Variant render pipeline: Playwright renders cards with cosmetic CSS, uploads to S3 - `battingcard.image_url` column populated for variant rows - Bot image resolution updated: variant → `battingcard.image_url` fallback to `player.image` - Verify all three tracks work correctly across all rarity levels (Rep through HoF) - Discord embed accent colors per tier (T2 gold, T3 purple, T4 teal) - Analytics query for admin: evolution completion rates, milestone drop-off, cosmetic revenue - Economy review: check cosmetics pricing against actual currency circulation data **Success Criteria:** Static cosmetics purchasable and visually distinct. Full T0-T4 evolution journey works end to end including cosmetics. ## 13.4 Phase 4 — Animated Cosmetics (Weeks 11-13) Focus: APNG render pipeline, animated cosmetic effects. **Prerequisite:** Phase 0 (persistent browser) must be complete. The APNG pipeline depends on the persistent browser for efficient multi-frame capture. **Deliverables:** - Deterministic multi-frame capture using `--anim-progress` CSS custom property - `capture_animated_frames()` function using persistent browser page pool - APNG assembly from frame buffers using `apng` Python library (`pip install apng`) - CSS custom property-driven animations in card HTML template (no `@keyframes` for animated cosmetics) - Animated cosmetics added to catalog: Holographic Frame, Rarity Glow (Subtle/Strong), T4 Badge Sparkle - S3 upload with `.apng` extension and `image/apng` content type and `CacheControl: public, max-age=31536000` - File size validation (target <4 MB per animated card) - Bot display verified: APNG auto-plays in Discord embeds (desktop + mobile) - Performance benchmarking: target ~4.5s for 8-frame, ~6s for 12-frame animated variant - pngquant integration for frame compression if file sizes exceed budget **Success Criteria:** Animated cosmetics render correctly as APNG, auto-play in Discord embeds, file sizes within budget, render time <8 seconds per animated variant. --- ## 13.5 Render Pipeline Optimization Checklist (Phase 0) - [ ] Download Source Sans 3 (400, 700) and Open Sans (300, 400, 700) WOFF2 files - [ ] Base64-encode font files and create `@font-face` declarations - [ ] Replace `@import url('fonts.googleapis.com/...')` in `style.html` with local `@font-face` — PR #96 (next-release) - [ ] Verify card renders identically with self-hosted fonts (visual diff) - [x] Create `get_browser()` / `shutdown_browser()` functions in players.py — merged 2026.3.17 - [x] Add FastAPI lifespan hooks for browser startup/shutdown — merged 2026.3.17 - [x] Replace `async with async_playwright()` block with persistent browser + page-per-request — merged 2026.3.17 - [x] Add `is_connected()` check with automatic browser reconnect — implemented via `asyncio.Lock`, merged 2026.3.17 - [ ] Test: render 10 cards sequentially, verify no browser leaks (page count stays at 0 between renders) - [ ] Test: concurrent renders (4 simultaneous requests) complete without errors - [ ] Benchmark: measure per-card render time (target: <1.0s, down from ~3.0s) — script in PR #95 (next-release) - [ ] Refactor `pd_cards/core/upload.py` loop to use `asyncio.Semaphore(8)` + `asyncio.gather` - [ ] Add error handling: individual card failures don't abort the batch - [ ] Add progress reporting: log every N completions (not every 20 starts) - [ ] Increase `fetch_card_image` timeout from 6s to 10s - [ ] Benchmark: full 800-card upload run (target: <5 minutes) - [ ] Update `check_cards_and_upload.py` legacy script with same concurrency pattern - [ ] Deploy to dev, run full cardset upload, verify all cards render correctly - [x] Deploy to production — persistent browser deployed 2026.3.17 ## 13.6 Database Checklist - [ ] Write Peewee model for `evolution_track` — pending PR #84 (card-evolution) - [ ] Write Peewee model for `evolution_milestone` — pending PR #84 (card-evolution) - [ ] Write Peewee model for `evolution_card_state` — pending PR #84 (card-evolution) - [ ] Write Peewee model for `evolution_progress` - [ ] Write Peewee model for `evolution_tier_boost` — stub schema in PR #84 - [ ] Write Peewee model for `evolution_cosmetic` — stub schema in PR #84 - [ ] Write migration scripts for all new tables (idempotent) — pending PR #84 (card-evolution) - [x] Write Peewee model for `player_season_stats` — split into `BattingSeasonStats` + `PitchingSeasonStats` in `app/models/season_stats.py`, merged 2026.3.17 - [ ] Implement incremental update in post-game callback (delta-based, not full recompute) - [ ] Add `last_game_id` check to prevent double-counting on callback retries - [ ] Write backfill script to populate from existing `stratplay`/`decision` rows - [ ] Verify backfill totals match on-demand `GROUP BY` queries for a sample of players - [ ] Reference Major Domo implementation for patterns and edge cases - [x] Seed three universal evolution tracks (Batter, SP, RP) — seed data + JSON in `app/seed/evolution_tracks.py`, merged 2026.3.17; runnable once migration (PR #84) lands - [ ] Add `image_url` nullable column to `battingcard` and `pitchingcard` — in PR #84 migration - [ ] Add `variant` column to `card` table (integer, default 0) — in PR #84 migration - [ ] Verify `battingcard.variant` UNIQUE constraint behavior with hash-based integers - [ ] Index all FK columns and query-hot columns on new tables — indexes in PR #84 migration - [ ] Ensure `player_season_stats` has appropriate indexes for `(team_id, player_id)` lookups — indexes in PR #84 migration - [ ] Test schema in dev environment before production migration - [ ] Write and test backfill script: create `evolution_card_state` for each unique `(player_id, team_id)` pair, propagate variant to all matching card instances ## 13.7 Card Creation System Checklist - [ ] Implement `apply_evolution_boosts(card_ratings_df, boost_tier, player_profile)` in `creation_helpers.py` or `batters/calcs_batter.py` - [ ] Implement batter player profile detection (power, contact, patient) from existing ratings - [ ] Implement pitcher player profile detection (SP power, SP control, RP) from existing ratings - [ ] Write unit tests for boost distribution: verify column sums remain valid post-boost - [ ] Write unit tests for rating cap enforcement: verify boosts truncate at caps (e.g., hold at -5) - [ ] Write unit tests for cap truncation: verify excess budget is discarded (not redistributed) when a stat hits its cap - [ ] Write unit test for rarity upgrade eligibility check (HoF ceiling) - [ ] Integrate `apply_evolution_boosts()` with API endpoint trigger (or standalone script callable by API service) - [ ] Implement `compute_variant_hash()` function for deterministic variant numbering - [ ] Handle variant field correctly: check for existing variant row before creating new one - [ ] Implement cosmetic CSS injection in card HTML template (frames, backgrounds, badges) - [ ] Implement CSS `--anim-progress` custom property animations for animated cosmetics (holographic, glow pulse, sparkle) - [ ] Implement multi-frame Playwright capture for animated variants - [ ] Implement APNG assembly from frame buffers - [ ] Add file size validation for animated output - [ ] Add CLI command: `pd-cards evolution apply-boost --card-id --tier ` for admin use - [ ] Add CLI command: `pd-cards evolution render-variant --player-id --variant ` for manual re-renders ## 13.8 API Development Checklist - [x] Implement track catalog endpoints (GET list, GET single) — `GET /api/v2/evolution/tracks` and `/tracks/{id}` in `app/routers_v2/evolution.py`, merged 2026.3.17; returns 500 until PR #84 (EvolutionTrack model) lands - [ ] Implement track milestones endpoint (GET milestones for track) - [x] Implement season stats CRUD — full REST endpoints in `app/routers_v2/season_stats.py`, merged 2026.3.17 - [x] Formula engine — `app/services/formula_engine.py`, batter OPS + pitcher ERA/WHIP/K formulas, merged 2026.3.17 - [ ] Implement card state endpoints (GET by team, GET by card) - [ ] Implement POST evaluate endpoint — in PR #98 (card-evolution); `POST /api/v2/evolution/cards/{card_id}/evaluate` with evaluator service - [ ] Implement cosmetics endpoints (GET catalog, GET card cosmetics, POST purchase, DELETE deactivate) - [ ] Modify battingcard/pitchingcard endpoints to accept optional `card_id` param - [ ] Implement variant resolution logic in card endpoints using `evolution_card_state` - [ ] Implement admin endpoints for track/milestone management and manual boost application - [ ] Implement idempotent milestone evaluator (recalculate from source, not increment) - [ ] Render endpoint: detect animated vs static cosmetics, route to correct pipeline - [ ] Write API integration tests for full T0-T4 evolution lifecycle - [ ] Write API integration tests for cosmetics purchase flow (static + animated) - [ ] Document all new endpoints in API reference ## 13.9 Bot Integration Checklist - [ ] Add `!evo status` command with roster-wide formatted embed - [ ] Add `!evo card ` command for detailed single-card milestone breakdown - [ ] Add `!evo cosmetics ` command for premium cosmetics browsing - [ ] Integrate post-game evolution evaluator in post-game processing pipeline - [ ] Verify evaluator fires after game resolution in all modes (campaign, gauntlet, exhibition, unlimited) - [ ] Add milestone completion notification to team channel - [ ] Add tier completion notification with rating boost summary - [ ] Add Full Evolution notification with rarity upgrade announcement - [ ] Update card display embed to show tier badge from `evolution_card_state.current_tier` - [ ] Update card display embed to show evolved name for fully evolved cards - [ ] Update card display embed to apply active cosmetic styling - [ ] Verify APNG auto-plays correctly in Discord embeds across desktop and mobile - [ ] Update trade processing: set traded card's variant to 0, check for existing state on new team's `(player_id, team_id)`, create fresh state if none exists ## 13.10 Testing Checklist - [ ] Unit tests: `apply_evolution_boosts()` for all player profiles and tiers - [ ] Unit tests: milestone evaluation logic for all challenge types - [ ] Unit tests: variant resolution in card endpoint - [ ] Unit tests: idempotent evaluator (calling twice yields same result) - [ ] Unit tests: APNG assembly produces valid APNG output - [ ] Integration tests: game -> post-game callback -> milestone completion -> tier boost full flow - [ ] Integration tests: T4 rarity upgrade across all rarity levels (Rep→Res, Sta→All, MVP→HoF, HoF stays HoF) - [ ] Integration tests: evolved card state and name update correctly after trade - [ ] Integration tests: cosmetics remain on selling team's `(player_id, team_id)` state after trade - [ ] Integration tests: animated cosmetic purchase triggers APNG render pipeline - [ ] Load test: milestone query latency against player_season_stats with large dataset - [ ] Regression test: base card ratings unaffected by evolution variant writes - [ ] Regression test: scouting report generation ignores variant rows - [ ] Visual test: animated APNG renders correctly in Discord (desktop + mobile) - [ ] Visual test: APNG graceful degradation (first frame displays if client doesn't support animation) ## 13.11 Security and Balance Checklist - [ ] Verify cosmetic purchase currency deducted atomically (wallet check + deduction in single transaction) - [ ] Verify card ownership validated before cosmetic purchase (card.team_id == requesting team_id) - [ ] Verify cosmetics are non-transferable on trade (team_id check in display layer) - [ ] Review milestone thresholds against expected games-per-week before launch - [ ] Verify rarity cap enforcement (no evolution beyond HoF) - [ ] Verify individual stat column caps enforced during boost application (e.g., hold ≥ -5) - [ ] Verify evolved card stats cannot exceed designed boost budget (4.0 chances/108 total) via any combination of track + boost ## 13.12 Deployment Checklist - [ ] Run schema migration in dev, verify all tables and indexes created correctly — blocked on PR #84 - [ ] Seed evolution tracks and milestones in dev — seed script ready, blocked on schema - [ ] Run backfill job on dev copy of card inventory, verify all cards get state records - [ ] Run full Phase 1 test suite in dev environment - [ ] Deploy API changes to dev, smoke test all new endpoints — season stats + formula engine deployed to prod 2026.3.17; track catalog deployed but non-functional (missing model) - [ ] Deploy bot changes to dev instance, run a game to verify post-game evaluator fires - [ ] Demo full T0 -> T1 progression flow in dev - [ ] Cal approves dev demo - [ ] Run schema migration in production during low-traffic window - [ ] Run backfill job in production for existing card inventory - [ ] Deploy API changes to production - [ ] Deploy bot changes to production - [ ] Monitor milestone query latency for first 48 hours - [ ] Monitor tier completion rate and cosmetic purchase rate after first week - [ ] Schedule economy review at Day 30