Scheduled jobs
A scheduled job is a class implementing ScheduledJob from
platform:scheduled-job. The interface is one method:
export interface ScheduledJob { run(): Promise<void>;}No arguments and no return value — a job decides for itself what to look at.
import type { ScheduledJob } from "platform:scheduled-job";import { sql, tableRef } from "platform:stdlib";import { Comment, Issue } from "platform:entities";
const i = tableRef(Issue);
export default class StaleIssueTriage implements ScheduledJob { async run(): Promise<void> { const overdue = await sql.systemMode.query<Issue>(` SELECT ${i.issue_id}, ${i.record_id} FROM ${i} WHERE ${i.due_date} < CURRENT_DATE AND ${i.status} <> 'closed' `);
const comments = overdue.rows.map((issue) => { const comment = new Comment(); comment.issue_id = issue.issue_id; comment.body = "Overdue — please assign an owner."; return comment; });
await sql.systemMode.insertOrThrow(comments); }}No user context
Section titled “No user context”A job is not triggered by a person, so there is no user whose permissions apply.
Plain sql therefore sees very little. Jobs almost always want
sql.systemMode.
Writes are attributed to the built-in SYSTEM user, which exists in every
workspace precisely so automated writes satisfy the foreign keys that reference
users.
Write in bulk
Section titled “Write in bulk”Build the full set, then issue one call. The example above collects every comment and inserts once. The shape to avoid:
// Don't: one round trip per rowfor (const issue of overdue.rows) { await sql.systemMode.insertOrThrow([makeComment(issue)]);}Deploying
Section titled “Deploying”Jobs live in your configuration directory and deploy with everything else:
polymesa deploy ./configSchedules are configured against the job in the app, so the same code can run at a different cadence per workspace.