feat: add 'blocked' status to tasks and todos (#22735)

This commit is contained in:
anj-s
2026-03-17 16:24:26 -07:00
committed by GitHub
parent e1eefffcf1
commit b8719bcd47
13 changed files with 64 additions and 9 deletions

View File

@@ -13,7 +13,8 @@ updates to the CLI interface.
- `todos` (array of objects, required): The complete list of tasks. Each object
includes:
- `description` (string): Technical description of the task.
- `status` (enum): `pending`, `in_progress`, `completed`, or `cancelled`.
- `status` (enum): `pending`, `in_progress`, `completed`, `cancelled`, or
`blocked`.
## Technical behavior

View File

@@ -15,6 +15,7 @@ describe('<ChecklistItem />', () => {
{ status: 'in_progress', label: 'Doing this' },
{ status: 'completed', label: 'Done this' },
{ status: 'cancelled', label: 'Skipped this' },
{ status: 'blocked', label: 'Blocked this' },
] as ChecklistItemData[])('renders %s item correctly', async (item) => {
const { lastFrame, waitUntilReady } = render(<ChecklistItem item={item} />);
await waitUntilReady();

View File

@@ -13,7 +13,8 @@ export type ChecklistStatus =
| 'pending'
| 'in_progress'
| 'completed'
| 'cancelled';
| 'cancelled'
| 'blocked';
export interface ChecklistItemData {
status: ChecklistStatus;
@@ -48,6 +49,12 @@ const ChecklistStatusDisplay: React.FC<{ status: ChecklistStatus }> = ({
</Text>
);
case 'blocked':
return (
<Text color={theme.status.warning} aria-label="Blocked">
</Text>
);
default:
checkExhaustive(status);
}
@@ -70,6 +77,7 @@ export const ChecklistItem: React.FC<ChecklistItemProps> = ({
return theme.text.accent;
case 'completed':
case 'cancelled':
case 'blocked':
return theme.text.secondary;
case 'pending':
return theme.text.primary;

View File

@@ -1,5 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`<ChecklistItem /> > renders { status: 'blocked', label: 'Blocked this' } item correctly 1`] = `
"⛔ Blocked this
"
`;
exports[`<ChecklistItem /> > renders { status: 'cancelled', label: 'Skipped this' } item correctly 1`] = `
"✗ Skipped this
"

View File

@@ -22,6 +22,7 @@ export const TASK_TYPE_LABELS: Record<TaskType, string> = {
export enum TaskStatus {
OPEN = 'open',
IN_PROGRESS = 'in_progress',
BLOCKED = 'blocked',
CLOSED = 'closed',
}
export const TaskStatusSchema = z.nativeEnum(TaskStatus);

View File

@@ -697,6 +697,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps
- in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time.
- completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed.
- cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled.
- blocked: Subtask is blocked and cannot be completed at this time.
## Methodology for using this tool
@@ -766,6 +767,7 @@ The agent did not use the todo list because this task could be completed by a ti
"in_progress",
"completed",
"cancelled",
"blocked",
],
"type": "string",
},
@@ -1451,6 +1453,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps
- in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time.
- completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed.
- cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled.
- blocked: Subtask is blocked and cannot be completed at this time.
## Methodology for using this tool
@@ -1520,6 +1523,7 @@ The agent did not use the todo list because this task could be completed by a ti
"in_progress",
"completed",
"cancelled",
"blocked",
],
"type": "string",
},

View File

@@ -543,6 +543,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps
- in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time.
- completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed.
- cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled.
- blocked: Subtask is blocked and cannot be completed at this time.
## Methodology for using this tool
@@ -609,7 +610,13 @@ The agent did not use the todo list because this task could be completed by a ti
[TODOS_ITEM_PARAM_STATUS]: {
type: 'string',
description: 'The current status of the task.',
enum: ['pending', 'in_progress', 'completed', 'cancelled'],
enum: [
'pending',
'in_progress',
'completed',
'cancelled',
'blocked',
],
},
},
required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS],

View File

@@ -518,6 +518,7 @@ DO NOT use this tool for simple tasks that can be completed in less than 2 steps
- in_progress: Marked just prior to beginning work on a given subtask. You should only have one subtask as in_progress at a time.
- completed: Subtask was successfully completed with no errors or issues. If the subtask required more steps to complete, update the todo list with the subtasks. All steps should be identified as completed only when they are completed.
- cancelled: As you update the todo list, some tasks are not required anymore due to the dynamic nature of the task. In this case, mark the subtasks as cancelled.
- blocked: Subtask is blocked and cannot be completed at this time.
## Methodology for using this tool
@@ -584,7 +585,13 @@ The agent did not use the todo list because this task could be completed by a ti
[TODOS_ITEM_PARAM_STATUS]: {
type: 'string',
description: 'The current status of the task.',
enum: ['pending', 'in_progress', 'completed', 'cancelled'],
enum: [
'pending',
'in_progress',
'completed',
'cancelled',
'blocked',
],
},
},
required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS],

View File

@@ -823,7 +823,12 @@ export type ToolResultDisplay =
| TodoList
| SubagentProgress;
export type TodoStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled';
export type TodoStatus =
| 'pending'
| 'in_progress'
| 'completed'
| 'cancelled'
| 'blocked';
export interface Todo {
description: string;

View File

@@ -222,15 +222,23 @@ describe('Tracker Tools Integration', () => {
status: TaskStatus.IN_PROGRESS,
dependencies: [],
};
const t4 = {
id: 't4',
title: 'T4',
type: TaskType.TASK,
status: TaskStatus.BLOCKED,
dependencies: [],
};
const mockService = {
listTasks: async () => [t1, t2, t3],
listTasks: async () => [t1, t2, t3, t4],
} as unknown as TrackerService;
const display = await buildTodosReturnDisplay(mockService);
expect(display.todos).toEqual([
{ description: `task: T3 (t3)`, status: 'in_progress' },
{ description: `task: T2 (t2)`, status: 'pending' },
{ description: `task: T4 (t4)`, status: 'blocked' },
{ description: `task: T1 (t1)`, status: 'completed' },
]);
});

View File

@@ -48,10 +48,11 @@ export async function buildTodosReturnDisplay(
}
}
const statusOrder = {
const statusOrder: Record<TaskStatus, number> = {
[TaskStatus.IN_PROGRESS]: 0,
[TaskStatus.OPEN]: 1,
[TaskStatus.CLOSED]: 2,
[TaskStatus.BLOCKED]: 2,
[TaskStatus.CLOSED]: 3,
};
const sortTasks = (a: TrackerTask, b: TrackerTask) => {
@@ -80,6 +81,8 @@ export async function buildTodosReturnDisplay(
status = 'in_progress';
} else if (task.status === TaskStatus.CLOSED) {
status = 'completed';
} else if (task.status === TaskStatus.BLOCKED) {
status = 'blocked';
}
const indent = ' '.repeat(depth);
@@ -585,6 +588,7 @@ class TrackerVisualizeInvocation extends BaseToolInvocation<
const statusEmojis: Record<TaskStatus, string> = {
open: '⭕',
in_progress: '🚧',
blocked: '⛔',
closed: '✅',
};

View File

@@ -19,6 +19,7 @@ describe('WriteTodosTool', () => {
{ description: 'Task 1', status: 'pending' },
{ description: 'Task 2', status: 'in_progress' },
{ description: 'Task 3', status: 'completed' },
{ description: 'Task 4', status: 'blocked' },
],
};
await expect(tool.buildAndExecute(params, signal)).resolves.toBeDefined();
@@ -96,13 +97,15 @@ describe('WriteTodosTool', () => {
{ description: 'First task', status: 'completed' },
{ description: 'Second task', status: 'in_progress' },
{ description: 'Third task', status: 'pending' },
{ description: 'Fourth task', status: 'blocked' },
],
};
const result = await tool.buildAndExecute(params, signal);
const expectedOutput = `Successfully updated the todo list. The current list is now:
1. [completed] First task
2. [in_progress] Second task
3. [pending] Third task`;
3. [pending] Third task
4. [blocked] Fourth task`;
expect(result.llmContent).toBe(expectedOutput);
expect(result.returnDisplay).toEqual(params);
});

View File

@@ -22,6 +22,7 @@ const TODO_STATUSES = [
'in_progress',
'completed',
'cancelled',
'blocked',
] as const;
export interface WriteTodosToolParams {