prizmkit 1.1.47 → 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/bugfix-pipeline-launcher/SKILL.md +16 -9
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +16 -11
- package/bundled/skills/feature-planner/SKILL.md +16 -9
- package/bundled/skills/feature-planner/references/browser-interaction.md +13 -1
- package/bundled/skills/feature-planner/scripts/validate-and-generate.py +82 -0
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +16 -9
- 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
|
@@ -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())
|
|
@@ -112,23 +112,30 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
112
112
|
--action status 2>/dev/null
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
4. **Ask execution mode** (first user decision):
|
|
115
|
+
4. **Ask execution mode** (first user decision — SEPARATE `AskUserQuestion` call):
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
- **(1) Foreground** (recommended) — pipeline runs in the current session via `run-bugfix.sh run`. Visible output and direct error feedback.
|
|
119
|
-
- **(2) Background daemon** — pipeline runs fully detached via `launch-bugfix-daemon.sh`. Survives AI CLI session closure.
|
|
120
|
-
- **(3) Manual** — display the final assembled commands only. Do not execute anything. User runs them on their own.
|
|
117
|
+
⛔ **This MUST be its own standalone `AskUserQuestion` call.** Do NOT combine execution mode with step 5 config questions. The execution mode question is asked ALONE, user responds, THEN you proceed to step 5.
|
|
121
118
|
|
|
122
|
-
|
|
119
|
+
Use `AskUserQuestion` with exactly 1 question:
|
|
123
120
|
|
|
124
|
-
|
|
121
|
+
**Question 1 — Execution mode** (multiSelect: false):
|
|
122
|
+
- Foreground (Recommended) — pipeline runs in the current session via `run-bugfix.sh run`. Visible output and direct error feedback.
|
|
123
|
+
- Background daemon — pipeline runs fully detached via `launch-bugfix-daemon.sh`. Survives AI CLI session closure.
|
|
124
|
+
- Manual — display the final assembled commands only. Do not execute anything. User runs them on their own.
|
|
125
|
+
|
|
126
|
+
⚠️ STOP HERE and wait for user response before continuing to step 5.
|
|
127
|
+
|
|
128
|
+
5. **Ask configuration options** ⚠️ MANDATORY INTERACTIVE STEP — applies to ALL execution modes (Foreground, Background, AND Manual). This is a SEPARATE `AskUserQuestion` call from step 4. You MUST ask the user to configure options and WAIT for their response BEFORE proceeding to step 6.
|
|
129
|
+
|
|
130
|
+
⛔ **HARD STOP**: You MUST call `AskUserQuestion` with the 4 questions below and WAIT for the user's response. You MUST NOT:
|
|
131
|
+
- Combine step 4 and step 5 into one `AskUserQuestion` call (this is the most common violation — execution mode MUST be asked separately in step 4)
|
|
125
132
|
- Skip this step and jump to the next step
|
|
126
133
|
- Merge this step and the next step into one response
|
|
127
134
|
- Assume default values and show the command without asking
|
|
128
135
|
- Show the command as text and ask "ready?" without presenting the options
|
|
129
|
-
If you find yourself writing the final command before the user has answered these questions, STOP — you are violating this rule.
|
|
136
|
+
If you find yourself writing the final command before the user has answered these 4 questions, STOP — you are violating this rule.
|
|
130
137
|
|
|
131
|
-
Use `AskUserQuestion` to present
|
|
138
|
+
Use `AskUserQuestion` to present ALL 4 configuration choices (the full 4-question budget goes to config, NOT shared with execution mode):
|
|
132
139
|
|
|
133
140
|
**Question 1 — Verbose logging** (multiSelect: false):
|
|
134
141
|
- On (default) — Detailed AI session logs including tool calls and subagent activity
|
|
@@ -129,23 +129,30 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
129
129
|
|
|
130
130
|
If `global_context.database` is absent and no features mention database keywords, the script skips DB checks automatically.
|
|
131
131
|
|
|
132
|
-
5. **Ask execution mode** (first user decision):
|
|
132
|
+
5. **Ask execution mode** (first user decision — SEPARATE `AskUserQuestion` call):
|
|
133
133
|
|
|
134
|
-
|
|
135
|
-
- **(1) Foreground** (recommended) — pipeline runs in the current session via `run-feature.sh run`. Visible output and direct error feedback.
|
|
136
|
-
- **(2) Background daemon** — pipeline runs fully detached via `launch-feature-daemon.sh`. Survives AI CLI session closure.
|
|
137
|
-
- **(3) Manual** — display the final assembled commands only. Do not execute anything. User runs them on their own.
|
|
134
|
+
⛔ **This MUST be its own standalone `AskUserQuestion` call.** Do NOT combine execution mode with step 6 config questions. The execution mode question is asked ALONE, user responds, THEN you proceed to step 6.
|
|
138
135
|
|
|
139
|
-
|
|
136
|
+
Use `AskUserQuestion` with exactly 1 question:
|
|
137
|
+
|
|
138
|
+
**Question 1 — Execution mode** (multiSelect: false):
|
|
139
|
+
- Foreground (Recommended) — pipeline runs in the current session via `run-feature.sh run`. Visible output and direct error feedback.
|
|
140
|
+
- Background daemon — pipeline runs fully detached via `launch-feature-daemon.sh`. Survives AI CLI session closure.
|
|
141
|
+
- Manual — display the final assembled commands only. Do not execute anything. User runs them on their own.
|
|
142
|
+
|
|
143
|
+
⚠️ STOP HERE and wait for user response before continuing to step 6.
|
|
144
|
+
|
|
145
|
+
6. **Ask configuration options** ⚠️ MANDATORY INTERACTIVE STEP — applies to ALL execution modes (Foreground, Background, AND Manual). This is a SEPARATE `AskUserQuestion` call from step 5. You MUST ask the user to configure options and WAIT for their response BEFORE proceeding to step 7.
|
|
140
146
|
|
|
141
147
|
⛔ **HARD STOP**: You MUST call `AskUserQuestion` with the 4 questions below and WAIT for the user's response. You MUST NOT:
|
|
148
|
+
- Combine step 5 and step 6 into one `AskUserQuestion` call (this is the most common violation — execution mode MUST be asked separately in step 5)
|
|
142
149
|
- Skip this step and jump to step 7
|
|
143
150
|
- Merge step 6 and step 7 into one response
|
|
144
151
|
- Assume default values and show the command without asking
|
|
145
152
|
- Show the command as text and ask "ready?" without presenting the options
|
|
146
|
-
If you find yourself writing the final command before the user has answered these questions, STOP — you are violating this rule.
|
|
153
|
+
If you find yourself writing the final command before the user has answered these 4 questions, STOP — you are violating this rule.
|
|
147
154
|
|
|
148
|
-
Use `AskUserQuestion` to present
|
|
155
|
+
Use `AskUserQuestion` to present ALL 4 configuration choices (the full 4-question budget goes to config, NOT shared with execution mode):
|
|
149
156
|
|
|
150
157
|
**Question 1 — Critic review** (multiSelect: false):
|
|
151
158
|
- Off (default) — Skip adversarial review
|
|
@@ -162,9 +169,7 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
162
169
|
|
|
163
170
|
**Question 4 — Advanced config?** (multiSelect: false):
|
|
164
171
|
- No (default) — Use defaults for session timeout and failure behavior
|
|
165
|
-
- Yes — Configure session timeout
|
|
166
|
-
|
|
167
|
-
Note: Due to the 4-question limit per `AskUserQuestion` call, Feature filter and Browser verify use their defaults (all features, auto-detect browser tools). If the user selects "Other" on any option, handle their custom input.
|
|
172
|
+
- Yes — Configure session timeout, stop-on-failure, and deploy-after-completion options
|
|
168
173
|
|
|
169
174
|
Default Critic to Off unless features have `estimated_complexity: "high"` or above (in which case default to On).
|
|
170
175
|
|
|
@@ -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
|
|
|
@@ -284,10 +287,11 @@ A feature is **exempt** when ANY true:
|
|
|
284
287
|
|
|
285
288
|
### Default Behavior (Phase 4.2)
|
|
286
289
|
|
|
287
|
-
1. **
|
|
288
|
-
2. **
|
|
289
|
-
3. **
|
|
290
|
-
4. **
|
|
290
|
+
1. **Ask user for browser tool preference (Mandatory)**: Before generating any `browser_interaction` fields, use `AskUserQuestion` to ask which browser verification tool to use as the project default. Read `${SKILL_DIR}/references/browser-interaction.md` §Browser Tool Selection for the exact question format and options (`auto`/`playwright-cli`/`opencli`). Do NOT skip this step or assume a default without asking.
|
|
291
|
+
2. **Auto-generate** `browser_interaction` for ALL qualifying features. Read `${SKILL_DIR}/references/browser-interaction.md` for the object format, field rules, and per-feature `tool` override logic.
|
|
292
|
+
3. **Tool assignment per feature**: Use the user's chosen default. Override to `"opencli"` when the feature involves OAuth/SSO callbacks, third-party dashboard verification, or requires real login state. Override to `"playwright-cli"` when the feature is purely local UI (forms, components, routing). If user chose `"auto"`, AI assigns per-feature based on these rules.
|
|
293
|
+
4. **Present a summary** to the user showing which features received `browser_interaction` (including the `tool` value for each).
|
|
294
|
+
5. **User can opt-OUT** specific features or override the `tool` choice.
|
|
291
295
|
|
|
292
296
|
## Output Rules
|
|
293
297
|
|
|
@@ -308,10 +312,13 @@ Key requirements:
|
|
|
308
312
|
- `high` → **standard** (orchestrator + dev + reviewer, 3 agents)
|
|
309
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
|
|
310
314
|
|
|
311
|
-
|
|
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:
|
|
312
318
|
```bash
|
|
313
|
-
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>
|
|
314
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.
|
|
315
322
|
|
|
316
323
|
## Testing Defaults (Phase 8)
|
|
317
324
|
|
|
@@ -323,7 +330,7 @@ Set default testing-related fields for each feature. The user can opt out.
|
|
|
323
330
|
| medium | `true` | `1` | Single critic review |
|
|
324
331
|
| low | `false` | (omitted) | Skip critic |
|
|
325
332
|
|
|
326
|
-
For frontend features with `browser_interaction`, browser verification is enabled by default. The `tool` field
|
|
333
|
+
For frontend features with `browser_interaction`, browser verification is enabled by default. The `tool` field uses the user's choice from the mandatory browser tool question in Phase 4.2 (see §Browser Interaction Planning → Default Behavior).
|
|
327
334
|
|
|
328
335
|
Present a consolidated testing summary table at Phase 8, then ask for confirmation.
|
|
329
336
|
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
# Browser Interaction Planning
|
|
2
2
|
|
|
3
|
-
For web apps with UI, features that involve user-facing pages or interactive flows can optionally include a `browser_interaction` field. This enables the dev-pipeline to verify UI behavior automatically using `playwright-cli`
|
|
3
|
+
For web apps with UI, features that involve user-facing pages or interactive flows can optionally include a `browser_interaction` field. This enables the dev-pipeline to verify UI behavior automatically after implementation using the configured browser tool (`playwright-cli` or `opencli`).
|
|
4
|
+
|
|
5
|
+
## Browser Tool Selection (Mandatory — Ask User)
|
|
6
|
+
|
|
7
|
+
Before auto-generating `browser_interaction` for features, you MUST ask the user which browser tool to use as the project default via `AskUserQuestion`:
|
|
8
|
+
|
|
9
|
+
**Question**: "Which browser verification tool should this project use by default?"
|
|
10
|
+
- **Auto — AI chooses per feature (Recommended)** — `playwright-cli` for local UI, `opencli` for authenticated flows. AI decides per-feature at planning time.
|
|
11
|
+
- **playwright-cli** — isolated browser instance, no login state. Best for local dev server, forms, components.
|
|
12
|
+
- **opencli** — reuses Chrome's logged-in sessions via Browser Bridge. Best for OAuth, third-party dashboards, SSO.
|
|
13
|
+
|
|
14
|
+
Store the user's choice as the project-level default. Individual features can still override (see Tool Selection per Feature below).
|
|
4
15
|
|
|
5
16
|
## How to Capture
|
|
6
17
|
|
|
@@ -11,6 +22,7 @@ For each qualifying feature, generate the `browser_interaction` object:
|
|
|
11
22
|
```json
|
|
12
23
|
{
|
|
13
24
|
"browser_interaction": {
|
|
25
|
+
"tool": "auto",
|
|
14
26
|
"verify_steps": [
|
|
15
27
|
"Verify login form renders with email and password fields",
|
|
16
28
|
"Verify valid credentials redirect to dashboard",
|