The one-shot VERIFY_DELAY_MINUTES check was too rigid: MassPrice's
processing time varies widely in practice (observed ~1 to over 20
minutes, and its queue can back up further under load — confirmed
live, a run still sitting in "Queued" 15+ minutes in), so a single
check window either misses slow-but-fine completions or has to be set
needlessly long.
Replaced with a poll loop: scheduler.py reschedules another one-off
check every VERIFY_POLL_MINUTES (default 5) until automation's
check_completion() reports "complete", up to VERIFY_MAX_ATTEMPTS
(default 12, ~1 hour) before giving up and marking "not_confirmed" for
good. verify_run() is split into a pure check_completion() (no storage
side effects) so scheduler.py's loop owns the retry/give-up decision;
automation.py just answers "is it done yet".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
Status now reads "Sent" when the confirmation click lands, since that
only means MassPrice accepted the run request, not that it finished —
their own processing is async and takes anywhere from ~1 to ~22
minutes in observed practice. A new "Complete" column tracks that
separately: run_rule() captures a watermark (the target rule's current
top data-activitylogid on /Activity/List, not the displayed timestamp
string, to sidestep fragile date/timezone parsing) before submitting,
and scheduler.py schedules a one-off verify_run() job
VERIFY_DELAY_MINUTES later to check whether a new "Complete" entry
appeared past that watermark.
rebuild_jobs() now only clears recurring "rule-*" jobs, not the one-off
"verify-*" jobs, so editing a schedule can't cancel a verification
that's already in flight for an earlier run.
Also scopes the Activity page to the last 24 hours per request.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
The America/Phoenix TZ fix earlier only passed timezone= to
BackgroundScheduler's constructor. That wasn't enough: APScheduler's
CronTrigger independently calls tzlocal's get_localzone() in its own
__init__ when not given an explicit timezone, regardless of what the
scheduler itself is configured with. This never surfaced in testing
because schedules.json was always empty — the rebuild_jobs() loop that
constructs CronTrigger never executed. It crashed for real once a real
schedule existed (confirmed live: the already-configured "Pokemon
Singles >10" 4x/day schedule crash-looped the container on every boot).
Fixed by passing the same resolved timezone to every CronTrigger call.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v
Drives massprice.tcgplayer.com's Run Rule action per-rule on a
user-configured schedule (multiple times/day, day-of-week toggles),
replacing MassPrice's native scheduler (capped at 15 schedules
account-wide, one run/day, with observed clock drift). Deployed via
Forgejo push-to-deploy on Unraid, matching arr-summary/card-grader.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H6TgffxTvYhqxykAbVQg2v