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