DEV Community

Cover image for A Receipt Is Not Proof Forever. It Is a Promise to Reopen the Claim.
Self-Correcting Systems
Self-Correcting Systems

Posted on

A Receipt Is Not Proof Forever. It Is a Promise to Reopen the Claim.

My memory gate passed 16 out of 16 frozen cases.

Then I blocked the article.

Not because the run was fake. The implementation did exactly what it claimed, and an independent checker rebuilt the whole thing from the raw fixtures — same 16/16, same regression suite, no mismatch. The score was real.

This continues the work from The Citation Lied Without Lying, where the first gate caught a specific kind of citation-shaped failure: the quote was real, but the relationship the model claimed from it was not.

Then it stopped grading the system against my answer key, and started grading my answer key against the law I said the system enforced.

Three doors were still open. An owner-consent record could pass with no real external authority behind it. A blanket standing rule could quietly rebuild the exact ambient power the design was supposed to kill. And a consent granted to one reviewer could be borrowed by a different requester who never had permission to touch the record at all.

The scoreboard was green. My definition of "passing" was incomplete.

That's the moment this project changed for me. I started out trying to stop a true quote from carrying a false relationship. I thought the hard part was getting a gate to confirm the relationship correctly. Jackson, Mudassir, Kartik, nexus-lab-zen, Mike, Dipankar, Alex, Nova, and Tae kept dragging me toward a harder question:

What happens when the basis of trust changes after the gate already said yes?

The quote never lied

The original bug was dangerous because every visible piece of it was real.

The quoted sentence existed. The old rule existed. The new rule existed. The citation was word-for-word correct. The lie lived in the edge between them — the system claimed one rule superseded another when the source never said so. A normal citation check finds the span in the document and waves it through. Word-precision gets mistaken for relation-precision.

My first fix was deliberately dumb. The proposer stays probabilistic, but the confirmer is deterministic and can't be argued with: if a model proposes supersedes, the gate demands explicit change language, scope overlap, a resolvable target, and a cited span that actually binds the claimed relation.

That made cheap citation-shaped lies expensive. It also hit its own wall fast. A deterministic span check can reject a claim when the operator is missing — but it can't honestly infer every relationship that language leaves implicit. Two rules can flatly contradict each other with no sentence saying replaces. Negation flips a perfect-looking span. The direction can be backwards even when both rule names and the change word sit side by side. When Kartik asked the clean version — how do you decide entailment direction without running NLI on every span? — the honest answer was: you don't. The gate confirms the narrow class it can actually adjudicate, and everything else has to fail loud as "outside coverage" instead of passing quietly because the citation looks respectable.

That's less impressive than claiming the gate understands the sentence. It's also more useful.

The comments moved the problem to write time

The thread didn't behave like applause. Every answer I gave, someone took and asked: and what authorized that?

Jackson pushed relations toward write-time facts instead of prose reconstructed after the fact — and caught the carve-out: "Rule B replaces Rule A for EU customers" must not silently retire Rule A everywhere else. Mudassir had lived the same thing in policy docs, models inferring supersession from conflicting text no sentence ever asserted. nexus-lab-zen carried it into agent completion reports: a real exit code and a real file path can still back a false claim that the work is done. Report-precision is not state-precision.

Then Mike named the next attack surface. The low-trust label isn't the failure — laundering is. Put a prose-only claim correctly into a lower-trust pile at write time, and if nothing alarms when someone later treats it as verified, your two-tier split is just a waiting room for the same lie.

And Dipankar broke the authority model from the other side. Permission to write the new record is not permission to retire the old one. In a single-author store you never notice, because one actor owns both ends. In a multi-agent store, supersession is a two-party edge: the owner of the target — or a narrow grant from that owner — has to authorize the retirement.

So a relation could no longer be this:

{ "from": "rule_b", "relation": "supersedes", "to": "rule_a" }
Enter fullscreen mode Exit fullscreen mode

It had to carry what authorized the edge: who requested it, who owned the target, which grant applied, whether that grant was still live at the moment of writing, what scope it covered, and where the authority actually came from. The gate stopped asking whether a sentence was convincing. It started asking whether a principal was allowed to spend authority over someone else's record.

The 16/16 that wasn't enough

