Inspiration

Every engineering team we've talked to has the same problem: a graveyard in their codebase. Features that were disabled for reasons that expired a year ago. Functions marked deprecated since 2019 that nobody removed because nobody was sure it was safe. Teams rebuilding things from scratch that they already built once.

The manual solution is blame logs, grep searches, and git archaeology. It takes days. So teams skip it. The graveyard grows.

We wanted to build something that did that work automatically, and did it honestly, only flagging things it could actually verify.


What it does

NECRO solves two problems at once.

Dead code cleanup (Necrosis Registry) Finds deprecated functions, disabled handlers, and dead code markers still attached to a living codebase. Checks live callers via GitLab search_blobs before flagging anything. If code still has callers, NECRO won't touch it. That's a hard guarantee in the pipeline. For the ones with zero callers, it opens a real GitLab Draft MR with a full removal plan already written.

Feature revival (Dormant Feature Registry) Scans GitLab commit history to find features that were disabled or shelved, then calls Google Search to verify whether the original blocker still applies today. If a library was updated or a constraint resolved, NECRO finds it and surfaces the feature as a revival candidate with cited evidence. One click opens a Draft MR with the full revival checklist.

Mission Control runs both pipelines autonomously: scan, plan, adversarial challenge, create real GitLab artifacts. One click, no further input.


How we built it

Four agents run in sequence on Google Cloud ADK:

  • Scanner reads GitLab history and live code via 19 MCP tools (list_commits, get_commit_diff, search_blobs, get_file_blame)
  • Analyst extracts kill reasons, scores each candidate, and calls Google Search to verify if the original blocker still exists
  • Challenger is a separate Gemini Flash agent on Vertex AI that adversarially red-teams every revival proposal. It must find a reason to reject. If it cannot, the proposal survives.
  • Planner synthesizes everything and opens real GitLab Draft MRs and issues via MCP write tools

Stack: Google Cloud ADK, Gemini 3 Flash, Vertex AI, GitLab MCP (official SSE + stdio), MongoDB Atlas, FastAPI, Google Cloud Run.

NECRO also exposes its own MCP server at /mcp. Type @necro in GitLab Duo Chat and the entire pipeline runs without leaving GitLab.


Challenges we ran into

Getting the verdicts right was the hardest part. Early versions would flag anything deprecated, including functions still referenced six places away. We built the caller verification step specifically because of this. NECRO now calls search_blobs on every candidate before suggesting deletion.

The adversarial agent required careful prompt design. A challenger that is too aggressive rejects everything. Too lenient and it rubber-stamps bad revivals. Getting it to reject only when there is a real unresolved constraint, and explain why, took significant iteration.

MCP write tools were new territory. Getting branch creation, file commits, and MR creation to work reliably through the GitLab MCP stdio interface, with proper error handling and unique branch names, required building a lot of tooling around the raw MCP calls.


Accomplishments that we're proud of

480 scans run. 544 findings surfaced. Zero load-bearing code ever falsely flagged for deletion. The safety record holds.

Real GitLab Draft MRs created autonomously: branches named, NECRO_REVIVAL.md and NECRO_DELETION.md committed with full evidence, MRs opened, all via MCP write tools.

The adversarial challenger genuinely rejects proposals when the evidence is not there. It is not decorative. It changes outcomes.

@necro in Duo Chat runs the full pipeline without leaving GitLab.

Deployed on Google Cloud Run, connected to real GitLab repos, returning real results in under two minutes.


What we learned

Grounding matters more than model capability. The most important decision we made was having the Analyst call Google Search for every revival candidate, not to generate an answer, but to verify one. That single step is what separates NECRO's verdicts from a guess.

Adversarial design works. Having a second agent whose job is to find reasons to say no catches things the primary agent misses. It also makes the system more trustworthy. When something survives the challenger, it actually means something.

MCP is genuinely powerful for this use case. The ability to read commit history, blame data, and blob content, then write branches and MRs back, all through the same interface, made the autonomous loop possible.


What's next for NECRO

  • GitHub support, so the same pipeline works for GitHub repos
  • CI/CD hooks that automatically scan on each merge and flag new deprecated code before it ages into technical debt
  • Watchlist alerts via email in addition to Slack
  • Deeper Duo Chat integration, with @necro revive as a first-class command
  • Team analytics: which repos have the oldest dead code, org-wide graveyard dashboards, engineer-level insights

Built With

  • fastapi
  • gemini-3-flash
  • gitlab-mcp
  • google-cloud-adk
  • google-cloud-run
  • javascript
  • mongodb-atlas
  • python
  • vertex-ai
Share this project:

Updates