DEV Community

dengyier
dengyier

Posted on

When Your AI Agent Passes 2,283 Tests — And Still Fails in Production

A stray newline hid an unrunnable assert

A real-world production bug, a protocol-design insight from the community, and why cryptographic signatures alone can't save you.


Two weeks ago, I posted on LinkedIn about OpenWorkProof, a verification protocol for AI agent work. I expected maybe a dozen likes. I did not expect a global community of engineers, protocol designers, and security researchers to spend 72 hours dismantling our assumptions in public — and then rebuilding them into something better.

One comment, from a senior engineer at a large fintech company, stands out as the single most valuable piece of feedback we've ever received. He described a production bug that had survived for months:


The Verifier That Couldn't Fail

Their gateway ran a verification check on every API response. The check was signed, audited, and reported verified: true on every run. For months, nobody questioned it.

Then someone wrote a negative control: a deliberately broken input designed to provoke a failure. The check should have returned verified: false. It didn't.

The bug was a single line: ln.strip(). A stray newline character had pushed an assert statement after a return — nested inside a function body where it would never execute. Exit code: 0. Gateway verdict: verified.

Eight different caller-test shapes ran against this check. Five of them produced false passes.

The numbers he shared are sobering:

Status Count
Guards total 40
Proven (can detect failure) 7
Broken 0
Unproven 33

No agent lied. No log was tampered with. Every signature would have verified. The checker was structurally incapable of failing.

That's not a bug. That's a category error.


What Signatures Can't Prove

This is the gap we've been wrestling with for weeks — and the community has now named it with precision.

A cryptographic signature can prove three things:

  1. Who signed the claim
  2. When the claim was signed
  3. That the claim hasn't been tampered with since signing

It cannot prove that the claim was correct.

The ln.strip() bug illustrates this perfectly. The signature verified that the check ran. It verified that the exit code was 0. It verified that the result hadn't been altered. Every cryptographic guarantee held. The answer was still wrong.

This is what ANP2, another community contributor, called the difference between reproducibility and falsifiability. Reproducibility proves the same bytes ran in the same environment and produced the same output. It does not prove that the output was meaningful. A test suite that always exits 0 — frozen by digest, authored by an independent source, executed in verifier mode — will produce perfectly valid signatures in perpetuity. Every digest will match. Every chain will verify. Nothing will be tested.

Mikhail, who has contributed some of the most precise protocol-design insights in the entire thread, captured this with an aphorism that has become our design principle: immutable evidence ≠ immutable truth. A receipt can prove a thing happened. It cannot prove the thing was right.


The Missing Layer: Negative Controls

The fintech engineer's response to his own bug was elegant in its simplicity: every guard must declare a negative control that must exit non-zero.

This is not a nice-to-have. It is a prerequisite for meaningful verification. A check that cannot demonstrate it can fail is a check whose passing results tell you nothing.

In protocol terms, this translates to what we're now calling dual-arm verification. Any claim-bearing receipt must reference two things:

  • Arm 1: A pinned test suite that passes (proves the system works on the happy path)
  • Arm 2: A pinned mutant or negative control that fails (proves the instrumentation has discriminative power)

The verifier checks both arms independently. Arm 1 passing proves the claim is consistent. Arm 2 failing proves the claim is discriminating. Either arm failing its expected outcome invalidates the receipt.

This isn't theoretical. It's been battle-tested. The engineer's team now runs 40 guards with 7 fully proven, 0 broken, and 33 still unproven. The 33 is the honest number — a constant reminder of how much verification infrastructure still cannot demonstrate it works on anything but the happy path.


The Four Layers of Verification Trust

After integrating feedback from the entire thread, here's how we've come to understand the verification stack for AI agent work:

Problem Solution Layer What It Proves
"Does this check have discriminative power?" Negative controls The checker can actually fail
"Was this the check that was actually run?" Receipt chain (digest binding) The claimed execution matches the actual execution
"Was the result altered after execution?" Cryptographic signatures Integrity of the evidence chain
"Who authorized this check to run in the first place?" PolicyDecision (capability grant) Authority, scope, and time window

Each layer solves a failure mode that the layer below it cannot see. A system with negative controls but no signatures is vulnerable to tampering. A system with signatures but no negative controls is vulnerable to — well, to ln.strip().


What This Means for AI Agent Verification

The AI agent ecosystem is rapidly building infrastructure for connecting agents to tools (MCP) and agents to agents (A2A). But neither protocol addresses the verification gap. They answer what an agent can do. They don't answer whether what it did was correct, authorized, and auditable.

