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.
- workhorse_workflows-0.2.0/.gitignore +239 -0
- workhorse_workflows-0.2.0/LICENSE +21 -0
- workhorse_workflows-0.2.0/PKG-INFO +253 -0
- workhorse_workflows-0.2.0/README.md +223 -0
- workhorse_workflows-0.2.0/pyproject.toml +107 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/__init__.py +16 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/__init__.py +15 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/__init__.py +71 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/_blueprint.py +13 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/_stubs.py +33 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/artifacts.py +477 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/config.py +174 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/coverage.py +182 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/epics.py +83 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/nodes/stories.py +636 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/__init__.py +17 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/flow.py +275 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/nodes/__init__.py +21 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/parity_surveyor/nodes/parity.py +292 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/audit-story.md +93 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/decompose-epics.md +98 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/design-mockup.md +80 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/resolve-integrity.md +87 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/resolve-operator.md +78 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/review-coverage.md +76 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/review-epics.md +54 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/rework-epics.md +37 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/rework-story.md +61 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/split-stories.md +115 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/assess-parity-unit.md +58 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/assess-unit.md +89 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/fix-record.md +61 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/partition-findings.md +84 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/plan-units.md +85 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/surveyor/resolve-operator.md +78 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/write-epic.md +97 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/prompts/write-story.md +157 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/__init__.py +20 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/paths.py +202 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/__init__.py +98 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/_base.py +36 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/main.py +258 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/parity.py +42 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/schemas/survey.py +197 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/__init__.py +53 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/blueprint.py +15 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/inventory.py +377 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/records.py +373 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/stubs.py +52 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/shared/survey/units.py +203 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/__init__.py +33 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/flow.py +595 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/nodes/__init__.py +21 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/nodes/config.py +100 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/surveyor/nodes/partition.py +313 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/author/workflow.py +1088 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/__init__.py +53 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dev/__init__.py +18 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dev/flow.py +574 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/docs/__init__.py +20 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/docs/flow.py +368 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dream/__init__.py +15 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dream/flow.py +119 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/dream/nodes.py +329 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix/__init__.py +16 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix/flow.py +361 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix_ci/__init__.py +16 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/fix_ci/flow.py +229 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/genesis/__init__.py +17 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/genesis/flow.py +385 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/genesis/nodes.py +674 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/nodes/__init__.py +50 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/nodes/pr.py +719 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/apply-genesis-conventions.md +78 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/apply-qa-fixes.md +137 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/apply-review.md +182 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/audit-qa.md +72 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/check-code-reuse.md +90 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/code-reuse.md +94 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/code-review.md +60 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/document-story.md +99 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/dream-reflect.md +101 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-ci.md +32 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-genesis.md +53 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-lint.md +44 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-merge.md +48 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/fix-regression.md +99 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/implement-plan.md +222 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/plan-qa.md +279 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/plan-story.md +395 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/qa-fix-item.md +77 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/qa-story.md +114 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/refine-plan.md +301 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/repair-qa-context.md +43 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/replan-epic.md +28 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/report-qa-dev-pass.md +90 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/report-qa-dev.md +86 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/resolve-operator.md +77 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/review-implementation.md +213 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/review-qa-plan.md +49 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/review-story-documentation.md +46 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/setup-fix.md +149 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/prompts/triage-qa.md +168 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/__init__.py +24 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/flow.py +1106 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/__init__.py +43 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/evidence.py +624 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/hygiene.py +293 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/qa.py +264 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/qa/nodes/regression.py +417 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/review/__init__.py +18 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/review/flow.py +510 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/__init__.py +40 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/backlog.py +652 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/blueprint.py +13 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/ci.py +372 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/commits.py +125 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/contract.py +50 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/dev.py +575 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/docs.py +393 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/okf.py +119 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/ostler_qa.py +151 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/paths.py +235 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/queue.py +1189 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/review.py +212 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/__init__.py +211 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/_base.py +37 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/backlog.py +74 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/ci.py +85 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/dev.py +253 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/docs.py +129 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/dream.py +66 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/genesis.py +133 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/okf.py +32 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/pr.py +101 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/qa.py +475 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/queue.py +192 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/review.py +107 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/schemas/story.py +56 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/story.py +234 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/story_status.py +164 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/stubs.py +77 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/shared/worktree.py +92 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/coder/workflow.py +1030 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/hello_world/__init__.py +12 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/hello_world/prompts/greet.md +12 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/hello_world/workflow.py +87 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/kit/__init__.py +140 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/kit/credentials.py +81 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/kit/git.py +353 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/kit/github.py +173 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/kit/workspace.py +389 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/__init__.py +19 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/__init__.py +37 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/coverage.py +391 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/prepare.py +149 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/nodes/waivers.py +112 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/enumerate-surfaces.md +84 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/investigate.md +225 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/recheck-coverage.md +130 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/prompts/walkthrough-web.md +193 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/__init__.py +23 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/blueprint.py +13 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/checkpoint.py +251 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/paths.py +117 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/schemas.py +285 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/stubs.py +60 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/shared/worklist.py +184 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/__init__.py +25 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/flow.py +356 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/nodes/__init__.py +35 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/nodes/stack.py +186 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/walkthrough_web/nodes/walkthrough.py +423 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/okf_builder/workflow.py +513 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/__init__.py +7 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/__init__.py +34 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/_blueprint.py +13 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/program.py +204 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/publish.py +55 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/nodes/setup.py +87 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/define-new-direction.md +70 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/extend-program.md +57 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/gate-check.md +45 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/implement-experiment.md +56 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/lead-goal-review.md +67 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/record-result.md +33 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/research-lead-review.md +77 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/revive-gate.md +43 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/rework-experiment.md +45 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/prompts/select-next-gate.md +37 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/__init__.py +9 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/new_program.py +121 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/templates/PROGRESS.md +15 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/templates/README.md +93 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/scaffold/templates/gate.md +41 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/schemas.py +284 -0
- workhorse_workflows-0.2.0/src/workhorse_workflows/research/workflow.py +534 -0
- workhorse_workflows-0.2.0/tests/_fakes.py +41 -0
- workhorse_workflows-0.2.0/tests/author/__init__.py +3 -0
- workhorse_workflows-0.2.0/tests/author/conftest.py +79 -0
- workhorse_workflows-0.2.0/tests/author/parity_surveyor/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/author/parity_surveyor/test_flow.py +397 -0
- workhorse_workflows-0.2.0/tests/author/parity_surveyor/test_parity.py +634 -0
- workhorse_workflows-0.2.0/tests/author/shared/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/author/shared/survey/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/author/shared/survey/test_inventory.py +355 -0
- workhorse_workflows-0.2.0/tests/author/shared/survey/test_records.py +480 -0
- workhorse_workflows-0.2.0/tests/author/shared/survey/test_units.py +249 -0
- workhorse_workflows-0.2.0/tests/author/surveyor/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/author/surveyor/test_config.py +117 -0
- workhorse_workflows-0.2.0/tests/author/surveyor/test_flow.py +650 -0
- workhorse_workflows-0.2.0/tests/author/surveyor/test_partition.py +422 -0
- workhorse_workflows-0.2.0/tests/author/test_workflow.py +851 -0
- workhorse_workflows-0.2.0/tests/coder/__init__.py +4 -0
- workhorse_workflows-0.2.0/tests/coder/conftest.py +208 -0
- workhorse_workflows-0.2.0/tests/coder/dev/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/dev/test_flow.py +866 -0
- workhorse_workflows-0.2.0/tests/coder/docs/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/docs/test_flow.py +706 -0
- workhorse_workflows-0.2.0/tests/coder/dream/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/dream/test_flow.py +338 -0
- workhorse_workflows-0.2.0/tests/coder/fix/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/fix/test_flow.py +593 -0
- workhorse_workflows-0.2.0/tests/coder/fix_ci/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/fix_ci/test_flow.py +410 -0
- workhorse_workflows-0.2.0/tests/coder/genesis/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/genesis/test_flow.py +488 -0
- workhorse_workflows-0.2.0/tests/coder/qa/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/qa/test_evidence_scope.py +78 -0
- workhorse_workflows-0.2.0/tests/coder/qa/test_evidence_unsupported_pass.py +89 -0
- workhorse_workflows-0.2.0/tests/coder/qa/test_flow.py +1381 -0
- workhorse_workflows-0.2.0/tests/coder/qa/test_hygiene.py +95 -0
- workhorse_workflows-0.2.0/tests/coder/review/__init__.py +1 -0
- workhorse_workflows-0.2.0/tests/coder/review/test_flow.py +659 -0
- workhorse_workflows-0.2.0/tests/coder/test_commits.py +118 -0
- workhorse_workflows-0.2.0/tests/coder/test_prompt_stack_neutrality.py +218 -0
- workhorse_workflows-0.2.0/tests/coder/test_qa_plan_prompt_schema.py +111 -0
- workhorse_workflows-0.2.0/tests/coder/test_status_line_ownership.py +59 -0
- workhorse_workflows-0.2.0/tests/coder/test_workflow.py +1140 -0
- workhorse_workflows-0.2.0/tests/okf_builder/__init__.py +5 -0
- workhorse_workflows-0.2.0/tests/okf_builder/conftest.py +138 -0
- workhorse_workflows-0.2.0/tests/okf_builder/test_workflow.py +411 -0
- workhorse_workflows-0.2.0/tests/research/test_workflow.py +477 -0
- workhorse_workflows-0.2.0/tests/test_hello_world.py +99 -0
- workhorse_workflows-0.2.0/tests/test_kit_git.py +100 -0
- workhorse_workflows-0.2.0/tests/test_kit_workspace.py +62 -0
- workhorse_workflows-0.2.0/tests/test_prompt_output_shape.py +171 -0
- workhorse_workflows-0.2.0/tests/test_prompt_variables.py +317 -0
- 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).
|