# Claim Lifecycle Architecture
This document provides comprehensive diagrams and descriptions of the claim lifecycle
in the Ergodic Insurance framework -- from loss generation through development, insurance
processing, accounting impact, and final payment.
## Overview
A claim in the framework follows a multi-stage lifecycle:
1. **Loss Generation** -- Stochastic processes produce loss events based on frequency and severity distributions, scaled by business exposure metrics.
2. **Claim Development** -- Raw loss events are assigned development patterns that determine how payments emerge over time.
3. **Insurance Processing** -- Each loss is processed through a multi-layer insurance program with deductibles, attachment points, limits, and reinstatements.
4. **Accounting Impact** -- Claim payments and recoveries are recorded through double-entry ledger transactions and accrual management.
5. **Cash Flow Projection** -- Future payment streams are projected, discounted, and reserved.
---
## 1. End-to-End Claim Lifecycle Flowchart
This diagram shows the complete flow from loss generation through final payment,
identifying which classes are responsible for each stage.
```{mermaid}
flowchart TB
subgraph Generation["Loss Generation"]
direction TB
MLG["ManufacturingLossGenerator
(composite)"]
ALG["AttritionalLossGenerator
freq: 5/yr, severity: $25K"]
LLG["LargeLossGenerator
freq: 0.3/yr, severity: $2M"]
CLG["CatastrophicLossGenerator
freq: 0.03/yr, Pareto"]
FG["FrequencyGenerator
Scaled Poisson Process"]
LD["LossDistribution
LognormalLoss | ParetoLoss | GPD"]
MLG --> ALG
MLG --> LLG
MLG --> CLG
ALG --> FG
LLG --> FG
CLG --> FG
FG -->|"event_times"| LE["LossEvent
time, amount, loss_type"]
ALG --> LD
LLG --> LD
CLG --> LD
LD -->|"severity samples"| LE
end
subgraph Development["Claim Development"]
direction TB
LE2["LossEvent"]
CD["ClaimDevelopment
pattern_name, development_factors, tail_factor"]
CL["Claim
claim_id, accident_year, reported_year,
initial_estimate, payments_made"]
CC["ClaimCohort
groups by accident_year"]
LE2 --> CL
CD --> CL
CL --> CC
end
subgraph Insurance["Insurance Processing"]
direction TB
IP["InsuranceProgram
deductible, layers[]"]
EIL["EnhancedInsuranceLayer
attachment_point, limit, reinstatements"]
LS["LayerState
current_limit, used_limit, is_exhausted"]
IP -->|"process_claim()"| DED{"Deductible
Applied?"}
DED -->|"Yes: company pays"| RET["retained_loss"]
DED -->|"Excess"| EIL
EIL --> LS
LS -->|"payment + reinstatement_premium"| REC["insurance_recovery"]
end
subgraph Accounting["Accounting Impact"]
direction TB
WM["WidgetManufacturer"]
LDG["Ledger
double-entry transactions"]
IA["InsuranceAccounting
recoveries, prepaid_insurance"]
AM["AccrualManager
payment timing"]
CLI["ClaimLiability
remaining_amount, payment_schedule"]
WM -->|"process_insurance_claim()"| LDG
WM --> IA
WM --> AM
WM --> CLI
end
subgraph CashFlow["Cash Flow Projection"]
direction TB
CFP["CashFlowProjector
discount_rate"]
PROJ["project_payments()"]
PV["calculate_present_value()"]
IBNR["estimate_ibnr()"]
RES["calculate_total_reserves()"]
CFP --> PROJ
CFP --> PV
CFP --> IBNR
CFP --> RES
end
Generation -->|"List[LossEvent]"| Development
Development -->|"claim_amount"| Insurance
Insurance -->|"retained_loss,
insurance_recovery"| Accounting
Accounting -->|"ClaimCohort"| CashFlow
```
### How it works
1. `ManufacturingLossGenerator` combines three sub-generators (attritional, large, catastrophic), each using a `FrequencyGenerator` for event timing and a `LossDistribution` for severity sampling.
2. Generated `LossEvent` objects are wrapped into `Claim` objects with a `ClaimDevelopment` pattern and grouped into `ClaimCohort` collections by accident year.
3. Each claim amount passes through `InsuranceProgram.process_claim()`, which applies the deductible, then routes the excess through sorted `EnhancedInsuranceLayer` objects tracked by `LayerState`.
4. The `WidgetManufacturer` records retained losses via the `Ledger` (double-entry accounting), creates `ClaimLiability` objects for multi-year payment schedules, and tracks insurance recoveries through `InsuranceAccounting`.
5. The `CashFlowProjector` uses `ClaimCohort` data to project future payment streams, estimate IBNR reserves, and calculate present values.
---
## 2. Insurance Layer Processing Sequence Diagram
This sequence diagram shows the detailed interaction when a claim is processed through
the multi-layer insurance program, including deductible application, layer responses,
and reinstatement mechanics.
```{mermaid}
sequenceDiagram
participant Caller as Caller
(Simulation)
participant IP as InsuranceProgram
participant L1State as LayerState
(Primary)
participant L1 as EnhancedInsuranceLayer
(Primary: $250K xs $250K)
participant L2State as LayerState
(1st Excess)
participant L2 as EnhancedInsuranceLayer
(1st Excess: $20M xs $5M)
participant L3State as LayerState
(2nd Excess)
participant L3 as EnhancedInsuranceLayer
(2nd Excess: $25M xs $25M)
Note over Caller,L3: Example: $12M Claim, $250K Deductible
Caller->>IP: process_claim(claim_amount=12,000,000)
activate IP
Note over IP: Step 1: Apply Deductible
IP->>IP: deductible_paid = min(claim, deductible)
= $250,000
IP->>IP: max_recoverable = claim - deductible
= $11,750,000
Note over IP: Step 2: Iterate Through Layers (sorted by attachment)
rect rgb(230, 245, 255)
Note over IP,L1: Primary Layer: $250K attachment, $4.75M limit
IP->>L1: can_respond(12,000,000)?
L1-->>IP: true (claim > attachment)
IP->>L1: calculate_layer_loss(12,000,000)
L1-->>IP: min(12M - 250K, 4.75M) = $4,750,000
IP->>L1State: process_claim(4,750,000, timing_factor)
Note over L1State: per-occurrence limit type:
payment = min(claim, limit)
L1State-->>IP: payment=$4,750,000, reinstatement_premium=$0
end
rect rgb(255, 245, 230)
Note over IP,L2: 1st Excess Layer: $5M attachment, $20M limit
IP->>L2: can_respond(12,000,000)?
L2-->>IP: true (claim > attachment)
IP->>L2: calculate_layer_loss(12,000,000)
L2-->>IP: min(12M - 5M, 20M) = $7,000,000
IP->>L2State: process_claim(7,000,000, timing_factor)
Note over L2State: Check aggregate limit,
process with reinstatements
L2State-->>IP: payment=$7,000,000, reinstatement_premium=$0
end
rect rgb(245, 255, 230)
Note over IP,L3: 2nd Excess Layer: $25M attachment, $25M limit
IP->>L3: can_respond(12,000,000)?
L3-->>IP: false (claim < attachment)
Note over IP,L3: Layer not triggered
end
Note over IP: Step 3: Calculate Result
IP->>IP: insurance_recovery = $11,750,000
Guard: min(recovery, max_recoverable)
IP->>IP: uncovered_loss = $0
IP-->>Caller: {total_claim: 12M,
deductible_paid: 250K,
insurance_recovery: 11.75M,
uncovered_loss: 0,
layers_triggered: [Primary, 1st Excess]}
deactivate IP
```
### Reinstatement Example
When a layer's aggregate limit is exhausted, the reinstatement mechanism activates:
```{mermaid}
sequenceDiagram
participant IP as InsuranceProgram
participant LS as LayerState
(Aggregate Type)
participant Layer as EnhancedInsuranceLayer
($5M agg limit, 1 reinstatement)
Note over IP,Layer: First Claim: $5M layer loss
IP->>LS: process_claim(5,000,000)
activate LS
LS->>LS: available_limit = $5,000,000
LS->>LS: payment = min(5M, 5M) = $5,000,000
LS->>LS: aggregate_used = $5M >= aggregate_limit
Note over LS: Aggregate exhausted!
Reinstatements available: 1
LS->>LS: reinstatements_used = 1
LS->>LS: current_limit = $5M (restored)
LS->>LS: aggregate_used = 0 (reset)
LS->>Layer: calculate_reinstatement_premium(timing=0.75)
Layer-->>LS: premium = base_premium * rate * 0.75
(pro-rata for time remaining)
LS-->>IP: payment=$5M, reinstatement_premium=$X
deactivate LS
Note over IP,Layer: Second Claim: $3M layer loss
IP->>LS: process_claim(3,000,000)
activate LS
LS->>LS: available_limit = $5,000,000 (reinstated)
LS->>LS: payment = $3,000,000
LS->>LS: aggregate_used = $3M < $5M limit
Note over LS: Still within reinstated limit
LS-->>IP: payment=$3M, reinstatement_premium=$0
deactivate LS
Note over IP,Layer: Third Claim: $4M layer loss
IP->>LS: process_claim(4,000,000)
activate LS
LS->>LS: available = min($2M remaining, $4M claim)
LS->>LS: payment = $2,000,000
LS->>LS: aggregate_used = $5M >= limit
Note over LS: Exhausted again!
No reinstatements left (1/1 used)
LS->>LS: is_exhausted = true
LS-->>IP: payment=$2M, reinstatement_premium=$0
deactivate LS
```
---
## 3. Claim State Diagram
This state diagram shows the lifecycle states a claim transitions through,
from initial generation to final settlement.
```{mermaid}
stateDiagram-v2
[*] --> Generated : LossEvent created by
ManufacturingLossGenerator
Generated --> Reported : Claim registered
(reported_year assigned)
Generated --> IBNR : Reporting lag
(not yet reported)
IBNR --> Reported : Late reporting
(IBNR estimate updated)
Reported --> InsuranceProcessed : InsuranceProgram
process_claim()
InsuranceProcessed --> Developing : ClaimDevelopment
pattern assigned
state InsuranceProcessed {
[*] --> DeductibleApplied
DeductibleApplied --> LayerAllocation : Excess above
deductible
LayerAllocation --> RecoveryCalculated : Sum layer
payments
RecoveryCalculated --> [*]
}
state Developing {
[*] --> PaymentYear1
PaymentYear1 --> PaymentYear2 : development_factors[0]
applied
PaymentYear2 --> PaymentYear3 : development_factors[1]
applied
PaymentYear3 --> FurtherYears : development_factors[2]
applied
FurtherYears --> TailPayments : Remaining factors
applied
TailPayments --> [*] : tail_factor or
fully developed
}
Developing --> Settled : get_cumulative_paid() >= 1.0
OR remaining_amount <= 0
Settled --> [*] : ClaimLiability removed
from active tracking
note right of IBNR
CashFlowProjector.estimate_ibnr()
estimates unreported claims
using chain-ladder method
end note
note right of Developing
ClaimDevelopment patterns:
- IMMEDIATE: [1.0]
- MEDIUM_TAIL_5YR: [0.40, 0.25, 0.15, 0.10, 0.10]
- LONG_TAIL_10YR: [0.10, 0.20, ... 0.02]
- VERY_LONG_TAIL_15YR: [0.05, 0.10, ... 0.01]
end note
note left of Settled
CashFlowProjector.calculate_total_reserves()
tracks case_reserves + IBNR
end note
```
### State Descriptions
| State | Description | Key Class |
|-------|-------------|-----------|
| **Generated** | A `LossEvent` has been produced by one of the loss generators with a time and amount. | `LossEvent` |
| **IBNR** | The loss occurred but has not yet been reported. Estimated via chain-ladder methods. | `CashFlowProjector` |
| **Reported** | The loss is registered as a `Claim` with a `reported_year` and `initial_estimate`. | `Claim` |
| **InsuranceProcessed** | The claim has been routed through `InsuranceProgram.process_claim()`, splitting it into retained loss and insurance recovery. | `InsuranceProgram` |
| **Developing** | Payments are being made according to the `ClaimDevelopment` pattern over multiple years. The `ClaimLiability` tracks remaining amounts. | `ClaimDevelopment`, `ClaimLiability` |
| **Settled** | All payments have been made. Cumulative paid reaches 100% of the claim amount. The liability is removed. | `Claim`, `ClaimLiability` |
---
## 4. Exposure-Based Frequency Scaling Flowchart
This diagram shows how exposure-based frequency scaling works, connecting the
financial state of the business to the frequency of loss events through the
`ExposureBase` hierarchy.
```{mermaid}
flowchart LR
subgraph BusinessState["Business Financial State"]
WM["WidgetManufacturer
(implements FinancialStateProvider)"]
CR["current_revenue"]
CA["current_assets"]
CE["current_equity"]
BR["base_revenue"]
BA["base_assets"]
BE["base_equity"]
WM --> CR
WM --> CA
WM --> CE
WM --> BR
WM --> BA
WM --> BE
end
subgraph ExposureLayer["Exposure Base Layer"]
direction TB
EB["ExposureBase (ABC)"]
RE["RevenueExposure
current_revenue / base_revenue"]
AE["AssetExposure
current_assets / base_assets"]
EE["EquityExposure
current_equity / base_equity"]
EME["EmployeeExposure
base_employees * (1+rate)^t"]
PE["ProductionExposure
base_units * growth * seasonality"]
COMP["CompositeExposure
weighted combination"]
EB --> RE
EB --> AE
EB --> EE
EB --> EME
EB --> PE
EB --> COMP
end
subgraph FrequencyScaling["Frequency Scaling"]
direction TB
FG["FrequencyGenerator"]
BF["base_frequency
(e.g., 5.0 events/yr)"]
RSE["revenue_scaling_exponent
(e.g., 0.5 = sqrt scaling)"]
SF["get_scaled_frequency(revenue)"]
PP["Poisson Process
n_events = Poisson(freq * duration)"]
ET["generate_event_times()
Uniform on [0, duration]"]
FG --> BF
FG --> RSE
BF --> SF
RSE --> SF
SF --> PP
PP --> ET
end
subgraph LossOutput["Loss Output"]
direction TB
LE["List[LossEvent]
time, amount, loss_type"]
LD["LossData
timestamps, loss_amounts"]
end
CR -->|"get_exposure(time)"| RE
CA -->|"get_exposure(time)"| AE
CE -->|"get_exposure(time)"| EE
RE -->|"get_frequency_multiplier()"| FM["frequency_multiplier
= current / base"]
AE -->|"get_frequency_multiplier()"| FM
EE -->|"get_frequency_multiplier()"| FM
FM -->|"actual_revenue"| SF
ET -->|"event times"| LE
LE --> LD
```
### Frequency Scaling Formula
The frequency scaling mechanism works as follows:
1. The `FinancialStateProvider` protocol (implemented by `WidgetManufacturer`) exposes current and base financial metrics.
2. An `ExposureBase` subclass calculates a frequency multiplier from the ratio of current-to-base metrics. For example, `RevenueExposure` computes `current_revenue / base_revenue`.
3. The `FrequencyGenerator` applies a power-law scaling: `scaled_frequency = base_frequency * (revenue / reference_revenue) ^ scaling_exponent`.
4. The scaled frequency parameterizes a Poisson process: `n_events ~ Poisson(scaled_frequency * duration)`.
5. Event times are drawn uniformly over the simulation period and sorted chronologically.
For attritional losses with `revenue_scaling_exponent = 0.5`, doubling revenue increases frequency by a factor of `sqrt(2) ~ 1.41`. This sub-linear scaling reflects that larger companies have more incidents but not proportionally more per unit of revenue.
---
## 5. Accounting Impact Detail
This diagram shows how insurance claim processing flows through the accounting
subsystem, covering ledger entries, accrual management, and claim liability tracking.
```{mermaid}
flowchart TB
subgraph ClaimInput["Claim Processing Input"]
CA["claim_amount"]
IR["insurance_recovery"]
RL["retained_loss
(company pays)"]
end
subgraph Insured["Insured Claim Path
WidgetManufacturer.process_insurance_claim()"]
direction TB
IC1["Calculate company_payment
= deductible + excess over limit"]
IC2["Calculate insurance_payment
= min(claim - deductible, limit)"]
IC3["Create ClaimLiability
original_amount = company_payment
development_strategy = long_tail_10yr"]
IC4["Post collateral
restricted_assets += company_payment"]
IC5["Record in Ledger:
DR Insurance Loss
CR Claim Liabilities"]
IC6["Record in InsuranceAccounting:
record_claim_recovery(insurance_payment)"]
IC7["Record in AccrualManager:
record_expense_accrual(INSURANCE_CLAIMS)"]
IC1 --> IC3
IC2 --> IC6
IC3 --> IC4
IC4 --> IC5
IC5 --> IC7
end
subgraph Uninsured["Uninsured Claim Path
WidgetManufacturer.process_uninsured_claim()"]
direction TB
UC1["Full amount borne by company"]
UC2{"immediate_payment?"}
UC3["Reduce cash/assets immediately"]
UC4["Create ClaimLiability
is_insured = false
No collateral required"]
UC5["Record in Ledger:
DR Insurance Loss
CR Cash (or Claim Liabilities)"]
UC1 --> UC2
UC2 -->|"Yes"| UC3
UC2 -->|"No"| UC4
UC3 --> UC5
UC4 --> UC5
end
subgraph Payment["Payment Over Time"]
direction TB
PY["pay_claim_liabilities()
called each period"]
CS["ClaimLiability.get_payment(dev_year)"]
MP["ClaimLiability.make_payment(amount)"]
RC["Release collateral
restricted_assets -= payment"]
LE["Ledger Entry:
DR Claim Liabilities
CR Cash"]
PY --> CS
CS --> MP
MP --> RC
RC --> LE
end
subgraph Recovery["Insurance Recovery"]
direction TB
IAR["InsuranceAccounting
.receive_recovery_payment()"]
LE2["Ledger Entry:
DR Cash
CR Insurance Receivables"]
IAR --> LE2
end
CA --> Insured
CA --> Uninsured
Insured -->|"ClaimLiability
created"| Payment
Insured -->|"InsuranceRecovery
receivable"| Recovery
Uninsured -->|"ClaimLiability
created"| Payment
```
### Key Accounting Entries
| Event | Debit | Credit | Module |
|-------|-------|--------|--------|
| Insured claim recognized | Insurance Loss (Expense) | Claim Liabilities (Liability) | `Ledger` |
| Collateral posted | Restricted Cash (Asset) | Cash (Asset) | `Ledger` |
| Claim payment made | Claim Liabilities (Liability) | Cash (Asset) | `Ledger` |
| Collateral released | Cash (Asset) | Restricted Cash (Asset) | `Ledger` |
| Recovery receivable recorded | Insurance Receivables (Asset) | Insurance Recovery (Revenue) | `InsuranceAccounting` |
| Recovery payment received | Cash (Asset) | Insurance Receivables (Asset) | `InsuranceAccounting` |
| Premium paid | Prepaid Insurance (Asset) | Cash (Asset) | `InsuranceAccounting` |
| Premium amortized monthly | Insurance Expense (Expense) | Prepaid Insurance (Asset) | `InsuranceAccounting` |
---
## 6. Class Relationship Summary
The following diagram summarizes the key classes and their relationships across
all stages of the claim lifecycle.
```{mermaid}
flowchart TB
subgraph LossGen["Loss Generation Module
loss_distributions.py"]
LossDistribution["LossDistribution (ABC)"]
LognormalLoss["LognormalLoss"]
ParetoLoss["ParetoLoss"]
GPDLoss["GeneralizedParetoLoss"]
FreqGen["FrequencyGenerator"]
AttritionalGen["AttritionalLossGenerator"]
LargeGen["LargeLossGenerator"]
CatGen["CatastrophicLossGenerator"]
MfgGen["ManufacturingLossGenerator"]
LossEvent2["LossEvent"]
LossData2["LossData"]
LossDistribution --> LognormalLoss
LossDistribution --> ParetoLoss
LossDistribution --> GPDLoss
MfgGen -->|"composes"| AttritionalGen
MfgGen -->|"composes"| LargeGen
MfgGen -->|"composes"| CatGen
AttritionalGen -->|"uses"| FreqGen
AttritionalGen -->|"uses"| LognormalLoss
LargeGen -->|"uses"| FreqGen
LargeGen -->|"uses"| LognormalLoss
CatGen -->|"uses"| FreqGen
CatGen -->|"uses"| ParetoLoss
AttritionalGen -->|"produces"| LossEvent2
LossEvent2 -->|"collected in"| LossData2
end
subgraph Exposure["Exposure Module
exposure_base.py"]
FSP["FinancialStateProvider
(Protocol)"]
ExposureBase2["ExposureBase (ABC)"]
RevExp["RevenueExposure"]
AssetExp["AssetExposure"]
EqExp["EquityExposure"]
ExposureBase2 --> RevExp
ExposureBase2 --> AssetExp
ExposureBase2 --> EqExp
FSP -.->|"queried by"| RevExp
FSP -.->|"queried by"| AssetExp
FSP -.->|"queried by"| EqExp
end
subgraph ClaimDev["Claim Development Module
claim_development.py"]
ClaimDevelopment2["ClaimDevelopment"]
Claim2["Claim"]
ClaimCohort2["ClaimCohort"]
CashFlowProjector2["CashFlowProjector"]
ClaimDevelopment2 -->|"defines pattern for"| Claim2
Claim2 -->|"grouped into"| ClaimCohort2
ClaimCohort2 -->|"analyzed by"| CashFlowProjector2
end
subgraph InsProg["Insurance Program Module
insurance_program.py"]
InsuranceProgram2["InsuranceProgram"]
EnhancedLayer["EnhancedInsuranceLayer"]
LayerState2["LayerState"]
InsuranceProgram2 -->|"manages"| EnhancedLayer
EnhancedLayer -->|"tracked by"| LayerState2
end
subgraph Acctg["Accounting Modules"]
Manufacturer["WidgetManufacturer
manufacturer.py"]
ClaimLiab["ClaimLiability
manufacturer.py"]
Ledger2["Ledger
ledger.py"]
InsAcct["InsuranceAccounting
insurance_accounting.py"]
AccrualMgr["AccrualManager
accrual_manager.py"]
Manufacturer -->|"creates"| ClaimLiab
Manufacturer -->|"records in"| Ledger2
Manufacturer -->|"tracks via"| InsAcct
Manufacturer -->|"schedules via"| AccrualMgr
end
MfgGen -.->|"uses for scaling"| ExposureBase2
LossEvent2 -.->|"becomes"| Claim2
LossData2 -.->|"processed by"| InsuranceProgram2
InsuranceProgram2 -.->|"results feed"| Manufacturer
Manufacturer -.->|"implements"| FSP
ClaimDevelopment2 -.->|"used by"| ClaimLiab
```
---
## 7. Development Pattern Reference
The framework provides four standard development patterns, each suited to different
claim types common in manufacturing operations.
| Pattern | Years | Use Case | Factor Distribution |
|---------|-------|----------|---------------------|
| `IMMEDIATE` | 1 | Property/equipment damage | `[1.0]` |
| `MEDIUM_TAIL_5YR` | 5 | Workers compensation | `[0.40, 0.25, 0.15, 0.10, 0.10]` |
| `LONG_TAIL_10YR` | 10 | General liability | `[0.10, 0.20, 0.20, 0.15, 0.10, 0.08, 0.07, 0.05, 0.03, 0.02]` |
| `VERY_LONG_TAIL_15YR` | 15 | Product liability | `[0.05, 0.10, 0.15, 0.15, 0.12, 0.10, 0.08, 0.06, 0.05, 0.04, 0.03, 0.03, 0.02, 0.01, 0.01]` |
All patterns' factors sum to 1.0. The `ClaimDevelopment` class validates this invariant
at construction time (within a tolerance of 0.01).
---
## Key Source Files
| File | Purpose |
|------|---------|
| `loss_distributions.py` | Loss generation: `LossDistribution`, `FrequencyGenerator`, `ManufacturingLossGenerator`, `LossEvent`, `LossData` |
| `exposure_base.py` | Exposure scaling: `FinancialStateProvider`, `ExposureBase`, `RevenueExposure`, `AssetExposure`, `EquityExposure` |
| `claim_development.py` | Claim development: `ClaimDevelopment`, `Claim`, `ClaimCohort`, `CashFlowProjector` |
| `insurance_program.py` | Insurance processing: `InsuranceProgram`, `EnhancedInsuranceLayer`, `LayerState` |
| `manufacturer.py` | Business model and accounting: `WidgetManufacturer`, `ClaimLiability` |
| `ledger.py` | Double-entry accounting: `Ledger`, `AccountName`, `AccountType` |
| `insurance_accounting.py` | Insurance-specific accounting: `InsuranceAccounting`, `InsuranceRecovery` |
| `accrual_manager.py` | Payment timing: `AccrualManager`, `AccrualItem`, `PaymentSchedule` |