todo: migrate tool output to structured JSON schemas#2045
Conversation
Replace plain-text string output with proper JSON-structured responses for all todo tools. Each tool now has a typed OutputSchema matching its actual output: - create_todo: returns a Todo object - create_todos: returns CreateTodosOutput with created []Todo - update_todos: returns UpdateTodosOutput with updated/not_found - list_todos: returns ListTodosOutput with todos []Todo Extract addTodo and jsonResult helpers to reduce duplication. Fix jsonschema descriptions for consistency. Assisted-By: docker-agent
There was a problem hiding this comment.
🔍 cagent Review
Assessment: 🟢 APPROVE
The refactoring successfully migrates todo tool output to structured JSON schemas with proper error handling and helper function extraction. No critical or high-severity bugs were found.
One minor design inconsistency was noted for consideration:
Optional Improvement
While not blocking, there's a pattern inconsistency in the output structure design:
createTodo returns bare Todo while other tools use wrapper types
createTodoreturns:TododirectlycreateTodosreturns:CreateTodosOutput{Created: []Todo}updateTodosreturns:UpdateTodosOutput{...}listTodosreturns:ListTodosOutput{...}
Since this PR explicitly aims for "proper JSON-structured responses," consider wrapping createTodo's output in a dedicated type (e.g., CreateTodoOutput{Todo: Todo}) for consistency with the other tools.
The current implementation works correctly and tests pass—this is purely about maintaining a consistent pattern across all tool outputs.
| }) | ||
| } | ||
| func (h *todoHandler) createTodo(_ context.Context, params CreateTodoArgs) (*tools.ToolCallResult, error) { | ||
| return h.jsonResult(h.addTodo(params.Description)) |
There was a problem hiding this comment.
Optional Improvement: Output Structure Consistency
The createTodo function returns a bare Todo object, while all other tools wrap their outputs in dedicated types:
- ✅
createTodos→CreateTodosOutput{Created: []Todo} - ✅
updateTodos→UpdateTodosOutput{...} - ✅
listTodos→ListTodosOutput{...} ⚠️ createTodo→Todo(bare object)
Since this PR explicitly refactors for "proper JSON-structured responses," consider wrapping this output in a dedicated type (e.g., CreateTodoOutput{Todo: Todo}) for consistency.
Not blocking—the current implementation works correctly.
There was a problem hiding this comment.
Review Summary
Assessment: 🟡 NEEDS ATTENTION
The migration to structured JSON schemas is well-executed with proper helper functions. However, there is a logic bug in the updateTodos function where the auto-clear mechanism can trigger even when some updates fail with NotFound errors.
Critical Issues
None
Notable Issues
- Auto-clear triggers on partial failures (pkg/tools/builtin/todo.go:236)
- The storage auto-clear can execute even when some todo updates fail with NotFound errors, which is inconsistent behavior
Summary
The refactoring successfully introduces type-safe JSON schemas for all todo tool outputs and reduces code duplication through helper functions. The identified issue should be addressed to ensure auto-clear only happens when ALL requested updates succeed.
| return res, nil | ||
| } | ||
|
|
||
| if h.allCompleted() { |
There was a problem hiding this comment.
MEDIUM: Auto-clear triggers even when some updates fail
The allCompleted() check executes even when len(result.NotFound) > 0, meaning some updates failed. This can cause storage to be cleared after a partially failed operation.
Example scenario:
- You have 2 existing todos, both already marked as completed
- You attempt to update all 3 todos (including a non-existent one) to completed
- Result: 2 updated successfully, 1 NotFound
- Bug: The code skips the error block (because
len(result.Updated) > 0), thenallCompleted()returns true and clears storage
Impact: A partially failed update operation inappropriately triggers auto-clear behavior.
Recommendation: Only trigger auto-clear when ALL requested updates succeeded:
if len(result.NotFound) == 0 && h.allCompleted() {
h.storage.Clear()
}
Replace plain-text string output with proper JSON-structured responses for all todo tools. Each tool now has a typed
OutputSchemamatching its actual output:create_todo: returns aTodoobjectcreate_todos: returnsCreateTodosOutputwithcreated []Todoupdate_todos: returnsUpdateTodosOutputwithupdated/not_foundlist_todos: returnsListTodosOutputwithtodos []TodoOther improvements
addTodohelper to deduplicate todo creation logicjsonResulthelper to deduplicate JSON marshaling +ToolCallResultconstructionTodotype ascreate_todooutput (eliminates redundantCreateTodoOutputstruct)jsonschemadescriptions for consistency ("New status" → "Status of the todo item", add missing space)updateTodoserror path to reusejsonResultinstead of duplicating marshal logicrequireMetatest helper to reduce assertion boilerplate