We froze the attacks before building the defense — and most of those attacks weren't mine. They were authored by readers in my comments — Jackson, Mike, Dipankar, Alex — which is the only thing that makes a self-graded fixture worth anything. I split the work into three hands that don't trust each other: a maker who builds the defense, an independent checker who never sees the maker's reasoning, and an adversary who only tries to break things and never patches what it broke.

The maker built the store-authority evaluator and produced the first clean run: 16 of 16, one known ceiling case reported separately, regressions green. The independent checker recomputed it from raw. Same score, same everything.

Then the check went one level deeper — and found the fixture only exercised the new law through some of the paths the code still accepted.

That distinction is the whole point.

The checker didn't prove the maker lied. It proved something worse for anyone who loves a green scoreboard:

a test suite can faithfully certify an incomplete promise.

Independence of check catches reproducibility failures — but a checker that only reruns your published test can never tell you that you forgot to test the most dangerous reading of your own rule.

The move wasn't to bury the 16/16 until everything looked clean. It was to keep both receipts: the implementation matched the frozen answer key, and an independent audit found the answer key incomplete. That's a stronger, more honest record than either PASS or FAIL alone.

"External" was only a better label

Mike came back for the word I was leaning on to end the authority regress: external.

My evaluator recognized a channel named owner_console and trusted its declared writers. Sounded like a root outside the tuple. It wasn't. I took a self-minted root the gate had correctly rejected, changed only its fields to claim owner_console and owner-only write access, and it passed.

The gate was trusting the label of the channel instead of proving the relation-minting component couldn't write through it.

Same authority. Better costume.

Mike's cut was sharper than provenance metadata: an external channel has to be defined by capability, not description. The component that mints relations must have no write path to the authority channel at all — separate keys, a separate infrastructure ACL, enforced below the application whose behavior is on trial. The test is brutal and concrete: if the minting component had full control of its own process, could it produce a byte-identical authority event through any path it can reach? If yes, the wall isn't a wall. It's a longer hallway.

Dipankar closed the same boundary from the principal side. Give the confirmer zero intrinsic retirement authority — every retirement edge must point back to a live grant the requester already holds from the owner of that exact record. The confirmer adds nothing; it only spends authority the requester brought. And it checks that grant at mint time, live, not from the snapshot the proposal carried in — or you've built a clean time-of-check-to-time-of-use hole.

Independence can fail on agreement

Then Alex pushed the work somewhere a write-time gate simply can't follow.

I'd been treating an adjudicator from a different institution as independent. Alex split two meanings hiding inside that word. One is interest: does the adjudicator gain if the edge stands? That fails loud — an interested arbiter hands you a confidently wrong verdict, and wrong verdicts get caught. The other is common cause: do the adjudicator and the writer draw from the same upstream? That one fails on agreement. If three "independent" sources all draw from the same upstream, they agree for the same wrong reason — one broken dependency echoing through three mouths. It looks exactly like confirmation. Nothing trips.

A deterministic gate can compare declared provenance paths and refuse to certify when they share a named node. Useful — but not proof of independence. Two vendors white-label the same feed. Two "separate" sources import the same broken library. Undeclared sharing stays invisible at write time.

Alex's next move is what actually reshaped the ending. Hidden common cause is invisible at write time without being invisible forever — the world leaks structure. Correlated sources refresh together, go stale together, carry the same rounding defect, fail on the same edge, later disclose a shared vendor. The signal is never agreement; honest independent sources agree too. The signal is correlated defect. And the clock that catches it has to be ours — observer-side fetch time, latency, parse result, malformed fields — not the source narrating its own freshness, because a self-reported timestamp costs a liar nothing.

He wouldn't even let me keep that clean. The observer can become the hidden common cause: shared proxy range, shared deploy, one retry policy, one cron slot, one parser, one egress. You can manufacture the exact correlation you think you're detecting. None of it convicts anyone — a shared CDN or a common publication schedule produces the same fingerprint innocently. It lowers confidence in a disjointness claim. It never proves the link.

So why keep the receipt at all? Because it preserved the declared paths behind every accepted relation. When a hidden dependency finally surfaces — six months later, in a leaked vendor page or an acquisition — the system can ask one precise question: which relations did we mint under a disjointness claim that just became false? Without the receipt, you only know trust broke somewhere. With it, you know exactly what to reopen.

