diff --git a/crates/tinyagents-graph/src/todos/test.rs b/crates/tinyagents-graph/src/todos/test.rs index dc7a5505..a3a0efdd 100644 --- a/crates/tinyagents-graph/src/todos/test.rs +++ b/crates/tinyagents-graph/src/todos/test.rs @@ -355,6 +355,33 @@ mod tool_tests { .expect("successful todo result has a JSON payload") } + /// The description is what makes a model treat the list as bookkeeping + /// rather than as the work itself: without the "same response carries the + /// next call" rule, models write a list, stop, and wait to be prompted; + /// without the "only after its work has run" rule they tick items off + /// ahead of doing them. + #[test] + fn description_states_when_an_item_may_be_completed_and_that_writing_is_not_working() { + let tool = TodoTool::new(store()); + let description = Tool::description(&tool); + assert!( + description.contains("only after its work has actually run"), + "an item is completed after its result exists: {description}" + ); + assert!( + description.contains("bookkeeping, not work"), + "writing the list is not the work: {description}" + ); + assert!( + description.contains("immediately carry out the next step"), + "the model advances after bookkeeping: {description}" + ); + assert!( + description.contains("next model turn"), + "sequential providers may advance on their next turn: {description}" + ); + } + #[test] fn todo_tools_builds_a_single_tool() { let tools = todo_tools(store()); diff --git a/crates/tinyagents-graph/src/todos/tool.rs b/crates/tinyagents-graph/src/todos/tool.rs index f84e9a2f..dcabec4e 100644 --- a/crates/tinyagents-graph/src/todos/tool.rs +++ b/crates/tinyagents-graph/src/todos/tool.rs @@ -25,9 +25,12 @@ const TODO_TOOL_NAME: &str = "todo"; const TODO_DESCRIPTION: &str = "Your todo list for this thread. Pass the complete list every \ time; it replaces what was there. Use it for work with 3+ steps: write the steps up front, \ - keep exactly one `in_progress`, mark each `completed` the moment it is done. Omit `todos` \ - to read the current list. The list is bound automatically to the current thread — do not \ - pass a thread id."; + keep exactly one `in_progress`, mark each `completed` only after its work has actually run \ + and its result is in this conversation. Writing the list is bookkeeping, not work: after \ + updating it, immediately carry out the next step. Providers that cannot issue parallel tool \ + calls may make that call in the next model turn, and one update per response is enough. Omit \ + `todos` to read the current list. The list is bound \ + automatically to the current thread — do not pass a thread id."; /// The `todo` harness [`Tool`], backed by a [`Store`](tinyagents_harness::store::Store). pub struct TodoTool {