From 848fc7a0727d88eb9b45522acc083c95d480407f Mon Sep 17 00:00:00 2001 From: sergio Date: Wed, 20 May 2026 00:24:48 +0000 Subject: [PATCH] =?UTF-8?q?refactor(brain):=20A2=20=E2=80=94=20split=20arj?= =?UTF-8?q?e-brain=20en=203=20sub-crates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DAG de dependencias limpio (modularidad horizontal): - arje-brain-rules — rules + engine + dispatch (motor determinista) - arje-brain-cognitive — observer + crystallize (estadística) - arje-brain-audit — audit chain → CAS (accountability) - arje-brain — umbrella de integración (introspect + autopromote + metrics + loader) Habilitador clave: TimedEvent movido de observer.rs a rules.rs (engine lo necesitaba, era el único acoplo que rompía el DAG). arje-brain re-exporta la API de los 3 sub-crates: arje-zero y chasqui (consumidores) no requieren cambios. cargo check --workspace verde. 24 tests del brain pasan (4 rules + 6 cognitive + 5 audit + 9 umbrella). Co-Authored-By: Claude Opus 4.7 --- Cargo.lock | 40 ++++++++++++++ Cargo.toml | 3 ++ crates/runtime/SDD.md | 26 +++++---- crates/runtime/arje-brain-audit/Cargo.toml | 17 ++++++ .../src/audit.rs | 10 ++-- crates/runtime/arje-brain-audit/src/lib.rs | 13 +++++ .../runtime/arje-brain-cognitive/Cargo.toml | 13 +++++ .../src/crystallize.rs | 4 +- .../runtime/arje-brain-cognitive/src/lib.rs | 11 ++++ .../src/observer.rs | 12 +---- crates/runtime/arje-brain-rules/Cargo.toml | 16 ++++++ .../src/dispatch.rs | 0 .../src/engine.rs | 2 +- crates/runtime/arje-brain-rules/src/lib.rs | 13 +++++ .../src/rules.rs | 10 ++++ crates/runtime/arje-brain/Cargo.toml | 4 ++ crates/runtime/arje-brain/src/autopromote.rs | 6 +-- crates/runtime/arje-brain/src/introspect.rs | 46 ++++++++-------- crates/runtime/arje-brain/src/lib.rs | 54 +++++++++---------- crates/runtime/arje-brain/src/loader.rs | 4 +- crates/runtime/arje-brain/src/metrics.rs | 6 +-- 21 files changed, 221 insertions(+), 89 deletions(-) create mode 100644 crates/runtime/arje-brain-audit/Cargo.toml rename crates/runtime/{arje-brain => arje-brain-audit}/src/audit.rs (98%) create mode 100644 crates/runtime/arje-brain-audit/src/lib.rs create mode 100644 crates/runtime/arje-brain-cognitive/Cargo.toml rename crates/runtime/{arje-brain => arje-brain-cognitive}/src/crystallize.rs (98%) create mode 100644 crates/runtime/arje-brain-cognitive/src/lib.rs rename crates/runtime/{arje-brain => arje-brain-cognitive}/src/observer.rs (98%) create mode 100644 crates/runtime/arje-brain-rules/Cargo.toml rename crates/runtime/{arje-brain => arje-brain-rules}/src/dispatch.rs (100%) rename crates/runtime/{arje-brain => arje-brain-rules}/src/engine.rs (99%) create mode 100644 crates/runtime/arje-brain-rules/src/lib.rs rename crates/runtime/{arje-brain => arje-brain-rules}/src/rules.rs (94%) diff --git a/Cargo.lock b/Cargo.lock index 225933b..1a8bad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,6 +299,9 @@ name = "arje-brain" version = "0.0.1" dependencies = [ "anyhow", + "arje-brain-audit", + "arje-brain-cognitive", + "arje-brain-rules", "arje-card", "arje-cas", "base64 0.22.1", @@ -311,6 +314,43 @@ dependencies = [ "ulid", ] +[[package]] +name = "arje-brain-audit" +version = "0.0.1" +dependencies = [ + "anyhow", + "arje-brain-cognitive", + "arje-brain-rules", + "arje-cas", + "serde", + "serde_json", + "tokio", + "ulid", +] + +[[package]] +name = "arje-brain-cognitive" +version = "0.0.1" +dependencies = [ + "arje-brain-rules", + "serde", + "serde_json", + "ulid", +] + +[[package]] +name = "arje-brain-rules" +version = "0.0.1" +dependencies = [ + "anyhow", + "arje-card", + "base64 0.22.1", + "serde", + "serde_json", + "tracing", + "ulid", +] + [[package]] name = "arje-bus" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 5fa01bf..4eca129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,9 @@ members = [ "crates/runtime/arje-bus", "crates/runtime/arje-cas", "crates/runtime/arje-wasm", + "crates/runtime/arje-brain-rules", + "crates/runtime/arje-brain-cognitive", + "crates/runtime/arje-brain-audit", "crates/runtime/arje-brain", "crates/runtime/arje-echo", diff --git a/crates/runtime/SDD.md b/crates/runtime/SDD.md index 1ac0f0a..06a2c7c 100644 --- a/crates/runtime/SDD.md +++ b/crates/runtime/SDD.md @@ -6,21 +6,25 @@ rule engine + audit log, y un ente de smoke test. ## Crates -| crate | tipo | rol | -| ------------- | ---- | ----------------------------------------------------------- | -| `arje-bus` | lib | Unix SOCK_STREAM + postcard framing. Announce/Invoke/List | -| `arje-cas` | lib | Content-addressed storage SHA-256: blobs Wasm + audit log | -| `arje-wasm` | lib | Encarna `Payload::Wasm` vía `wasmi` en thread dedicado | -| `arje-brain` | lib | Rule engine + observer estadístico + audit chain con CAS | -| `arje-echo` | bin | Ente prueba — provee `Capability::Endpoint(echo)` | +| crate | tipo | rol | +| ---------------------- | ---- | -------------------------------------------------- | +| `arje-bus` | lib | Unix SOCK_STREAM + postcard framing | +| `arje-cas` | lib | Content-addressed storage SHA-256: blobs + audit | +| `arje-wasm` | lib | Encarna `Payload::Wasm` vía `wasmi` | +| `arje-brain-rules` | lib | Motor determinista: rules + engine O(1) + dispatch | +| `arje-brain-cognitive` | lib | Observer estadístico + crystallize de patrones | +| `arje-brain-audit` | lib | Audit chain con hashes anclados al CAS | +| `arje-brain` | lib | Integración: introspect + autopromote + metrics | +| `arje-echo` | bin | Ente prueba — provee `Capability::Endpoint(echo)` | ## Dependencias - `arje-bus` ← `tokio` + `postcard`. Consumido por `init/arje-zero`. -- `arje-cas` ← `sha2` + `sled`. Consumido por `arje-brain` (audit log) - y `arje-wasm` (blobs). -- `arje-brain` ← `arje-bus`, `arje-cas`. Consumido por Init para - observabilidad estadística + reglas declarativas. +- `arje-cas` ← `sha2` + `sled`. Consumido por `arje-brain-audit` y `arje-wasm`. +- **Brain split (DAG limpio)**: `arje-brain-rules` (base) ← `arje-brain-cognitive` + ← `arje-brain-audit` ← `arje-brain` (umbrella de integración). +- `arje-brain` re-exporta la API de los 3 sub-crates para los + consumidores históricos (`arje-zero`, `chasqui`). ## Invariantes diff --git a/crates/runtime/arje-brain-audit/Cargo.toml b/crates/runtime/arje-brain-audit/Cargo.toml new file mode 100644 index 0000000..251a779 --- /dev/null +++ b/crates/runtime/arje-brain-audit/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "arje-brain-audit" +version = "0.0.1" +edition.workspace = true +license.workspace = true +publish.workspace = true +description = "Capa de accountability del brain: audit log con hash chain anclado al CAS." + +[dependencies] +arje-brain-rules = { path = "../arje-brain-rules" } +arje-brain-cognitive = { path = "../arje-brain-cognitive" } +arje-cas = { path = "../arje-cas" } +serde = { workspace = true } +serde_json = { workspace = true } +ulid = { workspace = true } +tokio = { workspace = true } +anyhow = { workspace = true } diff --git a/crates/runtime/arje-brain/src/audit.rs b/crates/runtime/arje-brain-audit/src/audit.rs similarity index 98% rename from crates/runtime/arje-brain/src/audit.rs rename to crates/runtime/arje-brain-audit/src/audit.rs index 24a8ec0..27af464 100644 --- a/crates/runtime/arje-brain/src/audit.rs +++ b/crates/runtime/arje-brain-audit/src/audit.rs @@ -6,7 +6,7 @@ //! escribe al content-addressable store y devuelve el SHA del head, que //! puede guardarse en un archivo de "head pointer" (fuera de scope aquí). -use crate::crystallize::Crystal; +use arje_brain_cognitive::crystallize::Crystal; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; use ulid::Ulid; @@ -323,7 +323,7 @@ pub fn collect_chain_from_cas(start_sha: [u8; 32]) -> anyhow::Result ReplayReport { let entries = match collect_chain_from_cas(start_sha) { Ok(es) => es, @@ -336,7 +336,7 @@ pub fn replay_chain( for entry in &entries { match &entry.action { AuditAction::PromoteCrystal { rule_id, crystal } => { - let mut rule = crate::crystallize::crystal_to_rule(crystal); + let mut rule = arje_brain_cognitive::crystallize::crystal_to_rule(crystal); rule.id = *rule_id; // preservar identidad histórica engine.insert(rule); } @@ -422,7 +422,7 @@ mod tests { // ---------- Tests de integración con CAS real (en directorio temporal) ---------- - use crate::engine::RuleEngine; + use arje_brain_rules::engine::RuleEngine; use std::sync::Mutex; /// Lock para serializar tests que mutan ENTE_CAS_ROOT (test threads @@ -459,7 +459,7 @@ mod tests { } } - use crate::rules::EventKind; + use arje_brain_rules::rules::EventKind; #[test] fn flush_round_trip_preserves_chain() { diff --git a/crates/runtime/arje-brain-audit/src/lib.rs b/crates/runtime/arje-brain-audit/src/lib.rs new file mode 100644 index 0000000..fec5570 --- /dev/null +++ b/crates/runtime/arje-brain-audit/src/lib.rs @@ -0,0 +1,13 @@ +//! arje-brain-audit — accountability del brain. +//! +//! Audit log con cadena de hashes anclada al content-addressed storage +//! (`arje-cas`). Permite verificar la integridad de la historia de +//! decisiones del brain y reconstruir el estado vía replay. + +pub mod audit; + +pub use audit::{ + AuditAction, AuditEntry, AuditHeadPointer, AuditLog, ReplayReport, + VerificationReport, collect_chain_from_cas, reachable_from_head, + replay_chain, verify_chain_from_cas, +}; diff --git a/crates/runtime/arje-brain-cognitive/Cargo.toml b/crates/runtime/arje-brain-cognitive/Cargo.toml new file mode 100644 index 0000000..72456e5 --- /dev/null +++ b/crates/runtime/arje-brain-cognitive/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "arje-brain-cognitive" +version = "0.0.1" +edition.workspace = true +license.workspace = true +publish.workspace = true +description = "Capa cognitiva del brain: observer estadístico (entropía + información mutua) + cristalización de patrones en reglas." + +[dependencies] +arje-brain-rules = { path = "../arje-brain-rules" } +serde = { workspace = true } +serde_json = { workspace = true } +ulid = { workspace = true } diff --git a/crates/runtime/arje-brain/src/crystallize.rs b/crates/runtime/arje-brain-cognitive/src/crystallize.rs similarity index 98% rename from crates/runtime/arje-brain/src/crystallize.rs rename to crates/runtime/arje-brain-cognitive/src/crystallize.rs index 6a90660..d7a84cc 100644 --- a/crates/runtime/arje-brain/src/crystallize.rs +++ b/crates/runtime/arje-brain-cognitive/src/crystallize.rs @@ -10,7 +10,7 @@ //! resultante con serde — sin formatos intermedios. use crate::observer::{GapStats, Observer}; -use crate::rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope}; +use arje_brain_rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope}; use serde::{Deserialize, Serialize}; use std::time::Instant; use ulid::Ulid; @@ -215,7 +215,7 @@ pub fn detect_pattern_crystals(obs: &Observer, params: &PatternParams) -> Vec` es el unit de compartición. Clonar una //! regla del motor para entregarla al dispatcher es un refcount bump, no copia. -use crate::observer::TimedEvent; +use crate::rules::TimedEvent; use crate::rules::{EventKind, EventPattern, Rule, Scope}; use arje_card::Capability; use std::collections::HashMap; diff --git a/crates/runtime/arje-brain-rules/src/lib.rs b/crates/runtime/arje-brain-rules/src/lib.rs new file mode 100644 index 0000000..bd9d698 --- /dev/null +++ b/crates/runtime/arje-brain-rules/src/lib.rs @@ -0,0 +1,13 @@ +//! arje-brain-rules — motor de reglas determinista. +//! +//! Capa base del brain: tipos de regla (triplet Subject+Event+Action), +//! `RuleEngine` con dispatch O(1) por discriminante de evento, y el +//! ejecutor async de acciones. Sin dependencias estadísticas ni de UI. + +pub mod rules; +pub mod engine; +pub mod dispatch; + +pub use rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope, TimedEvent}; +pub use engine::{EventKindDiscriminant, RuleEngine, SubjectInfo}; +pub use dispatch::{dispatch_actions, ActionSink, NullSink}; diff --git a/crates/runtime/arje-brain/src/rules.rs b/crates/runtime/arje-brain-rules/src/rules.rs similarity index 94% rename from crates/runtime/arje-brain/src/rules.rs rename to crates/runtime/arje-brain-rules/src/rules.rs index c78fa2e..78b2272 100644 --- a/crates/runtime/arje-brain/src/rules.rs +++ b/crates/runtime/arje-brain-rules/src/rules.rs @@ -7,8 +7,18 @@ use arje_card::Capability; use serde::{Deserialize, Serialize}; +use std::time::Instant; use ulid::Ulid; +/// Evento timestamped. El timestamp se conserva para futuras políticas de +/// expiración por tiempo (no sólo por count). Tipo base compartido entre +/// el motor de reglas (`engine`) y el observador estadístico (`cognitive`). +#[derive(Debug, Clone)] +pub struct TimedEvent { + pub kind: EventKind, + pub at: Instant, +} + /// Triplet [Sujeto + Evento + Acción]. Inmutable tras carga. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Rule { diff --git a/crates/runtime/arje-brain/Cargo.toml b/crates/runtime/arje-brain/Cargo.toml index a7a6a6e..bb1032f 100644 --- a/crates/runtime/arje-brain/Cargo.toml +++ b/crates/runtime/arje-brain/Cargo.toml @@ -4,8 +4,12 @@ version = "0.0.1" edition.workspace = true license.workspace = true publish.workspace = true +description = "Capa de integración del brain: introspect socket + autopromote loop + metrics HTTP + loader. Wirea arje-brain-{rules,cognitive,audit}." [dependencies] +arje-brain-rules = { path = "../arje-brain-rules" } +arje-brain-cognitive = { path = "../arje-brain-cognitive" } +arje-brain-audit = { path = "../arje-brain-audit" } arje-card = { path = "../../protocol/arje-card" } arje-cas = { path = "../arje-cas" } serde = { workspace = true } diff --git a/crates/runtime/arje-brain/src/autopromote.rs b/crates/runtime/arje-brain/src/autopromote.rs index a54929e..341a042 100644 --- a/crates/runtime/arje-brain/src/autopromote.rs +++ b/crates/runtime/arje-brain/src/autopromote.rs @@ -6,10 +6,10 @@ //! no exista ya una regla con el mismo trigger_kind (heurística simple — //! evita ráfagas de duplicados de la misma estadística). -use crate::audit::AuditAction; -use crate::crystallize::{crystal_to_rule, detect_crystals, Crystal, CrystallizationParams}; +use arje_brain_audit::audit::AuditAction; +use arje_brain_cognitive::crystallize::{crystal_to_rule, detect_crystals, Crystal, CrystallizationParams}; use crate::introspect::{append_rule_jsonl, BrainState}; -use crate::rules::EventKind; +use arje_brain_rules::rules::EventKind; use std::collections::HashSet; use std::sync::Arc; use std::time::Duration; diff --git a/crates/runtime/arje-brain/src/introspect.rs b/crates/runtime/arje-brain/src/introspect.rs index 8e4961e..8990cf9 100644 --- a/crates/runtime/arje-brain/src/introspect.rs +++ b/crates/runtime/arje-brain/src/introspect.rs @@ -4,10 +4,10 @@ //! cerebro sin tocar el bus interno del fractal. Esto separa observación de //! ejecución — la introspección es read-only por diseño. -use crate::crystallize::{detect_crystals, Crystal, CrystallizationParams}; -use crate::engine::RuleEngine; -use crate::observer::Observer; -use crate::rules::Rule; +use arje_brain_cognitive::crystallize::{detect_crystals, Crystal, CrystallizationParams}; +use arje_brain_rules::engine::RuleEngine; +use arje_brain_cognitive::observer::Observer; +use arje_brain_rules::rules::Rule; use serde::{Deserialize, Serialize}; use std::io::Write; use std::path::{Path, PathBuf}; @@ -32,7 +32,7 @@ pub struct BrainState { /// cada PromoteCrystal añade una línea (append-only) con la Rule serializada. pub rules_out: Option>, /// Audit log en memoria. Cada promote/remove deja huella aquí. - pub audit: Arc>, + pub audit: Arc>, } impl BrainState { @@ -46,7 +46,7 @@ impl BrainState { observer: Arc::new(RwLock::new(Observer::new(window_size))), params, rules_out: None, - audit: Arc::new(RwLock::new(crate::audit::AuditLog::new())), + audit: Arc::new(RwLock::new(arje_brain_audit::audit::AuditLog::new())), } } @@ -135,21 +135,21 @@ pub enum IntrospectResponse { /// Resultado de RemoveRule: true si existía, false si ya no. Removed(bool), /// Entradas del audit log (más recientes al final). - AuditEntries(Vec), + AuditEntries(Vec), /// Resultado de FlushAudit: cuántas entries se escribieron y SHA del head. Flushed { written: usize, head_sha: Option<[u8; 32]>, total_flushed: u64 }, /// Resultado de ReloadRules: número total de reglas tras el reload. Reloaded { count: usize }, /// Resultado de VerifyAudit. - AuditVerified(crate::audit::VerificationReport), + AuditVerified(arje_brain_audit::audit::VerificationReport), /// Resultado de ReplayAudit. - Replayed(crate::audit::ReplayReport), + Replayed(arje_brain_audit::audit::ReplayReport), /// Frame de streaming. El cliente lee estos en bucle hasta EOF. - AuditStreamFrame(crate::audit::AuditEntry), + AuditStreamFrame(arje_brain_audit::audit::AuditEntry), /// Resultado de GcCas: cuántos blobs eliminados y bytes liberados. GcResult { deleted: usize, freed_bytes: u64 }, /// Cristales de Burst/Silence detectados. - Patterns(Vec), + Patterns(Vec), Error(String), } @@ -306,7 +306,7 @@ impl IntrospectServer { let obs = self.state.observer.read().await; let crystals = detect_crystals(&obs, &self.state.params); match crystals.get(index) { - Some(c) => IntrospectResponse::Json(crate::crystallize::crystal_to_json_pretty(c)), + Some(c) => IntrospectResponse::Json(arje_brain_cognitive::crystallize::crystal_to_json_pretty(c)), None => IntrospectResponse::Error(format!("no crystal at index {index}")), } } @@ -317,7 +317,7 @@ impl IntrospectServer { }; match crystals.get(index) { Some(c) => { - let rule = crate::crystallize::crystal_to_rule(c); + let rule = arje_brain_cognitive::crystallize::crystal_to_rule(c); let rule_id = rule.id; let rule_json = serde_json::to_string_pretty(&rule) .unwrap_or_else(|_| "".into()); @@ -332,7 +332,7 @@ impl IntrospectServer { } // Audit entry self.state.audit.write().await.append( - crate::audit::AuditAction::PromoteCrystal { + arje_brain_audit::audit::AuditAction::PromoteCrystal { rule_id, crystal: c.clone(), } ); @@ -345,7 +345,7 @@ impl IntrospectServer { let removed = self.state.engine.write().await.remove(id); if removed { self.state.audit.write().await.append( - crate::audit::AuditAction::RemoveRule { rule_id: id } + arje_brain_audit::audit::AuditAction::RemoveRule { rule_id: id } ); } IntrospectResponse::Removed(removed) @@ -373,7 +373,7 @@ impl IntrospectServer { "audit log sin entries flushadas — nada que verificar".into() ), }; - let report = crate::audit::verify_chain_from_cas(head); + let report = arje_brain_audit::audit::verify_chain_from_cas(head); IntrospectResponse::AuditVerified(report) } IntrospectRequest::StreamAudit => { @@ -385,15 +385,15 @@ impl IntrospectServer { } IntrospectRequest::PatternCrystals => { let obs = self.state.observer.read().await; - let params = crate::crystallize::PatternParams::default(); - let patterns = crate::crystallize::detect_pattern_crystals(&obs, ¶ms); + let params = arje_brain_cognitive::crystallize::PatternParams::default(); + let patterns = arje_brain_cognitive::crystallize::detect_pattern_crystals(&obs, ¶ms); IntrospectResponse::Patterns(patterns) } IntrospectRequest::GcCas { extra_roots } => { // Reachable = audit chain desde head + extra_roots provistos. let mut reachable = std::collections::HashSet::new(); if let Some(head) = self.state.audit.read().await.last_flushed_sha() { - reachable.extend(crate::audit::reachable_from_head(head)); + reachable.extend(arje_brain_audit::audit::reachable_from_head(head)); } reachable.extend(extra_roots); match arje_cas::gc(&reachable) { @@ -410,8 +410,8 @@ impl IntrospectServer { ), }; let mut engine = self.state.engine.write().await; - *engine = crate::engine::RuleEngine::empty(); - let report = crate::audit::replay_chain(head, &mut engine); + *engine = arje_brain_rules::engine::RuleEngine::empty(); + let report = arje_brain_audit::audit::replay_chain(head, &mut engine); IntrospectResponse::Replayed(report) } IntrospectRequest::ReloadRules { path } => { @@ -430,12 +430,12 @@ impl IntrospectServer { }; // Vaciamos el engine antes de re-cargar — semántica clean-slate. let mut engine = self.state.engine.write().await; - *engine = crate::engine::RuleEngine::empty(); + *engine = arje_brain_rules::engine::RuleEngine::empty(); let count = rules.len(); for r in rules { engine.insert(r); } drop(engine); self.state.audit.write().await.append( - crate::audit::AuditAction::LoadRulesFile { + arje_brain_audit::audit::AuditAction::LoadRulesFile { path: path.to_string_lossy().into_owned(), count, } diff --git a/crates/runtime/arje-brain/src/lib.rs b/crates/runtime/arje-brain/src/lib.rs index 49ac4eb..372eeaf 100644 --- a/crates/runtime/arje-brain/src/lib.rs +++ b/crates/runtime/arje-brain/src/lib.rs @@ -1,38 +1,34 @@ -//! ente-brain: motor de reglas determinista + observador estadístico. +//! arje-brain — capa de integración del brain. //! -//! Tres capas: -//! 1. `rules` — tipos de regla (Triplet: Subject + Event + Action) -//! 2. `engine` — RuleEngine con HashMap>> -//! para dispatch O(1) -//! 3. `dispatch` — ejecutor async de Actions (vía tokio) -//! 4. `observer` — sliding window + marginales + co-ocurrencias -//! + Shannon entropy + información mutua -//! 5. `crystallize` — detección de patrones estadísticamente significativos -//! y materialización en `Rule` ejecutables -//! 6. `introspect` — Unix socket bincode API para tools externos +//! El brain se divide en tres sub-crates con un DAG de dependencias limpio: +//! - `arje-brain-rules` — motor determinista (rules + engine + dispatch) +//! - `arje-brain-cognitive` — estadística (observer + crystallize) +//! - `arje-brain-audit` — accountability (audit chain → CAS) //! -//! Diseño de inmutabilidad: -//! - Rules son `Arc` — clonar es zero-copy (refcount bump). -//! - El motor expone sólo lecturas; mutaciones pasan por `insert/remove`. -//! - Observer mantiene contadores incrementales — sin recomputación. +//! Este crate es la capa que los wirea: `introspect` (socket API), +//! `autopromote` (loop de promoción de cristales), `metrics` (HTTP) y +//! `loader` (carga de cards/rules). Re-exporta la API de los tres +//! sub-crates para compatibilidad de los consumidores históricos. -pub mod audit; -pub mod autopromote; -pub mod crystallize; -pub mod dispatch; -pub mod engine; pub mod introspect; -pub mod loader; +pub mod autopromote; pub mod metrics; -pub mod observer; -pub mod rules; +pub mod loader; + +// --- Re-export de los módulos de las 3 sub-crates --- +pub use arje_brain_rules::{dispatch, engine, rules}; +pub use arje_brain_cognitive::{crystallize, observer}; +pub use arje_brain_audit::audit; + +// --- Re-exports planos (API histórica que consumen arje-zero, chasqui) --- +pub use rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope, TimedEvent}; +pub use engine::{EventKindDiscriminant, RuleEngine, SubjectInfo}; +pub use dispatch::{dispatch_actions, ActionSink, NullSink}; +pub use crystallize::{detect_crystals, Crystal, CrystallizationParams}; +pub use observer::Observer; +pub use audit::AuditLog; pub use autopromote::{spawn_autopromote_loop, AutopromoteParams}; -pub use crystallize::{detect_crystals, Crystal, CrystallizationParams}; -pub use dispatch::{dispatch_actions, ActionSink, NullSink}; -pub use engine::{EventKindDiscriminant, RuleEngine, SubjectInfo}; -pub use introspect::{IntrospectRequest, IntrospectResponse, IntrospectServer, BrainState}; +pub use introspect::{BrainState, IntrospectRequest, IntrospectResponse, IntrospectServer}; pub use loader::{load_card_file, load_rules_file}; pub use metrics::serve_metrics; -pub use observer::{Observer, TimedEvent}; -pub use rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope}; diff --git a/crates/runtime/arje-brain/src/loader.rs b/crates/runtime/arje-brain/src/loader.rs index 2ca34ad..fba74f4 100644 --- a/crates/runtime/arje-brain/src/loader.rs +++ b/crates/runtime/arje-brain/src/loader.rs @@ -8,7 +8,7 @@ //! Ergonomía de autoría futura (RON, Dhall, etc.) se añade como ramas //! adicionales aquí cuando duela escribir JSON a mano. Hoy: una sola rama. -use crate::rules::Rule; +use arje_brain_rules::rules::Rule; use arje_card::EntityCard; use std::path::Path; use tracing::info; @@ -102,7 +102,7 @@ pub fn extract_rules_from_json(raw: &str) -> anyhow::Result> { mod tests { use super::*; use crate::introspect::append_rule_jsonl; - use crate::rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope}; + use arje_brain_rules::rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope}; use ulid::Ulid; fn sample_rule() -> Rule { diff --git a/crates/runtime/arje-brain/src/metrics.rs b/crates/runtime/arje-brain/src/metrics.rs index 06b7c79..0c201d7 100644 --- a/crates/runtime/arje-brain/src/metrics.rs +++ b/crates/runtime/arje-brain/src/metrics.rs @@ -5,7 +5,7 @@ //! `prometheus` con su Registry + encoders. use crate::introspect::BrainState; -use crate::rules::EventKind; +use arje_brain_rules::rules::EventKind; use std::net::SocketAddr; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; @@ -98,7 +98,7 @@ async fn format_metrics(state: &BrainState) -> String { } // ---- Cristales detectados (con params actuales) ---- - let crystals = crate::detect_crystals(&obs, &state.params); + let crystals = arje_brain_cognitive::detect_crystals(&obs, &state.params); out.push_str("# HELP ente_brain_crystals_total Number of crystals detected with current params.\n"); out.push_str("# TYPE ente_brain_crystals_total gauge\n"); out.push_str(&format!("ente_brain_crystals_total {}\n", crystals.len())); @@ -135,7 +135,7 @@ async fn format_metrics(state: &BrainState) -> String { // ---- Histogramas de gaps temporales (top-32 pares más frecuentes) ---- out.push_str("# HELP ente_brain_pair_gap_seconds Time gap between correlated events.\n"); out.push_str("# TYPE ente_brain_pair_gap_seconds histogram\n"); - let limits = crate::observer::GapHistogram::bucket_limits(); + let limits = arje_brain_cognitive::observer::GapHistogram::bucket_limits(); for ((a, b), hist) in obs.top_gap_pairs(32) { let labels = format!(r#"a="{}",b="{}""#, kind_label(a), kind_label(b)); for (i, &limit) in limits.iter().enumerate() {