ci-fix-loop · git:20260824.c71bceb · 2026-08-24 · sha256 7e83617bb4152118

ci-fix-loop git:20260824.c71bcebA

Immutable. This exact content never changes and is served at /api/v1/blob/7e83617bb4152118.

---
name: ci-fix-loop
description: >
  Diagnose GitHub Actions CI failures in a loop: fetch the failing check logs, identify
  the root cause, apply a targeted fix, push, and wait for the next run — repeating until
  all checks are green or a real blocker needs a human. Use when CI is red after a push,
  when the user says "fix CI", "make tests pass", "diagnose CI failures", or "drive this
  to green". Direct analog of qodoloop / coderabbitloop for automated test and lint failures.
license: MIT
allowed-tools: Bash, Read, Write, Edit
compatibility: Codex, Claude Code, Cursor, GitHub Copilot, Windsurf, Kiro, and other Agent Skills compatible tools. Requires git and gh (GitHub CLI) authenticated, with a GitHub Actions workflow configured.
metadata:
  targets: [_source-only]
  author: Oleg Koval
  tags:
    - github-actions
    - ci
    - testing
    - automation
    - loop
    - fix
source: weekly-pattern-learner
source_reason: "qodoloop and coderabbitloop share the push→poll→diagnose→fix→repeat loop; CI failures have the same shape but no dedicated skill existed"
source_date: "2026-07-28"
---

> 🤖 *Auto-generated by **weekly-pattern-learner** · qodoloop and coderabbitloop share the push→poll→diagnose→fix→repeat loop; CI failures have the same shape but no dedicated skill existed*

# CI Fix Loop

Drive a GitHub Actions CI run from red to green: fetch failure logs, diagnose, apply
a targeted fix, push, and repeat until all required checks pass or a blocker is hit.

## Inputs

- **PR number** (optional): detect from current branch if not given.
- **Check names** (optional): limit to specific failing checks instead of all.
- **Max iterations** (default: 5)

## Workflow

### 1. Identify the PR and current CI state

```bash
# Detect PR from current branch
gh pr view --json number,headRefName,headRefOid \
  -q '{number, branch: .headRefName, sha: .headRefOid}'

# Current check status
gh pr checks --json name,state,conclusion 2>/dev/null \
  || gh run list --branch <branch> --limit 5
```

Classify each check as: `pass`, `fail`, `pending`, or `skipped`.

**Exit immediately if all required checks already pass** — nothing to do.

### 2. Loop (max 5 iterations)

#### A. Identify failing checks

```bash
gh pr checks --json name,state,conclusion \
  --jq '.[] | select(.conclusion == "failure" or .state == "FAILURE")'
```

For each failing check, record: check name, workflow file, job name.

Stop if the working set is empty — all required checks are green.

#### B. Check for base-branch regression first

Before touching any code, verify the failure is not already present on `main`:

```bash
gh run list --branch <base-branch> --workflow <workflow-name> --limit 3 \
  --json conclusion --jq '.[].conclusion'
```

If the same check is failing on the base branch, **stop and report** —
this is not your PR's fault. Do not attempt a fix that masks a base-branch problem.
Wait for a `Base branch recovered` notice before re-trying.

#### C. Fetch failure logs

For each failing check:

```bash
# Latest run ID for this branch/workflow
gh run list --branch <branch> --workflow <workflow-name> --limit 1 \
  --json databaseId --jq '.[0].databaseId'

# Fetch only failing steps (not the full log)
gh run view <run-id> --log-failed
```

Cap log reading at ~200 lines per job — the error is almost always in the last
50 lines of a failed step.

#### D. Diagnose

Classify the failure before writing any code:

| Category | Examples | Fix target |
|---|---|---|
| Test failure | `FAIL TestFoo`, `AssertionError`, `expected X got Y` | Source or test |
| Lint / format | `gofmt`, `eslint`, `ruff`, `prettier` | Run auto-formatter |
| Build error | compile error, missing import, type mismatch | Source |
| Dependency | `npm ERR!`, missing module, lockfile mismatch | Update lockfile |
| CI config | YAML parse error, bad action ref, missing secret | Workflow YAML |
| Flaky test | same test passes on manual re-run | Re-run first; mark if it keeps failing |

For flaky-looking failures, re-run the check before touching code:

```bash
gh run rerun <run-id> --failed
```

Wait for the rerun result. If it passes on rerun, skip this iteration and go to
step F to poll for the new result.

#### E. Apply a targeted fix

Fix **only** what the log names as the failure. Do not refactor surrounding code,
add unrelated tests, or touch files the failing step never mentioned.

Common one-liners:

```bash
# Go format
gofmt -w .
goimports -w .

# Node format / lint
npx prettier --write .
npx eslint --fix .

# Python
ruff check --fix .
black .

# Lockfile
npm install          # Node
poetry lock --no-update  # Python
go mod tidy          # Go
```

For test failures: read the failing test and the code it exercises. Fix the code
or the assertion — but not both in one commit unless the test was clearly wrong
and the fix is trivial.

#### F. Commit and push

Stage **only the files touched in step E** — never `git add -A`, which can sweep
in unrelated local changes:

```bash
git add <files touched>
git commit -m "fix(ci): <what was broken> (ci-fix-loop iteration N)"
git push
```

Confirm the push succeeded before polling. A resolved check whose fix never reached
the branch is worse than an unresolved one.

#### G. Wait for CI on the new SHA

Poll every ~15s (timeout ~8min) for all previously-failing checks to complete
on the new push:

```bash
gh pr checks --json name,state,conclusion,startedAt
```

Wait until all previously-failing checks show a new result (`pass` or `fail`)
on the current SHA. Do not act on a result that still shows the old SHA.

Go back to step A.

### 3. Report

| Field | Value |
|---|---|
| Iterations | N |
| Checks fixed | names + root cause |
| Checks blocked | names + reason (flaky / needs human / base-branch red) |
| Final status | all green / partial / blocked |

```
CI fix loop complete.
  Iterations:  2
  Fixed:       lint (gofmt: 3 files), test (TestUserCreate — nil pointer in fixture)
  Blocked:     none
  Status:      all required checks green
```

## Chaining

| Before this skill | After this skill |
|---|---|
| `olko:pr-description-writer` | `olko:qodoloop` |
| Any `git push` with failing CI | `olko:coderabbitloop` |