Available upgrades

How DevUpdate.io surfaces available dependency upgrades, how risk is aggregated across the upgrade range, and how the auto-resolve / dismiss lifecycle works.

An available upgrade is a proactive heads-up that one of your monitored dependencies is trailing the latest known release of its tracked source. For every dependency whose pinned version sits behind the newest release we know about, DevUpdate.io surfaces the upgrade as pinned → latest, tied back to the version you're actually on. The heads-up is "your react@18.2.0 can move to 19.1.0," not just "react updated."

This replaces the old reactive "lockfile alert" model. Instead of waiting for a version to change between syncs and firing a one-off alert per change, we tell you up front what you could upgrade to, and we keep that picture current as releases land.

How an upgrade is computed #

For each monitored dependency we compare the version you pin against the latest release of its tracked source. When the latest release is newer, an available upgrade is offered.

Versions are compared under the ecosystem's own convention (semver for npm/Cargo/Go/Composer lockfiles, PEP 440 for Python), and the offered version always keeps the release tag's own spelling. (Treating npm tags as Python versions used to misread prerelease tags like 15.0.0-0 as post-releases; that's gone.)

Offered versions are also validated against the package's own registry: a release tag that names a version the package never published on npm/PyPI cannot become an upgrade target. This is what stops monorepo sources from leaking another package's versions into your list: client-only resolves to the React monorepo, whose v19.x tags are React's versions, not client-only's. When none of a source's tags match the registry, the package produces no upgrade at all rather than a fictional one.

Risk is aggregated over every release in the (pinned, latest] range, not just the newest one. If a security fix shipped three versions back and a breaking change two versions back, both still count toward the upgrade's risk even when the newest release itself is a benign patch. The upgrade carries flags for whether the range includes a major version jump, a verified security fix, or a breaking change, plus an aggregated risk score and a link to the source's full release history so you can see exactly what's in the jump.

Security flags are advisory-verified #

The security flag is grounded in registry advisories, not release-note prose: it means an OSV/GHSA advisory, the same data npm audit and pip-audit read, affects the version you pin, and its fix ships inside the upgrade range. Each flagged upgrade carries the advisory ids, severity, and the fixing version (linked from the upgrade card), so you or your agent can verify the claim instead of taking our word for it. Release notes that merely mention security work (upstream CI hardening reads as "security" to a summarizer) no longer drive the flag; they surface in the upgrade's reason as an unverified signal instead.

Security issues: the cases no upgrade can carry #

An upgrade row can only exist when your pin trails latest, which means two real vulnerability situations have nowhere to live on an upgrade list:

  • Vulnerable at latest. Your pin is the newest release, and an advisory affects it. Nothing to upgrade to; still exposed.
  • Fix unreleased. An advisory affects your pin, and no fixed version is released and reachable above it — the maintainer hasn't shipped a fix, or the version the advisory names isn't published on the registry yet. (A fix the registry has published above your pin reports as fix_available, even when our last release sync hadn't caught it yet — we check the registry's current published set, not a stale cache, so a fix that just shipped never masquerades as "no fix". When that registry check is unavailable we stay conservative and keep fix_unreleased rather than assume a fix exists, so cross-check the registry before accepting the risk.)

DevUpdate.io tracks these as security issues, separately from upgrades: one issue per (lockfile, package, advisory) affecting a pinned version, refreshed daily against OSV. Each issue carries its advisory ids, severity, references, and a fix_status that tells you what resolving it takes: fix_available (a released upgrade delivers it — linked once we've ingested that release), fix_blocked (the fix is in range but a parent constraint pins below it; the constraint and its override cure are named), or fix_unreleased. When the advisory documents a workaround, we include the maintainer's own text verbatim, never a paraphrase.

Security issues deliberately have no dismissal: an advisory that still affects your pin keeps listing on every audit until the pin moves out of the vulnerable range or the advisory is withdrawn. Accepted risks belong in your repo's own risk register, not silenced in the data source. Agents read the same list via the get_security_issues MCP tool, and the devupdate-audit skill turns it into a complete audit workflow.

Direct, transitive, and blocked upgrades #

For lockfile formats that carry a dependency graph (package-lock.json v2/v3 and uv.lock today), each upgrade is annotated as direct (the manifest's own dependency, yours to move) or transitive (pinned by the resolver). For transitive upgrades we additionally check every dependent's version constraint against the offered version: when a parent excludes it (aioboto3 exact-pins aiobotocore, next-auth caret-pins uuid), the upgrade is marked blocked by parent, with the constraint shown: it is not independently installable, so the fix is to upgrade the parent or, for security fixes, to add an override/resolution. The stat strip splits the headline "Upgradeable" count into direct vs. blocked so a large number of parent-gated transitive pins can't masquerade as actionable work.

Upgrades are (re)computed at three moments:

  • when monitoring starts for a dependency (after its source is discovered),
  • on each sync or re-upload of the lockfile, and
  • whenever a new release lands for a monitored source.

"Not monitored" dependencies #

A dependency only produces upgrades once its source has been discovered and is being monitored. Dependencies whose source we haven't resolved (no public GitHub repo found, an unsupported host, or simply not selected for tracking) are counted as not monitored and never generate an upgrade. They show up in the rollups so you can see the gap, but they aren't actionable until a source is attached. See Package matching for how discovery works and why some packages stay unmatched.

Monitoring is counted across your whole account, not per lockfile: if a package in a freshly added lockfile is already actively watched through another lockfile (or was added as a source directly), it counts as monitored right away: the "Unmonitored" number in the stat strip and the per-package "Monitoring" badges always agree.

The lifecycle: auto-resolve and dismiss #

There is no read/unread state and no "Mark all as read" anymore. An available upgrade has a simple lifecycle:

  • Auto-resolve. When a later sync shows the dependency was bumped to (or past) the offered version, the upgrade disappears on its own. Bump the dependency in your project, let the next sync pick it up, and the upgrade clears itself.
  • Dismiss. Not ready to move? Dismiss the upgrade to snooze it. A dismissed upgrade stays hidden until a newer version than the one you dismissed appears, at which point it resurfaces with the new target. You rarely need to dismiss after actually upgrading. The next sync auto-resolves it for you.

Risk levels #

An upgrade's level is a priority, computed from the signals aggregated over the whole (pinned, latest] range. Critical is reserved for urgency (you're behind a security fix), High for confirmed breaking signals; softer suspicion signals cap at Medium so that long upgrade ranges (which almost always contain some noise) don't all read as high-risk. The levels:

LevelWhat triggers itWhat it means
CriticalA registry advisory (OSV/GHSA) affects your pinned version and its fix ships in the range, verifiable by the advisory ids on the upgrade.You're provably on the vulnerable side of a known fix. Take it first; if a parent blocks it, take it through the parent or an override.
HighA confirmed breaking signal anywhere in the range: the upgrade crosses a major version, or a release documents breaking changes.Risky to take: read the releases in the range before upgrading.
MediumSuspicion without confirmation: undocumented changes we detected in a diff, security-flavored release notes with no matching registry advisory, an advisory whose fix is not in this range, or an elevated per-release risk score (40+).Worth a look; usually safe to batch and verify with your test suite.
LowNone of the above: patch/minor releases with quiet diffs.Routine catch-up.

A verified security fix outranks everything else: a major jump that also ships the fix is critical, not merely high: urgency dominates, and the breaking signals still show in the upgrade's flags and reason.

Where upgrades show up #

The UI is aggregates-first: you start from per-lockfile counts and drill down to per-dependency detail.

  • Pulse overview: the Pulse page rolls up each lockfile's upgrade picture into per-lockfile counts: how many dependencies are upgradeable, and of those how many involve a major jump, a security fix, or are otherwise high-risk, plus how many dependencies are not monitored. Click through to drill into a lockfile.
  • Lockfile detail: the header shows a stat strip with the same counts (upgradeable / major / security / high-risk / not-monitored) for that one lockfile. Below it, the upgrades are grouped into collapsible sections by risk level (critical / high / medium / low) so a long list reads as a handful of headline counts first; the highest-severity group starts expanded, and you drill into the others on demand. Each upgrade row shows pinned → latest, its flags, and its aggregated risk. The Resolve via MCP call-to-action sits at the top of the section, above the groups, since pointing an agent at the list is the fastest way to work through it.
  • Per-dependency drill-down: every row links to the source's release history at /dashboard/source/[id], where you can read each release in the (pinned, latest] range to understand the full scope of the upgrade before you take it.

Acting on an upgrade #

Open the source's release history from the upgrade row to see every release between your pinned version and the latest, with summaries, diffs, and the signals that drove each score (see Risk scoring). Decide whether to upgrade now, pin around a specific release, or wait for a follow-up.

When you do upgrade and the next sync reflects it, the upgrade auto-resolves. If you want to hold off, dismiss it to snooze until a newer version lands.

From an AI agent (MCP) #

If you've wired up the MCP server, an agent can audit and work your upgrades end to end:

  • get_security_issues(lockfile_id?): every advisory affecting a pinned version, independent of upgrade availability, with fix_status, blocked-by constraints, and verbatim advisory workarounds. The audit entry point: vulnerable-at-latest appears here and nowhere else.
  • get_upgrades(lockfile_id?, risk_level?, upgrade_ids?, include_dismissed=False): available upgrades in one call. By default a cheap per-lockfile rollup (the same counts shown on Pulse and the detail header, plus the direct / transitive / blocked-by-parent split) leads the per-package list (pinned → latest, major / security / breaking / direct / transitive / blocked flags, OSV/GHSA advisory ids with fix versions, and the parent constraints behind blocked upgrades, each (id: N)-tagged). Pass upgrade_ids for release-by-release detail across each upgrade's (pinned, latest] range — the agent equivalent of opening the release history before a risky upgrade. Folds in the former get_lockfile_upgrade_summary, get_available_upgrades, and get_upgrade_details (themselves the successors to the old get_alerts).
  • dismiss_upgrades(lockfile_id?, upgrade_ids?): snooze upgrades until a newer version appears, scoped to one lockfile or a specific set of upgrade ids. This replaces the old mark_alerts_read.

A common agent flow starts with get_security_issues (anything affecting a pin gets handled first, by fix_status), then get_upgrades unfiltered (its rollup tells you which lockfiles need attention; pass upgrade_ids before anything major or breaking), and resolves the ones it can. After actually bumping a dependency you usually don't need to call dismiss_upgrades at all. The next sync auto-resolves the upgrade. Use dismiss_upgrades only when you want to snooze an upgrade you've decided to defer.

The whole flow ships as downloadable agent skills (audit, upgrades, and security triage) you can drop into your repository, so any Claude Code session knows the safe order of operations without prompting.

Frequently asked questions #

Is this the same as a CVE scanner or SCA tool? #

We overlap where it matters and go further where they stop. Like an SCA tool, we match OSV/GHSA advisories against the exact versions you pin, with advisory ids attached so you can verify every flag on osv.dev. Unlike a scanner, we tell you what to do next: whether the fix is takeable, blocked behind a parent constraint (and which override cures it), or unreleased (and what the maintainer's documented workaround is), plus the release-by-release intelligence to survive the upgrade and the ecosystem context no scanner carries. And we do it without scanning your code: exposure checks in your codebase are your agent's job, locally, guided by our skills. Scan-and-score tools stay complementary for install-time malware blocking; use us to know what's wrong, what it takes to fix, and whether the fix is safe to merge.