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
271 changes: 212 additions & 59 deletions .github/workflows/bug-conductor.yml
Original file line number Diff line number Diff line change
@@ -1,71 +1,224 @@
# Bug Conductor
# Triggered when an issue is labeled "bug".
# Mentions @copilot to trigger the coding agent to investigate and fix the bug.
name: Bug Conductor
name: Bug Conductor (Autonomous)

on:
issues:
types: [labeled]
types: [opened]
issue_comment:
types: [created]
pull_request:
types: [opened, closed]

permissions:
contents: write
issues: write
pull-requests: write
issues: write

env:
DEFAULT_BRANCH: main
REPO_URL: https://github.com/thevalleydev/uncommitted
Comment on lines +17 to +18

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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 }}.

Suggested change
DEFAULT_BRANCH: main
REPO_URL: https://github.com/thevalleydev/uncommitted
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
REPO_URL: ${{ github.server_url }}/${{ github.repository }}

Copilot uses AI. Check for mistakes.

jobs:
conductor:
if: github.event.label.name == 'bug'

create-shadow-pr:
if: github.event_name == 'issues' && github.event.action == 'opened'

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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')

Copilot uses AI. Check for mistakes.
runs-on: ubuntu-latest

steps:
- name: Acknowledge bug
uses: actions/github-script@v7
with:
script: |
const repoUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
'**Bug conductor activated.** Here is the plan:',
'',
'1. Investigate the reported bug and identify root cause',
'2. Locate affected files and trace the issue',
'3. Implement a minimal fix',
'4. Verify the fix works and doesn\'t break anything',
'5. Open a PR for review',
'',
`See the [Bug Investigation Protocol](${repoUrl}/.github/agents/bug-investigation.md) for the full process.`,
'',
'Starting investigation now.'
].join('\n')
});

- name: Trigger Copilot
uses: actions/github-script@v7
- uses: actions/checkout@v4
with:
script: |
const repoUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: [
'@copilot Please investigate and fix this bug.',
'',
'**Required reading before fixing:**',
`- [Bug Investigation Protocol](${repoUrl}/.github/agents/bug-investigation.md) - How to diagnose`,
`- [Bug Fix Checklist](${repoUrl}/.github/agents/bug-fix-checklist.md) - Quality requirements`,
`- [Main Instructions](${repoUrl}/.github/copilot-instructions.md) - Overall guidance`,
'',
'**Your task:**',
'1. Understand the bug from the description and reproduction steps',
'2. Search the codebase for relevant files',
'3. Identify the root cause',
'4. Implement the minimal fix that solves the problem',
'5. Verify the fix by building the project',
'6. Open a PR with `Closes #' + context.issue.number + '` in the body',
'',
'If you cannot determine the root cause, open a draft PR with your findings.'
].join('\n')
});
ref: ${{ env.DEFAULT_BRANCH }}

- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Create shadow branch
id: branch
run: |
ISSUE=${{ github.event.issue.number }}
BRANCH="agent/issue-${ISSUE}-analysis"
echo "branch=$BRANCH" >> $GITHUB_OUTPUT

git checkout -b "$BRANCH"
mkdir -p .github/shadow-issues
echo "Shadow workspace for Issue #${ISSUE}" > .github/shadow-issues/issue-${ISSUE}.md
git add .
git commit -m "chore: create shadow workspace for issue #${ISSUE}"
git push origin "$BRANCH"

- name: Create shadow PR
id: pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE=${{ github.event.issue.number }}
BRANCH=${{ steps.branch.outputs.branch }}

