@pennyfarthing/core 10.0.2 → 10.0.5

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 (722) hide show
  1. package/README.md +287 -0
  2. package/package.json +29 -41
  3. package/{dist → packages/core/dist}/cli/commands/cyclist.d.ts +5 -1
  4. package/packages/core/dist/cli/commands/cyclist.d.ts.map +1 -0
  5. package/{dist → packages/core/dist}/cli/commands/cyclist.js +4 -4
  6. package/packages/core/dist/cli/commands/cyclist.js.map +1 -0
  7. package/{dist → packages/core/dist}/cli/commands/cyclist.test.js +2 -2
  8. package/packages/core/dist/cli/commands/cyclist.test.js.map +1 -0
  9. package/packages/core/dist/cli/commands/doctor-file-layout.test.d.ts +13 -0
  10. package/packages/core/dist/cli/commands/doctor-file-layout.test.d.ts.map +1 -0
  11. package/packages/core/dist/cli/commands/doctor-file-layout.test.js +234 -0
  12. package/packages/core/dist/cli/commands/doctor-file-layout.test.js.map +1 -0
  13. package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.js +17 -16
  14. package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.js.map +1 -1
  15. package/{dist → packages/core/dist}/cli/commands/doctor.d.ts +8 -0
  16. package/{dist → packages/core/dist}/cli/commands/doctor.d.ts.map +1 -1
  17. package/{dist → packages/core/dist}/cli/commands/doctor.js +224 -3
  18. package/packages/core/dist/cli/commands/doctor.js.map +1 -0
  19. package/{dist → packages/core/dist}/cli/commands/e2e-fresh-install.test.js +1 -1
  20. package/{dist → packages/core/dist}/cli/commands/e2e-fresh-install.test.js.map +1 -1
  21. package/{dist → packages/core/dist}/cli/commands/e2e-upgrade.test.js +1 -1
  22. package/{dist → packages/core/dist}/cli/commands/e2e-upgrade.test.js.map +1 -1
  23. package/packages/core/dist/cli/commands/hooks-consolidation.test.d.ts +19 -0
  24. package/packages/core/dist/cli/commands/hooks-consolidation.test.d.ts.map +1 -0
  25. package/packages/core/dist/cli/commands/hooks-consolidation.test.js +358 -0
  26. package/packages/core/dist/cli/commands/hooks-consolidation.test.js.map +1 -0
  27. package/{dist → packages/core/dist}/cli/commands/init.d.ts.map +1 -1
  28. package/{dist → packages/core/dist}/cli/commands/init.js +3 -0
  29. package/packages/core/dist/cli/commands/init.js.map +1 -0
  30. package/{dist → packages/core/dist}/cli/commands/update.d.ts.map +1 -1
  31. package/{dist → packages/core/dist}/cli/commands/update.js +53 -1
  32. package/{dist → packages/core/dist}/cli/commands/update.js.map +1 -1
  33. package/{dist → packages/core/dist}/cli/ocean-profiles.test.js +1 -1
  34. package/{dist → packages/core/dist}/cli/ocean-profiles.test.js.map +1 -1
  35. package/{dist → packages/core/dist}/cli/utils/files.d.ts +10 -0
  36. package/{dist → packages/core/dist}/cli/utils/files.d.ts.map +1 -1
  37. package/{dist → packages/core/dist}/cli/utils/files.js +35 -0
  38. package/{dist → packages/core/dist}/cli/utils/files.js.map +1 -1
  39. package/{dist → packages/core/dist}/cli/utils/settings.d.ts.map +1 -1
  40. package/{dist → packages/core/dist}/cli/utils/settings.js +24 -0
  41. package/packages/core/dist/cli/utils/settings.js.map +1 -0
  42. package/{dist → packages/core/dist}/cli/utils/themes.d.ts +1 -0
  43. package/packages/core/dist/cli/utils/themes.d.ts.map +1 -0
  44. package/{dist → packages/core/dist}/cli/utils/themes.js.map +1 -1
  45. package/{dist → packages/core/dist}/scripts/generate-report.d.ts.map +1 -1
  46. package/{dist → packages/core/dist}/scripts/generate-report.js +11 -7
  47. package/packages/core/dist/scripts/generate-report.js.map +1 -0
  48. package/{dist → packages/core/dist}/scripts/generate-spider-report.d.ts.map +1 -1
  49. package/{dist → packages/core/dist}/scripts/generate-spider-report.js +12 -8
  50. package/packages/core/dist/scripts/generate-spider-report.js.map +1 -0
  51. package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -0
  52. package/{dist → packages/core/dist}/scripts/generate-spider.js +6 -4
  53. package/packages/core/dist/scripts/generate-spider.js.map +1 -0
  54. package/{dist → packages/core/dist}/scripts/generate-spider.test.js +2 -2
  55. package/packages/core/dist/scripts/generate-spider.test.js.map +1 -0
  56. package/pennyfarthing-dist/agents/pm.md +1 -1
  57. package/pennyfarthing-dist/agents/sm-finish.md +1 -1
  58. package/pennyfarthing-dist/agents/sm-setup.md +6 -6
  59. package/pennyfarthing-dist/agents/sm.md +12 -6
  60. package/pennyfarthing-dist/agents/workflow-status-check.md +1 -1
  61. package/pennyfarthing-dist/commands/repo-status.md +2 -2
  62. package/pennyfarthing-dist/commands/sprint.md +8 -8
  63. package/pennyfarthing-dist/guides/bell-mode.md +65 -0
  64. package/pennyfarthing-dist/guides/benchmarks.md +62 -0
  65. package/pennyfarthing-dist/guides/bikelane.md +86 -0
  66. package/pennyfarthing-dist/guides/prime.md +72 -0
  67. package/pennyfarthing-dist/guides/reflector.md +59 -0
  68. package/pennyfarthing-dist/guides/relay-mode.md +53 -0
  69. package/pennyfarthing-dist/guides/skill-schema.md +25 -26
  70. package/pennyfarthing-dist/guides/tirepump.md +54 -0
  71. package/pennyfarthing-dist/guides/xml-tags.md +2 -2
  72. package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +59 -58
  73. package/pennyfarthing-dist/personas/themes/blade-runner.yaml +10 -10
  74. package/pennyfarthing-dist/personas/themes/doctor-who.yaml +10 -10
  75. package/pennyfarthing-dist/personas/themes/dune.yaml +64 -69
  76. package/pennyfarthing-dist/personas/themes/firefly.yaml +60 -73
  77. package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +60 -69
  78. package/pennyfarthing-dist/personas/themes/harry-potter.yaml +59 -73
  79. package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +45 -57
  80. package/pennyfarthing-dist/personas/themes/mad-max.yaml +5 -11
  81. package/pennyfarthing-dist/personas/themes/princess-bride.yaml +53 -63
  82. package/pennyfarthing-dist/personas/themes/sandman.yaml +59 -59
  83. package/pennyfarthing-dist/personas/themes/the-matrix.yaml +61 -62
  84. package/pennyfarthing-dist/personas/themes/west-wing.yaml +8 -9
  85. package/pennyfarthing-dist/scripts/README.md +2 -2
  86. package/pennyfarthing-dist/scripts/git/git-status-all.sh +1 -1
  87. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +3 -3
  88. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +32 -0
  89. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +2 -7
  90. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +1 -1
  91. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +12 -91
  92. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +11 -86
  93. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +11 -255
  94. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +3 -3
  95. package/pennyfarthing-dist/scripts/sprint/README.md +32 -17
  96. package/pennyfarthing-dist/scripts/story/README.md +1 -1
  97. package/pennyfarthing-dist/scripts/test/test-setup.sh +1 -1
  98. package/pennyfarthing-dist/skills/jira/SKILL.md +107 -408
  99. package/pennyfarthing-dist/skills/skill-registry.yaml +21 -12
  100. package/pennyfarthing-dist/skills/sprint/skill.md +386 -68
  101. package/pennyfarthing-dist/skills/story/skill.md +14 -206
  102. package/pennyfarthing-dist/templates/settings.local.json.template +9 -1
  103. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +1 -1
  104. package/pennyfarthing-dist/workflows/git-cleanup.yaml +1 -1
  105. package/pennyfarthing-dist/workflows/project-setup/steps/step-10-complete.md +1 -1
  106. package/pennyfarthing_scripts/README.md +66 -0
  107. package/pennyfarthing_scripts/__init__.py +17 -0
  108. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  109. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  110. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  111. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  112. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  113. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  114. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  116. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  121. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  124. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/bellmode_hook.py +154 -0
  126. package/pennyfarthing_scripts/brownfield/__init__.py +35 -0
  127. package/pennyfarthing_scripts/brownfield/__main__.py +7 -0
  128. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  129. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  130. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/brownfield/cli.py +131 -0
  133. package/pennyfarthing_scripts/brownfield/discover.py +753 -0
  134. package/pennyfarthing_scripts/cli.py +184 -0
  135. package/pennyfarthing_scripts/common/__init__.py +49 -0
  136. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  137. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  138. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  140. package/pennyfarthing_scripts/common/config.py +92 -0
  141. package/pennyfarthing_scripts/common/output.py +180 -0
  142. package/pennyfarthing_scripts/common/themes.py +253 -0
  143. package/pennyfarthing_scripts/config.py +21 -0
  144. package/pennyfarthing_scripts/context.py +414 -0
  145. package/pennyfarthing_scripts/git/__init__.py +29 -0
  146. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  147. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  148. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  149. package/pennyfarthing_scripts/git/create_branches.py +439 -0
  150. package/pennyfarthing_scripts/git/status_all.py +310 -0
  151. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +7 -0
  152. package/pennyfarthing_scripts/hooks.py +454 -0
  153. package/pennyfarthing_scripts/hotspots/__init__.py +31 -0
  154. package/pennyfarthing_scripts/hotspots/__main__.py +6 -0
  155. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  160. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  161. package/pennyfarthing_scripts/hotspots/analyze.py +472 -0
  162. package/pennyfarthing_scripts/hotspots/cli.py +152 -0
  163. package/pennyfarthing_scripts/hotspots/formatters.py +109 -0
  164. package/pennyfarthing_scripts/hotspots/models.py +60 -0
  165. package/pennyfarthing_scripts/jira/__init__.py +99 -0
  166. package/pennyfarthing_scripts/jira/__main__.py +10 -0
  167. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/jira/bidirectional.py +561 -0
  183. package/pennyfarthing_scripts/jira/claim.py +211 -0
  184. package/pennyfarthing_scripts/jira/cli.py +351 -0
  185. package/pennyfarthing_scripts/jira/client.py +762 -0
  186. package/pennyfarthing_scripts/jira/create.py +267 -0
  187. package/pennyfarthing_scripts/jira/epic.py +176 -0
  188. package/pennyfarthing_scripts/jira/operations.py +124 -0
  189. package/pennyfarthing_scripts/jira/reconcile.py +277 -0
  190. package/pennyfarthing_scripts/jira/story.py +219 -0
  191. package/pennyfarthing_scripts/jira/sync.py +350 -0
  192. package/pennyfarthing_scripts/jira_bidirectional_sync.py +37 -0
  193. package/pennyfarthing_scripts/jira_epic_creation.py +30 -0
  194. package/pennyfarthing_scripts/jira_sync.py +36 -0
  195. package/pennyfarthing_scripts/jira_sync_story.py +30 -0
  196. package/pennyfarthing_scripts/migration/__init__.py +39 -0
  197. package/pennyfarthing_scripts/migration/__main__.py +10 -0
  198. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/migration/cli.py +304 -0
  206. package/pennyfarthing_scripts/migration/session.py +384 -0
  207. package/pennyfarthing_scripts/migration/skill.py +188 -0
  208. package/pennyfarthing_scripts/migration/step.py +229 -0
  209. package/pennyfarthing_scripts/migration/validate.py +282 -0
  210. package/pennyfarthing_scripts/output.py +37 -0
  211. package/pennyfarthing_scripts/patch_mode.py +449 -0
  212. package/pennyfarthing_scripts/preflight/__init__.py +17 -0
  213. package/pennyfarthing_scripts/preflight/__main__.py +10 -0
  214. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/preflight/cli.py +141 -0
  219. package/pennyfarthing_scripts/preflight/finish.py +382 -0
  220. package/pennyfarthing_scripts/pretooluse_hook.py +193 -0
  221. package/pennyfarthing_scripts/prime/__init__.py +125 -0
  222. package/pennyfarthing_scripts/prime/__main__.py +8 -0
  223. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  226. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  228. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  229. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/prime/cli.py +645 -0
  233. package/pennyfarthing_scripts/prime/loader.py +239 -0
  234. package/pennyfarthing_scripts/prime/models.py +206 -0
  235. package/pennyfarthing_scripts/prime/persona.py +309 -0
  236. package/pennyfarthing_scripts/prime/session.py +183 -0
  237. package/pennyfarthing_scripts/prime/tiers.py +201 -0
  238. package/pennyfarthing_scripts/prime/workflow.py +277 -0
  239. package/pennyfarthing_scripts/schema_validation_hook.py +306 -0
  240. package/pennyfarthing_scripts/sprint/__init__.py +66 -0
  241. package/pennyfarthing_scripts/sprint/__main__.py +10 -0
  242. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  243. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/sprint/archive.py +165 -0
  257. package/pennyfarthing_scripts/sprint/archive_epic.py +408 -0
  258. package/pennyfarthing_scripts/sprint/cli.py +1863 -0
  259. package/pennyfarthing_scripts/sprint/epic_add.py +173 -0
  260. package/pennyfarthing_scripts/sprint/import_epic.py +431 -0
  261. package/pennyfarthing_scripts/sprint/loader.py +237 -0
  262. package/pennyfarthing_scripts/sprint/status.py +122 -0
  263. package/pennyfarthing_scripts/sprint/story_add.py +187 -0
  264. package/pennyfarthing_scripts/sprint/story_update.py +181 -0
  265. package/pennyfarthing_scripts/sprint/validate_cmd.py +307 -0
  266. package/pennyfarthing_scripts/sprint/validator.py +580 -0
  267. package/pennyfarthing_scripts/sprint/work.py +208 -0
  268. package/pennyfarthing_scripts/sprint/yaml_io.py +367 -0
  269. package/pennyfarthing_scripts/story/__init__.py +67 -0
  270. package/pennyfarthing_scripts/story/__main__.py +10 -0
  271. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/story/cli.py +105 -0
  278. package/pennyfarthing_scripts/story/create.py +167 -0
  279. package/pennyfarthing_scripts/story/size.py +113 -0
  280. package/pennyfarthing_scripts/story/template.py +151 -0
  281. package/pennyfarthing_scripts/swebench.py +216 -0
  282. package/pennyfarthing_scripts/tests/__init__.py +1 -0
  283. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  285. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  286. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  287. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  288. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  289. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  290. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  291. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  292. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  293. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  294. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  295. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  296. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  297. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  298. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  299. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  300. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  301. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  302. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  303. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  304. package/pennyfarthing_scripts/tests/conftest.py +106 -0
  305. package/pennyfarthing_scripts/tests/test_brownfield.py +842 -0
  306. package/pennyfarthing_scripts/tests/test_cli_modules.py +245 -0
  307. package/pennyfarthing_scripts/tests/test_common.py +180 -0
  308. package/pennyfarthing_scripts/tests/test_git_utils.py +866 -0
  309. package/pennyfarthing_scripts/tests/test_jira_package.py +334 -0
  310. package/pennyfarthing_scripts/tests/test_package_structure.py +372 -0
  311. package/pennyfarthing_scripts/tests/test_patch_mode.py +830 -0
  312. package/pennyfarthing_scripts/tests/test_prime.py +1050 -0
  313. package/pennyfarthing_scripts/tests/test_sprint_package.py +402 -0
  314. package/pennyfarthing_scripts/tests/test_sprint_validator.py +731 -0
  315. package/pennyfarthing_scripts/tests/test_story_add.py +921 -0
  316. package/pennyfarthing_scripts/tests/test_story_package.py +156 -0
  317. package/pennyfarthing_scripts/tests/test_story_update.py +769 -0
  318. package/pennyfarthing_scripts/tests/test_tiers.py +1090 -0
  319. package/pennyfarthing_scripts/tests/test_token_counting.py +559 -0
  320. package/pennyfarthing_scripts/tests/test_validate_cmd.py +500 -0
  321. package/pennyfarthing_scripts/tests/test_workflow_check.py +341 -0
  322. package/pennyfarthing_scripts/tests/test_yaml_io.py +815 -0
  323. package/pennyfarthing_scripts/welcome_hook.py +157 -0
  324. package/pennyfarthing_scripts/workflow.py +287 -0
  325. package/scripts/postinstall.cjs +34 -0
  326. package/dist/cli/commands/cyclist.d.ts.map +0 -1
  327. package/dist/cli/commands/cyclist.js.map +0 -1
  328. package/dist/cli/commands/cyclist.test.js.map +0 -1
  329. package/dist/cli/commands/doctor.js.map +0 -1
  330. package/dist/cli/commands/init.js.map +0 -1
  331. package/dist/cli/utils/settings.js.map +0 -1
  332. package/dist/cli/utils/themes.d.ts.map +0 -1
  333. package/dist/scripts/generate-report.js.map +0 -1
  334. package/dist/scripts/generate-spider-report.js.map +0 -1
  335. package/dist/scripts/generate-spider.d.ts.map +0 -1
  336. package/dist/scripts/generate-spider.js.map +0 -1
  337. package/dist/scripts/generate-spider.test.js.map +0 -1
  338. package/pennyfarthing-dist/scripts/jira/jira-lib.sh +0 -464
  339. package/pennyfarthing-dist/scripts/jira/jira-sync.sh +0 -16
  340. package/pennyfarthing-dist/scripts/jira/sync-epic-to-jira.sh +0 -16
  341. package/pennyfarthing-dist/scripts/sprint/archive-story.sh +0 -133
  342. package/pennyfarthing-dist/scripts/sprint/available-stories.sh +0 -91
  343. package/pennyfarthing-dist/scripts/sprint/check-story.sh +0 -158
  344. package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +0 -52
  345. package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +0 -63
  346. package/pennyfarthing-dist/scripts/sprint/list-future.sh +0 -145
  347. package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +0 -110
  348. package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +0 -148
  349. package/pennyfarthing-dist/scripts/sprint/sprint-common.sh +0 -415
  350. package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +0 -33
  351. package/pennyfarthing-dist/scripts/sprint/sprint-metrics.sh +0 -230
  352. package/pennyfarthing-dist/scripts/sprint/sprint-status.sh +0 -134
  353. package/pennyfarthing-dist/scripts/sprint/validate-sprint-yaml.sh +0 -139
  354. package/pennyfarthing-dist/skills/sprint/scripts/archive-story.sh +0 -101
  355. package/pennyfarthing-dist/skills/sprint/scripts/available-stories.sh +0 -97
  356. package/pennyfarthing-dist/skills/sprint/scripts/check-story.sh +0 -164
  357. package/pennyfarthing-dist/skills/sprint/scripts/create-jira-epic.sh +0 -101
  358. package/pennyfarthing-dist/skills/sprint/scripts/new-sprint.sh +0 -116
  359. package/pennyfarthing-dist/skills/sprint/scripts/promote-epic.sh +0 -164
  360. package/pennyfarthing-dist/skills/sprint/scripts/sprint-info.sh +0 -39
  361. package/pennyfarthing-dist/skills/sprint/scripts/sprint-status.sh +0 -147
  362. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +0 -93
  363. /package/{bin → packages/core/bin}/pennyfarthing.js +0 -0
  364. /package/{dist → packages/core/dist}/bmad/context-reader.d.ts +0 -0
  365. /package/{dist → packages/core/dist}/bmad/context-reader.d.ts.map +0 -0
  366. /package/{dist → packages/core/dist}/bmad/context-reader.js +0 -0
  367. /package/{dist → packages/core/dist}/bmad/context-reader.js.map +0 -0
  368. /package/{dist → packages/core/dist}/bmad/context-reader.test.d.ts +0 -0
  369. /package/{dist → packages/core/dist}/bmad/context-reader.test.d.ts.map +0 -0
  370. /package/{dist → packages/core/dist}/bmad/context-reader.test.js +0 -0
  371. /package/{dist → packages/core/dist}/bmad/context-reader.test.js.map +0 -0
  372. /package/{dist → packages/core/dist}/bmad/epics-parser.d.ts +0 -0
  373. /package/{dist → packages/core/dist}/bmad/epics-parser.d.ts.map +0 -0
  374. /package/{dist → packages/core/dist}/bmad/epics-parser.js +0 -0
  375. /package/{dist → packages/core/dist}/bmad/epics-parser.js.map +0 -0
  376. /package/{dist → packages/core/dist}/bmad/epics-parser.test.d.ts +0 -0
  377. /package/{dist → packages/core/dist}/bmad/epics-parser.test.d.ts.map +0 -0
  378. /package/{dist → packages/core/dist}/bmad/epics-parser.test.js +0 -0
  379. /package/{dist → packages/core/dist}/bmad/epics-parser.test.js.map +0 -0
  380. /package/{dist → packages/core/dist}/bmad/index.d.ts +0 -0
  381. /package/{dist → packages/core/dist}/bmad/index.d.ts.map +0 -0
  382. /package/{dist → packages/core/dist}/bmad/index.js +0 -0
  383. /package/{dist → packages/core/dist}/bmad/index.js.map +0 -0
  384. /package/{dist → packages/core/dist}/bmad/status-sync.d.ts +0 -0
  385. /package/{dist → packages/core/dist}/bmad/status-sync.d.ts.map +0 -0
  386. /package/{dist → packages/core/dist}/bmad/status-sync.js +0 -0
  387. /package/{dist → packages/core/dist}/bmad/status-sync.js.map +0 -0
  388. /package/{dist → packages/core/dist}/bmad/status-sync.test.d.ts +0 -0
  389. /package/{dist → packages/core/dist}/bmad/status-sync.test.d.ts.map +0 -0
  390. /package/{dist → packages/core/dist}/bmad/status-sync.test.js +0 -0
  391. /package/{dist → packages/core/dist}/bmad/status-sync.test.js.map +0 -0
  392. /package/{dist → packages/core/dist}/bmad/story-exporter.d.ts +0 -0
  393. /package/{dist → packages/core/dist}/bmad/story-exporter.d.ts.map +0 -0
  394. /package/{dist → packages/core/dist}/bmad/story-exporter.js +0 -0
  395. /package/{dist → packages/core/dist}/bmad/story-exporter.js.map +0 -0
  396. /package/{dist → packages/core/dist}/bmad/story-exporter.test.d.ts +0 -0
  397. /package/{dist → packages/core/dist}/bmad/story-exporter.test.d.ts.map +0 -0
  398. /package/{dist → packages/core/dist}/bmad/story-exporter.test.js +0 -0
  399. /package/{dist → packages/core/dist}/bmad/story-exporter.test.js.map +0 -0
  400. /package/{dist → packages/core/dist}/bmad/story-parser.d.ts +0 -0
  401. /package/{dist → packages/core/dist}/bmad/story-parser.d.ts.map +0 -0
  402. /package/{dist → packages/core/dist}/bmad/story-parser.js +0 -0
  403. /package/{dist → packages/core/dist}/bmad/story-parser.js.map +0 -0
  404. /package/{dist → packages/core/dist}/bmad/story-parser.test.d.ts +0 -0
  405. /package/{dist → packages/core/dist}/bmad/story-parser.test.d.ts.map +0 -0
  406. /package/{dist → packages/core/dist}/bmad/story-parser.test.js +0 -0
  407. /package/{dist → packages/core/dist}/bmad/story-parser.test.js.map +0 -0
  408. /package/{dist → packages/core/dist}/cli/commands/command.d.ts +0 -0
  409. /package/{dist → packages/core/dist}/cli/commands/command.d.ts.map +0 -0
  410. /package/{dist → packages/core/dist}/cli/commands/command.js +0 -0
  411. /package/{dist → packages/core/dist}/cli/commands/command.js.map +0 -0
  412. /package/{dist → packages/core/dist}/cli/commands/cyclist.test.d.ts +0 -0
  413. /package/{dist → packages/core/dist}/cli/commands/cyclist.test.d.ts.map +0 -0
  414. /package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.d.ts +0 -0
  415. /package/{dist → packages/core/dist}/cli/commands/doctor-legacy.test.d.ts.map +0 -0
  416. /package/{dist → packages/core/dist}/cli/commands/e2e-fresh-install.test.d.ts +0 -0
  417. /package/{dist → packages/core/dist}/cli/commands/e2e-fresh-install.test.d.ts.map +0 -0
  418. /package/{dist → packages/core/dist}/cli/commands/e2e-upgrade.test.d.ts +0 -0
  419. /package/{dist → packages/core/dist}/cli/commands/e2e-upgrade.test.d.ts.map +0 -0
  420. /package/{dist → packages/core/dist}/cli/commands/init-consolidation.test.d.ts +0 -0
  421. /package/{dist → packages/core/dist}/cli/commands/init-consolidation.test.d.ts.map +0 -0
  422. /package/{dist → packages/core/dist}/cli/commands/init-consolidation.test.js +0 -0
  423. /package/{dist → packages/core/dist}/cli/commands/init-consolidation.test.js.map +0 -0
  424. /package/{dist → packages/core/dist}/cli/commands/init.d.ts +0 -0
  425. /package/{dist → packages/core/dist}/cli/commands/persona-config-consolidation.test.d.ts +0 -0
  426. /package/{dist → packages/core/dist}/cli/commands/persona-config-consolidation.test.d.ts.map +0 -0
  427. /package/{dist → packages/core/dist}/cli/commands/persona-config-consolidation.test.js +0 -0
  428. /package/{dist → packages/core/dist}/cli/commands/persona-config-consolidation.test.js.map +0 -0
  429. /package/{dist → packages/core/dist}/cli/commands/skill.d.ts +0 -0
  430. /package/{dist → packages/core/dist}/cli/commands/skill.d.ts.map +0 -0
  431. /package/{dist → packages/core/dist}/cli/commands/skill.js +0 -0
  432. /package/{dist → packages/core/dist}/cli/commands/skill.js.map +0 -0
  433. /package/{dist → packages/core/dist}/cli/commands/theme.d.ts +0 -0
  434. /package/{dist → packages/core/dist}/cli/commands/theme.d.ts.map +0 -0
  435. /package/{dist → packages/core/dist}/cli/commands/theme.js +0 -0
  436. /package/{dist → packages/core/dist}/cli/commands/theme.js.map +0 -0
  437. /package/{dist → packages/core/dist}/cli/commands/uninstall.d.ts +0 -0
  438. /package/{dist → packages/core/dist}/cli/commands/uninstall.d.ts.map +0 -0
  439. /package/{dist → packages/core/dist}/cli/commands/uninstall.js +0 -0
  440. /package/{dist → packages/core/dist}/cli/commands/uninstall.js.map +0 -0
  441. /package/{dist → packages/core/dist}/cli/commands/update-consolidation.test.d.ts +0 -0
  442. /package/{dist → packages/core/dist}/cli/commands/update-consolidation.test.d.ts.map +0 -0
  443. /package/{dist → packages/core/dist}/cli/commands/update-consolidation.test.js +0 -0
  444. /package/{dist → packages/core/dist}/cli/commands/update-consolidation.test.js.map +0 -0
  445. /package/{dist → packages/core/dist}/cli/commands/update.d.ts +0 -0
  446. /package/{dist → packages/core/dist}/cli/commands/version.d.ts +0 -0
  447. /package/{dist → packages/core/dist}/cli/commands/version.d.ts.map +0 -0
  448. /package/{dist → packages/core/dist}/cli/commands/version.js +0 -0
  449. /package/{dist → packages/core/dist}/cli/commands/version.js.map +0 -0
  450. /package/{dist → packages/core/dist}/cli/customization.test.d.ts +0 -0
  451. /package/{dist → packages/core/dist}/cli/customization.test.d.ts.map +0 -0
  452. /package/{dist → packages/core/dist}/cli/customization.test.js +0 -0
  453. /package/{dist → packages/core/dist}/cli/customization.test.js.map +0 -0
  454. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.d.ts +0 -0
  455. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.d.ts.map +0 -0
  456. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.js +0 -0
  457. /package/{dist → packages/core/dist}/cli/cyclist-migration.test.js.map +0 -0
  458. /package/{dist → packages/core/dist}/cli/index.d.ts +0 -0
  459. /package/{dist → packages/core/dist}/cli/index.d.ts.map +0 -0
  460. /package/{dist → packages/core/dist}/cli/index.js +0 -0
  461. /package/{dist → packages/core/dist}/cli/index.js.map +0 -0
  462. /package/{dist → packages/core/dist}/cli/ocean-profiles.test.d.ts +0 -0
  463. /package/{dist → packages/core/dist}/cli/ocean-profiles.test.d.ts.map +0 -0
  464. /package/{dist → packages/core/dist}/cli/theme-maker.test.d.ts +0 -0
  465. /package/{dist → packages/core/dist}/cli/theme-maker.test.d.ts.map +0 -0
  466. /package/{dist → packages/core/dist}/cli/theme-maker.test.js +0 -0
  467. /package/{dist → packages/core/dist}/cli/theme-maker.test.js.map +0 -0
  468. /package/{dist → packages/core/dist}/cli/utils/constants.d.ts +0 -0
  469. /package/{dist → packages/core/dist}/cli/utils/constants.d.ts.map +0 -0
  470. /package/{dist → packages/core/dist}/cli/utils/constants.js +0 -0
  471. /package/{dist → packages/core/dist}/cli/utils/constants.js.map +0 -0
  472. /package/{dist → packages/core/dist}/cli/utils/logger.d.ts +0 -0
  473. /package/{dist → packages/core/dist}/cli/utils/logger.d.ts.map +0 -0
  474. /package/{dist → packages/core/dist}/cli/utils/logger.js +0 -0
  475. /package/{dist → packages/core/dist}/cli/utils/logger.js.map +0 -0
  476. /package/{dist → packages/core/dist}/cli/utils/manifest.d.ts +0 -0
  477. /package/{dist → packages/core/dist}/cli/utils/manifest.d.ts.map +0 -0
  478. /package/{dist → packages/core/dist}/cli/utils/manifest.js +0 -0
  479. /package/{dist → packages/core/dist}/cli/utils/manifest.js.map +0 -0
  480. /package/{dist → packages/core/dist}/cli/utils/node-modules.d.ts +0 -0
  481. /package/{dist → packages/core/dist}/cli/utils/node-modules.d.ts.map +0 -0
  482. /package/{dist → packages/core/dist}/cli/utils/node-modules.js +0 -0
  483. /package/{dist → packages/core/dist}/cli/utils/node-modules.js.map +0 -0
  484. /package/{dist → packages/core/dist}/cli/utils/prompts.d.ts +0 -0
  485. /package/{dist → packages/core/dist}/cli/utils/prompts.d.ts.map +0 -0
  486. /package/{dist → packages/core/dist}/cli/utils/prompts.js +0 -0
  487. /package/{dist → packages/core/dist}/cli/utils/prompts.js.map +0 -0
  488. /package/{dist → packages/core/dist}/cli/utils/settings-consolidation.test.d.ts +0 -0
  489. /package/{dist → packages/core/dist}/cli/utils/settings-consolidation.test.d.ts.map +0 -0
  490. /package/{dist → packages/core/dist}/cli/utils/settings-consolidation.test.js +0 -0
  491. /package/{dist → packages/core/dist}/cli/utils/settings-consolidation.test.js.map +0 -0
  492. /package/{dist → packages/core/dist}/cli/utils/settings.d.ts +0 -0
  493. /package/{dist → packages/core/dist}/cli/utils/symlinks.d.ts +0 -0
  494. /package/{dist → packages/core/dist}/cli/utils/symlinks.d.ts.map +0 -0
  495. /package/{dist → packages/core/dist}/cli/utils/symlinks.js +0 -0
  496. /package/{dist → packages/core/dist}/cli/utils/symlinks.js.map +0 -0
  497. /package/{dist → packages/core/dist}/cli/utils/themes.js +0 -0
  498. /package/{dist → packages/core/dist}/cli/utils/themes.test.d.ts +0 -0
  499. /package/{dist → packages/core/dist}/cli/utils/themes.test.d.ts.map +0 -0
  500. /package/{dist → packages/core/dist}/cli/utils/themes.test.js +0 -0
  501. /package/{dist → packages/core/dist}/cli/utils/themes.test.js.map +0 -0
  502. /package/{dist → packages/core/dist}/cli/utils/version.d.ts +0 -0
  503. /package/{dist → packages/core/dist}/cli/utils/version.d.ts.map +0 -0
  504. /package/{dist → packages/core/dist}/cli/utils/version.js +0 -0
  505. /package/{dist → packages/core/dist}/cli/utils/version.js.map +0 -0
  506. /package/{dist → packages/core/dist}/cli/workspace.test.d.ts +0 -0
  507. /package/{dist → packages/core/dist}/cli/workspace.test.d.ts.map +0 -0
  508. /package/{dist → packages/core/dist}/cli/workspace.test.js +0 -0
  509. /package/{dist → packages/core/dist}/cli/workspace.test.js.map +0 -0
  510. /package/{dist → packages/core/dist}/index.d.ts +0 -0
  511. /package/{dist → packages/core/dist}/index.d.ts.map +0 -0
  512. /package/{dist → packages/core/dist}/index.js +0 -0
  513. /package/{dist → packages/core/dist}/index.js.map +0 -0
  514. /package/{dist → packages/core/dist}/jira/jira-epic-creation.d.ts +0 -0
  515. /package/{dist → packages/core/dist}/jira/jira-epic-creation.d.ts.map +0 -0
  516. /package/{dist → packages/core/dist}/jira/jira-epic-creation.js +0 -0
  517. /package/{dist → packages/core/dist}/jira/jira-epic-creation.js.map +0 -0
  518. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.d.ts +0 -0
  519. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.d.ts.map +0 -0
  520. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.js +0 -0
  521. /package/{dist → packages/core/dist}/jira/jira-epic-creation.test.js.map +0 -0
  522. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.d.ts +0 -0
  523. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.d.ts.map +0 -0
  524. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.js +0 -0
  525. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.js.map +0 -0
  526. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.d.ts +0 -0
  527. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.d.ts.map +0 -0
  528. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.js +0 -0
  529. /package/{dist → packages/core/dist}/jira/jira-sprint-sync.test.js.map +0 -0
  530. /package/{dist → packages/core/dist}/permissions/index.d.ts +0 -0
  531. /package/{dist → packages/core/dist}/permissions/index.d.ts.map +0 -0
  532. /package/{dist → packages/core/dist}/permissions/index.js +0 -0
  533. /package/{dist → packages/core/dist}/permissions/index.js.map +0 -0
  534. /package/{dist → packages/core/dist}/permissions/permission-schema.d.ts +0 -0
  535. /package/{dist → packages/core/dist}/permissions/permission-schema.d.ts.map +0 -0
  536. /package/{dist → packages/core/dist}/permissions/permission-schema.js +0 -0
  537. /package/{dist → packages/core/dist}/permissions/permission-schema.js.map +0 -0
  538. /package/{dist → packages/core/dist}/permissions/permission-schema.test.d.ts +0 -0
  539. /package/{dist → packages/core/dist}/permissions/permission-schema.test.d.ts.map +0 -0
  540. /package/{dist → packages/core/dist}/permissions/permission-schema.test.js +0 -0
  541. /package/{dist → packages/core/dist}/permissions/permission-schema.test.js.map +0 -0
  542. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.d.ts +0 -0
  543. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.d.ts.map +0 -0
  544. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.js +0 -0
  545. /package/{dist → packages/core/dist}/scripts/add-ocean-profiles.js.map +0 -0
  546. /package/{dist → packages/core/dist}/scripts/benchmark-integration.d.ts +0 -0
  547. /package/{dist → packages/core/dist}/scripts/benchmark-integration.d.ts.map +0 -0
  548. /package/{dist → packages/core/dist}/scripts/benchmark-integration.js +0 -0
  549. /package/{dist → packages/core/dist}/scripts/benchmark-integration.js.map +0 -0
  550. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.d.ts +0 -0
  551. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.d.ts.map +0 -0
  552. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.js +0 -0
  553. /package/{dist → packages/core/dist}/scripts/benchmark-integration.test.js.map +0 -0
  554. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.d.ts +0 -0
  555. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.d.ts.map +0 -0
  556. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.js +0 -0
  557. /package/{dist → packages/core/dist}/scripts/debugging-scenarios.test.js.map +0 -0
  558. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.d.ts +0 -0
  559. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.d.ts.map +0 -0
  560. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.js +0 -0
  561. /package/{dist → packages/core/dist}/scripts/generate-all-spiders.js.map +0 -0
  562. /package/{dist → packages/core/dist}/scripts/generate-report.d.ts +0 -0
  563. /package/{dist → packages/core/dist}/scripts/generate-report.test.d.ts +0 -0
  564. /package/{dist → packages/core/dist}/scripts/generate-report.test.d.ts.map +0 -0
  565. /package/{dist → packages/core/dist}/scripts/generate-report.test.js +0 -0
  566. /package/{dist → packages/core/dist}/scripts/generate-report.test.js.map +0 -0
  567. /package/{dist → packages/core/dist}/scripts/generate-spider-report.d.ts +0 -0
  568. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.d.ts +0 -0
  569. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.d.ts.map +0 -0
  570. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.js +0 -0
  571. /package/{dist → packages/core/dist}/scripts/generate-spider-report.test.js.map +0 -0
  572. /package/{dist → packages/core/dist}/scripts/generate-spider.d.ts +0 -0
  573. /package/{dist → packages/core/dist}/scripts/generate-spider.test.d.ts +0 -0
  574. /package/{dist → packages/core/dist}/scripts/generate-spider.test.d.ts.map +0 -0
  575. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.d.ts +0 -0
  576. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.d.ts.map +0 -0
  577. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.js +0 -0
  578. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.js.map +0 -0
  579. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.d.ts +0 -0
  580. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.d.ts.map +0 -0
  581. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.js +0 -0
  582. /package/{dist → packages/core/dist}/scripts/job-fair-aggregator.test.js.map +0 -0
  583. /package/{dist → packages/core/dist}/scripts/run-ci.test.d.ts +0 -0
  584. /package/{dist → packages/core/dist}/scripts/run-ci.test.d.ts.map +0 -0
  585. /package/{dist → packages/core/dist}/scripts/run-ci.test.js +0 -0
  586. /package/{dist → packages/core/dist}/scripts/run-ci.test.js.map +0 -0
  587. /package/{dist → packages/core/dist}/scripts/theme-detail.test.d.ts +0 -0
  588. /package/{dist → packages/core/dist}/scripts/theme-detail.test.d.ts.map +0 -0
  589. /package/{dist → packages/core/dist}/scripts/theme-detail.test.js +0 -0
  590. /package/{dist → packages/core/dist}/scripts/theme-detail.test.js.map +0 -0
  591. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.d.ts +0 -0
  592. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.d.ts.map +0 -0
  593. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.js +0 -0
  594. /package/{dist → packages/core/dist}/scripts/validate-ocean-profiles.js.map +0 -0
  595. /package/{dist → packages/core/dist}/workflow/complete-step-integration.test.d.ts +0 -0
  596. /package/{dist → packages/core/dist}/workflow/complete-step-integration.test.d.ts.map +0 -0
  597. /package/{dist → packages/core/dist}/workflow/complete-step-integration.test.js +0 -0
  598. /package/{dist → packages/core/dist}/workflow/complete-step-integration.test.js.map +0 -0
  599. /package/{dist → packages/core/dist}/workflow/gate-handler.d.ts +0 -0
  600. /package/{dist → packages/core/dist}/workflow/gate-handler.d.ts.map +0 -0
  601. /package/{dist → packages/core/dist}/workflow/gate-handler.js +0 -0
  602. /package/{dist → packages/core/dist}/workflow/gate-handler.js.map +0 -0
  603. /package/{dist → packages/core/dist}/workflow/gate-handler.test.d.ts +0 -0
  604. /package/{dist → packages/core/dist}/workflow/gate-handler.test.d.ts.map +0 -0
  605. /package/{dist → packages/core/dist}/workflow/gate-handler.test.js +0 -0
  606. /package/{dist → packages/core/dist}/workflow/gate-handler.test.js.map +0 -0
  607. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.d.ts +0 -0
  608. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.d.ts.map +0 -0
  609. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.js +0 -0
  610. /package/{dist → packages/core/dist}/workflow/generic-sm-finish.js.map +0 -0
  611. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.d.ts +0 -0
  612. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.d.ts.map +0 -0
  613. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.js +0 -0
  614. /package/{dist → packages/core/dist}/workflow/generic-sm-setup.js.map +0 -0
  615. /package/{dist → packages/core/dist}/workflow/handoff.d.ts +0 -0
  616. /package/{dist → packages/core/dist}/workflow/handoff.d.ts.map +0 -0
  617. /package/{dist → packages/core/dist}/workflow/handoff.js +0 -0
  618. /package/{dist → packages/core/dist}/workflow/handoff.js.map +0 -0
  619. /package/{dist → packages/core/dist}/workflow/handoff.test.d.ts +0 -0
  620. /package/{dist → packages/core/dist}/workflow/handoff.test.d.ts.map +0 -0
  621. /package/{dist → packages/core/dist}/workflow/handoff.test.js +0 -0
  622. /package/{dist → packages/core/dist}/workflow/handoff.test.js.map +0 -0
  623. /package/{dist → packages/core/dist}/workflow/index.d.ts +0 -0
  624. /package/{dist → packages/core/dist}/workflow/index.d.ts.map +0 -0
  625. /package/{dist → packages/core/dist}/workflow/index.js +0 -0
  626. /package/{dist → packages/core/dist}/workflow/index.js.map +0 -0
  627. /package/{dist → packages/core/dist}/workflow/session-state.d.ts +0 -0
  628. /package/{dist → packages/core/dist}/workflow/session-state.d.ts.map +0 -0
  629. /package/{dist → packages/core/dist}/workflow/session-state.js +0 -0
  630. /package/{dist → packages/core/dist}/workflow/session-state.js.map +0 -0
  631. /package/{dist → packages/core/dist}/workflow/session-state.test.d.ts +0 -0
  632. /package/{dist → packages/core/dist}/workflow/session-state.test.d.ts.map +0 -0
  633. /package/{dist → packages/core/dist}/workflow/session-state.test.js +0 -0
  634. /package/{dist → packages/core/dist}/workflow/session-state.test.js.map +0 -0
  635. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.d.ts +0 -0
  636. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.d.ts.map +0 -0
  637. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.js +0 -0
  638. /package/{dist → packages/core/dist}/workflow/sm-subagents.test.js.map +0 -0
  639. /package/{dist → packages/core/dist}/workflow/step-parser.d.ts +0 -0
  640. /package/{dist → packages/core/dist}/workflow/step-parser.d.ts.map +0 -0
  641. /package/{dist → packages/core/dist}/workflow/step-parser.js +0 -0
  642. /package/{dist → packages/core/dist}/workflow/step-parser.js.map +0 -0
  643. /package/{dist → packages/core/dist}/workflow/step-parser.test.d.ts +0 -0
  644. /package/{dist → packages/core/dist}/workflow/step-parser.test.d.ts.map +0 -0
  645. /package/{dist → packages/core/dist}/workflow/step-parser.test.js +0 -0
  646. /package/{dist → packages/core/dist}/workflow/step-parser.test.js.map +0 -0
  647. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.d.ts +0 -0
  648. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.d.ts.map +0 -0
  649. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.js +0 -0
  650. /package/{dist → packages/core/dist}/workflow/story-workflow-routing.test.js.map +0 -0
  651. /package/{dist → packages/core/dist}/workflow/test-cache.d.ts +0 -0
  652. /package/{dist → packages/core/dist}/workflow/test-cache.d.ts.map +0 -0
  653. /package/{dist → packages/core/dist}/workflow/test-cache.js +0 -0
  654. /package/{dist → packages/core/dist}/workflow/test-cache.js.map +0 -0
  655. /package/{dist → packages/core/dist}/workflow/test-cache.test.d.ts +0 -0
  656. /package/{dist → packages/core/dist}/workflow/test-cache.test.d.ts.map +0 -0
  657. /package/{dist → packages/core/dist}/workflow/test-cache.test.js +0 -0
  658. /package/{dist → packages/core/dist}/workflow/test-cache.test.js.map +0 -0
  659. /package/{dist → packages/core/dist}/workflow/trimodal.d.ts +0 -0
  660. /package/{dist → packages/core/dist}/workflow/trimodal.d.ts.map +0 -0
  661. /package/{dist → packages/core/dist}/workflow/trimodal.js +0 -0
  662. /package/{dist → packages/core/dist}/workflow/trimodal.js.map +0 -0
  663. /package/{dist → packages/core/dist}/workflow/trimodal.test.d.ts +0 -0
  664. /package/{dist → packages/core/dist}/workflow/trimodal.test.d.ts.map +0 -0
  665. /package/{dist → packages/core/dist}/workflow/trimodal.test.js +0 -0
  666. /package/{dist → packages/core/dist}/workflow/trimodal.test.js.map +0 -0
  667. /package/{dist → packages/core/dist}/workflow/variable-resolver.d.ts +0 -0
  668. /package/{dist → packages/core/dist}/workflow/variable-resolver.d.ts.map +0 -0
  669. /package/{dist → packages/core/dist}/workflow/variable-resolver.js +0 -0
  670. /package/{dist → packages/core/dist}/workflow/variable-resolver.js.map +0 -0
  671. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.d.ts +0 -0
  672. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.d.ts.map +0 -0
  673. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.js +0 -0
  674. /package/{dist → packages/core/dist}/workflow/variable-resolver.test.js.map +0 -0
  675. /package/{dist → packages/core/dist}/workflow/workflow-executor.d.ts +0 -0
  676. /package/{dist → packages/core/dist}/workflow/workflow-executor.d.ts.map +0 -0
  677. /package/{dist → packages/core/dist}/workflow/workflow-executor.js +0 -0
  678. /package/{dist → packages/core/dist}/workflow/workflow-executor.js.map +0 -0
  679. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.d.ts +0 -0
  680. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.d.ts.map +0 -0
  681. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.js +0 -0
  682. /package/{dist → packages/core/dist}/workflow/workflow-executor.test.js.map +0 -0
  683. /package/{dist → packages/core/dist}/workflow/workflow-loader.d.ts +0 -0
  684. /package/{dist → packages/core/dist}/workflow/workflow-loader.d.ts.map +0 -0
  685. /package/{dist → packages/core/dist}/workflow/workflow-loader.js +0 -0
  686. /package/{dist → packages/core/dist}/workflow/workflow-loader.js.map +0 -0
  687. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.d.ts +0 -0
  688. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.d.ts.map +0 -0
  689. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.js +0 -0
  690. /package/{dist → packages/core/dist}/workflow/workflow-loader.test.js.map +0 -0
  691. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.d.ts +0 -0
  692. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.d.ts.map +0 -0
  693. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.js +0 -0
  694. /package/{dist → packages/core/dist}/workflow/workflow-migration.test.js.map +0 -0
  695. /package/{dist → packages/core/dist}/workflow/workflow-permissions.d.ts +0 -0
  696. /package/{dist → packages/core/dist}/workflow/workflow-permissions.d.ts.map +0 -0
  697. /package/{dist → packages/core/dist}/workflow/workflow-permissions.js +0 -0
  698. /package/{dist → packages/core/dist}/workflow/workflow-permissions.js.map +0 -0
  699. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.d.ts +0 -0
  700. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.d.ts.map +0 -0
  701. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.js +0 -0
  702. /package/{dist → packages/core/dist}/workflow/workflow-permissions.test.js.map +0 -0
  703. /package/{dist → packages/core/dist}/workflow/workflow-router.d.ts +0 -0
  704. /package/{dist → packages/core/dist}/workflow/workflow-router.d.ts.map +0 -0
  705. /package/{dist → packages/core/dist}/workflow/workflow-router.js +0 -0
  706. /package/{dist → packages/core/dist}/workflow/workflow-router.js.map +0 -0
  707. /package/{dist → packages/core/dist}/workflow/workflow-router.test.d.ts +0 -0
  708. /package/{dist → packages/core/dist}/workflow/workflow-router.test.d.ts.map +0 -0
  709. /package/{dist → packages/core/dist}/workflow/workflow-router.test.js +0 -0
  710. /package/{dist → packages/core/dist}/workflow/workflow-router.test.js.map +0 -0
  711. /package/{dist → packages/core/dist}/workflow/workflow-schema.d.ts +0 -0
  712. /package/{dist → packages/core/dist}/workflow/workflow-schema.d.ts.map +0 -0
  713. /package/{dist → packages/core/dist}/workflow/workflow-schema.js +0 -0
  714. /package/{dist → packages/core/dist}/workflow/workflow-schema.js.map +0 -0
  715. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.d.ts +0 -0
  716. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.d.ts.map +0 -0
  717. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.js +0 -0
  718. /package/{dist → packages/core/dist}/workflow/workflow-schema.test.js.map +0 -0
  719. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.d.ts +0 -0
  720. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.d.ts.map +0 -0
  721. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.js +0 -0
  722. /package/{dist → packages/core/dist}/workflow/workflow-stepped-schema.test.js.map +0 -0
