Kanban

Persistent task board for multi-agent coordination

Kanban enables agents to coordinate through persistent tasks, creating a shared workspace where work is tracked and distributed across multiple agents.

python
from praisonaiagents import Agent

coordinator = Agent(name="Coordinator", instructions="Break work into kanban tasks")
coordinator.start("Plan the auth feature and assign workers")

The user asks for a feature; the coordinator creates tasks workers pick up from the shared board.

Diagram

Kanban Task Flow

👤 User

🧠 Coordinator Agent

📋 Kanban Store

📋 Board

⚡ Worker Agent

Quick Start

  1. Create Agent with Kanban Tools
    python
    from praisonaiagents import Agent
    from praisonaiagents.kanban import KanbanStoreProtocol
    
    # Agent with kanban protocols (implementation needed from wrapper)
    agent = Agent(name="Coordinator", instructions="Break tasks down")
    result = agent.start("Create user auth system")
  2. Add Worker Agent
    python
    from praisonaiagents import Agent
    from praisonaiagents.kanban import VALID_KANBAN_STATUSES
    
    worker = Agent(name="Worker", instructions="Claim and complete tasks")
    result = worker.start("Find ready tasks")

How It Works

Diagram
UIWorkerDispatcherStoreCoordinatorUserUIWorkerDispatcherStoreCoordinatorUser“Build login system”Create tasks (design, implement, test)Dispatch ready tasksClaim taskAdd comments/heartbeatMark completedUpdate statusShow progress

Task coordination happens through a SQLite-backed persistent store that all agents and the UI share.

ComponentPurpose
Kanban StoreSQLite database storing tasks, comments, links
Agent Tools8 functions for task CRUD operations
CLI CommandsHuman interface for task management
Background DispatcherAuto-claims ready tasks for processing

Concepts

Task Statuses

Tasks flow through 8 defined states from creation to completion:

Diagram

Task Lifecycle

🔍 triage

📝 todo

🟢 ready

⚡ running

👁️ review

🚫 blocked

✅ done

📦 archived

Boards

Boards provide workspace isolation for different projects or contexts:

Diagram

Board Architecture

🏠 Default Board

kanban.db

📁 Project A

project-a/kanban.db

📁 Project B

project-b/kanban.db

DAG Links

Tasks form directed acyclic graphs through parent-child dependencies:

Diagram

Task Dependencies

🎨 Design Task

⚙️ Backend Task

🖥️ Frontend Task

🔗 Integration

🧪 Testing

Claim/Release

Workers coordinate through atomic claim and release operations:

Diagram
Worker2StoreWorker1Worker2StoreWorker1Claim ready taskTask claimed (status: running)Try claim same taskAlready claimedHeartbeat signalRelease (complete/block)Task updated

Agent Tools

Kanban tools are available through the SDK protocols. The wrapper implementation provides these agent tools:

Task Management

ToolPurposeExample
kanban_createCreate new taskkanban_create("Implement auth", assignee="dev", idempotency_key="auth-v1")
kanban_listFilter taskskanban_list(status="ready", assignee="dev")
kanban_showGet task detailskanban_show("task_abc123")
kanban_runsRead attempt historykanban_runs("task_abc123")

Status Changes

ToolPurposeExample
kanban_completeMark task done with structured handoffkanban_complete("task_abc123", summary="Auth done; 14 tests pass", metadata={"changed_files": ["auth.py"]})
kanban_blockBlock with reasonkanban_block("task_abc123", "Need API keys")

Coordination

ToolPurposeExample
kanban_commentAdd progress notekanban_comment("task_abc123", "50% complete")
kanban_linkCreate dependencykanban_link("design_task", "implement_task")
kanban_heartbeatSignal livenesskanban_heartbeat("task_abc123", "testing")

Boards & Storage

Single Board (Default)

~/.praisonai/kanban.db

Multi-Board Layout

~/.praisonai/kanban/boards/
├── project-a/kanban.db
├── project-b/kanban.db
└── team-x/kanban.db

Environment Configuration

VariableEffectExample
PRAISONAI_KANBAN_BOARDSelect active boardexport PRAISONAI_KANBAN_BOARD=project-a
PRAISONAI_KANBAN_DBOverride DB pathexport PRAISONAI_KANBAN_DB=/custom/path.db

Attempt History & Retry

Each kanban task records every attempt, auto-blocks after repeated failures, and hands off a structured summary on completion.

Diagram

