Measured latency by graph size.
Incidents pool into events as evidence, so volume never bloats the graph — size is set by the tree's events and gates. Here is the analysis pipeline measured at each size.
How the data flows
Pool Incident volume aggregates into event probabilities by (component × failure mode) — count never grows the graph
Propagate The mechanism is a free graph, loops included. loops and over-deep branches are cut and reported with reasons
Compute A tree compiles per hazard and quantifies deterministically
The numbers
| events | gates | compute+persist | what-if median | what-if / s* |
|---|---|---|---|---|
| 50 | 8 | 36 ms | 3.7 ms | ≈ 270 |
| 200 | 30 | 138 ms | 24.0 ms | ≈ 41 |
| 500 | 72 | 445 ms | 120.2 ms | ≈ 8.3 |
| 1000 | 144 | 1.35 s | 701 ms | ≈ 1.4 |
* Derived from median latency, single stream. Measured 2026-08-08 on an Apple M-series dev machine, release build, Docker, balanced OR trees (fanout 8). "Compute+persist" is one cold pass of the full pipeline — evidence quantification, cut-set enumeration, importance, fixed-seed MC, revision stamping. What-if is the median of 20 calls. A debug build is ~30× slower on this path.
Boundaries, reported as numbers
Realistic FTA/RCA sizes — tens to a few hundred events — recompute in milliseconds, so re-analysis on every edit and interactive what-if sweeps are free. Growth is super-linear (~n1.7 in this shape), which puts a full counterfactual at ~0.7 s around 1,000 events. Every boundary is reported inside the response:
- Cut sets keep the top 10,000 per gate in probability order, with the discarded probability mass included in the response.
- Propagation depth cuts at 24; every cut branch is listed in
cut_brancheswith its reason. - k-of-n expansion stops at 64 combinations — beyond that, the tree is no longer one a person can review.
Reproduce it
docker compose -f deploy/docker-compose.yml -p bench up -d
CAUSAL_DB_URL=http://localhost:9876 python3 tools/benchmark-analyze.py