grit-core 0.2.2

Embedded, bi-temporal property graph for agent memory: one SQLite file, in-process, deterministic
Documentation
# grit

[![CI](https://github.com/bofeizhu/grit/actions/workflows/ci.yml/badge.svg)](https://github.com/bofeizhu/grit/actions/workflows/ci.yml)
[![crates.io](https://img.shields.io/crates/v/grit-core)](https://crates.io/crates/grit-core)
[![docs.rs](https://img.shields.io/docsrs/grit-core)](https://docs.rs/grit-core)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)

An embedded, **bi-temporal property graph for agent memory**. One SQLite
file, in-process, written in Rust. No server, no daemon, no network —
compiles for macOS, Windows, Linux, iOS, and Android.

grit is the piece of grit at the center of the
[pearl](https://github.com/bofeizhu/nacre#why-this-exists) stack — Layer 1,
doing deterministic graph **storage and hybrid retrieval** only. The LLM
extraction pipeline that deposits memory around it is
[nacre](https://github.com/bofeizhu/nacre) (Layer 2); the agent app is
Layer 3. The full design contract — invariants, scope limits, testing bar —
is [AGENTS.md](AGENTS.md), and it is binding for anyone (human or agent)
working here.

```rust
use grit_core::{Budget, GraphOp, Grit, Options, Query, Traversal};

let g = Grit::open("memory.db", Options::new("laptop"))?;
g.apply(GraphOp::AddEpisode { /* provenance: a chat turn, a doc chunk … */ })?;
g.apply(GraphOp::AddEdge { /* a fact, with event-time validity … */ })?;

// Hybrid recall: BM25 + vectors + graph expansion, RRF-fused, budgeted.
let hits = g.search(Query::text("exactness").group("algebra").budget(Budget::items(20)))?;

// Time travel is a query, not archaeology.
let then = g.traverse(&[node_id], &Traversal::default().as_of(march).as_at(march))?;
let past = g.node_history(node_id)?;             // every edge ever believed
```

## Why on SQLite

The embedded-graph-database niche is a graveyard: the ideal engine is
archived, others are dormant, server-based, or license-encumbered — and
none of it runs inside an iOS app. Meanwhile the graph semantics agent
memory needs are a **recipe, not a product**. So grit is a deliberately
thin layer (~a few kLOC) on the one storage engine that is in-process,
iOS-native, public domain, and older than most databases. SQLite carries
the dangerous parts — durability, crash recovery, decades of hardening —
plus FTS5 and [sqlite-vec](https://github.com/asg017/sqlite-vec),
statically linked.

## The model

**Every mutation is a `GraphOp`**, appended to an op-log; graph tables are
derived state in the same transaction. The vocabulary is closed and small —
it is also the future sync unit:

| Op | Semantics |
|---|---|
| `AddNode` | create an entity (id-collision: lowest HLC wins, deterministically) |
| `AddEdge` | create a **fat edge** — the fact sentence lives on it, with event-time `valid_at`/`invalid_at` |
| `AddEpisode` | record raw provenance and its mentions — every fact traces to sources |
| `UpdateNode` | revise entity metadata (name/summary/kind/attrs) — per-field last-writer-wins registers; history stays in the op-log |
| `InvalidateEdge` | close a fact's event-time interval; concurrent invalidations converge to the earliest |
| `MergeNodes` | execute a dedup decision (Layer 2 decides *whether*; cycle-safe canonical resolution) |
| `Purge` | the only destructive op — exact-id right-to-forget, tombstoned, audited |

**Bi-temporal throughout.** Edges carry event time (when a fact was true in
the world) and system time (when it was believed). Invalidations are
belief-versioned, so *"back in March, did we think this job still held?"*
is `as_of`/`as_at` on an ordinary traversal.

**Sync-ready before sync exists.** UUIDv7 keys, hybrid logical clocks, and
op application that is idempotent and commutative for concurrent ops —
property-tested across adversarial interleavings (merge cycles, purges
racing adds, updates arriving before their nodes, out-of-order
invalidations). Embeddings stay out of the op-log: they're recomputable
local state tagged with a model identity, stored and served back through
typed getters/setters.

**Data outlives the library.** Lossless JSONL export/import of the full
graph + op-log is a permanent compatibility surface; schema migrations are
forward-only and tested against frozen fixture databases from every
released version.

## Proven by an oracle

grit's API is exercised by more than unit tests:
[nacre](https://github.com/bofeizhu/nacre)'s golden-trace conformance
replays recorded LLM/embedder responses through the full Rust stack and
diffs the resulting grit graph against **pinned Python Graphiti's** frozen
output — byte-equal facts, timestamps, attributions. Several grit 0.2
capabilities (`UpdateNode`, `AddEdge.invalid_at`, embedding getters, group
scans) exist because that oracle demanded them.

## Performance envelope

Scale target: **≤100k nodes / ≤1M edges** — years of a person's learned
knowledge, not a fleet's raw perception (extract first; that's the whole
architecture). Measured at v0.1: traversal (3 hops, validity-filtered,
256-node budget) holds its ≤10 ms p95 target with ≥2× margin at 100k
nodes / 300k edges; on a deliberately adversarial 1M-edge uniform-random
fixture it measures ~17 ms warm p95, enforced by a CI latency tripwire.
Criterion benches fail CI on >20 % regression.

## Non-goals

No server mode, wire protocol, or networking. No general Cypher (a parked,
fail-loud translator stub exists in `grit-compat`). No graph algorithms
(PageRank, community detection) without a proven need. No multi-process
access — one process, one writer. No chasing scale beyond the envelope.
Each of these is an explicit decision in AGENTS.md, reversible only as a
new decision.

## Workspace

| Crate | Purpose |
|-------|---------|
| [`grit-core`]https://crates.io/crates/grit-core | schema, op-log writer actor, traversal, hybrid search — the product |
| [`grit-compat`]https://crates.io/crates/grit-compat | parked, fail-loud Graphiti/FalkorDB-dialect translator stub |
| `grit-cli` | dev tool: import/export, stats, ad-hoc search/traverse |

## Commands

```bash
cargo build --workspace
cargo test  --workspace          # unit + property + crash + migration + plan-pinning tests
cargo test -p grit-core --release --test envelope -- --ignored   # latency tripwire
cargo bench -p grit-core --bench hot_paths                       # criterion
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt   --all
```

The suite includes a `kill -9` crash harness (derived tables must equal a
full op-log replay after any crash point), op-log merge-law property tests,
frozen schema-version fixtures, and `EXPLAIN QUERY PLAN` pinning — an index
regression is a correctness bug at this latency budget. Everything runs
offline.

## License

MIT OR Apache-2.0, at your option. See [NOTICE](NOTICE) for attributions
(Graphiti schema concepts, simple-graph traversal templates, Cortex scoring
ideas).