completed

failed/crashed

no

yes

🎯 Claim

▶️ Open run

🤖 Worker

Outcome?

✅ done + handoff

⏪ record_failure

≥ max_retries?

🔄 back to ready

🛑 auto-blocked

Diagram
WorkerKanban StoreCoordinatorUserWorkerKanban StoreCoordinatorUserretry reads kanban_runs(task_id)alt[Success][Failure (below limit)][Failure (≥ max_retries)]“Refactor auth module”kanban_create(title=…, max_retries=3, idempotency_key=”auth-refactor”)task_id (existing if dup key, else new)dispatchclaim → open runkanban_complete(task_id, summary, metadata)task=done, run=completed, counter resetrecord_failure(task_id, error)task back to ready, run=failed/crashedrecord_failure(task_id, error)task auto-blocked, run=gave_up
  1. Idempotent create with retry limit
    python
    from praisonaiagents import Agent
    from praisonai.tools.kanban_tools import kanban_create
    
    agent = Agent(name="Coordinator", instructions="Break work into tasks")
    
    task = kanban_create(
        "Replace bcrypt with argon2",
        body="Migrate auth module to argon2",
        assignee="coder",
        max_retries=3,                       # per-task circuit-breaker
        idempotency_key="auth-refactor-q3",  # safe to repeat
    )
  2. Structured completion handoff
    python
    from praisonai.tools.kanban_tools import kanban_complete
    
    kanban_complete(
        task["id"],
        summary="argon2 migration; 14 tests pass; CHANGELOG updated",
        metadata={
            "changed_files": ["auth.py", "tests/test_auth.py"],
            "tests_run": 14,
            "residual_risk": "rotate old hashes on next login",
        },
    )
  3. Read attempt history on retry
    python
    from praisonai.tools.kanban_tools import kanban_runs
    
    history = kanban_runs(task["id"])
    for run in history["runs"]:
        print(run["outcome"], run["error"] or run["summary"])
    # crashed: ModuleNotFoundError: argon2
    # completed: argon2 migration; 14 tests pass; CHANGELOG updated

What the dispatcher does automatically

When a worker claims a task, the dispatcher opens a run with the claim — anything the worker writes via kanban_complete closes it as completed, anything that crashes closes it as crashed/failed. Below the max_retries limit, failed attempts release the claim and put the task back on ready for re-dispatch. At the limit, the task auto-blocks with gave_up so a human can step in. A successful completion resets the failure counter — the limit is consecutive, not cumulative.

Non-blocking git. With per-task worktree isolation (workspace_kind="worktree" or repo_path), every git command the dispatcher runs — fork, merge, cleanup — happens off the gateway event loop, so chat and WebSocket traffic stay responsive during a slow merge. Integrations into the shared repo are serialised, so two tasks never run git merge at the same time. If the dispatcher stops mid-dispatch, a half-prepared worktree and its branch are removed so a retry can recreate them.

New params on kanban_create

OptionTypeDefaultDescription
max_retriesint3 (board default)Per-task circuit-breaker limit. After this many consecutive failed attempts the task auto-blocks. Invalid values (<1 / non-numeric) fall back to the board default.
idempotency_keystrNonePer-board, per-tenant dedup key. Repeating a create with the same key returns the existing task instead of a duplicate — safe for retrying automation and webhooks.

New params on kanban_complete

OptionTypeDefaultDescription
summarystr""Structured summary of what was done. Surfaced to linked children and retrying workers.
metadatadict{}Structured handoff fields. Common keys: changed_files, tests_run, residual_risk, next_steps.
commentstr""Free-text completion comment (kept for back-compat).

New tool: kanban_runs

ToolPurposeExample
kanban_runsRead attempt history (oldest first)kanban_runs("task_abc123") → {"runs": [{"outcome": "crashed", "error": "...", "started_at": "...", "ended_at": "..."}, ...], "count": 2}

Each row matches the KanbanRunProtocol shape:

FieldTypeDescription
idintAuto-incremented run ID
task_idstrParent task ID
profilestrWorker/profile identifier
outcome`str \None`completed, blocked, crashed, failed, or gave_up; None while the run is open
summarystrStructured handoff summary
metadatadictStructured handoff fields
errorstrError text for failed/crashed attempts
started_at`str \None`ISO-8601 timestamp
ended_at`str \None`ISO-8601 timestamp; None while the run is open

New Task fields

