# FullCard Migration Status **Branch:** `feature/fullcard-migration` (5 commits ahead of main) **Last Updated:** 2026-02-26 ## What This Branch Does Moves card-building logic (fitting continuous chances to discrete 2d6×d20 card mechanics) from the database to Python. Previously, Python sent raw continuous values and the database fitted them — meaning what you sent ≠ what got stored. Now Python builds the complete discrete card structure before POSTing. ### New Files | File | Purpose | |------|---------| | `card_layout.py` | Core card models: PlayResult, CardResult, CardColumn, FullCard, FullBattingCard, FullPitchingCard. Ported from database's `card_creation.py`. Uses `col_*` key names. | | `batters/card_builder.py` | `build_batter_full_cards()` — takes vL/vR ratings, returns two FullBattingCard objects | | `pitchers/card_builder.py` | `build_pitcher_full_cards()` — same for pitchers | | `batters/models.py` | Extracted `BattingCardRatingsModel` from `calcs_batter.py` | | `pitchers/models.py` | Extracted `PitchingCardRatingsModel` from `calcs_pitcher.py` | | `offense_col_resolver.py` | Maps player→offense_col for retrosheet pipeline (fixed 883 silent KeyErrors) | | `tests/test_rate_stats_formulas.py` | Tests for extracted rating model formulas | ### Integration Path ``` retrosheet_data.py → calcs_batter.py → build_batter_full_cards() → card_output() → vl_dict.update() → calcs_pitcher.py → build_pitcher_full_cards() → card_output() → vr_dict.update() ``` Card builders are called inside a `try/except` in `calcs_batter.py:339` and `calcs_pitcher.py:134`. On failure, logs a warning and the card still posts without col_* layout data (backwards compatible). ## Commits on Branch 1. `a72abc0` — Add FullCard/CardColumn/CardResult models and card builder pipeline 2. `39c652e` — Extract BattingCardRatingsModel and PitchingCardRatingsModel into models.py files 3. `2bf3a6c` — Fix SLG formula drift in extracted rating models 4. `32cadb1` — Fix two bugs in pitcher card builder dispatch logic 5. `db38225` — Add offense_col resolver for retrosheet pipeline to fix 883 silent KeyErrors ## What's Left Before Merge ### 1. Database Migration (BLOCKING if you want col_* data persisted) The database repo (`paper-dynasty-database`) has a parallel `feature/fullcard-migration` branch with: - 9 new nullable TextFields on `BattingCardRatings` and `PitchingCardRatings` tables - Pydantic model updates in `routers_v2/battingcardratings.py` and `pitchingcardratings.py` - Migration SQL documented but **intentionally not run** Without this migration, the col_* fields in `card_output()` are computed but silently ignored by the API. The card-creation side works either way — it's a no-op until the DB accepts those fields. ### 2. `live_series_update.py` Not Integrated This file has its own inline card generation — does NOT use `calcs_batter`/`calcs_pitcher`. Only the retrosheet pipeline benefits from the new card builders. Live series integration is a separate effort. ### 3. No Tests for Core New Code `card_layout.py` (1015 lines), `batters/card_builder.py` (802 lines), `pitchers/card_builder.py` (776 lines) have zero test coverage. Priority targets: - `get_chances()` — maps continuous values to discrete EXACT_CHANCES - `CardColumn.add_result()` / `FullCard.card_fill()` — the filling algorithm - `card_output()` — serialization to col_* dict format - End-to-end: feed known ratings → assert expected card structure ### 4. Test Failures (Pre-existing, Not From This Branch) - `test_wh_singles` — `wh_singles(12, .45)` returns `8.0` vs expected `Decimal('7.95')`. Fails on main too. The test file has a 1-line diff on this branch (import change). - 11 failures in `test_automated_data_fetcher.py` — mock setup issues, no diff on this branch. ## Key Bugs Fixed During Development 1. **Float/Decimal mismatch** — card_layout uses Decimal internally, models use float. Fix: wrap `card_fill()` outputs with `float()` in `assign_bchances()`/`assign_pchances()`. 2. **PitchingCardRatingsModel xcheck defaults** — Non-zero defaults (xcheck_ss=7.0, etc.) corrupted accumulation. Fix: explicitly zero all xcheck fields in `new_ratings` constructor. 3. **Pitcher dispatch logic** — Two bugs in how pitcher card builder routed plays to columns. 4. **offense_col KeyError** — Retrosheet pipeline had no offense_col resolver, causing 883 silent failures. ## Architecture Reference Full design doc: `docs/architecture/CARD_BUILDER_REDESIGN.md` Migration phases: - **Phase 1 (Extract & Validate)** — Done - **Phase 2 (Python Adoption)** — In progress (retrosheet pipeline wired up) - **Phase 3 (Database Simplification)** — Pending DB migration + removing fitting logic from DB - **Phase 4 (Enhancements)** — Future (contracts/card personalities, preview endpoint) ## Decision Points for Next Session 1. **Merge as-is?** Branch is safe to merge — col_* fields are computed but harmlessly ignored until DB migrates. Card generation behavior is unchanged. 2. **Add tests first?** Recommended but not strictly required since card builders are behind try/except. 3. **Run DB migration?** Enables end-to-end persistence. Requires deploying database branch too. 4. **Wire up live series?** Separate PR recommended — different pipeline, different concerns.