@@ -0,0 +1,731 @@
1
+ """Tests for sprint/validator.py module.
2
+
3
+ Story: MSSCI-12394 - Sprint and Story YAML validators
4
+
5
+ TDD RED phase: All tests should FAIL until implementation.
6
+
7
+ Acceptance Criteria:
8
+ 1. Sprint YAML validates structure and required fields
9
+ 2. Story entries validate all required fields
10
+ 3. Epic entries validate story references
11
+ 4. Clear error messages for validation failures
12
+ 5. Can validate archived sprints
13
+ 6. just validate-sprint recipe works
14
+ """
15
+
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ import pytest
20
+
21
+ from pennyfarthing_scripts.sprint.validator import (
22
+ ValidationError,
23
+ ValidationResult,
24
+ ValidationSeverity,
25
+ format_validation_errors,
26
+ validate_archived_sprint,
27
+ validate_epic,
28
+ validate_full_sprint,
29
+ validate_sprint,
30
+ validate_sprint_file,
31
+ validate_story,
32
+ )
33
+
34
+
35
+ # =============================================================================
36
+ # Test Fixtures - Valid Data
37
+ # =============================================================================
38
+
39
+
40
+ @pytest.fixture
41
+ def valid_sprint_data() -> dict[str, Any]:
42
+ """A minimal valid sprint structure."""
43
+ return {
44
+ "sprint": {
45
+ "number": 12,
46
+ "jira_sprint_id": 276,
47
+ "jira_sprint_name": "TO Sprint 2604",
48
+ "goal": "Complete the sprint",
49
+ "start_date": "2026-01-20",
50
+ "end_date": "2026-02-02",
51
+ "status": "active",
52
+ },
53
+ "epics": [],
54
+ }
55
+
56
+
57
+ @pytest.fixture
58
+ def valid_story() -> dict[str, Any]:
59
+ """A minimal valid story."""
60
+ return {
61
+ "id": "63-1",
62
+ "title": "Test Story",
63
+ "status": "backlog",
64
+ "points": 3,
65
+ }
66
+
67
+
68
+ @pytest.fixture
69
+ def valid_epic(valid_story: dict[str, Any]) -> dict[str, Any]:
70
+ """A minimal valid epic with one story."""
71
+ return {
72
+ "id": "epic-63",
73
+ "title": "Test Epic",
74
+ "stories": [valid_story],
75
+ }
76
+
77
+
78
+ @pytest.fixture
79
+ def full_valid_sprint(valid_epic: dict[str, Any]) -> dict[str, Any]:
80
+ """A complete valid sprint with epic and story."""
81
+ return {
82
+ "sprint": {
83
+ "number": 12,
84
+ "jira_sprint_id": 276,
85
+ "jira_sprint_name": "TO Sprint 2604",
86
+ "goal": "Complete the sprint",
87
+ "start_date": "2026-01-20",
88
+ "end_date": "2026-02-02",
89
+ "status": "active",
90
+ },
91
+ "epics": [valid_epic],
92
+ }
93
+
94
+
95
+ # =============================================================================
96
+ # AC1: Sprint YAML validates structure and required fields
97
+ # =============================================================================
98
+
99
+
100
+ class TestSprintValidation:
101
+ """Tests for sprint-level validation."""
102
+
103
+ def test_valid_sprint_passes(self, valid_sprint_data: dict[str, Any]) -> None:
104
+ """A valid sprint structure should pass validation."""
105
+ result = validate_sprint(valid_sprint_data)
106
+
107
+ assert result.valid is True
108
+ assert len(result.errors) == 0
109
+
110
+ def test_missing_sprint_section_fails(self) -> None:
111
+ """Missing 'sprint' section should fail."""
112
+ data = {"epics": []}
113
+
114
+ result = validate_sprint(data)
115
+
116
+ assert result.valid is False
117
+ assert any("sprint" in e.message.lower() for e in result.errors)
118
+
119
+ def test_missing_required_sprint_fields_fails(self) -> None:
120
+ """Missing required sprint fields should produce errors."""
121
+ data = {
122
+ "sprint": {
123
+ "number": 12,
124
+ # Missing: jira_sprint_id, goal, start_date, end_date, status
125
+ },
126
+ "epics": [],
127
+ }
128
+
129
+ result = validate_sprint(data)
130
+
131
+ assert result.valid is False
132
+ # Should report specific missing fields
133
+ error_messages = " ".join(e.message for e in result.errors)
134
+ assert "goal" in error_messages.lower() or len(result.errors) > 0
135
+
136
+ def test_invalid_sprint_status_fails(self) -> None:
137
+ """Invalid sprint status value should fail."""
138
+ data = {
139
+ "sprint": {
140
+ "number": 12,
141
+ "jira_sprint_id": 276,
142
+ "goal": "Test",
143
+ "start_date": "2026-01-20",
144
+ "end_date": "2026-02-02",
145
+ "status": "invalid_status", # Should be 'active' or 'closed'
146
+ },
147
+ "epics": [],
148
+ }
149
+
150
+ result = validate_sprint(data)
151
+
152
+ assert result.valid is False
153
+ assert any("status" in e.message.lower() for e in result.errors)
154
+
155
+ def test_invalid_date_format_fails(self) -> None:
156
+ """Non-ISO date format should fail."""
157
+ data = {
158
+ "sprint": {
159
+ "number": 12,
160
+ "jira_sprint_id": 276,
161
+ "goal": "Test",
162
+ "start_date": "01/20/2026", # Wrong format
163
+ "end_date": "2026-02-02",
164
+ "status": "active",
165
+ },
166
+ "epics": [],
167
+ }
168
+
169
+ result = validate_sprint(data)
170
+
171
+ assert result.valid is False
172
+ assert any("date" in e.message.lower() for e in result.errors)
173
+
174
+
175
+ # =============================================================================
176
+ # AC2: Story entries validate all required fields
177
+ # =============================================================================
178
+
179
+
180
+ class TestStoryValidation:
181
+ """Tests for story-level validation."""
182
+
183
+ def test_valid_story_passes(self, valid_story: dict[str, Any]) -> None:
184
+ """A valid story should pass validation."""
185
+ result = validate_story(valid_story, "epic-63")
186
+
187
+ assert result.valid is True
188
+ assert len(result.errors) == 0
189
+
190
+ def test_missing_story_id_fails(self) -> None:
191
+ """Story without id should fail."""
192
+ story = {"title": "Test", "status": "backlog", "points": 3}
193
+
194
+ result = validate_story(story, "epic-63")
195
+
196
+ assert result.valid is False
197
+ assert any("id" in e.message.lower() for e in result.errors)
198
+
199
+ def test_missing_story_title_fails(self) -> None:
200
+ """Story without title should fail."""
201
+ story = {"id": "63-1", "status": "backlog", "points": 3}
202
+
203
+ result = validate_story(story, "epic-63")
204
+
205
+ assert result.valid is False
206
+ assert any("title" in e.message.lower() for e in result.errors)
207
+
208
+ def test_missing_story_status_fails(self) -> None:
209
+ """Story without status should fail."""
210
+ story = {"id": "63-1", "title": "Test", "points": 3}
211
+
212
+ result = validate_story(story, "epic-63")
213
+
214
+ assert result.valid is False
215
+ assert any("status" in e.message.lower() for e in result.errors)
216
+
217
+ def test_missing_story_points_fails(self) -> None:
218
+ """Story without points should fail."""
219
+ story = {"id": "63-1", "title": "Test", "status": "backlog"}
220
+
221
+ result = validate_story(story, "epic-63")
222
+
223
+ assert result.valid is False
224
+ assert any("points" in e.message.lower() for e in result.errors)
225
+
226
+ def test_invalid_story_status_fails(self) -> None:
227
+ """Story with invalid status should fail."""
228
+ story = {
229
+ "id": "63-1",
230
+ "title": "Test",
231
+ "status": "invalid", # Should be backlog/in_progress/done/cancelled
232
+ "points": 3,
233
+ }
234
+
235
+ result = validate_story(story, "epic-63")
236
+
237
+ assert result.valid is False
238
+ assert any("status" in e.message.lower() for e in result.errors)
239
+
240
+ def test_all_valid_story_statuses_pass(self) -> None:
241
+ """All valid status values should pass."""
242
+ valid_statuses = ["backlog", "in_progress", "done", "cancelled"]
243
+
244
+ for status in valid_statuses:
245
+ story = {"id": "63-1", "title": "Test", "status": status, "points": 3}
246
+ result = validate_story(story, "epic-63")
247
+ assert result.valid is True, f"Status '{status}' should be valid"
248
+
249
+ def test_non_numeric_points_fails(self) -> None:
250
+ """Story with non-numeric points should fail."""
251
+ story = {
252
+ "id": "63-1",
253
+ "title": "Test",
254
+ "status": "backlog",
255
+ "points": "three", # Should be numeric
256
+ }
257
+
258
+ result = validate_story(story, "epic-63")
259
+
260
+ assert result.valid is False
261
+ assert any("points" in e.message.lower() for e in result.errors)
262
+
263
+ def test_invalid_jira_key_fails(self) -> None:
264
+ """Story with invalid Jira key format should fail."""
265
+ story = {
266
+ "id": "63-1",
267
+ "title": "Test",
268
+ "status": "backlog",
269
+ "points": 3,
270
+ "jira": "INVALID-KEY", # Should match MSSCI-NNNNN
271
+ }
272
+
273
+ result = validate_story(story, "epic-63")
274
+
275
+ assert result.valid is False
276
+ assert any("jira" in e.message.lower() for e in result.errors)
277
+
278
+ def test_valid_jira_key_passes(self) -> None:
279
+ """Story with valid Jira key should pass."""
280
+ story = {
281
+ "id": "63-1",
282
+ "title": "Test",
283
+ "status": "backlog",
284
+ "points": 3,
285
+ "jira": "MSSCI-12345",
286
+ }
287
+
288
+ result = validate_story(story, "epic-63")
289
+
290
+ assert result.valid is True
291
+
292
+ def test_error_path_includes_epic_context(self) -> None:
293
+ """Error path should include epic ID for context."""
294
+ story = {"id": "63-1", "title": "Test", "points": 3} # Missing status
295
+
296
+ result = validate_story(story, "epic-63")
297
+
298
+ assert result.valid is False
299
+ assert any("epic-63" in e.path for e in result.errors)
300
+
301
+
302
+ # =============================================================================
303
+ # AC3: Epic entries validate story references
304
+ # =============================================================================
305
+
306
+
307
+ class TestEpicValidation:
308
+ """Tests for epic-level validation."""
309
+
310
+ def test_valid_epic_passes(self, valid_epic: dict[str, Any]) -> None:
311
+ """A valid epic should pass validation."""
312
+ result = validate_epic(valid_epic, set())
313
+
314
+ assert result.valid is True
315
+ assert len(result.errors) == 0
316
+
317
+ def test_missing_epic_id_fails(self, valid_story: dict[str, Any]) -> None:
318
+ """Epic without id should fail."""
319
+ epic = {"title": "Test Epic", "stories": [valid_story]}
320
+
321
+ result = validate_epic(epic, set())
322
+
323
+ assert result.valid is False
324
+ assert any("id" in e.message.lower() for e in result.errors)
325
+
326
+ def test_missing_epic_title_fails(self, valid_story: dict[str, Any]) -> None:
327
+ """Epic without title should fail."""
328
+ epic = {"id": "epic-63", "stories": [valid_story]}
329
+
330
+ result = validate_epic(epic, set())
331
+
332
+ assert result.valid is False
333
+ assert any("title" in e.message.lower() for e in result.errors)
334
+
335
+ def test_duplicate_story_ids_in_epic_fails(self) -> None:
336
+ """Duplicate story IDs within an epic should fail."""
337
+ epic = {
338
+ "id": "epic-63",
339
+ "title": "Test Epic",
340
+ "stories": [
341
+ {"id": "63-1", "title": "Story 1", "status": "backlog", "points": 3},
342
+ {"id": "63-1", "title": "Story 2", "status": "done", "points": 2}, # Duplicate!
343
+ ],
344
+ }
345
+
346
+ result = validate_epic(epic, set())
347
+
348
+ assert result.valid is False
349
+ assert any("duplicate" in e.message.lower() for e in result.errors)
350
+
351
+ def test_duplicate_story_ids_across_epics_fails(self, valid_story: dict[str, Any]) -> None:
352
+ """Story ID already used in another epic should fail."""
353
+ epic = {
354
+ "id": "epic-63",
355
+ "title": "Test Epic",
356
+ "stories": [valid_story], # Has id "63-1"
357
+ }
358
+
359
+ # Simulate "63-1" already existing in another epic
360
+ existing_ids = {"63-1"}
361
+
362
+ result = validate_epic(epic, existing_ids)
363
+
364
+ assert result.valid is False
365
+ assert any("duplicate" in e.message.lower() for e in result.errors)
366
+
367
+
368
+ # =============================================================================
369
+ # AC4: Clear error messages for validation failures
370
+ # =============================================================================
371
+
372
+
373
+ class TestErrorMessages:
374
+ """Tests for error message clarity."""
375
+
376
+ def test_error_message_specifies_field_name(self) -> None:
377
+ """Error message should specify which field is invalid."""
378
+ story = {"id": "63-1", "title": "Test", "points": 3} # Missing status
379
+
380
+ result = validate_story(story, "epic-63")
381
+
382
+ assert result.valid is False
383
+ # Error should mention "status" specifically
384
+ assert any("status" in e.message.lower() for e in result.errors)
385
+
386
+ def test_error_message_specifies_expected_values(self) -> None:
387
+ """Error for invalid enum should list valid values."""
388
+ story = {
389
+ "id": "63-1",
390
+ "title": "Test",
391
+ "status": "invalid",
392
+ "points": 3,
393
+ }
394
+
395
+ result = validate_story(story, "epic-63")
396
+
397
+ assert result.valid is False
398
+ # Error should list valid status values
399
+ error_text = " ".join(e.message for e in result.errors).lower()
400
+ assert "backlog" in error_text or "in_progress" in error_text
401
+
402
+ def test_error_path_is_json_path_format(self) -> None:
403
+ """Error path should be JSON-path format for navigation."""
404
+ story = {"id": "63-1", "title": "Test", "points": 3} # Missing status
405
+
406
+ result = validate_story(story, "epic-63")
407
+
408
+ assert result.valid is False
409
+ # Path should include epic and story context
410
+ error = result.errors[0]
411
+ assert "." in error.path or "[" in error.path # JSON path-like
412
+
413
+ def test_format_validation_errors_is_readable(self) -> None:
414
+ """format_validation_errors should produce human-readable output."""
415
+ result = ValidationResult(valid=False)
416
+ result.add_error("Missing required field: status", "epics[0].stories[0]")
417
+ result.add_error("Invalid points value", "epics[0].stories[1].points")
418
+
419
+ output = format_validation_errors(result)
420
+
421
+ # Should be multi-line, human readable
422
+ assert "\n" in output or len(output) > 20
423
+ assert "status" in output
424
+ assert "points" in output
425
+
426
+ def test_format_shows_severity(self) -> None:
427
+ """Formatted output should show error severity."""
428
+ result = ValidationResult(valid=False)
429
+ result.add_error("Critical error", "sprint.status", ValidationSeverity.ERROR)
430
+ result.add_error("Minor warning", "epics[0].title", ValidationSeverity.WARNING)
431
+
432
+ output = format_validation_errors(result)
433
+
434
+ # Should distinguish errors from warnings
435
+ assert "error" in output.lower() or "warning" in output.lower()
436
+
437
+
438
+ # =============================================================================
439
+ # AC5: Can validate archived sprints
440
+ # =============================================================================
441
+
442
+
443
+ class TestArchivedSprintValidation:
444
+ """Tests for archived sprint validation."""
445
+
446
+ def test_valid_archived_sprint_passes(self) -> None:
447
+ """A valid archived sprint should pass validation."""
448
+ data = {
449
+ "sprint": {
450
+ "number": 11,
451
+ "jira_sprint_id": 275,
452
+ "goal": "Previous sprint",
453
+ "start_date": "2026-01-06",
454
+ "end_date": "2026-01-19",
455
+ "status": "closed", # Archived sprints should be closed
456
+ },
457
+ "epics": [
458
+ {
459
+ "id": "epic-50",
460
+ "title": "Old Epic",
461
+ "stories": [
462
+ {"id": "50-1", "title": "Done Story", "status": "done", "points": 3},
463
+ ],
464
+ }
465
+ ],
466
+ }
467
+
468
+ result = validate_archived_sprint(data)
469
+
470
+ assert result.valid is True
471
+
472
+ def test_archived_sprint_allows_all_done_stories(self) -> None:
473
+ """Archived sprints should allow all stories to be done/cancelled."""
474
+ data = {
475
+ "sprint": {
476
+ "number": 11,
477
+ "jira_sprint_id": 275,
478
+ "goal": "Previous sprint",
479
+ "start_date": "2026-01-06",
480
+ "end_date": "2026-01-19",
481
+ "status": "closed",
482
+ },
483
+ "epics": [
484
+ {
485
+ "id": "epic-50",
486
+ "title": "Old Epic",
487
+ "stories": [
488
+ {"id": "50-1", "title": "Done Story", "status": "done", "points": 3},
489
+ {"id": "50-2", "title": "Cancelled Story", "status": "cancelled", "points": 2},
490
+ ],
491
+ }
492
+ ],
493
+ }
494
+
495
+ result = validate_archived_sprint(data)
496
+
497
+ assert result.valid is True
498
+
499
+ def test_archived_sprint_validates_structure(self) -> None:
500
+ """Archived sprints should still validate structure (missing fields)."""
501
+ data = {
502
+ "sprint": {
503
+ "number": 11,
504
+ # Missing required fields
505
+ },
506
+ "epics": [],
507
+ }
508
+
509
+ result = validate_archived_sprint(data)
510
+
511
+ assert result.valid is False
512
+
513
+
514
+ # =============================================================================
515
+ # AC6: just validate-sprint recipe works
516
+ # =============================================================================
517
+
518
+
519
+ class TestValidateSprintFile:
520
+ """Tests for file-based validation."""
521
+
522
+ def test_validate_current_sprint_file(self) -> None:
523
+ """Should be able to validate current-sprint.yaml from disk."""
524
+ # Get project root
525
+ project_root = Path(__file__).parent.parent.parent
526
+ sprint_file = project_root / "sprint" / "current-sprint.yaml"
527
+
528
+ if sprint_file.exists():
529
+ result = validate_sprint_file(sprint_file)
530
+
531
+ # The actual sprint file should be valid (or we have bugs to fix!)
532
+ assert isinstance(result, ValidationResult)
533
+ # If it's invalid, print errors for debugging
534
+ if not result.valid:
535
+ print("\nValidation errors in current-sprint.yaml:")
536
+ for error in result.errors:
537
+ print(f" {error.path}: {error.message}")
538
+
539
+ def test_validate_nonexistent_file(self) -> None:
540
+ """Should return error for nonexistent file."""
541
+ result = validate_sprint_file(Path("/nonexistent/file.yaml"))
542
+
543
+ assert result.valid is False
544
+ assert any("not found" in e.message.lower() or "exist" in e.message.lower() for e in result.errors)
545
+
546
+ def test_validate_invalid_yaml_file(self, tmp_path: Path) -> None:
547
+ """Should return error for malformed YAML."""
548
+ bad_file = tmp_path / "bad.yaml"
549
+ bad_file.write_text("this: is: not: valid: yaml: [")
550
+
551
+ result = validate_sprint_file(bad_file)
552
+
553
+ assert result.valid is False
554
+ assert any("yaml" in e.message.lower() or "parse" in e.message.lower() for e in result.errors)
555
+
556
+ def test_single_quoted_strings_with_blank_lines_fail(self, tmp_path: Path) -> None:
557
+ """Single-quoted strings with blank lines break Node yaml parser (Cyclist panel)."""
558
+ bad_file = tmp_path / "bad-quotes.yaml"
559
+ bad_file.write_text(
560
+ "sprint:\n"
561
+ " number: 12\n"
562
+ " jira_sprint_id: 276\n"
563
+ " goal: Test\n"
564
+ " start_date: 2026-01-20\n"
565
+ " end_date: 2026-02-02\n"
566
+ " status: active\n"
567
+ "epics:\n"
568
+ " - id: epic-1\n"
569
+ " title: Test Epic\n"
570
+ " description: 'Line one\n"
571
+ "\n"
572
+ " Line after blank\n"
573
+ "\n"
574
+ "'\n"
575
+ " stories: []\n"
576
+ )
577
+
578
+ result = validate_sprint_file(bad_file)
579
+
580
+ assert result.valid is False
581
+ assert any("single-quoted" in e.message.lower() for e in result.errors)
582
+
583
+ def test_block_scalar_descriptions_pass(self, tmp_path: Path) -> None:
584
+ """Block scalar (|) descriptions should pass validation."""
585
+ good_file = tmp_path / "good-blocks.yaml"
586
+ good_file.write_text(
587
+ "sprint:\n"
588
+ " number: 12\n"
589
+ " jira_sprint_id: 276\n"
590
+ " goal: Test\n"
591
+ " start_date: 2026-01-20\n"
592
+ " end_date: 2026-02-02\n"
593
+ " status: active\n"
594
+ "epics:\n"
595
+ " - id: epic-1\n"
596
+ " title: Test Epic\n"
597
+ " description: |\n"
598
+ " Line one.\n"
599
+ "\n"
600
+ " Line after blank.\n"
601
+ " stories:\n"
602
+ " - id: MSSCI-10001\n"
603
+ " title: A story\n"
604
+ " status: backlog\n"
605
+ " points: 3\n"
606
+ )
607
+
608
+ result = validate_sprint_file(good_file)
609
+
610
+ assert result.valid is True
611
+
612
+
613
+ # =============================================================================
614
+ # Full Sprint Validation (integration)
615
+ # =============================================================================
616
+
617
+
618
+ class TestFullSprintValidation:
619
+ """Integration tests for complete sprint validation."""
620
+
621
+ def test_valid_full_sprint_passes(self, full_valid_sprint: dict[str, Any]) -> None:
622
+ """A complete valid sprint should pass all validations."""
623
+ result = validate_full_sprint(full_valid_sprint)
624
+
625
+ assert result.valid is True
626
+ assert len(result.errors) == 0
627
+
628
+ def test_errors_from_all_levels_collected(self) -> None:
629
+ """Validation should collect errors from sprint, epics, and stories."""
630
+ data = {
631
+ "sprint": {
632
+ "number": 12,
633
+ # Missing required fields
634
+ },
635
+ "epics": [
636
+ {
637
+ "id": "epic-63",
638
+ # Missing title
639
+ "stories": [
640
+ {
641
+ "id": "63-1",
642
+ "title": "Story",
643
+ # Missing status and points
644
+ }
645
+ ],
646
+ }
647
+ ],
648
+ }
649
+
650
+ result = validate_full_sprint(data)
651
+
652
+ assert result.valid is False
653
+ # Should have errors from multiple levels
654
+ assert len(result.errors) >= 3 # At least sprint, epic, and story errors
655
+
656
+ def test_multiple_epics_all_validated(self) -> None:
657
+ """All epics in a sprint should be validated."""
658
+ data = {
659
+ "sprint": {
660
+ "number": 12,
661
+ "jira_sprint_id": 276,
662
+ "goal": "Test",
663
+ "start_date": "2026-01-20",
664
+ "end_date": "2026-02-02",
665
+ "status": "active",
666
+ },
667
+ "epics": [
668
+ {
669
+ "id": "epic-63",
670
+ "title": "Epic 1",
671
+ "stories": [
672
+ {"id": "63-1", "title": "S1", "status": "backlog", "points": 3},
673
+ ],
674
+ },
675
+ {
676
+ # Missing id and title
677
+ "stories": [],
678
+ },
679
+ ],
680
+ }
681
+
682
+ result = validate_full_sprint(data)
683
+
684
+ assert result.valid is False
685
+ # Second epic should have errors
686
+ assert any("epic" in e.path.lower() and "1" in e.path for e in result.errors)
687
+
688
+
689
+ # =============================================================================
690
+ # ValidationResult behavior
691
+ # =============================================================================
692
+
693
+
694
+ class TestValidationResult:
695
+ """Tests for ValidationResult dataclass behavior."""
696
+
697
+ def test_starts_valid(self) -> None:
698
+ """New ValidationResult should be valid by default."""
699
+ result = ValidationResult(valid=True)
700
+
701
+ assert result.valid is True
702
+ assert len(result.errors) == 0
703
+
704
+ def test_add_error_makes_invalid(self) -> None:
705
+ """Adding an error should mark result as invalid."""
706
+ result = ValidationResult(valid=True)
707
+
708
+ result.add_error("Test error", "test.path")
709
+
710
+ assert result.valid is False
711
+ assert len(result.errors) == 1
712
+
713
+ def test_add_warning_keeps_valid(self) -> None:
714
+ """Adding a warning should NOT mark result as invalid."""
715
+ result = ValidationResult(valid=True)
716
+
717
+ result.add_error("Test warning", "test.path", ValidationSeverity.WARNING)
718
+
719
+ assert result.valid is True
720
+ assert len(result.errors) == 1
721
+
722
+ def test_multiple_errors_accumulate(self) -> None:
723
+ """Multiple errors should accumulate."""
724
+ result = ValidationResult(valid=True)
725
+
726
+ result.add_error("Error 1", "path.1")
727
+ result.add_error("Error 2", "path.2")
728
+ result.add_error("Error 3", "path.3")
729
+
730
+ assert result.valid is False
731
+ assert len(result.errors) == 3