@pennyfarthing/core 10.1.0 → 10.2.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 (407) hide show
  1. package/README.md +13 -18
  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 +1 -1
  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 +1 -1
  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/uninstall.d.ts.map +1 -1
  17. package/packages/core/dist/cli/commands/uninstall.js +24 -13
  18. package/packages/core/dist/cli/commands/uninstall.js.map +1 -1
  19. package/packages/core/dist/cli/commands/update-consolidation.test.js +0 -10
  20. package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
  21. package/packages/core/dist/cli/commands/update.js.map +1 -1
  22. package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
  23. package/packages/core/dist/cli/theme-maker.test.js +64 -115
  24. package/packages/core/dist/cli/theme-maker.test.js.map +1 -1
  25. package/packages/core/dist/index.d.ts +1 -1
  26. package/packages/core/dist/index.d.ts.map +1 -1
  27. package/packages/core/dist/index.js +2 -2
  28. package/packages/core/dist/index.js.map +1 -1
  29. package/packages/core/dist/plugins/plugin-discovery.d.ts +116 -0
  30. package/packages/core/dist/plugins/plugin-discovery.d.ts.map +1 -0
  31. package/packages/core/dist/plugins/plugin-discovery.js +165 -0
  32. package/packages/core/dist/plugins/plugin-discovery.js.map +1 -0
  33. package/packages/core/dist/plugins/plugin-discovery.test.d.ts +22 -0
  34. package/packages/core/dist/plugins/plugin-discovery.test.d.ts.map +1 -0
  35. package/packages/core/dist/plugins/plugin-discovery.test.js +498 -0
  36. package/packages/core/dist/plugins/plugin-discovery.test.js.map +1 -0
  37. package/packages/core/dist/scripts/generate-spider-report.js.map +1 -1
  38. package/packages/core/dist/workflow/context-watch.d.ts +80 -0
  39. package/packages/core/dist/workflow/context-watch.d.ts.map +1 -0
  40. package/packages/core/dist/workflow/context-watch.js +235 -0
  41. package/packages/core/dist/workflow/context-watch.js.map +1 -0
  42. package/packages/core/dist/workflow/context-watch.test.d.ts +1 -0
  43. package/packages/core/dist/workflow/context-watch.test.d.ts.map +1 -0
  44. package/packages/core/dist/workflow/context-watch.test.js +746 -0
  45. package/packages/core/dist/workflow/context-watch.test.js.map +1 -0
  46. package/packages/core/dist/workflow/file-watch.d.ts +82 -0
  47. package/packages/core/dist/workflow/file-watch.d.ts.map +1 -0
  48. package/packages/core/dist/workflow/file-watch.js +198 -0
  49. package/packages/core/dist/workflow/file-watch.js.map +1 -0
  50. package/packages/core/dist/workflow/file-watch.test.d.ts +21 -0
  51. package/packages/core/dist/workflow/file-watch.test.d.ts.map +1 -0
  52. package/packages/core/dist/workflow/file-watch.test.js +469 -0
  53. package/packages/core/dist/workflow/file-watch.test.js.map +1 -0
  54. package/packages/core/dist/workflow/observation-writer.d.ts +79 -0
  55. package/packages/core/dist/workflow/observation-writer.d.ts.map +1 -0
  56. package/packages/core/dist/workflow/observation-writer.js +97 -0
  57. package/packages/core/dist/workflow/observation-writer.js.map +1 -0
  58. package/packages/core/dist/workflow/observation-writer.test.d.ts +18 -0
  59. package/packages/core/dist/workflow/observation-writer.test.d.ts.map +1 -0
  60. package/packages/core/dist/workflow/observation-writer.test.js +424 -0
  61. package/packages/core/dist/workflow/observation-writer.test.js.map +1 -0
  62. package/packages/core/dist/workflow/story-workflow-routing.test.js +4 -2
  63. package/packages/core/dist/workflow/story-workflow-routing.test.js.map +1 -1
  64. package/packages/core/dist/workflow/tandem-lifecycle.d.ts +117 -0
  65. package/packages/core/dist/workflow/tandem-lifecycle.d.ts.map +1 -0
  66. package/packages/core/dist/workflow/tandem-lifecycle.js +186 -0
  67. package/packages/core/dist/workflow/tandem-lifecycle.js.map +1 -0
  68. package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts +16 -0
  69. package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts.map +1 -0
  70. package/packages/core/dist/workflow/tandem-lifecycle.test.js +531 -0
  71. package/packages/core/dist/workflow/tandem-lifecycle.test.js.map +1 -0
  72. package/packages/core/dist/workflow/tool-watch.d.ts +68 -0
  73. package/packages/core/dist/workflow/tool-watch.d.ts.map +1 -0
  74. package/packages/core/dist/workflow/tool-watch.js +166 -0
  75. package/packages/core/dist/workflow/tool-watch.js.map +1 -0
  76. package/packages/core/dist/workflow/tool-watch.test.d.ts +18 -0
  77. package/packages/core/dist/workflow/tool-watch.test.d.ts.map +1 -0
  78. package/packages/core/dist/workflow/tool-watch.test.js +718 -0
  79. package/packages/core/dist/workflow/tool-watch.test.js.map +1 -0
  80. package/packages/core/dist/workflow/workflow-migration.test.js +8 -4
  81. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  82. package/packages/core/dist/workflow/workflow-schema.d.ts +7 -0
  83. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  84. package/packages/core/dist/workflow/workflow-schema.js +44 -0
  85. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  86. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
  87. package/packages/core/dist/workflow/workflow-schema.test.js +192 -0
  88. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  89. package/pennyfarthing-dist/agents/handoff.md +18 -3
  90. package/pennyfarthing-dist/agents/sm-finish.md +1 -1
  91. package/pennyfarthing-dist/agents/sm-handoff.md +27 -4
  92. package/pennyfarthing-dist/agents/sm.md +11 -5
  93. package/pennyfarthing-dist/agents/tandem-backseat.md +119 -0
  94. package/pennyfarthing-dist/commands/setup.md +4 -0
  95. package/pennyfarthing-dist/guides/agent-behavior.md +62 -6
  96. package/pennyfarthing-dist/guides/bikelane.md +3 -2
  97. package/pennyfarthing-dist/guides/scale-levels.md +4 -6
  98. package/pennyfarthing-dist/guides/tandem-protocol.md +158 -0
  99. package/pennyfarthing-dist/personas/themes/discworld.yaml +1 -1
  100. package/pennyfarthing-dist/personas/themes/fifth-element.yaml +295 -0
  101. package/pennyfarthing-dist/scripts/README.md +1 -1
  102. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +131 -54
  103. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +20 -10
  104. package/pennyfarthing-dist/scripts/misc/statusline.sh +50 -8
  105. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  106. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +10 -189
  107. package/pennyfarthing-dist/skills/skill-registry.schema.json +8 -0
  108. package/pennyfarthing-dist/skills/skill-registry.yaml +1 -1
  109. package/pennyfarthing-dist/skills/sprint/skill.md +25 -2
  110. package/pennyfarthing-dist/skills/workflow/skill.md +24 -1
  111. package/pennyfarthing-dist/workflows/architecture/workflow.yaml +65 -0
  112. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +70 -0
  113. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +61 -0
  114. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  116. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  121. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  124. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  126. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  127. package/pennyfarthing_scripts/bellmode_hook.py +202 -47
  128. package/pennyfarthing_scripts/brownfield/__init__.py +6 -6
  129. package/pennyfarthing_scripts/brownfield/__main__.py +1 -0
  130. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/brownfield/cli.py +0 -1
  135. package/pennyfarthing_scripts/brownfield/discover.py +1 -2
  136. package/pennyfarthing_scripts/cli.py +11 -6
  137. package/pennyfarthing_scripts/codemarkers/__init__.py +5 -1
  138. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  140. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  141. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  142. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  143. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/codemarkers/analyze.py +177 -2
  145. package/pennyfarthing_scripts/codemarkers/cli.py +50 -0
  146. package/pennyfarthing_scripts/codemarkers/formatters.py +0 -1
  147. package/pennyfarthing_scripts/codemarkers/models.py +15 -0
  148. package/pennyfarthing_scripts/common/__init__.py +8 -9
  149. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  150. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  153. package/pennyfarthing_scripts/common/config.py +1 -1
  154. package/pennyfarthing_scripts/complexity/__init__.py +1 -1
  155. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  160. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  161. package/pennyfarthing_scripts/complexity/analyze.py +1 -1
  162. package/pennyfarthing_scripts/complexity/cli.py +5 -1
  163. package/pennyfarthing_scripts/complexity/formatters.py +1 -1
  164. package/pennyfarthing_scripts/context.py +14 -15
  165. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/deadcode/analyze.py +3 -4
  172. package/pennyfarthing_scripts/deadcode/cli.py +2 -2
  173. package/pennyfarthing_scripts/dependencies/__init__.py +2 -2
  174. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/dependencies/analyze.py +1 -1
  181. package/pennyfarthing_scripts/dependencies/cli.py +8 -4
  182. package/pennyfarthing_scripts/dependencies/formatters.py +1 -1
  183. package/pennyfarthing_scripts/git/__init__.py +5 -5
  184. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  186. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  187. package/pennyfarthing_scripts/git/create_branches.py +3 -2
  188. package/pennyfarthing_scripts/git/status_all.py +1 -1
  189. package/pennyfarthing_scripts/healthscore/__init__.py +2 -2
  190. package/pennyfarthing_scripts/healthscore/__main__.py +8 -0
  191. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  197. package/pennyfarthing_scripts/healthscore/analyze.py +451 -21
  198. package/pennyfarthing_scripts/healthscore/cli.py +5 -1
  199. package/pennyfarthing_scripts/healthscore/models.py +0 -1
  200. package/pennyfarthing_scripts/hooks.py +8 -11
  201. package/pennyfarthing_scripts/hotspots/__init__.py +6 -6
  202. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/hotspots/analyze.py +128 -14
  209. package/pennyfarthing_scripts/hotspots/cli.py +2 -2
  210. package/pennyfarthing_scripts/hotspots/models.py +0 -1
  211. package/pennyfarthing_scripts/jira/__init__.py +15 -17
  212. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  222. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/jira/bidirectional.py +2 -3
  225. package/pennyfarthing_scripts/jira/claim.py +21 -0
  226. package/pennyfarthing_scripts/jira/cli.py +2 -2
  227. package/pennyfarthing_scripts/jira/client.py +4 -4
  228. package/pennyfarthing_scripts/jira/create.py +45 -1
  229. package/pennyfarthing_scripts/jira/epic.py +3 -2
  230. package/pennyfarthing_scripts/jira/reconcile.py +0 -1
  231. package/pennyfarthing_scripts/jira/story.py +2 -0
  232. package/pennyfarthing_scripts/jira/sync.py +1 -1
  233. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/migration/skill.py +0 -1
  239. package/pennyfarthing_scripts/migration/step.py +0 -1
  240. package/pennyfarthing_scripts/migration/validate.py +8 -5
  241. package/pennyfarthing_scripts/patch_mode.py +2 -2
  242. package/pennyfarthing_scripts/preflight/__init__.py +1 -1
  243. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/preflight/finish.py +0 -1
  248. package/pennyfarthing_scripts/pretooluse_hook.py +6 -7
  249. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/prime/cli.py +5 -1
  258. package/pennyfarthing_scripts/prime/loader.py +2 -3
  259. package/pennyfarthing_scripts/prime/persona.py +2 -1
  260. package/pennyfarthing_scripts/prime/tiers.py +4 -4
  261. package/pennyfarthing_scripts/schema_validation_hook.py +2 -3
  262. package/pennyfarthing_scripts/sprint/__init__.py +10 -12
  263. package/pennyfarthing_scripts/sprint/__main__.py +2 -2
  264. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/sprint/archive.py +0 -1
  281. package/pennyfarthing_scripts/sprint/archive_epic.py +1 -4
  282. package/pennyfarthing_scripts/sprint/cli.py +34 -28
  283. package/pennyfarthing_scripts/sprint/epic_add.py +8 -1
  284. package/pennyfarthing_scripts/sprint/import_epic.py +42 -18
  285. package/pennyfarthing_scripts/sprint/loader.py +6 -0
  286. package/pennyfarthing_scripts/sprint/status.py +1 -2
  287. package/pennyfarthing_scripts/sprint/story_add.py +2 -2
  288. package/pennyfarthing_scripts/sprint/story_finish.py +3 -5
  289. package/pennyfarthing_scripts/sprint/story_update.py +11 -3
  290. package/pennyfarthing_scripts/sprint/validate_cmd.py +0 -1
  291. package/pennyfarthing_scripts/sprint/validator.py +120 -6
  292. package/pennyfarthing_scripts/sprint/work.py +1 -4
  293. package/pennyfarthing_scripts/sprint/yaml_io.py +10 -2
  294. package/pennyfarthing_scripts/story/__init__.py +14 -16
  295. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/story/size.py +0 -1
  302. package/pennyfarthing_scripts/story/template.py +0 -1
  303. package/pennyfarthing_scripts/swebench.py +1 -2
  304. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  306. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  307. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  308. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  309. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  310. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  311. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  312. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  313. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  314. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  315. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  316. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  317. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  318. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  319. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  320. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  321. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  322. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  323. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  324. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  325. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  326. package/pennyfarthing_scripts/tests/conftest.py +1 -2
  327. package/pennyfarthing_scripts/tests/test_brownfield.py +10 -13
  328. package/pennyfarthing_scripts/tests/test_cli_modules.py +0 -4
  329. package/pennyfarthing_scripts/tests/test_codemarkers.py +13 -8
  330. package/pennyfarthing_scripts/tests/test_common.py +9 -4
  331. package/pennyfarthing_scripts/tests/test_epic_shard_validation.py +699 -0
  332. package/pennyfarthing_scripts/tests/test_git_utils.py +10 -13
  333. package/pennyfarthing_scripts/tests/test_healthscore.py +17 -25
  334. package/pennyfarthing_scripts/tests/test_jira_package.py +0 -3
  335. package/pennyfarthing_scripts/tests/test_package_structure.py +3 -16
  336. package/pennyfarthing_scripts/tests/test_patch_mode.py +7 -11
  337. package/pennyfarthing_scripts/tests/test_prime.py +39 -21
  338. package/pennyfarthing_scripts/tests/test_sprint_package.py +3 -8
  339. package/pennyfarthing_scripts/tests/test_sprint_validator.py +53 -5
  340. package/pennyfarthing_scripts/tests/test_story_add.py +3 -7
  341. package/pennyfarthing_scripts/tests/test_story_package.py +0 -3
  342. package/pennyfarthing_scripts/tests/test_story_update.py +5 -10
  343. package/pennyfarthing_scripts/tests/test_tiers.py +18 -17
  344. package/pennyfarthing_scripts/tests/test_token_counting.py +19 -13
  345. package/pennyfarthing_scripts/tests/test_validate_cmd.py +2 -7
  346. package/pennyfarthing_scripts/tests/test_workflow_check.py +0 -2
  347. package/pennyfarthing_scripts/tests/test_yaml_io.py +0 -3
  348. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  349. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  350. package/pennyfarthing_scripts/theme/cli.py +3 -2
  351. package/pennyfarthing_scripts/validate/__init__.py +21 -0
  352. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  353. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  354. package/pennyfarthing_scripts/validate/adapters/__init__.py +0 -0
  355. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  356. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  357. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  358. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  359. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  360. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  361. package/pennyfarthing_scripts/validate/adapters/agent.py +239 -0
  362. package/pennyfarthing_scripts/validate/adapters/schema.py +30 -0
  363. package/pennyfarthing_scripts/validate/adapters/skill_command.py +292 -0
  364. package/pennyfarthing_scripts/validate/adapters/sprint.py +69 -0
  365. package/pennyfarthing_scripts/validate/adapters/workflow.py +320 -0
  366. package/pennyfarthing_scripts/validate/cli.py +141 -0
  367. package/pennyfarthing_scripts/welcome_hook.py +2 -3
  368. package/pennyfarthing_scripts/workflow.py +3 -3
  369. package/scripts/README.md +3 -15
  370. package/pennyfarthing-dist/commands/benchmark-control.md +0 -69
  371. package/pennyfarthing-dist/commands/benchmark.md +0 -485
  372. package/pennyfarthing-dist/commands/job-fair.md +0 -102
  373. package/pennyfarthing-dist/commands/solo.md +0 -447
  374. package/pennyfarthing-dist/guides/benchmarks.md +0 -62
  375. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  376. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +0 -59
  377. package/pennyfarthing-dist/scripts/test/ground-truth-judge.py +0 -220
  378. package/pennyfarthing-dist/scripts/test/swebench-judge.py +0 -374
  379. package/pennyfarthing-dist/scripts/test/test-cache.sh +0 -165
  380. package/pennyfarthing-dist/scripts/test/test-setup.sh +0 -337
  381. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +0 -13
  382. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +0 -402
  383. package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +0 -97
  384. package/pennyfarthing-dist/skills/finalize-run/SKILL.md +0 -261
  385. package/pennyfarthing-dist/skills/judge/SKILL.md +0 -644
  386. package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +0 -187
  387. package/pennyfarthing-dist/workflows/dev-story/checklist.md +0 -80
  388. package/pennyfarthing-dist/workflows/dev-story/instructions.xml +0 -410
  389. package/pennyfarthing-dist/workflows/dev-story/workflow.yaml +0 -50
  390. package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +0 -201
  391. package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +0 -156
  392. package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +0 -140
  393. package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +0 -203
  394. package/pennyfarthing-dist/workflows/quick-spec/tech-spec-template.md +0 -74
  395. package/pennyfarthing-dist/workflows/quick-spec/workflow.yaml +0 -27
  396. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  397. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  400. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  401. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  402. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  403. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  404. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  405. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  406. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  407. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -16,21 +16,11 @@ Acceptance Criteria:
