Keep setup before missing files
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
3cbd1bb06a2eee323b318aa4ccc414a8c47acd87- Parents
-
89ffea4 - Tree
b776be4
3cbd1bb
3cbd1bb06a2eee323b318aa4ccc414a8c47acd8789ffea4
b776be4| Status | File | + | - |
|---|---|---|---|
| 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( | |||
| 1515 | target, expect_directory = missing_artifact | 1515 | target, expect_directory = missing_artifact |
| 1516 | if _contains_any(text, _BROAD_SETUP_HINTS): | 1516 | if _contains_any(text, _BROAD_SETUP_HINTS): |
| 1517 | if not expect_directory: | 1517 | if not expect_directory: |
| 1518 | - return True | 1518 | + return target.parent.exists() |
| 1519 | return target.is_dir() | 1519 | return target.is_dir() |
| 1520 | return False | 1520 | return False |
| 1521 | 1521 | ||
tests/test_repair.pymodified@@ -308,22 +308,22 @@ def test_empty_response_retry_mentions_write_can_create_missing_parent_directori | |||
| 308 | assert decision.should_continue is True | 308 | assert decision.should_continue is True |
| 309 | assert decision.retry_message is not None | 309 | assert decision.retry_message is not None |
| 310 | assert ( | 310 | assert ( |
| 311 | - "Resume with this exact next step: continue `Write main index.html for nginx guide` " | 311 | + "Resume with this exact next step: create `index.html`." |
| 312 | - "by creating `index.html`." | ||
| 313 | in decision.retry_message | 312 | in decision.retry_message |
| 314 | ) | 313 | ) |
| 315 | assert ( | 314 | 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." |
| 317 | in decision.retry_message | 316 | in decision.retry_message |
| 318 | ) | 317 | ) |
| 319 | assert ( | 318 | 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." |
| 321 | in decision.retry_message | 320 | in decision.retry_message |
| 322 | ) | 321 | ) |
| 323 | assert ( | 322 | 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="...")`.' |
| 325 | in decision.retry_message | 324 | in decision.retry_message |
| 326 | ) | 325 | ) |
| 326 | + assert "Do not restart discovery unless one specific missing fact blocks this step." in decision.retry_message | ||
| 327 | 327 | ||
| 328 | 328 | ||
| 329 | def test_empty_response_retry_recovers_blocked_empty_file_path_to_concrete_target( | 329 | 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 ( | |||
| 20 | extract_verification_commands_from_markdown, | 20 | extract_verification_commands_from_markdown, |
| 21 | infer_pending_todo_output_target, | 21 | infer_pending_todo_output_target, |
| 22 | merge_refreshed_todos_with_existing_scope, | 22 | merge_refreshed_todos_with_existing_scope, |
| 23 | + preferred_pending_todo_item, | ||
| 23 | preserve_task_grounded_acceptance_criteria, | 24 | preserve_task_grounded_acceptance_criteria, |
| 24 | reconcile_aggregate_completion_steps, | 25 | reconcile_aggregate_completion_steps, |
| 25 | sync_todos_to_definition_of_done, | 26 | sync_todos_to_definition_of_done, |
@@ -1018,6 +1019,42 @@ def test_infer_pending_todo_output_target_maps_broad_setup_to_planned_directory( | |||
| 1018 | assert target == chapters.resolve(strict=False) | 1019 | assert target == chapters.resolve(strict=False) |
| 1019 | 1020 | ||
| 1020 | 1021 | ||
| 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 | + | ||
| 1021 | def test_advance_todos_from_tool_call_does_not_complete_content_study_from_root_index_read() -> None: | 1058 | def test_advance_todos_from_tool_call_does_not_complete_content_study_from_root_index_read() -> None: |
| 1022 | dod = create_definition_of_done("Create a multi-file nginx guide.") | 1059 | dod = create_definition_of_done("Create a multi-file nginx guide.") |
| 1023 | sync_todos_to_definition_of_done( | 1060 | sync_todos_to_definition_of_done( |