Skip to main content

Cascade — agent task orchestration platform combining Leantime's strategic coherence with AgentRQ's agent orchestration

Project description


Typing SVG



Cascade is a from-scratch, production-hardened reimplementation that fuses Leantime's strategic coherence (task → goal → milestone) with AgentRQ's agent orchestration (dequeue, status state machine, continuous monitoring) — rebuilt in async Python on FastAPI + SQLAlchemy 2.0, with a live HTMX/SSE dashboard.

Quick start · Architecture · MCP tools · Tests · Contributing


✨ Why Cascade

🎯 Strategic coherence, not vibes

Every task links explicitly to a Goal and Milestone. Progress is never denormalised — it's computed at read-time from linked task statuses, so there's nothing to drift and nothing to repair.

🔄 Pull-based, atomically-claimed queue

Agents call get_task to dequeue the highest-priority ready task. Claiming is a single conditional UPDATE … WHERE status='not_started' — when two agents race for the same task, exactly one wins.

⏱️ Continuous monitoring, not hourly cron

A 10-second loop runs the poller, pinger and scheduler concurrently — stalled tasks get nudged, dead agent sessions get evicted, and cron templates spawn child tasks — all in near real time.

🤖 Autonomy-first by design

AutoDecisionService picks the safest, fastest option itself and only escalates to a human for genuinely irreversible operations (delete, drop, production-deploy, …).

🔗 Cross-project choreography

Event + EventTrigger let a task completion in one project silently materialise a task in another — no polling, no glue code.

📡 Real-time everything

Server-Sent Events broadcast every status change, message and agent heartbeat straight into a dependency-free HTMX dashboard — drag-and-drop Kanban included.


🧱 Tech stack

Layer Choice
Runtime Python 3.11+ · FastAPI · Uvicorn
ORM SQLAlchemy 2.0 (async, Mapped[]) · aiosqlite · WAL mode
Migrations Alembic
Schemas Pydantic v2
IDs python-ulid — time-ordered, sortable
Real-time sse-starlette + in-memory pub/sub
Scheduling croniter (cron-template spawning on the monitoring-loop tick)
UI HTMX + Tailwind (CDN, zero build step)
Agent protocol Model Context Protocol (MCP) tool registry

🚀 Quick start

pip install cascade-orchestrator

# launch the API + dashboard
cascade

Or from source:

git clone https://github.com/nguyenminhduc9988/cascade.git
cd cascade
python -m venv venv && source venv/bin/activate
pip install -e ".[dev]"

python -m uvicorn cascade.main:app --reload --port 8100

Open http://localhost:8100 for the live dashboard. Interactive API docs live at /docs, and /api/health reports service health.

The database is created automatically on first boot (init_db). For managed schema changes, use Alembic:

python -m alembic upgrade head
python -m alembic revision --autogenerate -m "describe change"

🏗️ Architecture

flowchart LR
    subgraph Agents["🤖 Agents"]
        A1[Agent A]
        A2[Agent B]
    end

    subgraph Cascade["🌀 Cascade Core"]
        MCP[MCP Tool Registry<br/>get_task · reply · update_status]
        API[FastAPI REST + SSE]
        Loop[Monitoring Loop — 10s tick]
        DB[(SQLite / Postgres)]
    end

    subgraph UI["📊 Dashboard"]
        Board[Kanban Board]
        SSE[Live SSE Stream]
    end

    A1 -- dequeue / claim --> MCP
    A2 -- dequeue / claim --> MCP
    MCP --> API
    API <--> DB
    Loop -- poller / pinger / scheduler --> DB
    Loop -- broadcasts --> SSE
    SSE --> Board
    API --> Board

    style Cascade fill:#6366f1,stroke:#4338ca,color:#fff
    style Agents fill:#0ea5e9,stroke:#0284c7,color:#fff
    style UI fill:#f59e0b,stroke:#d97706,color:#fff

Data model

