Tolerate lean TodoWrite payloads
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
78c5d51dbc02597c5dfd9d9146afedf8d0868088- Parents
-
4e907a5 - Tree
b07571f
78c5d51
78c5d51dbc02597c5dfd9d9146afedf8d08680884e907a5
b07571f| Status | File | + | - |
|---|---|---|---|
| M |
src/loader/tools/workflow_tools.py
|
9 | 3 |
| M |
tests/test_workflow_tools.py
|
26 | 0 |
src/loader/tools/workflow_tools.pymodified@@ -28,14 +28,17 @@ class TodoItem: | ||
| 28 | 28 | |
| 29 | 29 | @classmethod |
| 30 | 30 | def from_dict(cls, data: dict[str, Any]) -> TodoItem: |
| 31 | + content = str(data.get("content", "")).strip() | |
| 31 | 32 | active_form = str( |
| 32 | 33 | data.get("active_form") |
| 33 | 34 | or data.get("activeForm") |
| 34 | 35 | or data.get("active") |
| 35 | 36 | or "" |
| 36 | 37 | ).strip() |
| 38 | + if not active_form: | |
| 39 | + active_form = content | |
| 37 | 40 | return cls( |
| 38 | - content=str(data.get("content", "")).strip(), | |
| 41 | + content=content, | |
| 39 | 42 | active_form=active_form, |
| 40 | 43 | status=str(data.get("status", "")).strip().lower(), |
| 41 | 44 | ) |
@@ -89,7 +92,10 @@ class TodoWriteTool(Tool): | ||
| 89 | 92 | }, |
| 90 | 93 | "active_form": { |
| 91 | 94 | "type": "string", |
| 92 | - "description": "Progressive-tense form, e.g. 'Running tests'.", | |
| 95 | + "description": ( | |
| 96 | + "Optional progressive-tense form, e.g. " | |
| 97 | + "'Running tests'. Defaults to content when omitted." | |
| 98 | + ), | |
| 93 | 99 | }, |
| 94 | 100 | "status": { |
| 95 | 101 | "type": "string", |
@@ -97,7 +103,7 @@ class TodoWriteTool(Tool): | ||
| 97 | 103 | "description": "Current todo status.", |
| 98 | 104 | }, |
| 99 | 105 | }, |
| 100 | - "required": ["content", "active_form", "status"], | |
| 106 | + "required": ["content", "status"], | |
| 101 | 107 | }, |
| 102 | 108 | } |
| 103 | 109 | }, |
tests/test_workflow_tools.pymodified@@ -145,6 +145,32 @@ async def test_todo_write_rejects_invalid_payloads_and_sets_verification_nudge( | ||
| 145 | 145 | assert json.loads(nudged.output)["verification_nudge_needed"] is True |
| 146 | 146 | |
| 147 | 147 | |
| 148 | +@pytest.mark.asyncio | |
| 149 | +async def test_todo_write_defaults_missing_active_form_to_content( | |
| 150 | + tmp_path: Path, | |
| 151 | +) -> None: | |
| 152 | + tool = TodoWriteTool(tmp_path) | |
| 153 | + | |
| 154 | + result = await tool.execute( | |
| 155 | + todos=[ | |
| 156 | + { | |
| 157 | + "content": "Create the nginx chapters content", | |
| 158 | + "status": "completed", | |
| 159 | + } | |
| 160 | + ] | |
| 161 | + ) | |
| 162 | + | |
| 163 | + payload = json.loads(result.output) | |
| 164 | + assert result.is_error is False | |
| 165 | + assert payload["new_todos"] == [ | |
| 166 | + { | |
| 167 | + "content": "Create the nginx chapters content", | |
| 168 | + "active_form": "Create the nginx chapters content", | |
| 169 | + "status": "completed", | |
| 170 | + } | |
| 171 | + ] | |
| 172 | + | |
| 173 | + | |
| 148 | 174 | @pytest.mark.asyncio |
| 149 | 175 | async def test_ask_user_question_uses_callback_and_resolves_numbered_options() -> None: |
| 150 | 176 | tool = AskUserQuestionTool() |