PR retargeted from next-release to card-evolution. Restore the
Dockerfile with correct COPY path and CMD from card-evolution base.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Mirror the batter_id is None guard in _build_pitching_groups() so that
a StratPlay row with a null pitcher_id is skipped rather than creating
a None key in the groups dict (which would fail on the NOT NULL FK
constraint during upsert).
- Revert Dockerfile to the next-release base: drop the COPY path change
and CMD addition that were already merged in PR #101 and are unrelated
to the ProcessedGame ledger feature.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#105
Replace the last_game FK guard in update_season_stats() with an atomic
INSERT into a new processed_game ledger table. The old guard only blocked
same-game immediate replay; it was silently bypassed if game G+1 was
processed first (last_game already overwritten). The ledger is keyed on
game_id so any re-delivery — including out-of-order — is caught reliably.
Changes:
- app/db_engine.py: add ProcessedGame model (game FK PK + processed_at)
- app/services/season_stats.py: replace last_game check with
ProcessedGame.get_or_create(); import ProcessedGame; update docstrings
- migrations/2026-03-18_add_processed_game.sql: CREATE TABLE IF NOT EXISTS
processed_game with FK to stratgame ON DELETE CASCADE
- tests/conftest.py: add ProcessedGame to imports and _TEST_MODELS list
- tests/test_season_stats_update.py: add test_out_of_order_replay_prevented;
update test_double_count_prevention docstring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>