prizmkit 1.1.48 → 1.1.49
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/dev-pipeline/scripts/update-checkpoint.py +173 -0
- package/bundled/dev-pipeline/templates/sections/checkpoint-system.md +39 -9
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification-auto.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification-opencli.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +14 -1
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +14 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan-full.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-critic-plan.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-full.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-implement-lite.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-plan-agent.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-plan-lite.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-review-full.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase0-init.md +7 -1
- package/bundled/dev-pipeline/templates/sections/phase0-test-baseline.md +7 -1
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bug-planner/SKILL.md +17 -5
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +234 -64
- package/bundled/skills/feature-planner/SKILL.md +10 -4
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +82 -0
- package/bundled/skills/refactor-planner/SKILL.md +11 -6
- package/bundled/skills/refactor-planner/scripts/validate-and-generate-refactor.py +72 -0
- package/package.json +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Safely update a workflow-checkpoint.json step status.
|
|
3
|
+
|
|
4
|
+
Instead of having the AI hand-write JSON, this script reads the checkpoint,
|
|
5
|
+
validates the update, and writes it back atomically.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python3 update-checkpoint.py --checkpoint-path <path> --step <skill-or-id> --status <status>
|
|
9
|
+
python3 update-checkpoint.py --checkpoint-path <path> --step prizmkit-implement --status completed
|
|
10
|
+
python3 update-checkpoint.py --checkpoint-path <path> --step S04 --status in_progress
|
|
11
|
+
python3 update-checkpoint.py --checkpoint-path <path> --step prizmkit-code-review --status failed --note "max rounds exhausted"
|
|
12
|
+
|
|
13
|
+
Exit codes:
|
|
14
|
+
0 = success
|
|
15
|
+
1 = validation error (step not found, invalid status, etc.)
|
|
16
|
+
2 = file error (not found, corrupted JSON)
|
|
17
|
+
|
|
18
|
+
Output (stdout): JSON with {ok: true, step_id, skill, old_status, new_status} or {ok: false, error}
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import sys
|
|
25
|
+
import tempfile
|
|
26
|
+
|
|
27
|
+
VALID_STATUSES = {"pending", "in_progress", "completed", "skipped", "failed"}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _load_checkpoint(path):
|
|
31
|
+
"""Load checkpoint JSON. Returns (data, error_string)."""
|
|
32
|
+
if not os.path.isfile(path):
|
|
33
|
+
return None, "File not found: {}".format(path)
|
|
34
|
+
try:
|
|
35
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
36
|
+
data = json.load(f)
|
|
37
|
+
except json.JSONDecodeError as e:
|
|
38
|
+
return None, "Corrupted JSON: {}".format(e)
|
|
39
|
+
except IOError as e:
|
|
40
|
+
return None, "Cannot read file: {}".format(e)
|
|
41
|
+
|
|
42
|
+
if "steps" not in data or not isinstance(data["steps"], list):
|
|
43
|
+
return None, "Invalid checkpoint: missing 'steps' array"
|
|
44
|
+
return data, None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _write_checkpoint(path, data):
|
|
48
|
+
"""Write checkpoint JSON atomically using a temp file + rename."""
|
|
49
|
+
parent = os.path.dirname(os.path.abspath(path))
|
|
50
|
+
if parent and not os.path.isdir(parent):
|
|
51
|
+
os.makedirs(parent, exist_ok=True)
|
|
52
|
+
|
|
53
|
+
tmp_path = None
|
|
54
|
+
try:
|
|
55
|
+
fd, tmp_path = tempfile.mkstemp(
|
|
56
|
+
dir=parent, suffix=".tmp", prefix=".checkpoint-"
|
|
57
|
+
)
|
|
58
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
59
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
60
|
+
f.write("\n")
|
|
61
|
+
os.replace(tmp_path, os.path.abspath(path))
|
|
62
|
+
except (IOError, OSError) as e:
|
|
63
|
+
# Clean up temp file on error
|
|
64
|
+
if tmp_path is not None and os.path.exists(tmp_path):
|
|
65
|
+
os.unlink(tmp_path)
|
|
66
|
+
return "Cannot write file: {}".format(e)
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _find_step(steps, identifier):
|
|
71
|
+
"""Find a step by skill name or step ID.
|
|
72
|
+
|
|
73
|
+
Returns (index, step_dict) or (None, None).
|
|
74
|
+
"""
|
|
75
|
+
for i, step in enumerate(steps):
|
|
76
|
+
if step.get("skill") == identifier or step.get("id") == identifier:
|
|
77
|
+
return i, step
|
|
78
|
+
return None, None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def update_checkpoint(checkpoint_path, step_identifier, new_status, note=None):
|
|
82
|
+
"""Update a single step's status in the checkpoint file.
|
|
83
|
+
|
|
84
|
+
Returns a result dict: {ok, step_id, skill, old_status, new_status} or {ok, error}.
|
|
85
|
+
"""
|
|
86
|
+
if new_status not in VALID_STATUSES:
|
|
87
|
+
return {
|
|
88
|
+
"ok": False,
|
|
89
|
+
"error": "Invalid status '{}'. Must be one of: {}".format(
|
|
90
|
+
new_status, ", ".join(sorted(VALID_STATUSES))
|
|
91
|
+
),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
data, err = _load_checkpoint(checkpoint_path)
|
|
95
|
+
if err:
|
|
96
|
+
return {"ok": False, "error": err}
|
|
97
|
+
|
|
98
|
+
idx, step = _find_step(data["steps"], step_identifier)
|
|
99
|
+
if step is None:
|
|
100
|
+
available = [
|
|
101
|
+
"{} ({})".format(s.get("id", "?"), s.get("skill", "?"))
|
|
102
|
+
for s in data["steps"]
|
|
103
|
+
]
|
|
104
|
+
return {
|
|
105
|
+
"ok": False,
|
|
106
|
+
"error": "Step '{}' not found. Available: {}".format(
|
|
107
|
+
step_identifier, ", ".join(available)
|
|
108
|
+
),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
old_status = step.get("status", "unknown")
|
|
112
|
+
step["status"] = new_status
|
|
113
|
+
|
|
114
|
+
if note:
|
|
115
|
+
step["note"] = note
|
|
116
|
+
|
|
117
|
+
err = _write_checkpoint(checkpoint_path, data)
|
|
118
|
+
if err:
|
|
119
|
+
return {"ok": False, "error": err}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
"ok": True,
|
|
123
|
+
"step_id": step.get("id"),
|
|
124
|
+
"skill": step.get("skill"),
|
|
125
|
+
"old_status": old_status,
|
|
126
|
+
"new_status": new_status,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main():
|
|
131
|
+
parser = argparse.ArgumentParser(
|
|
132
|
+
description="Safely update a workflow-checkpoint.json step status.",
|
|
133
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
134
|
+
epilog=(
|
|
135
|
+
"Examples:\n"
|
|
136
|
+
" %(prog)s --checkpoint-path .prizmkit/specs/my-feature/workflow-checkpoint.json "
|
|
137
|
+
"--step prizmkit-implement --status completed\n"
|
|
138
|
+
" %(prog)s --checkpoint-path .prizmkit/bugfix/B-001/workflow-checkpoint.json "
|
|
139
|
+
"--step S04 --status in_progress\n"
|
|
140
|
+
" %(prog)s --checkpoint-path .prizmkit/refactor/R-001/workflow-checkpoint.json "
|
|
141
|
+
"--step prizmkit-code-review --status failed --note 'max rounds exhausted'"
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
parser.add_argument(
|
|
145
|
+
"--checkpoint-path", required=True, help="Path to workflow-checkpoint.json"
|
|
146
|
+
)
|
|
147
|
+
parser.add_argument(
|
|
148
|
+
"--step",
|
|
149
|
+
required=True,
|
|
150
|
+
help="Step identifier (skill name like 'prizmkit-implement' or step ID like 'S04')",
|
|
151
|
+
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--status",
|
|
154
|
+
required=True,
|
|
155
|
+
choices=sorted(VALID_STATUSES),
|
|
156
|
+
help="New status for the step",
|
|
157
|
+
)
|
|
158
|
+
parser.add_argument(
|
|
159
|
+
"--note",
|
|
160
|
+
default=None,
|
|
161
|
+
help="Optional note to attach to the step (e.g., failure reason)",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
args = parser.parse_args()
|
|
165
|
+
result = update_checkpoint(
|
|
166
|
+
args.checkpoint_path, args.step, args.status, args.note
|
|
167
|
+
)
|
|
168
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
169
|
+
return 0 if result.get("ok") else (2 if "File not found" in result.get("error", "") or "Corrupted" in result.get("error", "") else 1)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
sys.exit(main())
|
|
@@ -8,23 +8,53 @@ A checkpoint file tracks your progress through this workflow:
|
|
|
8
8
|
|
|
9
9
|
1. **Before each skill**: Read `workflow-checkpoint.json`, verify the previous step has `status: "completed"` or `status: "skipped"`. If it is still `"pending"` or `"in_progress"`, you MUST complete it first before moving on.
|
|
10
10
|
|
|
11
|
-
2. **Starting a skill**:
|
|
11
|
+
2. **Starting a skill**: Use the update script to set the current step to `status: "in_progress"`.
|
|
12
12
|
|
|
13
|
-
3. **After each skill completes**:
|
|
13
|
+
3. **After each skill completes**: Use the update script to set the current step to `status: "completed"`.
|
|
14
14
|
|
|
15
|
-
4. **On failure**:
|
|
15
|
+
4. **On failure**: Use the update script to set the step to `status: "failed"` and continue to the next step if possible, or halt and write failure-log.md.
|
|
16
16
|
|
|
17
17
|
5. **On session exit**: The checkpoint file reflects your actual progress. Do NOT manually set future steps to "completed".
|
|
18
18
|
|
|
19
19
|
### Checkpoint Update Pattern
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
**IMPORTANT: NEVER hand-write or edit `workflow-checkpoint.json` directly. Always use the update script.**
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
After completing each skill, run:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
27
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
28
|
+
--step <SKILL_NAME> \
|
|
29
|
+
--status completed
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
For example, after completing `prizmkit-implement`:
|
|
33
|
+
```bash
|
|
34
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
35
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
36
|
+
--step prizmkit-implement \
|
|
37
|
+
--status completed
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Before starting the next skill:
|
|
41
|
+
```bash
|
|
42
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
43
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
44
|
+
--step <NEXT_SKILL_NAME> \
|
|
45
|
+
--status in_progress
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
On failure:
|
|
49
|
+
```bash
|
|
50
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
51
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
52
|
+
--step <SKILL_NAME> \
|
|
53
|
+
--status failed \
|
|
54
|
+
--note "brief failure reason"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The script outputs JSON: `{"ok": true, ...}` on success, `{"ok": false, "error": "..."}` on failure.
|
|
28
58
|
|
|
29
59
|
### Resume Behavior
|
|
30
60
|
|
|
@@ -150,4 +150,10 @@ Append results to `context-snapshot.md`:
|
|
|
150
150
|
If verification fails, log the failure details but continue to commit. Failures do NOT block the commit, but you MUST attempt verification and MUST clean up the dev server.
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
**Checkpoint update**:
|
|
153
|
+
**Checkpoint update**: Run the update script to set step `browser-verification` to `"completed"`:
|
|
154
|
+
```bash
|
|
155
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
156
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
157
|
+
--step browser-verification \
|
|
158
|
+
--status completed
|
|
159
|
+
```
|
|
@@ -121,4 +121,10 @@ Append results to `context-snapshot.md`:
|
|
|
121
121
|
If verification fails, log the failure details but continue to commit. Failures do NOT block the commit, but you MUST attempt verification and MUST clean up the dev server.
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
**Checkpoint update**:
|
|
124
|
+
**Checkpoint update**: Run the update script to set step `browser-verification` to `"completed"`:
|
|
125
|
+
```bash
|
|
126
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
127
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
128
|
+
--step browser-verification \
|
|
129
|
+
--status completed
|
|
130
|
+
```
|
|
@@ -143,4 +143,10 @@ Browser cleanup: confirmed
|
|
|
143
143
|
If verification fails, log the failure details but continue to commit. Failures do NOT block the commit, but you MUST attempt verification and MUST clean up the dev server.
|
|
144
144
|
|
|
145
145
|
|
|
146
|
-
**Checkpoint update**:
|
|
146
|
+
**Checkpoint update**: Run the update script to set step `browser-verification` to `"completed"`:
|
|
147
|
+
```bash
|
|
148
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
149
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
150
|
+
--step browser-verification \
|
|
151
|
+
--status completed
|
|
152
|
+
```
|
|
@@ -55,4 +55,17 @@ Rules for writing completion notes:
|
|
|
55
55
|
- If this feature has no downstream dependents, still write the summary (it serves as documentation)
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
**Checkpoint update**: After `/prizmkit-retrospective` completes,
|
|
58
|
+
**Checkpoint update**: After `/prizmkit-retrospective` completes, run the update script to set step `prizmkit-retrospective` to `"completed"`:
|
|
59
|
+
```bash
|
|
60
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
61
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
62
|
+
--step prizmkit-retrospective \
|
|
63
|
+
--status completed
|
|
64
|
+
```
|
|
65
|
+
After `/prizmkit-committer` completes, set step `prizmkit-committer` to `"completed"`:
|
|
66
|
+
```bash
|
|
67
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
68
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
69
|
+
--step prizmkit-committer \
|
|
70
|
+
--status completed
|
|
71
|
+
```
|
|
@@ -48,4 +48,17 @@ Rules for writing completion notes:
|
|
|
48
48
|
- If this feature has no downstream dependents, still write the summary (it serves as documentation)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
**Checkpoint update**: After `/prizmkit-retrospective` completes,
|
|
51
|
+
**Checkpoint update**: After `/prizmkit-retrospective` completes, run the update script to set step `prizmkit-retrospective` to `"completed"`:
|
|
52
|
+
```bash
|
|
53
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
54
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
55
|
+
--step prizmkit-retrospective \
|
|
56
|
+
--status completed
|
|
57
|
+
```
|
|
58
|
+
After `/prizmkit-committer` completes, set step `prizmkit-committer` to `"completed"`:
|
|
59
|
+
```bash
|
|
60
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
61
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
62
|
+
--step prizmkit-committer \
|
|
63
|
+
--status completed
|
|
64
|
+
```
|
|
@@ -14,4 +14,10 @@
|
|
|
14
14
|
- <trap entries extracted from L1/L2 docs>
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
**Checkpoint update**:
|
|
17
|
+
**Checkpoint update**: Run the update script to set step `context-snapshot` to `"completed"`:
|
|
18
|
+
```bash
|
|
19
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
20
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
21
|
+
--step context-snapshot \
|
|
22
|
+
--status completed
|
|
23
|
+
```
|
|
@@ -3,4 +3,10 @@
|
|
|
3
3
|
- **Section 5 — Existing Tests**: full content of related test files as code block
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
**Checkpoint update**:
|
|
6
|
+
**Checkpoint update**: Run the update script to set step `context-snapshot` to `"completed"`:
|
|
7
|
+
```bash
|
|
8
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
9
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
10
|
+
--step context-snapshot \
|
|
11
|
+
--status completed
|
|
12
|
+
```
|
|
@@ -44,4 +44,10 @@ After all critics return, read all 3 reports:
|
|
|
44
44
|
**CP-2.5**: Plan challenges reviewed and resolved.
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
**Checkpoint update**:
|
|
47
|
+
**Checkpoint update**: Run the update script to set step `critic-plan-review` to `"completed"`:
|
|
48
|
+
```bash
|
|
49
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
50
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
51
|
+
--step critic-plan-review \
|
|
52
|
+
--status completed
|
|
53
|
+
```
|
|
@@ -23,4 +23,10 @@ Wait for Critic to return.
|
|
|
23
23
|
**CP-2.5**: Plan challenges reviewed and resolved.
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
**Checkpoint update**:
|
|
26
|
+
**Checkpoint update**: Run the update script to set step `critic-plan-review` to `"completed"`:
|
|
27
|
+
```bash
|
|
28
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
29
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
30
|
+
--step critic-plan-review \
|
|
31
|
+
--status completed
|
|
32
|
+
```
|
|
@@ -28,4 +28,10 @@ grep -q "## Implementation Log" .prizmkit/specs/{{FEATURE_SLUG}}/context-snapsho
|
|
|
28
28
|
If GATE:MISSING — send message to Dev (re-spawn if needed): "Write the '## Implementation Log' section to context-snapshot.md before I can proceed to review. Include: files changed/created, key decisions, deviations from plan, notable discoveries."
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
**Checkpoint update**:
|
|
31
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-implement` to `"completed"`:
|
|
32
|
+
```bash
|
|
33
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
34
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
35
|
+
--step prizmkit-implement \
|
|
36
|
+
--status completed
|
|
37
|
+
```
|
|
@@ -41,4 +41,10 @@ Wait for Dev to return. **If Dev times out before all tasks are `[x]`**:
|
|
|
41
41
|
All tasks `[x]`, tests pass.
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
**Checkpoint update**:
|
|
44
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-implement` to `"completed"`:
|
|
45
|
+
```bash
|
|
46
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
47
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
48
|
+
--step prizmkit-implement \
|
|
49
|
+
--status completed
|
|
50
|
+
```
|
|
@@ -39,4 +39,10 @@ You know this project's tech stack. Identify ALL test commands that apply (e.g.,
|
|
|
39
39
|
**CP-2**: All acceptance criteria met, all tests pass.
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
**Checkpoint update**:
|
|
42
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-implement` to `"completed"`:
|
|
43
|
+
```bash
|
|
44
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
45
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
46
|
+
--step prizmkit-implement \
|
|
47
|
+
--status completed
|
|
48
|
+
```
|
|
@@ -18,4 +18,10 @@ Before proceeding past CP-1, verify:
|
|
|
18
18
|
**CP-1**: plan.md exists with Tasks section.
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
**Checkpoint update**:
|
|
21
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-plan` to `"completed"`:
|
|
22
|
+
```bash
|
|
23
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
24
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
25
|
+
--step prizmkit-plan \
|
|
26
|
+
--status completed
|
|
27
|
+
```
|
|
@@ -18,4 +18,10 @@ Before proceeding past CP-1:
|
|
|
18
18
|
**CP-1**: plan.md exists with Tasks section.
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
**Checkpoint update**:
|
|
21
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-plan` to `"completed"`:
|
|
22
|
+
```bash
|
|
23
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
24
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
25
|
+
--step prizmkit-plan \
|
|
26
|
+
--status completed
|
|
27
|
+
```
|
|
@@ -18,4 +18,10 @@ Read `review-report.md` and check the Verdict:
|
|
|
18
18
|
**CP-3**: Review complete, report written.
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
**Checkpoint update**:
|
|
21
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-code-review` to `"completed"`:
|
|
22
|
+
```bash
|
|
23
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
24
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
25
|
+
--step prizmkit-code-review \
|
|
26
|
+
--status completed
|
|
27
|
+
```
|
|
@@ -20,4 +20,10 @@ Run the full test suite: `({{TEST_CMD}}) 2>&1 | tee /tmp/review-test-out.txt | t
|
|
|
20
20
|
**CP-3**: Review complete, tests pass, report written.
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
**Checkpoint update**:
|
|
23
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-code-review` to `"completed"`:
|
|
24
|
+
```bash
|
|
25
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
26
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
27
|
+
--step prizmkit-code-review \
|
|
28
|
+
--status completed
|
|
29
|
+
```
|
|
@@ -64,4 +64,10 @@ Before proceeding past CP-1, verify:
|
|
|
64
64
|
**CP-1**: Both spec.md and plan.md exist.
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
**Checkpoint update**:
|
|
67
|
+
**Checkpoint update**: Run the update script to set step `context-snapshot-and-plan` to `"completed"`:
|
|
68
|
+
```bash
|
|
69
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
70
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
71
|
+
--step context-snapshot-and-plan \
|
|
72
|
+
--status completed
|
|
73
|
+
```
|
|
@@ -4,4 +4,10 @@
|
|
|
4
4
|
- **CP-0**: Verify `.prizmkit/prizm-docs/root.prizm`, `.prizmkit/config.json` exist
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
**Checkpoint update**:
|
|
7
|
+
**Checkpoint update**: Run the update script to set step `prizmkit-init` to `"completed"`:
|
|
8
|
+
```bash
|
|
9
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
10
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
11
|
+
--step prizmkit-init \
|
|
12
|
+
--status completed
|
|
13
|
+
```
|
|
@@ -12,4 +12,10 @@ Save the list of **pre-existing failing tests** (if any) as `BASELINE_FAILURES`.
|
|
|
12
12
|
> **Test Output Rule**: Always capture test output to a temp file (`tee /tmp/test-out.txt`). Then grep the file instead of re-running the suite.
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
**Checkpoint update**:
|
|
15
|
+
**Checkpoint update**: Run the update script to set step `test-baseline` to `"completed"`:
|
|
16
|
+
```bash
|
|
17
|
+
python3 $PIPELINE_DIR/scripts/update-checkpoint.py \
|
|
18
|
+
--checkpoint-path {{CHECKPOINT_PATH}} \
|
|
19
|
+
--step test-baseline \
|
|
20
|
+
--status completed
|
|
21
|
+
```
|
|
@@ -223,9 +223,13 @@ Only proceed to Phase 5 after user confirms.
|
|
|
223
223
|
|
|
224
224
|
### Phase 5: Generate & Validate
|
|
225
225
|
|
|
226
|
-
1. **
|
|
227
|
-
2. **
|
|
228
|
-
|
|
226
|
+
1. **Write draft JSON**: Write a draft `.prizmkit/plans/bug-fix-list.draft.json` with all collected bug data. Conform to `.prizmkit/dev-pipeline/templates/bug-fix-list-schema.json`.
|
|
227
|
+
2. **Generate and validate**: Run the generate script to validate and produce the final file:
|
|
228
|
+
```bash
|
|
229
|
+
python3 ${SKILL_DIR}/scripts/validate-bug-list.py generate --input .prizmkit/plans/bug-fix-list.draft.json --output .prizmkit/plans/bug-fix-list.json
|
|
230
|
+
```
|
|
231
|
+
The script fills in defaults (`$schema`, `created_at`, `created_by`), validates all fields, and writes the final file only if validation passes.
|
|
232
|
+
3. **If validation fails**: Fix the draft and retry (max 3 attempts). If the script is unavailable, use the checklist in `${SKILL_DIR}/references/schema-validation.md`.
|
|
229
233
|
4. **Output**: File path, summary, and next steps
|
|
230
234
|
|
|
231
235
|
**Gate → CP-BP-5**: `bug-fix-list.json` passes validation script with zero errors.
|
|
@@ -269,8 +273,12 @@ Batch-parse error logs to generate bug entries without interactive prompts:
|
|
|
269
273
|
- error_source: populated from log content
|
|
270
274
|
- verification_type: default to `automated`
|
|
271
275
|
- acceptance_criteria: auto-generate "Error no longer occurs in [scenario]"
|
|
272
|
-
4.
|
|
276
|
+
4. Write draft to `.prizmkit/plans/bug-fix-list.draft.json` for user review
|
|
273
277
|
5. Ask: "Review and confirm? You can edit individual entries."
|
|
278
|
+
6. After user confirms, call the generate script:
|
|
279
|
+
```bash
|
|
280
|
+
python3 ${SKILL_DIR}/scripts/validate-bug-list.py generate --input .prizmkit/plans/bug-fix-list.draft.json --output .prizmkit/plans/bug-fix-list.json
|
|
281
|
+
```
|
|
274
282
|
|
|
275
283
|
## Operation: From Tests
|
|
276
284
|
|
|
@@ -280,7 +288,11 @@ Batch-parse failed test output:
|
|
|
280
288
|
2. Parse each failed test case as a separate bug entry
|
|
281
289
|
3. Auto-populate `failed_test_path`, `error_message`
|
|
282
290
|
4. Set verification_type to `automated` (test already exists)
|
|
283
|
-
5.
|
|
291
|
+
5. Write draft to `.prizmkit/plans/bug-fix-list.draft.json`
|
|
292
|
+
6. After user confirms, call the generate script:
|
|
293
|
+
```bash
|
|
294
|
+
python3 ${SKILL_DIR}/scripts/validate-bug-list.py generate --input .prizmkit/plans/bug-fix-list.draft.json --output .prizmkit/plans/bug-fix-list.json
|
|
295
|
+
```
|
|
284
296
|
|
|
285
297
|
## Operation: Validate
|
|
286
298
|
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Validate .prizmkit/plans/bug-fix-list.json
|
|
3
|
+
Validate and generate .prizmkit/plans/bug-fix-list.json files
|
|
4
|
+
for the dev-pipeline system.
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
validate Validate an existing bug-fix-list.json
|
|
8
|
+
generate Validate a draft JSON and generate final bug-fix-list.json with defaults
|
|
4
9
|
|
|
5
10
|
Usage:
|
|
6
|
-
|
|
11
|
+
python3 validate-bug-list.py validate .prizmkit/plans/bug-fix-list.json [--feature-list .prizmkit/plans/feature-list.json]
|
|
12
|
+
python3 validate-bug-list.py generate --input draft.json --output .prizmkit/plans/bug-fix-list.json
|
|
7
13
|
|
|
8
14
|
Exit codes:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
0 = valid / generated
|
|
16
|
+
1 = validation errors found
|
|
17
|
+
2 = file not found or JSON parse error
|
|
18
|
+
|
|
19
|
+
Python 3.6+ required. No external dependencies.
|
|
12
20
|
"""
|
|
13
21
|
|
|
22
|
+
import argparse
|
|
14
23
|
import json
|
|
15
24
|
import sys
|
|
16
25
|
import os
|
|
17
26
|
import re
|
|
27
|
+
from datetime import datetime, timezone
|
|
18
28
|
|
|
19
29
|
VALID_SEVERITIES = {"critical", "high", "medium", "low"}
|
|
20
30
|
VALID_SOURCE_TYPES = {"stack_trace", "user_report", "failed_test", "log_pattern", "monitoring_alert"}
|
|
@@ -24,129 +34,289 @@ BUG_ID_PATTERN = re.compile(r"^B-\d{3}$")
|
|
|
24
34
|
SCHEMA_VERSION = "dev-pipeline-bug-fix-list-v1"
|
|
25
35
|
|
|
26
36
|
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
def _err(msg):
|
|
38
|
+
"""Print an error message to stderr."""
|
|
39
|
+
print("ERROR: {}".format(msg), file=sys.stderr)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _warn(msg):
|
|
43
|
+
"""Print a warning message to stderr."""
|
|
44
|
+
print("WARN: {}".format(msg), file=sys.stderr)
|
|
45
|
+
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
def _info(msg):
|
|
48
|
+
"""Print an informational message to stderr."""
|
|
49
|
+
print("INFO: {}".format(msg), file=sys.stderr)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _load_json(path):
|
|
53
|
+
"""Load and return parsed JSON from a file. Returns (data, error_string)."""
|
|
54
|
+
if not os.path.isfile(path):
|
|
55
|
+
return None, "File not found: {}".format(path)
|
|
32
56
|
try:
|
|
33
|
-
with open(
|
|
57
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
34
58
|
data = json.load(f)
|
|
35
|
-
|
|
36
|
-
print(f"ERROR: File not found: {bug_list_path}", file=sys.stderr)
|
|
37
|
-
return 2
|
|
59
|
+
return data, None
|
|
38
60
|
except json.JSONDecodeError as e:
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
return None, "Invalid JSON in {}: {}".format(path, e)
|
|
62
|
+
except Exception as e:
|
|
63
|
+
return None, "Cannot read {}: {}".format(path, e)
|
|
41
64
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
|
|
66
|
+
def _write_json(path, data):
|
|
67
|
+
"""Write data as pretty-printed JSON to path. Creates parent dirs if needed."""
|
|
68
|
+
parent = os.path.dirname(os.path.abspath(path))
|
|
69
|
+
if parent and not os.path.isdir(parent):
|
|
70
|
+
os.makedirs(parent, exist_ok=True)
|
|
71
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
72
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
73
|
+
f.write("\n")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def validate(data, feature_ids=None):
|
|
77
|
+
"""Validate a parsed bug-fix-list data structure.
|
|
78
|
+
|
|
79
|
+
Returns a dict with keys: valid, errors, warnings, stats.
|
|
80
|
+
"""
|
|
81
|
+
errors = []
|
|
82
|
+
warnings = []
|
|
51
83
|
|
|
52
84
|
# Top-level required fields
|
|
53
85
|
if "$schema" not in data:
|
|
54
86
|
errors.append("Missing required field: $schema")
|
|
55
87
|
elif data["$schema"] != SCHEMA_VERSION:
|
|
56
|
-
errors.append(
|
|
88
|
+
errors.append("Invalid $schema: expected '{}', got '{}'".format(SCHEMA_VERSION, data["$schema"]))
|
|
57
89
|
|
|
58
90
|
if not data.get("project_name"):
|
|
59
91
|
errors.append("Missing or empty required field: project_name")
|
|
60
92
|
|
|
61
93
|
bugs = data.get("bugs", [])
|
|
62
|
-
if not bugs:
|
|
94
|
+
if not bugs or not isinstance(bugs, list):
|
|
63
95
|
errors.append("Missing or empty required field: bugs")
|
|
96
|
+
return {
|
|
97
|
+
"valid": False,
|
|
98
|
+
"errors": errors,
|
|
99
|
+
"warnings": warnings,
|
|
100
|
+
"stats": {"total_bugs": 0, "severity_distribution": {}},
|
|
101
|
+
}
|
|
64
102
|
|
|
65
103
|
# Per-bug validation
|
|
66
104
|
seen_ids = set()
|
|
67
|
-
|
|
105
|
+
severity_counts = {}
|
|
68
106
|
|
|
69
107
|
for i, bug in enumerate(bugs):
|
|
70
|
-
prefix =
|
|
108
|
+
prefix = "bugs[{}]".format(i)
|
|
71
109
|
|
|
72
110
|
# Required fields
|
|
73
111
|
bug_id = bug.get("id", "")
|
|
74
112
|
if not bug_id:
|
|
75
|
-
errors.append(
|
|
113
|
+
errors.append("{}: missing required field 'id'".format(prefix))
|
|
76
114
|
elif not BUG_ID_PATTERN.match(bug_id):
|
|
77
|
-
errors.append(
|
|
115
|
+
errors.append("{}: id '{}' does not match pattern B-NNN".format(prefix, bug_id))
|
|
78
116
|
|
|
79
117
|
if bug_id in seen_ids:
|
|
80
|
-
errors.append(
|
|
118
|
+
errors.append("{}: duplicate bug id '{}'".format(prefix, bug_id))
|
|
81
119
|
seen_ids.add(bug_id)
|
|
82
120
|
|
|
83
121
|
if not bug.get("title"):
|
|
84
|
-
errors.append(
|
|
122
|
+
errors.append("{} ({}): missing required field 'title'".format(prefix, bug_id))
|
|
85
123
|
|
|
86
124
|
if not bug.get("description"):
|
|
87
|
-
errors.append(
|
|
125
|
+
errors.append("{} ({}): missing required field 'description'".format(prefix, bug_id))
|
|
88
126
|
|
|
89
127
|
severity = bug.get("severity", "")
|
|
90
128
|
if severity not in VALID_SEVERITIES:
|
|
91
|
-
errors.append(
|
|
129
|
+
errors.append("{} ({}): invalid severity '{}' — must be one of {}".format(
|
|
130
|
+
prefix, bug_id, severity, sorted(VALID_SEVERITIES)))
|
|
131
|
+
else:
|
|
132
|
+
severity_counts[severity] = severity_counts.get(severity, 0) + 1
|
|
92
133
|
|
|
93
134
|
# error_source
|
|
94
135
|
error_source = bug.get("error_source", {})
|
|
95
136
|
source_type = error_source.get("type", "") if isinstance(error_source, dict) else ""
|
|
96
137
|
if source_type not in VALID_SOURCE_TYPES:
|
|
97
|
-
errors.append(
|
|
138
|
+
errors.append("{} ({}): invalid error_source.type '{}' — must be one of {}".format(
|
|
139
|
+
prefix, bug_id, source_type, sorted(VALID_SOURCE_TYPES)))
|
|
98
140
|
|
|
99
141
|
# verification_type
|
|
100
142
|
vtype = bug.get("verification_type", "")
|
|
101
143
|
if vtype not in VALID_VERIFICATION_TYPES:
|
|
102
|
-
errors.append(
|
|
144
|
+
errors.append("{} ({}): invalid verification_type '{}' — must be one of {}".format(
|
|
145
|
+
prefix, bug_id, vtype, sorted(VALID_VERIFICATION_TYPES)))
|
|
103
146
|
|
|
104
147
|
# acceptance_criteria
|
|
105
148
|
ac = bug.get("acceptance_criteria", [])
|
|
106
149
|
if not ac or not isinstance(ac, list):
|
|
107
|
-
errors.append(
|
|
150
|
+
errors.append("{} ({}): missing or empty acceptance_criteria array".format(prefix, bug_id))
|
|
108
151
|
|
|
109
152
|
# status
|
|
110
153
|
status = bug.get("status", "")
|
|
111
154
|
if status not in VALID_STATUSES:
|
|
112
|
-
errors.append(
|
|
155
|
+
errors.append("{} ({}): invalid status '{}' — must be one of {}".format(
|
|
156
|
+
prefix, bug_id, status, sorted(VALID_STATUSES)))
|
|
113
157
|
|
|
114
158
|
# Priority validation (optional field)
|
|
115
159
|
priority = bug.get("priority")
|
|
116
160
|
if priority is not None:
|
|
117
161
|
if priority not in ("high", "medium", "low"):
|
|
118
|
-
errors.append(
|
|
162
|
+
errors.append("{} ({}): invalid priority '{}' — must be one of 'high', 'medium', 'low'".format(
|
|
163
|
+
prefix, bug_id, priority))
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
"valid": len(errors) == 0,
|
|
167
|
+
"errors": errors,
|
|
168
|
+
"warnings": warnings,
|
|
169
|
+
"stats": {
|
|
170
|
+
"total_bugs": len(bugs),
|
|
171
|
+
"severity_distribution": severity_counts,
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def cmd_validate(args):
|
|
177
|
+
"""Handle the 'validate' command."""
|
|
178
|
+
bug_list = args.input
|
|
179
|
+
feature_list = args.feature_list
|
|
180
|
+
|
|
181
|
+
data, load_err = _load_json(bug_list)
|
|
182
|
+
if load_err:
|
|
183
|
+
_err(load_err)
|
|
184
|
+
return 2
|
|
185
|
+
|
|
186
|
+
# Load feature-list.json (optional, for cross-reference)
|
|
187
|
+
if feature_list:
|
|
188
|
+
fl_data, fl_err = _load_json(feature_list)
|
|
189
|
+
if not fl_data:
|
|
190
|
+
_warn("Could not load feature-list.json at {}: {}".format(feature_list, fl_err))
|
|
191
|
+
|
|
192
|
+
result = validate(data)
|
|
119
193
|
|
|
194
|
+
# Print results to stdout
|
|
195
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
120
196
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
for
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
197
|
+
if result["valid"]:
|
|
198
|
+
bug_count = result["stats"]["total_bugs"]
|
|
199
|
+
sev = result["stats"]["severity_distribution"]
|
|
200
|
+
sev_str = ", ".join("{}={}".format(k, v) for k, v in sorted(sev.items()))
|
|
201
|
+
_info("VALIDATION PASSED — {} bugs ({})".format(bug_count, sev_str))
|
|
202
|
+
return 0
|
|
203
|
+
else:
|
|
204
|
+
_err("VALIDATION FAILED — {} error(s), {} warning(s)".format(
|
|
205
|
+
len(result["errors"]), len(result["warnings"])))
|
|
206
|
+
for e in result["errors"]:
|
|
207
|
+
_err(" ERROR: {}".format(e))
|
|
208
|
+
for w in result["warnings"]:
|
|
209
|
+
_warn(" WARN: {}".format(w))
|
|
128
210
|
return 1
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def cmd_generate(args):
|
|
214
|
+
"""Handle the 'generate' command.
|
|
215
|
+
|
|
216
|
+
Loads a draft JSON (produced by AI), fills in defaults, validates,
|
|
217
|
+
and writes the final bug-fix-list.json.
|
|
218
|
+
"""
|
|
219
|
+
# Load draft (supports stdin via '-')
|
|
220
|
+
if args.input == "-":
|
|
221
|
+
try:
|
|
222
|
+
data = json.load(sys.stdin)
|
|
223
|
+
except json.JSONDecodeError as exc:
|
|
224
|
+
_err("Invalid JSON from stdin: {}".format(exc))
|
|
225
|
+
return 2
|
|
129
226
|
else:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
227
|
+
data, load_err = _load_json(args.input)
|
|
228
|
+
if load_err:
|
|
229
|
+
_err(load_err)
|
|
230
|
+
return 2
|
|
231
|
+
|
|
232
|
+
# Fill in defaults
|
|
233
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
234
|
+
data.setdefault("$schema", SCHEMA_VERSION)
|
|
235
|
+
data.setdefault("created_at", now)
|
|
236
|
+
data.setdefault("created_by", "bug-planner")
|
|
237
|
+
|
|
238
|
+
# Set default status for bugs without one
|
|
239
|
+
for bug in data.get("bugs", []):
|
|
240
|
+
bug.setdefault("status", "pending")
|
|
241
|
+
|
|
242
|
+
# Validate
|
|
243
|
+
result = validate(data)
|
|
244
|
+
|
|
245
|
+
# Output validation result
|
|
246
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
247
|
+
|
|
248
|
+
if result["valid"]:
|
|
249
|
+
_write_json(args.output, data)
|
|
250
|
+
_info("Generated bug-fix-list written to {}".format(args.output))
|
|
140
251
|
return 0
|
|
252
|
+
else:
|
|
253
|
+
_err("Validation failed with {} error(s)".format(len(result["errors"])))
|
|
254
|
+
for e in result["errors"]:
|
|
255
|
+
_err(" ERROR: {}".format(e))
|
|
256
|
+
return 1
|
|
141
257
|
|
|
142
258
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
259
|
+
def main():
|
|
260
|
+
# Backward compatibility: if first arg is a file path (not a subcommand),
|
|
261
|
+
# treat it as 'validate' command
|
|
262
|
+
if len(sys.argv) > 1 and not sys.argv[1].startswith("-") and sys.argv[1] not in ("validate", "generate"):
|
|
263
|
+
sys.argv.insert(1, "validate")
|
|
264
|
+
|
|
265
|
+
parser = argparse.ArgumentParser(
|
|
266
|
+
description="Validate and generate .prizmkit/plans/bug-fix-list.json files.",
|
|
267
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
268
|
+
epilog=(
|
|
269
|
+
"Examples:\n"
|
|
270
|
+
" %(prog)s validate .prizmkit/plans/bug-fix-list.json\n"
|
|
271
|
+
" %(prog)s validate .prizmkit/plans/bug-fix-list.json --feature-list .prizmkit/plans/feature-list.json\n"
|
|
272
|
+
" %(prog)s generate --input draft.json --output .prizmkit/plans/bug-fix-list.json\n"
|
|
273
|
+
),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
|
277
|
+
|
|
278
|
+
# -- validate --
|
|
279
|
+
p_validate = subparsers.add_parser(
|
|
280
|
+
"validate",
|
|
281
|
+
help="Validate an existing bug-fix-list.json",
|
|
282
|
+
)
|
|
283
|
+
p_validate.add_argument(
|
|
284
|
+
"input", help="Path to bug-fix-list.json"
|
|
285
|
+
)
|
|
286
|
+
p_validate.add_argument(
|
|
287
|
+
"--feature-list", default=None, help="Path to feature-list.json for cross-reference"
|
|
288
|
+
)
|
|
146
289
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
290
|
+
# -- generate --
|
|
291
|
+
p_generate = subparsers.add_parser(
|
|
292
|
+
"generate",
|
|
293
|
+
help="Validate a draft and generate final bug-fix-list.json with defaults",
|
|
294
|
+
)
|
|
295
|
+
p_generate.add_argument(
|
|
296
|
+
"--input", required=True, help="Path to draft JSON (or '-' for stdin)"
|
|
297
|
+
)
|
|
298
|
+
p_generate.add_argument(
|
|
299
|
+
"--output", required=True, help="Path to write final bug-fix-list.json"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
args = parser.parse_args()
|
|
303
|
+
|
|
304
|
+
if not args.command:
|
|
305
|
+
parser.print_help(sys.stderr)
|
|
306
|
+
return 2
|
|
151
307
|
|
|
152
|
-
|
|
308
|
+
dispatch = {
|
|
309
|
+
"validate": cmd_validate,
|
|
310
|
+
"generate": cmd_generate,
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
handler = dispatch.get(args.command)
|
|
314
|
+
if handler is None:
|
|
315
|
+
_err("Unknown command: {}".format(args.command))
|
|
316
|
+
return 2
|
|
317
|
+
|
|
318
|
+
return handler(args)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
if __name__ == "__main__":
|
|
322
|
+
sys.exit(main())
|
|
@@ -263,9 +263,12 @@ For simple incremental planning, skip detailed Phase 2-3 analysis:
|
|
|
263
263
|
**NEVER proceed without explicit user selection via `AskUserQuestion`. Do NOT render options as plain text — the user must be able to click/select.**
|
|
264
264
|
3. Generate next sequential feature IDs
|
|
265
265
|
4. Draft features (title + description + acceptance_criteria + dependencies)
|
|
266
|
-
5.
|
|
266
|
+
5. Write draft to `.prizmkit/plans/feature-list.draft.json`, then call the generate script:
|
|
267
|
+
```bash
|
|
268
|
+
python3 ${SKILL_DIR}/scripts/validate-and-generate.py generate --input .prizmkit/plans/feature-list.draft.json --output .prizmkit/plans/feature-list.json --mode incremental
|
|
269
|
+
```
|
|
267
270
|
6. If valid → summarize and recommend next step
|
|
268
|
-
7. If invalid → apply fixes, re-
|
|
271
|
+
7. If invalid → apply fixes to the draft, re-run generate (max 2 attempts, then escalate to full workflow)
|
|
269
272
|
|
|
270
273
|
## Browser Interaction Planning
|
|
271
274
|
|
|
@@ -309,10 +312,13 @@ Key requirements:
|
|
|
309
312
|
- `high` → **standard** (orchestrator + dev + reviewer, 3 agents)
|
|
310
313
|
- `critical` → **full** (full team + critic agents, 5 agents). Use for: architectural changes touching 10+ files, cross-module refactoring with API surface changes, features requiring multi-critic voting
|
|
311
314
|
|
|
312
|
-
|
|
315
|
+
**IMPORTANT: Do NOT hand-write the final JSON file.** Instead:
|
|
316
|
+
1. Write a draft JSON to a temporary path (e.g., `.prizmkit/plans/feature-list.draft.json`)
|
|
317
|
+
2. Call the generate script to validate and produce the final file:
|
|
313
318
|
```bash
|
|
314
|
-
python3 ${SKILL_DIR}/scripts/validate-and-generate.py
|
|
319
|
+
python3 ${SKILL_DIR}/scripts/validate-and-generate.py generate --input .prizmkit/plans/feature-list.draft.json --output .prizmkit/plans/feature-list.json --mode <new|incremental>
|
|
315
320
|
```
|
|
321
|
+
The script fills in defaults (`$schema`, `created_at`, `created_by`), validates all fields, and writes the final file only if validation passes. If validation fails, fix the draft and retry.
|
|
316
322
|
|
|
317
323
|
## Testing Defaults (Phase 8)
|
|
318
324
|
|
|
@@ -6,12 +6,14 @@ for the dev-pipeline system.
|
|
|
6
6
|
Commands:
|
|
7
7
|
validate Validate an existing .prizmkit/plans/feature-list.json
|
|
8
8
|
template Generate a blank template .prizmkit/plans/feature-list.json
|
|
9
|
+
generate Validate a draft JSON and generate final feature-list.json with defaults
|
|
9
10
|
summary Print a summary table of features from a .prizmkit/plans/feature-list.json
|
|
10
11
|
grade Generate grading results from eval runs (for npm run skill:review)
|
|
11
12
|
|
|
12
13
|
Usage:
|
|
13
14
|
python3 validate-and-generate.py validate --input .prizmkit/plans/feature-list.json [--output validated.json] [--mode new|incremental]
|
|
14
15
|
python3 validate-and-generate.py template --output .prizmkit/plans/feature-list.json
|
|
16
|
+
python3 validate-and-generate.py generate --input draft.json --output .prizmkit/plans/feature-list.json [--mode new|incremental]
|
|
15
17
|
python3 validate-and-generate.py summary --input .prizmkit/plans/feature-list.json [--format markdown|json]
|
|
16
18
|
python3 validate-and-generate.py grade --workspace /.codebuddy/skill-evals/feature-planner-workspace --iteration iteration-1
|
|
17
19
|
|
|
@@ -716,6 +718,66 @@ def cmd_template(args):
|
|
|
716
718
|
return 0
|
|
717
719
|
|
|
718
720
|
|
|
721
|
+
def cmd_generate(args):
|
|
722
|
+
"""Handle the 'generate' command.
|
|
723
|
+
|
|
724
|
+
Loads a draft JSON (produced by AI), fills in defaults, validates,
|
|
725
|
+
and writes the final feature-list.json.
|
|
726
|
+
"""
|
|
727
|
+
if not args.input:
|
|
728
|
+
_err("--input is required for the generate command")
|
|
729
|
+
return 2
|
|
730
|
+
if not args.output:
|
|
731
|
+
_err("--output is required for the generate command")
|
|
732
|
+
return 2
|
|
733
|
+
|
|
734
|
+
# Load draft (supports stdin via '-')
|
|
735
|
+
if args.input == "-":
|
|
736
|
+
try:
|
|
737
|
+
data = json.load(sys.stdin)
|
|
738
|
+
except json.JSONDecodeError as exc:
|
|
739
|
+
_err("Invalid JSON from stdin: {}".format(exc))
|
|
740
|
+
return 2
|
|
741
|
+
else:
|
|
742
|
+
data, load_err = _load_json(args.input)
|
|
743
|
+
if load_err:
|
|
744
|
+
_err(load_err)
|
|
745
|
+
return 2
|
|
746
|
+
|
|
747
|
+
# Fill in defaults
|
|
748
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
749
|
+
data.setdefault("$schema", SCHEMA_VERSION)
|
|
750
|
+
data.setdefault("created_at", now)
|
|
751
|
+
data.setdefault("created_by", "feature-planner")
|
|
752
|
+
|
|
753
|
+
# Ensure project_name from app_name if needed
|
|
754
|
+
if "project_name" not in data and "app_name" in data:
|
|
755
|
+
data["project_name"] = data["app_name"]
|
|
756
|
+
|
|
757
|
+
# Set default status for features without one
|
|
758
|
+
for feature in data.get("features", []):
|
|
759
|
+
feature.setdefault("status", "pending")
|
|
760
|
+
|
|
761
|
+
# Validate
|
|
762
|
+
mode = getattr(args, "mode", "new") or "new"
|
|
763
|
+
result = validate_feature_list(data, planning_mode=mode)
|
|
764
|
+
|
|
765
|
+
# Output validation result
|
|
766
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
767
|
+
|
|
768
|
+
if result["valid"]:
|
|
769
|
+
_write_json(args.output, data)
|
|
770
|
+
_info("Generated feature-list written to {}".format(args.output))
|
|
771
|
+
return 0
|
|
772
|
+
else:
|
|
773
|
+
_err("Validation failed with {} error(s)".format(len(result["errors"])))
|
|
774
|
+
for e in result["errors"]:
|
|
775
|
+
_err(" " + e)
|
|
776
|
+
for w in result.get("warnings", []):
|
|
777
|
+
_warn(" " + w)
|
|
778
|
+
return 1
|
|
779
|
+
|
|
780
|
+
|
|
719
781
|
def cmd_summary(args):
|
|
720
782
|
"""Handle the 'summary' command."""
|
|
721
783
|
if not args.input:
|
|
@@ -857,6 +919,7 @@ def main():
|
|
|
857
919
|
" %(prog)s validate --input .prizmkit/plans/feature-list.json --mode incremental\n"
|
|
858
920
|
" %(prog)s validate --input .prizmkit/plans/feature-list.json --output validated.json\n"
|
|
859
921
|
" %(prog)s template --output .prizmkit/plans/feature-list.json\n"
|
|
922
|
+
" %(prog)s generate --input draft.json --output .prizmkit/plans/feature-list.json\n"
|
|
860
923
|
" %(prog)s summary --input .prizmkit/plans/feature-list.json\n"
|
|
861
924
|
" %(prog)s summary --input .prizmkit/plans/feature-list.json --format json\n"
|
|
862
925
|
),
|
|
@@ -891,6 +954,24 @@ def main():
|
|
|
891
954
|
"--output", required=True, help="Path to write template file"
|
|
892
955
|
)
|
|
893
956
|
|
|
957
|
+
# -- generate --
|
|
958
|
+
p_generate = subparsers.add_parser(
|
|
959
|
+
"generate",
|
|
960
|
+
help="Validate a draft and generate final feature-list.json with defaults filled in",
|
|
961
|
+
)
|
|
962
|
+
p_generate.add_argument(
|
|
963
|
+
"--input", required=True, help="Path to draft JSON (or '-' for stdin)"
|
|
964
|
+
)
|
|
965
|
+
p_generate.add_argument(
|
|
966
|
+
"--output", required=True, help="Path to write final feature-list.json"
|
|
967
|
+
)
|
|
968
|
+
p_generate.add_argument(
|
|
969
|
+
"--mode",
|
|
970
|
+
choices=["new", "incremental"],
|
|
971
|
+
default="new",
|
|
972
|
+
help="Validation mode (default: new)",
|
|
973
|
+
)
|
|
974
|
+
|
|
894
975
|
# -- summary --
|
|
895
976
|
p_summary = subparsers.add_parser(
|
|
896
977
|
"summary",
|
|
@@ -931,6 +1012,7 @@ def main():
|
|
|
931
1012
|
dispatch = {
|
|
932
1013
|
"validate": cmd_validate,
|
|
933
1014
|
"template": cmd_template,
|
|
1015
|
+
"generate": cmd_generate,
|
|
934
1016
|
"summary": cmd_summary,
|
|
935
1017
|
"grade": cmd_grade,
|
|
936
1018
|
}
|
|
@@ -293,12 +293,14 @@ If issues found, discuss with user and resolve before proceeding.
|
|
|
293
293
|
|
|
294
294
|
**Goal**: Produce `.prizmkit/plans/refactor-list.json` and validate it.
|
|
295
295
|
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
**IMPORTANT: Do NOT hand-write the final JSON file.** Instead:
|
|
297
|
+
1. Write a draft JSON to `.prizmkit/plans/refactor-list.draft.json` with all collected refactor data.
|
|
298
|
+
2. Call the generate script to validate and produce the final file:
|
|
298
299
|
```bash
|
|
299
|
-
python3 ${SKILL_DIR}/scripts/validate-and-generate-refactor.py
|
|
300
|
+
python3 ${SKILL_DIR}/scripts/validate-and-generate-refactor.py generate --input .prizmkit/plans/refactor-list.draft.json --output .prizmkit/plans/refactor-list.json
|
|
300
301
|
```
|
|
301
|
-
|
|
302
|
+
The script fills in defaults (`$schema`, `created_at`, `created_by`), validates all fields, and writes the final file only if validation passes.
|
|
303
|
+
3. If validation fails -> fix the draft and retry (max 3 attempts)
|
|
302
304
|
4. If validation passes -> present final summary
|
|
303
305
|
|
|
304
306
|
**CHECKPOINT CP-RP-6**: `.prizmkit/plans/refactor-list.json` generated and validated.
|
|
@@ -380,9 +382,12 @@ For simple refactoring with minimal scope:
|
|
|
380
382
|
|
|
381
383
|
**NEVER proceed without explicit user selection via `AskUserQuestion`. Do NOT render options as plain text — the user must be able to click/select.**
|
|
382
384
|
3. Draft items (title + type + scope + description + acceptance_criteria + behavior_preservation + dependencies)
|
|
383
|
-
4.
|
|
385
|
+
4. Write draft to `.prizmkit/plans/refactor-list.draft.json`, then call the generate script:
|
|
386
|
+
```bash
|
|
387
|
+
python3 ${SKILL_DIR}/scripts/validate-and-generate-refactor.py generate --input .prizmkit/plans/refactor-list.draft.json --output .prizmkit/plans/refactor-list.json
|
|
388
|
+
```
|
|
384
389
|
5. If valid -> summarize and recommend next step
|
|
385
|
-
6. If invalid -> apply fixes, re-
|
|
390
|
+
6. If invalid -> apply fixes to the draft, re-run generate (max 2 attempts, then escalate to full workflow)
|
|
386
391
|
|
|
387
392
|
### When NOT to Use Fast Path
|
|
388
393
|
- More than 2 refactor items
|
|
@@ -6,11 +6,13 @@ for the dev-pipeline system.
|
|
|
6
6
|
Commands:
|
|
7
7
|
validate Validate an existing .prizmkit/plans/refactor-list.json
|
|
8
8
|
template Generate a blank template .prizmkit/plans/refactor-list.json
|
|
9
|
+
generate Validate a draft JSON and generate final refactor-list.json with defaults
|
|
9
10
|
summary Print a summary table of refactors from a .prizmkit/plans/refactor-list.json
|
|
10
11
|
|
|
11
12
|
Usage:
|
|
12
13
|
python3 validate-and-generate-refactor.py validate --input .prizmkit/plans/refactor-list.json [--output validated.json]
|
|
13
14
|
python3 validate-and-generate-refactor.py template --output .prizmkit/plans/refactor-list.json
|
|
15
|
+
python3 validate-and-generate-refactor.py generate --input draft.json --output .prizmkit/plans/refactor-list.json
|
|
14
16
|
python3 validate-and-generate-refactor.py summary --input .prizmkit/plans/refactor-list.json [--format markdown|json]
|
|
15
17
|
|
|
16
18
|
Python 3.6+ required. No external dependencies.
|
|
@@ -22,6 +24,7 @@ import json
|
|
|
22
24
|
import os
|
|
23
25
|
import re
|
|
24
26
|
import sys
|
|
27
|
+
from datetime import datetime, timezone
|
|
25
28
|
|
|
26
29
|
# ---------------------------------------------------------------------------
|
|
27
30
|
# Constants
|
|
@@ -687,6 +690,61 @@ def cmd_template(args):
|
|
|
687
690
|
return 0
|
|
688
691
|
|
|
689
692
|
|
|
693
|
+
def cmd_generate(args):
|
|
694
|
+
"""Handle the 'generate' command.
|
|
695
|
+
|
|
696
|
+
Loads a draft JSON (produced by AI), fills in defaults, validates,
|
|
697
|
+
and writes the final refactor-list.json.
|
|
698
|
+
"""
|
|
699
|
+
if not args.input:
|
|
700
|
+
_err("--input is required for the generate command")
|
|
701
|
+
return 2
|
|
702
|
+
if not args.output:
|
|
703
|
+
_err("--output is required for the generate command")
|
|
704
|
+
return 2
|
|
705
|
+
|
|
706
|
+
# Load draft (supports stdin via '-')
|
|
707
|
+
if args.input == "-":
|
|
708
|
+
try:
|
|
709
|
+
data = json.load(sys.stdin)
|
|
710
|
+
except json.JSONDecodeError as exc:
|
|
711
|
+
_err("Invalid JSON from stdin: {}".format(exc))
|
|
712
|
+
return 2
|
|
713
|
+
else:
|
|
714
|
+
data, load_err = _load_json(args.input)
|
|
715
|
+
if load_err:
|
|
716
|
+
_err(load_err)
|
|
717
|
+
return 2
|
|
718
|
+
|
|
719
|
+
# Fill in defaults
|
|
720
|
+
now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
721
|
+
data.setdefault("$schema", SCHEMA_VERSION)
|
|
722
|
+
data.setdefault("created_at", now)
|
|
723
|
+
data.setdefault("created_by", "refactor-planner")
|
|
724
|
+
|
|
725
|
+
# Set default status for refactors without one
|
|
726
|
+
for refactor in data.get("refactors", []):
|
|
727
|
+
refactor.setdefault("status", "pending")
|
|
728
|
+
|
|
729
|
+
# Validate
|
|
730
|
+
result = validate_refactor_list(data)
|
|
731
|
+
|
|
732
|
+
# Output validation result
|
|
733
|
+
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
734
|
+
|
|
735
|
+
if result["valid"]:
|
|
736
|
+
_write_json(args.output, data)
|
|
737
|
+
_info("Generated refactor-list written to {}".format(args.output))
|
|
738
|
+
return 0
|
|
739
|
+
else:
|
|
740
|
+
_err("Validation failed with {} error(s)".format(len(result["errors"])))
|
|
741
|
+
for e in result["errors"]:
|
|
742
|
+
_err(" " + e)
|
|
743
|
+
for w in result.get("warnings", []):
|
|
744
|
+
_warn(" " + w)
|
|
745
|
+
return 1
|
|
746
|
+
|
|
747
|
+
|
|
690
748
|
def cmd_summary(args):
|
|
691
749
|
"""Handle the 'summary' command."""
|
|
692
750
|
if not args.input:
|
|
@@ -719,6 +777,7 @@ def main():
|
|
|
719
777
|
" %(prog)s validate --input .prizmkit/plans/refactor-list.json\n"
|
|
720
778
|
" %(prog)s validate --input .prizmkit/plans/refactor-list.json --output validated.json\n"
|
|
721
779
|
" %(prog)s template --output .prizmkit/plans/refactor-list.json\n"
|
|
780
|
+
" %(prog)s generate --input draft.json --output .prizmkit/plans/refactor-list.json\n"
|
|
722
781
|
" %(prog)s summary --input .prizmkit/plans/refactor-list.json\n"
|
|
723
782
|
" %(prog)s summary --input .prizmkit/plans/refactor-list.json --format json\n"
|
|
724
783
|
),
|
|
@@ -747,6 +806,18 @@ def main():
|
|
|
747
806
|
"--output", required=True, help="Path to write template file"
|
|
748
807
|
)
|
|
749
808
|
|
|
809
|
+
# -- generate --
|
|
810
|
+
p_generate = subparsers.add_parser(
|
|
811
|
+
"generate",
|
|
812
|
+
help="Validate a draft and generate final refactor-list.json with defaults",
|
|
813
|
+
)
|
|
814
|
+
p_generate.add_argument(
|
|
815
|
+
"--input", required=True, help="Path to draft JSON (or '-' for stdin)"
|
|
816
|
+
)
|
|
817
|
+
p_generate.add_argument(
|
|
818
|
+
"--output", required=True, help="Path to write final refactor-list.json"
|
|
819
|
+
)
|
|
820
|
+
|
|
750
821
|
# -- summary --
|
|
751
822
|
p_summary = subparsers.add_parser(
|
|
752
823
|
"summary",
|
|
@@ -771,6 +842,7 @@ def main():
|
|
|
771
842
|
dispatch = {
|
|
772
843
|
"validate": cmd_validate,
|
|
773
844
|
"template": cmd_template,
|
|
845
|
+
"generate": cmd_generate,
|
|
774
846
|
"summary": cmd_summary,
|
|
775
847
|
}
|
|
776
848
|
|