prizmkit 1.1.91 → 1.1.93
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/.env.example +0 -4
- package/bundled/dev-pipeline/README.md +9 -39
- package/bundled/dev-pipeline/SCHEMA_ANALYSIS.md +0 -4
- package/bundled/dev-pipeline/assets/prizm-dev-team-integration.md +0 -2
- package/bundled/dev-pipeline/launch-bugfix-daemon.sh +0 -1
- package/bundled/dev-pipeline/launch-feature-daemon.sh +2 -3
- package/bundled/dev-pipeline/launch-refactor-daemon.sh +0 -1
- package/bundled/dev-pipeline/lib/branch.sh +8 -5
- package/bundled/dev-pipeline/lib/common.sh +122 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +13 -3
- package/bundled/dev-pipeline/run-bugfix.sh +149 -98
- package/bundled/dev-pipeline/run-feature.sh +90 -103
- package/bundled/dev-pipeline/run-recovery.sh +1 -32
- package/bundled/dev-pipeline/run-refactor.sh +148 -99
- package/bundled/dev-pipeline/scripts/update-bug-status.py +39 -4
- package/bundled/dev-pipeline/scripts/update-feature-status.py +43 -6
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +36 -4
- package/bundled/dev-pipeline/tests/test-deploy-safety.sh +3 -3
- package/bundled/dev-pipeline/tests/test_auto_skip.py +92 -3
- package/bundled/dev-pipeline-windows/.env.example +0 -4
- package/bundled/dev-pipeline-windows/SCHEMA_ANALYSIS.md +0 -4
- package/bundled/dev-pipeline-windows/assets/prizm-dev-team-integration.md +0 -2
- package/bundled/dev-pipeline-windows/lib/branch.ps1 +17 -4
- package/bundled/dev-pipeline-windows/lib/common.ps1 +9 -0
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +89 -108
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +4 -19
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +39 -4
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +43 -6
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +36 -4
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/SKILL.md +1 -1
- package/bundled/skills/bugfix-pipeline-launcher/references/configuration.md +5 -15
- package/bundled/skills/feature-pipeline-launcher/SKILL.md +4 -4
- package/bundled/skills/feature-pipeline-launcher/references/configuration.md +3 -13
- package/bundled/skills/refactor-pipeline-launcher/SKILL.md +3 -3
- package/bundled/skills/refactor-pipeline-launcher/references/configuration.md +4 -14
- package/bundled/skills-windows/bugfix-pipeline-launcher/SKILL.md +1 -1
- package/bundled/skills-windows/bugfix-pipeline-launcher/references/configuration.md +4 -14
- package/bundled/skills-windows/feature-pipeline-launcher/SKILL.md +4 -4
- package/bundled/skills-windows/feature-pipeline-launcher/references/configuration.md +2 -12
- package/bundled/skills-windows/refactor-pipeline-launcher/SKILL.md +3 -3
- package/bundled/skills-windows/refactor-pipeline-launcher/references/configuration.md +4 -14
- package/package.json +1 -1
- package/bundled/dev-pipeline/scripts/cleanup-logs.py +0 -192
- package/bundled/dev-pipeline-windows/scripts/cleanup-logs.py +0 -192
|
@@ -8,14 +8,14 @@ set -euo pipefail
|
|
|
8
8
|
# fix bugs from a .prizmkit/plans/bug-fix-list.json specification.
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
11
|
-
# ./run-bugfix.sh run [.prizmkit/plans/bug-fix-list.json]
|
|
11
|
+
# ./run-bugfix.sh run [.prizmkit/plans/bug-fix-list.json] [options] Run all bugs
|
|
12
12
|
# ./run-bugfix.sh run <bug-id> [options] Run a single bug
|
|
13
13
|
# ./run-bugfix.sh status [.prizmkit/plans/bug-fix-list.json] Show pipeline status
|
|
14
14
|
# ./run-bugfix.sh reset Clear all state
|
|
15
15
|
#
|
|
16
16
|
# Environment Variables:
|
|
17
|
-
# MAX_RETRIES Max retries per bug (default: 3)
|
|
18
|
-
#
|
|
17
|
+
# MAX_RETRIES Max code retries per bug (default: 3)
|
|
18
|
+
# MAX_INFRA_RETRIES Max infrastructure/provider retries per bug (default: 3)
|
|
19
19
|
# AI_CLI AI CLI command name (auto-detected: cbc, claude, or codex)
|
|
20
20
|
# CODEBUDDY_CLI Legacy alias for AI_CLI (deprecated, use AI_CLI instead)
|
|
21
21
|
# PRIZMKIT_PLATFORM Platform hint for custom AI_CLI wrappers: codebuddy, claude, or codex
|
|
@@ -23,9 +23,6 @@ set -euo pipefail
|
|
|
23
23
|
# HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)
|
|
24
24
|
# STALE_KILL_THRESHOLD Auto-kill session after N seconds of no progress (default: 900)
|
|
25
25
|
# HEARTBEAT_STALE_THRESHOLD Heartbeat stale threshold in seconds (default: 600)
|
|
26
|
-
# LOG_CLEANUP_ENABLED Run periodic log cleanup (default: 1)
|
|
27
|
-
# LOG_RETENTION_DAYS Delete logs older than N days (default: 14)
|
|
28
|
-
# LOG_MAX_TOTAL_MB Keep total logs under N MB via oldest-first cleanup (default: 1024)
|
|
29
26
|
# DEV_BRANCH Custom dev branch name (default: auto-generated bugfix/pipeline-{run_id})
|
|
30
27
|
# AUTO_PUSH Auto-push to remote after successful bug fix (default: 0). Set to 1 to enable.
|
|
31
28
|
# STOP_ON_FAILURE Stop pipeline after a task exhausts all retries (default: 0). Set to 1 to stop.
|
|
@@ -42,13 +39,10 @@ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
|
42
39
|
|
|
43
40
|
# Configuration
|
|
44
41
|
MAX_RETRIES=${MAX_RETRIES:-3}
|
|
45
|
-
|
|
42
|
+
MAX_INFRA_RETRIES=${MAX_INFRA_RETRIES:-3}
|
|
46
43
|
HEARTBEAT_STALE_THRESHOLD=${HEARTBEAT_STALE_THRESHOLD:-600}
|
|
47
44
|
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
48
45
|
STALE_KILL_THRESHOLD=${STALE_KILL_THRESHOLD:-900}
|
|
49
|
-
LOG_CLEANUP_ENABLED=${LOG_CLEANUP_ENABLED:-1}
|
|
50
|
-
LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-14}
|
|
51
|
-
LOG_MAX_TOTAL_MB=${LOG_MAX_TOTAL_MB:-1024}
|
|
52
46
|
VERBOSE=${VERBOSE:-0}
|
|
53
47
|
MODEL=${MODEL:-""}
|
|
54
48
|
PRIZMKIT_EFFORT=${PRIZMKIT_EFFORT:-""}
|
|
@@ -90,8 +84,9 @@ spawn_and_wait_session() {
|
|
|
90
84
|
local bootstrap_prompt="$4"
|
|
91
85
|
local session_dir="$5"
|
|
92
86
|
local max_retries="$6"
|
|
93
|
-
local
|
|
94
|
-
local
|
|
87
|
+
local max_infra_retries="$7"
|
|
88
|
+
local item_model="${8:-}"
|
|
89
|
+
local base_branch="${9:-main}"
|
|
95
90
|
|
|
96
91
|
local session_log="$session_dir/logs/session.log"
|
|
97
92
|
local progress_json="$session_dir/logs/progress.json"
|
|
@@ -114,13 +109,6 @@ spawn_and_wait_session() {
|
|
|
114
109
|
start_progress_parser "$session_log" "$progress_json" "$SCRIPTS_DIR"
|
|
115
110
|
local parser_pid="${_PARSER_PID:-}"
|
|
116
111
|
|
|
117
|
-
# Timeout watchdog
|
|
118
|
-
local watcher_pid=""
|
|
119
|
-
if [[ $SESSION_TIMEOUT -gt 0 ]]; then
|
|
120
|
-
( sleep "$SESSION_TIMEOUT" && kill -TERM "$cli_pid" 2>/dev/null ) &
|
|
121
|
-
watcher_pid=$!
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
112
|
# Heartbeat monitor (with stale-kill protection)
|
|
125
113
|
start_heartbeat "$cli_pid" "$session_log" "$progress_json" "$HEARTBEAT_INTERVAL" "$STALE_KILL_THRESHOLD"
|
|
126
114
|
local heartbeat_pid="${_HEARTBEAT_PID:-}"
|
|
@@ -133,23 +121,34 @@ spawn_and_wait_session() {
|
|
|
133
121
|
exit_code=$?
|
|
134
122
|
fi
|
|
135
123
|
|
|
136
|
-
#
|
|
137
|
-
[[ -n "$watcher_pid" ]] && kill "$watcher_pid" 2>/dev/null || true
|
|
124
|
+
# Clean up heartbeat and parser
|
|
138
125
|
stop_heartbeat "$heartbeat_pid"
|
|
139
126
|
stop_progress_parser "$parser_pid"
|
|
140
|
-
[[ -n "$watcher_pid" ]] && wait "$watcher_pid" 2>/dev/null || true
|
|
141
127
|
|
|
142
128
|
# Recover session log from backup if truncated by subagent stdout redirection
|
|
143
129
|
prizm_recover_session_log "$session_log"
|
|
144
130
|
|
|
145
|
-
[[ $exit_code -eq 143 ]] && exit_code=124
|
|
146
131
|
|
|
147
132
|
# Check for stale-kill marker (heartbeat killed the process due to no progress)
|
|
148
133
|
local stale_kill_marker="$session_dir/logs/stale-kill.json"
|
|
149
134
|
local was_stale_killed=false
|
|
135
|
+
local stale_kill_reason=""
|
|
150
136
|
if [[ -f "$stale_kill_marker" ]]; then
|
|
151
137
|
was_stale_killed=true
|
|
152
|
-
|
|
138
|
+
stale_kill_reason=$(python3 - "$stale_kill_marker" <<'PY' 2>/dev/null || true
|
|
139
|
+
import json, sys
|
|
140
|
+
try:
|
|
141
|
+
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
142
|
+
print(json.load(fh).get("reason") or "")
|
|
143
|
+
except Exception:
|
|
144
|
+
pass
|
|
145
|
+
PY
|
|
146
|
+
)
|
|
147
|
+
if [[ "$stale_kill_reason" == "error_loop" ]]; then
|
|
148
|
+
log_warn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
149
|
+
else
|
|
150
|
+
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
151
|
+
fi
|
|
153
152
|
fi
|
|
154
153
|
|
|
155
154
|
local was_infra_error=false
|
|
@@ -175,15 +174,16 @@ spawn_and_wait_session() {
|
|
|
175
174
|
project_root="$PROJECT_ROOT"
|
|
176
175
|
local default_branch="$base_branch"
|
|
177
176
|
|
|
178
|
-
if [[ $
|
|
179
|
-
log_warn "Session timed out after ${SESSION_TIMEOUT}s"
|
|
180
|
-
session_status="timed_out"
|
|
181
|
-
elif [[ "$was_infra_error" == true ]]; then
|
|
177
|
+
if [[ "$was_infra_error" == true ]]; then
|
|
182
178
|
log_warn "Session failed due to AI CLI/provider infrastructure error"
|
|
183
179
|
log_warn "Infrastructure errors are retried without consuming code retry budget"
|
|
184
180
|
session_status="infra_error"
|
|
185
181
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
186
|
-
|
|
182
|
+
if [[ "$stale_kill_reason" == "error_loop" ]]; then
|
|
183
|
+
log_warn "Session killed due to repeated read-offset/wasted-call error loop"
|
|
184
|
+
else
|
|
185
|
+
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
186
|
+
fi
|
|
187
187
|
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
188
188
|
session_status="crashed"
|
|
189
189
|
elif [[ $exit_code -ne 0 ]]; then
|
|
@@ -200,10 +200,10 @@ spawn_and_wait_session() {
|
|
|
200
200
|
session_status="success"
|
|
201
201
|
else
|
|
202
202
|
local uncommitted=""
|
|
203
|
-
uncommitted=$(
|
|
203
|
+
uncommitted=$(prizm_git_status_safe "$project_root" | head -1 || true)
|
|
204
204
|
if [[ -n "$uncommitted" ]]; then
|
|
205
205
|
log_warn "Session exited cleanly but produced no commits (uncommitted changes found) — auto-committing..."
|
|
206
|
-
|
|
206
|
+
prizm_git_add_all_safe "$project_root" || true
|
|
207
207
|
if git -C "$project_root" commit --no-verify -m "chore($bug_id): auto-commit session work" 2>/dev/null; then
|
|
208
208
|
log_info "Auto-commit succeeded"
|
|
209
209
|
session_status="success"
|
|
@@ -222,10 +222,10 @@ spawn_and_wait_session() {
|
|
|
222
222
|
if [[ "$session_status" == "success" ]]; then
|
|
223
223
|
if git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
224
224
|
local dirty_files=""
|
|
225
|
-
dirty_files=$(
|
|
225
|
+
dirty_files=$(prizm_git_status_safe "$project_root" || true)
|
|
226
226
|
if [[ -n "$dirty_files" ]]; then
|
|
227
227
|
log_info "Auto-committing remaining session artifacts..."
|
|
228
|
-
|
|
228
|
+
prizm_git_add_all_safe "$project_root" || true
|
|
229
229
|
git -C "$project_root" commit --no-verify --amend --no-edit 2>/dev/null \
|
|
230
230
|
|| git -C "$project_root" commit --no-verify -m "chore($bug_id): include remaining session artifacts" 2>/dev/null \
|
|
231
231
|
|| true
|
|
@@ -278,7 +278,20 @@ sys.exit(0)
|
|
|
278
278
|
# Subagent detection
|
|
279
279
|
prizm_detect_subagents "$session_log"
|
|
280
280
|
|
|
281
|
-
#
|
|
281
|
+
# Final status updates happen after the caller returns to the original branch.
|
|
282
|
+
_SPAWN_RESULT="$session_status"
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
finalize_bug_status_after_branch_return() {
|
|
286
|
+
local bug_id="$1"
|
|
287
|
+
local bug_list="$2"
|
|
288
|
+
local session_id="$3"
|
|
289
|
+
local session_status="$4"
|
|
290
|
+
local max_retries="$5"
|
|
291
|
+
local max_infra_retries="$6"
|
|
292
|
+
|
|
293
|
+
# Update bug status on the original branch. The caller commits the resulting
|
|
294
|
+
# bug-list diff immediately after this helper returns.
|
|
282
295
|
local update_output
|
|
283
296
|
update_output=$(python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
284
297
|
--bug-list "$bug_list" \
|
|
@@ -287,14 +300,13 @@ sys.exit(0)
|
|
|
287
300
|
--session-status "$session_status" \
|
|
288
301
|
--session-id "$session_id" \
|
|
289
302
|
--max-retries "$max_retries" \
|
|
303
|
+
--max-infra-retries "$max_infra_retries" \
|
|
290
304
|
--action update 2>&1) || {
|
|
291
305
|
log_error "Failed to update bug status: $update_output"
|
|
292
306
|
update_output=""
|
|
293
307
|
}
|
|
294
308
|
|
|
295
309
|
_SPAWN_ITEM_STATUS="$(printf '%s' "$update_output" | prizm_extract_update_new_status)"
|
|
296
|
-
|
|
297
|
-
_SPAWN_RESULT="$session_status"
|
|
298
310
|
}
|
|
299
311
|
|
|
300
312
|
# ============================================================
|
|
@@ -334,6 +346,8 @@ for bug in data.get('bugs', []):
|
|
|
334
346
|
--state-dir "$STATE_DIR" \
|
|
335
347
|
--bug-id "$_interrupted_id" \
|
|
336
348
|
--session-status "failed" \
|
|
349
|
+
--max-retries "$MAX_RETRIES" \
|
|
350
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
337
351
|
--action update 2>/dev/null || true
|
|
338
352
|
log_info "Bug $_interrupted_id marked as failed due to interrupt"
|
|
339
353
|
fi
|
|
@@ -364,28 +378,6 @@ check_dependencies() {
|
|
|
364
378
|
prizm_ensure_git_repo "$_project_root"
|
|
365
379
|
}
|
|
366
380
|
|
|
367
|
-
run_log_cleanup() {
|
|
368
|
-
if [[ "$LOG_CLEANUP_ENABLED" != "1" ]]; then
|
|
369
|
-
return 0
|
|
370
|
-
fi
|
|
371
|
-
|
|
372
|
-
local cleanup_result
|
|
373
|
-
cleanup_result=$(python3 "$SCRIPTS_DIR/cleanup-logs.py" \
|
|
374
|
-
--state-dir "$STATE_DIR" \
|
|
375
|
-
--retention-days "$LOG_RETENTION_DAYS" \
|
|
376
|
-
--max-total-mb "$LOG_MAX_TOTAL_MB" 2>/dev/null) || {
|
|
377
|
-
log_warn "Log cleanup failed (continuing)"
|
|
378
|
-
return 0
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
local deleted reclaimed_kb
|
|
382
|
-
deleted=$(echo "$cleanup_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('deleted_files', 0))" 2>/dev/null || echo "0")
|
|
383
|
-
reclaimed_kb=$(echo "$cleanup_result" | python3 -c "import sys,json; print(int(json.load(sys.stdin).get('reclaimed_bytes', 0)/1024))" 2>/dev/null || echo "0")
|
|
384
|
-
|
|
385
|
-
if [[ "$deleted" -gt 0 ]]; then
|
|
386
|
-
log_info "Log cleanup: deleted $deleted files, reclaimed ${reclaimed_kb}KB"
|
|
387
|
-
fi
|
|
388
|
-
}
|
|
389
381
|
|
|
390
382
|
# ============================================================
|
|
391
383
|
# run-one: Run a single bug fix
|
|
@@ -405,7 +397,16 @@ run_one() {
|
|
|
405
397
|
--dry-run) dry_run=true; shift ;;
|
|
406
398
|
--clean) do_clean=true; shift ;;
|
|
407
399
|
--no-reset) no_reset=true; shift ;;
|
|
408
|
-
--
|
|
400
|
+
--max-infra-retries)
|
|
401
|
+
shift
|
|
402
|
+
if [[ $# -eq 0 ]]; then
|
|
403
|
+
log_error "--max-infra-retries requires a value"
|
|
404
|
+
exit 1
|
|
405
|
+
fi
|
|
406
|
+
prizm_require_positive_int "--max-infra-retries" "$1"
|
|
407
|
+
MAX_INFRA_RETRIES="$1"
|
|
408
|
+
shift
|
|
409
|
+
;;
|
|
409
410
|
--mode)
|
|
410
411
|
shift
|
|
411
412
|
if [[ $# -eq 0 ]]; then
|
|
@@ -446,7 +447,6 @@ run_one() {
|
|
|
446
447
|
fi
|
|
447
448
|
|
|
448
449
|
check_dependencies
|
|
449
|
-
run_log_cleanup
|
|
450
450
|
|
|
451
451
|
# Initialize state if needed
|
|
452
452
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -695,6 +695,8 @@ else:
|
|
|
695
695
|
--state-dir "$STATE_DIR" \
|
|
696
696
|
--bug-id "$bug_id" \
|
|
697
697
|
--session-status "failed" \
|
|
698
|
+
--max-retries "$MAX_RETRIES" \
|
|
699
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
698
700
|
--action update 2>/dev/null || true
|
|
699
701
|
log_info "Bug $bug_id marked as failed due to interrupt"
|
|
700
702
|
fi
|
|
@@ -728,12 +730,13 @@ else:
|
|
|
728
730
|
if branch_create "$_proj_root" "$_branch_name" "$_source_branch"; then
|
|
729
731
|
_DEV_BRANCH_NAME="$_branch_name"
|
|
730
732
|
else
|
|
731
|
-
|
|
733
|
+
log_error "Failed to create branch: $_branch_name from $_source_branch — aborting to avoid running on $_source_branch"
|
|
734
|
+
return 1
|
|
732
735
|
fi
|
|
733
736
|
|
|
734
737
|
spawn_and_wait_session \
|
|
735
738
|
"$bug_id" "$bug_list" "$session_id" \
|
|
736
|
-
"$bootstrap_prompt" "$session_dir"
|
|
739
|
+
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$bug_model" "$_ORIGINAL_BRANCH"
|
|
737
740
|
local session_status="$_SPAWN_RESULT"
|
|
738
741
|
|
|
739
742
|
# Merge dev branch back to original on success
|
|
@@ -743,7 +746,7 @@ else:
|
|
|
743
746
|
else
|
|
744
747
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
745
748
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
746
|
-
|
|
749
|
+
session_status="merge_conflict"
|
|
747
750
|
fi
|
|
748
751
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
749
752
|
# Session failed — preserve dev branch for inspection
|
|
@@ -754,10 +757,12 @@ else:
|
|
|
754
757
|
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
755
758
|
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
756
759
|
|
|
760
|
+
finalize_bug_status_after_branch_return \
|
|
761
|
+
"$bug_id" "$bug_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES"
|
|
762
|
+
|
|
757
763
|
# Commit bug status update on the original branch (after guaranteed return)
|
|
758
764
|
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
759
|
-
|
|
760
|
-
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): update bug status" 2>/dev/null || true
|
|
765
|
+
prizm_git_commit_paths "$_proj_root" "chore($bug_id): update bug status" "$bug_list"
|
|
761
766
|
fi
|
|
762
767
|
|
|
763
768
|
echo ""
|
|
@@ -778,7 +783,41 @@ else:
|
|
|
778
783
|
# ============================================================
|
|
779
784
|
|
|
780
785
|
main() {
|
|
781
|
-
local bug_list="$
|
|
786
|
+
local bug_list="$PRIZMKIT_DIR/plans/bug-fix-list.json"
|
|
787
|
+
|
|
788
|
+
while [[ $# -gt 0 ]]; do
|
|
789
|
+
case "$1" in
|
|
790
|
+
--max-infra-retries)
|
|
791
|
+
shift
|
|
792
|
+
if [[ $# -eq 0 ]]; then
|
|
793
|
+
log_error "--max-infra-retries requires a value"
|
|
794
|
+
exit 1
|
|
795
|
+
fi
|
|
796
|
+
prizm_require_positive_int "--max-infra-retries" "$1"
|
|
797
|
+
MAX_INFRA_RETRIES="$1"
|
|
798
|
+
shift
|
|
799
|
+
;;
|
|
800
|
+
--)
|
|
801
|
+
shift
|
|
802
|
+
break
|
|
803
|
+
;;
|
|
804
|
+
-*)
|
|
805
|
+
log_error "Unknown option: $1"
|
|
806
|
+
show_help
|
|
807
|
+
exit 1
|
|
808
|
+
;;
|
|
809
|
+
*)
|
|
810
|
+
bug_list="$1"
|
|
811
|
+
shift
|
|
812
|
+
;;
|
|
813
|
+
esac
|
|
814
|
+
done
|
|
815
|
+
|
|
816
|
+
if [[ $# -gt 0 ]]; then
|
|
817
|
+
log_error "Unexpected arguments: $*"
|
|
818
|
+
show_help
|
|
819
|
+
exit 1
|
|
820
|
+
fi
|
|
782
821
|
|
|
783
822
|
if [[ ! "$bug_list" = /* ]]; then
|
|
784
823
|
bug_list="$(pwd)/$bug_list"
|
|
@@ -793,7 +832,6 @@ main() {
|
|
|
793
832
|
fi
|
|
794
833
|
|
|
795
834
|
check_dependencies
|
|
796
|
-
run_log_cleanup
|
|
797
835
|
|
|
798
836
|
# Initialize pipeline state if needed
|
|
799
837
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -840,12 +878,8 @@ main() {
|
|
|
840
878
|
echo -e "${BOLD} Bug-Fix Pipeline Runner Started${NC}"
|
|
841
879
|
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
842
880
|
log_info "Bug fix list: $bug_list"
|
|
843
|
-
log_info "Max retries per bug: $MAX_RETRIES"
|
|
844
|
-
|
|
845
|
-
log_info "Session timeout: ${SESSION_TIMEOUT}s"
|
|
846
|
-
else
|
|
847
|
-
log_info "Session timeout: none"
|
|
848
|
-
fi
|
|
881
|
+
log_info "Max code retries per bug: $MAX_RETRIES"
|
|
882
|
+
log_info "Max infrastructure retries per bug: $MAX_INFRA_RETRIES"
|
|
849
883
|
log_info "AI CLI: $CLI_CMD (platform: $PLATFORM)"
|
|
850
884
|
if [[ -n "${MODEL:-}" ]]; then
|
|
851
885
|
log_info "Default Model: $MODEL"
|
|
@@ -885,6 +919,7 @@ main() {
|
|
|
885
919
|
--bug-list "$bug_list" \
|
|
886
920
|
--state-dir "$STATE_DIR" \
|
|
887
921
|
--max-retries "$MAX_RETRIES" \
|
|
922
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
888
923
|
--action get_next 2>/dev/null); then
|
|
889
924
|
log_error "Failed to get next bug"
|
|
890
925
|
break
|
|
@@ -972,27 +1007,40 @@ DEPLOY_PROMPT_EOF
|
|
|
972
1007
|
fi
|
|
973
1008
|
|
|
974
1009
|
# Parse bug info
|
|
975
|
-
local bug_id bug_title bug_severity retry_count resume_phase
|
|
1010
|
+
local bug_id bug_title bug_severity retry_count infra_error_count resume_phase
|
|
976
1011
|
bug_id=$(echo "$next_bug" | jq -r '.bug_id')
|
|
977
1012
|
bug_title=$(echo "$next_bug" | jq -r '.title')
|
|
978
1013
|
bug_severity=$(echo "$next_bug" | jq -r '.severity')
|
|
979
1014
|
retry_count=$(echo "$next_bug" | jq -r '.retry_count // 0')
|
|
1015
|
+
infra_error_count=$(echo "$next_bug" | jq -r '.infra_error_count // 0')
|
|
980
1016
|
resume_phase=$(echo "$next_bug" | jq -r '.resume_from_phase // "null"')
|
|
981
1017
|
|
|
982
1018
|
echo ""
|
|
983
1019
|
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
984
1020
|
log_info "Bug: ${BOLD}$bug_id${NC} — $bug_title"
|
|
985
|
-
log_info "Severity: $bug_severity
|
|
1021
|
+
log_info "Severity: $bug_severity"
|
|
1022
|
+
log_info "Code retry: $retry_count / $MAX_RETRIES"
|
|
1023
|
+
log_info "Infrastructure retry: $infra_error_count / $MAX_INFRA_RETRIES"
|
|
986
1024
|
if [[ "$resume_phase" != "null" ]]; then
|
|
987
1025
|
log_info "Resuming from Phase $resume_phase"
|
|
988
1026
|
fi
|
|
989
1027
|
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
990
1028
|
|
|
991
|
-
#
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1029
|
+
# Auto-commit any uncommitted workspace changes before starting the pipeline.
|
|
1030
|
+
# This ensures a clean baseline without blocking execution.
|
|
1031
|
+
local _dirty_files=""
|
|
1032
|
+
_dirty_files=$(prizm_git_status_non_bookkeeping "$_proj_root" "$STATE_DIR" "$bug_list" || true)
|
|
1033
|
+
if [[ -n "$_dirty_files" ]]; then
|
|
1034
|
+
log_info "Auto-committing workspace changes before $bug_id..."
|
|
1035
|
+
prizm_git_add_all_safe "$_proj_root"
|
|
1036
|
+
git -C "$_proj_root" commit --no-verify -m "chore: workspace snapshot ready for run $bug_id" 2>/dev/null || true
|
|
1037
|
+
fi
|
|
1038
|
+
|
|
1039
|
+
local _bookkeeping_dirty=""
|
|
1040
|
+
_bookkeeping_dirty=$(prizm_git_status_bookkeeping "$_proj_root" "$STATE_DIR" "$bug_list" || true)
|
|
1041
|
+
if [[ -n "$_bookkeeping_dirty" ]]; then
|
|
1042
|
+
log_info "Committing pending pipeline bookkeeping before $bug_id..."
|
|
1043
|
+
prizm_git_commit_paths "$_proj_root" "chore($bug_id): include pipeline bookkeeping" "$bug_list" "$STATE_DIR"
|
|
996
1044
|
fi
|
|
997
1045
|
|
|
998
1046
|
# Mark bug as in-progress BEFORE creating dev branch
|
|
@@ -1005,8 +1053,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1005
1053
|
--action start >/dev/null 2>&1 || true
|
|
1006
1054
|
# Commit the in_progress status on the original branch
|
|
1007
1055
|
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
1008
|
-
|
|
1009
|
-
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): mark in_progress" 2>/dev/null || true
|
|
1056
|
+
prizm_git_commit_paths "$_proj_root" "chore($bug_id): mark in_progress" "$bug_list"
|
|
1010
1057
|
fi
|
|
1011
1058
|
|
|
1012
1059
|
# Create per-bug dev branch (from the now-updated original branch)
|
|
@@ -1015,8 +1062,9 @@ DEPLOY_PROMPT_EOF
|
|
|
1015
1062
|
_DEV_BRANCH_NAME="$_bug_branch"
|
|
1016
1063
|
log_info "Dev branch: $_bug_branch"
|
|
1017
1064
|
else
|
|
1018
|
-
|
|
1065
|
+
log_error "Failed to create dev branch: $_bug_branch from $_ORIGINAL_BRANCH — aborting to avoid running on $_ORIGINAL_BRANCH"
|
|
1019
1066
|
_DEV_BRANCH_NAME=""
|
|
1067
|
+
break
|
|
1020
1068
|
fi
|
|
1021
1069
|
|
|
1022
1070
|
# Generate session
|
|
@@ -1086,10 +1134,9 @@ DEPLOY_PROMPT_EOF
|
|
|
1086
1134
|
|
|
1087
1135
|
spawn_and_wait_session \
|
|
1088
1136
|
"$bug_id" "$bug_list" "$session_id" \
|
|
1089
|
-
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$bug_model" "$_ORIGINAL_BRANCH"
|
|
1137
|
+
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$bug_model" "$_ORIGINAL_BRANCH"
|
|
1090
1138
|
|
|
1091
1139
|
local session_status="$_SPAWN_RESULT"
|
|
1092
|
-
local item_status_after_session="${_SPAWN_ITEM_STATUS:-}"
|
|
1093
1140
|
|
|
1094
1141
|
# Merge per-bug dev branch back to original on success
|
|
1095
1142
|
if [[ "$session_status" == "success" && -n "$_DEV_BRANCH_NAME" ]]; then
|
|
@@ -1098,7 +1145,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1098
1145
|
else
|
|
1099
1146
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
1100
1147
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
1101
|
-
|
|
1148
|
+
session_status="merge_conflict"
|
|
1102
1149
|
fi
|
|
1103
1150
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
1104
1151
|
# Session failed — preserve dev branch for inspection
|
|
@@ -1109,10 +1156,13 @@ DEPLOY_PROMPT_EOF
|
|
|
1109
1156
|
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
1110
1157
|
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1111
1158
|
|
|
1159
|
+
finalize_bug_status_after_branch_return \
|
|
1160
|
+
"$bug_id" "$bug_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES"
|
|
1161
|
+
local item_status_after_session="${_SPAWN_ITEM_STATUS:-}"
|
|
1162
|
+
|
|
1112
1163
|
# Commit bug status update on the original branch (after guaranteed return)
|
|
1113
1164
|
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
1114
|
-
|
|
1115
|
-
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): update bug status" 2>/dev/null || true
|
|
1165
|
+
prizm_git_commit_paths "$_proj_root" "chore($bug_id): update bug status" "$bug_list"
|
|
1116
1166
|
fi
|
|
1117
1167
|
|
|
1118
1168
|
session_count=$((session_count + 1))
|
|
@@ -1129,7 +1179,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1129
1179
|
log_error "════════════════════════════════════════════════════"
|
|
1130
1180
|
break
|
|
1131
1181
|
elif [[ "$session_status" != "success" && "$STOP_ON_FAILURE" == "1" ]]; then
|
|
1132
|
-
log_info "STOP_ON_FAILURE: $bug_id is ${item_status_after_session:-unknown}; retry budget not exhausted, continuing."
|
|
1182
|
+
log_info "STOP_ON_FAILURE: $bug_id is ${item_status_after_session:-unknown}; code/infra retry budget not exhausted, continuing."
|
|
1133
1183
|
fi
|
|
1134
1184
|
|
|
1135
1185
|
# Stuck detection
|
|
@@ -1166,7 +1216,7 @@ show_help() {
|
|
|
1166
1216
|
echo "Usage: $0 <command> [options]"
|
|
1167
1217
|
echo ""
|
|
1168
1218
|
echo "Commands:"
|
|
1169
|
-
echo " run [.prizmkit/plans/bug-fix-list.json]
|
|
1219
|
+
echo " run [.prizmkit/plans/bug-fix-list.json] [options] Run all bugs by severity/priority order"
|
|
1170
1220
|
echo " run <bug-id> [options] Run a single bug fix"
|
|
1171
1221
|
echo " status [.prizmkit/plans/bug-fix-list.json] Show bug fix pipeline status"
|
|
1172
1222
|
echo " reset Clear all bugfix state"
|
|
@@ -1176,14 +1226,17 @@ show_help() {
|
|
|
1176
1226
|
echo " --dry-run Generate bootstrap prompt only, don't spawn session"
|
|
1177
1227
|
echo " --clean Delete artifacts and reset before running"
|
|
1178
1228
|
echo " --no-reset Skip status reset (preserve retry count)"
|
|
1179
|
-
echo " --
|
|
1229
|
+
echo " --max-infra-retries N Max infrastructure/provider retries (default: 3)"
|
|
1180
1230
|
echo " --mode <lite|standard|full> Override pipeline mode"
|
|
1181
1231
|
echo " --critic Enable adversarial critic review"
|
|
1182
1232
|
echo " --no-critic Disable adversarial critic review"
|
|
1183
1233
|
echo ""
|
|
1234
|
+
echo "Pipeline Options (run all):"
|
|
1235
|
+
echo " --max-infra-retries N Max infrastructure/provider retries (default: 3)"
|
|
1236
|
+
echo ""
|
|
1184
1237
|
echo "Environment Variables:"
|
|
1185
|
-
echo " MAX_RETRIES Max retries per bug (default: 3)"
|
|
1186
|
-
echo "
|
|
1238
|
+
echo " MAX_RETRIES Max code retries per bug (default: 3)"
|
|
1239
|
+
echo " MAX_INFRA_RETRIES Max infrastructure/provider retries per bug (default: 3)"
|
|
1187
1240
|
echo " MODEL Default AI model (overridden by per-bug model in bug list)"
|
|
1188
1241
|
echo " PRIZMKIT_EFFORT AI reasoning effort (low|medium|high|xhigh|max; max is Claude Code only)"
|
|
1189
1242
|
echo " PIPELINE_MODE Default pipeline mode: lite|standard|full (overridden by --mode)"
|
|
@@ -1193,9 +1246,6 @@ show_help() {
|
|
|
1193
1246
|
echo " HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)"
|
|
1194
1247
|
echo " STALE_KILL_THRESHOLD Auto-kill session after N seconds of no progress (default: 900)"
|
|
1195
1248
|
echo " STOP_ON_FAILURE Stop pipeline when a task exhausts retries: 0|1 (default: 0)"
|
|
1196
|
-
echo " LOG_CLEANUP_ENABLED Run log cleanup before execution (default: 1)"
|
|
1197
|
-
echo " LOG_RETENTION_DAYS Delete logs older than N days (default: 14)"
|
|
1198
|
-
echo " LOG_MAX_TOTAL_MB Keep total logs under N MB (default: 1024)"
|
|
1199
1249
|
echo ""
|
|
1200
1250
|
echo "Examples:"
|
|
1201
1251
|
echo " ./run-bugfix.sh run # Run all bugs"
|
|
@@ -1203,7 +1253,6 @@ show_help() {
|
|
|
1203
1253
|
echo " ./run-bugfix.sh run B-001 --dry-run # Inspect generated prompt"
|
|
1204
1254
|
echo " ./run-bugfix.sh run B-001 --clean # Clean artifacts + reset + run"
|
|
1205
1255
|
echo " ./run-bugfix.sh run B-001 --no-reset # Retry without resetting status"
|
|
1206
|
-
echo " ./run-bugfix.sh run B-001 --timeout 3600 # 1h timeout"
|
|
1207
1256
|
echo " ./run-bugfix.sh status # Show status"
|
|
1208
1257
|
echo " MAX_RETRIES=5 ./run-bugfix.sh run # Custom retries"
|
|
1209
1258
|
}
|
|
@@ -1214,7 +1263,7 @@ case "${1:-run}" in
|
|
|
1214
1263
|
if [[ "${1:-}" =~ ^[Bb]-[0-9]+ ]]; then
|
|
1215
1264
|
run_one "$@"
|
|
1216
1265
|
else
|
|
1217
|
-
main "
|
|
1266
|
+
main "$@"
|
|
1218
1267
|
fi
|
|
1219
1268
|
;;
|
|
1220
1269
|
status)
|
|
@@ -1226,6 +1275,8 @@ case "${1:-run}" in
|
|
|
1226
1275
|
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
1227
1276
|
--bug-list "${2:-$PRIZMKIT_DIR/plans/bug-fix-list.json}" \
|
|
1228
1277
|
--state-dir "$STATE_DIR" \
|
|
1278
|
+
--max-retries "$MAX_RETRIES" \
|
|
1279
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
1229
1280
|
--action status
|
|
1230
1281
|
;;
|
|
1231
1282
|
reset)
|