Quick Start
This page runs one small scenario three ways: from the shell, from Python and from Go. An agent is booking a trip and has three plans in mind. A fare search and a disruption feed come back, a seat hold runs out, and one plan is left. Every output below is what the command really prints, with its clock fixed so the times match.
Get the multivergence command from the download page first, or install it with Go 1.24 or later:
go install github.com/multivergence/multivergence/cmd/multivergence@latest
From the Shell
The store is one file. Point the command at it once:
export MULTIVERGENCE_FILE=trip.mv
Put the shared inputs on the trunk, the state that is real now, then fork three plans. The weights are the agent’s starting guess.
$ multivergence set budget 400
$ multivergence fork plan-train -weight 0.5
$ multivergence fork plan-flight -weight 0.3
$ multivergence fork plan-bus -weight 0.2
Each plan writes to its own branch. The flight plan assumes a fare of 250 and can’t go over budget. The bus plan holds a seat for fifteen minutes.
$ multivergence set -b plan-flight fare/flight 250
$ multivergence constrain plan-flight 'fare/flight <= 400'
$ multivergence expire plan-bus -in 15m
$ multivergence branches
BRANCH PROB STATUS FROM DEADLINE CONSTRAINTS
plan-train 50.0% live trunk - -
plan-flight 30.0% live trunk - fare/flight <= 400
plan-bus 20.0% live trunk 2026-11-02T08:15:00Z -
The fare search comes back. Its answer is a fact, so it goes to every live plan, and the flight’s constraint breaks:
$ multivergence observe -fact fare/flight=460
killed plan-flight: violated (fare/flight <= 400)
BRANCH PROB STATUS FROM DEADLINE CONSTRAINTS
plan-train 71.4% live trunk - -
plan-bus 28.6% live trunk 2026-11-02T08:15:00Z -
Then a disruption feed: coaches are running late, trains are on time. The agent decides how likely that news is if each plan gets there by nine, and the engine applies it:
$ multivergence observe -l plan-train=0.9 -l plan-bus=0.3
BRANCH PROB STATUS FROM DEADLINE CONSTRAINTS
plan-train 88.2% live trunk - -
plan-bus 11.8% live trunk 2026-11-02T08:15:00Z -
That’s Bayes’ rule: 0.714 × 0.9 against 0.286 × 0.3, scaled to add up to one. Fifteen minutes later the seat hold has gone, and reads show it at once:
$ multivergence branches
BRANCH PROB STATUS FROM DEADLINE CONSTRAINTS
plan-train 100.0% live trunk - -
plan-flight - violated (fare/flight <= 400) trunk - fare/flight <= 400
plan-bus - expired trunk 2026-11-02T08:15:00Z -
Promote the survivor. Its state becomes the trunk in one step, and every branch goes:
$ multivergence promote plan-train
promoted plan-train: generation 1
$ multivergence history
2026-11-02T08:00:00Z gen 0 fork plan-train from trunk, weight 0.5
2026-11-02T08:00:00Z gen 0 fork plan-flight from trunk, weight 0.3
2026-11-02T08:00:00Z gen 0 fork plan-bus from trunk, weight 0.2
2026-11-02T08:00:00Z gen 0 constrain plan-flight fare/flight <= 400
2026-11-02T08:00:00Z gen 0 expire plan-bus at 2026-11-02T08:15:00Z
2026-11-02T08:02:00Z gen 0 killed plan-flight violated: fare/flight <= 400
2026-11-02T08:02:00Z gen 0 observe - 1 fact, 0 likelihoods, 2 live after
2026-11-02T08:05:00Z gen 0 observe - 0 facts, 2 likelihoods, 2 live after
2026-11-02T08:20:00Z gen 0 killed plan-bus expired
2026-11-02T08:20:00Z gen 1 promote plan-train generation 1
Two rules worth knowing early. A fork from the trunk takes the trunk as it is, so write shared inputs before forking; a branch forked before a direct trunk write is stale, and promote refuses it. And when a write breaks a branch’s constraint, the write is kept, the branch dies, and the command exits with 4.
From Python
Add -json and every command prints one JSON value, so any language can drive it:
import json
import subprocess
def mv(*args):
p = subprocess.run(["multivergence", "-json", *args], capture_output=True, text=True)
return json.loads(p.stdout) if p.stdout.strip() else None
mv("fork", "plan-a", "-weight", "0.6")
mv("fork", "plan-b", "-weight", "0.4")
out = mv("observe", "-l", "plan-a=0.2", "-l", "plan-b=0.9")
for b in out["live"]:
print(b["name"], round(b["probability"], 3)) # plan-a 0.25, plan-b 0.75
if out["settled"]:
mv("promote", out["settled"])
Errors come back as {"error": "...", "kind": "..."}, with kinds like not_found, stale and missing_likelihood. The repository’s scripts/cross_language.py runs the whole trip this way and checks every probability against its own arithmetic.
From Go
import "github.com/multivergence/multivergence"
s, err := multivergence.Open("trip.mv", nil)
if err != nil {
return err
}
defer s.Close()
s.Fork("plan-train", multivergence.Trunk, 0.5)
s.Fork("plan-flight", multivergence.Trunk, 0.3)
s.Set("plan-flight", "fare/flight", []byte("250"))
s.Constrain("plan-flight", multivergence.AtMost("fare/flight", 400))
out, err := s.Observe(multivergence.Evidence{
Facts: []multivergence.Fact{{Key: "fare/flight", Value: []byte("460")}},
Likelihoods: map[string]float64{"plan-train": 0.9},
Rest: multivergence.Likelihood(1),
})
if name, ok := out.Settled(); ok {
s.Promote(name)
}
multivergence.New(nil) gives a store in memory instead of a file. The reference lists every call and command.
The Demo
The download includes multivergence-demo, which runs the trip above with a simulated clock and checks every number it prints. From a clone of the repository, go run ./examples/agent does the same, and go run ./examples/speculate shows the other common use: preparing the likely next step in a branch before it’s asked for.