16
16
  """
17
17
 
18
18
  import asyncio
19
- import tempfile
20
19
  from pathlib import Path
21
- from typing import Any
22
- from unittest.mock import AsyncMock, MagicMock, patch
20
+ from unittest.mock import AsyncMock, patch
23
21
 
24
22
  import pytest
25
23
 
26
- from pennyfarthing_scripts.git.status_all import (
27
- RepoStatus,
28
- format_status_brief,
29
- format_status_full,
30
- format_summary,
31
- get_all_repo_status,
32
- get_repo_status,
33
- )
34
24
  from pennyfarthing_scripts.git.create_branches import (
35
25
  BranchAction,
36
26
  BranchResult,
@@ -40,7 +30,14 @@ from pennyfarthing_scripts.git.create_branches import (
40
30
  filter_repos,
41
31
  format_results,
42
32
  )
43
-
33
+ from pennyfarthing_scripts.git.status_all import (
34
+ RepoStatus,
35
+ format_status_brief,
36
+ format_status_full,
37
+ format_summary,
38
+ get_all_repo_status,
39
+ get_repo_status,
40
+ )
44
41
 
45
42
  # =============================================================================
46
43
  # Test Fixtures
@@ -793,8 +790,8 @@ class TestCrossPlatformCompatibility:
793
790
  def test_no_shell_specific_commands(self) -> None:
794
791
  """Implementation should not use shell-specific commands."""
795
792
  # This is more of a code review check, but we can verify the modules exist
796
- import pennyfarthing_scripts.git.status_all as status_mod
797
793
  import pennyfarthing_scripts.git.create_branches as branch_mod
794
+ import pennyfarthing_scripts.git.status_all as status_mod
798
795
 
799
796
  # Modules should exist and be importable
800
797
  assert status_mod is not None
@@ -17,19 +17,12 @@ from __future__ import annotations
17
17
 
18
18
  import asyncio
19
19
  import json
20
- import time
21
20
  from dataclasses import asdict
22
21
  from pathlib import Path
23
- from unittest.mock import patch, MagicMock
22
+ from unittest.mock import patch
24
23
 
25
- import pytest
26
24
  from click.testing import CliRunner
27
25
 
28
- from pennyfarthing_scripts.healthscore.models import (
29
- DimensionScore,
30
- HealthscoreResult,
31
- DEFAULT_WEIGHTS,
32
- )
33
26
  from pennyfarthing_scripts.healthscore.analyze import (
34
27
  analyze_healthscore,
35
28
  compute_composite_score,
@@ -37,13 +30,12 @@ from pennyfarthing_scripts.healthscore.analyze import (
37
30
  read_cached_score,
38
31
  write_cached_score,
39
32
  )
40
- from pennyfarthing_scripts.healthscore.formatters import (
41
- format_table,
42
- export_json,
43
- export_csv,
44
- )
45
33
  from pennyfarthing_scripts.healthscore.cli import healthscore
46
-
34
+ from pennyfarthing_scripts.healthscore.models import (
35
+ DEFAULT_WEIGHTS,
36
+ DimensionScore,
37
+ HealthscoreResult,
38
+ )
47
39
 
48
40
  # ---------------------------------------------------------------------------
49
41
  # AC1: Module structure
@@ -122,17 +114,17 @@ class TestWeightedScoring:
122
114
 
123
115
  def test_custom_weights_override_defaults(self):
124
116
  """compute_composite_score must accept custom weights."""
125
- custom = {k: 1.0 / 8 for k in DEFAULT_WEIGHTS}
126
- scores = {k: 80.0 for k in DEFAULT_WEIGHTS}
117
+ custom = dict.fromkeys(DEFAULT_WEIGHTS, 1.0 / 8)
118
+ scores = dict.fromkeys(DEFAULT_WEIGHTS, 80.0)
127
119
  result = compute_composite_score(scores, custom)
128
120
  assert abs(result - 80.0) < 1e-9
129
121
 
130
122
  def test_unequal_custom_weights(self):
131
123
  """Asymmetric weights should shift composite score."""
132
- weights = {k: 0.0 for k in DEFAULT_WEIGHTS}
124
+ weights = dict.fromkeys(DEFAULT_WEIGHTS, 0.0)
133
125
  weights["churn"] = 1.0 # All weight on churn
134
126
 
135
- scores = {k: 50.0 for k in DEFAULT_WEIGHTS}
127
+ scores = dict.fromkeys(DEFAULT_WEIGHTS, 50.0)
136
128
  scores["churn"] = 100.0
137
129
 
138
130
  result = compute_composite_score(scores, weights)
@@ -148,13 +140,13 @@ class TestScoreRanges:
148
140
 
149
141
  def test_all_zeros_yields_zero(self):
150
142
  """All dimensions at 0 → composite 0."""
151
- scores = {k: 0.0 for k in DEFAULT_WEIGHTS}
143
+ scores = dict.fromkeys(DEFAULT_WEIGHTS, 0.0)
152
144
  result = compute_composite_score(scores, DEFAULT_WEIGHTS)
153
145
  assert result == 0.0
154
146
 
155
147
  def test_all_hundreds_yields_hundred(self):
156
148
  """All dimensions at 100 → composite 100."""
157
- scores = {k: 100.0 for k in DEFAULT_WEIGHTS}
149
+ scores = dict.fromkeys(DEFAULT_WEIGHTS, 100.0)
158
150
  result = compute_composite_score(scores, DEFAULT_WEIGHTS)
159
151
  assert abs(result - 100.0) < 1e-9
160
152
 
@@ -176,7 +168,7 @@ class TestScoreRanges:
176
168
 
177
169
  def test_none_dimensions_excluded_and_renormalized(self):
178
170
  """Unavailable dimensions (None) are excluded; remaining weights renormalize."""
179
- scores = {k: None for k in DEFAULT_WEIGHTS}
171
+ scores = dict.fromkeys(DEFAULT_WEIGHTS)
180
172
  scores["churn"] = 80.0
181
173
  scores["complexity"] = 60.0
182
174
  # Only churn (0.15) and complexity (0.15) available → renorm to 0.5 each
@@ -186,7 +178,7 @@ class TestScoreRanges:
186
178
 
187
179
  def test_all_none_dimensions_returns_zero(self):
188
180
  """All dimensions unavailable → composite 0."""
189
- scores = {k: None for k in DEFAULT_WEIGHTS}
181
+ scores = dict.fromkeys(DEFAULT_WEIGHTS)
190
182
  result = compute_composite_score(scores, DEFAULT_WEIGHTS)
191
183
  assert result == 0.0
192
184
 
@@ -374,7 +366,7 @@ class TestCaching:
374
366
  "pennyfarthing_scripts.healthscore.analyze.read_cached_score",
375
367
  return_value=99.0,
376
368
  ) as mock_read:
377
- result = asyncio.run(
369
+ asyncio.run(
378
370
  analyze_healthscore(Path("/tmp/project"), cache_ttl=0)
379
371
  )
380
372
  # With ttl=0, cached values should not be used
@@ -484,7 +476,7 @@ class TestMainCLIRegistration:
484
476
  def test_healthscore_registered_in_main_cli(self):
485
477
  """Main CLI must have a 'healthscore' command group."""
486
478
  from pennyfarthing_scripts.cli import cli
487
- command_names = [cmd for cmd in cli.commands]
479
+ command_names = list(cli.commands)
488
480
  assert "healthscore" in command_names
489
481
 
490
482
 
@@ -507,7 +499,7 @@ class TestAnalyzeIntegration:
507
499
 
508
500
  def test_analyze_with_custom_weights(self):
509
501
  """Custom weights must be accepted and applied."""
510
- custom = {k: 1.0 / 8 for k in DEFAULT_WEIGHTS}
502
+ custom = dict.fromkeys(DEFAULT_WEIGHTS, 1.0 / 8)
511
503
  result = asyncio.run(analyze_healthscore(Path("/tmp/nonexistent"), weights=custom))
512
504
  assert isinstance(result, HealthscoreResult)
513
505
 
@@ -7,9 +7,6 @@ after reorganization from flat modules.
7
7
  """
