ASK KNOX
beta
LESSON 333

Level 5: The Kanban Pull Model

Stop dispatching work to agents and start letting agents claim it — the pull model scales without an orchestration bottleneck.

7 min read·The Seven Levels of Agent Orchestration

Introduction

One agent is a toy. A conducted orchestra is a business.

The shift from a single persistent agent to a team of specialized agents is level 5 — and the architecture that makes it work is the Kanban pull model. Instead of an orchestrator deciding which agent does which task and pushing assignments at them, tasks sit in a shared queue with status "available," and agents claim what they can handle.

This is not just a technical pattern. It is a different mental model for multi-agent systems. And it comes with a visual dashboard that replaces the terminal-watching you have been doing.

Core Concept

Push vs. Pull

Most multi-agent systems use a push model: there is an orchestrator at the center. The orchestrator knows about all available agents, all pending tasks, and makes a decision — "agent A gets task 1, agent B gets task 2." It then dispatches those assignments.

Push works. But it has a ceiling. The orchestrator becomes a bottleneck. Every task requires the orchestrator to make a decision. As you add agents and tasks, the orchestrator's decision-making load grows. And if the orchestrator misroutes a task — assigns research work to the code writer — you do not find out until the output is wrong.

The pull model inverts this. Tasks are placed in a queue with status "available." Agents have defined profiles: a researcher agent knows how to claim research tasks, a writer knows how to claim writing tasks, a reviewer knows how to claim review tasks. Each agent polls the queue, sees a task it can handle, claims it (atomically, to prevent double-claiming), and executes.

The orchestrator's role shrinks from "who does what" to "create the task and put it in the queue." That is a much simpler job.

Specialist Profiles

The Agent Framework Kanban implementation defines three default specialist profiles:

Researcher — claims tasks tagged type: research. Searches the web, reads documents, synthesizes information, produces a structured findings report. Does not write prose or review code.

Writer — claims tasks tagged type: write. Takes a brief or research output, produces a draft. Does not do original research or review.

Reviewer — claims tasks tagged type: review. Takes a draft, checks it against criteria, returns a structured review with pass/fail per criterion. Does not write or research.

This separation is intentional and important. A researcher that also writes prose will compromise on one or the other — the context it carries for research work crowds out the context needed for writing quality. Specialists are better than generalists in a multi-agent context because context is finite.

You can define additional profiles: type: code, type: data-analysis, type: qa. The pattern scales.

The Task Lifecycle

A task in the Kanban system moves through these states:

available → claimed → in_progress → review → done
                                  ↘ failed → available (retry, up to max_retries)
                                           ↘ dead_letter (retries exhausted)

available: no agent has claimed it yet claimed: an agent has atomically marked this task as theirs (prevents double-claiming) in_progress: the agent is actively working review: the primary agent's output is waiting for a reviewer to claim done: the task is complete; output is stored failed: the agent encountered an error; the task returns to available for retry — with its retry_count incremented dead_letter: the task has failed max_retries times (3 is a sensible default); it is parked for human review instead of being retried again

The dead-letter state matters more than it looks. Without a retry cap, a poison task — say, one with malformed input that throws every time — cycles claim → fail → available forever, burning agent loops and LLM spend on a task that can never succeed. Capping retries and parking the task where a human will see it turns an infinite money leak into a finite, visible incident.

The atomic claim step is critical. If two agents both see a task as "available" and both try to claim it, one of them must fail. In a database, this is a conditional update: UPDATE tasks SET status='claimed', agent_id=$id WHERE id=$task_id AND status='available'. The agent that loses the race gets a no-op and moves on to the next available task.

Human Observability Without Terminal Sprawl

With 5 agents running in parallel, you cannot watch 5 log files. The Kanban dashboard solves this.

The dashboard is a simple web UI (Agent Framework ships one; Mission Control has a Kanban view) that shows:

  • How many tasks are in each status column
  • Which agent has which task claimed
  • Time elapsed since claim (flag if too long — possible stuck agent)
  • Output previews when tasks complete

You manage the entire multi-agent operation from one browser tab. When something goes wrong, you see it at a glance: a task has been "in_progress" for 45 minutes with no state change. That is your signal to inspect that agent's logs.

In Knox's Mission Control, the is the sole live management interface for agent tasks. kanban-state.json is the source of truth. Agents that do not update their task status in Mission Control are treated as non-compliant.

Practical Application

Here is the minimal data model for a Kanban task queue:

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

@dataclass
class Task:
    id: str
    title: str
    type: str           # "research" | "write" | "review" | "code"
    status: str         # "available" | "claimed" | "in_progress" | "review" | "done" | "failed" | "dead_letter"
    priority: int       # 1 (high) to 5 (low)
    created_at: datetime
    retry_count: int = 0
    max_retries: int = 3
    claimed_by: Optional[str] = None
    claimed_at: Optional[datetime] = None
    output: Optional[str] = None
    error: Optional[str] = None

An agent's main loop:

async def agent_loop(agent_id: str, agent_type: str, queue):
    while True:
        # Poll for available tasks matching this agent's type
        task = await queue.claim_next(
            agent_id=agent_id,
            task_type=agent_type
        )

        if task is None:
            await asyncio.sleep(10)  # No tasks — back off and retry
            continue

        try:
            await queue.update_status(task.id, "in_progress")
            output = await execute_task(task)
            await queue.complete(task.id, output=output)
        except Exception as e:
            # queue.fail increments retry_count and returns the task to
            # "available" — or moves it to "dead_letter" once retry_count
            # reaches max_retries
            await queue.fail(task.id, error=str(e))

This is the complete agent loop: claim, update status, execute, complete or fail. No orchestrator decides which agent runs which task. The agent decides for itself based on its type profile.

Common Mistakes

Not making the claim atomic. The most common bug in pull-model implementations: two agents claim the same task. Use database transactions or conditional updates to ensure only one agent wins the claim.

Agents that span types. "Just have the researcher also write if there's nothing to research" sounds efficient. In practice, it degrades quality and makes the system unpredictable. Specialists idle when their queue is empty — that is acceptable behavior, not a bug to fix.

No timeout on claimed tasks. An agent can crash after claiming a task, leaving it stuck in claimed forever. Add a watchdog: if a task has been claimed for more than N minutes with no status update, return it to available.

No retry cap on failed tasks. Unconditional failed → available looks resilient until a poison task arrives — bad input that throws on every attempt. Agents will claim, fail, and re-queue it forever, burning loops and LLM spend. Track retry_count, and after max_retries move the task to dead_letter for a human to inspect.

Building the Kanban without the dashboard. The whole point of level 5 is observable multi-agent operation. A headless Kanban queue is just a push system with extra steps. Build or adopt the dashboard first — it is what makes this pattern operationally viable.

Summary

  • The pull model: agents claim available tasks from a shared queue rather than being dispatched by an orchestrator
  • Specialist profiles (researcher, writer, reviewer) define which task types each agent can claim — do not mix types
  • Atomic claims prevent two agents from taking the same task; always implement this with conditional updates
  • The visual Kanban dashboard replaces terminal-watching as your primary observability tool
  • Tasks follow a lifecycle: available → claimed → in_progress → review → done (or failed → retry, dead-lettering after max_retries so poison tasks cannot cycle forever)
  • One agent is a toy; a conducted specialist team is a business

What's Next

The next lesson goes to the layer that makes everything above it smarter: persistent memory. You will learn how a structured fact store with trust scores and contradiction detection differs from simple RAG, and why that distinction matters for agents that need to act on what they know.