Skip to content

Commit c04daa4

Browse files
authored
Merge pull request #260 from flyingrobots/graph-boaw
SPEC-0004: Worldlines, Playback, TruthBus + BOAW Phase 6B parallel execution
2 parents ea9e836 + c4d525d commit c04daa4

39 files changed

Lines changed: 11216 additions & 513 deletions

CHANGELOG.md

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,88 @@
55

66
## Unreleased
77

8+
### Added - SPEC-0004: Worldlines & Playback
9+
10+
- **`worldline.rs`**: Worldline types for history tracking
11+
- `WorldlineId(Hash)`: Opaque worldline identifier (derived from initial state hash in production; tests use fixed bytes)
12+
- `HashTriplet`: state_root + patch_digest + commit_hash per tick
13+
- `WorldlineTickPatchV1`: Per-warp projection of global tick operations
14+
- `apply_warp_op_to_store()`: Apply WarpOp to GraphStore with explicit variant coverage
15+
16+
- **`playback.rs`**: Playback cursor and session types
17+
- `PlaybackCursor`: Materialized viewpoint into worldline history
18+
- `PlaybackMode`: Paused, Play, StepForward, StepBack, Seek state machine
19+
- `ViewSession`: Client subscription binding with channel filtering
20+
- `TruthSink`: Minimal BTreeMap-based frame collector
21+
- `CursorReceipt`, `TruthFrame`: Cursor-addressed authoritative values
22+
23+
- **`provenance_store.rs`**: Provenance store trait (hexagonal port)
24+
- `ProvenanceStore` trait: Seam for history access (patches, expected hashes, outputs)
25+
- `LocalProvenanceStore`: In-memory Vec-backed implementation
26+
- `add_checkpoint()`: Record checkpoint for fast seek during replay
27+
- `fork()`: Prefix-copy worldline up to fork_tick
28+
29+
- **`retention.rs`**: Retention policy for worldline history
30+
- `RetentionPolicy` enum: KeepAll, CheckpointEvery, KeepRecent, ArchiveToWormhole
31+
32+
- **`materialization/frame_v2.rs`**: MBUS v2 wire format with cursor stamps
33+
- `V2Packet`: Cursor-stamped truth frame packets
34+
- `encode_v2_packet()`, `decode_v2_packet()`: Roundtrip encoding
35+
- Inline unit tests T19-T22 (SPEC-0004 test IDs): `mbus_v2_roundtrip_single_packet`, `mbus_v1_rejects_v2`, `mbus_v2_rejects_v1`, `mbus_v2_multi_packet_roundtrip`
36+
37+
#### Tests - SPEC-0004
38+
39+
- **All SPEC-0004 tests passing** (see test files for complete list; SPEC-0004 test IDs, not Rust function names)
40+
- **`crates/warp-core/tests/reducer_emission_tests.rs`**: Reducer integration tests (T11-T13)
41+
- **`crates/warp-core/tests/view_session_tests.rs`**: Worker count invariance tests (T16)
42+
- **Hexagonal testing**: Playback contract tested using ProvenanceStore fakes (T1, T7)
43+
- **Total warp-core tests**: all passing (run `cargo test -p warp-core -- --list 2>/dev/null | tail -1` for current count)
44+
45+
### Added - Cross-Warp Parallelism (Phase 6B+)
46+
47+
- **`WorkUnit` struct** (`boaw/exec.rs`): Work unit carrying `warp_id` + items for one shard
48+
- **`build_work_units()`** (`boaw/exec.rs`): Partitions items by warp then by shard into work units
49+
- **`execute_work_queue()`** (`boaw/exec.rs`): Global work queue with atomic unit claiming
50+
- Single spawn site (no nested threading)
51+
- Workers claim `(warp, shard)` units via `AtomicUsize`
52+
- Views resolved per-unit, dropped before claiming next unit
53+
- Fixed worker pool sized to `available_parallelism()`
54+
55+
### Changed - Cross-Warp Parallelism
56+
57+
- **Engine execution** (`engine_impl.rs`): Replaced serial per-warp for-loop with global work queue
58+
- Previous: `for (warp_id, rewrites) in by_warp { execute_parallel_sharded(...) }`
59+
- Now: `execute_work_queue(&units, workers, |warp_id| state.store(warp_id))`
60+
- Multi-warp ticks now parallelize across all `(warp, shard)` units simultaneously
61+
862
### Changed - API
963

