From dev-workflow
Fix failed GitHub Actions CI checks on a pull request. Fetches failure logs, identifies which failures were introduced by the PR (vs pre-existing on the base branch), evaluates whether failing tests are worth keeping or should be restructured/removed, applies fixes, verifies locally, pushes, and repeats until CI is green. Use this skill when the user says "fix CI", "fix the build", "CI is failing", "tests are failing on this PR", "get CI green", "fix GitHub Actions", or references a PR URL with failing checks. Also trigger when the user pastes a PR link and says "checks are red", "pipeline broke", or "why is this failing".
How this skill is triggered — by the user, by Claude, or both
Slash command
/dev-workflow:pr-fix-ciThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Iteratively fix failed GitHub Actions checks on a pull request until all
Iteratively fix failed GitHub Actions checks on a pull request until all CI jobs pass. This goes beyond mechanical fixes — it evaluates whether failing tests are actually useful and proposes restructuring or removal when tests are brittle, test phantom behavior, or don't meaningfully contribute to code quality.
The user provides one of:
https://github.com/owner/repo/pulls/13)Extract the owner, repo, and PR number from whichever form is given.
Repeat the following cycle until all CI checks pass.
Fetch in parallel:
gh pr view <N> --json title,body,headRefName,baseRefName,state --repo <owner>/<repo>
gh pr checks <N> --repo <owner>/<repo>
Note: gh pr checks exits with code 1 when any check fails — this is
expected, not an error.
Not all failing checks are fixable. Separate them:
github.com/.../actions/runs/....app.netlify.com, vercel.com). Skip these entirely.If no actionable failures exist (only external checks failing), report this to the user and stop.
Before analyzing failures, make sure the PR branch is up to date with its base branch. Many CI failures are pre-existing issues that have already been fixed on the base branch — rebasing picks up those fixes for free and avoids duplicating work.
git fetch origin <baseRefName>
git checkout <headRefName>
git rebase origin/<baseRefName>
git push origin <headRefName> --force-with-lease
If the rebase has conflicts, resolve them. After pushing, a new CI run will start. Wait for it to complete (step 9), then re-evaluate. If CI is now green, you're done. If failures remain, continue to step 4.
For each failed GitHub Actions job:
gh run view <run_id> --repo <owner>/<repo> --json jobs \
--jq '.jobs[] | select(.conclusion == "failure") | {name, conclusion, steps: [.steps[] | select(.conclusion == "failure") | {name, conclusion}]}'
gh api repos/<owner>/<repo>/actions/jobs/<job_id>/logs
This is the most important step. For each failure, read the failing code and its test/source context, then decide:
Look for these red flags:
expect(routes).toHaveLength(11)) that break
every time a new item is added. The count doesn't test behavior — it tests
that nothing changed. Replace with assertions on specific items.IntersectionObserver, matchMedia, ResizeObserver) that
other test files in the project already set up. Copy the established
mocking pattern.When you think a test should be restructured or removed but aren't certain, use AskUserQuestion to check. Frame it concisely:
"The test
it('shows only enabled service pages')assumes ServicesOverview reads from the route config, but the component uses hardcoded data. This test can never pass as written. Should I remove it, or rewrite it to test what the component actually does?"
Bias toward asking when:
HOME-04) suggesting someone
specifically wanted this coverageEdit files directly. Prefer Edit for surgical changes, Write only for
full rewrites. When restructuring tests:
toContain for specific
items) and remove assertions that test quantity (e.g. toHaveLength)Run the full test suite, not just the previously-failing files. A fix in one file can break another.
# Run all tests
npm test # or npx vitest run, npx jest, etc.
# Build
npm run build
# Lint changed files (to avoid noise from unrelated files)
npx eslint <changed-files>
Discover the right commands from package.json scripts, the CI workflow
file (.github/workflows/*.yml), or the project's CLAUDE.md. Don't assume
a specific test runner.
If verification fails, fix the new failure before proceeding.
Stage only the files you changed. Write a descriptive commit message that explains what was fixed and why, not just "fix tests".
git add <specific-files>
git commit -m "<message>"
git push origin <branch>
If the push is rejected because the remote has new commits, pull with rebase first:
git stash # if dirty working tree
git pull --rebase origin <branch>
# resolve any conflicts
git stash pop
git push origin <branch>
Poll until the new CI run completes:
# Find the latest run on this branch
gh run list --repo <owner>/<repo> --branch <branch> --limit 1 \
--json databaseId,status,conclusion
# Once you have the run ID, check its status
gh run view <run_id> --repo <owner>/<repo> \
--json status,conclusion,jobs \
--jq '{status, conclusion, jobs: [.jobs[] | {name, status, conclusion}]}'
Wait 60 seconds between polls. CI typically takes 1-3 minutes for most projects.
Once CI completes:
Also re-check the full PR checks (step 2) since external checks may have updated too.
When all actionable CI checks are green, report:
.github/workflows/*.yml) early to understand
what commands CI runs — they may differ from local dev commands.vitest.config.js setup files,
jest.setup.js) and fix it there instead of per-file.toHaveLength, toBe(N) where N is a count) and fix them proactively
to avoid another round.npx claudepluginhub magic-co/claude-plugins --plugin dev-workflowProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.