Inspiration

I've watched layered architectures look perfect in design docs and slowly fall apart in real codebases. One import line in a big merge request — domain code reaching into the web layer — and nobody catches it until the boundaries are gone.

I wanted something that turns those agreements into executable rules, not another diagram that rots. When GitLab Orbit launched as a knowledge graph over imports, merge requests, and code structure, it clicked: the graph can see what humans and single-file linters miss. Merge Passport is my answer — architecture policy as code, enforced on every merge request with Orbit and a GitLab Duo Flow.

What it does

Merge Passport reads a declarative policy file (merge-passport.yml) that defines:

  • Layers — which directories belong to domain, web, billing, etc.
  • Forbidden edges — e.g. domain must not import web
  • Required tests — domain changes must include matching test updates

On each merge request it produces a structured PASS or FAIL report — not open-ended chat. It checks:

  1. Direct violations — a file in one layer importing another (e.g. domain → web)
  2. Transitive chains — multi-hop paths like orders → shared → web where no single file breaks the rule, but the architecture does
  3. Missing tests — policy-defined test coverage on changed paths

It runs three ways:

  • CLImerge-passport check for local use and CI
  • GitLab CI — passport job on every MR and on main
  • Duo Flow — mention the flow on an MR; a three-step agent workflow (context → imports → passport check) uses Orbit and returns an actionable verdict

The flow is published publicly in the GitLab AI Catalog (MIT licensed). Any project can enable it.

Demo video: https://youtu.be/z7uBzdNYTII

How I built it

I built this solo for the GitLab Transcend Hackathon (Showcase Track).

Stack:

Piece Technology
Policy engine + CLI Python (merge_passport/)
Knowledge graph GitLab Orbit via glab orbit remote query
Agent platform GitLab Duo Flow + project Agent Skill
Catalog publish AI Catalog Sync CI component
Demo fixtures Layered Python app + transitive demo (examples/transitive_demo/)

Architecture:

  1. merge-passport.yml — single source of truth for layer rules
  2. merge_passport/ — parses policy, maps files to layers, validates imports, emits structured reports
  3. orbit_client.py — Orbit queries for MR changed files and project-wide ImportedSymbol edges; unit tests lock the query DSL shape
  4. transitive.py — walks Orbit's import graph for multi-hop forbidden paths
  5. flows/merge-passport-flow.yml — three-agent Duo Flow synced to AI Catalog on git tags
  6. skills/merge-passport/SKILL.md — teaches agents how to query Orbit and run checks
  7. .gitlab-ci.yml — test, passport, catalog validate, and catalog-sync jobs

I used AI assistants (Cursor, Claude) to iterate quickly on CI, catalog schema errors, and the demo script — but every design decision, Orbit query, and policy rule is mine and verified with tests.

Repo: https://gitlab.com/gitlab-ai-hackathon/transcend/6613781

Challenges I ran into

CI was a puzzle. The passport job failed because python:3.12-slim has no git, xargs needed --changed-files in the right place, and the branch smoke check tripped my own required_tests rule by listing domain files without test files.

AI Catalog publish took several tag attempts. I hit: missing deploy stage, invalid trigger name (pipelinepipeline_hooks), duplicate group_id in flow consumers vs CI enable_in_project, and fine-grained PAT permissions that created the flow but couldn't auto-enable it in the group. I fixed each one, published on tag 0.1.3, and enabled the flow manually at project level.

Orbit vs local AST. Orbit's ImportedSymbol reflects the indexed default branch, not unmerged MR code. I documented this honestly and label every finding Source: orbit or Source: local_ast so users know what they're looking at.

The transitive demo. The most compelling violation — architecture erosion through a chain no single file reveals — only works with Orbit's project-wide graph. I built examples/transitive_demo/ and a polling script to wait for Orbit indexing before recording the video.

Solo scope. I had to resist building a general-purpose linter for every language and stay focused: one policy file, one flow, one clear story.

Accomplishments that I'm proud of

  • Shipped a public Duo Flow in the AI Catalog — not just a chatbot, but workflow automation with PASS/FAIL output
  • Meaningful Orbit integration — MR diff traversal, ImportedSymbol lookups, and transitive import chains that AST alone cannot detect
  • Policy as code that teams can copy: drop in merge-passport.yml, customize layers, enable the flow
  • End-to-end pipeline — local CLI, GitLab CI, Duo Flow, and catalog publish all exercising the same validator
  • Honest engineering — source attribution on every violation, Orbit query contracts pinned by unit tests
  • Complete Showcase deliverables as one person: MIT repo, green CI, catalog publish, and a 2:36 demo video

What I learned

  • Orbit is user-scoped — every query needs project_id; the DSL distinguishes single-node (node) vs multi-node (nodes + relationships) shapes
  • AI Catalog sync has strict schema rules — trigger names, stage names, and consumer config must match the component exactly; reading the sync job log beats guessing
  • Architecture drift is often indirect — the violation that matters is frequently a chain, not a single import; that's where a knowledge graph earns its place
  • Agents work best with deterministic backends — the Duo Flow orchestrates, but merge-passport check gives reliable PASS/FAIL for CI and demos
  • Solo hackathon shipping — small scope, good fixtures (demo/violation, transitive demo), and fixing CI early saves demo day

What's next for Merge Passport

  • GitLab CI component — one-line include: for teams adopting Merge Passport
  • MR comments from the flow — post the structured report directly on the merge request
  • Multi-language support — Go/Ruby import detection via Orbit ImportedSymbol
  • Symbol-level boundaries — use Orbit CALLS edges, not just module imports
  • Group-level enablement — smoother catalog sync with classic PAT + Maintainer permissions

Built With

  • docker
  • gitlab-ai-catalog
  • gitlab-ci/cd
  • gitlab-duo-agent-platform
  • gitlab-orbit-(knowledge-graph)
  • glab-cli
  • node.js-(catalog-sync-component)
  • pytest
  • python
  • pyyaml
  • yaml
Share this project:

Updates