8
8
 
9
9
  from typing import Any
10
- from unittest.mock import MagicMock, patch
11
-
12
- import pytest
13
10
 
14
11
 
15
12
  class TestJiraClient:
@@ -10,12 +10,7 @@ These tests verify:
10
10
  5. Backwards compatibility for existing imports
11
11
  """
12
12
 
13
- import importlib
14
13
  import sys
15
- from pathlib import Path
16
- from typing import Any
17
-
18
- import pytest
19
14
 
20
15
 
21
16
  class TestCommonPackage:
@@ -126,8 +121,6 @@ class TestJiraPackage:
126
121
  from pennyfarthing_scripts.jira import (
127
122
  JiraClient,
128
123
  extract_jira_key,
129
- map_status_to_jira,
130
- map_jira_to_status,
131
124
  )
132
125
 
133
126
  assert JiraClient is not None
@@ -136,9 +129,7 @@ class TestJiraPackage:
136
129
  def test_jira_backwards_compatibility(self) -> None:
137
130
  """Old-style imports should still work."""
138
131
  # These imports should work for backwards compatibility
139
- from pennyfarthing_scripts.jira import JiraClient
140
- from pennyfarthing_scripts.jira import get_issue
141
- from pennyfarthing_scripts.jira import STATUS_TO_JIRA
132
+ from pennyfarthing_scripts.jira import STATUS_TO_JIRA, JiraClient, get_issue
142
133
 
143
134
  assert JiraClient is not None
144
135
  assert callable(get_issue)
@@ -188,10 +179,8 @@ class TestSprintPackage:
188
179
  def test_sprint_package_reexports(self) -> None:
189
180
  """sprint/__init__.py should re-export commonly used items."""
190
181
  from pennyfarthing_scripts.sprint import (
191
- load_sprint,
192
182
  find_epic,
193
- find_story,
194
- get_all_stories,
183
+ load_sprint,
195
184
  )
196
185
 
197
186
  assert callable(load_sprint)
@@ -199,9 +188,7 @@ class TestSprintPackage:
199
188
 
200
189
  def test_sprint_backwards_compatibility(self) -> None:
201
190
  """Old-style imports should still work."""
202
- from pennyfarthing_scripts.sprint import load_sprint
203
- from pennyfarthing_scripts.sprint import find_epic
204
- from pennyfarthing_scripts.sprint import load_current_sprint
191
+ from pennyfarthing_scripts.sprint import find_epic, load_current_sprint, load_sprint
205
192
 
206
193
  assert callable(load_sprint)
207
194
  assert callable(find_epic)
@@ -18,13 +18,9 @@ Acceptance Criteria:
18
18
  These tests should FAIL until patch_mode.py is implemented.
19
19
  """
