Inspiration
Code generation is solved; getting a change safely into production is not. Small teams in regulated UK businesses (payments, health, insurance) still need proof that every production change was reviewed, tested, scanned and approved, plus a rollback plan when it touches something risky. That proof is usually assembled by hand when an auditor asks, months later. Meanwhile AI-written merge requests pile up behind failing scans and coverage gaps that a person has to fix. I wanted the pipeline to produce the evidence and the agents to do the fixing, with a human approving outcomes rather than every step.
What it does
Each merge request pipeline produces a change passport: a JSON record of the
tests that ran, diff coverage for the changed lines, security findings in the
changed files, and policy rules from .passport/policy.yml (for example, changes
to pricing logic need a rollback plan and a customer impact statement in the MR).
It carries a sha256 digest, so any edit is detectable.
GitLab Duo flows act on it:
- Plan. When an issue is created, a flow adds acceptance criteria, a test plan, a predicted risk label and the MR checklist the policy will demand.
- Fix. When an MR pipeline fails, the MR flow reads the passport from the job log and routes. In the recorded run, the remediation lead had the security fixer replace an f-string SQL query (flagged by lint) with a parameterised one and the test engineer write 15 tests (changed-file coverage 100%). Those tests found a real bug: the amount limit was not enforced on the new path. It pushed one commit and listed the rollback plan and customer impact for the author, because agents never write those.
- Publish. When the passport is green, the flow posts a plain-English evidence
summary with the risk score and digest, and labels the MR
passport::pass. It even flagged that the author's rollback plan no longer matched the final code. A human approves and merges. - Release and monitor. The main pipeline builds the image, deploys that exact image to staging on Render, runs synthetic checks (health, commit match, known quote, p95 latency), waits for a person to run the manual production job, checks again and creates a GitLab Release with the passport attached. The release flow posts a summary, or opens an incident with the monitoring evidence if checks fail.
- Audit. The Passport Auditor agent answers "show me the evidence for this change" in Duo Chat from passports, pipelines and notes, and says plainly what it cannot prove (for example, a missing reviewer approval).
How I built it
- Passport engine (Python,
src/change_passport/): parses unified diffs, GitLab security reports, JUnit and Cobertura, maps uncovered changed lines to functions with the Python AST, scores risk, renders Markdown and computes the digest. It runs as a CLI in CI and as a dependency-free MCP server. - Flows: written against the flow registry v1 schema and checked by an offline
validator (
tools/validate_flows.py) for custom-flow restrictions, tool names, prompt placeholders, router graph, and the things GitLab.com's flow editor rejects (HTML comments and tags in prompts,max_cycles). - Pipeline:
.gitlab-ci.ymlwith SAST and Secret Detection, the passport gate, a Docker build to the GitLab registry, deploys to Render that pull the image by commit SHA (Cloud Run with keyless OIDC is also supported), and a post-deploy monitor. - Sample app: a FastAPI service that quotes card-acceptance fees in pence for a UK payments business, plus demo patches that drive the story.
- 114 automated tests cover the engine, CLI, MCP protocol, flow validator, CI config and monitor.
Challenges I ran into
- GitLab.com's flow editor rejected my first flows: HTML comments and
<placeholder>text in prompts fail its prompt security check, andmax_cyclesis not accepted. I added both rules to the offline validator so they fail locally first. - GitLab does not trigger flows from a pipeline that a flow's own push started. I first saw it as a stalled loop. It is a sensible guard, so the author re-runs the pipeline after each agent commit, which keeps a human between every agent change and the next verdict.
- The agent's first fix passed tests but failed
ruff format --check, so the second round failed on lint. The MR flow now formats before it commits. - Evidence has to be trustworthy: an LLM summarising logs is not audit evidence, so the evidence is computed deterministically and the agents only act on it.
Accomplishments that I'm proud of
- A real end-to-end run on MR !1: failing passport, two agent remediation rounds,
the author filling in the human-only sections, a green passport (risk 64, high)
and a merge. The run is documented in
docs/evidence/. - The test agent's new tests caught a genuine bug in the "AI-written" feature.
- Diff coverage and untested-function detection tell the test agent exactly which tests to write.
- A tamper-evident passport that anyone can verify with one command.
What I learned
- Flows, triggers and service accounts are separate pieces: a flow does nothing until a trigger is enabled and its service account is a project member.
- A supervisor with narrow sub-agents worked better than one large prompt, and a one-word gate made routing dependable.
- Wherever a prompt was doing arithmetic or parsing, deterministic code did it better.
What's next
- Sign passports with Sigstore keyless signing using the same GitLab OIDC token.
- Policy packs for ISO 27001 Annex A 8.32 and SOC 2 CC8.1 change management.
- Multi-service passports and merge-train awareness.
- Publish the flows in the AI Catalog for other teams.
Log in or sign up for Devpost to join the conversation.