agentic-dev-loop 0.12.1__tar.gz → 0.12.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. agentic_dev_loop-0.12.3/PKG-INFO +637 -0
  2. agentic_dev_loop-0.12.3/README.md +592 -0
  3. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/pyproject.toml +4 -1
  4. agentic_dev_loop-0.12.3/src/agentic_dev_loop/core/failure.py +21 -0
  5. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/verdict.py +87 -0
  6. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/actions.py +6 -5
  7. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/agents.py +94 -2
  8. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/cli.py +3 -0
  9. agentic_dev_loop-0.12.3/src/agentic_dev_loop/engine/completion.py +48 -0
  10. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/dryrun.py +8 -0
  11. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/graph.py +13 -1
  12. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/llm.py +59 -1
  13. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/templates/qa.agent.md +25 -18
  14. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/templates/review.agent.md +11 -14
  15. agentic_dev_loop-0.12.3/src/agentic_dev_loop/engine/provider_wire.py +197 -0
  16. agentic_dev_loop-0.12.3/src/agentic_dev_loop/engine/run_status.py +360 -0
  17. agentic_dev_loop-0.12.3/src/agentic_dev_loop/engine/run_status_command.md +23 -0
  18. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/stages.py +174 -45
  19. agentic_dev_loop-0.12.3/src/agentic_dev_loop/viz/page.py +9 -0
  20. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/viz/server.py +7 -3
  21. agentic_dev_loop-0.12.3/src/agentic_dev_loop.egg-info/PKG-INFO +637 -0
  22. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop.egg-info/SOURCES.txt +7 -0
  23. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop.egg-info/requires.txt +1 -0
  24. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_033_brief_step.py +8 -4
  25. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_041_brief_coverage.py +2 -1
  26. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_cli.py +1 -1
  27. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_actions.py +3 -3
  28. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_agent_error_guide.py +4 -3
  29. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_cli.py +3 -1
  30. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_dry_run.py +1 -1
  31. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_e2e_fixtures.py +15 -8
  32. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_graph.py +6 -4
  33. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_llm.py +2 -0
  34. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_qa_prompt.py +8 -6
  35. agentic_dev_loop-0.12.3/tests/test_run_status.py +283 -0
  36. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_sdk_compat.py +65 -0
  37. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_stages.py +57 -30
  38. agentic_dev_loop-0.12.3/tests/test_unit11.py +487 -0
  39. agentic_dev_loop-0.12.3/tests/test_unit11_pbt.py +125 -0
  40. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_viz.py +27 -2
  41. agentic_dev_loop-0.12.1/PKG-INFO +0 -911
  42. agentic_dev_loop-0.12.1/README.md +0 -867
  43. agentic_dev_loop-0.12.1/src/agentic_dev_loop/engine/provider_wire.py +0 -38
  44. agentic_dev_loop-0.12.1/src/agentic_dev_loop/viz/page.py +0 -9
  45. agentic_dev_loop-0.12.1/src/agentic_dev_loop.egg-info/PKG-INFO +0 -911
  46. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/LICENSE +0 -0
  47. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/setup.cfg +0 -0
  48. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/__init__.py +0 -0
  49. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/__init__.py +0 -0
  50. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/config.py +0 -0
  51. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/paths.py +0 -0
  52. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/profiles/__init__.py +0 -0
  53. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/profiles/data/dotnet-node.yaml +0 -0
  54. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/profiles/data/python.yaml +0 -0
  55. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/core/results.py +0 -0
  56. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/__init__.py +0 -0
  57. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
  58. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
  59. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/allowlist.py +0 -0
  60. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
  61. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
  62. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
  63. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/containers.py +0 -0
  64. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/copilot_auth.py +0 -0
  65. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/deps.py +0 -0
  66. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
  67. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
  68. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  69. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  70. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/forbidden.py +0 -0
  71. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
  72. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/isolation.py +0 -0
  73. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/loaders.py +0 -0
  74. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/mounts.py +0 -0
  75. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/__init__.py +0 -0
  76. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/checks.py +0 -0
  77. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/doctor.py +0 -0
  78. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/init.py +0 -0
  79. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml +0 -0
  80. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/recipes/python.yaml +0 -0
  81. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/recipes.py +0 -0
  82. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/templates/dev.agent.md +0 -0
  83. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/templates/env.example +0 -0
  84. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/templates/trigger-agentic-dev-loop.sh +0 -0
  85. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/templates.py +0 -0
  86. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/onboarding/upgrade.py +0 -0
  87. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/outputs.py +0 -0
  88. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/presets.py +0 -0
  89. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/proc.py +0 -0
  90. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/registry.py +0 -0
  91. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  92. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  93. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/secrets.py +0 -0
  94. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
  95. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/engine/sync_back.py +0 -0
  96. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/py.typed +0 -0
  97. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/viz/__init__.py +0 -0
  98. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/viz/__main__.py +0 -0
  99. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop/viz/layout.py +0 -0
  100. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  101. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  102. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  103. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_029_merge_on_conflict.py +0 -0
  104. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_035_finalize_nudge.py +0 -0
  105. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
  106. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_046_brief_prompt.py +0 -0
  107. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_doctor.py +0 -0
  108. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_init.py +0 -0
  109. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_profile_loading.py +0 -0
  110. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_profiles.py +0 -0
  111. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_recipes.py +0 -0
  112. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_schema_lock.py +0 -0
  113. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_templates.py +0 -0
  114. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_055_upgrade.py +0 -0
  115. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_agents.py +0 -0
  116. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_aidlc.py +0 -0
  117. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_build_viz_page.py +0 -0
  118. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_config.py +0 -0
  119. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_containers.py +0 -0
  120. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_copilot_auth.py +0 -0
  121. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_copilot_cli.py +0 -0
  122. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_deps.py +0 -0
  123. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_egress_proxy.py +0 -0
  124. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_hostboundary.py +0 -0
  125. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_hostboundary_integration.py +0 -0
  126. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_isolation.py +0 -0
  127. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_isolation_integration.py +0 -0
  128. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_isolation_pbt.py +0 -0
  129. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_loaders.py +0 -0
  130. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_module_contract.py +0 -0
  131. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_outputs.py +0 -0
  132. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_outputs_pbt.py +0 -0
  133. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_proc.py +0 -0
  134. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_provider_wire.py +0 -0
  135. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_publish.py +0 -0
  136. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_registry.py +0 -0
  137. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_repo_ascii.py +0 -0
  138. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_results.py +0 -0
  139. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_secrets.py +0 -0
  140. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_secrets_integration.py +0 -0
  141. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_secrets_pipeline.py +0 -0
  142. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_source_resolution.py +0 -0
  143. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_step_workspace.py +0 -0
  144. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_sync_back.py +0 -0
  145. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_trigger_script.py +0 -0
  146. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_unit5_config.py +0 -0
  147. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_unit5_pbt.py +0 -0
  148. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_unit5_pure.py +0 -0
  149. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_verdict.py +0 -0
  150. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_viz_demo.py +0 -0
  151. {agentic_dev_loop-0.12.1 → agentic_dev_loop-0.12.3}/tests/test_viz_layout.py +0 -0
