feat: Remove checkpoint task types — per-phase review is sufficient
Checkpoint tasks (human-verify, decision, human-action) silently blocked auto-dispatch with no UI to resolve them. Per-phase review + initiative review already cover human verification, making checkpoints redundant. Removed from: schema, dispatch manager, tRPC validators, detail prompt, frontend types, tests, and docs.
This commit is contained in:
@@ -13,7 +13,7 @@ ${CODEBASE_EXPLORATION}
|
|||||||
|
|
||||||
<output_format>
|
<output_format>
|
||||||
Write one file per task to \`.cw/output/tasks/{id}.md\`:
|
Write one file per task to \`.cw/output/tasks/{id}.md\`:
|
||||||
- Frontmatter: \`title\`, \`category\` (execute|research|discuss|plan|detail|refine|verify|merge|review), \`type\` (auto|checkpoint:human-verify|checkpoint:decision|checkpoint:human-action), \`dependencies\` (list of task IDs that must complete before this task can start)
|
- Frontmatter: \`title\`, \`category\` (execute|research|discuss|plan|detail|refine|verify|merge|review), \`dependencies\` (list of task IDs that must complete before this task can start)
|
||||||
- Body: Detailed task description
|
- Body: Detailed task description
|
||||||
</output_format>
|
</output_format>
|
||||||
|
|
||||||
@@ -92,14 +92,6 @@ Each task is handled by a separate agent that must load the full codebase contex
|
|||||||
Bundle related changes into one task. "Add user validation" + "Add user API route" + "Add user route tests" is ONE task ("Add user creation endpoint with validation and tests"), not three.
|
Bundle related changes into one task. "Add user validation" + "Add user API route" + "Add user route tests" is ONE task ("Add user creation endpoint with validation and tests"), not three.
|
||||||
</task_sizing>
|
</task_sizing>
|
||||||
|
|
||||||
<checkpoint_tasks>
|
|
||||||
- \`checkpoint:human-verify\`: Visual changes, migrations, API contracts
|
|
||||||
- \`checkpoint:decision\`: Architecture choices affecting multiple phases
|
|
||||||
- \`checkpoint:human-action\`: External setup (DNS, credentials, third-party config)
|
|
||||||
|
|
||||||
~90% of tasks should be \`auto\`.
|
|
||||||
</checkpoint_tasks>
|
|
||||||
|
|
||||||
<existing_context>
|
<existing_context>
|
||||||
- Read ALL \`context/tasks/\` files before generating output
|
- Read ALL \`context/tasks/\` files before generating output
|
||||||
- Only create tasks for THIS phase (\`phase.md\`)
|
- Only create tasks for THIS phase (\`phase.md\`)
|
||||||
|
|||||||
@@ -71,13 +71,13 @@ describe('DrizzleTaskRepository', () => {
|
|||||||
it('should accept custom type and priority', async () => {
|
it('should accept custom type and priority', async () => {
|
||||||
const task = await taskRepo.create({
|
const task = await taskRepo.create({
|
||||||
phaseId: testPhaseId,
|
phaseId: testPhaseId,
|
||||||
name: 'Checkpoint Task',
|
name: 'High Priority Task',
|
||||||
type: 'checkpoint:human-verify',
|
type: 'auto',
|
||||||
priority: 'high',
|
priority: 'high',
|
||||||
order: 1,
|
order: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(task.type).toBe('checkpoint:human-verify');
|
expect(task.type).toBe('auto');
|
||||||
expect(task.priority).toBe('high');
|
expect(task.priority).toBe('high');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export const tasks = sqliteTable('tasks', {
|
|||||||
name: text('name').notNull(),
|
name: text('name').notNull(),
|
||||||
description: text('description'),
|
description: text('description'),
|
||||||
type: text('type', {
|
type: text('type', {
|
||||||
enum: ['auto', 'checkpoint:human-verify', 'checkpoint:decision', 'checkpoint:human-action'],
|
enum: ['auto'],
|
||||||
})
|
})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default('auto'),
|
.default('auto'),
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ export class DefaultDispatchManager implements DispatchManager {
|
|||||||
/**
|
/**
|
||||||
* Queue a task for dispatch.
|
* Queue a task for dispatch.
|
||||||
* Fetches task dependencies and adds to internal queue.
|
* Fetches task dependencies and adds to internal queue.
|
||||||
* Checkpoint tasks are queued but won't auto-dispatch.
|
|
||||||
*/
|
*/
|
||||||
async queue(taskId: string): Promise<void> {
|
async queue(taskId: string): Promise<void> {
|
||||||
// Fetch task to verify it exists and get priority
|
// Fetch task to verify it exists and get priority
|
||||||
@@ -100,7 +99,7 @@ export class DefaultDispatchManager implements DispatchManager {
|
|||||||
|
|
||||||
this.taskQueue.set(taskId, queuedTask);
|
this.taskQueue.set(taskId, queuedTask);
|
||||||
|
|
||||||
log.info({ taskId, priority: task.priority, isCheckpoint: this.isCheckpointTask(task) }, 'task queued');
|
log.info({ taskId, priority: task.priority }, 'task queued');
|
||||||
|
|
||||||
// Emit TaskQueuedEvent
|
// Emit TaskQueuedEvent
|
||||||
const event: TaskQueuedEvent = {
|
const event: TaskQueuedEvent = {
|
||||||
@@ -118,7 +117,6 @@ export class DefaultDispatchManager implements DispatchManager {
|
|||||||
/**
|
/**
|
||||||
* Get next dispatchable task.
|
* Get next dispatchable task.
|
||||||
* Returns task with all dependencies complete, highest priority first.
|
* Returns task with all dependencies complete, highest priority first.
|
||||||
* Checkpoint tasks are excluded (require human action).
|
|
||||||
*/
|
*/
|
||||||
async getNextDispatchable(): Promise<QueuedTask | null> {
|
async getNextDispatchable(): Promise<QueuedTask | null> {
|
||||||
const queuedTasks = Array.from(this.taskQueue.values());
|
const queuedTasks = Array.from(this.taskQueue.values());
|
||||||
@@ -127,7 +125,7 @@ export class DefaultDispatchManager implements DispatchManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter to only tasks with all dependencies complete and not checkpoint tasks
|
// Filter to only tasks with all dependencies complete
|
||||||
const readyTasks: QueuedTask[] = [];
|
const readyTasks: QueuedTask[] = [];
|
||||||
|
|
||||||
log.debug({ queueSize: queuedTasks.length }, 'evaluating dispatchable tasks');
|
log.debug({ queueSize: queuedTasks.length }, 'evaluating dispatchable tasks');
|
||||||
@@ -139,14 +137,8 @@ export class DefaultDispatchManager implements DispatchManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a checkpoint task (requires human action)
|
|
||||||
const task = await this.taskRepository.findById(qt.taskId);
|
|
||||||
if (task && this.isCheckpointTask(task)) {
|
|
||||||
log.debug({ taskId: qt.taskId, type: task.type }, 'skipping checkpoint task');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip planning-category tasks (handled by architect flow)
|
// Skip planning-category tasks (handled by architect flow)
|
||||||
|
const task = await this.taskRepository.findById(qt.taskId);
|
||||||
if (task && isPlanningCategory(task.category)) {
|
if (task && isPlanningCategory(task.category)) {
|
||||||
log.debug({ taskId: qt.taskId, category: task.category }, 'skipping planning-category task');
|
log.debug({ taskId: qt.taskId, category: task.category }, 'skipping planning-category task');
|
||||||
continue;
|
continue;
|
||||||
@@ -478,14 +470,6 @@ export class DefaultDispatchManager implements DispatchManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a task is a checkpoint task.
|
|
||||||
* Checkpoint tasks require human action and don't auto-dispatch.
|
|
||||||
*/
|
|
||||||
private isCheckpointTask(task: Task): boolean {
|
|
||||||
return task.type.startsWith('checkpoint:');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store the completing agent's result summary on the task record.
|
* Store the completing agent's result summary on the task record.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ describe('Detail Workflow E2E', () => {
|
|||||||
harness.setArchitectDetailComplete('detailer', [
|
harness.setArchitectDetailComplete('detailer', [
|
||||||
{ number: 1, name: 'Task 1', content: 'First task', type: 'auto', dependencies: [] },
|
{ number: 1, name: 'Task 1', content: 'First task', type: 'auto', dependencies: [] },
|
||||||
{ number: 2, name: 'Task 2', content: 'Second task', type: 'auto', dependencies: [1] },
|
{ number: 2, name: 'Task 2', content: 'Second task', type: 'auto', dependencies: [1] },
|
||||||
{ number: 3, name: 'Verify', content: 'Verify all', type: 'checkpoint:human-verify', dependencies: [2] },
|
{ number: 3, name: 'Verify', content: 'Verify all', type: 'auto', dependencies: [2] },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Resume with all answers
|
// Resume with all answers
|
||||||
@@ -261,7 +261,7 @@ describe('Detail Workflow E2E', () => {
|
|||||||
tasks: [
|
tasks: [
|
||||||
{ number: 1, name: 'Schema', description: 'Create tables', type: 'auto', dependencies: [] },
|
{ number: 1, name: 'Schema', description: 'Create tables', type: 'auto', dependencies: [] },
|
||||||
{ number: 2, name: 'API', description: 'Create endpoints', type: 'auto', dependencies: [1] },
|
{ number: 2, name: 'API', description: 'Create endpoints', type: 'auto', dependencies: [1] },
|
||||||
{ number: 3, name: 'Verify', description: 'Test flow', type: 'checkpoint:human-verify', dependencies: [2] },
|
{ number: 3, name: 'Verify', description: 'Test flow', type: 'auto', dependencies: [2] },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -271,33 +271,31 @@ describe('Detail Workflow E2E', () => {
|
|||||||
expect(tasks[0].name).toBe('Schema');
|
expect(tasks[0].name).toBe('Schema');
|
||||||
expect(tasks[1].name).toBe('API');
|
expect(tasks[1].name).toBe('API');
|
||||||
expect(tasks[2].name).toBe('Verify');
|
expect(tasks[2].name).toBe('Verify');
|
||||||
expect(tasks[2].type).toBe('checkpoint:human-verify');
|
expect(tasks[2].type).toBe('auto');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle all task types', async () => {
|
it('should create tasks with auto type', async () => {
|
||||||
const initiative = await harness.createInitiative('Task Types Test');
|
const initiative = await harness.createInitiative('Task Types Test');
|
||||||
const phases = await harness.createPhasesFromPlan(initiative.id, [
|
const phases = await harness.createPhasesFromPlan(initiative.id, [
|
||||||
{ name: 'Phase 1' },
|
{ name: 'Phase 1' },
|
||||||
]);
|
]);
|
||||||
const detailTask = await harness.createDetailTask(phases[0].id, 'Mixed Tasks');
|
const detailTask = await harness.createDetailTask(phases[0].id, 'Mixed Tasks');
|
||||||
|
|
||||||
// Create tasks with all types
|
|
||||||
await harness.caller.createChildTasks({
|
await harness.caller.createChildTasks({
|
||||||
parentTaskId: detailTask.id,
|
parentTaskId: detailTask.id,
|
||||||
tasks: [
|
tasks: [
|
||||||
{ number: 1, name: 'Auto Task', description: 'Automated work', type: 'auto' },
|
{ number: 1, name: 'Auto Task', description: 'Automated work', type: 'auto' },
|
||||||
{ number: 2, name: 'Human Verify', description: 'Visual check', type: 'checkpoint:human-verify', dependencies: [1] },
|
{ number: 2, name: 'Second Task', description: 'More work', type: 'auto', dependencies: [1] },
|
||||||
{ number: 3, name: 'Decision', description: 'Choose approach', type: 'checkpoint:decision', dependencies: [2] },
|
{ number: 3, name: 'Third Task', description: 'Even more', type: 'auto', dependencies: [2] },
|
||||||
{ number: 4, name: 'Human Action', description: 'Manual step', type: 'checkpoint:human-action', dependencies: [3] },
|
{ number: 4, name: 'Final Task', description: 'Last step', type: 'auto', dependencies: [3] },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const tasks = await harness.getChildTasks(detailTask.id);
|
const tasks = await harness.getChildTasks(detailTask.id);
|
||||||
expect(tasks).toHaveLength(4);
|
expect(tasks).toHaveLength(4);
|
||||||
expect(tasks[0].type).toBe('auto');
|
for (const task of tasks) {
|
||||||
expect(tasks[1].type).toBe('checkpoint:human-verify');
|
expect(task.type).toBe('auto');
|
||||||
expect(tasks[2].type).toBe('checkpoint:decision');
|
}
|
||||||
expect(tasks[3].type).toBe('checkpoint:human-action');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create task dependencies', async () => {
|
it('should create task dependencies', async () => {
|
||||||
@@ -346,7 +344,7 @@ describe('Detail Workflow E2E', () => {
|
|||||||
{ number: 1, name: 'Create user schema', content: 'Define User model', type: 'auto', dependencies: [] },
|
{ number: 1, name: 'Create user schema', content: 'Define User model', type: 'auto', dependencies: [] },
|
||||||
{ number: 2, name: 'Implement JWT', content: 'Token generation', type: 'auto', dependencies: [1] },
|
{ number: 2, name: 'Implement JWT', content: 'Token generation', type: 'auto', dependencies: [1] },
|
||||||
{ number: 3, name: 'Protected routes', content: 'Middleware', type: 'auto', dependencies: [2] },
|
{ number: 3, name: 'Protected routes', content: 'Middleware', type: 'auto', dependencies: [2] },
|
||||||
{ number: 4, name: 'Verify auth', content: 'Test login flow', type: 'checkpoint:human-verify', dependencies: [3] },
|
{ number: 4, name: 'Verify auth', content: 'Test login flow', type: 'auto', dependencies: [3] },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await harness.caller.spawnArchitectDetail({
|
await harness.caller.spawnArchitectDetail({
|
||||||
@@ -367,7 +365,7 @@ describe('Detail Workflow E2E', () => {
|
|||||||
{ number: 1, name: 'Create user schema', description: 'Define User model', type: 'auto', dependencies: [] },
|
{ number: 1, name: 'Create user schema', description: 'Define User model', type: 'auto', dependencies: [] },
|
||||||
{ number: 2, name: 'Implement JWT', description: 'Token generation', type: 'auto', dependencies: [1] },
|
{ number: 2, name: 'Implement JWT', description: 'Token generation', type: 'auto', dependencies: [1] },
|
||||||
{ number: 3, name: 'Protected routes', description: 'Middleware', type: 'auto', dependencies: [2] },
|
{ number: 3, name: 'Protected routes', description: 'Middleware', type: 'auto', dependencies: [2] },
|
||||||
{ number: 4, name: 'Verify auth', description: 'Test login flow', type: 'checkpoint:human-verify', dependencies: [3] },
|
{ number: 4, name: 'Verify auth', description: 'Test login flow', type: 'auto', dependencies: [3] },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -375,7 +373,7 @@ describe('Detail Workflow E2E', () => {
|
|||||||
const tasks = await harness.getChildTasks(detailTask.id);
|
const tasks = await harness.getChildTasks(detailTask.id);
|
||||||
expect(tasks).toHaveLength(4);
|
expect(tasks).toHaveLength(4);
|
||||||
expect(tasks[0].name).toBe('Create user schema');
|
expect(tasks[0].name).toBe('Create user schema');
|
||||||
expect(tasks[3].type).toBe('checkpoint:human-verify');
|
expect(tasks[3].type).toBe('auto');
|
||||||
|
|
||||||
// Agent should be idle
|
// Agent should be idle
|
||||||
const finalAgent = await harness.caller.getAgent({ name: 'detailer' });
|
const finalAgent = await harness.caller.getAgent({ name: 'detailer' });
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function phaseDispatchProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
number: z.number().int().positive(),
|
number: z.number().int().positive(),
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
type: z.enum(['auto', 'checkpoint:human-verify', 'checkpoint:decision', 'checkpoint:human-action']).default('auto'),
|
type: z.enum(['auto']).default('auto'),
|
||||||
dependencies: z.array(z.number().int().positive()).optional(),
|
dependencies: z.array(z.number().int().positive()).optional(),
|
||||||
})),
|
})),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export function taskProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
category: z.enum(['execute', 'research', 'discuss', 'plan', 'detail', 'refine', 'verify', 'merge', 'review']).optional(),
|
category: z.enum(['execute', 'research', 'discuss', 'plan', 'detail', 'refine', 'verify', 'merge', 'review']).optional(),
|
||||||
type: z.enum(['auto', 'checkpoint:human-verify', 'checkpoint:decision', 'checkpoint:human-action']).optional(),
|
type: z.enum(['auto']).optional(),
|
||||||
}))
|
}))
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const taskRepository = requireTaskRepository(ctx);
|
const taskRepository = requireTaskRepository(ctx);
|
||||||
@@ -88,7 +88,7 @@ export function taskProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
category: z.enum(['execute', 'research', 'discuss', 'plan', 'detail', 'refine', 'verify', 'merge', 'review']).optional(),
|
category: z.enum(['execute', 'research', 'discuss', 'plan', 'detail', 'refine', 'verify', 'merge', 'review']).optional(),
|
||||||
type: z.enum(['auto', 'checkpoint:human-verify', 'checkpoint:decision', 'checkpoint:human-action']).optional(),
|
type: z.enum(['auto']).optional(),
|
||||||
}))
|
}))
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
const taskRepository = requireTaskRepository(ctx);
|
const taskRepository = requireTaskRepository(ctx);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export interface SerializedTask {
|
|||||||
parentTaskId: string | null;
|
parentTaskId: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
type: "auto" | "checkpoint:human-verify" | "checkpoint:decision" | "checkpoint:human-action";
|
type: "auto";
|
||||||
category: string;
|
category: string;
|
||||||
priority: "low" | "medium" | "high";
|
priority: "low" | "medium" | "high";
|
||||||
status: "pending" | "in_progress" | "completed" | "blocked";
|
status: "pending" | "in_progress" | "completed" | "blocked";
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ All prompts follow a consistent tag ordering:
|
|||||||
|------|------|--------------------|
|
|------|------|--------------------|
|
||||||
| **execute** | `execute.ts` | `<task>`, `<execution_protocol>`, `<anti_patterns>`, `<scope_rules>` |
|
| **execute** | `execute.ts` | `<task>`, `<execution_protocol>`, `<anti_patterns>`, `<scope_rules>` |
|
||||||
| **plan** | `plan.ts` | `<phase_design>`, `<dependencies>`, `<file_ownership>`, `<specificity>`, `<existing_context>` |
|
| **plan** | `plan.ts` | `<phase_design>`, `<dependencies>`, `<file_ownership>`, `<specificity>`, `<existing_context>` |
|
||||||
| **detail** | `detail.ts` | `<task_body_requirements>`, `<file_ownership>`, `<task_sizing>`, `<checkpoint_tasks>`, `<existing_context>` |
|
| **detail** | `detail.ts` | `<task_body_requirements>`, `<file_ownership>`, `<task_sizing>`, `<existing_context>` |
|
||||||
| **discuss** | `discuss.ts` | `<analysis_method>`, `<question_quality>`, `<decision_quality>`, `<question_categories>`, `<rules>` |
|
| **discuss** | `discuss.ts` | `<analysis_method>`, `<question_quality>`, `<decision_quality>`, `<question_categories>`, `<rules>` |
|
||||||
| **refine** | `refine.ts` | `<improvement_priorities>`, `<rules>` |
|
| **refine** | `refine.ts` | `<improvement_priorities>`, `<rules>` |
|
||||||
| **chat** | `chat.ts` | `<chat_history>`, `<instruction>` — iterative refinement loop, uses action field (create/update/delete) in output files, signals "questions" after each change to stay alive |
|
| **chat** | `chat.ts` | `<chat_history>`, `<instruction>` — iterative refinement loop, uses action field (create/update/delete) in output files, signals "questions" after each change to stay alive |
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ All adapters use nanoid() for IDs, auto-manage timestamps, and use Drizzle's `.r
|
|||||||
| parentTaskId | text nullable self-ref FK (cascade) | decomposition hierarchy |
|
| parentTaskId | text nullable self-ref FK (cascade) | decomposition hierarchy |
|
||||||
| name | text NOT NULL | |
|
| name | text NOT NULL | |
|
||||||
| description | text nullable | |
|
| description | text nullable | |
|
||||||
| type | text enum | 'auto' \| 'checkpoint:human-verify' \| 'checkpoint:decision' \| 'checkpoint:human-action' |
|
| type | text enum | 'auto' |
|
||||||
| category | text enum | 'execute' \| 'research' \| 'discuss' \| 'plan' \| 'detail' \| 'refine' \| 'verify' \| 'merge' \| 'review' |
|
| category | text enum | 'execute' \| 'research' \| 'discuss' \| 'plan' \| 'detail' \| 'refine' \| 'verify' \| 'merge' \| 'review' |
|
||||||
| priority | text enum | 'low' \| 'medium' \| 'high' |
|
| priority | text enum | 'low' \| 'medium' \| 'high' |
|
||||||
| status | text enum | 'pending' \| 'in_progress' \| 'completed' \| 'blocked' |
|
| status | text enum | 'pending' \| 'in_progress' \| 'completed' \| 'blocked' |
|
||||||
|
|||||||
@@ -64,8 +64,7 @@ InitiativeReviewApprovedEvent { initiativeId, branch, strategy: 'push_branch' |
|
|||||||
2. **Dispatch** — `dispatchNext()` finds highest-priority task with all deps complete
|
2. **Dispatch** — `dispatchNext()` finds highest-priority task with all deps complete
|
||||||
3. **Context gathering** — Before spawn, `dispatchNext()` gathers initiative context (initiative, phase, tasks, pages) and passes as `inputContext` to the agent. Agents receive `.cw/input/task.md`, `initiative.md`, `phase.md`, `context/tasks/`, `context/phases/`, and `pages/`.
|
3. **Context gathering** — Before spawn, `dispatchNext()` gathers initiative context (initiative, phase, tasks, pages) and passes as `inputContext` to the agent. Agents receive `.cw/input/task.md`, `initiative.md`, `phase.md`, `context/tasks/`, `context/phases/`, and `pages/`.
|
||||||
4. **Priority order**: high > medium > low, then oldest first (FIFO within priority)
|
4. **Priority order**: high > medium > low, then oldest first (FIFO within priority)
|
||||||
5. **Checkpoint skip** — Tasks with type starting with `checkpoint:` skip auto-dispatch
|
5. **Planning skip** — Planning-category tasks (research, discuss, plan, detail, refine) skip auto-dispatch — they use the architect flow
|
||||||
6. **Planning skip** — Planning-category tasks (research, discuss, plan, detail, refine) skip auto-dispatch — they use the architect flow
|
|
||||||
7. **Summary propagation** — `completeTask()` reads the completing agent's `result.message` and stores it on the task's `summary` column. Dependent tasks see this summary in `context/tasks/<id>.md` frontmatter.
|
7. **Summary propagation** — `completeTask()` reads the completing agent's `result.message` and stores it on the task's `summary` column. Dependent tasks see this summary in `context/tasks/<id>.md` frontmatter.
|
||||||
8. **Spawn failure** — If `agentManager.spawn()` throws, the task is blocked via `blockTask()` with the error message. The dispatch cycle continues instead of crashing.
|
8. **Spawn failure** — If `agentManager.spawn()` throws, the task is blocked via `blockTask()` with the error message. The dispatch cycle continues instead of crashing.
|
||||||
9. **Retry blocked** — `retryBlockedTask(taskId)` resets a blocked task to pending and re-queues it. Exposed via tRPC `retryBlockedTask` mutation. The UI shows a Retry button in the task slide-over when status is `blocked`.
|
9. **Retry blocked** — `retryBlockedTask(taskId)` resets a blocked task to pending and re-queues it. Exposed via tRPC `retryBlockedTask` mutation. The UI shows a Retry button in the task slide-over when status is `blocked`.
|
||||||
|
|||||||
Reference in New Issue
Block a user