fix(scheduler): reject expired one-shot schedules atomically - #1684
Conversation
A one-time 'at' schedule with a timestamp already in the past is accepted, fires on the next scheduler tick, and the one-shot job is then deleted — so the payload runs immediately with no future occurrence and disappears. That is almost never what a caller scheduling a one-time reminder intends. Validate the 'at' timestamp against the current time in SchedulerOps.add and SchedulerOps.update (the two structured-schedule entry points) and raise a field-named ValueError before persisting, matching the existing timezone and interval validation style. Recurring cron/every schedules are unaffected. Fixes #1516
…o agent-tasks/1516
|
Merge queue CI: failure — run 35107000795, attempt 1.
The run summary lists reused suites and executed checks. A green PR check alone does not mean queue validation has passed. |
…o fix/validate-one-shot-schedules-atomically
|
Merge queue CI: failure — run 35110881749, attempt 1.
The run summary lists reused suites and executed checks. A green PR check alone does not mean queue validation has passed. |
|
Merge queue CI: cancelled — run 35122228608, attempt 1.
The run summary lists reused suites and executed checks. A green PR check alone does not mean queue validation has passed. |
|
Merge queue CI: cancelled — run 35123038242, attempt 1.
The run summary lists reused suites and executed checks. A green PR check alone does not mean queue validation has passed. |
|
Merge queue CI: success — run 35126044362, attempt 1. The run summary lists reused suites and executed checks. A green PR check alone does not mean queue validation has passed. |
Scope
Past one-time schedules previously executed immediately and disappeared after their run. New or rescheduled
atjobs now reject past timestamps before persistence, with an actionable field-named error.Scope boundary: scheduler creation/update validation, idempotent creation, the cron update RPC, and regression tests. An update containing both an enabled toggle and an invalid schedule leaves the entire stored job unchanged. Valid combined updates persist once. Idempotent retries return an existing job even after its scheduled time; only a genuinely new job is checked for expiry, after acquiring the creation lock with a fresh clock.
Recurring schedules and metadata-only changes to existing overdue jobs retain their behavior.
Merge-queue validation also exposed an existing preview-driver race: two 16 ms timer samples could occur in one rendering frame and treat a moving control as stable. The driver now samples across rendered frames, keeps the original shared four-second action deadline, cancels stalled frame callbacks, and preserves the last confirmed semantic failure.
This is the stable integration of #1526. The original contribution and maintainer follow-up commits are retained; ongoing main-branch synchronization on the contributor branch does not change this integration head.
Branch
Base branch: main.
Target exception: none.
Integrated mainline fixes: #1674 (SQLite fallback connection compatibility) and #1677 (atomic initial generated publication receipts) resolve existing main-branch failures exposed by CI. Both fixes are included and tested with this change. #1674 has merged; #1677 also has a separate review and CI run for generated publication behavior.
Issue
Linked issue: Fixes #1516.
Release Note
Release note: Reject expired one-time reminders without partially updating tasks or breaking idempotent creation retries. No standalone changelog entry added.
Tests
git diff --checkpassed.Maintainer Live Check
Maintainer live check: no.
Surface: gateway.
Safety
Offline, deterministic, credential-free tests use synthetic jobs and isolated temporary databases. No secrets, runtime transcripts, or local artifacts are included.
Third-Party Origin
Third-party origin: original OpenSquilla contribution by @lifelmy in #1526, retained with its commit history. Maintainer follow-ups make updates atomic and preserve idempotent retries. No external vendored implementation.