Skip to main content

Scheduler

The scheduler daemon is the background process that claims and fires triggers from pgserve. It tracks worker liveness via the state management layer. It runs as a persistent loop, combining real-time PostgreSQL notifications with poll-based fallback for reliability.

Architecture

Configuration

Override the concurrency cap with GENIE_MAX_CONCURRENT.

Trigger Lifecycle

Claiming

Triggers are claimed using PostgreSQL’s SELECT FOR UPDATE SKIP LOCKED, which provides lease-based atomic claiming across multiple scheduler instances:
This ensures exactly-once execution even if multiple scheduler daemons are running.

Idempotency

Each trigger can carry an idempotency_key. A unique index on this column prevents double-fire:

State Flow

Cron Expressions

The cron parser supports standard 5-field expressions with extensions:
Supported syntax:
  • Wildcards: *
  • Ranges: 1-5
  • Steps: */10, 1-5/2
  • Lists: 1,3,5
Duration strings are also supported for interval-based scheduling:

Heartbeat Collection

Every 60 seconds, the scheduler collects heartbeats from all active agents:
  1. Pane liveness — checks if tmux panes are still alive
  2. Agent state — reads current state from the agent registry
  3. Context capture — stores pane content snapshot
Heartbeats are stored in the heartbeats table:

Machine Snapshots

Every 60 seconds (alongside heartbeats), the scheduler captures a machine-level snapshot:

Orphan Reconciliation

Every 5 minutes, the scheduler scans for orphaned runs — agents that have stopped responding:
  1. Find runs in leased or running status
  2. Check if the agent has missed more than 2 consecutive heartbeats
  3. Mark dead runs as failed with a reconciliation reason
  4. Reclaim expired leases for retry

Reboot Recovery

On startup, the scheduler performs recovery:
  1. Reclaim expired leases — triggers where leased_until < now() are reset to pending
  2. Reconcile orphaned runs — runs without matching live agents are marked failed
  3. Resume polling — normal LISTEN + poll loop begins

Structured Logging

The scheduler writes structured JSON logs to ~/.genie/logs/scheduler.log:
Trace IDs are propagated from the trigger into the spawned agent’s environment, enabling end-to-end observability from schedule definition to agent execution.