diff --git a/apps/web/components/task-create-dialog.tsx b/apps/web/components/task-create-dialog.tsx
index 7dcd23fcdf..0c6ae0c2f2 100644
--- a/apps/web/components/task-create-dialog.tsx
+++ b/apps/web/components/task-create-dialog.tsx
@@ -1296,7 +1296,7 @@ export function TaskCreateDialog({
type="submit"
variant="default"
className="rounded-none border-0 cursor-pointer gap-1.5 h-full"
- disabled={isCreatingTask || !hasTitle || (!repositoryId && !selectedLocalRepo) || !branch || !agentProfileId}
+ disabled={isCreatingTask || !hasTitle || (!repositoryId && !selectedLocalRepo) || !branch || !agentProfileId || (isCreateMode && (!workspaceId || !effectiveWorkflowId))}
>
{isCreatingTask ?
:
}
{isCreatingTask ? 'Starting...' : 'Start task'}
@@ -1307,7 +1307,7 @@ export function TaskCreateDialog({
type="button"
variant="default"
className="rounded-none border-0 border-l border-primary-foreground/20 px-2 cursor-pointer h-full"
- disabled={isCreatingTask || !hasTitle || (!repositoryId && !selectedLocalRepo) || !branch || !agentProfileId}
+ disabled={isCreatingTask || !hasTitle || (!repositoryId && !selectedLocalRepo) || !branch || !agentProfileId || (isCreateMode && (!workspaceId || !effectiveWorkflowId))}
>
@@ -1332,7 +1332,7 @@ export function TaskCreateDialog({
isCreatingTask ||
(isSessionMode
? (!hasDescription && !isPassthroughProfile) || !agentProfileId
- : !hasTitle || (!repositoryId && !selectedLocalRepo) || !branch)
+ : !hasTitle || (!repositoryId && !selectedLocalRepo) || !branch || (isCreateMode && (!workspaceId || !effectiveWorkflowId)))
}
>
{isCreatingSession || isCreatingTask ? (
diff --git a/apps/web/hooks/domains/settings/use-settings-data.ts b/apps/web/hooks/domains/settings/use-settings-data.ts
index c5043a75f9..eb8101dfce 100644
--- a/apps/web/hooks/domains/settings/use-settings-data.ts
+++ b/apps/web/hooks/domains/settings/use-settings-data.ts
@@ -1,6 +1,7 @@
import { useEffect } from 'react';
import { useAppStore } from '@/components/state-provider';
import { listAgents, listEnvironments, listExecutors } from '@/lib/api';
+import { toAgentProfileOption } from '@/lib/state/slices/settings/types';
export function useSettingsData(enabled = true) {
const executors = useAppStore((state) => state.executors.items);
@@ -48,13 +49,7 @@ export function useSettingsData(enabled = true) {
setSettingsAgents(response.agents);
setAgentProfiles(
response.agents.flatMap((agent) =>
- agent.profiles.map((profile) => ({
- id: profile.id,
- label: `${profile.agent_display_name} • ${profile.name}`,
- agent_id: agent.id,
- agent_name: agent.name,
- cli_passthrough: profile.cli_passthrough,
- }))
+ agent.profiles.map((profile) => toAgentProfileOption(agent, profile))
)
);
})
diff --git a/apps/web/lib/state/slices/settings/types.ts b/apps/web/lib/state/slices/settings/types.ts
index 51c85fc547..4df55ce0a9 100644
--- a/apps/web/lib/state/slices/settings/types.ts
+++ b/apps/web/lib/state/slices/settings/types.ts
@@ -1,5 +1,6 @@
import type {
Agent,
+ AgentProfile,
AvailableAgent,
AgentDiscovery,
CustomPrompt,
@@ -36,9 +37,23 @@ export type AgentProfileOption = {
label: string;
agent_id: string;
agent_name: string;
- cli_passthrough?: boolean;
+ cli_passthrough: boolean;
};
+/** Single source of truth for mapping an API Agent+Profile to a store AgentProfileOption. */
+export function toAgentProfileOption(
+ agent: Pick
,
+ profile: Pick & { cli_passthrough?: boolean },
+): AgentProfileOption {
+ return {
+ id: profile.id,
+ label: `${profile.agent_display_name} • ${profile.name}`,
+ agent_id: agent.id,
+ agent_name: agent.name,
+ cli_passthrough: profile.cli_passthrough ?? false,
+ };
+}
+
export type AgentProfilesState = {
items: AgentProfileOption[];
version: number;
diff --git a/apps/web/lib/ws/handlers/agents.ts b/apps/web/lib/ws/handlers/agents.ts
index dcacd252da..c37c481de5 100644
--- a/apps/web/lib/ws/handlers/agents.ts
+++ b/apps/web/lib/ws/handlers/agents.ts
@@ -1,6 +1,7 @@
import type { StoreApi } from 'zustand';
import type { AppState } from '@/lib/state/store';
import type { WsHandlers } from '@/lib/ws/handlers/types';
+import { toAgentProfileOption } from '@/lib/state/slices/settings/types';
export function registerAgentsHandlers(store: StoreApi): WsHandlers {
return {
'agent.available.updated': (message) => {
@@ -28,11 +29,11 @@ export function registerAgentsHandlers(store: StoreApi): WsHandlers {
},
'agent.profile.created': (message) => {
store.setState((state) => {
- const label = `${message.payload.profile.agent_display_name} • ${message.payload.profile.name}`;
- const agentName = state.settingsAgents.items.find((a) => a.id === message.payload.profile.agent_id)?.name ?? '';
+ const agent = state.settingsAgents.items.find((a) => a.id === message.payload.profile.agent_id);
+ const agentStub = { id: message.payload.profile.agent_id, name: agent?.name ?? '' };
const nextProfiles = [
...state.agentProfiles.items.filter((profile) => profile.id !== message.payload.profile.id),
- { id: message.payload.profile.id, label, agent_id: message.payload.profile.agent_id, agent_name: agentName },
+ toAgentProfileOption(agentStub, message.payload.profile),
];
const nextAgents = state.settingsAgents.items.map((item) =>
item.id === message.payload.profile.agent_id
@@ -67,11 +68,11 @@ export function registerAgentsHandlers(store: StoreApi): WsHandlers {
},
'agent.profile.updated': (message) => {
store.setState((state) => {
- const label = `${message.payload.profile.agent_display_name} • ${message.payload.profile.name}`;
- const agentName = state.settingsAgents.items.find((a) => a.id === message.payload.profile.agent_id)?.name ?? '';
+ const agent = state.settingsAgents.items.find((a) => a.id === message.payload.profile.agent_id);
+ const agentStub = { id: message.payload.profile.agent_id, name: agent?.name ?? '' };
const nextProfiles = state.agentProfiles.items.map((profile) =>
profile.id === message.payload.profile.id
- ? { ...profile, label, agent_id: message.payload.profile.agent_id, agent_name: agentName }
+ ? toAgentProfileOption(agentStub, message.payload.profile)
: profile
);
const nextAgents = state.settingsAgents.items.map((item) =>
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 9a105ca0fc..bc0fbec32a 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -15,8 +15,6 @@ The current implementation uses a **unified binary architecture** instead of sep
- ✅ **Permission request handling** - Auto-approval of workspace indexing and tool permissions
- ✅ **Session resumption** for multi-turn agent conversations
- ✅ WebSocket-first API for all operations (replacing REST)
-- 📋 JWT authentication
-- 📋 NATS event bus for distributed deployment
---
@@ -48,50 +46,6 @@ flowchart TB
---
-## Planned Architecture (Microservices) 📋
-
-### 1. API Gateway (WebSocket-First)
-**Port:** 8080
-**Purpose:** Single entry point for all client requests via WebSocket
-
-**Responsibilities:**
-- Route WebSocket messages to appropriate services
-- JWT authentication and authorization
-- **WebSocket connection management for all API operations**
-- **WebSocket message routing for real-time ACP streaming**
-- Rate limiting and request validation
-- CORS handling for WebSocket upgrade requests
-- API versioning via message schema
-
-**Dependencies:**
-- Task Service (internal)
-- Agent Manager (internal)
-- Orchestrator (internal - WebSocket message routing)
-- PostgreSQL (for user auth)
-
----
-
-### 2. Task Service
-**Port:** 8081
-**Purpose:** Manage kanban tasks and boards
-
-**Responsibilities:**
-- CRUD operations for tasks and boards
-- Task state transitions
-- Task priority and scheduling metadata
-- Publish task events to NATS
-
-**Dependencies:**
-- PostgreSQL (tasks, boards, task_events tables)
-- NATS (event publishing)
-
-**Events Published:**
-- `task.created`
-- `task.updated`
-- `task.state_changed`
-- `task.deleted`
-
----
### 3. Orchestrator Service
**Port:** 8082
diff --git a/docs/add-agent-cli.md b/docs/add-agent-cli.md
new file mode 100644
index 0000000000..b4a262525e
--- /dev/null
+++ b/docs/add-agent-cli.md
@@ -0,0 +1,241 @@
+# Adding a New Agent CLI Integration
+
+This guide explains how to add support for a new coding agent CLI to Kandev.
+
+Agents are defined entirely in Go. The backend discovers them through a registry, and the frontend picks them up automatically via the API - **no frontend changes are needed**.
+
+For a clean reference implementation, see [`gemini.go`](../apps/backend/internal/agent/agents/gemini.go) (ACP protocol). For a CLI-only TUI agent, see the `TUIAgent` declarative type in [`tui_agent.go`](../apps/backend/internal/agent/agents/tui_agent.go).
+
+---
+
+## CLI-Only Agent (Passthrough Only) - Recommended for TUI tools
+
+If an agent has a CLI but no structured protocol (no JSON-RPC, no streaming API), use the **`TUIAgent` declarative type**. The agent runs in a terminal session and input/output passes through a WebSocket - users interact with the full agent TUI directly.
+
+This is the simplest path. A complete agent definition is ~20 lines.
+
+### Step 1: Create the agent file
+
+Create `apps/backend/internal/agent/agents/{agent_id}.go`:
+
+```go
+package agents
+
+func NewClaude() *TUIAgent {
+ return NewTUIAgent(TUIAgentConfig{
+ AgentID: "claude",
+ AgentName: "Claude",
+ Command: "claude",
+ Desc: "Claude Code - an agentic coding tool by Anthropic.",
+
+ // Optional: let users select a model in profile settings.
+ // The {model} placeholder is replaced with the user's chosen model at launch.
+ // Omit this if the CLI doesn't accept a model flag.
+ ModelFlag: NewParam("--model", "{model}"),
+ })
+}
+```
+
+Logos are optional - the UI shows a blank placeholder when none is provided. To add them, embed SVGs and pass `LogoLight` / `LogoDark`:
+
+```go
+package agents
+
+import _ "embed"
+
+//go:embed logos/claude_light.svg
+var claudeLogoLight []byte
+
+//go:embed logos/claude_dark.svg
+var claudeLogoDark []byte
+
+func NewClaude() *TUIAgent {
+ return NewTUIAgent(TUIAgentConfig{
+ AgentID: "claude",
+ AgentName: "Claude",
+ Command: "claude",
+ Desc: "Claude Code - an agentic coding tool by Anthropic.",
+ ModelFlag: NewParam("--model", "{model}"),
+ LogoLight: claudeLogoLight,
+ LogoDark: claudeLogoDark,
+ })
+}
+```
+
+`TUIAgentConfig` fields (all optional fields have sensible defaults):
+
+| Field | Required | Description |
+|-------|----------|-------------|
+| `AgentID` | Yes | Unique lowercase identifier (e.g. `"claude"`) |
+| `AgentName` | Yes | Display name (e.g. `"Claude"`) |
+| `Command` | Yes | Binary name to run (e.g. `"claude"`) |
+| `Desc` | Yes | Description shown in the UI |
+| `ModelFlag` | No | Model CLI flag, e.g. `NewParam("--model", "{model}")`. Lets users pick a model in profile settings. Omit if the CLI doesn't support model selection. |
+| `CommandArgs` | No | Extra args appended after `Command` (e.g. `[]string{"--debug"}`) |
+| `WaitForTerm` | No | When `true`, delays process start until the terminal WebSocket connects and sends its first resize event. Use this for TUI apps that need accurate terminal dimensions on startup (e.g. ncurses-based UIs). Most agents don't need this. Defaults to `false`. |
+| `LogoLight` / `LogoDark` | No | Embedded SVG bytes for light/dark themes. The UI shows a blank placeholder if omitted. |
+| `DetectOpts` | No | Custom detection options. Defaults to `WithCommand(Command)` which checks if the binary is in `$PATH`. |
+
+### Step 2: Register the agent
+
+Add your agent to `LoadDefaults()` in [`apps/backend/internal/agent/registry/registry.go`](../apps/backend/internal/agent/registry/registry.go):
+
+```go
+func (r *Registry) LoadDefaults() {
+ all := []agents.Agent{
+ // ... existing agents ...
+ agents.NewMyAgent(), // add here
+ }
+ // ...
+}
+```
+
+### Step 3: Build and test
+
+```bash
+make -C apps/backend build && make -C apps/backend test && make -C apps/backend lint
+```
+
+Note: passthrough-only agents provide terminal interaction only. There are no structured chat messages, tool call visibility, or progress tracking in the chat UI.
+
+---
+
+## Adding a New Agent (Full Integration)
+
+For agents with a structured protocol (JSON-RPC, streaming JSON, etc.), implement the full [`Agent`](../apps/backend/internal/agent/agents/agent.go) interface.
+
+### Step 1: Create the agent definition file
+
+Create `apps/backend/internal/agent/agents/{agent_id}.go`.
+
+This file implements the [`Agent`](../apps/backend/internal/agent/agents/agent.go) interface. See the interface definition in `agent.go` for the full contract.
+
+Embed `StandardPassthrough` to get CLI passthrough support for free:
+
+```go
+type MyAgent struct {
+ StandardPassthrough
+}
+
+func NewMyAgent() *MyAgent {
+ return &MyAgent{
+ StandardPassthrough: StandardPassthrough{
+ PermSettings: myAgentPermSettings,
+ Cfg: PassthroughConfig{
+ Supported: true,
+ Label: "CLI Passthrough",
+ Description: "Show terminal directly instead of chat interface",
+ PassthroughCmd: NewCommand("myagent"),
+ ModelFlag: NewParam("--model", "{model}"),
+ PromptFlag: NewParam("--prompt", "{prompt}"),
+ IdleTimeout: 3 * time.Second,
+ BufferMaxBytes: DefaultBufferMaxBytes,
+ },
+ },
+ }
+}
+```
+
+Key implementation notes:
+
+- **`BuildCommand()`** returns the CLI command for structured protocol mode (the agent communicates via stdin/stdout with a protocol adapter). Include the protocol flag here (e.g. `--experimental-acp`, `--stream-json`).
+- **`Runtime()`** returns configuration used when running the agent in Docker or as a standalone process. Set `Protocol` to tell the adapter factory which transport to use.
+- **`IsInstalled()`** uses the `Detect()` helper with `WithFileExists()` to check for known installation paths. Set `SupportsMCP` and `MCPConfigPaths` on the result.
+- **`ListModels()`** can return a static list or dynamically query the CLI. Use `execAndParse()` from `helpers.go` for dynamic model discovery.
+- Use `Cmd()` builder for constructing commands fluently: `Cmd("myagent", "--flag").Model(...).Settings(...).Build()`.
+
+### Step 2: Add logos (optional)
+
+Logos are optional - the UI shows a blank placeholder when none is provided. To add them, place SVGs in:
+
+```
+apps/backend/internal/agent/agents/logos/{agent_id}_light.svg
+apps/backend/internal/agent/agents/logos/{agent_id}_dark.svg
+```
+
+Embed them in your agent file:
+
+```go
+//go:embed logos/myagent_light.svg
+var myagentLogoLight []byte
+
+//go:embed logos/myagent_dark.svg
+var myagentLogoDark []byte
+```
+
+### Step 3: Register the agent
+
+Add your agent to `LoadDefaults()` in [`apps/backend/internal/agent/registry/registry.go`](../apps/backend/internal/agent/registry/registry.go):
+
+```go
+func (r *Registry) LoadDefaults() {
+ all := []agents.Agent{
+ // ... existing agents ...
+ agents.NewMyAgent(), // add here
+ }
+ // ...
+}
+```
+
+That's it for registration. The frontend discovers agents through the API automatically.
+
+### Step 4: Choose a protocol
+
+Check [`apps/backend/pkg/agent/protocol.go`](../apps/backend/pkg/agent/protocol.go) for available protocols:
+
+| Protocol | Constant | Transport | Used By |
+|----------|----------|-----------|---------|
+| ACP | `ProtocolACP` | JSON-RPC 2.0 over stdin/stdout | Gemini, Auggie |
+| stream-json | `ProtocolClaudeCode` | Streaming JSON over stdin/stdout | Claude Code |
+| Codex | `ProtocolCodex` | JSON-RPC variant over stdin/stdout | Codex |
+| OpenCode | `ProtocolOpenCode` | REST/SSE over HTTP | OpenCode |
+| Copilot | `ProtocolCopilot` | Go SDK (JSON-RPC internally) | GitHub Copilot |
+| Amp | `ProtocolAmp` | Streaming JSON over stdin/stdout | Amp |
+
+**If an existing protocol fits your agent**, reuse it - set `Protocol` in `Runtime()`. The adapter factory in `factory.go` selects the right transport automatically based on this value.
+
+**If you need a new protocol**, you'll need to:
+
+1. Add a new `Protocol` constant in `apps/backend/pkg/agent/protocol.go`
+2. Create a transport adapter in `apps/backend/internal/agentctl/server/adapter/transport/{protocol}/`
+3. Add the case to the factory switch in [`apps/backend/internal/agentctl/server/adapter/factory.go`](../apps/backend/internal/agentctl/server/adapter/factory.go)
+
+### Step 5: Build and test
+
+```bash
+make -C apps/backend build && make -C apps/backend test && make -C apps/backend lint
+```
+
+Start the dev server and verify:
+- The agent appears in the agent selector in the UI
+- Agent execution works (start a task, send a prompt)
+- Passthrough mode works (toggle to CLI passthrough in the session)
+- Model listing works
+
+---
+
+## Updating an Existing Agent
+
+| Change | Where |
+|--------|-------|
+| Update CLI version | `BuildCommand()` and `Runtime().Cmd` in the agent file |
+| Add/remove models | `ListModels()` - update static list or dynamic command |
+| Update permissions | `PermissionSettings()` variable and `PassthroughConfig` |
+| Update Docker image | `Runtime()` - change `Image`/`Tag` fields |
+| Change protocol | `Runtime().Protocol` in the agent file |
+
+---
+
+## File Reference
+
+| File | When to modify |
+|------|---------------|
+| `apps/backend/internal/agent/agents/{agent_id}.go` | Always - agent definition |
+| `apps/backend/internal/agent/agents/logos/{agent_id}_{light,dark}.svg` | Optional - agent logos (UI shows blank placeholder if omitted) |
+| `apps/backend/internal/agent/registry/registry.go` | Always - register in `LoadDefaults()` |
+| `apps/backend/internal/agent/agents/tui_agent.go` | Never - provides `TUIAgent` declarative type (read-only reference) |
+| `apps/backend/pkg/agent/protocol.go` | Only if adding a new protocol |
+| `apps/backend/internal/agentctl/server/adapter/factory.go` | Only if adding a new protocol |
+| `apps/backend/internal/agentctl/server/adapter/transport/{proto}/` | Only if adding a new protocol |
+| `apps/backend/internal/agent/agents/passthrough.go` | Never - provides `StandardPassthrough` (read-only reference) |
+| `apps/backend/internal/agent/agents/agent.go` | Never - defines the `Agent` interface (read-only reference) |
diff --git a/docs/db.md b/docs/db.md
index e6ca816794..20c73950b1 100644
--- a/docs/db.md
+++ b/docs/db.md
@@ -21,7 +21,7 @@ database:
path: ./kandev.db
```
-No additional setup is needed — the database file is created automatically on first run.
+No additional setup is needed - the database file is created automatically on first run.
## PostgreSQL
@@ -71,4 +71,4 @@ KANDEV_DATABASE_PASSWORD=your-password \
kandev
```
-Tables are created automatically on first run — no manual migrations needed.
+Tables are created automatically on first run - no manual migrations needed.
diff --git a/docs/git-operations.md b/docs/git-operations.md
index c250b62b86..705b68686c 100644
--- a/docs/git-operations.md
+++ b/docs/git-operations.md
@@ -14,7 +14,7 @@ Git operations are executed **in agentctl**, not in the backend. This ensures co
| **Push** | Worktree → Remote | Push local commits to remote |
| **Rebase** | Base → Worktree | Rebase worktree branch onto base branch (rewrites history) |
| **Merge** | Base → Worktree | Merge base branch into worktree branch (creates merge commit) |
-| **Abort** | — | Abort an in-progress merge or rebase |
+| **Abort** | - | Abort an in-progress merge or rebase |
## Architecture
diff --git a/docs/roadmap.md b/docs/roadmap.md
new file mode 100644
index 0000000000..823cbb3d98
--- /dev/null
+++ b/docs/roadmap.md
@@ -0,0 +1,21 @@
+# Roadmap
+
+High-level direction for the project. This is not a commitment - priorities shift as we learn more.
+
+## Now
+
+- Stability and bug fixes across all agent integrations
+- Improved plan mode - richer plan editing, version history, plan diffs
+
+## Next
+
+- Github integration - link tasks to GitHub issues, PRs
+- Workflow templates sharing - import/export workflows
+- Mobile-friendly UI - orchestrate and review from your phone
+- Remote SSH runtime - run agents on remote servers over SSH
+- Linear / Jira integration - sync tasks and workflows with popular project management tools
+
+## Later
+
+- Kubernetes operator - auto-scaling agent workloads in a cluster
+- Analytics dashboard - agent performance, cost tracking, success rates
diff --git a/docs/stats.md b/docs/stats.md
index 1f401c27cd..7f478c22a2 100644
--- a/docs/stats.md
+++ b/docs/stats.md
@@ -2,7 +2,7 @@
## Overview
-The stats system provides workspace-level analytics about tasks, agent sessions, repository activity, and git commits. It computes everything on-demand from existing tables — there are no dedicated stats tables.
+The stats system provides workspace-level analytics about tasks, agent sessions, repository activity, and git commits. It computes everything on-demand from existing tables - there are no dedicated stats tables.
**Route**: `/stats` (with `?range=week|month|all`)
@@ -101,10 +101,10 @@ Key indexes: tasks(workspace_id, created_at), task_sessions(task_id, started_at)
## Known Limitations
-1. **No caching** — every page load runs all 7 queries
-2. **No real-time updates** — single fetch on page load, no WebSocket/polling
-3. **Git tracking gaps** — only commits made during task sessions are tracked
-4. **Duration precision** — `julianday()` returns float, cast to int64 (minor precision loss)
-5. **Incomplete turns** — turns with NULL `completed_at` count in totals but contribute 0ms to duration
-6. **"Month" = 30 days** — not a calendar month
-7. **Sequential queries** — the 7 repo calls in the handler run sequentially, not in parallel
+1. **No caching** - every page load runs all 7 queries
+2. **No real-time updates** - single fetch on page load, no WebSocket/polling
+3. **Git tracking gaps** - only commits made during task sessions are tracked
+4. **Duration precision** - `julianday()` returns float, cast to int64 (minor precision loss)
+5. **Incomplete turns** - turns with NULL `completed_at` count in totals but contribute 0ms to duration
+6. **"Month" = 30 days** - not a calendar month
+7. **Sequential queries** - the 7 repo calls in the handler run sequentially, not in parallel
diff --git a/docs/workflow-tips.md b/docs/workflow-tips.md
new file mode 100644
index 0000000000..fe374124b6
--- /dev/null
+++ b/docs/workflow-tips.md
@@ -0,0 +1,104 @@
+# Workflows
+
+Kandev ships with four workflow templates. Each defines a sequence of steps with automated transitions - agents start, stop, and move between steps based on events like entering a step, sending a message, or an agent completing its turn.
+
+You can use these as-is or **create custom workflows** tailored to your or your team.
+
+## Default Workflows
+
+### Kanban
+
+Classic kanban board with automated agent execution. Good for straightforward tasks where you want to assign work, let the agent run, and review the result.
+
+**Steps:** Backlog → In Progress → Review → Done
+
+| Step | What happens |
+|------|-------------|
+| **Backlog** | Tasks wait here. Sending a message moves the task to In Progress. |
+| **In Progress** | Agent starts automatically. When it completes, the task moves to Review. |
+| **Review** | You review the agent's work. Sending a message moves it back to In Progress for another iteration. |
+| **Done** | Final state. Sending a message reopens the task in In Progress. |
+
+**When to use it:**
+- Bug fixes with clear reproduction steps
+- Small, well-scoped features
+- Chores and refactoring tasks
+- Any task where you want a simple assign → run → review loop
+
+---
+
+### Plan & Build
+
+Two-phase workflow where the agent first creates a plan for your review, then implements it. The plan is saved as a structured document you can edit before the agent proceeds to implementation.
+
+**Steps:** Todo → Plan → Implementation → Done
+
+| Step | What happens |
+|------|-------------|
+| **Todo** | Backlog. Move tasks here when they're ready to be planned. |
+| **Plan** | Agent analyzes the task and creates a detailed implementation plan - requirements, files to modify, step-by-step approach, risks. Supports mermaid diagrams. The plan is saved via MCP tool and the agent stops for your review. You can edit the plan in the UI before moving forward. |
+| **Implementation** | Agent retrieves the plan (including your edits), acknowledges modifications, and implements step-by-step. Moves to Done on completion. |
+| **Done** | Final state. |
+
+**When to use it:**
+- Features that benefit from upfront design
+- Tasks where you want to steer the approach before code is written
+- Larger changes spanning multiple files
+- When working with less familiar codebases where you want to validate the agent's understanding first
+
+---
+
+### Architecture
+
+Focused on design and architecture. The agent creates technical designs for your review - no implementation happens in this workflow. Useful for capturing architectural decisions before any code is written.
+
+**Steps:** Ideas → Planning → Review → Approved
+
+| Step | What happens |
+|------|-------------|
+| **Ideas** | Backlog of architectural ideas and proposals. |
+| **Planning** | Agent analyzes the task, asks clarifying questions, and produces an architectural design with mermaid diagrams. Saves the design and stops for your review. |
+| **Review** | You review the design. Sending a message moves it back to Planning for revisions. |
+| **Approved** | Design is accepted. Ready for implementation (in a separate task/workflow). |
+
+**When to use it:**
+- System design and technical RFCs
+- Evaluating approaches before committing to implementation
+- Cross-team architectural proposals
+- Breaking down large projects into implementable pieces
+
+---
+
+### PR Review
+
+Track pull requests through automated code review. The agent reviews changed files and produces structured findings.
+
+**Steps:** Waiting → Review → Done
+
+| Step | What happens |
+|------|-------------|
+| **Waiting** | PR queue. Sending a message starts the review process. |
+| **Review** | Agent reviews the changed files in the git worktree. If there are uncommitted changes, it reviews those; otherwise, it reviews commits that diverged from the main branch. Findings are organized into four categories: **BUG**, **IMPROVEMENT**, **NITPICK**, **PERFORMANCE** - each with file:line references. |
+| **Done** | Review complete. Sending a message moves it back to Review for another pass. |
+
+**When to use it:**
+- Automated first-pass code review before human review
+- Catching bugs, performance issues, and style problems early
+- Reviewing agent-generated PRs from other workflows
+- Supplementing human review on large changesets
+
+---
+
+## Tips
+
+### Repository-Level Agent Configuration
+
+Each repository should maintain its own agent configuration - `CLAUDE.md`, `AGENTS.md`, custom skills, MCP servers, and any agent-specific instructions. This keeps agent behavior consistent with the codebase it's working on, regardless of who triggers the task or which workflow runs it.
+
+When an agent is assigned a task in a repository, it picks up that repository's configuration automatically. Coding standards, project context, and tooling constraints travel with the code, not with the platform.
+
+### Cross-Repository Workflows
+
+Workflow pipelines often span multiple repositories - a backend change triggers a client update, an infrastructure change follows a service deployment. Each step in the pipeline runs an agent inside a specific repository, and that agent should use the repository's own AI harness (prompts, rules, skills) rather than a shared global config.
+
+This keeps each agent grounded in the right context. A backend agent follows backend conventions, a frontend agent follows frontend conventions, even when they're part of the same pipeline.