Skip to main content

Workflow Schedule Template

The Workflow Schedule template demonstrates how BindAI represents time-based workflow schedules and checks whether they are due. A WorkflowSchedule stores:
  • The workflow identifier
  • The next scheduled execution time
  • An optional recurring interval
  • Whether the schedule is enabled
The WorkflowScheduler keeps schedules in memory, identifies enabled schedules that are due, and advances a schedule’s next-run time by its configured interval. Scheduling is separate from workflow execution. The scheduler determines when a workflow is due; application code is responsible for starting the workflow and managing the scheduling lifecycle. The current implementation provides scheduling primitives, not a complete background scheduling service.

Purpose

This template demonstrates how to:
  • Create a workflow schedule
  • Register schedules with a scheduler
  • Check which schedules are due
  • Configure recurring intervals
  • Enable or disable schedules
  • Advance recurring schedules after processing
  • Understand the current limitations of in-memory scheduling
The current scheduler is intentionally lightweight and in-memory.

Scheduling Architecture

The scheduling components provide timing state and due-checking:
The scheduler does not execute workflows itself. An application must call the scheduler’s methods, decide how to process due schedules, and initiate the corresponding workflow executions.

WorkflowSchedule

A WorkflowSchedule is a dataclass describing when a workflow should execute. For example:
The example sets the next run to 09:00 UTC on 11 October 2026, with a 24-hour interval. The current implementation contains four fields: There is no additional recurrence-rule object in the current implementation.

Registering a Schedule

Create a WorkflowScheduler and add schedules with add():
The scheduler stores the schedule in its internal list. The current add() method does not return the schedule. Multiple schedules can be added to the same scheduler.

In-Memory Scheduler

The current scheduler stores schedules in an in-memory list:
This means schedules are process-local and held in memory. The current implementation does not provide:
  • Database persistence
  • File persistence
  • Distributed scheduling
  • Scheduler recovery after process restart
  • A persistent schedule store
  • A background scheduler thread
  • A built-in scheduler service
If the process stops, the scheduler’s in-memory schedules are lost unless application code persists and reconstructs them separately.

Checking Due Schedules

The scheduler provides:
to identify schedules that are currently due. Conceptually:
A schedule is returned when both conditions are true:
  1. schedule.enabled is True.
  2. schedule.next_run <= datetime.now(UTC).
The scheduler evaluates the schedules currently registered in its in-memory list. The comparison requires compatible datetime values. Use timezone-aware UTC timestamps for next_run so they can be compared with the scheduler’s current UTC time. Calling due() does not automatically mark schedules as processed, advance their timestamps, or execute their workflows.

Due Schedule Example

Suppose the scheduler contains these schedules, with times expressed in UTC:
If the current UTC time is 10:30:
Calling:
returns the enabled schedules whose next_run is at or before the current UTC time. This example assumes that the listed times fall on the same date and that the schedules have not been advanced or otherwise modified.

Enabled Schedules

Schedules are enabled by default:
Because enabled defaults to True, the schedule can be returned by:
once its next_run time has arrived. You can also explicitly enable a schedule:
Changing this field affects subsequent due checks.

Disabled Schedules

A schedule can be disabled:
Disabled schedules remain in the scheduler’s internal collection but are ignored by due(). Even if:
the schedule will not be returned while:
This allows application code to temporarily disable a schedule without removing it from the in-memory scheduler.

One-Time Schedules

A schedule with no recurring interval can represent a one-time execution:
When the schedule becomes due, due() returns it. Calling:
does not change next_run because no interval is configured. The scheduler does not automatically disable or remove a one-time schedule. If application code calls due() again after the scheduled time has passed, the same enabled schedule can be returned again. For one-time application behavior, application code should disable or otherwise manage the schedule after processing it:
The application should also decide how to handle a failed workflow execution before treating the schedule as completed.

Recurring Schedules

A recurring schedule specifies an interval:
The interval is expressed in seconds. For example: The scheduler does not interpret calendar recurrence rules such as:
  • Every Monday
  • The first day of every month
  • Weekdays at 09:00
It advances next_run by the configured number of seconds.

Rescheduling

