prizmkit 1.0.125 → 1.0.127
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/README.md +3 -3
- package/bundled/dev-pipeline/assets/feature-list-example.json +6 -6
- package/bundled/dev-pipeline/lib/branch.sh +9 -3
- package/bundled/dev-pipeline/lib/common.sh +8 -4
- package/bundled/dev-pipeline/run-bugfix.sh +26 -0
- package/bundled/dev-pipeline/run.sh +19 -25
- package/bundled/dev-pipeline/scripts/update-bug-status.py +4 -2
- package/bundled/dev-pipeline/scripts/update-feature-status.py +7 -4
- package/bundled/dev-pipeline/templates/bug-fix-list-schema.json +3 -3
- package/bundled/dev-pipeline/templates/feature-list-schema.json +3 -2
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/scripts/validate-and-generate.py +4 -14
- package/bundled/skills/bug-planner/SKILL.md +2 -2
- package/bundled/skills/bug-planner/scripts/validate-bug-list.py +3 -4
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +5 -5
- package/package.json +1 -1
package/bundled/VERSION.json
CHANGED
|
@@ -205,7 +205,7 @@ Equivalent to `run.sh` but for the bug-fix pipeline.
|
|
|
205
205
|
|
|
206
206
|
**Single-bug options:** `--dry-run`, `--timeout N` (same as `run.sh`)
|
|
207
207
|
|
|
208
|
-
Processes bugs by: **severity** (critical > high > medium > low) then **priority**
|
|
208
|
+
Processes bugs by: **severity** (critical > high > medium > low) then **priority** (high > medium > low).
|
|
209
209
|
|
|
210
210
|
---
|
|
211
211
|
|
|
@@ -250,7 +250,7 @@ python3 scripts/init-pipeline.py \
|
|
|
250
250
|
**Validation checks:**
|
|
251
251
|
- Schema: `$schema == "dev-pipeline-feature-list-v1"`
|
|
252
252
|
- Required fields: `app_name` (string), `features` (non-empty array)
|
|
253
|
-
- Per-feature: `id` (F-NNN), `title`, `description`, `priority` (
|
|
253
|
+
- Per-feature: `id` (F-NNN), `title`, `description`, `priority` (high/medium/low), `dependencies` (array of F-NNN), `acceptance_criteria` (array), `status`
|
|
254
254
|
- Dependency DAG cycle detection (Kahn's algorithm)
|
|
255
255
|
|
|
256
256
|
**Output (JSON to stdout):**
|
|
@@ -406,7 +406,7 @@ python3 scripts/update-bug-status.py \
|
|
|
406
406
|
|
|
407
407
|
**Actions:** `get_next`, `update`, `status`, `pause`, `reset`, `clean`
|
|
408
408
|
|
|
409
|
-
**Priority order:** severity (critical > high > medium > low), then priority
|
|
409
|
+
**Priority order:** severity (critical > high > medium > low), then priority (high > medium > low).
|
|
410
410
|
|
|
411
411
|
---
|
|
412
412
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"id": "F-001",
|
|
11
11
|
"title": "Project Infrastructure Setup",
|
|
12
12
|
"description": "Initialize the monorepo structure with Next.js frontend, Express backend, and PostgreSQL database. Set up ESLint, Prettier, TypeScript configs, and CI/CD basics.",
|
|
13
|
-
"priority":
|
|
13
|
+
"priority": "high",
|
|
14
14
|
"estimated_complexity": "medium",
|
|
15
15
|
"dependencies": [],
|
|
16
16
|
"acceptance_criteria": [
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"id": "F-002",
|
|
29
29
|
"title": "User Authentication",
|
|
30
30
|
"description": "Implement email/password registration and login with JWT tokens. Include password reset via email, session management, and protected routes.",
|
|
31
|
-
"priority":
|
|
31
|
+
"priority": "high",
|
|
32
32
|
"estimated_complexity": "medium",
|
|
33
33
|
"dependencies": [
|
|
34
34
|
"F-001"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"id": "F-003",
|
|
49
49
|
"title": "Task CRUD Operations",
|
|
50
50
|
"description": "Core task management: create, read, update, delete tasks. Tasks have title, description, status (todo/in-progress/done), priority, due date, and assignee.",
|
|
51
|
-
"priority":
|
|
51
|
+
"priority": "high",
|
|
52
52
|
"estimated_complexity": "medium",
|
|
53
53
|
"dependencies": [
|
|
54
54
|
"F-002"
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"id": "F-004",
|
|
69
69
|
"title": "Project & Team Management",
|
|
70
70
|
"description": "Organize tasks into projects. Support team creation, member invitation, and role-based permissions (owner, admin, member, viewer).",
|
|
71
|
-
"priority":
|
|
71
|
+
"priority": "medium",
|
|
72
72
|
"estimated_complexity": "high",
|
|
73
73
|
"dependencies": [
|
|
74
74
|
"F-003"
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"id": "F-005",
|
|
100
100
|
"title": "Real-time Updates",
|
|
101
101
|
"description": "Add WebSocket-based real-time notifications for task changes, comments, and team activity. Show live updates without page refresh.",
|
|
102
|
-
"priority":
|
|
102
|
+
"priority": "medium",
|
|
103
103
|
"estimated_complexity": "high",
|
|
104
104
|
"dependencies": [
|
|
105
105
|
"F-003"
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"id": "F-006",
|
|
120
120
|
"title": "Analytics Dashboard",
|
|
121
121
|
"description": "Dashboard showing task completion rates, team productivity metrics, overdue task tracking, and project progress visualization.",
|
|
122
|
-
"priority":
|
|
122
|
+
"priority": "low",
|
|
123
123
|
"estimated_complexity": "medium",
|
|
124
124
|
"dependencies": [
|
|
125
125
|
"F-004",
|
|
@@ -94,12 +94,12 @@ branch_merge() {
|
|
|
94
94
|
local auto_push="${4:-0}"
|
|
95
95
|
|
|
96
96
|
# Step 1: Checkout original branch
|
|
97
|
-
#
|
|
97
|
+
# Stash any uncommitted changes (e.g. untracked state/ files) so checkout is not blocked
|
|
98
|
+
local had_stash=false
|
|
98
99
|
local remaining_dirty
|
|
99
100
|
remaining_dirty=$(git -C "$project_root" status --porcelain 2>/dev/null || true)
|
|
100
101
|
if [[ -n "$remaining_dirty" ]]; then
|
|
101
|
-
git -C "$project_root"
|
|
102
|
-
git -C "$project_root" commit --no-verify -m "chore: include pipeline state artifacts" 2>/dev/null || true
|
|
102
|
+
git -C "$project_root" stash push --include-untracked -m "pipeline-merge-stash" 2>/dev/null && had_stash=true || true
|
|
103
103
|
fi
|
|
104
104
|
|
|
105
105
|
# Step 2: Rebase dev branch onto original to make it fast-forwardable.
|
|
@@ -112,11 +112,13 @@ branch_merge() {
|
|
|
112
112
|
log_error " git rebase --abort # then resolve conflicts and retry"
|
|
113
113
|
git -C "$project_root" rebase --abort 2>/dev/null || true
|
|
114
114
|
git -C "$project_root" checkout "$dev_branch" 2>/dev/null || true
|
|
115
|
+
[[ "$had_stash" == true ]] && git -C "$project_root" stash pop 2>/dev/null || true
|
|
115
116
|
return 1
|
|
116
117
|
fi
|
|
117
118
|
# After the rebase we are on dev_branch — checkout original for the fast-forward
|
|
118
119
|
if ! git -C "$project_root" checkout "$original_branch" 2>/dev/null; then
|
|
119
120
|
log_error "Failed to checkout $original_branch for merge"
|
|
121
|
+
[[ "$had_stash" == true ]] && git -C "$project_root" stash pop 2>/dev/null || true
|
|
120
122
|
return 1
|
|
121
123
|
fi
|
|
122
124
|
|
|
@@ -125,6 +127,7 @@ branch_merge() {
|
|
|
125
127
|
log_error "Merge failed after rebase — this should not happen, resolve manually:"
|
|
126
128
|
log_error " git checkout $original_branch && git rebase $dev_branch"
|
|
127
129
|
git -C "$project_root" checkout "$dev_branch" 2>/dev/null || true
|
|
130
|
+
[[ "$had_stash" == true ]] && git -C "$project_root" stash pop 2>/dev/null || true
|
|
128
131
|
return 1
|
|
129
132
|
fi
|
|
130
133
|
|
|
@@ -144,5 +147,8 @@ branch_merge() {
|
|
|
144
147
|
git -C "$project_root" branch -d "$dev_branch" 2>/dev/null && \
|
|
145
148
|
log_info "Deleted merged branch: $dev_branch" || true
|
|
146
149
|
|
|
150
|
+
# Step 6: Restore stashed state/ files
|
|
151
|
+
[[ "$had_stash" == true ]] && git -C "$project_root" stash pop 2>/dev/null || true
|
|
152
|
+
|
|
147
153
|
return 0
|
|
148
154
|
}
|
|
@@ -43,6 +43,10 @@ try:
|
|
|
43
43
|
with open('.prizmkit/config.json') as f:
|
|
44
44
|
d = json.load(f)
|
|
45
45
|
v = d.get('ai_cli', '')
|
|
46
|
+
if not v:
|
|
47
|
+
p = d.get('platform', '')
|
|
48
|
+
if p == 'claude': v = 'claude'
|
|
49
|
+
elif p == 'codebuddy': v = 'cbc'
|
|
46
50
|
if v: print(v)
|
|
47
51
|
except: pass
|
|
48
52
|
" 2>/dev/null || true)
|
|
@@ -50,20 +54,20 @@ except: pass
|
|
|
50
54
|
_raw_cli="$_config_ai_cli"
|
|
51
55
|
elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
52
56
|
_raw_cli="$CODEBUDDY_CLI"
|
|
53
|
-
elif command -v cbc &>/dev/null; then
|
|
54
|
-
_raw_cli="cbc"
|
|
55
57
|
elif command -v claude &>/dev/null; then
|
|
56
58
|
_raw_cli="claude"
|
|
59
|
+
elif command -v cbc &>/dev/null; then
|
|
60
|
+
_raw_cli="cbc"
|
|
57
61
|
else
|
|
58
62
|
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
|
|
59
63
|
exit 1
|
|
60
64
|
fi
|
|
61
65
|
elif [[ -n "${CODEBUDDY_CLI:-}" ]]; then
|
|
62
66
|
_raw_cli="$CODEBUDDY_CLI"
|
|
63
|
-
elif command -v cbc &>/dev/null; then
|
|
64
|
-
_raw_cli="cbc"
|
|
65
67
|
elif command -v claude &>/dev/null; then
|
|
66
68
|
_raw_cli="claude"
|
|
69
|
+
elif command -v cbc &>/dev/null; then
|
|
70
|
+
_raw_cli="cbc"
|
|
67
71
|
else
|
|
68
72
|
echo "ERROR: No AI CLI found. Install CodeBuddy (cbc) or Claude Code (claude)." >&2
|
|
69
73
|
exit 1
|
|
@@ -348,6 +348,19 @@ run_one() {
|
|
|
348
348
|
log_error "Failed to initialize bugfix pipeline state"
|
|
349
349
|
exit 1
|
|
350
350
|
}
|
|
351
|
+
|
|
352
|
+
# Ensure state directory is gitignored
|
|
353
|
+
local _gitignore_path
|
|
354
|
+
_gitignore_path="$(cd "$SCRIPT_DIR/.." && pwd)/.gitignore"
|
|
355
|
+
local _state_rel
|
|
356
|
+
_state_rel=$(python3 -c "import os; print(os.path.relpath('$STATE_DIR', '$(cd "$SCRIPT_DIR/.." && pwd)'))" 2>/dev/null || echo "dev-pipeline/bugfix-state")
|
|
357
|
+
if [[ -f "$_gitignore_path" ]]; then
|
|
358
|
+
if ! grep -qF "$_state_rel" "$_gitignore_path" 2>/dev/null; then
|
|
359
|
+
printf '\n# Pipeline runtime state (auto-added by dev-pipeline)\n%s/\n' "$_state_rel" >> "$_gitignore_path"
|
|
360
|
+
fi
|
|
361
|
+
else
|
|
362
|
+
printf '# Pipeline runtime state (auto-added by dev-pipeline)\n%s/\n' "$_state_rel" > "$_gitignore_path"
|
|
363
|
+
fi
|
|
351
364
|
fi
|
|
352
365
|
|
|
353
366
|
# Verify bug exists
|
|
@@ -529,6 +542,19 @@ main() {
|
|
|
529
542
|
local bugs_count
|
|
530
543
|
bugs_count=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('bugs_count', 0))" 2>/dev/null || echo "0")
|
|
531
544
|
log_success "Bugfix pipeline initialized with $bugs_count bugs"
|
|
545
|
+
|
|
546
|
+
# Ensure state directory is gitignored
|
|
547
|
+
local _gitignore_path
|
|
548
|
+
_gitignore_path="$(cd "$SCRIPT_DIR/.." && pwd)/.gitignore"
|
|
549
|
+
local _state_rel
|
|
550
|
+
_state_rel=$(python3 -c "import os; print(os.path.relpath('$STATE_DIR', '$(cd "$SCRIPT_DIR/.." && pwd)'))" 2>/dev/null || echo "dev-pipeline/bugfix-state")
|
|
551
|
+
if [[ -f "$_gitignore_path" ]]; then
|
|
552
|
+
if ! grep -qF "$_state_rel" "$_gitignore_path" 2>/dev/null; then
|
|
553
|
+
printf '\n# Pipeline runtime state (auto-added by dev-pipeline)\n%s/\n' "$_state_rel" >> "$_gitignore_path"
|
|
554
|
+
fi
|
|
555
|
+
else
|
|
556
|
+
printf '# Pipeline runtime state (auto-added by dev-pipeline)\n%s/\n' "$_state_rel" > "$_gitignore_path"
|
|
557
|
+
fi
|
|
532
558
|
else
|
|
533
559
|
log_info "Resuming existing bugfix pipeline..."
|
|
534
560
|
fi
|
|
@@ -828,6 +828,21 @@ main() {
|
|
|
828
828
|
local features_count
|
|
829
829
|
features_count=$(echo "$init_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('features_count', 0))" 2>/dev/null || echo "0")
|
|
830
830
|
log_success "Pipeline initialized with $features_count features"
|
|
831
|
+
|
|
832
|
+
# Ensure state directory is gitignored (prevents branch-switch state loss)
|
|
833
|
+
local _gitignore_path
|
|
834
|
+
_gitignore_path="$(cd "$SCRIPT_DIR/.." && pwd)/.gitignore"
|
|
835
|
+
local _state_rel
|
|
836
|
+
_state_rel=$(python3 -c "import os; print(os.path.relpath('$STATE_DIR', '$(cd "$SCRIPT_DIR/.." && pwd)'))" 2>/dev/null || echo "dev-pipeline/state")
|
|
837
|
+
if [[ -f "$_gitignore_path" ]]; then
|
|
838
|
+
if ! grep -qF "$_state_rel" "$_gitignore_path" 2>/dev/null; then
|
|
839
|
+
printf '\n# Pipeline runtime state (auto-added by dev-pipeline)\n%s/\n' "$_state_rel" >> "$_gitignore_path"
|
|
840
|
+
log_info "Added $_state_rel/ to .gitignore"
|
|
841
|
+
fi
|
|
842
|
+
else
|
|
843
|
+
printf '# Pipeline runtime state (auto-added by dev-pipeline)\n%s/\n' "$_state_rel" > "$_gitignore_path"
|
|
844
|
+
log_info "Created .gitignore with $_state_rel/"
|
|
845
|
+
fi
|
|
831
846
|
else
|
|
832
847
|
log_info "Resuming existing pipeline..."
|
|
833
848
|
fi
|
|
@@ -1010,35 +1025,14 @@ for f in data.get('stuck_features', []):
|
|
|
1010
1025
|
else
|
|
1011
1026
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
1012
1027
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
1013
|
-
#
|
|
1014
|
-
|
|
1015
|
-
local _merge_failed_current _merge_failed_dirty
|
|
1016
|
-
_merge_failed_current=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
1017
|
-
if [[ "$_merge_failed_current" == "$_ORIGINAL_BRANCH" ]]; then
|
|
1018
|
-
_merge_failed_dirty=$(git -C "$_proj_root" status --porcelain 2>/dev/null || true)
|
|
1019
|
-
if [[ -n "$_merge_failed_dirty" ]]; then
|
|
1020
|
-
git -C "$_proj_root" add -A 2>/dev/null || true
|
|
1021
|
-
git -C "$_proj_root" commit --no-verify -m "chore: include pipeline state artifacts" 2>/dev/null || true
|
|
1022
|
-
fi
|
|
1023
|
-
fi
|
|
1024
|
-
fi
|
|
1028
|
+
# Return to original branch; state/ files are untracked and persist across checkout
|
|
1029
|
+
git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null || true
|
|
1025
1030
|
_DEV_BRANCH_NAME=""
|
|
1026
1031
|
fi
|
|
1027
1032
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
1028
1033
|
# Session failed — return to original branch, preserve dev branch for inspection
|
|
1029
|
-
if git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null; then
|
|
1030
|
-
|
|
1031
|
-
local _failed_current _failed_dirty
|
|
1032
|
-
_failed_current=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
1033
|
-
if [[ "$_failed_current" == "$_ORIGINAL_BRANCH" ]]; then
|
|
1034
|
-
_failed_dirty=$(git -C "$_proj_root" status --porcelain 2>/dev/null || true)
|
|
1035
|
-
if [[ -n "$_failed_dirty" ]]; then
|
|
1036
|
-
git -C "$_proj_root" add -A 2>/dev/null || true
|
|
1037
|
-
git -C "$_proj_root" commit --no-verify -m "chore: include pipeline state artifacts" 2>/dev/null || true
|
|
1038
|
-
fi
|
|
1039
|
-
fi
|
|
1040
|
-
else
|
|
1041
|
-
log_warn "Failed to checkout $_ORIGINAL_BRANCH after session failure — pipeline state may be uncommitted"
|
|
1034
|
+
if ! git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null; then
|
|
1035
|
+
log_warn "Failed to checkout $_ORIGINAL_BRANCH after session failure — staying on dev branch"
|
|
1042
1036
|
fi
|
|
1043
1037
|
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
1044
1038
|
_DEV_BRANCH_NAME=""
|
|
@@ -142,7 +142,7 @@ def action_get_next(bug_list_data, state_dir):
|
|
|
142
142
|
Priority logic:
|
|
143
143
|
1. Skip terminal statuses (completed, failed, skipped, needs_info)
|
|
144
144
|
2. Prefer in_progress bugs (interrupted session resume) over pending
|
|
145
|
-
3. Sort by: severity (critical > high > medium > low), then by priority
|
|
145
|
+
3. Sort by: severity (critical > high > medium > low), then by priority (high > medium > low)
|
|
146
146
|
"""
|
|
147
147
|
bugs = bug_list_data.get("bugs", [])
|
|
148
148
|
if not bugs:
|
|
@@ -183,10 +183,12 @@ def action_get_next(bug_list_data, state_dir):
|
|
|
183
183
|
elif bstatus == "pending":
|
|
184
184
|
pending_bugs.append(bug)
|
|
185
185
|
|
|
186
|
+
_PRIORITY_ORDER = {"high": 0, "medium": 1, "low": 2}
|
|
187
|
+
|
|
186
188
|
def sort_key(b):
|
|
187
189
|
severity = b.get("severity", "medium")
|
|
188
190
|
sev_order = SEVERITY_PRIORITY.get(severity, 2)
|
|
189
|
-
priority = b.get("priority",
|
|
191
|
+
priority = _PRIORITY_ORDER.get(b.get("priority", "low"), 2)
|
|
190
192
|
return (sev_order, priority)
|
|
191
193
|
|
|
192
194
|
if in_progress_bugs:
|
|
@@ -334,7 +334,7 @@ def action_get_next(feature_list_data, state_dir, feature_filter=None):
|
|
|
334
334
|
2. If feature_filter is set, skip features not in the filter
|
|
335
335
|
3. Check that all dependencies are completed
|
|
336
336
|
4. Prefer in_progress features over pending ones (interrupted session resume)
|
|
337
|
-
5. Among eligible features, pick
|
|
337
|
+
5. Among eligible features, pick highest priority (high > medium > low)
|
|
338
338
|
"""
|
|
339
339
|
features = feature_list_data.get("features", [])
|
|
340
340
|
if not features:
|
|
@@ -412,16 +412,19 @@ def action_get_next(feature_list_data, state_dir, feature_filter=None):
|
|
|
412
412
|
else:
|
|
413
413
|
pending_features.append(feature)
|
|
414
414
|
|
|
415
|
-
#
|
|
415
|
+
# Priority mapping: string enum → sort order (high first)
|
|
416
|
+
_PRIORITY_ORDER = {"high": 0, "medium": 1, "low": 2}
|
|
417
|
+
|
|
418
|
+
# Prefer in_progress features, then pending; sort by priority (high > medium > low)
|
|
416
419
|
if in_progress_features:
|
|
417
420
|
candidates = sorted(
|
|
418
421
|
in_progress_features,
|
|
419
|
-
key=lambda f: f.get("priority",
|
|
422
|
+
key=lambda f: _PRIORITY_ORDER.get(f.get("priority", "low"), 2)
|
|
420
423
|
)
|
|
421
424
|
else:
|
|
422
425
|
candidates = sorted(
|
|
423
426
|
pending_features,
|
|
424
|
-
key=lambda f: f.get("priority",
|
|
427
|
+
key=lambda f: _PRIORITY_ORDER.get(f.get("priority", "low"), 2)
|
|
425
428
|
)
|
|
426
429
|
|
|
427
430
|
chosen = candidates[0]
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"description": "critical=crash/data-loss; high=core-feature-broken; medium=workaround-exists; low=cosmetic"
|
|
56
56
|
},
|
|
57
57
|
"priority": {
|
|
58
|
-
"type": "
|
|
59
|
-
"
|
|
60
|
-
"description": "Fix priority
|
|
58
|
+
"type": "string",
|
|
59
|
+
"enum": ["high", "medium", "low"],
|
|
60
|
+
"description": "Fix priority. Pipeline processes high before medium before low; within same level, array order determines sequence."
|
|
61
61
|
},
|
|
62
62
|
"error_source": {
|
|
63
63
|
"type": "object",
|
|
@@ -46,8 +46,9 @@
|
|
|
46
46
|
"minLength": 1
|
|
47
47
|
},
|
|
48
48
|
"priority": {
|
|
49
|
-
"type": "
|
|
50
|
-
"
|
|
49
|
+
"type": "string",
|
|
50
|
+
"enum": ["high", "medium", "low"],
|
|
51
|
+
"description": "Feature priority. Pipeline processes high before medium before low; within same level, array order determines sequence."
|
|
51
52
|
},
|
|
52
53
|
"estimated_complexity": {
|
|
53
54
|
"type": "string",
|
|
@@ -34,6 +34,7 @@ SCHEMA_VERSION = "dev-pipeline-feature-list-v1"
|
|
|
34
34
|
|
|
35
35
|
VALID_STATUSES = {"pending", "in_progress", "completed", "failed", "skipped", "split"}
|
|
36
36
|
VALID_COMPLEXITIES = {"low", "medium", "high"}
|
|
37
|
+
VALID_PRIORITIES = {"high", "medium", "low"}
|
|
37
38
|
VALID_GRANULARITIES = {"feature", "sub_feature", "auto"}
|
|
38
39
|
VALID_PLANNING_MODES = {"new", "incremental"}
|
|
39
40
|
|
|
@@ -251,10 +252,10 @@ def validate_feature_list(data, planning_mode="new"):
|
|
|
251
252
|
|
|
252
253
|
# -- Priority --
|
|
253
254
|
priority = feat.get("priority")
|
|
254
|
-
if isinstance(priority,
|
|
255
|
+
if isinstance(priority, str) and priority in VALID_PRIORITIES:
|
|
255
256
|
priorities.append(priority)
|
|
256
257
|
else:
|
|
257
|
-
errors.append("{}: priority must be
|
|
258
|
+
errors.append("{}: priority must be one of 'high', 'medium', 'low', got {}".format(
|
|
258
259
|
label, repr(priority)
|
|
259
260
|
))
|
|
260
261
|
|
|
@@ -366,17 +367,6 @@ def validate_feature_list(data, planning_mode="new"):
|
|
|
366
367
|
seen_ids.add(sub_id)
|
|
367
368
|
total_sub_features += 1
|
|
368
369
|
|
|
369
|
-
# -- Priority uniqueness --
|
|
370
|
-
if len(priorities) != len(set(priorities)):
|
|
371
|
-
dup_prios = [
|
|
372
|
-
p for p, c in collections.Counter(priorities).items() if c > 1
|
|
373
|
-
]
|
|
374
|
-
warnings.append(
|
|
375
|
-
"Duplicate priorities found: {}".format(
|
|
376
|
-
", ".join(str(p) for p in sorted(dup_prios))
|
|
377
|
-
)
|
|
378
|
-
)
|
|
379
|
-
|
|
380
370
|
# ------------------------------------------------------------------
|
|
381
371
|
# 3. Dependency validation
|
|
382
372
|
# ------------------------------------------------------------------
|
|
@@ -439,7 +429,7 @@ def generate_template():
|
|
|
439
429
|
"Initialize project structure, configure build tools, "
|
|
440
430
|
"set up development environment."
|
|
441
431
|
),
|
|
442
|
-
"priority":
|
|
432
|
+
"priority": "high",
|
|
443
433
|
"estimated_complexity": "medium",
|
|
444
434
|
"dependencies": [],
|
|
445
435
|
"acceptance_criteria": [
|
|
@@ -163,7 +163,7 @@ Before writing the file, verify all items pass:
|
|
|
163
163
|
|
|
164
164
|
**Consistency checks:**
|
|
165
165
|
- [ ] No duplicate bug IDs
|
|
166
|
-
- [ ]
|
|
166
|
+
- [ ] If `priority` is set, must be one of `high`, `medium`, `low`
|
|
167
167
|
- [ ] If `affected_feature` is set, verify it exists in `feature-list.json` (if available)
|
|
168
168
|
|
|
169
169
|
If any check fails, fix before writing the file.
|
|
@@ -217,7 +217,7 @@ Validate existing `bug-fix-list.json`:
|
|
|
217
217
|
- Duplicate IDs
|
|
218
218
|
- Missing required fields
|
|
219
219
|
- Invalid status values
|
|
220
|
-
-
|
|
220
|
+
- Invalid priority values (must be 'high', 'medium', or 'low')
|
|
221
221
|
- Invalid `affected_feature` references (if feature-list.json exists)
|
|
222
222
|
4. Output: validation result with specific errors/warnings
|
|
223
223
|
|
|
@@ -110,12 +110,11 @@ def validate(bug_list_path, feature_list_path=None):
|
|
|
110
110
|
if status not in VALID_STATUSES:
|
|
111
111
|
errors.append(f"{prefix} ({bug_id}): invalid status '{status}' — must be one of {VALID_STATUSES}")
|
|
112
112
|
|
|
113
|
-
# Priority
|
|
113
|
+
# Priority validation (optional field)
|
|
114
114
|
priority = bug.get("priority")
|
|
115
115
|
if priority is not None:
|
|
116
|
-
if priority in
|
|
117
|
-
|
|
118
|
-
seen_priorities.add(priority)
|
|
116
|
+
if priority not in ("high", "medium", "low"):
|
|
117
|
+
errors.append(f"{prefix} ({bug_id}): invalid priority '{priority}' — must be one of 'high', 'medium', 'low'")
|
|
119
118
|
|
|
120
119
|
# Cross-reference affected_feature
|
|
121
120
|
affected_feature = bug.get("affected_feature")
|
|
@@ -151,13 +151,13 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
151
151
|
|
|
152
152
|
Default to (a). Only suggest (b) if features have `estimated_complexity: "high"` or above.
|
|
153
153
|
|
|
154
|
-
If user chooses (b),
|
|
154
|
+
If user chooses (b), prepend `ENABLE_CRITIC=true` to the launch command in step 6.
|
|
155
155
|
|
|
156
156
|
6. **Ask user to confirm**: "Ready to launch the pipeline? It will process N features."
|
|
157
157
|
|
|
158
|
-
7. **Launch** (based on chosen mode from step 4, with
|
|
159
|
-
- Foreground: `dev-pipeline/run.sh run feature-list.json
|
|
160
|
-
- Background: `dev-pipeline/launch-daemon.sh start feature-list.json
|
|
158
|
+
7. **Launch** (based on chosen mode from step 4, with `ENABLE_CRITIC=true` env var if chosen in step 5):
|
|
159
|
+
- Foreground: `ENABLE_CRITIC=true dev-pipeline/run.sh run feature-list.json`
|
|
160
|
+
- Background: `dev-pipeline/launch-daemon.sh start feature-list.json --env "ENABLE_CRITIC=true"`
|
|
161
161
|
- If user specified environment overrides:
|
|
162
162
|
```bash
|
|
163
163
|
dev-pipeline/launch-daemon.sh start feature-list.json --env "SESSION_TIMEOUT=7200 MAX_RETRIES=5"
|
|
@@ -259,7 +259,7 @@ When user specifies custom settings, map to environment variables:
|
|
|
259
259
|
| "max 5 retries" | `MAX_RETRIES=5` |
|
|
260
260
|
| "verbose mode" | `VERBOSE=1` |
|
|
261
261
|
| "heartbeat every 60s" | `HEARTBEAT_INTERVAL=60` |
|
|
262
|
-
| "enable critic review" | `--critic`
|
|
262
|
+
| "enable critic review" | `ENABLE_CRITIC=true` env var (or `--critic` for single feature) |
|
|
263
263
|
|
|
264
264
|
Pass via `--env`:
|
|
265
265
|
```bash
|