Skip to content

feat(llc): Heartbeat scheduler — Redis sorted set, cron dispatch, restart-safe #8225

Closed

Description

@mrveiss

Parent

Phase 3 (#8207) · Umbrella (#8204)

Depends On

#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
  • agent_org_nodes extended with: heartbeat_cron TEXT nullable (cron expression), heartbeat_enabled BOOLEAN DEFAULT FALSE, last_heartbeat_at TIMESTAMPTZ nullable, adapter_type TEXT, adapter_config JSONB, context_mode ENUM(thin, fat) DEFAULT thin
  • 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
  • python -m pytest autobot-backend/llc/ -k heartbeat_scheduler passes

Activity

  1. mrveiss commented on May 24, 2026

    @mrveiss
    OwnerAuthor

    Closed — implemented and merged ✅

    PR #8497 merged to Dev_new_gui on 2026-05-24.
    Merge commit: 36d4057f8ed43aa99743644028ec909433ccc849

    What was delivered

    • HeartbeatScheduler — Redis sorted-set cron dispatcher (llc/scheduler/heartbeat_scheduler.py)
      • ZADD NX restart-safe population on startup
      • 5s poll loop with ZRANGEBYSCORE + atomic ZREM claim (multi-worker safe)
      • _create_run → QUEUED → RUNNING → SUCCEEDED/FAILED lifecycle
      • last_heartbeat_at updated on SUCCEEDED only
    • Manual trigger API — POST /llc/agents/{agent_id}/heartbeat/trigger → 202, tenant-isolated, 404 oracle-safe
    • Run history API — GET /llc/agents/{agent_id}/runs[/{run_id}] with org-scoped filter
    • Migrations — 20260523_036 (scheduler fields on agent_org_nodes), 20260523_037 (company_id column)
    • Singleton — get_heartbeat_scheduler() shared across lifespan and API routes
    • 12 unit tests passing

    Related

    • Tracked in Paperclip as MVA-822
    • Authorized merge: CEO override after 7 CR rounds (CodeReviewer unresponsive)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions