Inspiration

The hackathon's theme said it best: code generation is solved — what happens next is the challenge. AI can already write code. The boring, stressful part is everything that comes after: a pipeline goes red at 2 a.m., a test breaks, a dependency goes missing, and a human has to drop everything to read logs and patch it.

I asked a simple question: what if the pipeline could fix itself?

That became Self-Healing Pipeline Agent — a project where a broken build doesn't wait for a human. The pipeline notices the failure, works out what went wrong, and either fixes it on its own or hands it to GitLab Duo for a smarter, code-level fix. You push code and walk away; the pipeline works to get itself back to green.


What it does

When CI fails, the project diagnoses the failure and heals it along two honest paths:

Path Failure type Who fixes it
Deterministic Lint (messy code), missing dependency Our own script fixes it and opens a merge request
AI-powered Test regression, build/syntax error GitLab Duo (Fix CI/CD Pipeline Flow) writes the code-level fix

The pipeline has four stages — verify → package → release → heal. The heal stage only runs when something fails. It:

  1. Reads the failed job's logs through the GitLab API.
  2. Classifies the root cause (lint, dependency, test, or build).
  3. Simple problems: runs the fix (for example ruff --fix), commits it, and opens a merge request labeled self-healing.
  4. Hard problems: hands the failure to GitLab Duo, because a logic bug needs real reasoning, not a regex.

The result is a pipeline that behaves less like a tripwire and more like a teammate.


How we built it

The sample app. A tiny Flask calculator API (add, subtract, multiply, divide, plus a /health endpoint). It's deliberately small so there's a real, breakable surface to demo on.

The safety net. A pytest suite that defines "correct." These tests are the gate — breaking them is what triggers the whole healing loop.

The pipeline (.gitlab-ci.yml). Four stages:

  • verify — lint with ruff and run the tests
  • package — compile the app to catch syntax errors
  • release — start the app and check /health
  • heal — runs only when: on_failure

The robot's brain (automation/). Two focused scripts:

  • diagnose.py — a classifier that turns messy job logs into a clean JSON verdict (category, confidence, evidence).
  • heal.py — the actor. It fixes lint/dependency failures itself and defers test/build failures to GitLab Duo instead of pretending it can fix them.

The GitLab Duo integration.

  • AGENTS.md — a rule book that steers Duo's Fix CI/CD Pipeline Flow: fix the code, never the tests; never disable a pipeline stage; make the smallest change; escalate when unsure.
  • Duo's Fix pipeline with Duo flow does the actual code-level repair and proposes it back as a merge request / suggestions.

Tech stack: Python 3.12, Flask, pytest, ruff, GitLab CI/CD, and the GitLab Duo Agent Platform.


Challenges we ran into

This was a journey, and the lessons came the hard way.

  • The "slim" image had no git. The heal job crashed with No such file or directory: 'git' because python:3.12-slim ships without it. Fix: install git in the job and do a full clone so it can branch and push.

  • We fought our own .gitignore. The heal job tried to commit a file under .heal/, which we had ignored, so git add refused. A self-inflicted bug that taught us to test the whole path, not just the happy path.

  • Pushing from CI used the wrong token. The runner's default remote is read-only. We had to push with the HEAL_BOT_TOKEN via an oauth2: URL so the bot could actually open merge requests.

  • "Pending forever." Pipelines sat in pending with no runner. The lesson: a new account needs verification and available shared runners before anything runs.

  • The honesty problem (the big one). Our first design pretended a custom YAML "agent" could fix any failure, including logic bugs. It couldn't — there was no LLM behind it. We rebuilt the project to be truthful: scripts handle what scripts can (lint, dependencies), and real code fixes are delegated to GitLab Duo. The project got simpler, smaller, and far more credible.

  • The subscription wall (M3006). Duo wouldn't run on a personal account without a subscription. The breakthrough was realizing the project belonged in the hackathon's showcase group, which had Duo enabled — and sure enough, a Duo agent session ran successfully there.


Accomplishments that we're proud of

  • A pipeline that genuinely heals itself end-to-end for lint and dependency failures — no human in the loop.
  • A clean, honest split between what automation should do and what AI should do.
  • A real GitLab Duo agent session running on our project in the hackathon group.
  • Documentation anyone can follow — including a version written for a 10-year-old.

What I learned

  • Be honest about an agent's limits. The project got dramatically better the moment we stopped faking capabilities and let each tool do what it's actually good at.
  • CI debugging is its own skill. Base images, tokens, runners, and ignore files all have sharp edges that only show up in the pipeline, not on your laptop.
  • GitLab Duo is the real deal for code-level healing — but it needs the right subscription, runners, and group settings, and those are easy to miss.
  • The log always has the answer. Nearly every blocker was solved by reading the failing job's output one more time.

What's next

  • Fully hands-off healing using a Duo Custom Flow triggered on pipeline failure, so no human clicks anything.
  • More failure categories — flaky tests, timeouts, and formatting drift.
  • A real deployment to Google Cloud Run so the release stage ships, not just health-checks.
  • A loop-breaker so the agent never retries the same failed fix twice.

Built With

  • docker
  • flask
  • gitlab
  • gitlab-ci-cd
  • gitlab-duo
  • gitlabrest
  • gunicorn
  • pytest
  • python
  • requests2.32.3
  • rest-api
  • ruff
  • yaml
Share this project:

Updates

Submission history