prizmkit 1.0.58 → 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.
- package/bundled/VERSION.json +3 -3
- package/bundled/adapters/claude/command-adapter.js +1 -2
- package/bundled/dev-pipeline/README.md +4 -5
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +23 -17
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +17 -4
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +8 -11
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +12 -16
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +24 -41
- package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +26 -38
- package/bundled/dev-pipeline/templates/session-status-schema.json +1 -1
- package/bundled/dev-pipeline/tests/conftest.py +15 -10
- package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +168 -0
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bug-fix-workflow/SKILL.md +5 -8
- package/bundled/skills/bug-planner/SKILL.md +5 -1
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +156 -0
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +3 -3
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +1 -1
- package/bundled/skills/prizm-kit/SKILL.md +9 -7
- package/bundled/skills/prizm-kit/assets/project-memory-template.md +1 -1
- package/bundled/skills/prizmkit-analyze/SKILL.md +4 -5
- package/bundled/skills/prizmkit-code-review/SKILL.md +10 -2
- package/bundled/skills/prizmkit-committer/SKILL.md +1 -1
- package/bundled/skills/prizmkit-implement/SKILL.md +13 -3
- package/bundled/skills/prizmkit-plan/SKILL.md +17 -6
- package/bundled/skills/prizmkit-prizm-docs/SKILL.md +15 -1
- package/bundled/skills/prizmkit-retrospective/SKILL.md +1 -1
- package/bundled/skills/prizmkit-specify/SKILL.md +8 -4
- package/bundled/skills/refactor-workflow/SKILL.md +14 -6
- package/bundled/team/prizm-dev-team.json +1 -1
- package/package.json +1 -1
- package/src/scaffold.js +1 -1
|
@@ -18,7 +18,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
18
18
|
|
|
19
19
|
**CRITICAL SESSION LIFECYCLE RULE**: You MUST NOT exit until ALL work is complete and session-status.json is written. When you spawn subagents, you MUST **wait for each to finish** (run_in_background=false) before proceeding. Do NOT spawn an agent in the background and exit — that kills the session.
|
|
20
20
|
|
|
21
|
-
**MANDATORY TEAM REQUIREMENT**: You MUST use the `prizm-dev-team`
|
|
21
|
+
**MANDATORY TEAM REQUIREMENT**: You MUST use the `prizm-dev-team` agents (Dev + Reviewer). This is NON-NEGOTIABLE. All implementation and review work MUST be performed by the appropriate team agents (Dev, Reviewer). You are the orchestrator — handle coordination, planning, and commit phases directly.
|
|
22
22
|
|
|
23
23
|
**BUG FIX DOCUMENTATION POLICY**: Bug fixes MUST NOT be recorded as new documentation entries:
|
|
24
24
|
- Run `/prizmkit-retrospective` with structural sync only (Job 1) — skip knowledge injection unless a genuinely new TRAP was discovered
|
|
@@ -28,8 +28,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
28
28
|
|
|
29
29
|
### Team Definition Reference
|
|
30
30
|
|
|
31
|
-
- **
|
|
32
|
-
- **Installed team config**: `{{TEAM_CONFIG_PATH}}`
|
|
31
|
+
- **Team config**: `{{TEAM_CONFIG_PATH}}`
|
|
33
32
|
|
|
34
33
|
### Bug Description
|
|
35
34
|
|
|
@@ -74,16 +73,11 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
74
73
|
|
|
75
74
|
### Step 1: Initialize
|
|
76
75
|
|
|
77
|
-
####
|
|
76
|
+
#### Agent Setup
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
2. **If no reusable team**, create a new one:
|
|
84
|
-
- Reference `{{TEAM_CONFIG_PATH}}`
|
|
85
|
-
- Call `TeamCreate` with `team_name="prizm-dev-team-{{BUG_ID}}"` and `description="Fixing {{BUG_TITLE}}"`
|
|
86
|
-
- Set `TEAM_REUSED=false`
|
|
78
|
+
Reference `{{TEAM_CONFIG_PATH}}` for agent definitions:
|
|
79
|
+
- Dev: `{{DEV_SUBAGENT_PATH}}`
|
|
80
|
+
- Reviewer: `{{REVIEWER_SUBAGENT_PATH}}`
|
|
87
81
|
|
|
88
82
|
3. Create bug fix artifacts directory:
|
|
89
83
|
```bash
|
|
@@ -96,7 +90,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
96
90
|
|
|
97
91
|
**Goal**: Classify the bug, identify scope and severity, check known issues, produce fix-plan.md.
|
|
98
92
|
|
|
99
|
-
- Spawn Dev agent (
|
|
93
|
+
- Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false)
|
|
100
94
|
Prompt: "Read {{DEV_SUBAGENT_PATH}}. For bug {{BUG_ID}} ('{{BUG_TITLE}}'):
|
|
101
95
|
1. Run `/prizmkit-tool-error-triage` with the bug description and error source
|
|
102
96
|
2. Check `.prizm-docs/` TRAPS sections for matching known issues
|
|
@@ -133,7 +127,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
133
127
|
|
|
134
128
|
**Goal**: Create an automated reproduction that proves the bug exists.
|
|
135
129
|
|
|
136
|
-
- Spawn Dev agent (
|
|
130
|
+
- Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false)
|
|
137
131
|
Prompt: "Read {{DEV_SUBAGENT_PATH}}. For bug {{BUG_ID}}:
|
|
138
132
|
1. Read the fix plan from `.prizmkit/bugfix/{{BUG_ID}}/fix-plan.md`
|
|
139
133
|
2. Run `/prizmkit-tool-bug-reproducer` with the bug description and triage results
|
|
@@ -155,7 +149,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
155
149
|
|
|
156
150
|
**Goal**: Implement the fix. The reproduction test goes from red to green.
|
|
157
151
|
|
|
158
|
-
- Spawn Dev agent (
|
|
152
|
+
- Spawn Dev agent (Agent tool, subagent_type="prizm-dev-team-dev", run_in_background=false)
|
|
159
153
|
Prompt: "Read {{DEV_SUBAGENT_PATH}}. For bug {{BUG_ID}}:
|
|
160
154
|
1. Read the fix plan from `.prizmkit/bugfix/{{BUG_ID}}/fix-plan.md`
|
|
161
155
|
2. Read `.prizm-docs/` for affected modules (TRAPS, RULES, PATTERNS)
|
|
@@ -177,7 +171,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
177
171
|
|
|
178
172
|
**Goal**: Ensure fix correctness and no regressions.
|
|
179
173
|
|
|
180
|
-
- Spawn Reviewer agent (
|
|
174
|
+
- Spawn Reviewer agent (Agent tool, subagent_type="prizm-dev-team-reviewer", run_in_background=false)
|
|
181
175
|
Prompt: "Read {{REVIEWER_SUBAGENT_PATH}}. For bug {{BUG_ID}}:
|
|
182
176
|
1. Run `/prizmkit-code-review` scoped to CHANGED FILES ONLY
|
|
183
177
|
2. Review dimensions (adjusted for bug fix):
|
|
@@ -206,17 +200,19 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
206
200
|
|
|
207
201
|
**Goal**: Commit the fix, update TRAPS, generate fix-report.md.
|
|
208
202
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
203
|
+
**This phase is executed by YOU (the orchestrator), NOT a subagent.**
|
|
204
|
+
|
|
205
|
+
1. If a new pitfall was discovered (not previously in TRAPS):
|
|
206
|
+
- Update the affected module's TRAPS section in `.prizm-docs/`
|
|
207
|
+
- Format: `- TRAP: <description> | FIX: <solution> | DATE: YYYY-MM-DD`
|
|
208
|
+
|
|
209
|
+
2. Run `/prizmkit-committer` with:
|
|
210
|
+
- Commit message: `fix({{FIX_SCOPE}}): {{BUG_TITLE}}`
|
|
211
|
+
- Include both fix code and reproduction test
|
|
212
|
+
- Do NOT run `/prizmkit-retrospective` with REGISTRY archiving
|
|
213
|
+
- Do NOT push (user will push manually)
|
|
214
|
+
|
|
215
|
+
3. Write the complete fix report to `.prizmkit/bugfix/{{BUG_ID}}/fix-report.md`
|
|
220
216
|
|
|
221
217
|
The fix-report.md MUST contain these sections:
|
|
222
218
|
- Bug Resolution Summary (ID, title, status, phases completed, duration)
|
|
@@ -224,8 +220,7 @@ You are the **bug fix session orchestrator**. Fix Bug {{BUG_ID}}: "{{BUG_TITLE}}
|
|
|
224
220
|
- Verification Results (reproduction test before/after, regression tests, review verdict)
|
|
225
221
|
- Knowledge Captured (TRAPS updated, prevention recommendation)
|
|
226
222
|
- Acceptance Criteria Verification (checklist with pass/fail for each criterion)
|
|
227
|
-
|
|
228
|
-
- **Wait for Dev to return**
|
|
223
|
+
|
|
229
224
|
- **CP-BF-5**: Commit recorded, fix-report.md written, TRAPS updated (if applicable)
|
|
230
225
|
|
|
231
226
|
### Step 3: Report Session Status
|
|
@@ -258,17 +253,11 @@ Write to: `{{SESSION_STATUS_PATH}}`
|
|
|
258
253
|
|
|
259
254
|
**Status values**: `success` (all phases done) | `partial` (can resume) | `failed` (unrecoverable)
|
|
260
255
|
|
|
261
|
-
### Step 4: Team Cleanup (conditional)
|
|
262
|
-
|
|
263
|
-
**Only if you CREATED the team** (`TEAM_REUSED=false`), clean up with `TeamDelete`.
|
|
264
|
-
**If you REUSED an existing team** (`TEAM_REUSED=true`), do NOT call `TeamDelete`.
|
|
265
|
-
|
|
266
256
|
## Critical Paths
|
|
267
257
|
|
|
268
258
|
| Resource | Path |
|
|
269
259
|
|----------|------|
|
|
270
260
|
| Team Definition (source of truth) | `{{TEAM_CONFIG_PATH}}` |
|
|
271
|
-
| Team Config (installed) | `{{TEAM_CONFIG_PATH}}` |
|
|
272
261
|
| Bug Fix Artifacts Dir | `.prizmkit/bugfix/{{BUG_ID}}/` |
|
|
273
262
|
| Fix Plan | `.prizmkit/bugfix/{{BUG_ID}}/fix-plan.md` |
|
|
274
263
|
| Fix Report | `.prizmkit/bugfix/{{BUG_ID}}/fix-report.md` |
|
|
@@ -279,13 +268,12 @@ Write to: `{{SESSION_STATUS_PATH}}`
|
|
|
279
268
|
|
|
280
269
|
## Reminders
|
|
281
270
|
|
|
282
|
-
- **MANDATORY**: Use `prizm-dev-team` — single-agent execution is FORBIDDEN
|
|
271
|
+
- **MANDATORY**: Use `prizm-dev-team` agents — single-agent execution is FORBIDDEN
|
|
283
272
|
- **Only 2 artifact files per bug**: fix-plan.md + fix-report.md — NEVER more
|
|
284
|
-
- **Do NOT create** spec.md
|
|
273
|
+
- **Do NOT create** spec.md or plan.md for bug fixes
|
|
285
274
|
- **Do NOT run** `/prizmkit-retrospective` knowledge injection for bugs (structural sync only, unless genuinely new TRAP discovered)
|
|
286
275
|
- **Commit with** `fix(<scope>):` prefix, NOT `feat:`
|
|
287
276
|
- **Update TRAPS** in `.prizm-docs/` only if a genuinely new pitfall was discovered
|
|
288
277
|
- Dev agents use TDD approach: reproduction test goes from RED → GREEN
|
|
289
278
|
- ALWAYS write session-status.json before exiting
|
|
290
279
|
- Do NOT use `run_in_background=true` when spawning agents
|
|
291
|
-
- Only call `TeamDelete` if you created the team
|
|
@@ -9,13 +9,18 @@ import sys
|
|
|
9
9
|
_scripts_dir = os.path.join(os.path.dirname(__file__), "..", "scripts")
|
|
10
10
|
sys.path.insert(0, _scripts_dir)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
sys.modules[
|
|
21
|
-
|
|
12
|
+
|
|
13
|
+
def _load_hyphenated_module(module_name, filename):
|
|
14
|
+
"""Load a hyphenated .py file and register it as an importable module."""
|
|
15
|
+
spec = importlib.util.spec_from_file_location(
|
|
16
|
+
module_name,
|
|
17
|
+
os.path.join(_scripts_dir, filename),
|
|
18
|
+
)
|
|
19
|
+
mod = importlib.util.module_from_spec(spec)
|
|
20
|
+
sys.modules[module_name] = mod
|
|
21
|
+
spec.loader.exec_module(mod)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Register hyphenated script files so tests can import them with underscores.
|
|
25
|
+
_load_hyphenated_module("generate_bootstrap_prompt", "generate-bootstrap-prompt.py")
|
|
26
|
+
_load_hyphenated_module("generate_bugfix_prompt", "generate-bugfix-prompt.py")
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Tests for generate-bugfix-prompt.py core functions."""
|
|
2
|
+
|
|
3
|
+
from generate_bugfix_prompt import (
|
|
4
|
+
find_bug,
|
|
5
|
+
format_acceptance_criteria,
|
|
6
|
+
format_global_context,
|
|
7
|
+
format_error_source_details,
|
|
8
|
+
format_environment,
|
|
9
|
+
process_conditional_blocks,
|
|
10
|
+
render_template,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# find_bug
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
class TestFindBug:
|
|
19
|
+
def test_found(self):
|
|
20
|
+
bugs = [{"id": "B-001", "title": "A"}, {"id": "B-002", "title": "B"}]
|
|
21
|
+
assert find_bug(bugs, "B-002") == {"id": "B-002", "title": "B"}
|
|
22
|
+
|
|
23
|
+
def test_not_found(self):
|
|
24
|
+
assert find_bug([{"id": "B-001"}], "B-999") is None
|
|
25
|
+
|
|
26
|
+
def test_empty_list(self):
|
|
27
|
+
assert find_bug([], "B-001") is None
|
|
28
|
+
|
|
29
|
+
def test_non_dict_items(self):
|
|
30
|
+
assert find_bug(["garbage", 42, None, {"id": "B-001"}], "B-001") == {"id": "B-001"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# format_error_source_details
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
class TestFormatErrorSourceDetails:
|
|
38
|
+
def test_none(self):
|
|
39
|
+
result = format_error_source_details(None)
|
|
40
|
+
assert "no error source" in result
|
|
41
|
+
|
|
42
|
+
def test_empty_dict(self):
|
|
43
|
+
result = format_error_source_details({})
|
|
44
|
+
assert "no error source" in result
|
|
45
|
+
|
|
46
|
+
def test_stack_trace(self):
|
|
47
|
+
source = {"type": "stack_trace", "stack_trace": "Error at line 42"}
|
|
48
|
+
result = format_error_source_details(source)
|
|
49
|
+
assert "Error at line 42" in result
|
|
50
|
+
assert "Stack Trace" in result
|
|
51
|
+
|
|
52
|
+
def test_error_message(self):
|
|
53
|
+
source = {"type": "unknown", "error_message": "Something went wrong"}
|
|
54
|
+
result = format_error_source_details(source)
|
|
55
|
+
assert "Something went wrong" in result
|
|
56
|
+
|
|
57
|
+
def test_log_pattern(self):
|
|
58
|
+
source = {"type": "log_pattern", "log_snippet": "FATAL: connection refused"}
|
|
59
|
+
result = format_error_source_details(source)
|
|
60
|
+
assert "connection refused" in result
|
|
61
|
+
|
|
62
|
+
def test_failed_test(self):
|
|
63
|
+
source = {"type": "failed_test", "failed_test_path": "tests/auth.test.js"}
|
|
64
|
+
result = format_error_source_details(source)
|
|
65
|
+
assert "tests/auth.test.js" in result
|
|
66
|
+
|
|
67
|
+
def test_user_report(self):
|
|
68
|
+
source = {"type": "user_report", "reproduction_steps": ["Click login", "Enter bad password"]}
|
|
69
|
+
result = format_error_source_details(source)
|
|
70
|
+
assert "Click login" in result
|
|
71
|
+
assert "Enter bad password" in result
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
# format_environment
|
|
76
|
+
# ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
class TestFormatEnvironment:
|
|
79
|
+
def test_none(self):
|
|
80
|
+
assert "not specified" in format_environment(None)
|
|
81
|
+
|
|
82
|
+
def test_empty(self):
|
|
83
|
+
assert "not specified" in format_environment({})
|
|
84
|
+
|
|
85
|
+
def test_with_values(self):
|
|
86
|
+
result = format_environment({"os": "Linux", "node": "20.1"})
|
|
87
|
+
assert "**node**: 20.1" in result
|
|
88
|
+
assert "**os**: Linux" in result
|
|
89
|
+
|
|
90
|
+
def test_skips_empty_values(self):
|
|
91
|
+
result = format_environment({"os": "Linux", "browser": ""})
|
|
92
|
+
assert "**os**: Linux" in result
|
|
93
|
+
assert "browser" not in result
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# process_conditional_blocks
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
class TestProcessConditionalBlocks:
|
|
101
|
+
def test_automated_removes_manual_block(self):
|
|
102
|
+
tpl = "before\n{{IF_VERIFICATION_MANUAL_OR_HYBRID}}\nmanual content\n{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}\nafter"
|
|
103
|
+
bug = {"verification_type": "automated"}
|
|
104
|
+
result = process_conditional_blocks(tpl, bug)
|
|
105
|
+
assert "manual content" not in result
|
|
106
|
+
assert "before" in result
|
|
107
|
+
assert "after" in result
|
|
108
|
+
|
|
109
|
+
def test_manual_keeps_block(self):
|
|
110
|
+
tpl = "before\n{{IF_VERIFICATION_MANUAL_OR_HYBRID}}\nmanual content\n{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}\nafter"
|
|
111
|
+
bug = {"verification_type": "manual"}
|
|
112
|
+
result = process_conditional_blocks(tpl, bug)
|
|
113
|
+
assert "manual content" in result
|
|
114
|
+
assert "IF_VERIFICATION" not in result
|
|
115
|
+
|
|
116
|
+
def test_hybrid_keeps_block(self):
|
|
117
|
+
tpl = "{{IF_VERIFICATION_MANUAL_OR_HYBRID}}hybrid{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}"
|
|
118
|
+
bug = {"verification_type": "hybrid"}
|
|
119
|
+
result = process_conditional_blocks(tpl, bug)
|
|
120
|
+
assert "hybrid" in result
|
|
121
|
+
|
|
122
|
+
def test_default_is_automated(self):
|
|
123
|
+
tpl = "{{IF_VERIFICATION_MANUAL_OR_HYBRID}}content{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}"
|
|
124
|
+
bug = {}
|
|
125
|
+
result = process_conditional_blocks(tpl, bug)
|
|
126
|
+
assert "content" not in result
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ---------------------------------------------------------------------------
|
|
130
|
+
# render_template (integration)
|
|
131
|
+
# ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
class TestRenderTemplate:
|
|
134
|
+
def test_placeholders_replaced(self):
|
|
135
|
+
tpl = "Bug: {{BUG_ID}} — {{BUG_TITLE}}"
|
|
136
|
+
replacements = {"{{BUG_ID}}": "B-042", "{{BUG_TITLE}}": "Login crash"}
|
|
137
|
+
bug = {"verification_type": "automated"}
|
|
138
|
+
result = render_template(tpl, replacements, bug)
|
|
139
|
+
assert result == "Bug: B-042 — Login crash"
|
|
140
|
+
|
|
141
|
+
def test_conditional_and_replacement(self):
|
|
142
|
+
tpl = (
|
|
143
|
+
"{{IF_VERIFICATION_MANUAL_OR_HYBRID}}manual{{END_IF_VERIFICATION_MANUAL_OR_HYBRID}}"
|
|
144
|
+
"id={{BUG_ID}}"
|
|
145
|
+
)
|
|
146
|
+
replacements = {"{{BUG_ID}}": "B-001"}
|
|
147
|
+
bug = {"verification_type": "automated"}
|
|
148
|
+
result = render_template(tpl, replacements, bug)
|
|
149
|
+
assert "manual" not in result
|
|
150
|
+
assert "id=B-001" in result
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ---------------------------------------------------------------------------
|
|
154
|
+
# Reuse shared helpers (same as bootstrap prompt — verify they are present)
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
class TestSharedHelpers:
|
|
158
|
+
def test_format_acceptance_criteria_items(self):
|
|
159
|
+
result = format_acceptance_criteria(["Fix works", "No regression"])
|
|
160
|
+
assert "- Fix works" in result
|
|
161
|
+
assert "- No regression" in result
|
|
162
|
+
|
|
163
|
+
def test_format_acceptance_criteria_empty(self):
|
|
164
|
+
assert "none specified" in format_acceptance_criteria([])
|
|
165
|
+
|
|
166
|
+
def test_format_global_context_dict(self):
|
|
167
|
+
result = format_global_context({"lang": "Python"})
|
|
168
|
+
assert "**lang**: Python" in result
|
|
@@ -112,15 +112,13 @@ If the fix causes test regressions:
|
|
|
112
112
|
|
|
113
113
|
**Goal**: Commit the fix with proper conventions.
|
|
114
114
|
|
|
115
|
-
1. **Run `/prizmkit-
|
|
116
|
-
- Update `.prizm-docs/` if file structure changed
|
|
117
|
-
- Add TRAPS entry only if a genuinely new pitfall was discovered
|
|
118
|
-
- Skip knowledge injection for routine bug fixes
|
|
119
|
-
2. **Run `/prizmkit-committer`**:
|
|
115
|
+
1. **Run `/prizmkit-committer`**:
|
|
120
116
|
- Commit message: `fix(<scope>): <description>`
|
|
121
117
|
- Include both fix code and reproduction test
|
|
122
118
|
- Do NOT push (user decides when to push)
|
|
123
|
-
|
|
119
|
+
- Do NOT run `/prizmkit-retrospective` — bug fixes do not update `.prizm-docs/` (per project rules: "bugs are incomplete features, recording bug details causes doc bloat with no AI value")
|
|
120
|
+
- `/prizmkit-committer` is a pure commit tool — it does NOT modify `.prizm-docs/` or any project files
|
|
121
|
+
2. **If bug came from bug-fix-list.json**: inform user to update bug status
|
|
124
122
|
```
|
|
125
123
|
Bug B-001 fixed and committed.
|
|
126
124
|
To update the bug list: manually set B-001 status to "fixed" in bug-fix-list.json
|
|
@@ -163,7 +161,7 @@ Only 2 artifact files per bug, consistent with the pipeline convention.
|
|
|
163
161
|
| `bug-planner` | **this skill** | User picks one bug to fix interactively |
|
|
164
162
|
| `bugfix-pipeline-launcher` | **this skill** | User wants to fix a stuck/complex bug manually |
|
|
165
163
|
| **this skill** | `bugfix-pipeline-launcher` | After fixing, user wants to continue with remaining bugs |
|
|
166
|
-
| **this skill** | `prizmkit-
|
|
164
|
+
| **this skill** | `prizmkit-committer` | Built into Phase 5 (pure commit, no doc sync) |
|
|
167
165
|
|
|
168
166
|
## Output
|
|
169
167
|
|
|
@@ -171,4 +169,3 @@ Only 2 artifact files per bug, consistent with the pipeline convention.
|
|
|
171
169
|
- `.prizmkit/bugfix/<BUG_ID>/fix-plan.md`
|
|
172
170
|
- `.prizmkit/bugfix/<BUG_ID>/fix-report.md`
|
|
173
171
|
- Git commit with `fix(<scope>):` prefix
|
|
174
|
-
- Updated `.prizm-docs/` TRAPS (if new pitfall discovered)
|
|
@@ -135,7 +135,11 @@ ALERT: Error rate spike: 500 errors/min on /api/login endpoint
|
|
|
135
135
|
### Phase 4: Generate & Validate
|
|
136
136
|
|
|
137
137
|
1. **Generate `bug-fix-list.json`**: Conform to `dev-pipeline/templates/bug-fix-list-schema.json`
|
|
138
|
-
2. **Validate against schema**: Run the validation
|
|
138
|
+
2. **Validate against schema**: Run the validation script:
|
|
139
|
+
```bash
|
|
140
|
+
python3 ${SKILL_DIR}/scripts/validate-bug-list.py bug-fix-list.json --feature-list feature-list.json
|
|
141
|
+
```
|
|
142
|
+
If the script is not available, perform the validation checks manually (see checklist below).
|
|
139
143
|
3. **Write file** to project root (or user-specified path)
|
|
140
144
|
4. **Output**: File path, summary, and next steps
|
|
141
145
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate bug-fix-list.json against the PrizmKit bug-fix-list schema.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python3 validate-bug-list.py [bug-fix-list.json] [--feature-list feature-list.json]
|
|
7
|
+
|
|
8
|
+
Exit codes:
|
|
9
|
+
0 = valid
|
|
10
|
+
1 = validation errors found
|
|
11
|
+
2 = file not found or JSON parse error
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
|
|
19
|
+
VALID_SEVERITIES = {"critical", "high", "medium", "low"}
|
|
20
|
+
VALID_SOURCE_TYPES = {"stack_trace", "user_report", "failed_test", "log_pattern", "monitoring_alert"}
|
|
21
|
+
VALID_VERIFICATION_TYPES = {"automated", "manual", "hybrid"}
|
|
22
|
+
VALID_STATUSES = {"pending", "in_progress", "fixed", "failed", "skipped", "needs_info"}
|
|
23
|
+
BUG_ID_PATTERN = re.compile(r"^B-\d{3}$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate(bug_list_path, feature_list_path=None):
|
|
27
|
+
errors = []
|
|
28
|
+
warnings = []
|
|
29
|
+
|
|
30
|
+
# Load bug-fix-list.json
|
|
31
|
+
try:
|
|
32
|
+
with open(bug_list_path) as f:
|
|
33
|
+
data = json.load(f)
|
|
34
|
+
except FileNotFoundError:
|
|
35
|
+
print(f"ERROR: File not found: {bug_list_path}", file=sys.stderr)
|
|
36
|
+
return 2
|
|
37
|
+
except json.JSONDecodeError as e:
|
|
38
|
+
print(f"ERROR: Invalid JSON in {bug_list_path}: {e}", file=sys.stderr)
|
|
39
|
+
return 2
|
|
40
|
+
|
|
41
|
+
# Load feature-list.json (optional, for cross-reference)
|
|
42
|
+
feature_ids = set()
|
|
43
|
+
if feature_list_path:
|
|
44
|
+
try:
|
|
45
|
+
with open(feature_list_path) as f:
|
|
46
|
+
fl_data = json.load(f)
|
|
47
|
+
feature_ids = {f.get("id") for f in fl_data.get("features", [])}
|
|
48
|
+
except (FileNotFoundError, json.JSONDecodeError):
|
|
49
|
+
warnings.append(f"Could not load feature-list.json at {feature_list_path}")
|
|
50
|
+
|
|
51
|
+
# Top-level required fields
|
|
52
|
+
if "$schema" not in data:
|
|
53
|
+
errors.append("Missing required field: $schema")
|
|
54
|
+
elif data["$schema"] != "dev-pipeline-bug-fix-list-v1":
|
|
55
|
+
errors.append(f"Invalid $schema: expected 'dev-pipeline-bug-fix-list-v1', got '{data['$schema']}'")
|
|
56
|
+
|
|
57
|
+
if not data.get("project_name"):
|
|
58
|
+
errors.append("Missing or empty required field: project_name")
|
|
59
|
+
|
|
60
|
+
bugs = data.get("bugs", [])
|
|
61
|
+
if not bugs:
|
|
62
|
+
errors.append("Missing or empty required field: bugs")
|
|
63
|
+
|
|
64
|
+
# Per-bug validation
|
|
65
|
+
seen_ids = set()
|
|
66
|
+
seen_priorities = set()
|
|
67
|
+
|
|
68
|
+
for i, bug in enumerate(bugs):
|
|
69
|
+
prefix = f"bugs[{i}]"
|
|
70
|
+
|
|
71
|
+
# Required fields
|
|
72
|
+
bug_id = bug.get("id", "")
|
|
73
|
+
if not bug_id:
|
|
74
|
+
errors.append(f"{prefix}: missing required field 'id'")
|
|
75
|
+
elif not BUG_ID_PATTERN.match(bug_id):
|
|
76
|
+
errors.append(f"{prefix}: id '{bug_id}' does not match pattern B-NNN")
|
|
77
|
+
|
|
78
|
+
if bug_id in seen_ids:
|
|
79
|
+
errors.append(f"{prefix}: duplicate bug id '{bug_id}'")
|
|
80
|
+
seen_ids.add(bug_id)
|
|
81
|
+
|
|
82
|
+
if not bug.get("title"):
|
|
83
|
+
errors.append(f"{prefix} ({bug_id}): missing required field 'title'")
|
|
84
|
+
|
|
85
|
+
if not bug.get("description"):
|
|
86
|
+
errors.append(f"{prefix} ({bug_id}): missing required field 'description'")
|
|
87
|
+
|
|
88
|
+
severity = bug.get("severity", "")
|
|
89
|
+
if severity not in VALID_SEVERITIES:
|
|
90
|
+
errors.append(f"{prefix} ({bug_id}): invalid severity '{severity}' — must be one of {VALID_SEVERITIES}")
|
|
91
|
+
|
|
92
|
+
# error_source
|
|
93
|
+
error_source = bug.get("error_source", {})
|
|
94
|
+
source_type = error_source.get("type", "") if isinstance(error_source, dict) else ""
|
|
95
|
+
if source_type not in VALID_SOURCE_TYPES:
|
|
96
|
+
errors.append(f"{prefix} ({bug_id}): invalid error_source.type '{source_type}' — must be one of {VALID_SOURCE_TYPES}")
|
|
97
|
+
|
|
98
|
+
# verification_type
|
|
99
|
+
vtype = bug.get("verification_type", "")
|
|
100
|
+
if vtype not in VALID_VERIFICATION_TYPES:
|
|
101
|
+
errors.append(f"{prefix} ({bug_id}): invalid verification_type '{vtype}' — must be one of {VALID_VERIFICATION_TYPES}")
|
|
102
|
+
|
|
103
|
+
# acceptance_criteria
|
|
104
|
+
ac = bug.get("acceptance_criteria", [])
|
|
105
|
+
if not ac or not isinstance(ac, list):
|
|
106
|
+
errors.append(f"{prefix} ({bug_id}): missing or empty acceptance_criteria array")
|
|
107
|
+
|
|
108
|
+
# status
|
|
109
|
+
status = bug.get("status", "")
|
|
110
|
+
if status not in VALID_STATUSES:
|
|
111
|
+
errors.append(f"{prefix} ({bug_id}): invalid status '{status}' — must be one of {VALID_STATUSES}")
|
|
112
|
+
|
|
113
|
+
# Priority uniqueness
|
|
114
|
+
priority = bug.get("priority")
|
|
115
|
+
if priority is not None:
|
|
116
|
+
if priority in seen_priorities:
|
|
117
|
+
warnings.append(f"{prefix} ({bug_id}): duplicate priority {priority}")
|
|
118
|
+
seen_priorities.add(priority)
|
|
119
|
+
|
|
120
|
+
# Cross-reference affected_feature
|
|
121
|
+
affected_feature = bug.get("affected_feature")
|
|
122
|
+
if affected_feature and feature_ids and affected_feature not in feature_ids:
|
|
123
|
+
warnings.append(f"{prefix} ({bug_id}): affected_feature '{affected_feature}' not found in feature-list.json")
|
|
124
|
+
|
|
125
|
+
# Output results
|
|
126
|
+
if errors:
|
|
127
|
+
print(f"VALIDATION FAILED — {len(errors)} error(s), {len(warnings)} warning(s)\n")
|
|
128
|
+
for e in errors:
|
|
129
|
+
print(f" ERROR: {e}")
|
|
130
|
+
for w in warnings:
|
|
131
|
+
print(f" WARN: {w}")
|
|
132
|
+
return 1
|
|
133
|
+
else:
|
|
134
|
+
bug_count = len(bugs)
|
|
135
|
+
severity_counts = {}
|
|
136
|
+
for b in bugs:
|
|
137
|
+
s = b.get("severity", "unknown")
|
|
138
|
+
severity_counts[s] = severity_counts.get(s, 0) + 1
|
|
139
|
+
sev_str = ", ".join(f"{k}={v}" for k, v in sorted(severity_counts.items()))
|
|
140
|
+
print(f"VALIDATION PASSED — {bug_count} bugs ({sev_str})")
|
|
141
|
+
if warnings:
|
|
142
|
+
for w in warnings:
|
|
143
|
+
print(f" WARN: {w}")
|
|
144
|
+
return 0
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == "__main__":
|
|
148
|
+
bug_list = sys.argv[1] if len(sys.argv) > 1 else "bug-fix-list.json"
|
|
149
|
+
feature_list = None
|
|
150
|
+
|
|
151
|
+
if "--feature-list" in sys.argv:
|
|
152
|
+
idx = sys.argv.index("--feature-list")
|
|
153
|
+
if idx + 1 < len(sys.argv):
|
|
154
|
+
feature_list = sys.argv[idx + 1]
|
|
155
|
+
|
|
156
|
+
sys.exit(validate(bug_list, feature_list))
|
|
@@ -5,11 +5,11 @@ description: "Launch and manage the bugfix pipeline from within an AI CLI sessio
|
|
|
5
5
|
|
|
6
6
|
# Bugfix-Pipeline Launcher
|
|
7
7
|
|
|
8
|
-
Launch the autonomous bug fix pipeline from within
|
|
8
|
+
Launch the autonomous bug fix pipeline from within an AI CLI conversation. The pipeline runs as a fully detached background process -- closing the AI CLI session does NOT stop the pipeline.
|
|
9
9
|
|
|
10
10
|
### Execution Mode
|
|
11
11
|
|
|
12
|
-
Always use daemon mode via `dev-pipeline/launch-bugfix-daemon.sh` for start/stop/status/log actions. Foreground `run-bugfix.sh` can be terminated by AI CLI command timeout
|
|
12
|
+
Always use daemon mode via `dev-pipeline/launch-bugfix-daemon.sh` for start/stop/status/log actions. Foreground `run-bugfix.sh` can be terminated by AI CLI command timeout, while daemon mode survives session timeout — this prevents half-finished bug fixes that leave the codebase in an inconsistent state.
|
|
13
13
|
|
|
14
14
|
### When to Use
|
|
15
15
|
|
|
@@ -246,7 +246,7 @@ SESSION_TIMEOUT=3600 dev-pipeline/retry-bug.sh B-001 bug-fix-list.json
|
|
|
246
246
|
### Integration Notes
|
|
247
247
|
|
|
248
248
|
- **After bug-planner**: This is the natural next step. When user finishes bug planning and has `bug-fix-list.json`, suggest launching the bugfix pipeline.
|
|
249
|
-
- **Session independence**: The bugfix pipeline runs completely detached. User can close
|
|
249
|
+
- **Session independence**: The bugfix pipeline runs completely detached. User can close the AI CLI, open a new session later, and use this skill to check progress or stop the pipeline.
|
|
250
250
|
- **Single instance**: Only one bugfix pipeline can run at a time. The PID file prevents duplicates.
|
|
251
251
|
- **Feature pipeline coexistence**: Bugfix and feature pipelines use separate state directories (`bugfix-state/` vs `state/`), so they can run simultaneously without conflict.
|
|
252
252
|
- **State preservation**: Stopping and restarting the bugfix pipeline resumes from where it left off -- completed bugs are not re-fixed.
|
|
@@ -9,7 +9,7 @@ Launch the autonomous development pipeline from within an AI CLI conversation. T
|
|
|
9
9
|
|
|
10
10
|
### Execution Mode
|
|
11
11
|
|
|
12
|
-
Always use daemon mode via `dev-pipeline/launch-daemon.sh` for start/stop/status/log actions. Foreground `run.sh` can be terminated by AI CLI
|
|
12
|
+
Always use daemon mode via `dev-pipeline/launch-daemon.sh` for start/stop/status/log actions. Foreground `run.sh` can be terminated by AI CLI session timeout, while daemon mode survives session closure — this prevents half-finished features that leave the codebase in an inconsistent state.
|
|
13
13
|
|
|
14
14
|
### When to Use
|
|
15
15
|
|
|
@@ -27,16 +27,16 @@ PrizmKit is a comprehensive, independent AI development toolkit that covers the
|
|
|
27
27
|
|
|
28
28
|
The full workflow generates spec, plan, and task artifacts that create a traceable record of what was built and why — this matters for future maintainability and AI context loading.
|
|
29
29
|
|
|
30
|
-
**Use fast path (implement
|
|
30
|
+
**Use fast path (plan → implement → commit):**
|
|
31
31
|
- Bug fixes with clear root cause
|
|
32
32
|
- Single-file config or typo fixes
|
|
33
33
|
- Simple refactors (rename, extract)
|
|
34
34
|
- Documentation-only changes
|
|
35
35
|
- Test additions for existing code
|
|
36
36
|
|
|
37
|
-
The fast path skips
|
|
37
|
+
The fast path skips specify and analyze but still generates a simplified plan.md (with Tasks section) so that implement has a task list to follow.
|
|
38
38
|
|
|
39
|
-
For fast-path changes, you can directly
|
|
39
|
+
For fast-path changes, you can directly generate a simplified plan.md, then use implement and commit commands.
|
|
40
40
|
|
|
41
41
|
## Workflow Example
|
|
42
42
|
|
|
@@ -47,17 +47,19 @@ For fast-path changes, you can directly use the implement command with inline ta
|
|
|
47
47
|
/prizmkit-analyze → checks spec↔plan consistency, finds gaps
|
|
48
48
|
/prizmkit-implement → executes tasks in order, marks [x] as done
|
|
49
49
|
/prizmkit-code-review → reviews against spec, outputs PASS/NEEDS FIXES
|
|
50
|
-
/prizmkit-
|
|
50
|
+
/prizmkit-retrospective → syncs .prizm-docs/ with code changes
|
|
51
|
+
/prizmkit-committer → commits feat(avatar): add upload
|
|
51
52
|
```
|
|
52
53
|
|
|
53
54
|
**Fast path** for fixing a null pointer bug:
|
|
54
55
|
```
|
|
55
|
-
/prizmkit-
|
|
56
|
+
/prizmkit-plan → "fix null check in UserService.getAvatar()" (simplified plan.md)
|
|
57
|
+
/prizmkit-implement → executes tasks from plan.md
|
|
56
58
|
/prizmkit-committer → commits fix(user): handle null avatar gracefully
|
|
57
59
|
```
|
|
58
60
|
|
|
59
61
|
### Fast Path Commands
|
|
60
|
-
`/prizmkit-implement` → `/prizmkit-committer`
|
|
62
|
+
`/prizmkit-plan` → `/prizmkit-implement` → `/prizmkit-committer`
|
|
61
63
|
|
|
62
64
|
### Bug Fix Documentation Policy
|
|
63
65
|
|
|
@@ -140,7 +142,7 @@ Not sure which skill to use? Follow this:
|
|
|
140
142
|
| Fix one specific bug right now, interactively | `bug-fix-workflow` |
|
|
141
143
|
| Refactor/restructure code without changing behavior | `refactor-workflow` |
|
|
142
144
|
| Add a single small feature (spec → plan → implement) | `/prizmkit-specify` → `/prizmkit-plan` → `/prizmkit-implement` |
|
|
143
|
-
| Quick bug fix or config change | Fast path: `/prizmkit-implement` → `/prizmkit-committer` |
|
|
145
|
+
| Quick bug fix or config change | Fast path: `/prizmkit-plan` → `/prizmkit-implement` → `/prizmkit-committer` |
|
|
144
146
|
|
|
145
147
|
### Tier Definitions
|
|
146
148
|
|