A scheduled digest with a long wait
This workflow runs every morning from its declarative
schedulesexport, so nothing outside it has to start a run. It also showssleep, which parks the run without holding a process open. The run stays parked until the timer fires, then resumes from where it slept.
The whole workflow is a single workflows/daily-digest.workflow.ts file. It
needs no trigger of its own: the schedules export is the trigger. Stackbone
reads every cron pattern in UTC, so 0 8 * * * fires at 08:00 UTC, not at
08:00 local time.
import { z } from '@stackbone/sdk';
import { sleep } from '@stackbone/sdk/workflow';
export const schedules = [{ cron: '0 8 * * *', input: { scope: 'daily' } }];
export const inputSchema = z.object({ scope: z.enum(['daily', 'weekly']) });
export const outputSchema = z.object({ sent: z.number() });
export async function dailyDigestWorkflow(input: z.infer<typeof inputSchema>) {
'use workflow';
const recipients = await collectRecipients(input.scope);
let sent = 0;
for (const to of recipients) {
await deliver(to, input.scope);
sent += 1;
await sleep('5s'); // gentle pacing between sends, durable across a restart
}
return { sent };
}
async function collectRecipients(scope: string) {
'use step';
return scope === 'weekly' ? ['team@acme.com'] : ['sam@acme.com', 'lee@acme.com'];
}
async function deliver(to: string, scope: string) {
'use step'; // idempotent on (to, scope, day)
return { to, scope, delivered: true };
}Adding a schedule at run time
The runtime reconciles the schedules export on every boot. That makes it the
right home for a cadence that ships with the workspace. To add or drop one while
the box is running, call the namespaced accessor from inside a workflow:
import { stackbone } from '@stackbone/sdk';
await stackbone.workflows.schedule('daily-digest', { scope: 'weekly' }, '0 8 * * 1');
await stackbone.workflows.unschedule('daily-digest');
const active = await stackbone.workflows.listSchedules();These three live only on stackbone.workflows. There is no top-level
scheduleWorkflow import to reach for. The runtime keeps dynamic schedules
apart from the declarative ones, so a deploy never prunes them.
What's next
- An onboarding pipeline: a linear run you trigger by hand.
- A refund gated by a human approval: park the run until a person decides.
- What is a workflow: the model behind this example.
- Workflow + Agents examples: let a step hand work to a conversational agent.