workhorse-workflows 0.2.0__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 (249) hide show
  1. workhorse_workflows-0.2.0/.gitignore +239 -0
  2. workhorse_workflows-0.2.0/LICENSE +21 -0
  3. workhorse_workflows-0.2.0/PKG-INFO +253 -0
  4. workhorse_workflows-0.2.0/README.md +223 -0
  5. workhorse_workflows-0.2.0/pyproject.toml +107 -0
  6. workhorse_workflows-0.2.0/src/workhorse_workflows/__init__.py +16 -0
  7. workhorse_workflows-0.2.0/src/workhorse_workflows/author/__init__.py +15 -0
  8. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/__init__.py +71 -0
  9. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/_blueprint.py +13 -0
  10. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/_stubs.py +33 -0
  11. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/artifacts.py +477 -0
  12. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/config.py +174 -0
  13. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/coverage.py +182 -0
  14. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/epics.py +83 -0
  15. workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/stories.py +636 -0
  16. workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/__init__.py +17 -0
  17. workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/flow.py +275 -0
  18. workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/nodes/__init__.py +21 -0
  19. workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/nodes/parity.py +292 -0
  20. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/audit-story.md +93 -0
  21. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/decompose-epics.md +98 -0
  22. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/design-mockup.md +80 -0
  23. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/resolve-integrity.md +87 -0
  24. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/resolve-operator.md +78 -0
  25. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/review-coverage.md +76 -0
  26. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/review-epics.md +54 -0
  27. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/rework-epics.md +37 -0
  28. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/rework-story.md +61 -0
  29. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/split-stories.md +115 -0
  30. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/assess-parity-unit.md +58 -0
  31. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/assess-unit.md +89 -0
  32. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/fix-record.md +61 -0
  33. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/partition-findings.md +84 -0
  34. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/plan-units.md +85 -0
  35. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/resolve-operator.md +78 -0
  36. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/write-epic.md +97 -0
  37. workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/write-story.md +157 -0
  38. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/__init__.py +20 -0
  39. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/paths.py +202 -0
  40. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/__init__.py +98 -0
  41. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/_base.py +36 -0
  42. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/main.py +258 -0
  43. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/parity.py +42 -0
  44. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/survey.py +197 -0
  45. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/__init__.py +53 -0
  46. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/blueprint.py +15 -0
  47. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/inventory.py +377 -0
  48. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/records.py +373 -0
  49. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/stubs.py +52 -0
  50. workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/units.py +203 -0
  51. workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/__init__.py +33 -0
  52. workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/flow.py +595 -0
  53. workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/nodes/__init__.py +21 -0
  54. workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/nodes/config.py +100 -0
  55. workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/nodes/partition.py +313 -0
  56. workhorse_workflows-0.2.0/src/workhorse_workflows/author/workflow.py +1088 -0
  57. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/__init__.py +53 -0
  58. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dev/__init__.py +18 -0
  59. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dev/flow.py +574 -0
  60. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/docs/__init__.py +20 -0
  61. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/docs/flow.py +368 -0
  62. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dream/__init__.py +15 -0
  63. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dream/flow.py +119 -0
  64. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dream/nodes.py +329 -0
  65. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix/__init__.py +16 -0
  66. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix/flow.py +361 -0
  67. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix_ci/__init__.py +16 -0
  68. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix_ci/flow.py +229 -0
  69. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/genesis/__init__.py +17 -0
  70. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/genesis/flow.py +385 -0
  71. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/genesis/nodes.py +674 -0
  72. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/nodes/__init__.py +50 -0
  73. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/nodes/pr.py +719 -0
  74. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/apply-genesis-conventions.md +78 -0
  75. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/apply-qa-fixes.md +137 -0
  76. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/apply-review.md +182 -0
  77. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/audit-qa.md +72 -0
  78. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/check-code-reuse.md +90 -0
  79. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/code-reuse.md +94 -0
  80. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/code-review.md +60 -0
  81. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/document-story.md +99 -0
  82. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/dream-reflect.md +101 -0
  83. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-ci.md +32 -0
  84. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-genesis.md +53 -0
  85. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-lint.md +44 -0
  86. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-merge.md +48 -0
  87. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-regression.md +99 -0
  88. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/implement-plan.md +222 -0
  89. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/plan-qa.md +279 -0
  90. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/plan-story.md +395 -0
  91. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/qa-fix-item.md +77 -0
  92. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/qa-story.md +114 -0
  93. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/refine-plan.md +301 -0
  94. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/repair-qa-context.md +43 -0
  95. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/replan-epic.md +28 -0
  96. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/report-qa-dev-pass.md +90 -0
  97. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/report-qa-dev.md +86 -0
  98. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/resolve-operator.md +77 -0
  99. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/review-implementation.md +213 -0
  100. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/review-qa-plan.md +49 -0
  101. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/review-story-documentation.md +46 -0
  102. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/setup-fix.md +149 -0
  103. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/triage-qa.md +168 -0
  104. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/__init__.py +24 -0
  105. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/flow.py +1106 -0
  106. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/__init__.py +43 -0
  107. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/evidence.py +624 -0
  108. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/hygiene.py +293 -0
  109. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/qa.py +264 -0
  110. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/regression.py +417 -0
  111. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/review/__init__.py +18 -0
  112. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/review/flow.py +510 -0
  113. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/__init__.py +40 -0
  114. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/backlog.py +652 -0
  115. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/blueprint.py +13 -0
  116. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/ci.py +372 -0
  117. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/commits.py +125 -0
  118. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/contract.py +50 -0
  119. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/dev.py +575 -0
  120. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/docs.py +393 -0
  121. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/okf.py +119 -0
  122. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/ostler_qa.py +151 -0
  123. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/paths.py +235 -0
  124. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/queue.py +1189 -0
  125. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/review.py +212 -0
  126. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/__init__.py +211 -0
  127. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/_base.py +37 -0
  128. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/backlog.py +74 -0
  129. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/ci.py +85 -0
  130. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/dev.py +253 -0
  131. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/docs.py +129 -0
  132. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/dream.py +66 -0
  133. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/genesis.py +133 -0
  134. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/okf.py +32 -0
  135. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/pr.py +101 -0
  136. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/qa.py +475 -0
  137. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/queue.py +192 -0
  138. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/review.py +107 -0
  139. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/story.py +56 -0
  140. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/story.py +234 -0
  141. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/story_status.py +164 -0
  142. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/stubs.py +77 -0
  143. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/worktree.py +92 -0
  144. workhorse_workflows-0.2.0/src/workhorse_workflows/coder/workflow.py +1030 -0
  145. workhorse_workflows-0.2.0/src/workhorse_workflows/hello_world/__init__.py +12 -0
  146. workhorse_workflows-0.2.0/src/workhorse_workflows/hello_world/prompts/greet.md +12 -0
  147. workhorse_workflows-0.2.0/src/workhorse_workflows/hello_world/workflow.py +87 -0
  148. workhorse_workflows-0.2.0/src/workhorse_workflows/kit/__init__.py +140 -0
  149. workhorse_workflows-0.2.0/src/workhorse_workflows/kit/credentials.py +81 -0
  150. workhorse_workflows-0.2.0/src/workhorse_workflows/kit/git.py +353 -0
  151. workhorse_workflows-0.2.0/src/workhorse_workflows/kit/github.py +173 -0
  152. workhorse_workflows-0.2.0/src/workhorse_workflows/kit/workspace.py +389 -0
  153. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/__init__.py +19 -0
  154. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/__init__.py +37 -0
  155. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/coverage.py +391 -0
  156. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/prepare.py +149 -0
  157. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/waivers.py +112 -0
  158. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/enumerate-surfaces.md +84 -0
  159. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/investigate.md +225 -0
  160. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/recheck-coverage.md +130 -0
  161. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/walkthrough-web.md +193 -0
  162. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/__init__.py +23 -0
  163. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/blueprint.py +13 -0
  164. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/checkpoint.py +251 -0
  165. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/paths.py +117 -0
  166. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/schemas.py +285 -0
  167. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/stubs.py +60 -0
  168. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/worklist.py +184 -0
  169. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/__init__.py +25 -0
  170. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/flow.py +356 -0
  171. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/nodes/__init__.py +35 -0
  172. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/nodes/stack.py +186 -0
  173. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/nodes/walkthrough.py +423 -0
  174. workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/workflow.py +513 -0
  175. workhorse_workflows-0.2.0/src/workhorse_workflows/research/__init__.py +7 -0
  176. workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/__init__.py +34 -0
  177. workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/_blueprint.py +13 -0
  178. workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/program.py +204 -0
  179. workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/publish.py +55 -0
  180. workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/setup.py +87 -0
  181. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/define-new-direction.md +70 -0
  182. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/extend-program.md +57 -0
  183. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/gate-check.md +45 -0
  184. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/implement-experiment.md +56 -0
  185. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/lead-goal-review.md +67 -0
  186. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/record-result.md +33 -0
  187. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/research-lead-review.md +77 -0
  188. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/revive-gate.md +43 -0
  189. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/rework-experiment.md +45 -0
  190. workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/select-next-gate.md +37 -0
  191. workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/__init__.py +9 -0
  192. workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/new_program.py +121 -0
  193. workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/templates/PROGRESS.md +15 -0
  194. workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/templates/README.md +93 -0
  195. workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/templates/gate.md +41 -0
  196. workhorse_workflows-0.2.0/src/workhorse_workflows/research/schemas.py +284 -0
  197. workhorse_workflows-0.2.0/src/workhorse_workflows/research/workflow.py +534 -0
  198. workhorse_workflows-0.2.0/tests/_fakes.py +41 -0
  199. workhorse_workflows-0.2.0/tests/author/__init__.py +3 -0
  200. workhorse_workflows-0.2.0/tests/author/conftest.py +79 -0
  201. workhorse_workflows-0.2.0/tests/author/parity_surveyor/__init__.py +1 -0
  202. workhorse_workflows-0.2.0/tests/author/parity_surveyor/test_flow.py +397 -0
  203. workhorse_workflows-0.2.0/tests/author/parity_surveyor/test_parity.py +634 -0
  204. workhorse_workflows-0.2.0/tests/author/shared/__init__.py +1 -0
  205. workhorse_workflows-0.2.0/tests/author/shared/survey/__init__.py +1 -0
  206. workhorse_workflows-0.2.0/tests/author/shared/survey/test_inventory.py +355 -0
  207. workhorse_workflows-0.2.0/tests/author/shared/survey/test_records.py +480 -0
  208. workhorse_workflows-0.2.0/tests/author/shared/survey/test_units.py +249 -0
  209. workhorse_workflows-0.2.0/tests/author/surveyor/__init__.py +1 -0
  210. workhorse_workflows-0.2.0/tests/author/surveyor/test_config.py +117 -0
  211. workhorse_workflows-0.2.0/tests/author/surveyor/test_flow.py +650 -0
  212. workhorse_workflows-0.2.0/tests/author/surveyor/test_partition.py +422 -0
  213. workhorse_workflows-0.2.0/tests/author/test_workflow.py +851 -0
  214. workhorse_workflows-0.2.0/tests/coder/__init__.py +4 -0
  215. workhorse_workflows-0.2.0/tests/coder/conftest.py +208 -0
  216. workhorse_workflows-0.2.0/tests/coder/dev/__init__.py +1 -0
  217. workhorse_workflows-0.2.0/tests/coder/dev/test_flow.py +866 -0
  218. workhorse_workflows-0.2.0/tests/coder/docs/__init__.py +1 -0
  219. workhorse_workflows-0.2.0/tests/coder/docs/test_flow.py +706 -0
  220. workhorse_workflows-0.2.0/tests/coder/dream/__init__.py +1 -0
  221. workhorse_workflows-0.2.0/tests/coder/dream/test_flow.py +338 -0
  222. workhorse_workflows-0.2.0/tests/coder/fix/__init__.py +1 -0
  223. workhorse_workflows-0.2.0/tests/coder/fix/test_flow.py +593 -0
  224. workhorse_workflows-0.2.0/tests/coder/fix_ci/__init__.py +1 -0
  225. workhorse_workflows-0.2.0/tests/coder/fix_ci/test_flow.py +410 -0
  226. workhorse_workflows-0.2.0/tests/coder/genesis/__init__.py +1 -0
  227. workhorse_workflows-0.2.0/tests/coder/genesis/test_flow.py +488 -0
  228. workhorse_workflows-0.2.0/tests/coder/qa/__init__.py +1 -0
  229. workhorse_workflows-0.2.0/tests/coder/qa/test_evidence_scope.py +78 -0
  230. workhorse_workflows-0.2.0/tests/coder/qa/test_evidence_unsupported_pass.py +89 -0
  231. workhorse_workflows-0.2.0/tests/coder/qa/test_flow.py +1381 -0
  232. workhorse_workflows-0.2.0/tests/coder/qa/test_hygiene.py +95 -0
  233. workhorse_workflows-0.2.0/tests/coder/review/__init__.py +1 -0
  234. workhorse_workflows-0.2.0/tests/coder/review/test_flow.py +659 -0
  235. workhorse_workflows-0.2.0/tests/coder/test_commits.py +118 -0
  236. workhorse_workflows-0.2.0/tests/coder/test_prompt_stack_neutrality.py +218 -0
  237. workhorse_workflows-0.2.0/tests/coder/test_qa_plan_prompt_schema.py +111 -0
  238. workhorse_workflows-0.2.0/tests/coder/test_status_line_ownership.py +59 -0
  239. workhorse_workflows-0.2.0/tests/coder/test_workflow.py +1140 -0
  240. workhorse_workflows-0.2.0/tests/okf_builder/__init__.py +5 -0
  241. workhorse_workflows-0.2.0/tests/okf_builder/conftest.py +138 -0
  242. workhorse_workflows-0.2.0/tests/okf_builder/test_workflow.py +411 -0
  243. workhorse_workflows-0.2.0/tests/research/test_workflow.py +477 -0
  244. workhorse_workflows-0.2.0/tests/test_hello_world.py +99 -0
  245. workhorse_workflows-0.2.0/tests/test_kit_git.py +100 -0
  246. workhorse_workflows-0.2.0/tests/test_kit_workspace.py +62 -0
  247. workhorse_workflows-0.2.0/tests/test_prompt_output_shape.py +171 -0
  248. workhorse_workflows-0.2.0/tests/test_prompt_variables.py +317 -0
  249. workhorse_workflows-0.2.0/tests/test_prompts_exist.py +91 -0
