README.md

[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/redberrythread/weave/blob/main/LICENSE)
[![Rust Version](https://img.shields.io/badge/rust-1.88%2B-blue.svg)](https://www.rust-lang.org)

# Weave

Graph analysis and processing NIFs for [RedBerryThread](https://redberrythread.org), an OSINT knowledge graph platform.

Weave is a Cargo workspace that publishes Rust crates to [crates.io](https://crates.io) and an Elixir hex package to [hex.pm](https://hex.pm). The Elixir package uses [Rustler](https://hex.pm/packages/rustler) to expose Rust functions as NIFs.

## Crates

| Crate | Description |
| ----- | ----------- |
| `weave-graph` | Conflict-of-interest pattern detection on knowledge graphs |
| `weave_nif` | Rustler NIF bridge (not published, used by the Elixir package) |

## Conflict Detection

`weave-graph` detects six conflict-of-interest patterns (COI-001 through COI-006) using three algorithms:

- **Cycle detection** -- directed simple cycles matching edge-type sequences (e.g. donation-appointment kickbacks)
- **Path detection** -- constrained DFS matching edge types and node labels (e.g. family appointments, donor influence chains)
- **Hub detection** -- Actor-Institution pairs with concentrated influence edges exceeding a threshold

All algorithms operate on in-memory subgraphs extracted from Neo4j with O(1) adjacency lookups. Detection completes in <50ms for subgraphs up to 10K nodes / 50K edges.

## Elixir Usage

Add `weave` to your dependencies:

```elixir
def deps do
  [
    {:rbt_weave, "~> 0.1.0"}
  ]
end
```

A Rust toolchain (1.88+) is required to compile the NIF.

```elixir
# Verify NIF is loaded
"ok" = Weave.Graph.health_check()

# Detect conflicts (JSON-in, JSON-out)
{:ok, results_json} = Weave.Graph.detect_conflicts(subgraph_json, opts_json)
```

## Rust Usage

Add `weave-graph` to your `Cargo.toml`:

```toml
[dependencies]
weave-graph = "0.1"
```

```rust
use weave_graph::graph::{Subgraph, IndexedSubgraph};
use weave_graph::detect::{detect_conflicts, DetectOpts};

let subgraph = Subgraph { nodes: vec![/* ... */], edges: vec![/* ... */] };
let indexed = IndexedSubgraph::from_subgraph(&subgraph);
let results = detect_conflicts(&indexed, &DetectOpts::default());
```

## Development

```bash
# Run Rust tests
cargo test

# Run clippy
cargo clippy --workspace -- -D warnings

# Run Elixir tests
mix deps.get && mix test

# Format
cargo fmt --all --check
mix format --check-formatted
```

## License

MIT -- see [LICENSE](LICENSE) for details.