20
20
 
21
- import json
22
- import os
23
- import subprocess
24
- import sys
25
21
  from pathlib import Path
26
- from typing import Any, Generator
27
- from unittest.mock import MagicMock, patch, call
22
+ from typing import Any
23
+ from unittest.mock import MagicMock, patch
28
24
 
29
25
  import pytest
30
26
  import yaml
@@ -32,17 +28,17 @@ import yaml
32
28
  # Import will fail until module exists - this is intentional for RED state
33
29
  try:
34
30
  from pennyfarthing_scripts.patch_mode import (
35
- PatchState,
36
31
  PatchStack,
32
+ PatchState,
33
+ create_patch_branch,
37
34
  enter_patch_mode,
38
35
  exit_patch_mode,
36
+ generate_patch_commit_message,
39
37
  get_patch_stack,
40
- create_patch_branch,
38
+ is_in_patch_mode,
39
+ log_patch_to_session,
41
40
  merge_patch_branch,
42
41
  restore_workflow_state,
43
- log_patch_to_session,
44
- generate_patch_commit_message,
45
- is_in_patch_mode,
46
42
  )
47
43
  IMPORT_SUCCESS = True
48
44
  except ImportError:
@@ -3,21 +3,22 @@
3
3
  Tests context loading for prime command.
