- Move `import os` from inside evaluate_game() to module top-level imports
(lazy imports are only for circular dependency avoidance)
- Add get_or_none idempotency guard before RefractorBoostAudit.create()
inside db.atomic() to prevent IntegrityError on UNIQUE(card_state, tier)
constraint in PostgreSQL when apply_tier_boost is called twice for the
same tier
- Update atomicity test stub to provide card_state/tier attributes for
the new Peewee expression in the idempotency guard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a card reaches a new Refractor tier during game evaluation, the
system now creates a boosted variant card with modified ratings. This
connects the Phase 2 Foundation pure functions (PR #176) to the live
evaluate-game endpoint.
Key changes:
- evaluate_card() gains dry_run parameter so apply_tier_boost() is the
sole writer of current_tier, ensuring atomicity with variant creation
- apply_tier_boost() orchestrates the full boost flow: source card
lookup, boost application, variant card + ratings creation, audit
record, and atomic state mutations inside db.atomic()
- evaluate_game() calls evaluate_card(dry_run=True) then loops through
intermediate tiers on tier-up, with error isolation per player
- Display stat helpers compute fresh avg/obp/slg for variant cards
- REFRACTOR_BOOST_ENABLED env var provides a kill switch
- 51 new tests: unit tests for display stats, integration tests for
orchestration, HTTP endpoint tests for multi-tier jumps, pitcher
path, kill switch, atomicity, idempotency, and cross-player isolation
- Clarified all "79-sum" references to note the 108-total card invariant
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>