← James Koh
A CI agent that fixes failed pipelines, and is not allowed to merge
At NCS I built a self-healing agent that runs inside GitLab CI. When a job fails, an LLM diagnoses the failure and opens a draft merge request with the fix. It is in production, fully air-gapped, and a person approves every change.
- Job fails
- LLM reads the log
- Confidence 80% or more?
- Draft MR on its own branch
- A person approves
The problem
I look after the path to production for about 300 repositories and more than 50 pipelines. When a pipeline breaks, a delivery team stops until someone reads the log, finds the cause and corrects the YAML. Many of those failures are routine, and the diagnosis is the slow part.
What I built
A two-phase agent. In phase 1 it reads the failed job's log and produces a diagnosis with a confidence score. In phase 2, when that score clears the gate, it writes the YAML fix on an isolated branch and opens a Draft merge request for the owning team.
The four decisions that matter
A confidence gate before any fix
The agent only acts when its diagnosis scores 80% or higher.
A wrong fix costs more than no fix: someone has to read it, reject it and then do the real work. Below the gate the agent stays quiet and the failure goes to a person as before.
Draft merge requests, never a merge
The fix arrives as a Draft MR on an isolated branch. The agent has no way to merge it.
In a regulated estate every change to a pipeline needs a named person behind it. A draft MR keeps the normal review and change-control path, so the agent adds speed without removing accountability.
Two phases with a hard line between them
Phase 1 only analyses failures. Phase 2 writes the draft fixes.
Reading a log is low risk. Changing a pipeline is not. Keeping the two apart means the diagnosis can be checked, tuned or switched off without touching the part that writes code.
Air-gapped, on the runners we already had
The model (Qwen3-Coder, served by Ollama) runs on CPU-only GitLab runners with no external API calls.
Build logs can contain internal hostnames, paths and code. Nothing leaves the network, and no GPU or new vendor contract was needed to start.
Stack
GitLab CI · Docker · Qwen3-Coder on Ollama · Node.js · Python
What I took from it
The hard part of putting an LLM in a pipeline is deciding what it may not do. The model was the easy choice. The gate, the draft-only rule and the air gap are what made it acceptable to run in production.
Ask me about it on LinkedInBack to my experience