View as Markdown, for AI agents Edit on GitHub

Causality analysis

When a seed fails, you know it failed but not always which event decided it. seedfall cause replays the run from many points and shows when the failure became likely and which fault tipped it over. Use it after shrink when the short fault list still leaves you asking which fault matters.

seedfall shrink tells you which faults are needed for a failure. seedfall cause tells you when the failure became likely, and which event made it so.

seedfall cause -c seedfall.always.json -seed 1
seed 1 fails at t=2.891820635s: client#1: acknowledged write lost: k96=v96, server now answers "NONE"

chance of failing when everything after a moment is re-randomized (20 futures each):

         0s     5%  ██
  292.108ms     5%  ██                              <- after 292.108ms  partition [client] from the rest until 627.143ms
  722.955ms     0%
  757.268ms     0%
  757.268ms    75%  ███████████████████████         <- after 757.268ms  crash server during disk op #56, restart at 1.121388s
  1.084433s    75%  ███████████████████████
    1.7052s    65%  ████████████████████
    1.7052s   100%  ██████████████████████████████  <- after 1.7052s  crash server, restart at 1.942589s
  2.711082s   100%  ██████████████████████████████

biggest jump: 0% -> 75% at 757.268ms  <- after 757.268ms  crash server during disk op #56, restart at 1.121388s

440 runs in 24.296s

(Abridged: the full output has one row per moment.)

Read it from top to bottom. Before anything happens, a random future of this world fails 5% of the time. The partition changes nothing, so it is a bystander. The crash in the middle of disk operation #56 takes the odds from 0% to 75%: that is when the torn record lands on disk. The second crash makes the failure certain. Even before reading any code, you know which event to look at.

How it works

For each moment T, seedfall replays the seed exactly up to T. At the first scheduler step at or after T, it re-keys every random stream: scheduling, network latency, disk tearing, I/O faults, what random_get hands each node (so crypto/rand and sim.Random, but not Go's runtime-seeded math/rand), and the remaining fault schedule. Then it lets the run continue into a new future. Running many such branches from the same moment estimates the probability of failing from that state.

This needs no snapshots. Because replay is exact, "the state at T" is reproduced by running the seed to T. That is the same property Antithesis's multiverse debugging relies on, with deterministic replay standing in for VM snapshots.

Moments are chosen evenly across the run (-points, default 16), plus just before and just after every fault. Each moment gets -branches futures (default 20), so the default costs a few hundred runs, all in parallel.

Branches are also the basis of feedback-guided exploration, which keeps branching from runs that reached something new.

From an agent

The MCP server exposes the same analysis as seedfall_cause, returning the curve as JSON with the faults between consecutive moments. See agents.md.