@pennyfarthing/core 10.1.0 → 10.3.0

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 (422) hide show
  1. package/README.md +22 -24
  2. package/package.json +3 -1
  3. package/packages/core/dist/cli/commands/doctor-file-layout.test.js.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor-legacy.test.js +24 -0
  5. package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
  6. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/packages/core/dist/cli/commands/doctor.js +101 -15
  8. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  9. package/packages/core/dist/cli/commands/e2e-fresh-install.test.js +2 -2
  10. package/packages/core/dist/cli/commands/e2e-fresh-install.test.js.map +1 -1
  11. package/packages/core/dist/cli/commands/e2e-upgrade.test.js +2 -2
  12. package/packages/core/dist/cli/commands/e2e-upgrade.test.js.map +1 -1
  13. package/packages/core/dist/cli/commands/hooks-consolidation.test.js +2 -2
  14. package/packages/core/dist/cli/commands/hooks-consolidation.test.js.map +1 -1
  15. package/packages/core/dist/cli/commands/init-consolidation.test.js.map +1 -1
  16. package/packages/core/dist/cli/commands/theme.js +1 -1
  17. package/packages/core/dist/cli/commands/theme.js.map +1 -1
  18. package/packages/core/dist/cli/commands/uninstall.d.ts.map +1 -1
  19. package/packages/core/dist/cli/commands/uninstall.js +24 -13
  20. package/packages/core/dist/cli/commands/uninstall.js.map +1 -1
  21. package/packages/core/dist/cli/commands/update-consolidation.test.js +0 -10
  22. package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
  23. package/packages/core/dist/cli/commands/update.js.map +1 -1
  24. package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
  25. package/packages/core/dist/cli/theme-maker.test.js +64 -115
  26. package/packages/core/dist/cli/theme-maker.test.js.map +1 -1
  27. package/packages/core/dist/cli/utils/themes.d.ts.map +1 -1
  28. package/packages/core/dist/cli/utils/themes.js +3 -2
  29. package/packages/core/dist/cli/utils/themes.js.map +1 -1
  30. package/packages/core/dist/index.d.ts +1 -1
  31. package/packages/core/dist/index.d.ts.map +1 -1
  32. package/packages/core/dist/index.js +2 -2
  33. package/packages/core/dist/index.js.map +1 -1
  34. package/packages/core/dist/plugins/plugin-discovery.d.ts +116 -0
  35. package/packages/core/dist/plugins/plugin-discovery.d.ts.map +1 -0
  36. package/packages/core/dist/plugins/plugin-discovery.js +165 -0
  37. package/packages/core/dist/plugins/plugin-discovery.js.map +1 -0
  38. package/packages/core/dist/plugins/plugin-discovery.test.d.ts +22 -0
  39. package/packages/core/dist/plugins/plugin-discovery.test.d.ts.map +1 -0
  40. package/packages/core/dist/plugins/plugin-discovery.test.js +498 -0
  41. package/packages/core/dist/plugins/plugin-discovery.test.js.map +1 -0
  42. package/packages/core/dist/scripts/add-ocean-profiles.js +1 -1
  43. package/packages/core/dist/scripts/add-ocean-profiles.js.map +1 -1
  44. package/packages/core/dist/scripts/generate-all-spiders.js +2 -0
  45. package/packages/core/dist/scripts/generate-all-spiders.js.map +1 -1
  46. package/packages/core/dist/scripts/generate-report.d.ts.map +1 -1
  47. package/packages/core/dist/scripts/generate-report.js +2 -0
  48. package/packages/core/dist/scripts/generate-report.js.map +1 -1
  49. package/packages/core/dist/scripts/generate-spider-report.js.map +1 -1
  50. package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -1
  51. package/packages/core/dist/scripts/generate-spider.js +2 -0
  52. package/packages/core/dist/scripts/generate-spider.js.map +1 -1
  53. package/packages/core/dist/scripts/validate-ocean-profiles.js +1 -1
  54. package/packages/core/dist/scripts/validate-ocean-profiles.js.map +1 -1
  55. package/packages/core/dist/workflow/file-watch.d.ts +82 -0
  56. package/packages/core/dist/workflow/file-watch.d.ts.map +1 -0
  57. package/packages/core/dist/workflow/file-watch.js +198 -0
  58. package/packages/core/dist/workflow/file-watch.js.map +1 -0
  59. package/packages/core/dist/workflow/file-watch.test.d.ts +21 -0
  60. package/packages/core/dist/workflow/file-watch.test.d.ts.map +1 -0
  61. package/packages/core/dist/workflow/file-watch.test.js +469 -0
  62. package/packages/core/dist/workflow/file-watch.test.js.map +1 -0
  63. package/packages/core/dist/workflow/observation-writer.d.ts +79 -0
  64. package/packages/core/dist/workflow/observation-writer.d.ts.map +1 -0
  65. package/packages/core/dist/workflow/observation-writer.js +97 -0
  66. package/packages/core/dist/workflow/observation-writer.js.map +1 -0
  67. package/packages/core/dist/workflow/observation-writer.test.d.ts +18 -0
  68. package/packages/core/dist/workflow/observation-writer.test.d.ts.map +1 -0
  69. package/packages/core/dist/workflow/observation-writer.test.js +424 -0
  70. package/packages/core/dist/workflow/observation-writer.test.js.map +1 -0
  71. package/packages/core/dist/workflow/output-path-normalizer.d.ts +47 -0
  72. package/packages/core/dist/workflow/output-path-normalizer.d.ts.map +1 -0
  73. package/packages/core/dist/workflow/output-path-normalizer.js +79 -0
  74. package/packages/core/dist/workflow/output-path-normalizer.js.map +1 -0
  75. package/packages/core/dist/workflow/output-path-normalizer.test.d.ts +16 -0
  76. package/packages/core/dist/workflow/output-path-normalizer.test.d.ts.map +1 -0
  77. package/packages/core/dist/workflow/output-path-normalizer.test.js +157 -0
  78. package/packages/core/dist/workflow/output-path-normalizer.test.js.map +1 -0
  79. package/packages/core/dist/workflow/story-workflow-routing.test.js +4 -2
  80. package/packages/core/dist/workflow/story-workflow-routing.test.js.map +1 -1
  81. package/packages/core/dist/workflow/tandem-lifecycle.d.ts +117 -0
  82. package/packages/core/dist/workflow/tandem-lifecycle.d.ts.map +1 -0
  83. package/packages/core/dist/workflow/tandem-lifecycle.js +186 -0
  84. package/packages/core/dist/workflow/tandem-lifecycle.js.map +1 -0
  85. package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts +16 -0
  86. package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts.map +1 -0
  87. package/packages/core/dist/workflow/tandem-lifecycle.test.js +531 -0
  88. package/packages/core/dist/workflow/tandem-lifecycle.test.js.map +1 -0
  89. package/packages/core/dist/workflow/tool-watch.d.ts +68 -0
  90. package/packages/core/dist/workflow/tool-watch.d.ts.map +1 -0
  91. package/packages/core/dist/workflow/tool-watch.js +166 -0
  92. package/packages/core/dist/workflow/tool-watch.js.map +1 -0
  93. package/packages/core/dist/workflow/tool-watch.test.d.ts +18 -0
  94. package/packages/core/dist/workflow/tool-watch.test.d.ts.map +1 -0
  95. package/packages/core/dist/workflow/tool-watch.test.js +717 -0
  96. package/packages/core/dist/workflow/tool-watch.test.js.map +1 -0
  97. package/packages/core/dist/workflow/variable-resolver.js +1 -1
  98. package/packages/core/dist/workflow/variable-resolver.js.map +1 -1
  99. package/packages/core/dist/workflow/workflow-migration.test.js +8 -4
  100. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  101. package/packages/core/dist/workflow/workflow-schema.d.ts +7 -0
  102. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  103. package/packages/core/dist/workflow/workflow-schema.js +44 -0
  104. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  105. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
  106. package/packages/core/dist/workflow/workflow-schema.test.js +192 -0
  107. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  108. package/pennyfarthing-dist/agents/README.md +3 -1
  109. package/pennyfarthing-dist/agents/ba.md +165 -0
  110. package/pennyfarthing-dist/agents/handoff.md +18 -3
  111. package/pennyfarthing-dist/agents/sm-finish.md +1 -1
  112. package/pennyfarthing-dist/agents/sm-handoff.md +27 -4
  113. package/pennyfarthing-dist/agents/sm.md +11 -5
  114. package/pennyfarthing-dist/agents/tandem-backseat.md +119 -0
  115. package/pennyfarthing-dist/commands/ba.md +17 -0
  116. package/pennyfarthing-dist/commands/setup.md +4 -0
  117. package/pennyfarthing-dist/guides/agent-behavior.md +62 -6
  118. package/pennyfarthing-dist/guides/bikelane.md +3 -2
  119. package/pennyfarthing-dist/guides/scale-levels.md +4 -6
  120. package/pennyfarthing-dist/guides/tandem-protocol.md +158 -0
  121. package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
  122. package/pennyfarthing-dist/personas/themes/a-team.yaml +30 -0
  123. package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +30 -0
  124. package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +30 -0
  125. package/pennyfarthing-dist/personas/themes/blade-runner.yaml +30 -0
  126. package/pennyfarthing-dist/personas/themes/catch-22.yaml +30 -0
  127. package/pennyfarthing-dist/personas/themes/control.yaml +30 -0
  128. package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +31 -0
  129. package/pennyfarthing-dist/personas/themes/discworld.yaml +32 -1
  130. package/pennyfarthing-dist/personas/themes/doctor-who.yaml +31 -0
  131. package/pennyfarthing-dist/personas/themes/dune.yaml +32 -0
  132. package/pennyfarthing-dist/personas/themes/fifth-element.yaml +327 -0
  133. package/pennyfarthing-dist/personas/themes/firefly.yaml +31 -0
  134. package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +30 -0
  135. package/pennyfarthing-dist/personas/themes/harry-potter.yaml +30 -0
  136. package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +30 -0
  137. package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +30 -0
  138. package/pennyfarthing-dist/personas/themes/mad-max.yaml +30 -0
  139. package/pennyfarthing-dist/personas/themes/mash.yaml +33 -0
  140. package/pennyfarthing-dist/personas/themes/princess-bride.yaml +34 -0
  141. package/pennyfarthing-dist/personas/themes/sandman.yaml +33 -0
  142. package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +34 -0
  143. package/pennyfarthing-dist/personas/themes/star-wars.yaml +33 -0
  144. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +30 -0
  145. package/pennyfarthing-dist/personas/themes/the-matrix.yaml +30 -0
  146. package/pennyfarthing-dist/personas/themes/watchmen.yaml +30 -0
  147. package/pennyfarthing-dist/personas/themes/west-wing.yaml +30 -0
  148. package/pennyfarthing-dist/personas/themes/x-files.yaml +30 -0
  149. package/pennyfarthing-dist/scripts/README.md +1 -1
  150. package/pennyfarthing-dist/scripts/core/agent-session.sh +1 -1
  151. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +131 -54
  152. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +20 -10
  153. package/pennyfarthing-dist/scripts/misc/statusline.sh +50 -8
  154. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +2 -2
  155. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +1 -0
  156. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  157. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +10 -189
  158. package/pennyfarthing-dist/skills/skill-registry.schema.json +8 -0
  159. package/pennyfarthing-dist/skills/skill-registry.yaml +1 -1
  160. package/pennyfarthing-dist/skills/sprint/skill.md +25 -2
  161. package/pennyfarthing-dist/skills/theme/skill.md +1 -1
  162. package/pennyfarthing-dist/skills/workflow/skill.md +24 -1
  163. package/pennyfarthing-dist/workflows/architecture/workflow.yaml +65 -0
  164. package/pennyfarthing-dist/workflows/architecture.yaml +2 -2
  165. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +70 -0
  166. package/pennyfarthing-dist/workflows/epics-and-stories/workflow.yaml +2 -2
  167. package/pennyfarthing-dist/workflows/implementation-readiness/workflow.yaml +2 -2
  168. package/pennyfarthing-dist/workflows/prd/workflow.yaml +2 -2
  169. package/pennyfarthing-dist/workflows/product-brief/workflow.yaml +2 -2
  170. package/pennyfarthing-dist/workflows/project-context/workflow.yaml +2 -2
  171. package/pennyfarthing-dist/workflows/quick-dev/workflow.yaml +2 -2
  172. package/pennyfarthing-dist/workflows/research/workflow.yaml +2 -2
  173. package/pennyfarthing-dist/workflows/retrospective/workflow.yaml +1 -1
  174. package/pennyfarthing-dist/workflows/sprint-planning/workflow.yaml +3 -3
  175. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +61 -0
  176. package/pennyfarthing-dist/workflows/ux-design/workflow.yaml +2 -2
  177. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/bellmode_hook.py +202 -47
  183. package/pennyfarthing_scripts/bikerack/__init__.py +36 -0
  184. package/pennyfarthing_scripts/bikerack/__main__.py +5 -0
  185. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  186. package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
  187. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  188. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/bikerack/cli.py +148 -0
  190. package/pennyfarthing_scripts/bikerack/launcher.py +181 -0
  191. package/pennyfarthing_scripts/brownfield/__init__.py +6 -6
  192. package/pennyfarthing_scripts/brownfield/__main__.py +1 -0
  193. package/pennyfarthing_scripts/brownfield/cli.py +0 -1
  194. package/pennyfarthing_scripts/brownfield/discover.py +1 -2
  195. package/pennyfarthing_scripts/cli.py +16 -6
  196. package/pennyfarthing_scripts/codemarkers/__init__.py +5 -1
  197. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  198. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/codemarkers/analyze.py +177 -2
  204. package/pennyfarthing_scripts/codemarkers/cli.py +50 -0
  205. package/pennyfarthing_scripts/codemarkers/formatters.py +0 -1
  206. package/pennyfarthing_scripts/codemarkers/models.py +15 -0
  207. package/pennyfarthing_scripts/common/__init__.py +8 -9
  208. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/common/config.py +1 -1
  211. package/pennyfarthing_scripts/complexity/__init__.py +1 -1
  212. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/complexity/analyze.py +1 -1
  219. package/pennyfarthing_scripts/complexity/cli.py +5 -1
  220. package/pennyfarthing_scripts/complexity/formatters.py +1 -1
  221. package/pennyfarthing_scripts/context.py +14 -15
  222. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  226. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  228. package/pennyfarthing_scripts/deadcode/analyze.py +3 -4
  229. package/pennyfarthing_scripts/deadcode/cli.py +2 -2
  230. package/pennyfarthing_scripts/dependencies/__init__.py +2 -2
  231. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/dependencies/analyze.py +1 -1
  238. package/pennyfarthing_scripts/dependencies/cli.py +8 -4
  239. package/pennyfarthing_scripts/dependencies/formatters.py +1 -1
  240. package/pennyfarthing_scripts/git/__init__.py +5 -5
  241. package/pennyfarthing_scripts/git/create_branches.py +3 -2
  242. package/pennyfarthing_scripts/git/status_all.py +1 -1
  243. package/pennyfarthing_scripts/healthscore/__init__.py +2 -2
  244. package/pennyfarthing_scripts/healthscore/__main__.py +8 -0
  245. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/healthscore/analyze.py +452 -21
  252. package/pennyfarthing_scripts/healthscore/cli.py +5 -1
  253. package/pennyfarthing_scripts/healthscore/models.py +0 -1
  254. package/pennyfarthing_scripts/hooks.py +8 -11
  255. package/pennyfarthing_scripts/hotspots/__init__.py +6 -6
  256. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/hotspots/analyze.py +128 -14
  261. package/pennyfarthing_scripts/hotspots/cli.py +2 -2
  262. package/pennyfarthing_scripts/hotspots/models.py +0 -1
  263. package/pennyfarthing_scripts/jira/__init__.py +15 -17
  264. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/jira/bidirectional.py +2 -3
  275. package/pennyfarthing_scripts/jira/claim.py +21 -0
  276. package/pennyfarthing_scripts/jira/cli.py +2 -2
  277. package/pennyfarthing_scripts/jira/client.py +4 -4
  278. package/pennyfarthing_scripts/jira/create.py +45 -1
  279. package/pennyfarthing_scripts/jira/epic.py +3 -2
  280. package/pennyfarthing_scripts/jira/reconcile.py +0 -1
  281. package/pennyfarthing_scripts/jira/story.py +2 -0
  282. package/pennyfarthing_scripts/jira/sync.py +1 -1
  283. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/migration/skill.py +0 -1
  289. package/pennyfarthing_scripts/migration/step.py +0 -1
  290. package/pennyfarthing_scripts/migration/validate.py +8 -5
  291. package/pennyfarthing_scripts/patch_mode.py +2 -2
  292. package/pennyfarthing_scripts/preflight/__init__.py +1 -1
  293. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  294. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  295. package/pennyfarthing_scripts/preflight/finish.py +0 -1
  296. package/pennyfarthing_scripts/pretooluse_hook.py +6 -7
  297. package/pennyfarthing_scripts/prime/__init__.py +2 -0
  298. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  302. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  303. package/pennyfarthing_scripts/prime/cli.py +18 -1
  304. package/pennyfarthing_scripts/prime/loader.py +72 -3
  305. package/pennyfarthing_scripts/prime/persona.py +4 -2
  306. package/pennyfarthing_scripts/prime/tiers.py +17 -4
  307. package/pennyfarthing_scripts/schema_validation_hook.py +2 -3
  308. package/pennyfarthing_scripts/sprint/__init__.py +10 -12
  309. package/pennyfarthing_scripts/sprint/__main__.py +2 -2
  310. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  313. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  315. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  317. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  318. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  319. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  320. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  321. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  322. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  323. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  324. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  325. package/pennyfarthing_scripts/sprint/archive.py +0 -1
  326. package/pennyfarthing_scripts/sprint/archive_epic.py +1 -4
  327. package/pennyfarthing_scripts/sprint/cli.py +34 -28
  328. package/pennyfarthing_scripts/sprint/epic_add.py +8 -1
  329. package/pennyfarthing_scripts/sprint/import_epic.py +42 -18
  330. package/pennyfarthing_scripts/sprint/loader.py +6 -0
  331. package/pennyfarthing_scripts/sprint/status.py +1 -2
  332. package/pennyfarthing_scripts/sprint/story_add.py +2 -2
  333. package/pennyfarthing_scripts/sprint/story_finish.py +3 -5
  334. package/pennyfarthing_scripts/sprint/story_update.py +11 -3
  335. package/pennyfarthing_scripts/sprint/validate_cmd.py +0 -1
  336. package/pennyfarthing_scripts/sprint/validator.py +120 -6
  337. package/pennyfarthing_scripts/sprint/work.py +1 -4
  338. package/pennyfarthing_scripts/sprint/yaml_io.py +10 -2
  339. package/pennyfarthing_scripts/story/__init__.py +14 -16
  340. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  341. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  342. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  343. package/pennyfarthing_scripts/story/size.py +0 -1
  344. package/pennyfarthing_scripts/story/template.py +0 -1
  345. package/pennyfarthing_scripts/swebench.py +1 -2
  346. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  347. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  348. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  349. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  350. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  351. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  352. package/pennyfarthing_scripts/tests/conftest.py +1 -2
  353. package/pennyfarthing_scripts/tests/test_bikerack.py +785 -0
  354. package/pennyfarthing_scripts/tests/test_brownfield.py +10 -13
  355. package/pennyfarthing_scripts/tests/test_cli_modules.py +0 -4
  356. package/pennyfarthing_scripts/tests/test_codemarkers.py +13 -8
  357. package/pennyfarthing_scripts/tests/test_common.py +9 -4
  358. package/pennyfarthing_scripts/tests/test_epic_shard_validation.py +699 -0
  359. package/pennyfarthing_scripts/tests/test_git_utils.py +10 -13
  360. package/pennyfarthing_scripts/tests/test_healthscore.py +17 -25
  361. package/pennyfarthing_scripts/tests/test_jira_package.py +0 -3
  362. package/pennyfarthing_scripts/tests/test_package_structure.py +3 -16
  363. package/pennyfarthing_scripts/tests/test_patch_mode.py +7 -11
  364. package/pennyfarthing_scripts/tests/test_prime.py +39 -21
  365. package/pennyfarthing_scripts/tests/test_sprint_package.py +3 -8
  366. package/pennyfarthing_scripts/tests/test_sprint_validator.py +53 -5
  367. package/pennyfarthing_scripts/tests/test_story_add.py +3 -7
  368. package/pennyfarthing_scripts/tests/test_story_package.py +0 -3
  369. package/pennyfarthing_scripts/tests/test_story_update.py +5 -10
  370. package/pennyfarthing_scripts/tests/test_tiers.py +18 -17
  371. package/pennyfarthing_scripts/tests/test_token_counting.py +19 -13
  372. package/pennyfarthing_scripts/tests/test_topology_loader.py +620 -0
  373. package/pennyfarthing_scripts/tests/test_validate_cmd.py +2 -7
  374. package/pennyfarthing_scripts/tests/test_workflow_check.py +0 -2
  375. package/pennyfarthing_scripts/tests/test_yaml_io.py +0 -3
  376. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  377. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  378. package/pennyfarthing_scripts/theme/cli.py +3 -2
  379. package/pennyfarthing_scripts/validate/__init__.py +21 -0
  380. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  381. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  382. package/pennyfarthing_scripts/validate/adapters/__init__.py +0 -0
  383. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  384. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  385. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  386. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  387. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  388. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  389. package/pennyfarthing_scripts/validate/adapters/agent.py +239 -0
  390. package/pennyfarthing_scripts/validate/adapters/schema.py +30 -0
  391. package/pennyfarthing_scripts/validate/adapters/skill_command.py +291 -0
  392. package/pennyfarthing_scripts/validate/adapters/sprint.py +69 -0
  393. package/pennyfarthing_scripts/validate/adapters/workflow.py +320 -0
  394. package/pennyfarthing_scripts/validate/cli.py +141 -0
  395. package/pennyfarthing_scripts/welcome_hook.py +2 -3
  396. package/pennyfarthing_scripts/workflow.py +3 -3
  397. package/scripts/README.md +3 -15
  398. package/pennyfarthing-dist/commands/benchmark-control.md +0 -69
  399. package/pennyfarthing-dist/commands/benchmark.md +0 -485
  400. package/pennyfarthing-dist/commands/job-fair.md +0 -102
  401. package/pennyfarthing-dist/commands/solo.md +0 -447
  402. package/pennyfarthing-dist/guides/benchmarks.md +0 -62
  403. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +0 -59
  404. package/pennyfarthing-dist/scripts/test/ground-truth-judge.py +0 -220
  405. package/pennyfarthing-dist/scripts/test/swebench-judge.py +0 -374
  406. package/pennyfarthing-dist/scripts/test/test-cache.sh +0 -165
  407. package/pennyfarthing-dist/scripts/test/test-setup.sh +0 -337
  408. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +0 -13
  409. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +0 -402
  410. package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +0 -97
  411. package/pennyfarthing-dist/skills/finalize-run/SKILL.md +0 -261
  412. package/pennyfarthing-dist/skills/judge/SKILL.md +0 -644
  413. package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +0 -187
  414. package/pennyfarthing-dist/workflows/dev-story/checklist.md +0 -80
  415. package/pennyfarthing-dist/workflows/dev-story/instructions.xml +0 -410
  416. package/pennyfarthing-dist/workflows/dev-story/workflow.yaml +0 -50
  417. package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +0 -201
  418. package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +0 -156
  419. package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +0 -140
  420. package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +0 -203
  421. package/pennyfarthing-dist/workflows/quick-spec/tech-spec-template.md +0 -74
  422. package/pennyfarthing-dist/workflows/quick-spec/workflow.yaml +0 -27
@@ -0,0 +1,320 @@
1
+ """Workflow YAML schema validator adapter.
2
+
3
+ Validates workflow definition files in pennyfarthing-dist/workflows/.
4
+ Checks common fields, variant-specific structure (phased/stepped/procedural),
5
+ and cross-references agent names against agent definitions.
6
+
7
+ Story: MSSCI-14709 (91-11)
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pathlib import Path
13
+
14
+ import yaml
15
+
16
+ from pennyfarthing_scripts.validate import ValidateReport
17
+
18
+ # Known workflow types
19
+ VALID_TYPES = {"phased", "stepped", "procedural"}
20
+
21
+ # Known gate types for phased workflows
22
+ VALID_GATE_TYPES = {
23
+ "tests_pass",
24
+ "tests_fail",
25
+ "approval",
26
+ "manual",
27
+ "validation",
28
+ "design_review",
29
+ "quality_pass",
30
+ }
31
+
32
+
33
+ def discover_workflow_files(workflows_dir: Path) -> list[Path]:
34
+ """Discover all workflow YAML files.
35
+
36
+ Finds root-level *.yaml files and subdirectory workflow.yaml files.
37
+ Excludes non-workflow YAML (e.g., templates).
38
+
39
+ Returns:
40
+ Sorted list of Path objects for workflow YAML files.
41
+ """
42
+ files: list[Path] = []
43
+
44
+ if not workflows_dir.is_dir():
45
+ return files
46
+
47
+ # Root-level *.yaml files
48
+ for f in sorted(workflows_dir.glob("*.yaml")):
49
+ files.append(f)
50
+
51
+ # Subdirectory workflow.yaml files
52
+ for f in sorted(workflows_dir.glob("*/workflow.yaml")):
53
+ files.append(f)
54
+
55
+ return files
56
+
57
+
58
+ def _get_agent_stems(agents_dir: Path) -> set[str]:
59
+ """Get set of agent file stems from agents directory."""
60
+ if not agents_dir.is_dir():
61
+ return set()
62
+ return {f.stem for f in agents_dir.glob("*.md") if f.name != "README.md"}
63
+
64
+
65
+ def _check_agent_ref(
66
+ agent_name: str, agent_stems: set[str], context: str
67
+ ) -> list[str]:
68
+ """Check if an agent reference exists. Returns warnings for unknown agents."""
69
+ warnings: list[str] = []
70
+ if agent_name and agent_name not in agent_stems:
71
+ warnings.append(
72
+ f"Agent '{agent_name}' referenced in {context} not found in agents/"
73
+ )
74
+ return warnings
75
+
76
+
77
+ def validate_common(data: dict, path: Path) -> tuple[list[str], list[str]]:
78
+ """Validate common fields present in all workflow types.
79
+
80
+ Returns:
81
+ (errors, warnings) — two lists of message strings.
82
+ """
83
+ errors: list[str] = []
84
+ warnings: list[str] = []
85
+
86
+ if "name" not in data:
87
+ errors.append("Missing required field: name")
88
+
89
+ wtype = data.get("type")
90
+ if wtype is not None and wtype not in VALID_TYPES:
91
+ errors.append(
92
+ f"Invalid type '{wtype}' (must be one of: {', '.join(sorted(VALID_TYPES))})"
93
+ )
94
+
95
+ if "description" not in data:
96
+ warnings.append("Missing recommended field: description")
97
+
98
+ return errors, warnings
99
+
100
+
101
+ def validate_phased(
102
+ data: dict, path: Path, agents_dir: Path
103
+ ) -> tuple[list[str], list[str]]:
104
+ """Validate phased workflow structure.
105
+
106
+ Returns:
107
+ (errors, warnings) — two lists of message strings.
108
+ """
109
+ errors: list[str] = []
110
+ warnings: list[str] = []
111
+ agent_stems = _get_agent_stems(agents_dir)
112
+
113
+ phases = data.get("phases")
114
+
115
+ if phases is None:
116
+ errors.append("Missing required field: phases")
117
+ return errors, warnings
118
+
119
+ if not isinstance(phases, list):
120
+ errors.append("Field 'phases' must be a list")
121
+ return errors, warnings
122
+
123
+ if len(phases) == 0:
124
+ errors.append("Field 'phases' must not be empty")
125
+ return errors, warnings
126
+
127
+ seen_names: set[str] = set()
128
+
129
+ for i, phase in enumerate(phases):
130
+ if not isinstance(phase, dict):
131
+ errors.append(f"Phase {i} must be a mapping")
132
+ continue
133
+
134
+ phase_name = phase.get("name")
135
+ if not phase_name:
136
+ errors.append(f"Phase {i} missing required field: name")
137
+ else:
138
+ if phase_name in seen_names:
139
+ warnings.append(f"Duplicate phase name: '{phase_name}'")
140
+ seen_names.add(phase_name)
141
+
142
+ agent = phase.get("agent")
143
+ if not agent:
144
+ errors.append(
145
+ f"Phase {i}{' (' + phase_name + ')' if phase_name else ''} "
146
+ f"missing required field: agent"
147
+ )
148
+ else:
149
+ warnings.extend(
150
+ _check_agent_ref(agent, agent_stems, f"phase '{phase_name or i}'")
151
+ )
152
+
153
+ # Gate validation
154
+ gate = phase.get("gate")
155
+ if gate and isinstance(gate, dict):
156
+ gate_type = gate.get("type")
157
+ if gate_type is None:
158
+ errors.append(
159
+ f"Phase '{phase_name or i}' gate missing required field: type"
160
+ )
161
+ elif gate_type not in VALID_GATE_TYPES:
162
+ warnings.append(
163
+ f"Phase '{phase_name or i}' has unknown gate type: "
164
+ f"'{gate_type}'"
165
+ )
166
+
167
+ return errors, warnings
168
+
169
+
170
+ def validate_stepped(
171
+ data: dict, path: Path, agents_dir: Path
172
+ ) -> tuple[list[str], list[str]]:
173
+ """Validate stepped workflow structure.
174
+
175
+ Returns:
176
+ (errors, warnings) — two lists of message strings.
177
+ """
178
+ errors: list[str] = []
179
+ warnings: list[str] = []
180
+ agent_stems = _get_agent_stems(agents_dir)
181
+
182
+ agent = data.get("agent")
183
+ if not agent:
184
+ errors.append("Missing required field: agent")
185
+ else:
186
+ warnings.extend(_check_agent_ref(agent, agent_stems, "workflow"))
187
+
188
+ steps = data.get("steps")
189
+ if steps is None:
190
+ errors.append("Missing required field: steps")
191
+ return errors, warnings
192
+
193
+ if not isinstance(steps, dict):
194
+ errors.append("Field 'steps' must be a mapping")
195
+ return errors, warnings
196
+
197
+ if "path" not in steps:
198
+ errors.append("Missing required field: steps.path")
199
+
200
+ if "pattern" not in steps:
201
+ errors.append("Missing required field: steps.pattern")
202
+
203
+ return errors, warnings
204
+
205
+
206
+ def validate_procedural(
207
+ data: dict, path: Path, agents_dir: Path
208
+ ) -> tuple[list[str], list[str]]:
209
+ """Validate procedural workflow structure.
210
+
211
+ Returns:
212
+ (errors, warnings) — two lists of message strings.
213
+ """
214
+ errors: list[str] = []
215
+ warnings: list[str] = []
216
+ agent_stems = _get_agent_stems(agents_dir)
217
+
218
+ agent = data.get("agent")
219
+ if not agent:
220
+ errors.append("Missing required field: agent")
221
+ else:
222
+ warnings.extend(_check_agent_ref(agent, agent_stems, "workflow"))
223
+
224
+ if "instructions" not in data and "checklist" not in data:
225
+ warnings.append(
226
+ "Missing recommended field: instructions or checklist"
227
+ )
228
+
229
+ return errors, warnings
230
+
231
+
232
+ def run(root: Path, *, fix: bool = False, strict: bool = False) -> ValidateReport:
233
+ """Validate all workflow definition files."""
234
+ report = ValidateReport(validator="workflow")
235
+ workflows_dir = root / "pennyfarthing-dist" / "workflows"
236
+
237
+ if not workflows_dir.is_dir():
238
+ report.details.append("[ERROR] workflows directory not found")
239
+ report.errors += 1
240
+ return report
241
+
242
+ agents_dir = root / "pennyfarthing-dist" / "agents"
243
+ files = discover_workflow_files(workflows_dir)
244
+
245
+ for path in files:
246
+ try:
247
+ content = path.read_text()
248
+ raw = yaml.safe_load(content)
249
+ except yaml.YAMLError:
250
+ report.errors += 1
251
+ report.details.append(f"[ERROR] {path.name}: YAML parse error")
252
+ continue
253
+
254
+ if not isinstance(raw, dict) or "workflow" not in raw:
255
+ report.errors += 1
256
+ report.details.append(
257
+ f"[ERROR] {path.name}: Missing 'workflow' top-level key"
258
+ )
259
+ continue
260
+
261
+ data = raw["workflow"]
262
+
263
+ if not isinstance(data, dict):
264
+ report.errors += 1
265
+ report.details.append(
266
+ f"[ERROR] {path.name}: 'workflow' must be a mapping"
267
+ )
268
+ continue
269
+
270
+ file_errors: list[str] = []
271
+ file_warnings: list[str] = []
272
+
273
+ # Common validation
274
+ common_errors, common_warnings = validate_common(data, path)
275
+ file_errors.extend(common_errors)
276
+ file_warnings.extend(common_warnings)
277
+
278
+ # Variant-specific validation
279
+ wtype = data.get("type", "phased")
280
+
281
+ if wtype == "phased":
282
+ variant_errors, variant_warnings = validate_phased(
283
+ data, path, agents_dir
284
+ )
285
+ elif wtype == "stepped":
286
+ variant_errors, variant_warnings = validate_stepped(
287
+ data, path, agents_dir
288
+ )
289
+ elif wtype == "procedural":
290
+ variant_errors, variant_warnings = validate_procedural(
291
+ data, path, agents_dir
292
+ )
293
+ else:
294
+ variant_errors, variant_warnings = [], []
295
+
296
+ file_errors.extend(variant_errors)
297
+ file_warnings.extend(variant_warnings)
298
+
299
+ # Determine display name (use parent/name for subdirectory workflows)
300
+ if path.name == "workflow.yaml":
301
+ display = f"{path.parent.name}/{path.name}"
302
+ else:
303
+ display = path.name
304
+
305
+ for e in file_errors:
306
+ report.errors += 1
307
+ report.details.append(f"[ERROR] {display}: {e}")
308
+
309
+ for w in file_warnings:
310
+ if strict:
311
+ report.errors += 1
312
+ report.details.append(f"[ERROR] {display}: {w}")
313
+ else:
314
+ report.warnings += 1
315
+ report.details.append(f"[WARN] {display}: {w}")
316
+
317
+ if not file_errors:
318
+ report.passed += 1
319
+
320
+ return report
@@ -0,0 +1,141 @@
1
+ """Validate CLI — top-level validation command.
2
+
3
+ Usage:
4
+ pf validate # Run all validators
5
+ pf validate sprint # Sprint YAML only
6
+ pf validate schema # XML schema only
7
+ pf validate --fix # Auto-fix where supported
8
+ pf validate --strict # Treat warnings as errors
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import click
14
+
15
+ from pennyfarthing_scripts.common.config import get_project_root
16
+ from pennyfarthing_scripts.common.output import error, header, info, success, warn
17
+ from pennyfarthing_scripts.validate import ValidateReport
18
+
19
+ VALIDATORS = {
20
+ "sprint": "pennyfarthing_scripts.validate.adapters.sprint",
21
+ "schema": "pennyfarthing_scripts.validate.adapters.schema",
22
+ "agent": "pennyfarthing_scripts.validate.adapters.agent",
23
+ "workflow": "pennyfarthing_scripts.validate.adapters.workflow",
24
+ "skill-command": "pennyfarthing_scripts.validate.adapters.skill_command",
25
+ }
26
+
27
+
28
+ def _run_validator(name: str, *, fix: bool, strict: bool) -> ValidateReport:
29
+ """Run a single validator by name."""
30
+ import importlib
31
+
32
+ mod = importlib.import_module(VALIDATORS[name])
33
+ root = get_project_root()
34
+ return mod.run(root, fix=fix, strict=strict)
35
+
36
+
37
+ def _print_reports(reports: list[ValidateReport]) -> None:
38
+ """Print validation reports with color output."""
39
+ for report in reports:
40
+ header(f"Validator: {report.validator}", char="-", width=50)
41
+
42
+ if report.details:
43
+ for line in report.details:
44
+ if "[ERROR]" in line or "[SYNTAX]" in line or "[SCHEMA]" in line:
45
+ error(line)
46
+ elif "[WARN]" in line or "[FORMAT]" in line:
47
+ warn(line)
48
+ else:
49
+ click.echo(f" {line}", err=True)
50
+
51
+ parts = [f"{report.passed} passed"]
52
+ if report.warnings:
53
+ parts.append(f"{report.warnings} warnings")
54
+ if report.errors:
55
+ parts.append(f"{report.errors} errors")
56
+ if report.fixed:
57
+ parts.append("(fixed)")
58
+
59
+ summary_line = ", ".join(parts)
60
+ if report.success:
61
+ success(f"{report.validator}: {summary_line}")
62
+ else:
63
+ error(f"{report.validator}: {summary_line}")
64
+
65
+ click.echo("", err=True)
66
+ total_errors = sum(r.errors for r in reports)
67
+ total_warnings = sum(r.warnings for r in reports)
68
+ total_passed = sum(r.passed for r in reports)
69
+
70
+ info(f"Total: {total_passed} passed, {total_warnings} warnings, {total_errors} errors")
71
+
72
+
73
+ @click.group(invoke_without_command=True)
74
+ @click.option("--fix", is_flag=True, help="Auto-fix format issues where supported")
75
+ @click.option("--strict", is_flag=True, help="Treat warnings as errors")
76
+ @click.pass_context
77
+ def validate(ctx, fix: bool, strict: bool):
78
+ """Run project validators.
79
+
80
+ \b
81
+ With no subcommand, runs ALL validators.
82
+ Specify a validator name to run only that one.
83
+
84
+ \b
85
+ Validators:
86
+ sprint - Sprint YAML (epics, initiatives, future, current-sprint)
87
+ schema - XML schema (sessions, skills, workflow steps)
88
+ agent - Agent definitions (required sections, model values, subagent refs)
89
+ workflow - Workflow definitions (phased/stepped/procedural structure)
90
+ """
91
+ ctx.ensure_object(dict)
92
+ ctx.obj["fix"] = fix
93
+ ctx.obj["strict"] = strict
94
+
95
+ if ctx.invoked_subcommand is None:
96
+ reports = []
97
+ for name in VALIDATORS:
98
+ reports.append(_run_validator(name, fix=fix, strict=strict))
99
+ _print_reports(reports)
100
+ if any(not r.success for r in reports):
101
+ raise SystemExit(1)
102
+
103
+
104
+ @validate.command("sprint")
105
+ @click.pass_context
106
+ def validate_sprint(ctx):
107
+ """Validate sprint YAML files (auto-discovers all files in sprint/)."""
108
+ report = _run_validator("sprint", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
109
+ _print_reports([report])
110
+ if not report.success:
111
+ raise SystemExit(1)
112
+
113
+
114
+ @validate.command("schema")
115
+ @click.pass_context
116
+ def validate_schema(ctx):
117
+ """Validate XML schema files (sessions, skills, workflow steps)."""
118
+ report = _run_validator("schema", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
119
+ _print_reports([report])
120
+ if not report.success:
121
+ raise SystemExit(1)
122
+
123
+
124
+ @validate.command("agent")
125
+ @click.pass_context
126
+ def validate_agent(ctx):
127
+ """Validate agent definition files (required sections, model values, refs)."""
128
+ report = _run_validator("agent", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
129
+ _print_reports([report])
130
+ if not report.success:
131
+ raise SystemExit(1)
132
+
133
+
134
+ @validate.command("workflow")
135
+ @click.pass_context
136
+ def validate_workflow(ctx):
137
+ """Validate workflow definitions (phased/stepped/procedural structure)."""
138
+ report = _run_validator("workflow", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
139
+ _print_reports([report])
140
+ if not report.success:
141
+ raise SystemExit(1)
@@ -22,12 +22,11 @@ sys.path.insert(0, str(Path(__file__).parent))
22
22
 
23
23
  from hooks import (
24
24
  find_project_root,
25
- send_to_cyclist,
26
- load_settings,
27
25
  is_cyclist_running,
26
+ load_settings,
27
+ send_to_cyclist,
28
28
  )
29
29
 
30
-
31
30
  # Once-per-session guard
32
31
  _welcome_shown_file: Path | None = None
33
32
 
@@ -12,8 +12,8 @@ Story: MSSCI-12416 - Define Scale Levels
12
12
  Epic: MSSCI-12415 - Scale Adaptation and Brownfield Support
13
13
  """
14
14
 
15
- from typing import Any
16
15
  import re
16
+ from typing import Any
17
17
 
18
18
  # Scale level definitions with keywords, thresholds, and metadata
19
19
  SCALE_LEVELS: dict[int, dict[str, Any]] = {
@@ -32,7 +32,7 @@ SCALE_LEVELS: dict[int, dict[str, Any]] = {
32
32
  "keywords": ["simple", "basic", "small", "add", "minor"],
33
33
  "stories_min": 1,
34
34
  "stories_max": 10,
35
- "workflow": "quick-spec",
35
+ "workflow": "prd",
36
36
  "artifacts": ["tech-spec"],
37
37
  },
38
38
  2: {
@@ -124,7 +124,7 @@ def get_workflow_for_scale_level(level: int) -> str:
124
124
  level: Scale level 0-4
125
125
 
126
126
  Returns:
127
- Workflow name (trivial, quick-spec, or prd)
127
+ Workflow name (trivial or prd)
128
128
  """
129
129
  if level not in SCALE_LEVELS:
130
130
  return "prd" # Safe default for unknown levels
package/scripts/README.md CHANGED
@@ -7,21 +7,15 @@
7
7
  | Script | Purpose |
8
8
  |--------|---------|
9
9
  | `deploy.sh` | Release Pennyfarthing (version bump, tag, push, GitHub release) |
10
- | `benchmark-runner.{sh,js}` | Run persona benchmarks |
11
- | `job-fair-*.sh` | Job Fair character evaluations |
12
- | `aggregate-benchmark-stats.{sh,js}` | Aggregate benchmark results |
13
- | `solo-runner.sh` | Run single agent on a scenario |
14
- | `parallel-benchmark.sh` | Parallel benchmark execution |
15
- | `consolidate-job-fair.sh` | Consolidate Job Fair results |
16
- | `convert-jobfair-to-benchmarks.sh` | Format conversion |
17
- | `generate-leaderboard.sh` | Build leaderboard from benchmarks |
18
- | `regenerate-summaries.sh` | Regenerate benchmark summaries |
19
10
  | `cyclist-debug.mjs` | Debug Cyclist connection |
20
11
  | `handoff-cli.{sh,js}` | Test handoff flow |
21
12
  | `verify-visual-mapping.js` | Verify theme visual mappings |
22
13
  | `migrate-assets-to-slug.sh` | One-time migration script |
23
14
  | `resize-portraits.sh` | Resize portrait images |
24
15
  | `resolve-portrait.mjs` | Portrait resolution logic |
16
+ | `validate-refs.js` | Validate internal references |
17
+
18
+ > Benchmark scripts have been moved to `packages/benchmark/`.
25
19
 
26
20
  ## Usage
27
21
 
@@ -31,12 +25,6 @@ Run from pennyfarthing repo root:
31
25
  # Release a new version
32
26
  ./scripts/deploy.sh --dry-run patch
33
27
  ./scripts/deploy.sh patch
34
-
35
- # Run benchmarks
36
- ./scripts/benchmark-runner.sh --theme mash --agent sm
37
-
38
- # Job Fair
39
- ./scripts/job-fair-runner.sh mash
40
28
  ```
41
29
 
42
30
  ## Where Should My Script Go?
@@ -1,69 +0,0 @@
1
- ---
2
- description: Create control baseline for a scenario (shortcut for /benchmark control <agent>)
3
- argument-hint: <agent> [--scenario <name>] [--runs N]
4
- ---
5
-
6
- # Benchmark Control
7
-
8
- <purpose>
9
- Shortcut to run `/benchmark` with the `control` theme. Creates or extends a control baseline for comparing other personas against.
10
-
11
- This is equivalent to running:
12
- ```
13
- /benchmark control <agent> [--scenario <name>] [--runs N]
14
- ```
15
-
16
- Default: 10 runs for statistically meaningful baseline data.
17
- </purpose>
18
-
19
- <critical-integrity-requirements>
20
- **See `/benchmark` for full integrity requirements.**
21
-
22
- Baselines are saved to `internal/results/baselines/{scenario}/{agent}/` with:
23
- - Individual runs in `runs/*.json` with proof-of-work
24
- - Summary statistics in `summary.yaml` (mean, std_dev, CI)
25
- - Timestamp validation (runs must take 30+ seconds each)
26
-
27
- Control theme runs must include all proof fields. NO FABRICATION.
28
- </critical-integrity-requirements>
29
-
30
- <usage>
31
- ```
32
- # Pick scenario interactively
33
- /benchmark-control sm
34
- /benchmark-control reviewer
35
-
36
- # Specify scenario directly
37
- /benchmark-control reviewer --scenario order-service
38
- /benchmark-control dev --scenario tdd-shopping-cart --runs 15
39
- ```
40
-
41
- **Arguments:**
42
- - `agent` - The agent role (e.g., `sm`, `dev`, `reviewer`, `architect`)
43
- - `--scenario` - (Optional) Scenario name. If omitted, shows matching scenarios.
44
- - `--runs N` - Number of runs (default: 10 for baselines, max: 20)
45
- </usage>
46
-
47
- <on-invoke>
48
- The user invoked this command with: $ARGUMENTS
49
-
50
- **This is a shortcut.** Translate the arguments and invoke `/benchmark`:
51
-
52
- 1. Prepend `control` as the theme
53
- 2. Pass through all other arguments
54
-
55
- **Examples:**
56
- - `/benchmark-control sm` → `/benchmark control sm` (runs control:sm)
57
- - `/benchmark-control reviewer --scenario order-service` → `/benchmark control reviewer --scenario order-service` (runs control:reviewer)
58
- - `/benchmark-control dev --runs 15` → `/benchmark control dev --runs 15` (runs control:dev)
59
-
60
- **Default runs override:** If `--runs` is not specified, default to 10 (instead of 4) since baselines need more data.
61
-
62
- Now execute the equivalent `/benchmark` command with the translated arguments.
63
- </on-invoke>
64
-
65
- <reference>
66
- - Main command: `.claude/project/commands/benchmark.md`
67
- - Baselines location: `internal/results/baselines/{scenario}/{role}/`
68
- - Results README: `internal/results/README.md`
69
- </reference>