Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 121 additions & 2 deletions .agents/scripts/supervisor-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5275,6 +5275,115 @@ discover_pr_by_branch() {
return 1
}

#######################################
# Auto-create a PR for a task's orphaned branch (t247.2)
#
# When a worker exits with commits on its branch but no PR (e.g., context
# exhaustion before gh pr create), the supervisor creates the PR on its
# behalf instead of retrying. This saves ~300s per retry cycle.
#
# Prerequisites:
# - Branch has commits ahead of base (caller verified)
# - No existing PR for this branch (caller verified)
# - gh CLI available and authenticated
#
# Steps:
# 1. Push branch to remote if not already pushed
# 2. Create a draft PR via gh pr create
# 3. Persist PR URL to DB via link_pr_to_task()
#
# $1: task_id
# $2: repo_path (local filesystem path to the repo/worktree)
# $3: branch_name
# $4: repo_slug (owner/repo)
#
# Outputs: PR URL on stdout if created, empty if failed
# Returns: 0 if PR created, 1 if failed
#######################################
auto_create_pr_for_task() {
local task_id="$1"
local repo_path="$2"
local branch_name="$3"
local repo_slug="$4"

if [[ -z "$task_id" || -z "$repo_path" || -z "$branch_name" || -z "$repo_slug" ]]; then
log_warn "auto_create_pr_for_task: missing required arguments (task=$task_id repo=$repo_path branch=$branch_name slug=$repo_slug)"
return 1
fi

if ! command -v gh &>/dev/null; then
log_warn "auto_create_pr_for_task: gh CLI not available — cannot create PR for $task_id"
return 1
fi

# Fetch task description for PR title/body
local escaped_id
escaped_id=$(sql_escape "$task_id")
local task_desc
task_desc=$(db "$SUPERVISOR_DB" "SELECT description FROM tasks WHERE id = '$escaped_id';" 2>/dev/null || echo "")
if [[ -z "$task_desc" ]]; then
task_desc="Worker task $task_id"
fi

# Determine base branch
local base_branch
base_branch=$(git -C "$repo_path" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")

# Ensure branch is pushed to remote
local remote_branch_exists
remote_branch_exists=$(git -C "$repo_path" ls-remote --heads origin "$branch_name" 2>/dev/null | head -1 || echo "")
if [[ -z "$remote_branch_exists" ]]; then
log_info "auto_create_pr_for_task: pushing $branch_name to origin for $task_id"
if ! git -C "$repo_path" push -u origin "$branch_name" 2>>"${SUPERVISOR_LOG:-/dev/null}"; then
log_warn "auto_create_pr_for_task: failed to push $branch_name for $task_id"
return 1
fi
fi

# Build commit summary for PR body (last 10 commits on branch)
local commit_log
commit_log=$(git -C "$repo_path" log --oneline "${base_branch}..${branch_name}" 2>/dev/null | head -10 || echo "(no commits)")

# Create draft PR
local pr_body
pr_body="## Auto-created by supervisor (t247.2)

Worker session ended with commits on branch but no PR (likely context exhaustion).
Supervisor auto-created this PR to preserve work and enable review.

### Commits

\`\`\`
${commit_log}
\`\`\`

### Task

${task_desc}"

local pr_url
pr_url=$(gh pr create \
--repo "$repo_slug" \
--head "$branch_name" \
--base "$base_branch" \
--title "${task_id}: ${task_desc}" \
--body "$pr_body" \
--draft 2>>"${SUPERVISOR_LOG:-/dev/null}") || pr_url=""
Comment on lines +5303 to +5371

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check the file size and locate the function
wc -l .agents/scripts/supervisor-helper.sh

Repository: marcusquinn/aidevops

Length of output: 106


🏁 Script executed:

# Extract the auto_create_pr_for_task function and surrounding context
sed -n '5303,5385p' .agents/scripts/supervisor-helper.sh

Repository: marcusquinn/aidevops

Length of output: 2931


🏁 Script executed:

# Search for check_gh_auth function definition in the codebase
rg 'check_gh_auth' --type sh -B 2 -A 5

Repository: marcusquinn/aidevops

Length of output: 6473


🏁 Script executed:

# Run ShellCheck on the supervisor-helper.sh file to identify any violations
shellcheck .agents/scripts/supervisor-helper.sh 2>&1 | head -50

Repository: marcusquinn/aidevops

Length of output: 2812


🏁 Script executed:

# Check if there are tests or integration checks related to origin/HEAD handling
rg 'origin/HEAD|symbolic-ref' --type sh

Repository: marcusquinn/aidevops

Length of output: 1273


Add pre-check for GitHub authentication before attempting PR creation (Line 5314).

The function currently validates gh CLI availability but proceeds directly to gh pr create without confirming authentication. Adding a check_gh_auth call before expensive operations (db query, git log, push) improves reliability and avoids resource waste on guaranteed failures. This pattern is already established elsewhere in the script.

🔧 Suggested hardening
    if ! command -v gh &>/dev/null; then
        log_warn "auto_create_pr_for_task: gh CLI not available — cannot create PR for $task_id"
        return 1
    fi
+
+    if ! check_gh_auth; then
+        log_warn "auto_create_pr_for_task: gh auth unavailable — cannot create PR for $task_id"
+        return 1
+    fi

Optional refinement: The base branch fallback (line 5328) could also check for origin/master before defaulting to main, making it more robust for repos using non-standard defaults:

-    base_branch=$(git -C "$repo_path" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")
+    base_branch=$(git -C "$repo_path" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "")
+    if [[ -z "$base_branch" ]]; then
+        git -C "$repo_path" show-ref --quiet refs/remotes/origin/main && base_branch="main"
+        git -C "$repo_path" show-ref --quiet refs/remotes/origin/master && base_branch="master"
+        base_branch="${base_branch:-main}"
+    fi
📝 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.

Suggested change
auto_create_pr_for_task() {
local task_id="$1"
local repo_path="$2"
local branch_name="$3"
local repo_slug="$4"
if [[ -z "$task_id" || -z "$repo_path" || -z "$branch_name" || -z "$repo_slug" ]]; then
log_warn "auto_create_pr_for_task: missing required arguments (task=$task_id repo=$repo_path branch=$branch_name slug=$repo_slug)"
return 1
fi
if ! command -v gh &>/dev/null; then
log_warn "auto_create_pr_for_task: gh CLI not available — cannot create PR for $task_id"
return 1
fi
# Fetch task description for PR title/body
local escaped_id
escaped_id=$(sql_escape "$task_id")
local task_desc
task_desc=$(db "$SUPERVISOR_DB" "SELECT description FROM tasks WHERE id = '$escaped_id';" 2>/dev/null || echo "")
if [[ -z "$task_desc" ]]; then
task_desc="Worker task $task_id"
fi
# Determine base branch
local base_branch
base_branch=$(git -C "$repo_path" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")
# Ensure branch is pushed to remote
local remote_branch_exists
remote_branch_exists=$(git -C "$repo_path" ls-remote --heads origin "$branch_name" 2>/dev/null | head -1 || echo "")
if [[ -z "$remote_branch_exists" ]]; then
log_info "auto_create_pr_for_task: pushing $branch_name to origin for $task_id"
if ! git -C "$repo_path" push -u origin "$branch_name" 2>>"${SUPERVISOR_LOG:-/dev/null}"; then
log_warn "auto_create_pr_for_task: failed to push $branch_name for $task_id"
return 1
fi
fi
# Build commit summary for PR body (last 10 commits on branch)
local commit_log
commit_log=$(git -C "$repo_path" log --oneline "${base_branch}..${branch_name}" 2>/dev/null | head -10 || echo "(no commits)")
# Create draft PR
local pr_body
pr_body="## Auto-created by supervisor (t247.2)
Worker session ended with commits on branch but no PR (likely context exhaustion).
Supervisor auto-created this PR to preserve work and enable review.
### Commits
\`\`\`
${commit_log}
\`\`\`
### Task
${task_desc}"
local pr_url
pr_url=$(gh pr create \
--repo "$repo_slug" \
--head "$branch_name" \
--base "$base_branch" \
--title "${task_id}: ${task_desc}" \
--body "$pr_body" \
--draft 2>>"${SUPERVISOR_LOG:-/dev/null}") || pr_url=""
auto_create_pr_for_task() {
local task_id="$1"
local repo_path="$2"
local branch_name="$3"
local repo_slug="$4"
if [[ -z "$task_id" || -z "$repo_path" || -z "$branch_name" || -z "$repo_slug" ]]; then
log_warn "auto_create_pr_for_task: missing required arguments (task=$task_id repo=$repo_path branch=$branch_name slug=$repo_slug)"
return 1
fi
if ! command -v gh &>/dev/null; then
log_warn "auto_create_pr_for_task: gh CLI not available — cannot create PR for $task_id"
return 1
fi
if ! check_gh_auth; then
log_warn "auto_create_pr_for_task: gh auth unavailable — cannot create PR for $task_id"
return 1
fi
# Fetch task description for PR title/body
local escaped_id
escaped_id=$(sql_escape "$task_id")
local task_desc
task_desc=$(db "$SUPERVISOR_DB" "SELECT description FROM tasks WHERE id = '$escaped_id';" 2>/dev/null || echo "")
if [[ -z "$task_desc" ]]; then
task_desc="Worker task $task_id"
fi
# Determine base branch
local base_branch
base_branch=$(git -C "$repo_path" symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||' || echo "main")
# Ensure branch is pushed to remote
local remote_branch_exists
remote_branch_exists=$(git -C "$repo_path" ls-remote --heads origin "$branch_name" 2>/dev/null | head -1 || echo "")
if [[ -z "$remote_branch_exists" ]]; then
log_info "auto_create_pr_for_task: pushing $branch_name to origin for $task_id"
if ! git -C "$repo_path" push -u origin "$branch_name" 2>>"${SUPERVISOR_LOG:-/dev/null}"; then
log_warn "auto_create_pr_for_task: failed to push $branch_name for $task_id"
return 1
fi
fi
# Build commit summary for PR body (last 10 commits on branch)
local commit_log
commit_log=$(git -C "$repo_path" log --oneline "${base_branch}..${branch_name}" 2>/dev/null | head -10 || echo "(no commits)")
# Create draft PR
local pr_body
pr_body="## Auto-created by supervisor (t247.2)
Worker session ended with commits on branch but no PR (likely context exhaustion).
Supervisor auto-created this PR to preserve work and enable review.
### Commits
\`\`\`
${commit_log}
\`\`\`
### Task
${task_desc}"
local pr_url
pr_url=$(gh pr create \
--repo "$repo_slug" \
--head "$branch_name" \
--base "$base_branch" \
--title "${task_id}: ${task_desc}" \
--body "$pr_body" \
--draft 2>>"${SUPERVISOR_LOG:-/dev/null}") || pr_url=""
🤖 Prompt for AI Agents
In @.agents/scripts/supervisor-helper.sh around lines 5303 - 5371, The
auto_create_pr_for_task function should verify GitHub authentication before
doing DB queries, git operations, or pushes; call the existing check_gh_auth
helper early in auto_create_pr_for_task (immediately after the gh CLI
availability check) and return non-zero on failure to avoid wasted work. Also
optionally harden base branch detection in the base_branch assignment inside
auto_create_pr_for_task by checking for origin/master as a fallback before
defaulting to "main" (i.e., try symbolic-ref refs/remotes/origin/HEAD, then
origin/master, then "main"). Ensure you reference the same symbols:
auto_create_pr_for_task, check_gh_auth, base_branch, and SUPERVISOR_DB when
making the change.


if [[ -z "$pr_url" ]]; then
log_warn "auto_create_pr_for_task: gh pr create failed for $task_id ($branch_name)"
return 1
fi

log_success "auto_create_pr_for_task: created draft PR for $task_id: $pr_url"

# Persist via centralized link_pr_to_task (t232)
link_pr_to_task "$task_id" --url "$pr_url" --caller "auto_create_pr" 2>>"${SUPERVISOR_LOG:-/dev/null}" || true

echo "$pr_url"
return 0
}

#######################################
# Link a PR to a task — single source of truth (t232)
#
Expand Down Expand Up @@ -5794,15 +5903,25 @@ evaluate_worker() {

# Decision matrix:
# - Commits + PR URL → complete (worker finished, signal was lost)
# - Commits + no PR → complete:task_only (PR creation may have failed)
# - Commits + no PR → auto-create PR (t247.2), fallback to task_only
# - No commits + uncommitted changes → retry:work_in_progress
# - No commits + no changes → genuine ambiguity (fall through to AI/retry)

if [[ "$branch_commits" -gt 0 ]]; then
if [[ -n "$meta_pr_url" ]]; then
echo "complete:${meta_pr_url}"
else
echo "complete:task_only"
# t247.2: Auto-create PR instead of returning task_only.
# Saves ~300s per retry by preserving work for review.
local auto_pr_url=""
if [[ -n "$repo_slug_detect" && -n "$task_branch" ]]; then
auto_pr_url=$(auto_create_pr_for_task "$task_id" "$git_dir" "$task_branch" "$repo_slug_detect" 2>>"${SUPERVISOR_LOG:-/dev/null}") || auto_pr_url=""
fi
if [[ -n "$auto_pr_url" ]]; then
echo "complete:${auto_pr_url}"
else
echo "complete:task_only"
fi
fi
return 0
fi
Expand Down
Loading