The scheduler provides:
to advance a recurring schedule. Conceptually:
For example, if the schedule has:
then calling:
advances the schedule to:
The implementation adds a timedelta to the existing timestamp:
This operation advances the stored schedule time. It does not start a workflow or verify that a workflow execution succeeded.

Rescheduling Without an Interval

If:
the scheduler does not change next_run. For example:
leaves the scheduled time unchanged.

Zero Interval

The current implementation checks the interval using truthiness:
Therefore:
does not advance the schedule. A positive interval should be used for recurring schedules. The scheduler does not validate the interval value when a schedule is created. Application code should validate its scheduling configuration.

Missed Recurring Schedules

reschedule() advances next_run by one interval only. For example:
Calling:
changes:
It does not automatically advance the schedule to 14:00 UTC or repeatedly catch up missed intervals. Because 10:00 UTC is still in the past, another call to due() can return the schedule again. Application code is responsible for deciding how missed executions should be handled. Possible strategies include processing each missed interval, skipping missed intervals, or advancing directly to the next future run time. Those strategies are not implemented by the scheduler itself.

Scheduler Lifecycle

A typical application-level scheduling loop looks like:
The current WorkflowScheduler provides these core operations:
The surrounding application must determine:
  • When to call due()
  • How to execute due workflows
  • When to call reschedule()
  • How to handle workflow failures
  • How to handle missed schedules
  • How frequently to poll
  • How to prevent duplicate or overlapping executions if required
There is no built-in scheduler loop.

Scheduler Does Not Execute Workflows

WorkflowScheduler does not invoke WorkflowExecutor. Its responsibility is to identify schedules that are due and update schedule timestamps when requested. The separation is:
Application code must resolve workflow_id to the appropriate workflow and initiate execution. This keeps scheduling state separate from workflow execution.

Scheduler vs Workflow Executor

The two components have different responsibilities. The scheduler answers:
When is this workflow due?
The executor answers:
How does this workflow execute?
Application code connects these responsibilities.

Scheduler vs Workflow Loop

Scheduling and workflow loops both support repeated behavior, but at different levels. A scheduler helps determine when a workflow may start. A loop determines how a running workflow repeats.

Example: Hourly Workflow

An application can register an hourly workflow schedule:
The application can poll for due schedules:
This is a simplified illustration of application-managed scheduling. In production code, workflow execution and error handling should be implemented before rescheduling the schedule. The scheduler maintains the timing state. It does not resolve workflow_id to a workflow or execute it automatically.

Multiple Schedules

A single scheduler can contain multiple schedules:
Calling:
evaluates all schedules currently stored in the scheduler. Every enabled schedule whose next_run is at or before the current UTC time is included in the returned list. The scheduler does not check whether multiple schedules refer to the same workflow or whether that workflow is already running.

Current Time Handling

The current implementation uses:
inside WorkflowScheduler.due(). Therefore, due() compares each schedule’s next_run against a timezone-aware UTC datetime. Applications should supply compatible timezone-aware next_run values. Mixing naive and timezone-aware datetimes can cause Python datetime comparisons to raise an exception. Use consistent UTC timestamps to avoid this issue. The scheduler does not currently provide:
  • Timezone conversion
  • Local-time scheduling rules
  • Daylight-saving-time handling
  • Calendar-based recurrence
  • User timezone configuration
These concerns belong to additional application-level scheduling logic.

Timezone Considerations

The scheduler uses UTC for its due-time comparison and advances the stored next_run by a duration. For example:
This represents 09:00 UTC. The current scheduler does not convert local-time rules into UTC or handle daylight-saving transitions for recurring local schedules. For example, a 24-hour interval is a duration of 86,400 seconds; it is not a guarantee that a workflow will run at the same local clock time every day across daylight-saving transitions. Applications that require named-timezone or local-calendar behavior must calculate the next scheduled timestamp themselves or use a scheduling system that supports those requirements.

Recurring Execution

Recurring schedules use a fixed interval. For example:
represents a duration of 24 hours. The scheduler calculates the next execution time by adding that duration:
This is duration-based recurrence rather than calendar-based recurrence. The scheduler does not guarantee that the workflow actually executes at the calculated time. Application code must poll for due schedules and initiate execution.

Schedule State

