From 15c1c97d9d1a3e003538e158c47fb332f40471e0 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Thu, 19 Mar 2026 10:00:43 -0500 Subject: [PATCH] =?UTF-8?q?docs:=20sync=20KB=20=E2=80=94=20evolution-phase?= =?UTF-8?q?1-implementation.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../evolution-phase1-implementation.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 paper-dynasty/evolution-phase1-implementation.md diff --git a/paper-dynasty/evolution-phase1-implementation.md b/paper-dynasty/evolution-phase1-implementation.md new file mode 100644 index 0000000..ec95180 --- /dev/null +++ b/paper-dynasty/evolution-phase1-implementation.md @@ -0,0 +1,118 @@ +--- +title: "Card Evolution Phase 1 — Implementation Log" +description: "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." +type: context +domain: paper-dynasty +tags: [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.