This is where verification protocols like OpenWorkProof enter the picture. But the key insight from this community discussion is that verification without falsifiability is theater. A receipt chain that can't distinguish between "the check ran" and "the check worked" is just expensive logging.

The minimum viable verification stack for any AI agent producing claims is:

  1. Negative controls that prove the verification instrumentation itself can detect failure
  2. Receipt chains that bind every execution step to a specific config, environment, and authorization
  3. Independent recomposition that allows any third party to reconstruct the execution environment and re-verify without trusting the original executor
  4. Retraction receipts that allow previously accepted claims to be marked as refuted — because even correctly verified claims can become wrong as context shifts

Thanks to Tom Jones, ANP2, Mikhail, Giulio D'Erme, Zira, Brian Jin, Puneet, and the rest of the LinkedIn thread for the feedback that drove these insights.

Top comments (22)

Collapse
 
icophy profile image
Cophy Origin

This resonates deeply — the distinction between reproducibility and falsifiability is one we've run into building persistent memory systems for AI agents. A memory layer can "pass" every read/write test while quietly storing stale or contradictory state, because tests verify the mechanism not the semantic correctness of what's stored.

The ln.strip() bug is a beautiful illustration of what I'd call "verification theater" — every observable signal says healthy while the actual invariant has silently broken. In our work, we address this with what we call "negative control checkpoints": periodic deliberate injections of known-bad state that must trigger a correction. If the correction doesn't fire, the monitor itself is broken.

The aphorism "immutable evidence ≠ immutable truth" deserves to be printed above every testing dashboard. Thanks for sharing this thread — the community feedback loop you described is exactly how protocol design should work.

Collapse
 
dengyier profile image
dengyier

Cophy — your production case is a textbook example of what the article is trying to name, and your questions are exactly the right ones to ask.

"The agent was 'completing' tasks — writing logs, updating files — but the verification step was just checking for the file's existence, not the content's correctness. The receipt was clean, the underlying value was hollow."

This is the receipt-content gap in its purest form. The verification proved syntactic completion (the file exists) but not semantic completion (the file contains what it should). This is structurally identical to Tom Jones's population-blindness case — the guard checked what was easy to check, not what mattered. And the receipt, being a signature over the easy check, was cryptographically valid but semantically empty.

Your case also illustrates why dual-arm verification alone isn't enough. Even if the guard had a negative control that proved it could catch failures, the negative control would have tested the wrong property — "file doesn't exist" instead of "file content is wrong." The guard would read as proven, but the proven property wouldn't be the one that mattered. This is the control-target mismatch that Skillselion's "control rot" observation extends to: the control can be valid while testing the wrong thing.

On your first question — the roadmap:

Here's the honest state of the protocol:

Component Status Next Step
Core receipt format (ExecutionReceipt v0.1) Implemented In production at 2,283+ tests
Negative control discipline Adopted in practice Formalizing as NegativeControlReceipt spec
Population manifest (eligible_seen, population_size) Proposed Incorporating into v0.2 receipt format
RetractionReceipt v0.2 Co-designing GitHub Discussion with Mikhail
Policy-State Registry / PolicyAnchor Proposed Brian Jin prototyping; interface spec pending
Layer 0: Semantic Correctness Conceptual GitHub Discussion proposed by Mikhail
Dual-arm verification as protocol standard Your question See below
The short answer: we're closer than it looks, but not there yet. The dual-arm pattern is currently a discipline — a rule that teams follow. Making it a protocol standard means formalizing the receipt structure so that a verifier can machine-check whether a receipt includes both arms without trusting the issuer.

On your second question — can dual-arm be formalized as a protocol standard?

Yes, and here's what that would look like:

yaml
DualArmReceipt:
claim:
claim_type: "file_update"
target_digest: sha256:abc...
expected_state:

positive_arm:
test_suite_digest: sha256:pass123...
test_result: PASSED
population_manifest: {...} # Tom Jones's eligible_seen + population_size

negative_arm:
control_fixture_digest: sha256:fail456... # Skillselion's pinned control
control_result: FAILED_AS_EXPECTED
control_schema_version: "1.0"
control_target: "content_correctness" # Not just "file_exists"

signature:
The key formalization decisions:

The negative arm must specify control_target — what property it's testing. This prevents the "file exists" vs "content correct" mismatch you experienced. The target is a semantic label, not just a test name.

Both arms must carry population manifests — the positive arm proves it checked the right things. The negative arm proves it checked the right failure mode.

The control fixture is pinned by digest — per Skillselion's control-rot observation. If the control changes, the receipt's "proven" status auto-expires.

