---
title: "Card Evolution Phase 1a — Schema & Data Foundation (dev branch)"
description: "Phase 1a implementation details for Card Evolution on paper-dynasty-database card-evolution branch: 5 work packages, Peewee models, migration, seed data, season stats service"
type: context
domain: paper-dynasty
tags: [paper-dynasty, evolution, database, peewee, migration, development]
---

# Card Evolution Phase 1a — Schema & Data Foundation

**Status:** Merged into `card-evolution` dev branch (PR #104, 2026-03-18)
**NOT deployed to production** — sitting on long-lived `card-evolution` branch on paper-dynasty-database repo.
**Project plan:** `card-creation/docs/prd-evolution/PHASE1_PROJECT_PLAN.md`

## What was implemented (5 work packages, 25+ tests)

### WP-01: Evolution Peewee Models
- 4 new models in `app/db_engine.py`: `EvolutionTrack`, `EvolutionCardState`, `EvolutionTierBoost`, `EvolutionCosmetic`
- Uses `ModelIndex` + `add_index()` for composite unique constraints
- Follows existing BaseModel pattern in the monolithic db_engine.py

### WP-02: PlayerSeasonStats Model
- 25 stat columns (14 batting + 11 pitching), all `IntegerField(default=0)`
- FK to `StratGame` for `last_game` tracking (double-count prevention)
- 3 indexes: unique (player, team, season) + 2 query indexes

### WP-03: Track Seed Data
- `app/seed/evolution_tracks.json` — 3 tracks (Batter, SP, RP) with formulas and thresholds
- `app/seed/evolution_tracks.py` — idempotent `seed_evolution_tracks()` using get_or_create + update on rerun
- Uses `logging.getLogger(__name__)` (not print)

### WP-04: SQL Migration
- `migrations/2026-03-17_add_evolution_tables.sql` — 5 new tables + 3 ALTER TABLE columns
- `IF NOT EXISTS` pattern, `BEGIN/COMMIT` wrapping
- **Critical fix:** FK references use `player(player_id)` not `player(id)` — Player PK is `player_id`

### WP-05: Season Stats Service
- `app/services/season_stats.py` — `update_season_stats(game_id)` entry point
- Dual-backend upsert: PostgreSQL `ON CONFLICT ... DO UPDATE EXCLUDED` / SQLite `get_or_create` + field addition
- Aggregates StratPlay rows into batting/pitching groups, merges Decision records
- Double-count prevention via `last_game` check

## Test infrastructure
- `conftest.py` updated with all 5 new models in `_TEST_MODELS` and shared fixtures (`track`, extended `player`, `team`)
- `ruff.toml` added with F403/F405 ignore for peewee star imports in db_engine.py
- Tests use `DATABASE_TYPE=postgresql` env var + SQLite rebind pattern

## Key technical decisions
- **Player PK is `player_id`**, not `id` — all FKs must reference `player(player_id)`
- **Career-scoped tiers** — permanent once earned, progress accumulates across seasons
- **No retroactive backfill** — evolution launches fresh at Season 11, all T0
- **Single metric per track** with escalating thresholds (no multi-milestone tiers)

## What's next: Phase 1b — API & Evaluation Engine
- WP-07: Card State API endpoints (issue #72)
- WP-08: Evaluate endpoint / formula engine (issue #73)
- WP-10: Pack opening hook — evolution_card_state init (issue #75)
- WP-07 and WP-08 can be parallelized
