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
|
# execute refactors from a .prizmkit/plans/refactor-list.json specification.
|
|
9
9
|
#
|
|
10
10
|
# Usage:
|
|
11
|
-
# ./run-refactor.sh run [.prizmkit/plans/refactor-list.json]
|
|
11
|
+
# ./run-refactor.sh run [.prizmkit/plans/refactor-list.json] [options] Run all refactors
|
|
12
12
|
# ./run-refactor.sh run <refactor-id> [options] Run a single refactor
|
|
13
13
|
# ./run-refactor.sh status [.prizmkit/plans/refactor-list.json] Show pipeline status
|
|
14
14
|
# ./run-refactor.sh reset Clear all state
|
|
15
15
|
#
|
|
16
16
|
# Environment Variables:
|
|
17
|
-
# MAX_RETRIES Max retries per refactor (default: 3)
|
|
18
|
-
#
|
|
17
|
+
# MAX_RETRIES Max code retries per refactor (default: 3)
|
|
18
|
+
# MAX_INFRA_RETRIES Max infrastructure/provider retries per refactor (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 refactor/pipeline-{run_id})
|
|
30
27
|
# AUTO_PUSH Auto-push to remote after successful refactor (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.
|
|
@@ -43,13 +40,10 @@ SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
|
43
40
|
|
|
44
41
|
# Configuration
|
|
45
42
|
MAX_RETRIES=${MAX_RETRIES:-3}
|
|
46
|
-
|
|
43
|
+
MAX_INFRA_RETRIES=${MAX_INFRA_RETRIES:-3}
|
|
47
44
|
HEARTBEAT_STALE_THRESHOLD=${HEARTBEAT_STALE_THRESHOLD:-600}
|
|
48
45
|
HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-30}
|
|
49
46
|
STALE_KILL_THRESHOLD=${STALE_KILL_THRESHOLD:-900}
|
|
50
|
-
LOG_CLEANUP_ENABLED=${LOG_CLEANUP_ENABLED:-1}
|
|
51
|
-
LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-14}
|
|
52
|
-
LOG_MAX_TOTAL_MB=${LOG_MAX_TOTAL_MB:-1024}
|
|
53
47
|
VERBOSE=${VERBOSE:-0}
|
|
54
48
|
MODEL=${MODEL:-""}
|
|
55
49
|
PRIZMKIT_EFFORT=${PRIZMKIT_EFFORT:-""}
|
|
@@ -92,8 +86,9 @@ spawn_and_wait_session() {
|
|
|
92
86
|
local bootstrap_prompt="$4"
|
|
93
87
|
local session_dir="$5"
|
|
94
88
|
local max_retries="$6"
|
|
95
|
-
local
|
|
96
|
-
local
|
|
89
|
+
local max_infra_retries="$7"
|
|
90
|
+
local item_model="${8:-}"
|
|
91
|
+
local base_branch="${9:-main}"
|
|
97
92
|
|
|
98
93
|
local session_log="$session_dir/logs/session.log"
|
|
99
94
|
local progress_json="$session_dir/logs/progress.json"
|
|
@@ -116,13 +111,6 @@ spawn_and_wait_session() {
|
|
|
116
111
|
start_progress_parser "$session_log" "$progress_json" "$SCRIPTS_DIR"
|
|
117
112
|
local parser_pid="${_PARSER_PID:-}"
|
|
118
113
|
|
|
119
|
-
# Timeout watchdog
|
|
120
|
-
local watcher_pid=""
|
|
121
|
-
if [[ $SESSION_TIMEOUT -gt 0 ]]; then
|
|
122
|
-
( sleep "$SESSION_TIMEOUT" && kill -TERM "$cli_pid" 2>/dev/null ) &
|
|
123
|
-
watcher_pid=$!
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
114
|
# Heartbeat monitor (with stale-kill protection)
|
|
127
115
|
start_heartbeat "$cli_pid" "$session_log" "$progress_json" "$HEARTBEAT_INTERVAL" "$STALE_KILL_THRESHOLD"
|
|
128
116
|
local heartbeat_pid="${_HEARTBEAT_PID:-}"
|
|
@@ -136,22 +124,32 @@ spawn_and_wait_session() {
|
|
|
136
124
|
fi
|
|
137
125
|
|
|
138
126
|
# Cleanup
|
|
139
|
-
[[ -n "$watcher_pid" ]] && kill "$watcher_pid" 2>/dev/null || true
|
|
140
127
|
stop_heartbeat "$heartbeat_pid"
|
|
141
128
|
stop_progress_parser "$parser_pid"
|
|
142
|
-
[[ -n "$watcher_pid" ]] && wait "$watcher_pid" 2>/dev/null || true
|
|
143
129
|
|
|
144
130
|
# Recover session log from backup if truncated by subagent stdout redirection
|
|
145
131
|
prizm_recover_session_log "$session_log"
|
|
146
132
|
|
|
147
|
-
[[ $exit_code -eq 143 ]] && exit_code=124
|
|
148
|
-
|
|
149
133
|
# Check for stale-kill marker (heartbeat killed the process due to no progress)
|
|
150
134
|
local stale_kill_marker="$session_dir/logs/stale-kill.json"
|
|
151
135
|
local was_stale_killed=false
|
|
136
|
+
local stale_kill_reason=""
|
|
152
137
|
if [[ -f "$stale_kill_marker" ]]; then
|
|
153
138
|
was_stale_killed=true
|
|
154
|
-
|
|
139
|
+
stale_kill_reason=$(python3 - "$stale_kill_marker" <<'PY' 2>/dev/null || true
|
|
140
|
+
import json, sys
|
|
141
|
+
try:
|
|
142
|
+
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
143
|
+
print(json.load(fh).get("reason") or "")
|
|
144
|
+
except Exception:
|
|
145
|
+
pass
|
|
146
|
+
PY
|
|
147
|
+
)
|
|
148
|
+
if [[ "$stale_kill_reason" == "error_loop" ]]; then
|
|
149
|
+
log_warn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
150
|
+
else
|
|
151
|
+
log_warn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
152
|
+
fi
|
|
155
153
|
fi
|
|
156
154
|
|
|
157
155
|
local was_infra_error=false
|
|
@@ -177,15 +175,16 @@ spawn_and_wait_session() {
|
|
|
177
175
|
project_root="$PROJECT_ROOT"
|
|
178
176
|
local default_branch="$base_branch"
|
|
179
177
|
|
|
180
|
-
if [[ $
|
|
181
|
-
log_warn "Session timed out after ${SESSION_TIMEOUT}s"
|
|
182
|
-
session_status="timed_out"
|
|
183
|
-
elif [[ "$was_infra_error" == true ]]; then
|
|
178
|
+
if [[ "$was_infra_error" == true ]]; then
|
|
184
179
|
log_warn "Session failed due to AI CLI/provider infrastructure error"
|
|
185
180
|
log_warn "Infrastructure errors are retried without consuming code retry budget"
|
|
186
181
|
session_status="infra_error"
|
|
187
182
|
elif [[ "$was_stale_killed" == true ]]; then
|
|
188
|
-
|
|
183
|
+
if [[ "$stale_kill_reason" == "error_loop" ]]; then
|
|
184
|
+
log_warn "Session killed due to repeated read-offset/wasted-call error loop"
|
|
185
|
+
else
|
|
186
|
+
log_warn "Session stale-killed (no progress for ${STALE_KILL_THRESHOLD}s)"
|
|
187
|
+
fi
|
|
189
188
|
log_warn "Stale-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
190
189
|
session_status="crashed"
|
|
191
190
|
elif [[ $exit_code -ne 0 ]]; then
|
|
@@ -202,10 +201,10 @@ spawn_and_wait_session() {
|
|
|
202
201
|
session_status="success"
|
|
203
202
|
else
|
|
204
203
|
local uncommitted=""
|
|
205
|
-
uncommitted=$(
|
|
204
|
+
uncommitted=$(prizm_git_status_safe "$project_root" | head -1 || true)
|
|
206
205
|
if [[ -n "$uncommitted" ]]; then
|
|
207
206
|
log_warn "Session exited cleanly but produced no commits (uncommitted changes found) — auto-committing..."
|
|
208
|
-
|
|
207
|
+
prizm_git_add_all_safe "$project_root" || true
|
|
209
208
|
if git -C "$project_root" commit --no-verify -m "chore($refactor_id): auto-commit session work" 2>/dev/null; then
|
|
210
209
|
log_info "Auto-commit succeeded"
|
|
211
210
|
session_status="success"
|
|
@@ -224,10 +223,10 @@ spawn_and_wait_session() {
|
|
|
224
223
|
if [[ "$session_status" == "success" ]]; then
|
|
225
224
|
if git -C "$project_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
226
225
|
local dirty_files=""
|
|
227
|
-
dirty_files=$(
|
|
226
|
+
dirty_files=$(prizm_git_status_safe "$project_root" || true)
|
|
228
227
|
if [[ -n "$dirty_files" ]]; then
|
|
229
228
|
log_info "Auto-committing remaining session artifacts..."
|
|
230
|
-
|
|
229
|
+
prizm_git_add_all_safe "$project_root" || true
|
|
231
230
|
git -C "$project_root" commit --no-verify --amend --no-edit 2>/dev/null \
|
|
232
231
|
|| git -C "$project_root" commit --no-verify -m "chore($refactor_id): include remaining session artifacts" 2>/dev/null \
|
|
233
232
|
|| true
|
|
@@ -305,7 +304,20 @@ sys.exit(0)
|
|
|
305
304
|
fi
|
|
306
305
|
fi
|
|
307
306
|
|
|
308
|
-
#
|
|
307
|
+
# Final status updates happen after the caller returns to the original branch.
|
|
308
|
+
_SPAWN_RESULT="$session_status"
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
finalize_refactor_status_after_branch_return() {
|
|
312
|
+
local refactor_id="$1"
|
|
313
|
+
local refactor_list="$2"
|
|
314
|
+
local session_id="$3"
|
|
315
|
+
local session_status="$4"
|
|
316
|
+
local max_retries="$5"
|
|
317
|
+
local max_infra_retries="$6"
|
|
318
|
+
|
|
319
|
+
# Update refactor status on the original branch. The caller commits the
|
|
320
|
+
# resulting refactor-list diff immediately after this helper returns.
|
|
309
321
|
local update_output
|
|
310
322
|
update_output=$(python3 "$SCRIPTS_DIR/update-refactor-status.py" \
|
|
311
323
|
--refactor-list "$refactor_list" \
|
|
@@ -314,14 +326,13 @@ sys.exit(0)
|
|
|
314
326
|
--session-status "$session_status" \
|
|
315
327
|
--session-id "$session_id" \
|
|
316
328
|
--max-retries "$max_retries" \
|
|
329
|
+
--max-infra-retries "$max_infra_retries" \
|
|
317
330
|
--action update 2>&1) || {
|
|
318
331
|
log_error "Failed to update refactor status: $update_output"
|
|
319
332
|
update_output=""
|
|
320
333
|
}
|
|
321
334
|
|
|
322
335
|
_SPAWN_ITEM_STATUS="$(printf '%s' "$update_output" | prizm_extract_update_new_status)"
|
|
323
|
-
|
|
324
|
-
_SPAWN_RESULT="$session_status"
|
|
325
336
|
}
|
|
326
337
|
|
|
327
338
|
# ============================================================
|
|
@@ -361,6 +372,8 @@ for item in data.get('refactors', []):
|
|
|
361
372
|
--state-dir "$STATE_DIR" \
|
|
362
373
|
--refactor-id "$_interrupted_id" \
|
|
363
374
|
--session-status "failed" \
|
|
375
|
+
--max-retries "$MAX_RETRIES" \
|
|
376
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
364
377
|
--action update 2>/dev/null || true
|
|
365
378
|
log_info "Refactor $_interrupted_id marked as failed due to interrupt"
|
|
366
379
|
fi
|
|
@@ -391,29 +404,6 @@ check_dependencies() {
|
|
|
391
404
|
prizm_ensure_git_repo "$_project_root"
|
|
392
405
|
}
|
|
393
406
|
|
|
394
|
-
run_log_cleanup() {
|
|
395
|
-
if [[ "$LOG_CLEANUP_ENABLED" != "1" ]]; then
|
|
396
|
-
return 0
|
|
397
|
-
fi
|
|
398
|
-
|
|
399
|
-
local cleanup_result
|
|
400
|
-
cleanup_result=$(python3 "$SCRIPTS_DIR/cleanup-logs.py" \
|
|
401
|
-
--state-dir "$STATE_DIR" \
|
|
402
|
-
--retention-days "$LOG_RETENTION_DAYS" \
|
|
403
|
-
--max-total-mb "$LOG_MAX_TOTAL_MB" 2>/dev/null) || {
|
|
404
|
-
log_warn "Log cleanup failed (continuing)"
|
|
405
|
-
return 0
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
local deleted reclaimed_kb
|
|
409
|
-
deleted=$(echo "$cleanup_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('deleted_files', 0))" 2>/dev/null || echo "0")
|
|
410
|
-
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")
|
|
411
|
-
|
|
412
|
-
if [[ "$deleted" -gt 0 ]]; then
|
|
413
|
-
log_info "Log cleanup: deleted $deleted files, reclaimed ${reclaimed_kb}KB"
|
|
414
|
-
fi
|
|
415
|
-
}
|
|
416
|
-
|
|
417
407
|
# ============================================================
|
|
418
408
|
# run-one: Run a single refactor
|
|
419
409
|
# ============================================================
|
|
@@ -432,7 +422,16 @@ run_one() {
|
|
|
432
422
|
--dry-run) dry_run=true; shift ;;
|
|
433
423
|
--clean) do_clean=true; shift ;;
|
|
434
424
|
--no-reset) no_reset=true; shift ;;
|
|
435
|
-
--
|
|
425
|
+
--max-infra-retries)
|
|
426
|
+
shift
|
|
427
|
+
if [[ $# -eq 0 ]]; then
|
|
428
|
+
log_error "--max-infra-retries requires a value"
|
|
429
|
+
exit 1
|
|
430
|
+
fi
|
|
431
|
+
prizm_require_positive_int "--max-infra-retries" "$1"
|
|
432
|
+
MAX_INFRA_RETRIES="$1"
|
|
433
|
+
shift
|
|
434
|
+
;;
|
|
436
435
|
--mode)
|
|
437
436
|
shift
|
|
438
437
|
if [[ $# -eq 0 ]]; then
|
|
@@ -473,7 +472,6 @@ run_one() {
|
|
|
473
472
|
fi
|
|
474
473
|
|
|
475
474
|
check_dependencies
|
|
476
|
-
run_log_cleanup
|
|
477
475
|
|
|
478
476
|
# Initialize state if needed
|
|
479
477
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -725,6 +723,8 @@ else:
|
|
|
725
723
|
--state-dir "$STATE_DIR" \
|
|
726
724
|
--refactor-id "$refactor_id" \
|
|
727
725
|
--session-status "failed" \
|
|
726
|
+
--max-retries "$MAX_RETRIES" \
|
|
727
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
728
728
|
--action update 2>/dev/null || true
|
|
729
729
|
log_info "Refactor $refactor_id marked as failed due to interrupt"
|
|
730
730
|
fi
|
|
@@ -758,12 +758,13 @@ else:
|
|
|
758
758
|
if branch_create "$_proj_root" "$_branch_name" "$_source_branch"; then
|
|
759
759
|
_DEV_BRANCH_NAME="$_branch_name"
|
|
760
760
|
else
|
|
761
|
-
|
|
761
|
+
log_error "Failed to create branch: $_branch_name from $_source_branch — aborting to avoid running on $_source_branch"
|
|
762
|
+
return 1
|
|
762
763
|
fi
|
|
763
764
|
|
|
764
765
|
spawn_and_wait_session \
|
|
765
766
|
"$refactor_id" "$refactor_list" "$session_id" \
|
|
766
|
-
"$bootstrap_prompt" "$session_dir"
|
|
767
|
+
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$refactor_model" "$_ORIGINAL_BRANCH"
|
|
767
768
|
local session_status="$_SPAWN_RESULT"
|
|
768
769
|
|
|
769
770
|
# Merge dev branch back to original on success
|
|
@@ -773,7 +774,7 @@ else:
|
|
|
773
774
|
else
|
|
774
775
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
775
776
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
776
|
-
|
|
777
|
+
session_status="merge_conflict"
|
|
777
778
|
fi
|
|
778
779
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
779
780
|
# Session failed — preserve dev branch for inspection
|
|
@@ -784,10 +785,12 @@ else:
|
|
|
784
785
|
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
785
786
|
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
786
787
|
|
|
788
|
+
finalize_refactor_status_after_branch_return \
|
|
789
|
+
"$refactor_id" "$refactor_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES"
|
|
790
|
+
|
|
787
791
|
# Commit refactor status update on the original branch (after guaranteed return)
|
|
788
792
|
if ! git -C "$_proj_root" diff --quiet "$refactor_list" 2>/dev/null; then
|
|
789
|
-
|
|
790
|
-
git -C "$_proj_root" commit --no-verify -m "chore($refactor_id): update refactor status" 2>/dev/null || true
|
|
793
|
+
prizm_git_commit_paths "$_proj_root" "chore($refactor_id): update refactor status" "$refactor_list"
|
|
791
794
|
fi
|
|
792
795
|
|
|
793
796
|
echo ""
|
|
@@ -808,7 +811,41 @@ else:
|
|
|
808
811
|
# ============================================================
|
|
809
812
|
|
|
810
813
|
main() {
|
|
811
|
-
local refactor_list="$
|
|
814
|
+
local refactor_list="$PRIZMKIT_DIR/plans/refactor-list.json"
|
|
815
|
+
|
|
816
|
+
while [[ $# -gt 0 ]]; do
|
|
817
|
+
case "$1" in
|
|
818
|
+
--max-infra-retries)
|
|
819
|
+
shift
|
|
820
|
+
if [[ $# -eq 0 ]]; then
|
|
821
|
+
log_error "--max-infra-retries requires a value"
|
|
822
|
+
exit 1
|
|
823
|
+
fi
|
|
824
|
+
prizm_require_positive_int "--max-infra-retries" "$1"
|
|
825
|
+
MAX_INFRA_RETRIES="$1"
|
|
826
|
+
shift
|
|
827
|
+
;;
|
|
828
|
+
--)
|
|
829
|
+
shift
|
|
830
|
+
break
|
|
831
|
+
;;
|
|
832
|
+
-*)
|
|
833
|
+
log_error "Unknown option: $1"
|
|
834
|
+
show_help
|
|
835
|
+
exit 1
|
|
836
|
+
;;
|
|
837
|
+
*)
|
|
838
|
+
refactor_list="$1"
|
|
839
|
+
shift
|
|
840
|
+
;;
|
|
841
|
+
esac
|
|
842
|
+
done
|
|
843
|
+
|
|
844
|
+
if [[ $# -gt 0 ]]; then
|
|
845
|
+
log_error "Unexpected arguments: $*"
|
|
846
|
+
show_help
|
|
847
|
+
exit 1
|
|
848
|
+
fi
|
|
812
849
|
|
|
813
850
|
if [[ ! "$refactor_list" = /* ]]; then
|
|
814
851
|
refactor_list="$(pwd)/$refactor_list"
|
|
@@ -823,7 +860,6 @@ main() {
|
|
|
823
860
|
fi
|
|
824
861
|
|
|
825
862
|
check_dependencies
|
|
826
|
-
run_log_cleanup
|
|
827
863
|
|
|
828
864
|
# Initialize pipeline state if needed
|
|
829
865
|
if [[ ! -f "$STATE_DIR/pipeline.json" ]]; then
|
|
@@ -870,12 +906,8 @@ main() {
|
|
|
870
906
|
echo -e "${BOLD} Refactor Pipeline Runner Started${NC}"
|
|
871
907
|
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
872
908
|
log_info "Refactor list: $refactor_list"
|
|
873
|
-
log_info "Max retries per refactor: $MAX_RETRIES"
|
|
874
|
-
|
|
875
|
-
log_info "Session timeout: ${SESSION_TIMEOUT}s"
|
|
876
|
-
else
|
|
877
|
-
log_info "Session timeout: none"
|
|
878
|
-
fi
|
|
909
|
+
log_info "Max code retries per refactor: $MAX_RETRIES"
|
|
910
|
+
log_info "Max infrastructure retries per refactor: $MAX_INFRA_RETRIES"
|
|
879
911
|
log_info "AI CLI: $CLI_CMD (platform: $PLATFORM)"
|
|
880
912
|
if [[ -n "${MODEL:-}" ]]; then
|
|
881
913
|
log_info "Default Model: $MODEL"
|
|
@@ -920,6 +952,7 @@ main() {
|
|
|
920
952
|
--refactor-list "$refactor_list" \
|
|
921
953
|
--state-dir "$STATE_DIR" \
|
|
922
954
|
--max-retries "$MAX_RETRIES" \
|
|
955
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
923
956
|
--action get_next 2>/dev/null); then
|
|
924
957
|
log_error "Failed to get next refactor"
|
|
925
958
|
break
|
|
@@ -1007,27 +1040,40 @@ DEPLOY_PROMPT_EOF
|
|
|
1007
1040
|
fi
|
|
1008
1041
|
|
|
1009
1042
|
# Parse refactor info
|
|
1010
|
-
local refactor_id refactor_title refactor_complexity retry_count resume_phase
|
|
1043
|
+
local refactor_id refactor_title refactor_complexity retry_count infra_error_count resume_phase
|
|
1011
1044
|
refactor_id=$(echo "$next_refactor" | jq -r '.refactor_id')
|
|
1012
1045
|
refactor_title=$(echo "$next_refactor" | jq -r '.title')
|
|
1013
1046
|
refactor_complexity=$(echo "$next_refactor" | jq -r '.complexity')
|
|
1014
1047
|
retry_count=$(echo "$next_refactor" | jq -r '.retry_count // 0')
|
|
1048
|
+
infra_error_count=$(echo "$next_refactor" | jq -r '.infra_error_count // 0')
|
|
1015
1049
|
resume_phase=$(echo "$next_refactor" | jq -r '.resume_from_phase // "null"')
|
|
1016
1050
|
|
|
1017
1051
|
echo ""
|
|
1018
1052
|
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
1019
1053
|
log_info "Refactor: ${BOLD}$refactor_id${NC} — $refactor_title"
|
|
1020
|
-
log_info "Complexity: $refactor_complexity
|
|
1054
|
+
log_info "Complexity: $refactor_complexity"
|
|
1055
|
+
log_info "Code retry: $retry_count / $MAX_RETRIES"
|
|
1056
|
+
log_info "Infrastructure retry: $infra_error_count / $MAX_INFRA_RETRIES"
|
|
1021
1057
|
if [[ "$resume_phase" != "null" ]]; then
|
|
1022
1058
|
log_info "Resuming from Phase $resume_phase"
|
|
1023
1059
|
fi
|
|
1024
1060
|
echo -e "${BOLD}────────────────────────────────────────────────────${NC}"
|
|
1025
1061
|
|
|
1026
|
-
#
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1062
|
+
# Auto-commit any uncommitted workspace changes before starting the pipeline.
|
|
1063
|
+
# This ensures a clean baseline without blocking execution.
|
|
1064
|
+
local _dirty_files=""
|
|
1065
|
+
_dirty_files=$(prizm_git_status_non_bookkeeping "$_proj_root" "$STATE_DIR" "$refactor_list" || true)
|
|
1066
|
+
if [[ -n "$_dirty_files" ]]; then
|
|
1067
|
+
log_info "Auto-committing workspace changes before $refactor_id..."
|
|
1068
|
+
prizm_git_add_all_safe "$_proj_root"
|
|
1069
|
+
git -C "$_proj_root" commit --no-verify -m "chore: workspace snapshot ready for run $refactor_id" 2>/dev/null || true
|
|
1070
|
+
fi
|
|
1071
|
+
|
|
1072
|
+
local _bookkeeping_dirty=""
|
|
1073
|
+
_bookkeeping_dirty=$(prizm_git_status_bookkeeping "$_proj_root" "$STATE_DIR" "$refactor_list" || true)
|
|
1074
|
+
if [[ -n "$_bookkeeping_dirty" ]]; then
|
|
1075
|
+
log_info "Committing pending pipeline bookkeeping before $refactor_id..."
|
|
1076
|
+
prizm_git_commit_paths "$_proj_root" "chore($refactor_id): include pipeline bookkeeping" "$refactor_list" "$STATE_DIR"
|
|
1031
1077
|
fi
|
|
1032
1078
|
|
|
1033
1079
|
# Mark refactor as in-progress BEFORE creating dev branch
|
|
@@ -1040,8 +1086,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1040
1086
|
--action start >/dev/null 2>&1 || true
|
|
1041
1087
|
# Commit the in_progress status on the original branch
|
|
1042
1088
|
if ! git -C "$_proj_root" diff --quiet "$refactor_list" 2>/dev/null; then
|
|
1043
|
-
|
|
1044
|
-
git -C "$_proj_root" commit --no-verify -m "chore($refactor_id): mark in_progress" 2>/dev/null || true
|
|
1089
|
+
prizm_git_commit_paths "$_proj_root" "chore($refactor_id): mark in_progress" "$refactor_list"
|
|
1045
1090
|
fi
|
|
1046
1091
|
|
|
1047
1092
|
# Create per-refactor dev branch (from the now-updated original branch)
|
|
@@ -1050,8 +1095,9 @@ DEPLOY_PROMPT_EOF
|
|
|
1050
1095
|
_DEV_BRANCH_NAME="$_refactor_branch"
|
|
1051
1096
|
log_info "Dev branch: $_refactor_branch"
|
|
1052
1097
|
else
|
|
1053
|
-
|
|
1098
|
+
log_error "Failed to create dev branch: $_refactor_branch from $_ORIGINAL_BRANCH — aborting to avoid running on $_ORIGINAL_BRANCH"
|
|
1054
1099
|
_DEV_BRANCH_NAME=""
|
|
1100
|
+
break
|
|
1055
1101
|
fi
|
|
1056
1102
|
|
|
1057
1103
|
# Generate session
|
|
@@ -1122,7 +1168,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1122
1168
|
|
|
1123
1169
|
spawn_and_wait_session \
|
|
1124
1170
|
"$refactor_id" "$refactor_list" "$session_id" \
|
|
1125
|
-
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$refactor_model" "$_ORIGINAL_BRANCH"
|
|
1171
|
+
"$bootstrap_prompt" "$session_dir" "$MAX_RETRIES" "$MAX_INFRA_RETRIES" "$refactor_model" "$_ORIGINAL_BRANCH"
|
|
1126
1172
|
|
|
1127
1173
|
# Validate key artifacts after successful session
|
|
1128
1174
|
if [[ "$_SPAWN_RESULT" == "success" ]]; then
|
|
@@ -1135,7 +1181,6 @@ DEPLOY_PROMPT_EOF
|
|
|
1135
1181
|
fi
|
|
1136
1182
|
|
|
1137
1183
|
local session_status="$_SPAWN_RESULT"
|
|
1138
|
-
local item_status_after_session="${_SPAWN_ITEM_STATUS:-}"
|
|
1139
1184
|
|
|
1140
1185
|
# Merge per-refactor dev branch back to original on success
|
|
1141
1186
|
if [[ "$session_status" == "success" && -n "$_DEV_BRANCH_NAME" ]]; then
|
|
@@ -1144,7 +1189,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1144
1189
|
else
|
|
1145
1190
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
1146
1191
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
1147
|
-
|
|
1192
|
+
session_status="merge_conflict"
|
|
1148
1193
|
fi
|
|
1149
1194
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
1150
1195
|
# Session failed — preserve dev branch for inspection
|
|
@@ -1155,10 +1200,13 @@ DEPLOY_PROMPT_EOF
|
|
|
1155
1200
|
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
1156
1201
|
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1157
1202
|
|
|
1203
|
+
finalize_refactor_status_after_branch_return \
|
|
1204
|
+
"$refactor_id" "$refactor_list" "$session_id" "$session_status" "$MAX_RETRIES" "$MAX_INFRA_RETRIES"
|
|
1205
|
+
local item_status_after_session="${_SPAWN_ITEM_STATUS:-}"
|
|
1206
|
+
|
|
1158
1207
|
# Commit refactor status update on the original branch (after guaranteed return)
|
|
1159
1208
|
if ! git -C "$_proj_root" diff --quiet "$refactor_list" 2>/dev/null; then
|
|
1160
|
-
|
|
1161
|
-
git -C "$_proj_root" commit --no-verify -m "chore($refactor_id): update refactor status" 2>/dev/null || true
|
|
1209
|
+
prizm_git_commit_paths "$_proj_root" "chore($refactor_id): update refactor status" "$refactor_list"
|
|
1162
1210
|
fi
|
|
1163
1211
|
|
|
1164
1212
|
# Stuck detection
|
|
@@ -1185,7 +1233,7 @@ DEPLOY_PROMPT_EOF
|
|
|
1185
1233
|
log_error "════════════════════════════════════════════════════"
|
|
1186
1234
|
break
|
|
1187
1235
|
elif [[ "$session_status" != "success" && "$STOP_ON_FAILURE" == "1" ]]; then
|
|
1188
|
-
log_info "STOP_ON_FAILURE: $refactor_id is ${item_status_after_session:-unknown}; retry budget not exhausted, continuing."
|
|
1236
|
+
log_info "STOP_ON_FAILURE: $refactor_id is ${item_status_after_session:-unknown}; code/infra retry budget not exhausted, continuing."
|
|
1189
1237
|
fi
|
|
1190
1238
|
|
|
1191
1239
|
log_info "Pausing 5s before next refactor..."
|
|
@@ -1201,7 +1249,7 @@ show_help() {
|
|
|
1201
1249
|
echo "Usage: $0 <command> [options]"
|
|
1202
1250
|
echo ""
|
|
1203
1251
|
echo "Commands:"
|
|
1204
|
-
echo " run [.prizmkit/plans/refactor-list.json]
|
|
1252
|
+
echo " run [.prizmkit/plans/refactor-list.json] [options] Run all refactors in dependency-topological order"
|
|
1205
1253
|
echo " run <refactor-id> [options] Run a single refactor"
|
|
1206
1254
|
echo " status [.prizmkit/plans/refactor-list.json] Show refactor pipeline status"
|
|
1207
1255
|
echo " reset Clear all refactor state"
|
|
@@ -1211,14 +1259,17 @@ show_help() {
|
|
|
1211
1259
|
echo " --dry-run Generate bootstrap prompt only, don't spawn session"
|
|
1212
1260
|
echo " --clean Delete artifacts and reset before running"
|
|
1213
1261
|
echo " --no-reset Skip status reset (preserve retry count)"
|
|
1214
|
-
echo " --
|
|
1262
|
+
echo " --max-infra-retries N Max infrastructure/provider retries (default: 3)"
|
|
1215
1263
|
echo " --mode <lite|standard|full> Override pipeline mode"
|
|
1216
1264
|
echo " --critic Enable adversarial critic review"
|
|
1217
1265
|
echo " --no-critic Disable adversarial critic review"
|
|
1218
1266
|
echo ""
|
|
1267
|
+
echo "Pipeline Options (run all):"
|
|
1268
|
+
echo " --max-infra-retries N Max infrastructure/provider retries (default: 3)"
|
|
1269
|
+
echo ""
|
|
1219
1270
|
echo "Environment Variables:"
|
|
1220
|
-
echo " MAX_RETRIES Max retries per refactor (default: 3)"
|
|
1221
|
-
echo "
|
|
1271
|
+
echo " MAX_RETRIES Max code retries per refactor (default: 3)"
|
|
1272
|
+
echo " MAX_INFRA_RETRIES Max infrastructure/provider retries per refactor (default: 3)"
|
|
1222
1273
|
echo " MODEL Default AI model (overridden by per-refactor model in refactor list)"
|
|
1223
1274
|
echo " PRIZMKIT_EFFORT AI reasoning effort (low|medium|high|xhigh|max; max is Claude Code only)"
|
|
1224
1275
|
echo " PIPELINE_MODE Default pipeline mode: lite|standard|full (overridden by --mode)"
|
|
@@ -1229,15 +1280,11 @@ show_help() {
|
|
|
1229
1280
|
echo " HEARTBEAT_INTERVAL Heartbeat log interval in seconds (default: 30)"
|
|
1230
1281
|
echo " STALE_KILL_THRESHOLD Auto-kill session after N seconds of no progress (default: 900)"
|
|
1231
1282
|
echo " STOP_ON_FAILURE Stop pipeline when a task exhausts retries: 0|1 (default: 0)"
|
|
1232
|
-
echo " LOG_CLEANUP_ENABLED Run log cleanup before execution (default: 1)"
|
|
1233
|
-
echo " LOG_RETENTION_DAYS Delete logs older than N days (default: 14)"
|
|
1234
|
-
echo " LOG_MAX_TOTAL_MB Keep total logs under N MB (default: 1024)"
|
|
1235
1283
|
echo ""
|
|
1236
1284
|
echo "Examples:"
|
|
1237
1285
|
echo " ./run-refactor.sh run # Run all refactors"
|
|
1238
1286
|
echo " ./run-refactor.sh run .prizmkit/plans/refactor-list.json # Custom refactor list"
|
|
1239
1287
|
echo " ./run-refactor.sh run R-001 --dry-run # Inspect generated prompt"
|
|
1240
|
-
echo " ./run-refactor.sh run R-001 --timeout 3600 # 1h timeout"
|
|
1241
1288
|
echo " ./run-refactor.sh status # Show status"
|
|
1242
1289
|
echo " STRICT_BEHAVIOR_CHECK=0 ./run-refactor.sh run # Skip full test suite"
|
|
1243
1290
|
echo " MAX_RETRIES=5 ./run-refactor.sh run # Custom retries"
|
|
@@ -1249,7 +1296,7 @@ case "${1:-run}" in
|
|
|
1249
1296
|
if [[ "${1:-}" =~ ^[Rr]-[0-9]+ ]]; then
|
|
1250
1297
|
run_one "$@"
|
|
1251
1298
|
else
|
|
1252
|
-
main "
|
|
1299
|
+
main "$@"
|
|
1253
1300
|
fi
|
|
1254
1301
|
;;
|
|
1255
1302
|
status)
|
|
@@ -1261,6 +1308,8 @@ case "${1:-run}" in
|
|
|
1261
1308
|
python3 "$SCRIPTS_DIR/update-refactor-status.py" \
|
|
1262
1309
|
--refactor-list "${2:-$PRIZMKIT_DIR/plans/refactor-list.json}" \
|
|
1263
1310
|
--state-dir "$STATE_DIR" \
|
|
1311
|
+
--max-retries "$MAX_RETRIES" \
|
|
1312
|
+
--max-infra-retries "$MAX_INFRA_RETRIES" \
|
|
1264
1313
|
--action status
|
|
1265
1314
|
;;
|
|
1266
1315
|
reset)
|