You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#8218 — Agent API keys. #8213 — Work item atomic checkout.
Context
The heartbeat scheduler is the engine that makes agents autonomous. It fires adapter invocations on cron without human intervention. It must survive process restarts without losing scheduled runs.
Acceptance Criteria
llc_heartbeat_runs table:
id UUID PK, company_id FK, agent_id FK, invocation_source ENUM(scheduler, manual, callback), status ENUM(queued, running, succeeded, failed, cancelled, timed_out), started_at, finished_at, error TEXT nullable, external_run_id TEXT nullable, context_snapshot JSONB nullable, created_at
HeartbeatScheduler in llc/scheduler/heartbeat_scheduler.py:
On startup: loads all heartbeat_enabled = true agents from DB, computes next fire time per cron, writes to Redis sorted set llc:heartbeat:schedule with score = next fire timestamp (epoch seconds)
Polling loop: every 5s, ZRANGEBYSCORE llc:heartbeat:schedule 0 {now} to find due agents
For each due agent: creates heartbeat_run record, dispatches adapter (async), updates sorted set with next fire time
On process restart: re-reads DB, re-populates sorted set (idempotent)
Manual trigger: POST /api/llc/agents/{id}/heartbeat/trigger — creates queued run, dispatches immediately
GET /api/llc/agents/{id}/runs — list heartbeat runs, paginated, filterable by status
GET /api/llc/agents/{id}/runs/{run_id} — run detail with context snapshot
Scheduler is started in autobot-backend/initialization/background_tasks.py
Activity
fix(llc): resolve circular import in llc/services/__init__.py (#8225)
fix(llc): rename migration 033→036, split heartbeat_runs to its own m…
fix(llc): company_id UUID type — join agents table, guard None on run…
feat(llc): heartbeat scheduler — Redis sorted set, cron dispatch, res…
fix(llc): resolve circular import in llc/services/__init__.py (#8225)
fix(llc): rename migration 033→036, split heartbeat_runs to its own m…
fix(llc): company_id UUID type — join agents table, guard None on run…
refactor(llc/8229): rename HeartbeatScheduler → RoutineScheduler, mov…
refactor(llc/8229): rename HeartbeatScheduler → RoutineScheduler, mov…
feat(llc): heartbeat scheduler — Redis sorted set, cron dispatch, res…
fix(llc): resolve circular import in llc/services/__init__.py (#8225)
fix(llc): rename migration 033→036, split heartbeat_runs to its own m…
fix(llc): company_id UUID type — join agents table, guard None on run…
feat(llc): heartbeat scheduler — Redis sorted set, cron dispatch, res…
fix(llc): rename migration 033→036, split heartbeat_runs to its own m…
fix(llc): company_id UUID type — join agents table, guard None on run…
mrveiss commentedon May 24, 2026
Closed — implemented and merged ✅
PR #8497 merged to
Dev_new_guion 2026-05-24.Merge commit:
36d4057f8ed43aa99743644028ec909433ccc849What was delivered
llc/scheduler/heartbeat_scheduler.py)ZADD NXrestart-safe population on startupZRANGEBYSCORE+ atomicZREMclaim (multi-worker safe)_create_run→ QUEUED → RUNNING → SUCCEEDED/FAILED lifecyclelast_heartbeat_atupdated on SUCCEEDED onlyPOST /llc/agents/{agent_id}/heartbeat/trigger→ 202, tenant-isolated, 404 oracle-safeGET /llc/agents/{agent_id}/runs[/{run_id}]with org-scoped filter20260523_036(scheduler fields onagent_org_nodes),20260523_037(company_idcolumn)get_heartbeat_scheduler()shared across lifespan and API routesRelated
fix(migrations): baseline adoption for never-migrated DBs + strict An…