@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
|
@@ -295,21 +295,26 @@ class TestResolveGateReady:
|
|
|
295
295
|
|
|
296
296
|
|
|
297
297
|
class TestResolveGateBlocked:
|
|
298
|
-
"""AC1 + AC5: resolve-gate
|
|
298
|
+
"""AC1 + AC5: resolve-gate no longer blocks on missing assessment.
|
|
299
299
|
|
|
300
|
-
|
|
300
|
+
Assessment guard moved to complete_phase to prevent race conditions
|
|
301
|
+
where agents call resolve-gate before writing their assessment.
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
def test_missing_assessment_resolve_gate_still_ready(
|
|
301
305
|
self, project: Path, session_without_assessment: Path
|
|
302
306
|
) -> None:
|
|
303
|
-
"""
|
|
307
|
+
"""resolve-gate returns ready regardless of assessment (guard moved to complete-phase)."""
|
|
304
308
|
result = resolve_gate("105-1", "tdd", "green", project_root=project)
|
|
305
|
-
assert result["status"] == "
|
|
309
|
+
assert result["status"] == "ready"
|
|
306
310
|
|
|
307
|
-
def
|
|
311
|
+
def test_missing_assessment_complete_phase_blocks(
|
|
308
312
|
self, project: Path, session_without_assessment: Path
|
|
309
313
|
) -> None:
|
|
310
|
-
"""
|
|
311
|
-
result =
|
|
312
|
-
assert result["
|
|
314
|
+
"""complete-phase blocks when no assessment found in session file."""
|
|
315
|
+
result = complete_phase("105-1", "tdd", "green", "review", "tests_pass", project)
|
|
316
|
+
assert result["status"] == "error"
|
|
317
|
+
assert "assessment" in result["error"].lower()
|
|
313
318
|
|
|
314
319
|
def test_blocked_exit_code_one(self, runner: CliRunner) -> None:
|
|
315
320
|
"""AC5: Exit code 1 when gate resolves to blocked."""
|
|
@@ -383,10 +388,10 @@ class TestResolveGateErrors:
|
|
|
383
388
|
result = resolve_gate("105-1", "tdd", "nonexistent", project_root=project)
|
|
384
389
|
assert result["status"] == "error" or result.get("error") is not None
|
|
385
390
|
|
|
386
|
-
def
|
|
387
|
-
"""
|
|
391
|
+
def test_missing_session_file_resolve_gate_ready(self, project: Path) -> None:
|
|
392
|
+
"""resolve-gate returns ready even without session file (guard moved to complete-phase)."""
|
|
388
393
|
result = resolve_gate("105-1", "tdd", "green", project_root=project)
|
|
389
|
-
assert result["status"]
|
|
394
|
+
assert result["status"] == "ready"
|
|
390
395
|
|
|
391
396
|
|
|
392
397
|
class TestResolveGateOutputContract:
|
|
@@ -24,9 +24,8 @@ from click.testing import CliRunner
|
|
|
24
24
|
from pennyfarthing_scripts.cli import cli
|
|
25
25
|
from pennyfarthing_scripts.workflow import get_workflow_state
|
|
26
26
|
|
|
27
|
-
# Mock path:
|
|
28
|
-
|
|
29
|
-
MOCK_PATH = "pennyfarthing_scripts.workflow.get_workflow_state"
|
|
27
|
+
# Mock path: workflow.cli imports from workflow.state, so mock at the source module
|
|
28
|
+
MOCK_PATH = "pennyfarthing_scripts.workflow.state.get_workflow_state"
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class TestWorkflowCheckCLI:
|
|
Binary file
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""Tandem awareness validator adapter.
|
|
2
|
+
|
|
3
|
+
Validates that agent definitions include proper tandem consultation sections
|
|
4
|
+
per ADR-0012 and the tandem-consultation protocol.
|
|
5
|
+
|
|
6
|
+
Story: MSSCI-14499 (86-4)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from pennyfarthing_scripts.validate import ValidateReport
|
|
15
|
+
|
|
16
|
+
# Regex to extract <tandem-consultation> section content
|
|
17
|
+
_TANDEM_RE = re.compile(
|
|
18
|
+
r"<tandem-consultation>(.*?)</tandem-consultation>", re.DOTALL
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Regex to extract role from heading: ## Tandem Consultation (Leader + Partner)
|
|
22
|
+
_HEADING_RE = re.compile(r"##\s+Tandem Consultation\s*\(([^)]+)\)")
|
|
23
|
+
|
|
24
|
+
# Required response format fields for partner agents
|
|
25
|
+
_PARTNER_FIELDS = {
|
|
26
|
+
"Recommendation": "**Recommendation:**",
|
|
27
|
+
"Rationale": "**Rationale:**",
|
|
28
|
+
"Watch-Out-For": "**Watch-Out-For:**",
|
|
29
|
+
"Confidence": "**Confidence:**",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# ADR-0012 high-value pairings (leader, partner) — directional
|
|
33
|
+
ADR_0012_PAIRINGS: list[tuple[str, str]] = [
|
|
34
|
+
("dev", "architect"),
|
|
35
|
+
("dev", "tea"),
|
|
36
|
+
("reviewer", "architect"),
|
|
37
|
+
("dev", "devops"),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _extract_tandem_section(content: str) -> str | None:
|
|
42
|
+
"""Extract content between <tandem-consultation> tags."""
|
|
43
|
+
m = _TANDEM_RE.search(content)
|
|
44
|
+
return m.group(1) if m else None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def classify_tandem_roles(
|
|
48
|
+
agents_dir: Path,
|
|
49
|
+
) -> tuple[list[Path], list[Path]]:
|
|
50
|
+
"""Classify agent files into leaders and partners based on tandem sections.
|
|
51
|
+
|
|
52
|
+
Classification is based on the heading within the tandem-consultation section:
|
|
53
|
+
- "(Leader)" → leader only
|
|
54
|
+
- "(Partner)" → partner only
|
|
55
|
+
- "(Leader + Partner)" → both
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
(leaders, partners) — two lists of Path objects. An agent may appear in both.
|
|
59
|
+
"""
|
|
60
|
+
leaders: list[Path] = []
|
|
61
|
+
partners: list[Path] = []
|
|
62
|
+
|
|
63
|
+
for f in sorted(agents_dir.glob("*.md")):
|
|
64
|
+
if f.name == "README.md":
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
content = f.read_text()
|
|
68
|
+
section = _extract_tandem_section(content)
|
|
69
|
+
if section is None:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
# Match heading like "## Tandem Consultation (Leader + Partner)"
|
|
73
|
+
heading = _HEADING_RE.search(section)
|
|
74
|
+
if heading:
|
|
75
|
+
role = heading.group(1)
|
|
76
|
+
if "Leader" in role:
|
|
77
|
+
leaders.append(f)
|
|
78
|
+
if "Partner" in role:
|
|
79
|
+
partners.append(f)
|
|
80
|
+
|
|
81
|
+
return leaders, partners
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def validate_leader_tandem(path: Path) -> tuple[list[str], list[str]]:
|
|
85
|
+
"""Validate leader tandem consultation section content.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
(errors, warnings) — two lists of message strings.
|
|
89
|
+
"""
|
|
90
|
+
errors: list[str] = []
|
|
91
|
+
warnings: list[str] = []
|
|
92
|
+
content = path.read_text()
|
|
93
|
+
section = _extract_tandem_section(content)
|
|
94
|
+
|
|
95
|
+
if section is None:
|
|
96
|
+
errors.append("Missing <tandem-consultation> section")
|
|
97
|
+
return errors, warnings
|
|
98
|
+
|
|
99
|
+
stripped = section.strip()
|
|
100
|
+
if not stripped:
|
|
101
|
+
errors.append("Empty <tandem-consultation> section — no content")
|
|
102
|
+
return errors, warnings
|
|
103
|
+
|
|
104
|
+
# Workflow phase check — must reference tandem.mode
|
|
105
|
+
if "tandem.mode" not in section:
|
|
106
|
+
errors.append(
|
|
107
|
+
"Leader section must reference workflow phase availability (tandem.mode)"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Request format template (recommended)
|
|
111
|
+
if "request format" not in section.lower():
|
|
112
|
+
warnings.append("Missing request format template in leader section")
|
|
113
|
+
|
|
114
|
+
# Graceful degradation guidance (recommended)
|
|
115
|
+
section_lower = section.lower()
|
|
116
|
+
if not any(term in section_lower for term in ("fail", "degrad", "solo")):
|
|
117
|
+
warnings.append(
|
|
118
|
+
"Missing graceful degradation guidance (what to do if consultation fails)"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return errors, warnings
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def validate_partner_tandem(path: Path) -> tuple[list[str], list[str]]:
|
|
125
|
+
"""Validate partner tandem consultation response guidance.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
(errors, warnings) — two lists of message strings.
|
|
129
|
+
"""
|
|
130
|
+
errors: list[str] = []
|
|
131
|
+
warnings: list[str] = []
|
|
132
|
+
content = path.read_text()
|
|
133
|
+
section = _extract_tandem_section(content)
|
|
134
|
+
|
|
135
|
+
if section is None:
|
|
136
|
+
errors.append("Missing <tandem-consultation> section")
|
|
137
|
+
return errors, warnings
|
|
138
|
+
|
|
139
|
+
# Check for required response format fields
|
|
140
|
+
missing = [
|
|
141
|
+
name for name, marker in _PARTNER_FIELDS.items() if marker not in section
|
|
142
|
+
]
|
|
143
|
+
if missing:
|
|
144
|
+
errors.append(
|
|
145
|
+
f"Partner response format missing required fields: {', '.join(missing)}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return errors, warnings
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def validate_pairings_documented(
|
|
152
|
+
leader_names: set[str],
|
|
153
|
+
partner_names: set[str],
|
|
154
|
+
pairings: list[tuple[str, str]],
|
|
155
|
+
) -> tuple[list[tuple[str, str]], list[tuple[str, str]]]:
|
|
156
|
+
"""Check which ADR-0012 pairings are covered by agent tandem sections.
|
|
157
|
+
|
|
158
|
+
Directional check: verifies the leader agent is classified as a leader
|
|
159
|
+
and the partner agent is classified as a partner.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
(covered, missing) — two lists of (leader, partner) tuples.
|
|
163
|
+
"""
|
|
164
|
+
covered: list[tuple[str, str]] = []
|
|
165
|
+
missing: list[tuple[str, str]] = []
|
|
166
|
+
|
|
167
|
+
for leader, partner in pairings:
|
|
168
|
+
if leader in leader_names and partner in partner_names:
|
|
169
|
+
covered.append((leader, partner))
|
|
170
|
+
else:
|
|
171
|
+
missing.append((leader, partner))
|
|
172
|
+
|
|
173
|
+
return covered, missing
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def run(
|
|
177
|
+
root: Path, *, fix: bool = False, strict: bool = False
|
|
178
|
+
) -> ValidateReport:
|
|
179
|
+
"""Validate agent tandem awareness sections."""
|
|
180
|
+
report = ValidateReport(validator="tandem-awareness")
|
|
181
|
+
agents_dir = root / "pennyfarthing-dist" / "agents"
|
|
182
|
+
|
|
183
|
+
if not agents_dir.is_dir():
|
|
184
|
+
report.details.append("[ERROR] agents directory not found")
|
|
185
|
+
report.errors += 1
|
|
186
|
+
return report
|
|
187
|
+
|
|
188
|
+
leaders, partners = classify_tandem_roles(agents_dir)
|
|
189
|
+
|
|
190
|
+
for path in leaders:
|
|
191
|
+
file_errors, file_warnings = validate_leader_tandem(path)
|
|
192
|
+
|
|
193
|
+
for e in file_errors:
|
|
194
|
+
report.errors += 1
|
|
195
|
+
report.details.append(f"[ERROR] {path.name}: {e}")
|
|
196
|
+
|
|
197
|
+
for w in file_warnings:
|
|
198
|
+
if strict:
|
|
199
|
+
report.errors += 1
|
|
200
|
+
report.details.append(f"[ERROR] {path.name}: {w}")
|
|
201
|
+
else:
|
|
202
|
+
report.warnings += 1
|
|
203
|
+
report.details.append(f"[WARN] {path.name}: {w}")
|
|
204
|
+
|
|
205
|
+
if not file_errors:
|
|
206
|
+
report.passed += 1
|
|
207
|
+
|
|
208
|
+
for path in partners:
|
|
209
|
+
file_errors, file_warnings = validate_partner_tandem(path)
|
|
210
|
+
|
|
211
|
+
for e in file_errors:
|
|
212
|
+
report.errors += 1
|
|
213
|
+
report.details.append(f"[ERROR] {path.name}: {e}")
|
|
214
|
+
|
|
215
|
+
for w in file_warnings:
|
|
216
|
+
if strict:
|
|
217
|
+
report.errors += 1
|
|
218
|
+
report.details.append(f"[ERROR] {path.name}: {w}")
|
|
219
|
+
else:
|
|
220
|
+
report.warnings += 1
|
|
221
|
+
report.details.append(f"[WARN] {path.name}: {w}")
|
|
222
|
+
|
|
223
|
+
if not file_errors:
|
|
224
|
+
report.passed += 1
|
|
225
|
+
|
|
226
|
+
# Validate ADR-0012 pairings (directional)
|
|
227
|
+
leader_names = {f.stem for f in leaders}
|
|
228
|
+
partner_names = {f.stem for f in partners}
|
|
229
|
+
covered, missing_pairings = validate_pairings_documented(
|
|
230
|
+
leader_names, partner_names, ADR_0012_PAIRINGS
|
|
231
|
+
)
|
|
232
|
+
for leader_name, partner_name in missing_pairings:
|
|
233
|
+
report.errors += 1
|
|
234
|
+
report.details.append(
|
|
235
|
+
f"[ERROR] ADR-0012 pairing {leader_name}\u2192{partner_name} not covered "
|
|
236
|
+
f"(leader or partner missing tandem section with correct role)"
|
|
237
|
+
)
|
|
238
|
+
if covered:
|
|
239
|
+
report.passed += 1
|
|
240
|
+
|
|
241
|
+
# Detect agents with tandem section but no matching heading
|
|
242
|
+
classified_stems = leader_names | partner_names
|
|
243
|
+
for f in sorted(agents_dir.glob("*.md")):
|
|
244
|
+
if f.name == "README.md":
|
|
245
|
+
continue
|
|
246
|
+
content = f.read_text()
|
|
247
|
+
if _extract_tandem_section(content) is not None and f.stem not in classified_stems:
|
|
248
|
+
report.warnings += 1
|
|
249
|
+
report.details.append(
|
|
250
|
+
f"[WARN] {f.name}: has <tandem-consultation> section but no "
|
|
251
|
+
f"matching '## Tandem Consultation (Role)' heading"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return report
|
|
@@ -22,6 +22,7 @@ VALIDATORS = {
|
|
|
22
22
|
"agent": "pennyfarthing_scripts.validate.adapters.agent",
|
|
23
23
|
"workflow": "pennyfarthing_scripts.validate.adapters.workflow",
|
|
24
24
|
"skill-command": "pennyfarthing_scripts.validate.adapters.skill_command",
|
|
25
|
+
"tandem-awareness": "pennyfarthing_scripts.validate.adapters.tandem_awareness",
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
|
|
@@ -83,11 +84,12 @@ def validate(ctx, fix: bool, strict: bool):
|
|
|
83
84
|
|
|
84
85
|
\b
|
|
85
86
|
Validators:
|
|
86
|
-
sprint
|
|
87
|
-
schema
|
|
88
|
-
agent
|
|
89
|
-
workflow
|
|
90
|
-
skill-command
|
|
87
|
+
sprint - Sprint YAML (epics, initiatives, future, current-sprint)
|
|
88
|
+
schema - XML schema (sessions, skills, workflow steps)
|
|
89
|
+
agent - Agent definitions (required sections, model values, subagent refs)
|
|
90
|
+
workflow - Workflow definitions (phased/stepped/procedural structure)
|
|
91
|
+
skill-command - Skill registry and command files (prefix, deprecated, cross-ref)
|
|
92
|
+
tandem-awareness - Agent tandem consultation sections (ADR-0012 pairings)
|
|
91
93
|
"""
|
|
92
94
|
ctx.ensure_object(dict)
|
|
93
95
|
ctx.obj["fix"] = fix
|
|
@@ -150,3 +152,13 @@ def validate_skill_command(ctx):
|
|
|
150
152
|
_print_reports([report])
|
|
151
153
|
if not report.success:
|
|
152
154
|
raise SystemExit(1)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@validate.command("tandem-awareness")
|
|
158
|
+
@click.pass_context
|
|
159
|
+
def validate_tandem_awareness(ctx):
|
|
160
|
+
"""Validate agent tandem consultation sections (ADR-0012 pairings, roles)."""
|
|
161
|
+
report = _run_validator("tandem-awareness", fix=ctx.obj["fix"], strict=ctx.obj["strict"])
|
|
162
|
+
_print_reports([report])
|
|
163
|
+
if not report.success:
|
|
164
|
+
raise SystemExit(1)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Workflow package — scale levels, phase ownership, and workflow CLI commands.
|
|
3
|
+
|
|
4
|
+
Re-exports all public symbols for backward compatibility with
|
|
5
|
+
`from pennyfarthing_scripts.workflow import ...`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pennyfarthing_scripts.workflow.scale import (
|
|
9
|
+
SCALE_LEVELS,
|
|
10
|
+
detect_scale_level,
|
|
11
|
+
determine_scale_level,
|
|
12
|
+
get_required_artifacts,
|
|
13
|
+
get_scale_level_info,
|
|
14
|
+
get_workflow_for_scale_level,
|
|
15
|
+
scale_level_from_story_count,
|
|
16
|
+
)
|
|
17
|
+
from pennyfarthing_scripts.workflow.state import (
|
|
18
|
+
TDD_PHASE_OWNERS,
|
|
19
|
+
TRIVIAL_PHASE_OWNERS,
|
|
20
|
+
WORKFLOW_PHASES,
|
|
21
|
+
get_phase_owner,
|
|
22
|
+
get_workflow_state,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Scale
|
|
27
|
+
"SCALE_LEVELS",
|
|
28
|
+
"detect_scale_level",
|
|
29
|
+
"determine_scale_level",
|
|
30
|
+
"get_required_artifacts",
|
|
31
|
+
"get_scale_level_info",
|
|
32
|
+
"get_workflow_for_scale_level",
|
|
33
|
+
"scale_level_from_story_count",
|
|
34
|
+
# State
|
|
35
|
+
"TDD_PHASE_OWNERS",
|
|
36
|
+
"TRIVIAL_PHASE_OWNERS",
|
|
37
|
+
"WORKFLOW_PHASES",
|
|
38
|
+
"get_phase_owner",
|
|
39
|
+
"get_workflow_state",
|
|
40
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|