@@ -0,0 +1,637 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentic-dev-loop
3
+ Version: 0.12.3
4
+ Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
+ Author: Luke Wilson
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/lukedawilson/agentic-dev-loop
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Classifier: Topic :: Security
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: pydantic>=2.0
29
+ Requires-Dist: PyYAML>=6.0
30
+ Requires-Dist: openhands-sdk~=1.17.0
31
+ Requires-Dist: openhands-tools~=1.17.0
32
+ Requires-Dist: agent-client-protocol<1.0.0
33
+ Requires-Dist: langgraph>=1.2
34
+ Requires-Dist: langgraph-checkpoint-sqlite>=3.0
35
+ Requires-Dist: msgpack>=1.0
36
+ Requires-Dist: filelock>=4.2.1
37
+ Requires-Dist: pyasn1>=0.6.5
38
+ Requires-Dist: pypdf>=6.21.4
39
+ Provides-Extra: dev
40
+ Requires-Dist: hypothesis>=6.0; extra == "dev"
41
+ Requires-Dist: pytest>=8.0; extra == "dev"
42
+ Requires-Dist: pip-audit>=2.0; extra == "dev"
43
+ Requires-Dist: pre-commit>=3.0; extra == "dev"
44
+ Dynamic: license-file
45
+
46
+ # agentic-dev-loop
47
+
48
+ > **Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.**
49
+
50
+ Describe your pipeline in one YAML file - agent steps, mechanical test gates, and
51
+ trusted actions - and `agentic-dev-loop` runs it end to end: agent and command steps get
52
+ kernel-level sandboxing via Docker, verdicts route the graph, every boundary is
53
+ checkpointed, and nothing gets committed until every gate passes.
54
+
55
+ ![The live graph view - pipeline graph with fail loopbacks, summary tiles, per-stage consoles, and event log](https://raw.githubusercontent.com/lukedawilson/agentic-dev-loop/main/docs/viz-pipeline.png)
56
+
57
+ - **A tool, not a wiring project.** One CLI entry point, opinionated defaults,
58
+ fixed safety policies. You write the pipeline and the agent definitions -
59
+ nothing else.
60
+ - **Deterministic.** Same pipeline + same state → same routing. Verdicts are
61
+ parsed from files, retries are bounded, nothing is best-effort.
62
+ - **Sandboxed by default.** The agent's shell and every command step run inside
63
+ an always-on Docker sandbox. There is no opt-out.
64
+
65
+ ## How it fits together
66
+
67
+ The loop is five layers. You author the first two; the engine owns the rest.
68
+
69
+ | Layer | What it is | Who authors it |
70
+ | --- | --- | --- |
71
+ | **Pipeline** | One `pipeline.yaml` declaring the stage graph: agent steps, mechanical command gates, verdict routing, retry loops, and per-step isolation. | You |
72
+ | **Agent prompts** | Markdown prompt files with YAML frontmatter (`<name>.agent.md`) defining each agent's role, rules, sandbox notes, and tools. | You |
73
+ | **Sandbox** | Every agent shell and command step runs in a Docker container: no network access by default, writes only to declared `mounts:`, and no `.git` - steps run against a fresh export. If no sandbox is available, the run stops instead of degrading. | Engine |
74
+ | **Dev image** | The digest-pinned dev image pulled on first run. The supply-chain check refuses to continue on a digest mismatch. | Engine |
75
+ | **Extensions** | Optional plugins: your own actions, tools, and document loaders, loaded from outside the workspace. | You (optional) |
76
+
77
+ Every run is crash-safe (interrupt and resume from the exact checkpoint),
78
+ watchable in a live browser graph view, and gated: nothing gets committed
79
+ until every stage passes. The fixed policies are part of the product - fail
80
+ fast on missing config or secrets, no silent fallbacks, verdicts only from
81
+ artifacts, bounded retries, and a single clean-worktree-gated commit point.
82
+
83
+ ## Supported platforms
84
+
85
+ The engine is plain Linux under the hood and runs wherever a compliant
86
+ Docker engine does:
87
+
88
+ | Platform | Support |
89
+ | --- | --- |
90
+ | **Linux** | Native. Docker Engine, OrbStack, Colima, or podman with docker compat. |
91
+ | **macOS** | Native. Docker Desktop, OrbStack, or Colima. |
92
+ | **Windows** | Via **WSL2** (Ubuntu distro). Enable Docker Desktop's WSL integration (or run Docker Engine inside the distro) and keep the repo on the WSL filesystem (`~/dev/...`), not `/mnt/c` - bind-mounting a 9p path into containers breaks permissions and is slow. |
93
+
94
+ ## Quick start
95
+
96
+ **Prerequisites**
97
+
98
+ | Requirement | Detail |
99
+ | --- | --- |
100
+ | Python | ≥ 3.12 |
101
+ | Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
102
+ | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription (see [LLM providers](#llm-providers)). |
103
+
104
+ ```bash
105
+ pipx install agentic-dev-loop
106
+ agentic-dev-loop --version
107
+ ```
108
+
109
+ Install with **pipx**: the engine's venv lives outside the workspace, so it
110
+ is unmounted from the sandbox and never agent-writable. On the first run the
111
+ engine pulls the digest-pinned dev image and verifies its digest. Setup
112
+ details and troubleshooting live in [docs/setup.md](docs/setup.md).
113
+
114
+ ```bash
115
+ cd <your-repo>
116
+ agentic-dev-loop init --profile python # writes the pipeline + agents (see below)
117
+ export OPENAI_API_KEY=... # whatever api_key_env names
118
+ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run # validate - no LLM, no Docker
119
+ agentic-dev-loop agentic-dev-loop/pipeline.yaml # run it
120
+ ```
121
+
122
+ ## Running a pipeline
123
+
124
+ | Flag | Effect |
125
+ | --- | --- |
126
+ | `--dry-run` | Full load-and-validate pass, then print the resolved plan. No LLM, no Docker, no state writes, no API key. Exit `0` on a clean load. |
127
+ | `--resume <thread-id>` | Continue the latest checkpoint of a previous run (crash, Ctrl-C, exhaustion) with all accumulated notes intact. |
128
+ | `--max-attempts <n>` | Override the pipeline's `max_attempts`. |
129
+ | `--no-viz` / `--viz-port <port>` | Suppress the live graph view / set a preferred port (default 8321; falls back to a free port when busy). |
130
+
131
+ Exit code is `0` on pipeline success, `1` otherwise.
132
+
133
+ Other commands:
134
+
135
+ - `agentic-dev-loop run-status <pipeline>` renders the latest run as a
136
+ mermaid flowchart with the current step highlighted, reading the run-state
137
+ checkpoint database directly. `run-status --install` installs the bundled
138
+ `/run-status` command into OpenCode.
139
+ - `agentic-dev-loop containers` inspects the engine's sandbox containers and
140
+ networks from the host; `containers --reap` removes registry-owned orphans
141
+ whose owner process is dead. A SIGTERM to a running pipeline still closes
142
+ the sandbox; SIGKILLed runs are reclaimed by the startup reaper on the next
143
+ run or by `containers --reap`.
144
+
145
+ **What a run looks like** (the shipped self-loop example):
146
+
147
+ ```mermaid
148
+ flowchart LR
149
+ dev["agent: dev"] --> test{{"test gate"}} --> review["agent: review"] --> qa["agent: qa"] --> commit["action: commit"]
150
+ test -- fail --> dev
151
+ review -- fail --> dev
152
+ qa -- fail --> dev
153
+ ```
154
+
155
+ 1. **dev** - the dev agent works with full context (loaded docs + shared files).
156
+ Its edits are untracked; only declared `produces:` files are hygiene-tracked.
157
+ 2. **test** - a mechanical command step runs the suite inside the sandbox.
158
+ Exit 0 moves on; anything else rewinds to `dev` with the failing output.
159
+ 3. **review / qa** - verdict agents finish by calling the `record_verdict`
160
+ tool; the validated tool call routes the graph. `fail` rewinds to `dev`
161
+ and consumes an attempt.
162
+ 4. **commit** - the single commit point fires only when all gates passed and
163
+ the worktree was clean at start.
164
+ 5. **success** - committed; or **failure** when attempts are exhausted.
165
+
166
+ ## LLM providers
167
+
168
+ The engine speaks OpenAI-compatible wire format throughout, so every step,
169
+ profile, and gate works identically on either provider style:
170
+
171
+ | Provider | Setup |
172
+ | --- | --- |
173
+ | **Any OpenAI-compatible endpoint** | API key via `api_key_env` + `base_url` in the `llm:` block: DeepSeek, OpenAI, or any OpenAI-compatible API. |
174
+ | **GitHub Copilot subscription** | `agentic-dev-loop copilot-login` once, then point the `llm:` block at `https://api.githubcopilot.com` with the file-backed token. |
175
+
176
+ ```yaml
177
+ # OpenAI-compatible endpoint (any provider)
178
+ llm:
179
+ model: openai/deepseek-v4-pro
180
+ base_url: https://api.deepseek.com
181
+ api_key_env: OPENAI_API_KEY
182
+ ```
183
+
184
+ ```yaml
185
+ # GitHub Copilot subscription
186
+ llm:
187
+ model: openai/gpt-5.6-luna # pro tier; gpt-4.1 is the lite tier
188
+ base_url: https://api.githubcopilot.com
189
+ api_key_secret:
190
+ from: file
191
+ key: ~/.agentic-dev-loop/auth/copilot.json
192
+ json_key: access_token
193
+ headers:
194
+ Copilot-Integration-Id: vscode-chat
195
+ Editor-Version: vscode/1.94.0
196
+ Openai-Intent: conversation-panel
197
+ ```
198
+
199
+ Copilot specifics:
200
+
201
+ - The `openai/` prefix on model names is required - it forces the
202
+ OpenAI-compatible route through the gateway.
203
+ - `gpt-4.1` (lite tier) rejects any `reasoning_effort`, so lite-tier steps
204
+ set none.
205
+ - Some models on this endpoint do not work for agent steps. The current
206
+ working set, the token lifecycle, and known endpoint limitations are in
207
+ [docs/setup.md](docs/setup.md).
208
+
209
+ ## pipeline.yaml
210
+
211
+ ```yaml
212
+ name: my-construction-loop
213
+ extensions:
214
+ - agentic_dev_loop.engine.extensions.aidlc:AidlcExtension # bundled AI-DLC doc loader
215
+ additional_files:
216
+ - ../README.md # shared context for every agent step
217
+
218
+ llm:
219
+ model: openai/deepseek-v4-pro
220
+ base_url: https://api.deepseek.com
221
+ api_key_env: OPENAI_API_KEY
222
+ # reasoning_effort: medium # optional: low | medium | high | xhigh | none
223
+
224
+ # Named LLM profiles: a step names one with `llm: <name>` to move the whole
225
+ # profile (model, endpoint, key, effort default) together. The `llm:` block
226
+ # above remains the default.
227
+ # llms:
228
+ # review:
229
+ # model: openai/deepseek-v4-pro
230
+ # base_url: https://api.deepseek.com
231
+ # api_key_env: REVIEW_API_KEY
232
+ # reasoning_effort: xhigh
233
+
234
+ max_attempts: 10 # CLI --max-attempts overrides
235
+ agents_dir: sdk_agents # your <name>.agent.md files
236
+
237
+ steps:
238
+ - name: brief
239
+ reasoning_effort: medium
240
+ brief_inputs:
241
+ include: [AGENTS.md, coding-standards-ddd.yaml]
242
+ sections: [Repo rules digest, Unit scope digest, Environment facts, Open risks]
243
+ env_facts: ["the workspace is an export with no .git; verify with find/ls, never git status"]
244
+ on_pass: dev
245
+
246
+ - name: dev
247
+ agent: dev
248
+ produces: implementation-summary.md
249
+ terminal_timeout: 900
250
+ # forbidden_commands: ["npm install", "npm ci"] # per-step tool deny-list; engine defaults are git mutations + docker
251
+ on_pass: test
252
+
253
+ - name: test
254
+ command: .venv-sandbox/bin/python -m pytest tests/ -q -m "not docker"
255
+ setup: python -m venv .venv-sandbox && .venv-sandbox/bin/pip install -e ".[dev]"
256
+ timeout: 600
257
+ on_pass: review
258
+ on_fail: { goto: dev, retry: true }
259
+
260
+ - name: review
261
+ agent: review
262
+ artifact: review-findings.md
263
+ verdicts: { pass: APPROVED, fail: NEEDS CHANGES }
264
+ # reasoning_effort: xhigh # per-step override of the effective profile's effort
265
+ # llm: review # select a named `llms:` profile for this step
266
+ on_pass: qa
267
+ on_fail: { goto: dev, retry: true }
268
+
269
+ - name: qa
270
+ agent: qa
271
+ artifact: qa-report.md
272
+ verdicts: { pass: PASS, fail: FAIL }
273
+ max_iterations: 100
274
+ finalize_nudge_turns: 10
275
+ on_pass: commit
276
+ on_fail: { goto: dev, retry: true }
277
+
278
+ - name: commit
279
+ action: commit
280
+ params: { message: "feat: construct {subject} (agent dev loop)" }
281
+ ```
282
+
283
+ | Key | Notes |
284
+ | --- | --- |
285
+ | `agent:` / `command:` / `action:` / `brief_inputs:` | Exactly one per step. Agents live in `agents_dir` as `<name>.agent.md`; commands run sandboxed (mechanical, never an LLM call). `commit` is the sole host-side built-in action; consumer actions are sandbox-executed scripts (see [Host boundary](#host-boundary-security-model)). |
286
+ | `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs structured-verdict routing. The verdict comes from a validated `record_verdict` tool call, never from prose; stale artifacts are deleted at each attempt start. |
287
+ | `brief_inputs:` | A native brief step: `{ include: [<paths/globs>], env_facts: [<strings>], sections: [<headings>] }`. It digests the included docs into an engine-owned `attempt-brief.md` that later agents read, and re-runs only when its inputs change. |
288
+ | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
289
+ | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` runs fresh at every step boundary in a one-shot provision container targeting the export (command or agent steps; network ON); `network: true` grants egress scoped by `egress:` (default off) - see [Per-stage networking](#per-stage-networking). |
290
+ | `mounts:` / `egress:` | Per-step workspace isolation. `mounts:` lists `{path, mode}` entries (`rw`/`ro`, default `ro`), strict-by-default: empty/missing mounts nothing, and only declared-rw paths sync back to the repo. `egress:` names the hosts a networked step may reach (`{class, presets, targets}`). See [docs/setup.md](docs/setup.md). |
291
+ | `secrets:` (top-level) / step `secrets:` | Declares secret **refs only - never values**; each step opts in to the refs it needs. Resolved host-side and injected into that step's exec env only. See [Secrets](#secrets). |
292
+ | `profile:` (top-level) | Names a bundled toolchain profile (`python`, `dotnet-node`) or a consumer profile file path, merged at load time: isolation key-wise with the pipeline winning, caches unioned profile-first. |
293
+ | `isolation:` (optional) | Configures the sandbox - image override, resource caps, `allow_git_mutations`, `seccomp_profile`. It can never disable it. See [browser sandbox](docs/browser-sandbox.md). |
294
+ | `terminal_timeout:` | Optional agent-step per-action timeout in seconds; overrides the SDK tool default. |
295
+ | `finalize_nudge_turns:` | Optional agent-step final-response nudge; requires `max_iterations:` and must be less than it. |
296
+ | `deps_policy:` / `lockfiles:` | `locked` (default) or `open`. The human-owned lockfile is the source of truth under `locked`; see [Dependency policy](#dependency-policy-locked-by-default). |
297
+ | `caches:` / `sync_excludes:` | Top-level run-wide lists. `caches:` names toolchain cache dirs bind-mounted rw from host-side stores (a `setup:` hook can warm them); `sync_excludes:` names non-gitignored scratch paths skipped from export and sync-back. |
298
+ | `merge_on_conflict:` | Top-level list of append-style logs. A concurrent host edit to a listed path is union-merged instead of raising; see [Sync-back conflicts](#sync-back-conflicts-host-edits-during-a-run). |
299
+ | `llms:` (top-level) / step `llm:` / `reasoning_effort:` | Named LLM profiles, each a full `LlmConfig`; a step selects one whole with `llm: <name>`. `reasoning_effort` (`low`/`medium`/`high`/`xhigh`/`none`) is opt-in on the `llm` block, a profile, or a step. See [Reasoning effort](#reasoning-effort-opt-in). |
300
+
301
+ ### Banned commands (`forbidden_commands:`)
302
+
303
+ `forbidden_commands:` is a per-step list of regex patterns checked against
304
+ every agent command before any shell spawn; a match returns a structured
305
+ `[forbidden: <label>]` observation instead of running. The engine defaults
306
+ (git mutations and docker) always apply and have no disable switch; a
307
+ toolchain profile may add tool-specific rules (for example the `python`
308
+ recipe denies `pip install`, `uv add`, and `pip-compile` on agent steps).
309
+ Command steps bypass the filter by design.
310
+
311
+ ### Reasoning effort (opt-in)
312
+
313
+ `reasoning_effort` is an opt-in latency dial on the pipeline `llm` block, a
314
+ named `llms:` profile, and individual agent steps. Unset means reasoning is
315
+ off - the field is forwarded as none and reasoning is stripped from requests.
316
+ A thinking model without an effort level is invalid config and fails fast
317
+ with an actionable engine error rather than a provider 400. A step's value
318
+ overrides the effective profile's value.
319
+
320
+ Recommended lane defaults (guidance only, not enforced):
321
+
322
+ | Lane | reasoning_effort | Why |
323
+ | --- | --- | --- |
324
+ | brief | medium | digest and classify, no design work |
325
+ | dev | high | the most demanding construction work |
326
+ | qa | medium | classification discipline must stay reliable |
327
+ | review | xhigh | highest stakes: a missed detection sails past the last gate into commit |
328
+
329
+ ### Secrets
330
+
331
+ Secret values never belong in the pipeline yaml, the workspace, or `state_dir`.
332
+ Declare the refs at the top level and opt each step into the refs it needs:
333
+
334
+ ```yaml
335
+ secrets: # refs only - never values
336
+ kinde-client-secret: # ref name (arbitrary)
337
+ from: env # provider name
338
+ key: KINDIE_CLIENT_SECRET # provider-specific locator
339
+ r2-keys:
340
+ from: env
341
+ key: R2_KEYS
342
+
343
+ steps:
344
+ - name: qa
345
+ agent: qa
346
+ secrets: [kinde-client-secret, r2-keys]
347
+ ```
348
+
349
+ - The engine resolves every declared ref host-side at run start; a missing
350
+ variable, unknown provider, or undeclared ref fails fast, naming the ref.
351
+ A resolved secret reaches only the steps that list it.
352
+ - Prefer a mechanical `command` step for secret-consuming work so the LLM and
353
+ the secret never meet; an `agent` step holds a secret only when its job
354
+ genuinely requires it.
355
+ - **`.env*` ban.** At run start the workspace root is scanned for `.env*`
356
+ files; any hit fails fast, naming the files. The convention fixtures
357
+ `.env.example`, `.env.template`, and `.env.sample` are whitelisted.
358
+ - **Output scrubbing.** Resolved secret values are scrubbed from every
359
+ engine-written output - captured step output, retry notes, artifacts, and
360
+ `state_dir` files.
361
+ - `from: file` reads a host file (`key` is the path, optional `json_key`
362
+ selects a JSON field); the `llm:` block accepts exactly one of `api_key_env`
363
+ or `api_key_secret`. See [docs/setup.md](docs/setup.md).
364
+
365
+ ### Per-stage networking
366
+
367
+ The sandbox is network-off by default. A step that legitimately needs egress
368
+ declares `network: true` and names exactly what it may reach:
369
+
370
+ ```yaml
371
+ steps:
372
+ - name: fetch-docs
373
+ command: curl -fsSL https://example.com/spec.json -o spec.json
374
+ mounts: [{ path: ".", mode: rw }] # the cwd + a place to write spec.json
375
+ network: true # egress for this step only
376
+ egress: { targets: [example.com] } # ...and only these hosts
377
+
378
+ - name: restore
379
+ command: .venv-sandbox/bin/pip install -e ".[dev]"
380
+ mounts: [{ path: ".", mode: rw }]
381
+ network: true
382
+ egress: { presets: [package-registries] } # pypi/npm/NuGet/cargo + VCS
383
+ ```
384
+
385
+ - `network: true` is scoped by the `egress:` allowlist, structurally: the
386
+ step's traffic is forced through a containerized egress proxy on a per-run
387
+ internal docker network with no route to the host, and a request to an
388
+ undeclared host is denied and logged. `egress: { class: browser }` is the
389
+ explicit broad-but-logged class (every connection logged, nothing
390
+ filtered); otherwise the allowlist is strict.
391
+ - A networked step that writes output must also declare a writable mount:
392
+ with no `mounts:` a step mounts nothing, so `curl ... -o spec.json` has no
393
+ writable cwd.
394
+ - Host services are reachable through the proxy's `host.docker.internal`
395
+ host-gateway alias, so an S3 SDK against a local MinIO works through the
396
+ enforced, logged path. Sandbox containers have no host route.
397
+ - Command, agent, and sandbox-action steps may declare it; the host-side
398
+ `commit` action never takes it. Author opt-in is the control - reserve it
399
+ for stages that need it (QA, integration).
400
+
401
+ ### Sync-back conflicts (host edits during a run)
402
+
403
+ Only declared-`rw` paths sync back to the real repo after a step. If the host
404
+ also edits a declared-`rw` path while the step is running, sync-back raises
405
+ `AE-SBX-016`; the host's version wins, the baseline is re-snapshotted, and the
406
+ next step runs clean instead of re-raising forever. For append-style logs
407
+ edited by both sides, declare them in the top-level `merge_on_conflict:`
408
+ list: they are union-merged - the host keeps its lines, the sandbox's new
409
+ lines are appended, and the run continues.
410
+
411
+ ### Dependency policy (locked by default)
412
+
413
+ The supply-chain dial is a pipeline-level `deps_policy:` field, **locked by
414
+ default**. The engine injects the policy into every agent step's context:
415
+
416
+ ```yaml
417
+ deps_policy: locked # or: open (default: locked)
418
+ lockfiles: [uv.lock] # or: [packages.lock.json, package-lock.json]
419
+ ```
420
+
421
+ - **`locked`** - the human-owned lockfile is the source of truth; the agent
422
+ may not add dependencies. If the design requires a missing dependency, the
423
+ agent declares it in its final response under `## Missing dependencies`,
424
+ which fails the run fast with one human lockfile edit - not a retry loop.
425
+ - **`open`** - the agent may add exact-pinned manifest entries directly
426
+ (never a package-manager `add` command); the network-on restore step
427
+ fetches them.
428
+
429
+ The **restore step** runs the toolchain's locked mode and fails when the
430
+ manifest and lockfile disagree:
431
+
432
+ | Toolchain | Locked restore | Lockfile |
433
+ | --- | --- | --- |
434
+ | .NET | `dotnet restore --locked-mode` | `packages.lock.json` |
435
+ | node | `npm ci` | `package-lock.json` |
436
+ | python | `uv sync --locked` | `uv.lock` |
437
+
438
+ The restore step also fails on known-CVE findings (per-toolchain thresholds:
439
+ NuGetAudit at `error`, `npm audit --audit-level=high`, `pip-audit`). A human
440
+ generates the lockfile once, on a network machine, and commits it - the
441
+ engine never fetches on the agent's behalf.
442
+
443
+ ### Host boundary (security model)
444
+
445
+ The orchestrator, tool layer, and the `commit` action run in the host process
446
+ - they hold your credentials and your full user account, so the engine keeps
447
+ anything agent-writable out of that process:
448
+
449
+ - **Consumer actions are sandbox-executed.** An action you register via an
450
+ extension is a command-shaped script (`script + params`), run through the
451
+ same sandbox path as command steps. Only the engine's trusted built-ins
452
+ (`commit`, document loaders) run host-side.
453
+ - **Extensions load from outside the workspace.** Any extension or tool
454
+ module whose file resolves under the workspace is refused before import,
455
+ naming the file.
456
+ - **The engine installs outside the workspace.** Install with pipx so the
457
+ venv is unmounted from the sandbox; a startup self-check refuses to run if
458
+ `agentic_dev_loop` resolves under the workspace.
459
+ - **Tooling is read-only by the loop.** At run start the pipeline file,
460
+ `agents_dir`, `additional_files`, `aidlc-docs/`, and `tools/` must be
461
+ git-clean relative to HEAD, and the `commit` action excludes those paths
462
+ from its sweep, so the loop never commits its own tooling.
463
+ - **`commit` stays host-side, deliberately.** It holds push credentials and
464
+ runs argv-style with a scrubbed child env and a hooks override, so repo
465
+ hooks never execute.
466
+
467
+ Net: the host executes nothing from the workspace, reads only git-clean
468
+ control files, and the only agent-writable host-consumed thing left is the
469
+ code the loop is building.
470
+
471
+ ## Authoring a loop for your own repo
472
+
473
+ ### Start with `agentic-dev-loop init`
474
+
475
+ ```bash
476
+ agentic-dev-loop init --profile python # or: --profile dotnet-node
477
+ ```
478
+
479
+ `init` writes the onboarding surface from a bundled toolchain profile:
480
+ `agentic-dev-loop/pipeline.yaml`, the three agent files, an executable
481
+ `trigger-agentic-dev-loop.sh`, `agentic-dev-loop/.engine-version` (the exact
482
+ installed engine version), any missing premise files, gitignore additions,
483
+ and an env-file template. It detects solution files, `package.json`
484
+ directories, lockfiles, README, and AGENTS.md, asks only what detection
485
+ cannot answer, and refuses to overwrite an existing `agentic-dev-loop/`
486
+ directory. A consumer-authored profile file can be passed by path instead of
487
+ a bundled name.
488
+
489
+ `agentic-dev-loop doctor --fast` is the per-run gate the trigger runs first:
490
+ hard failures abort the run (engine pin mismatch, engine runtime
491
+ availability, API version, git repo, `.env*` files); warnings print and
492
+ continue. On a hard failure the trigger runs `agentic-dev-loop upgrade`
493
+ exactly once, then the run fails hard if doctor still fails.
494
+
495
+ A consumer repo needs a pipeline file and the agent files it names, both
496
+ committed before the first run. `init` writes them; the layout is:
497
+
498
+ ```
499
+ <repo>/agentic-dev-loop/pipeline.yaml
500
+ <repo>/agentic-dev-loop/sdk_agents/dev.agent.md
501
+ <repo>/agentic-dev-loop/sdk_agents/review.agent.md
502
+ <repo>/agentic-dev-loop/sdk_agents/qa.agent.md
503
+ ```
504
+
505
+ ### The agent files
506
+
507
+ Each `sdk_agents/<name>.agent.md` is a markdown prompt plus YAML frontmatter:
508
+ `name` matches a step's `agent:` value; `tools` lists the tool names the step
509
+ may use (`terminal` always; verdict steps also need `record_verdict`; a repo
510
+ can add more via extensions). A step configured with `verdicts:` whose agent
511
+ file does not grant `record_verdict` fails at load, never at routing time.
512
+
513
+ ```markdown
514
+ ---
515
+ name: review # matches a step's `agent:` value
516
+ tools: [terminal, record_verdict]
517
+ ---
518
+ # Review Agent
519
+ ## WORKFLOW # numbered steps
520
+ ## RULES # absolute never/always rules
521
+ ## SANDBOX NOTES # the sandbox truth for THIS step
522
+ ## OUTPUT # the verdict contract
523
+ ```
524
+
525
+ Three roles, three contracts:
526
+
527
+ | Role | Verdict contract | Typical sandbox |
528
+ | --- | --- | --- |
529
+ | dev | none - the final response is captured as the `produces:` summary | rw mount, offline |
530
+ | review | `record_verdict` with `APPROVED` / `NEEDS CHANGES`; the prose keeps the findings for humans | ro mount, offline |
531
+ | qa | `record_verdict` with `PASS` / `FAIL`; map `unverifiable: UNVERIFIABLE` if you want a third way | shaped by the repo: rw + network + `class: browser` when it boots servers and drives a browser; ro + offline when it probes the CLI only |
532
+
533
+ Conventions that make a good agent file:
534
+
535
+ - **State the sandbox truth in the prompt.** Every agent file carries a
536
+ `SANDBOX NOTES` section naming exactly what its step allows: mounts ro or
537
+ rw, network on or off, and which gates have already run.
538
+ - **Gates are the source of truth - cite the record, never re-run.** Review
539
+ and qa never re-run builds or suites; qa maps every acceptance criterion to
540
+ existing coverage and probes only the remainder.
541
+ - **dev enumerates its impossible set and stops at it.** List the commands
542
+ the sandbox cannot run (offline package fetches, browser downloads), one
543
+ attempt per command - keeps the productive loop and kills doomed retries.
544
+ - **Failure output returns as *Previous attempt feedback*.** Steps route
545
+ `on_fail: { goto: dev, retry: true }`, so gate failure output lands in the
546
+ dev agent's next context; the dev prompt makes every listed issue a
547
+ required fix.
548
+ - **No git mutations, ever.** The `commit` step is the single commit point.
549
+
550
+ ### The pipeline file
551
+
552
+ The full field reference is above; this is the stage lineup and what to
553
+ adapt per repo.
554
+
555
+ | Stage | Kind | Typical shape | Why |
556
+ | --- | --- | --- | --- |
557
+ | dev | agent | rw, offline | does the work; edits are untracked |
558
+ | restore | command | network + `presets: [package-registries]` | fetches what dev pinned (`deps_policy: open`) or the locked install |
559
+ | test | command | offline is fine (warm cache) | exit code is the verdict; the source of truth |
560
+ | editor-tests | command | network + `presets: [package-registries]` | frontend suites that cannot run offline; a separate step keeps its failure diagnosis and timeout independent of the backend suite |
561
+ | conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks |
562
+ | review | agent | ro, offline | static conformance verdict |
563
+ | qa | agent | per repo (see table above) | live-behaviour probes; inject secrets only if the probes need them |
564
+ | commit | action | host-side | the single commit point |
565
+
566
+ Adaptation checklist for a new repo:
567
+
568
+ 1. `llm:` - model, `base_url`, `api_key_env`.
569
+ 2. `llms:` (optional) - named profiles for multi-provider lanes (keep review
570
+ on the strongest model); a step opts in with `llm: <name>`.
571
+ 3. `profile:` - name a bundled profile (`python`, `dotnet-node`) or a
572
+ consumer profile path.
573
+ 4. `caches:` - profile-supplied toolchain caches plus any run-specific dirs;
574
+ caches are never synced back, and a step's `setup:` hook runs with them
575
+ mounted so it can warm them.
576
+ 5. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
577
+ 6. `secrets:` - refs only, resolved from host env; each step opts in to the
578
+ refs it needs.
579
+ 7. `egress:` - `presets: [package-registries]` for fetch steps;
580
+ `class: browser` (broad-but-logged) for browser-tier qa only.
581
+ 8. `deps_policy:` and `lockfiles:` - see
582
+ [Dependency policy](#dependency-policy-locked-by-default).
583
+ 9. The dev prompt's impossible set - write it for your toolchain.
584
+ 10. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
585
+ docs: shared context for every agent step.
586
+
587
+ Validate before the first run - dry-run loads and prints the resolved plan
588
+ with no LLM, no Docker, no state writes:
589
+
590
+ ```bash
591
+ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run
592
+ ```
593
+
594
+ Then commit the tooling and run for real.
595
+
596
+ ## Development environment
597
+
598
+ ```bash
599
+ git clone git@github.com:lukedawilson/agentic-dev-loop.git
600
+ cd agentic-dev-loop
601
+ pipx install agentic-dev-loop # engine venv OUTSIDE the workspace (self-check)
602
+ export OPENAI_API_KEY=... # set your LLM key in the host env (no .env in the repo)
603
+ python -m pytest tests/ -q # fast suite (in-repo .venv is for tests only)
604
+ python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
605
+ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run # run the shipped pipeline
606
+ ./run-demo.sh # viz demo - scripted run replay, no pipeline needed
607
+ ```
608
+
609
+ - `setup-venv.sh` builds the throwaway `.venv` for local `pytest` runs. The
610
+ loop itself must run from the pipx install - never an in-workspace `.venv`,
611
+ which the startup self-check refuses.
612
+ - Docker-marked integration tests need a local daemon; CI runs them on Linux.
613
+ - The shipped pipeline (`agentic-dev-loop/pipeline.yaml`) dogfoods the tool
614
+ on its own repo - dev → test → review → qa → commit, with the engine's own
615
+ three agent definitions in `sdk_agents/`.
616
+ - CI: `ci.yml` (unit + integration suite, ubuntu), `publish-image.yml`
617
+ (multi-arch dev image, amd64 + arm64), `release.yml` (PyPI publishing).
618
+ - Pre-publish tooling can point `AGENTIC_DEV_LOOP_DEV_IMAGE` at a locally
619
+ built dev image.
620
+
621
+ ## Contributing
622
+
623
+ - **Read [AGENTS.md](AGENTS.md) first** - it is the instruction set for both
624
+ human and agent contributors.
625
+ - Development is structured by **AI-DLC** - the lifecycle workflow in
626
+ `.aidlc/aidlc-rules/aws-aidlc-rules/` with working docs in `aidlc-docs/`.
627
+ The bundled document loader autodetects the current unit via
628
+ `--aidlc-unit`, or loads any plan with `--plan`.
629
+ - OpenCode commands over the workflow: `/unit-status`, `/epic-status`,
630
+ `/ticket-details`, `/update-subtask`, `/apply-aidlc-patches`, and more.
631
+ - Working extension samples (actions, tools, document loaders) ship as
632
+ `tests/sample_ext.py`.
633
+
634
+ ---
635
+
636
+ Built on [OpenHands SDK](https://github.com/OpenHands/software-agent-sdk) and
637
+ [LangGraph](https://github.com/langchain-ai/langgraph).