diff --git a/CHANGELOG.md b/CHANGELOG.md index 24feb81..0f4e4c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,60 @@ ratio/diff ver `git show `. ## 2026-05-08 +### feat(nouser): Phase B-2 — daemon que publica Mónadas al Init +Cierra la unificación: el `nouser daemon` se sidecarea como Ente y +publica cada Mónada como su propia sesión Data. Un solo +`brahman-status` muestra procesos y datos en la misma lista, exactamente +como buscaba el diseño. + +Cambios: + +- `crates/modules/nouser/core/Cargo.toml`: deps nuevas `brahman-card` + y `brahman-sidecar`. +- `crates/modules/nouser/core/src/bin/nouser.rs`: subcomando + `daemon `. + - Spawna un sidecar para el "engine" (`brahman.nouser_engine`, + kind=Ente) — el ser que produce y administra Mónadas. + - Scan + cluster del dir. + - Para cada Mónada, llama `monad.to_brahman_card()` y spawnea un + sidecar (kind=Data). Cada Mónada es una sesión brahman propia + con su ULID estable. + - Park del thread principal: los sidecars siguen pingueando. + +Validación end-to-end: + + $ ente-zero & + $ NOUSER_MIN_FILES=5 nouser daemon crates/core & + $ brahman-status + + Sessions (6): + [ente] ... brahman.nouser_engine lifecycle=Daemon + [data] ... src summary: 5 archivos en crates/core/brahman-admin/src + members: 5 (dispersion=0.00) + lens hint: code + [data] ... src summary: 11 archivos en crates/core/ente-brain/src + ... + [data] ... graph summary: 7 archivos en crates/core/ente-zero/src/graph + +El protocolo de presentación es uno solo: la Card. La función — anunciar +identidad, exponer metadata, ser descubierto — es idéntica para procesos +vivos y agrupaciones de datos. La UI lo ve como una lista uniforme. + +Costo conocido: cada Mónada consume un thread + tokio runtime +current_thread (legacy del sidecar API). Para muchas Mónadas (>50) +conviene consolidar en un único runtime con N tasks. Defer a Phase B-3. + +Pendientes propuestos: +- **B-3**: consolidar todos los sidecars en un único runtime tokio + para no spawnear N threads. +- **C**: pseudo-embeddings + atracción por centroide. +- **D**: módulo `nouser-nous` para LLM, swappable por priority_contexts. +- **Polish**: labels con 2-3 componentes del path. +- **Crossreferencia**: que un Ente pueda anunciar "estoy procesando la + Mónada X" y la Mónada anuncie "Ente Y me está procesando". + +cargo check --workspace: 0 errores, 0 warnings. + ### feat: Phase B-1 — unificación ontológica de Cards (Ente ↔ Data) La Card es **el** protocolo de presentación del ecosistema, no sólo de los procesos. Una Mónada Nouser y un Ente Brahman son ambos "entidades diff --git a/Cargo.lock b/Cargo.lock index baaf039..9bd57dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6060,6 +6060,8 @@ dependencies = [ name = "nouser-core" version = "0.1.0" dependencies = [ + "brahman-card", + "brahman-sidecar", "nouser-card", "serde", "serde_json", diff --git a/crates/modules/nouser/core/Cargo.toml b/crates/modules/nouser/core/Cargo.toml index 023d764..edfad77 100644 --- a/crates/modules/nouser/core/Cargo.toml +++ b/crates/modules/nouser/core/Cargo.toml @@ -10,6 +10,8 @@ description = "Nouser — explorador de Mónadas: scanner, clustering determinis [dependencies] nouser-card = { path = "../card" } +brahman-card = { path = "../../../core/brahman-card" } +brahman-sidecar = { path = "../../../shared/brahman-sidecar" } serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } diff --git a/crates/modules/nouser/core/src/bin/nouser.rs b/crates/modules/nouser/core/src/bin/nouser.rs index 03fa85d..f073033 100644 --- a/crates/modules/nouser/core/src/bin/nouser.rs +++ b/crates/modules/nouser/core/src/bin/nouser.rs @@ -34,6 +34,7 @@ fn main() -> ExitCode { "scan" => cmd_scan(rest), "show" => cmd_show(rest), "json" => cmd_json(rest), + "daemon" => cmd_daemon(rest), "--help" | "-h" | "help" => { print_usage(&prog); return ExitCode::SUCCESS; @@ -61,9 +62,11 @@ fn print_usage(prog: &str) { eprintln!(" scan recorre un directorio y lista las Mónadas detectadas"); eprintln!(" show scan + detalle de la Mónada cuyo ID empieza con "); eprintln!(" json scan + dump JSON de todos los manifests"); + eprintln!(" daemon scan + sidecarea cada Mónada al Init brahman"); eprintln!(); eprintln!("env:"); - eprintln!(" NOUSER_MIN_FILES mínimo de archivos por Mónada (default: 3)"); + eprintln!(" NOUSER_MIN_FILES mínimo de archivos por Mónada (default: 3)"); + eprintln!(" BRAHMAN_INIT_SOCKET socket del Init (heredado de brahman-handshake)"); } type Cmd = Result<(), Box>; @@ -155,3 +158,64 @@ fn cmd_json(args: &[String]) -> Cmd { println!("{}", serde_json::to_string_pretty(&manifests)?); Ok(()) } + +fn cmd_daemon(args: &[String]) -> Cmd { + let dir = require_dir(args)?; + + // 1. El propio engine se presenta como Ente. + let engine_card = build_engine_card(); + eprintln!( + "nouser daemon: publicando engine '{}' (kind=Ente)", + engine_card.label + ); + brahman_sidecar::spawn(engine_card); + + // 2. Scan y cluster. + let (db, n_files) = run_scan(&dir)?; + eprintln!( + "nouser daemon: {} archivos en {}, {} mónadas detectadas", + n_files, + dir.display(), + db.monad_count() + ); + + // 3. Cada Mónada se presenta como Card de tipo Data. + let mut handles = Vec::with_capacity(db.monad_count()); + for monad in db.monads() { + let card = monad.to_brahman_card(); + match brahman_sidecar::spawn_with_handle(brahman_sidecar::SidecarConfig::new(card)) { + Ok(h) => handles.push(h), + Err(e) => eprintln!( + "nouser daemon: falló sidecar para mónada '{}': {e}", + monad.label + ), + } + } + eprintln!( + "nouser daemon: {} sidecars activos (1 ente + {} data). Ctrl-C para terminar.", + handles.len() + 1, + handles.len() + ); + + // 4. Park: el proceso principal queda dormido, los sidecars siguen + // pingueando en sus threads. + std::thread::park(); + Ok(()) +} + +/// Card del propio engine (kind=Ente). Es el "ser" que produce y +/// administra Mónadas; aparece en brahman-status junto a sus Mónadas. +fn build_engine_card() -> brahman_card::Card { + use brahman_card::{ulid::Ulid, Card, CardKind, Lifecycle, Payload, Priority, Supervision}; + Card { + schema_version: brahman_card::CARD_SCHEMA_VERSION, + id: Ulid::new(), + label: "brahman.nouser_engine".into(), + payload: Payload::Virtual, + supervision: Supervision::Delegate, + lifecycle: Lifecycle::Daemon, + priority: Priority::Normal, + kind: CardKind::Ente, + ..Default::default() + } +}