Files
kandev/docs/design/workflow-system.md
T

1773 lines
97 KiB
Markdown

# 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**:
1. User clicks "Request Changes" and provides feedback
2. Session stays in the review step
3. Session metadata is updated with rejection info
4. Agent can be re-triggered to address the feedback
5. User reviews again, can approve or request more changes
6. On approval, session moves to the configured `onApproval` step
**Session metadata on rejection**:
```json
{
"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:
1. **Use a template as-is** - Quick start with pre-configured workflow
2. **Customize a template** - Clone a template and modify steps
3. **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:
```typescript
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)
```yaml
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
```yaml
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
```sql
-- 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/`)
```go
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:
```go
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:
```go
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**:
1. User clicks "New Board"
2. Dialog opens with board name input and workflow template selector
3. Selecting a template shows a preview of the workflow steps
4. User can choose "Custom" to start with a blank workflow
5. On create, columns are generated from the workflow steps
```tsx
<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
```tsx
<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.)
```tsx
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:
1. Previous feedback (if any)
2. Approval actions
```tsx
// 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
```tsx
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:
1. **Check if manual move is allowed** - Some steps may restrict manual moves
2. **Trigger step entry behavior** - If the new step has `autoStartAgent: true`, start the agent
3. **Show confirmation for review steps** - If moving to a review step, confirm the user wants to submit for review
```tsx
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