Skip to content
Latchkey LogoLatchkey home

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

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.

The catalog split: 4,555 rows, 760 carrying a failure class, 762 bundled offline by class file
What is in the committed catalog, counted from references/index.tsv on 2026-09-21 rather than quoted from the README.

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 fileRows pointing at itWhat it holds
self-healable-errors/network.md257Timeouts, resets, 5xx, 429, failed downloads
self-healable-errors/setup.md135Missing commands, extensions and runtime versions
self-healable-errors/memory.md120Exit 137, OOM kills, heap exhaustion
self-healable-errors/timeout.md96Hung steps and exceeded time limits
self-healing-playbooks.md90The transient and mechanical failures, and how each is handled
self-healable-errors/disk.md37No space left on device, mid-build write errors
self-healable-errors/other.md27Everything classed but not in the five above

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.

ClassRows in the catalogWhat the failure actually isThe right response
network311A momentary blip, not a bugRetry with backoff; authenticate or cache the source
setup150A missing dependency, not a bugInstall or pin the tool durably, in the image or a step
memory128A resource ceiling, not a bugRaise the limit or the runner size; cap parallelism
timeout105Usually transientRetry; debug only if it reproduces deterministically
disk44A capacity ceiling, not a bugPrune caches and layers, reclaim space, bigger disk
No class3,795A real bugFix the root cause; no retry will save it

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.

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

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, which carries its own recorded run.

Frequently asked questions

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.

Related guides

References

CI Doctor names the failure class. A Latchkey runner acts on it while the job is still running. Start free → 30-day trial · No credit card