FieldTypeMeaning
max_retries`int \None`Per-task circuit-breaker limit. None means use the board default (3).
consecutive_failuresintCount of consecutive failed attempts since the last completed outcome. Reset to 0 by a successful completion.
current_run_id`int \None`Active attempt id; None when no run is open.

Common Patterns

Automation-safe creation — pass idempotency_key derived from the trigger (f"github-issue-{n}", f"webhook-{uuid}"). Re-firing the webhook is a no-op instead of spawning duplicates. Idempotency is per-board, per-tenant — two tenants on the same board never collide on the same key.

Long-running flaky network tasks — set max_retries=5 per task to absorb transient flakes without giving up too early. Watch consecutive_failures in kanban_show to spot stuck tasks.

Retrying workers use prior failures — call kanban_runs(task_id) at the start of a retry attempt to read prior outcomes and errors. Use summary and metadata from the most recent failed run as context: “previous attempt crashed at line X; avoid path Y.”

Use idempotency_key for every automated kanban_create

Webhooks, schedulers, and GitHub-issue triagers should always pass an idempotency_key. The cost is one extra string; the value is zero duplicate tasks under retry.

Tune max_retries per task type

Fast deterministic work: max_retries=1. Flaky network or integration tests: max_retries=3–5. Leave the rest at the board default (3).

Send a real summary and metadata

Use summary and metadata in kanban_complete instead of free-text comments. Downstream child tasks and retrying workers consume the structured fields; humans still read them fine.

Read kanban_runs before retrying

The structured run history is the cheapest “what went wrong last time” context a retrying worker can fetch. Call kanban_runs(task_id) at the start of every retry attempt.


Common Patterns

Coordinator-Worker Pattern

python
# Coordinator breaks down requests
from praisonaiagents import Agent
from praisonaiagents.kanban import VALID_KANBAN_STATUSES

coordinator = Agent(
    name="Coordinator", 
    instructions="Break user requests into kanban tasks"
)

# Worker with heartbeat reporting
worker = Agent(
    name="Worker",
    instructions="Claim ready tasks, report progress via heartbeat"
)

Background Processing

python
# Background processing requires wrapper implementation
# The praisonaiagents.kanban protocols support:
# - Task claiming and status updates
# - Heartbeat reporting for long-running tasks
# - Multi-board coordination

Worker with Heartbeat

python
from praisonaiagents import Agent
from praisonaiagents.kanban import VALID_KANBAN_STATUSES

worker = Agent(
    name="Worker",
    instructions="""Claim ready tasks and report liveness via heartbeat. 
    Use kanban_heartbeat every 30 seconds during long-running work."""
)

# Worker claims task and reports progress
result = worker.start("Find ready tasks, claim one, and report progress")

Human-Agent Collaboration

bash
# Human-agent collaboration pattern
# Requires wrapper implementation of:
# - CLI commands for task management
# - UI board visualization
# - Agent-to-store protocol bindings

Runs, Retries, and Structured Handoff

Kanban now tracks every execution attempt for each task via the task_runs table, supports per-task retry limits, and allows agents to pass structured data when completing a task.

kanban_complete — structured handoff

python
kanban_complete(
    task_id="task-123",
    summary="Implemented OAuth2 flow with PKCE",
    metadata={"pr_url": "https://github.com/org/repo/pull/42", "test_coverage": 0.91},
)
ParameterTypeDefaultDescription
task_idstr_(required)_Task to complete
summarystr""Human-readable completion summary stored in the run record
metadatadict{}Arbitrary structured data for downstream consumers (e.g. PR URL, metrics)

kanban_create — idempotency and retries

python
kanban_create(
    title="Write unit tests for auth module",
    body="Cover all edge cases in JWT validation",
    idempotency_key="auth-tests-v1",
    max_retries=3,
)
ParameterTypeDefaultDescription
titlestr_(required)_Task title
bodystr""Task description
idempotency_key`str \None`NoneIf set, calling kanban_create again with the same key returns the existing task instead of creating a duplicate
max_retriesint0Max number of times the dispatcher retries a failed run. After N failures the task is auto-moved to blocked

kanban_runs — per-task run history

List all execution attempts for a task:

python
runs = kanban_runs(task_id="task-123")
for run in runs:
    print(run["outcome"], run["summary"], run["created_at"])

Each run record contains:

