use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Resource {
Galaxy(String),
KarmaLedger,
DharmaRules,
SearchIndex,
VectorStore,
Network,
Filesystem,
Process,
Inference,
Session,
EventBus,
CoordinationLease,
CoordinationRelease,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Capability {
MemoryRead,
MemoryWrite,
MemoryDelete,
Search,
VectorSearch,
Embed,
LlmInfer,
Delegate,
Execute,
NetworkRequest,
Dream,
CittaUpdate,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CostEstimate {
pub cpu_ns: u64,
pub memory_bytes: u64,
pub disk_bytes: u64,
pub network_bytes: u64,
pub expensive: bool,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Sandbox {
#[default]
Inherit,
StoreScoped,
Subprocess,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EffectRow {
pub reads: Vec<Resource>,
pub writes: Vec<Resource>,
pub invokes: Vec<Capability>,
pub spawns: bool,
pub destructive: bool,
#[serde(default)]
pub sandbox: Sandbox,
pub cost: CostEstimate,
}
impl EffectRow {
#[must_use]
pub fn pure() -> Self {
Self::default()
}
#[must_use]
pub fn read_only(resources: Vec<Resource>) -> Self {
Self {
reads: resources,
writes: vec![],
invokes: vec![],
spawns: false,
destructive: false,
sandbox: Sandbox::Inherit,
cost: CostEstimate::default(),
}
}
#[must_use]
pub fn is_available_in(&self, brain_wave: crate::BrainWave) -> bool {
use crate::BrainWave::{Alpha, Beta, Delta, Gamma, Theta};
match brain_wave {
Gamma => true,
Beta => true,
Alpha => !self.cost.expensive && self.writes.is_empty(),
Theta => !self.cost.expensive && self.writes.is_empty() && !self.spawns,
Delta => false, }
}
#[must_use]
pub fn acquires_coordination_lease(&self) -> bool {
self.writes
.iter()
.any(|r| matches!(r, Resource::CoordinationLease))
}
#[must_use]
pub fn is_coordination_cleanup(&self) -> bool {
!self.spawns
&& !self.destructive
&& self.writes.len() == 1
&& matches!(self.writes[0], Resource::CoordinationRelease)
}
#[must_use]
pub fn is_no_discovery_checkpoint(&self) -> bool {
!self.spawns
&& !self.destructive
&& self.writes.len() == 1
&& matches!(&self.writes[0], Resource::Galaxy(g) if g == "sessions")
}
#[must_use]
pub fn conflicts_with(&self, other: &Self) -> bool {
for w in &self.writes {
if other.writes.contains(w) || other.reads.contains(w) {
return true;
}
}
for w in &other.writes {
if self.reads.contains(w) {
return true;
}
}
if self.spawns && other.spawns {
return true;
}
false
}
}
impl fmt::Display for EffectRow {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"reads:{}, writes:{}, invokes:{}, spawns:{}",
self.reads.len(),
self.writes.len(),
self.invokes.len(),
self.spawns
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pure_effect_has_no_side_effects() {
let e = EffectRow::pure();
assert!(e.reads.is_empty());
assert!(e.writes.is_empty());
assert!(!e.spawns);
}
#[test]
fn effect_conflict_detection() {
let writer = EffectRow {
writes: vec![Resource::Galaxy("citta".into())],
..Default::default()
};
let reader = EffectRow {
reads: vec![Resource::Galaxy("citta".into())],
..Default::default()
};
assert!(writer.conflicts_with(&reader));
assert!(reader.conflicts_with(&writer));
let other_reader = EffectRow {
reads: vec![Resource::Galaxy("codex".into())],
..Default::default()
};
assert!(!reader.conflicts_with(&other_reader));
}
#[test]
fn brain_wave_filtering() {
use crate::BrainWave::*;
let expensive = EffectRow {
cost: CostEstimate {
expensive: true,
..Default::default()
},
..Default::default()
};
assert!(expensive.is_available_in(Gamma));
assert!(!expensive.is_available_in(Alpha));
assert!(!expensive.is_available_in(Delta));
}
#[test]
fn sandbox_seam_defaults_and_serializes() {
let row = EffectRow::pure();
assert_eq!(row.sandbox, Sandbox::Inherit);
let scoped = EffectRow {
sandbox: Sandbox::StoreScoped,
..Default::default()
};
assert_eq!(scoped.sandbox, Sandbox::StoreScoped);
let json = serde_json::to_string(&scoped).expect("serialize");
assert!(json.contains("store_scoped"));
let back: EffectRow = serde_json::from_str(&json).expect("deserialize");
assert_eq!(back.sandbox, Sandbox::StoreScoped);
let legacy: EffectRow = serde_json::from_str(
"{\"reads\":[],\"writes\":[],\"invokes\":[],\"spawns\":false,\"destructive\":false,\
\"cost\":{\"cpu_ns\":0,\"memory_bytes\":0,\"disk_bytes\":0,\"network_bytes\":0,\"expensive\":false}}",
)
.expect("legacy payload without sandbox field");
assert_eq!(legacy.sandbox, Sandbox::Inherit);
}
use crate::BrainWave;
use proptest::prelude::*;
fn arb_resource() -> impl Strategy<Value = Resource> {
prop_oneof![
Just(Resource::Galaxy("codex".into())),
Just(Resource::Galaxy("citta".into())),
Just(Resource::Filesystem),
Just(Resource::Network),
Just(Resource::Process),
]
}
fn arb_effect_row() -> impl Strategy<Value = EffectRow> {
(
proptest::collection::vec(arb_resource(), 0..6),
proptest::collection::vec(arb_resource(), 0..6),
any::<bool>(),
any::<bool>(),
)
.prop_map(|(reads, writes, spawns, expensive)| EffectRow {
reads,
writes,
spawns,
cost: CostEstimate {
expensive,
..Default::default()
},
..Default::default()
})
}
proptest! {
#[test]
fn delta_blocks_all(effects in arb_effect_row()) {
prop_assert!(!effects.is_available_in(BrainWave::Delta));
}
#[test]
fn gamma_allows_all(effects in arb_effect_row()) {
prop_assert!(effects.is_available_in(BrainWave::Gamma));
}
#[test]
fn beta_allows_all(effects in arb_effect_row()) {
prop_assert!(effects.is_available_in(BrainWave::Beta));
}
#[test]
fn alpha_blocks_writes_and_expensive(effects in arb_effect_row()) {
let result = effects.is_available_in(BrainWave::Alpha);
if !effects.writes.is_empty() || effects.cost.expensive {
prop_assert!(!result, "Alpha should block writes/expensive: {effects}");
} else {
prop_assert!(result, "Alpha should allow pure reads: {effects}");
}
}
#[test]
fn theta_blocks_writes_spawns_expensive(effects in arb_effect_row()) {
let result = effects.is_available_in(BrainWave::Theta);
if !effects.writes.is_empty() || effects.cost.expensive || effects.spawns {
prop_assert!(!result, "Theta should block: {effects}");
} else {
prop_assert!(result, "Theta should allow pure reads: {effects}");
}
}
#[test]
fn conflicts_symmetric(a in arb_effect_row(), b in arb_effect_row()) {
let ab = a.conflicts_with(&b);
let ba = b.conflicts_with(&a);
prop_assert_eq!(ab, ba, "conflicts_with must be symmetric");
}
#[test]
fn conflicts_self_with_writes_or_spawns(effects in arb_effect_row()) {
let self_conflict = effects.conflicts_with(&effects);
if !effects.writes.is_empty() || effects.spawns {
prop_assert!(self_conflict, "effect row with writes/spawns should conflict with itself");
} else {
prop_assert!(!self_conflict, "pure effect row should not conflict with itself");
}
}
}
}