VSURE

Reserve leakage, release-before-pay, and the duplicate payment

Four money defects that surfaced when agents were allowed to run claims end to end — and what each one looked like in the ledger.

Claims 10 min read Key figure · 4 defect classes, all caught All posts

When we let agents run claims end to end — FNOL, reserve, settle, close — four money defects surfaced in our own claims product within the first few weeks. None of them was an AI failure. All four were bugs that had been sitting in the software, waiting for something patient enough to walk every path.

Here they are, with what each looked like in the ledger and how to detect it in yours.

1. Reserve leakage on closure

What happened: a claim was settled and closed. The settlement posted correctly. The outstanding reserve was never released, so it stayed on the books as an open liability against a closed claim.

Why it survived: closing and reserve release were separate operations in separate service methods, and the closure path called only one of them. Every individual operation was correct. Every test passed, because the tests tested the operations.

flowchart LR
    subgraph expected["Expected"]
        A1["Settle $58k"] --> B1["Release reserve
$250k to $0"] --> C1["Close claim"] end subgraph actual["What happened"] A2["Settle $58k"] --> C2["Close claim"] R2["Reserve still $250k
on a closed claim"] end style B1 fill:#e2f5f9,stroke:#0e97b0 style R2 fill:#fbede3,stroke:#b4531b
A closed claim carrying an open reserve. Aggregated across a book this overstates outstanding liabilities and understates the accuracy of every reserve adequacy metric you report.

The detection query. This one is worth running against any claims system today:

-- Closed claims still carrying a reserve. Should be empty. SELECT claim_ref, closed_at, outstanding_reserve FROM claims WHERE status = 'closed' AND outstanding_reserve > 0 ORDER BY outstanding_reserve DESC;

The fix: closure now requires the reserve movement as part of the same transaction. You cannot close a claim without passing the release, in the same way you cannot perform a write without a decision object.

2. The settlement that was never written

What happened: a settlement was approved, the payment was issued, the claim was marked settled — and no settlement record was created. The money left. The row describing why never existed.

Why it survived: the settlement entity was written by a code path that had been refactored into a helper, and one caller stopped invoking it. The claim status update and the payment were on the main path; the record of the settlement itself was on the branch that got orphaned.

This is the most dangerous of the four, because from the outside everything looks complete. The claimant was paid. The claim shows settled. Only when an auditor asks "on what terms was this settled, and who agreed them" does the absence appear.

-- Claims marked settled with no settlement record. SELECT c.claim_ref, c.settled_at, c.settled_amount FROM claims c LEFT JOIN settlements s ON s.claim_id = c.id WHERE c.status = 'settled' AND s.id IS NULL;

3. Release before pay

What happened: the reserve was released before the payment was confirmed. For a window of time the claim showed no outstanding liability and no completed payment — the money was in neither place.

Why it matters: if the payment then fails — a rejected bank detail, a hold, a downstream error — the claim is left with no reserve and no payment, which means it has silently disappeared from your outstanding liabilities. Nobody is watching a claim that looks finished.

The pattern to look for: ordering. Release after confirmation, never before. If the payment is asynchronous, the reserve stays until the confirmation arrives, and a reconciliation job catches anything that never confirms.

The general form of this bug

Any pair of operations where one reduces a liability and the other creates an asset must be ordered so that a failure between them leaves the liability standing. The safe ordering is always the one that fails conservatively — an unreleased reserve is a reporting nuisance; a released reserve with no payment is a claim that has vanished.

4. The duplicate payment

What happened: the same settlement was paid twice. A retry after a timeout produced a second payment because the operation was not idempotent.

Why an agent found it and a human had not: this is the clearest example of why running agents against your own systems is worth doing. A human adjuster who clicks Pay and sees a spinner will wait, then refresh, then check whether it went through, and then call someone. An agent gets a timeout and does the correct engineering thing — it retries.

The retry was correct behaviour. The endpoint was not idempotent. The combination paid twice.

# The fix: an idempotency key derived from the business facts, # not from the request. A retry of the same settlement is the # same key and returns the original result rather than paying again. idempotency_key = f"settle:{claim_ref}:{settlement_id}" # The payment table has a unique constraint on it, so the second # attempt fails at the database rather than relying on a check # that could race.

An agent will find every non-idempotent endpoint you have, because it retries exactly the way your documentation says it should.

What the four have in common

None of them is a model failure. The agents reasoned fine. Every defect lived in the software between the decision and the system of record — the plumbing, not the intelligence.

All four are also invisible to a model-call log. The calls succeeded. The tools returned 200. The run looked clean end to end. What made them visible was a ledger where money movements sit next to each other and can be checked for the relationships that must hold:

  • A closed claim has no outstanding reserve.
  • A settled claim has exactly one settlement record.
  • A released reserve has a confirmed payment.
  • A settlement has exactly one payment.

Each of those is a one-line query against a business event ledger and impossible to express against a trace store, because a trace store has no concept of a reserve.

The four queries, as a standing check

We now run these as scheduled assertions rather than as an investigation. Any non-empty result raises an incident.

AssertionExpectedCatches
Closed claims with reserve > 0 Empty Reserve leakage
Settled claims with no settlement row Empty Orphaned code path
Released reserves with no confirmed payment after 24h Empty Release before pay, failed payments
Settlements with more than one payment Empty Non-idempotent retry

If you run a claims system, these four take about an hour to write and we would be genuinely surprised if all four came back empty the first time.

What this looks like in InsightLense

The four relationships at the end of this post are now standing monitors. InsightLense runs them on a schedule and raises an incident on any non-empty result, so these defects cannot recur silently.

Ledger assertions claims 1 breach open
AssertionExpectedFoundExposureStatus
Closed claims with reserve > 00 rows0passing
Settled with no settlement row0 rows0passing
Released reserve, no payment 24h0 rows3$412,000incident
Settlement with > 1 payment0 rows0passing
Three claims with a released reserve and no confirmed payment. $412,000 that has left the liabilities and not arrived anywhere — caught the same day rather than at quarter end, because the assertion runs against the ledger rather than waiting for a reconciliation.

An assertion is worth more than a fix here. Each of these bugs was individually fixed in an afternoon; what stops the class returning is that InsightLense holds the invariant, in a place that is independent of the claims code, and complains when it stops being true.

The uncomfortable conclusion

These bugs were all older than the agents. They had been in the product through human use, because humans navigate software forgivingly — they wait instead of retrying, they notice a spinner, they work around a broken path without reporting it, and they carry in their heads the knowledge that a closed claim ought to have its reserve released.

Put something patient, literal and tireless in front of the same software and it walks every path exactly as specified. That is uncomfortable and it is the point. The carrier was built as a conformance harness, and this is what it was for.

Where do your agents already act on real records?

Tell us that, and what you would need to prove about those actions to an auditor. We will set up a hands-on walkthrough within two weeks.

Request a demo →