@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,540 @@
|
|
|
1
|
+
"""Tests for gate file migration — Story 108-1.
|
|
2
|
+
|
|
3
|
+
Epic: 108 (Full Migration & Cleanup)
|
|
4
|
+
Story: 108-1 — Migrate tests-fail and approval gates to files
|
|
5
|
+
|
|
6
|
+
Tests the creation of tests-fail.md and approval.md gate files,
|
|
7
|
+
and the addition of gate.file entries to workflow YAMLs.
|
|
8
|
+
|
|
9
|
+
Acceptance Criteria:
|
|
10
|
+
- [AC1] Gate files tests-fail.md and approval.md exist with correct structure
|
|
11
|
+
- [AC2] Workflow YAMLs have gate.file entries for tests_fail and approval phases
|
|
12
|
+
- [AC3] Gate files resolve via resolve_gate_file() and validate correctly
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
import yaml
|
|
22
|
+
|
|
23
|
+
from pennyfarthing_scripts.handoff.gate_file import resolve_gate_file
|
|
24
|
+
from pennyfarthing_scripts.handoff.resolve_gate import resolve_gate
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# Paths
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
DIST_DIR = Path(__file__).resolve().parents[2] / "pennyfarthing-dist"
|
|
31
|
+
GATES_DIR = DIST_DIR / "gates"
|
|
32
|
+
WORKFLOWS_DIR = DIST_DIR / "workflows"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Helpers
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _read_gate(name: str) -> str:
|
|
41
|
+
"""Read a gate file by name from pennyfarthing-dist/gates/."""
|
|
42
|
+
path = GATES_DIR / f"{name}.md"
|
|
43
|
+
assert path.exists(), f"Gate file not found: {path}"
|
|
44
|
+
return path.read_text()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _load_workflow(name: str) -> dict:
|
|
48
|
+
"""Load a workflow YAML from pennyfarthing-dist/workflows/."""
|
|
49
|
+
path = WORKFLOWS_DIR / f"{name}.yaml"
|
|
50
|
+
assert path.exists(), f"Workflow not found: {path}"
|
|
51
|
+
return yaml.safe_load(path.read_text())
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _get_phase(workflow_data: dict, phase_name: str) -> dict:
|
|
55
|
+
"""Extract a phase dict from workflow data by name."""
|
|
56
|
+
for phase in workflow_data["workflow"]["phases"]:
|
|
57
|
+
if phase["name"] == phase_name:
|
|
58
|
+
return phase
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"Phase '{phase_name}' not found in workflow "
|
|
61
|
+
f"'{workflow_data['workflow']['name']}'"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _get_gate_file(workflow_data: dict, phase_name: str) -> str | None:
|
|
66
|
+
"""Get the gate.file value for a phase, or None."""
|
|
67
|
+
phase = _get_phase(workflow_data, phase_name)
|
|
68
|
+
gate = phase.get("gate") or {}
|
|
69
|
+
return gate.get("file")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_gate_type(workflow_data: dict, phase_name: str) -> str | None:
|
|
73
|
+
"""Get the gate.type value for a phase, or None."""
|
|
74
|
+
phase = _get_phase(workflow_data, phase_name)
|
|
75
|
+
gate = phase.get("gate") or {}
|
|
76
|
+
return gate.get("type")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ===========================================================================
|
|
80
|
+
# AC1: Gate files exist with correct structure
|
|
81
|
+
# ===========================================================================
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class TestTestsFailGateFile:
|
|
85
|
+
"""AC1: tests-fail.md gate file exists and has correct structure."""
|
|
86
|
+
|
|
87
|
+
def test_file_exists(self) -> None:
|
|
88
|
+
"""AC1: tests-fail.md must exist in pennyfarthing-dist/gates/."""
|
|
89
|
+
assert (GATES_DIR / "tests-fail.md").exists()
|
|
90
|
+
|
|
91
|
+
def test_has_gate_root_element(self) -> None:
|
|
92
|
+
"""AC1: File must have <gate> root element."""
|
|
93
|
+
content = _read_gate("tests-fail")
|
|
94
|
+
assert "<gate " in content, "Missing <gate> root element"
|
|
95
|
+
assert "</gate>" in content, "Missing closing </gate> tag"
|
|
96
|
+
|
|
97
|
+
def test_gate_name_attribute(self) -> None:
|
|
98
|
+
"""AC1: Gate name must be 'tests-fail'."""
|
|
99
|
+
content = _read_gate("tests-fail")
|
|
100
|
+
assert 'name="tests-fail"' in content
|
|
101
|
+
|
|
102
|
+
def test_gate_model_attribute(self) -> None:
|
|
103
|
+
"""AC1: Gate model must be 'haiku'."""
|
|
104
|
+
content = _read_gate("tests-fail")
|
|
105
|
+
assert 'model="haiku"' in content
|
|
106
|
+
|
|
107
|
+
def test_has_purpose_section(self) -> None:
|
|
108
|
+
"""AC1: Must have <purpose> section."""
|
|
109
|
+
content = _read_gate("tests-fail")
|
|
110
|
+
assert "<purpose>" in content
|
|
111
|
+
assert "</purpose>" in content
|
|
112
|
+
|
|
113
|
+
def test_has_pass_section(self) -> None:
|
|
114
|
+
"""AC1: Must have <pass> section."""
|
|
115
|
+
content = _read_gate("tests-fail")
|
|
116
|
+
assert "<pass>" in content
|
|
117
|
+
assert "</pass>" in content
|
|
118
|
+
|
|
119
|
+
def test_has_fail_section(self) -> None:
|
|
120
|
+
"""AC1: Must have <fail> section."""
|
|
121
|
+
content = _read_gate("tests-fail")
|
|
122
|
+
assert "<fail>" in content
|
|
123
|
+
assert "</fail>" in content
|
|
124
|
+
|
|
125
|
+
def test_purpose_mentions_failing_tests(self) -> None:
|
|
126
|
+
"""AC1: Purpose should describe checking for failing/RED tests."""
|
|
127
|
+
content = _read_gate("tests-fail")
|
|
128
|
+
purpose = re.search(
|
|
129
|
+
r"<purpose>(.*?)</purpose>", content, re.DOTALL
|
|
130
|
+
)
|
|
131
|
+
assert purpose is not None
|
|
132
|
+
purpose_text = purpose.group(1).lower()
|
|
133
|
+
assert "fail" in purpose_text or "red" in purpose_text
|
|
134
|
+
|
|
135
|
+
def test_pass_section_mentions_test_coverage(self) -> None:
|
|
136
|
+
"""AC1: Pass section should reference test coverage of acceptance criteria."""
|
|
137
|
+
content = _read_gate("tests-fail")
|
|
138
|
+
pass_section = re.search(r"<pass>(.*?)</pass>", content, re.DOTALL)
|
|
139
|
+
assert pass_section is not None
|
|
140
|
+
pass_text = pass_section.group(1).lower()
|
|
141
|
+
assert "test" in pass_text
|
|
142
|
+
assert "acceptance" in pass_text or "criteria" in pass_text or "committed" in pass_text
|
|
143
|
+
|
|
144
|
+
def test_pass_section_mentions_gate_result(self) -> None:
|
|
145
|
+
"""AC1: Pass section should return GATE_RESULT."""
|
|
146
|
+
content = _read_gate("tests-fail")
|
|
147
|
+
pass_section = re.search(r"<pass>(.*?)</pass>", content, re.DOTALL)
|
|
148
|
+
assert pass_section is not None
|
|
149
|
+
assert "GATE_RESULT" in pass_section.group(1)
|
|
150
|
+
|
|
151
|
+
def test_fail_section_mentions_gate_result(self) -> None:
|
|
152
|
+
"""AC1: Fail section should return GATE_RESULT."""
|
|
153
|
+
content = _read_gate("tests-fail")
|
|
154
|
+
fail_section = re.search(r"<fail>(.*?)</fail>", content, re.DOTALL)
|
|
155
|
+
assert fail_section is not None
|
|
156
|
+
assert "GATE_RESULT" in fail_section.group(1)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class TestApprovalGateFile:
|
|
160
|
+
"""AC1: approval.md gate file exists and has correct structure."""
|
|
161
|
+
|
|
162
|
+
def test_file_exists(self) -> None:
|
|
163
|
+
"""AC1: approval.md must exist in pennyfarthing-dist/gates/."""
|
|
164
|
+
assert (GATES_DIR / "approval.md").exists()
|
|
165
|
+
|
|
166
|
+
def test_has_gate_root_element(self) -> None:
|
|
167
|
+
"""AC1: File must have <gate> root element."""
|
|
168
|
+
content = _read_gate("approval")
|
|
169
|
+
assert "<gate " in content, "Missing <gate> root element"
|
|
170
|
+
assert "</gate>" in content, "Missing closing </gate> tag"
|
|
171
|
+
|
|
172
|
+
def test_gate_name_attribute(self) -> None:
|
|
173
|
+
"""AC1: Gate name must be 'approval'."""
|
|
174
|
+
content = _read_gate("approval")
|
|
175
|
+
assert 'name="approval"' in content
|
|
176
|
+
|
|
177
|
+
def test_gate_model_attribute(self) -> None:
|
|
178
|
+
"""AC1: Gate model must be 'haiku'."""
|
|
179
|
+
content = _read_gate("approval")
|
|
180
|
+
assert 'model="haiku"' in content
|
|
181
|
+
|
|
182
|
+
def test_has_purpose_section(self) -> None:
|
|
183
|
+
"""AC1: Must have <purpose> section."""
|
|
184
|
+
content = _read_gate("approval")
|
|
185
|
+
assert "<purpose>" in content
|
|
186
|
+
assert "</purpose>" in content
|
|
187
|
+
|
|
188
|
+
def test_has_pass_section(self) -> None:
|
|
189
|
+
"""AC1: Must have <pass> section."""
|
|
190
|
+
content = _read_gate("approval")
|
|
191
|
+
assert "<pass>" in content
|
|
192
|
+
assert "</pass>" in content
|
|
193
|
+
|
|
194
|
+
def test_has_fail_section(self) -> None:
|
|
195
|
+
"""AC1: Must have <fail> section."""
|
|
196
|
+
content = _read_gate("approval")
|
|
197
|
+
assert "<fail>" in content
|
|
198
|
+
assert "</fail>" in content
|
|
199
|
+
|
|
200
|
+
def test_purpose_mentions_reviewer(self) -> None:
|
|
201
|
+
"""AC1: Purpose should describe checking reviewer verdict."""
|
|
202
|
+
content = _read_gate("approval")
|
|
203
|
+
purpose = re.search(
|
|
204
|
+
r"<purpose>(.*?)</purpose>", content, re.DOTALL
|
|
205
|
+
)
|
|
206
|
+
assert purpose is not None
|
|
207
|
+
purpose_text = purpose.group(1).lower()
|
|
208
|
+
assert "review" in purpose_text or "verdict" in purpose_text
|
|
209
|
+
|
|
210
|
+
def test_pass_section_mentions_approved(self) -> None:
|
|
211
|
+
"""AC1: Pass section should reference APPROVED verdict."""
|
|
212
|
+
content = _read_gate("approval")
|
|
213
|
+
pass_section = re.search(r"<pass>(.*?)</pass>", content, re.DOTALL)
|
|
214
|
+
assert pass_section is not None
|
|
215
|
+
assert "APPROVED" in pass_section.group(1) or "approved" in pass_section.group(1).lower()
|
|
216
|
+
|
|
217
|
+
def test_fail_section_handles_rejected(self) -> None:
|
|
218
|
+
"""AC1: Fail section should handle REJECTED verdict."""
|
|
219
|
+
content = _read_gate("approval")
|
|
220
|
+
fail_section = re.search(r"<fail>(.*?)</fail>", content, re.DOTALL)
|
|
221
|
+
assert fail_section is not None
|
|
222
|
+
fail_text = fail_section.group(1)
|
|
223
|
+
assert "REJECTED" in fail_text or "rejected" in fail_text.lower()
|
|
224
|
+
|
|
225
|
+
def test_pass_section_mentions_gate_result(self) -> None:
|
|
226
|
+
"""AC1: Pass section should return GATE_RESULT."""
|
|
227
|
+
content = _read_gate("approval")
|
|
228
|
+
pass_section = re.search(r"<pass>(.*?)</pass>", content, re.DOTALL)
|
|
229
|
+
assert pass_section is not None
|
|
230
|
+
assert "GATE_RESULT" in pass_section.group(1)
|
|
231
|
+
|
|
232
|
+
def test_fail_section_mentions_gate_result(self) -> None:
|
|
233
|
+
"""AC1: Fail section should return GATE_RESULT."""
|
|
234
|
+
content = _read_gate("approval")
|
|
235
|
+
fail_section = re.search(r"<fail>(.*?)</fail>", content, re.DOTALL)
|
|
236
|
+
assert fail_section is not None
|
|
237
|
+
assert "GATE_RESULT" in fail_section.group(1)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ===========================================================================
|
|
241
|
+
# AC2: Workflow YAMLs have gate.file entries
|
|
242
|
+
# ===========================================================================
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class TestTddWorkflowGateFiles:
|
|
246
|
+
"""AC2: tdd.yaml has gate.file for red (tests_fail) and review (approval)."""
|
|
247
|
+
|
|
248
|
+
@pytest.fixture
|
|
249
|
+
def tdd(self) -> dict:
|
|
250
|
+
return _load_workflow("tdd")
|
|
251
|
+
|
|
252
|
+
def test_red_phase_has_gate_file(self, tdd: dict) -> None:
|
|
253
|
+
"""AC2: tdd red phase must have gate.file = 'gates/tests-fail'."""
|
|
254
|
+
assert _get_gate_file(tdd, "red") == "gates/tests-fail"
|
|
255
|
+
|
|
256
|
+
def test_red_phase_keeps_gate_type(self, tdd: dict) -> None:
|
|
257
|
+
"""AC2: tdd red phase must retain gate.type = 'tests_fail' during transition."""
|
|
258
|
+
assert _get_gate_type(tdd, "red") == "tests_fail"
|
|
259
|
+
|
|
260
|
+
def test_review_phase_has_gate_file(self, tdd: dict) -> None:
|
|
261
|
+
"""AC2: tdd review phase must have gate.file = 'gates/approval'."""
|
|
262
|
+
assert _get_gate_file(tdd, "review") == "gates/approval"
|
|
263
|
+
|
|
264
|
+
def test_review_phase_keeps_gate_type(self, tdd: dict) -> None:
|
|
265
|
+
"""AC2: tdd review phase must retain gate.type = 'approval' during transition."""
|
|
266
|
+
assert _get_gate_type(tdd, "review") == "approval"
|
|
267
|
+
|
|
268
|
+
def test_green_phase_unchanged(self, tdd: dict) -> None:
|
|
269
|
+
"""AC2: tdd green phase gate.file should still be gates/tests-pass (from 106)."""
|
|
270
|
+
assert _get_gate_file(tdd, "green") == "gates/tests-pass"
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class TestTrivialWorkflowGateFiles:
|
|
274
|
+
"""AC2: trivial.yaml has gate.file for review (approval)."""
|
|
275
|
+
|
|
276
|
+
@pytest.fixture
|
|
277
|
+
def trivial(self) -> dict:
|
|
278
|
+
return _load_workflow("trivial")
|
|
279
|
+
|
|
280
|
+
def test_review_phase_has_gate_file(self, trivial: dict) -> None:
|
|
281
|
+
"""AC2: trivial review phase must have gate.file = 'gates/approval'."""
|
|
282
|
+
assert _get_gate_file(trivial, "review") == "gates/approval"
|
|
283
|
+
|
|
284
|
+
def test_review_phase_keeps_gate_type(self, trivial: dict) -> None:
|
|
285
|
+
"""AC2: trivial review phase must retain gate.type = 'approval'."""
|
|
286
|
+
assert _get_gate_type(trivial, "review") == "approval"
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class TestBddWorkflowGateFiles:
|
|
290
|
+
"""AC2: bdd.yaml has gate.file for red (tests_fail) and review (approval)."""
|
|
291
|
+
|
|
292
|
+
@pytest.fixture
|
|
293
|
+
def bdd(self) -> dict:
|
|
294
|
+
return _load_workflow("bdd")
|
|
295
|
+
|
|
296
|
+
def test_red_phase_has_gate_file(self, bdd: dict) -> None:
|
|
297
|
+
"""AC2: bdd red phase must have gate.file = 'gates/tests-fail'."""
|
|
298
|
+
assert _get_gate_file(bdd, "red") == "gates/tests-fail"
|
|
299
|
+
|
|
300
|
+
def test_red_phase_keeps_gate_type(self, bdd: dict) -> None:
|
|
301
|
+
"""AC2: bdd red phase must retain gate.type = 'tests_fail'."""
|
|
302
|
+
assert _get_gate_type(bdd, "red") == "tests_fail"
|
|
303
|
+
|
|
304
|
+
def test_review_phase_has_gate_file(self, bdd: dict) -> None:
|
|
305
|
+
"""AC2: bdd review phase must have gate.file = 'gates/approval'."""
|
|
306
|
+
assert _get_gate_file(bdd, "review") == "gates/approval"
|
|
307
|
+
|
|
308
|
+
def test_review_phase_keeps_gate_type(self, bdd: dict) -> None:
|
|
309
|
+
"""AC2: bdd review phase must retain gate.type = 'approval'."""
|
|
310
|
+
assert _get_gate_type(bdd, "review") == "approval"
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class TestTddTandemWorkflowGateFiles:
|
|
314
|
+
"""AC2: tdd-tandem.yaml has gate.file for red and review."""
|
|
315
|
+
|
|
316
|
+
@pytest.fixture
|
|
317
|
+
def tdd_tandem(self) -> dict:
|
|
318
|
+
return _load_workflow("tdd-tandem")
|
|
319
|
+
|
|
320
|
+
def test_red_phase_has_gate_file(self, tdd_tandem: dict) -> None:
|
|
321
|
+
"""AC2: tdd-tandem red phase must have gate.file = 'gates/tests-fail'."""
|
|
322
|
+
assert _get_gate_file(tdd_tandem, "red") == "gates/tests-fail"
|
|
323
|
+
|
|
324
|
+
def test_review_phase_has_gate_file(self, tdd_tandem: dict) -> None:
|
|
325
|
+
"""AC2: tdd-tandem review phase must have gate.file = 'gates/approval'."""
|
|
326
|
+
assert _get_gate_file(tdd_tandem, "review") == "gates/approval"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
class TestBddTandemWorkflowGateFiles:
|
|
330
|
+
"""AC2: bdd-tandem.yaml has gate.file for red and review."""
|
|
331
|
+
|
|
332
|
+
@pytest.fixture
|
|
333
|
+
def bdd_tandem(self) -> dict:
|
|
334
|
+
return _load_workflow("bdd-tandem")
|
|
335
|
+
|
|
336
|
+
def test_red_phase_has_gate_file(self, bdd_tandem: dict) -> None:
|
|
337
|
+
"""AC2: bdd-tandem red phase must have gate.file = 'gates/tests-fail'."""
|
|
338
|
+
assert _get_gate_file(bdd_tandem, "red") == "gates/tests-fail"
|
|
339
|
+
|
|
340
|
+
def test_review_phase_has_gate_file(self, bdd_tandem: dict) -> None:
|
|
341
|
+
"""AC2: bdd-tandem review phase must have gate.file = 'gates/approval'."""
|
|
342
|
+
assert _get_gate_file(bdd_tandem, "review") == "gates/approval"
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
class TestTwoPartyTddWorkflowGateFiles:
|
|
346
|
+
"""AC2: 2party-tdd.yaml has gate.file for all tests_fail and approval phases."""
|
|
347
|
+
|
|
348
|
+
@pytest.fixture
|
|
349
|
+
def two_party(self) -> dict:
|
|
350
|
+
return _load_workflow("2party-tdd")
|
|
351
|
+
|
|
352
|
+
# tests_fail phases
|
|
353
|
+
def test_red_phase_has_gate_file(self, two_party: dict) -> None:
|
|
354
|
+
"""AC2: 2party-tdd red phase must have gate.file = 'gates/tests-fail'."""
|
|
355
|
+
assert _get_gate_file(two_party, "red") == "gates/tests-fail"
|
|
356
|
+
|
|
357
|
+
def test_review_fix_tea_has_gate_file(self, two_party: dict) -> None:
|
|
358
|
+
"""AC2: 2party-tdd review-fix-tea phase must have gate.file = 'gates/tests-fail'."""
|
|
359
|
+
assert _get_gate_file(two_party, "review-fix-tea") == "gates/tests-fail"
|
|
360
|
+
|
|
361
|
+
# approval phases
|
|
362
|
+
def test_refine_dev_has_gate_file(self, two_party: dict) -> None:
|
|
363
|
+
"""AC2: 2party-tdd refine-dev phase must have gate.file = 'gates/approval'."""
|
|
364
|
+
assert _get_gate_file(two_party, "refine-dev") == "gates/approval"
|
|
365
|
+
|
|
366
|
+
def test_refine_tea_has_gate_file(self, two_party: dict) -> None:
|
|
367
|
+
"""AC2: 2party-tdd refine-tea phase must have gate.file = 'gates/approval'."""
|
|
368
|
+
assert _get_gate_file(two_party, "refine-tea") == "gates/approval"
|
|
369
|
+
|
|
370
|
+
def test_quality_pass_has_gate_file(self, two_party: dict) -> None:
|
|
371
|
+
"""AC2: 2party-tdd quality-pass phase must have gate.file = 'gates/approval'."""
|
|
372
|
+
assert _get_gate_file(two_party, "quality-pass") == "gates/approval"
|
|
373
|
+
|
|
374
|
+
def test_review_has_gate_file(self, two_party: dict) -> None:
|
|
375
|
+
"""AC2: 2party-tdd review phase must have gate.file = 'gates/approval'."""
|
|
376
|
+
assert _get_gate_file(two_party, "review") == "gates/approval"
|
|
377
|
+
|
|
378
|
+
def test_pr_prepare_has_gate_file(self, two_party: dict) -> None:
|
|
379
|
+
"""AC2: 2party-tdd pr-prepare phase must have gate.file = 'gates/approval'."""
|
|
380
|
+
assert _get_gate_file(two_party, "pr-prepare") == "gates/approval"
|
|
381
|
+
|
|
382
|
+
def test_pr_review_party_has_gate_file(self, two_party: dict) -> None:
|
|
383
|
+
"""AC2: 2party-tdd pr-review-party phase must have gate.file = 'gates/approval'."""
|
|
384
|
+
assert _get_gate_file(two_party, "pr-review-party") == "gates/approval"
|
|
385
|
+
|
|
386
|
+
def test_pr_review_respond_has_gate_file(self, two_party: dict) -> None:
|
|
387
|
+
"""AC2: 2party-tdd pr-review-respond phase must have gate.file = 'gates/approval'."""
|
|
388
|
+
assert _get_gate_file(two_party, "pr-review-respond") == "gates/approval"
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class TestAgentDocsWorkflowGateFiles:
|
|
392
|
+
"""AC2: agent-docs.yaml has gate.file for review (approval)."""
|
|
393
|
+
|
|
394
|
+
@pytest.fixture
|
|
395
|
+
def agent_docs(self) -> dict:
|
|
396
|
+
return _load_workflow("agent-docs")
|
|
397
|
+
|
|
398
|
+
def test_review_phase_has_gate_file(self, agent_docs: dict) -> None:
|
|
399
|
+
"""AC2: agent-docs review phase must have gate.file = 'gates/approval'."""
|
|
400
|
+
assert _get_gate_file(agent_docs, "review") == "gates/approval"
|
|
401
|
+
|
|
402
|
+
def test_review_phase_keeps_gate_type(self, agent_docs: dict) -> None:
|
|
403
|
+
"""AC2: agent-docs review phase must retain gate.type = 'approval'."""
|
|
404
|
+
assert _get_gate_type(agent_docs, "review") == "approval"
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# ===========================================================================
|
|
408
|
+
# AC2: gate.type retained during transition
|
|
409
|
+
# ===========================================================================
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class TestGateTypeRetainedDuringTransition:
|
|
413
|
+
"""AC2: All phases that get gate.file must also keep gate.type."""
|
|
414
|
+
|
|
415
|
+
@pytest.mark.parametrize(
|
|
416
|
+
"workflow_name,phase_name,expected_type",
|
|
417
|
+
[
|
|
418
|
+
("tdd", "red", "tests_fail"),
|
|
419
|
+
("tdd", "review", "approval"),
|
|
420
|
+
("trivial", "review", "approval"),
|
|
421
|
+
("bdd", "red", "tests_fail"),
|
|
422
|
+
("bdd", "review", "approval"),
|
|
423
|
+
("tdd-tandem", "red", "tests_fail"),
|
|
424
|
+
("tdd-tandem", "review", "approval"),
|
|
425
|
+
("bdd-tandem", "red", "tests_fail"),
|
|
426
|
+
("bdd-tandem", "review", "approval"),
|
|
427
|
+
("2party-tdd", "red", "tests_fail"),
|
|
428
|
+
("2party-tdd", "review", "approval"),
|
|
429
|
+
("2party-tdd", "review-fix-tea", "tests_fail"),
|
|
430
|
+
("agent-docs", "review", "approval"),
|
|
431
|
+
],
|
|
432
|
+
)
|
|
433
|
+
def test_gate_type_retained(
|
|
434
|
+
self, workflow_name: str, phase_name: str, expected_type: str
|
|
435
|
+
) -> None:
|
|
436
|
+
"""AC2: gate.type must be retained alongside gate.file during transition."""
|
|
437
|
+
wf = _load_workflow(workflow_name)
|
|
438
|
+
assert _get_gate_type(wf, phase_name) == expected_type
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
# ===========================================================================
|
|
442
|
+
# AC3: Gate file resolution
|
|
443
|
+
# ===========================================================================
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class TestGateFileResolution:
|
|
447
|
+
"""AC3: New gate files resolve via resolve_gate_file()."""
|
|
448
|
+
|
|
449
|
+
@pytest.fixture
|
|
450
|
+
def project(self, tmp_path: Path) -> Path:
|
|
451
|
+
"""Create project structure with symlink to real gates."""
|
|
452
|
+
(tmp_path / ".pennyfarthing" / "gates").mkdir(parents=True)
|
|
453
|
+
(tmp_path / "pennyfarthing-dist" / "gates").mkdir(parents=True)
|
|
454
|
+
|
|
455
|
+
# Copy the actual gate files (they should exist after implementation)
|
|
456
|
+
for name in ("tests-fail", "approval"):
|
|
457
|
+
src = GATES_DIR / f"{name}.md"
|
|
458
|
+
if src.exists():
|
|
459
|
+
dst = tmp_path / "pennyfarthing-dist" / "gates" / f"{name}.md"
|
|
460
|
+
dst.write_text(src.read_text())
|
|
461
|
+
return tmp_path
|
|
462
|
+
|
|
463
|
+
def test_tests_fail_resolves(self, project: Path) -> None:
|
|
464
|
+
"""AC3: resolve_gate_file('tests-fail') should return 'found'."""
|
|
465
|
+
result = resolve_gate_file("tests-fail", project_root=project)
|
|
466
|
+
assert result["status"] == "found", f"Expected 'found', got: {result}"
|
|
467
|
+
|
|
468
|
+
def test_approval_resolves(self, project: Path) -> None:
|
|
469
|
+
"""AC3: resolve_gate_file('approval') should return 'found'."""
|
|
470
|
+
result = resolve_gate_file("approval", project_root=project)
|
|
471
|
+
assert result["status"] == "found", f"Expected 'found', got: {result}"
|
|
472
|
+
|
|
473
|
+
def test_tests_fail_with_prefix_resolves(self, project: Path) -> None:
|
|
474
|
+
"""AC3: resolve_gate_file('gates/tests-fail') should resolve same path."""
|
|
475
|
+
result = resolve_gate_file("gates/tests-fail", project_root=project)
|
|
476
|
+
assert result["status"] == "found"
|
|
477
|
+
|
|
478
|
+
def test_approval_with_prefix_resolves(self, project: Path) -> None:
|
|
479
|
+
"""AC3: resolve_gate_file('gates/approval') should resolve same path."""
|
|
480
|
+
result = resolve_gate_file("gates/approval", project_root=project)
|
|
481
|
+
assert result["status"] == "found"
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
class TestResolveGateIntegration:
|
|
485
|
+
"""AC3: resolve_gate returns gate_file for migrated phases."""
|
|
486
|
+
|
|
487
|
+
@pytest.fixture
|
|
488
|
+
def project(self, tmp_path: Path) -> Path:
|
|
489
|
+
"""Create project with real workflow YAMLs and a session file."""
|
|
490
|
+
workflows_dir = tmp_path / ".pennyfarthing" / "workflows"
|
|
491
|
+
workflows_dir.mkdir(parents=True)
|
|
492
|
+
|
|
493
|
+
# Copy real workflow YAMLs
|
|
494
|
+
for name in ("tdd", "trivial", "bdd"):
|
|
495
|
+
src = WORKFLOWS_DIR / f"{name}.yaml"
|
|
496
|
+
if src.exists():
|
|
497
|
+
(workflows_dir / f"{name}.yaml").write_text(src.read_text())
|
|
498
|
+
|
|
499
|
+
# Create session with assessment
|
|
500
|
+
(tmp_path / ".session").mkdir()
|
|
501
|
+
session = tmp_path / ".session" / "108-1-session.md"
|
|
502
|
+
session.write_text(
|
|
503
|
+
"# Story 108-1\n\n"
|
|
504
|
+
"**Workflow:** tdd\n"
|
|
505
|
+
"**Phase:** red\n\n"
|
|
506
|
+
"## TEA Assessment\n\n"
|
|
507
|
+
"**Tests Written:** 5\n"
|
|
508
|
+
"**Status:** RED confirmed\n"
|
|
509
|
+
)
|
|
510
|
+
return tmp_path
|
|
511
|
+
|
|
512
|
+
def test_tdd_red_returns_gate_file(self, project: Path) -> None:
|
|
513
|
+
"""AC3: resolve_gate for tdd/red should return gate_file='gates/tests-fail'."""
|
|
514
|
+
result = resolve_gate("108-1", "tdd", "red", project_root=project)
|
|
515
|
+
assert result["gate_file"] == "gates/tests-fail", (
|
|
516
|
+
f"Expected gate_file='gates/tests-fail', got: {result}"
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
def test_tdd_review_returns_gate_file(self, project: Path) -> None:
|
|
520
|
+
"""AC3: resolve_gate for tdd/review should return gate_file='gates/approval'."""
|
|
521
|
+
result = resolve_gate("108-1", "tdd", "review", project_root=project)
|
|
522
|
+
assert result["gate_file"] == "gates/approval", (
|
|
523
|
+
f"Expected gate_file='gates/approval', got: {result}"
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
def test_trivial_review_returns_gate_file(self, project: Path) -> None:
|
|
527
|
+
"""AC3: resolve_gate for trivial/review should return gate_file='gates/approval'."""
|
|
528
|
+
result = resolve_gate(
|
|
529
|
+
"108-1", "trivial", "review", project_root=project
|
|
530
|
+
)
|
|
531
|
+
assert result["gate_file"] == "gates/approval", (
|
|
532
|
+
f"Expected gate_file='gates/approval', got: {result}"
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
def test_bdd_red_returns_gate_file(self, project: Path) -> None:
|
|
536
|
+
"""AC3: resolve_gate for bdd/red should return gate_file='gates/tests-fail'."""
|
|
537
|
+
result = resolve_gate("108-1", "bdd", "red", project_root=project)
|
|
538
|
+
assert result["gate_file"] == "gates/tests-fail", (
|
|
539
|
+
f"Expected gate_file='gates/tests-fail', got: {result}"
|
|
540
|
+
)
|