@pennyfarthing/core 11.0.0 → 11.1.1

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 (401) hide show
  1. package/README.md +81 -23
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts +20 -0
  4. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts.map +1 -0
  5. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js +278 -0
  6. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js.map +1 -0
  7. package/packages/core/dist/cli/utils/constants.d.ts +8 -2
  8. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  9. package/packages/core/dist/cli/utils/constants.js +4 -1
  10. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  11. package/packages/core/dist/cli/utils/constants.test.d.ts +10 -0
  12. package/packages/core/dist/cli/utils/constants.test.d.ts.map +1 -0
  13. package/packages/core/dist/cli/utils/constants.test.js +38 -0
  14. package/packages/core/dist/cli/utils/constants.test.js.map +1 -0
  15. package/packages/core/dist/consultation/consultation-protocol.d.ts +139 -0
  16. package/packages/core/dist/consultation/consultation-protocol.d.ts.map +1 -0
  17. package/packages/core/dist/consultation/consultation-protocol.js +178 -0
  18. package/packages/core/dist/consultation/consultation-protocol.js.map +1 -0
  19. package/packages/core/dist/consultation/consultation-protocol.test.d.ts +20 -0
  20. package/packages/core/dist/consultation/consultation-protocol.test.d.ts.map +1 -0
  21. package/packages/core/dist/consultation/consultation-protocol.test.js +474 -0
  22. package/packages/core/dist/consultation/consultation-protocol.test.js.map +1 -0
  23. package/packages/core/dist/consultation/dialogue-manager.d.ts +75 -0
  24. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -0
  25. package/packages/core/dist/consultation/dialogue-manager.js +334 -0
  26. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -0
  27. package/packages/core/dist/consultation/dialogue-manager.test.d.ts +19 -0
  28. package/packages/core/dist/consultation/dialogue-manager.test.d.ts.map +1 -0
  29. package/packages/core/dist/consultation/dialogue-manager.test.js +444 -0
  30. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -0
  31. package/packages/core/dist/public/js/react/react.js +3 -3
  32. package/packages/core/dist/scripts/theme-detail.test.d.ts +10 -0
  33. package/packages/core/dist/scripts/theme-detail.test.js +199 -0
  34. package/packages/core/dist/server/api/git.d.ts +13 -1
  35. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  36. package/packages/core/dist/server/api/git.js +53 -34
  37. package/packages/core/dist/server/api/git.js.map +1 -1
  38. package/packages/core/dist/server/api/health-score.d.ts.map +1 -1
  39. package/packages/core/dist/server/api/health-score.js +25 -1
  40. package/packages/core/dist/server/api/health-score.js.map +1 -1
  41. package/packages/core/dist/server/api/settings.d.ts.map +1 -1
  42. package/packages/core/dist/server/api/settings.js +63 -1
  43. package/packages/core/dist/server/api/settings.js.map +1 -1
  44. package/packages/core/dist/server/api/theme-agents.d.ts.map +1 -1
  45. package/packages/core/dist/server/api/theme-agents.js +61 -0
  46. package/packages/core/dist/server/api/theme-agents.js.map +1 -1
  47. package/packages/core/dist/server/server.d.ts.map +1 -1
  48. package/packages/core/dist/server/server.js +17 -12
  49. package/packages/core/dist/server/server.js.map +1 -1
  50. package/packages/core/dist/shared/skill-search.test.js +2 -2
  51. package/packages/core/dist/workflow/gate-file-validation.d.ts +49 -0
  52. package/packages/core/dist/workflow/gate-file-validation.d.ts.map +1 -0
  53. package/packages/core/dist/workflow/gate-file-validation.js +157 -0
  54. package/packages/core/dist/workflow/gate-file-validation.js.map +1 -0
  55. package/packages/core/dist/workflow/gate-file-validation.test.d.ts +19 -0
  56. package/packages/core/dist/workflow/gate-file-validation.test.d.ts.map +1 -0
  57. package/packages/core/dist/workflow/gate-file-validation.test.js +536 -0
  58. package/packages/core/dist/workflow/gate-file-validation.test.js.map +1 -0
  59. package/packages/core/dist/workflow/gate-schema-validation.test.d.ts +14 -0
  60. package/packages/core/dist/workflow/gate-schema-validation.test.d.ts.map +1 -0
  61. package/packages/core/dist/workflow/gate-schema-validation.test.js +339 -0
  62. package/packages/core/dist/workflow/gate-schema-validation.test.js.map +1 -0
  63. package/packages/core/dist/workflow/handoff.js +2 -2
  64. package/packages/core/dist/workflow/handoff.js.map +1 -1
  65. package/packages/core/dist/workflow/handoff.test.js +16 -0
  66. package/packages/core/dist/workflow/handoff.test.js.map +1 -1
  67. package/packages/core/dist/workflow/workflow-schema.d.ts +4 -2
  68. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  69. package/packages/core/dist/workflow/workflow-schema.js +43 -8
  70. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  71. package/pennyfarthing-dist/agents/README.md +6 -14
  72. package/pennyfarthing-dist/agents/architect.md +43 -29
  73. package/pennyfarthing-dist/agents/ba.md +30 -29
  74. package/pennyfarthing-dist/agents/dev.md +32 -43
  75. package/pennyfarthing-dist/agents/devops.md +57 -21
  76. package/pennyfarthing-dist/agents/orchestrator.md +3 -10
  77. package/pennyfarthing-dist/agents/pm.md +45 -31
  78. package/pennyfarthing-dist/agents/reviewer.md +20 -66
  79. package/pennyfarthing-dist/agents/sm-setup.md +2 -2
  80. package/pennyfarthing-dist/agents/sm.md +8 -30
  81. package/pennyfarthing-dist/agents/tea.md +25 -41
  82. package/pennyfarthing-dist/agents/tech-writer.md +33 -90
  83. package/pennyfarthing-dist/agents/ux-designer.md +39 -39
  84. package/pennyfarthing-dist/commands/benchmark-control.md +8 -64
  85. package/pennyfarthing-dist/commands/benchmark.md +8 -480
  86. package/pennyfarthing-dist/commands/job-fair.md +8 -97
  87. package/pennyfarthing-dist/commands/pf-benchmark-control.md +70 -0
  88. package/pennyfarthing-dist/commands/pf-benchmark.md +486 -0
  89. package/pennyfarthing-dist/commands/pf-chore.md +4 -4
  90. package/pennyfarthing-dist/commands/pf-ci.md +40 -0
  91. package/pennyfarthing-dist/commands/pf-close-epic.md +9 -27
  92. package/pennyfarthing-dist/commands/pf-continue-session.md +9 -213
  93. package/pennyfarthing-dist/commands/pf-create-branches-from-story.md +11 -353
  94. package/pennyfarthing-dist/commands/pf-docs.md +28 -0
  95. package/pennyfarthing-dist/commands/pf-epic.md +67 -0
  96. package/pennyfarthing-dist/commands/pf-git-cleanup.md +11 -52
  97. package/pennyfarthing-dist/commands/pf-git.md +75 -0
  98. package/pennyfarthing-dist/commands/pf-help.md +110 -128
  99. package/pennyfarthing-dist/commands/pf-job-fair.md +102 -0
  100. package/pennyfarthing-dist/commands/pf-new-work.md +9 -18
  101. package/pennyfarthing-dist/commands/pf-parallel-work.md +6 -66
  102. package/pennyfarthing-dist/commands/pf-release.md +11 -76
  103. package/pennyfarthing-dist/commands/pf-repo-status.md +11 -44
  104. package/pennyfarthing-dist/commands/pf-run-ci.md +8 -111
  105. package/pennyfarthing-dist/commands/pf-session.md +51 -0
  106. package/pennyfarthing-dist/commands/pf-solo.md +447 -0
  107. package/pennyfarthing-dist/commands/pf-sprint-planning.md +8 -104
  108. package/pennyfarthing-dist/commands/pf-standalone.md +1 -1
  109. package/pennyfarthing-dist/commands/pf-start-epic.md +9 -163
  110. package/pennyfarthing-dist/commands/pf-sync-epic-to-jira.md +8 -179
  111. package/pennyfarthing-dist/commands/pf-sync-work-with-sprint.md +8 -368
  112. package/pennyfarthing-dist/commands/pf-update-domain-docs.md +8 -78
  113. package/pennyfarthing-dist/commands/solo.md +8 -442
  114. package/pennyfarthing-dist/guides/agent-behavior.md +13 -13
  115. package/pennyfarthing-dist/guides/agent-coordination.md +7 -7
  116. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -5
  117. package/pennyfarthing-dist/guides/bikerack.md +128 -0
  118. package/pennyfarthing-dist/guides/brownfield-tools.md +133 -0
  119. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +2 -2
  120. package/pennyfarthing-dist/guides/gate-schema.md +227 -0
  121. package/pennyfarthing-dist/guides/gates.md +120 -0
  122. package/pennyfarthing-dist/guides/handoff-cli.md +116 -0
  123. package/pennyfarthing-dist/guides/hooks.md +86 -4
  124. package/pennyfarthing-dist/guides/output-styles.md +65 -0
  125. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +5 -5
  126. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -4
  127. package/pennyfarthing-dist/guides/prompt-patterns.md +5 -5
  128. package/pennyfarthing-dist/guides/reflector.md +4 -4
  129. package/pennyfarthing-dist/guides/session-artifacts.md +1 -1
  130. package/pennyfarthing-dist/guides/skill-schema.md +1 -1
  131. package/pennyfarthing-dist/guides/tandem-protocol.md +13 -1
  132. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  133. package/pennyfarthing-dist/guides/xml-tags.md +5 -4
  134. package/pennyfarthing-dist/personas/themes/hogans-heroes.yaml +11 -22
  135. package/pennyfarthing-dist/personas/themes/stephen-king.yaml +13 -24
  136. package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +322 -0
  137. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +1 -1
  138. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +19 -14
  139. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +191 -57
  140. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +26 -10
  141. package/pennyfarthing-dist/skills/pf-changelog/SKILL.md +4 -4
  142. package/pennyfarthing-dist/skills/pf-sprint/skill.md +1 -1
  143. package/pennyfarthing-dist/skills/skill-registry.schema.json +4 -0
  144. package/pennyfarthing-dist/skills/skill-registry.yaml +5 -0
  145. package/pennyfarthing-dist/workflows/2party-tdd.yaml +11 -0
  146. package/pennyfarthing-dist/workflows/agent-docs.yaml +2 -0
  147. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +4 -0
  148. package/pennyfarthing-dist/workflows/bdd.yaml +4 -0
  149. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  150. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +3 -0
  151. package/pennyfarthing-dist/workflows/tdd.yaml +3 -0
  152. package/pennyfarthing-dist/workflows/trivial.yaml +2 -0
  153. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  154. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  155. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  160. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  161. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  162. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  163. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  164. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  165. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/bikerack/cli.py +10 -11
  186. package/pennyfarthing_scripts/bikerack/debug_panel.py +218 -0
  187. package/pennyfarthing_scripts/bikerack/diffs_panel.py +203 -27
  188. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  190. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/cli.py +114 -0
  193. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  197. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  198. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  212. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/epic/__init__.py +0 -0
  222. package/pennyfarthing_scripts/epic/cli.py +64 -0
  223. package/pennyfarthing_scripts/gate/__init__.py +1 -0
  224. package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
  226. package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/gate/cli.py +56 -0
  228. package/pennyfarthing_scripts/gate/validate.py +266 -0
  229. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/git_group/__init__.py +0 -0
  233. package/pennyfarthing_scripts/git_group/cli.py +100 -0
  234. package/pennyfarthing_scripts/handoff/__init__.py +1 -0
  235. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/handoff/cli.py +120 -0
  243. package/pennyfarthing_scripts/handoff/complete_phase.py +155 -0
  244. package/pennyfarthing_scripts/handoff/gate_file.py +105 -0
  245. package/pennyfarthing_scripts/handoff/gate_runner.py +152 -0
  246. package/pennyfarthing_scripts/handoff/marker.py +109 -0
  247. package/pennyfarthing_scripts/handoff/resolve_gate.py +152 -0
  248. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
  291. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  292. package/pennyfarthing_scripts/prime/workflow.py +39 -0
  293. package/pennyfarthing_scripts/session/__init__.py +0 -0
  294. package/pennyfarthing_scripts/session/cli.py +87 -0
  295. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  302. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  303. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  307. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/sprint/story_finish.py +14 -0
  313. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  315. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  317. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  318. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  319. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  320. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  321. package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
  322. package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
  323. package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
  324. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  325. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  326. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  327. package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
  328. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  329. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  330. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  331. package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
  332. package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
  333. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  334. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  335. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  336. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  337. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  338. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  339. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  340. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  341. package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
  342. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  343. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
  344. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  345. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  346. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  347. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  348. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  349. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  350. package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
  351. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  352. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  353. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  354. package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
  355. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  356. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  357. package/pennyfarthing_scripts/tests/test_108_1_gate_migration.py +540 -0
  358. package/pennyfarthing_scripts/tests/test_108_2_remove_handoff_fallback.py +339 -0
  359. package/pennyfarthing_scripts/tests/test_confidence_sm_evaluation.py +253 -0
  360. package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +315 -0
  361. package/pennyfarthing_scripts/tests/test_gate_file_resolution.py +341 -0
  362. package/pennyfarthing_scripts/tests/test_gate_runner.py +620 -0
  363. package/pennyfarthing_scripts/tests/test_handoff_cli.py +929 -0
  364. package/pennyfarthing_scripts/tests/test_handoff_e2e.py +454 -0
  365. package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +464 -0
  366. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  367. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  368. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  369. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  370. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  372. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  373. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  374. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  375. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  376. package/pennyfarthing_scripts/validate/adapters/skill_command.py +200 -0
  377. package/pennyfarthing_scripts/validate/adapters/workflow.py +64 -0
  378. package/pennyfarthing_scripts/validate/cli.py +15 -4
  379. package/packages/core/dist/scripts/benchmark-integration.d.ts +0 -182
  380. package/packages/core/dist/scripts/benchmark-integration.d.ts.map +0 -1
  381. package/packages/core/dist/scripts/benchmark-integration.js +0 -691
  382. package/packages/core/dist/scripts/benchmark-integration.js.map +0 -1
  383. package/packages/core/dist/scripts/job-fair-aggregator.d.ts +0 -150
  384. package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +0 -1
  385. package/packages/core/dist/scripts/job-fair-aggregator.js +0 -547
  386. package/packages/core/dist/scripts/job-fair-aggregator.js.map +0 -1
  387. package/pennyfarthing-dist/agents/handoff.md +0 -250
  388. package/pennyfarthing-dist/agents/sm-handoff.md +0 -152
  389. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -112
  390. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  391. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  392. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  393. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  394. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  395. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  396. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  397. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  400. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  401. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -0,0 +1,454 @@
