A finding with no owner is just anxiety
Detection is the easy half. Zero turns findings into a loop — owner, workflow, ticket, verified fix — so the dashboard stops being a guilt list.
Every scanner you own can produce a list of problems. Your CSPM has one. Your cost tool has one. Dependabot has one. Between them, most teams already have more findings than they will ever act on. The list grows faster than the team shrinks it, and after a while nobody opens it, because opening it just confirms that the number went up again.
That is not observability. That is anxiety with a login page. A finding with no owner and no next action isn’t information — it’s a reminder that something is wrong and you don’t know whose job it is. The dashboard becomes a guilt list.
This is part 3 of a three-part series on building Zero — b0gy’s platform for engineering truth. Part 1 was the keyless connector model that made it safe to see everything. Part 2 was deploy truth — the record of what shipped where, which makes a finding attributable to a change. This part is what all that seeing was for: the loop from found to fixed and verified, and the machinery that routes each fix to the person who actually owns it.
Findings, not a parallel alert stack
The first decision was to have exactly one pipeline. Not a CVE pipeline and a drift pipeline and a cost pipeline that each grew their own triage, their own notification rules, their own way of being ignored. One.
So everything is a finding in the same shape: config drift, orphaned infrastructure, a CVE from OSV, a cost step-up, an error-rate spike, a cert about to expire, an exposed port, a connector that stopped authenticating. They enter the same pipeline, carry the same fields, and are acted on by the same machinery. Triage, routing, ticketing, and automation get built once — and every new detector we add inherits all of it for free. When we shipped observability heuristics, “api is erroring” became a finding that could be ticketed and routed on day one, because it was a finding like any other.
The alternative — a parallel alert stack per signal type — is how teams end up with six inboxes and no loop. We didn’t want six inboxes.
Each finding carries severity, a category, and an act-on priority, so the list sorts by what actually deserves your attention. A critical CVE outranks a generic “this looks stale” note. You can filter to just security, or just cost. The point is that priority is a property of the finding, computed the same way for all of them, rather than a vibe you assign after the fact.
Routing needs an owner — so “no owner” is a finding
A finding you can’t route is a finding that can’t move. It sits on the list forever because there is nobody to hand it to. So ownership is not an afterthought in the loop — it’s a precondition for the loop to run at all.
Zero resolves ownership from CODEOWNERS and resource tags. When a finding lands on a service, it already knows which team’s tracker it belongs in. And when it can’t resolve an owner, that fact becomes its own finding: missing_owner. Because an unroutable finding is a real problem — not a rounding error to swallow silently. Making “no owner” visible is what keeps the rest of the loop from quietly leaking work into a void.
That ownership graph also powers the thing developers actually open. The “needs me” inbox composes, per person, the findings on services they own, the workflow runs waiting on their approval, and the open tickets on their findings. A developer opens Zero and sees their work, not the org’s entire backlog. The org-wide list is where a finding is born; the “needs me” inbox is where it becomes somebody’s Tuesday.
Workflows: when this shows up, do that — with gates where they matter
Once every finding has a shape and an owner, you can automate responses to them as data instead of code. Zero’s workflow engine is a rules engine over findings — when a finding matches this, do that: conditions in, an action out.
Conditions match on kind, category, priority, provider, service, age, and tags. Actions cover the real range of responses — delete, archive-then-delete, tag, suppress, dismiss, or notify-only. “When a security finding lands on an archived repo, dismiss it” is a rule, not a patch. “When an orphaned disk is older than 30 days, propose a delete” is a rule. New situations slot in without shipping code.
The part that makes this safe to run is the gate. Destructive actions require review — N-of-group approvals before anything happens. Safe actions can auto-apply. And for findings that are valid but genuinely un-actionable — the CVEs in a repo you archived last year — a rule auto-dismisses them, with a full audit trail, so they stop haunting the list without anyone pretending they were fixed.
Every state change is recorded — proposed, approved, dismissed, verified, cancelled — with the actor and the reason. The audit trail isn’t a compliance bolt-on. It’s how a loop stays trustworthy when parts of it run without a human in the path.
The ticket in the owner’s tracker — that stays honest
Findings that need a human’s hands become tickets, opened automatically in the owner’s tracker — GitHub or Jira, routed per service via CODEOWNERS. The team that owns the code gets the issue in the tool they already work in. Nobody has to come to Zero to find out they have homework.
The detail that earns its keep is reconciliation. A ticket and the finding it tracks drift apart constantly — someone closes the ticket, but the finding is still true; or the finding resolves, but the ticket sits open for a month. Zero watches both sides. Close a ticket whose finding still persists, and it gets reopened with a comment explaining why. Resolve the underlying finding, and the ticket gets closed with a comment. The tracker stays honest without anyone babysitting it — which matters, because the moment a tracker starts lying, people stop trusting it, and then you’re back to the guilt list.
Closing the loop honestly
A loop is only worth building if “done” means done. Two things make that real.
First, verification. A workflow’s delete isn’t marked complete because the action ran — it’s complete when the resource is actually gone, re-checked on the next sync. “We asked it to be deleted” and “it’s deleted” are different claims, and only one of them closes the loop.
Second, the findings ledger. An issue fixed at the source — a dependency upgraded, a resource deleted, a tag added — used to just vanish from the next scan, which is its own small dishonesty: you can’t tell “fixed” from “the scanner missed it.” Now every finding’s lifecycle is tracked, so a source-fixed issue shows up as resolved, in a resolved view and in the cleared-per-day chart, instead of disappearing without a trace. And the ledger is smart about intent: delete a resource that an open finding told you to remove, and that counts as the fix — not an “a resource unexpectedly disappeared, was that you?” alarm. The deletion was the answer.
Dismissal is held to the same standard. It’s auditable — who, why, how. It’s snoozable — and a snoozed finding re-surfaces when the snooze lapses, so “later” never quietly becomes “forgotten.” Org policy can require a reason per category, so security dismissals carry a note while low-stakes cruft stays one-click. And here’s the incentive we cared most about getting right: actually fixing a finding credits you in the burndown and leaderboard exactly like dismissing it does. Fixing must never earn less than dismissing — otherwise you’ve built a system that rewards making the number go down over making the problem go away.
Notifications without the fatigue
None of this works if the loop screams. A scan that flags 40 resources must not send 40 emails. So digests are batched — one email per reviewer per pass, priority-badged and sorted, linking straight to the runs that need them. Each person picks their channels and the categories they care about. And it lands in Slack, where the team already is, instead of a fifth place they have to remember to check.
Notification fatigue is how good detection quietly dies. The fix isn’t fewer findings — it’s fewer interruptions per finding that matters.
What this costs us
The loop is real, but parts of it still put a human where we’d eventually like the platform.
The platform proposes destructive actions; a human still executes them. A delete workflow proposes and, after the fix, verifies — but today a person runs the actual delete. The Deleter/Archiver port with per-cloud adapters comes next, behind capability gating. We’d rather ship approval and verification first and prove the gates than hand a platform delete rights on day one. Verified-then-executed is the right order to earn that trust.
Auto-PR remediation is deferred. Today a CVE finding routes a ticket; it doesn’t open the fix. Dependency-fix PRs are on the list — they need GitHub App contents and PR-write scope — but for now the loop ends at “here’s the ticket, upgrade it,” not “here’s the PR.”
Routing is only as good as your ownership data. CODEOWNERS gaps mean unroutable findings. That’s exactly why missing_owner is a first-class finding — but it’s still a bootstrap problem, and we’d rather say so than pretend the graph is complete on day one.
We shipped the loop with these edges visible because a loop that’s honest about where it still needs a human beats a dashboard that’s confident about a fix nobody actually made.
The heuristic
Detection is the easy half — the hard half is routing every finding to an owner and proving the fix landed. A finding without an owner and a next action isn’t observability, it’s anxiety; build the loop, not the list, and make sure actually fixing something never scores lower than dismissing it.
tl;dr
The pattern. Every scanner produces a list, so teams drown in findings that have no owner and no next action — the dashboard becomes a guilt list nobody opens. The fix. Run one pipeline where everything is a finding, resolve an owner from CODEOWNERS (and make “no owner” its own finding), automate responses with a gated rules engine, route the work as a ticket that reconciles against the finding, and verify the fix actually landed. The outcome. You get a loop from found to fixed and verified instead of a growing backlog — the tracker stays honest on its own, “later” never means “forgotten,” and fixing an issue is rewarded at least as much as dismissing it.
This is part 3 of 3, and the end of the series on building Zero. Part 1 made it safe to see everything — keyless connectors, no stored cloud secrets. Part 2 made what you see attributable — deploy truth, the record of which commit is live where. Part 3 is what the seeing was for — the loop from found to fixed, routed to whoever owns it. That’s the whole arc: see it, explain it, fix it. It’s live at portal.b0gy.com.