Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,16 @@ Push `Expr` predicate clauses (e.g. `[(> ?age 30)]`) down to filter bindings as
- Migration tools for any format changes
- Long-term support

### v2.0 — File Format Policy

**v2.0 supports file format v7 only.** Support for v1–v6 is dropped. The v1–v6 → v7 auto-migration code in `persistent_facts.rs` can be removed when cutting 2.0. Any database that was opened at least once under a v1.x release will already be on v7; there are no known users on older formats.

v2.0 scope (GitHub milestone "2.0"):
- `lag`/`lead` window functions (#182)
- Sliding row frames — `:rows N preceding` (#183)
- PreparedQuery over UniFFI (#181)
- UDF registration over UniFFI (#180)

---

## Decision Framework
Expand Down Expand Up @@ -1664,6 +1674,7 @@ When evaluating features, ask:
- ✅ Wave 1 Performance: Complete (May 2026) — hash-join cluster + selective B+Tree lookup (#202, #203, #204, #208), 850 tests
- ✅ Wave 2 Optimizer & Benchmarks: Complete (May 2026) — predicate push-down (#207, #206), cost-based not/or ordering (#205), SIMD crossover analysis (#229), 850 tests
- ✅ Wave 3 Reliability: Complete (May 2026) — WAL fault injection, migration matrix, index corruption resilience, property-based testing, coverage gates, long-haul smoke, XTDB/Datomic compat (#209, #210, #212, #213, #214, #215, #216, #217, #219, #220, #221), 935 tests
- ✅ Wave 4 Deferred Features: Complete (May 2026) — #182, #183, #180, #181 tagged milestone 2.0; #187 closed (no pre-1.0 users); #201 deferred to Wave 6
- 🎯 Phase 9: Ongoing (Ecosystem — integration examples, cookbook, GraphRAG/LangChain examples)

**Note**: This is a hobby project. Timeline is flexible but realistic.
Expand Down Expand Up @@ -1699,4 +1710,4 @@ See [GitHub Issues](https://github.com/project-minigraf/minigraf/issues) for spe

---

**Last Updated**: Wave 3 Reliability Complete (May 2026) — 935 tests passing, v1.0.0
**Last Updated**: Wave 4 Complete (May 2026) — 935 tests passing, v1.0.0; milestone 2.0 issues queued; #231 repo-split is next gate
24 changes: 12 additions & 12 deletions docs/superpowers/plans/2026-05-16-wave3-pr4-query-correctness.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add proptest-based cross-feature property tests with a reference evaluator (#221), Datalog parser/evaluator fuzz targets with seed corpus (#213), and CI coverage gates (#212).
**Goal:** Add proptest-based cross-feature property tests with a reference evaluator (#212), Datalog parser/evaluator fuzz targets with seed corpus (#213), and CI coverage gates (#219).

**Architecture:** The reference evaluator is a naive nested-loop implementation (~150 lines) intentionally independent of production code. proptest generates random small graphs and queries; both evaluators are compared after normalizing output. Fuzz targets replace the stubs from PR 1. Coverage gates use the existing `llvm-cov.yml` pattern with per-module thresholds in `coverage-thresholds.toml`.

**Tech Stack:** Rust stable for property tests; Rust nightly for fuzz runs; proptest 1.x (added in PR 1); cargo-llvm-cov (already in CI); GitHub Actions

**Prerequisites:** PR 1 merged (proptest dev-dep, fuzz/ stub targets)

**Closes:** #221, #213, #212
**Closes:** #212, #213, #219

---

## File Map

| Action | Path | Purpose |
|---|---|---|
| Create | `tests/property_test.rs` | proptest reference evaluator (#221) |
| Create | `tests/property_test.rs` | proptest reference evaluator (#212) |
| Modify | `fuzz/fuzz_targets/datalog_parser.rs` | real fuzz target (#213) |
| Modify | `fuzz/fuzz_targets/datalog_eval.rs` | real fuzz target (#213) |
| Create | `fuzz/corpus/datalog_parser/` | seed corpus (#213) |
| Create | `fuzz/corpus/datalog_eval/` | seed corpus (#213) |
| Create | `.github/workflows/coverage-gates.yml` | per-module coverage CI (#212) |
| Create | `coverage-thresholds.toml` | threshold values (#212) |
| Create | `.github/workflows/coverage-gates.yml` | per-module coverage CI (#219) |
| Create | `coverage-thresholds.toml` | threshold values (#219) |
| Modify | `.github/workflows/fuzz.yml` | add Datalog fuzz targets if file exists, else create |

---

## Task 1: proptest reference evaluator (#221)
## Task 1: proptest reference evaluator (#212)

**Files:**
- Create: `tests/property_test.rs`
Expand All @@ -41,7 +41,7 @@ The reference evaluator handles only the small query shapes generated by the tes
Create `tests/property_test.rs`:

```rust
//! Cross-feature property tests (#221).
//! Cross-feature property tests (#212).
//!
//! Compares Minigraf query results against a deliberately simple reference
//! evaluator for randomly-generated small graphs and queries.
Expand Down Expand Up @@ -322,7 +322,7 @@ Expected: all 3 proptest cases pass. If any fails, the output includes the minim

```bash
git add tests/property_test.rs
git commit -m "test(query): add #221 proptest reference evaluator for cross-feature property tests"
git commit -m "test(query): add #212 proptest reference evaluator for cross-feature property tests"
```

---
Expand Down Expand Up @@ -547,7 +547,7 @@ git commit -m "test(fuzz): add #213 Datalog parser/eval fuzz targets with seed c

---

## Task 3: Coverage gates (#212)
## Task 3: Coverage gates (#219)

**Files:**
- Create: `coverage-thresholds.toml`
Expand Down Expand Up @@ -704,7 +704,7 @@ Expected: all tests pass.

```bash
git add coverage-thresholds.toml .github/workflows/coverage-gates.yml
git commit -m "ci: add #212 per-module coverage gates with cargo-llvm-cov"
git commit -m "ci: add #219 per-module coverage gates with cargo-llvm-cov"
```

---
Expand All @@ -716,11 +716,11 @@ git commit -m "ci: add #212 per-module coverage gates with cargo-llvm-cov"
```bash
git push -u origin HEAD
gh pr create \
--title "test(query): proptest reference evaluator, Datalog fuzz targets, coverage gates (#221, #213, #212)" \
--title "test(query): proptest reference evaluator, Datalog fuzz targets, coverage gates (#212, #213, #219)" \
--body "$(cat <<'EOF'
## Wave 3 PR 4 — Query Correctness

Closes #221, #213, #212.
Closes #212, #213, #219.

### #221 — proptest reference evaluator (`tests/property_test.rs`)
- Naive reference evaluator (independent of production code)
Expand Down
20 changes: 10 additions & 10 deletions docs/superpowers/plans/2026-05-16-wave3-pr6-compat.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add XTDB and Datomic compatibility test suites (#219) by porting semantically equivalent test cases from each corpus, with license review documented inline.
**Goal:** Add XTDB and Datomic compatibility test suites (#221) by porting semantically equivalent test cases from each corpus, with license review documented inline.

**Architecture:** Two new test files — `tests/xtdb_compat_test.rs` and `tests/datomic_compat_test.rs`. Each test case is a semantic port of a known test from the source corpus. XTDB (Apache 2.0) allows direct semantic porting; Datomic test material is restricted so all cases are independently rewritten from the semantic intent. Unsupported or intentionally divergent cases are listed with rationale.

**Tech Stack:** Rust stable, Minigraf's Datalog engine

**Prerequisites:** PRs 2, 3, 4, 5 all merged

**Closes:** #219
**Closes:** #221

---

Expand Down Expand Up @@ -58,7 +58,7 @@ If the XTDB repo is inaccessible, the tests below are independently written sema
Create `tests/xtdb_compat_test.rs`:

```rust
//! XTDB compatibility tests (#219).
//! XTDB compatibility tests (#221).
//!
//! License: XTDB is Apache 2.0. These tests are semantic ports of query
//! concepts from the XTDB documentation and test suite. Each test is
Expand Down Expand Up @@ -338,7 +338,7 @@ Expected: all tests pass. If any fail due to syntax differences (e.g., `:valid-a

```bash
git add tests/xtdb_compat_test.rs
git commit -m "test(compat): add XTDB compatibility tests (#219)"
git commit -m "test(compat): add XTDB compatibility tests (#221)"
```

---
Expand All @@ -355,7 +355,7 @@ All Datomic-inspired tests are independently written semantic ports — they tes
Create `tests/datomic_compat_test.rs`:

```rust
//! Datomic-inspired compatibility tests (#219).
//! Datomic-inspired compatibility tests (#221).
//!
//! License notice: Datomic is a commercial product. These tests are
//! INDEPENDENTLY WRITTEN semantic ports — they test concepts from Datomic's
Expand Down Expand Up @@ -630,7 +630,7 @@ Expected: all tests pass.

```bash
git add tests/datomic_compat_test.rs
git commit -m "test(compat): add Datomic-inspired compatibility tests (#219)"
git commit -m "test(compat): add Datomic-inspired compatibility tests (#221)"
```

---
Expand All @@ -642,11 +642,11 @@ git commit -m "test(compat): add Datomic-inspired compatibility tests (#219)"
```bash
git push -u origin HEAD
gh pr create \
--title "test(compat): XTDB and Datomic compatibility test corpus (#219)" \
--title "test(compat): XTDB and Datomic compatibility test corpus (#221)" \
--body "$(cat <<'EOF'
## Wave 3 PR 6 — Compat Gate

Closes #219.
Closes #221.

### XTDB (`tests/xtdb_compat_test.rs`)
- License: Apache 2.0 — semantic ports permitted
Expand Down Expand Up @@ -678,8 +678,8 @@ After this PR merges, all 11 Wave 3 issues are closed:
- ✅ #215 Migration matrix fixtures
- ✅ #216 Index corruption recovery
- ✅ #217 Concurrency stress tests
- ✅ #219 XTDB/Datomic compat corpus
- ✅ #221 XTDB/Datomic compat corpus
- ✅ #220 Long-haul smoke suite
- ✅ #221 Cross-feature property tests
- ✅ #212 Cross-feature property tests

Update `CLAUDE.md` test count and `CHANGELOG.md` with Wave 3 completion after all PRs merge.
8 changes: 4 additions & 4 deletions minigraf-c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,11 @@ fn value_to_json(v: &Value) -> serde_json::Value {

fn query_result_to_json(result: QueryResult) -> String {
let val = match result {
QueryResult::Transacted { tx_id, tx_count } => {
serde_json::json!({"transacted": tx_id, "tx_count": tx_count})
QueryResult::Transacted(tx_id) => {
serde_json::json!({"transacted": tx_id})
}
QueryResult::Retracted { tx_id, tx_count } => {
serde_json::json!({"retracted": tx_id, "tx_count": tx_count})
QueryResult::Retracted(tx_id) => {
serde_json::json!({"retracted": tx_id})
}
QueryResult::Ok => serde_json::json!({"ok": true}),
QueryResult::QueryResults { vars, results } => {
Expand Down
20 changes: 6 additions & 14 deletions minigraf-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ fn value_to_json(v: &Value) -> serde_json::Value {
fn query_result_to_json(result: QueryResult) -> String {
use serde_json::json;
let val = match result {
QueryResult::Transacted { tx_id, tx_count } => {
json!({"transacted": tx_id, "tx_count": tx_count})
QueryResult::Transacted(tx_id) => {
json!({"transacted": tx_id})
}
QueryResult::Retracted { tx_id, tx_count } => {
json!({"retracted": tx_id, "tx_count": tx_count})
QueryResult::Retracted(tx_id) => {
json!({"retracted": tx_id})
}
QueryResult::Ok => json!({"ok": true}),
QueryResult::QueryResults { vars, results } => {
Expand Down Expand Up @@ -166,13 +166,9 @@ mod tests {

#[test]
fn query_result_to_json_transacted() {
let json = query_result_to_json(QueryResult::Transacted {
tx_id: 12345,
tx_count: 1,
});
let json = query_result_to_json(QueryResult::Transacted(12345));
let v: serde_json::Value = serde_json::from_str(&json).expect("valid json");
assert_eq!(v["transacted"], serde_json::json!(12345));
assert_eq!(v["tx_count"], serde_json::json!(1));
}

#[test]
Expand Down Expand Up @@ -239,13 +235,9 @@ mod tests {

#[test]
fn query_result_to_json_retracted() {
let json = query_result_to_json(QueryResult::Retracted {
tx_id: 99,
tx_count: 2,
});
let json = query_result_to_json(QueryResult::Retracted(99));
let v: serde_json::Value = serde_json::from_str(&json).expect("valid json");
assert_eq!(v["retracted"], serde_json::json!(99));
assert_eq!(v["tx_count"], serde_json::json!(2));
}

#[test]
Expand Down
8 changes: 4 additions & 4 deletions minigraf-node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ fn value_to_json(v: &Value) -> serde_json::Value {

fn query_result_to_json(result: QueryResult) -> String {
let val = match result {
QueryResult::Transacted { tx_id, tx_count } => {
serde_json::json!({"transacted": tx_id, "tx_count": tx_count})
QueryResult::Transacted(tx_id) => {
serde_json::json!({"transacted": tx_id})
}
QueryResult::Retracted { tx_id, tx_count } => {
serde_json::json!({"retracted": tx_id, "tx_count": tx_count})
QueryResult::Retracted(tx_id) => {
serde_json::json!({"retracted": tx_id})
}
QueryResult::Ok => serde_json::json!({"ok": true}),
QueryResult::QueryResults { vars, results } => {
Expand Down
8 changes: 4 additions & 4 deletions src/browser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,11 @@ fn query_result_to_json(result: QueryResult) -> String {
use serde_json::{Value as JVal, json};

let val: JVal = match result {
QueryResult::Transacted { tx_id, tx_count } => {
json!({"transacted": tx_id, "tx_count": tx_count})
QueryResult::Transacted(tx_id) => {
json!({"transacted": tx_id})
}
QueryResult::Retracted { tx_id, tx_count } => {
json!({"retracted": tx_id, "tx_count": tx_count})
QueryResult::Retracted(tx_id) => {
json!({"retracted": tx_id})
}
QueryResult::Ok => json!({"ok": true}),
QueryResult::QueryResults { vars, results } => {
Expand Down
16 changes: 14 additions & 2 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,9 @@ impl Minigraf {

// Return the same QueryResult the executor would have returned.
if is_retract {
Ok(QueryResult::Retracted { tx_id, tx_count })
Ok(QueryResult::Retracted(tx_id))
} else {
Ok(QueryResult::Transacted { tx_id, tx_count })
Ok(QueryResult::Transacted(tx_id))
}
} else {
// Read-only: no lock needed
Expand Down Expand Up @@ -575,6 +575,18 @@ impl Minigraf {
Self::do_checkpoint(&self.inner.fact_storage, &mut ctx)
}

/// Returns the current monotonic transaction counter.
///
/// This is the value that `:as-of N` compares against. After a successful
/// [`execute`](Self::execute) that returns [`QueryResult::Transacted`] or
/// [`QueryResult::Retracted`], this reflects the count of that transaction.
///
/// Starts at 0 for a new database and increments once per `transact`/`retract` call,
/// regardless of how many facts the batch contains.
pub fn current_tx_count(&self) -> u64 {
self.inner.fact_storage.current_tx_count()
}

/// Internal checkpoint logic (operates on an already-held write-lock guard).
fn do_checkpoint(_fact_storage: &FactStorage, ctx: &mut WriteContext) -> Result<()> {
match ctx {
Expand Down
Loading
Loading