prizmkit 1.1.20 → 1.1.23
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/lib/branch.sh +187 -23
- package/bundled/dev-pipeline/reset-bug.sh +21 -13
- package/bundled/dev-pipeline/reset-feature.sh +21 -13
- package/bundled/dev-pipeline/reset-refactor.sh +21 -13
- package/bundled/dev-pipeline/run-bugfix.sh +113 -26
- package/bundled/dev-pipeline/run-feature.sh +113 -27
- package/bundled/dev-pipeline/run-refactor.sh +113 -26
- package/bundled/dev-pipeline/scripts/detect-stuck.py +25 -14
- package/bundled/dev-pipeline/scripts/generate-bootstrap-prompt.py +32 -0
- package/bundled/dev-pipeline/scripts/init-bugfix-pipeline.py +0 -5
- package/bundled/dev-pipeline/scripts/init-pipeline.py +0 -5
- package/bundled/dev-pipeline/scripts/init-refactor-pipeline.py +0 -5
- package/bundled/dev-pipeline/scripts/update-bug-status.py +40 -31
- package/bundled/dev-pipeline/scripts/update-feature-status.py +54 -60
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +43 -34
- package/bundled/dev-pipeline/templates/bootstrap-tier1.md +22 -7
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +22 -7
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +22 -7
- package/bundled/dev-pipeline/templates/sections/phase-browser-verification.md +22 -7
- package/bundled/dev-pipeline/tests/test_auto_skip.py +10 -3
- package/bundled/skills/_metadata.json +1 -1
- package/package.json +1 -1
|
@@ -254,8 +254,15 @@ if incomplete:
|
|
|
254
254
|
sys.exit(1)
|
|
255
255
|
print('ALL_COMPLETE')
|
|
256
256
|
sys.exit(0)
|
|
257
|
-
" "$checkpoint_file" 2>&1)
|
|
257
|
+
" "$checkpoint_file" 2>&1) || checkpoint_result="CHECK_FAILED"
|
|
258
258
|
local check_exit=$?
|
|
259
|
+
if [[ "$checkpoint_result" == "CHECK_FAILED" ]]; then
|
|
260
|
+
check_exit=2
|
|
261
|
+
elif [[ "$checkpoint_result" == *"INCOMPLETE"* ]]; then
|
|
262
|
+
check_exit=1
|
|
263
|
+
else
|
|
264
|
+
check_exit=0
|
|
265
|
+
fi
|
|
259
266
|
if [[ $check_exit -eq 2 ]]; then
|
|
260
267
|
log_warn "CHECKPOINT_CORRUPTED: workflow-checkpoint.json is not valid JSON"
|
|
261
268
|
elif [[ $check_exit -eq 1 ]]; then
|
|
@@ -270,7 +277,7 @@ sys.exit(0)
|
|
|
270
277
|
# Subagent detection
|
|
271
278
|
prizm_detect_subagents "$session_log"
|
|
272
279
|
|
|
273
|
-
# Update bug status
|
|
280
|
+
# Update bug status (do NOT commit on dev branch — commit happens after merge)
|
|
274
281
|
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
275
282
|
--bug-list "$bug_list" \
|
|
276
283
|
--state-dir "$STATE_DIR" \
|
|
@@ -280,12 +287,6 @@ sys.exit(0)
|
|
|
280
287
|
--max-retries "$max_retries" \
|
|
281
288
|
--action update >/dev/null 2>&1 || true
|
|
282
289
|
|
|
283
|
-
# Commit .prizmkit/plans/bug-fix-list.json status update (pipeline management commit)
|
|
284
|
-
if ! git -C "$project_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
285
|
-
git -C "$project_root" add "$bug_list"
|
|
286
|
-
git -C "$project_root" commit --no-verify -m "chore($bug_id): update bug status" 2>/dev/null || true
|
|
287
|
-
fi
|
|
288
|
-
|
|
289
290
|
_SPAWN_RESULT="$session_status"
|
|
290
291
|
}
|
|
291
292
|
|
|
@@ -306,13 +307,40 @@ cleanup() {
|
|
|
306
307
|
log_info "Original branch was: $_ORIGINAL_BRANCH"
|
|
307
308
|
fi
|
|
308
309
|
|
|
310
|
+
# Update status of currently in-progress bug to interrupted
|
|
309
311
|
if [[ -n "$BUG_LIST" && -f "$BUG_LIST" ]]; then
|
|
312
|
+
# Find any in-progress bug and mark it as failed
|
|
313
|
+
local _interrupted_id
|
|
314
|
+
_interrupted_id=$(python3 -c "
|
|
315
|
+
import json, sys
|
|
316
|
+
with open(sys.argv[1]) as f:
|
|
317
|
+
data = json.load(f)
|
|
318
|
+
for bug in data.get('bugs', []):
|
|
319
|
+
if bug.get('status') == 'in_progress':
|
|
320
|
+
print(bug['id'])
|
|
321
|
+
break
|
|
322
|
+
" "$BUG_LIST" 2>/dev/null || echo "")
|
|
323
|
+
|
|
324
|
+
if [[ -n "$_interrupted_id" ]]; then
|
|
325
|
+
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
326
|
+
--bug-list "$BUG_LIST" \
|
|
327
|
+
--state-dir "$STATE_DIR" \
|
|
328
|
+
--bug-id "$_interrupted_id" \
|
|
329
|
+
--session-status "failed" \
|
|
330
|
+
--action update 2>/dev/null || true
|
|
331
|
+
log_info "Bug $_interrupted_id marked as failed due to interrupt"
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
# Pause the pipeline
|
|
310
335
|
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
311
336
|
--bug-list "$BUG_LIST" \
|
|
312
337
|
--state-dir "$STATE_DIR" \
|
|
313
338
|
--action pause 2>/dev/null || true
|
|
314
339
|
fi
|
|
315
340
|
|
|
341
|
+
# GUARANTEED: always return to original branch (save WIP on dev branch first)
|
|
342
|
+
branch_ensure_return "$(cd "$SCRIPT_DIR/.." && pwd)" "$_ORIGINAL_BRANCH" "$_DEV_BRANCH_NAME"
|
|
343
|
+
|
|
316
344
|
log_info "Bug fix pipeline paused. Run './run-bugfix.sh run' to resume."
|
|
317
345
|
exit 130
|
|
318
346
|
}
|
|
@@ -639,6 +667,20 @@ else:
|
|
|
639
667
|
log_info "Development was on branch: $_DEV_BRANCH_NAME"
|
|
640
668
|
fi
|
|
641
669
|
log_info "Session log: $session_dir/logs/session.log"
|
|
670
|
+
|
|
671
|
+
# Update bug status to failed on interrupt
|
|
672
|
+
if [[ -n "$bug_list" && -f "$bug_list" ]]; then
|
|
673
|
+
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
674
|
+
--bug-list "$bug_list" \
|
|
675
|
+
--state-dir "$STATE_DIR" \
|
|
676
|
+
--bug-id "$bug_id" \
|
|
677
|
+
--session-status "failed" \
|
|
678
|
+
--action update 2>/dev/null || true
|
|
679
|
+
log_info "Bug $bug_id marked as failed due to interrupt"
|
|
680
|
+
fi
|
|
681
|
+
|
|
682
|
+
# GUARANTEED: always return to original branch (save WIP on dev branch first)
|
|
683
|
+
branch_ensure_return "$(cd "$SCRIPT_DIR/.." && pwd)" "$_ORIGINAL_BRANCH" "$_DEV_BRANCH_NAME"
|
|
642
684
|
exit 130
|
|
643
685
|
}
|
|
644
686
|
trap cleanup_single_bug SIGINT SIGTERM
|
|
@@ -652,6 +694,20 @@ else:
|
|
|
652
694
|
_source_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
|
653
695
|
_ORIGINAL_BRANCH="$_source_branch"
|
|
654
696
|
|
|
697
|
+
# Mark bug as in-progress BEFORE creating dev branch
|
|
698
|
+
# This ensures the in_progress status commit lands on the original branch,
|
|
699
|
+
# not the dev branch — preventing rebase conflicts in branch_merge later.
|
|
700
|
+
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
701
|
+
--bug-list "$bug_list" \
|
|
702
|
+
--state-dir "$STATE_DIR" \
|
|
703
|
+
--bug-id "$bug_id" \
|
|
704
|
+
--action start >/dev/null 2>&1 || true
|
|
705
|
+
# Commit the in_progress status on the original branch
|
|
706
|
+
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
707
|
+
git -C "$_proj_root" add "$bug_list" 2>/dev/null || true
|
|
708
|
+
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): mark in_progress" 2>/dev/null || true
|
|
709
|
+
fi
|
|
710
|
+
|
|
655
711
|
local _branch_name="${DEV_BRANCH:-bugfix/${bug_id}-$(date +%s)}"
|
|
656
712
|
if branch_create "$_proj_root" "$_branch_name" "$_source_branch"; then
|
|
657
713
|
_DEV_BRANCH_NAME="$_branch_name"
|
|
@@ -671,18 +727,23 @@ else:
|
|
|
671
727
|
else
|
|
672
728
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
673
729
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
674
|
-
git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null || true
|
|
675
730
|
_DEV_BRANCH_NAME=""
|
|
676
731
|
fi
|
|
677
732
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
678
|
-
# Session failed —
|
|
679
|
-
if ! git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null; then
|
|
680
|
-
log_warn "Failed to checkout $_ORIGINAL_BRANCH after session failure — staying on dev branch"
|
|
681
|
-
fi
|
|
733
|
+
# Session failed — preserve dev branch for inspection
|
|
682
734
|
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
683
735
|
_DEV_BRANCH_NAME=""
|
|
684
736
|
fi
|
|
685
737
|
|
|
738
|
+
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
739
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
740
|
+
|
|
741
|
+
# Commit bug status update on the original branch (after guaranteed return)
|
|
742
|
+
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
743
|
+
git -C "$_proj_root" add "$bug_list"
|
|
744
|
+
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): update bug status" 2>/dev/null || true
|
|
745
|
+
fi
|
|
746
|
+
|
|
686
747
|
echo ""
|
|
687
748
|
if [[ "$session_status" == "success" ]]; then
|
|
688
749
|
log_success "════════════════════════════════════════════════════"
|
|
@@ -785,6 +846,18 @@ main() {
|
|
|
785
846
|
local total_subagent_calls=0
|
|
786
847
|
|
|
787
848
|
while true; do
|
|
849
|
+
# Safety net: ensure we're on the original branch at the start of each iteration.
|
|
850
|
+
# If a previous iteration's `continue` skipped branch_ensure_return, we could
|
|
851
|
+
# still be on a dev branch. This prevents cascading branch confusion.
|
|
852
|
+
if [[ -n "$_ORIGINAL_BRANCH" ]]; then
|
|
853
|
+
local _cur_branch
|
|
854
|
+
_cur_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
855
|
+
if [[ -n "$_cur_branch" && "$_cur_branch" != "$_ORIGINAL_BRANCH" ]]; then
|
|
856
|
+
log_warn "Still on branch $_cur_branch at loop start — returning to $_ORIGINAL_BRANCH"
|
|
857
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
858
|
+
fi
|
|
859
|
+
fi
|
|
860
|
+
|
|
788
861
|
# Find next bug to process
|
|
789
862
|
local next_bug
|
|
790
863
|
if ! next_bug=$(python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
@@ -839,7 +912,21 @@ main() {
|
|
|
839
912
|
git -C "$_proj_root" commit --no-verify -m "chore: capture artifacts before $bug_id session" 2>/dev/null || true
|
|
840
913
|
fi
|
|
841
914
|
|
|
842
|
-
#
|
|
915
|
+
# Mark bug as in-progress BEFORE creating dev branch
|
|
916
|
+
# This ensures the in_progress status commit lands on the original branch,
|
|
917
|
+
# not the dev branch — preventing rebase conflicts in branch_merge later.
|
|
918
|
+
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
919
|
+
--bug-list "$bug_list" \
|
|
920
|
+
--state-dir "$STATE_DIR" \
|
|
921
|
+
--bug-id "$bug_id" \
|
|
922
|
+
--action start >/dev/null 2>&1 || true
|
|
923
|
+
# Commit the in_progress status on the original branch
|
|
924
|
+
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
925
|
+
git -C "$_proj_root" add "$bug_list" 2>/dev/null || true
|
|
926
|
+
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): mark in_progress" 2>/dev/null || true
|
|
927
|
+
fi
|
|
928
|
+
|
|
929
|
+
# Create per-bug dev branch (from the now-updated original branch)
|
|
843
930
|
local _bug_branch="${DEV_BRANCH:-bugfix/${bug_id}-$(date +%Y%m%d%H%M)}"
|
|
844
931
|
if branch_create "$_proj_root" "$_bug_branch" "$_ORIGINAL_BRANCH"; then
|
|
845
932
|
_DEV_BRANCH_NAME="$_bug_branch"
|
|
@@ -885,6 +972,8 @@ main() {
|
|
|
885
972
|
local gen_output
|
|
886
973
|
gen_output=$(python3 "$SCRIPTS_DIR/generate-bugfix-prompt.py" "${main_prompt_args[@]}" 2>/dev/null) || {
|
|
887
974
|
log_error "Failed to generate bootstrap prompt for $bug_id"
|
|
975
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
976
|
+
_DEV_BRANCH_NAME=""
|
|
888
977
|
continue
|
|
889
978
|
}
|
|
890
979
|
local bug_model pipeline_mode agent_count critic_enabled
|
|
@@ -907,13 +996,6 @@ main() {
|
|
|
907
996
|
log_info "Bug model: $bug_model"
|
|
908
997
|
fi
|
|
909
998
|
|
|
910
|
-
# Mark bug as in-progress before spawning session
|
|
911
|
-
python3 "$SCRIPTS_DIR/update-bug-status.py" \
|
|
912
|
-
--bug-list "$bug_list" \
|
|
913
|
-
--state-dir "$STATE_DIR" \
|
|
914
|
-
--bug-id "$bug_id" \
|
|
915
|
-
--action start >/dev/null 2>&1 || true
|
|
916
|
-
|
|
917
999
|
# Spawn session
|
|
918
1000
|
log_info "Spawning AI CLI session: $session_id"
|
|
919
1001
|
_SPAWN_RESULT=""
|
|
@@ -931,18 +1013,23 @@ main() {
|
|
|
931
1013
|
else
|
|
932
1014
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
933
1015
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
934
|
-
git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null || true
|
|
935
1016
|
_DEV_BRANCH_NAME=""
|
|
936
1017
|
fi
|
|
937
1018
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
938
|
-
# Session failed —
|
|
939
|
-
if ! git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null; then
|
|
940
|
-
log_warn "Failed to checkout $_ORIGINAL_BRANCH after session failure — staying on dev branch"
|
|
941
|
-
fi
|
|
1019
|
+
# Session failed — preserve dev branch for inspection
|
|
942
1020
|
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
943
1021
|
_DEV_BRANCH_NAME=""
|
|
944
1022
|
fi
|
|
945
1023
|
|
|
1024
|
+
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
1025
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1026
|
+
|
|
1027
|
+
# Commit bug status update on the original branch (after guaranteed return)
|
|
1028
|
+
if ! git -C "$_proj_root" diff --quiet "$bug_list" 2>/dev/null; then
|
|
1029
|
+
git -C "$_proj_root" add "$bug_list"
|
|
1030
|
+
git -C "$_proj_root" commit --no-verify -m "chore($bug_id): update bug status" 2>/dev/null || true
|
|
1031
|
+
fi
|
|
1032
|
+
|
|
946
1033
|
session_count=$((session_count + 1))
|
|
947
1034
|
total_subagent_calls=$((total_subagent_calls + _SUBAGENT_COUNT))
|
|
948
1035
|
|
|
@@ -308,8 +308,15 @@ if incomplete:
|
|
|
308
308
|
sys.exit(1)
|
|
309
309
|
print('ALL_COMPLETE')
|
|
310
310
|
sys.exit(0)
|
|
311
|
-
" "$checkpoint_file" 2>&1)
|
|
311
|
+
" "$checkpoint_file" 2>&1) || checkpoint_result="CHECK_FAILED"
|
|
312
312
|
local check_exit=$?
|
|
313
|
+
if [[ "$checkpoint_result" == "CHECK_FAILED" ]]; then
|
|
314
|
+
check_exit=2
|
|
315
|
+
elif [[ "$checkpoint_result" == *"INCOMPLETE"* ]]; then
|
|
316
|
+
check_exit=1
|
|
317
|
+
else
|
|
318
|
+
check_exit=0
|
|
319
|
+
fi
|
|
313
320
|
if [[ $check_exit -eq 2 ]]; then
|
|
314
321
|
log_warn "CHECKPOINT_CORRUPTED: workflow-checkpoint.json is not valid JSON"
|
|
315
322
|
elif [[ $check_exit -eq 1 ]]; then
|
|
@@ -352,7 +359,7 @@ sys.exit(0)
|
|
|
352
359
|
fi
|
|
353
360
|
fi
|
|
354
361
|
|
|
355
|
-
# Update feature status
|
|
362
|
+
# Update feature status (do NOT commit on dev branch — commit happens after merge)
|
|
356
363
|
local update_output
|
|
357
364
|
update_output=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
358
365
|
--feature-list "$feature_list" \
|
|
@@ -366,12 +373,6 @@ sys.exit(0)
|
|
|
366
373
|
log_error ".prizmkit/plans/feature-list.json may be out of sync. Manual intervention needed."
|
|
367
374
|
}
|
|
368
375
|
|
|
369
|
-
# Commit feature status update (pipeline management commit)
|
|
370
|
-
if ! git -C "$project_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
371
|
-
git -C "$project_root" add "$feature_list"
|
|
372
|
-
git -C "$project_root" commit --no-verify -m "chore($feature_id): update feature status" 2>/dev/null || true
|
|
373
|
-
fi
|
|
374
|
-
|
|
375
376
|
# Return status via global variable (avoids $() swallowing stdout)
|
|
376
377
|
_SPAWN_RESULT="$session_status"
|
|
377
378
|
}
|
|
@@ -393,13 +394,40 @@ cleanup() {
|
|
|
393
394
|
log_info "Original branch was: $_ORIGINAL_BRANCH"
|
|
394
395
|
fi
|
|
395
396
|
|
|
397
|
+
# Update status of currently in-progress feature to interrupted
|
|
396
398
|
if [[ -n "$FEATURE_LIST" && -f "$FEATURE_LIST" ]]; then
|
|
399
|
+
# Find any in-progress feature and mark it as interrupted
|
|
400
|
+
local _interrupted_id
|
|
401
|
+
_interrupted_id=$(python3 -c "
|
|
402
|
+
import json, sys
|
|
403
|
+
with open(sys.argv[1]) as f:
|
|
404
|
+
data = json.load(f)
|
|
405
|
+
for feat in data.get('features', []):
|
|
406
|
+
if feat.get('status') == 'in_progress':
|
|
407
|
+
print(feat['id'])
|
|
408
|
+
break
|
|
409
|
+
" "$FEATURE_LIST" 2>/dev/null || echo "")
|
|
410
|
+
|
|
411
|
+
if [[ -n "$_interrupted_id" ]]; then
|
|
412
|
+
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
413
|
+
--feature-list "$FEATURE_LIST" \
|
|
414
|
+
--state-dir "$STATE_DIR" \
|
|
415
|
+
--feature-id "$_interrupted_id" \
|
|
416
|
+
--session-status "failed" \
|
|
417
|
+
--action update 2>/dev/null || true
|
|
418
|
+
log_info "Feature $_interrupted_id marked as failed due to interrupt"
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
# Pause the pipeline (mark remaining pending items)
|
|
397
422
|
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
398
423
|
--feature-list "$FEATURE_LIST" \
|
|
399
424
|
--state-dir "$STATE_DIR" \
|
|
400
425
|
--action pause 2>/dev/null || true
|
|
401
426
|
fi
|
|
402
427
|
|
|
428
|
+
# GUARANTEED: always return to original branch (save WIP on dev branch first)
|
|
429
|
+
branch_ensure_return "$(cd "$SCRIPT_DIR/.." && pwd)" "$_ORIGINAL_BRANCH" "$_DEV_BRANCH_NAME"
|
|
430
|
+
|
|
403
431
|
log_info "Pipeline paused. Run './run-feature.sh run' to resume."
|
|
404
432
|
exit 130
|
|
405
433
|
}
|
|
@@ -814,6 +842,20 @@ else:
|
|
|
814
842
|
log_info "Development was on branch: $_DEV_BRANCH_NAME"
|
|
815
843
|
fi
|
|
816
844
|
log_info "Session log: $session_dir/logs/session.log"
|
|
845
|
+
|
|
846
|
+
# Update feature status to failed on interrupt
|
|
847
|
+
if [[ -n "$feature_list" && -f "$feature_list" ]]; then
|
|
848
|
+
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
849
|
+
--feature-list "$feature_list" \
|
|
850
|
+
--state-dir "$STATE_DIR" \
|
|
851
|
+
--feature-id "$feature_id" \
|
|
852
|
+
--session-status "failed" \
|
|
853
|
+
--action update 2>/dev/null || true
|
|
854
|
+
log_info "Feature $feature_id marked as failed due to interrupt"
|
|
855
|
+
fi
|
|
856
|
+
|
|
857
|
+
# GUARANTEED: always return to original branch (save WIP on dev branch first)
|
|
858
|
+
branch_ensure_return "$(cd "$SCRIPT_DIR/.." && pwd)" "$_ORIGINAL_BRANCH" "$_DEV_BRANCH_NAME"
|
|
817
859
|
exit 130
|
|
818
860
|
}
|
|
819
861
|
trap cleanup_single_feature SIGINT SIGTERM
|
|
@@ -827,6 +869,20 @@ else:
|
|
|
827
869
|
_source_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
|
828
870
|
_ORIGINAL_BRANCH="$_source_branch"
|
|
829
871
|
|
|
872
|
+
# Mark feature as in-progress BEFORE creating dev branch
|
|
873
|
+
# This ensures the in_progress status commit lands on the original branch,
|
|
874
|
+
# not the dev branch — preventing rebase conflicts in branch_merge later.
|
|
875
|
+
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
876
|
+
--feature-list "$feature_list" \
|
|
877
|
+
--state-dir "$STATE_DIR" \
|
|
878
|
+
--feature-id "$feature_id" \
|
|
879
|
+
--action start >/dev/null 2>&1 || true
|
|
880
|
+
# Commit the in_progress status on the original branch
|
|
881
|
+
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
882
|
+
git -C "$_proj_root" add "$feature_list" 2>/dev/null || true
|
|
883
|
+
git -C "$_proj_root" commit --no-verify -m "chore($feature_id): mark in_progress" 2>/dev/null || true
|
|
884
|
+
fi
|
|
885
|
+
|
|
830
886
|
local _branch_name="${DEV_BRANCH:-dev/${feature_id}-$(date +%Y%m%d%H%M)}"
|
|
831
887
|
if branch_create "$_proj_root" "$_branch_name" "$_source_branch"; then
|
|
832
888
|
_DEV_BRANCH_NAME="$_branch_name"
|
|
@@ -846,18 +902,23 @@ else:
|
|
|
846
902
|
else
|
|
847
903
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
848
904
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
849
|
-
git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null || true
|
|
850
905
|
_DEV_BRANCH_NAME=""
|
|
851
906
|
fi
|
|
852
907
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
853
|
-
# Session failed —
|
|
854
|
-
if ! git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null; then
|
|
855
|
-
log_warn "Failed to checkout $_ORIGINAL_BRANCH after session failure — staying on dev branch"
|
|
856
|
-
fi
|
|
908
|
+
# Session failed — preserve dev branch for inspection
|
|
857
909
|
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
858
910
|
_DEV_BRANCH_NAME=""
|
|
859
911
|
fi
|
|
860
912
|
|
|
913
|
+
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
914
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
915
|
+
|
|
916
|
+
# Commit feature status update on the original branch (after guaranteed return)
|
|
917
|
+
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
918
|
+
git -C "$_proj_root" add "$feature_list"
|
|
919
|
+
git -C "$_proj_root" commit --no-verify -m "chore($feature_id): update feature status" 2>/dev/null || true
|
|
920
|
+
fi
|
|
921
|
+
|
|
861
922
|
echo ""
|
|
862
923
|
if [[ "$session_status" == "success" ]]; then
|
|
863
924
|
log_success "════════════════════════════════════════════════════"
|
|
@@ -980,6 +1041,18 @@ main() {
|
|
|
980
1041
|
local total_subagent_calls=0
|
|
981
1042
|
|
|
982
1043
|
while true; do
|
|
1044
|
+
# Safety net: ensure we're on the original branch at the start of each iteration.
|
|
1045
|
+
# If a previous iteration's `continue` skipped branch_ensure_return, we could
|
|
1046
|
+
# still be on a dev branch. This prevents cascading branch confusion.
|
|
1047
|
+
if [[ -n "$_ORIGINAL_BRANCH" ]]; then
|
|
1048
|
+
local _cur_branch
|
|
1049
|
+
_cur_branch=$(git -C "$_proj_root" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
1050
|
+
if [[ -n "$_cur_branch" && "$_cur_branch" != "$_ORIGINAL_BRANCH" ]]; then
|
|
1051
|
+
log_warn "Still on branch $_cur_branch at loop start — returning to $_ORIGINAL_BRANCH"
|
|
1052
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1053
|
+
fi
|
|
1054
|
+
fi
|
|
1055
|
+
|
|
983
1056
|
# Check for stuck features
|
|
984
1057
|
local stuck_result
|
|
985
1058
|
stuck_result=$(python3 "$SCRIPTS_DIR/detect-stuck.py" \
|
|
@@ -1081,7 +1154,21 @@ print(count)
|
|
|
1081
1154
|
git -C "$_proj_root" commit --no-verify -m "ready for run $feature_id" 2>/dev/null || true
|
|
1082
1155
|
fi
|
|
1083
1156
|
|
|
1084
|
-
#
|
|
1157
|
+
# Mark feature as in-progress BEFORE creating dev branch
|
|
1158
|
+
# This ensures the in_progress status commit lands on the original branch,
|
|
1159
|
+
# not the dev branch — preventing rebase conflicts in branch_merge later.
|
|
1160
|
+
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
1161
|
+
--feature-list "$feature_list" \
|
|
1162
|
+
--state-dir "$STATE_DIR" \
|
|
1163
|
+
--feature-id "$feature_id" \
|
|
1164
|
+
--action start >/dev/null 2>&1 || true
|
|
1165
|
+
# Commit the in_progress status on the original branch
|
|
1166
|
+
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
1167
|
+
git -C "$_proj_root" add "$feature_list" 2>/dev/null || true
|
|
1168
|
+
git -C "$_proj_root" commit --no-verify -m "chore($feature_id): mark in_progress" 2>/dev/null || true
|
|
1169
|
+
fi
|
|
1170
|
+
|
|
1171
|
+
# Create per-feature dev branch (from the now-updated original branch)
|
|
1085
1172
|
local _feature_branch="${DEV_BRANCH:-dev/${feature_id}-$(date +%Y%m%d%H%M)}"
|
|
1086
1173
|
if branch_create "$_proj_root" "$_feature_branch" "$_ORIGINAL_BRANCH"; then
|
|
1087
1174
|
_DEV_BRANCH_NAME="$_feature_branch"
|
|
@@ -1127,6 +1214,8 @@ print(count)
|
|
|
1127
1214
|
local gen_output
|
|
1128
1215
|
gen_output=$(python3 "$SCRIPTS_DIR/generate-bootstrap-prompt.py" "${main_prompt_args[@]}" 2>/dev/null) || {
|
|
1129
1216
|
log_error "Failed to generate bootstrap prompt for $feature_id"
|
|
1217
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1218
|
+
_DEV_BRANCH_NAME=""
|
|
1130
1219
|
continue
|
|
1131
1220
|
}
|
|
1132
1221
|
local feature_model pipeline_mode agent_count critic_enabled
|
|
@@ -1146,13 +1235,6 @@ print(count)
|
|
|
1146
1235
|
log_info "Pipeline mode: ${BOLD}$pipeline_mode${NC} ($_mode_desc)"
|
|
1147
1236
|
log_info "Agents: $agent_count (critic: $([ "$critic_enabled" = "true" ] && echo "enabled" || echo "disabled"))"
|
|
1148
1237
|
|
|
1149
|
-
# Mark feature as in-progress before spawning session
|
|
1150
|
-
python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
1151
|
-
--feature-list "$feature_list" \
|
|
1152
|
-
--state-dir "$STATE_DIR" \
|
|
1153
|
-
--feature-id "$feature_id" \
|
|
1154
|
-
--action start >/dev/null 2>&1 || true
|
|
1155
|
-
|
|
1156
1238
|
# Spawn session and wait
|
|
1157
1239
|
prizm_log_bootstrap_prompt "$bootstrap_prompt" "$feature_id"
|
|
1158
1240
|
log_info "Spawning AI CLI session: $session_id"
|
|
@@ -1173,19 +1255,23 @@ print(count)
|
|
|
1173
1255
|
else
|
|
1174
1256
|
log_warn "Auto-merge failed — dev branch preserved: $_DEV_BRANCH_NAME"
|
|
1175
1257
|
log_warn "Merge manually: git checkout $_ORIGINAL_BRANCH && git rebase $_DEV_BRANCH_NAME"
|
|
1176
|
-
# Return to original branch; state/ files are untracked and persist across checkout
|
|
1177
|
-
git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null || true
|
|
1178
1258
|
_DEV_BRANCH_NAME=""
|
|
1179
1259
|
fi
|
|
1180
1260
|
elif [[ -n "$_DEV_BRANCH_NAME" ]]; then
|
|
1181
|
-
# Session failed —
|
|
1182
|
-
if ! git -C "$_proj_root" checkout "$_ORIGINAL_BRANCH" 2>/dev/null; then
|
|
1183
|
-
log_warn "Failed to checkout $_ORIGINAL_BRANCH after session failure — staying on dev branch"
|
|
1184
|
-
fi
|
|
1261
|
+
# Session failed — preserve dev branch for inspection
|
|
1185
1262
|
log_warn "Session failed — dev branch preserved for inspection: $_DEV_BRANCH_NAME"
|
|
1186
1263
|
_DEV_BRANCH_NAME=""
|
|
1187
1264
|
fi
|
|
1188
1265
|
|
|
1266
|
+
# GUARANTEED: always return to original branch regardless of success/failure/merge outcome
|
|
1267
|
+
branch_ensure_return "$_proj_root" "$_ORIGINAL_BRANCH"
|
|
1268
|
+
|
|
1269
|
+
# Commit feature status update on the original branch (after guaranteed return)
|
|
1270
|
+
if ! git -C "$_proj_root" diff --quiet "$feature_list" 2>/dev/null; then
|
|
1271
|
+
git -C "$_proj_root" add "$feature_list"
|
|
1272
|
+
git -C "$_proj_root" commit --no-verify -m "chore($feature_id): update feature status" 2>/dev/null || true
|
|
1273
|
+
fi
|
|
1274
|
+
|
|
1189
1275
|
session_count=$((session_count + 1))
|
|
1190
1276
|
total_subagent_calls=$((total_subagent_calls + _SUBAGENT_COUNT))
|
|
1191
1277
|
|