tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definitions

arje-link: resync del espejo con arje-bus (Subscribe 13→14 + Parked/Refloored

arje inserto StopCardFromDisk en el medio de BusRequest el 2026-06-26 y
corrio Subscribe a indice 14 — el puente estaba suscribiendose con el byte
viejo desde entonces (roto en silencio; lo delato el golden test de arje-bus
el 2026-07-16, lado tawasuyu 41f4accc7). Ademas el espejo de BusEvent no
conocia EnteParked/EnteRefloored: se agregan como consumo-sin-senal
(to_lifecycle -> Option). Tests unit+e2e actualizados y verdes.

49fd38d53b01a79a063cf88d9e00edfc90aaebf7 · by sergio · (3 months ago) · parent 87634f319 · browse the tree at this commit

Semantic delta · 7

crates/hammerd/src/arje_link.rs

crates/hammerd/tests/arje_link_e2e.rs

2 files · +29 −18

diff --git a/crates/hammerd/src/arje_link.rs b/crates/hammerd/src/arje_link.rs
index 6198db84..bebf744f 100644
--- a/crates/hammerd/src/arje_link.rs
+++ b/crates/hammerd/src/arje_link.rs
@@ -40,8 +40,10 @@ const MAX_FRAME: usize = 1 << 20;
 //
 // Discriminantes (postcard = varint del índice de variante), verificados contra arje-bus:
 //   BusPayload: Request=0, Response=1, Event=2
-//   BusRequest::Subscribe = 13   (último; ver arje-bus/src/lib.rs)
-//   BusEvent: EnteCrashed=0, EnteRestarting=1, EnteExited=2
+//   BusRequest::Subscribe = 14   (arje insertó StopCardFromDisk el 2026-06-26 y corrió
+//                                 el índice 13→14; re-sincronizado 2026-07-16 — ver el
+//                                 golden test de arje-bus `contrato_wire`)
+//   BusEvent: EnteCrashed=0, EnteRestarting=1, EnteExited=2, EnteParked=3, EnteRefloored=4
 //   LifecycleStatus: Exited=0, Killed=1
 
 #[derive(Deserialize, Debug)]
@@ -73,6 +75,12 @@ enum BusEvent {
     EnteCrashed { id: Ulid, label: String, status: LifecycleStatus },
     EnteRestarting { id: Ulid, label: String, delay_ms: u64 },
     EnteExited { id: Ulid, label: String },
+    /// Ente `Restart` aparcado: su "piso" (una capability) perdió proveedor.
+    /// No es un crash — no hay señal para la capa de IA; se consume del wire
+    /// para no tirar el frame como indescifrable.
+    EnteParked { id: Ulid, label: String },
+    /// El piso volvió y arje re-erigió al Ente aparcado. Ídem: sólo consumo.
+    EnteRefloored { id: Ulid, label: String },
 }
 
 #[derive(Deserialize, Debug)]
@@ -84,29 +92,31 @@ enum LifecycleStatus {
 impl BusEvent {
     /// Traduce el evento de arje a la señal normalizada de hammer. El `label` del Ente es el
     /// `service`; una muerte por señal usa la convención de shell `128 + signum` para que el
-    /// código sea siempre ≠ 0.
-    fn to_lifecycle(self) -> Lifecycle {
+    /// código sea siempre ≠ 0. `None` = evento consumido sin señal (Parked/Refloored: son
+    /// vaivén del piso gráfico, no ciclo de vida que le importe a la capa de IA).
+    fn to_lifecycle(self) -> Option<Lifecycle> {
         match self {
             BusEvent::EnteCrashed { label, status, .. } => {
                 let code = match status {
                     LifecycleStatus::Exited(c) => c,
                     LifecycleStatus::Killed(sig) => 128 + sig,
                 };
-                Lifecycle { service: label, status: LifeStatus::Crashed { code } }
+                Some(Lifecycle { service: label, status: LifeStatus::Crashed { code } })
             }
             BusEvent::EnteRestarting { label, delay_ms, .. } => {
-                Lifecycle { service: label, status: LifeStatus::Restarting { delay_ms } }
+                Some(Lifecycle { service: label, status: LifeStatus::Restarting { delay_ms } })
             }
             BusEvent::EnteExited { label, .. } => {
-                Lifecycle { service: label, status: LifeStatus::Exited }
+                Some(Lifecycle { service: label, status: LifeStatus::Exited })
             }
+            BusEvent::EnteParked { .. } | BusEvent::EnteRefloored { .. } => None,
         }
     }
 }
 
 /// Cuerpo del frame `Subscribe` (sin el prefijo de longitud), hand-encodeado: `from=None` +
-/// `seq` (varint) + `BusPayload::Request`(0) + `BusRequest::Subscribe`(13). Verificado
-/// byte-a-byte contra `postcard::to_stdvec` de arje-bus.
+/// `seq` (varint) + `BusPayload::Request`(0) + `BusRequest::Subscribe`(14). Verificado
+/// byte-a-byte contra `postcard::to_stdvec` de arje-bus (golden test `contrato_wire`).
 fn subscribe_body(seq: u64) -> Vec<u8> {
     let mut v = vec![0x00u8]; // Option::None
     let mut s = seq; // u64 como varint LEB128
@@ -121,7 +131,7 @@ fn subscribe_body(seq: u64) -> Vec<u8> {
         }
     }
     v.push(0x00); // BusPayload::Request
-    v.push(0x0D); // BusRequest::Subscribe (índice 13)
+    v.push(0x0E); // BusRequest::Subscribe (índice 14 desde 2026-06-26)
     v
 }
 
@@ -165,9 +175,10 @@ pub(crate) fn run(sock: &PathBuf, bus: &EventBus) -> std::io::Result<()> {
         };
         match msg.payload {
             BusPayload::Event(ev) => {
-                let life = ev.to_lifecycle();
-                if let Some(event) = crashes::to_event(&life) {
-                    bus.publish(&event);
+                if let Some(life) = ev.to_lifecycle() {
+                    if let Some(event) = crashes::to_event(&life) {
+                        bus.publish(&event);
+                    }
                 }
             }
             // El ack del Subscribe y cualquier otra respuesta: nada que hacer.
@@ -198,10 +209,10 @@ mod tests {
 
     #[test]
     fn subscribe_body_es_el_frame_esperado() {
-        // Verificado contra arje-bus real: [from=None, seq=1, Request, Subscribe=13].
-        assert_eq!(subscribe_body(1), vec![0x00, 0x01, 0x00, 0x0D]);
+        // Verificado contra arje-bus real: [from=None, seq=1, Request, Subscribe=14].
+        assert_eq!(subscribe_body(1), vec![0x00, 0x01, 0x00, 0x0E]);
         // seq grande → varint multibyte (300 = 0xAC 0x02).
-        assert_eq!(subscribe_body(300), vec![0x00, 0xAC, 0x02, 0x00, 0x0D]);
+        assert_eq!(subscribe_body(300), vec![0x00, 0xAC, 0x02, 0x00, 0x0E]);
     }
 
     /// Round-trip local con el MISMO `postcard`/`ulid` que arje-bus: serializamos un
@@ -250,7 +261,7 @@ mod tests {
         let msg: BusMessage = postcard::from_bytes(&bytes).unwrap();
         match msg.payload {
             BusPayload::Event(ev) => {
-                let life = ev.to_lifecycle();
+                let life = ev.to_lifecycle().expect("un crash sí produce señal");
                 assert_eq!(life.service, "demonio");
                 // Killed(11) → 128 + 11 = 139.
                 assert_eq!(life.status, LifeStatus::Crashed { code: 139 });
diff --git a/crates/hammerd/tests/arje_link_e2e.rs b/crates/hammerd/tests/arje_link_e2e.rs
index 5364695e..725d18b9 100644
--- a/crates/hammerd/tests/arje_link_e2e.rs
+++ b/crates/hammerd/tests/arje_link_e2e.rs
@@ -81,7 +81,7 @@ fn crashed_de_un_bus_falso_llega_como_event_crashed() {
         let n = u32::from_be_bytes(len) as usize;
         let mut body = vec![0u8; n];
         conn.read_exact(&mut body).unwrap();
-        assert_eq!(body, vec![0x00, 0x01, 0x00, 0x0D], "frame Subscribe inesperado");
+        assert_eq!(body, vec![0x00, 0x01, 0x00, 0x0E], "frame Subscribe inesperado");
 
         // 2) Le inyectamos un EnteCrashed por señal (SIGSEGV=11 → 128+11=139).
         let id = Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap();