minigraf 1.2.1

Zero-config, single-file, embedded graph database with bi-temporal Datalog queries
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
# Minigraf Test Coverage Report

**Last Updated**: v1.2.0 (June 2026), 974 tests ✅

## Test Summary

**Total Tests**: 974 ✅ (966 passing, 8 ignored)
- ✅ 653 unit tests (lib — includes Wave 1 hash-join and selective-lookup test modules, Wave 3 fault-injection unit tests, per-query limits #288, magic sets #289)
- ✅ 12 bi-temporal tests (integration)
- ✅ 11 complex query tests (integration)
- ✅ 9 recursive rules tests (integration)
- ✅ 12 concurrency tests (integration, 1 ignored: nightly stress)
- ✅ 21 WAL / crash recovery tests (integration)
- ✅ 2 cross-platform compat tests (integration, Phase 8.1)
- ✅ 6 index tests (integration, Phase 6.1)
- ✅ 7 performance tests (integration, Phase 6.2/6.4b)
- ✅ 7 retraction tests (integration, Phase 6.4a)
- ✅ 4 edge case tests (integration, Phase 6.4a)
- ✅ 8 B+tree v6 tests (integration, Phase 6.5)
- ✅ 10 negation (`not`) tests (integration, Phase 7.1a)
- ✅ 14 not-join tests (integration, Phase 7.1b)
- ✅ 24 aggregation tests (integration, Phase 7.2a)
- ✅ 28 predicate expression tests (integration, Phase 7.2b)
- ✅ 18 disjunction tests (integration, Phase 7.3)
- ✅ 8 production pattern tests (integration, Phase 7.5 — cross-feature scenarios)
- ✅ 8 error handling tests (integration, Phase 7.5 — error-path coverage)
- ✅ 22 temporal metadata tests (integration, Phase 7.6 — `:db/valid-from`, `:db/valid-to`, `:db/tx-count`, `:db/tx-id`, `:db/valid-at`)
- ✅ 14 window function tests (integration, Phase 7.7a — cumulative sum/count/min/avg, rank with ties, row-number, partition-by, desc ordering, mixed aggregate+window, edge cases, lag/lead parse rejection)
- ✅ 10 UDF tests (integration, Phase 7.7b — custom aggregates, custom predicates, UDF as window function, name collision guards, runtime errors, thread safety)
- ✅ 17 prepared statement tests (integration, Phase 7.8 — entity/value/as-of/valid-at slots, combined temporal+entity, AnyValidTime, error paths, plan reuse)
- ✅ 3 grammar conformance tests (integration, Phase 7.9 — pest shadow grammar + EDN corpus)
- ✅ 5 migration matrix tests (integration, Wave 3 #215 — v7 round-trip, v3 empty migrate, corrupt magic, unsupported version, WAL replay idempotent)
- ✅ 5 index corruption tests (integration, Wave 3 #216 — checksum corruption, btree leaf/internal no-panic, root pointer mismatch, non-critical corruption query check)
- ✅ 3 property-based tests (integration, Wave 3 #212/#213/#219 — proptest Datalog correctness vs naive reference evaluator)
- ✅ 1 long-haul smoke test (integration, Wave 3 #220 — 500 entities × 10 attrs × 10 cycles; ignored: nightly)
- ✅ 10 XTDB compat tests (integration, Wave 3 #221 — Apache 2.0 semantic ports of XTDB concepts)
- ✅ 9 Datomic compat tests (integration, Wave 3 #221 — independently written semantic ports of Datomic concepts)
- ✅ 5 magic sets tests (integration, #289 — demand-driven recursive evaluation correctness: bound transitive closure, all-free closure, subset invariant, multi-hop, mutual recursion)
- ✅ 15 doc tests (9 passing, 6 ignored: doc examples referencing internal types that cannot compile as standalone rustdoc tests)

**Status**: ✅ **All 966 tests passing** (8 ignored: 6 internal-type doc examples, 1 nightly concurrency stress, 1 nightly smoke)

## Wave 3 Reliability Completion Status: ✅ COMPLETE

**Wave 3 issues**: #209, #210, #214 (WAL fault injection), #215 (migration matrix), #216 (index corruption), #217 (concurrency stress), #212, #213, #219 (property-based / coverage), #220 (long-haul smoke), #221 (XTDB/Datomic compat)

**New tests added by Wave 3** (+87 total):
- `wal_test.rs` — 9 new fault-injection tests (FaultInjectingBackend: write fail, flush fail, read fault, WAL CRC corruption, checkpoint atomicity, partial checkpoint recovery, multi-writer serialisation, concurrent write+checkpoint, backend error propagation)
-`tests/migration_matrix_test.rs` — 5 migration tests (v7 round-trip, v3 empty migrate, corrupt magic, unsupported version, WAL replay idempotent)
-`tests/index_corruption_test.rs` — 5 corruption-resilience tests (checksum corruption, btree leaf/internal no-panic, root pointer mismatch, non-critical corruption query check)
-`tests/concurrency_test.rs` — 5 new stress tests (stress readers during writer, failed write then success, rollback after partial work, open/write/checkpoint/query loop per thread, nightly stress loop)
-`tests/property_test.rs` — 3 proptest property tests (EAV fact model, bi-temporal monotonicity, retract visibility)
-`tests/smoke_test.rs` — 1 long-haul smoke test (500 entities × 10 attrs × 10 cycles, 7 invariants; `#[ignore]` nightly)
-`tests/xtdb_compat_test.rs` — 10 XTDB semantic compatibility tests
-`tests/datomic_compat_test.rs` — 9 Datomic semantic compatibility tests
- ✅ 40 new lib unit tests (FaultInjectingBackend unit tests, WAL corruption helpers, property test infrastructure)

**New CI workflows added**:
- `.github/workflows/fuzz.yml` — nightly fuzzing, 6 libFuzzer targets × 60s each
-`.github/workflows/coverage-gates.yml` — per-module coverage thresholds, fails PR if coverage drops
-`.github/workflows/smoke.yml` — nightly 5am UTC, 15-min timeout, `--include-ignored`

---

## Wave 2 Optimizer & Benchmarks Completion Status: ✅ COMPLETE

**Wave 2 issues**: #207 + #206 (predicate push-down + mixed rule optimization), #205 (cost-based not/or ordering), #229 (SIMD benchmarking + crossover analysis)

No new integration tests added — Wave 2 is entirely optimizer and benchmark work. Existing 850 tests cover all affected code paths. New benchmark groups: `simd_temporal`, `simd_as_of`, `simd_aggregate`.

---

## Wave 1 Performance Completion Status: ✅ COMPLETE

**Wave 1 issues**: #208 (selective B+Tree lookup), #202 (not/not-join hash-join), #203 (or/or-join hash-join), #204 (join_with_pattern hash-join)

**New unit test modules added**:
- `selective_lookup_tests` in `executor.rs` — entity-bound and attribute-bound point queries, threshold fallback, `as_of` full-scan path
-`not_hash_join_tests` in `executor.rs``not`/`not-join` pre-computed exclusion set at 1k/10k scale
-`or_hash_join_tests` in `executor.rs``or`/`or-join` empty-seed branch evaluation and hash-join back-join at scale
-`hash_join_tests` in `matcher.rs` — shared-`?e` join, value-position join, no-join-var fallback

---

## Phase 8 Completion Status: ✅ COMPLETE — v1.0.0

All Phase 8 sub-phases complete. See per-phase sections below.

---

## Phase 8.3d Completion Status: ✅ COMPLETE

**Phase 8.3d Features** (Node.js, complete — v0.25.0):
- `minigraf-node/src/lib.rs` — napi-rs bindings: `MiniGrafDb` class (open, inMemory, execute, checkpoint)
-`minigraf-node/package.json``minigraf` npm package; prebuilt `.node` binaries for Linux x86_64/aarch64, macOS universal2, Windows x86_64
-`node-ci.yml` — PR test matrix on 4 platforms
-`node-release.yml` — cross-compile, assemble platform packages, publish to npm on tag

---

## Phase 8.3c Completion Status: ✅ COMPLETE

**Phase 8.3c Features** (C FFI, complete — v0.24.0):
- `minigraf-c/src/lib.rs``cdylib` + `staticlib`; 7 exported functions: `minigraf_open`, `minigraf_open_in_memory`, `minigraf_execute`, `minigraf_string_free`, `minigraf_checkpoint`, `minigraf_close`, `minigraf_last_error`
-`minigraf-c/include/minigraf.h` — committed stable header (cbindgen-generated); header drift check in CI
-`c-ci.yml` — PR test matrix on 4 platforms + header drift check
-`c-release.yml` — builds platform tarballs (`.tar.gz` / `.zip`), uploads to GitHub Releases

---

## Phase 8.3b Completion Status: ✅ COMPLETE

**Phase 8.3b Features** (Java/JVM, complete — v0.23.0):
- `minigraf-ffi/java/` — Gradle 8.11 project: `build.gradle.kts`, `settings.gradle.kts`, `NativeLoader.kt` (runtime native extraction from JAR resources)
-`minigraf-ffi/java/src/test/kotlin/.../BasicTest.kt` — JUnit 5 suite: in-memory, transact/query, error handling, file-backed persistence
-`java-ci.yml` — PR test matrix on 4 platforms (Linux x86_64, Linux aarch64, macOS universal2, Windows x86_64)
-`java-release.yml` — cross-compiles natives, assembles fat JAR, publishes to Maven Central via NMCP

---

## Phase 8.3a Completion Status: ✅ COMPLETE

**Phase 8.3a Features** (Python, complete — v0.22.0):
- `minigraf-ffi/python/` — maturin project: `pyproject.toml`, Python extension module via UniFFI
- ✅ Pre-built wheels for Linux x86_64/aarch64, macOS universal2, Windows x86_64; no Rust toolchain required by end users
-`python-ci.yml` — PR test matrix on 4 platforms
-`python-release.yml` — builds wheels, publishes to PyPI on tag

---

## Phase 8.2 Completion Status: ✅ COMPLETE

**Phase 8.2 Features** (Mobile, complete — v0.21.0):
- `minigraf-ffi/src/lib.rs` — UniFFI 0.31 bindings: `MiniGrafDb` (open, openInMemory, execute, checkpoint), `MiniGrafError` (Parse, Query, Storage, Other)
- ✅ Android `.aar` release artifact — published to GitHub Packages (`io.github.adityamukho:minigraf-android`)
- ✅ iOS `.xcframework` release artifact — distributed via Swift Package Manager (`Package.swift` at repo root)
-`mobile.yml` CI — cross-compiles Android targets with `cargo-ndk`, generates Kotlin/Swift UniFFI bindings, assembles AAR and xcframework, publishes both on every tag
-`docs-check` CI job added to `rust.yml` and `release.yml` — gates releases on `cargo doc --all-features` passing cleanly

---

## Phase 8.1 Completion Status: ✅ COMPLETE

**Phase 8.1a Features** (browser WASM, complete):
- `BrowserDb` public API: `open_in_memory`, `execute`, `checkpoint`, `export_graph`, `import_graph`
-`BrowserBufferBackend` — in-memory `StorageBackend` over a flat page buffer, byte-identical to native `.graph` format
-`IndexedDbBackend` — page-granular IndexedDB storage via `web-sys` + `wasm-bindgen`
-`wasm-pack` build generating `minigraf-wasm/` with JS glue and TypeScript `.d.ts`
-`wasm-bindgen-test` suite: 6 browser integration tests (Chrome + Firefox in CI)

**Phase 8.1b Features** (WASI, complete):
- `FileBackend` verified under WASI capability-based filesystem (no changes needed)
-`wasm32-wasip1` CI workflow: build, unit tests (Wasmtime runner), smoke tests (Wasmtime + Wasmer)
- ✅ Thread-dependent tests gated with `#[cfg(not(target_os = "wasi"))]`

**Cross-platform compatibility** (issue #150, complete):
- `tests/cross_platform_compat_test.rs`: 2 native tests (raw page byte round-trip + fixture readability)
-`tests/fixtures/compat.graph`: committed v7 binary fixture with known facts
-`native_fixture_readable_by_browser_db` wasm-bindgen-test: imports native fixture, verifies both facts
- ✅ 795 tests passing (unit + integration + doc + wasm); version bumped to v0.20.0

## Phase 7.9 Completion Status: ✅ COMPLETE

**Phase 7.9 Features** (current, complete):
- `Minigraf::repl(&self) -> Repl<'_>` factory method — `Repl` now borrows `&Minigraf` for lifetime safety
- ✅ All internal types narrowed to `pub(crate)`: `FactStorage`, `PersistentFactStorage`, `FileHeader`, `StorageBackend`, `DatalogExecutor`, `PatternMatcher`, `Fact`, `TxId`, `VALID_TIME_FOREVER`, `Wal`, etc.
- ✅ Full rustdoc on all public API items with `# Examples` doctests; 8 new doctests added
-`[package.metadata.docs.rs]` in `Cargo.toml` — docs.rs builds with `all-features = true`
-`#![warn(missing_docs)]` — enforces documentation coverage going forward
- ✅ Bare `.unwrap()` in library code replaced with `.expect("lock poisoned")` / `.expect("WAL not initialized")`
-`cargo clippy -- -D warnings` clean
- ✅ macOS and Windows added to CI test matrix (`rust.yml`)
- ✅ crates.io and docs.rs badges + Installation section in `README.md`
- ✅ 788 tests passing (unit + integration + doc); version bumped to v0.19.0

## Phase 7.8 Completion Status: ✅ COMPLETE

**Phase 7.8 Features** (complete):
- `EdnValue::BindSlot(String)`, `AsOf::Slot(String)`, `ValidAt::Slot(String)`, `Expr::Slot(String)` AST variants in `types.rs`
-`BindValue` enum in `src/query/datalog/prepared.rs`: `Entity(Uuid)`, `Val(Value)`, `TxCount(u64)`, `Timestamp(i64)`, `AnyValidTime`
-`PreparedQuery` struct — stores parsed AST + optimised plan + `Arc` handles to fact store and registries; re-executes against live fact store state
-`prepare_query()` (pub(crate)) — parse, validate, compute query plan once
-`PreparedQuery::execute(bindings)` — deep-clone + AST walk substitution; type-checked per bind position; executor, optimizer, matcher unchanged
- ✅ Panic guards (no slot-name interpolation) in `executor.rs` (4 `ValidAt::Slot` sites, 1 `Expr::Slot` site) and `storage.rs` (`AsOf::Slot`)
-`Minigraf::prepare(query_str) -> Result<PreparedQuery>` on public API (`db.rs`)
-`BindValue` and `PreparedQuery` re-exported from `lib.rs`
-`tests/prepared_statements_test.rs` — 17 integration tests
- ✅ 780 tests passing (unit + integration + doc)

## Phase 7.7b Completion Status: ✅ COMPLETE

**Phase 7.7b Features** (current, complete):
- `UdfOps` and `PredicateDesc` types in `src/query/datalog/functions.rs` — register custom aggregates (init/step/finalise closures) and custom predicates (filter closure)
-`FunctionRegistry::register_aggregate` and `register_predicate` methods; collision guards reject re-registration of built-in names or duplicate UDFs
-`FindSpec::Udf` and `WhereClause::UdfPredicate` variants in `types.rs`; UDF aggregates usable in `:find` and `:over` window specs; UDF predicates usable in `:where`
- ✅ Parser extended: UDF aggregate invocations in `:find` / `:over`; UDF predicate invocations in `:where`; unknown function names deferred to runtime, not rejected at parse time
- ✅ Executor routes UDF aggregates through `FunctionRegistry` at query time; UDF predicates evaluated per binding row
-`Minigraf::register_aggregate` and `register_predicate` on the public API (`db.rs`)
-`tests/udf_test.rs` — 14 integration tests
- ✅ 753 tests passing (unit + integration + doc)

## Phase 7.7a Completion Status: ✅ COMPLETE

**Phase 7.7a Features** (current, complete):
- `FunctionRegistry` in `src/query/datalog/functions.rs` — string-keyed registry; built-in aggregates (`sum`, `count`, `min`, `max`, `avg`, `count-distinct`, `sum-distinct`) migrated into it; `window_ops` (init/step/finalise) on window-compatible entries; `is_builtin` flag
-`WindowFunc`, `Order`, `WindowSpec`, `FindSpec::Window` types in `types.rs`; `AggFunc` enum removed; `FindSpec::Aggregate.func` changed to `String`
-`parse_window_expr` in `parser.rs``(func ?v :over (:partition-by ?p :order-by ?o :desc))` syntax; `lag`/`lead` rejected; unknown function → parse error; non-window-compatible in `:over` → parse error
-`apply_post_processing`, `compute_aggregation`, `apply_window_functions`, `project_find_specs` in `executor.rs` — replaces `apply_aggregation`/`apply_agg_func`
-`FunctionRegistry` wired through `db.rs` (`Minigraf::Inner` gains `Arc<RwLock<FunctionRegistry>>`)
-`tests/window_functions_test.rs` — 12 integration tests (cumulative sum, running count/min/avg, rank with ties, row-number, partition-by, desc ordering, mixed aggregate+window, single-row and empty-result edge cases, lag/lead parse rejection)
- ✅ 746 tests passing (unit + integration + doc)

## Phase 7.6 Completion Status: ✅ COMPLETE

**Phase 7.6 Features** (current, complete):
- `PseudoAttr` enum and `AttributeSpec` wrapper type in `types.rs`
-`parse_query_pattern` in `parser.rs` — detects `:db/*` keywords in attribute position; rejects in entity/value positions
-`PatternMatcher::from_slice_with_valid_at` constructor — passes query-level `valid_at` into the matcher
- ✅ Hard-error guard in executor: per-fact pseudo-attrs require `:any-valid-time`
-`:db/valid-at` binds the effective query timestamp; `:any-valid-time` accepted as standalone keyword
-`tests/temporal_metadata_test.rs` — 16 integration tests (time-interval range queries, time-point lookups, tx-time correlation, `:db/valid-at` semantics, parse/runtime error guards)
- ✅ 647 tests passing (438 unit + 209 integration)

## Phase 7.5 Completion Status: ✅ COMPLETE

**Phase 7.5 Features** (complete):
- `cargo-llvm-cov` branch coverage tooling documented in `CONTRIBUTING.md`
- ✅ Baseline branch coverage recorded; executor.rs ~86.61%, evaluator.rs ~89.29% (up from ~75% / ~73%)
-`tests/production_patterns_test.rs` — 8 cross-feature integration tests
-`tests/error_handling_test.rs` — 8 error-path integration tests (1 ignored: confirmed or+neg-cycle stratification bug)
- ✅ Stream 3 unit tests: ~53 new tests for previously uncovered branches in executor.rs and evaluator.rs
- ✅ 617 tests passing (424 unit + 187 integration + 6 doc)

## Phase 7.4 Completion Status: ✅ COMPLETE

**Phase 7.4 Features** (current, complete):
- `filter_facts_for_query` returns `Arc<[Fact]>` — eliminates O(N) four-BTreeMap index rebuild on every non-rules query call
-`execute_query` path constructs zero `FactStorage` objects; `execute_query_with_rules` still converts for `StratifiedEvaluator`
-`PatternMatcher::from_slice(Arc<[Fact]>)` constructor added
-`apply_or_clauses` and `evaluate_not_join` signatures updated to accept `Arc<[Fact]>`
- ✅ Evaluator loop: `accumulated_facts` computed once per iteration (was 4 separate `get_asserted_facts()` calls)
-~62–65% speedup on non-rules queries at 10K facts (`query/point_entity/10k`: 22 ms → 8.6 ms; `aggregation/count_scale/10k`: 28 ms → 9.7 ms)
- ✅ 4 new unit tests in `matcher.rs`, 2 new unit tests in `executor.rs` (6 total)
- ✅ Version bumped to v0.13.1

## Phase 7.3 Completion Status: ✅ COMPLETE

**Phase 7.3 Features** (current, complete):
- `WhereClause::Or(Vec<Vec<WhereClause>>)` and `WhereClause::OrJoin { join_vars, branches }` variants in `types.rs`
-`(or branch1 branch2 ...)` and `(or-join [?v...] branch1 branch2 ...)` in `:where` clauses and rule bodies
-`(and ...)` grouping clause to collect multiple clauses into a single branch
-`match_patterns_seeded` on `PatternMatcher`; `evaluate_branch` and `apply_or_clauses` helpers in `executor.rs`
-`DependencyGraph::from_rules` refactored with recursive `collect_clause_deps`; `Or`/`OrJoin` branches contribute positive dependency edges
- ✅ Rules with `or`/`or-join` in bodies routed to `mixed_rules` path in `StratifiedEvaluator`
-`tests/disjunction_test.rs`: 16 integration tests (Phase 7.3)
- ✅ Version bumped to v0.13.0

**Core Features Implemented** (Phase 6.2):
- ✅ Packed fact pages (`page_type = 0x02`): ~25 facts per 4KB page (~25× space reduction)
- ✅ LRU page cache (`cache.rs`): approximate-LRU, read-lock on hits, `Arc<Vec<u8>>` entries
-`CommittedFactReader` trait + `CommittedFactLoaderImpl`: on-demand fact resolution
- ✅ Pending `FactRef` (`page_id = 0`): resolves to in-memory pending facts vec
-`FileHeader` v5: `fact_page_format` byte (0x02 = packed); auto v4→v5 migration on open
-`OpenOptions::page_cache_size(usize)` builder method (default 256)
- ✅ EAVT/AEVT range scans in `get_facts_by_entity` / `get_facts_by_attribute`

**Phase 6.1 Features** (also complete):
- ✅ EAVT, AEVT, AVET, VAET covering indexes with bi-temporal keys
-`FactRef { page_id, slot_index }`: forward-compatible disk location pointer
- ✅ Canonical value encoding (`encode_value`) for sort-order-preserving comparisons
- ✅ B+tree page serialisation for index persistence (`btree.rs`)
-`FileHeader` v4: `eavt/aevt/avet/vaet_root_page` + `index_checksum` (CRC32)
- ✅ Auto-rebuild on checksum mismatch
- ✅ Query optimizer: `IndexHint`, `select_index()`, selectivity-based `plan()`

**Phase 5 Features** (also complete):
- ✅ Fact-level sidecar WAL (`<db>.wal`) with CRC32-protected binary entries
- ✅ WAL-before-apply ordering: WAL fsynced before facts touch in-memory state
-`FileHeader` v3 with `last_checkpointed_tx_count` (replay deduplication)
-`WriteTransaction` API (`begin_write`, `commit`, `rollback`)
- ✅ Crash recovery: WAL replay on open, corrupt entries discarded at first bad CRC32
- ✅ Checkpoint: WAL flushed to `.graph` file, then WAL cleared
- ✅ Thread-safe: concurrent readers + exclusive writer (Mutex + RwLock)
- ✅ File format v2→v3 migration on first checkpoint

**Phase 4 Features** (also complete):
- ✅ EAV data model with `tx_count`, `valid_from`, `valid_to` fields
-`VALID_TIME_FOREVER = i64::MAX` sentinel
-`FactStorage` temporal query methods (`get_facts_as_of`, `get_facts_valid_at`)
- ✅ Parser: EDN maps, `:as-of`, `:valid-at`, per-fact valid time overrides
- ✅ Executor: 3-step temporal filter (tx-time → asserted → valid-time)
- ✅ File format v1→v2 migration
- ✅ UTC-only timestamp parsing (chrono, avoids GHSA-wcg3-cvx6-7396)

**Phase 7.2b Features** (also complete):
- `BinOp` (14 variants), `UnaryOp` (5 variants), `Expr` AST, `WhereClause::Expr { expr, binding }` in `types.rs`
- ✅ Filter predicates: `[(< ?age 30)]`, `[(string? ?v)]`, `[(starts-with? ?tag "work")]`, `[(matches? ?email "...")]`
- ✅ Arithmetic bindings: `[(+ ?price ?tax) ?total]`, nested `[(+ (* ?a 2) ?b) ?result]`, type-predicate binding `[(integer? ?v) ?is-int]`
-`parse_expr` / `parse_expr_clause` with parse-time regex validation; `check_expr_safety` recurses into `not`/`not-join` bodies
- ✅ Dispatch at all 4 clause sites; `outer_vars_from_clause` updated for binding variable scope
-`eval_expr` / `eval_binop` / `is_truthy` / `apply_expr_clauses` in `executor.rs`; `apply_expr_clauses_in_evaluator` in `evaluator.rs`
-`tests/predicate_expr_test.rs`: 28 integration tests (Phase 7.2b)
- ✅ Version bumped to v0.12.0

**Phase 7.2a Features** (also complete):
- `count`, `count-distinct`, `sum`, `sum-distinct`, `min`, `max` aggregate functions in `:find` clause
-`:with` grouping clause — variables that participate in grouping but are excluded from output rows
-`AggFunc` enum, `FindSpec` enum; `DatalogQuery.find` migrated from `Vec<String>` to `Vec<FindSpec>`
-`apply_aggregation` post-processing in `executor.rs`; parse-time validation
-`tests/aggregation_test.rs`: 24 integration tests (Phase 7.2a)
- ✅ Version bumped to v0.11.0

**Phase 7.1 Features** (also complete):
- `src/query/datalog/stratification.rs`: `DependencyGraph`, `stratify()` — negative dependency edges + Bellman-Ford cycle detection; negative cycles rejected at rule registration time
-`WhereClause::Not(Vec<WhereClause>)` and `WhereClause::NotJoin { join_vars, clauses }` variants; all match arms updated
-`(not clause…)` — stratified negation; all body variables must be pre-bound by outer clauses
-`(not-join [?v…] clause…)` — existentially-quantified negation; only `join_vars` are shared from outer scope; remaining body variables are fresh
- ✅ Safety validation at parse time (unbound join vars → parse error; nesting constraint enforced)
-`StratifiedEvaluator`: stratifies rules, evaluates strata in order; `not`/`not-join` filters applied per binding in mixed-rule strata
-`evaluate_not_join` free function: handles both `Pattern` and `RuleInvocation` body clauses
-`tests/negation_test.rs`: 10 integration tests (Phase 7.1a)
-`tests/not_join_test.rs`: 14 integration tests (Phase 7.1b)
- ✅ Version bumped to v0.10.0

**Phase 6.5 Features** (also complete):
- `src/storage/btree_v6.rs`: proper on-disk B+tree with `build_btree` bulk-load and `range_scan` leaf-chain traversal
-`OnDiskIndexReader` + `CommittedIndexReader` trait: page-cache-backed index lookup; no full in-memory BTreeMap
-`MutexStorageBackend<B>`: backend mutex held per page read; cache-warm pages require no lock
- ✅ FileHeader v6 (80 bytes): adds `fact_page_count u64` field at bytes 72–80; auto v5→v6 migration
-`tests/btree_v6_test.rs`: 8 integration tests (B+tree insert/scan, multi-page, concurrent correctness, v5→v6 migration)
-`test_concurrent_range_scans_correctness` unit test: 8 barrier-synchronised threads, all return identical results
- ✅ Version bumped to v0.9.0; BENCHMARKS.md updated with v6 results

**Phase 6.4b Features** (also complete):
- ✅ Criterion benchmark suite at 1K–1M facts; results documented in `BENCHMARKS.md`
- ✅ heaptrack memory profiles: 10K=14.4MB / 100K=136MB / 1M=1.33GB peak heap (v5 baseline)
- ✅ Byte-layout unit tests pin all FileHeader v5 field offsets (`src/storage/mod.rs`)
- ✅ Byte-layout unit tests pin packed page header + record directory offsets (`src/storage/packed_pages.rs`)
- ✅ Dead `clap` dependency removed; `Cargo.toml` metadata complete; version bumped to v0.8.0
- ✅ README trimmed (794 → 166 lines); detail offloaded to GitHub wiki

**Phase 6.4a Features** (also complete):
- ✅ Retraction semantics fix: `net_asserted_facts()` computes net view per EAV triple in `filter_facts_for_query`
-`check_fact_sizes()` early validation in `db.rs`: rejects oversized facts before WAL write
-`MAX_FACT_BYTES` constant (`packed_pages.rs`): 4 080 bytes — maximum serialised size per fact
- ✅ 7 new retraction integration tests (`tests/retraction_test.rs`)
- ✅ 4 new edge case integration tests (`tests/edge_cases_test.rs`)

**Phase 3 Features** (also complete):
- ✅ Datalog parser (EDN syntax)
- ✅ Pattern matching with variable unification
- ✅ Query executor (transact, retract, query)
- ✅ Recursive rules with semi-naive evaluation
- ✅ Transitive closure queries
- ✅ Persistent storage (postcard serialization)
- ✅ REPL with multi-line and comment support

---

## Test Coverage by Module

### 1. Graph Types (`src/graph/types.rs`) - ✅ Excellent (8 tests)

- ✅ Fact creation, equality, retraction, entity references
- ✅ Transaction ID generation and ordering
-`VALID_TIME_FOREVER` sentinel, `with_valid_time()`, `TransactOptions`
- ✅ All `Value` types (String, Integer, Float, Boolean, Ref, Keyword, Null)

**Coverage**: ~95%

### 2. Fact Storage (`src/graph/storage.rs`) - ✅ Excellent (18+ tests)

**Core Operations**:
- ✅ Transact, retract, batch transact
- ✅ Get facts by entity/attribute, history tracking

**Phase 4 (Bi-temporal)**:
- `tx_count` increments, `get_facts_as_of()`, `get_facts_valid_at()`
-`load_fact()` preserves original `tx_id`/`tx_count`

**Phase 5 (WAL helpers)**:
- `get_all_facts()`, `restore_tx_counter()`, `allocate_tx_count()`

**Phase 6.1-6.2 (Index + CommittedFactReader)**:
- `set_committed_reader()` wires CommittedFactReader
-`get_facts_by_entity()` uses EAVT range scan
-`get_facts_by_attribute()` uses AEVT range scan
-`FactRef { page_id: 0 }` resolved to pending facts; `page_id >= 1` via CommittedFactReader
- ✅ MockLoader in tests verifies committed path

**Coverage**: ~93%

### 3. WAL (`src/wal.rs`) - ✅ Excellent (8 unit tests)

- ✅ Empty WAL round-trip
- ✅ Single-fact and multi-fact entry round-trips
- ✅ Reopen-and-append
- ✅ Bad magic header rejected
- ✅ Truncated entry stops replay (partial write discard)
-`delete_file()` removes WAL

**Coverage**: ~97%

### 4. Database API (`src/db.rs`) - ✅ Excellent (12 unit tests)

- ✅ In-memory transact and query round-trip
- ✅ Explicit `WriteTransaction` commit and rollback
-`build_query_view()` read-your-own-writes within transaction
- ✅ Reentrant `begin_write()` returns error
- ✅ File-backed open, transact, reopen (persistence)
- ✅ WAL written before in-memory apply
- ✅ Auto-checkpoint threshold fires, `checkpoint()` manual trigger
- ✅ Concurrent `execute()` during active `WriteTransaction`

**Coverage**: ~93%

### 5. Covering Indexes (`src/storage/index.rs`) - ✅ Excellent (11 tests)

- `FactRef` field access
-`encode_value` sort order: integers, cross-type, floats, NaN canonicalization
- ✅ EAVT key ordering by entity
- ✅ AVET key ordering by value bytes
- ✅ VAET only populated for `Value::Ref`
-`Indexes::insert` populates all four indexes

**Coverage**: ~98%

### 6. B+tree Persistence (`src/storage/btree.rs`) - ✅ Good (4 tests)

- ✅ Empty EAVT roundtrip (exactly 1 page)
- ✅ Small EAVT roundtrip (10 entries)
- ✅ Large EAVT roundtrip (150 entries, multi-page)
- ✅ Sort order preserved after serialise/deserialise

**Coverage**: ~90%

### 7. LRU Page Cache (`src/storage/cache.rs`) - ✅ Good (6 tests)

- ✅ Cache miss loads from backend
- ✅ Cache hit returns same `Arc` without backend read
- ✅ LRU eviction evicts correct (oldest) page
-`put_dirty` / `flush` writes back to backend
-`invalidate` removes entry
-`cached_page_count` reports correctly

**Coverage**: ~92%

### 8. Packed Pages (`src/storage/packed_pages.rs`) - ✅ Good (8 tests)

- ✅ Single fact pack/unpack roundtrip
- ✅ Multiple facts pack/unpack roundtrip
- ✅ Correct `FactRef` slot assignments
- ✅ Oversized fact returns `Err` (not panic)
-`read_all_from_pages` with known page IDs
- ✅ Wrong page type returns `Err`
-**Byte-layout pin**: page header (bytes 0–11) field positions verified (Phase 6.4b)
-**Byte-layout pin**: record directory entries at byte 12+ verified (Phase 6.4b)

**Coverage**: ~93%

### 9. FileHeader (`src/storage/mod.rs`) - ✅ Excellent (10 tests)

- ✅ v6 serialisation: 80 bytes, correct field offsets
- ✅ v6 roundtrip with all index root pages, checksum, and `fact_page_count`
- ✅ v3/v4/v5 headers accepted with appropriate zero-filling
- ✅ v6 header with <80 bytes rejected
- ✅ Header validation (magic, version range 1-6)
- ✅ Version 0 and 7 rejected
-`FORMAT_VERSION == 6`
-**Byte-layout pin**: all 11 fields at exact offsets with LE encoding verified (Phase 6.4b / v6 update)

**Coverage**: ~98%

### 10. Datalog Parser (`src/query/datalog/parser.rs`) - ✅ Excellent (25 tests)

- ✅ All tokens, numbers, strings, booleans, UUIDs, nil
- ✅ Transact/Retract/Query/Rule commands
-`:as-of` (counter + ISO 8601 timestamp)
-`:valid-at` (timestamp + `:any-valid-time`)
- ✅ EDN map `{:key val}` with transaction-level valid time
- ✅ Per-fact valid time override (4-element fact vector)
- ✅ Reject negative `:as-of` counter and invalid timestamps

**Coverage**: ~98%

### 11. Datalog Types, Matcher, Executor, Rules, Evaluator - ✅ Good-Excellent

- Types: ~95% (7 tests)
- Matcher: ~85% (6 tests)
- Executor: ~94% (18 tests) — including temporal filter and optimizer integration
- Rule Registry: ~95% (6 tests)
- Recursive Evaluator: ~95% (10 tests)

### 12. Storage Backends (`src/storage/backend/`) - ✅ Good (8 tests)

- ✅ FileBackend create/write/read, persistence across close/reopen
- ✅ MemoryBackend write/read, error handling

**Coverage**: ~85%

### 13. Temporal (`src/temporal.rs`) - ✅ Good

- ✅ UTC timestamp parsing and formatting
- ✅ Chrono CVE GHSA-wcg3-cvx6-7396 avoidance verified

**Coverage**: ~90%

---

## Integration Tests

### Complex Queries (`tests/complex_queries_test.rs`) - ✅ 10 tests

- ✅ 3-pattern and 4-pattern joins, self-joins, entity reference joins
- ✅ No results, partial matches, variable reuse, multiple values, empty database

### Recursive Rules (`tests/recursive_rules_test.rs`) - ✅ 9 tests

- ✅ Transitive closure, cycles, long chains, diamond patterns
- ✅ Ancestor/descendant, family trees, multiple recursive predicates

### Concurrency (`tests/concurrency_test.rs`) - ✅ 12 tests (1 ignored: nightly)

- ✅ Concurrent rule registration (5 threads), concurrent queries with rules (10 threads)
- ✅ Read-heavy workload (50 threads), recursive evaluation concurrency
- ✅ No deadlocks (20 threads mixed), RwLock consistency (10 writers + 10 readers)
- ✅ Stress readers during writer: concurrent readers see consistent state while writer holds lock (Wave 3 #217)
- ✅ Failed write followed by successful write: DB remains usable after write error (Wave 3 #217)
- ✅ Rollback after partial work: partial transaction leaves no trace (Wave 3 #217)
- ✅ Open/write/checkpoint/query loop per thread: 10-thread concurrent lifecycle (Wave 3 #217)
- ✅ Stress open/write loop (nightly, `#[ignore]`): high-contention loop stress test (Wave 3 #217)

### Bi-temporal (`tests/bitemporal_test.rs`) - ✅ 10 tests

- ✅ As-of counter and timestamp snapshots
- ✅ Valid-at inside/outside/boundary, default filter, any-valid-time
- ✅ Combined bi-temporal (both dimensions), multi-entity valid ranges

### WAL / Crash Recovery (`tests/wal_test.rs`) - ✅ 21 tests

- ✅ Basic persistence (file-backed transact and query)
- ✅ WAL replay after `mem::forget` crash simulation
- ✅ Stale WAL dedup via `last_checkpointed_tx_count`
- ✅ Corrupt/partial entry discard on recovery
- ✅ Manual checkpoint clears WAL and updates header
- ✅ Auto-checkpoint fires at threshold
- ✅ Explicit transaction crash safety and rollback
- ✅ Multi-transact rollback leaves no trace
- ✅ Concurrent reads while writer holds exclusive lock
- ✅ Implicit `execute()` WAL ordering verified
- ✅ v2→v3 format migration on checkpoint
- ✅ FaultInjectingBackend write-fail: WAL write error propagates correctly (Wave 3 #209)
- ✅ FaultInjectingBackend flush-fail: flush error propagates without data corruption (Wave 3 #209)
- ✅ FaultInjectingBackend read-fault: read error on replay returns Err (Wave 3 #210)
- ✅ WAL CRC corruption: corrupt entry discarded, replay continues (Wave 3 #210)
- ✅ Checkpoint atomicity: backend write-fail during checkpoint leaves WAL intact (Wave 3 #214)
- ✅ Partial checkpoint recovery: incomplete checkpoint detected and WAL replayed (Wave 3 #214)
- ✅ Multi-writer serialisation: concurrent writers serialise correctly under fault conditions (Wave 3 #217)
- ✅ Concurrent write+checkpoint: no deadlock or data loss under concurrent fault injection (Wave 3 #214)
- ✅ Backend error propagation: storage errors surface as Err, not panic (Wave 3 #209)

### Covering Indexes (`tests/index_test.rs`) - ✅ 6 tests (Phase 6.1)

- ✅ EAVT/AEVT/AVET/VAET save and reload roundtrip
- ✅ Bi-temporal queries still correct after index save/reload
- ✅ Recursive rules regression (indexes don't break rule evaluation)
- ✅ Index checksum mismatch triggers rebuild
- ✅ v3→v4 format migration on first save

### Packed Pages / Performance (`tests/performance_test.rs`) - ✅ 7 tests (Phase 6.2)

- ✅ 1K facts correct after packed save/reload
- ✅ Packed file size < one-per-page estimate (compactness check)
- ✅ Bitemporal query correct after packed reload
- ✅ As-of query correct after packed reload
- ✅ Recursive rules unchanged after Phase 6.2
- ✅ Explicit transaction survives packed reload
-`page_cache_size` option accepted without panic

### Retraction Semantics (`tests/retraction_test.rs`) - ✅ 7 tests (Phase 6.4a)

- ✅ Assert then retract; current-time query returns no results
- ✅ Assert at tx=1, retract at tx=3; `:as-of 2` shows fact, `:as-of 4` hides it
- ✅ Assert, retract, re-assert; current-time query returns fact
- ✅ Retraction + `:any-valid-time` combo
- ✅ Recursive rule: retracted edge not traversed (`:as-of` after retraction)
- ✅ Recursive rule: retracted edge is visible in historical snapshot before retraction
- ✅ Multiple retractions for different entities in same transaction

### Edge Cases (`tests/edge_cases_test.rs`) - ✅ 4 tests (Phase 6.4a)

- ✅ Oversized fact in file-backed database returns `Err`, not panic
- ✅ In-memory database accepts facts of any size (no size limit)
- ✅ Fact at exactly `MAX_FACT_BYTES` is accepted
- ✅ Fact at `MAX_FACT_BYTES + 1` is rejected with clear error message

### B+Tree v6 (`tests/btree_v6_test.rs`) - ✅ 8 tests (Phase 6.5)

- ✅ Single-page B+tree insert and range scan correctness
- ✅ Multi-page B+tree (leaf chain traversal across multiple pages)
- ✅ Range scan with exclusive upper bound
- ✅ Empty range scan returns empty result
- ✅ Concurrent range scans — 8 barrier-synchronised threads all return identical results
- ✅ v5 database opens and migrates to v6 on first checkpoint
- ✅ v6 database survives close/reopen with correct fact count
- ✅ Index lookup via `OnDiskIndexReader` returns correct `FactRef`s

### Negation — `not` (`tests/negation_test.rs`) - ✅ 10 tests (Phase 7.1a)

- ✅ Basic `not` — exclude entities where a pattern matches
-`not` with multi-clause body
-`not` in a rule body (stratification + derived negation)
-`not` with `:as-of` time travel
-`not` with `:valid-at`
- ✅ Negative cycle via `not` at rule registration → `Err`, rule not registered
-`not` where no entities match the body — all outer bindings survive
- ✅ Safety check: unbound variable in `not` body → parse error
- ✅ Nested `not` rejected at parse time
-`not` with `RuleInvocation` in body — derived rule facts correctly negated end-to-end

### Not-Join (`tests/not_join_test.rs`) - ✅ 14 tests (Phase 7.1b)

- ✅ Basic `not-join` — exclude entities where existentially-quantified dependency exists
- ✅ Multiple join variables in `not-join`
- ✅ Multi-clause body with a local variable linking inner patterns
-`not-join` in a rule body
- ✅ Multi-stage filtering chain (two independent `not-join` rules applied progressively)
-`not-join` vs `not` semantic difference (inner-only variable)
-`not-join` with `:as-of` time travel
- ✅ Unbound join variable → parse error naming the variable
- ✅ Nested `not-join` rejected at parse time
-`RuleInvocation` in `not-join` body — derived facts correctly negated end-to-end
- ✅ No-match survival — when no entity satisfies the body, all outer bindings survive
-`not-join` with `:valid-at`
- ✅ Negative cycle via `not-join` at rule registration → `Err`, rule not registered
-`not` and `not-join` coexist in the same query

### Window Functions (`tests/window_functions_test.rs`) - ✅ 12 tests (Phase 7.7a)

- ✅ Cumulative sum over ordered partition
- ✅ Running count and running min
- ✅ Running average
- ✅ Rank with ties (equal values share rank)
- ✅ Row-number (unique sequential position regardless of ties)
- ✅ Partition-by — window resets per group
- ✅ Descending order in window spec
- ✅ Mixed aggregate + window in same `:find`
- ✅ Single-row result (window function on one row)
- ✅ Empty-result edge case (no matching facts)
-`lag` / `lead` rejected at parse time

### User-Defined Functions (`tests/udf_test.rs`) - ✅ 9 tests (Phase 7.7b)

- `custom_aggregate_geometric_mean` — UDF aggregate registered and used in `:find`
-`custom_aggregate_empty_result` — UDF aggregate on empty result set returns correct identity
-`custom_predicate_filter` — UDF predicate in `:where` filters binding rows
-`udf_as_window_function` — UDF aggregate used inside `:over` window spec
-`name_collision_builtin_aggregate` — registering a UDF with a built-in name returns `Err`
-`name_collision_udf_on_udf` — registering a second UDF with the same name returns `Err`
-`unknown_function_runtime_error` — invoking an unregistered aggregate name at query time returns `Err`
-`unknown_predicate_runtime_error` — invoking an unregistered predicate name at query time returns `Err`
-`thread_safety` — concurrent UDF registration and query execution from multiple threads

### Prepared Statements (`tests/prepared_statements_test.rs`) - ✅ 17 tests (Phase 7.8)

- `prepare_and_execute_entity_slot` — entity `$slot` substituted at execute time; correct results returned
-`prepare_and_execute_value_slot` — value `$slot` substituted at execute time; correct filtering
-`prepare_and_execute_as_of_tx_count``:as-of $tx` with `TxCount` variant; time-travel query returns correct snapshot
-`prepare_and_execute_as_of_timestamp``:as-of $tx` with `Timestamp` variant (millis)
-`prepare_and_execute_valid_at_timestamp``:valid-at $date` with `Timestamp` variant
-`prepare_and_execute_valid_at_any``:valid-at $va` with `AnyValidTime` variant; all time-windows returned
-`prepare_and_execute_combined_temporal_and_entity``:as-of $tx` + entity `$slot` simultaneously (primary agentic loop pattern)
-`plan_is_reused_across_executions` — same `PreparedQuery` executed twice with different bindings; both correct
-`error_missing_bind_value` — missing `$slot` at execute time returns `Err`
-`error_wrong_type_for_as_of``Val` supplied for `:as-of` slot returns type-mismatch `Err`
-`error_wrong_type_for_valid_at``TxCount` supplied for `:valid-at` slot returns type-mismatch `Err`
-`error_wrong_type_for_entity``Val` supplied for entity slot returns type-mismatch `Err`
-`error_attribute_slot_rejected``$slot` in attribute position rejected at prepare time
-`prepare_with_no_slots` — static query prepared and executed correctly (no bindings needed)
-`prepare_transact_rejected` — preparing a `(transact ...)` command returns `Err`
-`execute_with_extra_bindings` — extra `BindValue`s beyond declared slots are silently ignored
-`multiple_slots_same_execute` — multiple distinct `$slot` names resolved in a single `execute()` call

### Migration Matrix (`tests/migration_matrix_test.rs`) - ✅ 5 tests (Wave 3 #215)

- ✅ v7 round-trip — facts written and read back correctly after save/load
- ✅ v3 empty migrate — empty v3 database opens and migrates to v7 cleanly
- ✅ corrupt magic — file with bad magic header returns `Err`, not panic
- ✅ unsupported version — file with unrecognised format version returns `Err`
- ✅ WAL replay idempotent — replaying a WAL twice produces the same result as replaying once

### Index Corruption (`tests/index_corruption_test.rs`) - ✅ 5 tests (Wave 3 #216)

- ✅ checksum corruption — database with corrupted index checksum rebuilds index and serves correct query
- ✅ btree leaf no-panic — corrupt btree leaf page returns `Err` without panic
- ✅ btree internal no-panic — corrupt btree internal page returns `Err` without panic
- ✅ root pointer mismatch no-panic — mismatched root pointer in header handled without panic
- ✅ non-critical corruption query check — database with non-critical corruption still serves queries on good data

### Property-Based Tests (`tests/property_test.rs`) - ✅ 3 tests (Wave 3 #212/#213/#219)

- `prop_eav_model` — arbitrary EAV fact sets stored and retrieved correctly (proptest)
-`prop_bitemporal_monotonicity` — tx-time advances monotonically across arbitrary transactions (proptest)
-`prop_retract_visibility` — retracted facts are invisible in current view and visible in pre-retraction `:as-of` snapshot (proptest)

### Long-Haul Smoke (`tests/smoke_test.rs`) - ✅ 1 test (Wave 3 #220, `#[ignore]` nightly)

- `smoke_large_graph_10_cycles` — 500 entities × 10 attributes × 10 update cycles; 7 invariants verified: active count (333), retracted count, fact count bounds, temporal snapshot integrity, prepared query consistency, rule transitive closure, WAL checkpoint round-trip

### XTDB Compatibility (`tests/xtdb_compat_test.rs`) - ✅ 10 tests (Wave 3 #221)

- `xtdb_eav_triple_model` — entity attributes are independent queryable facts
-`xtdb_transaction_time_as_of``:as-of` by tx_count matches XTDB transaction-time semantics
-`xtdb_valid_time_travel``:valid-at` point-in-time filter matches XTDB valid-time semantics
-`xtdb_retraction_current_view` — retracted facts absent from current-time view
-`xtdb_retraction_historical_view` — retracted facts visible in pre-retraction snapshot
-`xtdb_datalog_join` — multi-pattern join matches XTDB Datalog query semantics
-`xtdb_datalog_negation``not` clause matches XTDB negation semantics
-`xtdb_recursive_rules` — recursive rule transitive closure matches XTDB rule semantics
-`xtdb_parameterized_query` — prepared-statement `$slot` bindings match XTDB `:in` semantics
-`xtdb_bitemporal_combined` — combined `:as-of` + `:valid-at` query matches XTDB bi-temporal semantics

### Datomic Compatibility (`tests/datomic_compat_test.rs`) - ✅ 9 tests (Wave 3 #221)

- `datomic_entity_attributes_are_independent_facts` — EAV datom model: each attribute independently queryable
-`datomic_multiple_entities_same_attribute` — multiple entities share the same attribute; all (entity, value) pairs returned
-`datomic_transaction_time_as_of``:as-of tx_count` matches Datomic transaction-time semantics
-`datomic_retract_all_entity_facts` — fully-retracted entity absent from all queries
-`datomic_multi_variable_find` — multi-variable `:find` returns correct tuple count
-`datomic_ground_value_binding` — constant binding in `:where` clause filters correctly
-`datomic_parameterized_query_prepared` — prepared `$slot` bindings match Datomic `:in` clause semantics
-`datomic_named_rule_reuse` — named reusable rules match Datomic rule semantics
-`datomic_predicate_expression_filter` — predicate expression `[(>= ?a 18)]` matches Datomic expression clause semantics

---

## Coverage Metrics

**Overall Code Coverage**: ~94% (estimate)

**By Category**:
- ✅ Happy path: ~98%
- ✅ Core Datalog operations: ~95%
- ✅ Recursive rules: ~95%
- ✅ Bi-temporal queries: ~95%
- ✅ WAL and crash recovery: ~94%
- ✅ Transaction API: ~93%
- ✅ Covering indexes: ~94%
- ✅ Packed pages + LRU cache: ~91%
- ✅ Error handling: ~84% (raised from ~82% via edge case tests)
- ✅ Edge cases: ~90% (raised from ~87% via edge case + retraction tests)
- ✅ Concurrency: ~92%
- ✅ Performance benchmarks: Criterion suite run at 1K–1M facts; documented in `BENCHMARKS.md` (Phase 6.4b)

---

## What's Thoroughly Tested ✅

### Phase 3 Core Features
1. Datalog Core — Transact, retract, query
2. Pattern Matching — Variable unification, multi-pattern joins
3. Fact Storage — EAV model, history, retractions
4. EDN Parsing — All Datalog syntax variations
5. Storage Backends — File and memory persistence
6. Recursive Rules — Semi-naive evaluation, fixed-point iteration
7. Transitive Closure — Multi-hop reachability
8. Cycle Handling — Graphs with cycles converge correctly
9. Complex Queries — 3+ patterns, self-joins, entity references
10. Concurrency — Thread-safe rule registration and querying

### Phase 4 Bi-temporal Features
11. Transaction Time — `tx_count` increments, `get_facts_as_of()` snapshots
12. Valid Time — `valid_from`/`valid_to` filtering, boundary semantics
13. Time Travel Queries — `:as-of` counter and timestamp
14. Valid-at Queries — Point-in-time filter, `:any-valid-time`
15. Combined Bi-temporal — Both dimensions in one query
16. Transact with Valid Time — Batch-level and per-fact overrides
17. File Format Migration — v1→v2 with correct temporal defaults

### Phase 5 ACID + WAL Features
18. WAL Format — CRC32-protected entries, partial-write discard
19. Crash Recovery — WAL replay on open, dedup via `last_checkpointed_tx_count`
20. Explicit Transactions — `begin_write` / `commit` / `rollback`
21. WAL Ordering — WAL fsynced before in-memory apply (both implicit and explicit paths)
22. Checkpoint — WAL flushed to `.graph`, WAL deleted, header updated
23. Auto-checkpoint — Fires at configurable WAL entry threshold
24. Thread Safety — Concurrent readers + exclusive writer verified with Barrier

### Phase 6.1 Index Features
25. EAVT/AEVT/AVET/VAET — Four covering indexes with bi-temporal keys
26. FactRef — Disk location pointer, slot_index always 0 in 6.1
27. Value Encoding — Sort-order-preserving canonical encoding
28. B+tree Persistence — Multi-page blob strategy, sort order preserved
29. FileHeader v4 — Index root pages, CRC32 checksum
30. Index Rebuild — Triggered by checksum mismatch on open
31. Query Optimizer — Index hint selection, join reordering by selectivity

### Phase 6.2 Packed Page + Cache Features
32. Packed Pages — ~25 facts/page, header + directory + records layout
33. FactRef Semantics — `page_id=0` = pending, `page_id>=1` = committed via cache
34. CommittedFactReader — Trait + impl wired in PersistentFactStorage::load()
35. LRU Page Cache — Read-lock on hits, Arc cloning, eviction correctness
36. v4→v5 Migration — Reads one-per-page, repacks, saves with new format
37. EAVT/AEVT Range Scans — O(log n) entity and attribute lookups

### Phase 6.4a Retraction Semantics + Edge Cases
38. Retraction Net View — `net_asserted_facts()` groups by EAV triple, keeps highest `tx_count`
39. Current-Time Retraction — Retracted fact absent from query results with no `:as-of`
40. As-Of Retraction — Retraction visible/invisible at correct tx boundary
41. Re-Assert After Retract — Fact reappears when re-asserted
42. Retraction in Recursive Rules — Retracted edges not traversed in rule derivation
43. Oversized-Fact Early Validation — `check_fact_sizes()` rejects before WAL write
44. `MAX_FACT_BYTES` Boundary — Exact-size accepted, +1 rejected with clear error

### Phase 6.4b Byte-Layout Pins
45. FileHeader v5 Field Offsets — All 10 fields pinned at exact byte positions (big-endian detection coverage)
46. Packed Page Header Layout — page_type, reserved, record_count u16 LE, next_page u64 LE at bytes 0–11
47. Packed Page Record Directory — (offset u16 LE, length u16 LE) per slot, starting at byte 12

### Phase 6.5 On-Disk B+Tree Indexes
48. B+Tree Build + Range Scan — `build_btree` inserts and `range_scan` retrieves with correct ordering
49. Multi-Page Leaf Chain — range scan correctly follows `next_leaf` pointers across page boundaries
50. Concurrent Range Scans — 8 barrier-synchronised threads, all return identical non-empty results
51. v5→v6 Migration — database opened from v5 format migrates to v6 on first checkpoint
52. `OnDiskIndexReader` FactRef Lookup — committed facts resolved correctly via page cache
53. `MutexStorageBackend` — cache-warm pages acquire no backend lock; cache-cold pages lock briefly

### Phase 7.1 Stratified Negation
54. `not` — basic absence query excludes entities where pattern matches
55. `not` in rule body — stratified mixed-rule evaluation applies negation per binding
56. `not-join` — existentially-quantified exclusion with explicit join variables
57. `not-join` multi-clause body — inner variables link patterns without escaping to outer scope
58. `not-join` in rule body — negation inside derived rules
59. Negative cycle rejection — `not` / `not-join` creating a dependency cycle → `Err` at registration, rule not added
60. Safety validation — unbound variables in `not` body or `join_vars` → parse error with variable name
61. Nesting constraint — `not-join` inside `not` or `not-join` → parse error
62. `RuleInvocation` in `not-join` body — derived facts in accumulated store correctly negated
63. Time travel with negation — `not-join` respects `:as-of` and `:valid-at` temporal filters
64. `not` and `not-join` coexistence in the same query

---

## What's Not Tested Yet ⏳

### Phase 7.3+ (Remaining Datalog Completeness)
- ⏳ Disjunction (`or` / `or-join`) — Phase 7.3
- ⏳ Query optimizer improvements for new clause types (aggregation, expr, disjunction) — Phase 7.4
- ⏳ Prepared statements with temporal bind slots — Phase 7.6
- ⏳ Temporal metadata pseudo-attributes (`:db/valid-from`, `:db/valid-to`, `:db/tx-count`) — Phase 7.7

### Known Limitations (Acceptable for Phase 3-7.2b)
- ⏳ Crash during checkpoint write (safe by construction — WAL not deleted until save succeeds; explicit test deferred to Phase 7.5)
- ⏳ Disjunction — Phase 7.3
- ⏳ Known `not-join` limitation: when a rule B positively invokes rule A and both are stratum 0, single-pass mixed-rule evaluation means B may not see A's derived facts unless rules are declared in dependency order
-`matches?` pattern compiled per-row (no caching); will be optimised in Phase 7.9b (`FunctionRegistry`)

---

## Test Execution

```bash
# Run all tests
cargo test

# Run tests quietly with summary
cargo test --quiet

# Run specific test suites
cargo test --lib                       # Unit tests (216)
cargo test --test bitemporal           # Bi-temporal (10)
cargo test --test complex_queries      # Complex queries (10)
cargo test --test recursive_rules      # Recursive rules (9)
cargo test --test concurrency          # Concurrency (12, 1 ignored)
cargo test --test wal_test             # WAL / crash recovery (21)
cargo test --test index_test           # Covering indexes (6)
cargo test --test performance_test     # Packed pages (7)
cargo test --test retraction_test      # Retraction semantics (7)
cargo test --test edge_cases_test      # Edge cases (4)
cargo test --test btree_v6_test        # B+tree v6 (8)
cargo test --test negation_test        # stratified not (10)
cargo test --test not_join_test        # not-join (14)
cargo test --test aggregation_test     # aggregation (24)
cargo test --test predicate_expr_test  # arithmetic & predicate expr (28)
cargo test --test window_functions_test # window functions (12)
cargo test --test udf_test             # user-defined functions (9)
cargo test --test prepared_statements_test # prepared statements (17)
cargo test --test migration_matrix_test    # migration matrix (5)
cargo test --test index_corruption_test    # index corruption (5)
cargo test --test property_test            # property-based (3)
cargo test --test xtdb_compat_test         # XTDB compat (10)
cargo test --test datomic_compat_test      # Datomic compat (9)
cargo test --test smoke_test -- --include-ignored  # long-haul smoke (1, nightly)

# Run with output
cargo test -- --nocapture
```

---

## Conclusion

**Wave 3 Status**: ✅ **COMPLETE**

**Test Quality**: ✅ **Excellent** — High confidence in all Phase 3-8.1 + Wave 3 reliability features

**Strengths**:
- WAL crash safety verified with real `mem::forget` simulation
- Both implicit and explicit transaction write paths verified
- Thread safety proven with Barrier-synchronized concurrent tests
- Index persistence and CRC32 sync check verified
- Packed page compactness verified against one-per-page estimate
- CommittedFactReader wiring verified with MockLoader in unit tests
- Retraction semantics verified across current-time, as-of, and recursive-rule queries
- Oversized-fact early rejection verified for file-backed databases
- Criterion benchmarks validated performance at 1K–1M facts
- Byte-layout tests pin FileHeader v5/v6 and packed page header field offsets
- On-disk B+tree correctness and concurrent scan safety verified (Phase 6.5)
- Stratified negation (`not` / `not-join`) verified: safety validation, stratification, negative cycle rejection, time-travel integration (Phase 7.1)
- Aggregation verified: all 6 aggregate functions, `:with` grouping, bi-temporal + aggregate, rule + aggregate (Phase 7.2a)
- Arithmetic & predicate expressions verified: all operators, silent-drop semantics, int/float promotion, regex validation, expr in not/rule body, bi-temporal + expr (Phase 7.2b)
- Disjunction (`or` / `or-join`) verified: flat queries, rule bodies, nested or/not/expr, or-join with private variables, dependency graph (Phase 7.3)
- Window functions verified: cumulative aggregates, rank/row-number, partition-by, desc ordering, mixed aggregate+window (Phase 7.7a)
- User-defined functions verified: custom aggregates, custom predicates, UDF as window function, name collision guards, runtime error handling, thread safety (Phase 7.7b)
- Prepared statements verified: entity/value/as-of/valid-at slot positions, AnyValidTime, combined temporal+entity (agentic loop pattern), plan reuse, all error paths (Phase 7.8)
- Public API surface verified via rustdoc doctests: `Minigraf::open`, `execute`, `prepare`, `repl`, `WriteTransaction`, `OpenOptions` (Phase 7.9)
- WAL fault injection verified: write-fail, flush-fail, read-fault, CRC corruption, checkpoint atomicity, concurrent write+checkpoint (Wave 3)
- Migration matrix verified: v7 round-trip, v3 empty migrate, corrupt magic, unsupported version, WAL replay idempotent (Wave 3)
- Index corruption resilience verified: checksum corruption triggers rebuild, btree corruption returns Err not panic (Wave 3)
- Property-based testing verified: EAV model, bi-temporal monotonicity, retract visibility (Wave 3)
- Long-haul smoke verified: 500 entities × 10 attrs × 10 cycles, 7 invariants, nightly CI (Wave 3)
- XTDB compatibility verified: 10 semantic ports covering EAV, time travel, negation, rules, prepared queries (Wave 3)
- Datomic compatibility verified: 9 independently written semantic ports covering datom model, tx-time, retraction, Datalog patterns (Wave 3)
- 935 tests covering all Phase 3-8.1 features + Wave 3 reliability/compat (including browser WASM + WASI + cross-platform compat + fuzzing CI)

**Confidence Level**: ✅ **Production-ready for Wave 3 scope**

**Readiness for Phase 9**: ✅ **Ready to proceed**

The fault-injection-tested, property-based-tested, XTDB/Datomic-compatible, fuzz-hardened, WebAssembly-capable (browser + WASI), publish-ready, prepared-statement-capable, UDF-capable, window-function-capable, disjunction + aggregation + arithmetic/predicate expression capable, stratified-negation-capable, on-disk B+tree indexed, packed, cached bi-temporal Datalog engine is **solid, well-tested, documented, and benchmarked**.

---

**Next Steps**: Phase 9 (Ecosystem & Tooling — examples, wiki guides, performance baseline updates)