sqlite-graphrag 1.2.8

Persistent GraphRAG memory for Claude Code, Codex, Cursor, and 27 AI agents — one self-contained ~19 MiB Rust binary, zero daemon. Never re-explain your codebase again. Hybrid retrieval (FTS5 BM25 + cosine similarity + multi-hop graph traversal) surfaces the right memory in milliseconds. Embedding and entity enrichment run as parallel REST calls against your cloud LLM — no fragile headless subprocesses, no ONNX runtime, no model downloads. Soft-delete with full version history, transactional atomic writes, BLAKE3-tracked mutations. OAuth-only: raw API keys ABORT the spawn.
Documentation
//! Integration test for the v1.1.02 `--prune-dead-entity-orphans` flag.
//! Guards the clap wiring (flag accepted, conflicts_with the memory variant)
//! and the envelope contract (action label). The row-level DELETE logic is
//! covered by the unit test `prune_dead_entity_orphans_removes_only_entity_dead_rows`
//! in `src/commands/enrich/queue.rs`.

use assert_cmd::Command;
use serial_test::serial;
use tempfile::TempDir;

#[path = "common/mod.rs"]
mod common;

fn cmd(temp: &TempDir) -> Command {
    let mock_dir = common::mock_llm_path();
    let mut c = Command::cargo_bin("sqlite-graphrag").expect("sqlite-graphrag binary not found");
    c.env_clear()
        .env("HOME", temp.path())
        .env("HOME", temp.path())
        .arg("--lang")
        .arg("en")
        .current_dir(temp.path());
    for var in &["LOCALAPPDATA", "APPDATA", "USERPROFILE", "SystemRoot"] {
        if let Ok(v) = std::env::var(var) {
            c.env(var, v);
        }
    }
    c.env("PATH", common::prepend_path(&mock_dir));
    c
}

#[test]
#[serial]
fn prune_dead_entity_orphans_emits_envelope() {
    let tmp = TempDir::new().unwrap();
    cmd(&tmp)
        .args(["init", "--embedding-dim", "64", "--json"])
        .assert()
        .success();

    // GAP-SG-207: `enrich` changes durable state, so it names its target. The
    // `init` above created the XDG default inside this sandbox, and relying on
    // that default is exactly the inheritance the rule refuses — naming it here
    // also makes the test say WHICH database it is asserting about.
    let db = tmp
        .path()
        .join(".local/share/sqlite-graphrag/graphrag.sqlite");
    let db_arg = db.to_str().expect("sandbox path is utf-8").to_string();
    let output = cmd(&tmp)
        .args([
            "enrich",
            "--db",
            &db_arg,
            "--operation",
            "re-embed",
            "--prune-dead-entity-orphans",
            "--json",
        ])
        .output()
        .expect("run enrich --prune-dead-entity-orphans");
    assert!(
        output.status.success(),
        "enrich --prune-dead-entity-orphans failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(
        stdout.contains("\"action\":\"prune-dead-entity-orphans\""),
        "action label missing in envelope: {stdout}"
    );
}

#[test]
#[serial]
fn prune_dead_entity_orphans_conflicts_with_memory_variant() {
    let tmp = TempDir::new().unwrap();
    cmd(&tmp)
        .args(["init", "--embedding-dim", "64", "--json"])
        .assert()
        .success();

    // Passing both prune flags must be rejected by clap (conflicts_with).
    cmd(&tmp)
        .args([
            "enrich",
            "--operation",
            "re-embed",
            "--prune-dead-orphans",
            "--prune-dead-entity-orphans",
            "--json",
        ])
        .assert()
        .failure()
        .code(2);
}