schedule-core
August 26, 2026 · View on GitHub
Part of the DSH plugin suite — six Apache-2.0 plugins for DeepSeek Harness.
A persistent, cross-restart scheduler core: SQLite as the archive, lease-claim as the coordination, wall-clock discipline as the law. Zero framework dependencies — execution side effects are host-injected. Every claim carries an experiment number and a control group.
中文版见 README.zh-CN.md。
Why this exists
In-process reminders (setTimeout) and session-local scheduling have two fatal problems: the process dies, the schedule dies (no persistence), and the session dies, the reminder dies (no cross-session claiming). This core stores schedule state in a SQLite archive and uses a single conditional UPDATE for multi-process lease preemption — no Redis, no message queue; SQLite is the arbiter.
| Capability | Mechanism | Experiment |
|---|---|---|
| Survives restarts | SQLite three tables + append-only event log | Unit / real-machine closed loop |
| Multi-consumer mutual exclusion | Lease claiming (claimed_by/lease_until single conditional UPDATE, changes=1 wins) | EXP-3 I5 |
| Due dispatch | C-hybrid driver (single timer for the nearest due + fallback polling) | EXP-2 |
| Clock discipline | Strict RFC3339 validation / IANA time zones / DST rollover detection / rollback never fires early / jump-forward goes straight overdue | EXP-5 |
| Cold start | Lease-release fast path + due-flip unified into sweep | EXP-1 |
| Verification | Wall-clock disorder fuzz + implementation×model differential | EXP-3 / EXP-4 |
State model
scheduled ──(now >= scheduledAt)──▶ overdue ──(claimed + dispatched ok)──▶ dispatched
│ │
└──(cancel)──▶ cancelled ◀──(cancel)┘
dispatched/cancelledare terminal states.- Crash window: dispatched but the event never landed on disk → the lease expires (default 60s) and is re-claimed → at-least-once (no exactly-once promise — the same honest statement as the official one).
- An
everyrecord only tracks the latest occurrence (missed intervals are not enumerated); the anchor advances in alignment.
Quick start
import { ScheduleRegistry } from 'schedule-core'
const reg = new ScheduleRegistry({
dbPath: './data/schedules.db',
now: Date.now, // injectable clock (key design for testing/differential verification)
executor: {
executeJob: (rec) => { /* host executes the task */ return 'done' },
deliverReminder: (rec) => { /* host delivers the reminder */ return 'done' },
},
})
const rec = reg.create({ prompt: 'backup', rule: 'after', afterSeconds: 60, action: 'job',
jobSpec: { kind: 'pwsh', command: 'Backup.ps1', label: 'nightly' } })
reg.list() // active records
reg.delete(rec.id) // { deleted: true }
reg.dispose()
When the executor returns 'retry' the record stays overdue and retries next cycle — natural backoff while the host is not ready.
Honest boundaries
- at-least-once: a crash window may double-dispatch (by design, not a defect).
- every tracks only the latest: missed intervals are not replayed.
- No Cron/calendar rules:
every_seconds ≥ 300(aligned with the official lower bound). - Single-machine scheduling: cross-machine scheduling needs shared storage + distributed locks — this core does neither, and does not claim them.
- Offline applicability: architecturally no network dependency (local SQLite + local timer); multi-day offline runs are not measured, not claimed.
- Execution side effects (job/remind) are the host's business; this core only guarantees when, by whom, exactly one claim.
Development
npm run build # tsc → lib/
npm test # 37 unit + 200-seed fuzz + 644 differential assertions
Requires: Node ≥ 22.6 (node:sqlite, measured on 25.8).
License
Apache-2.0