The TransactionBuilder cached pre-existing transactions on first load
and never refreshed them. This meant transactions submitted by other
sessions (or newly visible after API fixes) were invisible for the
lifetime of the builder session, causing incorrect roster projections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds next-release branch trigger and replaces separate dev/production
build steps with the shared docker-tags action for tag resolution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removes the @pytest.mark.skip and implements the test using a
patched RosterValidation that raises on the first call, triggering
the except clause in validate_transaction. Verifies the method
returns is_legal=False with a descriptive error message.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both get_disappointment_gif and get_gif previously created a new
ClientSession per call. Replace with a lazily-initialised shared
session stored on the instance, eliminating per-call TCP handshake
and DNS overhead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both _should_sync_commands and _save_command_hash contained an identical
~35-line block building the command data list and computing the SHA-256 hash.
Extracted into a new synchronous helper method _compute_command_hash() that
both callers now delegate to.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add section headers (Active Roster, Minor League, Injured List) to the
main summary embed in _create_roster_embeds so each roster section is
clearly labeled for users
- Fix incorrect docstring in team_service.get_team_roster that had the
shortil/longil mapping backwards
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `maintenance_mode: bool = False` flag to `SBABot.__init__`
- Register a global `@tree.interaction_check` that blocks non-admin users
from all commands when maintenance mode is active
- Update `admin_maintenance` command to set `self.bot.maintenance_mode`
and log the state change, replacing the no-op placeholder comment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Simplify review found that force_refresh=True on every validate_transaction()
call caused redundant API fetches on every embed render and button press.
Instead, invalidate the roster cache after successful submit_transaction() so
the next operation fetches fresh data. This preserves the cache for normal
interaction flows while still preventing stale data after submissions.
Also adds type annotation for roster_svc DI parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TransactionBuilder cached roster data indefinitely via _roster_loaded flag,
causing validation to use stale counts when builders persisted across multiple
/ilmove invocations. Now validate_transaction() always fetches fresh roster
data. Also adds dependency injection for roster_service to improve testability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codebase audit identified ~50 lazy imports. Moved 42 unnecessary ones to
top-level imports — only keeping those justified by circular imports,
init-order dependencies, or optional dependency guards. Updated test mock
patch targets where needed. See #57 for remaining DI candidates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
/trade add-player hardcoded from_roster=MAJOR_LEAGUE for all players,
causing incorrect transaction records when trading MiL/IL players. This
cascaded into false roster validation errors for other teams' /dropadd
commands because pre-existing transaction processing misidentified which
roster a player was leaving from.
Now looks up the player's actual team and calls roster_type() to determine
the correct source roster. Same fix applied to /trade supplementary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Prevents users from managing injuries for players not on their team.
Admins bypass the check; org affiliates (MiL/IL) are recognized.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The else branch in descriptive_text() caught both "away leading" and
"tied" cases, always overwriting the tied text. Changed to if/elif/else
so tied scores display correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
load_existing_transactions only queried for the base team abbreviation
(e.g. "POR"), missing trades involving MiL/IL affiliates ("PORMIL",
"PORIL"). This caused false "too many players" errors when a pending
trade would have cleared a roster spot.
- get_team_transactions now accepts Union[str, List[str]] for team_abbrev
- load_existing_transactions queries all org affiliates [BASE, BASEMiL, BASEIL]
- Added 5 tests covering the fix and backwards compatibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The #weekly-info message already shows season and game times but not
which week it is, making managers check elsewhere for that context.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The production container has ambiguous timezone config — /etc/localtime
points to Etc/UTC but date reports CST. The transaction freeze/thaw task
used datetime.now() (naive, relying on OS timezone), causing scheduling
to fire at unpredictable wall-clock times.
- Add utils/timezone.py with centralized Chicago timezone helpers
- Fix tasks/transaction_freeze.py to use now_chicago() for scheduling
- Fix utils/logging.py timestamp to use proper UTC-aware datetime
- Add 14 timezone utility tests
- Update freeze task tests to mock now_chicago instead of datetime
Closes#43
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the next-release staging branch pattern used for
batching changes before merging to main.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
#40: ScorecardTracker cached data in memory at startup — background task never
saw newly published scorecards. Fixed by reloading from disk on every read.
#39: Win percentage defaulted to 50% when unavailable, showing a misleading
50/50 bar. Now defaults to None with "unavailable" message in embed. Parsing
handles decimal (0.75), percentage string, and empty values. Also fixed
orientation bug where win% was always shown as home team's even when the
sheet reports the away team as the leader.
Additionally: live scorebug tracker now distinguishes between "all games
confirmed final" and "sheet read failures" — transient Google Sheets errors
no longer hide the live scores channel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- #37: Fix stale comment in transaction_freeze.py referencing wrong moveid format
- #27: Change config.testing default from True to False (was masking prod behavior)
- #25: Replace deprecated asyncio.get_event_loop() with get_running_loop()
- #38: Replace naive datetime.now() with timezone-aware datetime.now(UTC) across
7 source files and 4 test files to prevent subtle timezone bugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ran `ruff check --select F401 --fix` to auto-remove 221 unused imports,
manually removed 4 unused `import discord` from package __init__.py files,
and fixed test import for DISAPPOINTMENT_TIERS to reference canonical location.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove hardcoded Giphy API key from config.py, load from env var (#19)
- URL-encode query parameters in APIClient._add_params (#20)
- URL-encode Giphy search phrases before building request URLs (#21)
- Replace internal exception details with generic messages to users (#22)
- Replace all bare except: with except Exception: (#23)
- Guard interaction.guild access in has_player_role (#24)
- Replace MD5 with SHA-256 for command change detection hash (#32)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automates the SSH-to-akamai deploy workflow: pulls latest image,
restarts the container, and verifies health. Includes pre-deploy
checks (dirty git warning, confirmation prompt) and prints a
rollback command if the image changed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Stadium Image field was added to the weather embed but the
test_full_weather_workflow assertion wasn't updated to match.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ContextualLogger methods forwarded all **kwargs as extra={} to Python's
standard logger. When callers passed exc_info=True, it landed in the
extra dict and Python's LogRecord raised KeyError("Attempt to overwrite
'exc_info' in LogRecord") since exc_info is a reserved attribute.
This caused /submit-scorecard to crash after game data was already
posted, masking the original error and preventing proper rollback.
Fix: Extract exc_info and stack_info from kwargs before passing as extra,
forwarding them as proper logging parameters instead. Also fix direct
callers in submit_scorecard.py and views/players.py to use error=e.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DEFAULT_ACTIONS_URL=self requires local actions use short form
(cal/gitea-actions/...) so the runner passes its auth token, and
GitHub actions use full URLs (https://github.com/...) to bypass
local resolution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>