@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
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Observation File Writer for Story 95-3
3
+ *
4
+ * Creates and appends to observation files used by tandem backseat agents.
5
+ * Files are append-only markdown at `.session/{storyId}-tandem-{agent}.md`.
6
+ * Each write is entry-atomic — the file remains valid markdown on crash.
7
+ */
8
+ import { writeFileSync, appendFileSync, readFileSync, mkdirSync, existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ // =============================================================================
11
+ // Implementations
12
+ // =============================================================================
13
+ /**
14
+ * Initialize an observation file with header metadata.
15
+ *
16
+ * Creates the file at `.session/{storyId}-tandem-{agent}.md` with
17
+ * a header containing observer agent, persona, phase, and start timestamp.
18
+ */
19
+ export function initObservationFile(config) {
20
+ try {
21
+ const { storyId, agent, persona, phase, sessionDir } = config;
22
+ const filePath = join(sessionDir, `${storyId}-tandem-${agent}.md`);
23
+ if (!existsSync(sessionDir)) {
24
+ mkdirSync(sessionDir, { recursive: true });
25
+ }
26
+ const now = new Date().toISOString();
27
+ const header = `# Tandem Observations: ${storyId}\n**Observer:** ${agent} (${persona})\n**Phase:** ${phase}\n**Started:** ${now}\n\n---\n`;
28
+ writeFileSync(filePath, header, 'utf-8');
29
+ return { success: true, data: { path: filePath } };
30
+ }
31
+ catch (err) {
32
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
33
+ }
34
+ }
35
+ /**
36
+ * Append an observation entry to an existing observation file.
37
+ *
38
+ * Each append is entry-atomic: the complete entry (separator, timestamp,
39
+ * trigger, observation text) is written in a single operation.
40
+ */
41
+ export function appendObservation(filePath, entry) {
42
+ try {
43
+ if (!existsSync(filePath)) {
44
+ return { success: false, error: `File not found: ${filePath}` };
45
+ }
46
+ const now = new Date();
47
+ const hh = String(now.getHours()).padStart(2, '0');
48
+ const mm = String(now.getMinutes()).padStart(2, '0');
49
+ const block = `\n## [${hh}:${mm}] Observation\n**Trigger:** ${entry.triggerType}: ${entry.triggerDetail}\n${entry.observation}\n\n---\n`;
50
+ appendFileSync(filePath, block, 'utf-8');
51
+ return { success: true };
52
+ }
53
+ catch (err) {
54
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
55
+ }
56
+ }
57
+ /**
58
+ * Parse an observation file and return its entries.
59
+ *
60
+ * Used by bell mode to read recent observations for injection.
61
+ */
62
+ export function parseObservationFile(filePath) {
63
+ try {
64
+ if (!existsSync(filePath)) {
65
+ return { success: false, error: `File not found: ${filePath}` };
66
+ }
67
+ const content = readFileSync(filePath, 'utf-8');
68
+ // Parse header
69
+ const storyMatch = content.match(/^# Tandem Observations: (.+)$/m);
70
+ const observerMatch = content.match(/^\*\*Observer:\*\* (.+)$/m);
71
+ const phaseMatch = content.match(/^\*\*Phase:\*\* (.+)$/m);
72
+ const startedMatch = content.match(/^\*\*Started:\*\* (.+)$/m);
73
+ const header = {
74
+ storyId: storyMatch?.[1] ?? '',
75
+ observer: observerMatch?.[1] ?? '',
76
+ phase: phaseMatch?.[1] ?? '',
77
+ started: startedMatch?.[1] ?? '',
78
+ };
79
+ // Parse entries — split on ## [HH:MM] Observation headings
80
+ const entries = [];
81
+ const entryPattern = /## \[(\d{2}:\d{2})\] Observation\n\*\*Trigger:\*\* ([^:]+): ([^\n]+)\n([\s\S]*?)(?=\n---)/g;
82
+ let match;
83
+ while ((match = entryPattern.exec(content)) !== null) {
84
+ entries.push({
85
+ timestamp: match[1],
86
+ triggerType: match[2],
87
+ triggerDetail: match[3],
88
+ observation: match[4].trim(),
89
+ });
90
+ }
91
+ return { success: true, data: { header, entries } };
92
+ }
93
+ catch (err) {
94
+ return { success: false, error: err instanceof Error ? err.message : String(err) };
95
+ }
96
+ }
97
+ //# sourceMappingURL=observation-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation-writer.js","sourceRoot":"","sources":["../../src/workflow/observation-writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tests for Story 95-3: Observation File Format and Writer
3
+ *
4
+ * RED state tests for tandem observation file creation and append.
5
+ * These tests cover all acceptance criteria:
6
+ *
7
+ * AC1: File created at .session/{storyId}-tandem-{agent}.md
8
+ * AC2: Header includes observer agent, persona, phase, start timestamp
9
+ * AC3: Entry format — timestamp (HH:MM), trigger type, trigger detail, observation
10
+ * AC4: Entries separated by --- horizontal rules
11
+ * AC5: Entry-atomic appends — each write is a complete markdown entry
12
+ * AC6: Valid markdown at all times, including after crash during write
13
+ * AC7: Functions return result objects ({success, error?}) instead of throwing
14
+ *
15
+ * Run with: npm test
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=observation-writer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation-writer.test.d.ts","sourceRoot":"","sources":["../../src/workflow/observation-writer.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Tests for Story 95-3: Observation File Format and Writer
3
+ *
4
+ * RED state tests for tandem observation file creation and append.
5
+ * These tests cover all acceptance criteria:
6
+ *
7
+ * AC1: File created at .session/{storyId}-tandem-{agent}.md
8
+ * AC2: Header includes observer agent, persona, phase, start timestamp
9
+ * AC3: Entry format — timestamp (HH:MM), trigger type, trigger detail, observation
10
+ * AC4: Entries separated by --- horizontal rules
11
+ * AC5: Entry-atomic appends — each write is a complete markdown entry
12
+ * AC6: Valid markdown at all times, including after crash during write
13
+ * AC7: Functions return result objects ({success, error?}) instead of throwing
14
+ *
15
+ * Run with: npm test
16
+ */
17
+ import { describe, it, beforeEach, afterEach } from 'node:test';
18
+ import assert from 'node:assert';
19
+ import { mkdirSync, rmSync, existsSync, readFileSync } from 'node:fs';
20
+ import { join, dirname } from 'node:path';
21
+ import { fileURLToPath } from 'node:url';
22
+ import { initObservationFile, appendObservation, parseObservationFile, } from './observation-writer.js';
23
+ // Get directory for test fixtures
24
+ const __dirname = dirname(fileURLToPath(import.meta.url));
25
+ const TEST_DIR = join(__dirname, '__test_observation_writer__');
26
+ const SESSION_DIR = join(TEST_DIR, '.session');
27
+ // =============================================================================
28
+ // Test Fixtures
29
+ // =============================================================================
30
+ const DEFAULT_CONFIG = {
31
+ storyId: '95-3',
32
+ agent: 'architect',
33
+ persona: 'Will Bailey',
34
+ phase: 'implement',
35
+ sessionDir: SESSION_DIR,
36
+ };
37
+ const SAMPLE_ENTRY = {
38
+ triggerType: 'file-watch',
39
+ triggerDetail: 'src/components/Header.tsx modified',
40
+ observation: 'The Header component now imports useTheme but doesn\'t destructure the return value consistently.',
41
+ };
42
+ const TOOL_WATCH_ENTRY = {
43
+ triggerType: 'tool-watch',
44
+ triggerDetail: 'Bash (npm test)',
45
+ observation: 'Test suite passed with 2 warnings about deprecated API usage.',
46
+ };
47
+ const CONTEXT_WATCH_ENTRY = {
48
+ triggerType: 'context-watch',
49
+ triggerDetail: 'conversation summary at turn 15',
50
+ observation: 'Developer is refactoring the auth module but hasn\'t updated the middleware tests yet.',
51
+ };
52
+ // =============================================================================
53
+ // Setup / Teardown
54
+ // =============================================================================
55
+ describe('95-3: Observation File Writer', () => {
56
+ beforeEach(() => {
57
+ if (existsSync(TEST_DIR)) {
58
+ rmSync(TEST_DIR, { recursive: true });
59
+ }
60
+ mkdirSync(SESSION_DIR, { recursive: true });
61
+ });
62
+ afterEach(() => {
63
+ if (existsSync(TEST_DIR)) {
64
+ rmSync(TEST_DIR, { recursive: true });
65
+ }
66
+ });
67
+ // ===========================================================================
68
+ // AC1: File created at .session/{storyId}-tandem-{agent}.md
69
+ // ===========================================================================
70
+ describe('AC1: File created at correct path', () => {
71
+ it('should create observation file at .session/{storyId}-tandem-{agent}.md', () => {
72
+ const result = initObservationFile(DEFAULT_CONFIG);
73
+ assert.strictEqual(result.success, true, 'initObservationFile should succeed');
74
+ const expectedPath = join(SESSION_DIR, '95-3-tandem-architect.md');
75
+ assert.strictEqual(result.data?.path, expectedPath, 'Should return correct file path');
76
+ assert.strictEqual(existsSync(expectedPath), true, 'File should exist on disk');
77
+ });
78
+ it('should use storyId and agent in filename', () => {
79
+ const config = {
80
+ ...DEFAULT_CONFIG,
81
+ storyId: '42-7',
82
+ agent: 'tea',
83
+ };
84
+ const result = initObservationFile(config);
85
+ assert.strictEqual(result.success, true);
86
+ const expectedPath = join(SESSION_DIR, '42-7-tandem-tea.md');
87
+ assert.strictEqual(result.data?.path, expectedPath);
88
+ assert.strictEqual(existsSync(expectedPath), true);
89
+ });
90
+ it('should create session directory if it does not exist', () => {
91
+ const nestedDir = join(TEST_DIR, 'nested', '.session');
92
+ const config = {
93
+ ...DEFAULT_CONFIG,
94
+ sessionDir: nestedDir,
95
+ };
96
+ const result = initObservationFile(config);
97
+ assert.strictEqual(result.success, true, 'Should succeed even if directory missing');
98
+ assert.strictEqual(existsSync(nestedDir), true, 'Directory should be created');
99
+ });
100
+ });
101
+ // ===========================================================================
102
+ // AC2: Header includes observer agent, persona, phase, start timestamp
103
+ // ===========================================================================
104
+ describe('AC2: File header includes metadata', () => {
105
+ it('should include story ID in header title', () => {
106
+ initObservationFile(DEFAULT_CONFIG);
107
+ const content = readFileSync(join(SESSION_DIR, '95-3-tandem-architect.md'), 'utf-8');
108
+ assert.ok(content.includes('# Tandem Observations: 95-3'), 'Header should contain story ID in title');
109
+ });
110
+ it('should include observer agent and persona', () => {
111
+ initObservationFile(DEFAULT_CONFIG);
112
+ const content = readFileSync(join(SESSION_DIR, '95-3-tandem-architect.md'), 'utf-8');
113
+ assert.ok(content.includes('**Observer:** architect (Will Bailey)'), 'Header should include agent and persona');
114
+ });
115
+ it('should include phase', () => {
116
+ initObservationFile(DEFAULT_CONFIG);
117
+ const content = readFileSync(join(SESSION_DIR, '95-3-tandem-architect.md'), 'utf-8');
118
+ assert.ok(content.includes('**Phase:** implement'), 'Header should include current phase');
119
+ });
120
+ it('should include ISO start timestamp', () => {
121
+ initObservationFile(DEFAULT_CONFIG);
122
+ const content = readFileSync(join(SESSION_DIR, '95-3-tandem-architect.md'), 'utf-8');
123
+ // Match ISO 8601 pattern: YYYY-MM-DDTHH:MM:SS
124
+ const isoPattern = /\*\*Started:\*\* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
125
+ assert.ok(isoPattern.test(content), `Header should include ISO timestamp, got:\n${content}`);
126
+ });
127
+ it('should end header with --- separator', () => {
128
+ initObservationFile(DEFAULT_CONFIG);
129
+ const content = readFileSync(join(SESSION_DIR, '95-3-tandem-architect.md'), 'utf-8');
130
+ // After the header block, there should be a --- separator
131
+ const lines = content.trim().split('\n');
132
+ const lastLine = lines[lines.length - 1].trim();
133
+ assert.strictEqual(lastLine, '---', 'Header should end with --- separator');
134
+ });
135
+ });
136
+ // ===========================================================================
137
+ // AC3: Entry format — HH:MM timestamp, trigger type, trigger detail, observation
138
+ // ===========================================================================
139
+ describe('AC3: Observation entry format', () => {
140
+ it('should include HH:MM timestamp in entry heading', () => {
141
+ initObservationFile(DEFAULT_CONFIG);
142
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
143
+ appendObservation(filePath, SAMPLE_ENTRY);
144
+ const content = readFileSync(filePath, 'utf-8');
145
+ // Match ## [HH:MM] Observation
146
+ const timePattern = /## \[\d{2}:\d{2}\] Observation/;
147
+ assert.ok(timePattern.test(content), `Entry should have [HH:MM] timestamp heading, got:\n${content}`);
148
+ });
149
+ it('should include trigger type and detail', () => {
150
+ initObservationFile(DEFAULT_CONFIG);
151
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
152
+ appendObservation(filePath, SAMPLE_ENTRY);
153
+ const content = readFileSync(filePath, 'utf-8');
154
+ assert.ok(content.includes('**Trigger:** file-watch: src/components/Header.tsx modified'), 'Entry should include trigger type and detail');
155
+ });
156
+ it('should include observation text', () => {
157
+ initObservationFile(DEFAULT_CONFIG);
158
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
159
+ appendObservation(filePath, SAMPLE_ENTRY);
160
+ const content = readFileSync(filePath, 'utf-8');
161
+ assert.ok(content.includes(SAMPLE_ENTRY.observation), 'Entry should include observation text');
162
+ });
163
+ it('should handle all trigger types: file-watch, tool-watch, context-watch', () => {
164
+ initObservationFile(DEFAULT_CONFIG);
165
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
166
+ appendObservation(filePath, SAMPLE_ENTRY);
167
+ appendObservation(filePath, TOOL_WATCH_ENTRY);
168
+ appendObservation(filePath, CONTEXT_WATCH_ENTRY);
169
+ const content = readFileSync(filePath, 'utf-8');
170
+ assert.ok(content.includes('file-watch:'), 'Should handle file-watch trigger');
171
+ assert.ok(content.includes('tool-watch:'), 'Should handle tool-watch trigger');
172
+ assert.ok(content.includes('context-watch:'), 'Should handle context-watch trigger');
173
+ });
174
+ });
175
+ // ===========================================================================
176
+ // AC4: Entries separated by --- horizontal rules
177
+ // ===========================================================================
178
+ describe('AC4: Entries separated by --- horizontal rules', () => {
179
+ it('should separate first entry from header with ---', () => {
180
+ initObservationFile(DEFAULT_CONFIG);
181
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
182
+ appendObservation(filePath, SAMPLE_ENTRY);
183
+ const content = readFileSync(filePath, 'utf-8');
184
+ // The header ends with ---. The entry should also end with ---.
185
+ // Count occurrences of ---
186
+ const separators = content.match(/^---$/gm) || [];
187
+ assert.ok(separators.length >= 2, `Should have at least 2 separators (header + entry), found ${separators.length}`);
188
+ });
189
+ it('should separate multiple entries with ---', () => {
190
+ initObservationFile(DEFAULT_CONFIG);
191
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
192
+ appendObservation(filePath, SAMPLE_ENTRY);
193
+ appendObservation(filePath, TOOL_WATCH_ENTRY);
194
+ appendObservation(filePath, CONTEXT_WATCH_ENTRY);
195
+ const content = readFileSync(filePath, 'utf-8');
196
+ // Header separator + 3 entry separators = at least 4
197
+ const separators = content.match(/^---$/gm) || [];
198
+ assert.ok(separators.length >= 4, `Should have at least 4 separators (1 header + 3 entries), found ${separators.length}`);
199
+ });
200
+ it('should end each entry with a trailing ---', () => {
201
+ initObservationFile(DEFAULT_CONFIG);
202
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
203
+ appendObservation(filePath, SAMPLE_ENTRY);
204
+ const content = readFileSync(filePath, 'utf-8');
205
+ const trimmed = content.trimEnd();
206
+ assert.ok(trimmed.endsWith('---'), 'File should end with --- after last entry');
207
+ });
208
+ });
209
+ // ===========================================================================
210
+ // AC5: Entry-atomic appends — each write is a complete markdown entry
211
+ // ===========================================================================
212
+ describe('AC5: Entry-atomic appends', () => {
213
+ it('should write complete entry in single append (not partial)', () => {
214
+ initObservationFile(DEFAULT_CONFIG);
215
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
216
+ appendObservation(filePath, SAMPLE_ENTRY);
217
+ const content = readFileSync(filePath, 'utf-8');
218
+ // A complete entry has: heading with timestamp, trigger line, observation text, trailing ---
219
+ const entryPattern = /## \[\d{2}:\d{2}\] Observation\n\*\*Trigger:\*\*.*\n.*\n+---/;
220
+ assert.ok(entryPattern.test(content), 'Entry should be written as a complete block');
221
+ });
222
+ it('should not leave partial entries on sequential appends', () => {
223
+ initObservationFile(DEFAULT_CONFIG);
224
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
225
+ // Append multiple entries rapidly
226
+ for (let i = 0; i < 5; i++) {
227
+ appendObservation(filePath, {
228
+ triggerType: 'file-watch',
229
+ triggerDetail: `file-${i}.ts modified`,
230
+ observation: `Observation number ${i}`,
231
+ });
232
+ }
233
+ const content = readFileSync(filePath, 'utf-8');
234
+ // Count entries by timestamp headings
235
+ const entries = content.match(/## \[\d{2}:\d{2}\] Observation/g) || [];
236
+ assert.strictEqual(entries.length, 5, 'Should have exactly 5 entries');
237
+ // Count trailing separators (each entry ends with ---)
238
+ const separators = content.match(/^---$/gm) || [];
239
+ // 1 header separator + 5 entry separators = 6
240
+ assert.ok(separators.length >= 6, `Should have at least 6 separators, found ${separators.length}`);
241
+ });
242
+ });
243
+ // ===========================================================================
244
+ // AC6: Valid markdown at all times
245
+ // ===========================================================================
246
+ describe('AC6: Valid markdown at all times', () => {
247
+ it('should produce valid markdown with header only (no entries)', () => {
248
+ initObservationFile(DEFAULT_CONFIG);
249
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
250
+ const content = readFileSync(filePath, 'utf-8');
251
+ // Should start with # heading
252
+ assert.ok(content.startsWith('#'), 'Should start with markdown heading');
253
+ // Should have bold metadata fields
254
+ assert.ok(content.includes('**Observer:**'), 'Should have bold Observer field');
255
+ assert.ok(content.includes('**Phase:**'), 'Should have bold Phase field');
256
+ assert.ok(content.includes('**Started:**'), 'Should have bold Started field');
257
+ });
258
+ it('should produce valid markdown after one entry', () => {
259
+ initObservationFile(DEFAULT_CONFIG);
260
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
261
+ appendObservation(filePath, SAMPLE_ENTRY);
262
+ const content = readFileSync(filePath, 'utf-8');
263
+ // Verify structure: heading, metadata, ---, entry, ---
264
+ assert.ok(content.includes('# Tandem Observations:'), 'Has top-level heading');
265
+ assert.ok(content.includes('## ['), 'Has entry heading');
266
+ // No unclosed markers or broken formatting
267
+ const openBold = (content.match(/\*\*/g) || []).length;
268
+ assert.strictEqual(openBold % 2, 0, 'All bold markers should be paired');
269
+ });
270
+ it('should produce valid markdown after many entries', () => {
271
+ initObservationFile(DEFAULT_CONFIG);
272
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
273
+ for (let i = 0; i < 10; i++) {
274
+ appendObservation(filePath, {
275
+ triggerType: 'file-watch',
276
+ triggerDetail: `file-${i}.ts modified`,
277
+ observation: `Change ${i}: Updated the component with new props.`,
278
+ });
279
+ }
280
+ const content = readFileSync(filePath, 'utf-8');
281
+ const entries = content.match(/## \[\d{2}:\d{2}\] Observation/g) || [];
282
+ assert.strictEqual(entries.length, 10, 'All 10 entries should be present');
283
+ // File ends cleanly
284
+ assert.ok(content.trimEnd().endsWith('---'), 'File should end with separator');
285
+ });
286
+ });
287
+ // ===========================================================================
288
+ // AC7: Result objects — {success, error?} instead of throwing
289
+ // ===========================================================================
290
+ describe('AC7: Result objects instead of throwing', () => {
291
+ it('should return {success: true, data: {path}} from initObservationFile', () => {
292
+ const result = initObservationFile(DEFAULT_CONFIG);
293
+ assert.ok('success' in result, 'Must have success field');
294
+ assert.strictEqual(typeof result.success, 'boolean');
295
+ assert.strictEqual(result.success, true);
296
+ assert.ok(result.data, 'Success result should have data');
297
+ assert.ok(typeof result.data.path === 'string', 'Data should have path string');
298
+ });
299
+ it('should return {success: true} from appendObservation', () => {
300
+ initObservationFile(DEFAULT_CONFIG);
301
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
302
+ const result = appendObservation(filePath, SAMPLE_ENTRY);
303
+ assert.ok('success' in result, 'Must have success field');
304
+ assert.strictEqual(result.success, true);
305
+ });
306
+ it('should return error result for invalid sessionDir (not throw)', () => {
307
+ const badConfig = {
308
+ ...DEFAULT_CONFIG,
309
+ sessionDir: '/nonexistent/deeply/nested/path/that/cannot/exist',
310
+ };
311
+ // Must NOT throw — return error result instead
312
+ const result = initObservationFile(badConfig);
313
+ assert.strictEqual(typeof result.success, 'boolean', 'Must return result object');
314
+ // Either succeeds (created dir) or fails gracefully
315
+ if (!result.success) {
316
+ assert.ok(result.error, 'Failed result should have error message');
317
+ }
318
+ });
319
+ it('should return error result when appending to nonexistent file (not throw)', () => {
320
+ const result = appendObservation('/nonexistent/file.md', SAMPLE_ENTRY);
321
+ assert.strictEqual(result.success, false, 'Should fail for nonexistent file');
322
+ assert.ok(result.error, 'Failed result should have error message');
323
+ });
324
+ it('should never throw from initObservationFile', () => {
325
+ // Even with completely bogus config
326
+ assert.doesNotThrow(() => initObservationFile({ storyId: '', agent: '', persona: '', phase: '', sessionDir: '' }), 'initObservationFile must not throw');
327
+ });
328
+ it('should never throw from appendObservation', () => {
329
+ assert.doesNotThrow(() => appendObservation('', { triggerType: '', triggerDetail: '', observation: '' }), 'appendObservation must not throw');
330
+ });
331
+ });
332
+ // ===========================================================================
333
+ // parseObservationFile — used by bell mode (95-7)
334
+ // ===========================================================================
335
+ describe('parseObservationFile: Read observations back', () => {
336
+ it('should parse header from observation file', () => {
337
+ initObservationFile(DEFAULT_CONFIG);
338
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
339
+ const result = parseObservationFile(filePath);
340
+ assert.strictEqual(result.success, true, 'Parsing should succeed');
341
+ assert.ok(result.data, 'Should return parsed data');
342
+ assert.strictEqual(result.data.header.storyId, '95-3');
343
+ assert.strictEqual(result.data.header.observer, 'architect (Will Bailey)');
344
+ assert.strictEqual(result.data.header.phase, 'implement');
345
+ assert.ok(result.data.header.started, 'Should have started timestamp');
346
+ });
347
+ it('should parse entries from observation file', () => {
348
+ initObservationFile(DEFAULT_CONFIG);
349
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
350
+ appendObservation(filePath, SAMPLE_ENTRY);
351
+ appendObservation(filePath, TOOL_WATCH_ENTRY);
352
+ const result = parseObservationFile(filePath);
353
+ assert.strictEqual(result.success, true);
354
+ assert.strictEqual(result.data?.entries.length, 2, 'Should find 2 entries');
355
+ assert.strictEqual(result.data?.entries[0].triggerType, 'file-watch');
356
+ assert.strictEqual(result.data?.entries[1].triggerType, 'tool-watch');
357
+ });
358
+ it('should return empty entries array for file with only header', () => {
359
+ initObservationFile(DEFAULT_CONFIG);
360
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
361
+ const result = parseObservationFile(filePath);
362
+ assert.strictEqual(result.success, true);
363
+ assert.strictEqual(result.data?.entries.length, 0, 'No entries in header-only file');
364
+ });
365
+ it('should return error result for nonexistent file (not throw)', () => {
366
+ const result = parseObservationFile('/nonexistent/file.md');
367
+ assert.strictEqual(result.success, false);
368
+ assert.ok(result.error, 'Should have error message');
369
+ });
370
+ });
371
+ // ===========================================================================
372
+ // Edge cases
373
+ // ===========================================================================
374
+ describe('Edge cases', () => {
375
+ it('should handle observation text with markdown special characters', () => {
376
+ initObservationFile(DEFAULT_CONFIG);
377
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
378
+ const tricky = {
379
+ triggerType: 'file-watch',
380
+ triggerDetail: 'README.md modified',
381
+ observation: 'The `code block` has **bold** and [links](http://example.com) and --- separators in text.',
382
+ };
383
+ const result = appendObservation(filePath, tricky);
384
+ assert.strictEqual(result.success, true, 'Should handle markdown chars in observation');
385
+ const content = readFileSync(filePath, 'utf-8');
386
+ assert.ok(content.includes(tricky.observation), 'Observation text should be preserved verbatim');
387
+ });
388
+ it('should handle empty observation text', () => {
389
+ initObservationFile(DEFAULT_CONFIG);
390
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
391
+ const result = appendObservation(filePath, {
392
+ triggerType: 'file-watch',
393
+ triggerDetail: 'test.ts modified',
394
+ observation: '',
395
+ });
396
+ assert.strictEqual(result.success, true, 'Empty observation should still succeed');
397
+ });
398
+ it('should handle multiline observation text', () => {
399
+ initObservationFile(DEFAULT_CONFIG);
400
+ const filePath = join(SESSION_DIR, '95-3-tandem-architect.md');
401
+ const multiline = {
402
+ triggerType: 'context-watch',
403
+ triggerDetail: 'conversation turn 20',
404
+ observation: 'First line of observation.\nSecond line with more detail.\nThird line wrapping up.',
405
+ };
406
+ const result = appendObservation(filePath, multiline);
407
+ assert.strictEqual(result.success, true);
408
+ const content = readFileSync(filePath, 'utf-8');
409
+ assert.ok(content.includes('First line of observation.'), 'Multiline should be preserved');
410
+ assert.ok(content.includes('Third line wrapping up.'), 'All lines should be present');
411
+ });
412
+ it('should not corrupt file if initObservationFile called twice', () => {
413
+ const result1 = initObservationFile(DEFAULT_CONFIG);
414
+ assert.strictEqual(result1.success, true);
415
+ // Append an entry
416
+ appendObservation(result1.data.path, SAMPLE_ENTRY);
417
+ // Init again — should not destroy existing content or should fail gracefully
418
+ const result2 = initObservationFile(DEFAULT_CONFIG);
419
+ // Either: returns error (file exists) or overwrites cleanly
420
+ assert.ok('success' in result2, 'Must return result object');
421
+ });
422
+ });
423
+ });
424
+ //# sourceMappingURL=observation-writer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation-writer.test.js","sourceRoot":"","sources":["../../src/workflow/observation-writer.test.ts"],"names":[],"mappings":"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"}
@@ -395,8 +395,10 @@ describe('AC4: Fallback to TDD workflow', () => {
395
395
  };
396
396
  const result = routeStoryToWorkflow(story, workflows);
397
397
  assert.ok(result, 'Should route to a workflow');
398
- // Feature type should match tdd workflow triggers
399
- assert.strictEqual(result.workflow.name, 'tdd');
398
+ // Feature type with 5 points matches both 2party-tdd and tdd triggers
399
+ // (types: [feature, enhancement], points.min: 3). 2party-tdd loads first
400
+ // alphabetically and has equal specificity, so it wins the type match.
401
+ assert.strictEqual(result.workflow.name, '2party-tdd');
400
402
  });
401
403
  it('should use points-based routing for low-point stories without tag', async () => {
402
404
  if (!workflowsDir) {