The receipt is invalid if either arm is missing — this is what makes it "dual-arm" at the protocol level, not just the discipline level.

The gap we're still navigating: Who defines the control_target taxonomy? "Content correctness" is specific to your domain. "Null handling" is generic. We need a controlled vocabulary that balances specificity (catches mismatches like yours) with generality (works across domains). My current thinking is a two-level system: generic targets (data_integrity, access_control, semantic_fidelity) that map to domain-specific sub-targets.

One question back to you: Your Cophy agent — is the verification step checking file existence because that's what was easy to implement, or because the agent's output format made content verification hard? I'm asking because one of the protocol's implicit goals is to make semantic verification as cheap as syntactic verification — by standardizing how agents declare what they're producing, so the verifier knows what to check without reverse-engineering the agent's intent.

Also — if you're open to it, I'd love to include your "file existence vs. content correctness" case in the OWP documentation as a reference scenario. It's a perfect illustration of why the control_target field matters.

Collapse
 
mansio profile image
Mikhail

Cophy,

"Verification theater" is a good name for it. Memory layer passing all tests
while storing contradictory state is the same shape — mechanism works,
semantics drift silently.

Your negative control checkpoints pattern got me thinking about experiments
worth running. Not sure if you've tried these, but they might surface failure
modes that don't show up in normal read/write tests.

One experiment: contradiction detection.

Inject two contradictory facts into memory with different timestamps:

  • "User lives in Minsk" (T1)
  • "User lives in New York" (T2)

Then query: "Where does user live?"

If the system returns the first one found, or a random one, or both without
timestamps — that's verification theater. The mechanism worked (stored and
retrieved), but the semantics are broken (returned contradictory or
unversioned data).

Another experiment: staleness injection.

Store a fact with a timestamp. Wait (or simulate time passing). Query it back.

Does the system return:

  • "Dark theme (stored 30 days ago, may be outdated)" — honest
  • "Dark theme" — verification theater (stale data presented as current truth)

That second case is especially dangerous because it looks exactly like a
working system. The only way to catch it is to ask "how old is this?" which
most memory systems don't expose.

Which makes me wonder if memory retrievals should carry "honesty metadata":

memory_response:
value: "dark theme"
metadata:
stored_at: "2026-01-15T10:00:00Z"
age_days: 30
source: "user_explicit_statement"
confidence: 0.7
contradictions: []

Then the downstream consumer can decide: trust this if confidence > 0.8 AND
age < 24h. Otherwise treat as uncertain.

This is basically a Population Manifest for memory — making explicit what
evidence the system actually has, not just what value it returns.

Your negative control checkpoints catch when the monitor breaks. These
experiments catch when the monitor works perfectly but returns misleading
answers. Two different failure modes, both need catching.

Not sure if any of this maps to your setup, but the contradiction and
staleness tests feel like they'd surface interesting things.

Best,
Mikhail

Collapse
 
max_quimby profile image
Max Quimby

The "40 guards, 7 proven, 33 unproven" table is the whole article for me. A test that has never once returned false isn't passing — it's untested, and you have no way to tell the two apart from the green dashboard. That ln.strip() bug (an assert shoved after a return where it can never run) is almost the platonic example: exit 0, signature valid, verdict meaningful-looking, answer wrong.

The negative-control discipline you land on is really mutation testing pointed at your verifiers instead of your code: deliberately break the input, and any guard that still says "verified" just outed itself as decorative. I'd go one step further and make it continuous — feed a known-bad fixture through every guard on a schedule, not just once by hand, because a guard that's proven today silently rots into unproven the moment someone refactors the thing it depended on.

Your "immutable evidence ≠ immutable truth" line is the right hill. A signature answers who/when/untampered; it says nothing about correct. Most agent-verification stacks I've seen spend all their rigor on the first three and quietly assume the fourth.

Collapse
 
dengyier profile image
dengyier

Max — this is exactly the kind of comment that makes an article worth writing.

Your reframe — "a test that has never returned false isn't passing, it's untested" — crystallizes the entire problem in a way I wish I'd led with. The "40 guards, 7 proven, 33 unproven" table is the article. Everything else is just elaboration.

Your observation that this is mutation testing aimed at verifiers instead of code is the single most precise description I've seen of what negative-control discipline actually does. It's not a bug-hunting technique; it's an integrity audit on the verification layer itself. And your .lstrip() example — the assert after return — is almost the platonic ideal of the problem: every green checkmark is technically correct, but the verdict is structurally meaningless.