A schedule contains four pieces of state:
For example:
The scheduler does not maintain additional execution metadata for the schedule. There is no built-in:
  • Last-run timestamp
  • Run count
  • Failure count
  • Execution result
  • Retry state
  • Schedule history
Applications that need these features must manage them separately.

No Duplicate Detection

The scheduler does not check whether a workflow is already registered. For example, application code can add multiple schedules with the same workflow_id:
Both schedules remain in the internal list. due() evaluates them independently. Applications should avoid registering duplicate schedules unintentionally and should implement deduplication if their use case requires it.

No Automatic Execution

The scheduler does not start a background thread or process. An application must actively call:
for due schedules to be detected. A typical application might use its own timer, worker, service loop, or deployment infrastructure to call the scheduler periodically. Without that orchestration, schedules do not trigger workflows automatically.

Typical Use Cases

The scheduling primitives can support application-managed workflows such as:
  • Periodic reports
  • Recurring data synchronization
  • Scheduled document processing
  • Knowledge refreshes
  • Recurring maintenance
  • Automated backups
  • Monitoring workflows
  • Periodic AI processing
The scheduler only manages timing state. Workflow execution remains the responsibility of the surrounding application.

Scheduling and Workflow Execution

A complete application-level flow can look like:
The scheduler intentionally does not contain workflow registry or execution logic. This allows applications to choose how workflows are resolved, executed, monitored, and rescheduled.

Error Handling

The current scheduler does not execute workflows, so it does not manage workflow execution errors. If application code starts a workflow and that workflow fails, the application is responsible for deciding whether and when to reschedule the associated schedule. The scheduler does not implement:
  • Retry policies for workflow executions
  • Backoff
  • Workflow failure handling
  • Dead-letter behavior
  • Execution cancellation
  • Automatic rescheduling after failure
Those concerns belong to the workflow execution or application infrastructure.

Persistence and Recovery

Schedules are stored only in memory:
There is no persistent scheduler store in the current implementation. If the process exits, the schedules held in that scheduler instance disappear. Applications requiring durable scheduling should persist schedule definitions externally and reconstruct the scheduler after startup. Schedule persistence is separate from workflow execution persistence and history.

Best Practices

  • Use timezone-aware UTC values for next_run.
  • Prefer positive interval_seconds values for recurring schedules.
  • Explicitly disable one-time schedules after processing if they should not run again.
  • Decide whether to reschedule recurring schedules after success, failure, or both.
  • Define how missed intervals should be handled.
  • Keep scheduling separate from workflow execution.
  • Avoid registering duplicate schedules unintentionally.
  • Do not assume the scheduler persists schedules.
  • Do not assume the scheduler executes workflows automatically.
  • Handle workflow failures separately from schedule management.
  • Use application-level infrastructure for polling or background scheduling.
  • Design recurring workflows so repeated execution is safe.
  • Add locking or deduplication if multiple application workers might process the same schedule.

Related Templates

The Schedule template relates to:
  • 08.3 workflow-loop.md
  • 08.4 workflow-parallel.md
  • 08.6 workflow-retry.md
  • 08.7 workflow-timeout.md
Scheduling determines when a workflow is due, while these workflow features affect how execution behaves.

Current Limitations

The current WorkflowScheduler is intentionally simple. It does not currently provide:
  • Persistent schedules
  • Background polling
  • Distributed scheduling
  • Scheduler workers
  • Timezone conversion or local-time recurrence
  • Calendar recurrence rules
  • Automatic workflow execution
  • Automatic retries
  • Missed-run recovery
  • Schedule history
  • Duplicate detection
  • Scheduler locking
  • Distributed coordination
These capabilities require additional application or infrastructure layers.

Summary

The Workflow Schedule template demonstrates how BindAI represents and checks time-based workflow schedules. WorkflowSchedule stores:
WorkflowScheduler provides three core operations:
  • add() stores schedules in memory.
  • due() returns enabled schedules whose next_run is at or before the current UTC time.
  • reschedule() advances a schedule by one configured interval when interval_seconds is truthy.
The scheduler does not execute workflows, persist schedules, run a background polling loop, or provide timezone-aware calendar recurrence. It is best understood as a lightweight scheduling-state and due-checking component that application code can combine with BindAI workflow execution.