4
4
  """
5
5
 
6
- import pytest
7
6
  from pathlib import Path
8
- from unittest.mock import patch, MagicMock
7
+ from unittest.mock import patch
8
+
9
+ import pytest
9
10
 
11
+ from pennyfarthing_scripts.prime.cli import main, prime
10
12
  from pennyfarthing_scripts.prime.loader import (
13
+ _extract_session_parts,
14
+ _find_session_file,
11
15
  load_agent_definition,
12
16
  load_behavior_guide,
13
- load_sprint_context,
17
+ load_domain_docs,
14
18
  load_session_context,
15
19
  load_sidecars,
16
- load_domain_docs,
17
- _extract_session_parts,
18
- _find_session_file,
20
+ load_sprint_context,
19
21
  )
20
- from pennyfarthing_scripts.prime.cli import prime, main
21
22
 
22
23
 
23
24
  class TestLoadAgentDefinition:
@@ -407,8 +408,8 @@ class TestWorkflowStateDetection:
407
408
 
408
409
  def test_detect_finish_state(self, tmp_path: Path) -> None:
409
410
  """Test detecting FINISH_STATE when phase is approved."""
410
- from pennyfarthing_scripts.prime.workflow import detect_workflow_state
411
411
  from pennyfarthing_scripts.prime.models import WorkflowState
412
+ from pennyfarthing_scripts.prime.workflow import detect_workflow_state
412
413
 
413
414
  # Setup
414
415
  pf_dir = tmp_path / ".pennyfarthing"
@@ -436,10 +437,11 @@ class TestWorkflowStateDetection:
436
437
 
437
438
  def test_detect_in_progress_state(self, tmp_path: Path) -> None:
438
439
  """Test detecting IN_PROGRESS_STATE with active phase."""
439
- from pennyfarthing_scripts.prime.workflow import detect_workflow_state
440
- from pennyfarthing_scripts.prime.models import WorkflowState
441
440
  import yaml
442
441
 
442
+ from pennyfarthing_scripts.prime.models import WorkflowState
443
+ from pennyfarthing_scripts.prime.workflow import detect_workflow_state
444
+
443
445
  # Setup
444
446
  pf_dir = tmp_path / ".pennyfarthing"
445
447
  pf_dir.mkdir()
@@ -483,10 +485,11 @@ class TestWorkflowStateDetection:
483
485
 
484
486
  def test_detect_new_work_state(self, tmp_path: Path) -> None:
485
487
  """Test detecting NEW_WORK_STATE with backlog stories."""
486
- from pennyfarthing_scripts.prime.workflow import detect_workflow_state
487
- from pennyfarthing_scripts.prime.models import WorkflowState
488
488
  import yaml
489
489
 
490
+ from pennyfarthing_scripts.prime.models import WorkflowState
491
+ from pennyfarthing_scripts.prime.workflow import detect_workflow_state
492
+
490
493
  # Setup
491
494
  pf_dir = tmp_path / ".pennyfarthing"
492
495
  pf_dir.mkdir()
@@ -514,10 +517,11 @@ class TestWorkflowStateDetection:
514
517
 
515
518
  def test_detect_empty_backlog_state(self, tmp_path: Path) -> None:
516
519
  """Test detecting EMPTY_BACKLOG_STATE with no backlog."""
517
- from pennyfarthing_scripts.prime.workflow import detect_workflow_state
518
- from pennyfarthing_scripts.prime.models import WorkflowState
519
520
  import yaml
520
521
 
522
+ from pennyfarthing_scripts.prime.models import WorkflowState
523
+ from pennyfarthing_scripts.prime.workflow import detect_workflow_state
524
+
521
525
  # Setup
522
526
  pf_dir = tmp_path / ".pennyfarthing"
523
527
  pf_dir.mkdir()
@@ -587,8 +591,8 @@ class TestCheckRedirect:
587
591
 
588
592
  def test_redirect_when_wrong_agent(self) -> None:
589
593
  """Test redirect is detected when wrong agent is activated."""
590
- from pennyfarthing_scripts.prime.workflow import check_redirect
591
594
  from pennyfarthing_scripts.prime.models import WorkflowState, WorkflowStatus
595
+ from pennyfarthing_scripts.prime.workflow import check_redirect
592
596
 
593
597
  status = WorkflowStatus(
594
598
  state=WorkflowState.IN_PROGRESS_STATE,
@@ -606,8 +610,8 @@ class TestCheckRedirect:
606
610
 
607
611
  def test_no_redirect_when_correct_agent(self) -> None:
608
612
  """Test no redirect when correct agent is activated."""
609
- from pennyfarthing_scripts.prime.workflow import check_redirect
610
613
  from pennyfarthing_scripts.prime.models import WorkflowState, WorkflowStatus
614
+ from pennyfarthing_scripts.prime.workflow import check_redirect
611
615
 
612
616
  status = WorkflowStatus(
613
617
  state=WorkflowState.IN_PROGRESS_STATE,
@@ -621,8 +625,8 @@ class TestCheckRedirect:
621
625
 
622
626
  def test_no_redirect_for_new_work(self) -> None:
623
627
  """Test no redirect for NEW_WORK_STATE."""
624
- from pennyfarthing_scripts.prime.workflow import check_redirect
625
628
  from pennyfarthing_scripts.prime.models import WorkflowState, WorkflowStatus
629
+ from pennyfarthing_scripts.prime.workflow import check_redirect
626
630
 
627
631
  status = WorkflowStatus(
628
632
  state=WorkflowState.NEW_WORK_STATE,
@@ -644,9 +648,10 @@ class TestPersonaLoading:
644
648
 
645
649
  def test_load_persona_from_theme(self, tmp_path: Path) -> None:
646
650
  """Test loading persona from theme YAML."""
647
- from pennyfarthing_scripts.prime.persona import load_persona
648
651
  import yaml
649
652
 
653
+ from pennyfarthing_scripts.prime.persona import load_persona
654
+
650
655
  # Setup
651
656
  pf_dir = tmp_path / ".pennyfarthing"
652
657
  pf_dir.mkdir()
@@ -697,9 +702,10 @@ class TestPersonaLoading:
697
702
 
698
703
  def test_get_crew_manifest(self, tmp_path: Path) -> None:
699
704
  """Test getting crew manifest for handoff reference."""
700
- from pennyfarthing_scripts.prime.persona import get_crew_manifest
701
705
  import yaml
702
706
 
707
+ from pennyfarthing_scripts.prime.persona import get_crew_manifest
708
+
703
709
  # Setup
704
710
  pf_dir = tmp_path / ".pennyfarthing"
705
711
  pf_dir.mkdir()
@@ -730,8 +736,8 @@ class TestPersonaLoading:
730
736
 
731
737
  def test_format_persona_output(self) -> None:
732
738
  """Test formatting persona as XML."""
739
+ from pennyfarthing_scripts.prime.models import CrewMember, Persona
733
740
  from pennyfarthing_scripts.prime.persona import format_persona_output
734
- from pennyfarthing_scripts.prime.models import Persona, CrewMember
735
741
 
736
742
  persona = Persona(
737
743
  character="Naomi Nagata",
@@ -798,9 +804,10 @@ class TestSessionRegistration:
798
804
 
799
805
  def test_cleanup_old_sessions(self, tmp_path: Path) -> None:
800
806
  """Test cleanup of old session files."""
801
- from pennyfarthing_scripts.prime.session import cleanup_old_sessions
802
807
  import time
803
808
 
809
+ from pennyfarthing_scripts.prime.session import cleanup_old_sessions
810
+
804
811
  # Setup
805
812
  pf_dir = tmp_path / ".pennyfarthing"
806
813
  pf_dir.mkdir()
@@ -886,11 +893,15 @@ class TestJSONOutput:
886
893
  def test_json_output_with_workflow(self, tmp_path: Path, capsys) -> None:
887
894
  """Test JSON output includes workflow status."""
888
895
  import json
896
+
889
897
  import yaml
890
898
 
891
899
  # Setup
892
900
  pf_dir = tmp_path / ".pennyfarthing"
893
901
  pf_dir.mkdir()
902
+ agents_dir = pf_dir / "agents"
903
+ agents_dir.mkdir()
904
+ (agents_dir / "sm.md").write_text("# SM Agent\nScrum Master")
894
905
  sprint_dir = tmp_path / "sprint"
895
906
  sprint_dir.mkdir()
896
907
  (sprint_dir / "current-sprint.yaml").write_text(yaml.dump({
@@ -925,11 +936,15 @@ class TestJSONOutput:
925
936
  def test_json_output_with_redirect(self, tmp_path: Path, capsys) -> None:
926
937
  """Test JSON output includes redirect info."""
927
938
  import json
939
+
928
940
  import yaml
929
941
 
930
942
  # Setup
931
943
  pf_dir = tmp_path / ".pennyfarthing"
932
944
  pf_dir.mkdir()
945
+ agents_dir = pf_dir / "agents"
946
+ agents_dir.mkdir()
947
+ (agents_dir / "tea.md").write_text("# TEA Agent\nTest Engineer")
933
948
 
934
949
  # Create workflow YAML
935
950
  workflows_dir = tmp_path / "pennyfarthing-dist" / "workflows"
@@ -1039,6 +1054,9 @@ class TestCLIFlagsV2:
1039
1054
 
1040
1055
  pf_dir = tmp_path / ".pennyfarthing"
1041
1056
  pf_dir.mkdir()
1057
+ agents_dir = pf_dir / "agents"
1058
+ agents_dir.mkdir()
1059
+ (agents_dir / "sm.md").write_text("# SM Agent\nScrum Master")
1042
1060
 
1043
1061
  with patch("pennyfarthing_scripts.prime.cli.get_project_root", return_value=tmp_path):
1044
1062
  result = main(["--agent", "sm", "--session-id", "explicit-123", "--no-workflow"])
@@ -7,10 +7,7 @@ after reorganization from flat modules.
7
7
  """
