gioser

tawasuyu/brahman

Rust 226TOML 67JSON 14HTML 12860 Definitions

feat(yahweh-shell): primer módulo brahman vivo

yahweh-shell se presenta al Init brahman como módulo Widget mediante un
sidecar en thread aparte. La GUI GPUI levanta normalmente; el sidecar
mantiene la sesión brahman en paralelo, desacoplado.

Cambios:

- crates/apps/yahweh-shell/Cargo.toml: deps brahman-card, brahman-handshake,
  ulid.
- crates/apps/yahweh-shell/src/brahman_client.rs: thread con tokio
  current_thread runtime que arma una Card, llama Client::connect, y
  loop de pings cada 30s. Si el Init no está disponible, loggea y
  termina — yahweh sigue funcionando standalone.
- crates/apps/yahweh-shell/src/main.rs: brahman_client::spawn() antes
  de Application::new(). El spawn no bloquea.

Card declarada por yahweh:
- label: "brahman.ui_engine"
- lifecycle: Widget
- payload: Virtual (yahweh no se inicia desde el Init, se presenta)
- supervision: Delegate
- permissions: filesystem read-write (persiste layout.json), IPC wit-v1
- flow.input: render-data (json)
- flow.output: user-intent (json)

Validación end-to-end:
  $ ente-zero &
  $ probe                          → session=...8G, init_attached=true
  $ yahweh                         → [brahman] attached: session=...Y7

Ambos clientes (probe + yahweh sidecar) se registran en el broker del
Init en sesiones distintas. yahweh es el primer módulo "real" — no un
tester — que vive como nodo del fractal mientras corre.

Tests: 27/27 verdes. cargo check --workspace: 0 errores.

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

595f68e2526c8dae4e8bc2a810f671bcdcc9f018 · by Sergio · (5 months ago) · parent df9d10cc5 · browse the tree at this commit

Semantic delta · 7

crates/apps/yahweh-shell/src/brahman_client.rs

crates/apps/yahweh-shell/src/main.rs

4 files · +134 −0

