PRD: portable durable scheduling — remaining work
Status: Partially implemented
Owner: Solid Objects maintainers
Reminders now cover most of the original scope in both runtimes. This description has been rewritten to the parts that are not done, so nobody re-implements what shipped. The shipped portion is recorded at the bottom.
Remaining requirements
- SCHED-003 (partial): Cancellation shall report whether a schedule was removed.
unschedule and unscheduleAll stage an intent and return nothing, so a caller cannot tell a cancellation that removed a schedule from one that found nothing. Cancellation is already idempotent; only the report is missing.
- SCHED-005 (partial): A
skip missed-run policy shall exist, dropping missed occurrences and waiting for the next scheduled time. Only latest (run once for a missed window) and all (catch up on every missed occurrence) exist. Decide at the same time whether to keep those names or adopt the PRD's run_once / catch_up, and document the mapping either way.
- SCHED-007 (partial): Actor-facing inspection shall return last run, attempt, and failure information.
- Last run is not stored. Neither schema has a column for it. This is the only item here that needs a migration.
- Attempt and failure are stored but not reachable from an actor.
occurrence and error are on the row and are exposed on the operator-facing ReminderRecord, while the actor-facing ScheduledReminder carries only name, operation, key, next run, interval, missed policy, status, and handle.
Remaining acceptance criteria
- One conformance suite across adapters. Create, replace, cancel, recurring execution, each missed-run policy, restart recovery, authorization failure, and duplicate delivery are covered today, but by separate SQL and Durable Objects tests rather than one suite both adapters run.
test/support/portable-runtime-contract.ts exercises no scheduling at all. Repeated parity audits on this project have found gaps that only a shared contract catches, so this is the highest-value item left.
- Document the timing contract. Time zone, clock skew, retention, and retry behaviour for scheduled work are undocumented in both runtimes.
Not planned
reschedule(handle, { runAt }) as a distinct method. SCHED-002 is satisfied: scheduling an existing name replaces its next occurrence atomically, and the repository reports the replacement with its previous run time. A second spelling of that would add surface without adding capability. Reopen if the ergonomics prove otherwise.
Already implemented, for reference
- SCHED-001
schedule({ at:, every:, missed:, key: }) returns a durable handle in both runtimes.
- SCHED-002 A name identifies at most one active schedule per actor, and rescheduling replaces the next occurrence atomically.
- SCHED-004 One-shot and recurring schedules, cancellable by operation, by key, or by handle.
- SCHED-006 A fired reminder enqueues an ordinary message carrying
reminder:<id>:<occurrence> as its idempotency key, so it inherits mailbox ordering, authorization, retry, dead letters, and deduplication.
- SCHED-008 Restart recovery on every adapter: Durable Objects re-arm their alarm on construction, and the SQL scheduler reclaims from the reminders table.
- The portability outcome holds. The same API runs over SQL polling, PostgreSQL notifications, and Durable Object alarms, and no caller names the mechanism.
PRD: portable durable scheduling — remaining work
Status: Partially implemented
Owner: Solid Objects maintainers
Reminders now cover most of the original scope in both runtimes. This description has been rewritten to the parts that are not done, so nobody re-implements what shipped. The shipped portion is recorded at the bottom.
Remaining requirements
unscheduleandunscheduleAllstage an intent and return nothing, so a caller cannot tell a cancellation that removed a schedule from one that found nothing. Cancellation is already idempotent; only the report is missing.skipmissed-run policy shall exist, dropping missed occurrences and waiting for the next scheduled time. Onlylatest(run once for a missed window) andall(catch up on every missed occurrence) exist. Decide at the same time whether to keep those names or adopt the PRD'srun_once/catch_up, and document the mapping either way.occurrenceanderrorare on the row and are exposed on the operator-facingReminderRecord, while the actor-facingScheduledRemindercarries only name, operation, key, next run, interval, missed policy, status, and handle.Remaining acceptance criteria
test/support/portable-runtime-contract.tsexercises no scheduling at all. Repeated parity audits on this project have found gaps that only a shared contract catches, so this is the highest-value item left.Not planned
reschedule(handle, { runAt })as a distinct method. SCHED-002 is satisfied: scheduling an existing name replaces its next occurrence atomically, and the repository reports the replacement with its previous run time. A second spelling of that would add surface without adding capability. Reopen if the ergonomics prove otherwise.Already implemented, for reference
schedule({ at:, every:, missed:, key: })returns a durable handle in both runtimes.reminder:<id>:<occurrence>as its idempotency key, so it inherits mailbox ordering, authorization, retry, dead letters, and deduplication.