feat(sandokan-core): B1.1 — contrato del orquestador

Primer crate de la Fase B. Define SOLO el contrato del orquestador
sandokan (library horizontal embebible, no daemon supremo):

- Intent / ExecContext / IsolationLevel — qué orquestar
- ExecHandle — referencia a una entidad encarnada
- LifecycleEvent / TelemetryFrame — observabilidad (wire types)
- EngineError — taxonomía de fallas
- trait Engine — run/stop/list/status/telemetry (poll-based, sin
  streams sobre trait objects, para que las 3 impls lo cumplan
  uniformemente)

Las impls concretas (LocalEngine, DaemonEngine, RemoteEngine) vendrán
en crates separados (sandokan-local, sandokan-daemon, sandokan-remote).

3 tests verdes. cargo check --workspace verde.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-20 00:38:22 +00:00
parent f8a2547b45
commit af5d4a1f22
8 changed files with 272 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
//! Errores del orquestador.
use ulid::Ulid;
/// Falla de una operación del `Engine`. Las impls concretas mapean sus
/// errores internos (encarnación, IPC, SSH) a estas variantes.
#[derive(Debug, thiserror::Error)]
pub enum EngineError {
/// No existe ninguna entidad activa con ese `card_id`.
#[error("card `{0}` no encontrada")]
NotFound(Ulid),
/// La encarnación falló (clone/namespaces/exec).
#[error("encarnación falló: {0}")]
Incarnate(String),
/// Falla de transporte (Unix socket del daemon, túnel SSH).
#[error("transporte: {0}")]
Transport(String),
/// La intención es inconsistente (Card inválida, contexto imposible).
#[error("intención inválida: {0}")]
InvalidIntent(String),
/// La operación excedió su deadline.
#[error("timeout")]
Timeout,
}