diff --git a/Cargo.lock b/Cargo.lock
index 2c2e0b1..ae516db 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11439,11 +11439,14 @@ dependencies = [
 name = "yahweh-shell"
 version = "0.1.0"
 dependencies = [
+ "brahman-card",
+ "brahman-handshake",
  "gpui",
  "notify",
  "serde",
  "serde_json",
  "tokio",
+ "ulid",
  "yahweh-bus",
  "yahweh-core",
  "yahweh-database-explorer",
diff --git a/crates/apps/yahweh-shell/Cargo.toml b/crates/apps/yahweh-shell/Cargo.toml
index 3d66cae..f42a371 100644
--- a/crates/apps/yahweh-shell/Cargo.toml
+++ b/crates/apps/yahweh-shell/Cargo.toml
@@ -26,6 +26,11 @@ serde = { workspace = true }
 serde_json = { workspace = true }
 notify = { workspace = true }
 
+# Brahman protocol — sidecar thread que se presenta al Init.
+brahman-card = { path = "../../core/brahman-card" }
+brahman-handshake = { path = "../../core/brahman-handshake" }
+ulid = { workspace = true }
+
 [[bin]]
 name = "yahweh"
 path = "src/main.rs"
diff --git a/crates/apps/yahweh-shell/src/brahman_client.rs b/crates/apps/yahweh-shell/src/brahman_client.rs
new file mode 100644
index 0000000..cc87c90
--- /dev/null
+++ b/crates/apps/yahweh-shell/src/brahman_client.rs
@@ -0,0 +1,121 @@
+//! Sidecar brahman: yahweh se presenta al Init como módulo `Widget`.
+//!
+//! Vive en un thread aparte con tokio runtime current_thread, desacoplado
+//! de GPUI. Si el Init no está disponible, loggea y termina — yahweh
+//! sigue funcionando standalone. Si conecta, mantiene la sesión viva
+//! con pings periódicos hasta que la GUI termine o el server caiga.
+//!
+//! Card declarada:
+//! - label: `brahman.ui_engine`
+//! - lifecycle: `Widget`
+//! - flow.input: `render-data` (json)
+//! - flow.output: `user-intent` (json)
+//! - permissions: filesystem read-write (yahweh persiste `layout.json`),
+//!   IPC `wit-v1`.
+
+use std::collections::BTreeSet;
+use std::time::Duration;
+
+use brahman_card::{
+    Card, Flow, Flows, FsPolicy, IpcPolicy, Lifecycle, Payload, Permissions, Priority, Supervision,
+    TypeRef, CARD_SCHEMA_VERSION,
+};
+use brahman_handshake::{client::Client, transport};
+use ulid::Ulid;
+
+/// Período entre pings al Init.
+const PING_INTERVAL: Duration = Duration::from_secs(30);
+
+/// Spawn del sidecar brahman. No-op si el thread no se puede crear.
+/// Devuelve inmediatamente; la conexión se establece en background.
+pub fn spawn() {
+    let result = std::thread::Builder::new()
+        .name("brahman-client".into())
+        .spawn(run_thread);
+    if let Err(e) = result {
+        eprintln!("[brahman] no se pudo spawnear el sidecar: {e}");
+    }
+}
+
+fn run_thread() {
+    let rt = match tokio::runtime::Builder::new_current_thread()
+        .enable_io()
+        .enable_time()
+        .build()
+    {
+        Ok(rt) => rt,
+        Err(e) => {
+            eprintln!("[brahman] tokio runtime falló: {e}");
+            return;
+        }
+    };
+    rt.block_on(run_client());
+}
+
+async fn run_client() {
+    let path = transport::default_socket_path();
+    let card = build_card();
+
+    let mut client = match Client::connect(&path, card).await {
+        Ok(c) => {
+            eprintln!(
+                "[brahman] attached: session={} init_attached={} server={}",
+                c.session(),
+                c.server_info().init_attached,
+                c.server_info().server_version
+            );
+            c
+        }
+        Err(e) => {
+            eprintln!("[brahman] no conectado a {} ({e})", path.display());
+            return;
+        }
+    };
+
+    loop {
+        tokio::time::sleep(PING_INTERVAL).await;
+        if let Err(e) = client.ping().await {
+            eprintln!("[brahman] ping falló: {e}");
+            return;
+        }
+    }
+}
+
+fn build_card() -> Card {
+    Card {
+        schema_version: CARD_SCHEMA_VERSION,
+        id: Ulid::new(),
+        lineage: None,
+        label: "brahman.ui_engine".into(),
+        provides: BTreeSet::new(),
+        requires: BTreeSet::new(),
+        payload: Payload::Virtual,
+        supervision: Supervision::Delegate,
+        lifecycle: Lifecycle::Widget,
+        priority: Priority::Normal,
+        permissions: Permissions {
+            filesystem: FsPolicy::ReadWrite,
+            ipc: IpcPolicy {
+                allow: vec!["wit-v1".into()],
+            },
+            ..Default::default()
+        },
+        flow: Flows {
+            input: vec![Flow {
+                name: "render-data".into(),
+                ty: TypeRef::Primitive {
+                    name: "json".into(),
+                },
+                pin_to: None,
+            }],
+            output: vec![Flow {
+                name: "user-intent".into(),
+                ty: TypeRef::Primitive {
+                    name: "json".into(),
+                },
+                pin_to: None,
+            }],
+        },
+        ..Default::default()
+    }
+}
diff --git a/crates/apps/yahweh-shell/src/main.rs b/crates/apps/yahweh-shell/src/main.rs
index 9a31784..8afbea6 100644
--- a/crates/apps/yahweh-shell/src/main.rs
+++ b/crates/apps/yahweh-shell/src/main.rs
@@ -6,6 +6,7 @@
 //! eventos tipados de los explorers (FileExplorer, DatabaseExplorer)
 //! traducidos a AppEvent.
 
+mod brahman_client;
 mod hot_reload;
 mod layout_host;
 mod layout_model;
@@ -26,6 +27,10 @@ use crate::persister::Persister;
 const LAYOUT_PATH: &str = "layout.json";
 
 fn main() {
+    // Sidecar brahman: yahweh se presenta al Init antes de levantar GPUI.
+    // No bloquea: si el Init no está, el thread loggea y termina.
+    brahman_client::spawn();
+
     Application::new().run(|cx: &mut App| {
         Theme::install_default(cx);