97 KiB
Workflow System Design
Overview
A Workflow defines the lifecycle of task sessions on a board through a sequence of Steps. Each step maps to a column and defines:
- Behavior: What happens when a session enters the step (auto-start agent, append prompts, etc.)
- Transitions: Rules for moving to next/previous steps
Session-Level Workflows
Important: Workflows operate at the session level, not the task level. A task can have multiple sessions (different attempts, different agents, parallel work), and each session progresses through the workflow independently.
Task
├── Session 1 (Claude) ──▶ Planning ──▶ Review ──▶ Implementation ──▶ Done ✓
├── Session 2 (Codex) ──▶ Planning ──▶ Review ──▶ (rejected, abandoned)
└── Session 3 (Claude) ──▶ Planning ──▶ (in progress...) ← PRIMARY
Rationale:
- A task might need multiple attempts before completion
- Different agents can work on the same task in parallel
- Review feedback and approval are per-session (each attempt is reviewed independently)
- Session history provides audit trail of all attempts
Primary Session
Each task has a primary session - the session that represents the task's current workflow position and state.
- Default: The latest created session automatically becomes primary
- User can change: Pin a different session as primary if needed
- Determines task display: Task card appears in the column of the primary session's step
- Default for operations: Approve/reject buttons operate on the primary session
Task: "Implement auth"
├── Session 1 (done) → archived, was primary before
├── Session 2 (abandoned) → user abandoned after rejection
└── Session 3 (active) → PRIMARY - task appears in this session's column
When a new session is created, it automatically becomes the primary session (latest attempt is most relevant). Other sessions become "alternative attempts" - viewable but not the main path.
Design Decisions
Columns ARE Workflow Steps
Simplify the model: columns and workflow steps are the same entity. The columns table is replaced by workflow_steps.
Board → Workflow (required)
→ Workflow Steps (these ARE the columns)
Rationale:
- Simplicity: One entity instead of two with a relationship
- No indirection: Step behavior is directly on the column
- Cleaner data model: Every board has a workflow, every column is a step
Rejection Behavior: Stay in Review
When a user rejects at a review step, the session stays in the same step but gets marked with "changes requested" status. This enables iterative review cycles without moving the session back and forth.
How it works:
- User clicks "Request Changes" and provides feedback
- Session stays in the review step
- Session metadata is updated with rejection info
- Agent can be re-triggered to address the feedback
- User reviews again, can approve or request more changes
- On approval, session moves to the configured
onApprovalstep
Session metadata on rejection:
{
"review_status": "changes_requested",
"review_feedback": [
{
"id": "fb-1",
"message": "Need to handle edge case X",
"created_at": "2024-01-15T10:30:00Z",
"resolved": false
},
{
"id": "fb-2",
"message": "Add unit tests for the new function",
"created_at": "2024-01-15T11:00:00Z",
"resolved": true
}
],
"review_iteration": 2
}
Custom Workflows Supported
Users can:
- Use a template as-is - Quick start with pre-configured workflow
- Customize a template - Clone a template and modify steps
- Create from scratch - Blank workflow, add steps manually
The workflow_templates table has is_system flag:
is_system = 1: Built-in templates (read-only, can be cloned)is_system = 0: User-created templates (fully editable)
Key Concepts
1. Workflow Template
A pre-defined workflow type that boards can adopt. Examples:
| Template | Description | Steps |
|---|---|---|
dev |
Software development | Backlog → Planning → Implementation → Review → Done |
architecture |
System design | Backlog → Research → Design → Review → Approved |
bug-fix |
Bug resolution | Triage → Reproduce → Fix → Verify → Done |
simple |
Basic kanban | Todo → In Progress → Done |
2. Workflow Step
Each step has:
interface WorkflowStep {
id: string;
name: string; // Display name (e.g., "Planning")
position: number; // Order in workflow
stepType: WorkflowStepType; // Semantic type (see below)
// Behavior configuration
behavior: {
autoStartAgent: boolean; // Auto-start agent when task enters
promptPrefix?: string; // Prepended to user/task prompt
promptSuffix?: string; // Appended to user/task prompt
systemPromptOverride?: string; // Override agent's system prompt
planMode?: boolean; // Use plan mode (no execution)
requireApproval?: boolean; // Require user approval before proceeding
};
// Transition rules
transitions: {
onComplete?: string; // Step ID to move to on agent completion
onApproval?: string; // Step ID to move to on user approval
allowManualMove?: boolean; // Can user drag to any column?
};
// Visual
color: string;
taskState: TaskState; // Maps to existing TaskState enum
}
3. Step Types
Semantic step types that define default behaviors:
| Type | Default Behavior |
|---|---|
backlog |
No auto-action. Tasks wait here. |
planning |
Auto-start agent in plan mode. Produces plan.md. |
implementation |
Auto-start agent with implementation prompt. Uses plan if available. |
review |
Agent paused. Awaits user review of changes. |
verification |
Auto-start agent to verify/test changes. |
done |
Terminal state. No actions. |
blocked |
Task is blocked. No auto-actions. |
Workflow Templates
Dev Workflow (Default)
id: dev
name: Development
description: Standard software development workflow with planning phase
steps:
- id: backlog
name: Backlog
stepType: backlog
position: 0
taskState: TODO
color: bg-neutral-400
behavior:
autoStartAgent: false
transitions:
allowManualMove: true
- id: planning
name: Planning
stepType: planning
position: 1
taskState: IN_PROGRESS
color: bg-purple-500
behavior:
autoStartAgent: true
planMode: true
promptPrefix: |
[PLANNING PHASE]
Analyze this task and create a detailed implementation plan.
Do NOT make any code changes yet - only analyze and plan.
Create a plan that includes:
1. Understanding of the requirements
2. Files that need to be modified or created
3. Step-by-step implementation approach
4. Potential risks or considerations
Save your plan to `.kandev/plans/{task_id}.md`
transitions:
onComplete: review-plan
allowManualMove: true
- id: review-plan
name: Review Plan
stepType: review
position: 2
taskState: REVIEW
color: bg-yellow-500
behavior:
autoStartAgent: false
requireApproval: true
transitions:
onApproval: implementation
allowManualMove: true
- id: implementation
name: Implementation
stepType: implementation
position: 3
taskState: IN_PROGRESS
color: bg-blue-500
behavior:
autoStartAgent: true
promptPrefix: |
[IMPLEMENTATION PHASE]
Implement the task according to the plan in `.kandev/plans/{task_id}.md`.
Follow the plan step by step.
transitions:
onComplete: review-code
allowManualMove: true
- id: review-code
name: Code Review
stepType: review
position: 4
taskState: REVIEW
color: bg-orange-500
behavior:
autoStartAgent: false
requireApproval: true
transitions:
onApproval: done
allowManualMove: true
- id: done
name: Done
stepType: done
position: 5
taskState: COMPLETED
color: bg-green-500
behavior:
autoStartAgent: false
transitions:
allowManualMove: true
Architecture Workflow
id: architecture
name: Architecture
description: System design and architecture workflow
steps:
- id: backlog
name: Ideas
stepType: backlog
position: 0
taskState: TODO
behavior:
autoStartAgent: false
- id: research
name: Research
stepType: planning
position: 1
taskState: IN_PROGRESS
behavior:
autoStartAgent: true
planMode: true
promptPrefix: |
[RESEARCH PHASE]
Research this architecture topic. Analyze:
1. Current state of the codebase
2. Industry best practices
3. Trade-offs of different approaches
Document your findings in `.kandev/research/{task_id}.md`
transitions:
onComplete: design
- id: design
name: Design
stepType: planning
position: 2
taskState: IN_PROGRESS
behavior:
autoStartAgent: true
planMode: true
promptPrefix: |
[DESIGN PHASE]
Based on research in `.kandev/research/{task_id}.md`, create a design:
1. High-level architecture
2. Component diagram
3. API contracts
4. Migration path (if applicable)
Save design to `.kandev/designs/{task_id}.md`
transitions:
onComplete: review
- id: review
name: Review
stepType: review
position: 3
taskState: REVIEW
behavior:
requireApproval: true
transitions:
onApproval: approved
- id: approved
name: Approved
stepType: done
position: 4
taskState: COMPLETED
Data Model
Schema
-- Workflow templates (system + user-defined)
-- These are blueprints that can be used when creating a board
CREATE TABLE workflow_templates (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
is_system INTEGER DEFAULT 0, -- System templates are read-only
steps JSON NOT NULL, -- Array of step definitions (blueprint)
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Boards now require a workflow
-- The workflow_template_id tracks which template was used (for reference only)
CREATE TABLE boards (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id),
name TEXT NOT NULL,
description TEXT,
workflow_template_id TEXT REFERENCES workflow_templates(id), -- Template used (nullable for custom)
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Workflow steps replace columns entirely
-- Each step IS a column with behavior attached
CREATE TABLE workflow_steps (
id TEXT PRIMARY KEY,
board_id TEXT NOT NULL REFERENCES boards(id) ON DELETE CASCADE,
name TEXT NOT NULL,
step_type TEXT NOT NULL, -- backlog, planning, implementation, review, done, blocked
position INTEGER NOT NULL,
color TEXT NOT NULL,
-- Behavior configuration
auto_start_agent INTEGER DEFAULT 0,
plan_mode INTEGER DEFAULT 0,
require_approval INTEGER DEFAULT 0,
prompt_prefix TEXT,
prompt_suffix TEXT,
-- Transition rules
on_complete_step_id TEXT REFERENCES workflow_steps(id), -- Step to move to on agent completion
on_approval_step_id TEXT REFERENCES workflow_steps(id), -- Step to move to on approval
allow_manual_move INTEGER DEFAULT 1,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Tasks remain simple - workflow progress is tracked per-session
CREATE TABLE tasks (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL REFERENCES workspaces(id),
board_id TEXT NOT NULL REFERENCES boards(id),
title TEXT NOT NULL,
description TEXT,
priority INTEGER DEFAULT 0,
position INTEGER DEFAULT 0,
metadata JSON,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Task sessions track workflow progress (each session goes through workflow independently)
-- Extends existing task_sessions table with workflow fields
CREATE TABLE task_sessions (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
-- Primary session flag (NEW)
is_primary BOOLEAN DEFAULT FALSE, -- Only one session per task should be primary
-- Workflow tracking (NEW)
workflow_step_id TEXT REFERENCES workflow_steps(id), -- Current step in workflow
review_status TEXT, -- NULL, 'pending', 'changes_requested', 'approved'
review_feedback JSON, -- Array of feedback items
-- Existing fields
agent_profile_id TEXT NOT NULL,
executor_id TEXT,
environment_id TEXT,
repository_id TEXT,
base_branch TEXT,
agent_profile_snapshot JSON,
executor_snapshot JSON,
environment_snapshot JSON,
repository_snapshot JSON,
state TEXT NOT NULL DEFAULT 'CREATED', -- CREATED, RUNNING, COMPLETED, etc.
error_message TEXT,
metadata JSON,
started_at TIMESTAMP NOT NULL,
completed_at TIMESTAMP,
updated_at TIMESTAMP NOT NULL
);
-- Index to quickly find primary session for a task
CREATE INDEX idx_task_sessions_primary ON task_sessions(task_id, is_primary) WHERE is_primary = TRUE;
-- Session step history (audit trail per session)
CREATE TABLE session_step_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL REFERENCES task_sessions(id) ON DELETE CASCADE,
from_step_id TEXT REFERENCES workflow_steps(id),
to_step_id TEXT NOT NULL REFERENCES workflow_steps(id),
trigger TEXT NOT NULL, -- manual, auto_complete, approval
actor_id TEXT, -- User or system
metadata JSON,
created_at TIMESTAMP
);
Key Changes from Current Schema
| Current | New |
|---|---|
columns table |
Replaced by workflow_steps |
task.column_id |
Removed (tasks don't track workflow position) |
| No workflow on sessions | task_sessions.workflow_step_id tracks session's position |
| No review status | task_sessions.review_status + review_feedback |
Column has state field |
Step has step_type + behavior fields |
| No workflow concept | workflow_templates + board has workflow_template_id |
| No primary session | task_sessions.is_primary flag (one per task) |
Primary Session and Task Display
┌─────────────────────────────────────────────────────────────────────────────┐
│ Task: "Implement user authentication" │
│ Board: Development Board │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Session 1 (completed) │
│ ├── is_primary: FALSE │
│ ├── workflow_step: "Done" ✓ │
│ └── review_status: "approved" │
│ │
│ Session 2 (abandoned) │
│ ├── is_primary: FALSE │
│ ├── workflow_step: "Review Plan" │
│ └── review_status: "changes_requested" │
│ │
│ Session 3 (active) ← PRIMARY │
│ ├── is_primary: TRUE │
│ ├── workflow_step: "Implementation" │
│ └── review_status: NULL │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Task's kanban position = PRIMARY session's workflow_step
(latest session is automatically primary; user can change)
Backend Components
1. Workflow Service (internal/workflow/)
package workflow
type Service struct {
repo Repository
sessionService *task.SessionService
orchestrator *orchestrator.Service
eventBus *events.Bus
}
// Core operations
func (s *Service) CreateWorkflowFromTemplate(ctx, boardID, templateID string) error
func (s *Service) GetBoardWorkflow(ctx, boardID string) (*Workflow, error)
func (s *Service) GetStepForColumn(ctx, columnID string) (*WorkflowStep, error)
// Primary session operations
func (s *Service) GetPrimarySession(ctx, taskID string) (*TaskSession, error)
func (s *Service) SetPrimarySession(ctx, taskID, sessionID string) error // Unsets previous primary
// Session transitions (workflow operates at session level)
func (s *Service) StartSession(ctx, taskID string, stepID string) (*TaskSession, error) // Auto-sets as primary
func (s *Service) MoveSessionToStep(ctx, sessionID, toStepID string) error
func (s *Service) OnAgentComplete(ctx, sessionID string) error
func (s *Service) ApproveSession(ctx, sessionID string) error
func (s *Service) RejectSession(ctx, sessionID, reason string) error
func (s *Service) GetSessionStep(ctx, sessionID string) (*WorkflowStep, error)
2. Step Executor (internal/workflow/executor/)
Handles step-specific behaviors for sessions:
type StepExecutor struct {
orchestrator *orchestrator.Service
}
func (e *StepExecutor) ExecuteStep(ctx, session *TaskSession, task *Task, step *WorkflowStep) error {
// 1. Apply prompt modifications
prompt := e.buildPrompt(task, session, step)
// 2. Start agent if autoStartAgent
if step.Behavior.AutoStartAgent {
return e.orchestrator.StartSession(ctx, session.ID, prompt, step.Behavior.PlanMode)
}
return nil
}
func (e *StepExecutor) buildPrompt(task *Task, session *TaskSession, step *WorkflowStep) string {
var sb strings.Builder
if step.Behavior.PromptPrefix != "" {
sb.WriteString(e.interpolate(step.Behavior.PromptPrefix, task, session))
sb.WriteString("\n\n")
}
sb.WriteString(task.Description)
// Include previous feedback if session has changes_requested
if session.ReviewStatus == "changes_requested" && len(session.ReviewFeedback) > 0 {
sb.WriteString("\n\n[FEEDBACK TO ADDRESS]\n")
for _, fb := range session.ReviewFeedback {
if !fb.Resolved {
sb.WriteString("- " + fb.Message + "\n")
}
}
}
if step.Behavior.PromptSuffix != "" {
sb.WriteString("\n\n")
sb.WriteString(e.interpolate(step.Behavior.PromptSuffix, task, session))
}
return sb.String()
}
3. Transition Handler
Listens for events and triggers session transitions:
func (s *Service) handleAgentComplete(event *events.AgentCompleteEvent) {
// Workflow operates at session level, not task level
step := s.GetSessionStep(ctx, event.SessionID)
if step.Transitions.OnComplete != "" {
nextStep := s.GetStep(ctx, step.Transitions.OnComplete)
s.MoveSessionToStep(ctx, event.SessionID, nextStep.ID)
}
}
Frontend Design Diagrams
User Flow: Board Creation with Workflow
┌─────────────────────────────────────────────────────────────────────────────┐
│ BOARD CREATION FLOW │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌───────────────────┐ ┌──────────────────────────┐
│ Click "New │────▶│ Board Creation │────▶│ Select Workflow │
│ Board" │ │ Dialog Opens │ │ Template │
└──────────────┘ └───────────────────┘ └──────────────────────────┘
│
┌────────────────────────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ "Development" │ │ "Architecture" │ │ "Custom" │
│ │ │ │ │ │
│ Backlog │ │ Ideas │ │ (Empty board, │
│ Planning │ │ Research │ │ add steps │
│ Review Plan │ │ Design │ │ manually) │
│ Implementation │ │ Review │ │ │
│ Code Review │ │ Approved │ │ │
│ Done │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└────────────────────────────────────┼────────────────┘
│
▼
┌──────────────────────────┐
│ Preview Workflow Steps │
│ (icons show behaviors) │
│ │
│ 🤖 = auto-start agent │
│ ✓ = requires approval │
│ 📋 = plan mode │
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ Click "Create Board" │
│ │
│ → Creates board │
│ → Creates workflow_steps│
│ → Redirects to board │
└──────────────────────────┘
User Flow: Session Lifecycle Through Workflow
┌─────────────────────────────────────────────────────────────────────────────┐
│ SESSION LIFECYCLE (Development Workflow) │
│ │
│ Note: Each SESSION progresses through steps independently. │
│ A task can have multiple sessions at different steps. │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────┐ ┌──────────┐ ┌─────────────┐ ┌──────────────┐ ┌──────────┐ ┌──────┐
│ BACKLOG │───▶│ PLANNING │───▶│ REVIEW PLAN │───▶│IMPLEMENTATION│───▶│CODE REVIEW│───▶│ DONE │
└─────────┘ └──────────┘ └─────────────┘ └──────────────┘ └──────────┘ └──────┘
│ │ │ │ │ │
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌─────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌─────────┐
│ User │ │ 🤖 Agent │ │ ✓ User │ │ 🤖 Agent │ │ ✓ User │ │ Session │
│ starts │ │ auto- │ │ reviews │ │ auto- │ │ reviews │ │ complete│
│ new │ │ starts │ │ plan │ │ starts │ │ code │ │ │
│ session │ │ (plan │ │ │ │ (uses │ │ │ │ │
│ │ │ mode) │ │ │ │ plan) │ │ │ │ │
└─────────┘ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └─────────┘
│ │ │ │
▼ │ ▼ │
┌───────────┐ │ ┌───────────┐ │
│ Creates │ │ │ Makes │ │
│ plan.md │ │ │ code │ │
│ in work- │ │ │ changes │ │
│ tree │ │ │ │ │
└───────────┘ │ └───────────┘ │
│ │ │
▼ ▼ ▼
┌───────────┐ ┌─────────────┐ ┌─────────────┐
│ On agent │ │ Approve │ │ Approve │
│ complete │ │ session? │ │ session? │
│ → session │ │ │ │ │
│ moves to │ │ YES → next │ │ YES → Done │
│ Review │ │ NO → stay │ │ NO → stay │
│ Plan │ │ + feedback │ + feedback
└───────────┘ └─────────────┘ └─────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ MULTIPLE SESSIONS EXAMPLE: │
│ │
│ Task: "Implement auth" │
│ ├── Session 1: ────▶ Planning ────▶ Review ────▶ Implementation ── (done) │
│ ├── Session 2: ────▶ Planning ────▶ Review (rejected, abandoned) │
│ └── Session 3: ────▶ Planning (in progress...) ← PRIMARY │
│ │
│ Task card shows in column of PRIMARY session (Session 3 → "Planning") │
│ User can change primary session if needed (e.g., pin Session 1 as primary) │
└─────────────────────────────────────────────────────────────────────────────┘
User Flow: Session Review and Rejection Cycle
┌─────────────────────────────────────────────────────────────────────────────┐
│ SESSION REVIEW & REJECTION FLOW │
│ │
│ Review and approval happen per-SESSION, not per-task. │
│ Each session's plan/work is reviewed independently. │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────┐
│ Session in REVIEW │
│ PLAN step │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ User opens task │
│ → selects session │
│ to review │
└─────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ SESSION REVIEW PANEL │
│ ┌─────────────────────────────────┐ │
│ │ Session: #3 (Claude 3.5) │ │
│ │ Step: Review Plan │ │
│ │ ───────────────────────────── │ │
│ │ 📋 Implementation Plan │ │
│ │ ## Overview │ │
│ │ This task will implement... │ │
│ │ ## Steps │ │
│ │ 1. Create new component │ │
│ │ 2. Add API endpoint │ │
│ │ ... │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ ✓ Approve │ │ ✗ Request │ │
│ │ Session │ │ Changes │ │
│ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────┘
│
┌────────────────┴────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────────┐
│ APPROVE │ │ REQUEST CHANGES │
│ │ │ │
│ Session moves │ │ Feedback dialog │
│ to next step │ │ opens │
│ (IMPLEMENTATION)│ │ │
└─────────────────┘ └─────────────────────┘
│
▼
┌─────────────────────┐
│ Enter feedback: │
│ ┌─────────────────┐│
│ │ "Need to handle ││
│ │ edge case X" ││
│ └─────────────────┘│
│ [Submit Feedback] │
└─────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Session stays in REVIEW PLAN step │
│ with "Changes Requested" status │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Session #3: Claude 3.5 │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ 🔴 Changes Requested (1) │ │ │
│ │ └─────────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ SESSION REVIEW PANEL (updated) │
│ │
│ ⚠️ Session Feedback History │
│ ┌─────────────────────────────────┐ │
│ │ 🟡 "Need to handle edge case X" │ │
│ │ 2 minutes ago │ │
│ │ [Mark Resolved] │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ 🤖 Re-run Session Agent │ │
│ │ to Address Feedback │ │
│ └─────────────────────────────────┘ │
│ │
│ ⚠️ 1 unresolved feedback item. │
│ Resolve all before approving. │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ User clicks "Re-run Session Agent" │
│ → Agent restarts in same session │
│ → Agent sees previous plan + feedback │
│ → Agent updates plan file │
│ → On complete, user reviews again │
└─────────────────────────────────────────┘
UI Component Layout: Kanban Board
┌─────────────────────────────────────────────────────────────────────────────────────────────┐
│ 📋 My Project Board [⚙️ Settings] │
├─────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ○ Backlog │ │ ○ Planning │ │ ○ Review │ │ ○ Implement │ │ ○ Done │ │
│ │ │ │ 🤖 📋 │ │ ✓ │ │ 🤖 │ │ │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │ Task A │ │ │ │ Task B │ │ │ │ Task D │ │ │ │ Task E │ │ │ │ Task G │ │ │
│ │ │ (no │ │ │ │ ●●○○○○ │ │ │ │ 🔴 (1) │ │ │ │ ●●●●○○ │ │ │ │ ●●●●●● │ │ │
│ │ │ session)│ │ │ │ 🤖 S#1 │ │ │ │ S#2 │ │ │ │ 🤖 S#3 │ │ │ │ ✓ S#1 │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │ └─────────┘ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ │ │ │ │ ┌─────────┐ │ │
│ │ │ Task C │ │ │ │ Task F │ │ │ │ │ │ │ │ Task H │ │ │
│ │ │ (no │ │ │ │ ●●○○○○ │ │ │ │ │ │ │ │ ●●●●●● │ │ │
│ │ │ session)│ │ │ │ S#1 │ │ │ │ │ │ │ │ ✓ S#2 │ │ │
│ │ └─────────┘ │ │ └─────────┘ │ │ │ │ │ │ └─────────┘ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ [+ Add] │ │ │ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────┘
LEGEND:
○ Column color indicator 🤖 Agent auto-starts in this column
📋 Plan mode enabled ✓ Requires approval
●●●○○○ Session workflow progress 🔴 (1) Changes requested (on session)
🤖 S#1 Active session running S#2 Session indicator (shows primary session)
★ Primary session indicator
NOTE: Task card position in column = PRIMARY session's workflow step
Latest session is automatically primary; user can change
Click task → select specific session to view/manage
UI Component Layout: Task Detail Panel (Session-Based)
┌─────────────────────────────────────────────────────────────────────────────┐
│ ← Back [⋮ More] │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Implement user authentication │
│ ════════════════════════════════════════════════════════════════════════ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Sessions [+ New Session]│ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ │
│ │ │ ● Session #3 │ │ ○ Session #2 │ │ ○ Session #1 │ │ │
│ │ │ Claude 3.5 │ │ Codex │ │ Claude 3.5 │ │ │
│ │ │ In: Review │ │ Abandoned │ │ ✓ Completed │ │ │
│ │ │ 🔴 (1 issue) │ │ │ │ │ │ │
│ │ └────────────────┘ └────────────────┘ └────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ═══════════════════════════════════════════════════════════════════════ │
│ SESSION #3 DETAILS │
│ ═══════════════════════════════════════════════════════════════════════ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Session Workflow Progress │ │
│ │ │ │
│ │ ✓ Backlog → ✓ Planning → ● Review → ○ Implement → ○ Done │ │
│ │ Plan │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⚠️ Session Feedback History │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ 🟡 "Consider using httpOnly cookies for refresh tokens" │ │ │
│ │ │ 5 minutes ago │ │ │
│ │ │ [Mark Resolved] │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ ✅ "Add rate limiting to login endpoint" │ │ │
│ │ │ 1 hour ago Resolved │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ⚠️ 1 unresolved feedback item. Resolve all before approving. │ │
│ │ │ │
│ │ ┌─────────────────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ 🤖 Re-run Session │ │ [Abandon Session] │ │ │
│ │ │ Agent │ │ │ │ │
│ │ └─────────────────────────┘ └─────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ ✓ Approve Session │ │ ✗ Request Changes │ │ │
│ │ │ (disabled) │ │ │ │ │
│ │ └─────────────────────────┘ └─────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
UI Component Layout: Workflow Editor (Board Settings)
┌─────────────────────────────────────────────────────────────────────────────┐
│ ⚙️ Board Settings │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ General │ Workflow │ Permissions │
│ ──────── ════════ ─────────── │
│ │
│ Workflow Steps [+ Add Step] │
│ ═══════════════════════════════════════════════════════════════════════ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ 🟢 │ Backlog │ backlog ▼ │ │ 🗑️ │ ▼ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ 🔵 │ Planning │ planning ▼ │ 🤖 📋 │ 🗑️ │ ▼ │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ ☑️ Auto-start agent ☑️ Plan mode (no execution) │ │
│ │ ☐ Require approval │ │
│ │ │ │
│ │ Prompt Prefix: │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ [PLANNING PHASE] Analyze the task and create a detailed │ │ │
│ │ │ implementation plan. Do NOT write any code yet. │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ On Completion, move to: [ Review Plan ▼ ] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ 🟡 │ Review Plan │ review ▼ │ ✓ │ 🗑️ │ ▶ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ 🟣 │ Implementation │ implement ▼ │ 🤖 │ 🗑️ │ ▶ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ 🟠 │ Code Review │ review ▼ │ ✓ │ 🗑️ │ ▶ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ⋮⋮ │ ⬜ │ Done │ done ▼ │ │ 🗑️ │ ▶ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ │
│ LEGEND: │
│ ⋮⋮ = Drag handle 🤖 = Auto-start agent 📋 = Plan mode │
│ ✓ = Requires approval ▼/▶ = Expand/collapse step details │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
State Diagram: Session in Review Step
┌─────────────────────────────────────────────────────────────────────────────┐
│ SESSION STATE IN REVIEW STEP │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────┐
│ │
│ PENDING_REVIEW │◀─────────────────────────┐
│ │ │
└──────────────────────┘ │
│ │
┌───────────────┴───────────────┐ │
│ │ │
▼ ▼ │
┌──────────────────┐ ┌──────────────────┐ │
│ │ │ │ │
│ APPROVED │ │ CHANGES_REQUESTED│ │
│ │ │ │ │
└──────────────────┘ └──────────────────┘ │
│ │ │
│ │ │
▼ ▼ │
┌──────────────────┐ ┌──────────────────┐ │
│ │ │ │ │
│ Move session to │ │ Agent re-runs │──────────────┘
│ next step │ │ with feedback │
│ │ │ │
└──────────────────┘ └──────────────────┘
Session Fields (in task_sessions table):
┌─────────────────────────────────────────────────────────────────────┐
│ workflow_step_id: "step-review-plan" -- Current workflow position │
│ review_status: "changes_requested" -- pending | approved | ... │
│ review_feedback: [ │
│ { │
│ "id": "fb-1", │
│ "message": "Need to handle edge case X", │
│ "created_at": "2024-01-15T10:30:00Z", │
│ "resolved": false │
│ }, │
│ { │
│ "id": "fb-2", │
│ "message": "Add error handling", │
│ "created_at": "2024-01-15T09:00:00Z", │
│ "resolved": true, │
│ "resolved_at": "2024-01-15T10:00:00Z" │
│ } │
│ ] │
└─────────────────────────────────────────────────────────────────────┘
NOTE: Each session tracks its own workflow position independently.
A task's position on the kanban board is derived from its most
advanced active session.
Data Flow: Session Start with Agent Auto-Start
┌─────────────────────────────────────────────────────────────────────────────┐
│ DATA FLOW: SESSION START WITH AGENT AUTO-START │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ Frontend │
│ (Task Panel) │
└──────────────┘
│
│ 1. User clicks "New Session" or "Start" on task
│ (selects target step, e.g., "Planning")
│
▼
┌──────────────┐ 2. WS: session.create ┌──────────────┐
│ WebSocket │ ──────────────────────────────────────▶ │ Backend │
│ Client │ { task_id, step_id } │ (WS GW) │
└──────────────┘ └──────────────┘
│
│ 3. WorkflowService.StartSession()
▼
┌──────────────┐
│ Workflow │
│ Service │
│ │
│ Creates new │
│ TaskSession │
│ with step_id │
└──────────────┘
│
│ 4. Get target step config
▼
┌──────────────┐
│ WorkflowStep │
│ auto_start: │
│ true │
│ plan_mode: │
│ true │
│ prompt_ │
│ prefix:... │
└──────────────┘
│
│ 5. Publish event
▼
┌──────────────┐
│ Event Bus │
│ │
│ session. │
│ created │
└──────────────┘
│
│ 6. Orchestrator handles event
▼
┌──────────────┐
│ Orchestrator │
│ │
│ if step. │
│ auto_start: │
│ LaunchAgent│
└──────────────┘
│
│ 7. Build prompt with prefix + feedback
▼
┌──────────────────────────────┐
│ Prompt = step.prompt_prefix │
│ + task.description │
│ + session.feedback │
│ + step.prompt_suffix │
│ │
│ "[PLANNING PHASE] Analyze │
│ the task and create a │
│ detailed implementation │
│ plan. Do NOT write code.\n" │
│ + "Implement user auth..." │
│ + "[FEEDBACK TO ADDRESS]..."│
└──────────────────────────────┘
│
│ 8. Start agent for session
▼
┌──────────────┐
│ Lifecycle │
│ Manager │
│ │
│ StartSession │
│ (session has │
│ workflow_ │
│ step_id) │
└──────────────┘
│
┌────────────────────────────────────────────────────────┘
│ 9. WS: session.started, session.update (streaming)
▼
┌──────────────┐
│ Frontend │
│ (Updates │
│ session in │
│ task panel │
│ with 🤖) │
└──────────────┘
NOTE: Task card position on kanban = PRIMARY session's workflow step
Frontend Integration
Board Creation with Workflow Selection
When creating a new board, users select a workflow template that determines the columns and their behaviors.
Dialog Flow:
- User clicks "New Board"
- Dialog opens with board name input and workflow template selector
- Selecting a template shows a preview of the workflow steps
- User can choose "Custom" to start with a blank workflow
- On create, columns are generated from the workflow steps
<Dialog>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Create Board</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<Input label="Board Name" placeholder="My Project Board" />
<div>
<Label>Workflow Template</Label>
<RadioGroup value={selectedTemplate} onValueChange={setSelectedTemplate}>
<RadioGroupItem value="standard">
<div className="flex flex-col">
<span className="font-medium">Standard</span>
<span className="text-sm text-muted-foreground">
Todo → Plan → Implementation → Done
</span>
</div>
</RadioGroupItem>
<RadioGroupItem value="architecture">
<div className="flex flex-col">
<span className="font-medium">Architecture</span>
<span className="text-sm text-muted-foreground">
Ideas → Research → Design → Review → Approved
</span>
</div>
</RadioGroupItem>
<RadioGroupItem value="simple">
<div className="flex flex-col">
<span className="font-medium">Simple Kanban</span>
<span className="text-sm text-muted-foreground">
Todo → In Progress → Done (no automation)
</span>
</div>
</RadioGroupItem>
<RadioGroupItem value="custom">
<div className="flex flex-col">
<span className="font-medium">Custom</span>
<span className="text-sm text-muted-foreground">
Start with a blank workflow and add steps manually
</span>
</div>
</RadioGroupItem>
</RadioGroup>
</div>
{/* Workflow Preview */}
{selectedTemplate && selectedTemplate !== 'custom' && (
<div className="border rounded-lg p-4 bg-muted/50">
<Label className="text-sm">Workflow Preview</Label>
<div className="flex items-center gap-2 mt-2 overflow-x-auto">
{templateSteps.map((step, i) => (
<React.Fragment key={step.id}>
<div className={cn("px-3 py-1.5 rounded text-sm", step.color)}>
{step.name}
{step.behavior.autoStartAgent && (
<IconRobot className="inline ml-1 h-3 w-3" />
)}
{step.behavior.requireApproval && (
<IconCheck className="inline ml-1 h-3 w-3" />
)}
</div>
{i < templateSteps.length - 1 && (
<IconArrowRight className="h-4 w-4 text-muted-foreground" />
)}
</React.Fragment>
))}
</div>
<div className="flex gap-4 mt-2 text-xs text-muted-foreground">
<span><IconRobot className="inline h-3 w-3" /> Auto-starts agent</span>
<span><IconCheck className="inline h-3 w-3" /> Requires approval</span>
</div>
</div>
)}
</div>
<DialogFooter>
<Button variant="outline" onClick={onClose}>Cancel</Button>
<Button onClick={handleCreate}>Create Board</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Kanban Board with Workflow Columns
The kanban board displays columns based on workflow steps. Each column header shows:
- Column name
- Step type indicator (icon)
- Auto-start badge if enabled
<div className="kanban-board flex gap-4 overflow-x-auto p-4">
{columns.map((column) => (
<div key={column.id} className="kanban-column w-80 flex-shrink-0">
{/* Column Header */}
<div className="flex items-center justify-between p-3 bg-muted rounded-t-lg">
<div className="flex items-center gap-2">
<div className={cn("w-3 h-3 rounded-full", column.step?.color)} />
<span className="font-medium">{column.name}</span>
<Badge variant="outline" className="text-xs">
{column.step?.stepType}
</Badge>
</div>
<div className="flex items-center gap-1">
{column.step?.behavior.autoStartAgent && (
<Tooltip content="Agent auto-starts when tasks enter this column">
<IconRobot className="h-4 w-4 text-blue-500" />
</Tooltip>
)}
{column.step?.behavior.requireApproval && (
<Tooltip content="Tasks require approval to proceed">
<IconShieldCheck className="h-4 w-4 text-yellow-500" />
</Tooltip>
)}
</div>
</div>
{/* Task Cards */}
<div className="space-y-2 p-2 bg-muted/30 min-h-[200px]">
{column.tasks.map((task) => (
<TaskCard key={task.id} task={task} step={column.step} />
))}
</div>
</div>
))}
</div>
Task Card with Primary Session Workflow Status
Task cards show workflow-specific information based on the primary session:
- Primary session workflow progress dots
- Review status badge (if primary session has changes requested)
- Primary session indicator (★ marker)
- Agent status (running, waiting, etc.)
function TaskCard({ task, step }: { task: Task; step: WorkflowStep }) {
// Get the primary session for display (latest session by default)
const primarySession = usePrimarySession(task.id);
const sessionStep = primarySession?.workflow_step_id;
const hasChangesRequested = primarySession?.review_status === 'changes_requested';
const feedbackCount = primarySession?.review_feedback?.filter(f => !f.resolved).length ?? 0;
const sessionCount = task.sessions?.length ?? 0;
return (
<Card className="p-3 cursor-pointer hover:shadow-md transition-shadow">
{/* Header */}
<div className="flex items-start justify-between">
<h4 className="font-medium text-sm line-clamp-2">{task.title}</h4>
{hasChangesRequested && (
<Badge variant="destructive" className="text-xs">
Changes Requested ({feedbackCount})
</Badge>
)}
</div>
{/* Primary Session Workflow Progress Dots */}
{primarySession && (
<div className="flex items-center gap-1 mt-2">
{workflowSteps.map((s, i) => (
<div
key={s.id}
className={cn(
"w-2 h-2 rounded-full",
i < currentStepIndex && "bg-green-500", // Completed
i === currentStepIndex && "bg-blue-500 ring-2 ring-blue-200", // Current
i > currentStepIndex && "bg-gray-300" // Future
)}
/>
))}
</div>
)}
{/* Primary Session Indicator */}
<div className="flex items-center gap-1 mt-2 text-xs text-muted-foreground">
{primarySession ? (
<>
<span>★ Session #{primarySession.number}</span>
{sessionCount > 1 && (
<Badge variant="outline" className="text-xs ml-1">
+{sessionCount - 1} more
</Badge>
)}
</>
) : (
<span className="text-muted-foreground">No sessions</span>
)}
</div>
{/* Primary Session Agent Status */}
{primarySession && (
<div className="flex items-center gap-1 mt-1 text-xs text-muted-foreground">
{primarySession.state === 'RUNNING' && (
<>
<IconLoader className="h-3 w-3 animate-spin" />
<span>Agent working...</span>
</>
)}
{primarySession.state === 'WAITING_FOR_INPUT' && (
<>
<IconMessageCircle className="h-3 w-3" />
<span>Waiting for input</span>
</>
)}
</div>
)}
</Card>
);
}
Review Step UI - Session Review Panel
When a session is in a review step, the session detail panel shows:
- Previous feedback (if any)
- Approval actions
// Session-based review panel - operates on a specific session, not the task
function SessionReviewPanel({ session, task, step }: {
session: TaskSession;
task: Task;
step: WorkflowStep
}) {
return (
<div className="space-y-4">
{/* Session Info Header */}
<div className="flex items-center justify-between p-2 bg-muted rounded">
<span className="text-sm font-medium">
Session #{session.number} • {session.agent_profile?.name ?? 'Agent'}
</span>
<Badge variant={session.review_status === 'changes_requested' ? 'destructive' : 'secondary'}>
{session.review_status ?? 'pending'}
</Badge>
</div>
{/* Session Feedback History */}
{existingFeedback.length > 0 && (
<div className="space-y-2">
<Label>Session Feedback History</Label>
{existingFeedback.map((fb) => (
<div
key={fb.id}
className={cn(
"p-3 rounded-lg border",
fb.resolved ? "bg-green-50 border-green-200" : "bg-yellow-50 border-yellow-200"
)}
>
<div className="flex items-start justify-between">
<p className="text-sm">{fb.message}</p>
{fb.resolved ? (
<Badge variant="outline" className="text-green-600">Resolved</Badge>
) : (
<Button
variant="ghost"
size="sm"
onClick={() => markSessionFeedbackResolved(session.id, fb.id)}
>
Mark Resolved
</Button>
)}
</div>
<span className="text-xs text-muted-foreground">
{formatRelativeTime(fb.created_at)}
</span>
</div>
))}
</div>
)}
{/* Session Approval Actions */}
<div className="flex flex-col gap-3 pt-4 border-t">
{!showFeedbackInput ? (
<div className="flex gap-2">
<Button
className="flex-1"
onClick={() => approveSession(session.id)} // Session-level approval
disabled={unresolvedFeedback.length > 0}
>
<IconCheck className="mr-2 h-4 w-4" />
Approve Session
</Button>
<Button
variant="outline"
className="flex-1"
onClick={() => setShowFeedbackInput(true)}
>
<IconX className="mr-2 h-4 w-4" />
Request Changes
</Button>
</div>
) : (
<div className="space-y-2">
<Textarea
placeholder="Describe what changes are needed..."
value={feedback}
onChange={(e) => setFeedback(e.target.value)}
rows={3}
/>
<div className="flex gap-2">
<Button
onClick={() => {
rejectSession(session.id, feedback); // Session-level rejection
setFeedback('');
setShowFeedbackInput(false);
}}
disabled={!feedback.trim()}
>
Submit Feedback
</Button>
<Button
variant="ghost"
onClick={() => {
setFeedback('');
setShowFeedbackInput(false);
}}
>
Cancel
</Button>
</div>
</div>
)}
{unresolvedFeedback.length > 0 && (
<p className="text-sm text-muted-foreground">
⚠️ {unresolvedFeedback.length} unresolved feedback item(s).
Resolve all feedback before approving this session.
</p>
)}
{/* Re-run Session Agent Button */}
{unresolvedFeedback.length > 0 && (
<Button
variant="secondary"
onClick={() => rerunSessionWithFeedback(session.id)} // Session-level re-run
>
<IconRobot className="mr-2 h-4 w-4" />
Re-run Session Agent with Feedback
</Button>
)}
{/* Abandon Session Option */}
<Button
variant="ghost"
className="text-destructive"
onClick={() => abandonSession(session.id)}
>
Abandon This Session
</Button>
</div>
</div>
);
}
Workflow Editor (Settings)
Users can customize workflows in the board settings. This allows:
- Reordering steps
- Editing step behaviors (prompts, auto-start, etc.)
- Adding/removing steps
- Changing step colors
function WorkflowEditor({ boardId }: { boardId: string }) {
const { workflow, updateStep, addStep, removeStep, reorderSteps } = useWorkflow(boardId);
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium">Workflow Steps</h3>
<Button variant="outline" size="sm" onClick={() => addStep()}>
<IconPlus className="mr-2 h-4 w-4" />
Add Step
</Button>
</div>
<DndContext onDragEnd={handleDragEnd}>
<SortableContext items={workflow.steps.map(s => s.id)}>
{workflow.steps.map((step, index) => (
<SortableStepCard
key={step.id}
step={step}
index={index}
onUpdate={(updates) => updateStep(step.id, updates)}
onRemove={() => removeStep(step.id)}
/>
))}
</SortableContext>
</DndContext>
</div>
);
}
function SortableStepCard({ step, index, onUpdate, onRemove }) {
const [expanded, setExpanded] = useState(false);
return (
<Card className="p-4">
<div className="flex items-center gap-3">
<IconGripVertical className="h-5 w-5 text-muted-foreground cursor-grab" />
<div className={cn("w-4 h-4 rounded", step.color)} />
<Input
value={step.name}
onChange={(e) => onUpdate({ name: e.target.value })}
className="flex-1"
/>
<Select value={step.stepType} onValueChange={(v) => onUpdate({ stepType: v })}>
<SelectItem value="backlog">Backlog</SelectItem>
<SelectItem value="planning">Planning</SelectItem>
<SelectItem value="implementation">Implementation</SelectItem>
<SelectItem value="review">Review</SelectItem>
<SelectItem value="done">Done</SelectItem>
</Select>
<Button variant="ghost" size="sm" onClick={() => setExpanded(!expanded)}>
{expanded ? <IconChevronUp /> : <IconChevronDown />}
</Button>
<Button variant="ghost" size="sm" onClick={onRemove}>
<IconTrash className="h-4 w-4 text-destructive" />
</Button>
</div>
{expanded && (
<div className="mt-4 pl-8 space-y-4 border-t pt-4">
{/* Behavior Settings */}
<div className="grid grid-cols-2 gap-4">
<div className="flex items-center gap-2">
<Switch
checked={step.behavior.autoStartAgent}
onCheckedChange={(v) => onUpdate({ behavior: { ...step.behavior, autoStartAgent: v }})}
/>
<Label>Auto-start agent</Label>
</div>
<div className="flex items-center gap-2">
<Switch
checked={step.behavior.planMode}
onCheckedChange={(v) => onUpdate({ behavior: { ...step.behavior, planMode: v }})}
/>
<Label>Plan mode (no execution)</Label>
</div>
<div className="flex items-center gap-2">
<Switch
checked={step.behavior.requireApproval}
onCheckedChange={(v) => onUpdate({ behavior: { ...step.behavior, requireApproval: v }})}
/>
<Label>Require approval</Label>
</div>
</div>
{/* Prompt Prefix */}
<div>
<Label>Prompt Prefix</Label>
<Textarea
value={step.behavior.promptPrefix ?? ''}
onChange={(e) => onUpdate({ behavior: { ...step.behavior, promptPrefix: e.target.value }})}
placeholder="Text prepended to the task description when agent starts..."
rows={3}
/>
</div>
{/* Transition Settings */}
<div>
<Label>On Completion, move to:</Label>
<Select
value={step.transitions.onComplete ?? ''}
onValueChange={(v) => onUpdate({ transitions: { ...step.transitions, onComplete: v }})}
>
<SelectItem value="">Stay in this step</SelectItem>
{workflow.steps.filter(s => s.id !== step.id).map(s => (
<SelectItem key={s.id} value={s.id}>{s.name}</SelectItem>
))}
</Select>
</div>
</div>
)}
</Card>
);
}
Drag-and-Drop Behavior
When a task is dragged to a new column:
- Check if manual move is allowed - Some steps may restrict manual moves
- Trigger step entry behavior - If the new step has
autoStartAgent: true, start the agent - Show confirmation for review steps - If moving to a review step, confirm the user wants to submit for review
function handleTaskDrop(taskId: string, fromColumnId: string, toColumnId: string) {
const fromStep = getStepForColumn(fromColumnId);
const toStep = getStepForColumn(toColumnId);
// Check if move is allowed
if (!toStep.transitions.allowManualMove) {
toast.error(`Cannot manually move tasks to "${toStep.name}"`);
return;
}
// Confirm if moving to review
if (toStep.stepType === 'review' && fromStep.stepType !== 'review') {
const confirmed = await confirm({
title: 'Submit for Review?',
description: 'This will submit the task for review. The agent will stop working.',
});
if (!confirmed) return;
}
// Execute move
await moveTask(taskId, toColumnId);
// Backend handles step entry behavior (auto-start agent, etc.)
}
## API Endpoints
### HTTP Endpoints
Workflow Templates & Board Workflows
GET /api/v1/workflow-templates # List available templates GET /api/v1/workflow-templates/:id # Get template details POST /api/v1/boards/:id/workflow # Apply workflow to board GET /api/v1/boards/:id/workflow # Get board's workflow GET /api/v1/boards/:id/workflow/steps # Get workflow steps
Session Workflow Operations (workflow operates at session level)
POST /api/v1/sessions/:id/workflow/approve # Approve session's current step POST /api/v1/sessions/:id/workflow/reject # Reject session / request changes POST /api/v1/sessions/:id/workflow/move # Move session to a different step GET /api/v1/sessions/:id/step # Get session's current workflow step
Primary Session Operations
GET /api/v1/tasks/:id/primary-session # Get task's primary session PUT /api/v1/tasks/:id/primary-session # Set primary session { session_id }
### WebSocket Actions
Session-level workflow actions
session.workflow.approve { session_id } # Approve session step session.workflow.reject { session_id, reason } # Reject session with feedback session.workflow.move { session_id, step_id } # Move session to step
Primary session actions
task.primary_session.set { task_id, session_id } # Change primary session
Notifications
session.step.changed { session_id, step_id, ... } # Session moved to new step session.review.requested { session_id, ... } # Session entered review step session.approved { session_id, ... } # Session was approved task.primary_session.changed { task_id, session_id } # Primary session changed
## Resolved Design Decisions
| Decision | Resolution | Rationale |
|----------|------------|-----------|
| **Columns vs Steps** | Columns ARE steps (`workflow_steps` replaces `columns`) | Simplicity - one entity instead of two with a relationship |
| **Plan Storage** | Files in worktree (`.kandev/plans/{session_id}.md`) | Simple, no database storage needed, accessible in editor |
| **Custom Workflows** | Full support (templates + from scratch) | Users can use templates, customize them, or create entirely new workflows |
| **Rejection Behavior** | Stay in review + mark "changes requested" | Enables iterative review cycles without moving cards back and forth |
| **Backward Compatibility** | None required | Implementing from scratch, following current patterns and abstractions |
| **Workflow Level** | Session-level (not task-level) | Multiple sessions can work on a task independently, each progressing through the workflow |
| **Task Display** | Primary session determines kanban column | Latest session is auto-primary; user can change. Simpler than "most advanced" logic |
## Open Questions
1. **Step Skipping**: Should users be able to skip steps when starting a new session?
- Currently: Sessions can start at any step
- Consider: Add `allowedStartSteps` array for more control
---
## Next Steps
### Phase 1: Database Schema
1. [ ] Create `workflow_templates` table with seed data (dev, architecture, simple)
2. [ ] Create `workflow_steps` table (replaces `columns`)
3. [ ] Update `boards` table with `workflow_template_id`
4. [ ] Update `task_sessions` table: add `is_primary`, `workflow_step_id`, `review_status`, `review_feedback`
5. [ ] Create `session_step_history` table
### Phase 2: Backend Services
6. [ ] Implement `WorkflowService` (CRUD for templates and steps)
7. [ ] Update `SessionService` with workflow step operations (`MoveSessionToStep`, etc.)
10. [ ] Implement primary session operations (`GetPrimarySession`, `SetPrimarySession`)
11. [ ] Add workflow event handlers (on session created → auto-set as primary, on agent complete)
12. [ ] Add session approval/rejection endpoints
### Phase 3: Frontend - Board Creation
13. [ ] Add workflow template selector to board creation dialog
14. [ ] Show workflow preview when template selected
15. [ ] Create workflow steps when board is created
### Phase 4: Frontend - Session Workflow
16. [ ] Display task card based on primary session's step
17. [ ] Add primary session indicator (★) to task cards
18. [ ] Add session selector to task detail panel with "Set as Primary" action
19. [ ] Add session workflow progress indicator
20. [ ] Add session review status badge to task cards
21. [ ] Implement session review panel with approval/rejection UI
22. [ ] Add session feedback input and history display
23. [ ] Implement "Re-run Session Agent" button for addressing feedback
24. [ ] Add "Start New Session" flow (new session auto-becomes primary)
### Phase 5: Frontend - Workflow Editor
25. [ ] Add workflow editor to board settings
26. [ ] Implement step reordering (drag and drop)
27. [ ] Implement step behavior editing (prompts, auto-start, etc.)
28. [ ] Add step add/remove functionality