8
8
 
9
9
  from pathlib import Path
10
- from typing import Any
11
- from unittest.mock import MagicMock, patch
12
-
13
- import pytest
10
+ from unittest.mock import patch
14
11
 
15
12
 
16
13
  class TestSprintLoader:
@@ -28,8 +25,8 @@ class TestSprintLoader:
28
25
 
29
26
  def test_load_sprint_with_custom_root(self) -> None:
30
27
  """load_sprint should accept custom project root."""
31
- from pennyfarthing_scripts.sprint.loader import load_sprint
32
28
  from pennyfarthing_scripts.common.config import get_project_root
29
+ from pennyfarthing_scripts.sprint.loader import load_sprint
33
30
 
34
31
  root = get_project_root()
35
32
  result = load_sprint(project_root=root)
@@ -341,8 +338,7 @@ epics:
341
338
 
342
339
  def test_get_all_stories_with_shards(self, tmp_path: Path) -> None:
343
340
  """get_all_stories should return stories from merged shards."""
344
- from pennyfarthing_scripts.sprint.loader import get_all_stories, load_sprint
345
- from unittest.mock import patch
341
+ from pennyfarthing_scripts.sprint.loader import get_all_stories
346
342
 
347
343
  root = self._create_sharded_sprint(tmp_path)
