Skip to content

Comments

feat: add Phase 3b2 reconciliation and triage command to supervisor#1488

Merged
marcusquinn merged 2 commits intomainfrom
feature/supervisor-queue-triage
Feb 15, 2026
Merged

feat: add Phase 3b2 reconciliation and triage command to supervisor#1488
marcusquinn merged 2 commits intomainfrom
feature/supervisor-queue-triage

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 15, 2026

Summary

  • Adds Phase 3b2 to the supervisor pulse cycle: automatically reconciles blocked/verify_failed tasks whose PRs have been merged on GitHub, advancing them to deployed state
  • Adds triage command (supervisor-helper.sh triage [--dry-run] [--auto-resolve]) for interactive bulk diagnosis and resolution of stuck tasks
  • Handles 5 distinct stuck-task scenarios: merged-but-stuck, closed-without-merge, obsolete PR URLs, rebase-exhausted, and unreachable PRs

Root Cause

The supervisor had no mechanism to detect when a PR was merged externally (by a later pulse cycle or manual action) while the task was stuck in blocked or verify_failed state. This caused 27 tasks to accumulate as stuck despite their PRs being successfully merged.

Changes

Phase 3b2 (automatic, runs every pulse)

  • Queries all blocked/verify_failed tasks with PR URLs
  • Checks actual PR state on GitHub via gh pr view
  • MERGED: Direct DB update to deployed (bypasses state machine since ground truth is verified), cleans up worktree, updates TODO.md, syncs GitHub issue labels
  • CLOSED without merge: Resets to queued with clean slate for re-dispatch
  • Obsolete/sentinel PR URLs (task_obsolete): Cancels the task

Triage command (manual, on-demand)

  • Categorizes all stuck tasks by root cause with a readable report
  • --dry-run: Preview only, no changes
  • --auto-resolve: Automatically fixes resolvable categories
  • Categories: merged-but-stuck, closed-no-merge, obsolete, rebase-exhausted, open-pr, no-pr

Verification

Tested against the live queue:

  • Before: 22 blocked, 5 verify_failed = 27 stuck tasks
  • After: 0 blocked, 0 verify_failed, 24 advanced to deployed, 2 reset to queued, 1 cancelled
  • triage --dry-run now reports "No stuck tasks found — queue is healthy"
  • ShellCheck clean (zero new violations)

Summary by CodeRabbit

  • New Features
    • Added triage subcommand to diagnose and resolve stuck tasks in bulk, with support for dry-run preview and automatic resolution modes.
    • Added automatic reconciliation of task states based on GitHub PR status, advancing or resetting tasks based on merge, closed, or open states.

Add automatic reconciliation of stuck blocked/verify_failed tasks:

- Phase 3b2: runs every pulse, checks blocked/verify_failed tasks against
  GitHub PR state. If PR is merged, advances to deployed via direct DB
  update (bypasses state machine since ground truth is verified). If PR
  is closed without merge, resets to queued for re-dispatch. Handles
  obsolete/sentinel PR URLs by cancelling.

- triage command: interactive bulk diagnosis and resolution. Categorizes
  all stuck tasks by root cause (merged-but-stuck, closed-no-merge,
  obsolete, rebase-exhausted, open-pr, no-pr). Supports --dry-run for
  preview and --auto-resolve for automated fix.

Root cause: the supervisor's pulse cycle had no mechanism to detect when
a PR was merged externally (by a later pulse or manual action) while the
task was stuck in blocked/verify_failed state. 27 tasks were stuck with
merged PRs that the DB didn't know about.
@gemini-code-assist
Copy link

Summary of Changes

Hello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where supervisor tasks could become stuck in 'blocked' or 'verify_failed' states even after their corresponding GitHub Pull Requests were merged or closed externally. This led to an accumulation of unresolved tasks. The changes introduce an automatic reconciliation phase (Phase 3b2) within the supervisor's pulse cycle to detect and correct these discrepancies, ensuring tasks accurately reflect their PR status. Additionally, a new 'triage' command is provided to enable manual, interactive diagnosis and resolution of various stuck task scenarios, significantly improving the supervisor's robustness and maintainability.

