From 7b155ecc284f373b011a3162d363e30c69a9e7e9 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 30 Jan 2026 20:22:17 +0100 Subject: [PATCH] docs(05): create phase 5 plan - task dispatch Phase 05: Task Dispatch - 5 plans in 3 waves - 3 parallel (Wave 1), 1 sequential (Wave 2), 1 sequential (Wave 3) - Ready for execution Requirements covered: - AGENT-06: Message queue for agent questions - TASK-01: Task status visibility - TASK-04: Dependency-ordered dispatch - TASK-05: Work queue for agents --- .../phases/05-task-dispatch/05-01-PLAN.md | 116 ++++++++++++ .../phases/05-task-dispatch/05-02-PLAN.md | 122 ++++++++++++ .../phases/05-task-dispatch/05-03-PLAN.md | 165 +++++++++++++++++ .../phases/05-task-dispatch/05-04-PLAN.md | 174 ++++++++++++++++++ .../phases/05-task-dispatch/05-05-PLAN.md | 174 ++++++++++++++++++ 5 files changed, 751 insertions(+) create mode 100644 .planning/phases/05-task-dispatch/05-01-PLAN.md create mode 100644 .planning/phases/05-task-dispatch/05-02-PLAN.md create mode 100644 .planning/phases/05-task-dispatch/05-03-PLAN.md create mode 100644 .planning/phases/05-task-dispatch/05-04-PLAN.md create mode 100644 .planning/phases/05-task-dispatch/05-05-PLAN.md diff --git a/.planning/phases/05-task-dispatch/05-01-PLAN.md b/.planning/phases/05-task-dispatch/05-01-PLAN.md new file mode 100644 index 0000000..2bf1c08 --- /dev/null +++ b/.planning/phases/05-task-dispatch/05-01-PLAN.md @@ -0,0 +1,116 @@ +--- +phase: 05-task-dispatch +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: [src/db/schema.ts, src/db/repositories/message-repository.ts, src/db/repositories/drizzle/message.ts, src/db/repositories/drizzle/message.test.ts, src/db/repositories/index.ts, src/db/repositories/drizzle/index.ts] +autonomous: true +--- + + +Add message schema and repository for persisting agent questions. + +Purpose: AGENT-06 requires agents to surface questions to users. When an agent pauses on AskUserQuestion, the question needs to persist in the database so users can query pending messages and respond later. +Output: messages table, MessageRepository port/adapter with full CRUD and tests. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +@src/db/schema.ts +@src/db/repositories/task-repository.ts +@src/db/repositories/drizzle/task.ts +@src/db/repositories/drizzle/agent.ts +@src/events/types.ts + + + + + + Task 1: Add messages table to schema + src/db/schema.ts + +Add messages table after agents table. Schema: +- id: text primary key +- agentId: text NOT NULL foreign key to agents.id with SET NULL on delete (message persists even if agent deleted) +- type: text enum ['question', 'info', 'error'] NOT NULL default 'question' +- content: text NOT NULL (the question or message text) +- status: text enum ['pending', 'read', 'responded'] NOT NULL default 'pending' +- response: text nullable (user's response when status is 'responded') +- createdAt: integer timestamp NOT NULL +- updatedAt: integer timestamp NOT NULL + +Add relations: message belongs to agent (nullable after delete). +Export Message and NewMessage types. + +Follow existing patterns: use sqliteTable, relations, InferSelectModel/InferInsertModel. + + npm run build passes, no TypeScript errors + messages table in schema with types exported + + + + Task 2: Create MessageRepository port and adapter + src/db/repositories/message-repository.ts, src/db/repositories/drizzle/message.ts, src/db/repositories/drizzle/message.test.ts, src/db/repositories/index.ts, src/db/repositories/drizzle/index.ts + +Create MessageRepository following TaskRepository pattern exactly: + +1. Port interface (message-repository.ts): + - CreateMessageData type (omit id, createdAt, updatedAt) + - UpdateMessageData type (partial of create data) + - MessageRepository interface with: + - create(data: CreateMessageData): Promise + - findById(id: string): Promise + - findByAgentId(agentId: string): Promise + - findPending(): Promise (status = 'pending') + - update(id: string, data: UpdateMessageData): Promise + - delete(id: string): Promise + +2. Drizzle adapter (drizzle/message.ts): + - DrizzleMessageRepository class implementing port + - Use nanoid for id generation + - Fetch after insert to get schema defaults + - Order by createdAt DESC for lists + +3. Tests (drizzle/message.test.ts): + - Use createTestDatabase helper + - Test create, findById, findByAgentId, findPending, update, delete + - Test pending filter returns only pending messages + - Test response field update when marking as responded + +4. Re-export from index files (same pattern as AgentRepository) + + npm test -- src/db/repositories/drizzle/message.test.ts passes + MessageRepository port/adapter with passing tests + + + + + +Before declaring plan complete: +- [ ] npm run build succeeds +- [ ] npm test passes all tests including new message tests +- [ ] messages table accessible via DrizzleMessageRepository +- [ ] findPending returns only pending messages + + + + +- messages table added to schema with proper types +- MessageRepository port interface defined +- DrizzleMessageRepository adapter implemented +- All tests pass +- Exports wired up in index files + + + +After completion, create `.planning/phases/05-task-dispatch/05-01-SUMMARY.md` + diff --git a/.planning/phases/05-task-dispatch/05-02-PLAN.md b/.planning/phases/05-task-dispatch/05-02-PLAN.md new file mode 100644 index 0000000..0894b42 --- /dev/null +++ b/.planning/phases/05-task-dispatch/05-02-PLAN.md @@ -0,0 +1,122 @@ +--- +phase: 05-task-dispatch +plan: 02 +type: execute +wave: 1 +depends_on: [] +files_modified: [src/trpc/router.ts, src/trpc/context.ts, src/cli/index.ts] +autonomous: true +--- + + +Add task visibility via tRPC and CLI. + +Purpose: TASK-01 requires users to see status of all tasks at a glance. Expose existing TaskRepository through tRPC endpoints and CLI commands for task listing and status viewing. +Output: tRPC task procedures, CLI `cw task list` and `cw task get` commands. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +@src/trpc/router.ts +@src/trpc/context.ts +@src/cli/index.ts +@src/db/repositories/task-repository.ts +@src/db/schema.ts + + + + + + Task 1: Add task tRPC procedures + src/trpc/router.ts, src/trpc/context.ts + +1. Add TaskRepository to TRPCContext (optional, same pattern as agentManager): + - Add taskRepository?: TaskRepository to context type + - Add requireTaskRepository helper function + +2. Add task procedures to appRouter: + + listTasks: publicProcedure + .input(z.object({ planId: z.string().optional() })) + .query(...) + - If planId provided, return tasks for that plan (ordered by order field) + - If no planId, return all tasks (need to add findAll to TaskRepository first - OR use a simple "list all" query) + - Actually, for simplicity: require planId. Users query by plan. + - Return array of Task objects + + getTask: publicProcedure + .input(z.object({ id: z.string() })) + .query(...) + - Return single task or throw NOT_FOUND + + updateTaskStatus: publicProcedure + .input(z.object({ + id: z.string(), + status: z.enum(['pending', 'in_progress', 'completed', 'blocked']) + })) + .mutation(...) + - Update task status + - Return updated task + +Follow existing patterns from agent procedures. Use Zod schemas for input validation. + + npm run build passes, npm test -- src/trpc/router.test.ts passes + Task tRPC procedures available: listTasks, getTask, updateTaskStatus + + + + Task 2: Add task CLI commands + src/cli/index.ts + +Add task subcommands to the existing 'task' command group: + +1. cw task list --plan + - Call listTasks tRPC procedure + - Display tasks as table: name, status, type, priority + - Show count of pending/in_progress/completed + +2. cw task get + - Call getTask tRPC procedure + - Display full task details: id, name, description, type, priority, status, order, timestamps + +3. cw task status + - Call updateTaskStatus tRPC procedure + - Validate status is one of: pending, in_progress, completed, blocked + - Confirm status update + +Replace the placeholder "not implemented" action with real commands. +Follow existing CLI patterns (error handling, client creation, output formatting). + + cw task list --plan test-id shows error (expected - no server), cw task --help shows commands + CLI task commands functional: list, get, status + + + + + +Before declaring plan complete: +- [ ] npm run build succeeds +- [ ] npm test passes +- [ ] cw task --help shows list, get, status commands +- [ ] Task procedures added to router + + + + +- tRPC task procedures (listTasks, getTask, updateTaskStatus) added +- TaskRepository optional in context (same pattern as AgentManager) +- CLI task commands: list, get, status +- All existing tests still pass + + + +After completion, create `.planning/phases/05-task-dispatch/05-02-SUMMARY.md` + diff --git a/.planning/phases/05-task-dispatch/05-03-PLAN.md b/.planning/phases/05-task-dispatch/05-03-PLAN.md new file mode 100644 index 0000000..b68dd47 --- /dev/null +++ b/.planning/phases/05-task-dispatch/05-03-PLAN.md @@ -0,0 +1,165 @@ +--- +phase: 05-task-dispatch +plan: 03 +type: execute +wave: 1 +depends_on: [] +files_modified: [src/dispatch/types.ts, src/dispatch/index.ts, src/events/types.ts, src/events/index.ts] +autonomous: true +--- + + +Define DispatchManager port interface and dispatch events. + +Purpose: TASK-04 and TASK-05 require dependency-ordered task dispatch and work queue. Define the port interface that the adapter will implement, plus domain events for dispatch lifecycle. +Output: DispatchManager port interface, dispatch domain events, exported from dispatch module. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +@src/agent/types.ts +@src/git/types.ts +@src/events/types.ts +@src/db/schema.ts + + + + + + Task 1: Create DispatchManager port interface + src/dispatch/types.ts, src/dispatch/index.ts + +Create src/dispatch/ directory with types.ts and index.ts. + +In types.ts, define: + +1. QueuedTask type: + - taskId: string + - priority: 'low' | 'medium' | 'high' + - queuedAt: Date + - dependsOn: string[] (task IDs that must complete first) + +2. DispatchResult type: + - success: boolean + - taskId: string + - agentId?: string (assigned agent if dispatched) + - reason?: string (why dispatch failed if not success) + +3. DispatchManager port interface: + /** + * DispatchManager Port Interface + * + * Manages task dispatch queue with dependency ordering. + * + * Covers requirements: + * - TASK-04: Dependency-ordered dispatch + * - TASK-05: Work queue for available agents + */ + interface DispatchManager { + /** + * Queue a task for dispatch. + * Task will be dispatched when all dependencies complete. + */ + queue(taskId: string): Promise; + + /** + * Get next dispatchable task. + * Returns task with all dependencies complete, highest priority first. + * Returns null if no tasks ready. + */ + getNextDispatchable(): Promise; + + /** + * Dispatch next available task to an agent. + * Finds available agent, assigns task, spawns agent. + * Returns dispatch result. + */ + dispatchNext(): Promise; + + /** + * Mark a task as complete. + * Triggers re-evaluation of dependent tasks. + */ + completeTask(taskId: string): Promise; + + /** + * Mark a task as blocked. + * Task will not be dispatched until unblocked. + */ + blockTask(taskId: string, reason: string): Promise; + + /** + * Get current queue state. + * Returns all queued tasks with their dispatch readiness. + */ + getQueueState(): Promise<{ + queued: QueuedTask[]; + ready: QueuedTask[]; + blocked: Array<{ taskId: string; reason: string }>; + }>; + } + +Export all types from index.ts. + + npm run build passes, types importable from src/dispatch + DispatchManager port interface defined and exported + + + + Task 2: Add dispatch domain events + src/events/types.ts, src/events/index.ts + +Add dispatch events to events/types.ts, following existing patterns: + +1. TaskQueuedEvent: + type: 'task:queued' + payload: { taskId: string; priority: string; dependsOn: string[] } + +2. TaskDispatchedEvent: + type: 'task:dispatched' + payload: { taskId: string; agentId: string; agentName: string } + +3. TaskCompletedEvent: + type: 'task:completed' + payload: { taskId: string; agentId: string; success: boolean; message: string } + +4. TaskBlockedEvent: + type: 'task:blocked' + payload: { taskId: string; reason: string; blockedBy?: string[] } + +Add all four to DomainEventMap union type. +No changes needed to index.ts (types auto-exported). + + npm run build passes, events compile without errors + Dispatch events added to DomainEventMap + + + + + +Before declaring plan complete: +- [ ] npm run build succeeds +- [ ] npm test passes +- [ ] DispatchManager interface importable from src/dispatch +- [ ] Dispatch events in DomainEventMap + + + + +- src/dispatch/types.ts with DispatchManager interface +- QueuedTask, DispatchResult types defined +- Four dispatch events added to events/types.ts +- All types properly exported + + + +After completion, create `.planning/phases/05-task-dispatch/05-03-SUMMARY.md` + diff --git a/.planning/phases/05-task-dispatch/05-04-PLAN.md b/.planning/phases/05-task-dispatch/05-04-PLAN.md new file mode 100644 index 0000000..99038b9 --- /dev/null +++ b/.planning/phases/05-task-dispatch/05-04-PLAN.md @@ -0,0 +1,174 @@ +--- +phase: 05-task-dispatch +plan: 04 +type: execute +wave: 2 +depends_on: ["05-01", "05-03"] +files_modified: [src/dispatch/manager.ts, src/dispatch/manager.test.ts, src/dispatch/index.ts] +autonomous: true +--- + + +Implement DispatchManager adapter with dependency checking and queue management. + +Purpose: TASK-04 requires dependency-ordered dispatch, TASK-05 requires work queue. Implement the DispatchManager adapter that checks task dependencies before dispatch and manages the work queue. +Output: DispatchManager adapter with dependency algorithm, queue management, and tests. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +@.planning/phases/05-task-dispatch/05-01-SUMMARY.md +@.planning/phases/05-task-dispatch/05-03-SUMMARY.md + +@src/dispatch/types.ts +@src/db/schema.ts +@src/db/repositories/task-repository.ts +@src/agent/types.ts +@src/events/types.ts + + + + + + Task 1: Implement dependency checking algorithm + src/dispatch/manager.ts + +Create DefaultDispatchManager class implementing DispatchManager interface. + +Constructor dependencies (inject via constructor): +- taskRepository: TaskRepository +- messageRepository: MessageRepository (from 05-01) +- agentManager: AgentManager +- eventBus: EventBus + +Implement core dependency algorithm: + +1. queue(taskId): + - Fetch task and its dependencies from task_dependencies table + - Add to internal queue (use Map) + - Emit TaskQueuedEvent + +2. getNextDispatchable(): + - Get all queued tasks + - For each, check if ALL dependencies have status='completed' + - Filter to only ready tasks (all deps complete) + - Sort by priority (high > medium > low), then by queuedAt (oldest first) + - Return first or null + +3. completeTask(taskId): + - Update task status to 'completed' via taskRepository + - Remove from queue + - Emit TaskCompletedEvent + - Note: dependent tasks automatically become ready on next getNextDispatchable call + +4. blockTask(taskId, reason): + - Update task status to 'blocked' via taskRepository + - Emit TaskBlockedEvent + +Use simple in-memory queue (Map). Production could use persistent queue but in-memory is fine for v1. + + npm run build passes + Dependency checking algorithm implemented + + + + Task 2: Implement dispatchNext and queue state + src/dispatch/manager.ts + +Complete DispatchManager implementation: + +1. dispatchNext(): + - Call getNextDispatchable() to get next task + - If null, return { success: false, taskId: '', reason: 'No dispatchable tasks' } + - Find available agent (status='idle') via agentManager.list() + - If no available agent, return { success: false, taskId, reason: 'No available agents' } + - Get task details from taskRepository + - Spawn agent with task: agentManager.spawn({ name: generateAgentName(), taskId, prompt: task.description || task.name }) + - Update task status to 'in_progress' + - Emit TaskDispatchedEvent + - Return { success: true, taskId, agentId } + +2. getQueueState(): + - Return { queued: [...allQueuedTasks], ready: [...readyTasks], blocked: [...blockedTasks] } + - Uses internal queue state + +Helper: generateAgentName(): + - Generate unique name for agent (e.g., 'agent-${nanoid(6)}' or use Vancouver neighborhood names: 'gastown', 'yaletown', 'kitsilano', etc.) + - For v1: simple 'agent-${taskId.slice(0,6)}' is fine + +Handle edge cases: +- Task not found: throw error +- Circular dependencies: not checked in v1 (assume valid DAG) + + npm run build passes + dispatchNext and getQueueState implemented + + + + Task 3: Add tests and wire up exports + src/dispatch/manager.test.ts, src/dispatch/index.ts + +Create manager.test.ts with tests: + +1. Setup: + - Create in-memory database with createTestDatabase helper + - Create mock/real repositories and EventBus + - Create test tasks with dependencies + +2. Test cases: + - queue adds task to queue and emits event + - getNextDispatchable returns null when queue empty + - getNextDispatchable returns task when dependencies complete + - getNextDispatchable returns null when dependencies incomplete + - getNextDispatchable respects priority ordering + - completeTask updates status and emits event + - blockTask updates status and emits event + - dispatchNext returns failure when no tasks ready + - dispatchNext returns failure when no agents available + - getQueueState returns correct state + +3. Test dependency scenario: + - Task A (no deps) + - Task B (depends on A) + - Task C (depends on A) + - Queue all three + - Only A should be dispatchable + - Complete A + - B and C should become dispatchable + +Update index.ts to export DefaultDispatchManager class. + + npm test -- src/dispatch/manager.test.ts passes + Tests pass, DispatchManager exported + + + + + +Before declaring plan complete: +- [ ] npm run build succeeds +- [ ] npm test passes all tests +- [ ] Dependency ordering works correctly +- [ ] Queue state accurately reflects task readiness + + + + +- DefaultDispatchManager implements DispatchManager interface +- Dependency checking prevents premature dispatch +- Priority ordering respected +- All dispatch events emitted correctly +- Tests cover core scenarios + + + +After completion, create `.planning/phases/05-task-dispatch/05-04-SUMMARY.md` + diff --git a/.planning/phases/05-task-dispatch/05-05-PLAN.md b/.planning/phases/05-task-dispatch/05-05-PLAN.md new file mode 100644 index 0000000..aaedf4d --- /dev/null +++ b/.planning/phases/05-task-dispatch/05-05-PLAN.md @@ -0,0 +1,174 @@ +--- +phase: 05-task-dispatch +plan: 05 +type: execute +wave: 3 +depends_on: ["05-02", "05-04"] +files_modified: [src/trpc/router.ts, src/trpc/context.ts, src/cli/index.ts] +autonomous: true +--- + + +Add message and dispatch tRPC procedures and CLI commands. + +Purpose: Complete AGENT-06 (message visibility) and TASK-04/TASK-05 (dispatch control) with user-facing interfaces. Users can view pending agent questions and control task dispatch. +Output: tRPC message and dispatch procedures, CLI commands for messages and dispatch. + + + +@~/.claude/get-shit-done/workflows/execute-plan.md +@~/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md + +@.planning/phases/05-task-dispatch/05-01-SUMMARY.md +@.planning/phases/05-task-dispatch/05-02-SUMMARY.md +@.planning/phases/05-task-dispatch/05-04-SUMMARY.md + +@src/trpc/router.ts +@src/trpc/context.ts +@src/cli/index.ts +@src/db/repositories/message-repository.ts +@src/dispatch/types.ts + + + + + + Task 1: Add message and dispatch tRPC procedures + src/trpc/router.ts, src/trpc/context.ts + +1. Add to TRPCContext: + - messageRepository?: MessageRepository + - dispatchManager?: DispatchManager + - Add requireMessageRepository and requireDispatchManager helpers + +2. Add message procedures to appRouter: + + listMessages: publicProcedure + .input(z.object({ agentId: z.string().optional(), status: z.enum(['pending', 'read', 'responded']).optional() })) + .query(...) + - If agentId, filter by agent + - If status, filter by status + - Return array of Message objects + + getMessage: publicProcedure + .input(z.object({ id: z.string() })) + .query(...) + - Return single message or throw NOT_FOUND + + respondToMessage: publicProcedure + .input(z.object({ id: z.string(), response: z.string() })) + .mutation(...) + - Update message with response and status='responded' + - Return updated message + +3. Add dispatch procedures: + + queueTask: publicProcedure + .input(z.object({ taskId: z.string() })) + .mutation(...) + - Call dispatchManager.queue(taskId) + - Return { success: true } + + dispatchNext: publicProcedure + .mutation(...) + - Call dispatchManager.dispatchNext() + - Return DispatchResult + + getQueueState: publicProcedure + .query(...) + - Call dispatchManager.getQueueState() + - Return queue state object + + completeTask: publicProcedure + .input(z.object({ taskId: z.string() })) + .mutation(...) + - Call dispatchManager.completeTask(taskId) + - Return { success: true } + + npm run build passes + Message and dispatch tRPC procedures added + + + + Task 2: Add message and dispatch CLI commands + src/cli/index.ts + +1. Add message command group: + + const messageCommand = program + .command('message') + .description('View agent messages and questions'); + + cw message list [--agent ] [--status ] + - Call listMessages tRPC + - Display as table: id (short), agent, type, content (truncated), status, createdAt + - Show "(pending)" count prominently + + cw message read + - Call getMessage tRPC + - Display full message content + - If pending, show prompt to respond + + cw message respond + - Call respondToMessage tRPC + - Confirm response recorded + - Suggest resuming agent if appropriate + +2. Add dispatch command group: + + const dispatchCommand = program + .command('dispatch') + .description('Control task dispatch queue'); + + cw dispatch queue + - Call queueTask tRPC + - Confirm task queued + + cw dispatch next + - Call dispatchNext tRPC + - Show result: which task dispatched to which agent, or why it failed + + cw dispatch status + - Call getQueueState tRPC + - Show: queued count, ready count, blocked count + - List ready tasks with their priority + + cw dispatch complete + - Call completeTask tRPC + - Confirm task completed + +Follow existing CLI patterns for error handling and output formatting. + + cw message --help and cw dispatch --help show commands + Message and dispatch CLI commands functional + + + + + +Before declaring plan complete: +- [ ] npm run build succeeds +- [ ] npm test passes +- [ ] cw message --help shows list, read, respond +- [ ] cw dispatch --help shows queue, next, status, complete +- [ ] All tRPC procedures added + + + + +- Message tRPC procedures: listMessages, getMessage, respondToMessage +- Dispatch tRPC procedures: queueTask, dispatchNext, getQueueState, completeTask +- CLI message commands: list, read, respond +- CLI dispatch commands: queue, next, status, complete +- All requirements (AGENT-06, TASK-01, TASK-04, TASK-05) have user-facing interfaces + + + +After completion, create `.planning/phases/05-task-dispatch/05-05-SUMMARY.md` +