Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Every row below is one installable skill (`skills/<name>/SKILL.md`, which is wha
| `parallel-review` | Build a reviewer roster, dispatch it in parallel over a local diff, and merge to one ranked list. The merge is not done while any reviewer is outstanding |
| `review-pr` | Deep anti-slop review of a GitHub PR with critic-pass filtering, persistent multi-round state, and automatic GitHub submission; self-authored PRs receive the same assessment as a comment |
| `fix-pr-review` | Triage and fix CodeRabbit / `review-pr` findings, then reply + resolve PR conversations |
| `fix-ci` | Fix a red check on any PR. Read the failing log first, rerun a flake at most once per run counting earlier attempts, stop when the base is red too, fix forward without force-push or a weakened check, run `done`, push behind a `preflight-mutations` card, and stop after two rounds |
| `audit-ticket` | Audit a stale GitHub issue against current code, giving per-requirement verdicts with file:line evidence that is re-checked before printing, then update, sunset, or split it |
| `fix-ts-errors` | Fix TypeScript errors and loop the **workspace** type-check until it exits 0. A file whose squiggles cleared is not green |
| `grill-me` | Interview-style stress-test of a plan, one decision at a time, then verification of every factual claim against the codebase. No "grill complete" until every decision has an answer and every claim is checked |
Expand Down Expand Up @@ -121,6 +122,7 @@ Several skills use progressive disclosure. `SKILL.md` holds the spine, and branc
/parallel-review # Review locally-changed code
/review-pr <pr-url> # Review a GitHub PR (or several at once in batch mode)
/fix-pr-review # Triage and apply CodeRabbit / review-pr findings
/fix-ci <pr-url> # Fix a red CI check on a PR, two rounds at most
/audit-ticket <n> # Audit a stale issue against current code, then update or sunset it
/fix-ts-errors # Fix TypeScript errors, loop until the workspace check is green
/browser-qa # Drive a UI flow in a real browser, or run endpoint QA with tabulated results
Expand Down
8 changes: 1 addition & 7 deletions reference/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,13 +418,7 @@ Stop and ask anyway when:

**Green CI, not an open PR, marks the end of the task.** Once `/file-pr` returns, watch the checks and drive them to green without waiting for instructions. Run `gh pr checks <number> --watch --fail-fast` through a backgrounded Bash call so the session remains usable. Never park a turn on a blocking wait.

When a check goes red:

- **Read the failing job's log before touching anything.** Run `gh run view <run-id> --log-failed`. The failure class decides the response, and guessing wastes a whole round.
- **Fix forward, never force-push.** New commits on the same branch. Force-push stays on the stop-and-ask list above, and some remotes reject it outright.
- **Never reach green by weakening the check.** Do not delete or skip the failing test, loosen a threshold, add an ignore directive, or drop a file from lint. A red check is a finding. If the test is right and the change is wrong, fix the change. If the test is genuinely wrong, fix it deliberately and say so. Never change a test merely to reach green.
- **Flaky tests and infrastructure failures get exactly one rerun.** Use `gh run rerun --failed`. Treat a second failure as real. Investigate the code instead of rerunning again.
- **Two fix rounds, then stop.** Push a fix, wait for the rerun; if the second full run is still red, stop and report what failed, what you tried, and what you'd try next. Don't stack a third patch.
When a check goes red, on my PR or anyone's, run `/fix-ci`. It owns reading the failing log first, the one-rerun budget, fixing forward without force-push or a weakened check, the preflight before pushing, and the stop after two fix rounds.

Every condition that would have stopped me before the PR still stops me afterward. This includes a fix that needs a migration, a fix that grows beyond the request, or a failure that rejects the approach rather than the code. `/done` already enforces the same rule. Its post-publication run consumes `file-pr`'s evidence and cannot reach `ready` while CI remains unverified. Red checks leave the task incomplete whether I notice them or not. Always report the CI state. "Opened PR #N" without the check result is an unfinished report.

Expand Down
6 changes: 4 additions & 2 deletions skills/done/references/readiness-card.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ Preserve every required subcheck inside its facet. Aggregate to the weakest stat

Subject only to the `ready-to-publish` exception below, set final **Readiness** to `ready` only when every request row and required lane is `verified` and every evidence facet is `verified` or `not-applicable`. Any required `pending`, `assumed`, `deferred`, or `blocked` row yields `not ready` and sets the **Evidence ceiling**.

For Git work whose only remaining evidence requires a PR to exist, `ready-to-publish` is allowed when every pre-publication request, lane, and evidence row is `verified` or an allowed `not-applicable`, and every unresolved required row is `pending` with an explicit `PR-dependent` gap. Any unresolved `assumed`, `deferred`, or `blocked` row prevents this transition. `ready-to-publish` authorizes only `file-pr`; it is not task completion and must not be reported as such.
For Git work whose only remaining evidence requires a PR to exist, `ready-to-publish` is allowed when every pre-publication request, lane, and evidence row is `verified` or an allowed `not-applicable`, and every unresolved required row is `pending` with an explicit `PR-dependent` gap. Any unresolved `assumed`, `deferred`, or `blocked` row prevents this transition. `ready-to-publish` authorizes only `file-pr`; it is not task completion and must not be reported as such. The one exception is a run `fix-ci` started for a PR that already exists: there it authorizes only `fix-ci`'s commit, push, and watch, and never `file-pr`.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

For `not ready`, set **Exact next action** to the first dependency-ready unresolved action, naming its target. Do not select a later task whose prerequisite remains unresolved. For `ready-to-publish`, name invocation of `file-pr` for the recorded branch. For `ready`, use `none`.
For `not ready`, set **Exact next action** to the first dependency-ready unresolved action, naming its target. Do not select a later task whose prerequisite remains unresolved. For `ready-to-publish`, name invocation of `file-pr` for the recorded branch, or `fix-ci` step 6's commit when `fix-ci` started the run. For `ready`, use `none`.

Render the card with the applicable currency header before the three tables. Git work uses:

Expand Down Expand Up @@ -170,6 +170,8 @@ The post-publication card ends with the `Left after this merges:` line from `fil

New unresolved review threads or review-body findings on the PR, including ones a PR-watch update reports, go through `fix-pr-review` with the review or discussion URL (`#pullrequestreview-` or `#discussion_r`), not the bare PR URL. This post-publication handoff authorizes `fix-pr-review` to execute. Never edit, reply to, or resolve them inline. A thread left open as NEEDS-INPUT keeps the card from final `ready`.

A required check that is red on the PR goes through `fix-ci` with the PR URL. This post-publication handoff authorizes `fix-ci` to execute. Never rerun or patch a red check inline. A check `fix-ci` leaves red keeps the card from final `ready`.

Before final `ready`, repeat the post-commit content seal:

```bash
Expand Down
102 changes: 102 additions & 0 deletions skills/fix-ci/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: fix-ci
description: "Fix a failing CI check on an existing GitHub PR: read the failing log, rerun once only for a flake, fix forward, verify with done, push behind a preflight, and watch the next run. Use when asked to fix ci, when ci is failing or a check is red on a PR or PR URL, or when a check goes red after file-pr."
---

# Fix failing CI on a PR

This skill owns what happens when a check goes red on a PR, whether the PR is the user's own, a teammate's, or one `file-pr` just opened. A red check is a finding about the change. The goal is a green run that still checks what it checked before.

The stop-and-ask conditions in the global rules' "Commit and PR autonomy" section still apply on every round.

## 1. Bind the PR

Take the PR from the URL or number the user gave, or from the current branch with `gh pr view`. Record:

```bash
gh pr view <pr> --json url,number,author,baseRefName,headRefName,headRefOid,headRepository,headRepositoryOwner,isCrossRepository,maintainerCanModify
gh api user -q .login
gh api repos/<head-owner>/<head-repo> -q .permissions.push
```

The **branch owner** is the PR author. The **head is writable** when the last command prints `true`, or when the PR is cross-repository and `maintainerCanModify` is `true`. The **expected remote SHA** starts as `headRefOid`; step 7 updates it after each push. Resolve `<head-remote>`, the configured remote whose URL points at `headRepository`; on a fork PR it differs from the base's remote. When no remote points there, stop and ask.

Check out the head branch in a clean worktree and require local `HEAD` to equal `headRefOid`. On a mismatch, stop, show both SHAs, and ask. Someone may have pushed since, or the local branch carries unpublished work.

## 2. Read the failing log before touching anything

```bash
gh pr checks <pr> --json name,state,bucket,link,workflow
gh run view <run-id> --log-failed
```

Take the run ID from each failed check's `link`. Read the log of every failed job before any rerun, edit, or push. For each one, record the job name, run ID, and the first error line.

Classify each failure. The class decides what comes next.

- **Code**: the error points at a test, lint rule, type check, or build step.
- **Flake or infrastructure**: a timeout, network or registry error, lost runner, rate limit, or cache service failure, where the error points at nothing in the diff.
- **External check**: a status from an app with no Actions run, so `gh run view` has no log. Report its name and link, and leave it out of the fix.

Send every Code and Flake failure through step 3 before acting on it. The step is done when every failed check has a class and a quoted error line.

## 3. Check whether the base is red too

For each failed job, read the base branch's latest completed run of the same workflow:

```bash
gh run list --branch <base> --workflow <workflow> --status completed --limit 1 --json databaseId,conclusion,headSha,url
gh run view <base-run-id> --log-failed
```

The failure is **base breakage** when the same job failed there with the same first error line. A same-named job that failed for another reason does not count. When no completed base run exists, as with a workflow that runs only on `pull_request`, the base is unchecked: continue, and say so in the report.

On base breakage the PR did not cause the failure. Stop. Report the PR's run URL and the base run URL, and propose filing the breakage through `file-issue`. File it only when the user says yes. Do not fix base breakage on the PR, because it mixes an unrelated change into someone's PR and leaves the base broken for every other branch.

Otherwise a Code failure goes to step 5 and a Flake failure to step 4. When a run has both, fix the Code failures first, since their push starts a new run that retries the flakes too. Rerun only when every remaining failure is a Flake.

## 4. Rerun a flake at most once per run

```bash
gh run view <run-id> --json attempt -q .attempt
```

Attempt `1` allows exactly one `gh run rerun <run-id> --failed`, then go to step 8. Attempt `2` or higher means someone already reran it, so no rerun is left. Treat the failure as code and go to step 5. A rerun that fails again is real: investigate the code instead of rerunning again.

## 5. Fix forward

Reproduce the failure locally with the failing job's own command when the project can run it. Then fix the change, not the check. Never reach green by deleting or skipping the failing test, loosening a threshold, adding an ignore directive, or dropping a file from lint. When the test is right and the change is wrong, fix the change. When the test is genuinely wrong, fix it deliberately and say so in the commit message and the report.

Fix forward with new commits on the same branch. Never amend, rebase, or force-push a published commit, including `--force-with-lease`. Some remotes reject it outright, and a teammate's checkout breaks when it lands.

The step is done when the failing command passes locally, or when the report says why it cannot run locally.

## 6. Verify and commit

Run `done` on the fix as an initial run, with the PR's base as its base. Continue on its `ready-to-publish` verdict, with only the Publication or deployment lane and the CI and Review facets pending. This skill takes that verdict in place of `file-pr`, because the PR already exists. Commit through `git-commit` in sealed-index mode against the card's **Verified content snapshot**, and record each SHA it creates in the card's **Expected append-only commits**, as `file-pr` does. Then push in step 7.

When step 1 found the head not writable, stop here. Report the local branch, the fix commit's SHA, and a suggested review comment the contributor can apply. Nothing gets pushed.

## 7. Push behind a preflight

Pushing changes a shared ref. Invoke `preflight-mutations` immediately before the push. Give it the branch, `<head-remote>`, the local SHA, the remote SHA from `git ls-remote <head-remote> refs/heads/<branch>`, and the branch owner in its ownership field.

- When the branch owner is not the user, the card's verdict is `confirmation-required`. Show `git log --oneline <remote-sha>..HEAD` and the diff stat, and wait for a yes before each push.
- When the owner is the user, the fix request authorizes the push.

When the remote SHA differs from the expected remote SHA, someone pushed meanwhile. Stop and ask. Push without force:

```bash
git push <head-remote> HEAD:refs/heads/<branch>
git ls-remote <head-remote> refs/heads/<branch>
```

When the remote rejects the push for lack of access, stop and report as step 6 does for a head that is not writable. The step is done when the read-back SHA equals local `HEAD`. That SHA becomes the expected remote SHA for the next round.

## 8. Watch the next run, two rounds at most

Start `gh pr checks <pr> --watch --fail-fast` as a background command, and wait for it through the host's background wait rather than a blocking call. A round is one pushed fix, and only a push advances the count. When the run goes green, rerun `done` on the same card as its post-publication run, with step 7's read-back as the publication evidence and the green run as the CI evidence, then report. When a rerun with no push goes red, return to step 2 in the same round. When a pushed fix's run goes red, start the next round from step 2 on the new run. When round two's run is still red, stop. Do not stack a third patch. Report what failed, what each round tried, and what to try next.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

## Report

Always state the CI state with run URLs. "Pushed a fix" without the check result is an unfinished report. For each round, give the failure class, the quoted error line, the fix commit SHA, and the push read-back. Name every external check left out and every base breakage proposed for `file-issue`.
12 changes: 12 additions & 0 deletions tools/eval/triggers.json
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@
"expect": "fix-pr-review",
"why": "the user's most common review request: a bare review URL after check, from any reviewer, routes to fix-pr-review" },

{ "utterance": "fix ci",
"expect": "fix-ci",
"why": "shortest form of the most common phrasing in the November 2026 session audit (issue #106)" },

{ "utterance": "ci check failing https://github.com/acme/shop/pull/42",
"expect": "fix-ci",
"why": "a bare PR URL with a failing check, often a teammate's PR, routes to fix-ci rather than a review skill" },

{ "utterance": "fix the review comments on https://github.com/acme/shop/pull/42",
"expect": "fix-pr-review",
"why": "COLLISION: a PR with review findings and no CI mention stays with fix-pr-review; fix-ci must not take it" },

{ "utterance": "resolve conflicts on https://github.com/acme/shop/pull/42, it can't merge into dev",
"expect": "resolving-merge-conflicts",
"why": "a conflict request before any merge has started still routes here; the skill starts the merge itself" },
Expand Down
Loading