@pennyfarthing/core 11.1.0 → 11.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 (263) hide show
  1. package/README.md +8 -8
  2. package/package.json +16 -14
  3. package/packages/core/dist/cli/utils/constants.d.ts +1 -1
  4. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  5. package/packages/core/dist/cli/utils/constants.js +2 -1
  6. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  7. package/packages/core/dist/consultation/dialogue-manager.d.ts +75 -0
  8. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -0
  9. package/packages/core/dist/consultation/dialogue-manager.js +334 -0
  10. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -0
  11. package/packages/core/dist/consultation/dialogue-manager.test.d.ts +19 -0
  12. package/packages/core/dist/consultation/dialogue-manager.test.d.ts.map +1 -0
  13. package/packages/core/dist/consultation/dialogue-manager.test.js +444 -0
  14. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -0
  15. package/packages/core/dist/server/api/git.d.ts +13 -1
  16. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  17. package/packages/core/dist/server/api/git.js +53 -34
  18. package/packages/core/dist/server/api/git.js.map +1 -1
  19. package/packages/core/dist/server/otlp-receiver.d.ts +16 -11
  20. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  21. package/packages/core/dist/server/otlp-receiver.js +185 -24
  22. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  23. package/packages/core/dist/server/otlp-receiver.test.d.ts +21 -0
  24. package/packages/core/dist/server/otlp-receiver.test.d.ts.map +1 -0
  25. package/packages/core/dist/server/otlp-receiver.test.js +446 -0
  26. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -0
  27. package/packages/core/dist/shared/portrait-resolver.d.ts +9 -0
  28. package/packages/core/dist/shared/portrait-resolver.d.ts.map +1 -1
  29. package/packages/core/dist/shared/portrait-resolver.js +27 -0
  30. package/packages/core/dist/shared/portrait-resolver.js.map +1 -1
  31. package/packages/core/dist/shared/portrait-resolver.test.js +47 -1
  32. package/packages/core/dist/shared/portrait-resolver.test.js.map +1 -1
  33. package/packages/core/dist/shared/skill-search.test.js +2 -2
  34. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts +13 -0
  35. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts.map +1 -0
  36. package/packages/core/dist/shared/tandem-portrait-inventory.test.js +126 -0
  37. package/packages/core/dist/shared/tandem-portrait-inventory.test.js.map +1 -0
  38. package/pennyfarthing-dist/agents/dev.md +1 -1
  39. package/pennyfarthing-dist/agents/reviewer.md +1 -1
  40. package/pennyfarthing-dist/agents/sm-setup.md +1 -1
  41. package/pennyfarthing-dist/agents/sm.md +2 -2
  42. package/pennyfarthing-dist/agents/tea.md +1 -1
  43. package/pennyfarthing-dist/agents/testing-runner.md +2 -1
  44. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  45. package/pennyfarthing-dist/commands/pf-standalone.md +7 -2
  46. package/pennyfarthing-dist/guides/agent-behavior.md +1 -1
  47. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -1
  48. package/pennyfarthing-dist/guides/bikerack.md +3 -3
  49. package/pennyfarthing-dist/guides/hooks.md +1 -1
  50. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  51. package/pennyfarthing-dist/guides/xml-tags.md +2 -2
  52. package/pennyfarthing-dist/scripts/README.md +1 -1
  53. package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
  54. package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
  55. package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +322 -0
  56. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
  57. package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
  58. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  59. package/pennyfarthing-dist/scripts/git/README.md +24 -14
  60. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +5 -266
  61. package/pennyfarthing-dist/scripts/git/git-status-all.sh +5 -151
  62. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +6 -144
  63. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  64. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +5 -496
  65. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  66. package/pennyfarthing-dist/scripts/hooks/README.md +1 -1
  67. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +1 -1
  68. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  69. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  70. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  71. package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
  72. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +9 -11
  73. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
  74. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
  75. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  76. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
  77. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  78. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  79. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  80. package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
  81. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  82. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  83. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +1 -1
  84. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  85. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  86. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  87. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  88. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  89. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  90. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  91. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  92. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  93. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  94. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  95. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  96. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  97. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  98. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  99. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  100. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  101. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  102. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  103. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  104. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  105. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  106. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
  107. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  108. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  109. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  110. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  111. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  112. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  113. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  114. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  115. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
  116. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
  117. package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +76 -0
  118. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  119. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  120. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  121. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  122. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  123. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  124. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
  125. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  126. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  127. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  128. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  129. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  130. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
  131. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  132. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  133. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  134. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  135. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  136. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
  137. package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
  138. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  139. package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
  140. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
  141. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +4 -221
  142. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  143. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +5 -13
  144. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +4 -123
  145. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +4 -33
  146. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +4 -156
  147. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +4 -131
  148. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +4 -249
  149. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +4 -160
  150. package/pennyfarthing-dist/skills/pf-bc/usage.md +1 -1
  151. package/pennyfarthing-dist/skills/pf-jira/examples.md +5 -2
  152. package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
  153. package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
  154. package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
  155. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -16
  156. package/pennyfarthing-dist/skills/pf-workflow/skill.md +9 -12
  157. package/pennyfarthing-dist/skills/pf-workflow/usage.md +33 -8
  158. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +18 -6
  159. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  160. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  161. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  162. package/pennyfarthing-dist/workflows/review-tandem.yaml +65 -0
  163. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +16 -8
  164. package/pennyfarthing_scripts/CLAUDE.md +26 -4
  165. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/bc/cli.py +3 -5
  171. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/bikerack/background_panel.py +86 -5
  185. package/pennyfarthing_scripts/bikerack/base_panel.py +62 -0
  186. package/pennyfarthing_scripts/bikerack/changed_panel.py +32 -28
  187. package/pennyfarthing_scripts/bikerack/cli.py +10 -11
  188. package/pennyfarthing_scripts/bikerack/debug_panel.py +31 -1
  189. package/pennyfarthing_scripts/bikerack/diffs_panel.py +74 -17
  190. package/pennyfarthing_scripts/bikerack/git_panel.py +103 -33
  191. package/pennyfarthing_scripts/bikerack/launcher.py +15 -15
  192. package/pennyfarthing_scripts/bikerack/progress_panel.py +315 -0
  193. package/pennyfarthing_scripts/bikerack/sprint_panel.py +158 -26
  194. package/pennyfarthing_scripts/bikerack/tui.py +336 -30
  195. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  196. package/pennyfarthing_scripts/cli.py +37 -65
  197. package/pennyfarthing_scripts/consultation/__init__.py +1 -0
  198. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/consultation/cli.py +149 -0
  201. package/pennyfarthing_scripts/consultation/dialogue_manager.py +417 -0
  202. package/pennyfarthing_scripts/context.py +3 -3
  203. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/git/__init__.py +12 -1
  206. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
  212. package/pennyfarthing_scripts/git/create_branches.py +3 -4
  213. package/pennyfarthing_scripts/git/hooks_installer.py +152 -0
  214. package/pennyfarthing_scripts/git/repos.py +196 -0
  215. package/pennyfarthing_scripts/git/status_all.py +27 -11
  216. package/pennyfarthing_scripts/git/worktree.py +302 -0
  217. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/git_group/cli.py +143 -40
  220. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  222. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/handoff/complete_phase.py +12 -0
  225. package/pennyfarthing_scripts/handoff/resolve_gate.py +5 -14
  226. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  227. package/pennyfarthing_scripts/hooks.py +3 -17
  228. package/pennyfarthing_scripts/pretooluse_hook.py +1 -1
  229. package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/prime/heatmap.py +655 -0
  232. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/session_start_hook.py +1 -1
  235. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/sprint/loader.py +15 -1
  237. package/pennyfarthing_scripts/sprint/story_finish.py +14 -0
  238. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  239. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  240. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  241. package/pennyfarthing_scripts/tests/test_bikerack.py +51 -51
  242. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +811 -0
  243. package/pennyfarthing_scripts/tests/test_handoff_cli.py +16 -11
  244. package/pennyfarthing_scripts/tests/test_workflow_check.py +2 -3
  245. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/validate/adapters/tandem_awareness.py +254 -0
  247. package/pennyfarthing_scripts/validate/cli.py +17 -5
  248. package/pennyfarthing_scripts/workflow/__init__.py +40 -0
  249. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/workflow/cli.py +1099 -0
  255. package/pennyfarthing_scripts/workflow/helpers.py +241 -0
  256. package/pennyfarthing_scripts/{workflow.py → workflow/scale.py} +0 -104
  257. package/pennyfarthing_scripts/workflow/state.py +112 -0
  258. package/scripts/README.md +41 -0
  259. package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -91
  260. package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -163
  261. package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -138
  262. package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -273
  263. package/pennyfarthing-dist/skills/pf-workflow/scripts/workflow-status.sh +0 -167
