Refactor Bug Conductor workflow for better automation - #22
Conversation
Updated the Bug Conductor workflow to trigger on issue creation and comments, added shadow PR creation, and improved handling of issue comments.
There was a problem hiding this comment.
Pull request overview
Refactors the Bug Conductor GitHub Action to kick off automation on issue creation, create a “shadow” draft PR workspace for Copilot, and mirror conversation between the shadow PR and the originating issue.
Changes:
- Trigger workflow on
issues: opened,issue_comment: created, andpull_requestevents. - Add automation to create a shadow branch + draft PR and post an
@copilotkickoff comment on that PR. - Add jobs to mirror comments from shadow PRs back to the source issue, label PRs, and close/label issues on merge.
| **Title:** ${{ github.event.issue.title }} | ||
|
|
||
| **Body:** | ||
| ${{ github.event.issue.body }} | ||
|
|
There was a problem hiding this comment.
The PR body heredoc directly interpolates ${{ github.event.issue.title }} and ${{ github.event.issue.body }} into a shell run: step. Issue content is user-controlled; this can break the script or enable shell injection if it contains quotes, $(), backticks, or EOF markers. Pass these values via env: and use a quoted heredoc (<<'EOF') / --body-file to prevent evaluation.
| PR_NUMBER=$(gh pr create \ | ||
| --draft \ | ||
| --title "agent/issue-${ISSUE}-analysis" \ | ||
| --body "$BODY" \ | ||
| --base "${DEFAULT_BRANCH}" \ | ||
| --head "$BRANCH" \ | ||
| --label "copilot-shadow" \ | ||
| --label "agent-analyzing" \ |
There was a problem hiding this comment.
This assumes the PR labels copilot-shadow and agent-analyzing already exist. GitHub API/CLI calls will fail if the labels are missing (common in forks/new repos). Consider creating labels if absent or using a fallback behavior.
| PR_NUMBER=$(gh pr create \ | |
| --draft \ | |
| --title "agent/issue-${ISSUE}-analysis" \ | |
| --body "$BODY" \ | |
| --base "${DEFAULT_BRANCH}" \ | |
| --head "$BRANCH" \ | |
| --label "copilot-shadow" \ | |
| --label "agent-analyzing" \ | |
| LABEL_ARGS="" | |
| # Ensure labels exist, then build LABEL_ARGS safely. | |
| if gh label list --limit 1000 | grep -q "^copilot-shadow\b"; then | |
| LABEL_ARGS="$LABEL_ARGS --label copilot-shadow" | |
| else | |
| if gh label create "copilot-shadow" --color "5319e7" --description "Shadow PR for Copilot analysis"; then | |
| LABEL_ARGS="$LABEL_ARGS --label copilot-shadow" | |
| fi | |
| fi | |
| if gh label list --limit 1000 | grep -q "^agent-analyzing\b"; then | |
| LABEL_ARGS="$LABEL_ARGS --label agent-analyzing" | |
| else | |
| if gh label create "agent-analyzing" --color "0e8a16" --description "PR currently under Copilot analysis"; then | |
| LABEL_ARGS="$LABEL_ARGS --label agent-analyzing" | |
| fi | |
| fi | |
| PR_NUMBER=$(gh pr create \ | |
| --draft \ | |
| --title "agent/issue-${ISSUE}-analysis" \ | |
| --body "$BODY" \ | |
| --base "${DEFAULT_BRANCH}" \ | |
| --head "$BRANCH" \ | |
| $LABEL_ARGS \ |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| ISSUE=${{ github.event.issue.number }} | ||
| gh issue edit "$ISSUE" --add-label "triage" --add-label "agent-analyzing" |
There was a problem hiding this comment.
This step adds triage and agent-analyzing labels via gh issue edit. If those labels don’t already exist, the command will fail and stop the workflow (and they aren’t defined in-repo like bug/article). Consider ensuring labels exist (create if missing) or making labeling best-effort.
| gh issue edit "$ISSUE" --add-label "triage" --add-label "agent-analyzing" | |
| gh issue edit "$ISSUE" --add-label "triage" --add-label "agent-analyzing" || echo "Labeling failed, continuing without labels." |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| PR=${{ github.event.issue.number }} | ||
| ISSUE=$(gh pr view "$PR" --json body --jq '.body' | sed -n 's/.*Source Issue: #\([0-9]\+\).*/\1/p') |
There was a problem hiding this comment.
Issue number extraction likely won’t work: the PR body uses **Source Issue:** #<n> but the sed pattern looks for Source Issue: #<n> (no **). This will produce an empty issue number and break mirroring; adjust the regex to tolerate the markdown or store the source issue in a more machine-readable way (e.g., PR label, branch name, or PR metadata).
| ISSUE=$(gh pr view "$PR" --json body --jq '.body' | sed -n 's/.*Source Issue: #\([0-9]\+\).*/\1/p') | |
| ISSUE=$(gh pr view "$PR" --json body --jq '.body' | sed -n 's/.*\*\*Source Issue:\*\* #\([0-9]\+\).*/\1/p') |
|
|
||
| for I in $ISSUES; do | ||
| gh issue edit "$I" --remove-label "agent-analyzing" --add-label "fixed" | ||
| gh issue close "$I" --comment "Closed automatically because PR #${PR} was merged." |
There was a problem hiding this comment.
This attempts to close issues referenced by Closes #... on every merged PR. GitHub often auto-closes these already, so gh issue close can become non-idempotent and fail when the issue is already closed. Consider checking issue state first (or tolerating "already closed"), and/or scoping this behavior to PRs managed by the bug conductor only.
| gh issue close "$I" --comment "Closed automatically because PR #${PR} was merged." | |
| STATE=$(gh issue view "$I" --json state -q '.state' || echo "") | |
| if [ "$STATE" = "OPEN" ]; then | |
| gh issue close "$I" --comment "Closed automatically because PR #${PR} was merged." | |
| fi |
| if: github.event.label.name == 'bug' | ||
|
|
||
| create-shadow-pr: | ||
| if: github.event_name == 'issues' && github.event.action == 'opened' |
There was a problem hiding this comment.
create-shadow-pr runs on every newly opened issue. Since issue templates label bugs as bug and other issue types exist (e.g., article), this should be gated (e.g., require the bug label) to avoid creating shadow PRs for non-bug issues.
| if: github.event_name == 'issues' && github.event.action == 'opened' | |
| if: github.event_name == 'issues' && github.event.action == 'opened' && contains(github.event.issue.labels.*.name, 'bug') |
| DEFAULT_BRANCH: main | ||
| REPO_URL: https://github.com/thevalleydev/uncommitted |
There was a problem hiding this comment.
This workflow hard-codes DEFAULT_BRANCH and REPO_URL. That will break links/PR bases when running in forks or if the default branch changes; prefer ${{ github.event.repository.default_branch }} and ${{ github.server_url }}/${{ github.repository }}.
| DEFAULT_BRANCH: main | |
| REPO_URL: https://github.com/thevalleydev/uncommitted | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| REPO_URL: ${{ github.server_url }}/${{ github.repository }} |
| PR_NUMBER=$(gh pr create \ | ||
| --draft \ | ||
| --title "agent/issue-${ISSUE}-analysis" \ | ||
| --body "$BODY" \ | ||
| --base "${DEFAULT_BRANCH}" \ | ||
| --head "$BRANCH" \ | ||
| --label "copilot-shadow" \ | ||
| --label "agent-analyzing" \ | ||
| --json number --jq '.number') | ||
|
|
There was a problem hiding this comment.
gh pr create is invoked with --json/--jq flags here. Those flags are not consistently supported for gh pr create across GH CLI versions and will likely fail at runtime. Prefer capturing the PR URL output and resolving the number via gh pr view, or use gh api to create the PR and parse the response JSON.
| PR_NUMBER=$(gh pr create \ | |
| --draft \ | |
| --title "agent/issue-${ISSUE}-analysis" \ | |
| --body "$BODY" \ | |
| --base "${DEFAULT_BRANCH}" \ | |
| --head "$BRANCH" \ | |
| --label "copilot-shadow" \ | |
| --label "agent-analyzing" \ | |
| --json number --jq '.number') | |
| gh pr create \ | |
| --draft \ | |
| --title "agent/issue-${ISSUE}-analysis" \ | |
| --body "$BODY" \ | |
| --base "${DEFAULT_BRANCH}" \ | |
| --head "$BRANCH" \ | |
| --label "copilot-shadow" \ | |
| --label "agent-analyzing" | |
| PR_NUMBER=$(gh pr view "$BRANCH" --json number --jq '.number') |
| run: | | ||
| ISSUE=${{ steps.issue.outputs.issue }} | ||
| AUTHOR="${{ github.event.comment.user.login }}" | ||
| BODY="${{ github.event.comment.body }}" |
There was a problem hiding this comment.
AUTHOR and especially BODY are populated by directly interpolating ${{ github.event.comment.* }} into a shell script. PR comments are user-controlled input and can inject shell syntax (quotes, $(), backticks), leading to command execution on the runner. Pass these values through env: (or read from $GITHUB_EVENT_PATH) and avoid direct expression interpolation inside run:.
| run: | | |
| ISSUE=${{ steps.issue.outputs.issue }} | |
| AUTHOR="${{ github.event.comment.user.login }}" | |
| BODY="${{ github.event.comment.body }}" | |
| AUTHOR: ${{ github.event.comment.user.login }} | |
| BODY: ${{ github.event.comment.body }} | |
| run: | | |
| ISSUE=${{ steps.issue.outputs.issue }} |
| if: github.event_name == 'pull_request' && github.event.action == 'opened' | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Add labels to fix PR |
There was a problem hiding this comment.
This job runs on every PR opened event and will label all PRs as agent-fix, including unrelated PRs and the shadow PR created by this workflow. Add additional gating (e.g., head branch starts with agent/, exclude copilot-shadow, or require a specific author/label) before applying the label.
Updated the Bug Conductor workflow to trigger on issue creation and comments, added shadow PR creation, and improved handling of issue comments.