Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
17 KiB
13. Implementation Phases and Checklists
< Back to Index | Next: Risks and Open Questions >
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
@importfromstyle.html - Concurrent upload pipeline in
pd_cards/core/upload.pyusingasyncio.Semaphore(8)+asyncio.gather - Increase
fetch_card_imagetimeout 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
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_statsmaterialized aggregate table created (reference Major Domo implementation)- Incremental update logic in post-game callback for
player_season_stats - Backfill script to populate
player_season_statsfrom existingstratplay/decisiondata - All evolution database tables created with migrations
evolution_card_stateinitialization 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_statefor all unique(player_id, team_id)pairs withprogress_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_boostaudit table populated- Updated card API:
card_idparam 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_urlcolumn populated for variant rows- Bot image resolution updated: variant →
battingcard.image_urlfallback toplayer.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-progressCSS custom property capture_animated_frames()function using persistent browser page pool- APNG assembly from frame buffers using
apngPython library (pip install apng) - CSS custom property-driven animations in card HTML template (no
@keyframesfor animated cosmetics) - Animated cosmetics added to catalog: Holographic Frame, Rarity Glow (Subtle/Strong), T4 Badge Sparkle
- S3 upload with
.apngextension andimage/apngcontent type andCacheControl: 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-facedeclarations - Replace
@import url('fonts.googleapis.com/...')instyle.htmlwith local@font-face— PR #96 (next-release) - Verify card renders identically with self-hosted fonts (visual diff)
- Create
get_browser()/shutdown_browser()functions in players.py — merged 2026.3.17 - Add FastAPI lifespan hooks for browser startup/shutdown — merged 2026.3.17
- Replace
async with async_playwright()block with persistent browser + page-per-request — merged 2026.3.17 - Add
is_connected()check with automatic browser reconnect — implemented viaasyncio.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.pyloop to useasyncio.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_imagetimeout from 6s to 10s - Benchmark: full 800-card upload run (target: <5 minutes)
- Update
check_cards_and_upload.pylegacy script with same concurrency pattern - Deploy to dev, run full cardset upload, verify all cards render correctly
- 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)
- Write Peewee model for
player_season_stats— split intoBattingSeasonStats+PitchingSeasonStatsinapp/models/season_stats.py, merged 2026.3.17 - Implement incremental update in post-game callback (delta-based, not full recompute)
- Add
last_game_idcheck to prevent double-counting on callback retries - Write backfill script to populate from existing
stratplay/decisionrows - Verify backfill totals match on-demand
GROUP BYqueries for a sample of players - Reference Major Domo implementation for patterns and edge cases
- 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_urlnullable column tobattingcardandpitchingcard— in PR #84 migration - Add
variantcolumn tocardtable (integer, default 0) — in PR #84 migration - Verify
battingcard.variantUNIQUE 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_statshas 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_statefor 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)increation_helpers.pyorbatters/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-progresscustom 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 <id> --tier <n>for admin use - Add CLI command:
pd-cards evolution render-variant --player-id <id> --variant <hash>for manual re-renders
13.8 API Development Checklist
- Implement track catalog endpoints (GET list, GET single) —
GET /api/v2/evolution/tracksand/tracks/{id}inapp/routers_v2/evolution.py, merged 2026.3.17; returns 500 until PR #84 (EvolutionTrack model) lands - Implement track milestones endpoint (GET milestones for track)
- Implement season stats CRUD — full REST endpoints in
app/routers_v2/season_stats.py, merged 2026.3.17 - 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}/evaluatewith evaluator service - Implement cosmetics endpoints (GET catalog, GET card cosmetics, POST purchase, DELETE deactivate)
- Modify battingcard/pitchingcard endpoints to accept optional
card_idparam - 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 statuscommand with roster-wide formatted embed - Add
!evo card <card_id>command for detailed single-card milestone breakdown - Add
!evo cosmetics <card_id>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