BODY=$(cat <<EOF
Shadow PR for Copilot analysis of Issue #${ISSUE}

**Source Issue:** #${ISSUE}

---

### Issue context

**Title:** ${{ github.event.issue.title }}

**Body:**
${{ github.event.issue.body }}

Comment on lines +67 to +71

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
---

### Required reading before fixing:
- [Bug Investigation Protocol](${REPO_URL}/.github/agents/bug-investigation.md)
- [Bug Fix Checklist](${REPO_URL}/.github/agents/bug-fix-checklist.md)
- [Main Instructions](${REPO_URL}/.github/copilot-instructions.md)

### Your task:
1. Understand the bug from the description and reproduction steps
2. Search the codebase for relevant files
3. Identify the root cause
4. Implement the minimal fix that solves the problem
5. Verify the fix by building the project
6. Open a PR with \`Closes #${ISSUE}\` in the body

If you cannot determine the root cause, open a draft PR with your findings.
EOF
)

PR_NUMBER=$(gh pr create \
--draft \
--title "agent/issue-${ISSUE}-analysis" \
--body "$BODY" \
--base "${DEFAULT_BRANCH}" \
--head "$BRANCH" \
--label "copilot-shadow" \
--label "agent-analyzing" \
Comment on lines +91 to +98

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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 \

Copilot uses AI. Check for mistakes.
--json number --jq '.number')

Comment on lines +91 to +100

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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')

Copilot uses AI. Check for mistakes.
echo "pr=$PR_NUMBER" >> $GITHUB_OUTPUT

- name: Summon Copilot
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE=${{ github.event.issue.number }}
PR=${{ steps.pr.outputs.pr }}

COMMENT=$(cat <<EOF
@copilot Please investigate and fix this bug.

**Required reading before fixing:**
- [Bug Investigation Protocol](${REPO_URL}/.github/agents/bug-investigation.md)
- [Bug Fix Checklist](${REPO_URL}/.github/agents/bug-fix-checklist.md)
- [Main Instructions](${REPO_URL}/.github/copilot-instructions.md)

**Your task:**
1. Understand the bug from the description and reproduction steps
2. Search the codebase for relevant files
3. Identify the root cause
4. Implement the minimal fix that solves the problem
5. Verify the fix by building the project
6. Open a PR with \`Closes #${ISSUE}\` in the body

If you cannot determine the root cause, open a draft PR with your findings.
EOF
)

gh pr comment "$PR" --body "$COMMENT"

- name: Label original Issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE=${{ github.event.issue.number }}
gh issue edit "$ISSUE" --add-label "triage" --add-label "agent-analyzing"

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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."

Copilot uses AI. Check for mistakes.

- name: Notify Issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE=${{ github.event.issue.number }}
PR=${{ steps.pr.outputs.pr }}
gh issue comment "$ISSUE" --body "Bug Conductor activated. Copilot is analyzing this issue in a shadow workspace (PR #${PR})."


mirror-agent-comments:
if: github.event_name == 'issue_comment' && github.event.issue.pull_request != null
runs-on: ubuntu-latest

steps:
- name: Check if PR is a shadow PR
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR=${{ github.event.issue.number }}
LABELS=$(gh pr view "$PR" --json labels --jq '.labels[].name')
if echo "$LABELS" | grep -q "copilot-shadow"; then
echo "shadow=true" >> $GITHUB_OUTPUT
else
echo "shadow=false" >> $GITHUB_OUTPUT
fi

- name: Exit if not shadow PR
if: steps.check.outputs.shadow != 'true'
run: echo "Not a shadow PR."

- name: Extract Issue number
id: issue
if: steps.check.outputs.shadow == 'true'
env:
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')

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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).

Suggested change
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')

Copilot uses AI. Check for mistakes.
echo "issue=$ISSUE" >> $GITHUB_OUTPUT

- name: Mirror comment to Issue
if: steps.check.outputs.shadow == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE=${{ steps.issue.outputs.issue }}
AUTHOR="${{ github.event.comment.user.login }}"
BODY="${{ github.event.comment.body }}"
Comment on lines +184 to +187

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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:.

Suggested change
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 }}

Copilot uses AI. Check for mistakes.

if [ "$AUTHOR" = "github-actions[bot]" ]; then exit 0; fi

gh issue comment "$ISSUE" --body "_Comment from shadow PR by **@$AUTHOR**:_\n\n$BODY"


label-fix-pr:
if: github.event_name == 'pull_request' && github.event.action == 'opened'
runs-on: ubuntu-latest

steps:
- name: Add labels to fix PR
Comment on lines +195 to +199

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR=${{ github.event.pull_request.number }}
gh pr edit "$PR" --add-label "agent-fix"


close-issue-on-merge:
if: github.event_name == 'pull_request' && github.event.pull_request.merged == true
runs-on: ubuntu-latest

steps:
- name: Close referenced Issues and update labels
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BODY="${{ github.event.pull_request.body }}"
PR=${{ github.event.pull_request.number }}

ISSUES=$(printf "%s\n" "$BODY" | sed -n 's/.*[Cc]loses #\([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."

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

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.

Suggested change
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

Copilot uses AI. Check for mistakes.
done
Loading