@@ -62,6 +62,17 @@ def _merge_epic_shards(data: dict[str, Any], sprint_dir: Path) -> dict[str, Any]
62
62
  stacklevel=2,
63
63
  )
64
64
 
65
+ # Collect epic refs owned by initiatives so we don't warn about them.
66
+ initiative_refs: set[str] = set()
67
+ for init_file in sorted(sprint_dir.glob("initiative-*.yaml")):
68
+ init_data = load_yaml_config(init_file)
69
+ if init_data and isinstance(init_data, dict):
70
+ for ref in init_data.get("epics", []):
71
+ if isinstance(ref, str):
72
+ initiative_refs.add(ref)
73
+ # Also add normalized form (strip "epic-" prefix)
74
+ initiative_refs.add(ref.replace("epic-", ""))
75
+
65
76
  # Log unindexed shard files on disk (but do NOT auto-merge —
66
77
  # orphan shards may belong to future initiatives).
67
78
  for shard_file in sorted(sprint_dir.glob("epic-*.yaml")):
@@ -74,7 +85,10 @@ def _merge_epic_shards(data: dict[str, Any], sprint_dir: Path) -> dict[str, Any]
74
85
  jira_key = str(epic_data.get("jira", ""))
75
86
  if eid in loaded_epic_ids or (jira_key and jira_key in loaded_epic_ids):