A receipt is a promise to reopen

I used to think the strongest gate was the one that made the right call at the moment of writing. I still want that gate. I just no longer think it's the whole job.

Some claims fall outside deterministic coverage. Some grants get revoked after a proposal starts. Some "independent" sources turn out to share a backend. Some low-trust claims get promoted through a path nobody was watching. The world can change what the evidence means after the system already acted on it.

So the stronger design carries four obligations: reject what it can prove is malformed or unauthorized; fail loud when a claim is outside its coverage; preserve the exact authority, scope, provenance, and evidence behind every accepted relation; and reopen the downstream claims when one of those dependencies later breaks.

But Alex named the ceiling under all of it, and he has more scar tissue on it than theory. Reopening has an attention limit. A reopen queue that fires on everything stops being read, and "we logged it" quietly becomes another silent pass — the exact failure the whole system was built to kill, wearing a dashboard.

So the receipt's real job isn't only can we reopen this? It's can we rank the queue so the scarce human eye lands where the damage is highest? And the obvious ranking is a trap. Sort by recency and volume and it feels neutral — but in Alex's own run history, one Trustpilot collector accounted for 962 of 2,190 production runs. That does not mean Trustpilot is the most important source of defects. It means you just surfaced the busiest thing you built. Volume is a fact about your schedule, not about the world: the same observer bias we spent two rounds scrubbing out of the clock, re-entering one level up, exactly when you think you're being fair. A busy queue looks like a working queue.

A better starting axis is the cost of a silent pass — where does a wrong value go if nobody looks? Output that only lands in a table gets read later. Output that feeds another pipeline moves first, because a defect there gets laundered into something that no longer looks like scraped data. Then subtract yourself first: if the correlated defect lines up with your own proxy range, deploy, cron window, retry logic, parser, egress path, or code version, it's your engineering bug, and it never reaches the world-facing queue at all. What survives that subtraction is the queue worth human review, not proof that the defect belongs to the world.

And the part that keeps it honest: three states, not two. Reviewed-clean, reviewed-bad, and not read. Unread must never collapse into passed — because unlike a promise to reopen, the count of unread items is a number you can publish, and that number is the real ceiling on how correctable the memory actually is.

Which leaves the last limit where it belongs: the damage model that ranks the queue is one you wrote yourself, so the thing that will hurt is whatever you ranked last. No version of this lets the author escape their own blind spot. There's only a version where the blind spot is small, named, and counted.

And that's the thread running through every one of these attacks.

Verification kept bottoming out in something the verifier could not manufacture for itself.

The capability separation lives below the app.
The authority root lives out of band.
Real independence lives in a world the gate can't fully see.
And the last-mile judgment lives in human attention the system can't create more of.

The mature move isn't to fake those from inside the tuple. It's to stop pretending, preserve the dependencies faithfully, fail loud when coverage ends, and point the scarce human eye at what matters most.

The 16/16 is still real. So are the three doors it forgot. So is the relabeled root that walked right through. None of those receipts cancels the others — together they describe the system more honestly than any single score could. That's where v3 actually stands: not solved, not empty, and no longer asking one gate to carry more certainty than it earned.

A receipt is not proof forever. It's a promise that when the basis of trust changes, we'll know what depended on it — and we'll open the case again.


The implementation and every frozen artifact are public in memory-authority-auditor, in order:

  • read-time PASS 0 — 84a2d70
  • carve-out C0 / C1 loop — 78d66a3 -> 1735134
  • store-authority run — d134e9e
  • the independent block that started this piece — e1ce236
  • frozen parity fixture — 49066c8
  • frozen capability-root fixture — 5825a31
  • frozen mint-time-revocation fixture — f516e58

The parity and capability defenses are still being repaired and independently checked. This article does not report v3 as solved, and won't attach a repaired final score until a non-maker recomputes the raw rows. Clone the repo, re-run it, break it.

Every turn in it came from someone treating the last article as something to attack instead of applaud: Jackson, Mudassir, Kartik, nexus-lab-zen, Mike, Dipankar, and Alex, and before them Nova and Tae. I'm grateful for the pressure. The gate is better because none of you were nice about it.

Top comments (0)