Python · 60590 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_preferred_pending_todo_item_keeps_setup_step_when_missing_file_parent_absent(
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 index_path = nginx_root / "index.html"
1162 implementation_plan = tmp_path / "implementation.md"
1163 implementation_plan.write_text(
1164 "\n".join(
1165 [
1166 "# Implementation Plan",
1167 "",
1168 "## File Changes",
1169 f"- `{chapters}/`",
1170 f"- `{index_path}`",
1171 "",
1172 ]
1173 )
1174 )
1175 dod.implementation_plan = str(implementation_plan)
1176 dod.pending_items = [
1177 "Create the nginx directory structure",
1178 "Create the main index.html file for nginx guide",
1179 "Complete the requested work",
1180 ]
1181
1182 preferred = preferred_pending_todo_item(
1183 dod,
1184 project_root=tmp_path,
1185 missing_artifact=(index_path.resolve(strict=False), False),
1186 )
1187
1188 assert preferred == "Create the nginx directory structure"
1189
1190
1191 def test_advance_todos_from_tool_call_does_not_complete_content_study_from_root_index_read() -> None:
1192 dod = create_definition_of_done("Create a multi-file nginx guide.")
1193 sync_todos_to_definition_of_done(
1194 dod,
1195 [
1196 {
1197 "content": "First, examine the existing fortran guide structure and content",
1198 "active_form": "Working on: First, examine the existing fortran guide structure and content",
1199 "status": "pending",
1200 },
1201 {
1202 "content": "Develop the main index.html file for the nginx guide",
1203 "active_form": "Working on: Develop the main index.html file for the nginx guide",
1204 "status": "pending",
1205 },
1206 ],
1207 )
1208
1209 assert (
1210 advance_todos_from_tool_call(
1211 dod,
1212 ToolCall(
1213 id="read-reference-index",
1214 name="read",
1215 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1216 ),
1217 )
1218 is False
1219 )
1220 assert (
1221 "First, examine the existing fortran guide structure and content"
1222 in dod.pending_items
1223 )
1224 assert "Develop the main index.html file for the nginx guide" in dod.pending_items
1225
1226
1227 def test_advance_todos_from_tool_call_does_not_complete_content_examination_from_shallow_glob() -> None:
1228 dod = create_definition_of_done("Create a multi-file nginx guide.")
1229 sync_todos_to_definition_of_done(
1230 dod,
1231 [
1232 {
1233 "content": "First, examine the existing fortran guide structure and content",
1234 "active_form": "Working on: First, examine the existing fortran guide structure and content",
1235 "status": "pending",
1236 },
1237 {
1238 "content": "Develop the main index.html file for the nginx guide",
1239 "active_form": "Working on: Develop the main index.html file for the nginx guide",
1240 "status": "pending",
1241 },
1242 ],
1243 )
1244
1245 assert (
1246 advance_todos_from_tool_call(
1247 dod,
1248 ToolCall(
1249 id="glob-reference-root",
1250 name="glob",
1251 arguments={"path": "~/Loader/guides/fortran", "pattern": "**"},
1252 ),
1253 )
1254 is False
1255 )
1256 assert (
1257 "First, examine the existing fortran guide structure and content"
1258 in dod.pending_items
1259 )
1260 assert "Develop the main index.html file for the nginx guide" in dod.pending_items
1261
1262
1263 def test_advance_todos_from_tool_call_does_not_complete_format_study_from_shallow_glob() -> None:
1264 dod = create_definition_of_done("Create a multi-file nginx guide.")
1265 sync_todos_to_definition_of_done(
1266 dod,
1267 [
1268 {
1269 "content": "First, examine the existing fortran guide structure to understand the format",
1270 "active_form": "Working on: First, examine the existing fortran guide structure to understand the format",
1271 "status": "pending",
1272 },
1273 {
1274 "content": "Create the main index.html file for nginx guide",
1275 "active_form": "Working on: Create the main index.html file for nginx guide",
1276 "status": "pending",
1277 },
1278 ],
1279 )
1280
1281 assert (
1282 advance_todos_from_tool_call(
1283 dod,
1284 ToolCall(
1285 id="glob-reference-root",
1286 name="glob",
1287 arguments={"path": "~/Loader/guides/fortran", "pattern": "**"},
1288 ),
1289 )
1290 is False
1291 )
1292 assert (
1293 "First, examine the existing fortran guide structure to understand the format"
1294 in dod.pending_items
1295 )
1296 assert "Create the main index.html file for nginx guide" in dod.pending_items
1297
1298
1299 def test_advance_todos_from_tool_call_does_not_complete_deep_guide_study_from_root_index_read() -> None:
1300 dod = create_definition_of_done("Create a multi-file nginx guide.")
1301 sync_todos_to_definition_of_done(
1302 dod,
1303 [
1304 {
1305 "content": "First, examine the existing fortran guide structure to understand the content organization and cadence",
1306 "active_form": "Working on: First, examine the existing fortran guide structure to understand the content organization and cadence",
1307 "status": "pending",
1308 },
1309 {
1310 "content": "Develop the main index.html file for the nginx guide",
1311 "active_form": "Working on: Develop the main index.html file for the nginx guide",
1312 "status": "pending",
1313 },
1314 ],
1315 )
1316
1317 assert (
1318 advance_todos_from_tool_call(
1319 dod,
1320 ToolCall(
1321 id="read-reference-index",
1322 name="read",
1323 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1324 ),
1325 )
1326 is False
1327 )
1328 assert (
1329 "First, examine the existing fortran guide structure to understand the content organization and cadence"
1330 in dod.pending_items
1331 )
1332 assert "Develop the main index.html file for the nginx guide" in dod.pending_items
1333
1334
1335 def test_advance_todos_from_tool_call_does_not_complete_populate_step_from_reference_read() -> None:
1336 dod = create_definition_of_done("Create a multi-file nginx guide.")
1337 sync_todos_to_definition_of_done(
1338 dod,
1339 [
1340 {
1341 "content": "First, examine the existing fortran guide structure and content",
1342 "active_form": "Working on: First, examine the existing fortran guide structure and content",
1343 "status": "pending",
1344 },
1345 {
1346 "content": "Populate content for each chapter",
1347 "active_form": "Working on: Populate content for each chapter",
1348 "status": "pending",
1349 },
1350 ],
1351 )
1352
1353 assert advance_todos_from_tool_call(
1354 dod,
1355 ToolCall(
1356 id="read-reference-chapter",
1357 name="read",
1358 arguments={"file_path": "~/Loader/guides/fortran/chapters/01-introduction.html"},
1359 ),
1360 )
1361 assert (
1362 "First, examine the existing fortran guide structure and content"
1363 in dod.completed_items
1364 )
1365 assert "Populate content for each chapter" in dod.pending_items
1366
1367
1368 def test_advance_todos_from_tool_call_does_not_complete_linking_step_from_glob() -> None:
1369 dod = create_definition_of_done("Create a multi-file nginx guide.")
1370 sync_todos_to_definition_of_done(
1371 dod,
1372 [
1373 {
1374 "content": "Link all chapters together properly in the index file",
1375 "active_form": "Working on: Link all chapters together properly in the index file",
1376 "status": "pending",
1377 },
1378 ],
1379 )
1380
1381 assert (
1382 advance_todos_from_tool_call(
1383 dod,
1384 ToolCall(
1385 id="glob-reference-chapters",
1386 name="glob",
1387 arguments={"path": "~/Loader", "pattern": "**/fortran/chapters/*"},
1388 ),
1389 )
1390 is False
1391 )
1392 assert "Link all chapters together properly in the index file" in dod.pending_items
1393
1394
1395 def test_advance_todos_from_tool_call_does_not_complete_aggregate_style_step_from_reference_read() -> None:
1396 dod = create_definition_of_done("Create a multi-file nginx guide.")
1397 sync_todos_to_definition_of_done(
1398 dod,
1399 [
1400 {
1401 "content": "Create each chapter file with appropriate content",
1402 "active_form": "Working on: Create each chapter file with appropriate content",
1403 "status": "pending",
1404 },
1405 {
1406 "content": "Ensure all files follow the same structure and style as the Fortran guide",
1407 "active_form": "Working on: Ensure all files follow the same structure and style as the Fortran guide",
1408 "status": "pending",
1409 },
1410 ],
1411 )
1412
1413 assert (
1414 advance_todos_from_tool_call(
1415 dod,
1416 ToolCall(
1417 id="read-reference-index",
1418 name="read",
1419 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1420 ),
1421 )
1422 is False
1423 )
1424 assert "Create each chapter file with appropriate content" in dod.pending_items
1425 assert (
1426 "Ensure all files follow the same structure and style as the Fortran guide"
1427 in dod.pending_items
1428 )
1429
1430
1431 def test_advance_todos_from_tool_call_does_not_complete_consistency_style_step_from_reference_read() -> None:
1432 dod = create_definition_of_done("Create a multi-file nginx guide.")
1433 sync_todos_to_definition_of_done(
1434 dod,
1435 [
1436 {
1437 "content": "First, examine the existing fortran guide structure to understand the format",
1438 "active_form": "Working on: First, examine the existing fortran guide structure to understand the format",
1439 "status": "pending",
1440 },
1441 {
1442 "content": "Ensure consistency with fortran guide style and structure",
1443 "active_form": "Working on: Ensure consistency with fortran guide style and structure",
1444 "status": "pending",
1445 },
1446 ],
1447 )
1448
1449 assert (
1450 advance_todos_from_tool_call(
1451 dod,
1452 ToolCall(
1453 id="read-reference-index",
1454 name="read",
1455 arguments={"file_path": "~/Loader/guides/fortran/index.html"},
1456 ),
1457 )
1458 is False
1459 )
1460 assert (
1461 "First, examine the existing fortran guide structure to understand the format"
1462 in dod.pending_items
1463 )
1464 assert (
1465 "Ensure consistency with fortran guide style and structure"
1466 in dod.pending_items
1467 )
1468
1469 assert advance_todos_from_tool_call(
1470 dod,
1471 ToolCall(
1472 id="read-reference-chapter",
1473 name="read",
1474 arguments={"file_path": "~/Loader/guides/fortran/chapters/01-introduction.html"},
1475 ),
1476 )
1477 assert (
1478 "First, examine the existing fortran guide structure to understand the format"
1479 in dod.completed_items
1480 )
1481 assert (
1482 "Ensure consistency with fortran guide style and structure"
1483 in dod.pending_items
1484 )
1485
1486
1487 def test_sync_todos_to_definition_of_done_keeps_linking_step_pending_while_artifacts_missing(
1488 temp_dir: Path,
1489 ) -> None:
1490 guide_root = temp_dir / "guides" / "nginx"
1491 chapters = guide_root / "chapters"
1492 guide_root.mkdir(parents=True)
1493 chapters.mkdir()
1494 index_path = guide_root / "index.html"
1495 chapter_one = chapters / "01-getting-started.html"
1496 chapter_two = chapters / "02-installation.html"
1497 index_path.write_text("<html></html>\n")
1498 chapter_one.write_text("<h1>One</h1>\n")
1499
1500 implementation_plan = temp_dir / "implementation.md"
1501 implementation_plan.write_text(
1502 "\n".join(
1503 [
1504 "# Implementation Plan",
1505 "",
1506 "## File Changes",
1507 f"- `{guide_root}/`",
1508 f"- `{chapters}/`",
1509 f"- `{index_path}`",
1510 f"- `{chapter_one}`",
1511 f"- `{chapter_two}`",
1512 "",
1513 ]
1514 )
1515 )
1516
1517 dod = create_definition_of_done("Create a multi-file nginx guide.")
1518 dod.implementation_plan = str(implementation_plan)
1519 sync_todos_to_definition_of_done(
1520 dod,
1521 [
1522 {
1523 "content": "Create 01-getting-started.html chapter file",
1524 "active_form": "Creating 01-getting-started.html chapter file",
1525 "status": "completed",
1526 },
1527 {
1528 "content": "Link all chapters together properly in the index file",
1529 "active_form": "Linking chapters in the index file",
1530 "status": "completed",
1531 },
1532 {
1533 "content": "Create 02-installation.html chapter file",
1534 "active_form": "Creating 02-installation.html chapter file",
1535 "status": "pending",
1536 },
1537 ],
1538 project_root=temp_dir,
1539 )
1540
1541 assert "Link all chapters together properly in the index file" in dod.pending_items
1542 assert "Link all chapters together properly in the index file" not in dod.completed_items
1543
1544
1545 def test_sync_todos_to_definition_of_done_allows_linking_step_when_artifacts_exist(
1546 temp_dir: Path,
1547 ) -> None:
1548 guide_root = temp_dir / "guides" / "nginx"
1549 chapters = guide_root / "chapters"
1550 guide_root.mkdir(parents=True)
1551 chapters.mkdir()
1552 index_path = guide_root / "index.html"
1553 chapter_one = chapters / "01-getting-started.html"
1554 chapter_two = chapters / "02-installation.html"
1555 index_path.write_text("<html></html>\n")
1556 chapter_one.write_text("<h1>One</h1>\n")
1557 chapter_two.write_text("<h1>Two</h1>\n")
1558
1559 implementation_plan = temp_dir / "implementation.md"
1560 implementation_plan.write_text(
1561 "\n".join(
1562 [
1563 "# Implementation Plan",
1564 "",
1565 "## File Changes",
1566 f"- `{guide_root}/`",
1567 f"- `{chapters}/`",
1568 f"- `{index_path}`",
1569 f"- `{chapter_one}`",
1570 f"- `{chapter_two}`",
1571 "",
1572 ]
1573 )
1574 )
1575
1576 dod = create_definition_of_done("Create a multi-file nginx guide.")
1577 dod.implementation_plan = str(implementation_plan)
1578 sync_todos_to_definition_of_done(
1579 dod,
1580 [
1581 {
1582 "content": "Link all chapters together properly in the index file",
1583 "active_form": "Linking chapters in the index file",
1584 "status": "completed",
1585 },
1586 ],
1587 project_root=temp_dir,
1588 )
1589
1590 assert "Link all chapters together properly in the index file" in dod.completed_items
1591
1592
1593 def test_sync_todos_to_definition_of_done_reopens_directory_content_step_when_output_dir_is_empty(
1594 temp_dir: Path,
1595 ) -> None:
1596 guide_root = temp_dir / "guides" / "nginx"
1597 chapters = guide_root / "chapters"
1598 guide_root.mkdir(parents=True)
1599 chapters.mkdir()
1600 index_path = guide_root / "index.html"
1601 index_path.write_text("<html></html>\n")
1602
1603 implementation_plan = temp_dir / "implementation.md"
1604 implementation_plan.write_text(
1605 "\n".join(
1606 [
1607 "# Implementation Plan",
1608 "",
1609 "## File Changes",
1610 f"- `{guide_root / 'index.html'}`",
1611 f"- `{chapters}/` (directory for chapter files)",
1612 "",
1613 "## Execution Order",
1614 "- Create chapter files with appropriate content",
1615 ]
1616 )
1617 )
1618
1619 dod = create_definition_of_done("Create an equally thorough nginx guide with chapters.")
1620 dod.implementation_plan = str(implementation_plan)
1621 sync_todos_to_definition_of_done(
1622 dod,
1623 [
1624 {
1625 "content": "Create chapter files with appropriate content",
1626 "active_form": "Creating chapter files with appropriate content",
1627 "status": "completed",
1628 },
1629 ],
1630 project_root=temp_dir,
1631 )
1632
1633 assert "Create chapter files with appropriate content" in dod.pending_items
1634 assert "Create chapter files with appropriate content" not in dod.completed_items
1635
1636
1637 def test_reconcile_aggregate_completion_steps_reopens_linking_step_when_artifacts_missing(
1638 temp_dir: Path,
1639 ) -> None:
1640 guide_root = temp_dir / "guides" / "nginx"
1641 chapters = guide_root / "chapters"
1642 guide_root.mkdir(parents=True)
1643 chapters.mkdir()
1644 index_path = guide_root / "index.html"
1645 chapter_one = chapters / "01-getting-started.html"
1646 chapter_two = chapters / "02-installation.html"
1647 chapter_three = chapters / "03-first-website.html"
1648 index_path.write_text("<html></html>\n")
1649 chapter_one.write_text("<h1>One</h1>\n")
1650 chapter_two.write_text("<h1>Two</h1>\n")
1651
1652 implementation_plan = temp_dir / "implementation.md"
1653 implementation_plan.write_text(
1654 "\n".join(
1655 [
1656 "# Implementation Plan",
1657 "",
1658 "## File Changes",
1659 f"- `{guide_root}/`",
1660 f"- `{chapters}/`",
1661 f"- `{index_path}`",
1662 f"- `{chapter_one}`",
1663 f"- `{chapter_two}`",
1664 f"- `{chapter_three}`",
1665 "",
1666 ]
1667 )
1668 )
1669
1670 dod = create_definition_of_done("Create a multi-file nginx guide.")
1671 dod.implementation_plan = str(implementation_plan)
1672 dod.completed_items.append("Link all chapters together properly")
1673
1674 reconcile_aggregate_completion_steps(dod, project_root=temp_dir)
1675
1676 assert "Link all chapters together properly" not in dod.completed_items
1677 assert "Link all chapters together properly" in dod.pending_items
1678
1679
1680 def test_sync_todos_to_definition_of_done_drops_unplanned_artifact_expansion_after_plan_complete(
1681 temp_dir: Path,
1682 ) -> None:
1683 guide_root = temp_dir / "guides" / "nginx"
1684 chapters = guide_root / "chapters"
1685 guide_root.mkdir(parents=True)
1686 chapters.mkdir()
1687 index_path = guide_root / "index.html"
1688 chapter_one = chapters / "01-getting-started.html"
1689 chapter_two = chapters / "02-installation.html"
1690 index_path.write_text("<html></html>\n")
1691 chapter_one.write_text("<h1>One</h1>\n")
1692 chapter_two.write_text("<h1>Two</h1>\n")
1693
1694 implementation_plan = temp_dir / "implementation.md"
1695 implementation_plan.write_text(
1696 "\n".join(
1697 [
1698 "# Implementation Plan",
1699 "",
1700 "## File Changes",
1701 f"- `{guide_root}/`",
1702 f"- `{chapters}/`",
1703 f"- `{index_path}`",
1704 f"- `{chapter_one}`",
1705 f"- `{chapter_two}`",
1706 "",
1707 ]
1708 )
1709 )
1710
1711 dod = create_definition_of_done("Create a multi-file nginx guide.")
1712 dod.implementation_plan = str(implementation_plan)
1713 sync_todos_to_definition_of_done(
1714 dod,
1715 [
1716 {
1717 "content": "Create 01-getting-started.html",
1718 "active_form": "Creating 01-getting-started.html",
1719 "status": "completed",
1720 },
1721 {
1722 "content": "Create 02-installation.html",
1723 "active_form": "Creating 02-installation.html",
1724 "status": "completed",
1725 },
1726 {
1727 "content": "Create 07-performance-tuning.html",
1728 "active_form": "Creating 07-performance-tuning.html",
1729 "status": "in_progress",
1730 },
1731 ],
1732 project_root=temp_dir,
1733 )
1734
1735 assert "Creating 07-performance-tuning.html" not in dod.pending_items
1736 assert "Create 01-getting-started.html" in dod.completed_items
1737 assert "Create 02-installation.html" in dod.completed_items