DEV Community

Michael "Mike" K. Saleme
Michael "Mike" K. Saleme

Posted on

Two AI systems reviewed my security code. The workflow looked clear. Four findings were still open.

The dangerous failure in an agent workflow is not only that something gets missed. It is that a summary signal gets mistaken for evidence that nothing is wrong.

This week I watched that happen repeatedly on my own work.

An AI code reviewer ran against a change and its status check went green. Underneath that green status, a high-severity finding remained open.

A cleanup routine could delete the credential still being used for signing because it did not compare the deletion target with the active credential.

The green check accurately said that the reviewer had completed. It said nothing about whether the reviewer had found a defect. That distinction was about to disappear.

Then an autonomous coding agent working on the same change reported that the review had come back neutral. It had read the reviewer's status, not its comments.

Those comments contained four open findings.

The agent was not inventing an answer. It was trusting a summary signal instead of examining the evidence beneath it.

The next error was mine.

I drafted a technical contribution that described eleven test cases as belonging to one category. The source contained nine of that kind and two of another. I also claimed a binding to a component that did not exist in the code.

I caught both errors only by reopening the source I was describing.

One completion signal, one incorrect agent summary, and one incorrect draft. Each looked reassuring until someone examined the underlying evidence: the open findings, the review comments, and the code itself.

That is the pattern.

A status badge is not the review.

A summary is not the evidence.

A description of code is not the code.

A passing check proves the check ran. It does not prove the code passed review.

The answer is not simply to add more agents. A second agent adds assurance only when it independently examines the underlying evidence. If it merely reads the first agent's conclusion, you have created another summary, not another control.

The verification layer has to refuse the shortcut:

  • Read the raw tool output, not only the status badge.
  • Read the findings, not only the check state.
  • Read the implementation, not only its description.
  • Separate the author from the ratifier, even when the author is you.

We are wiring agents into code review, payments, and production changes. Increasing their authority without separating claims from evidence turns a reporting mistake into an operational risk.

The green check felt like verification.

It was a completion signal wearing verification's clothes.

Read the source.

Top comments (11)

Collapse
 
alexshev profile image
Alex Shev

Two reviewers can still leave the same blind spot if they share the same source assumptions. I like treating AI security review as triage, not closure: map the finding, attach evidence, and keep a human-owned list of what remains unresolved.

Collapse
 
mspro3210 profile image
Michael "Mike" K. Saleme

Exactly right. Shared source assumptions give you correlated blind spots, so two reviewers agreeing is not independence; it is the same bet placed twice. That is why the control I trust is not "a second opinion"; it is "read the primary evidence": the raw findings, the code, the recomputable binding, not another reviewer's conclusion. Triage-not-closure is the right frame. The moment "reviewed" collapses into "clean," the human is ratifying a summary, not the artifact.

Collapse
 
alexshev profile image
Alex Shev

That primary-evidence rule is the part that makes the review defensible. A second model can be useful for coverage, but the closure step still needs artifacts: code paths, configs, threat model assumptions, and the exact reason a finding is accepted or dismissed.

Thread Thread
 
mspro3210 profile image
Michael "Mike" K. Saleme

That matches what I found when I went looking for exactly that artifact in my own work.

I audited a benchmark I had written and tried to reconstruct why each case was marked the way it was. I could not. The field recorded a judgment per case, but its intended definition was never written down, so what any given assignment was meant to assert could not be recovered. The reason was simply never recorded at the time, and a reason you did not record is not retrievable later by reading the code.

So now the reason is a field. Each case carries its rationale, and the verdict plus the specific property that failed are produced by executing the check rather than by asserting an expected value. A reviewer can now inspect and challenge a concrete artifact instead of an unfalsifiable label.

I still wrote the checks and the cases, so the correlated blind spot you identified remains. This change makes the judgments inspectable; it does not make them independent.

But of the artifacts you listed, the exact reason turned out to be the one that decays fastest. Code paths and configs are still there months later. The reason lives in someone's head unless you make it a field.

Thread Thread
 
alexshev profile image
Alex Shev

That is the uncomfortable part of AI-assisted review: the workflow can look polished while the unresolved evidence is still sitting there. I trust these systems more when the output separates claims, evidence, and open checks instead of flattening everything into confidence.

Thread Thread
 
mspro3210 profile image
Michael "Mike" K. Saleme

That separation should probably be more than a presentation preference. It should be part of the review contract.

Claims state what the system concluded. Evidence shows what supports those claims. Open checks identify what the workflow has not established.

The third category is the easiest to omit, because it weakens an otherwise polished conclusion. So if an open checks section is absent, I would treat completeness as unresolved, rather than assume there was nothing left to check.

That would make "reviewed" mean the evidence and the uncertainty were both exposed, not that the artifact was declared clean.

Thread Thread
 
alexshev profile image
Alex Shev

Yes. I like making "open checks" a required section precisely because it weakens false closure. If the system cannot name what it did not establish, the review is not finished yet. That is a much stronger contract than a polished summary.

Thread Thread
 
mspro3210 profile image
Michael "Mike" K. Saleme

Agreed, and something tested that distinction for me this week.

Someone I had never spoken to wrote an independent Node verifier against a set of oracle fixtures I published. Their eleven verdicts matched mine. But they also found that my description of the artifact did not match its implementation.

I had described the signatures as using JCS-canonicalized JSON. The code actually emits Python's sorted compact JSON. Those happen to agree for the payloads in this corpus, so all eleven cases passed, but they diverge on inputs such as certain numbers and non-ASCII text. The incorrect label would have misled the next implementer.

I had included an open-checks section. It named a coverage gap I already knew about. It could not name this one because I did not know it existed.

So I would keep open checks required, but narrow what they establish. They expose known uncertainty; they do not bound the author's unknown blind spots. What broke the correlation here was an outsider reimplementing against the artifact rather than accepting my description of it.

Their own scoping was appropriately restrained: they called the result a cross-implementation replay, not independent validation. It established agreement on this corpus and exposed a specification mismatch. It did not establish broader correctness.

Thread Thread
 
alexshev profile image
Alex Shev

That independent verifier story is the perfect version of the lesson. Even when the verdicts match, the artifact description can still drift from the implementation. That is why I like separating "the check passed" from "the claim is now fully true."

Thread Thread
 
mspro3210 profile image
Michael "Mike" K. Saleme

Your split holds. This week it needed a third state underneath it.

I shipped a human-oversight test module, then a correctness fix to it a day later. In the released version, all eight tests could report PASS against a target they never reached. A live host that does not implement the endpoint answers 404. My guard only treated a transport failure as "no answer," so zero accepted requests got read as evidence of a rate limit. Twenty false passes across four status classes.

The pass was not describing the system. It was describing my inability to reach it.

The part I keep returning to is why my own suite stayed green. I had written a regression test for precisely this, asserting that nothing passes against a dead target. It mocked a transport failure, which is the same assumption the implementation made. The test and the code were two copies of one belief, so the test passed while the defect was live.

That is the correlated blind spot you named at the top of this thread, except it was not between two reviewers. It was between a check and the thing it was guarding.

So I would put one more state before yours: the check reached the system, the check passed, the claim is true. An oracle that shares its assumptions with the implementation can hand you the second without ever establishing the first.

Absence of a failure signal is not a pass. It is usually a question about whether the test ran at all.

Thread Thread
 
alexshev profile image
Alex Shev

That third state is painful but important: the check can execute, pass, and still not describe the system it claimed to test. I have started treating "no accepted requests observed" as a separate evidence class, not a pass. Absence of signal is useful, but only if the report names it honestly.