348
344
 
@@ -379,7 +375,6 @@ epics:
379
375
  def test_backlog_count_defensive_on_strings(self) -> None:
380
376
  """get_backlog_count should not crash on string epics."""
381
377
  from pennyfarthing_scripts.prime.workflow import get_backlog_count
382
- from unittest.mock import patch
383
378
 
384
379
  fake_data = {"epics": ["MSSCI-14298", "MSSCI-14317"]}
385
380
  with patch("pennyfarthing_scripts.sprint.loader.load_sprint", return_value=fake_data):
@@ -19,19 +19,18 @@ from typing import Any
19
19
  import pytest
20
20
 
21
21
  from pennyfarthing_scripts.sprint.validator import (
22
- ValidationError,
23
22
  ValidationResult,
24
23
  ValidationSeverity,
25
24
  format_validation_errors,
26
25
  validate_archived_sprint,
27
26
  validate_epic,
27
+ validate_epic_shard,
28
28
  validate_full_sprint,
29
29
  validate_sprint,
30
30
  validate_sprint_file,
31
31
  validate_story,
32
32
  )
33
33
 
34
-
35
34
  # =============================================================================
36
35
  # Test Fixtures - Valid Data
37
36
  # =============================================================================
@@ -239,7 +238,7 @@ class TestStoryValidation:
239
238
 
