Schedule & Cron Jobs
SCHEDULE runs an expression once, at a future moment. CRON runs one repeatedly, on a fixed interval. Both register a persistent job in Ocalt's own scheduler — the script that created the job does not need to stay running, or even exist, for the job to fire later.
Writing the Job Body
A registered job runs later, in a script of its own — the script that
registered it is long gone, and operations are script-scoped. The bracket also
takes one statement, so an AFTER chain inside it
will not parse. Use SIDELOAD to carry a
whole body inline, or INCLUDE to point at a reusable file.
SCHEDULE [SIDELOAD `
NEW OPERATION report
OPEN
MAIL TEXT BODY "The scheduled report ran" TO "admin@example.com" SUBJECT "Report"
CLOSE
AFTER RUN report
`] AT ?dateandtime SET ?job
INCLUDE is the alternative when the same body is shared by
several jobs — put the definition and the call together in one
.oql file in your namespace:
NEW OPERATION report
OPEN
EMIT "Ran at the scheduled time"
CLOSE
AFTER RUN report
SIDELOAD
keeps everything in the registered row, and a parse error inside it is an
ordinary catchable error. INCLUDE shares one file across many jobs,
but a missing include is INFINITE — uncatchable: delete
the file and every fire records status = failed with the reason.SCHEDULE — Run Once, at a Future Moment
SCHEDULE [INCLUDE "/root/ops/report.oql"] AT ?dateandtime SET ?job
CRON — Run Repeatedly, on an Interval
Ten unit words are accepted: SECOND SECONDS
MINUTE MINUTES HOUR HOURS
DAY DAYS WEEK WEEKS.
The master scheduler wakes every 10 seconds, so that is the
effective floor: EVERY 1 SECOND and EVERY 9 SECONDS
both behave as roughly ten. Intervals are also a minimum, not a grid
— the next run is scheduled from the moment the previous one finished, so
a job drifts progressively later rather than holding the clock.
NEW OPERATION opname
OPEN
EMIT "Hourly run"
CLOSE
AFTER NEW CRON [INCLUDE "/root/ops/report.oql"] EVERY 1 HOUR STARTING NOW SET ?job
NEW DATE AND TIME SET ?dateandtime
AFTER NEW OPERATION opname
OPEN
EMIT "Bi-hourly run"
CLOSE
AFTER NEW CRON [INCLUDE "/root/ops/report.oql"] EVERY 2 HOURS STARTING ?dateandtime SET ?job
The Triggered Expression Is Not Limited to Operations
The bracket takes one statement, and any statement will do
— [CALCULATE 1 + 1], [FETCH "url"],
[MAIL TEXT BODY "hi" TO "a@b.com" SUBJECT "x"]. An
AFTER chain inside the bracket does not parse; that is what the
.oql file is for.
SCHEDULE [CALCULATE 1 + 1] AT ?dateandtime SET ?job
Registry — List, Delete, Describe
Every scheduled and cron job is stored in a persistent registry — listable, deletable, and describable at any time, from any script.
SCHEDULE LIST SET ?jobs
AFTER FOREACH ?jobs SET ?key AS ?job
OPEN
SCHEDULE DESCRIBE ?job SET ?info
AFTER EMIT ?info & "\n"
CLOSE
SCHEDULE LIST SET ?jobs
AFTER FOREACH ?jobs SET ?key AS ?job
OPEN
SCHEDULE DESCRIBE ?job SET ?info
AFTER SCHEDULE DELETE ?job
CLOSE
CRON LIST SET ?jobs
AFTER FOREACH ?jobs SET ?key AS ?job
OPEN
CRON DESCRIBE ?job SET ?info
AFTER EMIT ?info & "\n"
CLOSE
CRON LIST SET ?jobs
AFTER FOREACH ?jobs SET ?key AS ?job
OPEN
CRON DESCRIBE ?job SET ?info
AFTER CRON DELETE ?job
CLOSE
Lifecycle, Status and Errors
A SCHEDULE fires once and is then marked done. The
row is never removed — it keeps its status and its error, and deleting it
is your call. CRON repeats by definition and is never marked done.
LIST and DESCRIBE return done,
status (pending, ok or
failed) and, on DESCRIBE, last_error with
the failure message verbatim. A completed one-shot reports
next_due: null.
ocalt-core, the same infrastructure already backing subdomains and quota. A single master scheduler loop inside ocalt-core reads this registry continuously, and when a job comes due, calls back into ql-runtime’s credentialed API internally, authenticated as that specific job’s own owner — the same internal-authentication shape already used by Site Mode. The script that registered the job does not need to still be running, or even exist, for it to fire.