Docs: Comprehensive inline rustdoc and architectural summary PDF
This commit is contained in:
@@ -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 Dolt‑backed graph each decision is versioned
|
||||
//! and can be replay‑backed, satisfying CHORUS’s 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 Dolt‑backed 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 in‑line 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,
|
||||
/// Free‑form 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
|
||||
/// Dolt‑backed 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();
|
||||
|
||||
Reference in New Issue
Block a user