Facio's Heartbeat System: How Interval-Based Task Polling Complements Cron Scheduling
Cron is the right answer for time-anchored work: "run this at 9 AM every Monday." It's the wrong answer for interval-driven work: "check if there's something to do every 5 minutes."
Facio's Heartbeat system fills that gap. Where cron fires at specific wall-clock times, Heartbeat fires on a configurable interval, reads HEARTBEAT.md — a plain markdown file the agent manages at runtime — and executes any pending tasks it finds.
Here's how it works, how it differs from cron, and when to use each.
The Architecture: File-Based Task Queue
The Heartbeat system is deliberately simple. At its core are three things:
- A configurable interval — the runtime checks
HEARTBEAT.mdevery N seconds/minutes. - A task file —
HEARTBEAT.mdin the workspace root, a markdown file listing pending tasks. - File tool integration — agents use
edit_file,write_file, andread_fileto manage the task list.
No database. No queue server. No external scheduler. Just a file that the agent reads on every heartbeat tick and a runtime that knows to check it regularly.
# HEARTBEAT.md (example)
## Pending Tasks
- [ ] Check disk usage on production volume and alert if >80%
- [ ] Verify all MCP servers responding, restart any that are down
- [ ] Pull latest social media mentions and queue for review
- [ ] Rotate temporary workspace files older than 24 hours
On each heartbeat tick, the agent reads the file, processes pending tasks, and updates the file — marking completed tasks done or removing them entirely.
When to Use Heartbeat vs. Cron
The distinction matters more than it first appears:
| Heartbeat | Cron | |
|---|---|---|
| Trigger | Interval ("every 5 minutes") | Wall-clock time ("at 09:00 UTC") |
| Task source | HEARTBEAT.md file | Cron job definition |
| Task management | Agent edits the file at runtime | Immutable after scheduling (edit via cron tool) |
| Best for | Queue processing, health checks, polling, maintenance sweeps | Scheduled reports, daily digests, time-specific actions |
| Failure handling | Tasks persist in file until completed | Job runs or doesn't; state is external |
Heartbeat is for pull-based work — the agent checks what needs doing and does it. Cron is for push-based work — the scheduler triggers a specific action at a specific time.
Use heartbeat when:
- A task should run "soon, but not on a precise clock"
- Multiple agents might add tasks to a shared queue
- Tasks are ephemeral and should be self-cleaning (complete → remove)
- You need a maintenance loop that runs continuously in the background
Use cron when:
- A task must fire at a specific wall-clock time
- The schedule is known in advance and rarely changes
- You need exactly-once execution guarantees
- The task has downstream dependencies on exact timing
How Agents Manage HEARTBEAT.md at Runtime
The agent interacts with the heartbeat task list using the same file tools it uses for everything else:
Adding a task:
edit_file(
path="HEARTBEAT.md",
old_text="## Pending Tasks\n",
new_text="## Pending Tasks\n- [ ] Verify SSL certificate expiry for all domains\n"
)
Removing a completed task:
edit_file(
path="HEARTBEAT.md",
old_text="- [ ] Check disk usage on production volume and alert if >80%\n",
new_text=""
)
Rewriting the entire task list:
write_file(
path="HEARTBEAT.md",
content="# Heartbeat Tasks\n\n## Pending Tasks\n- [ ] Daily health check suite\n- [ ] Weekly dependency audit\n"
)
This is inline learning applied to task management — the agent uses the same edit_file tool it uses to update MEMORY.md and BUGS.md. The mechanism is identical; the target file is different.
The result: an agent can add a maintenance task to its own queue, the heartbeat picks it up on the next tick, and the agent processes it. Self-service task management, no human needed to schedule a cron job.
Production Patterns
Pattern 1: Health Check Loop
Heartbeat interval: 5 minutes
Tasks in HEARTBEAT.md:
- [ ] Ping all configured MCP servers
- [ ] Check workspace disk usage
- [ ] Verify Placet.io channel connectivity
Every 5 minutes, the agent runs the health check suite. Failed checks get logged. Successful checks get re-queued for the next interval. If something breaks at 3 AM, the agent detects it within 5 minutes — no cron expression for every 5 minutes, 24/7, needed.
Pattern 2: Queue Processing
Heartbeat interval: 2 minutes
Tasks in HEARTBEAT.md:
- [ ] Process pending social media posts (queue: social-queue.json)
- [ ] Send any queued email notifications
- [ ] Retry failed webhook deliveries
External systems or other agents append tasks to a queue file. The heartbeat agent polls every 2 minutes, drains the queue, and processes. This is a lightweight producer-consumer pattern without a message broker — just a file and a polling interval.
Pattern 3: Maintenance Sweeps
Heartbeat interval: 60 minutes
Tasks in HEARTBEAT.md:
- [ ] Delete workspace temp files older than 24 hours
- [ ] Archive conversation logs older than 30 days
- [ ] Compact the memory search index
Maintenance tasks that don't need precise scheduling — "sometime in the next hour" is good enough. Heartbeat handles them without cluttering the cron schedule with one-off cleanup jobs.
Pattern 4: Graduated Cron-to-Heartbeat
Sometimes a task starts as cron-scheduled and graduates to heartbeat-polled:
- Initially: cron job at 9 AM daily processes the previous day's data.
- System grows: data arrives continuously, daily batch is too slow.
- Transition: cron job stops. Task moves to HEARTBEAT.md. Heartbeat interval set to 10 minutes.
- Result: near-real-time processing without rewriting the scheduling logic.
The migration is a file edit, not an infrastructure change.
Combined Scheduling: A Complete Automation Architecture
Facio's scheduling ecosystem has three layers:
| Layer | Mechanism | Trigger | Use case |
|---|---|---|---|
| Cron | cron tool | Time-based (cron expressions, ISO timestamps) | Scheduled reports, daily digests, reminders |
| Heartbeat | HEARTBEAT.md | Interval-based (configurable ticks) | Health checks, queue processing, maintenance |
| Spawn | spawn tool | Event-based (agent decision) | Parallel research, one-off delegations |
Together they cover the full scheduling spectrum: time-driven, interval-driven, and event-driven execution. No external scheduler needed. No separate worker pool. The agent manages all three layers with the same tooling.
Bottom Line
Not every recurring task fits a cron expression. Health checks that should run every 5 minutes forever, queue processing that should drain as fast as the interval allows, maintenance sweeps that just need to happen "regularly" — these are interval-driven problems, and forcing them into cron's time-anchored model creates fragile schedules and unnecessary complexity.
Facio's Heartbeat system gives you interval-based polling with a file-based task queue that agents manage at runtime. It's cron's complement — not its replacement — and together they give agents a complete scheduling surface for autonomous operation.
See the Heartbeat documentation for interval configuration, task file conventions, and combined scheduling patterns with cron and spawn.