feat(shipote): multi-core CPU% + quota report + restart-on-failure (fase K)

- WorkspaceStats.cpu_cores via sysconf cacheado. CLI muestra
  `cpu_pct: 98.7 % (24.7% total / 4 cores)`.
- workspace_quota compara SomaSpec.rlimits contra accounting actual.
  Reporta breaches humanos. NO enforcement automático en v1.
- run_with_options(.., restart_on_failure): si exit != 0, reaper
  relaunch con backoff exponencial 200ms → 30s cap. Inner.restart_specs
  persiste el spec entre intentos.

81 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8,
shipote-core 22, shipote-discern 5, yahweh-provider-fs 3).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-11 01:32:39 +00:00
parent d8727a3038
commit 324a0c2d5d
5 changed files with 331 additions and 20 deletions
@@ -50,6 +50,10 @@ pub enum Request {
exec: String,
argv: Vec<String>,
envp: Vec<(String, String)>,
/// Si `true` y el comando muere con exit_status != 0, el reaper
/// lo relaunch con backoff exponencial.
#[serde(default)]
restart_on_failure: bool,
},
/// Lanzar un Pipeline completo dentro de un workspace.
@@ -103,6 +107,9 @@ pub enum Request {
/// Resource accounting de un workspace.
WorkspaceStats { workspace: shipote_card::WorkspaceId },
/// Reporte de quotas (rlimits declarados vs uso actual).
WorkspaceQuota { workspace: shipote_card::WorkspaceId },
/// Detener selectivamente los comandos de un pipeline (no el workspace
/// entero). `grace_ms`: SIGTERM → wait → SIGKILL.
PipelineStop {
@@ -194,6 +201,10 @@ pub enum Response {
info: WorkspaceStatsInfo,
},
WorkspaceQuota {
info: QuotaReportInfo,
},
FlowList {
items: Vec<FlowInfo>,
},
@@ -208,6 +219,13 @@ pub enum Response {
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuotaReportInfo {
pub mem_limit: Option<u64>,
pub nproc_limit: Option<u32>,
pub breaches: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceStatsInfo {
pub commands_alive: u32,
@@ -218,10 +236,16 @@ pub struct WorkspaceStatsInfo {
pub cpu_usec: Option<u64>,
#[serde(default)]
pub cpu_percent: Option<f32>,
#[serde(default = "default_cpu_cores")]
pub cpu_cores: u32,
pub source: String,
pub uptime_ms: u64,
}
fn default_cpu_cores() -> u32 {
1
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowInfo {
pub pipeline: Ulid,