prizmkit 1.0.45 → 1.0.66

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.
Files changed (67) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/claude/agent-adapter.js +2 -1
  3. package/bundled/adapters/claude/command-adapter.js +3 -3
  4. package/bundled/agents/prizm-dev-team-dev.md +1 -1
  5. package/bundled/dev-pipeline/README.md +6 -8
  6. package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +24 -19
  7. package/bundled/dev-pipeline/launch-bugfix-daemon.sh +2 -2
  8. package/bundled/dev-pipeline/launch-daemon.sh +2 -2
  9. package/bundled/dev-pipeline/lib/branch.sh +76 -0
  10. package/bundled/dev-pipeline/run-bugfix.sh +58 -149
  11. package/bundled/dev-pipeline/run.sh +60 -153
  12. package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -4
  13. package/bundled/dev-pipeline/scripts/parse-stream-progress.py +2 -2
  14. package/bundled/dev-pipeline/templates/bootstrap-tier1.md +16 -27
  15. package/bundled/dev-pipeline/templates/bootstrap-tier2.md +20 -32
  16. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +32 -53
  17. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +29 -41
  18. package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
  19. package/bundled/dev-pipeline/tests/conftest.py +19 -126
  20. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +207 -0
  21. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +128 -141
  22. package/bundled/dev-pipeline/tests/test_utils.py +51 -110
  23. package/bundled/rules/prizm/prizm-commit-workflow.md +3 -3
  24. package/bundled/skills/_metadata.json +15 -16
  25. package/bundled/skills/app-planner/SKILL.md +8 -7
  26. package/bundled/skills/bug-fix-workflow/SKILL.md +171 -0
  27. package/bundled/skills/bug-planner/SKILL.md +25 -33
  28. package/bundled/skills/bug-planner/scripts/validate-bug-list.py +156 -0
  29. package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +5 -7
  30. package/bundled/skills/dev-pipeline-launcher/SKILL.md +4 -6
  31. package/bundled/skills/feature-workflow/SKILL.md +25 -42
  32. package/bundled/skills/prizm-kit/SKILL.md +61 -23
  33. package/bundled/skills/prizm-kit/assets/{claude-md-template.md → project-memory-template.md} +3 -3
  34. package/bundled/skills/prizmkit-analyze/SKILL.md +44 -33
  35. package/bundled/skills/prizmkit-clarify/SKILL.md +40 -30
  36. package/bundled/skills/prizmkit-code-review/SKILL.md +58 -45
  37. package/bundled/skills/prizmkit-committer/SKILL.md +30 -68
  38. package/bundled/skills/prizmkit-implement/SKILL.md +60 -28
  39. package/bundled/skills/prizmkit-init/SKILL.md +57 -66
  40. package/bundled/skills/prizmkit-plan/SKILL.md +60 -23
  41. package/bundled/skills/prizmkit-prizm-docs/SKILL.md +74 -19
  42. package/bundled/skills/prizmkit-prizm-docs/assets/PRIZM-SPEC.md +23 -23
  43. package/bundled/skills/prizmkit-retrospective/SKILL.md +142 -65
  44. package/bundled/skills/prizmkit-retrospective/assets/retrospective-template.md +13 -0
  45. package/bundled/skills/prizmkit-specify/SKILL.md +69 -15
  46. package/bundled/skills/refactor-workflow/SKILL.md +116 -52
  47. package/bundled/team/prizm-dev-team.json +2 -2
  48. package/package.json +1 -1
  49. package/src/scaffold.js +4 -4
  50. package/bundled/dev-pipeline/lib/worktree.sh +0 -164
  51. package/bundled/dev-pipeline/tests/__init__.py +0 -0
  52. package/bundled/dev-pipeline/tests/test_check_session.py +0 -131
  53. package/bundled/dev-pipeline/tests/test_cleanup_logs.py +0 -119
  54. package/bundled/dev-pipeline/tests/test_detect_stuck.py +0 -207
  55. package/bundled/dev-pipeline/tests/test_generate_prompt.py +0 -190
  56. package/bundled/dev-pipeline/tests/test_init_bugfix_pipeline.py +0 -153
  57. package/bundled/dev-pipeline/tests/test_init_pipeline.py +0 -241
  58. package/bundled/dev-pipeline/tests/test_update_bug_status.py +0 -142
  59. package/bundled/dev-pipeline/tests/test_update_feature_status.py +0 -338
  60. package/bundled/dev-pipeline/tests/test_worktree.py +0 -236
  61. package/bundled/dev-pipeline/tests/test_worktree_integration.py +0 -796
  62. package/bundled/skills/prizm-kit/assets/codebuddy-md-template.md +0 -35
  63. package/bundled/skills/prizm-kit/assets/hooks/prizm-commit-hook.json +0 -15
  64. package/bundled/skills/prizmkit-summarize/SKILL.md +0 -51
  65. package/bundled/skills/prizmkit-summarize/assets/registry-template.md +0 -18
  66. package/bundled/templates/hooks/commit-intent-claude.json +0 -26
  67. /package/bundled/templates/hooks/{commit-intent-codebuddy.json → commit-intent.json} +0 -0
