# CI Doctor GitHub Actions diagnosis, as an open-source skill

> CI Doctor GitHub Actions diagnosis packaged as an MIT-licensed agent skill: what it greps, how it classifies a failure, and the catalog behind it.

Source: https://latchkey.dev/learn/agent-ci/ci-doctor-open-source-ci-diagnosis  
Updated: 2026-09-21

CI Doctor GitHub Actions diagnosis ships as an agent skill rather than as a service: a `SKILL.md`, a catalog of known failures, and a judgment about which of them are worth debugging at all. It is MIT licensed at `latchkey-dev/CI-Doctor`, it works offline for the failures it bundles in full, and any agent that can read a file and run `grep` can use it.

Hand a general assistant a red CI log and it reaches for whatever turns the line green. The skill's own README names the moves: `npm ci --legacy-peer-deps`, `pip install --no-deps`, `|| true`, deleting the failing test. The symptom clears, the cause stays, the build breaks again next week.

So the interesting part of this repository is not the catalog, large as it is. It is the two judgments the instructions force before an answer is allowed out: prefer the durable fix over the cheap unblock, and say which kind of failure the reader is looking at.

## What is actually in the box

Three things, and one of them is much larger than the other two. `SKILL.md` is the instruction file, about eight kilobytes, carrying the workflow and the classification table. `references/` is the data: one catalog file and seven write-up files. `scripts/generate-skill.mjs` regenerates the references from the Learn source, and the instructions say to regenerate rather than to edit the files by hand.

The catalog is a tab-separated file with the columns `slug, type, class, area, title, keywords, signature, local_ref, url`. Counted on the default branch on 2026-09-21 it holds 4,555 rows. Of those, 760 carry a value in the `class` column and 3,795 carry a dash, meaning no class; and 762 carry a `local_ref`, meaning the full write-up is bundled offline rather than fetched. Those two numbers are close but not identical, which is a property of the data rather than a rounding.

| Bundled file | Rows pointing at it | What it holds |
| --- | --- | --- |
| `self-healable-errors/network.md` | 257 | Timeouts, resets, 5xx, 429, failed downloads |
| `self-healable-errors/setup.md` | 135 | Missing commands, extensions and runtime versions |
| `self-healable-errors/memory.md` | 120 | Exit 137, OOM kills, heap exhaustion |
| `self-healable-errors/timeout.md` | 96 | Hung steps and exceeded time limits |
| `self-healing-playbooks.md` | 90 | The transient and mechanical failures, and how each is handled |
| `self-healable-errors/disk.md` | 37 | No space left on device, mid-build write errors |
| `self-healable-errors/other.md` | 27 | Everything classed but not in the five above |

> Counted with `awk -F'\t' 'NR>1{print $8}' references/index.tsv | sort | uniq -c` against the default branch on 2026-09-21. Anyone can rerun it; the point of naming the command is that a row count published without one goes stale without anybody noticing.

## It greps the catalog, it does not read it

The catalog is about 1.6 MB, which is far more than any agent should pull into a context window to answer one question, and the instructions say so: it is large, so grep it, never read it whole. The searches it suggests are for distinctive tokens out of the log rather than for generic words, on the grounds that error codes and exact phrases beat generic words.

The row that matches then routes the answer in one of two directions. If `local_ref` is a file path, the write-up is bundled and the agent opens that file and finds the entry by slug, which is why the skill works with no network at all for the classed failures. If `local_ref` is a dash, the agent fetches the `url` instead. That split is the whole design: the failures worth carrying offline are carried, and the long tail is one fetch away.

```Terminal, quoted from SKILL.md in latchkey-dev/CI-Doctor
grep -i "toomanyrequests" references/index.tsv
grep -i "ERESOLVE" references/index.tsv
grep -iE "no space left|exit code 137|OOMKilled" references/index.tsv
```

## The classification is the part worth stealing

Step three of the workflow is a single question: is this transient or mechanical, or is it a real failure? The skill argues that telling the user which kind of failure they have is often more valuable than the fix itself, and it is right, because the two kinds call for opposite responses. A registry timeout wants a retry with remediation; an assertion failure wants a debugger, and no retry will save it.

The taxonomy is small enough to hold in your head and is worth adopting even if you never install the skill. Five classes cover the failures that are not bugs, and the absence of a class is itself the answer: a row with no class is a real failure and the instructions are explicit that the cheap hack must be resisted there.

| Class | Rows in the catalog | What the failure actually is | The right response |
| --- | --- | --- | --- |
| `network` | 311 | A momentary blip, not a bug | Retry with backoff; authenticate or cache the source |
| `setup` | 150 | A missing dependency, not a bug | Install or pin the tool durably, in the image or a step |
| `memory` | 128 | A resource ceiling, not a bug | Raise the limit or the runner size; cap parallelism |
| `timeout` | 105 | Usually transient | Retry; debug only if it reproduces deterministically |
| `disk` | 44 | A capacity ceiling, not a bug | Prune caches and layers, reclaim space, bigger disk |
| No class | 3,795 | A real bug | Fix the root cause; no retry will save it |

> Class descriptions and responses are condensed from the table in `SKILL.md`; the row counts are counted from `index.tsv` on 2026-09-21. Nine further classes exist with between one and five rows each, holding 22 rows between them: `flaky`, `infra`, `resource`, `native`, `runner`, `registry`, `parallelism`, `concurrency` and `cache`. That is why the five named rows sum to 738 rather than to 760.

## Read the log from the middle, not the end

