claude-home/paper-dynasty/evolution-phase1-implementation.md
Cal Corum 15c1c97d9d
All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 2s
docs: sync KB — evolution-phase1-implementation.md
2026-03-19 10:00:43 -05:00

6.9 KiB

title description type domain tags
Card Evolution Phase 1 — Implementation Log Full implementation log for Card Evolution Phase 1 (schema, API, formula engine, bot integration) across paper-dynasty-database and paper-dynasty-discord repos. Includes architecture decisions, bug fixes found in review, and first smoke test results. context paper-dynasty
paper-dynasty
evolution
deployment
architecture
testing

Card Evolution Phase 1 — Implementation Log

Date: 2026-03-18 through 2026-03-19 Repos: paper-dynasty-database (card-evolution branch), paper-dynasty-discord (main/next-release) PRD: docs/prd-evolution/ in card-creation repo Plan: docs/prd-evolution/PHASE1_PROJECT_PLAN.md v2.2

Overview

Phase 1 delivers the structural foundation for the Card Evolution system. Every card gets an evolution state tracking progress toward 4 tiers via simple formulas (batters: PA + TB*2, pitchers: IP + K). No rating boosts are applied — tiers are tracked but boosts are deferred to Phase 2.

Architecture

Single Metric Per Track (Key Design Decision)

Each track uses one cumulative formula. Progress is computed from career totals (SUM across all season stats rows for a player-team pair) and compared against four tier thresholds stored on the track itself. No separate milestone rows — thresholds ARE the milestones.

Track Formula T1 T2 T3 T4
Batter PA + (TB x 2) 37 149 448 896
Starting Pitcher IP + K 10 40 120 240
Relief Pitcher IP + K 3 12 35 70

Data Flow

  1. Game completes -> bot calls POST /season-stats/update-game/{game_id}
  2. Season stats upserted into batting_season_stats / pitching_season_stats
  3. Bot calls POST /evolution/evaluate-game/{game_id}
  4. For each player in the game with an evolution_card_state, career totals are summed, formula applied, tier checked
  5. Tier-ups returned to bot -> Discord notification embeds sent

Tables Created

  • batting_season_stats — per-player per-team per-season batting totals
  • pitching_season_stats — per-player per-team per-season pitching totals
  • evolution_track — 3 tracks with formulas and thresholds
  • evolution_card_state — per-player per-team evolution progress (tier, value, fully_evolved)
  • evolution_tier_boost — Phase 2 stub for stat boosts
  • evolution_cosmetic — Phase 2 stub for visual unlocks
  • processed_game — idempotency ledger for update_season_stats()

Sub-Phases Completed

Phase 1a — Schema & Data Foundation (PR #104)

  • WP-01: Evolution Peewee models
  • WP-02: PlayerSeasonStats model (BattingSeasonStats + PitchingSeasonStats)
  • WP-03: Track seed data (JSON + idempotent seed function)
  • WP-04: SQL migration (7 new tables + card.variant, battingcard/pitchingcard.image_url)
  • WP-05: update_season_stats(game_id) service with dual-backend upsert

Phase 1b — API & Evaluation Engine (PRs #98, #106, #107, #108)

  • WP-06: Track Catalog API (GET /v2/evolution/tracks)
  • WP-07: Card State API (GET /v2/evolution/cards/{card_id}, GET /v2/teams/{team_id}/evolutions)
  • WP-08: Evaluate Endpoint (POST /v2/evolution/cards/{card_id}/evaluate)
  • WP-09: Formula Engine (compute_batter_value, compute_pitcher_value, tier_from_value)
  • WP-10: Pack Opening Hook (evolution_card_state init on card acquisition)
  • ProcessedGame Ledger (#105) — emerged from Phase 1a review

Phase 1c — Bot Integration (PRs #91-94 discord, #109 database)

  • WP-11: /evo status slash command with progress bars
  • WP-12: Tier badge on card embeds ([T1]/[T2]/[T3]/[EVO])
  • WP-13: Post-game callback (bot hook + DB endpoints)
  • WP-14: Tier completion notification embeds

Bugs Found in Review

Phase 1b Review Fixes

  1. stats.strikeouts vs stats.k — Formula engine Protocol used strikeouts but evaluator's _CareerTotals exposed k. Runtime AttributeError on any pitcher evaluation.
  2. track.t1 vs track.t1_threshold — Formula engine read track.t1 but DB model defines t1_threshold. Runtime crash on tier evaluation.
  3. fully_evolved logic — Was derived from new_tier instead of post-max current_tier, could produce contradictory state (tier=2 but fully_evolved=True after regression guard).
  4. Missing pitcher_id=None guard_build_pitching_groups didn't filter null pitcher IDs, would crash on NOT NULL FK constraint.
  5. Missing pg_conn fixture — Test conftest.py missing the PostgreSQL connection fixture for integration tests.

Phase 1c Review Fixes

  1. Missing @pytest.mark.asyncio decorators — 9 async test methods silently didn't run.
  2. WP-14 files leaked into WP-13 PR — Worktree agent picked up untracked files from another branch.
  3. Unused Optional import in evolution_notifs.py.

First Smoke Test (2026-03-19)

Environment

  • Database API: pddev.manticorum.com, image manticorum67/paper-dynasty-database:next-release
  • Discord Bot: Local Docker, image manticorum67/paper-dynasty-discordapp:next-release
  • Database: PostgreSQL on pd-database host, database paperdynasty_dev

Steps Taken

  1. Ran SQL migration (evolution tables + processed_game) -> all tables created
  2. Seeded 3 evolution tracks -> verified via GET /v2/evolution/tracks (200 OK, 3 items)
  3. Seeded 2753 evolution_card_state rows for team 31 (Normal CornBelters)
  4. Called POST /season-stats/update-game/1517 -> {"updated": 27, "skipped": false}
  5. Called POST /evolution/evaluate-game/1517 -> {"evaluated": 0, "tier_ups": []}

Issue Found

The evaluate-game endpoint returned evaluated: 0 despite states existing. Root cause: the deployed evaluator imports PlayerSeasonStats from db_engine, but the actual model names are BattingSeasonStats and PitchingSeasonStats. This is a naming mismatch between the WP-13 agent's evaluator and the Phase 1a models. The except Exception in the evaluate loop silently swallows the ImportError.

Architectural Concern Identified

The incremental delta upsert approach for season stats is fragile:

  • Partial processing corrupts stats
  • Upsert bugs compound over time
  • No self-healing mechanism

Proposed fix: Replace delta upserts with full recalculation (SBA-style materialized view pattern). After each game, recalculate full season stats by SELECT SUM(...) from stratplay across all games that season. Always correct, idempotent by nature.

Operational Notes

  • Docker tag mapping: next-release branch -> :next-release and :rc tags on Docker Hub
  • Discord repo branch protection: Empty approvals whitelist means API merges fail. Use Claude Gitea token at ~/.claude/secrets/gitea_claude_token for approvals, merge via UI.
  • Discord repo ruff: helpers/main.py has 2300+ pre-existing violations. Commits need --no-verify.
  • Dev database: Migrations must be run manually via docker exec -i sba_postgres psql on pd-database host.
  • Production bot on pd-bots: Uses :latest tag — do NOT update without explicit approval.