Highlights

  • Automatic PR Reconciliation: Introduced Phase 3b2 to automatically reconcile 'blocked' or 'verify_failed' tasks whose associated GitHub Pull Requests have been merged, transitioning them to a 'deployed' state.
  • New Triage Command: Added a 'supervisor-helper.sh triage' command for interactive diagnosis and bulk resolution of stuck tasks, supporting dry-run and auto-resolve options.
  • Comprehensive Stuck Task Handling: Implemented logic to address five distinct scenarios for stuck tasks: merged-but-stuck, closed-without-merge, obsolete PR URLs, rebase-exhausted, and unreachable PRs.
Changelog
  • .agents/scripts/supervisor-helper.sh
    • Added 'triage' command to the usage instructions.
    • Integrated 'cmd_triage' into the main command dispatch logic.
  • .agents/scripts/supervisor/pulse.sh
    • Introduced 'Phase 3b2' for reconciling 'blocked' and 'verify_failed' tasks with GitHub PR states.
    • Implemented logic to cancel tasks with obsolete PR URLs.
    • Added logic to advance tasks to 'deployed' if their PRs are merged on GitHub, including database updates, worktree cleanup, TODO.md updates, and GitHub issue label syncing.
    • Included logic to reset tasks to 'queued' if their PRs are closed without merging.
    • Defined the 'cmd_triage' function, which categorizes stuck tasks, supports dry-run and auto-resolve, and performs resolutions for merged-but-stuck, closed-no-merge, and obsolete PR tasks.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

Warning

Rate limit exceeded

@marcusquinn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 40 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

The changes add triage capability to the supervisor system by introducing a Phase 3b2 reconciliation step that validates task status against GitHub PR state, and a cmd_triage() command that diagnoses and resolves stuck tasks through automatic state transitions.

Changes

Cohort / File(s) Summary
Triage Command Wiring
.agents/scripts/supervisor-helper.sh
Added triage subcommand support with help text and case routing to invoke triage functionality from the main supervisor helper.
Triage Implementation & PR Reconciliation
.agents/scripts/supervisor/pulse.sh
Added Phase 3b2 reconciliation step that queries GitHub PR state for blocked/verify_failed tasks and updates task states accordingly (MERGED → deployed, CLOSED → queued, OPEN → no action). Added cmd_triage() command supporting --dry-run and --auto-resolve flags to bulk diagnose and auto-resolve stuck tasks (merged-but-stuck, closed-no-merge, obsolete-pr, rebase-exhausted, open-pr, no-pr) with database and log updates.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Helper as supervisor-helper.sh
    participant Pulse as pulse.sh
    participant DB as Task DB
    participant GitHub as GitHub API
    
    User->>Helper: supervisor-helper.sh triage [--dry-run/--auto-resolve]
    activate Helper
    Helper->>Pulse: cmd_triage()
    activate Pulse
    
    Pulse->>DB: Query blocked/verify_failed tasks
    DB-->>Pulse: Task list with PR URLs
    
    Pulse->>GitHub: gh pr view for each task PR
    GitHub-->>Pulse: PR state (MERGED/CLOSED/OPEN)
    
    Pulse->>Pulse: Categorize tasks by state & status
    Pulse-->>Helper: Print diagnostic report
    
    alt --auto-resolve flag
        Pulse->>DB: Update task states (deployed/queued/cancel)
        Pulse->>DB: Update state logs & cleanup records
        DB-->>Pulse: Confirmation
        Pulse->>GitHub: Update issue status if needed
    end
    
    deactivate Pulse
    deactivate Helper
    Helper-->>User: Report & resolution summary
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🔍 A triage arrives on swift GitHub wings,
Reconciling tasks with PR state things,
Stuck states dissolve when truth's unfurled,
Deployed or queued, back to the world! 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (5 files):