240
239
  def test_all_valid_story_statuses_pass(self) -> None:
241
240
  """All valid status values should pass."""
242
- valid_statuses = ["backlog", "in_progress", "done", "cancelled"]
241
+ valid_statuses = ["backlog", "ready", "in_progress", "done", "canceled", "planning"]
243
242
 
244
243
  for status in valid_statuses:
245
244
  story = {"id": "63-1", "title": "Test", "status": status, "points": 3}
@@ -364,6 +363,55 @@ class TestEpicValidation:
364
363
  assert result.valid is False
365
364
  assert any("duplicate" in e.message.lower() for e in result.errors)
366
365
 
366
+ def test_integer_epic_id_fails(self) -> None:
367
+ """Epic with integer id (YAML bare number) should fail.
368
+
369
+ YAML parses `id: 87` as int. Cyclist's sprint-data.ts calls
370
+ epicId.match() which crashes on non-strings, blanking the panel.
371
+ """
372
+ epic = {
373
+ "id": 87, # Bare integer — not quoted in YAML
374
+ "title": "Test Epic",
375
+ "stories": [
376
+ {"id": "87-1", "title": "Story 1", "status": "backlog", "points": 2},
377
+ ],
378
+ }
379
+
380
+ result = validate_epic(epic, set())
381
+
382
+ assert result.valid is False
383
+ assert any("must be a string" in e.message.lower() for e in result.errors)
384
+
385
+ def test_integer_epic_id_fails_in_shard(self) -> None:
386
+ """Epic shard with integer id should fail validation."""
387
+ epic = {
388
+ "id": 87,
389
+ "title": "Test Epic",
390
+ "status": "planning",
391
+ "stories": [
392
+ {"id": "87-1", "title": "Story 1", "status": "backlog", "points": 2},
393
+ ],
394
+ }
395
+
396
+ result = validate_epic_shard(epic)
397
+
398
+ assert result.valid is False
399
+ assert any("must be a string" in e.message.lower() for e in result.errors)
400
+
401
+ def test_string_epic_id_passes(self) -> None:
402
+ """Epic with properly quoted string id should pass."""
403
+ epic = {
404
+ "id": "87", # Quoted in YAML — parsed as string
405
+ "title": "Test Epic",
406
+ "stories": [
407
+ {"id": "87-1", "title": "Story 1", "status": "backlog", "points": 2},
408
+ ],
409
+ }
410
+
411
+ result = validate_epic(epic, set())
412
+
413
+ assert result.valid is True
414
+
367
415
 
368
416
  # =============================================================================
369
417
  # AC4: Clear error messages for validation failures
@@ -470,7 +518,7 @@ class TestArchivedSprintValidation:
470
518
  assert result.valid is True
471
519
 
472
520
  def test_archived_sprint_allows_all_done_stories(self) -> None:
473
- """Archived sprints should allow all stories to be done/cancelled."""
521
+ """Archived sprints should allow all stories to be done/canceled."""
474
522
  data = {
475
523
  "sprint": {
476
524
  "number": 11,
@@ -486,7 +534,7 @@ class TestArchivedSprintValidation:
486
534
  "title": "Old Epic",
487
535
  "stories": [
488
536
  {"id": "50-1", "title": "Done Story", "status": "done", "points": 3},
489
- {"id": "50-2", "title": "Cancelled Story", "status": "cancelled", "points": 2},
537
+ {"id": "50-2", "title": "Canceled Story", "status": "canceled", "points": 2},
490
538
  ],
491
539
  }
492
540
  ],
@@ -15,11 +15,10 @@ Acceptance Criteria:
15
15
  """
16
16
 
17
17
  from pathlib import Path
18
- from typing import Any
19
18
 
20
19
  import pytest
21
20
  from click.testing import CliRunner
22
- from ruamel.yaml.comments import CommentedMap, CommentedSeq
21
+ from ruamel.yaml.comments import CommentedMap
23
22
 
24
23
  from pennyfarthing_scripts.sprint.story_add import (
25
24
  add_story,
@@ -28,10 +27,8 @@ from pennyfarthing_scripts.sprint.story_add import (
28
27
  from pennyfarthing_scripts.sprint.yaml_io import (
29
28
  STORY_KEY_ORDER,
30
29
  read_sprint,
31
- write_sprint,
32
30
  )
33
31
 
34
-
35
32
  # =============================================================================
36
33
  # Test Fixtures
37
34
  # =============================================================================
@@ -676,7 +673,6 @@ epics:
676
673
  """
677
674
  p = tmp_path / "current-sprint.yaml"
678
675
  p.write_text(yaml_content)
679
- original_content = p.read_text()
680
676
 
681
677
  # Normal add should work fine
682
678
  result = add_story(
@@ -699,10 +695,10 @@ class TestCLIIntegration:
699
695
 
700
696
  def test_story_add_command_exists(self) -> None:
701
697
  """story_add_command should be importable and be a Click command."""
702
- from pennyfarthing_scripts.sprint.story_add import story_add_command
703
-
704
698
  import click
705
699
 
700
+ from pennyfarthing_scripts.sprint.story_add import story_add_command
701
+
706
702
  assert isinstance(story_add_command, click.BaseCommand)
707
703
 
708
704
  def test_basic_add_via_cli(self, runner: CliRunner, sprint_file: Path) -> None:
@@ -5,10 +5,7 @@ Story 63-9: Reorganize pennyfarthing_scripts into fan-out CLI pattern.
5
5
  These tests verify the story/ package modules work correctly.
6
6
  """
7
7
 
8
- from typing import Any
9
- from unittest.mock import patch
10
8
 
11
- import pytest
12
9
 
13
10
 
14
11
  class TestStorySizeModule: