chronicle-graph 0.1.0

Event-centric narrative knowledge graphs with temporal verification
Documentation
# chronicle

Event-centric narrative knowledge graphs with temporal verification.

A Rust crate for structured world-building: load authored narrative content into a typed, event-centric knowledge graph, validate it for consistency, and query it.

## The Problem

World-building for narrative-heavy games accumulates as disconnected documents that drift into inconsistency. Characters contradict themselves across files, timelines break, and AI-generated content introduces plausible-sounding details that conflict with established canon. At scale, nobody can hold the world in their head.

## The Approach

Replace disconnected prose with structured entity-relation graphs. Every piece of world-building — character, faction, location, event — is a typed node with explicit, validated relationships. Events are the connective tissue (adapted from [CIDOC CRM](https://cidoc-crm.org/), ISO 21127). Temporal consistency uses [Allen's Interval Algebra](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra).

## Usage

```rust
use chronicle::graph::Chronicle;
use chronicle::model::*;

// Load a world from RON files
let graph = Chronicle::from_directory("world/".as_ref())?;

// Validate consistency
let report = graph.validate();
for error in &report.errors {
    eprintln!("{error}");
}

// Typed queries
let kaine = graph.actor("kaine_durgan").unwrap();
let events = kaine.events();
let contacts = kaine.interactions().people();
let chain = graph.event("siege_of_silica").unwrap().causal_chain();
let status = graph.place("silica").unwrap().status_at(25); // Destroyed

// Check if a proposed event is consistent
let proposed = Event {
    id: "new_battle".into(),
    name: "New Battle".into(),
    event_type: EventType::Battle,
    time_span: TimeSpan { start: 25, end: 25 },
    location: Some("silica".into()),
    caused_by: vec![],
    participants: vec![],
    state_changes: vec![],
    description: String::new(),
};
match graph.can_add_event(&proposed) {
    Ok(()) => println!("Event is consistent"),
    Err(errors) => {
        for e in &errors {
            eprintln!("{e}");
            // "location 'silica' has status Destroyed as of 'siege_of_silica' (year 20),
            //  cannot host proposed event 'new_battle' (year 25)"
        }
    }
}

// Subjective accounts with fidelity ratings
let accounts = graph.accounts_of("siege_of_silica");
for account in accounts {
    println!("{}: {:?} fidelity", account.source, account.fidelity);
}
```

## Content Format

World data is authored as RON files in a directory structure. Entity type is inferred from the subdirectory name:

```
world/
├── actors/         # Vec<Actor> — characters, factions, organizations
├── places/         # Vec<Place> — settlements, regions, landmarks
├── events/         # Vec<Event> — battles, discoveries, political events
├── concepts/       # Vec<Concept> — religions, technologies, artifacts
└── accounts/       # Vec<Account> — subjective narrative text
```

Narrative text uses `{entity_id}` references:

```ron
(
    id: "kaine_siege_journal",
    source: "kaine_durgan",
    fidelity: Biased,
    event_refs: ["siege_of_silica"],
    text: "We burned {silica}. The {mirror_order} call it a massacre. We call it sanitation.",
)
```

## Features

- **Event-centric model**: All relationships pass through events (who, where, when, why)
- **Temporal verification**: Allen's Interval Algebra catches impossible timelines
- **State tracking**: Dead actors can't participate in future events; destroyed places can't host them
- **Configurable policies**: `ValidationConfig` controls which statuses are terminal
- **Subjective fragments**: Model unreliable narrators with fidelity ratings (Canonical, Partial, Biased, etc.)
- **Typed queries**: Method chains over structured data, not natural language
- **Insertion verification**: `can_add_event()` checks proposed records against the existing graph

## Custom Validation Config

```rust
use chronicle::model::{ValidationConfig, Status};

let config = ValidationConfig {
    terminal_statuses: [Status::Dead, Status::Destroyed, Status::Dissolved, Status::Captured].into(),
};
let graph = Chronicle::from_directory_with_config("world/".as_ref(), config)?;
```

## Dependencies

| Crate | Purpose |
|-------|---------|
| `petgraph` | Graph structure and traversal algorithms |
| `allen-intervals` | Temporal consistency verification (Allen's Interval Algebra) |
| `serde` + `ron` | Deserialization of hand-authored content |
| `thiserror` | Error types |

No async, no database, no network, no GPU.

## Status

Phases 1–4 complete. Not yet published on crates.io.

- ✅ Graph model, loading, edge construction
- ✅ Validation (referential, temporal, state, orphan detection)
- ✅ Typed query API (actor, event, place, concept queries)
- ✅ Insertion verification (`can_add_event` with `ValidationConfig`)
- ✅ Subjective fragments (accounts with fidelity ratings)
- 🔲 Saltglass-steppe integration (first consumer)

## License

MIT