Docs: Comprehensive inline rustdoc and architectural summary PDF

This commit is contained in:
anthonyrawlins
2026-03-03 18:05:53 +11:00
parent cc03616918
commit 0f28e4b669
2932 changed files with 14552 additions and 74 deletions

View File

@@ -1,19 +1,82 @@
use chrs_graph::{DoltGraph, GraphError};
use ucxl::UCXLAddress;
//! # chrs-slurp
//!
//! **Intelligence Crate** Provides the *curation* layer for the CHORUS system.
//!
//! The purpose of this crate is to take **Decision Records** generated by autonomous
//! agents, validate them, and persist them into the graph database. It isolates the
//! validation and storage concerns so that other components (e.g. provenance, security)
//! can work with a clean, audited data model.
//!
//! ## Architectural Rationale
//!
//! * **Separation of concerns** Agents produce raw decisions; this crate is the
//! single source of truth for how those decisions are stored.
//! * **Auditability** By persisting to a Doltbacked graph each decision is versioned
//! and can be replaybacked, satisfying CHORUSs requirement for reproducible
//! reasoning.
//! * **Extensibility** The `CurationEngine` can be extended with additional validation
//! steps (e.g. policy checks) without touching the agents themselves.
//!
//! The crate depends on:
//! * `chrs-graph` a thin wrapper around a Doltbacked graph implementation.
//! * `ucxl` for addressing external knowledge artefacts.
//! * `chrono`, `serde`, `uuid` standard utilities for timestamps, (de)serialization
//! and unique identifiers.
//!
//! ---
//!
//! # Public API
//!
//! The public surface consists of three items:
//!
//! * `DecisionRecord` data structure representing a curated decision.
//! * `SlurpError` enumeration of possible errors while curating.
//! * `CurationEngine` the engine that validates and persists `DecisionRecord`s.
//!
//! Each item is documented inline below.
use chrono::{DateTime, Utc};
use chrs_graph::{DoltGraph, GraphError};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use ucxl::UCXLAddress;
use uuid::Uuid;
/// A record representing a curated decision within the CHORUS system.
///
/// # What
///
/// This struct captures the essential metadata of a decision made by an
/// autonomous agent, including who authored it, the reasoning behind it, any
/// citations to external knowledge, and a timestamp.
///
/// # Why
///
/// Decision records are persisted in the graph database so that downstream
/// components (e.g., provenance analysis) can reason about the provenance and
/// justification of actions. Storing them as a dedicated table enables
/// reproducibility and auditability across the CHORUS architecture.
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DecisionRecord {
/// Unique identifier for the decision.
pub id: Uuid,
/// Identifier of the agent or human that authored the decision.
pub author: String,
/// Freeform textual reasoning explaining the decision.
pub reasoning: String,
pub citations: Vec<String>, // Serialized UCXL addresses
/// Serialized UCXL addresses that serve as citations for the decision.
/// Each entry should be a valid `UCXLAddress` string.
pub citations: Vec<String>,
/// The moment the decision was created.
pub timestamp: DateTime<Utc>,
}
/// Errors that can arise while slurping (curating) a decision record.
///
/// * `Graph` underlying graph database operation failed.
/// * `Serde` (de)serialization of the decision data failed.
/// * `ValidationError` a supplied citation could not be parsed as a
/// `UCXLAddress`.
#[derive(Debug, Error)]
pub enum SlurpError {
#[error("Graph error: {0}")]
@@ -24,39 +87,70 @@ pub enum SlurpError {
ValidationError(String),
}
/// Core engine that validates and persists `DecisionRecord`s into the
/// Doltbacked graph.
///
/// # Why
///
/// Centralising curation logic ensures a single place for validation and
/// storage semantics, keeping the rest of the codebase agnostic of the graph
/// implementation details.
pub struct CurationEngine {
graph: DoltGraph,
}
impl CurationEngine {
/// Creates a new `CurationEngine` bound to the supplied `DoltGraph`.
///
/// The engine holds a reference to the graph for the lifetime of the
/// instance; callers are responsible for providing a correctly initialised
/// graph.
pub fn new(graph: DoltGraph) -> Self {
Self { graph }
}
/// Validates the citations in `dr` and persists the decision into the
/// graph.
///
/// The method performs three steps:
/// 1. **Citation validation** each citation string is parsed into a
/// `UCXLAddress`. Invalid citations produce a `ValidationError`.
/// 2. **Table assurance** attempts to create the `curated_decisions`
/// table if it does not already exist. Errors are ignored because the
/// table may already be present.
/// 3. **Insertion & commit** the decision is serialised to JSON and
/// inserted as a node, then the graph transaction is committed.
///
/// # Errors
/// Propagates any `GraphError`, `serde_json::Error`, or custom
/// validation failures.
pub fn curate_decision(&self, dr: DecisionRecord) -> Result<(), SlurpError> {
// 1. Validate Citations
for citation in &dr.citations {
use std::str::FromStr;
UCXLAddress::from_str(citation)
.map_err(|e| SlurpError::ValidationError(format!("Invalid citation {}: {}", citation, e)))?;
UCXLAddress::from_str(citation).map_err(|e| {
SlurpError::ValidationError(format!("Invalid citation {}: {}", citation, e))
})?;
}
// 2. Log DR into Graph (create table if needed handled by insert_node in future,
// but for now let's ensure it's there).
// If it fails because it exists, that's fine.
let _ = self.graph.create_table("curated_decisions", "id VARCHAR(255) PRIMARY KEY, author TEXT, reasoning TEXT, citations TEXT, curated_at TEXT");
// 2. Ensure the table exists; ignore error if it already does.
let _ = self.graph.create_table(
"curated_decisions",
"id VARCHAR(255) PRIMARY KEY, author TEXT, reasoning TEXT, citations TEXT, curated_at TEXT",
);
// 3. Serialize the record and insert it.
let data = serde_json::json!({
"id": dr.id.to_string(),
"author": dr.author,
"reasoning": dr.reasoning,
"citations": serde_json::to_string(&dr.citations)?,
"curated_at": dr.timestamp.to_rfc3339()
"curated_at": dr.timestamp.to_rfc3339(),
});
self.graph.insert_node("curated_decisions", data)?;
self.graph.commit(&format!("Curation complete for DR: {}", dr.id))?;
self.graph
.commit(&format!("Curation complete for DR: {}", dr.id))?;
Ok(())
}
}
@@ -66,6 +160,8 @@ mod tests {
use super::*;
use tempfile::TempDir;
/// Integration test that exercises the full curation flow on a temporary
/// Dolt graph.
#[test]
fn test_curation_flow() {
let dir = TempDir::new().unwrap();