stemedb/.claude/guides/backend/rust-guidelines.md
jordan a776744889 Initial project setup with Claude Code monorepo structure
- Rust workspace with stemedb-core crate
- Full .claude/ configuration (agents, skills, commands, guides)
- ai-lookup/ for token-efficient fact storage
- Quality gates: clippy, fmt, jscpd duplication detection
- Pre-commit hook with 5-phase quality checks
- CLAUDE.md router and CODING_GUIDELINES.md standards

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 10:56:26 -07:00

3.1 KiB

Rust Guidelines for StemeDB

When to use: Writing Rust code for any StemeDB crate.

Framework

All code MUST follow patterns in CODING_GUIDELINES.md.

Core Principles

1. Append-Only Everything

// Good: Append new assertion
pub fn put(&self, assertion: &Assertion) -> Result<Hash> {
    let hash = assertion.hash();
    self.store.insert(hash.as_bytes(), assertion.to_bytes())?;
    Ok(hash)
}

// Bad: Mutation
pub fn update(&mut self, hash: &Hash, new_value: Value) {
    // NEVER DO THIS
}

2. Content-Addressed Storage

use blake3::Hasher;

impl Assertion {
    pub fn hash(&self) -> Hash {
        let mut hasher = Hasher::new();
        hasher.update(&self.subject.0.as_bytes());
        hasher.update(&self.predicate.0.as_bytes());
        hasher.update(&self.object.to_bytes());
        hasher.update(&self.timestamp.to_le_bytes());
        Hash(hasher.finalize().into())
    }
}

3. Defensive Error Handling

// Good: Explicit error handling
pub fn get(&self, hash: &Hash) -> Result<Assertion, StemeError> {
    let bytes = self.store
        .get(hash.as_bytes())
        .context("storage read failed")?
        .ok_or(StemeError::NotFound(*hash))?;

    rkyv::from_bytes(&bytes)
        .map_err(|e| StemeError::Deserialization(e.to_string()))
}

// Bad: Panic on failure
pub fn get(&self, hash: &Hash) -> Assertion {
    let bytes = self.store.get(hash.as_bytes()).unwrap().unwrap();
    rkyv::from_bytes(&bytes).unwrap()
}

4. Zero-Copy Serialization

use rkyv::{Archive, Deserialize, Serialize};

#[derive(Archive, Deserialize, Serialize)]
pub struct Assertion {
    pub subject: EntityId,
    pub predicate: RelationId,
    pub object: ObjectValue,
    // ...
}

// Read without copying
let archived = rkyv::check_archived_root::<Assertion>(&bytes)?;
println!("Subject: {}", archived.subject.0);

Module Patterns

Public API in lib.rs

// crates/stemedb-core/src/lib.rs

mod assertion;
mod error;
mod store;
mod types;

// Re-export public API
pub use assertion::Assertion;
pub use error::StemeError;
pub use store::{Store, MemoryStore};
pub use types::{EntityId, RelationId, Hash, AgentId, ObjectValue};

Error Types

// crates/stemedb-core/src/error.rs
use thiserror::Error;

#[derive(Debug, Error)]
pub enum StemeError {
    #[error("assertion not found: {0:?}")]
    NotFound(Hash),

    #[error("invalid signature for agent {agent:?}")]
    InvalidSignature { agent: AgentId },

    #[error("serialization failed: {0}")]
    Serialization(String),

    #[error("deserialization failed: {0}")]
    Deserialization(String),

    #[error("storage error: {0}")]
    Storage(#[from] sled::Error),
}

pub type Result<T, E = StemeError> = std::result::Result<T, E>;

Quality Gate

Before commit:

# All must pass
cargo build --workspace
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo fmt --check