1
+ """End-to-end handoff smoke tests (Story 105-3).
2
+
3
+ Epic: 105 (Script-First Handoff)
4
+ Story: 105-3 — End-to-end handoff smoke test
5
+
6
+ Unlike the unit tests in test_handoff_cli.py (story 105-1), these tests chain
7
+ operations together to verify the full handoff flow works end-to-end:
8
+ resolve-gate → (gate evaluation contract) → complete-phase → handoff-marker
9
+
10
+ Acceptance Criteria:
11
+ - [AC1] resolve-gate returns status:ready for green phase with assessment written
12
+ - [AC2] Gate subagent evaluates tests_pass using inline fallback
13
+ - [AC3] complete-phase updates session file phase correctly
14
+ - [AC4] handoff-marker emits correct CYCLIST marker
15
+ - [AC5] Trivial workflow skips gate (status:skip) with no LLM spawn
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import re
21
+ import subprocess
22
+ import textwrap
23
+ from pathlib import Path
24
+
25
+ import pytest
26
+ import yaml
27
+
28
+ from pennyfarthing_scripts.handoff.complete_phase import complete_phase
29
+ from pennyfarthing_scripts.handoff.resolve_gate import resolve_gate
30
+
31
+ # ---------------------------------------------------------------------------
32
+ # Workflow definitions (matching real workflow YAMLs)
33
+ # ---------------------------------------------------------------------------
34
+
35
+ TDD_WORKFLOW = {
36
+ "workflow": {
37
+ "name": "tdd",
38
+ "phases": [
39
+ {"name": "setup", "agent": "sm"},
40
+ {"name": "red", "agent": "tea", "gate": {"type": "tests_fail"}},
41
+ {"name": "green", "agent": "dev", "gate": {"type": "tests_pass"}},
42
+ {"name": "review", "agent": "reviewer", "gate": {"type": "approval"}},
43
+ {"name": "finish", "agent": "sm"},
44
+ ],
45
+ }
46
+ }
47
+
48
+ TRIVIAL_WORKFLOW = {
49
+ "workflow": {
50
+ "name": "trivial",
51
+ "phases": [
52
+ {"name": "setup", "agent": "sm"},
53
+ {"name": "implement", "agent": "dev", "gate": {"type": "tests_pass"}},
54
+ {"name": "review", "agent": "reviewer", "gate": {"type": "approval"}},
55
+ {"name": "finish", "agent": "sm"},
56
+ ],
57
+ }
58
+ }
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Session file templates
62
+ # ---------------------------------------------------------------------------
63
+
64
+ SESSION_TDD_GREEN_WITH_ASSESSMENT = textwrap.dedent("""\
65
+ # Story e2e-1: Smoke test story
66
+
67
+ **Story ID:** e2e-1
68
+ **Workflow:** tdd
69
+ **Phase:** green
70
+ **Phase Started:** 2026-02-15T10:00:00Z
71
+
72
+ ## TEA Assessment
73
+
74
+ **Tests Written:** 3 tests covering all ACs
75
+ **Status:** RED confirmed — all 3 failing as expected
76
+
77
+ ## Workflow Tracking
78
+
79
+ **Phase:** green
80
+ **Phase Started:** 2026-02-15T10:00:00Z
81
+
82
+ ### Phase History
83
+ | Phase | Started | Ended | Duration |
84
+ |-------|---------|-------|----------|
85
+ | setup | 2026-02-15T09:50:00Z | 2026-02-15T09:55:00Z | 5m |
86
+ | red | 2026-02-15T09:55:00Z | 2026-02-15T10:00:00Z | 5m |
87
+ | green | 2026-02-15T10:00:00Z | - | - |
88
+
89
+ ### Handoff History
90
+ | From | To | Gate | Status | Timestamp |
91
+ |------|-----|------|--------|-----------|
92
+ | setup (sm) | red (tea) | - | PASSED | 2026-02-15T09:55:00Z |
93
+ | red (tea) | green (dev) | tests_fail | PASSED | 2026-02-15T10:00:00Z |
94
+ """)
95
+
96
+ SESSION_TRIVIAL_SETUP = textwrap.dedent("""\
97
+ # Story e2e-2: Trivial smoke test
98
+
99
+ **Story ID:** e2e-2
100
+ **Workflow:** trivial
101
+ **Phase:** setup
102
+ **Phase Started:** 2026-02-15T10:00:00Z
103
+
104
+ ## Workflow Tracking
105
+
106
+ **Phase:** setup
107
+ **Phase Started:** 2026-02-15T10:00:00Z
108
+
109
+ ### Phase History
110
+ | Phase | Started | Ended | Duration |
111
+ |-------|---------|-------|----------|
112
+ | setup | 2026-02-15T10:00:00Z | - | - |
113
+
114
+ ### Handoff History
115
+ | From | To | Gate | Status | Timestamp |
116
+ |------|-----|------|--------|-----------|
117
+ """)
118
+
119
+
120
+ # ---------------------------------------------------------------------------
121
+ # Fixtures
122
+ # ---------------------------------------------------------------------------
123
+
124
+
125
+ @pytest.fixture
126
+ def project(tmp_path: Path) -> Path:
127
+ """Create minimal project structure with workflow YAMLs."""
128
+ workflows_dir = tmp_path / ".pennyfarthing" / "workflows"
129
+ workflows_dir.mkdir(parents=True)
130
+
131
+ for name, data in [("tdd", TDD_WORKFLOW), ("trivial", TRIVIAL_WORKFLOW)]:
132
+ (workflows_dir / f"{name}.yaml").write_text(
133
+ yaml.dump(data, default_flow_style=False)
134
+ )
135
+
136
+ (tmp_path / ".session").mkdir()
137
+ return tmp_path
138
+
139
+
140
+ @pytest.fixture
141
+ def tdd_session(project: Path) -> Path:
142
+ """Session in TDD green phase with assessment."""
143
+ session_file = project / ".session" / "e2e-1-session.md"
144
+ session_file.write_text(SESSION_TDD_GREEN_WITH_ASSESSMENT)
145
+ return session_file
146
+
147
+
148
+ @pytest.fixture
149
+ def trivial_session(project: Path) -> Path:
150
+ """Session in trivial setup phase (no assessment needed)."""
151
+ session_file = project / ".session" / "e2e-2-session.md"
152
+ session_file.write_text(SESSION_TRIVIAL_SETUP)
153
+ return session_file
154
+
155
+
156
+ # ===========================================================================
157
+ # AC1: resolve-gate returns status:ready for green phase with assessment
158
+ # ===========================================================================
159
+
160
+
161
+ class TestResolveGateReadyForGreen:
162
+ """AC1: TDD green phase with assessment → status:ready."""
163
+
164
+ def test_status_is_ready(self, project: Path, tdd_session: Path) -> None:
165
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
166
+ assert result["status"] == "ready"
167
+
168
+ def test_next_phase_is_review(self, project: Path, tdd_session: Path) -> None:
169
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
170
+ assert result["next_phase"] == "review"
171
+ assert result["next_agent"] == "reviewer"
172
+
173
+ def test_assessment_found_is_true(self, project: Path, tdd_session: Path) -> None:
174
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
175
+ assert result["assessment_found"] is True
176
+
177
+ def test_error_is_null(self, project: Path, tdd_session: Path) -> None:
178
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
179
+ assert result["error"] is None
180
+
181
+
182
+ # ===========================================================================
183
+ # AC2: Gate subagent evaluates tests_pass using inline fallback
184
+ # ===========================================================================
185
+
186
+
187
+ class TestInlineGateFallbackContract:
188
+ """AC2: When gate_file is None, gate_type provides inline evaluation info.
189
+
190
+ The "inline fallback" means the agent uses gate_type directly (e.g.,
191
+ "tests_pass") without loading criteria from a gate file. This test
192
+ verifies resolve-gate returns the correct contract for inline evaluation.
193
+ """
194
+
195
+ def test_gate_type_is_tests_pass(self, project: Path, tdd_session: Path) -> None:
196
+ """Gate type should be tests_pass for TDD green phase."""
197
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
198
+ assert result["gate_type"] == "tests_pass"
199
+
200
+ def test_gate_file_is_none(self, project: Path, tdd_session: Path) -> None:
201
+ """No gate file means agent uses inline type fallback."""
202
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
203
+ assert result["gate_file"] is None
204
+
205
+ def test_inline_fallback_provides_sufficient_info(
206
+ self, project: Path, tdd_session: Path
207
+ ) -> None:
208
+ """Inline fallback needs: status=ready + gate_type + no gate_file."""
209
+ result = resolve_gate("e2e-1", "tdd", "green", project_root=project)
210
+ assert result["status"] == "ready"
211
+ assert result["gate_type"] is not None
212
+ assert result["gate_file"] is None
213
+
214
+
215
+ # ===========================================================================
216
+ # AC3: complete-phase updates session file phase correctly
217
+ # ===========================================================================
218
+
219
+
220
+ class TestCompletePhaseUpdatesSession:
221
+ """AC3: complete-phase atomically updates session with phase transition."""
222
+
223
+ def test_phase_field_updated(self, project: Path, tdd_session: Path) -> None:
224
+ """Phase field changes from 'green' to 'review'."""
225
+ complete_phase("e2e-1", "tdd", "green", "review", "tests_pass", project)
226
+ content = tdd_session.read_text()
227
+ assert "**Phase:** review" in content
228
+ assert "**Phase:** green" not in content
229
+
230
+ def test_phase_started_timestamp_updated(
231
+ self, project: Path, tdd_session: Path
232
+ ) -> None:
233
+ """Phase Started timestamp updated to current time."""
234
+ complete_phase("e2e-1", "tdd", "green", "review", "tests_pass", project)
235
+ content = tdd_session.read_text()
236
+ # Original timestamp should be replaced
237
+ assert "2026-02-15T10:00:00Z" not in re.findall(
238
+ r"\*\*Phase Started:\*\* (\S+)", content
239
+ )
240
+
241
+ def test_phase_history_green_row_filled(
242
+ self, project: Path, tdd_session: Path
243
+ ) -> None:
244
+ """Green phase row gets Ended timestamp and Duration filled."""
245
+ complete_phase("e2e-1", "tdd", "green", "review", "tests_pass", project)
246
+ content = tdd_session.read_text()
247
+ # Match Phase History rows only (format: "| green | timestamp | ..."),
248
+ # not Handoff History rows (format: "| green (dev) | ...")
249
+ green_rows = [
250
+ line
251
+ for line in content.splitlines()
252
+ if line.strip().startswith("| green |")
253
+ ]
254
+ assert len(green_rows) == 1
255
+ cols = [c.strip() for c in green_rows[0].split("|") if c.strip()]
256
+ assert cols[2] != "-", "Ended column should be filled"
257
+ assert cols[3] != "-", "Duration column should be filled"
258
+
259
+ def test_phase_history_review_row_added(
260
+ self, project: Path, tdd_session: Path
261
+ ) -> None:
262
+ """New review phase row added to Phase History."""
263
+ complete_phase("e2e-1", "tdd", "green", "review", "tests_pass", project)
264
+ content = tdd_session.read_text()
265
+ review_rows = [
266
+ line for line in content.splitlines() if line.strip().startswith("| review")
267
+ ]
268
+ assert len(review_rows) == 1
269
+
270
+ def test_handoff_history_row_added(
271
+ self, project: Path, tdd_session: Path
272
+ ) -> None:
273
+ """Handoff History gets new row for green→review transition."""
274
+ complete_phase("e2e-1", "tdd", "green", "review", "tests_pass", project)
275
+ content = tdd_session.read_text()
276
+ handoff_rows = [
277
+ line
278
+ for line in content.splitlines()
279
+ if "green (dev)" in line and "review (reviewer)" in line
280
+ ]
281
+ assert len(handoff_rows) == 1
282
+ assert "tests_pass" in handoff_rows[0]
283
+ assert "PASSED" in handoff_rows[0]
284
+
285
+ def test_returns_success(self, project: Path, tdd_session: Path) -> None:
286
+ result = complete_phase("e2e-1", "tdd", "green", "review", "tests_pass", project)
287
+ assert result["status"] == "success"
288
+ assert result["error"] is None
289
+
290
+
291
+ # ===========================================================================
292
+ # AC1+AC2+AC3: Full chain — resolve-gate → complete-phase
293
+ # ===========================================================================
294
+
295
+
296
+ class TestFullChainE2E:
297
+ """Chain resolve-gate output into complete-phase to verify end-to-end."""
298
+
299
+ def test_resolve_then_complete_chain(
300
+ self, project: Path, tdd_session: Path
301
+ ) -> None:
302
+ """Full chain: resolve-gate → use output → complete-phase."""
303
+ # Step 1: Resolve gate
304
+ resolve_result = resolve_gate(
305
+ "e2e-1", "tdd", "green", project_root=project
306
+ )
307
+ assert resolve_result["status"] == "ready"
308
+
309
+ # Step 2: Use resolve output to drive complete-phase
310
+ complete_result = complete_phase(
311
+ "e2e-1",
312
+ "tdd",
313
+ "green",
314
+ resolve_result["next_phase"],
315
+ resolve_result["gate_type"],
316
+ project,
317
+ )
318
+ assert complete_result["status"] == "success"
319
+
320
+ # Step 3: Verify session reflects the transition
321
+ content = tdd_session.read_text()
322
+ assert "**Phase:** review" in content
323
+ assert "green (dev)" in content and "review (reviewer)" in content
324
+
325
+ def test_chain_preserves_prior_history(
326
+ self, project: Path, tdd_session: Path
327
+ ) -> None:
328
+ """Chain should preserve existing Phase History and Handoff History."""
329
+ resolve_result = resolve_gate(
330
+ "e2e-1", "tdd", "green", project_root=project
331
+ )
332
+ complete_phase(
333
+ "e2e-1",
334
+ "tdd",
335
+ "green",
336
+ resolve_result["next_phase"],
337
+ resolve_result["gate_type"],
338
+ project,
339
+ )
340
+ content = tdd_session.read_text()
341
+ # Prior rows should still exist
342
+ assert "setup (sm)" in content
343
+ assert "red (tea)" in content
344
+
345
+
346
+ # ===========================================================================
347
+ # AC4: handoff-marker emits correct CYCLIST marker
348
+ # ===========================================================================
349
+
350
+
351
+ class TestHandoffMarkerOutput:
352
+ """AC4: pf handoff marker emits correct AGENT_COMMAND block."""
353
+
354
+ def test_marker_contains_agent_command(self) -> None:
355
+ """Output contains AGENT_COMMAND YAML block."""
356
+ result = subprocess.run(
357
+ ["pf", "handoff", "marker", "reviewer"],
358
+ capture_output=True,
359
+ text=True,
360
+ )
361
+ assert result.returncode == 0
362
+ assert "AGENT_COMMAND:" in result.stdout
363
+
364
+ def test_marker_references_correct_agent(self) -> None:
365
+ """Marker fallback references the correct next agent."""
366
+ result = subprocess.run(
367
+ ["pf", "handoff", "marker", "reviewer"],
368
+ capture_output=True,
369
+ text=True,
370
+ )
371
+ assert "/reviewer" in result.stdout
372
+
373
+ def test_marker_for_dev_agent(self) -> None:
374
+ """Marker works for dev agent too."""
375
+ result = subprocess.run(
376
+ ["pf", "handoff", "marker", "dev"],
377
+ capture_output=True,
378
+ text=True,
379
+ )
380
+ assert result.returncode == 0
381
+ assert "/dev" in result.stdout
382
+
383
+ def test_marker_error_mode(self) -> None:
384
+ """Error flag produces error block."""
385
+ result = subprocess.run(
386
+ ["pf", "handoff", "marker", "--error", "Tests failing"],
387
+ capture_output=True,
388
+ text=True,
389
+ )
390
+ assert result.returncode == 0
391
+ assert "error: true" in result.stdout
392
+ assert "Tests failing" in result.stdout
393
+
394
+
395
+ # ===========================================================================
396
+ # AC5: Trivial workflow skips gate (status:skip) with no LLM spawn
397
+ # ===========================================================================
398
+
399
+
400
+ class TestTrivialSkipsGate:
401
+ """AC5: Trivial workflow setup phase skips gate evaluation entirely."""
402
+
403
+ def test_trivial_setup_returns_skip(
404
+ self, project: Path, trivial_session: Path
405
+ ) -> None:
406
+ """Setup phase has no gate → status:skip."""
407
+ result = resolve_gate("e2e-2", "trivial", "setup", project_root=project)
408
+ assert result["status"] == "skip"
409
+
410
+ def test_skip_has_no_gate_type(
411
+ self, project: Path, trivial_session: Path
412
+ ) -> None:
413
+ """Skip result has no gate_type — no LLM evaluation needed."""
414
+ result = resolve_gate("e2e-2", "trivial", "setup", project_root=project)
415
+ assert result["gate_type"] is None
416
+
417
+ def test_skip_has_no_gate_file(
418
+ self, project: Path, trivial_session: Path
419
+ ) -> None:
420
+ """Skip result has no gate_file — nothing to evaluate."""
421
+ result = resolve_gate("e2e-2", "trivial", "setup", project_root=project)
422
+ assert result["gate_file"] is None
423
+
424
+ def test_skip_routes_to_implement(
425
+ self, project: Path, trivial_session: Path
426
+ ) -> None:
427
+ """Even when skipping, next_phase/next_agent are populated."""
428
+ result = resolve_gate("e2e-2", "trivial", "setup", project_root=project)
429
+ assert result["next_phase"] == "implement"
430
+ assert result["next_agent"] == "dev"
431
+
432
+ def test_skip_to_complete_phase_chain(
433
+ self, project: Path, trivial_session: Path
434
+ ) -> None:
435
+ """AC5: Skip → complete-phase works without gate evaluation step."""
436
+ resolve_result = resolve_gate(
437
+ "e2e-2", "trivial", "setup", project_root=project
438
+ )
439
+ assert resolve_result["status"] == "skip"
440
+
441
+ # Skip gate evaluation entirely, go straight to complete-phase
442
+ complete_result = complete_phase(
443
+ "e2e-2",
444
+ "trivial",
445
+ "setup",
446
+ resolve_result["next_phase"],
447
+ "skip", # No gate was evaluated
448
+ project,
449
+ )
450
+ assert complete_result["status"] == "success"
451
+
452
+ # Verify session updated correctly
453
+ content = trivial_session.read_text()
454
+ assert "**Phase:** implement" in content