Skip to content

feat(trigger): schedule flows on cron via first-class trigger records (1.6.0) - #52

Merged
pallaoro merged 1 commit into
mainfrom
thinking-of-building-a-cron-trigger
Aug 21, 2026
Merged

feat(trigger): schedule flows on cron via first-class trigger records (1.6.0)#52
pallaoro merged 1 commit into
mainfrom
thinking-of-building-a-cron-trigger

Conversation

@pallaoro

Copy link
Copy Markdown
Member

Adds flow_trigger — cron schedules for flows on this box — plus the scheduler
that fires them.

A trigger is a separate record (.clawflow/triggers/.json), not a field on
the flow definition. A schedule is mutable operational state (pause at 2am,
retime, repoint at a version) while a published version is immutable: embedding
one in the other makes "pause" mean "publish a new version", and lets an
unrelated publish silently re-arm a schedule the draft happened to carry.
Temporal made the same move from cron-in-workflow to a separate Schedules
object. It also lets N triggers target one flow with different cadences and
different inputs, which a single embedded field cannot express.

  • src/core/triggers.ts TriggerStore, one file per record (mirrors store.ts)
  • src/core/scheduler.ts croner timer per record; fires runner.run() directly,
    the same path the HTTP flow server takes
  • flow_trigger tool create | update | list | pause | resume | delete | run_now

Behavior:

  • Standard 5-field cron, minimum interval 60s. Triggers are agent-writable, so
    sub-minute schedules are rejected at the trust boundary rather than becoming
    a runaway cost incident.
  • Missed runs are NOT replayed; replaying side-effecting flows after downtime
    is the worse failure.
  • Runs never overlap (croner protect).
  • flow_delete PAUSES a flow's triggers rather than removing them — it is a soft
    delete, and a restore must not silently re-arm unattended runs.
  • Mutating actions are gated by the existing approval gate: arming a schedule
    commits the box to running a flow unattended with tool access.
  • The scheduler re-reads the directory every 30s, so records written
    out-of-process (Clawnify hook server / dashboard, importing TriggerStore from
    dist as they already do for versions) go live without a restart. Unchanged
    timers are not rebuilt, so a resync never starves a slow schedule.

Also extracts resolveRunnableFlow() into manage.ts and rewires serve.ts through
it, so "which definition does a trigger run" lives in exactly one place rather
than being duplicated for the scheduler — the divergence 1.5.1 fixed.

croner@10.0.1 is the package's first runtime dependency: it is what OpenClaw
itself uses, so cron semantics match the host, and DST-correct parsing is not
something to hand-roll.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

… (1.6.0)

Adds flow_trigger — cron schedules for flows on this box — plus the scheduler
that fires them.

A trigger is a separate record (.clawflow/triggers/<id>.json), not a field on
the flow definition. A schedule is mutable operational state (pause at 2am,
retime, repoint at a version) while a published version is immutable: embedding
one in the other makes "pause" mean "publish a new version", and lets an
unrelated publish silently re-arm a schedule the draft happened to carry.
Temporal made the same move from cron-in-workflow to a separate Schedules
object. It also lets N triggers target one flow with different cadences and
different inputs, which a single embedded field cannot express.

- src/core/triggers.ts   TriggerStore, one file per record (mirrors store.ts)
- src/core/scheduler.ts  croner timer per record; fires runner.run() directly,
                         the same path the HTTP flow server takes
- flow_trigger tool      create | update | list | pause | resume | delete | run_now

Behavior:
- Standard 5-field cron, minimum interval 60s. Triggers are agent-writable, so
  sub-minute schedules are rejected at the trust boundary rather than becoming
  a runaway cost incident.
- Missed runs are NOT replayed; replaying side-effecting flows after downtime
  is the worse failure.
- Runs never overlap (croner protect).
- flow_delete PAUSES a flow's triggers rather than removing them — it is a soft
  delete, and a restore must not silently re-arm unattended runs.
- Mutating actions are gated by the existing approval gate: arming a schedule
  commits the box to running a flow unattended with tool access.
- The scheduler re-reads the directory every 30s, so records written
  out-of-process (Clawnify hook server / dashboard, importing TriggerStore from
  dist as they already do for versions) go live without a restart. Unchanged
  timers are not rebuilt, so a resync never starves a slow schedule.

Also extracts resolveRunnableFlow() into manage.ts and rewires serve.ts through
it, so "which definition does a trigger run" lives in exactly one place rather
than being duplicated for the scheduler — the divergence 1.5.1 fixed.

croner@10.0.1 is the package's first runtime dependency: it is what OpenClaw
itself uses, so cron semantics match the host, and DST-correct parsing is not
something to hand-roll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pallaoro
pallaoro merged commit ec7a9ce into main Aug 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant