feat(shipote): drain shutdown + persist live pipelines + batched query (fase N)
- Daemon SIGTERM/SIGINT: snapshot ANTES, stop_with_grace(1s) de todos los workspaces DESPUÉS. Grace permite app-level cleanup. - Snapshot v3 con live_pipelines: pipeline_supervisors se persisten; daemon relanza al restore con sus recursos (Incarnator+DiscernPipeline). RestoreOutcome separado para que core no necesite incarnator. Forward-compat v1/v2 via #[serde(default)]. - WorkspaceFullSummary: stats+quota+commands+flow_sockets en 1 roundtrip. Shell reduce N×4 requests/probe a N×1 + 4 globales. 83 tests pasan (ente-incarnate 16, nouser-core 27, shipote-card 8, shipote-core 24, shipote-discern 5, yahweh-provider-fs 3). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -174,42 +174,25 @@ fn probe_blocking(path: &std::path::Path) -> Result<Snapshot, String> {
|
||||
other => return Err(format!("unexpected list resp: {other:?}")),
|
||||
};
|
||||
|
||||
// Commands por workspace.
|
||||
// Batched: stats+quota+commands+flow_sockets en 1 roundtrip por ws.
|
||||
let mut commands_map = std::collections::BTreeMap::new();
|
||||
let mut fresh_stats = std::collections::BTreeMap::new();
|
||||
let mut quotas = std::collections::BTreeMap::new();
|
||||
for w in &workspaces {
|
||||
write_frame(&mut stream, &Request::CommandList { workspace: w.id })
|
||||
write_frame(&mut stream, &Request::WorkspaceFullSummary { workspace: w.id })
|
||||
.await
|
||||
.map_err(|e| format!("write commands: {e}"))?;
|
||||
.map_err(|e| format!("write summary: {e}"))?;
|
||||
let resp: Response = read_frame(&mut stream)
|
||||
.await
|
||||
.map_err(|e| format!("read commands: {e}"))?;
|
||||
if let Response::CommandList { items } = resp {
|
||||
if !items.is_empty() {
|
||||
commands_map.insert(w.id.to_string(), items);
|
||||
.map_err(|e| format!("read summary: {e}"))?;
|
||||
if let Response::WorkspaceFullSummary { stats, quota, commands, .. } = resp {
|
||||
let key = w.id.to_string();
|
||||
fresh_stats.insert(key.clone(), stats);
|
||||
quotas.insert(key.clone(), quota);
|
||||
if !commands.is_empty() {
|
||||
commands_map.insert(key, commands);
|
||||
}
|
||||
}
|
||||
// Stats por workspace.
|
||||
write_frame(&mut stream, &Request::WorkspaceStats { workspace: w.id })
|
||||
.await
|
||||
.map_err(|e| format!("write stats: {e}"))?;
|
||||
let resp: Response = read_frame(&mut stream)
|
||||
.await
|
||||
.map_err(|e| format!("read stats: {e}"))?;
|
||||
if let Response::WorkspaceStats { info } = resp {
|
||||
fresh_stats.insert(w.id.to_string(), info);
|
||||
}
|
||||
// Quota por workspace.
|
||||
write_frame(&mut stream, &Request::WorkspaceQuota { workspace: w.id })
|
||||
.await
|
||||
.map_err(|e| format!("write quota: {e}"))?;
|
||||
let resp: Response = read_frame(&mut stream)
|
||||
.await
|
||||
.map_err(|e| format!("read quota: {e}"))?;
|
||||
if let Response::WorkspaceQuota { info } = resp {
|
||||
quotas.insert(w.id.to_string(), info);
|
||||
}
|
||||
}
|
||||
|
||||
// Saved pipelines.
|
||||
|
||||
Reference in New Issue
Block a user