Python · 62361 bytes Raw Blame History
1 """Tests for Sprint 04 workflow routing and artifact persistence."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 from loader.llm.base import ToolCall
8 from loader.runtime.clarify_grounding import ClarifyGrounding, ClarifyRepoFact
9 from loader.runtime.dod import DefinitionOfDoneStore, create_definition_of_done
10 from loader.runtime.workflow import (
11 ClarifyBrief,
12 ModeRouter,
13 PlanningArtifacts,
14 WorkflowArtifactStore,
15 WorkflowMode,
16 advance_todos_from_tool_call,
17 build_execute_bridge,
18 effective_pending_todo_items,
19 enrich_clarify_brief_with_grounding,
20 extract_verification_commands_from_markdown,
21 infer_pending_todo_output_target,
22 merge_refreshed_todos_with_existing_scope,
23 preferred_pending_todo_item,
24 preserve_task_grounded_acceptance_criteria,
25 reconcile_aggregate_completion_steps,
26 sync_todos_to_definition_of_done,
27 )
28
29
30 def test_mode_router_routes_ambiguous_prompt_to_clarify() -> None:
31 router = ModeRouter()
32
33 decision = router.route("Improve Loader so it feels more like claw-code.")
34
35 assert decision.mode == WorkflowMode.CLARIFY
36 assert decision.reason_code == "task_is_ambiguous"
37 assert decision.route_score >= decision.runner_up_score
38
39
40 def test_mode_router_routes_complex_prompt_to_plan() -> None:
41 router = ModeRouter()
42
43 decision = router.route(
44 "Implement a persistent workflow mode router with clarify artifacts, "
45 "planning artifacts, and verification-plan wiring in the runtime."
46 )
47
48 assert decision.mode == WorkflowMode.PLAN
49 assert decision.complexity_score >= router.plan_threshold
50
51
52 def test_mode_router_routes_simple_prompt_to_execute() -> None:
53 router = ModeRouter()
54
55 decision = router.route("Read pyproject.toml and tell me the package name.")
56
57 assert decision.mode == WorkflowMode.EXECUTE
58
59
60 def test_clarify_brief_round_trips_and_seeds_acceptance_criteria() -> None:
61 brief = ClarifyBrief.fallback(
62 task_statement="Clarify the authentication change.",
63 question="What outcome matters most?",
64 answer="Add login without touching the signup flow.",
65 )
66 markdown = brief.to_markdown()
67
68 loaded = ClarifyBrief.from_markdown(
69 markdown,
70 task_statement=brief.task_statement,
71 question=brief.question,
72 answer=brief.answer,
73 )
74
75 assert "single-question clarify brief" in markdown
76 assert "return control to `execute` mode" in markdown
77 assert loaded.task_statement == brief.task_statement
78 assert "Add login" in loaded.acceptance_criteria[0]
79 assert loaded.non_goals
80
81
82 def test_enrich_clarify_brief_with_grounding_replaces_generic_sections() -> None:
83 brief = ClarifyBrief.fallback(
84 task_statement="Tighten Loader runtime clarify behavior.",
85 question="What part should change most?",
86 answer="Keep the work narrow.",
87 )
88 grounding = ClarifyGrounding(
89 existing_references=["src/loader/runtime/workflow_lanes.py"],
90 candidate_touchpoints=["src/loader/runtime/clarify_strategy.py"],
91 repo_facts=[
92 ClarifyRepoFact(
93 path="src/loader/runtime/workflow_lanes.py",
94 summary="class WorkflowLaneRunner:",
95 ),
96 ClarifyRepoFact(
97 path="src/loader/runtime/clarify_strategy.py",
98 summary="Intent-aware clarify strategy for runtime follow-up.",
99 ),
100 ],
101 )
102
103 enriched = enrich_clarify_brief_with_grounding(brief, grounding)
104
105 assert enriched.likely_touchpoints[:2] == [
106 "src/loader/runtime/workflow_lanes.py",
107 "src/loader/runtime/clarify_strategy.py",
108 ]
109 assert any("clarify_strategy.py" in item for item in enriched.constraints)
110 assert any("WorkflowLaneRunner" in item for item in enriched.assumptions)
111 assert any("Primary work stays scoped" in item for item in enriched.acceptance_criteria)
112 assert "likely_touchpoints" in enriched.explicit_sections
113
114
115 def test_planning_artifacts_round_trip_and_extract_commands() -> None:
116 artifacts = PlanningArtifacts.from_model_output(
117 "\n".join(
118 [
119 "# Implementation Plan",
120 "",
121 "## Execution Order",
122 "1. Inspect auth files.",
123 "2. Implement the change.",
124 "",
125 "## Risks",
126 "- Regression in signup.",
127 "",
128 "<<<VERIFICATION>>>",
129 "",
130 "# Verification Plan",
131 "",
132 "## Acceptance Criteria",
133 "- Login works without changing signup.",
134 "",
135 "## Verification Commands",
136 "- `uv run pytest tests/test_auth.py -q`",
137 "- `uv run mypy src/loader`",
138 ]
139 ),
140 task_statement="Clarify and implement the auth change.",
141 )
142
143 assert "single-pass planning artifact generation" in artifacts.implementation_markdown
144 assert "planner/critic consensus loop" in artifacts.implementation_markdown
145 assert "single-pass planning artifact generation" in artifacts.verification_markdown
146 assert artifacts.implementation_steps[:2] == [
147 "Inspect auth files.",
148 "Implement the change.",
149 ]
150 assert artifacts.acceptance_criteria == ["Login works without changing signup."]
151 assert artifacts.verification_commands == [
152 "uv run pytest tests/test_auth.py -q",
153 "uv run mypy src/loader",
154 ]
155 assert extract_verification_commands_from_markdown(artifacts.verification_markdown) == [
156 "uv run pytest tests/test_auth.py -q",
157 "uv run mypy src/loader",
158 ]
159
160
161 def test_planning_artifacts_recover_embedded_verification_from_legacy_separator() -> None:
162 artifacts = PlanningArtifacts.from_model_output(
163 "\n".join(
164 [
165 "# Implementation Plan",
166 "",
167 "## Execution Order",
168 "1. Inspect index.html.",
169 "2. Fix the chapter links.",
170 "",
171 "# Verification Plan",
172 "",
173 "## Acceptance Criteria",
174 "- All chapter links point to real files.",
175 "",
176 "## Verification Commands",
177 "- `grep -o 'href=\"[^\"]*\"' index.html`",
178 "- `ls chapters`",
179 "",
180 "<<VERIFICATION>>",
181 ]
182 ),
183 task_statement="Fix the broken chapter links in index.html.",
184 )
185
186 assert "## Verification Commands" not in artifacts.implementation_markdown
187 assert "## Verification Commands" in artifacts.verification_markdown
188 assert artifacts.verification_commands == [
189 "grep -o 'href=\"[^\"]*\"' index.html",
190 "ls chapters",
191 ]
192
193
194 def test_extract_verification_commands_from_markdown_splits_code_blocks() -> None:
195 markdown = "\n".join(
196 [
197 "# Verification Plan",
198 "",
199 "## Verification Commands",
200 "```bash",
201 "# Check chapter files",
202 "ls chapters",
203 "grep -n \"href=\" index.html",
204 "```",
205 ]
206 )
207
208 assert extract_verification_commands_from_markdown(markdown) == [
209 "ls chapters",
210 'grep -n "href=" index.html',
211 ]
212
213
214 def test_extract_verification_commands_from_markdown_ignores_prose_only_bullets() -> None:
215 markdown = "\n".join(
216 [
217 "# Verification Plan",
218 "",
219 "## Verification Commands",
220 "- Check that all chapter links in index.html resolve to existing files",
221 "- Validate chapter titles with `python3 scripts/check_titles.py`",
222 "- `test -f index.html`",
223 ]
224 )
225
226 assert extract_verification_commands_from_markdown(markdown) == [
227 "python3 scripts/check_titles.py",
228 "test -f index.html",
229 ]
230
231
232 def test_extract_verification_commands_from_markdown_trims_inline_command_explanations() -> None:
233 markdown = "\n".join(
234 [
235 "# Verification Plan",
236 "",
237 "## Verification Commands",
238 (
239 '- `grep -n "href" ~/Loader/guides/fortran/index.html` '
240 "- to identify all href attributes"
241 ),
242 ]
243 )
244
245 assert extract_verification_commands_from_markdown(markdown) == [
246 'grep -n "href" ~/Loader/guides/fortran/index.html',
247 ]
248
249
250 def test_extract_verification_commands_keeps_shell_pipelines_intact() -> None:
251 markdown = "\n".join(
252 [
253 "# Verification Plan",
254 "",
255 "## Verification Commands",
256 "```bash",
257 "ls -la chapters/",
258 "cat index.html | head -20",
259 "```",
260 ]
261 )
262
263 assert extract_verification_commands_from_markdown(markdown) == [
264 "ls -la chapters/",
265 "cat index.html | head -20",
266 ]
267
268
269 def test_preserve_task_grounded_acceptance_criteria_keeps_original_scope_on_refresh() -> None:
270 task = (
271 "Create an equally thorough nginx guide with index.html plus chapter files "
272 "covering getting started, installation, first website setup, configs, and "
273 "advanced topics."
274 )
275
276 preserved = preserve_task_grounded_acceptance_criteria(
277 task,
278 existing_acceptance_criteria=[
279 "All files are created in the correct locations with proper directory structure",
280 "Content covers all required topics: getting started, installation, first website, configuration basics, advanced configurations, and troubleshooting",
281 ],
282 refreshed_acceptance_criteria=[
283 "At least one chapter file exists in ~/Loader/guides/nginx/chapters/",
284 "~/Loader/guides/nginx/index.html exists and contains proper table of contents",
285 ],
286 )
287
288 assert (
289 "All files are created in the correct locations with proper directory structure"
290 in preserved
291 )
292 assert (
293 "Content covers all required topics: getting started, installation, first website, configuration basics, advanced configurations, and troubleshooting"
294 in preserved
295 )
296 assert "At least one chapter file exists in ~/Loader/guides/nginx/chapters/" in preserved
297
298
299 def test_preserve_task_grounded_acceptance_criteria_drops_stale_plan_specific_scope() -> None:
300 task = (
301 "Implement a persistent workflow artifact with planning artifacts, "
302 "verification commands, and plan refresh discipline."
303 )
304
305 preserved = preserve_task_grounded_acceptance_criteria(
306 task,
307 existing_acceptance_criteria=["planned.txt exists in the workspace root."],
308 refreshed_acceptance_criteria=["notes.txt exists in the workspace root."],
309 )
310
311 assert preserved == ["notes.txt exists in the workspace root."]
312
313
314 def test_planning_artifacts_with_acceptance_criteria_rewrites_verification_markdown() -> None:
315 artifacts = PlanningArtifacts.from_model_output(
316 "\n".join(
317 [
318 "# Implementation Plan",
319 "",
320 "## Execution Order",
321 "1. Create the guide files.",
322 "",
323 "<<<VERIFICATION>>>",
324 "",
325 "# Verification Plan",
326 "",
327 "## Acceptance Criteria",
328 "- At least one chapter file exists.",
329 "",
330 "## Verification Commands",
331 "- `find chapters -name \"*.html\" | wc -l`",
332 ]
333 ),
334 task_statement="Create a thorough nginx guide.",
335 )
336
337 updated = artifacts.with_acceptance_criteria(
338 [
339 "All files are created in the correct locations.",
340 "Content covers getting started, installation, and advanced topics.",
341 ]
342 )
343
344 assert "At least one chapter file exists." not in updated.verification_markdown
345 assert "All files are created in the correct locations." in updated.verification_markdown
346 assert (
347 "Content covers getting started, installation, and advanced topics."
348 in updated.verification_markdown
349 )
350
351
352 def test_planning_artifacts_with_progress_context_records_touched_and_completed_work() -> None:
353 artifacts = PlanningArtifacts.from_model_output(
354 "\n".join(
355 [
356 "# Implementation Plan",
357 "",
358 "## Execution Order",
359 "1. Create the guide files.",
360 "",
361 "<<<VERIFICATION>>>",
362 "",
363 "# Verification Plan",
364 "",
365 "## Acceptance Criteria",
366 "- At least one chapter file exists.",
367 "",
368 "## Verification Commands",
369 "- `find chapters -name \"*.html\" | wc -l`",
370 ]
371 ),
372 task_statement="Create a thorough nginx guide.",
373 )
374
375 updated = artifacts.with_progress_context(
376 touched_files=["/tmp/nginx/index.html"],
377 completed_items=[
378 "Create the guide scaffold",
379 "Collect verification evidence",
380 ],
381 )
382
383 assert "## Confirmed Progress" in updated.implementation_markdown
384 assert "Already touched during execution: `/tmp/nginx/index.html`." in (
385 updated.implementation_markdown
386 )
387 assert "Already completed during execution: Create the guide scaffold." in (
388 updated.implementation_markdown
389 )
390 assert "Collect verification evidence" not in updated.implementation_markdown
391
392
393 def test_merge_refreshed_todos_with_existing_scope_keeps_grounded_progress() -> None:
394 task = (
395 "Create an equally thorough nginx guide with index.html plus chapter files "
396 "covering getting started, installation, first website setup, configs, and "
397 "advanced topics."
398 )
399
400 todos = merge_refreshed_todos_with_existing_scope(
401 task,
402 existing_pending_items=[
403 "Create each chapter file in sequence, following the established pattern",
404 "Collect verification evidence",
405 ],
406 existing_completed_items=["Create directory structure for the new nginx guide"],
407 refreshed_steps=["Create sample chapter file to verify the structure works"],
408 )
409
410 assert todos[0]["content"] == "Create directory structure for the new nginx guide"
411 assert todos[0]["status"] == "completed"
412 assert any(
413 item["content"] == "Create each chapter file in sequence, following the established pattern"
414 and item["status"] == "pending"
415 for item in todos
416 )
417
418
419 def test_merge_refreshed_todos_with_existing_scope_filters_retro_refresh_noise() -> None:
420 task = (
421 "Create an equally thorough nginx guide with index.html plus chapter files "
422 "covering getting started, installation, first website setup, configs, and "
423 "advanced topics."
424 )
425
426 todos = merge_refreshed_todos_with_existing_scope(
427 task,
428 existing_pending_items=[
429 "Create each chapter file in sequence, following the same structure as the Fortran guide",
430 "Ensure all files are properly linked and formatted consistently",
431 ],
432 existing_completed_items=[
433 "First, examine the existing Fortran guide structure to understand the format and cadence",
434 "Create the directory structure for the new nginx guide",
435 "Create the main index.html file",
436 ],
437 refreshed_steps=[
438 "First examined the existing Fortran guide structure to understand format and cadence",
439 "Created the main index.html file with navigation",
440 "Created chapter files in sequence:",
441 "01-getting-started.html",
442 "02-installation.html",
443 "03-first-website.html",
444 "04-configuring.html",
445 "All files properly linked with navigation between chapters",
446 "Verify the final navigation links across the guide",
447 ],
448 )
449
450 labels = {item["content"]: item["status"] for item in todos}
451 assert (
452 labels["Create each chapter file in sequence, following the same structure as the Fortran guide"]
453 == "pending"
454 )
455 assert labels["Ensure all files are properly linked and formatted consistently"] == "pending"
456 assert labels["Verify the final navigation links across the guide"] == "pending"
457 assert "Created chapter files in sequence:" not in labels
458 assert "04-configuring.html" not in labels
459
460
461 def test_merge_refreshed_todos_with_existing_scope_drops_unplanned_filename_expansion() -> None:
462 task = (
463 "Create an equally thorough nginx guide with index.html plus chapter files "
464 "covering getting started, installation, configuration, usage, and troubleshooting."
465 )
466
467 todos = merge_refreshed_todos_with_existing_scope(
468 task,
469 existing_pending_items=[
470 "Create chapter files with appropriate content structure",
471 ],
472 existing_completed_items=[
473 "Create the nginx guide directory structure",
474 "Create introduction.html",
475 ],
476 refreshed_steps=[
477 "Create optimization.html",
478 "Create security.html",
479 "Ensure consistent chapter navigation",
480 ],
481 planned_files={
482 "index.html",
483 "introduction.html",
484 "installation.html",
485 "configuration.html",
486 "usage.html",
487 "troubleshooting.html",
488 },
489 )
490
491 labels = {item["content"]: item["status"] for item in todos}
492 assert "Create chapter files with appropriate content structure" in labels
493 assert "Ensure consistent chapter navigation" in labels
494 assert "Create optimization.html" not in labels
495 assert "Create security.html" not in labels
496
497
498 def test_planning_artifacts_with_file_changes_replaces_file_change_section() -> None:
499 artifacts = PlanningArtifacts(
500 implementation_markdown="\n".join(
501 [
502 "# Implementation Plan",
503 "",
504 "## File Changes",
505 "- `old.txt`",
506 "",
507 "## Execution Order",
508 "- Do the work",
509 "",
510 ]
511 )
512 + "\n",
513 verification_markdown="# Verification Plan\n",
514 verification_commands=[],
515 acceptance_criteria=["task"],
516 implementation_steps=["Do the work"],
517 )
518
519 updated = artifacts.with_file_changes(
520 ["`guides/nginx/index.html`", "`guides/nginx/chapters/`"]
521 )
522
523 assert "`old.txt`" not in updated.implementation_markdown
524 assert "`guides/nginx/index.html`" in updated.implementation_markdown
525 assert "`guides/nginx/chapters/`" in updated.implementation_markdown
526
527
528 def test_effective_pending_todo_items_filters_stale_discovery_after_artifacts_exist(
529 temp_dir: Path,
530 ) -> None:
531 guide_root = temp_dir / "guides" / "nginx"
532 chapters = guide_root / "chapters"
533 guide_root.mkdir(parents=True)
534 chapters.mkdir()
535 index_path = guide_root / "index.html"
536 chapter_one = chapters / "01-getting-started.html"
537 chapter_two = chapters / "02-installation.html"
538 index_path.write_text("<html></html>\n")
539 chapter_one.write_text("<h1>One</h1>\n")
540 chapter_two.write_text("<h1>Two</h1>\n")
541
542 implementation_plan = temp_dir / "implementation.md"
543 implementation_plan.write_text(
544 "\n".join(
545 [
546 "# Implementation Plan",
547 "",
548 "## File Changes",
549 f"- `{guide_root}/`",
550 f"- `{chapters}/`",
551 f"- `{index_path}`",
552 f"- `{chapter_one}`",
553 f"- `{chapter_two}`",
554 "",
555 ]
556 )
557 )
558
559 dod = create_definition_of_done("Create a multi-file nginx guide.")
560 dod.implementation_plan = str(implementation_plan)
561 dod.pending_items = [
562 "First, examine the existing Fortran guide structure to understand the format and content organization",
563 "Verify all guide files are linked and complete",
564 "Complete the requested work",
565 ]
566
567 pending = effective_pending_todo_items(dod, project_root=temp_dir)
568
569 assert "Verify all guide files are linked and complete" in pending
570 assert "Complete the requested work" in pending
571 assert not any("Fortran guide structure" in item for item in pending)
572
573
574 def test_effective_pending_todo_items_filters_completed_setup_before_build_finishes(
575 temp_dir: Path,
576 ) -> None:
577 guide_root = temp_dir / "guides" / "nginx"
578 chapters = guide_root / "chapters"
579 chapters.mkdir(parents=True)
580 index_path = guide_root / "index.html"
581 chapter_one = chapters / "01-introduction.html"
582 index_path.write_text("<html></html>\n")
583 chapter_one.write_text("<h1>One</h1>\n")
584
585 implementation_plan = temp_dir / "implementation.md"
586 implementation_plan.write_text(
587 "\n".join(
588 [
589 "# Implementation Plan",
590 "",
591 "## File Changes",
592 f"- `{guide_root}/`",
593 f"- `{chapters}/`",
594 f"- `{index_path}`",
595 f"- `{chapter_one}`",
596 f"- `{chapters / '02-installation.html'}`",
597 "",
598 ]
599 )
600 )
601
602 dod = create_definition_of_done("Create a multi-file nginx guide.")
603 dod.implementation_plan = str(implementation_plan)
604 dod.pending_items = [
605 "Create the nginx directory structure",
606 "Create each chapter file with appropriate content",
607 "Complete the requested work",
608 ]
609
610 pending = effective_pending_todo_items(dod, project_root=temp_dir)
611
612 assert "Create the nginx directory structure" not in pending
613 assert "Create each chapter file with appropriate content" in pending
614 assert "Complete the requested work" in pending
615
616
617 def test_effective_pending_todo_items_filters_stale_creation_steps_after_artifacts_exist(
618 temp_dir: Path,
619 ) -> None:
620 guide_root = temp_dir / "guides" / "nginx"
621 chapters = guide_root / "chapters"
622 guide_root.mkdir(parents=True)
623 chapters.mkdir()
624 index_path = guide_root / "index.html"
625 chapter_one = chapters / "01-getting-started.html"
626 chapter_two = chapters / "02-installation.html"
627 index_path.write_text("<html></html>\n")
628 chapter_one.write_text("<h1>One</h1>\n")
629 chapter_two.write_text("<h1>Two</h1>\n")
630
631 implementation_plan = temp_dir / "implementation.md"
632 implementation_plan.write_text(
633 "\n".join(
634 [
635 "# Implementation Plan",
636 "",
637 "## File Changes",
638 f"- `{guide_root}/`",
639 f"- `{chapters}/`",
640 f"- `{index_path}`",
641 f"- `{chapter_one}`",
642 f"- `{chapter_two}`",
643 "",
644 ]
645 )
646 )
647
648 dod = create_definition_of_done("Create a multi-file nginx guide.")
649 dod.implementation_plan = str(implementation_plan)
650 dod.pending_items = [
651 "Create 01-getting-started.html",
652 "Creating 02-installation.html",
653 "Verify all guide files are linked and complete",
654 "Complete the requested work",
655 ]
656
657 pending = effective_pending_todo_items(dod, project_root=temp_dir)
658
659 assert "Verify all guide files are linked and complete" in pending
660 assert "Complete the requested work" in pending
661 assert "Create 01-getting-started.html" not in pending
662 assert "Creating 02-installation.html" not in pending
663
664
665 def test_effective_pending_todo_items_filters_unplanned_expansion_after_outputs_exist(
666 temp_dir: Path,
667 ) -> None:
668 guide_root = temp_dir / "guides" / "nginx"
669 chapters = guide_root / "chapters"
670 guide_root.mkdir(parents=True)
671 chapters.mkdir()
672 index_path = guide_root / "index.html"
673 chapter_one = chapters / "01-introduction.html"
674 chapter_two = chapters / "02-installation.html"
675 index_path.write_text(
676 "\n".join(
677 [
678 '<a href="chapters/01-introduction.html">Intro</a>',
679 '<a href="chapters/02-installation.html">Install</a>',
680 '<a href="../index.html">Back</a>',
681 "",
682 ]
683 )
684 )
685 chapter_one.write_text("<h1>One</h1>\n")
686 chapter_two.write_text("<h1>Two</h1>\n")
687
688 implementation_plan = temp_dir / "implementation.md"
689 implementation_plan.write_text(
690 "\n".join(
691 [
692 "# Implementation Plan",
693 "",
694 "## File Changes",
695 f"- `{guide_root}/`",
696 f"- `{chapters}/`",
697 f"- `{index_path}`",
698 f"- `{chapter_one}`",
699 f"- `{chapter_two}`",
700 "",
701 ]
702 )
703 )
704
705 dod = create_definition_of_done("Create a multi-file nginx guide.")
706 dod.implementation_plan = str(implementation_plan)
707 dod.pending_items = [
708 "Creating chapter 08-troubleshooting.html",
709 "Verify all guide files are linked and complete",
710 "Complete the requested work",
711 ]
712
713 pending = effective_pending_todo_items(dod, project_root=temp_dir)
714
715 assert "Verify all guide files are linked and complete" in pending
716 assert "Complete the requested work" in pending
717 assert "Creating chapter 08-troubleshooting.html" not in pending
718
719
720 def test_workflow_artifact_store_and_bridge_round_trip(tmp_path: Path) -> None:
721 store = WorkflowArtifactStore(tmp_path)
722 brief = ClarifyBrief.fallback(
723 task_statement="Clarify the runtime changes.",
724 question="What matters most?",
725 answer="Close the tool-use gap first.",
726 )
727 artifacts = PlanningArtifacts.fallback(task_statement=brief.task_statement)
728
729 brief_path = store.write_brief(brief.task_statement, brief)
730 implementation_path, verification_path = store.write_plan(
731 brief.task_statement,
732 artifacts,
733 )
734 bridge = build_execute_bridge(brief_path, implementation_path, verification_path)
735
736 assert brief_path.exists()
737 assert implementation_path.exists()
738 assert verification_path.exists()
739 assert bridge is not None
740 assert "Task Brief" in bridge
741 assert "Implementation Plan" in bridge
742 assert "Verification Plan" in bridge
743
744
745 def test_definition_of_done_round_trip_preserves_workflow_links(tmp_path: Path) -> None:
746 store = DefinitionOfDoneStore(tmp_path)
747 dod = create_definition_of_done("Implement Loader workflow routing.")
748 dod.current_mode = "plan"
749 dod.mode_history = ["clarify", "plan"]
750 dod.clarify_brief = str(tmp_path / ".loader" / "briefs" / "brief.md")
751 dod.implementation_plan = str(tmp_path / ".loader" / "plans" / "impl.md")
752 dod.verification_plan = str(tmp_path / ".loader" / "plans" / "verify.md")
753
754 saved_path = store.save(dod)
755 reloaded = store.load(saved_path)
756
757 assert reloaded.current_mode == "plan"
758 assert reloaded.mode_history == ["clarify", "plan"]
759 assert reloaded.clarify_brief == dod.clarify_brief
760 assert reloaded.implementation_plan == dod.implementation_plan
761 assert reloaded.verification_plan == dod.verification_plan
762
763
764 def test_sync_todos_to_definition_of_done_preserves_runtime_items() -> None:
765 dod = create_definition_of_done("Implement Loader workflow routing.")
766 dod.pending_items.append("Collect verification evidence")
767
768 sync_todos_to_definition_of_done(
769 dod,
770 [
771 {
772 "content": "Write router",
773 "active_form": "Writing router",
774 "status": "in_progress",
775 },
776 {
777 "content": "Update tests",
778 "active_form": "Updating tests",
779 "status": "completed",
780 },
781 ],
782 )
783
784 assert "Writing router" in dod.pending_items
785 assert "Collect verification evidence" in dod.pending_items
786 assert "Update tests" in dod.completed_items
787
788
789 def test_sync_todos_to_definition_of_done_keeps_completed_items_monotonic() -> None:
790 dod = create_definition_of_done("Create a multi-file nginx guide.")
791 sync_todos_to_definition_of_done(
792 dod,
793 [
794 {
795 "content": "Create 03-first-website.html",
796 "active_form": "Creating 03-first-website.html",
797 "status": "pending",
798 },
799 {
800 "content": "Create 04-configuration-basics.html",
801 "active_form": "Creating 04-configuration-basics.html",
802 "status": "pending",
803 },
804 ],
805 )
806
807 assert advance_todos_from_tool_call(
808 dod,
809 ToolCall(
810 id="write-third-chapter",
811 name="write",
812 arguments={
813 "file_path": "/tmp/nginx/chapters/03-first-website.html",
814 "content": "<html></html>",
815 },
816 ),
817 )
818 assert "Create 03-first-website.html" in dod.completed_items
819
820 sync_todos_to_definition_of_done(
821 dod,
822 [
823 {
824 "content": "Create 03-first-website.html",
825 "active_form": "Creating 03-first-website.html",
826 "status": "pending",
827 },
828 {
829 "content": "Create 04-configuration-basics.html",
830 "active_form": "Creating 04-configuration-basics.html",
831 "status": "pending",
832 },
833 ],
834 )
835
836 assert "Create 03-first-website.html" in dod.completed_items
837 assert "Create 03-first-website.html" not in dod.pending_items
838 assert "Create 04-configuration-basics.html" in dod.pending_items
839
840
841 def test_advance_todos_from_tool_call_tracks_plan_progress() -> None:
842 dod = create_definition_of_done("Fix the chapter links in index.html.")
843 sync_todos_to_definition_of_done(
844 dod,
845 [
846 {
847 "content": "First, examine the current index.html file to understand its structure",
848 "active_form": "Working on: First, examine the current index.html file to understand its structure",
849 "status": "pending",
850 },
851 {
852 "content": "List and read all HTML files in the chapters directory to extract chapter information",
853 "active_form": "Working on: List and read all HTML files in the chapters directory to extract chapter information",
854 "status": "pending",
855 },
856 {
857 "content": "Parse chapter titles from each HTML file",
858 "active_form": "Working on: Parse chapter titles from each HTML file",
859 "status": "pending",
860 },
861 {
862 "content": "Update index.html with correct chapter links and titles",
863 "active_form": "Working on: Update index.html with correct chapter links and titles",
864 "status": "pending",
865 },
866 {
867 "content": "Verify the updated index.html file is properly formatted",
868 "active_form": "Working on: Verify the updated index.html file is properly formatted",
869 "status": "pending",
870 },
871 ],
872 )
873
874 assert advance_todos_from_tool_call(
875 dod,
876 ToolCall(
877 id="read-index",
878 name="read",
879 arguments={"file_path": "/tmp/fortran/index.html"},
880 ),
881 )
882 assert (
883 "First, examine the current index.html file to understand its structure"
884 in dod.completed_items
885 )
886
887 assert not advance_todos_from_tool_call(
888 dod,
889 ToolCall(
890 id="glob-chapters",
891 name="glob",
892 arguments={"path": "/tmp/fortran/chapters", "pattern": "*.html"},
893 ),
894 )
895 assert (
896 "List and read all HTML files in the chapters directory to extract chapter information"
897 in dod.pending_items
898 )
899
900 assert advance_todos_from_tool_call(
901 dod,
902 ToolCall(
903 id="read-chapter",
904 name="read",
905 arguments={"file_path": "/tmp/fortran/chapters/01-introduction.html"},
906 ),
907 )
908 assert (
909 "List and read all HTML files in the chapters directory to extract chapter information"
910 in dod.completed_items
911 )
912
913 assert advance_todos_from_tool_call(
914 dod,
915 ToolCall(
916 id="read-second-chapter",
917 name="read",
918 arguments={"file_path": "/tmp/fortran/chapters/02-setup.html"},
919 ),
920 )
921 assert "Parse chapter titles from each HTML file" in dod.completed_items
922
923 assert advance_todos_from_tool_call(
924 dod,
925 ToolCall(
926 id="patch-index",
927 name="patch",
928 arguments={"file_path": "/tmp/fortran/index.html", "hunks": []},
929 ),
930 )
931 assert "Update index.html with correct chapter links and titles" in dod.completed_items
932
933 assert advance_todos_from_tool_call(
934 dod,
935 ToolCall(
936 id="verify-index",
937 name="bash",
938 arguments={"command": "grep -o 'href=\"[^\"]*\"' /tmp/fortran/index.html"},
939 ),
940 )
941 assert "Verify the updated index.html file is properly formatted" in dod.completed_items
942
943
944 def test_advance_todos_from_tool_call_keeps_aggregate_mutation_steps_pending() -> None:
945 dod = create_definition_of_done("Create a multi-file nginx guide.")
946 sync_todos_to_definition_of_done(
947 dod,
948 [
949 {
950 "content": "Create each chapter file in sequence, following the same structure as the Fortran guide",
951 "active_form": "Working on: Create each chapter file in sequence, following the same structure as the Fortran guide",
952 "status": "pending",
953 },
954 {
955 "content": "Ensure all files are properly linked and formatted consistently",
956 "active_form": "Working on: Ensure all files are properly linked and formatted consistently",
957 "status": "pending",
958 },
959 ],
960 )
961
962 assert (
963 advance_todos_from_tool_call(
964 dod,
965 ToolCall(
966 id="write-one-chapter",
967 name="write",
968 arguments={
969 "file_path": "/tmp/nginx/chapters/01-getting-started.html",
970 "content": "<html></html>",
971 },
972 ),
973 )
974 is False
975 )
976 assert (
977 "Create each chapter file in sequence, following the same structure as the Fortran guide"
978 in dod.pending_items
979 )
980
981
982 def test_advance_todos_from_tool_call_keeps_plural_chapter_creation_step_pending() -> None:
983 dod = create_definition_of_done("Create a multi-file nginx guide.")
984 sync_todos_to_definition_of_done(
985 dod,
986 [
987 {
988 "content": "Create chapter files following the established pattern",
989 "active_form": "Working on: Create chapter files following the established pattern",
990 "status": "pending",
991 },
992 {
993 "content": "Ensure consistency with existing guide formatting and content style",
994 "active_form": "Working on: Ensure consistency with existing guide formatting and content style",
995 "status": "pending",
996 },
997 ],
998 )
999
1000 assert (
1001 advance_todos_from_tool_call(
1002 dod,
1003 ToolCall(
1004 id="write-one-chapter",
1005 name="write",
1006 arguments={
1007 "file_path": "/tmp/nginx/chapters/01-overview.html",
1008 "content": "<html></html>",
1009 },
1010 ),
1011 )
1012 is False
1013 )
1014 assert "Create chapter files following the established pattern" in dod.pending_items
1015
1016
1017 def test_advance_todos_from_tool_call_tracks_bash_directory_creation_progress() -> None:
1018 dod = create_definition_of_done("Create a multi-file nginx guide.")
1019 sync_todos_to_definition_of_done(
1020 dod,
1021 [
1022 {
1023 "content": "Create the nginx directory structure",
1024 "active_form": "Working on: Create the nginx directory structure",
1025 "status": "pending",
1026 },
1027 {
1028 "content": "Create index.html for nginx guide",
1029 "active_form": "Working on: Create index.html for nginx guide",
1030 "status": "pending",
1031 },
1032 ],
1033 )
1034
1035 assert advance_todos_from_tool_call(
1036 dod,
1037 ToolCall(
1038 id="mkdir-nginx",
1039 name="bash",
1040 arguments={"command": "mkdir -p ~/Loader/guides/nginx/chapters"},
1041 ),
1042 )
1043 assert "Create the nginx directory structure" in dod.completed_items
1044 assert "Create index.html for nginx guide" in dod.pending_items
1045
1046
1047 def test_infer_pending_todo_output_target_maps_broad_setup_to_planned_directory(
1048 tmp_path: Path,
1049 ) -> None:
1050 dod = create_definition_of_done("Create a multi-file nginx guide.")
1051 nginx_root = tmp_path / "Loader" / "guides" / "nginx"
1052 chapters = nginx_root / "chapters"
1053 implementation_plan = tmp_path / "implementation.md"
1054 implementation_plan.write_text(
1055 "\n".join(
1056 [
1057 "# Implementation Plan",
1058 "",
1059 "## File Changes",
1060 f"- `{chapters}/`",
1061 f"- `{nginx_root / 'index.html'}`",
1062 "",
1063 ]
1064 )
1065 )
1066 dod.implementation_plan = str(implementation_plan)
1067
1068 target = infer_pending_todo_output_target(
1069 dod,
1070 "Create the nginx directory structure",
1071 project_root=tmp_path,
1072 )
1073
1074 assert target == chapters.resolve(strict=False)
1075
1076
1077 def test_infer_pending_todo_output_target_ignores_topic_specific_guide_suffixes(
1078 tmp_path: Path,
1079 ) -> None:
1080 dod = create_definition_of_done("Create a multi-file postgres guide.")
1081 guide_root = tmp_path / "Loader" / "guides" / "postgres"
1082 index_path = guide_root / "index.html"
1083 implementation_plan = tmp_path / "implementation.md"
1084 implementation_plan.write_text(
1085 "\n".join(
1086 [
1087 "# Implementation Plan",
1088 "",
1089 "## File Changes",
1090 f"- `{index_path}`",
1091 "",
1092 ]
1093 )
1094 )
1095 dod.implementation_plan = str(implementation_plan)
1096
1097 target = infer_pending_todo_output_target(
1098 dod,
1099 "Create index.html for the postgres administration guide",
1100 project_root=tmp_path,
1101 )
1102
1103 assert target == index_path.resolve(strict=False)
1104
1105
1106 def test_infer_pending_todo_output_target_maps_aggregate_chapter_step_to_next_declared_file(
1107 tmp_path: Path,
1108 ) -> None:
1109 dod = create_definition_of_done("Create a multi-file nginx guide.")
1110 nginx_root = tmp_path / "Loader" / "guides" / "nginx"
1111 chapters = nginx_root / "chapters"
1112 chapters.mkdir(parents=True)
1113 index_path = nginx_root / "index.html"
1114 chapter_one = chapters / "01-introduction.html"
1115 chapter_two = chapters / "02-installation.html"
1116 index_path.write_text(
1117 "\n".join(
1118 [
1119 "<html>",
1120 '<a href="chapters/01-introduction.html">Chapter 1: Introduction to Nginx</a>',
1121 '<a href="chapters/02-installation.html">Chapter 2: Installation and Setup</a>',
1122 "</html>",
1123 ]
1124 )
1125 + "\n"
1126 )
1127 chapter_one.write_text("<h1>Introduction</h1>\n")
1128
1129 implementation_plan = tmp_path / "implementation.md"
1130 implementation_plan.write_text(
1131 "\n".join(
1132 [
1133 "# Implementation Plan",
1134 "",
1135 "## File Changes",
1136 f"- `{nginx_root}/`",
1137 f"- `{chapters}/`",
1138 f"- `{index_path}`",
1139 "",
1140 ]
1141 )
1142 )
1143 dod.implementation_plan = str(implementation_plan)
1144 dod.touched_files.extend([str(index_path), str(chapter_one)])
1145
1146 target = infer_pending_todo_output_target(
1147 dod,
1148 "Create chapter files following the established pattern",
1149 project_root=tmp_path,
1150 )
1151
1152 assert target == chapter_two.resolve(strict=False)
1153
1154
1155 def test_infer_pending_todo_output_target_ignores_workspace_mirror_touched_paths(
1156 tmp_path: Path,
1157 ) -> None:
1158 dod = create_definition_of_done("Create a multi-file nginx guide.")
1159 nginx_root = tmp_path / "Loader" / "guides" / "nginx"
1160 chapters = nginx_root / "chapters"
1161 chapters.mkdir(parents=True)
1162 index_path = nginx_root / "index.html"
1163 chapter_one = chapters / "01-introduction.html"
1164 chapter_two = chapters / "02-installation.html"
1165 repo_mirror = tmp_path / "workspace-mirror" / "chapters" / "01-introduction.html"
1166 repo_mirror.parent.mkdir(parents=True)
1167 repo_mirror.write_text("<h1>Wrong Root</h1>\n")
1168
1169 index_path.write_text(
1170 "\n".join(
1171 [
1172 "<html>",
1173 '<a href="chapters/01-introduction.html">Chapter 1: Introduction to Nginx</a>',
1174 '<a href="chapters/02-installation.html">Chapter 2: Installation and Setup</a>',
1175 "</html>",
1176 ]
1177 )
1178 + "\n"
1179 )
1180 chapter_one.write_text("<h1>Introduction</h1>\n")
1181
1182 implementation_plan = tmp_path / "implementation.md"
1183 implementation_plan.write_text(
1184 "\n".join(
1185 [
1186 "# Implementation Plan",
1187 "",
1188 "## File Changes",
1189 f"- `{nginx_root / 'index.html'}`",
1190 f"- `{chapters}/`",
1191 "",
1192 ]
1193 )
1194 )
1195 dod.implementation_plan = str(implementation_plan)
1196 dod.touched_files.extend([str(index_path), str(chapter_one), str(repo_mirror)])
1197
1198 target = infer_pending_todo_output_target(
1199 dod,
1200 "Create chapter files following the established pattern",
1201 project_root=tmp_path,
1202 )
1203
1204 assert target == chapter_two.resolve(strict=False)
1205
1206
1207 def test_preferred_pending_todo_item_keeps_setup_step_when_missing_file_parent_absent(
1208 tmp_path: Path,
1209 ) -> None:
1210 dod = create_definition_of_done("Create a multi-file nginx guide.")
1211 nginx_root = tmp_path / "Loader" / "guides" / "nginx"
1212 chapters = nginx_root / "chapters"
1213 index_path = nginx_root / "index.html"
1214 implementation_plan = tmp_path / "implementation.md"
1215 implementation_plan.write_text(
1216 "\n".join(
1217 [
1218 "# Implementation Plan",
1219 "",
1220 "## File Changes",
1221 f"- `{chapters}/`",
1222 f"- `{index_path}`",
1223 "",
1224 ]
1225 )
1226 )
1227 dod.implementation_plan = str(implementation_plan)
1228 dod.pending_items = [
1229 "Create the nginx directory structure",
1230 "Create the main index.html file for nginx guide",
1231 "Complete the requested work",
1232 ]
1233
1234 preferred = preferred_pending_todo_item(
1235 dod,
1236 project_root=tmp_path,
1237 missing_artifact=(index_path.resolve(strict=False), False),
1238 )
1239
1240 assert preferred == "Create the nginx directory structure"
1241
1242
1243 def test_advance_todos_from_tool_call_does_not_complete_content_study_from_root_index_read() -> None:
1244 dod = create_definition_of_done("Create a multi-file nginx guide.")
1245 sync_todos_to_definition_of_done(
1246 dod,
1247 [
1248 {
1249 "content": "First, examine the existing fortran guide structure and content",
1250 "active_form": "Working on: First, examine the existing fortran guide structure and content",
1251 "status": "pending",
1252 },
1253 {
1254 "content": "Develop the main index.html file for the nginx guide",
1255 "active_form": "Working on: Develop the main index.html file for the nginx guide",
1256 "status": "pending",
1257 },
1258 ],
1259 )
1260
1261 assert (
1262 advance_todos_from_tool_call(
1263 dod,
1264 ToolCall(
1265 id="read-reference-index",
1266 name="read",
1267 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1268 ),
1269 )
1270 is False
1271 )
1272 assert (
1273 "First, examine the existing fortran guide structure and content"
1274 in dod.pending_items
1275 )
1276 assert "Develop the main index.html file for the nginx guide" in dod.pending_items
1277
1278
1279 def test_advance_todos_from_tool_call_does_not_complete_content_examination_from_shallow_glob() -> None:
1280 dod = create_definition_of_done("Create a multi-file nginx guide.")
1281 sync_todos_to_definition_of_done(
1282 dod,
1283 [
1284 {
1285 "content": "First, examine the existing fortran guide structure and content",
1286 "active_form": "Working on: First, examine the existing fortran guide structure and content",
1287 "status": "pending",
1288 },
1289 {
1290 "content": "Develop the main index.html file for the nginx guide",
1291 "active_form": "Working on: Develop the main index.html file for the nginx guide",
1292 "status": "pending",
1293 },
1294 ],
1295 )
1296
1297 assert (
1298 advance_todos_from_tool_call(
1299 dod,
1300 ToolCall(
1301 id="glob-reference-root",
1302 name="glob",
1303 arguments={"path": "~/Loader/guides/fortran", "pattern": "**"},
1304 ),
1305 )
1306 is False
1307 )
1308 assert (
1309 "First, examine the existing fortran guide structure and content"
1310 in dod.pending_items
1311 )
1312 assert "Develop the main index.html file for the nginx guide" in dod.pending_items
1313
1314
1315 def test_advance_todos_from_tool_call_does_not_complete_format_study_from_shallow_glob() -> None:
1316 dod = create_definition_of_done("Create a multi-file nginx guide.")
1317 sync_todos_to_definition_of_done(
1318 dod,
1319 [
1320 {
1321 "content": "First, examine the existing fortran guide structure to understand the format",
1322 "active_form": "Working on: First, examine the existing fortran guide structure to understand the format",
1323 "status": "pending",
1324 },
1325 {
1326 "content": "Create the main index.html file for nginx guide",
1327 "active_form": "Working on: Create the main index.html file for nginx guide",
1328 "status": "pending",
1329 },
1330 ],
1331 )
1332
1333 assert (
1334 advance_todos_from_tool_call(
1335 dod,
1336 ToolCall(
1337 id="glob-reference-root",
1338 name="glob",
1339 arguments={"path": "~/Loader/guides/fortran", "pattern": "**"},
1340 ),
1341 )
1342 is False
1343 )
1344 assert (
1345 "First, examine the existing fortran guide structure to understand the format"
1346 in dod.pending_items
1347 )
1348 assert "Create the main index.html file for nginx guide" in dod.pending_items
1349
1350
1351 def test_advance_todos_from_tool_call_does_not_complete_deep_guide_study_from_root_index_read() -> None:
1352 dod = create_definition_of_done("Create a multi-file nginx guide.")
1353 sync_todos_to_definition_of_done(
1354 dod,
1355 [
1356 {
1357 "content": "First, examine the existing fortran guide structure to understand the content organization and cadence",
1358 "active_form": "Working on: First, examine the existing fortran guide structure to understand the content organization and cadence",
1359 "status": "pending",
1360 },
1361 {
1362 "content": "Develop the main index.html file for the nginx guide",
1363 "active_form": "Working on: Develop the main index.html file for the nginx guide",
1364 "status": "pending",
1365 },
1366 ],
1367 )
1368
1369 assert (
1370 advance_todos_from_tool_call(
1371 dod,
1372 ToolCall(
1373 id="read-reference-index",
1374 name="read",
1375 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1376 ),
1377 )
1378 is False
1379 )
1380 assert (
1381 "First, examine the existing fortran guide structure to understand the content organization and cadence"
1382 in dod.pending_items
1383 )
1384 assert "Develop the main index.html file for the nginx guide" in dod.pending_items
1385
1386
1387 def test_advance_todos_from_tool_call_does_not_complete_populate_step_from_reference_read() -> None:
1388 dod = create_definition_of_done("Create a multi-file nginx guide.")
1389 sync_todos_to_definition_of_done(
1390 dod,
1391 [
1392 {
1393 "content": "First, examine the existing fortran guide structure and content",
1394 "active_form": "Working on: First, examine the existing fortran guide structure and content",
1395 "status": "pending",
1396 },
1397 {
1398 "content": "Populate content for each chapter",
1399 "active_form": "Working on: Populate content for each chapter",
1400 "status": "pending",
1401 },
1402 ],
1403 )
1404
1405 assert advance_todos_from_tool_call(
1406 dod,
1407 ToolCall(
1408 id="read-reference-chapter",
1409 name="read",
1410 arguments={"file_path": "~/Loader/guides/fortran/chapters/01-introduction.html"},
1411 ),
1412 )
1413 assert (
1414 "First, examine the existing fortran guide structure and content"
1415 in dod.completed_items
1416 )
1417 assert "Populate content for each chapter" in dod.pending_items
1418
1419
1420 def test_advance_todos_from_tool_call_does_not_complete_linking_step_from_glob() -> None:
1421 dod = create_definition_of_done("Create a multi-file nginx guide.")
1422 sync_todos_to_definition_of_done(
1423 dod,
1424 [
1425 {
1426 "content": "Link all chapters together properly in the index file",
1427 "active_form": "Working on: Link all chapters together properly in the index file",
1428 "status": "pending",
1429 },
1430 ],
1431 )
1432
1433 assert (
1434 advance_todos_from_tool_call(
1435 dod,
1436 ToolCall(
1437 id="glob-reference-chapters",
1438 name="glob",
1439 arguments={"path": "~/Loader", "pattern": "**/fortran/chapters/*"},
1440 ),
1441 )
1442 is False
1443 )
1444 assert "Link all chapters together properly in the index file" in dod.pending_items
1445
1446
1447 def test_advance_todos_from_tool_call_does_not_complete_aggregate_style_step_from_reference_read() -> None:
1448 dod = create_definition_of_done("Create a multi-file nginx guide.")
1449 sync_todos_to_definition_of_done(
1450 dod,
1451 [
1452 {
1453 "content": "Create each chapter file with appropriate content",
1454 "active_form": "Working on: Create each chapter file with appropriate content",
1455 "status": "pending",
1456 },
1457 {
1458 "content": "Ensure all files follow the same structure and style as the Fortran guide",
1459 "active_form": "Working on: Ensure all files follow the same structure and style as the Fortran guide",
1460 "status": "pending",
1461 },
1462 ],
1463 )
1464
1465 assert (
1466 advance_todos_from_tool_call(
1467 dod,
1468 ToolCall(
1469 id="read-reference-index",
1470 name="read",
1471 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1472 ),
1473 )
1474 is False
1475 )
1476 assert "Create each chapter file with appropriate content" in dod.pending_items
1477 assert (
1478 "Ensure all files follow the same structure and style as the Fortran guide"
1479 in dod.pending_items
1480 )
1481
1482
1483 def test_advance_todos_from_tool_call_does_not_complete_consistency_style_step_from_reference_read() -> None:
1484 dod = create_definition_of_done("Create a multi-file nginx guide.")
1485 sync_todos_to_definition_of_done(
1486 dod,
1487 [
1488 {
1489 "content": "First, examine the existing fortran guide structure to understand the format",
1490 "active_form": "Working on: First, examine the existing fortran guide structure to understand the format",
1491 "status": "pending",
1492 },
1493 {
1494 "content": "Ensure consistency with fortran guide style and structure",
1495 "active_form": "Working on: Ensure consistency with fortran guide style and structure",
1496 "status": "pending",
1497 },
1498 ],
1499 )
1500
1501 assert (
1502 advance_todos_from_tool_call(
1503 dod,
1504 ToolCall(
1505 id="read-reference-index",
1506 name="read",
1507 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1508 ),
1509 )
1510 is False
1511 )
1512 assert (
1513 "First, examine the existing fortran guide structure to understand the format"
1514 in dod.pending_items
1515 )
1516 assert (
1517 "Ensure consistency with fortran guide style and structure"
1518 in dod.pending_items
1519 )
1520
1521 assert advance_todos_from_tool_call(
1522 dod,
1523 ToolCall(
1524 id="read-reference-chapter",
1525 name="read",
1526 arguments={"file_path": "~/Loader/guides/fortran/chapters/01-introduction.html"},
1527 ),
1528 )
1529 assert (
1530 "First, examine the existing fortran guide structure to understand the format"
1531 in dod.completed_items
1532 )
1533 assert (
1534 "Ensure consistency with fortran guide style and structure"
1535 in dod.pending_items
1536 )
1537
1538
1539 def test_sync_todos_to_definition_of_done_keeps_linking_step_pending_while_artifacts_missing(
1540 temp_dir: Path,
1541 ) -> None:
1542 guide_root = temp_dir / "guides" / "nginx"
1543 chapters = guide_root / "chapters"
1544 guide_root.mkdir(parents=True)
1545 chapters.mkdir()
1546 index_path = guide_root / "index.html"
1547 chapter_one = chapters / "01-getting-started.html"
1548 chapter_two = chapters / "02-installation.html"
1549 index_path.write_text("<html></html>\n")
1550 chapter_one.write_text("<h1>One</h1>\n")
1551
1552 implementation_plan = temp_dir / "implementation.md"
1553 implementation_plan.write_text(
1554 "\n".join(
1555 [
1556 "# Implementation Plan",
1557 "",
1558 "## File Changes",
1559 f"- `{guide_root}/`",
1560 f"- `{chapters}/`",
1561 f"- `{index_path}`",
1562 f"- `{chapter_one}`",
1563 f"- `{chapter_two}`",
1564 "",
1565 ]
1566 )
1567 )
1568
1569 dod = create_definition_of_done("Create a multi-file nginx guide.")
1570 dod.implementation_plan = str(implementation_plan)
1571 sync_todos_to_definition_of_done(
1572 dod,
1573 [
1574 {
1575 "content": "Create 01-getting-started.html chapter file",
1576 "active_form": "Creating 01-getting-started.html chapter file",
1577 "status": "completed",
1578 },
1579 {
1580 "content": "Link all chapters together properly in the index file",
1581 "active_form": "Linking chapters in the index file",
1582 "status": "completed",
1583 },
1584 {
1585 "content": "Create 02-installation.html chapter file",
1586 "active_form": "Creating 02-installation.html chapter file",
1587 "status": "pending",
1588 },
1589 ],
1590 project_root=temp_dir,
1591 )
1592
1593 assert "Link all chapters together properly in the index file" in dod.pending_items
1594 assert "Link all chapters together properly in the index file" not in dod.completed_items
1595
1596
1597 def test_sync_todos_to_definition_of_done_allows_linking_step_when_artifacts_exist(
1598 temp_dir: Path,
1599 ) -> None:
1600 guide_root = temp_dir / "guides" / "nginx"
1601 chapters = guide_root / "chapters"
1602 guide_root.mkdir(parents=True)
1603 chapters.mkdir()
1604 index_path = guide_root / "index.html"
1605 chapter_one = chapters / "01-getting-started.html"
1606 chapter_two = chapters / "02-installation.html"
1607 index_path.write_text("<html></html>\n")
1608 chapter_one.write_text("<h1>One</h1>\n")
1609 chapter_two.write_text("<h1>Two</h1>\n")
1610
1611 implementation_plan = temp_dir / "implementation.md"
1612 implementation_plan.write_text(
1613 "\n".join(
1614 [
1615 "# Implementation Plan",
1616 "",
1617 "## File Changes",
1618 f"- `{guide_root}/`",
1619 f"- `{chapters}/`",
1620 f"- `{index_path}`",
1621 f"- `{chapter_one}`",
1622 f"- `{chapter_two}`",
1623 "",
1624 ]
1625 )
1626 )
1627
1628 dod = create_definition_of_done("Create a multi-file nginx guide.")
1629 dod.implementation_plan = str(implementation_plan)
1630 sync_todos_to_definition_of_done(
1631 dod,
1632 [
1633 {
1634 "content": "Link all chapters together properly in the index file",
1635 "active_form": "Linking chapters in the index file",
1636 "status": "completed",
1637 },
1638 ],
1639 project_root=temp_dir,
1640 )
1641
1642 assert "Link all chapters together properly in the index file" in dod.completed_items
1643
1644
1645 def test_sync_todos_to_definition_of_done_reopens_directory_content_step_when_output_dir_is_empty(
1646 temp_dir: Path,
1647 ) -> None:
1648 guide_root = temp_dir / "guides" / "nginx"
1649 chapters = guide_root / "chapters"
1650 guide_root.mkdir(parents=True)
1651 chapters.mkdir()
1652 index_path = guide_root / "index.html"
1653 index_path.write_text("<html></html>\n")
1654
1655 implementation_plan = temp_dir / "implementation.md"
1656 implementation_plan.write_text(
1657 "\n".join(
1658 [
1659 "# Implementation Plan",
1660 "",
1661 "## File Changes",
1662 f"- `{guide_root / 'index.html'}`",
1663 f"- `{chapters}/` (directory for chapter files)",
1664 "",
1665 "## Execution Order",
1666 "- Create chapter files with appropriate content",
1667 ]
1668 )
1669 )
1670
1671 dod = create_definition_of_done("Create an equally thorough nginx guide with chapters.")
1672 dod.implementation_plan = str(implementation_plan)
1673 sync_todos_to_definition_of_done(
1674 dod,
1675 [
1676 {
1677 "content": "Create chapter files with appropriate content",
1678 "active_form": "Creating chapter files with appropriate content",
1679 "status": "completed",
1680 },
1681 ],
1682 project_root=temp_dir,
1683 )
1684
1685 assert "Create chapter files with appropriate content" in dod.pending_items
1686 assert "Create chapter files with appropriate content" not in dod.completed_items
1687
1688
1689 def test_reconcile_aggregate_completion_steps_reopens_linking_step_when_artifacts_missing(
1690 temp_dir: Path,
1691 ) -> None:
1692 guide_root = temp_dir / "guides" / "nginx"
1693 chapters = guide_root / "chapters"
1694 guide_root.mkdir(parents=True)
1695 chapters.mkdir()
1696 index_path = guide_root / "index.html"
1697 chapter_one = chapters / "01-getting-started.html"
1698 chapter_two = chapters / "02-installation.html"
1699 chapter_three = chapters / "03-first-website.html"
1700 index_path.write_text("<html></html>\n")
1701 chapter_one.write_text("<h1>One</h1>\n")
1702 chapter_two.write_text("<h1>Two</h1>\n")
1703
1704 implementation_plan = temp_dir / "implementation.md"
1705 implementation_plan.write_text(
1706 "\n".join(
1707 [
1708 "# Implementation Plan",
1709 "",
1710 "## File Changes",
1711 f"- `{guide_root}/`",
1712 f"- `{chapters}/`",
1713 f"- `{index_path}`",
1714 f"- `{chapter_one}`",
1715 f"- `{chapter_two}`",
1716 f"- `{chapter_three}`",
1717 "",
1718 ]
1719 )
1720 )
1721
1722 dod = create_definition_of_done("Create a multi-file nginx guide.")
1723 dod.implementation_plan = str(implementation_plan)
1724 dod.completed_items.append("Link all chapters together properly")
1725
1726 reconcile_aggregate_completion_steps(dod, project_root=temp_dir)
1727
1728 assert "Link all chapters together properly" not in dod.completed_items
1729 assert "Link all chapters together properly" in dod.pending_items
1730
1731
1732 def test_sync_todos_to_definition_of_done_drops_unplanned_artifact_expansion_after_plan_complete(
1733 temp_dir: Path,
1734 ) -> None:
1735 guide_root = temp_dir / "guides" / "nginx"
1736 chapters = guide_root / "chapters"
1737 guide_root.mkdir(parents=True)
1738 chapters.mkdir()
1739 index_path = guide_root / "index.html"
1740 chapter_one = chapters / "01-getting-started.html"
1741 chapter_two = chapters / "02-installation.html"
1742 index_path.write_text("<html></html>\n")
1743 chapter_one.write_text("<h1>One</h1>\n")
1744 chapter_two.write_text("<h1>Two</h1>\n")
1745
1746 implementation_plan = temp_dir / "implementation.md"
1747 implementation_plan.write_text(
1748 "\n".join(
1749 [
1750 "# Implementation Plan",
1751 "",
1752 "## File Changes",
1753 f"- `{guide_root}/`",
1754 f"- `{chapters}/`",
1755 f"- `{index_path}`",
1756 f"- `{chapter_one}`",
1757 f"- `{chapter_two}`",
1758 "",
1759 ]
1760 )
1761 )
1762
1763 dod = create_definition_of_done("Create a multi-file nginx guide.")
1764 dod.implementation_plan = str(implementation_plan)
1765 sync_todos_to_definition_of_done(
1766 dod,
1767 [
1768 {
1769 "content": "Create 01-getting-started.html",
1770 "active_form": "Creating 01-getting-started.html",
1771 "status": "completed",
1772 },
1773 {
1774 "content": "Create 02-installation.html",
1775 "active_form": "Creating 02-installation.html",
1776 "status": "completed",
1777 },
1778 {
1779 "content": "Create 07-performance-tuning.html",
1780 "active_form": "Creating 07-performance-tuning.html",
1781 "status": "in_progress",
1782 },
1783 ],
1784 project_root=temp_dir,
1785 )
1786
1787 assert "Creating 07-performance-tuning.html" not in dod.pending_items
1788 assert "Create 01-getting-started.html" in dod.completed_items
1789 assert "Create 02-installation.html" in dod.completed_items