Skip to main content

agentic_codebase/collective/
delta.rs

1//! Delta compression for collective sync.
2//!
3//! Provides types and utilities for creating, compressing, and decompressing
4//! deltas that represent knowledge to be shared across codebases.
5
6use serde::{Deserialize, Serialize};
7
8use crate::types::AcbError;
9use crate::types::AcbResult;
10
11/// Category of mistake that can be reported.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub enum MistakeCategory {
14    /// A bug pattern that was discovered.
15    BugPattern,
16    /// A performance anti-pattern.
17    PerformanceAntiPattern,
18    /// A security vulnerability pattern.
19    SecurityVulnerability,
20    /// An API misuse pattern.
21    ApiMisuse,
22    /// A code smell or maintainability issue.
23    CodeSmell,
24}
25
26impl std::fmt::Display for MistakeCategory {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Self::BugPattern => write!(f, "bug-pattern"),
30            Self::PerformanceAntiPattern => write!(f, "performance-anti-pattern"),
31            Self::SecurityVulnerability => write!(f, "security-vulnerability"),
32            Self::ApiMisuse => write!(f, "api-misuse"),
33            Self::CodeSmell => write!(f, "code-smell"),
34        }
35    }
36}
37
38/// A report of a coding mistake or anti-pattern.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct MistakeReport {
41    /// Category of the mistake.
42    pub category: MistakeCategory,
43    /// Short description of what went wrong.
44    pub description: String,
45    /// The pattern signature that triggered the detection.
46    pub pattern_signature: String,
47    /// Suggested fix or improvement.
48    pub suggestion: String,
49    /// Severity (0.0 = informational, 1.0 = critical).
50    pub severity: f32,
51}
52
53/// A collective delta containing patterns and mistakes to share.
54///
55/// Deltas are the unit of exchange for collective intelligence.
56/// They can be compressed for efficient transmission and storage.
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct CollectiveDelta {
59    /// Schema version for forward compatibility.
60    pub version: u32,
61    /// Unique identifier for this delta (blake3 hash of contents).
62    pub delta_id: String,
63    /// Unix timestamp when this delta was created.
64    pub created_at: u64,
65    /// Source codebase identifier (anonymised).
66    pub source_id: String,
67    /// Patterns discovered in this delta.
68    pub patterns: Vec<DeltaPattern>,
69    /// Mistakes reported in this delta.
70    pub mistakes: Vec<MistakeReport>,
71}
72
73/// A pattern included in a collective delta.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct DeltaPattern {
76    /// Pattern name or identifier.
77    pub name: String,
78    /// Language this pattern applies to.
79    pub language: String,
80    /// Structural signature of the pattern.
81    pub signature: String,
82    /// How many times this pattern was observed.
83    pub occurrence_count: u32,
84    /// Confidence that this is a meaningful pattern (0.0 to 1.0).
85    pub confidence: f32,
86}
87
88impl CollectiveDelta {
89    /// Create a new empty delta.
90    pub fn new(source_id: String) -> Self {
91        let now = crate::types::now_micros();
92        Self {
93            version: 1,
94            delta_id: String::new(),
95            created_at: now,
96            source_id,
97            patterns: Vec::new(),
98            mistakes: Vec::new(),
99        }
100    }
101
102    /// Add a pattern to the delta.
103    pub fn add_pattern(&mut self, pattern: DeltaPattern) {
104        self.patterns.push(pattern);
105    }
106
107    /// Add a mistake report to the delta.
108    pub fn add_mistake(&mut self, mistake: MistakeReport) {
109        self.mistakes.push(mistake);
110    }
111
112    /// Compute and set the delta ID based on content hash.
113    pub fn finalize(&mut self) -> AcbResult<()> {
114        let json = serde_json::to_vec(self)
115            .map_err(|e| AcbError::Compression(format!("Failed to serialize delta: {}", e)))?;
116        let hash = blake3::hash(&json);
117        self.delta_id = hash.to_hex().to_string();
118        Ok(())
119    }
120
121    /// Compress the delta to a byte vector using lz4.
122    ///
123    /// Serialises to JSON and then compresses with lz4_flex.
124    pub fn compress(&self) -> AcbResult<Vec<u8>> {
125        let json = serde_json::to_vec(self)
126            .map_err(|e| AcbError::Compression(format!("Failed to serialize delta: {}", e)))?;
127        let compressed = lz4_flex::compress_prepend_size(&json);
128        Ok(compressed)
129    }
130
131    /// Decompress a delta from a compressed byte vector.
132    ///
133    /// Decompresses with lz4_flex and deserialises from JSON.
134    pub fn decompress(data: &[u8]) -> AcbResult<Self> {
135        let decompressed = lz4_flex::decompress_size_prepended(data)
136            .map_err(|e| AcbError::Compression(format!("Failed to decompress delta: {}", e)))?;
137        let delta: CollectiveDelta = serde_json::from_slice(&decompressed)
138            .map_err(|e| AcbError::Compression(format!("Failed to deserialize delta: {}", e)))?;
139        Ok(delta)
140    }
141
142    /// Returns true if this delta has any content to share.
143    pub fn is_empty(&self) -> bool {
144        self.patterns.is_empty() && self.mistakes.is_empty()
145    }
146}