Triage at Scale: Using AI to Keep Up With AI-Assisted Security Reports
In February, Jacob Walls wrote about what the Django security team has been seeing: more reports, more AI-assisted, more derivative. The volume is up and the signal-to-noise ratio isn’t keeping pace. If you follow Django development, it’s worth reading. I’ve been sitting with it since, because it describes a problem I’ve been experiencing firsthand.
If AI is lowering the marginal cost of filing a security report — and it clearly is — then triage volume will keep climbing regardless of what the team does on the intake side. The logical response isn’t to wish reporters would stop using AI. It’s to use AI deliberately on the triage side, in the specific places where load actually causes failures.
What breaks under load
Most of the Django security team are volunteers. This is the asymmetry that matters. An AI-assisted reporter can generate five variations of a report for essentially nothing. From the triage side, each report still requires someone with domain knowledge to read it, assess it, check it against prior work, and respond — in time carved out from other things.
What gets missed under load aren’t the obviously critical issues. Those get caught. It’s the contextual connections: a related Trac ticket, a recently patched CVE in the same area, a report targeting versions that already have the fix. I missed exactly this kind of cross-reference when reviewing a recent issue. Not a knowledge gap — a load failure. That’s the failure mode worth designing against.
The intake workflow
I built a structured intake workflow around the specific points where triage breaks. In practice, it’s a set of slash commands in a Claude Code workspace — each one scoped to a specific step so the full workflow stays modular rather than monolithic. Each piece is there because something actually failed without it.
Structured severity reasoning (/triage) forces explicit preconditions rather than gut calls. Under time pressure, the default is pattern-matching — “this looks like an XSS” — and assigning a number. The intake command walks through preconditions, affected versions, and severity before any deeper analysis starts.
Trac cross-referencing (/trac-refresh) is the direct response to the missed ticket. A separate command rebuilds a local index of Trac tickets for the affected component. The LLM doesn’t know Django’s Trac history, so the relevant data gets fed in explicitly — the model identifies potential connections, but only from what it’s actually given.
CVE index checks (/cve-refresh) catch derivative reports — the ones that describe something already patched or closely mirror an existing CVE. Jacob’s post confirms these are common. A dedicated command rebuilds the local CVE index from Django’s release notes, so the cross-reference step works against current data rather than stale snapshots.
Patch verification (/verify-patch) closes the loop after a fix is drafted — red-to-green confirmation, regression checks, PoC coverage. It’s a separate step because verification under load is where corners get cut first.
What this doesn’t solve
The workflow handles context-gathering and structure. It doesn’t replace judgment on genuinely novel issues. A new vulnerability class that doesn’t pattern-match to prior CVEs and requires deep framework knowledge to evaluate still needs a human making the call. The workflow frees up attention for exactly those cases.
The broader point
The Trac step exists because that’s where things failed. The CVE check exists because derivative reports are expensive to mis-triage. Each piece is a targeted response to a real failure mode — not a general endorsement of AI tooling.
If there’s a principle here, it’s that intentional AI use means identifying where failures actually occur and designing for those specific points. For Django security triage in 2026, the answer to AI-assisted report volume is AI-assisted triage — not as a novelty, but as the straightforward response to where things actually break.
This article was drafted with the assistance of an LLM.