README.md

[![CI](https://github.com/redberrythread/weave/actions/workflows/ci.yml/badge.svg)](https://github.com/redberrythread/weave/actions/workflows/ci.yml)
[![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, image processing, and content DSL tools for OSINT knowledge graphs.

Weave is a Cargo workspace that publishes Rust crates to [crates.io](https://crates.io) and an Elixir NIF package to [hex.pm](https://hex.pm).

## Packages

| Package | Registry | Version | Description |
| ------- | -------- | ------- | ----------- |
| `weave-graph` | [crates.io](https://crates.io/crates/weave-graph) | [![crates.io](https://img.shields.io/crates/v/weave-graph.svg)](https://crates.io/crates/weave-graph) | Conflict-of-interest pattern detection |
| `weave-image` | [crates.io](https://crates.io/crates/weave-image) | [![crates.io](https://img.shields.io/crates/v/weave-image.svg)](https://crates.io/crates/weave-image) | Thumbnail download, resize, WebP conversion |
| `weave-content` | [crates.io](https://crates.io/crates/weave-content) | [![crates.io](https://img.shields.io/crates/v/weave-content.svg)](https://crates.io/crates/weave-content) | Content DSL parser, validator, HTML generator |
| `rbt_weave` | [hex.pm](https://hex.pm/packages/rbt_weave) | [![hex.pm](https://img.shields.io/hexpm/v/rbt_weave.svg)](https://hex.pm/packages/rbt_weave) | Elixir NIF bridge (graph + image) |

## 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. payment-appointment kickbacks)
- **Path detection** -- constrained DFS matching edge types and node labels (e.g. family appointments, payment influence chains)
- **Hub detection** -- Person-Organization 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.

## Thumbnail Processing

`weave-image` handles thumbnail generation for entity images:

- Download from URL with 5 MB size limit and 15s timeout
- Center-crop and resize to 256x256 lossless WebP
- Deterministic storage keys via SHA-256 of source URL
- Output capped at 50 KB

## Content DSL

`weave-content` parses, validates, and builds Markdown case files into structured output:

- **Parser** -- YAML front matter + Markdown sections (entities, relationships, timeline, related cases)
- **Validator** -- schema validation, enum enforcement, URL format checks, duplicate detection
- **URL verifier** -- concurrent HEAD/GET checks with caching and configurable concurrency
- **Staleness checker** -- flags cases that may need updates based on status and timeline gaps
- **HTML generator** -- produces semantic HTML fragments for case, entity, tag, and country pages
- **Sitemap generator** -- XML sitemap with cases, entities, countries, and tags

The HTML generator produces fragments (no `<html>`/`<head>` wrapper) with `data-og-*` attributes and Schema.org microdata, suitable for embedding in a Phoenix layout.

### Generated pages

| Page | Path | Description |
| ---- | ---- | ----------- |
| Case | `/cases/{cc}/{type}/{year}/{slug}.html` | Full case with entities, timeline, sources |
| Person | `/people/{cc}/{slug}.html` | Person profile with linked cases |
| Organization | `/organizations/{cc}/{slug}.html` | Organization profile with linked cases |
| Tag (global) | `/tags/{tag}.html` | Cases tagged with a specific tag |
| Tag (country) | `/tags/{cc}/{tag}.html` | Country-scoped tag page |
| Country | `/countries/{cc}.html` | Cases in a specific country |
| Countries index | `/countries.html` | All countries with case counts |
| Tags index | `/tags.html` | All tags with case counts |
| Sitemap | `/sitemap.xml` | Full XML sitemap |

## Elixir Usage

Add `rbt_weave` to your dependencies:

```elixir
def deps do
  [
    {:weave, "~> 0.2", hex: :rbt_weave}
  ]
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)

# Country name resolution (249 ISO 3166-1 codes)
"Indonesia" = Weave.Content.country_name("id")
```

## Rust Usage

Add crates to your `Cargo.toml`:

```toml
[dependencies]
weave-graph = "0.2"
weave-image = "0.2"
weave-content = "0.2"
```

```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
make ci        # format check + clippy + test (Rust + Elixir)
cargo test     # Rust tests only
mix test       # Elixir NIF tests only
```

## License

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