tenseleyflow/loader / 3cbd1bb

Browse files

Keep setup before missing files

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
3cbd1bb06a2eee323b318aa4ccc414a8c47acd87
Parents
89ffea4
Tree
b776be4

3 changed files

StatusFile+-
M src/loader/runtime/workflow.py 1 1
M tests/test_repair.py 5 5
M tests/test_workflow.py 37 0
src/loader/runtime/workflow.pymodified
@@ -1515,7 +1515,7 @@ def _todo_is_subsumed_by_concrete_missing_artifact(
15151515
     target, expect_directory = missing_artifact
15161516
     if _contains_any(text, _BROAD_SETUP_HINTS):
15171517
         if not expect_directory:
1518
-            return True
1518
+            return target.parent.exists()
15191519
         return target.is_dir()
15201520
     return False
15211521
 
tests/test_repair.pymodified
@@ -308,22 +308,22 @@ def test_empty_response_retry_mentions_write_can_create_missing_parent_directori
308308
     assert decision.should_continue is True
309309
     assert decision.retry_message is not None
310310
     assert (
311
-        "Resume with this exact next step: continue `Write main index.html for nginx guide` "
312
-        "by creating `index.html`."
311
+        "Resume with this exact next step: create `index.html`."
313312
         in decision.retry_message
314313
     )
315314
     assert (
316
-        f"Prefer one `write(content=...)` call for `{index_path}` before more research."
315
+        f"Prefer one `write` call for `{index_path}` before any more reference reads."
317316
         in decision.retry_message
318317
     )
319318
     assert (
320
-        f'Emit this tool shape now: `write(file_path="{index_path.resolve(strict=False)}", content="...")`.'
319
+        "The `write` tool can create that file's parent directories automatically, so do the write in one step instead of stopping for a separate mkdir."
321320
         in decision.retry_message
322321
     )
323322
     assert (
324
-        "Do not restart discovery unless one specific missing fact blocks that file write."
323
+        f'Emit this tool shape now: `write(file_path="{index_path.resolve(strict=False)}", content="...")`.'
325324
         in decision.retry_message
326325
     )
326
+    assert "Do not restart discovery unless one specific missing fact blocks this step." in decision.retry_message
327327
 
328328
 
329329
 def test_empty_response_retry_recovers_blocked_empty_file_path_to_concrete_target(
tests/test_workflow.pymodified
@@ -20,6 +20,7 @@ from loader.runtime.workflow import (
2020
     extract_verification_commands_from_markdown,
2121
     infer_pending_todo_output_target,
2222
     merge_refreshed_todos_with_existing_scope,
23
+    preferred_pending_todo_item,
2324
     preserve_task_grounded_acceptance_criteria,
2425
     reconcile_aggregate_completion_steps,
2526
     sync_todos_to_definition_of_done,
@@ -1018,6 +1019,42 @@ def test_infer_pending_todo_output_target_maps_broad_setup_to_planned_directory(
10181019
     assert target == chapters.resolve(strict=False)
10191020
 
10201021
 
1022
+def test_preferred_pending_todo_item_keeps_setup_step_when_missing_file_parent_absent(
1023
+    tmp_path: Path,
1024
+) -> None:
1025
+    dod = create_definition_of_done("Create a multi-file nginx guide.")
1026
+    nginx_root = tmp_path / "Loader" / "guides" / "nginx"
1027
+    chapters = nginx_root / "chapters"
1028
+    index_path = nginx_root / "index.html"
1029
+    implementation_plan = tmp_path / "implementation.md"
1030
+    implementation_plan.write_text(
1031
+        "\n".join(
1032
+            [
1033
+                "# Implementation Plan",
1034
+                "",
1035
+                "## File Changes",
1036
+                f"- `{chapters}/`",
1037
+                f"- `{index_path}`",
1038
+                "",
1039
+            ]
1040
+        )
1041
+    )
1042
+    dod.implementation_plan = str(implementation_plan)
1043
+    dod.pending_items = [
1044
+        "Create the nginx directory structure",
1045
+        "Create the main index.html file for nginx guide",
1046
+        "Complete the requested work",
1047
+    ]
1048
+
1049
+    preferred = preferred_pending_todo_item(
1050
+        dod,
1051
+        project_root=tmp_path,
1052
+        missing_artifact=(index_path.resolve(strict=False), False),
1053
+    )
1054
+
1055
+    assert preferred == "Create the nginx directory structure"
1056
+
1057
+
10211058
 def test_advance_todos_from_tool_call_does_not_complete_content_study_from_root_index_read() -> None:
10221059
     dod = create_definition_of_done("Create a multi-file nginx guide.")
10231060
     sync_todos_to_definition_of_done(