Deployguard Governance β Project Story
π‘ Inspiration
Modern software isn't one repo β it's a fleet of interconnected microservices. And the scariest changes aren't the obvious ones; they're the quiet ones. Someone relaxes a JWT signature check in auth-service "just for a legacy client," and three services downstream silently start trusting forged tokens. Code review is per-repo and human-paced; CI is blind to cross-service impact; and governance is mostly a graveyard of issues that get filed and forgotten.
We kept coming back to one question: what if an AI agent didn't just flag risk, but actually closed the loop β reasoned about the blast radius, remediated it, verified the fix, and only then let the change through? With Gemini on Vertex AI for the reasoning and GitLab's official MCP server for real actions, that suddenly felt buildable. So we built it.
π οΈ What it does
For every change event, the agent:
- Reasons about risk with Geminnce + explainable rationale).
- Maps the cross-service blast radius from a service dependency graph + Gemini judgmen3. Acts for real β branch, commiommit status, andescalation/impact issues via the official GitLab MCP server. 4. Governs β a human Approve/Rejous mode where the agent writes,merges, and Gemini-verifies a fix for each downstream impact, then merges the parent β rejecting if any fix can't be ve
- Closes the loop β reconciles remediation status live from GitLab; system risk returns to nominal once contained.
Nothing is mocked. Every decision is a real Gemini call; every action is a real GitLab write.
βοΈ How we built it
The core is a TypeScript/Express service on Cloud Run that serves a React dashboard from thesame origin.
- Reasoning β Gemini 2.5 Flash on Vertex AI. Gemini plays five distinct roles in one agent: risk classification, cross-service impact judgment, autonomous approve/reject adjudication,remediation code generation, and remediation verification. Deterministic rules are a safe fallback when the model is unavailable.
- Blast radius β a dependency graph. Each service is a node; impact edges define propagation. The blast radius of a change to service $s$ is the set of services reachable downstream,computed by BFS over the impact adjacency $A$:
$$ \text{blast}(s) = \bigl|{, t ;:; s \rightsquigarrow t ,}\bigr|, \qquad s \rightsquigarrow t\iff \exists, \text{path } s \to \dots \to t \text{ in } A $$
A service's structural criticality is just its blast radius, $\text{crit}(s) =\text{blast}(s)$, which is what drives the topology tiers. Gemini then layers a judged system impact score $\in [0,100]$ over that structure, and the live system-risk level is
$$ R_{\text{sys}} = \max_{e ,\in, \text{unresolved events}} \text{severity}(e), $$
so the board only stays "red" while something genuinely needs attention.
- Action β GitLab's official MCP server. We connect over JSON-RPC with OAuth 2.0 (DynamicClient Registration + Authorization Code + PKCE) and create issues via the create_issue tool, with a REST layer as fallback for branches, commits, MRs, and statuses.
- Durability & scale β Firestore. Events, governance mode, and the MCP OAuth tokens all live in Firestore, so state survives redeploys and is shared across instances. Every event request is scoped by a session id (X-Session-Id), making the public demo multi-tenant β visitors never see or affect each other's events.
π§ Challenges we ran into
This is where the hackathon really happened.
- "Fire-and-forget" doesn't work on serverless. Our first autonomous orchestrator spawnedremediation work after the HTTP response. On Cloud Run, that background work gets cut off β the first fix would complete, the rest would silently die. We rebuilt orchestration as aserver-side setInterval driver on the always-on instance, where each fix is awaited, not fired-and-forgotten.
- Lost updates under concurrency. The remediation background task and the 15-second reconcile loop both did read β modify β write on the same event document, clobbering each other. Weintroduced a Firestore transaction (updateEvent) with field-level merges, so interleaved writers can never lose a verdict β verified by firing three parallel remediations on oneevent.
- Transient network blips. Intermittent fetch failed errors to Vertex dropped Gemini straight to a generic fallback fix, which then failed verification and made the agent reject the whole cascade. We added AbortController timeouts and retry-on-network-error to everyGemini/GitLab call so a single blip no longer derails an orchestration.
- A verified β pending flicker. Right after a fix merges, GitLab briefly still reports theissue as open (replication lag), and our reconcile loop reset the status back to pending. We made the merge logic preserve any existing verdict through that lag.
- Ephemeral auth + async merges. MCP tokens lived on the container's local disk (lost on every redeploy) β moved to Firestore. And GitLab computes merge_status asynchronously, so animmediate merge returns 405 β we made the merge retry patiently until it's mergeable.
π What we learned
- Closing the loop is the hard, valuable part. Classifying a diff is easy; reliably remediating, verifying, and merging across services β idempotently, concurrently, and durably β is where the engineering lives.
- Autonomy needs guardrails, not just intelligence. Transactions, timeouts, retries, atomicfinalize-claims, and a deterministic fallback are what make an AI agent trustworthy enough to merge code on its own.
- An agent can wear many hats. One Gemini model doing five jobs β judge, impact analyst, decision-maker, author, and verifier β turned out to be a clean, powerful pattern.
- Serverless changes your architecture. "Where does long-running work actually run?" became the question that reshaped the whole orchestration design.
π What's next
Backfill governance state from GitLab on startup, richer remediation policies, agent-proposed (not auto-merged) PRs for human-in-the-loop teams, and extending the blast-radius model with runtime call-graph data.
Built With
- cloud-build
- cloud-run
- docker
- docker-compose
- express.js
- firestore
- gemini
- gemini-2.5-flash
- gitlab
- gitlab-mcp
- google-cloud
- iam
- json-rpc
- model-context-protocol
- node.js
- oauth2
- pkce
- react
- secret-manager
- typescript
- vertex-ai
- vite
Log in or sign up for Devpost to join the conversation.