Detect emergent patterns across task outputs in Rust. Fire tasks, cool the kiln, discover clustering, correlations, phase transitions, and conservation laws you didn't design.
cargo add crackle-runtimeuse crackle_runtime::{CrackleTask, Kiln, ThermalProfile, TaskOutput};
struct Sensor { reading: f64, id: String }
impl CrackleTask for Sensor {
type Output = f64;
fn fire(&self) -> TaskOutput<Self::Output> {
TaskOutput::new(self.reading, vec![
("value".into(), self.reading),
("is_anomaly".into(), if self.reading > 40.0 { 1.0 } else { 0.0 }),
])
}
fn label(&self) -> String { self.id.clone() }
}
let mut kiln = Kiln::new(ThermalProfile::fast_cooling());
kiln.fire_and_record(Sensor { reading: 22.5, id: "s1".into() });
kiln.fire_and_record(Sensor { reading: 23.1, id: "s2".into() });
kiln.fire_and_record(Sensor { reading: 45.2, id: "s3".into() }); // outlier
let patterns = kiln.cool();
for p in &patterns {
println!("[{}] {} (confidence: {:.2})", p.kind(), p.description(), p.confidence());
}
// [clustering] 2 tasks clustered together in metric space (avg distance: 0.600)
// [phase transition] metric 'value' shifted by 87.5% between first and second half of taskscrackle-runtime runs tasks that produce named metrics, then analyzes all metrics together to find patterns that were invisible during individual execution:
| Pattern | What it detects |
|---|---|
| Clustering | Tasks whose metrics are close in Euclidean space |
| Phase Transition | Metrics that shifted significantly between the first and second half of tasks |
| Conservation | Metrics that stay near-constant across a group (low coefficient of variation) |
| Correlation | Pairs of metrics that move together (Pearson correlation) |
All detection runs after execution completes — no overhead during your hot path.
- CI/CD anomaly detection — Feed build durations and test counts as tasks, detect when build times cluster oddly or shift unexpectedly
- Load test analysis — Run hundreds of request tasks, let the runtime find response-time clustering and throughput correlations
- API response monitoring — Track latency, status codes, payload sizes across endpoints; detect emerging patterns before they become incidents
- Data pipeline quality — Process records as tasks, detect conservation laws (row counts in = row counts out) and unexpected metric correlations
Implement fire() to produce output with named metrics:
pub trait CrackleTask {
type Output;
fn fire(&self) -> TaskOutput<Self::Output>;
fn cool(&self, output: &TaskOutput<Self::Output>, all_metrics: &[(String, Vec<(String, f64)>)]) -> Vec<(String, f64)>;
fn label(&self) -> String;
}fire()— Execute and returnTaskOutputwith your value and metricscool()— Optional post-completion hook (default is no-op)label()— Human-readable task name for pattern reports
let mut kiln = Kiln::new(ThermalProfile::default());
kiln.fire_and_record(task); // Execute and store metrics
kiln.fire_all(tasks); // Batch execute
let patterns = kiln.cool(); // Run pattern detection
kiln.reset(); // Clear for next cyclelet output = TaskOutput::new(42.0, vec![("x".into(), 1.0)])
.with_metric("y", 2.0);for p in kiln.cool() {
p.kind(); // PatternKind::Clustering | PhaseTransition | Conservation | Correlation
p.description(); // Human-readable explanation
p.confidence(); // 0.0–1.0
p.involved_tasks(); // Which task labels are involved
p.metrics(); // Additional detected metrics
}| Profile | Behavior |
|---|---|
fast_cooling() |
Low thresholds, more patterns detected (good for exploration) |
default() |
Balanced |
slow_cooling() |
High thresholds, only strong patterns (good for production) |
no_detection() |
Skip detection entirely (benchmarking) |
let profile = ThermalProfile::default()
.with_rate(CoolingRate::Fast)
.without_clustering() // disable specific detectors
.without_correlations();Enriches any task with derived metrics for better pattern detection:
let glazed = GlazeLayer::new(my_task)
.with_derived_metric("magnitude", |out| out.value.abs())
.with_derived_metric("log_value", |out| out.value.ln());- Fire phase — Each task executes independently, producing a value and a set of named metrics
- Record — The kiln stores all task outputs and their metrics
- Cool phase — After all tasks complete, the runtime runs four detectors across the full metric set:
- Clustering: pairwise Euclidean distance with configurable threshold
- Phase transitions: first-half vs second-half mean comparison
- Conservation: coefficient of variation below tolerance
- Correlation: Pearson correlation between metric pairs
- Results — Patterns sorted by confidence, with involved tasks and explanatory descriptions
No background threads. No async runtime. Pure synchronous detection that runs when you call cool().
Note: Despite the name "runtime", this crate is fully synchronous. There is no async executor, no background thread pool, and no event loop. The "runtime" refers to the firing/cooling lifecycle of the kiln, not an async runtime.
The name comes from pottery: crackle glaze forms during cooling, not firing. The glaze and clay contract at different rates, producing fine cracks you can't design — only create conditions for. Similarly, the patterns crackle-runtime detects emerge from the aggregate behavior of your tasks, visible only in retrospect.
Inspired by "The Potter Who Cracked the Glaze" from the Ford Creative Wheel collection.
The firing was the transformation. The crack was the autobiography.
MIT