11 KiB
Tasks Module
Beads-inspired task management optimized for multi-agent coordination. Unlike beads (Git-distributed JSONL), this uses centralized SQLite for simplicity since all agents share the same workspace.
Design Rationale
Why Not Just Use Beads?
Beads solves a different problem: distributed task tracking across forked repos with zero coordination. We don't need that:
- All Workers operate in the same workspace under one
cwserver - SQLite is the single source of truth
- tRPC exposes task queries directly to agents and dashboard
- No merge conflicts, no Git overhead
Core Agent Problem Solved
Agents need to answer: "What should I work on next?"
The ready query solves this: tasks that are open with all dependencies closed. Combined with priority ordering, agents can self-coordinate without human intervention.
Data Model
Task Entity
| Field | Type | Description |
|---|---|---|
id |
TEXT | Primary key. Hash-based (e.g., tsk-a1b2c3) or UUID |
parent_id |
TEXT | Optional. References parent task for hierarchies |
initiative_id |
TEXT | Optional. Links to Initiatives module |
phase_id |
TEXT | Optional. Links to initiative phase (for grouped approval) |
project_id |
TEXT | Optional. Scopes task to a project |
title |
TEXT | Required. Short task name |
description |
TEXT | Optional. Markdown-formatted details |
type |
TEXT | task (default), epic, subtask |
status |
TEXT | open, in_progress, blocked, closed |
priority |
INTEGER | 0=critical, 1=high, 2=normal (default), 3=low |
assigned_to |
TEXT | Agent/worker ID currently working on this |
assigned_at |
INTEGER | Unix timestamp when assigned |
metadata |
TEXT | JSON blob for extensibility |
created_at |
INTEGER | Unix timestamp |
updated_at |
INTEGER | Unix timestamp |
closed_at |
INTEGER | Unix timestamp when closed |
closed_reason |
TEXT | Why/how the task was completed |
Task Dependencies
| Field | Type | Description |
|---|---|---|
task_id |
TEXT | The task that is blocked |
depends_on |
TEXT | The task that must complete first |
type |
TEXT | blocks (default), related |
Task History
| Field | Type | Description |
|---|---|---|
id |
INTEGER | Auto-increment primary key |
task_id |
TEXT | The task that changed |
field |
TEXT | Which field changed |
old_value |
TEXT | Previous value |
new_value |
TEXT | New value |
changed_by |
TEXT | Agent/user ID |
changed_at |
INTEGER | Unix timestamp |
SQLite Schema
CREATE TABLE tasks (
id TEXT PRIMARY KEY,
parent_id TEXT REFERENCES tasks(id),
initiative_id TEXT,
phase_id TEXT,
project_id TEXT,
title TEXT NOT NULL,
description TEXT,
type TEXT NOT NULL DEFAULT 'task' CHECK (type IN ('task', 'epic', 'subtask')),
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'in_progress', 'blocked', 'closed')),
priority INTEGER NOT NULL DEFAULT 2 CHECK (priority BETWEEN 0 AND 3),
assigned_to TEXT,
assigned_at INTEGER,
metadata TEXT,
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
closed_at INTEGER,
closed_reason TEXT
);
CREATE TABLE task_dependencies (
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
depends_on TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
type TEXT NOT NULL DEFAULT 'blocks' CHECK (type IN ('blocks', 'related')),
PRIMARY KEY (task_id, depends_on),
CHECK (task_id != depends_on)
);
CREATE TABLE task_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
field TEXT NOT NULL,
old_value TEXT,
new_value TEXT,
changed_by TEXT,
changed_at INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_priority ON tasks(priority);
CREATE INDEX idx_tasks_assigned ON tasks(assigned_to);
CREATE INDEX idx_tasks_project ON tasks(project_id);
CREATE INDEX idx_tasks_initiative ON tasks(initiative_id);
CREATE INDEX idx_tasks_phase ON tasks(phase_id);
CREATE INDEX idx_task_history_task ON task_history(task_id);
-- The critical view for agent work discovery
-- Tasks are ready when: open, no blocking deps, and phase approved (if linked)
CREATE VIEW ready_tasks AS
SELECT t.* FROM tasks t
LEFT JOIN initiative_phases p ON t.phase_id = p.id
WHERE t.status = 'open'
AND (t.phase_id IS NULL OR p.status IN ('approved', 'in_progress'))
AND NOT EXISTS (
SELECT 1 FROM task_dependencies d
JOIN tasks dep ON d.depends_on = dep.id
WHERE d.task_id = t.id
AND d.type = 'blocks'
AND dep.status != 'closed'
)
ORDER BY t.priority ASC, t.created_at ASC;
Status Workflow
┌──────────────────────────────────────┐
│ │
▼ │
[open] ──claim──▶ [in_progress] ──done──▶ [closed]
│ │
│ │ blocked
│ ▼
└───────────── [blocked] ◀─────unblock───┘
| Transition | Trigger | Notes |
|---|---|---|
open → in_progress |
Agent claims task | Sets assigned_to, assigned_at |
in_progress → closed |
Work completed | Sets closed_at, closed_reason |
in_progress → blocked |
External dependency | Manual or auto-detected |
blocked → open |
Blocker resolved | Clears assignment |
open → closed |
Cancelled/won't do | Direct close without work |
CLI Reference
All commands under cw task subcommand.
Core Commands
| Command | Description |
|---|---|
cw task ready |
List tasks ready for work (open + no blockers) |
cw task list [--status STATUS] [--project ID] |
List tasks with filters |
cw task show <id> |
Show task details + history |
cw task create <title> [-p PRIORITY] [-d DESC] |
Create new task |
cw task update <id> [--status STATUS] [--priority P] |
Update task fields |
cw task close <id> [--reason REASON] |
Mark task complete |
Dependency Commands
| Command | Description |
|---|---|
cw task dep add <task> <depends-on> |
Task blocked by another |
cw task dep rm <task> <depends-on> |
Remove dependency |
cw task dep tree <id> |
Show dependency graph |
Assignment Commands
| Command | Description |
|---|---|
cw task assign <id> <agent> |
Assign task to agent |
cw task unassign <id> |
Release task |
cw task mine |
List tasks assigned to current agent |
Output Flags (global)
| Flag | Description |
|---|---|
--json |
Output as JSON (for agent consumption) |
--quiet |
Minimal output (just IDs) |
Agent Workflow
Standard loop for Workers:
1. cw task ready --json
2. Pick highest priority task from result
3. cw task update <id> --status in_progress
4. Do the work
5. cw task close <id> --reason "Implemented X"
6. Loop to step 1
If cw task ready returns empty, the agent's work is done.
Integration Points
With Initiatives
- Tasks can link to an initiative via
initiative_id - When initiative is approved, tasks are generated from its technical concept
- Closing all tasks for an initiative signals initiative completion
With Orchestrator
- Orchestrator queries
ready_tasksview to dispatch work - Assignment tracked to prevent double-dispatch
- Orchestrator can bulk-create tasks from job definitions
With Workers
- Workers claim tasks via
cw task update --status in_progress - Worker ID stored in
assigned_to - On worker crash, Supervisor can detect stale assignments and reset
tRPC Procedures
// Suggested tRPC router shape
task.list(filters) // → Task[]
task.ready(projectId?) // → Task[]
task.get(id) // → Task | null
task.create(input) // → Task
task.update(id, input) // → Task
task.close(id, reason) // → Task
task.assign(id, agent) // → Task
task.history(id) // → TaskHistory[]
task.depAdd(id, dep) // → void
task.depRemove(id, dep) // → void
task.depTree(id) // → DependencyTree
Task Granularity Standards
A task must be specific enough for execution without interpretation. Vague tasks cause agents to guess, leading to inconsistent results.
Quick Reference
| Too Vague | Just Right |
|---|---|
| "Add authentication" | "Add JWT auth with refresh rotation using jose, httpOnly cookie, 15min access / 7day refresh" |
| "Create the API" | "Create POST /api/projects accepting {name, description}, validates name 3-50 chars, returns 201" |
| "Handle errors" | "Wrap API calls in try/catch, return {error: string} on 4xx/5xx, show toast via sonner" |
Required Task Components
Every task MUST include:
- files — Exact paths modified/created
- action — What to do, what to avoid, WHY
- verify — Command or check to prove completion
- done — Measurable acceptance criteria
See task-granularity.md for comprehensive examples and anti-patterns.
Context Budget
Tasks are sized to fit agent context budgets:
| Complexity | Context % | Example |
|---|---|---|
| Simple | 10-20% | Add form field |
| Medium | 20-35% | Create API endpoint |
| Complex | 35-50% | Implement auth flow |
| Too Large | >50% | SPLIT REQUIRED |
See context-engineering.md for context management rules.
Deviation Handling
When Workers encounter unexpected issues during execution, they follow deviation rules:
| Rule | Action | Permission |
|---|---|---|
| Rule 1: Bug fixes | Auto-fix | None needed |
| Rule 2: Missing critical (validation, auth) | Auto-add | None needed |
| Rule 3: Blocking issues (deps, imports) | Auto-fix | None needed |
| Rule 4: Architectural changes | ASK | Required |
See deviation-rules.md for detailed guidance.
Execution Artifacts
Task execution produces artifacts:
| Artifact | Purpose |
|---|---|
| Commits | Per-task atomic commits |
| SUMMARY.md | Record of what happened |
| STATE.md updates | Position tracking |
See execution-artifacts.md for artifact specifications.
Future Considerations
- Compaction: Summarize old closed tasks to reduce DB size (beads does this with LLM)
- Labels/tags: Additional categorization beyond type
- Time tracking: Estimated vs actual time for capacity planning
- Recurring tasks: Templates that spawn new tasks on schedule