feat(supervisor): eager orphaned PR scan after worker evaluation (t216)#917
feat(supervisor): eager orphaned PR scan after worker evaluation (t216)#917marcusquinn merged 1 commit intomainfrom
Conversation
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Caution Review failedThe pull request is closed. WalkthroughAdded a new per-task eager orphan-PR scanner Changes
Sequence Diagram(s)sequenceDiagram
participant Pulse as cmd_pulse
participant Eval as Task Evaluator
participant Scanner as scan_orphaned_pr_for_task
participant PRRepo as PR Repository
participant DB as Task Database
Pulse->>Eval: Evaluate task(task_id)
Eval-->>Pulse: Outcome (not complete)
Pulse->>Scanner: scan_orphaned_pr_for_task(task_id)
Scanner->>PRRepo: Fetch open/merged PRs for repo
PRRepo-->>Scanner: PR list
Scanner->>Scanner: Match PR by task ID (title/branch)
alt PR Found & Valid
Scanner->>DB: Link PR -> task row
Scanner->>DB: Update task row & transition to complete
Scanner-->>Pulse: Linked & completed
Pulse->>Pulse: Update outcome to complete (short-circuit)
else PR Not Found
Scanner-->>Pulse: No match
Pulse->>Pulse: Continue processing
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Tue Feb 10 04:57:49 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @.agents/scripts/supervisor-helper.sh:
- Around line 6065-6187: The eager orphaned-PR scan (scan_orphaned_pr_for_task)
links PRs while tasks can still be in status evaluating, but the case in
scan_orphaned_pr_for_task only transitions failed|blocked|retrying to complete;
update that case to also treat evaluating as eligible for automatic transition
to complete (i.e., add evaluating to the first case pattern that logs and calls
cmd_transition/update_todo_on_complete/send_task_notification/store_success_pattern),
or alternatively ensure cmd_pulse moves the task out of evaluating before
calling scan_orphaned_pr_for_task so the existing case will apply.
| ####################################### | ||
| # Eager orphaned PR scan for a single task (t216). | ||
| # | ||
| # Called immediately after worker evaluation when the outcome is | ||
| # retry/failed/blocked and no PR was linked. Unlike scan_orphaned_prs() | ||
| # which is a throttled batch sweep (Phase 6), this does a targeted | ||
| # single-task lookup — one repo, one API call — with no throttle. | ||
| # | ||
| # This catches the common case where a worker created a PR but exited | ||
| # without the FULL_LOOP_COMPLETE signal, and evaluate_worker()'s | ||
| # fallback PR detection missed it (API timeout, non-standard branch, etc.). | ||
| # | ||
| # $1: task_id | ||
| # | ||
| # Returns 0 on success. Sets pr_url in DB and transitions task to | ||
| # complete if a matching PR is found. | ||
| ####################################### | ||
| scan_orphaned_pr_for_task() { | ||
| local task_id="$1" | ||
|
|
||
| ensure_db | ||
|
|
||
| local escaped_id | ||
| escaped_id=$(sql_escape "$task_id") | ||
|
|
||
| # Get task details | ||
| local task_row | ||
| task_row=$(db -separator '|' "$SUPERVISOR_DB" " | ||
| SELECT status, repo, branch, pr_url FROM tasks | ||
| WHERE id = '$escaped_id'; | ||
| " 2>/dev/null || echo "") | ||
|
|
||
| if [[ -z "$task_row" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| local tstatus trepo tbranch tpr_url | ||
| IFS='|' read -r tstatus trepo tbranch tpr_url <<< "$task_row" | ||
|
|
||
| # Skip if PR already linked (not orphaned) | ||
| if [[ -n "$tpr_url" && "$tpr_url" != "no_pr" && "$tpr_url" != "task_only" && "$tpr_url" != "task_obsolete" && "$tpr_url" != "" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| # Need a repo to scan | ||
| if [[ -z "$trepo" || ! -d "$trepo" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| local repo_slug | ||
| repo_slug=$(detect_repo_slug "$trepo" 2>/dev/null || echo "") | ||
| if [[ -z "$repo_slug" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| # Fetch open PRs for this repo (single API call) | ||
| local pr_list | ||
| pr_list=$(gh pr list --repo "$repo_slug" --state open --limit 100 \ | ||
| --json number,title,headRefName,url 2>>"$SUPERVISOR_LOG" || echo "") | ||
|
|
||
| # Also check recently merged PRs | ||
| local merged_pr_list | ||
| merged_pr_list=$(gh pr list --repo "$repo_slug" --state merged --limit 50 \ | ||
| --json number,title,headRefName,url 2>>"$SUPERVISOR_LOG" || echo "") | ||
|
|
||
| # Combine open and merged PR lists | ||
| local all_prs | ||
| if [[ -n "$merged_pr_list" && "$merged_pr_list" != "[]" && -n "$pr_list" && "$pr_list" != "[]" ]]; then | ||
| all_prs=$(echo "$pr_list" "$merged_pr_list" | jq -s 'add' 2>/dev/null || echo "$pr_list") | ||
| elif [[ -n "$pr_list" && "$pr_list" != "[]" ]]; then | ||
| all_prs="$pr_list" | ||
| elif [[ -n "$merged_pr_list" && "$merged_pr_list" != "[]" ]]; then | ||
| all_prs="$merged_pr_list" | ||
| else | ||
| return 0 | ||
| fi | ||
|
|
||
| # Match PRs to this task by task ID in title or branch name | ||
| local matched_pr_url | ||
| matched_pr_url=$(echo "$all_prs" | jq -r --arg tid "$task_id" ' | ||
| .[] | select( | ||
| (.title | test("\\b" + $tid + "\\b"; "i")) or | ||
| (.headRefName | test("\\b" + $tid + "\\b"; "i")) | ||
| ) | .url | ||
| ' 2>/dev/null | head -1 || echo "") | ||
|
|
||
| if [[ -z "$matched_pr_url" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| # Validate the PR belongs to this task | ||
| local validated_url | ||
| validated_url=$(validate_pr_belongs_to_task "$task_id" "$repo_slug" "$matched_pr_url") || validated_url="" | ||
|
|
||
| if [[ -z "$validated_url" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| # Link the PR to the task | ||
| db "$SUPERVISOR_DB" "UPDATE tasks SET pr_url = '$(sql_escape "$validated_url")' WHERE id = '$escaped_id';" 2>/dev/null || true | ||
|
|
||
| # Transition eligible tasks to complete | ||
| case "$tstatus" in | ||
| failed|blocked|retrying) | ||
| log_info " Eager scan: ORPHANED PR found for $task_id ($tstatus -> complete): $validated_url" | ||
| cmd_transition "$task_id" "complete" --pr-url "$validated_url" 2>>"$SUPERVISOR_LOG" || true | ||
| # Run post-completion hooks | ||
| update_todo_on_complete "$task_id" 2>>"$SUPERVISOR_LOG" || true | ||
| send_task_notification "$task_id" "complete" "orphaned_pr_linked:$validated_url" 2>>"$SUPERVISOR_LOG" || true | ||
| local tid_desc | ||
| tid_desc=$(db "$SUPERVISOR_DB" "SELECT description FROM tasks WHERE id = '$escaped_id';" 2>/dev/null || echo "") | ||
| store_success_pattern "$task_id" "orphaned_pr_linked_eager" "$tid_desc" 2>>"$SUPERVISOR_LOG" || true | ||
| ;; | ||
| complete) | ||
| log_info " Eager scan: Linked orphaned PR to completed task $task_id: $validated_url" | ||
| ;; | ||
| *) | ||
| log_info " Eager scan: Linked orphaned PR to $task_id ($tstatus): $validated_url" | ||
| ;; | ||
| esac | ||
|
|
||
| return 0 | ||
| } |
There was a problem hiding this comment.
Eager scan won’t complete tasks still in evaluating state.
In cmd_pulse, the eager scan runs immediately after evaluate_worker() while the task is still evaluating. Your case statement only transitions failed|blocked|retrying, so a найден PR just gets linked, then the task can still be marked retry/failed/blocked. Since pr_url is now set, the Phase 6 sweep won’t correct it. Please include evaluating as an eligible transition (or explicitly transition before scan).
🔧 Suggested fix
- case "$tstatus" in
- failed|blocked|retrying)
+ case "$tstatus" in
+ failed|blocked|retrying|evaluating)
log_info " Eager scan: ORPHANED PR found for $task_id ($tstatus -> complete): $validated_url"
cmd_transition "$task_id" "complete" --pr-url "$validated_url" 2>>"$SUPERVISOR_LOG" || true📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ####################################### | |
| # Eager orphaned PR scan for a single task (t216). | |
| # | |
| # Called immediately after worker evaluation when the outcome is | |
| # retry/failed/blocked and no PR was linked. Unlike scan_orphaned_prs() | |
| # which is a throttled batch sweep (Phase 6), this does a targeted | |
| # single-task lookup — one repo, one API call — with no throttle. | |
| # | |
| # This catches the common case where a worker created a PR but exited | |
| # without the FULL_LOOP_COMPLETE signal, and evaluate_worker()'s | |
| # fallback PR detection missed it (API timeout, non-standard branch, etc.). | |
| # | |
| # $1: task_id | |
| # | |
| # Returns 0 on success. Sets pr_url in DB and transitions task to | |
| # complete if a matching PR is found. | |
| ####################################### | |
| scan_orphaned_pr_for_task() { | |
| local task_id="$1" | |
| ensure_db | |
| local escaped_id | |
| escaped_id=$(sql_escape "$task_id") | |
| # Get task details | |
| local task_row | |
| task_row=$(db -separator '|' "$SUPERVISOR_DB" " | |
| SELECT status, repo, branch, pr_url FROM tasks | |
| WHERE id = '$escaped_id'; | |
| " 2>/dev/null || echo "") | |
| if [[ -z "$task_row" ]]; then | |
| return 0 | |
| fi | |
| local tstatus trepo tbranch tpr_url | |
| IFS='|' read -r tstatus trepo tbranch tpr_url <<< "$task_row" | |
| # Skip if PR already linked (not orphaned) | |
| if [[ -n "$tpr_url" && "$tpr_url" != "no_pr" && "$tpr_url" != "task_only" && "$tpr_url" != "task_obsolete" && "$tpr_url" != "" ]]; then | |
| return 0 | |
| fi | |
| # Need a repo to scan | |
| if [[ -z "$trepo" || ! -d "$trepo" ]]; then | |
| return 0 | |
| fi | |
| local repo_slug | |
| repo_slug=$(detect_repo_slug "$trepo" 2>/dev/null || echo "") | |
| if [[ -z "$repo_slug" ]]; then | |
| return 0 | |
| fi | |
| # Fetch open PRs for this repo (single API call) | |
| local pr_list | |
| pr_list=$(gh pr list --repo "$repo_slug" --state open --limit 100 \ | |
| --json number,title,headRefName,url 2>>"$SUPERVISOR_LOG" || echo "") | |
| # Also check recently merged PRs | |
| local merged_pr_list | |
| merged_pr_list=$(gh pr list --repo "$repo_slug" --state merged --limit 50 \ | |
| --json number,title,headRefName,url 2>>"$SUPERVISOR_LOG" || echo "") | |
| # Combine open and merged PR lists | |
| local all_prs | |
| if [[ -n "$merged_pr_list" && "$merged_pr_list" != "[]" && -n "$pr_list" && "$pr_list" != "[]" ]]; then | |
| all_prs=$(echo "$pr_list" "$merged_pr_list" | jq -s 'add' 2>/dev/null || echo "$pr_list") | |
| elif [[ -n "$pr_list" && "$pr_list" != "[]" ]]; then | |
| all_prs="$pr_list" | |
| elif [[ -n "$merged_pr_list" && "$merged_pr_list" != "[]" ]]; then | |
| all_prs="$merged_pr_list" | |
| else | |
| return 0 | |
| fi | |
| # Match PRs to this task by task ID in title or branch name | |
| local matched_pr_url | |
| matched_pr_url=$(echo "$all_prs" | jq -r --arg tid "$task_id" ' | |
| .[] | select( | |
| (.title | test("\\b" + $tid + "\\b"; "i")) or | |
| (.headRefName | test("\\b" + $tid + "\\b"; "i")) | |
| ) | .url | |
| ' 2>/dev/null | head -1 || echo "") | |
| if [[ -z "$matched_pr_url" ]]; then | |
| return 0 | |
| fi | |
| # Validate the PR belongs to this task | |
| local validated_url | |
| validated_url=$(validate_pr_belongs_to_task "$task_id" "$repo_slug" "$matched_pr_url") || validated_url="" | |
| if [[ -z "$validated_url" ]]; then | |
| return 0 | |
| fi | |
| # Link the PR to the task | |
| db "$SUPERVISOR_DB" "UPDATE tasks SET pr_url = '$(sql_escape "$validated_url")' WHERE id = '$escaped_id';" 2>/dev/null || true | |
| # Transition eligible tasks to complete | |
| case "$tstatus" in | |
| failed|blocked|retrying) | |
| log_info " Eager scan: ORPHANED PR found for $task_id ($tstatus -> complete): $validated_url" | |
| cmd_transition "$task_id" "complete" --pr-url "$validated_url" 2>>"$SUPERVISOR_LOG" || true | |
| # Run post-completion hooks | |
| update_todo_on_complete "$task_id" 2>>"$SUPERVISOR_LOG" || true | |
| send_task_notification "$task_id" "complete" "orphaned_pr_linked:$validated_url" 2>>"$SUPERVISOR_LOG" || true | |
| local tid_desc | |
| tid_desc=$(db "$SUPERVISOR_DB" "SELECT description FROM tasks WHERE id = '$escaped_id';" 2>/dev/null || echo "") | |
| store_success_pattern "$task_id" "orphaned_pr_linked_eager" "$tid_desc" 2>>"$SUPERVISOR_LOG" || true | |
| ;; | |
| complete) | |
| log_info " Eager scan: Linked orphaned PR to completed task $task_id: $validated_url" | |
| ;; | |
| *) | |
| log_info " Eager scan: Linked orphaned PR to $task_id ($tstatus): $validated_url" | |
| ;; | |
| esac | |
| return 0 | |
| } | |
| ####################################### | |
| # Eager orphaned PR scan for a single task (t216). | |
| # | |
| # Called immediately after worker evaluation when the outcome is | |
| # retry/failed/blocked and no PR was linked. Unlike scan_orphaned_prs() | |
| # which is a throttled batch sweep (Phase 6), this does a targeted | |
| # single-task lookup — one repo, one API call — with no throttle. | |
| # | |
| # This catches the common case where a worker created a PR but exited | |
| # without the FULL_LOOP_COMPLETE signal, and evaluate_worker()'s | |
| # fallback PR detection missed it (API timeout, non-standard branch, etc.). | |
| # | |
| # $1: task_id | |
| # | |
| # Returns 0 on success. Sets pr_url in DB and transitions task to | |
| # complete if a matching PR is found. | |
| ####################################### | |
| scan_orphaned_pr_for_task() { | |
| local task_id="$1" | |
| ensure_db | |
| local escaped_id | |
| escaped_id=$(sql_escape "$task_id") | |
| # Get task details | |
| local task_row | |
| task_row=$(db -separator '|' "$SUPERVISOR_DB" " | |
| SELECT status, repo, branch, pr_url FROM tasks | |
| WHERE id = '$escaped_id'; | |
| " 2>/dev/null || echo "") | |
| if [[ -z "$task_row" ]]; then | |
| return 0 | |
| fi | |
| local tstatus trepo tbranch tpr_url | |
| IFS='|' read -r tstatus trepo tbranch tpr_url <<< "$task_row" | |
| # Skip if PR already linked (not orphaned) | |
| if [[ -n "$tpr_url" && "$tpr_url" != "no_pr" && "$tpr_url" != "task_only" && "$tpr_url" != "task_obsolete" && "$tpr_url" != "" ]]; then | |
| return 0 | |
| fi | |
| # Need a repo to scan | |
| if [[ -z "$trepo" || ! -d "$trepo" ]]; then | |
| return 0 | |
| fi | |
| local repo_slug | |
| repo_slug=$(detect_repo_slug "$trepo" 2>/dev/null || echo "") | |
| if [[ -z "$repo_slug" ]]; then | |
| return 0 | |
| fi | |
| # Fetch open PRs for this repo (single API call) | |
| local pr_list | |
| pr_list=$(gh pr list --repo "$repo_slug" --state open --limit 100 \ | |
| --json number,title,headRefName,url 2>>"$SUPERVISOR_LOG" || echo "") | |
| # Also check recently merged PRs | |
| local merged_pr_list | |
| merged_pr_list=$(gh pr list --repo "$repo_slug" --state merged --limit 50 \ | |
| --json number,title,headRefName,url 2>>"$SUPERVISOR_LOG" || echo "") | |
| # Combine open and merged PR lists | |
| local all_prs | |
| if [[ -n "$merged_pr_list" && "$merged_pr_list" != "[]" && -n "$pr_list" && "$pr_list" != "[]" ]]; then | |
| all_prs=$(echo "$pr_list" "$merged_pr_list" | jq -s 'add' 2>/dev/null || echo "$pr_list") | |
| elif [[ -n "$pr_list" && "$pr_list" != "[]" ]]; then | |
| all_prs="$pr_list" | |
| elif [[ -n "$merged_pr_list" && "$merged_pr_list" != "[]" ]]; then | |
| all_prs="$merged_pr_list" | |
| else | |
| return 0 | |
| fi | |
| # Match PRs to this task by task ID in title or branch name | |
| local matched_pr_url | |
| matched_pr_url=$(echo "$all_prs" | jq -r --arg tid "$task_id" ' | |
| .[] | select( | |
| (.title | test("\\b" + $tid + "\\b"; "i")) or | |
| (.headRefName | test("\\b" + $tid + "\\b"; "i")) | |
| ) | .url | |
| ' 2>/dev/null | head -1 || echo "") | |
| if [[ -z "$matched_pr_url" ]]; then | |
| return 0 | |
| fi | |
| # Validate the PR belongs to this task | |
| local validated_url | |
| validated_url=$(validate_pr_belongs_to_task "$task_id" "$repo_slug" "$matched_pr_url") || validated_url="" | |
| if [[ -z "$validated_url" ]]; then | |
| return 0 | |
| fi | |
| # Link the PR to the task | |
| db "$SUPERVISOR_DB" "UPDATE tasks SET pr_url = '$(sql_escape "$validated_url")' WHERE id = '$escaped_id';" 2>/dev/null || true | |
| # Transition eligible tasks to complete | |
| case "$tstatus" in | |
| failed|blocked|retrying|evaluating) | |
| log_info " Eager scan: ORPHANED PR found for $task_id ($tstatus -> complete): $validated_url" | |
| cmd_transition "$task_id" "complete" --pr-url "$validated_url" 2>>"$SUPERVISOR_LOG" || true | |
| # Run post-completion hooks | |
| update_todo_on_complete "$task_id" 2>>"$SUPERVISOR_LOG" || true | |
| send_task_notification "$task_id" "complete" "orphaned_pr_linked:$validated_url" 2>>"$SUPERVISOR_LOG" || true | |
| local tid_desc | |
| tid_desc=$(db "$SUPERVISOR_DB" "SELECT description FROM tasks WHERE id = '$escaped_id';" 2>/dev/null || echo "") | |
| store_success_pattern "$task_id" "orphaned_pr_linked_eager" "$tid_desc" 2>>"$SUPERVISOR_LOG" || true | |
| ;; | |
| complete) | |
| log_info " Eager scan: Linked orphaned PR to completed task $task_id: $validated_url" | |
| ;; | |
| *) | |
| log_info " Eager scan: Linked orphaned PR to $task_id ($tstatus): $validated_url" | |
| ;; | |
| esac | |
| return 0 | |
| } |
🤖 Prompt for AI Agents
In @.agents/scripts/supervisor-helper.sh around lines 6065 - 6187, The eager
orphaned-PR scan (scan_orphaned_pr_for_task) links PRs while tasks can still be
in status evaluating, but the case in scan_orphaned_pr_for_task only transitions
failed|blocked|retrying to complete; update that case to also treat evaluating
as eligible for automatic transition to complete (i.e., add evaluating to the
first case pattern that logs and calls
cmd_transition/update_todo_on_complete/send_task_notification/store_success_pattern),
or alternatively ensure cmd_pulse moves the task out of evaluating before
calling scan_orphaned_pr_for_task so the existing case will apply.
Bot review dismissed - all CI checks pass
Add scan_orphaned_pr_for_task() — a targeted single-task orphaned PR scanner that runs immediately after evaluate_worker() returns a non-complete outcome in Phase 1 of cmd_pulse(). Previously, orphaned PR detection only ran in Phase 6 as a throttled batch sweep (every 10 minutes). Workers that created PRs but exited without FULL_LOOP_COMPLETE would sit in retry/failed/blocked state until the next Phase 6 run passed the throttle check. Now the flow is: 1. evaluate_worker() returns retry/failed/blocked 2. scan_orphaned_pr_for_task() immediately checks GitHub for a PR matching the task ID in title or branch name 3. If found, transitions the task to complete (skipping retry/fail) 4. Phase 6 broad sweep remains as a safety net for edge cases The eager scan makes 1-2 gh API calls per evaluated task (open + merged PRs), which is acceptable since it only fires when a worker finishes — not on every pulse for every task.
17a9bda to
bc6151f
Compare
🔍 Code Quality Report�[0;35m[MONITOR]�[0m Code Review Monitoring Report �[0;34m[INFO]�[0m Latest Quality Status: �[0;34m[INFO]�[0m Recent monitoring activity: 📈 Current Quality Metrics
Generated on: Tue Feb 10 12:39:45 UTC 2026 Generated by AI DevOps Framework Code Review Monitoring |
|



Summary
scan_orphaned_pr_for_task()— a targeted single-task orphaned PR scanner that runs immediately afterevaluate_worker()returns a non-complete outcome in Phase 1 ofcmd_pulse()scan_orphaned_prs()) remains as a safety net for edge cases (Phase 4b DB orphans, pre-existing failed tasks)Changes
New function:
scan_orphaned_pr_for_task()validate_pr_belongs_to_task()before linkingorphaned_pr_linked_eagersuccess pattern for trackingPhase 1 integration
evaluate_worker()returns a non-complete outcome, immediately callsscan_orphaned_pr_for_task()continuePhase 6 updates
Testing
bash -nsyntax check passestbranch, matching existing pattern inscan_orphaned_prs())Summary by CodeRabbit
New Features
Chores