feat(todo): allow several in_progress todos at once
Remove the single-in_progress cap from todo_write execute validation and the durable-log invariant so a task list can mirror genuinely parallel work (concurrent subagents, background commands). Update the tool description to instruct marking every actively worked task in_progress, refresh the tool catalog and keyless snapshot expected outputs, and record the decision in a new Agent Note superseding the original cap.
This commit is contained in:
@@ -17,11 +17,12 @@ function event(todos: unknown): SessionEvent {
|
||||
}
|
||||
|
||||
describe('todo snapshot invariants', () => {
|
||||
it('accepts a unique whole-list snapshot with one active item', async () => {
|
||||
it('accepts a unique whole-list snapshot, including several active items', async () => {
|
||||
const ctx = await setup()
|
||||
expect(() => { ctx.emit('session/event', {} as Session, event([
|
||||
{ content: 'Inspect state', status: 'completed' },
|
||||
{ content: 'Apply fix', status: 'in_progress' },
|
||||
{ content: 'Watch background build', status: 'in_progress' },
|
||||
{ content: 'Run checks', status: 'pending' },
|
||||
])) }).not.toThrow()
|
||||
})
|
||||
@@ -36,7 +37,6 @@ describe('todo snapshot invariants', () => {
|
||||
[[{ content: 'same', status: 'pending' }, { content: 'same', status: 'completed' }], /repeats content/],
|
||||
[[{ content: 'task', status: 42 }], /unknown status/],
|
||||
[[{ content: 'task', status: 'paused' }], /unknown status/],
|
||||
[[{ content: 'one', status: 'in_progress' }, { content: 'two', status: 'in_progress' }], /at most one/],
|
||||
])('rejects an incoherent durable todo snapshot', async (todos, message) => {
|
||||
const ctx = await setup()
|
||||
expect(() => { ctx.emit('session/event', {} as Session, event(todos)) }).toThrow(message)
|
||||
|
||||
@@ -122,10 +122,27 @@ describe('dsh-tool-todo', () => {
|
||||
expect(result.isError).toBe(true)
|
||||
})
|
||||
|
||||
it('accepts several in_progress items at once (parallel work)', async () => {
|
||||
const ctx = await setup()
|
||||
const agent = agentWithSession('parallel')
|
||||
const todos: TodoItem[] = [
|
||||
{ content: 'run subagent a', status: 'in_progress' },
|
||||
{ content: 'run subagent b', status: 'in_progress' },
|
||||
{ content: 'merge results', status: 'pending' },
|
||||
]
|
||||
const result = await callTodo(ctx, { todos }, { agent })
|
||||
expect(result.isError).toBe(false)
|
||||
if (result.isError) throw new Error('expected todo_write success')
|
||||
expect(result.value).toEqual({
|
||||
todos,
|
||||
counts: { pending: 1, inProgress: 2, completed: 0 },
|
||||
})
|
||||
expect(agent.session.events.findLast(e => e.type === 'todo/write')!.data.todos).toEqual(todos)
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ label: 'empty content', todos: [{ content: ' ', status: 'pending' }], fragment: 'non-empty' },
|
||||
{ label: 'duplicate content', todos: [{ content: 'dup', status: 'pending' }, { content: 'dup', status: 'completed' }], fragment: 'duplicate' },
|
||||
{ label: 'two in_progress', todos: [{ content: 'a', status: 'in_progress' }, { content: 'b', status: 'in_progress' }], fragment: 'in_progress' },
|
||||
])('rejects $label as an isError result', async ({ todos, fragment }) => {
|
||||
const ctx = await setup()
|
||||
const result = await callTodo(ctx, { todos })
|
||||
|
||||
Reference in New Issue
Block a user