@@ -0,0 +1,239 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
219
+
220
+ # Aider (workhorse `aider` backend writes these into whatever repo it runs in:
221
+ # chat/input history, tags cache). Keep the committable config files, per
222
+ # https://aider.chat/docs/faq.html#how-can-i-add-all-the-files-to-the-chat
223
+ .aider*
224
+ !.aider.conf.yml
225
+ !.aider.model.settings.yml
226
+ !.aider.model.metadata.json
227
+
228
+ .playwright-mcp
229
+
230
+ .agents/*
231
+
232
+ /.agents/runs/
233
+ /.agents/skills/
234
+ /.agents/prompts/
235
+ /.agents/workflows/
236
+ /.agents/operator/
237
+ /.agents/local.compose.yaml
238
+ /.agents/agents-context.json
239
+ /.agents/agents-context.*.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gabriel Côté
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,253 @@
1
+ Metadata-Version: 2.4
2
+ Name: workhorse-workflows
3
+ Version: 0.2.0
4
+ Summary: The stablemate agent workflows — hello-world, research, author, coder, okf-builder — as an installable package workhorse discovers by entry point.
5
+ Project-URL: Homepage, https://github.com/GabrielCpp/stablemate/tree/main/workflows
6
+ Project-URL: Repository, https://github.com/GabrielCpp/stablemate
7
+ Project-URL: Issues, https://github.com/GabrielCpp/stablemate/issues
8
+ Author: Gabriel Côté
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,automation,claude,llm,orchestration,workflow
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Build Tools
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: gitpython>=3.1
21
+ Requires-Dist: ostler>=0.1
22
+ Requires-Dist: pygithub>=2.0
23
+ Requires-Dist: pyyaml>=6.0
24
+ Requires-Dist: ruamel-yaml>=0.18
25
+ Requires-Dist: unidiff>=0.7
26
+ Requires-Dist: workhorse-agent<2,>=1
27
+ Provides-Extra: test
28
+ Requires-Dist: pytest>=8.0; extra == 'test'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # workhorse-workflows
32
+
33
+ The stablemate agent workflows as an installable Python distribution: `research`,
34
+ `author`, `okf-builder` and `coder`, each a checkpointed state machine. Workhorse is the
35
+ engine; this is its content.
36
+
37
+ **Not on PyPI yet** — install it from a checkout of the
38
+ [stablemate](https://github.com/GabrielCpp/stablemate) workspace, into the *same*
39
+ interpreter as workhorse, because a workflow runs in workhorse's own process:
40
+
41
+ ```bash
42
+ make sync # at the workspace root: engine + workflows, one venv
43
+ uv run workhorse-research run # the workflow's own command
44
+ ```
45
+
46
+ The shape came from the
47
+ [workflow-as-python-state-machine](../docs/plans/workflow-as-python-state-machine.md)
48
+ design brief, which shipped and is now kept only for its reasoning. What is still
49
+ outstanding *here* is noted under [Status](#status).
50
+
51
+ ## How a workflow gets a command
52
+
53
+ Each workflow binds one, and that is the only way it is reached:
54
+
55
+ ```toml
56
+ [project.scripts]
57
+ workhorse-research = "workhorse_workflows.research.workflow:main"
58
+ ```
59
+
60
+ The script points at `main` — the callable `console_script(workflow.entry_point(Entry))`
61
+ *returns*, since a script target is called after import, not during it. `entry_point`
62
+ declares which flow a bare run starts and returns the registry; `console_script` — which
63
+ lives in `workhorse.cli`, the ring a console script actually starts — turns it into that
64
+ callable.
65
+
66
+ Nothing resolves a workflow by **name**: the command carries the `Registry` it runs, so
67
+ there is no lookup to disagree with, no group to register in, and a workflow with no row
68
+ in this table has no command at all — a gap you meet at install time rather than
69
+ mid-resolution. Workhorse itself ships no executable.
70
+
71
+ Nothing here is a plugin API for workhorse either. A third party ships their own
72
+ distribution with their own scripts, and the engine still knows no workflow's
73
+ vocabulary — this package imports the engine and never the other way.
74
+
75
+ ## Installed unpacked, always
76
+
77
+ Workhorse renders a workflow's prompts with a filesystem template loader rooted at the
78
+ workflow's own package directory, and keys per-node overrides on that directory's
79
+ name. A wheel installed by pip or uv satisfies this; a zipapp or zip-safe egg does
80
+ not, and workhorse refuses one at startup rather than failing later as a missing
81
+ template.
82
+
83
+ ## A workflow reads no environment (load-bearing)
84
+
85
+ **Prohibited:** `os.environ`, `os.getenv` and their kin anywhere under
86
+ `src/workhorse_workflows/`. Everything a node or a state needs is an **argument** or a
87
+ **workflow parameter** — a field on the `Workflow` subclass, settable with `--param`.
88
+
89
+ The reason is the checkpoint. A run's inputs are recorded there, so a resume days later
90
+ on another machine replays the values the run actually started with; a value read from
91
+ the environment is in no checkpoint, so a resume silently takes a *different* one and
92
+ nothing in the artifacts says so. It is also absent from the run's telemetry, and
93
+ unreachable from the CLI — `--params` cannot set it — which splits the operator contract
94
+ across two spellings that no test compares.
95
+
96
+ The **process boundary** is where the environment legitimately lives, and it is outside
97
+ this package: `workhorse/cli/run.py` and `workhorse/entrypoint.sh` translate `$FOO` into
98
+ `--params` once, on the way in. That is why `repo_dir` reaches every workflow without any
99
+ of them reading `AGENT_REPO_DIR`.
100
+
101
+ Ambient *paths* — `repo_dir`, `docs_path`, `workspace_file` — are wanted by roughly every
102
+ second node and chosen by no state, which is exactly the shape that used to be an
103
+ environment read. They are fields, and `Workflow.injects` (see `coder/shared/paths.AMBIENT`)
104
+ fills them into any node or sub-flow that declares a parameter of the same name and was
105
+ not passed one. A callsite value always wins, and an empty field injects nothing, so the
106
+ target's own default stands.
107
+
108
+ One exception, and it is a security property rather than an exemption:
109
+ `kit/credentials.py` resolves tokens from the environment **because** a secret must never
110
+ become a `--param` — params are checkpointed to disk and echoed in logs and telemetry,
111
+ which is precisely what a token must not be. A credential crosses into a subprocess by
112
+ *name* (`_git_network_command` names the variable; git expands it), never by value.
113
+ Keeping that in one auditable module is the point.
114
+
115
+ The rule is enforced, not just written down:
116
+
117
+ ```bash
118
+ make check-no-env # also runs as part of `make test`
119
+ ```
120
+
121
+ ## A workflow does not spell a doc path (load-bearing)
122
+
123
+ **Where a document lives is ostler's answer.** The epics root, the epic folder, the story
124
+ folder, the epics index, the backlog file, the feature book and its waivers and screenshots
125
+ all come from `ostler.path`; no module under `src/workhorse_workflows/` writes
126
+ `docs/epics`, `docs/backlog.md` or `docs/features` as a literal.
127
+
128
+ The reason is that these locations are **configurable** — a repo moves them with
129
+ `docRoots:` in `ostler.yml` / `agents.yml` — and that epic directories are **numbered**
130
+ (`0001-checkout-flow`) while every queue entry, prompt and operator names them by bare
131
+ slug. A hand-built join gets both wrong, and gets them wrong *silently*: the run writes
132
+ into a directory nothing reads, and `ostler doctor` reports a book that is not there. Two
133
+ derivations of the same location is the failure the rule exists for; the second one never
134
+ learns about the config.
135
+
136
+ What a workflow still owns is the **filename it invented** — `context.md`, `feedback.md`,
137
+ `_author-context.md`, `<gate>-context.md`, `attempts.md`, `dependencies.json` — joined onto
138
+ a directory ostler resolved. Run artifacts that are not documents at all (`.agents/operator`,
139
+ `.agents/okf-build`, the dream inbox and ledger, the surveyor's `docs/survey/` scratch) stay
140
+ with the workflow, because ostler has no opinion about them.
141
+
142
+ Each workflow's `shared/paths.py` is the one place that calls `ostler.path`, and it is also
143
+ where an operator override is honoured: a `backlog` or `epics_dir` **parameter** wins when
144
+ it is set, and empty — the normal case — means "as this repo configures it". An override
145
+ still gets ostler's resolution rules applied inside it, via the `*_under` family.
146
+
147
+ Two probes look like exceptions and are not: the repo-root resolvers test for a `docs/epics/`
148
+ directory, and that is how a *root* is recognized before there is a root to ask ostler about.
149
+
150
+ **And what is *in* the document is ostler's answer too.** A node that reads a backlog entry,
151
+ an epic's stories, a gate's frontmatter or a table in the book goes through
152
+ `ostler.markdown` — `find_section` / `Section.bullets` / `Bullet.bracketed` /
153
+ `walk_tables` — not a `^\s*-\s*\[(\w+)\]` of its own. Same failure mode as a hand-built
154
+ path, one layer down: a bullet regex matches inside a fenced example, misses the
155
+ `- **Status**:` spelling of the field it wants, and reports a confident wrong answer rather
156
+ than raising. Agent *output* is different — a CLI's log line has no grammar, and regex is
157
+ what reads it. The boundary, the parser for each format, and how to declare an exemption
158
+ are in the `stablemate-structured-parsing` skill; `make check-parsers` enforces it.
159
+
160
+ ## A prompt does not name a skill (load-bearing)
161
+
162
+ **Which skill teaches a subject is the repo's answer, asked for by tag.** A prompt in here
163
+ has never met the repo it will run against, so it may not write
164
+ `instruction_refs("go-testing", "flutter-testing", "react-router-testing", …)` — the menu
165
+ of every stack the author happened to think of. It asks for the capability instead:
166
+
167
+ ```jinja
168
+ {% raw %}{{ find_by_tags("web", "tests") }}{% endraw %}
169
+ ```
170
+
171
+ The query is an AND over the `tags:` in each installed skill's front matter, it renders the
172
+ matching skills' paths (backticked, comma-joined) and it renders **nothing** when the repo
173
+ installs no match — so the sentence around it carries a `| default("(none installed — …)",
174
+ true)`, or the whole paragraph sits behind `{% raw %}{% if %}{% endraw %}`.
175
+
176
+ The hand-listed menu was wrong in both directions. A repo with a stack the list forgot got
177
+ no guidance at all, and — the defect that cost a run — a repo with only a web app and no
178
+ mobile code rendered `plan-story` with ten mentions of Flutter and Dart, because the helper
179
+ correctly dropped the unresolvable names while the prose around it still enumerated them.
180
+ `tests/coder/test_prompt_stack_neutrality.py` renders every coder prompt against a
181
+ one-stack manifest and holds that line.
182
+
183
+ The vocabulary the coder prompts query is `runbook`, `standards`, `tests`, `qa` and
184
+ `codegen`, each combined with a layer — `backend`, `cli`, `web`, `mobile`, `infra`. It is
185
+ documented for skill authors in the `stablemate-agent-library` skill, which is where
186
+ someone adding `tags:` to a skill is reading. Adding a *new* tag to a prompt means teaching
187
+ that vocabulary there in the same change; a tag no skill declares silently matches nothing.
188
+
189
+ ## Layout
190
+
191
+ **One directory per machine.** A workflow is a graph plus the sub-graphs it hands off to,
192
+ and each of those is a state machine with nodes of its own. The layout says so: a machine's
193
+ `flow.py` and the nodes only that machine calls are one directory, and nothing else is.
194
+
195
+ ```
196
+ src/workhorse_workflows/
197
+ kit/ shared workflow-side helpers (git.py, github.py, workspace.py)
198
+ <workflow>/
199
+ workflow.py the main machine — one Workflow subclass, `main`, nothing else
200
+ nodes/ the callables only workflow.py calls, one module per subject
201
+ <flow>/ one directory per sub-graph, named for the flow:
202
+ flow.py its Workflow subclass, reached by handoff() or run directly
203
+ nodes.py the callables only this flow calls (→ nodes/ when it grows)
204
+ shared/ what a second machine also reaches:
205
+ blueprint.py the one Blueprint every node in the workflow registers on
206
+ paths.py the only caller of `ostler.path`: doc dirs + this workflow's filenames
207
+ schemas.py agent-reply schemas and node return types (→ schemas/ when it grows)
208
+ stubs.py the --dry-run stand-ins
209
+ <subject>.py a node module more than one machine calls
210
+ prompts/ the agent-facing markdown, rendered by a filesystem template loader
211
+
212
+ tests/<workflow>/ outside src/ and outside the wheel; mirrors the tree above —
213
+ tests/<workflow>/<flow>/test_flow.py, plus one test_workflow.py
214
+ ```
215
+
216
+ **What goes in `shared/` is a count, not a judgement.** Every module belongs to the machine
217
+ that calls it; a module a *second* machine also calls moves to `shared/`, and the move is
218
+ mechanical enough to check by grep. That is what keeps `coder/qa/nodes/evidence.py` beside
219
+ the QA flow (one caller) while `coder/shared/story.py` is shared (seven), and why a shared
220
+ node module keeps the name of its **subject** rather than of the flow that reads it most —
221
+ naming one of two callers is the mirroring this layout undoes.
222
+
223
+ A workflow with no sub-flows has no `shared/` and no `<flow>/`: with one machine there is
224
+ nothing to share, which is why `research/` is `workflow.py` + `nodes/` + a `scaffold/`
225
+ package and nothing more.
226
+
227
+ Prompts stay at the workflow root even though flows have directories of their own, because
228
+ `handoff` subscopes only the run *writer*, not the environment: a sub-flow's prompt path
229
+ resolves against the **parent** package directory.
230
+
231
+ Imports point one way: `workflow.py` imports `nodes/`, each `<flow>/` and `shared/`; a
232
+ `<flow>/` imports its own nodes and `shared/`; nothing under either imports `workflow.py`,
233
+ and nothing in `shared/` imports a flow.
234
+
235
+ How small each of those files has to be is **normative, not per-workflow taste** — one
236
+ subject per module, `nodes/` is a package even when it holds three functions, and
237
+ `~400 lines` is the trigger to apply the rule. This README is where that rule is stated;
238
+ the argument behind it is "One workflow, several files" in the
239
+ [retired design brief](../docs/plans/workflow-as-python-state-machine.md).
240
+ `coder`'s nodes alone run to ~6,600 lines across 18
241
+ modules, and a single `nodes.py` at that size is `scriptutil.py` again.
242
+
243
+ ## Status
244
+
245
+ All four workflows are ported and resolve through the entry-point group; the YAML engine
246
+ they came from is retired, so this package is the only place a stablemate workflow lives.
247
+
248
+ What is still outstanding: this distribution is unpublished, so there is no `pip install`
249
+ route to it and a `pipx` layout needs `pipx inject workhorse-agent <this distribution>`
250
+ to land it in the engine's venv. Farrier also still validates a `workflows:` selection
251
+ against a `workflows/<name>/` directory in a library layer rather than against the entry
252
+ points — see the end of
253
+ [farrier/docs/LAYOUT.md](../farrier/docs/LAYOUT.md).