Inspiration
Code generation is solved. Teams now stack gate after gate in front of production — AI code review, SAST, secret detection, dependency scanning, tests, approval rules — and hand agents more and more autonomy on the assumption that those gates work. But nobody measures whether they do. When a leader asks "can agents merge this kind of change without a human?", the honest answer is a guess.
GitLab's own Security Standard even names the missing control: verify the checks. We went looking for a product that does it, found none, and built one.
What it does
Fire Drill runs continuous fire drills on your pipeline. It plants realistic, harmless faults inside ordinary-looking merge requests on a shadow branch, lets every gate run on them exactly as it would on real work, and measures which gates catch what. Three things come out of it:
- a gate-efficacy scorecard — detection rate per fault class × gate, with Wilson confidence intervals, never a bare percentage;
- blind spots — fault classes no automated gate caught, each with a concrete fix (a custom SAST rule, a review instruction, a CODEOWNERS entry);
- an autonomy map — every change area gets an earned level, Assisted / Supervised / Hands-off, with the confidence interval behind it.
The magic moment: SAST reliably catches SQL injection, but a quietly removed ownership check sails past every automated gate — SAST, AI review, approvals — and only humans catch it. That's a blind spot you can finally see before you let an agent merge changes like it unattended.
How we built it
Fire Drill is built on the GitLab Duo Agent Platform, not beside it. It's a pnpm/TypeScript monorepo:
packages/core— the domain model: fault classes, gate matching, Wilson-interval scoring, the autonomy map, and Software Carbon Intensity. Pure and fully unit-tested.packages/gitlab— a typed GitLab REST/GraphQL client: shadow branches, drill MRs, security reports, review comments, gate inventory, and the Flows API.apps/cli— the engine. It plants a fault, opens a drill MR, explicitly starts thecode_review/v1Duo flow via the Flows API (bot-created MRs don't fire triggers), observes every gate until the window closes, judges the result, writes a record, then closes the MR and deletes the branch.apps/web— a Next.js dashboard (scorecard, autonomy map, blind spots, CI-carbon), live in demo mode so judges need no credentials.flows/— three Duo Agent Platform custom flows: Judge (did the AI reviewer catch the planted fault?), Plant (places a fault in context for adaptive drills), and Remediate (drafts a fix for a blind spot and opens an MR behind a HumanInputComponent approval step).packs/— drill packs (python-web, node-express): fault classes + planting templates.
The engine runs as a visible CI pipeline in a control project and writes every drill as a committed record, so the history is itself an audit log.
Challenges we ran into
The hard parts were all real-world GitLab integration, which is exactly the point:
- Bot MRs don't fire Duo triggers. We had to start
code_review/v1ourselves through the Flows API — and learned the hard way that itsgoalmust be the merge-request IID or URL, not prose (a 400 until we fixed it). - The SAST template's
sastjob is a config-only placeholder that exits 1. Overriding it instead of the realsemgrep-sastanalyzer meant SAST silently never scanned our MRs. Subtle, and a great example of a gate that looks green but isn't. - Shared runners require identity verification for new accounts, so we registered a self-hosted runner to run the real managed scanners.
- Making the measurement trustworthy. A tests job that failed on a missing import looked like a "catch" until we read the logs — so we made the engine judge from gate reports, not job pass/fail, and fixed the target's CI so verdicts are real.
- Keeping it safe. Drills target a shadow branch, never plant credentials, never wire a fault into a deploy path, and auto-close — a tool that plants vulnerabilities has to make accidental harm impossible.
Accomplishments we're proud of
- A real, green CI pipeline where the drill job plants faults, scores them, and writes a JUnit scorecard to the Tests tab — live automation, not a mock.
- An honest result on a well-tested target: SAST + lint catch the SQL injection, and the target's own test catches the ownership removal — so Fire Drill correctly reports that this codebase isn't blind there, while the demo workspace shows how a typical, under-tested service would be.
- Confidence intervals and an earned-autonomy ladder instead of vanity percentages.
- CI carbon per merge request, measured with the SCI framework.
What we learned
Gates that look like they work routinely don't — a green pipeline is not evidence. The only way to know your checks catch anything is to attack them on purpose and measure, continuously. "How much autonomy has this pipeline earned?" is an evidence question, and it finally has an answer.
What's next
Publish the Judge flow to the AI Catalog, expand the drill packs, wire the autonomy map into merge-request rules so hands-off areas can actually go hands-off, and turn blind spots into tracked issues automatically.
Built With
- biome
- dependency-scanning
- docker
- duo-agent-platform
- flows-api
- gitlab
- gitlab-ci
- gitlab-duo
- google-cloud-run
- next.js
- node.js
- pnpm
- python
- react
- sast
- secret-detection
- semgrep
- software-carbon-intensity
- tailwindcss
- typescript
- vercel
- vitest
- workload-identity-federation
Log in or sign up for Devpost to join the conversation.