Commit Graph

91 Commits

Author SHA1 Message Date
Cal Corum
39424f7157 feat: show refractor progress in post-game summary embed (#147)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 24s
Closes #147

Adds a "Refractor Progress" field to the game summary embed showing:
- Cards that tiered up during this game (⬆ Name → Tier Name)
- Cards currently ≥80% toward their next tier on either team (◈ Name (pct%))

The field is omitted entirely when there is nothing to show.

Implementation:
- _run_post_game_refractor_hook() now returns evo_result (or None on failure)
- New _build_refractor_progress_text() fetches close-to-tier cards from both
  teams via refractor/cards?progress=close and formats the combined output
- complete_game() adds the field between Rewards and Highlights sections

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 13:36:32 -05:00
Cal Corum
25b63b407f feat: include refractor card image in tier-up notification embed (#144)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 21s
Closes #144

- build_tier_up_embed() accepts optional image_url and calls set_image() when provided
- notify_tier_completion() accepts optional image_url and passes it through
- _trigger_variant_renders() now captures the render response image_url per player and returns a player_id->image_url dict
- _run_post_game_refractor_hook() triggers renders first (to obtain image URLs), then sends notifications with card art included
- Updated test_post_game_refractor_hook.py to reflect new render-before-notify ordering and image_url kwarg in notify calls

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 09:38:43 -05:00
Cal Corum
eb022c3d66 test: mock-based integration tests for post-game refractor hook (#148)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 28s
Extract _run_post_game_refractor_hook() from complete_game() so the hook
logic can be tested directly without a live game session. Add 13 tests
covering endpoint ordering, tier-up notification dispatch, variant render
triggers, and non-fatal error handling.

Closes #148

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 08:34:50 -05:00
Cal Corum
21bad7af51 refactor: extract TIER_NAMES/TIER_COLORS to shared constants module (#146)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 12s
Closes #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 00:03:46 -05:00
Cal Corum
59a41e0c39 docs: update refractor integration test plan with 2026-04-07 results
All checks were successful
Ruff Lint / lint (pull_request) Successful in 25s
Fix incorrect command names (/card→/player, /roster→/team, /buy→/buy card-by-name,
/openpack→/open-packs, /scout→/scout-tokens). Update execution checklist with full
Playwright test session results — API tests, filter tests, pagination, edge cases
all passing. Note badge propagation design gap and REF-22 fix (discord#141).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 16:08:37 -05:00
Cal Corum
8e5242a6b7 fix: move health server from port 8080 to 8081 (#130)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 18s
Adminer is exposed on host port 8080, shadowing the bot health endpoint.
Change health server default to 8081 to avoid the conflict.

Closes #130

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 15:53:33 +00:00
Cal Corum
78f313663e fix: review feedback — variant 0 guard, remove dead team param
All checks were successful
Ruff Lint / lint (pull_request) Successful in 11s
- Use `variant is None` instead of `not variant` to avoid skipping
  variant 0 tier-ups (0 is falsy in Python)
- Remove unused `team` parameter from _build_refractor_response
- Update tests to match

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:33:36 -05:00
Cal Corum
46744d139c feat: add /player refractor_tier parameter for viewing evolved cards
All checks were successful
Ruff Lint / lint (pull_request) Successful in 13s
Adds optional refractor_tier parameter to the /player slash command.
When provided: looks up the user's team refractor data, shows the
evolved card image if available, triggers on-demand render if image
not yet generated, or shows top 5 refractor cards as fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:21:28 -05:00
Cal Corum
730d4b4f60 feat: trigger variant card renders after post-game tier-ups
After refractor tier-ups, the bot hits the card render URL for each
variant card to trigger Playwright render + S3 upload as a side
effect. Fire-and-forget — failures are logged but never raised.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:10:13 -05:00
Cal Corum
80e99b075f chore: fix stale docstring referencing removed note field
All checks were successful
Ruff Lint / lint (pull_request) Successful in 15s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:09:21 -05:00
Cal Corum
ef270ec1ab fix: remove stale "Rating Boosts coming soon" from Superfractor notification
All checks were successful
Ruff Lint / lint (pull_request) Successful in 52s
Tier boosts shipped in Phase 2 — the teaser field in the T4 tier-up
embed was outdated. Remove it and update tests + test plan to match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:59:56 -05:00
Cal Corum
bbad1daba2 fix: clean up refractor status display — suffix tags, compact layout, dead code removal
All checks were successful
Ruff Lint / lint (pull_request) Successful in 20s
- Tier labels as suffix tags: **Name** — Base Chrome [T1] (T0 gets no suffix)
- Compact progress line: bar value/threshold (pct) — removed formula and tier arrow
- Fully evolved shows `MAX` instead of FULLY EVOLVED
- Deleted unused FORMULA_LABELS dict
- Added _FULL_BAR constant, moved T0-branch lookups into else
- Fixed mock API shape in test (cards → items)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 00:22:35 -05:00
Cal Corum
c3ff85fd2d fix: replace abstract tier symbols with readable labels in /refractor status
All checks were successful
Ruff Lint / lint (pull_request) Successful in 11s
Unicode symbols (○ ◈ ◆ ✦ ★) were too similar to distinguish at a glance.
Now uses T1/T2/T3/T4★ prefixes with no prefix for base cards (T0).
Summary header reads "Base: 1  T1: 9 — 64 total" instead of cryptic symbols.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:47:03 -05:00
Cal Corum
cd822857bf feat: redesign /refractor status with rich Unicode display and team branding
All checks were successful
Ruff Lint / lint (pull_request) Successful in 21s
Replace plain ASCII progress bars and text badges with a polished embed:
- Unicode block progress bars (▰▱) replacing ASCII [===---]
- Tier-specific symbols (○ ◈ ◆ ✦ ★) instead of [BC]/[R]/[GR]/[SF] badges
- Team-branded embeds via get_team_embed (color, logo, season footer)
- Tier distribution summary header in code block
- Percentage display and backtick-wrapped values
- Tier-specific accent colors for single-tier filtered views
- Sparkle treatment for fully evolved cards (✧ FULLY EVOLVED ✧)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 22:46:38 -05:00
Cal Corum
7191400b5b docs: update refractor test plan — resolve list endpoint gap, add API tests
All checks were successful
Ruff Lint / lint (pull_request) Successful in 17s
- Mark CRITICAL missing endpoint gap as resolved (database PR #173 merged)
- Add REF-API-06 through REF-API-10 covering the new GET /refractor/cards
  list endpoint (team filter, card_type, tier, progress, pagination)
- Update prerequisites, execution checklist, and time estimates
- Total test cases: 87 (was 82)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:19:40 -05:00
Cal Corum
3b36dc33ee docs: note manual game testing in refractor test plan
All checks were successful
Ruff Lint / lint (pull_request) Successful in 10s
Sections 7-8 (game simulation, tier-up notifications) are marked as
manual testing by Cal — impractical to automate via Playwright.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:28:21 -05:00
Cal Corum
190aa88d43 test: add refractor integration test plan and preflight script
All checks were successful
Ruff Lint / lint (pull_request) Successful in 21s
82-case in-app test plan for Playwright automation covering /refractor status,
tier badges, post-game hooks, tier-up notifications, and edge cases.
Preflight script for quick deploy verification via curl.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:16:21 -05:00
Cal Corum
571a86fe7e fix: wire WP-14 tier-up notification embeds into post-game hook
All checks were successful
Ruff Lint / lint (pull_request) Successful in 25s
Replace the logging-only stub in logic_gameplay.py with the real
notify_tier_completion from helpers/refractor_notifs.py. Tier-up
events now send Discord embeds instead of just logging.

- Import notify_tier_completion from helpers.refractor_notifs
- Remove 16-line stub function and redundant inline logging
- Update tests: verify real embed-sending behavior, replace
  bug-documenting T1-5 diagnostic with shape validation guards

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:43:05 -05:00
cal
8c0c2eb21a test: refractor system comprehensive test coverage (#117)
test: add comprehensive refractor system test coverage (23 tests)

Covers TIER_NAMES/TIER_BADGES cross-module consistency, WP-14 tier_up dict
shape mismatch (latent KeyError documented), None channel handling, filter
combinations, progress bar boundaries, and malformed API response handling.
2026-03-24 21:06:13 +00:00
Cal Corum
fddac59f7e fix: update test mock endpoint strings to refractor/cards/ (#114)
All checks were successful
Ruff Lint / lint (pull_request) Successful in 20s
Mock routing in _patch_db_get and _failing_db_get still checked for
"evolution/cards/" after the production endpoint was renamed, causing
all badge-presence assertions to pass vacuously (evo_state=None).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 00:19:23 -05:00
cal
dc128ad995 Merge branch 'main' into feature/wp14-tier-notifications
All checks were successful
Ruff Lint / lint (pull_request) Successful in 14s
2026-03-23 20:26:04 +00:00
cal
80344fe473 Merge branch 'main' into feature/wp13-postgame-hook
All checks were successful
Ruff Lint / lint (pull_request) Successful in 14s
2026-03-23 20:25:31 +00:00
Cal Corum
29f2a8683f fix: rename evolution/ to refractor/ endpoint and remove misplaced notifs module
All checks were successful
Ruff Lint / lint (pull_request) Successful in 14s
- Change `evolution/evaluate-game/` API call to `refractor/evaluate-game/` in
  complete_game() hook (was calling the wrong endpoint path)
- Update all test assertions in test_complete_game_hook.py to match the
  corrected endpoint path and update docstrings to "refractor" naming
- Remove helpers/evolution_notifs.py and tests/test_evolution_notifications.py
  from this PR — they belong to PR #112 (WP-14 tier notifications). The
  notify_tier_completion stub in logic_gameplay.py remains as the WP-14
  integration target.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 15:22:25 -05:00
Cal Corum
9940b160db fix: rename evolution to refractor terminology, fix tier names
All checks were successful
Ruff Lint / lint (pull_request) Successful in 12s
- Rename helpers/evolution_notifs.py -> helpers/refractor_notifs.py
- Rename tests/test_evolution_notifications.py -> tests/test_refractor_notifs.py
- Delete utilities/evolution_notifications.py (replaced by helpers/refractor_notifs.py)
- Update TIER_NAMES to canonical names: Base Card, Base Chrome, Refractor, Gold Refractor, Superfractor
- Update T4 embed title from "FULLY EVOLVED!" to "SUPERFRACTOR!"
- Update FOOTER_TEXT from "Paper Dynasty Evolution" to "Paper Dynasty Refractor"
- Update non-max tier embed title from "Evolution Tier Up!" to "Refractor Tier Up!"
- Add discord.abc.Messageable type annotation to notify_tier_completion channel param
- Update all test assertions to match new tier names and strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 15:12:59 -05:00
cal
c85359ca5d Merge branch 'main' into ai/paper-dynasty-database#76
All checks were successful
Ruff Lint / lint (pull_request) Successful in 21s
2026-03-23 20:11:17 +00:00
Cal Corum
45d71c61e3 fix: address reviewer issues — rename evolution endpoints, add TIER_BADGES
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m32s
- Update module docstring: replace evolution/cards with refractor/cards,
  drop old tier names (Unranked/Initiate/Rising/Ascendant/Evolved), add
  correct tier names (Base Card/Base Chrome/Refractor/Gold Refractor/
  Superfractor)
- Fix API call: db_get("evolution/cards") → db_get("refractor/cards")
- Add TIER_BADGES dict {1:"[BC]", 2:"[R]", 3:"[GR]", 4:"[SF]"}
- Update format_refractor_entry to prepend badge label for T1-T4 (T0 has
  no badge)
- Add TestTierBadges test class (11 tests) asserting badge values and
  presence in formatted output
- Update test_player_name_in_output to accommodate badge-prefixed bold name

Dead utilities/evolution_notifications.py has no source file on this branch
(WP-14/PR #112 already delivered the replacement).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 15:08:39 -05:00
cal
3ce5aebc57 Merge branch 'main' into ai/paper-dynasty-database#77
All checks were successful
Ruff Lint / lint (pull_request) Successful in 16s
2026-03-23 20:05:29 +00:00
Cal Corum
911c6842e4 feat: WP-14 tier completion notification embeds
Adds helpers/evolution_notifs.py with build_tier_up_embed() and
notify_tier_completion(). Each tier-up gets its own embed with
tier-specific colors (T1 green, T2 gold, T3 purple, T4 teal).
Tier 4 uses a special 'FULLY EVOLVED!' title with a future rating
boosts note. Notification failure is non-fatal (try/except). 23
unit tests cover all tiers, empty list, and failure path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 14:55:03 -05:00
Cal Corum
2c57fbcdf5 fix: remove dead real_notify import in test
All checks were successful
Ruff Lint / lint (pull_request) Successful in 23s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:52:49 -05:00
Cal Corum
b04219d208 feat: WP-13 post-game callback hook for season stats and evolution
After complete_game() saves the game result and posts rewards, fire two
non-blocking API calls in order:
  1. POST season-stats/update-game/{game_id}
  2. POST evolution/evaluate-game/{game_id}

Any failure in the evolution block is caught and logged as a warning —
the game is already persisted so evolution will self-heal on the next
evaluate pass. A notify_tier_completion stub is added as a WP-14 target.

Closes #78 on cal/paper-dynasty-database

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 14:52:36 -05:00
Cal Corum
687b91a009 fix: rename test file and docstring to Refractor terminology
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m8s
Renames tests/test_card_embed_evolution.py to tests/test_card_embed_refractor.py
and updates the module-level docstring to use "Refractor tier progression" /
"Refractor API" instead of "evolution progress" / "evolution API".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 12:31:38 -05:00
Cal Corum
cc02d6db1e fix: align badge labels with updated tier names
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m34s
Tier badges shifted to match updated spec:
  T1=[BC] Base Chrome, T2=[R] Refractor, T3=[GR] Gold Refractor, T4=[SF] Superfractor
T0 (Base Card) shows no badge. All 11 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:32:10 -05:00
Cal Corum
5670cd6e88 fix: correct tier names and group variable convention
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m45s
Tier names updated per Cal's spec:
  T0=Base Card, T1=Base Chrome, T2=Refractor, T3=Gold Refractor, T4=Superfractor

Also renames refractor_group → group_refractor per project convention.
All 39 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:30:47 -05:00
Cal Corum
fc8508fbd5 refactor: rename Evolution badges to Refractor tier names
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m24s
- Badge labels: [R] Refractor, [GR] Gold Refractor, [SF] Superfractor, [SF★] fully evolved
- Fix broken {e} log format strings (restore `as e` + add f-string prefix)
- Restore ruff.toml from main (branch had stripped global config)
- Update all test assertions for new badge names (11/11 pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:50:11 -05:00
Cal Corum
6b4957ec70 refactor: rename Evolution to Refractor system
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m34s
- cogs/evolution.py → cogs/refractor.py (class, group, command names)
- Tier names: Base Chrome, Refractor, Gold Refractor, Superfractor
- Fix import: helpers.main.get_team_by_owner
- Fix shadowed builtin: type → card_type parameter
- Tests renamed and updated (39/39 pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:48:31 -05:00
Cal Corum
208efd11a6 feat: tier completion notification embeds (WP-14) (#79)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m25s
Closes paper-dynasty-database#79

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:34:14 -05:00
Cal Corum
0304753e92 feat: tier badge prefix in card embed title (WP-12) (#77)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m32s
Add evolution tier badge to get_card_embeds() title. Fetches
evolution/cards/{id} endpoint; prepends [T1]/[T2]/[T3]/[EVO] when
current_tier > 0. API failure is silently swallowed so card display
is never broken.

Also add ruff.toml to suppress legacy star-import rules (F403/F405)
and bare-except/type-comparison rules (E722/E721) for helpers/main.py,
which predates the pre-commit hook installation.

Closes #77

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 15:07:35 -05:00
Cal Corum
d12cdb8d97 feat: /evo status slash command and tests (WP-11) (#76)
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m37s
Closes #76

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 09:07:28 -05:00
Cal Corum
3e9c2f6564 fix: add missing week field to scout reward POST
All checks were successful
Build Docker Image / build (pull_request) Successful in 1m15s
The rewards API requires a week field. The scout claim callback was
posting without it, causing a 422 validation error when a user
selected a card from a scout opportunity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 10:20:23 -05:00
Cal Corum
8b0c82f687 fix: invoke actual cog callback in test_error_handling_and_logging (#39)
The previous test patched api_calls.db_get and pygsheets.authorize then
called those mocks directly—never invoking any cog method. The test
passed even when all cog code was deleted.

Replace with a test that retrieves the real pull_roster_command.callback
from the cog instance, patches dependencies at the correct module-level
names, calls the callback, and asserts ctx.send was called with the
expected error message. If the cog cannot be imported, the test skips
gracefully via pytest.skip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:22:58 +00:00
Cal Corum
1b83be89bb feat: limit scouting to Standard/Premium packs, simplify scout view
- Add SCOUTABLE_PACK_TYPES env var (default: Standard,Premium) to control
  which pack types offer scout opportunities
- Unify embed construction into build_scout_embed() — removes 3 near-duplicate
  embed builders across scout_view.py and scouting.py
- Replace manual total_scouts counter with derived property from claims dict
- Remove redundant db_get("current") API call per scout click — use PD_SEASON
- Remove duplicate expiry computation in create_scout_opportunity
- Move send_to_channel to top-level import, remove redundant local import
- Update tests to match simplified code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:22:58 +00:00
Cal Corum
0432f9d3f4 fix: add missing pack, description, image fields to scouting test fixtures
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:22:58 +00:00
Cal Corum
755f74be92 fix: Address PR review findings — two bugs and cleanup
- Fix int_timestamp() no-arg path returning seconds instead of
  milliseconds, which would silently break the daily scout token cap
  against the real API
- Acknowledge double-click interactions with ephemeral message instead
  of silently returning (Discord requires all interactions to be acked)
- Reorder scout flow: create card copy before consuming token so a
  failure doesn't cost the player a token for nothing
- Move build_scouted_card_list import to top of scout_view.py
- Remove unused asyncio import from helpers/scouting.py
- Fix footer text inconsistency ("One scout per player" everywhere)
- Update tests for new operation order and double-click behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:22:58 +00:00
Cal Corum
3c0fa133fd refactor: Consolidate scouting utilities, add test suite, use Discord timestamps
- Consolidate SCOUT_TOKENS_PER_DAY and get_scout_tokens_used() into
  helpers/scouting.py (was duplicated across 3 files)
- Add midnight_timestamp() utility to helpers/utils.py
- Remove _build_scouted_ids() wrapper, use self.claims directly
- Fix build_scout_embed return type annotation
- Use Discord <t:UNIX:R> relative timestamps for scout window countdown
- Add 66-test suite covering helpers, ScoutView, and cog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 13:22:58 +00:00
Cal Corum
1eda66a06c fix: preserve batter at plate when half-inning ends on caught stealing
Some checks failed
Build Docker Image / build (pull_request) Failing after 15s
When a half-inning ended with a CS (or pickoff), the batter who was at
the plate was incorrectly skipped in the next inning. The side-switch
code unconditionally advanced the batting order by 1 without checking
whether the last play was a plate appearance. Now checks opponent_play.pa
before incrementing, matching the existing non-side-switch logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 22:37:42 -06:00
Cal Corum
1a9efa8f7e fix: add locked_play context manager to prevent stuck play locks
Early returns in log_chaos, log_sac_bunt, and log_stealing left play
locks permanently stuck because the lock was acquired but never released.
The new locked_play async context manager wraps checks_log_interaction()
and guarantees lock release on exception, early return, or normal exit.

Migrated all 18 locking commands in gameplay.py and removed redundant
double-locking in end_game_command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 21:54:44 -06:00
Cal Corum
ebf006e5c6 Fix play lock system to prevent permanent user lockouts
CRITICAL BUG FIX: Play locks were never released on exceptions, causing
permanent user lockouts. Found 13 stuck plays in production.

Changes:
1. Added lock_play parameter to checks_log_interaction() (default True)
2. Removed unnecessary locks from read-only commands:
   - /settings-ingame (game settings, not play state)
   - /show-card defense (read-only display)
   - /substitute commands (just show UI, lock in callback)
3. Added safe_play_lock() context manager for automatic lock release
4. Added play locking to substitution callbacks:
   - SelectBatterSub.callback()
   - SelectReliefPitcher.callback()
5. Global error handler now releases stuck locks automatically

Architecture:
- Commands that display UI or read data: No lock
- Commands that modify play state: Lock at last possible moment
- 3-layer defense: manual release, context manager, global handler

Resolves race condition from concurrent play modifications.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 09:21:18 -06:00
Cal Corum
00ed42befd Add tests for play lock release and cross-command racing
Adds two new test cases to test_play_locking.py to improve coverage:

1. test_lock_released_after_successful_completion
   - Verifies play.locked is set to False after complete_play()
   - Confirms play.complete is set to True
   - Validates database commit is called

2. test_different_commands_racing_on_locked_play
   - Tests that ANY command type is blocked on locked plays
   - Prevents race conditions between different command types
   - Tests multiple commands: walk, strikeout, single, xcheck

These tests ensure the play locking idempotency guard works correctly
for both lock acquisition and release, and prevents all command types
from racing (not just duplicate commands).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 17:13:57 -06:00
Cal Corum
4be6afb541 Add API timeout/retry logic and fix get_team_by_owner for PostgreSQL
- Add APITimeoutError exception and retry logic to db_get
- Add timeout handling to db_post, db_put, db_patch, db_delete
- Fix get_team_by_owner to prefer non-gauntlet team (PostgreSQL migration fix)
- Code formatting cleanup (black)
2026-01-31 15:52:14 -06:00
Cal Corum
af49704272 Catchup files 2025-11-11 13:22:06 -06:00