FieldTypeDescription
idstrUnique run ID
task_idstrParent task
outcomestr"success", "failure", or "in_progress"
summarystrSummary provided at completion
metadatadictStructured handoff data
created_atfloatUnix timestamp of run start
finished_at`float \None`Unix timestamp of run end

Auto-block after max retries

When max_retries > 0 and the task accumulates that many "failure" run outcomes, the dispatcher automatically moves the task from running to blocked. The task must be manually unblocked (kanban_move(task_id, "ready")) after the root cause is resolved.

Diagram

run fails

attempts < max_retries

attempts = max_retries

⚡ running

❌ failure run

🔄 retry → ready

🚫 blocked


Per-Task Worktree Isolation

Give every kanban worker its own git worktree so parallel tasks never overwrite each other’s files.

python
from praisonaiagents import Agent
from praisonai.tools.kanban_tools import kanban_create

# Opt this task into per-task worktree isolation
task = kanban_create(
    "Refactor auth module",
    workspace_kind="worktree",   # <-- the whole feature
)

One keyword moves a task onto its own branch and working directory. Everything else — creation, merge-back, and cleanup — happens automatically.

Diagram

Per-Task Worktree

clean merge

conflict

📋 Task
workspace_kind=worktree

🌱 git worktree
kanban/<id>

⚡ Worker cwd=worktree

Result?

✅ done + teardown

🛑 blocked
worktree kept

Auto-upgrade for repo-linked tasks

Link a task to a git repo and the dispatcher gives it a worktree automatically — no keyword needed.

python
from praisonai_bot.kanban.sqlite_store import SQLiteKanbanStore

store = SQLiteKanbanStore()

# repo_path alone opts the task into isolation — workspace_kind is set for you
task = store.create_task({
    "title": "Refactor auth module",
    "repo_path": "/repos/service-a",
})

assert task.workspace_kind == "worktree"
assert task.branch == f"kanban/{task.id}"
assert task.metadata["repo_path"] == "/repos/service-a"

The upgrade is triggered by linkage, not by task content — the task body is never inspected.

Diagram

yes

no

yes

no

yes

no

📋 create_task called

workspace_kind set
by caller?

Keep caller’s kind
e.g. default or worktree

auto_worktree=false?

workspace_kind=default

repo_path points
inside a git repo?

workspace_kind=worktree
branch=kanban/id
metadata.repo_path persisted

Task inputEffective workspace_kindEffective branchNotes
repo_path="/repos/svc" (git repo)worktreekanban/<task_id>Auto-upgrade fires. metadata["repo_path"] is persisted so dispatch sees the same linkage.
metadata={"repo_path": "/repos/svc"}worktreekanban/<task_id>Same — repo_path can live inside metadata too.
repo_path="/repos/svc" + workspace_kind="default"default(unset)Explicit kind always wins. Deliberate opt-out.
repo_path="/repos/svc" + auto_worktree=Falsedefault(unset)Board- or task-level escape hatch. metadata["auto_worktree"]=False works too.
workspace_kind="worktree" (no repo_path)worktreekanban/<task_id>Branch is derived at create time, not dispatch.
repo_path="/not/a/repo"default(unset)Bad path → not a repo → no upgrade. Never fails the create.
repo_path="/repos/svc" + metadata={"repo_path": "/explicit/other"}worktreekanban/<task_id>Explicit metadata.repo_path is respected — top-level repo_path does not overwrite.

Disable the upgrade per task (or per board via metadata) with auto_worktree=False:

python
from praisonai_bot.kanban.sqlite_store import SQLiteKanbanStore

store = SQLiteKanbanStore()

# repo-linked, but isolation explicitly disabled
task = store.create_task({
    "title": "Read-only audit",
    "repo_path": "/repos/service-a",
    "auto_worktree": False,
})

assert task.workspace_kind == "default"

How It Works

The dispatcher forks a worktree from the base branch, runs the worker inside it, then merges the branch back on success.

Diagram
HumanKanban StoreWorkerGitDispatcherHumanKanban StoreWorkerGitDispatcherread comment via kanban_show, resolve, re-readyalt[Clean merge][Merge conflict]git worktree add kanban/<id> from basepersist branch + worktree_pathspawn (cwd = worktree)exit 0commit worker edits, merge –no-ffmergedremove worktreetask → doneconflict (merge aborted)task → blocked + conflicted files commentKANBAN_TASK_BLOCKED

On success the dispatcher auto-commits any uncommitted worker edits as kanban worker output for kanban/<task_id>, merges the branch into the base with git merge --no-ff, then tears the worktree down. On conflict the merge is aborted, so the base branch is never left half-merged.

