Closes#90
Replace sequential awaits with asyncio.gather() in all locations identified
in the issue:
- commands/gameplay/scorebug.py: parallel team lookups in publish_scorecard
and scorebug commands; also fix missing await on async scorecard_tracker calls
- commands/league/submit_scorecard.py: parallel away/home team lookups
- tasks/live_scorebug_tracker.py: parallel team lookups inside per-scorecard
loop (compounds across multiple active games); fix missing await on
get_all_scorecards
- commands/injuries/management.py: parallel get_current_state() +
search_players() in injury_roll, injury_set_new, and injury_clear
- services/trade_builder.py: parallel per-participant roster validation in
validate_trade()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move inspect.signature(func) calls from inside wrapper functions to the
outer decorator function so the introspection cost is paid once at
decoration time instead of on every invocation.
- logged_command: sig, param_names, and exclude_set computed at decoration time;
wrapper.__signature__ reuses the pre-computed sig
- cached_api_call: sig moved to decorator scope; bound_args still computed
per-call (requires runtime args)
- cached_single_item: same as cached_api_call
Closes#97
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The PR trigger caused Docker builds on every push to PR branches,
wasting CI resources. Only build on merge to main/next-release.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes#96
Replaces the per-field `json.dumps(value)` probe — which fully serialized
and discarded the result just to check serializability — with a type-check
fast path using `isinstance()`. The `_SERIALIZABLE_TYPES` tuple is defined
at module level so it's not recreated on every log call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#104
- Remove duplicate `self.maintenance_mode: bool = False` assignment (merge
artifact from PR #83)
- Delete dead `@self.tree.interaction_check` block in `setup_hook` that
generated a RuntimeWarning at startup; `MaintenanceAwareTree.interaction_check()`
already handles this correctly via the `tree_cls=MaintenanceAwareTree` kwarg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The parameter was already ignored (body hardcodes range(1, 19)).
Remove from signature and the one caller that passed it.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace custom _make_game/_make_team helpers with existing test
factories for consistency with the rest of the test suite.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes#88
Replaced sequential for-loops in get_team_schedule(), get_recent_games(),
and get_upcoming_games() with asyncio.gather() to fire all per-week HTTP
requests concurrently. Also adds import asyncio which was missing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#98
Replace blocking `client.keys(pattern)` with non-blocking
`client.scan_iter(match=pattern)` to avoid full-keyspace scans
that block the Redis server during cache invalidation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#99
- Add module-level `_user_team_cache` dict with 60-second TTL so
`get_user_major_league_team` is called at most once per minute per
user instead of on every keystroke.
- Reduce `search_players(limit=50)` to `limit=25` to match Discord's
25-choice display cap and avoid fetching unused results.
- Add `TestGetCachedUserTeam` class covering cache hit, TTL expiry, and
None caching; add `clear_user_team_cache` autouse fixture to prevent
test interference via module-level state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The data/ volume was mounted :ro to protect Google Sheets credentials,
but this also prevented all state trackers from persisting JSON files
(scorecards, voice channels, trade channels, soak data), causing silent
save failures and stale data accumulating across restarts.
- Mount only the credentials file as :ro (file-level mount)
- Add a separate :rw storage/ volume for runtime state files
- Move all tracker default paths from data/ to storage/
- Add STATE_HOST_PATH env var (defaults to ./storage)
- Update SHEETS_CREDENTIALS_HOST_PATH semantics: now a file path
(e.g. ./data/major-domo-service-creds.json) instead of a directory
- Add storage/ to .gitignore
Closes#85
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>