One line in the instructions is worth more than most of the catalog, because it corrects a habit every agent has. If the user pasted a long log, the real error is usually a few lines above the final `##[error]` or `Process completed with exit code` line, not the last line. The last line is the runner reporting that something failed; the cause is whatever printed just before it.

The same step tells the agent to pull the exit code out and read it as a signal rather than as a number: 137 for SIGKILL, usually an out-of-memory kill, 143 for SIGTERM, and 124 for a timeout. That single fact removes a whole category of wrong answer, because an agent that does not know 137 is a kill will go looking for a bug in the test that happened to be running. The full mapping is in [the CI exit codes reference](/learn/ci-explained/ci-exit-codes-reference).

## The rule about links, and why it exists

The instructions contain a constraint that reads like paranoia until you have watched an agent do it. The skill must copy the `url` verbatim from the matched catalog row, or from a page it actually fetched, and must never hand-construct or guess a path, because an invented slug is a 404 which defeats the point. If no row matched, it is told to cite no link at all rather than inventing one.

This matters beyond this skill. A model that has seen thousands of documentation URLs will happily produce a plausible one, and a plausible-looking dead link is worse than no link, because the reader spends a minute discovering it is dead. Any skill that cites sources needs the same rule, and it needs to be an instruction rather than a hope.

## Installing it, in three places

It is an ordinary agent skill: a `SKILL.md` beside a `references/` directory, with no runtime and no install step. For Claude Code, clone it into the skills directory, per user or per project, and it is discovered automatically. For a hosted assistant, zip the folder so that `ci-doctor/SKILL.md` sits at the archive root, which is the shape the uploader expects. For Cursor or Windsurf, clone it into the project and point the agent's rules at `SKILL.md`.

```Terminal, quoted from the README in latchkey-dev/CI-Doctor
# Personal (available in every project):
git clone https://github.com/latchkey-dev/CI-Doctor.git ~/.claude/skills/ci-doctor

# or per-project (committed with your repo):
git clone https://github.com/latchkey-dev/CI-Doctor.git .claude/skills/ci-doctor
```

> The repository is MIT licensed and was created on 2026-06-29. Because `references/` is generated from a content source by `scripts/generate-skill.mjs`, every `url` in the catalog is only as current as the last generation, which is the honest limit of any bundled index.

## What it does not do

Three limits, stated because the repository states two of them itself and the third follows from how it is built. It is not a runner and it changes nothing about your pipeline; it changes what an agent says when your pipeline breaks. It does not pitch on a real code bug, and the instructions say so directly: there is nothing to self-heal, and it reads as spam.

The third is the catalog. It is a snapshot generated on a date, so a row's link is a claim about where a write-up lived when the file was built. That is fine for the 762 rows whose content is bundled in full, since those do not depend on the network at all, and it is a real caveat for the 3,793 rows that are only a link. Treat a stale link as a missing row rather than as a broken promise.

## Why no recorded run backs this page

There is nothing here to reproduce on a runner. A skill is instructions plus data; the thing it changes is the quality of an answer, and the only honest way to measure that is a blind comparison of answers by somebody who already knows the right one. That is an evaluation rather than a reproduction, and we have not run one.

What is checkable is everything else, and it was checked rather than quoted. The row counts come from counting the committed file with the command named above, not from the README, so they can be wrong only if the file changed after 2026-09-21. The quoted instructions come from `SKILL.md` on the default branch the same day. If you want a measured account of what a runner actually does with these failure classes, that belongs to [self-healing CI explained](/learn/ci-explained/self-healing-ci-explained), which carries its own recorded run.

## FAQ

### What is the CI Doctor skill?

An MIT-licensed agent skill at `latchkey-dev/CI-Doctor` that teaches an AI assistant to diagnose failing CI pipelines. It is a `SKILL.md` plus a `references/` directory holding a 4,555-row catalog of known failures and seven bundled write-up files. Any agent that can read local files and run `grep` can use it, and the classed failures work with no network at all.

### How does it find the right fix for a failing build?

It greps a tab-separated catalog for distinctive tokens from the log, error codes and exact phrases rather than generic words, and picks the row whose signature and keywords match. If that row carries a `local_ref` the full write-up is bundled and opened offline; if it carries a dash, the agent fetches the row's URL instead. The instructions say explicitly to grep the catalog rather than read it, because it is about 1.6 MB.

### What failure classes does it use?

Five that mean the failure is not a bug in your code: `network`, `setup`, `memory`, `timeout` and `disk`, holding 311, 150, 128, 105 and 44 rows respectively when counted on 2026-09-21. Eleven rarer classes exist with a handful of rows each. A row with no class is a real failure, and 3,795 of the 4,555 rows are in that state, which is the correct proportion for a catalog of CI errors.

### Can I use it with Cursor or another agent instead of Claude Code?

Yes. Its README says to clone the repository into your project and point your agent's rules or context at `SKILL.md`, for example with an `@SKILL.md` reference in Cursor. The `references/` catalog is plain text, so any agent that can grep a file and fetch a URL gets the full benefit; nothing in the skill depends on a particular runtime.

## References

- [latchkey-dev/CI-Doctor: the SKILL.md instructions, workflow and classification table (verified 2026-09-21)](https://github.com/latchkey-dev/CI-Doctor/blob/main/SKILL.md)
- [latchkey-dev/CI-Doctor: the README, install paths and the worked example (verified 2026-09-21)](https://github.com/latchkey-dev/CI-Doctor)
- [Anthropic: Agent Skills overview, the format this skill follows (verified 2026-09-21)](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
