A release passes every test, then breaks for real users. Next month, the same bug ships again.
Phoenix closes that loop on GitLab. It catches the bad release at 10% exposure, switches it off 28 seconds later with no human, files the incident, opens the fix, and turns the lesson into a review rule that catches the same bug in the next merge request.
What it does
One change travels through all nine GitLab stages, plus a Learn step:
- Plan. The
phx-specflow adds acceptance criteria and a risk rating to the work item. - Create. The
phx-buildflow writes the branch, code, tests and merge request, with the new behaviour behind a feature flag. - Verify and Secure. CI runs lint, tests, SAST, secret detection and a policy check. GitLab's Code Review flow reviews against the project's own rules.
- Merge. A human merges. That is the Supervised gate: Phoenix never merges anything itself.
- Package and Release. CI builds the image, makes an SBOM, tags a version and creates a GitLab Release.
- Configure and Monitor. CI rolls the flag out at 10, 50 and 100 percent. After each step, a canary sends 60 seconds of traffic and fails closed.
- Rollback. If the canary fails, a CI job switches the flag off and reads it back. No human, no LLM.
- Incident. The
phx-incidentflow reads the failed job, files an incident and opens a fix merge request with a regression test. - Govern. Every release gets a checksummed evidence pack: tests, scans, SBOM, policy result, rollout, rollback and links to the AI sessions.
- Learn. The
phx-retroflow turns the closed incident into a post-mortem, regression tests and a new review rule. On the next merge request, GitLab's Code Review flow cited that rule, named theTypeErrorthe change would raise, and requested changes.
Flows propose, CI executes
AI flows read messy text and write code, comments and issues. Nothing that merges, releases, runs a pipeline or flips a flag is in their hands. Those steps are plain, unit-tested CI scripts. A validator enforces this on every pipeline:
- A component that reads untrusted text (issues, logs) never holds a write tool, and every prompt labels outside text as untrusted.
- No flow may hold a tool that runs commands or calls the generic API.
- A flow's merge request may only touch
app/,tests/anddocs/(plus the review rules, for the Retro flow), within size limits, and code needs tests. - Feature flags fail closed. Auto-merge is off.
Built on GitLab Duo Agent Platform
| Custom flows | 4 (phx-spec, phx-build, phx-incident, phx-retro), using all three component types: Agent, OneOff and Deterministic step |
| Triggers | Work item created, status changed, assigned to the flow, pipeline failed |
| Foundational flow | Code Review, steered by the project's own rules |
| Custom agent | Phoenix Concierge, read-only |
| Project context | AGENTS.md, agent config with a network allow-list, MCP client config |
| Flows as code | YAML in the repo, validated in CI against GitLab's own schemas |
Results, live on GitLab.com
| Canary verdict to flag off | 28 s, no human |
| Exposure before rollback | 2 min 06 s, at 10 percent |
| Incident flow start to incident filed | about 2 min 35 s |
| Build start to a green merge request | about 6 min 20 s |
| Merge to flag at 100 percent | 7 min 46 s |
| Healthy canaries at 10 and 50 percent | 756 requests each, 0 errors |
| Retro flow assigned to rule merge request opened | 1 min 46 s |
| Review request to review citing the new rule | 1 min 26 s |
| AI credits for release, rollout, canary and rollback | 0 (plain CI) |
Challenges
- The review rules were hidden from the flows. GitLab's context exclusion hides
.gitlab/duo/, so the first Retro run stopped safely without changing anything. We added a readable mirror of the rules, kept identical by a CI check, and the next run wrote the rule. - Flows are not deterministic. One Build run forgot to register its feature flag, so we closed that merge request and ran Build again.
What's next
- A real deployment target. Today the canary runs the released image as a CI service against the live GitLab feature flags, and it can already point at a real URL.
- Locking down
main, so the policy check always runs from a protected copy.
See it run
- Bad release caught and rolled back (run B pipeline)
- Incident flow session and the incident it filed
- Fix merge request
- Retro merge request with the new rule
- Review citing the rule on the next merge request
- Build session, its merge request, release pipeline and evidence pack (run A)
Run B's fault was planted for the demo, and the second merge request in run C was written to test the new rule.
Log in or sign up for Devpost to join the conversation.