Summary
Add an optional precheck gate to Schedule-triggered workflows so a recurring workflow can cheaply decide whether there is work before executing its steps (which may call agents, webhooks, or post messages). When the gate says "no work," the run is recorded as skipped and no actions fire — no agent turn, no webhook, no channel noise.
This mirrors a pattern we recently upstreamed to two other agent runtimes and would make Buzz's scheduled agent workflows materially cheaper and quieter.
Problem
TriggerDef::Schedule fires a workflow on a cron/interval. Today the only convergence control is per-step if: (evalexpr) conditions evaluated against trigger context + prior step outputs. For a poller workflow ("every 15m, check X, and if there's something new, have an agent summarize it"), each tick still spins up the step chain (and any agent/webhook action) even when there is nothing to do. On a busy relay with many scheduled agent workflows, that is a large, silent cost and a source of empty-run channel noise.
Prior art (why we think this is the right shape)
We contributed the same idea to OpenClaw (job.precheck) and Hermes (NO_WORK wake gate) cron systems: a cheap gate runs before the expensive work; exit 0 / WORK_NEEDED → run, exit 2 / NO_WORK → skip. The key lessons we'd carry over:
- The gate lives inside the scheduler run path (not a sidecar) so there's one source of truth and no double-firing.
- One documented contract (exit code + a
NO_WORK stdout convention) with tests.
- Skipped runs are recorded distinctly (not errors) for observability.
Proposed shape (seeking maintainer direction before implementing)
A workflow-level, evalexpr-based gate that reuses the existing evaluate_condition machinery (no new execution surface, no shell):
name: pr-triage
trigger:
on: schedule
cron: "*/15 * * * *"
precheck: "trigger_open_pr_count > 0" # evalexpr, same vars as step if:
steps:
- action: { send_message: { text: "…" } }
- If
precheck evaluates false → the run is skipped (distinct status, metric), zero actions.
- Purely additive: workflows without
precheck behave exactly as today.
- Reuses
evaluate_condition (already bounded: 4 KB expr cap + 100 ms timeout), so it inherits the same safety envelope — no shell/command execution added to the relay.
Deliberately out of scope
- Arbitrary shell/command prechecks (security surface in the relay — not proposing that).
- Any change to non-schedule triggers.
Open questions for maintainers
- Is a workflow-level
precheck (vs. telling authors to gate step 1 with if:) worth the ergonomics + the distinct skipped-run status/metric?
- Preferred skipped-run surfacing: a workflow-run status value, a metric only, or both?
- Would you want the gate to have access to any state beyond current trigger context (e.g., last-run state) — or keep it stateless in v1?
Happy to implement behind a small, additive PR with tests + docs once the shape is agreed. I recently shipped #2289 (agent error_class) and #2296 (offline-mention notice) in this area and can follow the same minimal-diff approach.
Summary
Add an optional precheck gate to
Schedule-triggered workflows so a recurring workflow can cheaply decide whether there is work before executing its steps (which may call agents, webhooks, or post messages). When the gate says "no work," the run is recorded as skipped and no actions fire — no agent turn, no webhook, no channel noise.This mirrors a pattern we recently upstreamed to two other agent runtimes and would make Buzz's scheduled agent workflows materially cheaper and quieter.
Problem
TriggerDef::Schedulefires a workflow on a cron/interval. Today the only convergence control is per-stepif:(evalexpr) conditions evaluated against trigger context + prior step outputs. For a poller workflow ("every 15m, check X, and if there's something new, have an agent summarize it"), each tick still spins up the step chain (and any agent/webhook action) even when there is nothing to do. On a busy relay with many scheduled agent workflows, that is a large, silent cost and a source of empty-run channel noise.Prior art (why we think this is the right shape)
We contributed the same idea to OpenClaw (
job.precheck) and Hermes (NO_WORKwake gate) cron systems: a cheap gate runs before the expensive work; exit0/WORK_NEEDED→ run, exit2/NO_WORK→ skip. The key lessons we'd carry over:NO_WORKstdout convention) with tests.Proposed shape (seeking maintainer direction before implementing)
A workflow-level, evalexpr-based gate that reuses the existing
evaluate_conditionmachinery (no new execution surface, no shell):precheckevaluates false → the run is skipped (distinct status, metric), zero actions.precheckbehave exactly as today.evaluate_condition(already bounded: 4 KB expr cap + 100 ms timeout), so it inherits the same safety envelope — no shell/command execution added to the relay.Deliberately out of scope
Open questions for maintainers
precheck(vs. telling authors to gate step 1 withif:) worth the ergonomics + the distinct skipped-run status/metric?Happy to implement behind a small, additive PR with tests + docs once the shape is agreed. I recently shipped #2289 (agent
error_class) and #2296 (offline-mention notice) in this area and can follow the same minimal-diff approach.