Choosing default vs worktree

Pick isolation only when workers actually edit files in a git repo.

Diagram

Single worker, one repo edit

Many workers editing overlapping files

API calls / reports, no file edits

Non-git working directory

How do workers run?

workspace_kind=default

workspace_kind=worktree

workspace_kind=default

workspace_kind=default

New Task fields

FieldTypeDefaultDescription
workspace_kindstr"default""worktree" opts the task into per-task git worktree isolation. Any other value keeps the shared-cwd behaviour.
repo_path`str \None`NoneFilesystem path of the git repo this task is linked to. When set and the caller left workspace_kind unset, the store upgrades workspace_kind to "worktree" and persists the resolved path into metadata["repo_path"]. Non-git or unreadable paths are treated as unlinked (never fails the create). Also accepted inside metadata.
auto_worktreeboolTrueSet to False to disable the repo-linked auto-upgrade for this task (or a whole board via metadata["auto_worktree"]). Explicit workspace_kind always wins over both this flag and repo_path.
branch`str \None`NonePopulated at create_task time on worktree tasks (auto-upgraded or explicit workspace_kind="worktree") — kanban/<task_id>. An explicit branch in task_data is respected. Read it via kanban_show.
worktree_path`str \None`NonePopulated by the dispatcher — filesystem path of the worktree (~/.praisonai/kanban/worktrees/<task_id>).

Environment configuration

Add these to the Environment Configuration table when the dispatcher runs outside the repo.

VariableEffectExample
PRAISONAI_KANBAN_REPO_DIRBase repo path for worktree / merge operationsexport PRAISONAI_KANBAN_REPO_DIR=/repos/service-a
PRAISONAI_KANBAN_BASE_BRANCHFallback branch to fork from / integrate into when HEAD cannot be detectedexport PRAISONAI_KANBAN_BASE_BRANCH=develop
PRAISONAI_KANBAN_GIT_TIMEOUTTotal seconds allowed for any single git command the dispatcher runs (worktree add, merge, status, rev-parse). Timing out returns returncode=124 and a diagnosis in stderr — the dispatcher treats that as failure exactly like any other non-zero exit. See Subprocess Guard.export PRAISONAI_KANBAN_GIT_TIMEOUT=600

The dispatcher branches from the current HEAD of PRAISONAI_KANBAN_REPO_DIR (defaulting to the process working directory). When HEAD cannot be detected, it falls back to PRAISONAI_KANBAN_BASE_BRANCH (default main).

Merge conflict handling

A conflict never touches the base branch — it routes the task to blocked for a human to resolve.

  • The merge is aborted; the base branch is left untouched.
  • The task moves to blocked with a comment listing the conflicted files — read it via kanban_show(task_id).
  • The KANBAN_TASK_BLOCKED hook fires; the conflicted files live on the task comment thread, not the hook payload.
  • The worktree is preserved on disk for inspection.

To recover, resolve the conflicts inside the kept worktree, then re-run or discard it:

python
from praisonai.tools.kanban_tools import kanban_move

# After resolving conflicts inside ~/.praisonai/kanban/worktrees/<task_id>
kanban_move("task_abc123", "ready")   # re-dispatch the task

Or discard the worktree entirely from the base repo:

bash
git worktree remove --force ~/.praisonai/kanban/worktrees/task_abc123

Lossless-only worktree teardown

The dispatcher never destroys work a worker produced but failed to integrate — a worktree with uncommitted changes or unmerged commits is preserved on disk with a worktree_preserved comment on the task.

Diagram
sequenceDiagram
    participant Dispatcher
    participant Guard as Lossless guard
    participant Git
    participant Store as Kanban Store

    Dispatcher->>Git: git merge --no-ff kanban/<id>
    Dispatcher->>Guard: _remove_worktree(path, branch)
    Guard->>Git: git status --porcelain
    Guard->>Git: git rev-list --count base..branch
    alt Clean tree and 0 commits ahead
        Guard->>Git: git worktree remove --force
        alt remove succeeded
            Git-->>Guard: ok
            Guard-->>Dispatcher: None (removed)
            Dispatcher->>Store: drop tracking entry
        else remove failed (nonzero / exception)
            Git-->>Guard: error
            Guard-->>Dispatcher: "worktree removal failed: …"
            Dispatcher->>Store: add_comment worktree_preserved at <path>: …
            Note over Dispatcher: tracking entry kept — no orphan
        end
    else Dirty or ahead of base
        Guard-->>Dispatcher: reason string
        Dispatcher->>Store: add_comment worktree_preserved at <path>: <reason>
        Note over Dispatcher: worktree left on disk; tracking entry kept
    end

