Implements all gap tests identified in the PO review for the refractor
card progression system (Phase 1 foundation).
TIER 1 (critical):
- T1-1: Negative singles guard in compute_batter_value — documents that
hits=1, doubles=1, triples=1 produces singles=-1 and flows through
unclamped (value=8.0, not 10.0)
- T1-2: SP tier boundary precision with floats — outs=29 (IP=9.666) stays
T0, outs=30 (IP=10.0) promotes to T1; also covers T2 float boundary
- T1-3: evaluate-game with non-existent game_id returns 200 with empty results
- T1-4: Seed threshold ordering + positivity invariant (t1<t2<t3<t4, all >0)
TIER 2 (high):
- T2-1: fully_evolved=True persists when stats are zeroed or drop below
previous tier — no-regression applies to both tier and fully_evolved flag
- T2-2: Parametrized edge cases for _determine_card_type: DH, C, 2B, empty
string, None, and compound "SP/RP" (resolves to "sp", SP checked first)
- T2-3: evaluate-game with zero StratPlay rows returns empty batch result
- T2-4: GET /teams/{id}/refractors with valid team and zero states is empty
- T2-5: GET /teams/99999/refractors documents 200+empty (no team existence check)
- T2-6: POST /cards/{id}/evaluate with zero season stats stays at T0 value=0.0
- T2-9: Per-player error isolation — patches source module so router's local
from-import picks up the patched version; one failure, one success = evaluated=1
- T2-10: Each card_type has exactly one RefractorTrack after seeding
All 101 tests pass (15 PostgreSQL-only tests skip without POSTGRES_HOST).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Guard total_count with `if not csv` ternary to avoid unnecessary
COUNT query on CSV export paths (10 files)
- Consolidate rewards.py from 3 COUNT queries to 1 (used for both
empty-check and response)
- Clean up scout_claims.py double `if limit is not None` block
- Normalize scout_opportunities.py from max(1,...) to max(0,...)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures the `count` field in JSON responses reflects total matching
records rather than the page size, consistent with the notifications
endpoint pattern from PR #150.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses reviewer feedback: max(0,...) admitted limit=0 which would
silently return no results even when matching records exist.
Changed to max(1,...) consistent with feedback on PRs #149 and #152.