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