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.
This commit is contained in:
@@ -40,8 +40,10 @@ const MAX_FRAME: usize = 1 << 20;
|
|||||||
//
|
//
|
||||||
// Discriminantes (postcard = varint del índice de variante), verificados contra arje-bus:
|
// Discriminantes (postcard = varint del índice de variante), verificados contra arje-bus:
|
||||||
// BusPayload: Request=0, Response=1, Event=2
|
// BusPayload: Request=0, Response=1, Event=2
|
||||||
// BusRequest::Subscribe = 13 (último; ver arje-bus/src/lib.rs)
|
// BusRequest::Subscribe = 14 (arje insertó StopCardFromDisk el 2026-06-26 y corrió
|
||||||
// BusEvent: EnteCrashed=0, EnteRestarting=1, EnteExited=2
|
// 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
|
// LifecycleStatus: Exited=0, Killed=1
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@@ -73,6 +75,12 @@ enum BusEvent {
|
|||||||
EnteCrashed { id: Ulid, label: String, status: LifecycleStatus },
|
EnteCrashed { id: Ulid, label: String, status: LifecycleStatus },
|
||||||
EnteRestarting { id: Ulid, label: String, delay_ms: u64 },
|
EnteRestarting { id: Ulid, label: String, delay_ms: u64 },
|
||||||
EnteExited { id: Ulid, label: String },
|
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)]
|
#[derive(Deserialize, Debug)]
|
||||||
@@ -84,29 +92,31 @@ enum LifecycleStatus {
|
|||||||
impl BusEvent {
|
impl BusEvent {
|
||||||
/// Traduce el evento de arje a la señal normalizada de hammer. El `label` del Ente es el
|
/// 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
|
/// `service`; una muerte por señal usa la convención de shell `128 + signum` para que el
|
||||||
/// código sea siempre ≠ 0.
|
/// código sea siempre ≠ 0. `None` = evento consumido sin señal (Parked/Refloored: son
|
||||||
fn to_lifecycle(self) -> Lifecycle {
|
/// 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 {
|
match self {
|
||||||
BusEvent::EnteCrashed { label, status, .. } => {
|
BusEvent::EnteCrashed { label, status, .. } => {
|
||||||
let code = match status {
|
let code = match status {
|
||||||
LifecycleStatus::Exited(c) => c,
|
LifecycleStatus::Exited(c) => c,
|
||||||
LifecycleStatus::Killed(sig) => 128 + sig,
|
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, .. } => {
|
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, .. } => {
|
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` +
|
/// Cuerpo del frame `Subscribe` (sin el prefijo de longitud), hand-encodeado: `from=None` +
|
||||||
/// `seq` (varint) + `BusPayload::Request`(0) + `BusRequest::Subscribe`(13). Verificado
|
/// `seq` (varint) + `BusPayload::Request`(0) + `BusRequest::Subscribe`(14). Verificado
|
||||||
/// byte-a-byte contra `postcard::to_stdvec` de arje-bus.
|
/// byte-a-byte contra `postcard::to_stdvec` de arje-bus (golden test `contrato_wire`).
|
||||||
fn subscribe_body(seq: u64) -> Vec<u8> {
|
fn subscribe_body(seq: u64) -> Vec<u8> {
|
||||||
let mut v = vec![0x00u8]; // Option::None
|
let mut v = vec![0x00u8]; // Option::None
|
||||||
let mut s = seq; // u64 como varint LEB128
|
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(0x00); // BusPayload::Request
|
||||||
v.push(0x0D); // BusRequest::Subscribe (índice 13)
|
v.push(0x0E); // BusRequest::Subscribe (índice 14 desde 2026-06-26)
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,9 +175,10 @@ pub(crate) fn run(sock: &PathBuf, bus: &EventBus) -> std::io::Result<()> {
|
|||||||
};
|
};
|
||||||
match msg.payload {
|
match msg.payload {
|
||||||
BusPayload::Event(ev) => {
|
BusPayload::Event(ev) => {
|
||||||
let life = ev.to_lifecycle();
|
if let Some(life) = ev.to_lifecycle() {
|
||||||
if let Some(event) = crashes::to_event(&life) {
|
if let Some(event) = crashes::to_event(&life) {
|
||||||
bus.publish(&event);
|
bus.publish(&event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// El ack del Subscribe y cualquier otra respuesta: nada que hacer.
|
// El ack del Subscribe y cualquier otra respuesta: nada que hacer.
|
||||||
@@ -198,10 +209,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn subscribe_body_es_el_frame_esperado() {
|
fn subscribe_body_es_el_frame_esperado() {
|
||||||
// Verificado contra arje-bus real: [from=None, seq=1, Request, Subscribe=13].
|
// Verificado contra arje-bus real: [from=None, seq=1, Request, Subscribe=14].
|
||||||
assert_eq!(subscribe_body(1), vec![0x00, 0x01, 0x00, 0x0D]);
|
assert_eq!(subscribe_body(1), vec![0x00, 0x01, 0x00, 0x0E]);
|
||||||
// seq grande → varint multibyte (300 = 0xAC 0x02).
|
// 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
|
/// 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();
|
let msg: BusMessage = postcard::from_bytes(&bytes).unwrap();
|
||||||
match msg.payload {
|
match msg.payload {
|
||||||
BusPayload::Event(ev) => {
|
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");
|
assert_eq!(life.service, "demonio");
|
||||||
// Killed(11) → 128 + 11 = 139.
|
// Killed(11) → 128 + 11 = 139.
|
||||||
assert_eq!(life.status, LifeStatus::Crashed { code: 139 });
|
assert_eq!(life.status, LifeStatus::Crashed { code: 139 });
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ fn crashed_de_un_bus_falso_llega_como_event_crashed() {
|
|||||||
let n = u32::from_be_bytes(len) as usize;
|
let n = u32::from_be_bytes(len) as usize;
|
||||||
let mut body = vec![0u8; n];
|
let mut body = vec![0u8; n];
|
||||||
conn.read_exact(&mut body).unwrap();
|
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).
|
// 2) Le inyectamos un EnteCrashed por señal (SIGSEGV=11 → 128+11=139).
|
||||||
let id = Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap();
|
let id = Ulid::from_string("01ARZ3NDEKTSV4RRFFQ69G5FAV").unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user