gioser

tawasuyu/brahman

Rust 226TOML 67JSON 14HTML 12860 Definiciones

feat(nouser): Phase B-2 — daemon que publica Mónadas al Init brahman

Cierra la unificación ontológica de B-1: 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.

Cambios:

- nouser-core gana deps brahman-card + brahman-sidecar.
- bin nouser nuevo subcomando: daemon <dir>.
  1. Spawna sidecar para el engine (brahman.nouser_engine, kind=Ente):
     el "ser" que produce y administra Mónadas.
  2. Scan + cluster del directorio.
  3. Para cada Mónada, monad.to_brahman_card() + sidecar (kind=Data).
     Cada Mónada es una sesión brahman propia, con su ULID estable.
  4. Park del main thread; 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
    ...

La función de presentarse es la misma para procesos y datos. UI ve
una lista uniforme y discrimina por `kind` cuando le importa.

Costo conocido: cada Mónada consume thread + tokio runtime
current_thread (legacy del sidecar API). Para escalar a >50 Mónadas
conviene consolidar en un único runtime con N tasks. Defer a B-3.

Pendientes propuestos (en CHANGELOG):
- B-3: consolidar sidecars en un solo runtime.
- C: pseudo-embeddings + atracción por centroide.
- D: módulo nouser-nous para LLM, swappable por priority_contexts.
- Polish: labels con 2-3 componentes de path.
- Crossreferencia: Ente anuncia "procesando Mónada X", Mónada anuncia
  "siendo procesada por Ente Y".

cargo check --workspace: 0 errores, 0 warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

85886b7a3c5a997e35b52881560a765c74262733 · por Sergio · (hace 5 meses) · padre b85700c53 · ver el árbol en este commit

Delta semántico · 4

crates/modules/nouser/core/src/bin/nouser.rs

4 archivos · +123 −1

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 <sha>`.
 
 ## 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 <dir>`.
+  - 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 <dir>           recorre un directorio y lista las Mónadas detectadas");
     eprintln!("  show <dir> <prefix>  scan + detalle de la Mónada cuyo ID empieza con <prefix>");
     eprintln!("  json <dir>           scan + dump JSON de todos los manifests");
+    eprintln!("  daemon <dir>         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<dyn std::error::Error>>;
@@ -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()
+    }
+}