@@ -1,153 +0,0 @@
1
- """Tests for init-bugfix-pipeline.py."""
2
-
3
- import json
4
- import os
5
- import sys
6
- import pytest
7
-
8
-
9
- def _import_init_bugfix_pipeline():
10
- import importlib.util
11
- path = os.path.join(
12
- os.path.dirname(__file__), "..", "scripts", "init-bugfix-pipeline.py"
13
- )
14
- spec = importlib.util.spec_from_file_location("init_bugfix_pipeline", path)
15
- mod = importlib.util.module_from_spec(spec)
16
- sys.modules["init_bugfix_pipeline"] = mod
17
- spec.loader.exec_module(mod)
18
- return mod
19
-
20
-
21
- init_bugfix = _import_init_bugfix_pipeline()
22
- validate_schema = init_bugfix.validate_schema
23
- validate_bugs = init_bugfix.validate_bugs
24
- create_state_directory = init_bugfix.create_state_directory
25
-
26
-
27
- class TestValidateSchema:
28
- def test_valid_schema(self, sample_bug_list):
29
- errors = validate_schema(sample_bug_list)
30
- assert errors == []
31
-
32
- def test_wrong_schema(self):
33
- data = {
34
- "$schema": "wrong",
35
- "project_name": "Test",
36
- "bugs": [{"id": "B-001"}],
37
- }
38
- errors = validate_schema(data)
39
- assert any("$schema" in e for e in errors)
40
-
41
- def test_missing_project_name(self):
42
- data = {
43
- "$schema": "dev-pipeline-bug-fix-list-v1",
44
- "bugs": [{"id": "B-001"}],
45
- }
46
- errors = validate_schema(data)
47
- assert any("project_name" in e for e in errors)
48
-
49
- def test_empty_project_name(self):
50
- data = {
51
- "$schema": "dev-pipeline-bug-fix-list-v1",
52
- "project_name": " ",
53
- "bugs": [{"id": "B-001"}],
54
- }
55
- errors = validate_schema(data)
56
- assert any("project_name" in e for e in errors)
57
-
58
- def test_missing_bugs(self):
59
- data = {
60
- "$schema": "dev-pipeline-bug-fix-list-v1",
61
- "project_name": "Test",
62
- }
63
- errors = validate_schema(data)
64
- assert any("bugs" in e for e in errors)
65
-
66
- def test_bugs_not_array(self):
67
- data = {
68
- "$schema": "dev-pipeline-bug-fix-list-v1",
69
- "project_name": "Test",
70
- "bugs": "not array",
71
- }
72
- errors = validate_schema(data)
73
- assert any("bugs must be an array" in e for e in errors)
74
-
75
- def test_empty_bugs_array(self):
76
- data = {
77
- "$schema": "dev-pipeline-bug-fix-list-v1",
78
- "project_name": "Test",
79
- "bugs": [],
80
- }
81
- errors = validate_schema(data)
82
- assert any("at least one bug" in e for e in errors)
83
-
84
-
85
- class TestValidateBugs:
86
- def test_valid_bugs(self, sample_bug_list):
87
- errors, ids = validate_bugs(sample_bug_list["bugs"])
88
- assert errors == []
89
- assert ids == {"B-001", "B-002"}
90
-
91
- def test_duplicate_ids(self):
92
- bugs = [
93
- _make_bug("B-001"), _make_bug("B-001", title="Other"),
94
- ]
95
- errors, ids = validate_bugs(bugs)
96
- assert any("Duplicate" in e for e in errors)
97
-
98
- def test_invalid_id_format(self):
99
- bugs = [_make_bug("X-001")]
100
- errors, ids = validate_bugs(bugs)
101
- assert any("invalid id" in e for e in errors)
102
-
103
- def test_invalid_severity(self):
104
- bug = _make_bug("B-001")
105
- bug["severity"] = "super_critical"
106
- errors, ids = validate_bugs([bug])
107
- assert any("invalid severity" in e for e in errors)
108
-
109
- def test_invalid_verification_type(self):
110
- bug = _make_bug("B-001")
111
- bug["verification_type"] = "magic"
112
- errors, ids = validate_bugs([bug])
113
- assert any("invalid verification_type" in e for e in errors)
114
-
115
- def test_invalid_status(self):
116
- bug = _make_bug("B-001")
117
- bug["status"] = "nonexistent_status"
118
- errors, ids = validate_bugs([bug])
119
- assert any("invalid status" in e for e in errors)
120
-
121
- def test_missing_required_fields(self):
122
- bugs = [{"id": "B-001"}]
123
- errors, ids = validate_bugs(bugs)
124
- assert len(errors) > 0
125
-
126
- def test_error_source_not_object(self):
127
- bug = _make_bug("B-001")
128
- bug["error_source"] = "a string"
129
- errors, ids = validate_bugs([bug])
130
- assert any("error_source must be an object" in e for e in errors)
131
-
132
- def test_error_source_missing_type(self):
133
- bug = _make_bug("B-001")
134
- bug["error_source"] = {"detail": "something"}
135
- errors, ids = validate_bugs([bug])
136
- assert any("error_source missing required field: type" in e for e in errors)
137
-
138
- def test_non_dict_bug(self):
139
- errors, ids = validate_bugs(["not a dict"])
140
- assert any("not an object" in e for e in errors)
141
-
142
-
143
- def _make_bug(bid, title="Test Bug"):
144
- return {
145
- "id": bid,
146
- "title": title,
147
- "description": "A bug",
148
- "severity": "medium",
149
- "error_source": {"type": "stack_trace"},
150
- "verification_type": "automated",
151
- "acceptance_criteria": ["Fixed"],
152
- "status": "pending",
153
- }
@@ -1,241 +0,0 @@
1
- """Tests for init-pipeline.py."""
2
-
3
- import json
4
- import os
5
- import sys
6
- import importlib
7
- import pytest
8
-
9
-
10
- # The module has a hyphen in the name, so we need importlib
11
- def _import_init_pipeline():
12
- import importlib.util
13
- path = os.path.join(
14
- os.path.dirname(__file__), "..", "scripts", "init-pipeline.py"
15
- )
16
- spec = importlib.util.spec_from_file_location("init_pipeline", path)
17
- mod = importlib.util.module_from_spec(spec)
18
- # Prevent main() from running on import
19
- sys.modules["init_pipeline"] = mod
20
- spec.loader.exec_module(mod)
21
- return mod
22
-
23
-
24
- init_pipeline = _import_init_pipeline()
25
- validate_schema = init_pipeline.validate_schema
26
- validate_features = init_pipeline.validate_features
27
- check_dag = init_pipeline.check_dag
28
- create_state_directory = init_pipeline.create_state_directory
29
-
30
-
31
- class TestValidateSchema:
32
- def test_valid_schema(self, sample_feature_list):
33
- errors = validate_schema(sample_feature_list)
34
- assert errors == []
35
-
36
- def test_wrong_schema_value(self):
37
- data = {
38
- "$schema": "wrong-schema",
39
- "app_name": "Test",
40
- "features": [],
41
- }
42
- errors = validate_schema(data)
43
- assert len(errors) == 1
44
- assert "Invalid $schema" in errors[0]
45
-
46
- def test_missing_schema(self):
47
- data = {"app_name": "Test", "features": []}
48
- errors = validate_schema(data)
49
- assert any("$schema" in e for e in errors)
50
-
51
- def test_missing_app_name(self):
52
- data = {
53
- "$schema": "dev-pipeline-feature-list-v1",
54
- "features": [],
55
- }
56
- errors = validate_schema(data)
57
- assert any("app_name" in e for e in errors)
58
-
59
- def test_empty_app_name(self):
60
- data = {
61
- "$schema": "dev-pipeline-feature-list-v1",
62
- "app_name": " ",
63
- "features": [],
64
- }
65
- errors = validate_schema(data)
66
- assert any("app_name" in e for e in errors)
67
-
68
- def test_missing_features(self):
69
- data = {
70
- "$schema": "dev-pipeline-feature-list-v1",
71
- "app_name": "Test",
72
- }
73
- errors = validate_schema(data)
74
- assert any("features" in e for e in errors)
75
-
76
- def test_features_not_array(self):
77
- data = {
78
- "$schema": "dev-pipeline-feature-list-v1",
79
- "app_name": "Test",
80
- "features": "not an array",
81
- }
82
- errors = validate_schema(data)
83
- assert any("features must be an array" in e for e in errors)
84
-
85
-
86
- class TestValidateFeatures:
87
- def test_valid_features(self, sample_feature_list):
88
- errors, ids = validate_features(sample_feature_list["features"])
89
- assert errors == []
90
- assert ids == {"F-001", "F-002", "F-003"}
91
-
92
- def test_duplicate_ids(self):
93
- features = [
94
- {"id": "F-001", "title": "A", "description": "D", "priority": 1,
95
- "dependencies": [], "acceptance_criteria": [], "status": "pending"},
96
- {"id": "F-001", "title": "B", "description": "D", "priority": 2,
97
- "dependencies": [], "acceptance_criteria": [], "status": "pending"},
98
- ]
99
- errors, ids = validate_features(features)
100
- assert any("Duplicate" in e for e in errors)
101
-
102
- def test_invalid_id_format(self):
103
- features = [
104
- {"id": "X-001", "title": "A", "description": "D", "priority": 1,
105
- "dependencies": [], "acceptance_criteria": [], "status": "pending"},
106
- ]
107
- errors, ids = validate_features(features)
108
- assert any("invalid id" in e for e in errors)
109
-
110
- def test_missing_required_fields(self):
111
- features = [{"id": "F-001"}]
112
- errors, ids = validate_features(features)
113
- # Should have errors for missing title, description, etc.
114
- assert len(errors) > 0
115
- assert any("missing required field" in e for e in errors)
116
-
117
- def test_non_object_feature(self):
118
- features = ["not a dict"]
119
- errors, ids = validate_features(features)
120
- assert any("not an object" in e for e in errors)
121
-
122
- def test_unknown_dependency(self):
123
- features = [
124
- {"id": "F-001", "title": "A", "description": "D", "priority": 1,
125
- "dependencies": ["F-999"], "acceptance_criteria": [], "status": "pending"},
126
- ]
127
- errors, ids = validate_features(features)
128
- assert any("unknown feature" in e for e in errors)
129
-
130
- def test_dependencies_not_array(self):
131
- features = [
132
- {"id": "F-001", "title": "A", "description": "D", "priority": 1,
133
- "dependencies": "not-a-list", "acceptance_criteria": [], "status": "pending"},
134
- ]
135
- errors, ids = validate_features(features)
136
- assert any("dependencies must be an array" in e for e in errors)
137
-
138
-
139
- class TestCheckDag:
140
- def test_simple_chain(self):
141
- features = [
142
- {"id": "F-001", "dependencies": []},
143
- {"id": "F-002", "dependencies": ["F-001"]},
144
- {"id": "F-003", "dependencies": ["F-002"]},
145
- ]
146
- errors = check_dag(features)
147
- assert errors == []
148
-
149
- def test_diamond_dependency(self):
150
- features = [
151
- {"id": "F-001", "dependencies": []},
152
- {"id": "F-002", "dependencies": ["F-001"]},
153
- {"id": "F-003", "dependencies": ["F-001"]},
154
- {"id": "F-004", "dependencies": ["F-002", "F-003"]},
155
- ]
156
- errors = check_dag(features)
157
- assert errors == []
158
-
159
- def test_circular_dependency(self):
160
- features = [
161
- {"id": "F-001", "dependencies": ["F-003"]},
162
- {"id": "F-002", "dependencies": ["F-001"]},
163
- {"id": "F-003", "dependencies": ["F-002"]},
164
- ]
165
- errors = check_dag(features)
166
- assert len(errors) == 1
167
- assert "cycle" in errors[0].lower()
168
-
169
- def test_self_dependency(self):
170
- features = [
171
- {"id": "F-001", "dependencies": ["F-001"]},
172
- ]
173
- errors = check_dag(features)
174
- assert len(errors) == 1
175
- assert "cycle" in errors[0].lower()
176
-
177
- def test_no_features(self):
178
- errors = check_dag([])
179
- assert errors == []
180
-
181
- def test_independent_features(self):
182
- features = [
183
- {"id": "F-001", "dependencies": []},
184
- {"id": "F-002", "dependencies": []},
185
- ]
186
- errors = check_dag(features)
187
- assert errors == []
188
-
189
-
190
- class TestCreateStateDirectory:
191
- def test_creates_expected_structure(self, tmp_path, sample_feature_list):
192
- state_dir = str(tmp_path / "state")
193
- fl_path = str(tmp_path / "feature-list.json")
194
- with open(fl_path, "w") as f:
195
- json.dump(sample_feature_list, f)
196
-
197
- result = create_state_directory(
198
- state_dir, fl_path, sample_feature_list["features"]
199
- )
200
-
201
- assert os.path.isdir(result)
202
- assert os.path.isfile(os.path.join(result, "pipeline.json"))
203
-
204
- # Check per-feature dirs
205
- for feature in sample_feature_list["features"]:
206
- fid = feature["id"]
207
- fdir = os.path.join(result, "features", fid)
208
- assert os.path.isdir(fdir)
209
- assert os.path.isfile(os.path.join(fdir, "status.json"))
210
- assert os.path.isdir(os.path.join(fdir, "sessions"))
211
-
212
- def test_pipeline_json_contents(self, tmp_path, sample_feature_list):
213
- state_dir = str(tmp_path / "state")
214
- fl_path = str(tmp_path / "fl.json")
215
- with open(fl_path, "w") as f:
216
- json.dump(sample_feature_list, f)
217
-
218
- create_state_directory(state_dir, fl_path, sample_feature_list["features"])
219
-
220
- with open(os.path.join(state_dir, "pipeline.json")) as f:
221
- pipeline = json.load(f)
222
-
223
- assert pipeline["status"] == "initialized"
224
- assert pipeline["total_features"] == 3
225
- assert pipeline["completed_features"] == 0
226
- assert pipeline["run_id"].startswith("run-")
227
-
228
- def test_feature_status_contents(self, tmp_path, sample_feature_list):
229
- state_dir = str(tmp_path / "state")
230
- fl_path = str(tmp_path / "fl.json")
231
- with open(fl_path, "w") as f:
232
- json.dump(sample_feature_list, f)
233
-
234
- create_state_directory(state_dir, fl_path, sample_feature_list["features"])
235
-
236
- with open(os.path.join(state_dir, "features", "F-001", "status.json")) as f:
237
- status = json.load(f)
238
-
239
- assert status["feature_id"] == "F-001"
240
- assert status["status"] == "pending"
241
- assert status["retry_count"] == 0
@@ -1,142 +0,0 @@
1
- """Tests for update-bug-status.py."""
2
-
3
- import json
4
- import os
5
- import re
6
- import sys
7
- import pytest
8
-
9
-
10
- def _import_update_bug_status():
11
- import importlib.util
12
- path = os.path.join(
13
- os.path.dirname(__file__), "..", "scripts", "update-bug-status.py"
14
- )
15
- spec = importlib.util.spec_from_file_location("update_bug_status", path)
16
- mod = importlib.util.module_from_spec(spec)
17
- sys.modules["update_bug_status"] = mod
18
- spec.loader.exec_module(mod)
19
- return mod
20
-
21
-
22
- ubs = _import_update_bug_status()
23
- now_iso = ubs.now_iso
24
- load_bug_status = ubs.load_bug_status
25
- save_bug_status = ubs.save_bug_status
26
- action_get_next = ubs.action_get_next
27
- SEVERITY_PRIORITY = ubs.SEVERITY_PRIORITY
28
-
29
-
30
- class TestNowIso:
31
- def test_valid_format(self):
32
- result = now_iso()
33
- pattern = r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$"
34
- assert re.match(pattern, result) is not None
35
-
36
-
37
- class TestLoadAndSaveBugStatus:
38
- def test_round_trip(self, bugfix_state_dir):
39
- bid = "B-001"
40
- bdir = os.path.join(bugfix_state_dir, "bugs", bid)
41
- os.makedirs(bdir, exist_ok=True)
42
-
43
- data = {
44
- "bug_id": bid,
45
- "status": "in_progress",
46
- "retry_count": 2,
47
- "max_retries": 3,
48
- "sessions": ["s-001"],
49
- "last_session_id": "s-001",
50
- "resume_from_phase": None,
51
- "created_at": "2024-01-01T00:00:00Z",
52
- "updated_at": "2024-01-01T01:00:00Z",
53
- }
54
- err = save_bug_status(bugfix_state_dir, bid, data)
55
- assert err is None
56
-
57
- loaded = load_bug_status(bugfix_state_dir, bid)
58
- assert loaded["bug_id"] == bid
59
- assert loaded["status"] == "in_progress"
60
- assert loaded["retry_count"] == 2
61
-
62
- def test_load_missing_returns_default(self, bugfix_state_dir):
63
- result = load_bug_status(bugfix_state_dir, "B-999")
64
- assert result["bug_id"] == "B-999"
65
- assert result["status"] == "pending"
66
- assert result["retry_count"] == 0
67
-
68
- def test_load_invalid_json_returns_default(self, bugfix_state_dir):
69
- bid = "B-BAD"
70
- bdir = os.path.join(bugfix_state_dir, "bugs", bid)
71
- os.makedirs(bdir, exist_ok=True)
72
- with open(os.path.join(bdir, "status.json"), "w") as f:
73
- f.write("not json!")
74
- result = load_bug_status(bugfix_state_dir, bid)
75
- assert result["status"] == "pending"
76
-
77
-
78
- class TestActionGetNext:
79
- def test_selects_critical_before_medium(self, bugfix_state_dir, capsys):
80
- bug_list = {
81
- "bugs": [
82
- {"id": "B-001", "title": "Low", "severity": "medium", "priority": 1},
83
- {"id": "B-002", "title": "High", "severity": "critical", "priority": 2},
84
- ]
85
- }
86
- action_get_next(bug_list, bugfix_state_dir)
87
- captured = capsys.readouterr()
88
- result = json.loads(captured.out)
89
- assert result["bug_id"] == "B-002"
90
- assert result["severity"] == "critical"
91
-
92
- def test_same_severity_sorted_by_priority(self, bugfix_state_dir, capsys):
93
- bug_list = {
94
- "bugs": [
95
- {"id": "B-001", "title": "P3", "severity": "high", "priority": 3},
96
- {"id": "B-002", "title": "P1", "severity": "high", "priority": 1},
97
- ]
98
- }
99
- action_get_next(bug_list, bugfix_state_dir)
100
- captured = capsys.readouterr()
101
- result = json.loads(captured.out)
102
- assert result["bug_id"] == "B-002"
103
-
104
- def test_all_completed_prints_pipeline_complete(self, bugfix_state_dir, capsys):
105
- bug_list = {"bugs": [{"id": "B-001", "title": "Done", "severity": "low"}]}
106
- # Mark as completed
107
- bdir = os.path.join(bugfix_state_dir, "bugs", "B-001")
108
- os.makedirs(bdir, exist_ok=True)
109
- with open(os.path.join(bdir, "status.json"), "w") as f:
110
- json.dump({"status": "completed"}, f)
111
- action_get_next(bug_list, bugfix_state_dir)
112
- captured = capsys.readouterr()
113
- assert "PIPELINE_COMPLETE" in captured.out
114
-
115
- def test_empty_bugs_prints_pipeline_complete(self, bugfix_state_dir, capsys):
116
- action_get_next({"bugs": []}, bugfix_state_dir)
117
- captured = capsys.readouterr()
118
- assert "PIPELINE_COMPLETE" in captured.out
119
-
120
- def test_prefers_in_progress_over_pending(self, bugfix_state_dir, capsys):
121
- bug_list = {
122
- "bugs": [
123
- {"id": "B-001", "title": "Pending", "severity": "critical", "priority": 1},
124
- {"id": "B-002", "title": "In Progress", "severity": "low", "priority": 2},
125
- ]
126
- }
127
- # Mark B-002 as in_progress
128
- bdir = os.path.join(bugfix_state_dir, "bugs", "B-002")
129
- os.makedirs(bdir, exist_ok=True)
130
- with open(os.path.join(bdir, "status.json"), "w") as f:
131
- json.dump({"status": "in_progress", "retry_count": 0}, f)
132
- action_get_next(bug_list, bugfix_state_dir)
133
- captured = capsys.readouterr()
134
- result = json.loads(captured.out)
135
- assert result["bug_id"] == "B-002"
136
-
137
-
138
- class TestSeverityPriority:
139
- def test_order(self):
140
- assert SEVERITY_PRIORITY["critical"] < SEVERITY_PRIORITY["high"]
141
- assert SEVERITY_PRIORITY["high"] < SEVERITY_PRIORITY["medium"]
142
- assert SEVERITY_PRIORITY["medium"] < SEVERITY_PRIORITY["low"]