Skip to content

Commit b879490

Browse files
peppescgclaude
andauthored
refactor(ci): migrate bug-fix agent and triage to claude-code-action (#2073)
Replace manual `claude -p` CLI invocations with the official anthropics/claude-code-action GitHub Action. This eliminates the pnpm v10 postinstall issue and aligns with the project's existing claude.yml setup. Key changes: - Phase 1, Phase 2, Phase 2b are now separate steps using the action - Remove Install Claude Code step (action handles installation) - Remove retry loop (single attempt with separate steps) - Triage cron also migrated to use the action Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 060b020 commit b879490

3 files changed

Lines changed: 174 additions & 187 deletions

File tree

.github/workflows/_bug-fix-agent.yml

Lines changed: 132 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -73,153 +73,146 @@ jobs:
7373
if: steps.guard.outputs.skip != 'true'
7474
uses: ./.github/actions/setup
7575

76-
- name: Install Claude Code
76+
- name: 'Phase 1 — Analyze & Write Failing Test (Opus)'
77+
id: phase1
7778
if: steps.guard.outputs.skip != 'true'
78-
run: pnpm add -g --ignore-scripts=false @anthropic-ai/claude-code
79+
continue-on-error: true
80+
uses: anthropics/claude-code-action@5d5c10a4f389689f992ea10bb14dcb6fcc83146d # v1
81+
with:
82+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
83+
prompt: |
84+
Read .claude/skills/bug-fix-tdd/SKILL.md and follow it.
85+
Read issue-body.md for the bug report.
86+
87+
Your task (Phase 1 — Analysis & Test):
88+
1. Analyze the bug report: understand description, steps to reproduce, expected vs actual behavior.
89+
2. Search the codebase to find the relevant source code.
90+
3. Write a unit test that reproduces the bug — it MUST FAIL when you run it.
91+
4. Run the test with: pnpm run test:nonInteractive -- <test-file-path>
92+
5. Verify the test fails FOR THE RIGHT REASON (not import errors or unrelated failures).
93+
6. If the test passes (bug not reproduced), try a different approach (max 3 attempts).
94+
95+
Output:
96+
- The test file in the correct __tests__/ directory.
97+
- bug-analysis.md with your findings (follow the format in the skill).
98+
99+
Do NOT modify any source files. Only create/edit test files and bug-analysis.md.
100+
claude_args: >-
101+
--model opus
102+
--max-turns 50
103+
--allowedTools "Read,Grep,Glob,Edit,Write,Bash(pnpm run test:nonInteractive *),Bash(cat *),Bash(ls *)"
104+
105+
- name: 'Hard gate — Verify test fails'
106+
id: gate
107+
if: steps.guard.outputs.skip != 'true' && steps.phase1.outcome == 'success'
108+
run: |
109+
if [ ! -f bug-analysis.md ]; then
110+
echo "::warning::bug-analysis.md not found"
111+
echo "test_fails=false" >> $GITHUB_OUTPUT
112+
exit 0
113+
fi
79114
80-
- name: 'Bug Fix (up to 3 attempts)'
81-
id: tdd
82-
if: steps.guard.outputs.skip != 'true'
115+
TEST_FILE=$(grep "^Test file:" bug-analysis.md | sed 's/^Test file: //' || true)
116+
if [ -z "$TEST_FILE" ]; then
117+
echo "::warning::No 'Test file:' line found in bug-analysis.md"
118+
echo "test_fails=false" >> $GITHUB_OUTPUT
119+
exit 0
120+
fi
121+
122+
echo "test_file=$TEST_FILE" >> $GITHUB_OUTPUT
123+
124+
if pnpm run test:nonInteractive -- "$TEST_FILE" 2>&1; then
125+
echo "::warning::Test passed (bug not reproduced)"
126+
echo "test_fails=false" >> $GITHUB_OUTPUT
127+
else
128+
echo "::notice::Test fails as expected"
129+
echo "test_fails=true" >> $GITHUB_OUTPUT
130+
fi
131+
132+
- name: 'Phase 2 — TDD Fix (Sonnet)'
133+
id: phase2
134+
if: >-
135+
steps.guard.outputs.skip != 'true'
136+
&& steps.gate.outputs.test_fails == 'true'
137+
continue-on-error: true
138+
uses: anthropics/claude-code-action@5d5c10a4f389689f992ea10bb14dcb6fcc83146d # v1
139+
env:
140+
BUG_TEST_FILE: ${{ steps.gate.outputs.test_file }}
141+
BUG_ISSUE_NUMBER: ${{ inputs.issue-number }}
142+
with:
143+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
144+
prompt: |
145+
Read .claude/skills/bug-fix-tdd/SKILL.md and bug-analysis.md.
146+
147+
Your task (Phase 2 — Fix):
148+
1. Read the failing test and understand what it expects.
149+
2. Apply the MINIMUM fix to make the test pass.
150+
3. Run the test to verify it passes: pnpm run test:nonInteractive -- ${{ steps.gate.outputs.test_file }}
151+
4. Run the full test suite: pnpm run test:nonInteractive
152+
5. Run pnpm run lint and pnpm run type-check.
153+
6. If any check fails, adjust the fix (max 5 attempts).
154+
7. Write pr-body.md (include 'Fixes #${{ inputs.issue-number }}') and fix-title.txt.
155+
156+
Do NOT run git, gh, or modify .env files.
157+
Do NOT over-engineer — apply the smallest change that fixes the bug.
158+
claude_args: >-
159+
--model sonnet
160+
--max-turns 150
161+
--allowedTools "Read,Grep,Glob,Edit,Write,Bash(pnpm run test:nonInteractive *),Bash(pnpm run lint),Bash(pnpm run type-check),Bash(cat *),Bash(ls *)"
162+
163+
- name: 'Phase 2b — Direct Fix without TDD (Sonnet)'
164+
id: phase2b
165+
if: >-
166+
steps.guard.outputs.skip != 'true'
167+
&& steps.phase1.outcome == 'success'
168+
&& steps.gate.outputs.test_fails != 'true'
83169
continue-on-error: true
170+
uses: anthropics/claude-code-action@5d5c10a4f389689f992ea10bb14dcb6fcc83146d # v1
84171
env:
85-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
86172
BUG_ISSUE_NUMBER: ${{ inputs.issue-number }}
173+
with:
174+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
175+
prompt: |
176+
Read .claude/skills/bug-fix-tdd/SKILL.md and bug-analysis.md.
177+
Read issue-body.md for the original bug report.
178+
179+
The bug could NOT be reproduced in a unit test.
180+
bug-analysis.md contains the analysis of the bug from Phase 1.
181+
182+
Your task (Phase 2b — Direct Fix):
183+
1. Read the analysis and understand the root cause.
184+
2. Apply the MINIMUM fix to resolve the bug.
185+
3. If you CAN write a regression test, do so — but it is not required.
186+
4. Run the full test suite: pnpm run test:nonInteractive
187+
5. Run pnpm run lint and pnpm run type-check.
188+
6. If any check fails, adjust the fix (max 5 attempts).
189+
7. Write pr-body.md (include 'Fixes #${{ inputs.issue-number }}').
190+
Note in the PR body that no regression test was possible.
191+
8. Write fix-title.txt.
192+
193+
Do NOT run git, gh, or modify .env files.
194+
Do NOT over-engineer — apply the smallest change that fixes the bug.
195+
claude_args: >-
196+
--model sonnet
197+
--max-turns 150
198+
--allowedTools "Read,Grep,Glob,Edit,Write,Bash(pnpm run test:nonInteractive *),Bash(pnpm run lint),Bash(pnpm run type-check),Bash(cat *),Bash(ls *)"
199+
200+
- name: 'Hard gate — Verify all checks pass'
201+
id: verify
202+
if: >-
203+
steps.guard.outputs.skip != 'true'
204+
&& (steps.phase2.outcome == 'success' || steps.phase2b.outcome == 'success')
87205
run: |
88-
MAX_ATTEMPTS=3
89-
SUCCESS=false
90-
91-
for ATTEMPT in $(seq 1 $MAX_ATTEMPTS); do
92-
echo "::group::Attempt $ATTEMPT of $MAX_ATTEMPTS"
93-
94-
# Clean up previous attempt artifacts
95-
rm -f bug-analysis.md pr-body.md fix-title.txt
96-
git checkout -- . 2>/dev/null || true
97-
git clean -fd 2>/dev/null || true
98-
99-
# --- Phase 1: Analyze & Write Failing Test (Opus) ---
100-
echo "::notice::Phase 1 — Writing failing test (attempt $ATTEMPT)"
101-
if ! claude -p --model opus \
102-
--dangerously-skip-permissions \
103-
--allowedTools "Read,Grep,Glob,Edit,Write,Bash(pnpm run test:nonInteractive *),Bash(cat *),Bash(ls *)" \
104-
--max-turns 50 \
105-
"Read .claude/skills/bug-fix-tdd/SKILL.md and follow it.
106-
Read issue-body.md for the bug report.
107-
108-
Your task (Phase 1 — Analysis & Test):
109-
1. Analyze the bug report: understand description, steps to reproduce, expected vs actual behavior.
110-
2. Search the codebase to find the relevant source code.
111-
3. Write a unit test that reproduces the bug — it MUST FAIL when you run it.
112-
4. Run the test with: pnpm run test:nonInteractive -- <test-file-path>
113-
5. Verify the test fails FOR THE RIGHT REASON (not import errors or unrelated failures).
114-
6. If the test passes (bug not reproduced), try a different approach (max 3 attempts).
115-
116-
Output:
117-
- The test file in the correct __tests__/ directory.
118-
- bug-analysis.md with your findings (follow the format in the skill).
119-
120-
Do NOT modify any source files. Only create/edit test files and bug-analysis.md."; then
121-
echo "::warning::Phase 1 failed on attempt $ATTEMPT"
122-
echo "::endgroup::"
123-
continue
124-
fi
125-
126-
# --- Hard gate: verify test fails ---
127-
if [ ! -f bug-analysis.md ]; then
128-
echo "::warning::bug-analysis.md not found — skipping attempt $ATTEMPT"
129-
echo "::endgroup::"
130-
continue
131-
fi
132-
133-
TEST_FILE=$(grep "^Test file:" bug-analysis.md | sed 's/^Test file: //' || true)
134-
if [ -z "$TEST_FILE" ]; then
135-
echo "::warning::No 'Test file:' line found in bug-analysis.md — skipping attempt $ATTEMPT"
136-
echo "::endgroup::"
137-
continue
138-
fi
139-
140-
if pnpm run test:nonInteractive -- "$TEST_FILE" 2>&1; then
141-
echo "::warning::Test passed (bug not reproduced) — trying direct fix"
142-
143-
# --- Phase 2b: Direct fix without regression test ---
144-
echo "::notice::Phase 2b — Direct fix without TDD (attempt $ATTEMPT)"
145-
if ! claude -p --model sonnet \
146-
--dangerously-skip-permissions \
147-
--allowedTools "Read,Grep,Glob,Edit,Write,Bash(pnpm run test:nonInteractive *),Bash(pnpm run lint),Bash(pnpm run type-check),Bash(cat *),Bash(ls *)" \
148-
--max-turns 150 \
149-
"Read .claude/skills/bug-fix-tdd/SKILL.md and bug-analysis.md.
150-
Read issue-body.md for the original bug report.
151-
152-
The bug could NOT be reproduced in a unit test.
153-
bug-analysis.md contains the analysis of the bug from Phase 1.
154-
155-
Your task (Phase 2b — Direct Fix):
156-
1. Read the analysis and understand the root cause.
157-
2. Apply the MINIMUM fix to resolve the bug.
158-
3. If you CAN write a regression test, do so — but it is not required.
159-
4. Run the full test suite: pnpm run test:nonInteractive
160-
5. Run pnpm run lint and pnpm run type-check.
161-
6. If any check fails, adjust the fix (max 5 attempts).
162-
7. Write pr-body.md (include 'Fixes #$BUG_ISSUE_NUMBER').
163-
Note in the PR body that no regression test was possible.
164-
8. Write fix-title.txt.
165-
166-
Do NOT run git, gh, or modify .env files.
167-
Do NOT over-engineer — apply the smallest change that fixes the bug."; then
168-
echo "::warning::Phase 2b failed on attempt $ATTEMPT"
169-
echo "::endgroup::"
170-
continue
171-
fi
172-
else
173-
echo "::notice::Test fails as expected — proceeding to Phase 2 (TDD)"
174-
175-
# --- Phase 2: Implement Fix with TDD (Sonnet) ---
176-
export BUG_TEST_FILE="$TEST_FILE"
177-
echo "::notice::Phase 2 — Implementing fix (attempt $ATTEMPT)"
178-
if ! claude -p --model sonnet \
179-
--dangerously-skip-permissions \
180-
--allowedTools "Read,Grep,Glob,Edit,Write,Bash(pnpm run test:nonInteractive *),Bash(pnpm run lint),Bash(pnpm run type-check),Bash(cat *),Bash(ls *)" \
181-
--max-turns 150 \
182-
"Read .claude/skills/bug-fix-tdd/SKILL.md and bug-analysis.md.
183-
184-
Your task (Phase 2 — Fix):
185-
1. Read the failing test and understand what it expects.
186-
2. Apply the MINIMUM fix to make the test pass.
187-
3. Run the test to verify it passes: pnpm run test:nonInteractive -- $BUG_TEST_FILE
188-
4. Run the full test suite: pnpm run test:nonInteractive
189-
5. Run pnpm run lint and pnpm run type-check.
190-
6. If any check fails, adjust the fix (max 5 attempts).
191-
7. Write pr-body.md (include 'Fixes #$BUG_ISSUE_NUMBER') and fix-title.txt.
192-
193-
Do NOT run git, gh, or modify .env files.
194-
Do NOT over-engineer — apply the smallest change that fixes the bug."; then
195-
echo "::warning::Phase 2 failed on attempt $ATTEMPT"
196-
echo "::endgroup::"
197-
continue
198-
fi
199-
fi
200-
201-
# --- Hard gate: verify all checks pass ---
202-
if pnpm run test:nonInteractive && pnpm run lint && pnpm run type-check; then
203-
echo "::notice::All checks pass on attempt $ATTEMPT"
204-
SUCCESS=true
205-
echo "::endgroup::"
206-
break
207-
else
208-
echo "::warning::Verification failed on attempt $ATTEMPT"
209-
echo "::endgroup::"
210-
continue
211-
fi
212-
done
213-
214-
if [ "$SUCCESS" = "true" ]; then
206+
if pnpm run test:nonInteractive && pnpm run lint && pnpm run type-check; then
207+
echo "::notice::All checks pass"
215208
echo "result=success" >> $GITHUB_OUTPUT
216-
echo "test_file=$TEST_FILE" >> $GITHUB_OUTPUT
217209
else
210+
echo "::warning::Verification failed"
218211
echo "result=failure" >> $GITHUB_OUTPUT
219212
fi
220213
221214
- name: Create branch and commit
222-
if: steps.guard.outputs.skip != 'true' && steps.tdd.outputs.result == 'success'
215+
if: steps.guard.outputs.skip != 'true' && steps.verify.outputs.result == 'success'
223216
id: push
224217
run: |
225218
ISSUE_NUM="${{ inputs.issue-number }}"
@@ -244,7 +237,7 @@ jobs:
244237
fi
245238
246239
- name: Create Pull Request
247-
if: steps.guard.outputs.skip != 'true' && steps.tdd.outputs.result == 'success' && steps.push.outputs.has_changes == 'true'
240+
if: steps.guard.outputs.skip != 'true' && steps.verify.outputs.result == 'success' && steps.push.outputs.has_changes == 'true'
248241
env:
249242
GH_TOKEN: ${{ steps.app-token.outputs.token }}
250243
run: |
@@ -268,7 +261,8 @@ jobs:
268261
if: >-
269262
always()
270263
&& steps.guard.outputs.skip != 'true'
271-
&& steps.tdd.outputs.result == 'failure'
264+
&& steps.verify.outputs.result != 'success'
265+
&& steps.phase1.outcome != 'skipped'
272266
env:
273267
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
274268
run: |
@@ -278,7 +272,7 @@ jobs:
278272
ANALYSIS="The agent could not analyze this bug. The issue may be too vague or involve behavior that cannot be captured in a unit test."
279273
fi
280274
281-
printf '## Bug Fix Agent — Automated Analysis\n\n%s\n\n---\n*Automated analysis by Claude Code Bug Fix Agent. All 3 attempts exhausted. A developer will review this issue manually.*\n' \
275+
printf '## Bug Fix Agent — Automated Analysis\n\n%s\n\n---\n*Automated analysis by Claude Code Bug Fix Agent. A developer will review this issue manually.*\n' \
282276
"$ANALYSIS" > /tmp/comment-body.md
283277
284278
gh issue comment "${{ inputs.issue-number }}" --body-file /tmp/comment-body.md

.github/workflows/_security-fix-agent.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
6060
- name: Install Claude Code
6161
if: steps.existing.outputs.skip != 'true'
62-
run: pnpm add -g @anthropic-ai/claude-code
62+
run: npm install -g @anthropic-ai/claude-code
6363

6464
- name: 'Phase 1: Analyze and Plan (Opus)'
6565
if: steps.existing.outputs.skip != 'true'

0 commit comments

Comments
 (0)