erDiagram
    Project ||--o{ Goal : has
    Project ||--o{ Milestone : has
    Project ||--o{ Task : contains
    Project ||--o{ Event : defines
    Goal ||--o{ Task : "linked (read-time progress)"
    Milestone ||--o{ Task : "linked (rollup)"
    Task ||--o{ Task : "parent / subtasks"
    Task ||--o{ TaskDependency : "DAG edges"
    Task ||--o{ Message : "conversation log"
    Task ||--o{ Telemetry : "audit trail"
    Event ||--o{ EventTrigger : "fires"
    EventTrigger }o--|| Task : "materialises"

Task is the unified work item (polymorphic: epic / story / task / subtask) — a status state machine, bidirectional human/agent delegation, self-referential hierarchy, strategic goal/milestone links, cron-template spawning and event choreography, all in one model.


⚙️ How it works

🔄 Dequeue = atomic claim

GET /api/tasks/dequeue?project_id=…&assignee=agent scans not_started tasks in priority order, checks DAG readiness via check_dependencies, and claims the winner with a single conditional UPDATE:

UPDATE tasks SET status='ongoing' WHERE id=? AND status='not_started'

If the row-count is zero, another agent already won — Cascade moves to the next candidate instead of handing out duplicate work.

🧮 Status state machine

Every transition funnels through TaskService.update_status, validated against an explicit transition table:

not_started → ongoing → completed | blocked | rejected
    blocked → ongoing | rejected | completed
  completed → ongoing | not_started        (reopen)
   rejected → not_started                   (re-queue)

Each transition sets started_at/completed_at, records telemetry, posts a system message to the task's conversation log, broadcasts an SSE event — and if the task declares emit_event_id, fires that event on completion, driving cross-project choreography automatically.

📈 Goal progress — computed, never stored

GoalService.get_progress counts linked tasks completed/total live, every time. There is no progress_pct column to fall out of sync — the number you see is always true.

💓 Continuous monitoring loop

engine/loop.monitoring_loop runs every 10 seconds (not hourly), firing the poller, pinger and scheduler concurrently — each on its own database session, since AsyncSession isn't safe to share across coroutines:

  • Poller — finds ongoing tasks with no recent message and nudges them.
  • Pinger — evicts agent sessions past their heartbeat timeout and broadcasts liveness changes.
  • Scheduler — spawns child tasks from due cron-status templates, idempotently (never double-spawns while a child is still active).
🛡️ Autonomy without recklessness

AutoDecisionService.should_ask_human returns True only for destructive keywords (delete, drop, purge, production-deploy, force-push, refund, …). Everything else is auto-resolved by auto_resolve_choice, which scores options on risk / effort / reversibility and picks the safest, fastest one — recording its reasoning as a message on the task.


🤖 MCP tools

Cascade exposes a per-workspace MCP tool registry — each server instance is bound to one project_id and force-scopes every call to it, so an agent connected to one project can never read or write another's data.

Tool Purpose
get_task Dequeue + atomically claim the next ready task, or fetch by ID
create_task Decompose / delegate work (parent_id + depends_on)
reply Post progress / reply / permission messages
update_status Transition task status through the state machine
get_mission Big-picture mission + active goals
get_project_context Full project state for strategic coherence
publish_event Emit a cross-project choreography event
get_dependencies Dependency tree — what a task waits on / blocks
auto_decide Auto-resolve a choice without asking a human

See cascade/mcp/instructions.py for the full agent operating contract served as the MCP server's system instructions.


🧪 Tests

pip install -e ".[dev]"
pytest -q

39 tests run against an isolated in-memory SQLite database per test (with PRAGMA foreign_keys=ON to match production), covering the status state machine, DAG dependency resolution, concurrent dequeue race safety, concurrent status-transition race safety, cron template spawn integrity, goal/milestone progress aggregation, cascade-delete referential integrity, event-trigger choreography, MCP workspace isolation, and the full REST + HTMX page surface.


🔧 Configuration

All settings are overridable via CASCADE_-prefixed env vars or a .env file (see cascade/config.py):

Setting Default
CASCADE_DATABASE_URL sqlite+aiosqlite:///./cascade.db
CASCADE_PORT 8100
CASCADE_LOOP_TICK_SECONDS 10
CASCADE_STALL_THRESHOLD_MINUTES 30
CASCADE_SESSION_TIMEOUT_SECONDS 60
CASCADE_ENABLE_MONITORING_LOOP true
CASCADE_ENABLE_SCHEDULER true

🗂️ Project structure

cascade/
├── pyproject.toml          # packaging + pytest config
├── alembic/                # async migrations (env.py + versions/)
├── cascade/
│   ├── main.py             # FastAPI app factory + lifespan (monitoring loop)
│   ├── config.py           # Pydantic Settings (CASCADE_ env prefix)
│   ├── database.py         # async engine (WAL + busy_timeout + FK enforcement)
│   ├── models/              # SQLAlchemy 2.0 typed models
│   ├── schemas/              # Pydantic v2 request/response
│   ├── services/            # business logic (thin routers → services)
│   ├── routers/              # REST + SSE + HTMX page handlers
│   ├── mcp/                  # MCP server factory + tools + agent instructions
│   ├── engine/                # monitoring loop, poller, pinger, progress tracker
│   ├── integrations/          # Hermes bridge client + standalone monitor daemon
│   └── web/                   # Jinja2 templates + static app.js
└── tests/                     # pytest-asyncio, 39 tests

🛠️ Contributing

  1. Fork the repo and create a feature branch
  2. Add or update tests for any behaviour change — the suite runs against a FK-enforced in-memory database, so referential-integrity bugs get caught before they reach production
  3. pytest -q must pass
  4. Open a pull request describing the change and its rationale

📄 License

Released under the MIT License — see LICENSE.

Cascade reinterprets ideas from Leantime (strategic task–goal–milestone coherence) and AgentRQ (agent dequeue + status state machine + monitoring loop), reimplemented from scratch in async Python. It is an independent work and is not affiliated with or endorsed by either project.


⭐ Star this repo if Cascade helps you orchestrate your agents.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cascade_orchestrator-0.1.2.tar.gz (77.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cascade_orchestrator-0.1.2-py3-none-any.whl (92.7 kB view details)

Uploaded Python 3

File details

Details for the file cascade_orchestrator-0.1.2.tar.gz.

File metadata

  • Download URL: cascade_orchestrator-0.1.2.tar.gz
  • Upload date:
  • Size: 77.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for cascade_orchestrator-0.1.2.tar.gz
Algorithm Hash digest
SHA256 2d64c3f771c8a0f52e465d7e2e6cf3b5dc08672f40a7ce5f7d5c55e530499ff7
MD5 509db28862c4ae11a5ff8ef8e293ae00
BLAKE2b-256 eb8b83b0bc93180bdada7cc4420b343c1a25d262604a6c6dab644af9442abca7

See more details on using hashes here.

File details

Details for the file cascade_orchestrator-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: cascade_orchestrator-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 92.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for cascade_orchestrator-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 7c14e04894a59e789d572ec480154cc77485dc263746e6490adbcc22869aa509
MD5 38f1c0b9f12f66f39d4608cf9e324605
BLAKE2b-256 b2c38d3817c934ac11a36ba0c9c0ea789b6baaf003cdc86e151bc8a3dfff32b9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page