Skip to content

Commit 6f8ce93

Browse files
feat: improve claude fixer worker (#63)
1 parent 6cdf137 commit 6f8ce93

1 file changed

Lines changed: 99 additions & 9 deletions

File tree

.github/workflows/claude-fixer.yml

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,120 @@
11
name: Claude Test Fixer
22
on:
33
workflow_run:
4-
workflows: ["Test all cron (Linux)", "Test all cron (MacOS)"]
4+
workflows: ["Test all (Linux)", "Test all (MacOS)"]
55
types: [completed]
66

77
jobs:
8-
fix-on-failure:
8+
determine-runner:
99
runs-on: ubuntu-latest
1010
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
11+
outputs:
12+
runner: ${{ steps.pick.outputs.runner }}
13+
steps:
14+
- id: pick
15+
run: |
16+
if [[ "${{ github.event.workflow_run.name }}" == *"MacOS"* ]]; then
17+
echo "runner=macos-latest" >> $GITHUB_OUTPUT
18+
else
19+
echo "runner=ubuntu-latest" >> $GITHUB_OUTPUT
20+
fi
21+
22+
fix-on-failure:
23+
needs: determine-runner
24+
runs-on: ${{ needs.determine-runner.outputs.runner }}
1125
permissions:
1226
contents: write
1327
pull-requests: write
14-
actions: read # Allows Claude to read the logs of the failed run
28+
actions: read
1529
steps:
1630
- uses: actions/checkout@v4
31+
with:
32+
ref: ${{ github.event.workflow_run.head_branch }}
33+
fetch-depth: 0
34+
35+
- name: Use Node.js 24
36+
uses: actions/setup-node@v4
37+
with:
38+
node-version: '24.x'
39+
cache: 'npm'
40+
41+
- name: Enable linger (Linux only)
42+
if: runner.os == 'Linux'
43+
run: loginctl enable-linger $(whoami)
44+
45+
- run: npm ci
46+
47+
- name: Fetch and filter failure logs from all shards
48+
id: fetch-logs
49+
env:
50+
GH_TOKEN: ${{ github.token }}
51+
run: |
52+
REPO="${GITHUB_REPOSITORY}"
53+
RUN_ID="${{ github.event.workflow_run.id }}"
54+
55+
gh api "repos/${REPO}/actions/runs/${RUN_ID}/jobs" --paginate > all_jobs.json
56+
FAILED_JOB_IDS=$(jq -r '.jobs[] | select(.conclusion == "failure") | .id' all_jobs.json)
57+
58+
: > failed_logs.txt
59+
FAILED_TEST_FILES=""
60+
61+
for job_id in $FAILED_JOB_IDS; do
62+
JOB_NAME=$(jq -r ".jobs[] | select(.id == $job_id) | .name" all_jobs.json)
63+
echo "=== Failed Shard: ${JOB_NAME} ===" >> failed_logs.txt
64+
65+
gh api "repos/${REPO}/actions/jobs/${job_id}/logs" > raw_log.txt 2>/dev/null || {
66+
echo "[Could not fetch logs for job ${job_id}]" >> failed_logs.txt
67+
continue
68+
}
69+
70+
# "Failed Tests" is always the last section in vitest output —
71+
# take from its line to EOF so we capture all failures without a hard limit.
72+
START_LINE=$(grep -n "Failed Tests" raw_log.txt | tail -1 | cut -d: -f1)
73+
if [ -n "$START_LINE" ]; then
74+
CONTEXT_START=$((START_LINE > 5 ? START_LINE - 5 : 1))
75+
tail -n +"$CONTEXT_START" raw_log.txt >> failed_logs.txt
76+
else
77+
echo "[No 'Failed Tests' section found — showing last 100 lines]" >> failed_logs.txt
78+
tail -100 raw_log.txt >> failed_logs.txt
79+
fi
80+
echo "" >> failed_logs.txt
81+
82+
NEW_FILES=$(grep " FAIL test/" raw_log.txt | grep -o "test/[^[:space:]>]*\.test\.ts" | sort -u | tr '\n' ' ')
83+
FAILED_TEST_FILES="${FAILED_TEST_FILES}${NEW_FILES}"
84+
done
85+
86+
FAILED_TEST_FILES=$(echo "${FAILED_TEST_FILES}" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ' | xargs)
87+
echo "failed_test_files=${FAILED_TEST_FILES}" >> "$GITHUB_OUTPUT"
88+
1789
- name: Claude Fix Failed Tests
1890
uses: anthropics/claude-code-action@v1
1991
with:
2092
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
2193
prompt: |
22-
The "CI Tests" workflow just failed.
23-
1. Analyze the logs from the last failed run.
24-
2. Identify the root cause of the test failure.
25-
3. Implement a fix and create a new pull request.
26-
27-
94+
The "${{ github.event.workflow_run.name }}" workflow failed on branch "${{ github.event.workflow_run.head_branch }}".
95+
96+
Failing test files: ${{ steps.fetch-logs.outputs.failed_test_files }}
97+
98+
Filtered failure logs from all failed shards are in `failed_logs.txt` in the working directory. Read it first.
99+
100+
Follow these steps carefully:
101+
102+
1. **Diagnose the root cause**: Read `failed_logs.txt` thoroughly. Before writing a single line of code, understand WHY the tests are failing — not just the symptom. Read the relevant source files to understand the existing architecture and patterns.
103+
104+
2. **Implement an architecturally sound fix**: The fix must address the underlying root cause, match existing code patterns in this codebase, and introduce no workarounds or hacks. Do not mask symptoms.
105+
106+
3. **Verify each fix**: For each failing test file, run:
107+
```
108+
npm run test -- <test_file_path> --no-file-parallelism --disable-console-intercept
109+
```
110+
On macOS, prepend `CI=true` and ensure `/opt/homebrew/bin` is in PATH.
111+
112+
4. **Iterate until all tests pass**: If tests still fail after a fix, re-read the output, deepen your understanding of the root cause, and refine the fix. Do not give up and do not apply increasingly speculative patches. Each iteration must be grounded in analysis.
113+
114+
5. **Create a PR only after all failing tests pass**:
115+
- Create a new branch from "${{ github.event.workflow_run.head_branch }}" (e.g. `fix/claude-auto-<short-description>`)
116+
- Commit the changes with a descriptive message explaining the root cause and fix
117+
- Open a pull request targeting "${{ github.event.workflow_run.head_branch }}"
28118
29119
additional_permissions: |
30120
actions: read

0 commit comments

Comments
 (0)