@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.
- package/README.md +8 -8
- package/package.json +16 -14
- package/packages/core/dist/cli/utils/constants.d.ts +1 -1
- package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/constants.js +2 -1
- package/packages/core/dist/cli/utils/constants.js.map +1 -1
- package/packages/core/dist/consultation/dialogue-manager.d.ts +75 -0
- package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.js +334 -0
- package/packages/core/dist/consultation/dialogue-manager.js.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.test.d.ts +19 -0
- package/packages/core/dist/consultation/dialogue-manager.test.d.ts.map +1 -0
- package/packages/core/dist/consultation/dialogue-manager.test.js +444 -0
- package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -0
- package/packages/core/dist/server/api/git.d.ts +13 -1
- package/packages/core/dist/server/api/git.d.ts.map +1 -1
- package/packages/core/dist/server/api/git.js +53 -34
- package/packages/core/dist/server/api/git.js.map +1 -1
- package/packages/core/dist/server/otlp-receiver.d.ts +16 -11
- package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
- package/packages/core/dist/server/otlp-receiver.js +185 -24
- package/packages/core/dist/server/otlp-receiver.js.map +1 -1
- package/packages/core/dist/server/otlp-receiver.test.d.ts +21 -0
- package/packages/core/dist/server/otlp-receiver.test.d.ts.map +1 -0
- package/packages/core/dist/server/otlp-receiver.test.js +446 -0
- package/packages/core/dist/server/otlp-receiver.test.js.map +1 -0
- package/packages/core/dist/shared/portrait-resolver.d.ts +9 -0
- package/packages/core/dist/shared/portrait-resolver.d.ts.map +1 -1
- package/packages/core/dist/shared/portrait-resolver.js +27 -0
- package/packages/core/dist/shared/portrait-resolver.js.map +1 -1
- package/packages/core/dist/shared/portrait-resolver.test.js +47 -1
- package/packages/core/dist/shared/portrait-resolver.test.js.map +1 -1
- package/packages/core/dist/shared/skill-search.test.js +2 -2
- package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts +13 -0
- package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts.map +1 -0
- package/packages/core/dist/shared/tandem-portrait-inventory.test.js +126 -0
- package/packages/core/dist/shared/tandem-portrait-inventory.test.js.map +1 -0
- package/pennyfarthing-dist/agents/dev.md +1 -1
- package/pennyfarthing-dist/agents/reviewer.md +1 -1
- package/pennyfarthing-dist/agents/sm-setup.md +1 -1
- package/pennyfarthing-dist/agents/sm.md +2 -2
- package/pennyfarthing-dist/agents/tea.md +1 -1
- package/pennyfarthing-dist/agents/testing-runner.md +2 -1
- package/pennyfarthing-dist/commands/pf-chore.md +2 -2
- package/pennyfarthing-dist/commands/pf-standalone.md +7 -2
- package/pennyfarthing-dist/guides/agent-behavior.md +1 -1
- package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -1
- package/pennyfarthing-dist/guides/bikerack.md +3 -3
- package/pennyfarthing-dist/guides/hooks.md +1 -1
- package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
- package/pennyfarthing-dist/guides/xml-tags.md +2 -2
- package/pennyfarthing-dist/scripts/README.md +1 -1
- package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
- package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
- package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +322 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
- package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
- package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
- package/pennyfarthing-dist/scripts/git/README.md +24 -14
- package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +5 -266
- package/pennyfarthing-dist/scripts/git/git-status-all.sh +5 -151
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +6 -144
- package/pennyfarthing-dist/scripts/git/release.sh +0 -0
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +5 -496
- package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/README.md +1 -1
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +1 -1
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +9 -11
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +1 -1
- package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
- package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
- package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
- package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
- package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
- package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +76 -0
- package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
- package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
- package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
- package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +4 -221
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +5 -13
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +4 -123
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +4 -33
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +4 -156
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +4 -131
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +4 -249
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +4 -160
- package/pennyfarthing-dist/skills/pf-bc/usage.md +1 -1
- package/pennyfarthing-dist/skills/pf-jira/examples.md +5 -2
- package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
- package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -16
- package/pennyfarthing-dist/skills/pf-workflow/skill.md +9 -12
- package/pennyfarthing-dist/skills/pf-workflow/usage.md +33 -8
- package/pennyfarthing-dist/workflows/bdd-tandem.yaml +18 -6
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
- package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
- package/pennyfarthing-dist/workflows/review-tandem.yaml +65 -0
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +16 -8
- package/pennyfarthing_scripts/CLAUDE.md +26 -4
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/cli.py +3 -5
- package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/background_panel.py +86 -5
- package/pennyfarthing_scripts/bikerack/base_panel.py +62 -0
- package/pennyfarthing_scripts/bikerack/changed_panel.py +32 -28
- package/pennyfarthing_scripts/bikerack/cli.py +10 -11
- package/pennyfarthing_scripts/bikerack/debug_panel.py +31 -1
- package/pennyfarthing_scripts/bikerack/diffs_panel.py +74 -17
- package/pennyfarthing_scripts/bikerack/git_panel.py +103 -33
- package/pennyfarthing_scripts/bikerack/launcher.py +15 -15
- package/pennyfarthing_scripts/bikerack/progress_panel.py +315 -0
- package/pennyfarthing_scripts/bikerack/sprint_panel.py +158 -26
- package/pennyfarthing_scripts/bikerack/tui.py +336 -30
- package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
- package/pennyfarthing_scripts/cli.py +37 -65
- package/pennyfarthing_scripts/consultation/__init__.py +1 -0
- package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/cli.py +149 -0
- package/pennyfarthing_scripts/consultation/dialogue_manager.py +417 -0
- package/pennyfarthing_scripts/context.py +3 -3
- package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__init__.py +12 -1
- package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git/create_branches.py +3 -4
- package/pennyfarthing_scripts/git/hooks_installer.py +152 -0
- package/pennyfarthing_scripts/git/repos.py +196 -0
- package/pennyfarthing_scripts/git/status_all.py +27 -11
- package/pennyfarthing_scripts/git/worktree.py +302 -0
- package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/git_group/cli.py +143 -40
- package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/complete_phase.py +12 -0
- package/pennyfarthing_scripts/handoff/resolve_gate.py +5 -14
- package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
- package/pennyfarthing_scripts/hooks.py +3 -17
- package/pennyfarthing_scripts/pretooluse_hook.py +1 -1
- package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/heatmap.py +655 -0
- package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/session_start_hook.py +1 -1
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/loader.py +15 -1
- package/pennyfarthing_scripts/sprint/story_finish.py +14 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_bikerack.py +51 -51
- package/pennyfarthing_scripts/tests/test_dialogue_manager.py +811 -0
- package/pennyfarthing_scripts/tests/test_handoff_cli.py +16 -11
- package/pennyfarthing_scripts/tests/test_workflow_check.py +2 -3
- package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/validate/adapters/tandem_awareness.py +254 -0
- package/pennyfarthing_scripts/validate/cli.py +17 -5
- package/pennyfarthing_scripts/workflow/__init__.py +40 -0
- package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/cli.py +1099 -0
- package/pennyfarthing_scripts/workflow/helpers.py +241 -0
- package/pennyfarthing_scripts/{workflow.py → workflow/scale.py} +0 -104
- package/pennyfarthing_scripts/workflow/state.py +112 -0
- package/scripts/README.md +41 -0
- package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -91
- package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -163
- package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -138
- package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -273
- 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
|
-
#
|
|
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"])
|
package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc
CHANGED
|
Binary file
|
|
@@ -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 .
|
|
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 .
|
|
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 (.
|
|
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 .
|
|
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 / ".
|
|
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 / ".
|
|
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 / ".
|
|
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 .
|
|
344
|
-
port_file = tmp_path / ".
|
|
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(), ".
|
|
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 .
|
|
370
|
-
pid_file = tmp_path / ".
|
|
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(), ".
|
|
392
|
+
assert not pid_file.exists(), ".wheelhub-pid should be deleted by cleanup"
|
|
393
393
|
|
|
394
394
|
|
|
395
395
|
# ---------------------------------------------------------------------------
|
|
396
|
-
# AC6: Writes .
|
|
396
|
+
# AC6: Writes .wheelhub-pid after spawning WheelHub
|
|
397
397
|
# ---------------------------------------------------------------------------
|
|
398
398
|
|
|
399
399
|
|
|
400
400
|
class TestPidFile:
|
|
401
|
-
"""AC6: write_pid_file writes .
|
|
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 / ".
|
|
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 / ".
|
|
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 .
|
|
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 / ".
|
|
430
|
+
expected = tmp_path / ".wheelhub-pid"
|
|
431
431
|
assert expected.exists()
|
|
432
|
-
assert expected.name == ".
|
|
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 / ".
|
|
447
|
-
(tmp_path / ".
|
|
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 .
|
|
457
|
-
port_file = tmp_path / ".
|
|
456
|
+
"""stop_bikerack should delete .wheelhub-port."""
|
|
457
|
+
port_file = tmp_path / ".wheelhub-port"
|
|
458
458
|
port_file.write_text("2898")
|
|
459
|
-
(tmp_path / ".
|
|
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 .
|
|
469
|
-
(tmp_path / ".
|
|
470
|
-
pid_file = tmp_path / ".
|
|
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 / ".
|
|
482
|
-
(tmp_path / ".
|
|
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 / ".
|
|
510
|
-
(tmp_path / ".
|
|
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 / ".
|
|
528
|
-
(tmp_path / ".
|
|
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 / ".
|
|
538
|
-
(tmp_path / ".
|
|
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 (.
|
|
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 / ".
|
|
557
|
-
(tmp_path / ".
|
|
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 / ".
|
|
577
|
-
(tmp_path / ".
|
|
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 / ".
|
|
587
|
-
pid_file = tmp_path / ".
|
|
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 / ".
|
|
609
|
-
(tmp_path / ".
|
|
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 .
|
|
740
|
-
port_file = tmp_path / ".
|
|
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 .
|
|
749
|
-
pid_file = tmp_path / ".
|
|
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 / ".
|
|
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 / ".
|
|
781
|
+
(tmp_path / ".wheelhub-port").write_text("2898\n")
|
|
782
782
|
|
|
783
783
|
result = read_port_file(tmp_path)
|
|
784
784
|
|