From 1e7f99269ed7ad8b9033b609ad1fa1f5e70dd0ed Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Mon, 30 Mar 2026 14:00:43 -0500 Subject: [PATCH] =?UTF-8?q?docs:=20sync=20KB=20=E2=80=94=202026-03-30.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- paper-dynasty/2026-03-30.md | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 paper-dynasty/2026-03-30.md diff --git a/paper-dynasty/2026-03-30.md b/paper-dynasty/2026-03-30.md new file mode 100644 index 0000000..dc93ff1 --- /dev/null +++ b/paper-dynasty/2026-03-30.md @@ -0,0 +1,63 @@ +--- +title: "Refractor Phase 2: Integration — boost wiring, tests, and review" +description: "Implemented apply_tier_boost orchestration, dry_run evaluator, evaluate-game wiring with kill switch, and 51 new tests across paper-dynasty-database. PRs #176 and #177 merged." +type: context +domain: paper-dynasty +tags: [paper-dynasty-database, refractor, phase-2, testing] +--- + +# Refractor Phase 2: Integration — boost wiring, tests, and review + +**Date:** 2026-03-30 +**Branch:** `feature/refractor-phase2-integration` (merged to `main`) +**Repo:** paper-dynasty-database + +## What Was Done + +Full implementation of Refractor Phase 2 Integration — wiring the Phase 2 Foundation boost functions (PR #176) into the live evaluate-game endpoint so that tier-ups actually create boosted variant cards with modified ratings. + +1. **PR #176 merged (Foundation)** — Review findings fixed (renamed `evolution_tier` to `refractor_tier`, removed redundant parens), then merged via pd-ops +2. **`evaluate_card(dry_run=True)`** — Added dry_run parameter to separate tier detection from tier write. `apply_tier_boost()` becomes the sole writer of `current_tier`, ensuring atomicity with variant creation. Added `computed_tier` and `computed_fully_evolved` to return dict. +3. **`apply_tier_boost()` orchestration** — Full flow: source card lookup, boost application per vs_hand split, variant card + ratings creation with idempotency guards, audit record with idempotency guard, atomic state mutations via `db.atomic()`. Display stat helpers compute fresh avg/obp/slg. +4. **`evaluate_game()` wiring** — Calls evaluate_card with dry_run=True, loops through intermediate tiers on tier-up, handles partial multi-tier failures (reports last successful tier), `REFRACTOR_BOOST_ENABLED` env var kill switch, suppresses false notifications when boost is disabled or card_type is missing. +5. **79-sum documentation fix** — Clarified all references to "79-sum" across code, tests, and docs to note the 108-total card invariant (79 variable + 29 x-check for pitchers). +6. **51 new tests** — Display stat unit tests (12), integration tests for orchestration (27), HTTP endpoint tests (7), dry_run evaluator tests (6). Total suite: 223 passed. +7. **Five rounds of swarm reviews** — Each change reviewed individually by swarm-reviewer agents. All findings addressed: false notification on null card_type, wrong tier in log message, partial multi-tier failure reporting, atomicity test accuracy, audit idempotency gap, import os placement. +8. **PR #177 merged** — Review found two issues (import os inside function, audit idempotency gap on PostgreSQL UNIQUE constraint). Both fixed, pushed, approved by Claude, merged via pd-ops. + +## Decisions + +### Display stats computed fresh, not set to None +The original PO review note suggested setting avg/obp/slg to None on variant cards and deferring recalculation. Cal decided to compute them fresh using the exact Pydantic validator formulas instead — strictly better than stale or missing values. Design doc updated to reflect this. + +### Card/ratings creation outside db.atomic() +The design doc specified all writes inside `db.atomic()`. Implementation splits card/ratings creation outside (idempotent, retry-safe via get_or_none guards) with only state mutations (audit, tier write, Card.variant propagation) inside the atomic block. This is pragmatically correct — on retry, existing card/ratings are reused. Design doc updated. + +### Kill switch suppresses notifications entirely +When `REFRACTOR_BOOST_ENABLED=false`, the router skips both the boost AND the tier_up notification (via `continue`). This prevents false notifications to the Discord bot during maintenance windows. Initially the code fell through and emitted a notification without a variant — caught during coverage gap analysis and fixed. + +### Audit idempotency guard added +PR review identified that `RefractorBoostAudit` has a `UNIQUE(card_state_id, tier)` constraint in PostgreSQL (from the migration) that the SQLite test DB doesn't enforce. Added `get_or_none` before `create` to prevent IntegrityError on retry. + +## Follow-Up + +- Phase 3: Documentation updates in `card-creation` repo (docs only, no code) +- Phase 4a: Validation test cases in `database` repo +- Phase 4b: Discord bot tier-up notification fix (must ship alongside or after Phase 2 deploy) +- Deploy Phase 2 to dev: run migration `2026-03-28_refractor_phase2_boost.sql` on dev DB +- Stale branches to clean up in database repo: `feat/evolution-refractor-schema-migration`, `test/refractor-tier3` + +## Files Changed + +**paper-dynasty-database:** +- `app/services/refractor_boost.py` — apply_tier_boost orchestration, display stat helpers, card_type validation, audit idempotency guard +- `app/services/refractor_evaluator.py` — dry_run parameter, computed_tier/computed_fully_evolved in return dict +- `app/routers_v2/refractor.py` — evaluate_game wiring, kill switch, partial multi-tier failure, isoformat crash fix +- `tests/test_refractor_boost.py` — 12 new display stat tests, 79-sum comment fixes +- `tests/test_refractor_boost_integration.py` — 27 new integration tests (new file) +- `tests/test_postgame_refractor.py` — 7 new HTTP endpoint tests +- `tests/test_refractor_evaluator.py` — 6 new dry_run unit tests + +**paper-dynasty (parent repo):** +- `docs/refractor-phase2/01-phase1-foundation.md` — 79-sum clarifications +- `docs/refractor-phase2/02-phase2-integration.md` — atomicity boundary, display stats updates