1064
- **`WarpOpKey` now public** (`tick_patch.rs`): Export `WarpOpKey` from `warp_core` public API
1165
- **`WarpOp::sort_key()` now public**: Changed from `pub(crate)` to `pub` to enable external determinism verification
66+
- **`compute_commit_hash_v2` now public** (`snapshot.rs`): Promoted from `pub(crate)` to `pub` and re-exported from `warp_core`; enables external Merkle chain verification
67+
68+
### Removed - Tier 0 Cleanup
69+
70+
- **Stride fallback** (`boaw/exec.rs`): Deleted `execute_parallel_stride()` and `parallel-stride-fallback` feature
71+
- Phase 6A stride execution superseded by Phase 6B sharded execution
72+
- Removed feature gate, env var check, and ASCII warning banner
73+
- **Deprecated `emit_view_op_delta()`** (`rules.rs`): Deleted non-deterministic function that used `delta.len()` sequencing
74+
75+
### Fixed - Review Feedback
76+
77+
- **P0: Off-by-one in `publish_truth`** (`playback.rs`): Query `prov_tick = cursor.tick - 1` (0-based index of last applied patch) instead of `cursor.tick`; added early-return guard for `cursor.tick == 0`
78+
- **P0: Wrong package in bench docs** (`docs/notes/boaw-perf-baseline.md`): Corrected `warp-core``warp-benches`
79+
- **P1: Merkle chain verification** (`playback.rs`): `seek_to` now verifies `patch_digest`, recomputes `commit_hash` via `compute_commit_hash_v2`, and tracks parent chain per tick; added `SeekError::PatchDigestMismatch` and `SeekError::CommitHashMismatch` variants
80+
- **P1: Dead variant removal** (`playback.rs`): Removed `SeekThen::RestorePrevious` (broken semantics; treated identically to `Pause`)
81+
- **P1: OOM prevention** (`materialization/frame_v2.rs`): Bound `entry_count` by remaining payload size in `decode_v2_packet` to prevent malicious allocation
82+
- **P1: Fork guard** (`provenance_store.rs`): Added `WorldlineAlreadyExists` error variant; `fork()` rejects duplicate worldline IDs
83+
- **P1: Dangling edge validation** (`worldline.rs`): `UpsertEdge` now verifies `from`/`to` nodes exist in store before applying
84+
- **P1: Silent skip → Result** (`boaw/exec.rs`): `execute_work_queue` returns `Result<Vec<TickDelta>, WarpId>` instead of panicking on missing store; caller maps to `EngineError::InternalCorruption`
85+
- **P2: Tilde-pin bytes dep** (`crates/warp-benches/Cargo.toml`): `bytes = "~1.11"` for minor-version stability
86+
- **P2: Markdownlint MD060** (`.markdownlint.json`): Removed global MD060 disable (all tables are well-formed; no false positives to suppress)
87+
- **P2: Test hardening** (`tests/`): Real `compute_commit_hash_v2` in all test worldline setups, u8 truncation guards (`num_ticks <= 127`), updated playback tests to match corrected `publish_truth` indexing
88+
- **Trivial: Phase 6B benchmark** (`boaw_baseline.rs`): Added `bench_work_queue` exercising full `build_work_units → execute_work_queue` pipeline across multi-warp setups
89+
- **Trivial: Perf baseline stats** (`docs/notes/boaw-perf-baseline.md`): Expanded statistical context note with sample size, CI methodology, and Criterion report location
1290

1391
### Fixed - PR #257 Review
1492

@@ -49,14 +127,7 @@
49127
- Items in same shard processed together for cache locality
50128
- Worker count capped at `min(workers, NUM_SHARDS)` to prevent over-threading
51129

52-
- **Stride fallback** (`boaw/exec.rs`): Feature-gated Phase 6A fallback
53-
- Requires `parallel-stride-fallback` feature + `ECHO_PARALLEL_STRIDE=1` env var
54-
- Prints loud ASCII warning banner when activated
55-
- Temporary A/B benchmarking path; will be removed in a future release
56-
57-
- **5 new Phase 6B tests** (`tests/boaw_parallel_exec.rs`):
58-
- `sharded_equals_stride`: Key correctness proof for 6A → 6B transition
59-
- `sharded_equals_stride_permuted`: Permutation invariance with sharded execution
130+
- **3 new Phase 6B tests** (`tests/boaw_parallel_exec.rs`):
60131
- `worker_count_capped_at_num_shards`: Verifies cap at 256 workers
61132
- `sharded_distribution_is_deterministic`: Shard routing stability
62133
- `default_parallel_uses_sharded`: Default path verification

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ Hashes match. Determinism verified.
5353
5454
## Why?
5555

56-
| Problem | Echo's Answer |
57-
| -------------------------------------- | ------------------------------------------------- |
58-
| "Replay diverged after 10,000 ticks" | Deterministic scheduler + fixed-point math |
59-
| "Which client has the correct state?" | Compare 32-byte tick hashes |
60-
| "We can't reproduce that bug" | Every tick is content-addressed and replayable |
61-
| "Syncing state is expensive" | Stream diffs, verify hashes, done |
56+
| Problem | Echo's Answer |
57+
| ------------------------------------- | ---------------------------------------------- |
58+
| "Replay diverged after 10,000 ticks" | Deterministic scheduler + fixed-point math |
59+
| "Which client has the correct state?" | Compare 32-byte tick hashes |
60+
| "We can't reproduce that bug" | Every tick is content-addressed and replayable |
61+
| "Syncing state is expensive" | Stream diffs, verify hashes, done |
6262

6363
If you've ever built a game, simulation, or distributed system and wished state had Git-like properties—branches, merges, provable history—that's what we're building.
6464

@@ -87,12 +87,13 @@ Echo is a high-performance graph rewriting engine written in Rust, designed to r
8787

8888
#### Time Travel Debugger + WARPSITE
8989