76
87
  continue
77
- # Warn but don't merge
88
+ # Skip shards owned by initiatives (not orphans)
89
+ if eid in initiative_refs or jira_key in initiative_refs:
90
+ continue
91
+ # Warn only about truly orphaned shards
78
92
  warnings.warn(
79
93
  f"Unindexed shard {shard_file.name} (epic {eid}) not in epics list — skipping",
80
94
  stacklevel=2,
@@ -126,8 +126,16 @@ def finish_story(
126
126
  steps: list[dict[str, Any]] = []
127
127
  archive_name = f"{jira_key}-session.md" if jira_key else f"{story_id}-session.md"
128
128
 
129
+ # Check for dialogue file
130
+ dialogue_path = project_root / ".session" / f"{story_id}-dialogue.md"
131
+ dialogue_archive_name = (
132
+ f"{jira_key}-dialogue.md" if jira_key else f"{story_id}-dialogue.md"
133
+ )
134
+
129
135
  if dry_run:
130
136
  steps.append({"step": 1, "action": f"Archive session → {archive_dir / archive_name}"})
137
+ if dialogue_path.exists():
138
+ steps.append({"step": "1b", "action": f"Archive dialogue → {archive_dir / dialogue_archive_name}"})
131
139
  if pr_number:
132
140
  steps.append({"step": 2, "action": f"Merge PR #{pr_number} (squash, delete branch)"})
133
141
  else:
@@ -147,6 +155,12 @@ def finish_story(
147
155
  shutil.copy2(session_path, archive_dest)
148
156
  steps.append({"step": 1, "action": "archive_session", "dest": str(archive_dest)})
149
157
 
158
+ # --- Step 1b: Archive dialogue (if exists) ---
159
+ if dialogue_path.exists():
160
+ dialogue_dest = archive_dir / dialogue_archive_name
161
+ shutil.copy2(dialogue_path, dialogue_dest)
162
+ steps.append({"step": "1b", "action": "archive_dialogue", "dest": str(dialogue_dest)})
163
+
150
164
  # --- Step 2: Merge PR ---
151
165
  if pr_number:
152
166
  result = _run(["gh", "pr", "merge", pr_number, "--squash", "--delete-branch"])
@@ -5,14 +5,14 @@ Epic: 101 — BikeRack Mode (ADR-0024)
5
5
 
6
6
  Acceptance Criteria:
7
7
  - [AC1] `pf bikerack start` starts WheelHub background with IS_BIKERACK=1
8
- - [AC2] Polls for .bikerack-port file (100ms interval, 5s timeout)
8
+ - [AC2] Polls for .wheelhub-port file (100ms interval, 5s timeout)
9
9
  - [AC3] Sets exactly 5 OTEL env vars from discovered port (Rule 5)
10
10
  - [AC4] Uses exec (not spawn) for Claude CLI (CE-4)
11
11
  - [AC5] trap EXIT registered before exec to kill WheelHub PID (Rule 8)
12
- - [AC6] Writes .bikerack-pid after spawning WheelHub
12
+ - [AC6] Writes .wheelhub-pid after spawning WheelHub
13
13
  - [AC7] `pf bikerack stop` reads PID, sends SIGTERM, deletes files
14
14
  - [AC8] `pf bikerack status` shows running state (PID, port, uptime)
15
- - [AC9] Error if already running (.bikerack-port exists with live PID)
15
+ - [AC9] Error if already running (.wheelhub-port exists with live PID)
16
16
  - [AC10] Exit code 1 if WheelHub fails to start, 2 if already running
17
17
  - [AC11] Prints dashboard URL on startup
18
18
  - [AC12] `just bikerack` works as alias
@@ -101,7 +101,7 @@ class TestStartWheelHub:
101
101
 
102
102
 
103
103
  # ---------------------------------------------------------------------------
104
- # AC2: Polls for .bikerack-port file (100ms interval, 5s timeout)
104
+ # AC2: Polls for .wheelhub-port file (100ms interval, 5s timeout)
105
105
  # ---------------------------------------------------------------------------
106
106
 
107
107
 
@@ -110,7 +110,7 @@ class TestPortFilePolling:
110
110
 
111
111
  def test_returns_port_when_file_exists(self, tmp_path: Path) -> None:
112
112
  """poll_for_port_file should return port number from file."""
113
- port_file = tmp_path / ".bikerack-port"
113
+ port_file = tmp_path / ".wheelhub-port"
114
114
  port_file.write_text("2898")
115
115
 
116
116
  result = poll_for_port_file(tmp_path)
@@ -125,7 +125,7 @@ class TestPortFilePolling:
125
125
 
126
126
  def test_waits_for_file_to_appear(self, tmp_path: Path) -> None:
127
127
  """poll_for_port_file should poll until file appears."""
128
- port_file = tmp_path / ".bikerack-port"
128
+ port_file = tmp_path / ".wheelhub-port"
129
129
 
130
130
  # Simulate file appearing after short delay
131
131
  call_count = [0]
@@ -160,7 +160,7 @@ class TestPortFilePolling:
160
160
 
161
161
  def test_reads_integer_port(self, tmp_path: Path) -> None:
162
162
  """poll_for_port_file should parse port as integer."""
163
- port_file = tmp_path / ".bikerack-port"
163
+ port_file = tmp_path / ".wheelhub-port"
164
164
  port_file.write_text("3000\n") # Trailing newline should be handled
165
165
 
166
166
  result = poll_for_port_file(tmp_path)
@@ -340,8 +340,8 @@ class TestCleanupRegistration:
340
340
  assert kill_args[1] == signal.SIGTERM
341
341
 
342
342
  def test_cleanup_removes_port_file(self, tmp_path: Path) -> None:
343
- """Registered cleanup should delete .bikerack-port file."""
344
- port_file = tmp_path / ".bikerack-port"
343
+ """Registered cleanup should delete .wheelhub-port file."""
344
+ port_file = tmp_path / ".wheelhub-port"
345
345
  port_file.write_text("2898")
346
346
 
347
347
  cleanup_func = None
@@ -363,11 +363,11 @@ class TestCleanupRegistration:
363
363
  except (ProcessLookupError, OSError):
364
364
  pass
365
365
 
366
- assert not port_file.exists(), ".bikerack-port should be deleted by cleanup"
366
+ assert not port_file.exists(), ".wheelhub-port should be deleted by cleanup"
367
367
 
368
368
  def test_cleanup_removes_pid_file(self, tmp_path: Path) -> None:
369
- """Registered cleanup should delete .bikerack-pid file."""
370
- pid_file = tmp_path / ".bikerack-pid"
369
+ """Registered cleanup should delete .wheelhub-pid file."""
370
+ pid_file = tmp_path / ".wheelhub-pid"
371
371
  pid_file.write_text("12345")
372
372
 
373
373
  cleanup_func = None
@@ -389,28 +389,28 @@ class TestCleanupRegistration:
389
389
  except (ProcessLookupError, OSError):
390
390
  pass
391
391
 
392
- assert not pid_file.exists(), ".bikerack-pid should be deleted by cleanup"
392
+ assert not pid_file.exists(), ".wheelhub-pid should be deleted by cleanup"
393
393
 
394
394
 
395
395
  # ---------------------------------------------------------------------------
396
- # AC6: Writes .bikerack-pid after spawning WheelHub
396
+ # AC6: Writes .wheelhub-pid after spawning WheelHub
397
397
  # ---------------------------------------------------------------------------
398
398
 
399
399
 
400
400
  class TestPidFile:
401
- """AC6: write_pid_file writes .bikerack-pid."""
401
+ """AC6: write_pid_file writes .wheelhub-pid."""
402
402
 
403
403
  def test_writes_pid_to_file(self, tmp_path: Path) -> None:
404
404
  """write_pid_file should write PID as ASCII string."""
405
405
  write_pid_file(tmp_path, pid=48291)
406
406
 
407
- pid_file = tmp_path / ".bikerack-pid"
407
+ pid_file = tmp_path / ".wheelhub-pid"
408
408
  assert pid_file.exists()
409
409
  assert pid_file.read_text().strip() == "48291"
410
410
 
411
411
  def test_read_pid_file_returns_pid(self, tmp_path: Path) -> None:
412
412
  """read_pid_file should return PID as integer."""
413
- pid_file = tmp_path / ".bikerack-pid"
413
+ pid_file = tmp_path / ".wheelhub-pid"
414
414
  pid_file.write_text("48291")
415
415
 
416
416
  result = read_pid_file(tmp_path)
@@ -424,12 +424,12 @@ class TestPidFile:
424
424
  assert result is None
425
425
 
426
426
  def test_write_pid_creates_file_in_project_dir(self, tmp_path: Path) -> None:
427
- """write_pid_file should create .bikerack-pid in project directory."""
427
+ """write_pid_file should create .wheelhub-pid in project directory."""
428
428
  write_pid_file(tmp_path, pid=99999)
429
429
 
430
- expected = tmp_path / ".bikerack-pid"
430
+ expected = tmp_path / ".wheelhub-pid"
431
431
  assert expected.exists()
432
- assert expected.name == ".bikerack-pid"
432
+ assert expected.name == ".wheelhub-pid"
433
433
 
434
434
 
435
435
  # ---------------------------------------------------------------------------
@@ -443,8 +443,8 @@ class TestStopBikeRack:
443
443
  def test_sends_sigterm_to_pid(self, tmp_path: Path) -> None:
444
444
  """stop_bikerack should send SIGTERM to the WheelHub PID."""
445
445
  # Setup: create port and pid files
446
- (tmp_path / ".bikerack-port").write_text("2898")
447
- (tmp_path / ".bikerack-pid").write_text("12345")
446
+ (tmp_path / ".wheelhub-port").write_text("2898")
447
+ (tmp_path / ".wheelhub-pid").write_text("12345")
448
448
 
449
449
  with patch("pennyfarthing_scripts.bikerack.launcher.os.kill") as mock_kill:
450
450
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
@@ -453,10 +453,10 @@ class TestStopBikeRack:
453
453
  mock_kill.assert_called_with(12345, signal.SIGTERM)
454
454
 
455
455
  def test_deletes_port_file(self, tmp_path: Path) -> None:
456
- """stop_bikerack should delete .bikerack-port."""
457
- port_file = tmp_path / ".bikerack-port"
456
+ """stop_bikerack should delete .wheelhub-port."""
457
+ port_file = tmp_path / ".wheelhub-port"
458
458
  port_file.write_text("2898")
459
- (tmp_path / ".bikerack-pid").write_text("12345")
459
+ (tmp_path / ".wheelhub-pid").write_text("12345")
460
460
 
461
461
  with patch("pennyfarthing_scripts.bikerack.launcher.os.kill"):
462
462
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
@@ -465,9 +465,9 @@ class TestStopBikeRack:
465
465
  assert not port_file.exists()
466
466
 
467
467
  def test_deletes_pid_file(self, tmp_path: Path) -> None:
468
- """stop_bikerack should delete .bikerack-pid."""
469
- (tmp_path / ".bikerack-port").write_text("2898")
470
- pid_file = tmp_path / ".bikerack-pid"
468
+ """stop_bikerack should delete .wheelhub-pid."""
469
+ (tmp_path / ".wheelhub-port").write_text("2898")
470
+ pid_file = tmp_path / ".wheelhub-pid"
471
471
  pid_file.write_text("12345")
472
472
 
473
473
  with patch("pennyfarthing_scripts.bikerack.launcher.os.kill"):
@@ -478,8 +478,8 @@ class TestStopBikeRack:
478
478
 
479
479
  def test_returns_success_dict(self, tmp_path: Path) -> None:
480
480
  """stop_bikerack should return {success: True, pid: N, message: str}."""
481
- (tmp_path / ".bikerack-port").write_text("2898")
482
- (tmp_path / ".bikerack-pid").write_text("12345")
481
+ (tmp_path / ".wheelhub-port").write_text("2898")
482
+ (tmp_path / ".wheelhub-pid").write_text("12345")
483
483
 
484
484
  with patch("pennyfarthing_scripts.bikerack.launcher.os.kill"):
485
485
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
@@ -506,8 +506,8 @@ class TestStatus:
506
506
 
507
507
  def test_returns_running_state(self, tmp_path: Path) -> None:
508
508
  """get_status should detect running BikeRack."""
509
- (tmp_path / ".bikerack-port").write_text("2898")
510
- (tmp_path / ".bikerack-pid").write_text("12345")
509
+ (tmp_path / ".wheelhub-port").write_text("2898")
510
+ (tmp_path / ".wheelhub-pid").write_text("12345")
511
511
 
512
512
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
513
513
  result = get_status(tmp_path)
@@ -524,8 +524,8 @@ class TestStatus:
524
524
 
525
525
  def test_includes_dashboard_url(self, tmp_path: Path) -> None:
526
526
  """get_status should include dashboard URL when running."""
527
- (tmp_path / ".bikerack-port").write_text("2898")
528
- (tmp_path / ".bikerack-pid").write_text("12345")
527
+ (tmp_path / ".wheelhub-port").write_text("2898")
528
+ (tmp_path / ".wheelhub-pid").write_text("12345")
529
529
 
530
530
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
531
531
  result = get_status(tmp_path)
@@ -534,8 +534,8 @@ class TestStatus:
534
534
 
535
535
  def test_detects_stale_pid(self, tmp_path: Path) -> None:
536
536
  """get_status should detect stale PID (file exists, process dead)."""
537
- (tmp_path / ".bikerack-port").write_text("2898")
538
- (tmp_path / ".bikerack-pid").write_text("99999")
537
+ (tmp_path / ".wheelhub-port").write_text("2898")
538
+ (tmp_path / ".wheelhub-pid").write_text("99999")
539
539
 
540
540
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=False):
541
541
  result = get_status(tmp_path)
@@ -544,7 +544,7 @@ class TestStatus:
544
544
 
545
545
 
546
546
  # ---------------------------------------------------------------------------
547
- # AC9: Error if already running (.bikerack-port exists with live PID)
547
+ # AC9: Error if already running (.wheelhub-port exists with live PID)
548
548
  # ---------------------------------------------------------------------------
549
549
 
550
550
 
@@ -553,8 +553,8 @@ class TestAlreadyRunning:
553
553
 
554
554
  def test_detects_running_instance(self, tmp_path: Path) -> None:
555
555
  """is_already_running should return True when port file + live PID."""
556
- (tmp_path / ".bikerack-port").write_text("2898")
557
- (tmp_path / ".bikerack-pid").write_text("12345")
556
+ (tmp_path / ".wheelhub-port").write_text("2898")
557
+ (tmp_path / ".wheelhub-pid").write_text("12345")
558
558
 
559
559
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
560
560
  running, pid, port = is_already_running(tmp_path)
@@ -573,8 +573,8 @@ class TestAlreadyRunning:
573
573
 
574
574
  def test_not_running_when_stale_pid(self, tmp_path: Path) -> None:
575
575
  """is_already_running should return False when PID is dead (stale)."""
576
- (tmp_path / ".bikerack-port").write_text("2898")
577
- (tmp_path / ".bikerack-pid").write_text("99999")
576
+ (tmp_path / ".wheelhub-port").write_text("2898")
577
+ (tmp_path / ".wheelhub-pid").write_text("99999")
578
578
 
579
579
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=False):
580
580
  running, pid, port = is_already_running(tmp_path)
@@ -583,8 +583,8 @@ class TestAlreadyRunning:
583
583
 
584
584
  def test_cleans_stale_files(self, tmp_path: Path) -> None:
585
585
  """is_already_running should clean up stale files when PID is dead."""
586
- port_file = tmp_path / ".bikerack-port"
587
- pid_file = tmp_path / ".bikerack-pid"
586
+ port_file = tmp_path / ".wheelhub-port"
587
+ pid_file = tmp_path / ".wheelhub-pid"
588
588
  port_file.write_text("2898")
589
589
  pid_file.write_text("99999")
590
590
 
@@ -605,8 +605,8 @@ class TestExitCodes:
605
605
 
606
606
  def test_exit_code_2_when_already_running(self, tmp_path: Path) -> None:
607
607
  """Start should raise SystemExit(2) when already running."""
608
- (tmp_path / ".bikerack-port").write_text("2898")
609
- (tmp_path / ".bikerack-pid").write_text("12345")
608
+ (tmp_path / ".wheelhub-port").write_text("2898")
609
+ (tmp_path / ".wheelhub-pid").write_text("12345")
610
610
 
611
611
  with patch("pennyfarthing_scripts.bikerack.launcher.is_process_alive", return_value=True):
612
612
  with patch("pennyfarthing_scripts.bikerack.launcher.is_already_running",
@@ -736,8 +736,8 @@ class TestCleanupFiles:
736
736
  """Utility: cleanup_files removes port and PID files."""
737
737
 
738
738
  def test_removes_port_file(self, tmp_path: Path) -> None:
739
- """cleanup_files should remove .bikerack-port."""
740
- port_file = tmp_path / ".bikerack-port"
739
+ """cleanup_files should remove .wheelhub-port."""
740
+ port_file = tmp_path / ".wheelhub-port"
741
741
  port_file.write_text("2898")
742
742
 
743
743
  cleanup_files(tmp_path)
@@ -745,8 +745,8 @@ class TestCleanupFiles:
745
745
  assert not port_file.exists()
746
746
 
747
747
  def test_removes_pid_file(self, tmp_path: Path) -> None:
748
- """cleanup_files should remove .bikerack-pid."""
749
- pid_file = tmp_path / ".bikerack-pid"
748
+ """cleanup_files should remove .wheelhub-pid."""
749
+ pid_file = tmp_path / ".wheelhub-pid"
750
750
  pid_file.write_text("12345")
751
751
 
752
752
  cleanup_files(tmp_path)
@@ -764,7 +764,7 @@ class TestReadPortFile:
764
764
 
765
765
  def test_reads_port(self, tmp_path: Path) -> None:
766
766
  """read_port_file should return port as integer."""
767
- (tmp_path / ".bikerack-port").write_text("2898")
767
+ (tmp_path / ".wheelhub-port").write_text("2898")
768
768
 
769
769
  result = read_port_file(tmp_path)
770
770
 
@@ -778,7 +778,7 @@ class TestReadPortFile:
778
778
 
779
779
  def test_handles_trailing_whitespace(self, tmp_path: Path) -> None:
780
780
  """read_port_file should handle trailing newlines/spaces."""
781
- (tmp_path / ".bikerack-port").write_text("2898\n")
781
+ (tmp_path / ".wheelhub-port").write_text("2898\n")
782
782
 
783
783
  result = read_port_file(tmp_path)
784
784