Behavioral simulation with append-only ledger capture — simulate first, compile later, never break a verified behavior.
Inspired by Simulation-Driven Synthesis [2602.14690v4].
When refining a specification or implementation, how do you know you haven't broken anything? Tests are post-hoc — they check after the change. But what if you could capture the entire behavioral history of your system as an immutable ledger, then replay it against any proposed change?
Simulation Ledger provides:
- Append-only behavioral ledger that captures every input/output pair
- State mutations tracked atomically with each transaction
- Ledger diffs that detect output drift and state divergence
- Simulation runtime for step-by-step capture
- Regression checking against historical baselines
- Truncation with state rebuild for undoing mistakes
┌───────────────┐ ┌───────────────┐
│ Simulation │────→│ Ledger │
│ Runtime │ │ (append-only)│
└───────────────┘ └───────┬───────┘
│
┌───────────▼───────────┐
│ Ledger Diff │
│ • output drifts │
│ • state drifts │
│ • matching count │
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Regression Check │
│ historical vs proposed│
└───────────────────────┘
[dependencies]
simulation-ledger = { version = "0.1", features = ["serde"] }An irreducible unit of behavioral capture:
use simulation_ledger::SimTransaction;
use std::collections::HashMap;
let tx = SimTransaction::new("GET /api/test", r#"{"status": 200}"#);
let tx_with_state = SimTransaction::with_mutations(
"GET /api", "ok",
HashMap::from([("counter".into(), "42".into())])
);Append-only behavioral ledger with state tracking:
use simulation_ledger::*;
let mut ledger = Ledger::new();
ledger.append(SimTransaction::new("event1", "output1"));
ledger.append(SimTransaction::new("event2", "output2"));
assert_eq!(ledger.len(), 2);
let state = ledger.state(); // current accumulated stateCompare two ledgers for behavioral drift:
use simulation_ledger::*;
let diff = LedgerDiff::compute(&original, &proposed);
if diff.is_clean() {
println!("No drift detected");
} else {
for drift in &diff.output_drifts {
println!("Step {}: expected '{}', got '{}'",
drift.index, drift.expected, drift.actual);
}
}Step-by-step simulation capture:
use simulation_ledger::*;
let mut rt = SimulationRuntime::new("my_spec");
rt.simulate_step("GET /api/v1/test", r#"{"ok": true}"#);
rt.simulate_step("POST /api/v1/action", r#"{"done": true}"#);
assert_eq!(rt.step_count(), 2);Validate a proposed spec against historical behavior:
use simulation_ledger::*;
let report = run_regression(&historical, &proposed);
if report.passed() {
println!("All {} assertions passed", report.passed);
} else {
for failure in &report.failures {
println!("FAIL at step {}: expected '{}', got '{}'",
failure.index, failure.expected_output, failure.actual_output);
}
}use simulation_ledger::*;
let mut ledger = Ledger::new();
let mut m = HashMap::new();
m.insert("users".into(), "1".into());
ledger.append(SimTransaction::with_mutations("create_user", "ok", m));
let state = ledger.replay();
assert_eq!(state.get("users"), Some(&"1".into()));use simulation_ledger::*;
let mut original = Ledger::new();
original.append(SimTransaction::new("e1", "expected_output"));
let mut proposed = Ledger::new();
proposed.append(SimTransaction::new("e1", "drifted_output"));
let diff = LedgerDiff::compute(&original, &proposed);
assert!(!diff.is_clean());use simulation_ledger::*;
let mut ledger = Ledger::new();
// Add 5 transactions...
for i in 0..5 {
ledger.append(SimTransaction::new(&format!("e{}", i), &format!("o{}", i)));
}
// Undo last 2 transactions
let invalidated = ledger.truncate_at(2);
assert_eq!(invalidated, 2);
assert_eq!(ledger.len(), 3);
// State is automatically rebuilt from remaining entries| Operation | Complexity |
|---|---|
| Append | O(M) where M = mutations |
| Replay | O(N × M) |
| Diff | O(min(N₁, N₂)) |
| Truncate | O(N) + state rebuild |
| Regression | O(N) |
Licensed under the MIT License.
- Fork the repository
- Create a feature branch
- Write tests
- Push and open a Pull Request