Automating the Last Mile: Escalating Fixes Before They Breach SLA

The worst place for a security fix to die is one approval away from merging. The patch is written, CI is green, the PR is mergeable, and it sits there. Nobody's against it. The reviewer's on leave, or the code changed hands last quarter, or the request landed in a busy channel at 4pm on a Friday and scrolled out of sight.

We've written plenty about the half of this that looks technical: generating a patch that closes the vulnerability without breaking the program, then proving it did. The stalls we kept seeing had nothing to do with the patch. Someone had to work out who owned the code, then ask them, then ask them again.

So we built the asking in. On a schedule, we go through the open fix PRs in a campaign and decide what each one needs, and when one stalls we escalate it using the escalation policy your organization already wrote down.

Where fixes get stuck

A PR's state tells us what kind of stuck we're dealing with: CI failing, conflicted, review requested but never submitted, changes requested with no reply, approved but unmerged for days. Each kind wants a different move. An approved PR that nobody merged needs a quiet prompt to the person who approved it. Changes requested with no reply needs us to go write the code rather than ping anybody about it.

Your policy, not our defaults

Knowing a PR is stuck isn't the same as knowing what your organization wants done about it. That part comes from your own documents. During onboarding you upload security documentation to the Vault: incident response runbooks, escalation procedures, on-call rosters, the SLA table somebody in GRC maintains. An extraction agent reads them and turns whatever they say about escalation into something we can act on. Who to contact, in what order, through which channel, and how long each level has before it moves up.

The extractor is told not to invent policy that isn't in your documents, and every rule it produces cites the document and page it came from. Be clear about what that is and isn't: it's a prompt constraint and a citation you can check, not a proof. Inventing a plausible-sounding threshold is exactly the thing language models are good at, and the citation is there so you can catch it when it happens. If your runbook is silent on what happens when a critical fix goes unacknowledged overnight, we want that gap left open rather than filled with a sensible-sounding number.

Escalation thresholds are governance. They encode how much interruption your organization decided a critical risk is worth, and people with context we don't have argued them out. A vendor default here would be us overruling that with a number we made up.

Who should act, and whether they can

Escalating on time doesn't help if you escalate to the wrong person. We work out who to ask from three sources: git blame on the vulnerable code, the repository's CODEOWNERS file read at HEAD rather than from a stale copy, and your team and org structure from the platforms you've connected. They disagree with each other more often than you'd hope, which is why we read all three.

Then we check whether the person we picked can take the work. If a candidate is already sitting on five open review requests from us, we'll prefer someone who isn't, even when the overloaded person owns the file. That trade is worth arguing about. CODEOWNERS is a control, not a load-balancing hint, and there's a fair reading where routing around a busy owner is review-shopping. Our reasoning is that a review request sent to someone with no capacity isn't a request, it's a way of teaching a team to ignore us. If your ownership rules are load-bearing for audit, that's a conversation to have before you turn this on.

Escalations you can answer

An escalation lands in Slack or Teams as a direct message, a post in the team's channel, or both, depending on how you've set up routing. We write it down before we send it, and if we can't write it down we don't send it, because we'd rather drop a message than lose track of one.

The message stays tied to the thread it was posted in, so replying in that thread closes it out. Nobody has to go to a dashboard to finish a conversation they already finished in Slack. When someone makes a call on a finding, that quiets it for seven days; rejecting it or allowlisting it quiets it for good. Only a recurrence at higher severity interrupts that.

Not becoming the thing everyone mutes

The obvious failure mode is that we turn into a nag bot, engineers mute the channel, and the security team's credibility goes with it. Everyone reading this has watched some tool do exactly that.

Most of the engineering in this feature goes into not sending things. We deduplicate escalations across findings that share a CVE, a fix, or an author, so one dependency upgrade doesn't produce nine pings. An item gets claimed before it's sent, so two concurrent runs can't both post it. Unanswered escalations get raised again once a day up to a ceiling, and then we stop and hand it to your security channel. Quiet hours and time zones are respected. We hold ourselves to three open fix PRs per repository and five new ones per run, which is a limit we hit regularly.

The fix PRs also look after themselves, which takes away a whole category of reason to bother you. When CI fails on one of ours, the agent checks whether that same failure already exists on the base branch before it touches anything, so it doesn't go chasing breakage it didn't cause. It'll rebase when it hits a conflict, and flip the PR from draft to ready the first time CI goes green. Nothing merges without your approval.

What this doesn't do yet

There's no clock. We don't compute remaining SLA time on a PR and fire at a threshold. A scheduled agent looks at the PR with your policy's SLA in front of it and makes a judgment call.

We used to describe that as flexibility. It's more honest to call it a gap. Escalation is one of the places you probably want less flexibility, not more. When an auditor asks whether your process enforces a 72-hour critical SLA, "the model had the SLA in its context and generally agrees" isn't an answer. A dumb countdown would be more defensible than what we have, and we don't have one. If compliance evidence is your reason for buying this, wait.

The SLA compliance views in the dashboard aren't switched on for anyone yet, and neither is the richer escalation card in Slack. Escalations today are plain text. They read fine, but if you've seen a screenshot with a branded card in it, that isn't shipped.

Identity mapping is the sharpest edge. If the right code owner has no Slack or Teams account we can resolve, we can't reach them. We'll raise it with your security team, tell the campaign owner in the meantime, and retry once the mapping is fixed. That path gets used more than we'd like.

The unglamorous part

We've said before that automating the fix was the easy half, and that the hard half was proving the fix is real. This is a third thing, and it's the least interesting work on anyone's plate: knowing who owns the code, knowing whether they've got room to look at it, asking through the channel they actually read, waiting as long as your policy says to wait, then asking again one level up. Security teams do it by hand, every week.

If your escalation procedure is written down somewhere, we can run it. If it isn't written down, that's worth knowing too, and the extractor will tell you which questions your documents don't answer.

Book a demo