diff --git a/docs/dispatch-events.md b/docs/dispatch-events.md index a67649f..0ab305d 100644 --- a/docs/dispatch-events.md +++ b/docs/dispatch-events.md @@ -56,8 +56,9 @@ AccountCredentialsRefreshedEvent { accountId, expiresAt, previousExpiresAt? } 2. **Dispatch** — `dispatchNext()` finds highest-priority task with all deps complete 3. **Priority order**: high > medium > low, then oldest first (FIFO within priority) 4. **Checkpoint skip** — Tasks with type starting with `checkpoint:` skip auto-dispatch -5. **Approval check** — `completeTask()` checks `requiresApproval` (task-level, then initiative-level) -6. **Approval flow** — If approval required: status → `pending_approval`, emit `task:pending_approval` +5. **Planning skip** — Planning-category tasks (research, discuss, plan, detail, refine) skip auto-dispatch — they use the architect flow +6. **Approval check** — `completeTask()` checks `requiresApproval` (task-level, then initiative-level) +7. **Approval flow** — If approval required: status → `pending_approval`, emit `task:pending_approval` ### DispatchManager Methods @@ -77,7 +78,7 @@ AccountCredentialsRefreshedEvent { accountId, expiresAt, previousExpiresAt? } 1. **Queue** — `queuePhase(phaseId)` validates phase is approved, gets dependencies 2. **Dispatch** — `dispatchNextPhase()` finds phase with all deps complete -3. **Auto-queue tasks** — When phase starts, all pending tasks are queued +3. **Auto-queue tasks** — When phase starts, pending execution tasks are queued (planning-category tasks excluded) 4. **Events** — `phase:queued`, `phase:started`, `phase:completed`, `phase:blocked` ### PhaseDispatchManager Methods diff --git a/docs/server-api.md b/docs/server-api.md index bab3637..82b8b13 100644 --- a/docs/server-api.md +++ b/docs/server-api.md @@ -117,9 +117,9 @@ Each procedure uses `require*Repository(ctx)` helpers that throw `TRPCError(INTE | Procedure | Type | Description | |-----------|------|-------------| | spawnArchitectDiscuss | mutation | Discussion agent | -| spawnArchitectPlan | mutation | Plan agent (generates phases). Passes full initiative context (existing phases, tasks, pages) | +| spawnArchitectPlan | mutation | Plan agent (generates phases). Passes initiative context (phases, execution tasks only, pages) | | spawnArchitectRefine | mutation | Refine agent (generates proposals) | -| spawnArchitectDetail | mutation | Detail agent (generates tasks). Passes full initiative context (sibling phases, tasks, pages) | +| spawnArchitectDetail | mutation | Detail agent (generates tasks). Passes initiative context (phases, execution tasks only, pages) | ### Dispatch | Procedure | Type | Description | diff --git a/src/dispatch/manager.ts b/src/dispatch/manager.ts index ff6cbbd..59a1e06 100644 --- a/src/dispatch/manager.ts +++ b/src/dispatch/manager.ts @@ -135,6 +135,12 @@ export class DefaultDispatchManager implements DispatchManager { continue; } + // Skip planning-category tasks (handled by architect flow) + if (task && isPlanningCategory(task.category)) { + log.debug({ taskId: qt.taskId, category: task.category }, 'skipping planning-category task'); + continue; + } + readyTasks.push(qt); } diff --git a/src/dispatch/phase-manager.ts b/src/dispatch/phase-manager.ts index 112c921..b9c6a15 100644 --- a/src/dispatch/phase-manager.ts +++ b/src/dispatch/phase-manager.ts @@ -20,7 +20,7 @@ import type { InitiativeRepository } from '../db/repositories/initiative-reposit import type { ProjectRepository } from '../db/repositories/project-repository.js'; import type { BranchManager } from '../git/branch-manager.js'; import type { PhaseDispatchManager, DispatchManager, QueuedPhase, PhaseDispatchResult } from './types.js'; -import { phaseBranchName } from '../git/branch-naming.js'; +import { phaseBranchName, isPlanningCategory } from '../git/branch-naming.js'; import { ensureProjectClone } from '../git/project-clones.js'; import { createModuleLogger } from '../logger/index.js'; @@ -191,10 +191,10 @@ export class DefaultPhaseDispatchManager implements PhaseDispatchManager { // Remove from queue (now being worked on) this.phaseQueue.delete(nextPhase.phaseId); - // Auto-queue pending tasks for this phase + // Auto-queue pending execution tasks for this phase (skip planning-category tasks) const phaseTasks = await this.taskRepository.findByPhaseId(nextPhase.phaseId); for (const task of phaseTasks) { - if (task.status === 'pending') { + if (task.status === 'pending' && !isPlanningCategory(task.category)) { await this.dispatchManager.queue(task.id); } } diff --git a/src/trpc/routers/architect.ts b/src/trpc/routers/architect.ts index 4441920..9d437ed 100644 --- a/src/trpc/routers/architect.ts +++ b/src/trpc/routers/architect.ts @@ -18,6 +18,7 @@ import { buildRefinePrompt, buildDetailPrompt, } from '../../agent/prompts/index.js'; +import { isPlanningCategory } from '../../git/branch-naming.js'; import type { PhaseRepository } from '../../db/repositories/phase-repository.js'; import type { TaskRepository } from '../../db/repositories/task-repository.js'; import type { PageRepository } from '../../db/repositories/page-repository.js'; @@ -68,7 +69,10 @@ async function gatherInitiativeContext( } } - return { phases, tasks: allTasks, pages }; + // Only include implementation tasks in agent context — planning tasks are irrelevant noise + const implementationTasks = allTasks.filter(t => !isPlanningCategory(t.category)); + + return { phases, tasks: implementationTasks, pages }; } export function architectProcedures(publicProcedure: ProcedureBuilder) {