Every preserved worktree records its reason as a task comment, readable via kanban_show(task_id):

Reason (recorded on the task)TriggerRecovery
worktree has uncommitted changesWorktree still has files in git status --porcelain after integrationcd into the worktree, git add -A && git commit, then rerun cleanup — or discard with git -C <path> reset --hard HEAD then remove
`branch has

commit(s) not in <base> | git rev-list –count <base>..<branch> > 0 (worker committed something that never merged into base) | Merge or cherry-pick the branch into base by hand, then rerun cleanup, or force-remove if the commits are known-throwaway | | could not read worktree status: <err> / could not compute ahead-count: <err> | The git status / git rev-list command itself failed | Investigate the worktree state manually — the guard errs toward preservation, so no data is at risk | | worktree removal failed: <detail> | git worktree remove –force returned nonzero | Read the git error, resolve, then rerun cleanup or force-remove | | worktree removal error: <exc> | git worktree remove` raised (permissions, missing binary, etc.) | Fix the underlying environment issue, then retry |

To recover a preserved worktree, commit anything worth keeping first, then integrate or remove it from the base repo:

bash
# Read the preserved worktree, losslessly commit anything worth keeping
git -C ~/.praisonai/kanban/worktrees/task_abc123 status
git -C ~/.praisonai/kanban/worktrees/task_abc123 add -A
git -C ~/.praisonai/kanban/worktrees/task_abc123 commit -m "recover worker output"

# Then either integrate the branch by hand...
git merge kanban/task_abc123

# ...or, if the edits are known throwaway, remove the worktree manually
git worktree remove --force ~/.praisonai/kanban/worktrees/task_abc123

Inside a dispatcher-owned code path (e.g. a custom KanbanDispatcher subclass), the equivalent destructive teardown is an explicit force=True:

python
# Explicitly opt into destructive teardown — bypasses the lossless guard
dispatcher._remove_worktree(path, branch, force=True)

Best Practices

Enable worktree isolation only when workers edit files

Tasks that only call APIs or print reports gain nothing from isolation and pay the git overhead. Keep workspace_kind="default" for read-only work; reserve "worktree" for concurrent file edits.

Set PRAISONAI_KANBAN_REPO_DIR outside the repo

The dispatcher falls back to os.getcwd() at start, which is wrong under systemd/supervisord. Export PRAISONAI_KANBAN_REPO_DIR=/path/to/repo so worktrees and merges run against the right checkout.

Use short, git-ref-safe task IDs

The dispatcher rejects .., /, leading ., spaces, and other invalid chars. Unsafe IDs fail closed (no isolation, no worker) — keep IDs to [A-Za-z0-9._-].

Sweep stuck worktrees under ~/.praisonai/kanban/worktrees/

Both blocked-on-conflict tasks and clean-merge tasks with a worktree_preserved comment leave their worktree on disk by design. Read the comment for the reason (kanban_show <task_id>), integrate any recoverable work by hand, then git worktree remove --force ~/.praisonai/kanban/worktrees/<task_id> once resolved.

Expect serialised integration across concurrent worktree tasks

Workers run in parallel in their own worktrees; only the integration step (merge-back, cleanup) is serialised by an in-process lock. This is a safety guarantee, not throttling. If you run several dispatcher processes against the same repo, add your own file-level lock.

Prefer repo_path over workspace_kind for repo-scoped work

Linking a task to its repo (repo_path="/repos/foo") opts it into isolation and records the isolating repo in metadata so the dispatcher never loses the linkage. workspace_kind="worktree" still works for callers that already track the repo themselves.


Best Practices

Task Granularity

Create tasks that can be completed in 15-30 minutes. Break larger work into linked subtasks using kanban_link for proper dependency tracking.

Status Management

Move tasks through statuses systematically: todo → ready → running → done. Use blocked for dependencies and review for human approval.

Agent Coordination

Use kanban_heartbeat during long-running tasks to signal liveness. Add detailed comments with kanban_comment to track progress and decisions.

Board Organization

Use separate boards for different projects or contexts. Default board works well for single-project setups.


Related