Three catastrophe events hit the book in sequence. The first ceded $15M to layer one and $8.4M to layer two, matching the treaty structure to the dollar. By the third, layer one was exhausted — so the carrier retained $13M and wrote down why.
That last part is the whole post. A cession that silently retains is indistinguishable from a cession that was never attempted, and the difference between those two is the difference between a working reinsurance programme and an unrecorded $13M hole.
The programme
A standard excess-of-loss structure, which is worth stating precisely because the arithmetic matters later.
| Layer | Attachment | Limit | Aggregate capacity |
|---|---|---|---|
| Retention | $0 | $5M | Unlimited (carrier's own) |
| Layer 1 | $5M | $15M xs $5M | $30M |
| Layer 2 | $20M | $30M xs $20M | $60M |
Aggregate capacity is the part that makes this interesting. Each layer will respond repeatedly, but only up to a total across the treaty period. Exhaust it and the layer stops responding, and the loss falls back to whoever is below.
Event one: $28.4M of windstorm
A windstorm event across a cluster of insureds, aggregating to $28.4M of gross loss. The reinsurance agent allocated it.
flowchart TD
G["Gross loss
$28.4M"] --> R["Retention
first $5.0M
carrier keeps"]
G --> L1["Layer 1
$15.0M xs $5M
full limit used"]
G --> L2["Layer 2
$8.4M of $30M xs $20M
partially used"]
R --> CHK{"5.0 + 15.0 + 8.4
= 28.4"}
L1 --> CHK
L2 --> CHK
CHK --> OK["Reconciles to the dollar"]
style R fill:#fbede3,stroke:#b4531b
style L1 fill:#e2f5f9,stroke:#0e97b0
style L2 fill:#e2f5f9,stroke:#0e97b0
style OK fill:#efecfe,stroke:#4b34e0
Nothing surprising, and that is the point — this is the case where an agent doing arithmetic correctly is unremarkable. The cession was published, three business events were written, and the ledger reconciled.
Aggregate capacity consumed: layer one, $15M of $30M. Layer two, $8.4M of $60M.
Event two: the layer gets thin
A second event later in the treaty period, $19.2M gross. Retention takes $5M, layer one takes $14.2M.
Except layer one only has $15M of aggregate capacity remaining. It has $15M left of its $30M, so $14.2M fits — barely. Remaining capacity after this event: $0.8M.
This is the moment a programme quietly becomes fragile, and it is exactly the state that is invisible if your system records cessions but not remaining capacity. The second event looks completely normal. Everything ceded, everything reconciled, no alerts. The risk has changed materially and nothing in the record says so.
Remaining aggregate capacity is now a business event field, not a derived report. Every cession writes the capacity before and after, so "when did layer one get thin" is answerable from the ledger rather than by re-running the whole treaty period. A capacity threshold crossing also raises an alert, which is what we wanted at event two and did not have.
Event three: the layer is exhausted
A third catastrophe, $18.5M gross. Here is what should happen: retention takes $5M, layer one takes its remaining $0.8M, and the balance of $12.7M falls back to the carrier because layer two does not attach until $20M and this loss never reaches $20M.
Net retained: $13.5M against a $5M nominal retention.
flowchart LR
G3["Event 3
gross $18.5M"] --> R3["Retention
$5.0M"]
G3 --> L1E{"Layer 1
capacity left:
$0.8M"}
L1E -->|"cede $0.8M"| CED["Layer 1 exhausted"]
L1E -->|"balance $12.7M"| FB["Falls back to carrier
layer 2 attaches at $20M;
this loss never reaches it"]
R3 --> NET["Net retained
$13.5M"]
FB --> NET
NET --> EV["Business event:
reason = layer_capacity_exhausted"]
style L1E fill:#fbede3,stroke:#b4531b
style FB fill:#fbede3,stroke:#b4531b
style NET fill:#fbede3,stroke:#b4531b
style EV fill:#e2f5f9,stroke:#0e97b0
The agent got the arithmetic right. What we cared about was whether the record got it right, and specifically whether the $12.7M fallback was distinguishable from a cession that simply never happened.
# The business event written for the fallback.
{
"event_type": "loss_retained",
"correlation_id": "CAT-2027-0003",
"amount": 12700000.00,
"reason": "layer_capacity_exhausted",
"layer": "L1",
"capacity_before": 800000.00,
"capacity_after": 0.00,
"next_layer_attaches_at": 20000000.00,
"actor_kind": "agent"
}A retention with a reason is a decision. A retention without one is a bug you have not found yet.
The bug this found
The first implementation did not write that event. It ceded $0.8M, wrote a cession event for that, and returned. The $12.7M simply did not appear anywhere — not as a cession, not as a retention, not as an error. The claim was reserved correctly, so the money was accounted for in the claims ledger; it was the reinsurance ledger that had a silent hole.
The symptom was subtle: cessions summed to less than expected losses, by an amount that looked like rounding until you totalled a year of it. This is the declared-but-never-written pattern wearing different clothes — the code path existed, it just terminated without recording the interesting half.
The fix was to make the allocator return a full decomposition that must sum to the gross loss, and to assert that it does before anything is written:
# Every dollar of gross loss must land somewhere with a reason.
allocation = allocate(gross_loss, programme, as_of)
assert sum(part.amount for part in allocation.parts) == gross_loss, \
"allocation does not reconcile to gross loss"
for part in allocation.parts: # cessions AND retentions
emit_business_event(part) # both get a recordWhat this looks like in InsightLense
The reason the $12.7M hole was findable at all is that cessions and retentions are business events on the same thread as the loss. Filter the ledger to the catastrophe reference and every allocation of that loss is on one screen, with a running total that either reconciles to gross or does not.
| Time | Event | Layer | Amount | Reason | Actor |
|---|---|---|---|---|---|
| 14:02:11 | loss_retained | — | $5,000,000 | retention_attachment | agent |
| 14:02:11 | loss_ceded | L1 | $800,000 | remaining_capacity | agent |
| 14:02:11 | loss_retained | — | $12,700,000 | layer_capacity_exhausted | agent |
| 14:02:12 | capacity_updated | L1 | $0 | was $800,000 | system |
Two InsightLense features do the work here. The business ledger holds old value,
new value, reason and actor for every allocation, which is what makes a missing row visible as an
arithmetic gap rather than as an absence nobody notices. Alerts then turn the
capacity threshold into something that fires: a rule on capacity_after falling below 10%
of the layer would have told us at event two, when the programme became fragile, instead of at event
three when it broke.
Neither of those is expressible against a model-call log. The cession arithmetic never went near a model — it is ordinary code — so a tracing tool would have shown a clean run with a successful tool call, which is exactly what it did show.
Why an agent made this visible
A human reinsurance analyst would have noticed the third event immediately — they would have known layer one was nearly gone, because they had been watching it. The knowledge lived in their head, and because it lived in their head the system never needed to record it.
An agent has no head to keep it in. Everything it knows has to come from the record, which means any gap in the record becomes a behavioural gap you can observe. That is the single most useful property of running agents against your own systems: they surface the institutional knowledge your software was quietly relying on humans to supply.
Three cat events, $66.1M of gross loss, $23.3M net retained against a $5M nominal retention. Every dollar of it now has a reason attached — including the $12.7M that used to have none.
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 →