Implement chrs-slurp: Context Intelligence layer with DR curation and Dolt integration

This commit is contained in:
anthonyrawlins
2026-03-03 17:37:06 +11:00
parent f134b1d060
commit ce5c2238dd
3 changed files with 107 additions and 2 deletions

View File

@@ -44,20 +44,23 @@ impl DoltGraph {
.output()?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("Dolt Command Failed: {:?} -> {}", args, stderr);
return Err(GraphError::CommandFailed(stderr.to_string()));
}
Ok(())
}
pub fn commit(&self, message: &str) -> Result<(), GraphError> {
self.run_cmd(&["add", "."])?;
self.run_cmd(&["add", "-A"])?;
self.run_cmd(&["commit", "-m", message])?;
Ok(())
}
pub fn create_table(&self, table_name: &str, schema: &str) -> Result<(), GraphError> {
let query = format!("CREATE TABLE {} ({})", table_name, schema);
println!("Executing: dolt sql -q \"{}\"", query);
self.run_cmd(&["sql", "-q", &query])?;
self.commit(&format!("Create table {}", table_name))?;
Ok(())
}
@@ -90,6 +93,7 @@ impl DoltGraph {
columns.join(", "),
values.join(", ")
);
println!("Executing: dolt sql -q \"{}\"", query);
self.run_cmd(&["sql", "-q", &query])?;
Ok(())
}
@@ -105,6 +109,5 @@ mod tests {
let dir = TempDir::new().unwrap();
let graph = DoltGraph::init(dir.path()).expect("init failed");
graph.create_table("nodes", "id INT PRIMARY KEY, name TEXT").expect("create table failed");
graph.commit("initial commit with table").expect("commit failed");
}
}

17
chrs-slurp/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "chrs-slurp"
version = "0.1.0"
edition = "2021"
[dependencies]
chrs-graph = { path = "../chrs-graph" }
ucxl = { path = "../UCXL" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1.0", features = ["v4", "serde"] }
[dev-dependencies]
tempfile = "3"

85
chrs-slurp/src/lib.rs Normal file
View File

@@ -0,0 +1,85 @@
use chrs_graph::{DoltGraph, GraphError};
use ucxl::UCXLAddress;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DecisionRecord {
pub id: Uuid,
pub author: String,
pub reasoning: String,
pub citations: Vec<String>, // Serialized UCXL addresses
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Error)]
pub enum SlurpError {
#[error("Graph error: {0}")]
Graph(#[from] GraphError),
#[error("Serialization error: {0}")]
Serde(#[from] serde_json::Error),
#[error("Validation error: {0}")]
ValidationError(String),
}
pub struct CurationEngine {
graph: DoltGraph,
}
impl CurationEngine {
pub fn new(graph: DoltGraph) -> Self {
Self { graph }
}
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)))?;
}
// 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");
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()
});
self.graph.insert_node("curated_decisions", data)?;
self.graph.commit(&format!("Curation complete for DR: {}", dr.id))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_curation_flow() {
let dir = TempDir::new().unwrap();
let graph = DoltGraph::init(dir.path()).expect("graph init failed");
let engine = CurationEngine::new(graph);
let dr = DecisionRecord {
id: Uuid::new_v4(),
author: "agent-001".into(),
reasoning: "Tested the implementation of SLURP.".into(),
citations: vec!["ucxl://system:watcher@local:filesystem/#/UCXL/src/lib.rs".into()],
timestamp: Utc::now(),
};
engine.curate_decision(dr).expect("curation failed");
}
}