devforgeai 1.0.5 → 1.0.7
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/CLAUDE.md +120 -0
- package/bin/devforgeai.js +0 -0
- package/package.json +9 -1
- package/src/CLAUDE.md +699 -0
- package/src/claude/hooks/phase-completion-gate.sh +0 -0
- package/src/claude/scripts/README.md +396 -0
- package/src/claude/scripts/audit-command-skill-overlap.sh +67 -0
- package/src/claude/scripts/check-hooks-fast.sh +70 -0
- package/src/claude/scripts/devforgeai-validate +6 -0
- package/src/claude/scripts/devforgeai_cli/README.md +531 -0
- package/src/claude/scripts/devforgeai_cli/__init__.py +12 -0
- package/src/claude/scripts/devforgeai_cli/cli.py +716 -0
- package/src/claude/scripts/devforgeai_cli/commands/__init__.py +1 -0
- package/src/claude/scripts/devforgeai_cli/commands/check_hooks.py +384 -0
- package/src/claude/scripts/devforgeai_cli/commands/invoke_hooks.py +149 -0
- package/src/claude/scripts/devforgeai_cli/commands/phase_commands.py +731 -0
- package/src/claude/scripts/devforgeai_cli/commands/validate_installation.py +412 -0
- package/src/claude/scripts/devforgeai_cli/context_extraction.py +426 -0
- package/src/claude/scripts/devforgeai_cli/feedback/AC_TO_TEST_MAPPING.md +636 -0
- package/src/claude/scripts/devforgeai_cli/feedback/DELIVERY_SUMMARY.txt +329 -0
- package/src/claude/scripts/devforgeai_cli/feedback/README_TEST_SPECS.md +486 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_IMPLEMENTATION_GUIDE.md +529 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECIFICATIONS.md +2652 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECS_INDEX.md +398 -0
- package/src/claude/scripts/devforgeai_cli/feedback/__init__.py +34 -0
- package/src/claude/scripts/devforgeai_cli/feedback/adaptive_questioning_engine.py +581 -0
- package/src/claude/scripts/devforgeai_cli/feedback/aggregation.py +179 -0
- package/src/claude/scripts/devforgeai_cli/feedback/commands.py +535 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_defaults.py +58 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_manager.py +423 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_models.py +192 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_schema.py +140 -0
- package/src/claude/scripts/devforgeai_cli/feedback/coverage.json +1 -0
- package/src/claude/scripts/devforgeai_cli/feedback/feature_flag.py +152 -0
- package/src/claude/scripts/devforgeai_cli/feedback/feedback_indexer.py +394 -0
- package/src/claude/scripts/devforgeai_cli/feedback/hot_reload.py +226 -0
- package/src/claude/scripts/devforgeai_cli/feedback/longitudinal.py +115 -0
- package/src/claude/scripts/devforgeai_cli/feedback/models.py +67 -0
- package/src/claude/scripts/devforgeai_cli/feedback/question_router.py +236 -0
- package/src/claude/scripts/devforgeai_cli/feedback/retrospective.py +233 -0
- package/src/claude/scripts/devforgeai_cli/feedback/skip_tracker.py +177 -0
- package/src/claude/scripts/devforgeai_cli/feedback/skip_tracking.py +221 -0
- package/src/claude/scripts/devforgeai_cli/feedback/template_engine.py +549 -0
- package/src/claude/scripts/devforgeai_cli/feedback/validation.py +163 -0
- package/src/claude/scripts/devforgeai_cli/headless/__init__.py +30 -0
- package/src/claude/scripts/devforgeai_cli/headless/answer_models.py +206 -0
- package/src/claude/scripts/devforgeai_cli/headless/answer_resolver.py +204 -0
- package/src/claude/scripts/devforgeai_cli/headless/exceptions.py +36 -0
- package/src/claude/scripts/devforgeai_cli/headless/pattern_matcher.py +156 -0
- package/src/claude/scripts/devforgeai_cli/hooks.py +313 -0
- package/src/claude/scripts/devforgeai_cli/metrics/__init__.py +46 -0
- package/src/claude/scripts/devforgeai_cli/metrics/command_metrics.py +142 -0
- package/src/claude/scripts/devforgeai_cli/metrics/failure_modes.py +152 -0
- package/src/claude/scripts/devforgeai_cli/metrics/story_segmentation.py +181 -0
- package/src/claude/scripts/devforgeai_cli/orchestrate_hooks.py +780 -0
- package/src/claude/scripts/devforgeai_cli/phase_state.py +1229 -0
- package/src/claude/scripts/devforgeai_cli/session/__init__.py +30 -0
- package/src/claude/scripts/devforgeai_cli/session/checkpoint.py +268 -0
- package/src/claude/scripts/devforgeai_cli/tests/__init__.py +1 -0
- package/src/claude/scripts/devforgeai_cli/tests/conftest.py +29 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/TEST_EXECUTION_GUIDE.md +298 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/__init__.py +3 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_adaptive_questioning_engine.py +2171 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_aggregation.py +476 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_defaults.py +133 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_manager.py +592 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_models.py +373 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_schema.py +130 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_configuration_management.py +1355 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_edge_cases.py +308 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feature_flag.py +307 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feedback_indexer.py +384 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_hot_reload.py +580 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_integration.py +402 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_models.py +105 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_question_routing.py +262 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_retrospective.py +333 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracker.py +410 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking.py +159 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking_integration.py +1155 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_template_engine.py +1389 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_validation_comprehensive.py +210 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/autonomous-deferral-story.md +46 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/missing-impl-notes.md +31 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-deferral-story.md +46 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-story-complete.md +48 -0
- package/src/claude/scripts/devforgeai_cli/tests/manual_test_invoke_hooks.sh +200 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/DELIVERABLES.md +518 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/TEST_SUMMARY.md +468 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/__init__.py +6 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/corrupted-checkpoint.json +1 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/missing-fields-checkpoint.json +4 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/valid-checkpoint.json +15 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/test_checkpoint.py +851 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_check_hooks.py +1886 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_depends_on_normalizer.py +171 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_dod_validator.py +97 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_invoke_hooks.py +1902 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands.py +320 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_error_handling.py +1021 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_import.py +697 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_state.py +2187 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking.py +2141 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking_coverage_gap.py +195 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_subagent_enforcement.py +539 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_validate_installation.py +361 -0
- package/src/claude/scripts/devforgeai_cli/utils/__init__.py +11 -0
- package/src/claude/scripts/devforgeai_cli/utils/depends_on_normalizer.py +149 -0
- package/src/claude/scripts/devforgeai_cli/utils/markdown_parser.py +219 -0
- package/src/claude/scripts/devforgeai_cli/utils/story_analyzer.py +249 -0
- package/src/claude/scripts/devforgeai_cli/utils/yaml_parser.py +152 -0
- package/src/claude/scripts/devforgeai_cli/validators/__init__.py +27 -0
- package/src/claude/scripts/devforgeai_cli/validators/ast_grep_validator.py +373 -0
- package/src/claude/scripts/devforgeai_cli/validators/context_validator.py +180 -0
- package/src/claude/scripts/devforgeai_cli/validators/dod_validator.py +309 -0
- package/src/claude/scripts/devforgeai_cli/validators/git_validator.py +107 -0
- package/src/claude/scripts/devforgeai_cli/validators/grep_fallback.py +300 -0
- package/src/claude/scripts/install_hooks.sh +186 -0
- package/src/claude/scripts/invoke_feedback_hooks.sh +59 -0
- package/src/claude/scripts/migrate-ac-headers.sh +122 -0
- package/src/claude/scripts/plan_file_kb.sh +704 -0
- package/src/claude/scripts/requirements.txt +8 -0
- package/src/claude/scripts/session_catalog.sh +543 -0
- package/src/claude/scripts/setup.py +55 -0
- package/src/claude/scripts/start-devforgeai.sh +16 -0
- package/src/claude/scripts/statusline.sh +27 -0
- package/src/claude/scripts/validate_deferrals.py +344 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/detect_anti_patterns.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_all_context.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_architecture.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_dependencies.cpython-312.pyc +0 -0
- package/src/claude/skills/devforgeai-story-creation/scripts/__pycache__/migrate_story_v1_to_v2.cpython-312.pyc +0 -0
- package/src/claude/skills/devforgeai-story-creation/scripts/tests/__pycache__/measure_accuracy.cpython-312.pyc +0 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for phase CLI commands.
|
|
3
|
+
|
|
4
|
+
TDD Red Phase: These tests verify the phase-init, phase-check,
|
|
5
|
+
phase-complete, and phase-status CLI commands.
|
|
6
|
+
|
|
7
|
+
Exit Codes:
|
|
8
|
+
- phase-init: 0=created, 1=exists, 2=invalid ID
|
|
9
|
+
- phase-check: 0=allowed, 1=blocked, 2=missing subagents
|
|
10
|
+
- phase-complete: 0=success, 1=incomplete
|
|
11
|
+
- phase-status: 0=success, 1=not found
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from unittest.mock import patch, MagicMock
|
|
19
|
+
|
|
20
|
+
import pytest
|
|
21
|
+
|
|
22
|
+
# Add parent directory to path
|
|
23
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
24
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent.parent))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def temp_project_dir():
|
|
29
|
+
"""Create a temporary project directory."""
|
|
30
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
31
|
+
project_root = Path(tmpdir)
|
|
32
|
+
# Create devforgeai/workflows directory
|
|
33
|
+
workflows_dir = project_root / "devforgeai" / "workflows"
|
|
34
|
+
workflows_dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
yield project_root
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def existing_state(temp_project_dir):
|
|
40
|
+
"""Create an existing phase state file."""
|
|
41
|
+
state = {
|
|
42
|
+
"story_id": "STORY-001",
|
|
43
|
+
"workflow_started": "2025-12-24T10:00:00Z",
|
|
44
|
+
"current_phase": "02",
|
|
45
|
+
"phases": {
|
|
46
|
+
"01": {
|
|
47
|
+
"status": "completed",
|
|
48
|
+
"started_at": "2025-12-24T10:00:00Z",
|
|
49
|
+
"completed_at": "2025-12-24T10:05:00Z",
|
|
50
|
+
"subagents_required": ["git-validator", "tech-stack-detector"],
|
|
51
|
+
"subagents_invoked": ["git-validator", "tech-stack-detector"],
|
|
52
|
+
"checkpoint_passed": True
|
|
53
|
+
},
|
|
54
|
+
"02": {"status": "pending", "subagents_required": ["test-automator"], "subagents_invoked": []},
|
|
55
|
+
"03": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
56
|
+
"04": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
57
|
+
"05": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
58
|
+
"06": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
59
|
+
"07": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
60
|
+
"08": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
61
|
+
"09": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
|
|
62
|
+
"10": {"status": "pending", "subagents_required": [], "subagents_invoked": []}
|
|
63
|
+
},
|
|
64
|
+
"validation_errors": [],
|
|
65
|
+
"blocking_status": False
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-001-phase-state.json"
|
|
69
|
+
state_file.write_text(json.dumps(state, indent=2))
|
|
70
|
+
return state_file
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# =============================================================================
|
|
74
|
+
# phase-init Tests
|
|
75
|
+
# =============================================================================
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TestPhaseInitCommand:
|
|
79
|
+
"""Tests for phase-init command."""
|
|
80
|
+
|
|
81
|
+
def test_phase_init_creates_state_file(self, temp_project_dir):
|
|
82
|
+
"""Test phase-init creates a new state file."""
|
|
83
|
+
from commands.phase_commands import phase_init_command
|
|
84
|
+
|
|
85
|
+
exit_code = phase_init_command("STORY-100", str(temp_project_dir), format="text")
|
|
86
|
+
|
|
87
|
+
assert exit_code == 0
|
|
88
|
+
state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-100-phase-state.json"
|
|
89
|
+
assert state_file.exists()
|
|
90
|
+
|
|
91
|
+
def test_phase_init_returns_1_if_exists(self, temp_project_dir, existing_state):
|
|
92
|
+
"""Test phase-init returns 1 if state file already exists."""
|
|
93
|
+
from commands.phase_commands import phase_init_command
|
|
94
|
+
|
|
95
|
+
exit_code = phase_init_command("STORY-001", str(temp_project_dir), format="text")
|
|
96
|
+
|
|
97
|
+
assert exit_code == 1
|
|
98
|
+
|
|
99
|
+
def test_phase_init_returns_2_for_invalid_id(self, temp_project_dir):
|
|
100
|
+
"""Test phase-init returns 2 for invalid story ID."""
|
|
101
|
+
from commands.phase_commands import phase_init_command
|
|
102
|
+
|
|
103
|
+
exit_code = phase_init_command("INVALID-001", str(temp_project_dir), format="text")
|
|
104
|
+
|
|
105
|
+
assert exit_code == 2
|
|
106
|
+
|
|
107
|
+
def test_phase_init_json_output(self, temp_project_dir, capsys):
|
|
108
|
+
"""Test phase-init with JSON output format."""
|
|
109
|
+
from commands.phase_commands import phase_init_command
|
|
110
|
+
|
|
111
|
+
exit_code = phase_init_command("STORY-100", str(temp_project_dir), format="json")
|
|
112
|
+
|
|
113
|
+
assert exit_code == 0
|
|
114
|
+
output = capsys.readouterr().out
|
|
115
|
+
result = json.loads(output)
|
|
116
|
+
assert result["success"] is True
|
|
117
|
+
assert result["story_id"] == "STORY-100"
|
|
118
|
+
assert "path" in result
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# =============================================================================
|
|
122
|
+
# phase-check Tests
|
|
123
|
+
# =============================================================================
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TestPhaseCheckCommand:
|
|
127
|
+
"""Tests for phase-check command."""
|
|
128
|
+
|
|
129
|
+
def test_phase_check_allows_valid_transition(self, temp_project_dir, existing_state):
|
|
130
|
+
"""Test phase-check allows valid phase transition."""
|
|
131
|
+
from commands.phase_commands import phase_check_command
|
|
132
|
+
|
|
133
|
+
# Current phase is 02, should allow 01->02 check
|
|
134
|
+
exit_code = phase_check_command(
|
|
135
|
+
"STORY-001",
|
|
136
|
+
from_phase="01",
|
|
137
|
+
to_phase="02",
|
|
138
|
+
project_root=str(temp_project_dir),
|
|
139
|
+
format="text"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
assert exit_code == 0
|
|
143
|
+
|
|
144
|
+
def test_phase_check_blocks_skip(self, temp_project_dir, existing_state):
|
|
145
|
+
"""Test phase-check blocks phase skipping."""
|
|
146
|
+
from commands.phase_commands import phase_check_command
|
|
147
|
+
|
|
148
|
+
exit_code = phase_check_command(
|
|
149
|
+
"STORY-001",
|
|
150
|
+
from_phase="02",
|
|
151
|
+
to_phase="04", # Trying to skip phase 03
|
|
152
|
+
project_root=str(temp_project_dir),
|
|
153
|
+
format="text"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
assert exit_code == 1
|
|
157
|
+
|
|
158
|
+
def test_phase_check_reports_missing_subagents(self, temp_project_dir):
|
|
159
|
+
"""Test phase-check returns 2 when subagents are missing."""
|
|
160
|
+
from commands.phase_commands import phase_check_command
|
|
161
|
+
|
|
162
|
+
# Create state with phase 01 incomplete (missing subagents)
|
|
163
|
+
state = {
|
|
164
|
+
"story_id": "STORY-002",
|
|
165
|
+
"workflow_started": "2025-12-24T10:00:00Z",
|
|
166
|
+
"current_phase": "01",
|
|
167
|
+
"phases": {
|
|
168
|
+
"01": {
|
|
169
|
+
"status": "completed",
|
|
170
|
+
"subagents_required": ["git-validator", "tech-stack-detector"],
|
|
171
|
+
"subagents_invoked": ["git-validator"], # Missing tech-stack-detector
|
|
172
|
+
"checkpoint_passed": True
|
|
173
|
+
},
|
|
174
|
+
"02": {"status": "pending", "subagents_required": ["test-automator"], "subagents_invoked": []},
|
|
175
|
+
**{f"{i:02d}": {"status": "pending", "subagents_required": [], "subagents_invoked": []} for i in range(3, 11)}
|
|
176
|
+
},
|
|
177
|
+
"validation_errors": [],
|
|
178
|
+
"blocking_status": False
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-002-phase-state.json"
|
|
182
|
+
state_file.write_text(json.dumps(state, indent=2))
|
|
183
|
+
|
|
184
|
+
exit_code = phase_check_command(
|
|
185
|
+
"STORY-002",
|
|
186
|
+
from_phase="01",
|
|
187
|
+
to_phase="02",
|
|
188
|
+
project_root=str(temp_project_dir),
|
|
189
|
+
format="text"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
assert exit_code == 2
|
|
193
|
+
|
|
194
|
+
def test_phase_check_json_output(self, temp_project_dir, existing_state, capsys):
|
|
195
|
+
"""Test phase-check with JSON output format."""
|
|
196
|
+
from commands.phase_commands import phase_check_command
|
|
197
|
+
|
|
198
|
+
exit_code = phase_check_command(
|
|
199
|
+
"STORY-001",
|
|
200
|
+
from_phase="01",
|
|
201
|
+
to_phase="02",
|
|
202
|
+
project_root=str(temp_project_dir),
|
|
203
|
+
format="json"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
assert exit_code == 0
|
|
207
|
+
output = capsys.readouterr().out
|
|
208
|
+
result = json.loads(output)
|
|
209
|
+
assert result["allowed"] is True
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# =============================================================================
|
|
213
|
+
# phase-complete Tests
|
|
214
|
+
# =============================================================================
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class TestPhaseCompleteCommand:
|
|
218
|
+
"""Tests for phase-complete command."""
|
|
219
|
+
|
|
220
|
+
def test_phase_complete_success(self, temp_project_dir, existing_state):
|
|
221
|
+
"""Test phase-complete marks phase as complete."""
|
|
222
|
+
from commands.phase_commands import phase_complete_command
|
|
223
|
+
|
|
224
|
+
exit_code = phase_complete_command(
|
|
225
|
+
"STORY-001",
|
|
226
|
+
phase="02",
|
|
227
|
+
checkpoint_passed=True,
|
|
228
|
+
project_root=str(temp_project_dir),
|
|
229
|
+
format="text"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
assert exit_code == 0
|
|
233
|
+
|
|
234
|
+
# Verify state was updated
|
|
235
|
+
state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-001-phase-state.json"
|
|
236
|
+
state = json.loads(state_file.read_text())
|
|
237
|
+
assert state["phases"]["02"]["status"] == "completed"
|
|
238
|
+
assert state["current_phase"] == "03"
|
|
239
|
+
|
|
240
|
+
def test_phase_complete_returns_1_for_wrong_phase(self, temp_project_dir, existing_state):
|
|
241
|
+
"""Test phase-complete returns 1 when completing wrong phase."""
|
|
242
|
+
from commands.phase_commands import phase_complete_command
|
|
243
|
+
|
|
244
|
+
# Current phase is 02, can't complete 03
|
|
245
|
+
exit_code = phase_complete_command(
|
|
246
|
+
"STORY-001",
|
|
247
|
+
phase="03",
|
|
248
|
+
checkpoint_passed=True,
|
|
249
|
+
project_root=str(temp_project_dir),
|
|
250
|
+
format="text"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
assert exit_code == 1
|
|
254
|
+
|
|
255
|
+
def test_phase_complete_not_found(self, temp_project_dir):
|
|
256
|
+
"""Test phase-complete returns 1 for non-existent state."""
|
|
257
|
+
from commands.phase_commands import phase_complete_command
|
|
258
|
+
|
|
259
|
+
exit_code = phase_complete_command(
|
|
260
|
+
"STORY-999",
|
|
261
|
+
phase="01",
|
|
262
|
+
checkpoint_passed=True,
|
|
263
|
+
project_root=str(temp_project_dir),
|
|
264
|
+
format="text"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
assert exit_code == 1
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# =============================================================================
|
|
271
|
+
# phase-status Tests
|
|
272
|
+
# =============================================================================
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class TestPhaseStatusCommand:
|
|
276
|
+
"""Tests for phase-status command."""
|
|
277
|
+
|
|
278
|
+
def test_phase_status_success(self, temp_project_dir, existing_state, capsys):
|
|
279
|
+
"""Test phase-status displays current state."""
|
|
280
|
+
from commands.phase_commands import phase_status_command
|
|
281
|
+
|
|
282
|
+
exit_code = phase_status_command(
|
|
283
|
+
"STORY-001",
|
|
284
|
+
project_root=str(temp_project_dir),
|
|
285
|
+
format="text"
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
assert exit_code == 0
|
|
289
|
+
output = capsys.readouterr().out
|
|
290
|
+
assert "STORY-001" in output
|
|
291
|
+
assert "Phase 02" in output or "02" in output
|
|
292
|
+
|
|
293
|
+
def test_phase_status_not_found(self, temp_project_dir):
|
|
294
|
+
"""Test phase-status returns 1 for non-existent state."""
|
|
295
|
+
from commands.phase_commands import phase_status_command
|
|
296
|
+
|
|
297
|
+
exit_code = phase_status_command(
|
|
298
|
+
"STORY-999",
|
|
299
|
+
project_root=str(temp_project_dir),
|
|
300
|
+
format="text"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
assert exit_code == 1
|
|
304
|
+
|
|
305
|
+
def test_phase_status_json_output(self, temp_project_dir, existing_state, capsys):
|
|
306
|
+
"""Test phase-status with JSON output format."""
|
|
307
|
+
from commands.phase_commands import phase_status_command
|
|
308
|
+
|
|
309
|
+
exit_code = phase_status_command(
|
|
310
|
+
"STORY-001",
|
|
311
|
+
project_root=str(temp_project_dir),
|
|
312
|
+
format="json"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
assert exit_code == 0
|
|
316
|
+
output = capsys.readouterr().out
|
|
317
|
+
result = json.loads(output)
|
|
318
|
+
assert result["story_id"] == "STORY-001"
|
|
319
|
+
assert result["current_phase"] == "02"
|
|
320
|
+
assert "phases" in result
|