From b6f7c2b994745272fbbdab39927a47de667023d3 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Tue, 22 Sep 2026 19:34:32 +0300 Subject: [PATCH 1/3] fix(todos): clarify todo tool description for step completion and response coupling Reword the tool description to make it explicit that a step should only be marked `completed` after its work has actually run and its result is present in the conversation. Also add guidance that writing the list is bookkeeping, not work, and that the same response updating the list must carry the tool call for the next step, with one update per response being sufficient. Auto-committed-on: dragonfly Co-authored-by: Medulla --- crates/tinyagents-graph/src/todos/tool.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/tinyagents-graph/src/todos/tool.rs b/crates/tinyagents-graph/src/todos/tool.rs index f84e9a2f..1a3aa142 100644 --- a/crates/tinyagents-graph/src/todos/tool.rs +++ b/crates/tinyagents-graph/src/todos/tool.rs @@ -25,9 +25,11 @@ 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: the same \ + response that updates it must also carry the tool call that does the next step, 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 { From 5d9f4507ab0c6bd6de549445bdc73a12a3314773 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Tue, 22 Sep 2026 19:34:59 +0300 Subject: [PATCH 2/3] test(todos): add test for description rules on completion and bookkeeping Adds a test that verifies the tool description enforces two key rules: an item may only be completed after its work has actually run, and writing the list is bookkeeping rather than the work itself. The test also checks that the description requires the same response to carry the next tool call, preventing models from stopping after listing todos. Auto-committed-on: dragonfly Co-authored-by: Medulla --- crates/tinyagents-graph/src/todos/test.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/tinyagents-graph/src/todos/test.rs b/crates/tinyagents-graph/src/todos/test.rs index dc7a5505..5b758d60 100644 --- a/crates/tinyagents-graph/src/todos/test.rs +++ b/crates/tinyagents-graph/src/todos/test.rs @@ -355,6 +355,29 @@ 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("must also carry the tool call that does the next step"), + "the same response carries the next step: {description}" + ); + } + #[test] fn todo_tools_builds_a_single_tool() { let tools = todo_tools(store()); From 6e2680fee1cf427ce4177b27c67dfe3d47b1453d Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Tue, 22 Sep 2026 20:10:58 +0300 Subject: [PATCH 3/3] fix(todos): support sequential tool calling Co-authored-by: Medulla --- crates/tinyagents-graph/src/todos/test.rs | 8 ++++++-- crates/tinyagents-graph/src/todos/tool.rs | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/tinyagents-graph/src/todos/test.rs b/crates/tinyagents-graph/src/todos/test.rs index 5b758d60..a3a0efdd 100644 --- a/crates/tinyagents-graph/src/todos/test.rs +++ b/crates/tinyagents-graph/src/todos/test.rs @@ -373,8 +373,12 @@ mod tool_tests { "writing the list is not the work: {description}" ); assert!( - description.contains("must also carry the tool call that does the next step"), - "the same response carries the next step: {description}" + 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}" ); } diff --git a/crates/tinyagents-graph/src/todos/tool.rs b/crates/tinyagents-graph/src/todos/tool.rs index 1a3aa142..dcabec4e 100644 --- a/crates/tinyagents-graph/src/todos/tool.rs +++ b/crates/tinyagents-graph/src/todos/tool.rs @@ -26,9 +26,10 @@ 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` only after its work has actually run \ - and its result is in this conversation. Writing the list is bookkeeping, not work: the same \ - response that updates it must also carry the tool call that does the next step, and one \ - update per response is enough. Omit `todos` to read the current list. The list is bound \ + 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).