90-
True, deterministic **Time Travel Debugging** (TTD) is always available by default—not something you have to record or prepare for in advance. Made possible by [WARP graph](https://doi.org/10.5281/zenodo.17908005) [rewriting](https://doi.org/10.5281/zenodo.17963669), each tick's [computational hologram](https://doi.org/10.5281/zenodo.17963669) is captured in an immutable, append-only, tamper-evident ledger. Step backwards to any previous tick, then forward again. Exactly the same every time, bit-for-bit, cryptographically verified. Want to see what *could* have happened? Fork to a different worldline, try something different, then discard it and return to your original timeline.
90+
True, deterministic **Time Travel Debugging** (TTD) is always available by default—not something you have to record or prepare for in advance. Made possible by [WARP graph](https://doi.org/10.5281/zenodo.17908005) [rewriting](https://doi.org/10.5281/zenodo.17963669), each tick's [computational hologram](https://doi.org/10.5281/zenodo.17963669) is captured in an immutable, append-only, tamper-evident ledger. Step backwards to any previous tick, then forward again. Exactly the same every time, bit-for-bit, cryptographically verified. Want to see what _could_ have happened? Fork to a different worldline, try something different, then discard it and return to your original timeline.
9191

92+
-**Worldlines & Playback** (SPEC-0004)—Foundation for TTD: PlaybackCursor, ViewSession, ProvenanceStore
9293
- Time Travel Debugging (TTD) Part 1—Tick Inspector + Rewind/Jump to Previous Tick (In Progress)
9394
- WARPSITE (In Progress)
9495
- [Wesley](https://github.com/flyingrobots/wesley)—GraphQL-as-schema → Rust/TypeScript Compiler
95-
- TTD Part 2—Fork Worldlines
96+
- **Fork Worldlines** (SPEC-0004)—`LocalProvenanceStore::fork()` prefix-copy implemented
9697

9798
#### Then: Splash Guy Tutorial Demo
9899

crates/echo-dind-tests/src/rules.rs

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -590,63 +590,6 @@ fn emit_view_op_delta_scoped(
590590
});
591591
}
592592

593-
/// Emit ops for a view operation.
594-
///
595-
/// The `op_ix` parameter provides a deterministic per-op sequence to avoid ID collisions.
596-
/// Callers should pass `delta.len()` to get a unique index for each op in the tick.
597-
///
598-
/// **DEPRECATED**: Use [`emit_view_op_delta_scoped`] instead for parallel-safe determinism.
599-
#[allow(dead_code)]
600-
fn emit_view_op_delta(
601-
warp_id: WarpId,
602-
delta: &mut TickDelta,
603-
type_id: TypeId,
604-
payload: &[u8],
605-
op_ix: usize,
606-
) {
607-
let view_id = make_node_id("sim/view");
608-
delta.push(WarpOp::UpsertNode {
609-
node: NodeKey {
610-
warp_id,
611-
local_id: view_id,
612-
},
613-
record: NodeRecord {
614-
ty: make_type_id("sim/view"),
615-
},
616-
});
617-
// Use op_ix from caller (typically delta.len() before this call) for unique sequencing
618-
let seq = op_ix as u64;
619-
let op_id = make_node_id(&format!("sim/view/op:{:016}", seq));
620-
delta.push(WarpOp::UpsertNode {
621-
node: NodeKey {
622-
warp_id,
623-
local_id: op_id,
624-
},
625-
record: NodeRecord {
626-
ty: make_type_id(TYPE_VIEW_OP),
627-
},
628-
});
629-
delta.push(WarpOp::UpsertEdge {
630-
warp_id,
631-
record: EdgeRecord {
632-
id: make_edge_id(&format!("edge:view/op:{:016}", seq)),
633-
from: view_id,
634-
to: op_id,
635-
ty: make_type_id("edge:view/op"),
636-
},
637-
});
638-
delta.push(WarpOp::SetAttachment {
639-
key: AttachmentKey::node_alpha(NodeKey {
640-
warp_id,
641-
local_id: op_id,
642-
}),
643-
value: Some(AttachmentValue::Atom(AtomPayload::new(
644-
type_id,
645-
bytes::Bytes::copy_from_slice(payload),
646-
))),
647-
});
648-
}
649-
650593
/// Emit ops for a put KV operation.
651594
#[cfg(feature = "dind_ops")]
652595
fn emit_put_kv(warp_id: WarpId, delta: &mut TickDelta, key: String, value: String) {

crates/warp-benches/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ rayon = "~1.10"
1919
[dev-dependencies]
2020
# Tilde pins for reproducibility: allow patch updates only
2121
criterion = { version = "~0.5", default-features = false, features = ["html_reports"] }
22+
bytes = "~1.11"
2223
blake3 = { version = "~1.8.2", default-features = false, features = ["std"] }
2324
rustc-hash = "~2.1.1"
2425

@@ -37,3 +38,7 @@ harness = false
3738
[[bench]]
3839
name = "scheduler_adversarial"
3940
harness = false
41+
42+
[[bench]]
43+
name = "boaw_baseline"
44+
harness = false

0 commit comments

Comments
 (0)