tenseleyflow/loader / b69a3fe

Browse files

Handoff completed todos to verification

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
b69a3fe174fa9cda56249272eb667b47ed119969
Parents
ffab688
Tree
c8a1d53

2 changed files

StatusFile+-
M src/loader/runtime/tool_batches.py 59 0
M tests/test_tool_batches.py 14 7
src/loader/runtime/tool_batches.pymodified
@@ -381,6 +381,24 @@ class ToolBatchRunner:
381381
                     tool_call,
382382
                     dod=dod,
383383
                 )
384
+            completed_todowrite_response = (
385
+                self._completed_todowrite_verification_response(
386
+                    tool_call=executed_tool_call,
387
+                    dod=dod,
388
+                    single_tool_batch=len(tool_calls) == 1,
389
+                )
390
+            )
391
+            if completed_todowrite_response is not None:
392
+                summary.final_response = completed_todowrite_response
393
+                await emit(
394
+                    AgentEvent(
395
+                        type="response",
396
+                        content=completed_todowrite_response,
397
+                    )
398
+                )
399
+                result.halted = True
400
+                result.final_response = completed_todowrite_response
401
+                return result
384402
             if self._should_preempt_for_verification_handoff(
385403
                 tool_call=executed_tool_call,
386404
                 dod=dod,
@@ -503,6 +521,47 @@ class ToolBatchRunner:
503521
             return False
504522
         return tool_call.name in ({"TodoWrite"} | _BOOKKEEPING_NOTE_TOOL_NAMES)
505523
 
524
+    def _completed_todowrite_verification_response(
525
+        self,
526
+        *,
527
+        tool_call: ToolCall,
528
+        dod: DefinitionOfDone,
529
+        single_tool_batch: bool,
530
+    ) -> str | None:
531
+        if tool_call.name != "TodoWrite" or not single_tool_batch:
532
+            return None
533
+        if not all_planned_artifact_outputs_exist(
534
+            dod,
535
+            project_root=self.context.project_root,
536
+        ):
537
+            return None
538
+
539
+        pending_items = [
540
+            item
541
+            for item in effective_pending_todo_items(
542
+                dod,
543
+                project_root=self.context.project_root,
544
+            )
545
+            if item not in _TODO_NUDGE_EXCLUDED_ITEMS
546
+        ]
547
+        if pending_items:
548
+            return None
549
+
550
+        verification_commands = dod.verification_commands or derive_verification_commands(
551
+            dod,
552
+            project_root=self.context.project_root,
553
+            task_statement=getattr(self.context.session, "current_task", "") or "",
554
+            supplement_existing=True,
555
+        )
556
+        if not verification_commands:
557
+            return None
558
+
559
+        self.context.set_workflow_mode("verify")
560
+        return (
561
+            "Todo tracking is complete; running Loader verification on the generated "
562
+            "files now."
563
+        )
564
+
506565
     def _queue_duplicate_observation_nudge(
507566
         self,
508567
         tool_call: ToolCall,
tests/test_tool_batches.pymodified
@@ -4514,18 +4514,20 @@ async def test_tool_batch_runner_todowrite_complete_directory_plan_does_not_rein
45144514
         consecutive_errors=0,
45154515
     )
45164516
 
4517
-    assert result.continue_after_batch is True
4518
-    assert queued_messages
4519
-    message = queued_messages[-1]
4520
-    assert "Finish with a final response now so Loader can run verification automatically." in message
4521
-    assert "01-introduction.html" not in message
4522
-    assert "chapter files" not in message.lower()
4517
+    assert result.halted is True
4518
+    assert result.final_response == (
4519
+        "Todo tracking is complete; running Loader verification on the generated "
4520
+        "files now."
4521
+    )
4522
+    assert summary.final_response == result.final_response
45234523
     assert context.workflow_mode == "verify"
45244524
     assert summary.tool_result_messages
45254525
     assert (
45264526
         "final response should be provided next for Loader verification"
45274527
         in summary.tool_result_messages[-1].content
45284528
     )
4529
+    assert "01-introduction.html" not in summary.tool_result_messages[-1].content
4530
+    assert "chapter files" not in summary.tool_result_messages[-1].content.lower()
45294531
     assert "fortran guide structure" not in summary.tool_result_messages[-1].content.lower()
45304532
 
45314533
 
@@ -4985,7 +4987,12 @@ async def test_tool_batch_runner_rewrites_stale_todowrite_summary_from_reconcile
49854987
         consecutive_errors=0,
49864988
     )
49874989
 
4988
-    assert result.continue_after_batch is True
4990
+    assert result.halted is True
4991
+    assert result.final_response == (
4992
+        "Todo tracking is complete; running Loader verification on the generated "
4993
+        "files now."
4994
+    )
4995
+    assert summary.final_response == result.final_response
49894996
     assert summary.tool_result_messages
49904997
     message = summary.tool_result_messages[-1].content
49914998
     assert "updated todo list" in message