Skip to main content

Temporal Branching with Editions

"What if we had approved that claim?" Run the simulation without touching production.


The Concept

An edition is a branch of history. The main timeline (empty edition name) continues undisturbed while you explore an alternate path—a simulation, a what-if analysis, a training scenario.

When you're done, delete the branch. Editions are for exploration, not migration—there is no merge operation. Events build upon each other; merging branch events into the main timeline would corrupt that timeline by inserting events that reference different prior state. The event stream is immutable by design.

If an edition reveals the "correct" outcome (e.g., after dispute resolution), inject facts into the main timeline that adjust state accordingly. The original events remain (providing audit trail), and the corrective facts record the external determination. See Commands vs Facts for fact injection patterns.


Why Branch History?

A/B Testing Business Rules

Before deploying a new fee structure, run last month's transactions through both the old and new rules. Compare outcomes without risking production.

Regulatory What-If

"What would our exposure have been if we had applied the new risk model six months ago?" Replay history through the alternate model, with full audit trail.

ML Training Data

Generate thousands of counterfactual game outcomes for reinforcement learning. Each edition branch produces training data with the same structure as production events.

Speculative Execution

Preview the effects of a command before committing. "Show me what the order would look like after applying this discount"—without persisting until confirmed.


How It Works

Every event lives in an edition. The main timeline uses an empty edition name:

// Edition identifier with optional explicit divergence points.
//
// Two modes:
// - Implicit (divergences empty): Divergence derived from first edition event's sequence
// - Explicit (divergences populated): Per-domain divergence points for historical branching,
// saga coordination, or speculative execution
message Edition {
string name = 1; // Edition name, e.g., "v2"; empty = main timeline
repeated DomainDivergence divergences = 2; // Optional: explicit per-domain divergence points
}

// Explicit divergence point for a specific domain.
// Used when creating historical branches or coordinating saga writes across domains.
message DomainDivergence {
string domain = 1; // Domain name
uint32 sequence = 2; // Divergence sequence number
}

To branch, specify an edition name:

illustrative - speculative edition
# Simulate what would happen with different pricing
edition = Edition(name="pricing-experiment-2024-03")

result = speculate(
command=ApplyDiscount(order_id=order_id, percent=20),
edition=edition,
)

# result.events shows what would happen
# Main timeline unchanged

Divergence Points

By default, an edition diverges implicitly from its first event. For precise control, specify explicit divergence points:

illustrative - explicit divergence points
edition = Edition(
name="backtest-q4",
divergences=[
DomainDivergence(domain="orders", sequence=1000),
DomainDivergence(domain="inventory", sequence=500),
],
)

This says: "Branch from sequence 1000 in orders, sequence 500 in inventory." Events before those points are shared with the main timeline.


Cleanup

Edition events accumulate. When you're done with a simulation, delete them:

illustrative - edition cleanup
delete_edition_events(
domain="orders",
edition="pricing-experiment-2024-03",
)

The main timeline is unaffected. Only the branch disappears.


Example: Backtesting a Trading Strategy

illustrative - backtesting
# Create edition for backtest
edition = Edition(name=f"backtest-{strategy_id}-{date}")

# Replay historical market data through new strategy
for market_event in historical_feed(start_date, end_date):
result = speculate(
command=EvaluateTrade(
signal=market_event,
strategy=new_strategy,
),
edition=edition,
)
collect_metrics(result)

# Analyze results
performance = calculate_sharpe_ratio(edition)
print(f"Strategy {strategy_id}: Sharpe = {performance}")

# Cleanup
delete_edition_events(domain="trading", edition=edition.name)

Example: Training a Poker Bot

illustrative - training data generation
# Generate training data from alternate game outcomes
for hand_id in sample_hands(n=10000):
for alternate_action in possible_actions(hand_id):
edition = Edition(name=f"train-{hand_id}-{alternate_action}")

result = speculate(
command=PlayerAction(
hand_id=hand_id,
action=alternate_action,
),
edition=edition,
)

# Extract features and outcomes for PyTorch
training_sample = extract_features(result)
dataset.append(training_sample)

# Cleanup this branch
delete_edition_events(domain="hand", edition=edition.name)

Main Timeline Safety

Editions are isolated. Operations on an edition:

  • Cannot affect the main timeline
  • Cannot affect other editions
  • Are visible only to queries that specify that edition

Production continues normally while you experiment.


Regulated Industry Considerations

While edition isolation guarantees prevent experimental data from affecting the main timeline, running experiments on production infrastructure may still raise concerns in regulated environments—auditors may require complete separation of test and production systems regardless of software guarantees.

For these situations, duplicate the event storage and run editions in an isolated environment:

illustrative - isolated experimentation
Production Environment          Isolated Environment
┌─────────────────────┐ ┌─────────────────────┐
│ Event Store (main) │ ──copy──▶ Event Store (copy) │
│ - orders │ │ - orders │
│ - inventory │ │ - inventory │
└─────────────────────┘ └─────────────────────┘


┌─────────────────────┐
│ Run editions here │
│ - backtests │
│ - what-if analysis │
│ - ML training │
└─────────────────────┘

This provides the same analytical capabilities while satisfying regulatory requirements for environment separation.

Future: PII Stripping Hooks

A planned feature will add advice hooks for stripping personally identifiable information during event export, similar to the upcasting pattern. This will enable copying production events to isolated environments with automatic anonymization.

See Roadmap for details.


See Also