# bumpwarden: a dependency-triage agent that never merges

> Source: <https://dev.to/voyagi/bumpwarden-a-dependency-triage-agent-that-never-merges-2ci7>
> Published: 2026-08-30 20:04:36+00:00

Every neglected repository has the same pile: seven Dependabot pull requests, each one a question nobody has time to answer. Which of these breaks the build? That question is the actual work. The pull requests are just its packaging.

I built bumpwarden as my entry to Google's All Things Agentic hackathon, and this writeup is part of that entry. It is a background agent that does the triage itself.

Twice a day, Cloud Scheduler wakes a Cloud Run service. It reads the manifest and lockfile from GitHub, resolves candidate versions from the npm registry and deps.dev, and scores every pending bump from 0 to 100 against a fixed rubric: nine factors, published weights. Then it acts. A clear bump becomes a pull request that edits package.json and nothing else. A caution bump becomes an issue. A risky one becomes a hold issue with a migration plan. Run it again tomorrow and it updates the same items in place instead of opening more. There is no merge action anywhere in its policy. A person merges, or nobody does.

That one rule shaped the whole design. The score comes from checkable facts: semver distance, release age, advisories, deprecation, breaking markers in the release notes, engine and peer ranges, and whether this repository's own code calls a symbol the release evidence says changed. Gemini (gemini-3.5-flash, through an ADK agent) writes the brief: what changed upstream, what breaks in this codebase down to file and line, how to migrate, and how confident it is. Every claim is checked against the material the model was handed, and a claim that cannot be traced is dropped rather than shown. The model cannot move the score and has no write tools.

The free tier is a design constraint, and a good one. Five model requests a minute, twenty a day, and a brief costs two. So briefs go out two at a time, riskiest first, paced against a sliding minute, and cached by bump and rubric version. A repeat run costs zero model requests, which also means a judge pressing the button twice gets an idempotent demo instead of a quota error.

/healthz is not yours on Cloud Run. Google's frontend reserves the path and answers before your app ever sees the request. Every test passes locally, and the deployed route is dead anyway. I renamed mine to /health.

Do not read npm packuments whole. @prisma/client's weighs 68 MB. Reading the two small per-version documents instead took a full run from 197 MB of registry traffic down to 30.

GitHub drops labels silently when the token cannot push. No error, just an issue created without its labels. And if your idempotency looks issues up by label, every run now duplicates its own work. My test double modeled the documented API, not this quirk, which is exactly why 785 green tests missed it.

The dashboard, the scoring rubric and the audit log are public: [https://bumpwarden-729965149669.europe-west1.run.app](https://bumpwarden-729965149669.europe-west1.run.app)

The code: [https://github.com/voyagi/bumpwarden](https://github.com/voyagi/bumpwarden)

The four-minute demo, with the run itself one unbroken take: [https://youtu.be/5WJTfWbNeQ8](https://youtu.be/5WJTfWbNeQ8)
