docs: sync KB — evolution-phase1-implementation.md
All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 2s
All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 2s
This commit is contained in:
parent
dcee66978b
commit
15c1c97d9d
118
paper-dynasty/evolution-phase1-implementation.md
Normal file
118
paper-dynasty/evolution-phase1-implementation.md
Normal file
@ -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.
|
||||
Loading…
Reference in New Issue
Block a user