Inspiration
GitLab is betting on a world where people and AI agents open lots of merge requests in parallel. The problem nobody guards: two or more MRs that quietly depend on each other while Git swears there's no conflict.
Classic case: one MR changes what validateToken() returns as boolean to an object. Another MR, in a totally different file, still calls it as a boolean. No shared files, both pipelines green. Merge them in the wrong order and main branch breaks, because an object is always truthy. Every catalog agent reviews one diff and lets both through. I wanted the one that looks at the whole MR queue.
What it does
Switchyard is a Duo Agent Platform flow with three agents, published to the AI Catalog.
- Collector figures out which code definitions each open MR changes.
- Conflict Engine asks the Orbit graph who calls those definitions, intersects the MRs, and works out which one has to merge first.
- Conductor posts the verdict: a short note on each MR (the order, then why, with file and line),
switchyard::sequence::*labels, and one running "integration board" issue.
Every claim comes with the actual Orbit query and how many rows it returned. A confirmed CALLS edge is stated as fact; the "is this contract change actually safe" part is flagged for a human to take a look and is not asserted.
How I built it
I refused to guess any names, be it entity types, edges, the query DSL, the catalog YAML keys all came straight from the live schema and the Orbit ontology. Before building anything I ran the core queries by hand against the real graph (glab orbit remote query) to prove the chain worked:
open MRs → changed files → definitions → who calls them.
Then: three agents wired in order, a shared skill holding the query recipes, published through the catalog-sync CI component on a git tag. At runtime the agents reach Orbit ambiently over MCP. The demo is a tiny TypeScript service on main with validateToken and two callers, plus two MRs that collide.
Challenges I ran into
Most of the work was discovering how the beta platform actually behaves:
- Traversal depth caps at 3 hops, not 6.
query_typehas nosearch. This resulted in the schema winning every time. - The graph is shared across all projects. An unscoped query for
validateTokenreturned nine results from other repos, so everything had to be project-scoped. - Orbit only indexes the default branch, so code that lives only on an MR branch is invisible. I built the demo around definitions already on
main - Figured out that one can't declare an Orbit tool on a catalog agent, but it's there at runtime over MCP. Found this by trying different things.
- A pile of smaller gotchas: flow prompts ban HTML comments,
catalog-synconly runs on tags, protected variables don't reach unprotected tags, scoped labels evict each other, and so on.
Accomplishments that I'm proud of
It works, live, on a real collision: two MRs with no shared files and two green pipelines, and Switchyard still predicts the break and orders them with a re-runnable query as proof. And it's honest about the line between "the graph proves this" and "a human should check this."
What I learned
A lot about how Orbit really models code (cross-file calls go through an imported symbol, callers are incoming edges, default-branch only, multi-tenant) and how the Duo Agent Platform publishes and runs flows. And also how to constrain agents and not make them wander.
What's next for Switchyard
Catch brand-new callers added on a branch (needs branch-aware indexing), enforcing the merge order instead of just suggesting it, and collisions across a whole group.
Built With
- ai-catalog
- ai-catalog-catalog-sync-component
- gitlab-ci/cd
- gitlab-duo-agent-platform
- gitlab-graphql-api
- gitlab-orbit-(knowledge-graph)
- gitlab-rest-api
- glab-cli
- markdown
- model-context-protocol-(mcp)
- typescript
- yaml
Log in or sign up for Devpost to join the conversation.