Tell the pool when the picks page opens #451
No reviewers
Labels
No labels
Kestra
bug
enhancement
someday
subtask
☁️ api
🎛️ infrastructure
🐞 sentry
📆 2025 Season
📝 pages
allpicks
📝 pages
picks
📝 pages
standings
🚀 performance
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
johnsturgeon/tgfp-web!451
Loading…
Reference in a new issue
No description provided.
Delete branch "re-automate-the-picks-page-is-ready-discord-chat-210"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The odds lock moved to Wednesday 6am and nothing announced it, so the picks
page released to an empty room.
Where the announcement goes
Rule 4 is the only place
odds_statechanges, and_get_current_odds_stateisone way, so the only transition it can make is PRELIMINARY to LOCKED. The
announcement hangs off that transition instead of becoming a rule of its own:
Once per week by construction -- the reconciler falsifies its own condition, so
there is no staleness window to tune and nothing added to the rule table.
It could not have been a rule keyed on
_is_stale:successesis keyed byjob, not by week, so week 2's announcement would look already-done because week
1's row is sitting there. The nag gets away with that only because it is
time-relative and repeats.
The trade is at-most-once -- commit lands, container dies, nobody is told. If
that ever matters the upgrade is a
week.announced_atcolumn and its own rule,the same shape as
PlayerAward.notified_at.The dispatcher reads as a table again
Rules are grouped by the state each one turns on:
game_stateodds_stateOdds locking is now a reconciler shaped exactly like game state, with the
one-way rule expressed in
_get_current_odds_staterather than only in theOddsStatedocstring.Rules 8 and 9 ask
_game_finished_since_last_run(games, successes, job_id),which retires the early
if last_change is None: return-- a guard that wouldhave silently disabled any rule added below rule 9 on every week without a
finished game, which is most of every week.
Tests
283 passing. Five odds tests needed a seeded schedule refresh: they had been
relying on rule ordering without saying so, and the regrouping surfaced it.
Mutation-checked, each caught:
min()over no games)Noneguard removed from the finished-game checkJobStaterowresolves #210
🤖 Generated with Claude Code
This is a well-structured refactor with genuinely good test coverage; I checked the surrounding code and the core logic is sound.
Review
The change reorganizes
tick()into a clean rule table, expresses odds locking as a one-way reconciler (_get_current_odds_state), and hangs the new Discord announcement off the single PRELIMINARY→LOCKED transition. I verified the domain premise —app/main.py:305gates the picks page on exactlyodds_state == LOCKED, so coupling the announcement to that transition is correct — and confirmed rule 4 stays below rule 2, soweek.utc_lock_odds_at(which doesmin()overself.gamesatapp/models/week.py:111) is never evaluated on an empty game list.Worth fixing
app/jobs/dispatcher.py:110-114commits the LOCKED state and only then schedules the announce; if the process dies between the commit and the job running (or the webhook throws), the announcement is lost forever because the reconciler has already falsified its own condition. The PR calls this out and the "empty room" bug that motivated the whole change is precisely a missed announcement — so it's slightly ironic to ship a mechanism that can still silently drop it. The suggestedweek.announced_atcolumn is the right fix; consider whether it's worth doing now rather than later.Nits
app/jobs/announce_picks_page.py:11— "Announcment" → "Announcement"._picks_page_ready_message()(announce_picks_page.py:9-14) emitssentry_logger.info(...)as a side effect of a function that otherwise just returns a string. Move the log intoannounce_picks_page_to_discord()so the builder stays pure.nag_players.pymodule docstring warned thatfrom sentry_sdk import logger as sentry_loggermust be imported explicitly orsentry_sdk.loggerraisesAttributeError. That warning applied process-wide and the newannounce_picks_page.pyrelies on the same idiom; dropping it loses institutional knowledge. Consider relocating a one-line version somewhere durable rather than deleting it outright.announce_picks_page_to_discordhas no direct unit test (webhook mocked). This is consistent withnag_the_players, which also has no body-level test, so it's not a regression — but the dispatcher tests only prove the job is scheduled, not that it constructs a valid webhook.Module-level
Config.get_config()atannounce_picks_page.py:6mirrors the existing pattern inscheduler.py:69, andwebhook.execute()runs in the scheduler thread pool rather than an async handler, so neither is a concern.