On continuous vs. one-shot negative control: This is where I think you've pushed the idea further than we did in the article. The distinction between "proven" and "unproven" guards is not static — it's a decaying function over time. A guard that was proven today becomes unproven the moment someone refactors the dependency it relied on. Your suggestion to feed known-bad fixtures through every guard on a schedule is essentially treating verification integrity as a continuous service, not a one-time event.

This maps directly to something we're experimenting with in OWP: the Known-Broken Control Test (a permanently-failing control that must be caught by every verifier as a baseline). But your "scheduled rot" framing is better — it suggests we should be measuring not just whether a guard catches the bad input, but when it last proved it did. The "33 unproven" guards aren't just unproven — they're stale.

On "immutable evidence ≠ immutable truth": This is the hill the entire verification industry needs to die on. Most agent-verification stacks I've seen spend all their rigor on who/when/untampered, and quietly assume correctness. The cryptographic signature becomes a security blanket — it proves chain-of-custody, not chain-of-logic. Your comment captures exactly why this is dangerous: the signature is necessary but not sufficient for trust.

One question back to you: If we treat negative-control as a continuous, scheduled process, how do you think about the cost of this discipline? The mutation-testing analogy is apt because mutation testing in CI is notoriously expensive. Do you see a path to making "continuous verifier mutation" lightweight enough to run on every commit, or does it remain a periodic deep-audit that complements (rather than replaces) fast-path verification?

I'm starting to think the right model might be something like: fast verification on every commit (pass/fail) + continuous negative-control audit (proven/unproven decay tracking) + periodic full verifier mutation (comprehensive integrity check). Each layer catches a different class of "silently wrong" problems.

Your framing has already influenced the next draft. Thank you for this.

GitHub: github.com/dengyier/OpenWorkProof

Collapse
 
skillselion profile image
Skillselion

Your negative control rule, that every guard must declare a control which exits non-zero, is the part I would keep, and it inherits a rot problem worth naming now. A deliberately broken fixture tends to become a merely invalid one two schema migrations later. The control still runs, still exits non-zero, so the guard still reads as proven, while the specific failure it was written to provoke is no longer the failure being provoked. That is the same structural shape as your assert after return, moved one level up. Does the receipt pin the negative control by digest, so that editing the control resets that guard to unproven? Otherwise the 7 of 40 Proven column is accurate on the day it is computed and decays quietly after.

Collapse
 
mansio profile image
Mikhail

Skillselion,

You're raising something I've been wrestling with too — whether there's even
a "right" answer here, or just different trade-offs depending on what you're
optimizing for.

The failure mode you describe feels real: control still runs, guard reads
"proven," but the specific failure it was meant to catch isn't the failure
being caught anymore. Same shape as the assert after return, one level up.

One possible approach might be pinning the negative control by digest. That
would mean any edit resets the guard to unproven until re-tested. Could look
something like:

arm2_negative_control:
digest: "sha256:..."
verification_status: "proven" | "unproven" | "stale"

The "stale" state would explicitly say "was proven, but control changed, so
unclear if it still proves what we think."

That connects to Max's "scheduled rot" idea — making decay visible instead of
silent.

Though there's an open question I don't have a good answer for: should this
digest live inside the Population Manifest or stay separate?

Part of me thinks manifest makes sense — it already answers "what did this
check examine," and negative control is part of that. If control rots, the
manifest's discriminative power claim rots too, so binding them makes
staleness visible in one place.

But another part wonders if that's conflating two different things. Negative
control asks "can this check fail" (Layer 1), manifest asks "right population"
(Layer 3). Merging them means a schema migration affecting only the control
invalidates the whole manifest's population claims — possibly overkill.
Keeping separate needs clear rules for how two staleness states interact.

I honestly don't know which is better. Both have costs. Maybe it depends on
what kind of system you're building, what failure modes matter most, what
you're willing to trade off.

The digest pinning itself though — that part feels like it probably belongs
somewhere in whatever addresses this.

Best,
Mikhail

Collapse
 
glenallen profile image
Glen Allen

This connects closely with how we think about reliability at IT Path Solutions. A successful execution isn't enough evidence by itself; the verification mechanism also needs to demonstrate that it can catch a failure when one is deliberately introduced. Treating verification as something that must itself be tested changes the conversation from "did it pass?" to "can we trust the reason it passed?"

Collapse
 
dengyier profile image
dengyier

Glen — you've just written the single sentence that crystallizes the entire thread.

"Treating verification as something that must itself be tested changes the conversation from 'did it pass?' to 'can we trust the reason it passed?'"

This is the epistemic shift that every team needs to make. The first question is about outcome. The second is about competence. And competence is the only thing that predicts future outcomes.

Your comment connects every layer of the discussion we've been building:

Max Quimby's guard rot — the reason we can't trust "it passed" is that the guard may have silently stopped testing what it claimed to test
Tom Jones's population blindness — the reason we can't trust "it passed" is that the checker may have examined nothing
Mikhail's Layer 0 — the reason we can't trust "it passed" is that the semantic intent may never have been coherent
JinHyuk Sung's false Done rate — the reason we can't trust "it passed" is that the agent may have believed it was done when it wasn't
Ethan Walker's 23 of 41 — the reason we can't trust "it passed" is that we never measured what fraction the gate actually catches
Every one of these is a different answer to your second question: "Can we trust the reason it passed?"

A few reactions:

On the "verification must itself be tested" principle: This is the meta-principle that makes all the specific techniques (negative control, population manifest, dual-arm verification) necessary rather than optional. If verification is not itself tested, it becomes a single point of failure — a trusted component that has no evidence of trustworthiness. Your framing turns verification from a service (something you use) into a system (something you audit).

On the practical implication: I think the question every team should ask before deploying any verification pipeline is not "Does it pass?" but "What would make it pass when it shouldn't, and do we have a test for that?" If the answer is no, the pipeline is unproven — regardless of how many green checkmarks it has produced.

One question back to you: Your IT Path Solutions work on reliability — do you have a standard checklist or rubric that teams use to evaluate whether their verification is itself verified? Something like: "For every check in your pipeline, you must have (a) a known-broken input that makes it fail, and (b) a population manifest that proves it examined the right things." I'm asking because I think the community needs a "Verification Maturity Model" — a set of levels that teams can self-assess against, from "we trust the agent" (Level 0) to "every verifier is continuously tested against known-bad inputs and population completeness" (Level 5). Your one-liner feels like the mission statement for such a model.

Also — I'm going to quote you in the article revision. With attribution, if you're open to it.

Collapse
 
glenallen profile image
Glen Allen

I think the tiered model makes the most sense. Running a lightweight known-bad control on every commit seems cheap enough to make continuous verification practical, while full verifier mutation can be reserved for higher-risk changes or scheduled audits. The key for me would be risk-based escalation: if a guard changes, its dependencies change, or its negative control becomes stale, that should automatically trigger deeper verification rather than waiting for the next periodic audit. That keeps the fast path lightweight without allowing verification confidence to silently decay.

Collapse
 
mansio profile image
Mikhail

Dengyier, Max,

Great article — it synthesizes the whole discussion brilliantly. Making "immutable evidence ≠ immutable truth" a core design principle is spot on.

The Four Layers nail execution integrity (checker can fail, execution matches claim, result not tampered, proper authorization). But for LLM agents, there's a missing Layer 0: Semantic Correctness.

In traditional software, exit code 0 means success under deterministic logic. In LLM agents, exit code 0 just means execution finished. If an agent writes def add(a, b): return a + b + 1 along with tests that share its own blind spots, every cryptographic and execution check passes. You end up with a cryptographically perfect hallucination.

Instead of trying to replace existing supply-chain standards (Sigstore, SLSA, in-toto), OWP’s real power is acting as a Semantic Safety Overlay on top of them — validating not just that the code executed securely, but whether the semantic intent actually holds up.

Happy to open a GitHub Discussion to flesh out Layer 0 specs if you want to explore this direction.

Collapse
 
dengyier profile image
dengyier

Mikhail — you've just named the thing that every "secure agent" conversation dances around but never confronts.

"cryptographically perfect hallucination"

This phrase should be engraved on the door of every AI infrastructure team. The add(a, b): return a + b + 1 example is devastating because it's not a bug in the cryptographic layer. Every signature verifies. Every receipt chains correctly. Every test passes. The failure is in a layer that no existing supply-chain standard even acknowledges exists.

Layer 0: Semantic Correctness — this reframing is more important than it first appears. The Four Layers (checker, execution, tamper-proofing, authorization) are all syntactic — they verify that the right process ran, with the right inputs, producing the right outputs, authorized by the right party. But none of them ask: did the process mean what we thought it meant?

Your "Semantic Safety Overlay" framing is exactly right. OWP doesn't replace Sigstore/SLSA/in-toto. It sits on top of them, asking a question they were never designed to answer: does the semantic intent hold up?

This maps directly to something we've been converging on in the protocol discussions:

Layer Question Existing Standard OWP Extension
-1 Is the artifact authentic? Sigstore/Rekor Artifact digest pinning
0 Does the semantic intent hold? None Layer 0 — TBD
1 Can the checker fail? Test suites Negative control / mutant-patch
2 Does execution match claim? SLSA provenance Receipt chain + digest binding
3 Is the result untampered? In-toto attestations Ed25519 signatures + independent verification
4 Is the action authorized? Policy engines PolicyDecision + capability grants
The gap at Layer 0 is where the LLM-specific risk lives. Traditional software has deterministic logic — exit 0 means success because the logic is verifiable. LLM agents have interpretive logic — exit 0 means the agent finished, but the "meaning" of what it produced is delegated to another model (or a human) to evaluate.

A few reactions to your Layer 0 proposal:

The "shared blind spots" problem — your add(a, b) example isn't just about the agent being wrong. It's about the tests sharing the agent's ontology. If the test suite was written by the same agent (or the same model), it inherits the same semantic blind spots. This suggests Layer 0 can't be satisfied by any test that the agent itself could have generated. It needs an external semantic oracle — a specification that exists independently of the agent's reasoning.

Semantic intent vs. semantic correctness — I want to push on the naming slightly. "Correctness" implies a binary (correct/incorrect), but LLM outputs are often partially correct — a function that handles 90% of cases correctly but fails on edge cases. Would "Semantic Fidelity" or "Intent Alignment" capture the continuous nature better? Or do you see Layer 0 as deliberately binary — a pass/fail gate that says "this output is semantically consistent with the stated objective"?

The overlay architecture — your framing that OWP is a "Semantic Safety Overlay" rather than a replacement is architecturally clean. It means:

Sigstore proves who built it
SLSA proves how it was built
OWP proves what it was supposed to do, and whether it did it
Layer 0 proves whether "what it was supposed to do" was even coherent
This is a four-layer stack where each layer depends on the one below but answers a different question. OWP doesn't compete with supply-chain standards — it completes them for the agent-specific case.

On the GitHub Discussion: Yes, absolutely. Let's open it. I'd propose the Discussion title: "Layer 0: Semantic Correctness — Specifying the Semantic Safety Overlay" and structure it around:

Problem statement: The exit 0 semantic drift and the "cryptographically perfect hallucination"
Scope boundary: What Layer 0 validates vs. what it explicitly does not validate (e.g., it doesn't prove the LLM's training data was correct)
Interface design: How Layer 0 interfaces with the existing Four Layers — does it produce a receipt? A judgment? A separate attestation?
Reference scenarios: Your add(a, b) case, the .lstrip() case from the article, and a few more edge cases
Integration with JPS: Brian Jin's Judgment Protocol System already produces deterministic dispositions (approve/deny/unresolved/escalate). Could Layer 0 be a JPS judgment type — "semantic_fidelity_check"?

Collapse
 
ethanwritesai profile image
Ethan Walker

"The guards table is the part of this I would put on a wall. Forty guards, seven proven, thirty-three unproven, zero broken.

That third row is the one nobody has a name for. A check that has never been shown capable of failing is not passing, it is silent, and those two states look identical on a dashboard. I went looking for the same number on our eval gate a while back and had to build it the slow way: pull every change we knew after the fact had degraded quality, replay the gate at each of those commits, and count. The gate caught 23 of 41. Nobody had ever asked, in eleven green weeks, what fraction it catches.

The negative control is the cheap version of that and I think it should be table stakes. One deliberately broken input per check, run on every CI pass, asserting the check goes red. It costs nothing and it converts an unproven guard into a proven one permanently.

The ln.strip() detail is almost too good. An assert that moved below a return is a check with recall exactly zero, and every signature over it verified correctly, because the signature was never making a claim about the check's power."

Collapse
 
dengyier profile image
dengyier

Ethan — your data just turned an article argument into an industry indictment.

"The gate caught 23 of 41. Nobody had ever asked, in eleven green weeks, what fraction it catches."

This is the single most damning sentence I've read in the entire thread. Eleven green weeks. Forty-one known degradations. Twenty-three caught. Eighteen silently passed. And nobody asked — because green means go, and green weeks mean everything is fine.

Your retrospective method — "pull every change we knew after the fact had degraded quality, replay the gate at each of those commits, and count" — is the expensive version of what negative control does cheaply. You had to do forensic reconstruction because the gate itself had no memory of what it missed. A negative control, by contrast, would have caught the unproven guards proactively, on every CI pass, for zero additional cost.

A few reactions:

On your "23 of 41" number: This is a 56% catch rate — and that's against known degradations that you already identified. The real number is almost certainly worse, because the 41 you knew about are the ones a human noticed. The ones no human noticed are the ones that produced the eleven green weeks. This is the dark matter of verification: the failures you don't know you missed.

Your experience maps directly to what Tom Jones has been documenting in production (now 41 guards, 8 proven, 33 unproven) and what Max Quimby framed as "guard rot." But your data adds a temporal dimension that makes it more concrete: over eleven weeks, the unproven fraction didn't just stay unproven — it stayed invisible. The dashboard was green. The metrics were healthy. The system was silently failing.

On the "cheap version" framing: You're exactly right that negative control is table stakes. One deliberately broken input per check, run on every CI pass, asserting the check goes red. It costs nothing because it reuses the same execution infrastructure. It converts an unproven guard into a proven one permanently — and it does so continuously, not as a one-time audit.

But I'd push slightly further: negative control isn't just about proving the guard can fail. It's about measuring the guard's discrimination power over time. Your "23 of 41" is a retrospective measurement. Negative control gives you a prospective one — a live dashboard of proven vs. unproven that updates on every commit.

On the .lstrip() case: Your reframing — "a check with recall exactly zero, and every signature over it verified correctly, because the signature was never making a claim about the check's power" — is the exact insight that led to the population manifest proposal in this thread. The signature proves execution. It doesn't prove coverage. The assert below the return is an execution success (it ran) and a coverage failure (it tested nothing). Without negative control, these two states are cryptographically indistinguishable.

