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>
This commit is contained in:
@@ -6,6 +6,60 @@ ratio/diff ver `git show <sha>`.
|
|||||||
|
|
||||||
## 2026-05-08
|
## 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)
|
### 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
|
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
|
los procesos. Una Mónada Nouser y un Ente Brahman son ambos "entidades
|
||||||
|
|||||||
Generated
+2
@@ -6060,6 +6060,8 @@ dependencies = [
|
|||||||
name = "nouser-core"
|
name = "nouser-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"brahman-card",
|
||||||
|
"brahman-sidecar",
|
||||||
"nouser-card",
|
"nouser-card",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ description = "Nouser — explorador de Mónadas: scanner, clustering determinis
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nouser-card = { path = "../card" }
|
nouser-card = { path = "../card" }
|
||||||
|
brahman-card = { path = "../../../core/brahman-card" }
|
||||||
|
brahman-sidecar = { path = "../../../shared/brahman-sidecar" }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ fn main() -> ExitCode {
|
|||||||
"scan" => cmd_scan(rest),
|
"scan" => cmd_scan(rest),
|
||||||
"show" => cmd_show(rest),
|
"show" => cmd_show(rest),
|
||||||
"json" => cmd_json(rest),
|
"json" => cmd_json(rest),
|
||||||
|
"daemon" => cmd_daemon(rest),
|
||||||
"--help" | "-h" | "help" => {
|
"--help" | "-h" | "help" => {
|
||||||
print_usage(&prog);
|
print_usage(&prog);
|
||||||
return ExitCode::SUCCESS;
|
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!(" 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!(" 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!(" json <dir> scan + dump JSON de todos los manifests");
|
||||||
|
eprintln!(" daemon <dir> scan + sidecarea cada Mónada al Init brahman");
|
||||||
eprintln!();
|
eprintln!();
|
||||||
eprintln!("env:");
|
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>>;
|
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)?);
|
println!("{}", serde_json::to_string_pretty(&manifests)?);
|
||||||
Ok(())
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user