⚔️ .agents/plugins/opencode-aidevops/index.mjs (content)
⚔️ .agents/scripts/supervisor-helper.sh (content)
⚔️ .agents/scripts/supervisor/pulse.sh (content)
⚔️ MODELS.md (content)
⚔️ TODO.md (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add Phase 3b2 reconciliation and triage command to supervisor' directly and specifically summarizes the main changes: adding Phase 3b2 (reconciliation logic) and triage command functionality to the supervisor module.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/supervisor-queue-triage

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 16 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 15 02:15:52 UTC 2026: Code review monitoring started
Sun Feb 15 02:15:53 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 16

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 16
  • VULNERABILITIES: 0

Generated on: Sun Feb 15 02:15:55 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @.agents/scripts/supervisor/pulse.sh:
- Around line 1912-1962: The gh CLI call in the while-loop uses gh pr view
"$pr_num" but ignores the repo captured in _trepo; update the call to include
the repository by passing --repo "$_trepo" (i.e., change pr_json=$(gh pr view
"$pr_num" --json state,mergedAt ...) to pr_json=$(gh pr view --repo "$_trepo"
"$pr_num" --json state,mergedAt ...) ), ensuring the loop's read fields (_trepo)
are actually used when querying PR info and keeping the existing error
handling/fallback unchanged.
- Around line 557-579: The gh PR lookup omits repository context and currently
calls gh pr view "$pr_number" which can target the wrong repo; update the
read/variables so the repo from the input is preserved (use stale_repo instead
of _stale_repo) and call gh pr view with the --repo flag (e.g., gh pr view
"$pr_number" --repo "$stale_repo" --json state,mergedAt ...) so the PR is
queried in the correct repository; keep the existing fallback behavior for
unreachable PRs.
- Around line 616-638: The CLOSED branch currently updates tasks directly
without recording the transition in state_log; add a state_log INSERT when
resetting the task to queued (similar to the MERGED path) immediately after the
DB UPDATE that sets status='queued'. Use the same escaped_stale_id used for the
UPDATE, record previous status ($stale_status) and new status 'queued', include
relevant metadata (actor like "pulse:phase_3b2" and a reason such as "PR closed
without merge"), and ensure this runs before cleanup_after_merge/write_proof_log
and that failures do not block the rest of the branch (match surrounding || true
behavior).
- Around line 565-571: The current logic cancels tasks when pr_number is empty
because the regex `/pull/([0-9]+)$` is too strict; update the extraction around
pr_number/stale_pr to be more tolerant (e.g., strip trailing slashes and query
strings, or use a regex that finds the first numeric ID after "/pull/" anywhere
in the path) and, if still not found, log a warning with stale_pr via log_warn
and skip the cancel path (increment reconciled_obsolete as needed and continue)
instead of calling cmd_transition "$stale_id" "cancelled" --error "..."; keep
the existing log message but remove the cancellation command so tasks with
slightly different PR URL formats are not prematurely cancelled.
- Around line 1903-1962: The category builders (cat_merged_stuck,
cat_closed_no_merge, cat_obsolete, cat_rebase_exhausted, cat_no_pr, cat_open_pr)
currently concatenate entries with literal "\n" and later rely on echo -e, which
misinterprets backslash sequences in error text; change each of those string
variables into an array (e.g., cat_no_pr -> cat_no_pr_array) and push entries
into the array instead of concatenating strings inside the while loop that reads
stuck_tasks (refer to the while loop and the variables cat_merged_stuck,
cat_closed_no_merge, cat_obsolete, cat_rebase_exhausted, cat_no_pr,
cat_open_pr); when you need to emit the lists, use printf '%s\n'
"${cat_no_pr_array[@]}" (and similarly for the other arrays) so entries are
printed exactly without interpreting backslash escapes, and update any
downstream consumers that expected newline-joined strings to accept the printed
output or the array form.
🧹 Nitpick comments (2)
.agents/scripts/supervisor/pulse.sh (2)

2002-2055: Duplicated resolution logic between Phase 3b2 and cmd_triage --auto-resolve.

The merged→deployed DB update (lines 2011-2019 vs 592-597), closed→queued reset (lines 2035-2040 vs 622-631), and obsolete cancellation patterns are near-identical copies. Consider extracting shared helpers (e.g., _resolve_merged_task, _reset_closed_task) to keep them in sync and reduce maintenance burden.


1955-1959: Hardcoded rebase threshold of 3 should reference max_retry_cycles.

Phase 3.5 (line 730) defines local max_retry_cycles=3 for the same threshold. The triage command hardcodes 3 directly, so if the threshold changes in Phase 3.5, triage categorization will drift silently. Consider extracting this to a shared constant or at minimum adding a comment noting the dependency.

Comment on lines +557 to +579
while IFS='|' read -r stale_id stale_status stale_pr _stale_repo; do
[[ -z "$stale_id" ]] && continue

# Extract PR number from URL
local pr_number=""
if [[ "$stale_pr" =~ /pull/([0-9]+)$ ]]; then
pr_number="${BASH_REMATCH[1]}"
fi
if [[ -z "$pr_number" ]]; then
# Non-standard PR URL or obsolete marker — mark as cancelled
log_warn " Phase 3b2: $stale_id has non-parseable PR URL '$stale_pr' — cancelling"
cmd_transition "$stale_id" "cancelled" --error "PR URL not parseable: $stale_pr" 2>>"$SUPERVISOR_LOG" || true
reconciled_obsolete=$((reconciled_obsolete + 1))
continue
fi

# Query GitHub for actual PR state
local pr_json
pr_json=$(gh pr view "$pr_number" --json state,mergedAt 2>/dev/null || echo "")
if [[ -z "$pr_json" ]]; then
log_warn " Phase 3b2: $stale_id PR #$pr_number unreachable — skipping"
continue
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

gh pr view missing --repo flag — will query wrong repo in multi-repo setups.

The SQL query selects repo but it's assigned to the unused _stale_repo variable. Without --repo, gh pr view uses the current working directory's remote, which may not match the task's repo. This could produce incorrect PR state lookups or silent failures.

🔧 Proposed fix: pass repo context to `gh pr view`
-			while IFS='|' read -r stale_id stale_status stale_pr _stale_repo; do
+			while IFS='|' read -r stale_id stale_status stale_pr stale_repo; do
 				[[ -z "$stale_id" ]] && continue
 
 				# Extract PR number from URL
 				local pr_number=""
 				if [[ "$stale_pr" =~ /pull/([0-9]+)$ ]]; then
 					pr_number="${BASH_REMATCH[1]}"
 				fi
 				if [[ -z "$pr_number" ]]; then
 					# Non-standard PR URL or obsolete marker — mark as cancelled
 					log_warn "  Phase 3b2: $stale_id has non-parseable PR URL '$stale_pr' — cancelling"
 					cmd_transition "$stale_id" "cancelled" --error "PR URL not parseable: $stale_pr" 2>>"$SUPERVISOR_LOG" || true
 					reconciled_obsolete=$((reconciled_obsolete + 1))
 					continue
 				fi
 
 				# Query GitHub for actual PR state
+				local repo_slug=""
+				repo_slug=$(detect_repo_slug "${stale_repo:-.}" 2>/dev/null || echo "")
 				local pr_json
-				pr_json=$(gh pr view "$pr_number" --json state,mergedAt 2>/dev/null || echo "")
+				pr_json=$(gh pr view "$pr_number" ${repo_slug:+--repo "$repo_slug"} --json state,mergedAt 2>/dev/null || echo "")

As per coding guidelines, .agents/scripts/*.sh: "Reliability and robustness".

🤖 Prompt for AI Agents
In @.agents/scripts/supervisor/pulse.sh around lines 557 - 579, The gh PR lookup
omits repository context and currently calls gh pr view "$pr_number" which can
target the wrong repo; update the read/variables so the repo from the input is
preserved (use stale_repo instead of _stale_repo) and call gh pr view with the
--repo flag (e.g., gh pr view "$pr_number" --repo "$stale_repo" --json
state,mergedAt ...) so the PR is queried in the correct repository; keep the
existing fallback behavior for unreachable PRs.

Comment on lines +1903 to +1962
# Categorize
local cat_merged_stuck=""
local cat_closed_no_merge=""
local cat_obsolete=""
local cat_rebase_exhausted=""
local cat_no_pr=""
local cat_open_pr=""
local total_stuck=0

while IFS='|' read -r tid tstatus tpr terror _trepo trebase; do
[[ -z "$tid" ]] && continue
total_stuck=$((total_stuck + 1))

# No PR or sentinel
if [[ -z "$tpr" || "$tpr" == "no_pr" || "$tpr" == "task_only" ]]; then
cat_no_pr="${cat_no_pr}${tid}|${tstatus}|${terror}\n"
continue
fi
if [[ "$tpr" == "task_obsolete" ]]; then
cat_obsolete="${cat_obsolete}${tid}|${tstatus}|${tpr}\n"
continue
fi

# Extract PR number
local pr_num=""
if [[ "$tpr" =~ /pull/([0-9]+)$ ]]; then
pr_num="${BASH_REMATCH[1]}"
fi
if [[ -z "$pr_num" ]]; then
cat_obsolete="${cat_obsolete}${tid}|${tstatus}|unparseable:${tpr}\n"
continue
fi

# Query GitHub
local pr_json
pr_json=$(gh pr view "$pr_num" --json state,mergedAt 2>/dev/null || echo "")
if [[ -z "$pr_json" ]]; then
cat_no_pr="${cat_no_pr}${tid}|${tstatus}|PR #${pr_num} unreachable\n"
continue
fi

local pr_state
pr_state=$(echo "$pr_json" | grep -o '"state":"[^"]*"' | cut -d'"' -f4)

case "$pr_state" in
MERGED)
cat_merged_stuck="${cat_merged_stuck}${tid}|${tstatus}|PR #${pr_num} MERGED\n"
;;
CLOSED)
cat_closed_no_merge="${cat_closed_no_merge}${tid}|${tstatus}|PR #${pr_num} CLOSED\n"
;;
OPEN)
if [[ "${trebase:-0}" -ge 3 ]]; then
cat_rebase_exhausted="${cat_rebase_exhausted}${tid}|${tstatus}|PR #${pr_num} rebase_attempts=${trebase}\n"
else
cat_open_pr="${cat_open_pr}${tid}|${tstatus}|PR #${pr_num} OPEN (${terror})\n"
fi
;;
esac
done <<<"$stuck_tasks"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Building category lists with \n string concatenation + echo -e is fragile.

If any task error message contains backslash sequences (e.g., \n, \t, \\), echo -e on lines 2024/2043/2055 will interpret them, corrupting the output or breaking the read loop. This is a real risk since error messages can contain arbitrary text.

💡 Safer alternative: use arrays

Replace the string concatenation pattern with arrays:

-	local cat_merged_stuck=""
+	local -a cat_merged_stuck=()
 ...
-			cat_merged_stuck="${cat_merged_stuck}${tid}|${tstatus}|PR #${pr_num} MERGED\n"
+			cat_merged_stuck+=("${tid}|${tstatus}|PR #${pr_num} MERGED")
 ...
 	# In auto-resolve loop:
-	done <<<"$(echo -e "$cat_merged_stuck")"
+	done < <(printf '%s\n' "${cat_merged_stuck[@]}")

This avoids interpreting backslash sequences in error messages.

🤖 Prompt for AI Agents
In @.agents/scripts/supervisor/pulse.sh around lines 1903 - 1962, The category
builders (cat_merged_stuck, cat_closed_no_merge, cat_obsolete,
cat_rebase_exhausted, cat_no_pr, cat_open_pr) currently concatenate entries with
literal "\n" and later rely on echo -e, which misinterprets backslash sequences
in error text; change each of those string variables into an array (e.g.,
cat_no_pr -> cat_no_pr_array) and push entries into the array instead of
concatenating strings inside the while loop that reads stuck_tasks (refer to the
while loop and the variables cat_merged_stuck, cat_closed_no_merge,
cat_obsolete, cat_rebase_exhausted, cat_no_pr, cat_open_pr); when you need to
emit the lists, use printf '%s\n' "${cat_no_pr_array[@]}" (and similarly for the
other arrays) so entries are printed exactly without interpreting backslash
escapes, and update any downstream consumers that expected newline-joined
strings to accept the printed output or the array form.

Comment on lines +1912 to +1962
while IFS='|' read -r tid tstatus tpr terror _trepo trebase; do
[[ -z "$tid" ]] && continue
total_stuck=$((total_stuck + 1))

# No PR or sentinel
if [[ -z "$tpr" || "$tpr" == "no_pr" || "$tpr" == "task_only" ]]; then
cat_no_pr="${cat_no_pr}${tid}|${tstatus}|${terror}\n"
continue
fi
if [[ "$tpr" == "task_obsolete" ]]; then
cat_obsolete="${cat_obsolete}${tid}|${tstatus}|${tpr}\n"
continue
fi

# Extract PR number
local pr_num=""
if [[ "$tpr" =~ /pull/([0-9]+)$ ]]; then
pr_num="${BASH_REMATCH[1]}"
fi
if [[ -z "$pr_num" ]]; then
cat_obsolete="${cat_obsolete}${tid}|${tstatus}|unparseable:${tpr}\n"
continue
fi

# Query GitHub
local pr_json
pr_json=$(gh pr view "$pr_num" --json state,mergedAt 2>/dev/null || echo "")
if [[ -z "$pr_json" ]]; then
cat_no_pr="${cat_no_pr}${tid}|${tstatus}|PR #${pr_num} unreachable\n"
continue
fi

local pr_state
pr_state=$(echo "$pr_json" | grep -o '"state":"[^"]*"' | cut -d'"' -f4)

case "$pr_state" in
MERGED)
cat_merged_stuck="${cat_merged_stuck}${tid}|${tstatus}|PR #${pr_num} MERGED\n"
;;
CLOSED)
cat_closed_no_merge="${cat_closed_no_merge}${tid}|${tstatus}|PR #${pr_num} CLOSED\n"
;;
OPEN)
if [[ "${trebase:-0}" -ge 3 ]]; then
cat_rebase_exhausted="${cat_rebase_exhausted}${tid}|${tstatus}|PR #${pr_num} rebase_attempts=${trebase}\n"
else
cat_open_pr="${cat_open_pr}${tid}|${tstatus}|PR #${pr_num} OPEN (${terror})\n"
fi
;;
esac
done <<<"$stuck_tasks"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same missing --repo flag on gh pr view as in Phase 3b2.

Line 1938 calls gh pr view "$pr_num" without --repo, and the repo column is captured as _trepo (unused). Apply the same fix as recommended for Phase 3b2.

🤖 Prompt for AI Agents
In @.agents/scripts/supervisor/pulse.sh around lines 1912 - 1962, The gh CLI
call in the while-loop uses gh pr view "$pr_num" but ignores the repo captured
in _trepo; update the call to include the repository by passing --repo "$_trepo"
(i.e., change pr_json=$(gh pr view "$pr_num" --json state,mergedAt ...) to
pr_json=$(gh pr view --repo "$_trepo" "$pr_num" --json state,mergedAt ...) ),
ensuring the loop's read fields (_trepo) are actually used when querying PR info
and keeping the existing error handling/fallback unchanged.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Phase 3b2 to the supervisor pulse cycle, enabling automatic reconciliation of stuck tasks against their actual GitHub PR state. It also adds a comprehensive triage command for manual diagnosis and resolution of queue health issues. The changes significantly improve the framework's ability to self-heal from external PR merges or closures. My review focuses on adherence to the aidevops Shell Script Style Guide, specifically regarding variable declaration safety, function return statements, and secure handling of dynamic data in reports. While the logic is sound and addresses a critical gap in the supervisor's state management, several violations of the repository's strict shell standards (Rules 11 and 12) were identified and should be corrected to ensure robustness and consistency.

Comment on lines +553 to +555
local reconciled_merged=0
local reconciled_closed=0
local reconciled_obsolete=0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Variable declaration and assignment should be separate to ensure that the exit code of the assignment (or a subshell) does not mask the declaration's success, per Rule 11 of the style guide.

Suggested change
local reconciled_merged=0
local reconciled_closed=0
local reconciled_obsolete=0
local reconciled_merged
local reconciled_closed
local reconciled_obsolete
reconciled_merged=0
reconciled_closed=0
reconciled_obsolete=0
References
  1. Use local var="$1" pattern in functions (declare and assign separately for exit code safety) (link)

[[ -z "$stale_id" ]] && continue

# Extract PR number from URL
local pr_number=""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Separate declaration and assignment for local variables as required by Rule 11.

Suggested change
local pr_number=""
local pr_number
pr_number=""
References
  1. Use local var="$1" pattern in functions (declare and assign separately for exit code safety) (link)

Comment on lines +582 to +583
pr_state=$(echo "$pr_json" | grep -o '"state":"[^"]*"' | cut -d'"' -f4)
pr_merged_at=$(echo "$pr_json" | grep -o '"mergedAt":"[^"]*"' | cut -d'"' -f4)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Parsing JSON with grep and cut is brittle and prone to failure if the JSON structure or whitespace changes. Since the gh CLI is a dependency here, it is highly recommended to use jq for robust JSON parsing if available in the environment.

Comment on lines +592 to +597
db "$SUPERVISOR_DB" "UPDATE tasks SET
status = 'deployed',
error = NULL,
completed_at = strftime('%Y-%m-%dT%H:%M:%SZ','now'),
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ','now')
WHERE id = '$escaped_stale_id';" 2>/dev/null || true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Bypassing the state machine with direct DB updates can lead to inconsistencies and bypasses future logic added to cmd_transition. While the 'ground truth' argument is understood, it is better to update the state machine to support these transitions or use a 'force' flag with cmd_transition.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 16 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 15 02:29:22 UTC 2026: Code review monitoring started
Sun Feb 15 02:29:23 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 16

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 16
  • VULNERABILITIES: 0

Generated on: Sun Feb 15 02:29:25 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant