The problem with most dependency audit tools isn't that they're wrong. It's that their outputs are routed somewhere nobody looks. A weekly security digest sent to a distribution list. A separate dashboard that's bookmarked but not opened. A Jira board labeled "dep-security" that gets triaged once a month if anyone remembers it exists.
Reports read by nobody prevent zero incidents. The right place for dependency audit findings is the pull request that introduced them — not a report three weeks later, not a backlog item that competes with features, not an email that lands in a shared inbox.
Why the reporting location is the entire problem
When a dependency issue surfaces at the moment of the change — as an inline PR annotation on the specific diff line that updated the lockfile or manifest — several things are true simultaneously:
- The engineer who introduced the dep change is still looking at the diff.
- The context for why the change was made is still fresh in the PR description.
- The fix is cheap — update the version, swap the package, add a lockfile override, or get the PR reviewer to make a call. It's a five-minute conversation while the diff is open.
- No Jira ticket needs to be created. No triage meeting needs to happen. No one needs to re-discover which PR introduced the problem three weeks from now.
When that same finding goes to a separate tool and gets reviewed a week later, every one of those properties reverses. "Which PR introduced this? Which service actually loads this package? Did we already fix it in a parallel branch? Is this even a production dependency or only in devDependencies?" These are all questions that didn't need to be asked if the finding had been routed to the PR that introduced it.
Dependency issues found at PR time get fixed at PR time. Dependency issues filed as tickets get fixed sometime, if the backlog ever gets clear enough.
The anatomy of a workflow that engineers actually use
There are three components that have to work together. Getting any one of them wrong degrades the whole workflow:
1. Trigger on diff, not on schedule
Scheduled scans have two failure modes: they run when nothing has changed (wasted compute and alert noise) and they don't run immediately when something does change (gap between introduction and detection). A dependency audit that triggers only when a manifest or lockfile is modified in a PR is always checking the thing that changed, and never running when it doesn't need to.
The GitHub Actions trigger for this:
on:
pull_request:
paths:
- 'package.json'
- 'package-lock.json'
- 'yarn.lock'
- 'pnpm-lock.yaml'
- 'requirements.txt'
- 'poetry.lock'
- 'Cargo.toml'
- 'Cargo.lock'
- 'go.mod'
- 'go.sum'
The paths filter is the key. The scan only runs when it needs to — you're not spending a 3-minute CI slot on every non-dep commit, and you're not missing dep changes because the weekly cron fired during a holiday deployment freeze.
For monorepos, extend the paths list to cover workspace manifests in subdirectories: '**/package.json', '**/Cargo.toml', etc. The scan scope should be the changed packages only — not the full graph on every PR — to keep CI feedback time under 60 seconds.
2. Inline PR annotations, not summary comments
A comment at the bottom of a PR that says "3 issues found — see dashboard" is marginally better than the weekly email. The engineer has to context-switch to a separate tool, re-learn what the issues are, and navigate back to fix them — all while other reviewers are commenting on the actual code diff.
An inline annotation on the specific line of package-lock.json or package.json that introduced the problem is categorically different. It appears in the exact location where the reviewer's attention already is. The finding, the severity, the recommendation, and the affected package all appear on the same line the reviewer is reading. No navigation required. The review of the dependency change and the remediation of the finding happen in the same UI context.
This is the format that makes dependency review feel like a first-class part of code review, not a parallel audit process that competes for attention.
3. Block on critical, warn on everything else
Not every finding warrants blocking the merge. A maintainer health score of 55 on a new utility package in a non-critical code path is worth a warning so the reviewer is aware. An AGPL-3.0 license appearing in a package your API layer loads is worth a hard block — the legal exposure is significant enough that it shouldn't merge without explicit sign-off.
The severity logic should come from your policy config, not from hardcoded thresholds in a third-party tool. Your team's decisions about acceptable risk — "we'll accept maintainer scores down to 40 for packages that only run in tests" — should be reviewable in the same PR workflow as everything else, not locked behind a settings panel that only one person on the team knows how to find.
The policy-as-code piece
The workflow only functions if the policy is codified somewhere reviewable. If the only definition of "what licenses are acceptable" or "what maintainer score threshold triggers a warning" lives in someone's head or in a SaaS settings panel, every violation becomes a human judgment call that stalls on whoever owns that mental model.
Defining the policy in a depswright.yml committed to the repository root means the policy is:
- Reviewed in PRs when it changes — you can see in the commit history when someone made a policy decision, why they made it, and who approved it
- Unambiguous — when the build fails, the annotation references the specific rule; engineers don't have to guess why their PR was blocked
- Portable — the same config file runs identically in CI and in a local
depswright checkinvocation; there's no "it passes locally but fails in CI" divergence - Part of onboarding — a new engineer can read the policy file to understand what the team considers acceptable, without needing to extract that knowledge from a senior engineer in a sync
The alternative — institutional knowledge about what's acceptable — erodes as people change jobs. The policy file doesn't.
What a functioning workflow looks like in practice
Consider a platform team at a growing data infrastructure company managing 18 services across a Node/Python/Go stack. Before wiring dep audits into the PR flow, dependency issues showed up in three places: a Snyk weekly email, occasional npm audit output that engineers scrolled past in CI logs, and production incidents. After moving to PR-level annotations with a policy-as-code config, the team stopped hearing about dependency issues from any source other than the PR that introduced them.
The metric that matters isn't "how many vulnerabilities did we find." It's "how far from introduction did we find them." Findings discovered at PR time cost under an hour to remediate on average — the engineer who introduced the change is still in context, the fix is usually a version bump or a package swap, and no ticket needs to be created. The same finding discovered via a weekly report two weeks after merge can take a day or more to triage, trace to the originating commit, assess impact, and fix — because all the context that made it easy has dispersed.
Route the findings to the moment of change, not to a report queue. That's the entire workflow design principle, and nothing else changes if you get that one thing right.
Depswright runs PR-level dependency annotations out of the box. Set it up in the quickstart guide.