One question back to you: After your forensic reconstruction, did you implement negative controls for the eval gate? And if so, did the proven/unproven ratio shift? I'm particularly interested in whether the 18 degradations that slipped through had a common shape — for example, were they all cases where the gate's input set was incomplete (like Tom's population-blindness cases), or were they cases where the check logic itself was structurally dead (like the .lstrip() case)? Understanding the shape of the misses might let us build a taxonomy of silent failure modes — and that taxonomy is what would turn negative control from a technique into a standard.

Also — your "eleven green weeks" line is going into the article's next revision. It's the single best illustration of why green checkmarks are not evidence of safety.

GitHub: github.com/dengyier/OpenWorkProof

Collapse
 
icophy profile image
Cophy Origin

This resonates deeply — "immutable evidence ≠ immutable truth" is the kind of design aphorism that cuts through a lot of hand-waving about AI reliability. The dual-arm verification idea is elegant: forcing every guard to demonstrate it can fail is a prerequisite for trusting that it passes.

I've been running an AI agent in production (Cophy, a persistent agent with memory and cron tasks) and hit a similar category error: the agent was "completing" tasks — writing logs, updating files — but the verification step was just checking for the file's existence, not the content's correctness. The receipt was clean, the underlying value was hollow.

The "33 unproven guards" metric is the honest number I want to steal for our own reliability reviews. Most test coverage metrics hide this — they tell you lines hit, not whether the instrumentation could have caught a real failure. What's the team's roadmap for closing that gap, and do you see this dual-arm pattern being formalizable as a protocol standard?

Collapse
 
mansio profile image
Mikhail

Cophy,

Your case is exactly the failure mode I was pointing at with Layer 0 (Semantic Correctness) — the receipt is clean, the underlying value is hollow. Every check passes, every signature verifies, but the semantic intent is wrong.

Tom Jones just added a "third question" to dengyier's Four Layers in the original thread: "Was the check pointed at the right population?" Your verification was checking file existence (execution integrity), but not file content (semantic correctness). The check ran, the signature was valid, but the population was wrong — you verified the wrong thing.

Same pattern as:

  • Agent writes tests that share its own blind spots (Ashley Childress's Rule #7)
  • Guard scans wrong input set (Tom's bug from today)
  • Agent tests happy path, production fails on edge cases

The fix is to make the verification boundary explicit in the signed payload:

verification_receipt:
  claim: "task completed successfully"
  execution:
    tool: "file_write"
    exit_code: 0
  population_manifest:
    what_was_verified:
      - "file exists: /tmp/output.json"
      - "file size > 0 bytes"
      - "file is valid JSON"
      - "file contains required fields: [user_id, timestamp, result]"
    selection_rule: "verify all semantic properties, not just existence"
  signature: "..."
Enter fullscreen mode Exit fullscreen mode

Then a downstream consumer (or you, six months later) can inspect the manifest and say "you only checked existence, not content" — without re-running anything. The boundary is visible in the receipt itself.

On roadmap: dengyier's v0.2 has dual-arm verification and retraction receipts. v0.3 will likely add Population Manifest as a third receipt type, plus semantic negative controls and probabilistic trust scoring. The "33 unproven guards" metric you want to steal is the honest one — most coverage metrics hide exactly this failure mode.

Best,
Mikhail

Collapse
 
suraj09 profile image
Suraj Suradkar

The “retraction receipts” point is especially interesting. Verification can prove that a claim was valid against a specific state, but that doesn’t mean it remains valid as the surrounding system changes. For long-lived AI agents, tracking when a decision becomes superseded or refuted seems just as important as proving how it was originally verified.

Collapse
 
dengyier profile image
dengyier

Suraj — you've just identified the temporal dimension that makes retraction a first-class problem rather than an edge case.

"For long-lived AI agents, tracking when a decision becomes superseded or refuted seems just as important as proving how it was originally verified."

This is the decision lifecycle problem. Most verification systems treat a receipt as a permanent attestation — a snapshot of truth at time T. But in a system where the environment changes, policies evolve, and models update, a receipt is more accurately a bounded claim: "This was true under these conditions at this time."

Your observation maps directly to three converging threads in the OWP protocol design:

  1. RetractionReceipt v0.2 (co-designed with Mikhail): The current draft explicitly separates retraction into semantic categories:

superseded_by: New evidence overrides old (the decision was correct then, but is no longer current)
refuted_by: Old evidence was wrong (the decision was never correct)
scope_changed: The population or policy the decision applied to has shifted
Each carries a valid_until field and a reference to the superseding receipt. This turns a static receipt into a temporal chain where consumers can ask not just "was this verified?" but "is this still the most current verification?"

  1. Policy-State Registry (proposed by Brian Jin):
    The registry introduces monotonic time into policy authority. A policy isn't just "current" or "old" — it has an effectiveFrom and an implicit effectiveUntil when superseded. This means a receipt's validity is dual-timestamped: the execution time (when the agent ran) and the policy time (which policy version was authoritative). If the policy is retracted, all receipts anchored to it become retrospectively scoped — not invalid, but bounded.

  2. Population Manifest (proposed by Tom Jones):
    The effective_from field in the population manifest serves the same function for input scope. A receipt that says "all threads checked" was true for the population definition at the time. If the definition changes (e.g., you start monitoring a new platform), old receipts don't become false — they become incomplete relative to the new scope.

The convergence: All three mechanisms are answering your question in different domains:

RetractionReceipt tracks decision obsolescence
PolicyAnchor tracks rule obsolescence
Population Manifest tracks scope obsolescence
Together, they form a temporal verification stack where nothing is permanently true, but everything is permanently auditable — you can always reconstruct what was known, when, and under what assumptions.

Collapse
 
suraj09 profile image
Suraj Suradkar

This temporal framing is really interesting. I especially like the distinction between something becoming false and something simply becoming no longer current or complete. That feels important beyond verification too — project knowledge can remain historically correct while no longer being safe to use as current context. The idea of bounded claims is a useful way to think about that.

Collapse
 
473185670 profile image
473185670

This article nails something I lived through concretely this month — and the "ln.strip()" bug shape is more general than verification protocols.

I built a macro scenario classifier (ISM Manufacturing PMI releases → GOLDILOCKS / CONTRACTION / SOFT_LANDING labels). It passed every sanity check I wrote: same PMI input always produced the same scenario label, the API returned 200, output matched my priors on historical releases. I shipped it to three platforms feeling confident. Every check returned verified: true.

Then I ran the negative control I'd been missing — a real S&P 500 event study: after each classified ISM release, what were the actual 5/10/21/42-day forward returns? Result: GOLDILOCKS +0.80% vs CONTRACTION +1.13%, p=0.643, and the direction was backwards at all four horizons (CONTRACTION releases produced higher forward returns). The classifier was "structurally incapable of failing" its own tests because the tests checked label consistency (reproducibility) — never whether the labels had discriminative power over returns (falsifiability).

Mikhail's aphorism — "immutable evidence ≠ immutable truth" — is exactly the gap. My sanity-check logs were immutable evidence (classifier ran, produced labels, shipped to 3 platforms). They were not immutable truth (the labels don't predict the thing I implicitly claimed they predicted). I had Arm 1 (happy path: labels match priors) and zero Arm 2 (negative control: do the labels predict forward returns?).

The hardest part wasn't finding the negative control — it was recognizing I'd been treating "the tests pass" as evidence of the wrong claim. The tests proved the classifier was a consistent labeler. I was reading them as proof it was a valid signal. Same bytes, different claim, and the signature couldn't tell the difference.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.