mirror of
https://github.com/samvallad33/vestige.git
synced 2026-07-02 22:01:01 +02:00
feat(mcp): consolidate maintenance/lifecycle into maintain (7→1)
Tool Consolidation v2.2.0, Layer 1 commit 5/6. Advertised tools 21 → 15.
Folds consolidate + dream + gc + importance_score + backup + export + restore
into one action-dispatched tool:
action = consolidate | dream | gc | importance_score | backup | export | restore
- Thin facade forwards the same args envelope to each existing handler; the
`action` discriminator is cloned out before the envelope is moved into a
callee. No handler uses deny_unknown_fields, so per-action params validate.
- Safety defaults preserved (all handler-internal): gc dry_run=true by default,
restore path-confinement, export traversal guard. Verified by
test_maintain_actions_and_safety (gc dry-run + restore missing-path error).
- Events preserved end-to-end:
- Pre-dispatch Started events (ConsolidationStarted/DreamStarted) re-emitted
in the `maintain` arm keyed on action.
- emit_tool_event normalizes the `maintain` name to its effective action, so
the existing ConsolidationCompleted/DreamCompleted/ImportanceScored arms
fire unchanged — no duplicated emit logic.
- All 7 old names remain hidden warn!+redirect aliases (removed v2.3.0), keeping
their own pre-emits.
- Tests: count 21→15, 7 negatives, new dispatch/safety test.
Gates: cargo test --workspace, cargo clippy -D warnings — clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e3378316ed
commit
fa87186724
3 changed files with 304 additions and 62 deletions
|
|
@ -301,39 +301,14 @@ impl McpServer {
|
|||
..Default::default()
|
||||
},
|
||||
// ================================================================
|
||||
// MAINTENANCE TOOLS (v1.7: system_status replaces health_check + stats)
|
||||
// MAINTAIN — unified maintenance/lifecycle tool (v2.2)
|
||||
// Folds consolidate + dream + gc + importance_score + backup +
|
||||
// export + restore into one action-dispatched surface.
|
||||
// ================================================================
|
||||
ToolDescription {
|
||||
name: "consolidate".to_string(),
|
||||
description: Some("Run FSRS-6 memory consolidation cycle. Applies decay, generates embeddings, and performs maintenance. Use when memories seem stale.".to_string()),
|
||||
input_schema: tools::maintenance::consolidate_schema(),
|
||||
..Default::default()
|
||||
},
|
||||
ToolDescription {
|
||||
name: "backup".to_string(),
|
||||
description: Some("Create a SQLite database backup. Returns the backup file path.".to_string()),
|
||||
input_schema: tools::maintenance::backup_schema(),
|
||||
..Default::default()
|
||||
},
|
||||
ToolDescription {
|
||||
name: "export".to_string(),
|
||||
description: Some("Export memories as JSON or JSONL. Supports tag and date filters.".to_string()),
|
||||
input_schema: tools::maintenance::export_schema(),
|
||||
..Default::default()
|
||||
},
|
||||
ToolDescription {
|
||||
name: "gc".to_string(),
|
||||
description: Some("Garbage collect stale memories below retention threshold. Defaults to dry_run=true for safety.".to_string()),
|
||||
input_schema: tools::maintenance::gc_schema(),
|
||||
..Default::default()
|
||||
},
|
||||
// ================================================================
|
||||
// AUTO-SAVE & DEDUP TOOLS (v1.3+)
|
||||
// ================================================================
|
||||
ToolDescription {
|
||||
name: "importance_score".to_string(),
|
||||
description: Some("Score content importance using 4-channel neuroscience model (novelty/arousal/reward/attention). Returns composite score, channel breakdown, encoding boost, and explanations.".to_string()),
|
||||
input_schema: tools::importance::schema(),
|
||||
name: "maintain".to_string(),
|
||||
description: Some("Memory maintenance & lifecycle. Actions: 'consolidate' (run FSRS-6 decay/embedding cycle), 'dream' (replay memories → insights/connections + strengthen patterns), 'gc' (garbage-collect stale memories; dry_run=true by default for safety), 'importance_score' (4-channel neuroscience score for 'content'), 'backup' (SQLite DB backup), 'export' (memories as JSON/JSONL with tag/date filters), 'restore' (restore from a JSON backup at 'path').".to_string()),
|
||||
input_schema: tools::maintain::schema(),
|
||||
..Default::default()
|
||||
},
|
||||
// ================================================================
|
||||
|
|
@ -350,13 +325,8 @@ impl McpServer {
|
|||
},
|
||||
// ================================================================
|
||||
// COGNITIVE TOOLS (v1.5+)
|
||||
// (dream folded into `maintain` action='dream' in v2.2)
|
||||
// ================================================================
|
||||
ToolDescription {
|
||||
name: "dream".to_string(),
|
||||
description: Some("Trigger memory dreaming — replays recent memories to discover hidden connections, synthesize insights, and strengthen important patterns. Returns insights, connections, and dream stats.".to_string()),
|
||||
input_schema: tools::dream::schema(),
|
||||
..Default::default()
|
||||
},
|
||||
// ================================================================
|
||||
// GRAPH — unified graph/association/prediction tool (v2.2)
|
||||
// Folds explore_connections + predict + memory_graph + composed_graph.
|
||||
|
|
@ -369,13 +339,8 @@ impl McpServer {
|
|||
},
|
||||
// ================================================================
|
||||
// RESTORE TOOL (v1.5+)
|
||||
// (folded into `maintain` action='restore' in v2.2)
|
||||
// ================================================================
|
||||
ToolDescription {
|
||||
name: "restore".to_string(),
|
||||
description: Some("Restore memories from a JSON backup file. Supports MCP wrapper format, RecallResult format, and direct memory array format.".to_string()),
|
||||
input_schema: tools::restore::schema(),
|
||||
..Default::default()
|
||||
},
|
||||
// ================================================================
|
||||
// CONTEXT PACKETS (v1.8+)
|
||||
// ================================================================
|
||||
|
|
@ -942,22 +907,65 @@ impl McpServer {
|
|||
}
|
||||
|
||||
// ================================================================
|
||||
// MAINTENANCE TOOLS (v1.2+, non-deprecated)
|
||||
// MAINTAIN — unified maintenance/lifecycle tool (v2.2)
|
||||
// action = consolidate | dream | gc | importance_score | backup
|
||||
// | export | restore
|
||||
// ================================================================
|
||||
"maintain" => {
|
||||
// Mirror the pre-dispatch *Started* events that the standalone
|
||||
// consolidate/dream arms emit, keyed off the action.
|
||||
match request
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|a| a.get("action"))
|
||||
.and_then(|v| v.as_str())
|
||||
{
|
||||
Some("consolidate") => self.emit(VestigeEvent::ConsolidationStarted {
|
||||
timestamp: chrono::Utc::now(),
|
||||
}),
|
||||
Some("dream") => self.emit(VestigeEvent::DreamStarted {
|
||||
memory_count: self
|
||||
.storage
|
||||
.get_stats()
|
||||
.map(|s| s.total_nodes as usize)
|
||||
.unwrap_or(0),
|
||||
timestamp: chrono::Utc::now(),
|
||||
}),
|
||||
_ => {}
|
||||
}
|
||||
tools::maintain::execute(&self.storage, &self.cognitive, request.arguments).await
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// MAINTENANCE TOOLS (v1.2+) — DEPRECATED (v2.2): folded into
|
||||
// `maintain`. Hidden aliases; pre-emit Started events preserved.
|
||||
// ================================================================
|
||||
"consolidate" => {
|
||||
warn!("Tool 'consolidate' is deprecated in v2.2. Use 'maintain' (action='consolidate').");
|
||||
self.emit(VestigeEvent::ConsolidationStarted {
|
||||
timestamp: chrono::Utc::now(),
|
||||
});
|
||||
tools::maintenance::execute_consolidate(&self.storage, request.arguments).await
|
||||
}
|
||||
"backup" => tools::maintenance::execute_backup(&self.storage, request.arguments).await,
|
||||
"export" => tools::maintenance::execute_export(&self.storage, request.arguments).await,
|
||||
"gc" => tools::maintenance::execute_gc(&self.storage, request.arguments).await,
|
||||
"backup" => {
|
||||
warn!("Tool 'backup' is deprecated in v2.2. Use 'maintain' (action='backup').");
|
||||
tools::maintenance::execute_backup(&self.storage, request.arguments).await
|
||||
}
|
||||
"export" => {
|
||||
warn!("Tool 'export' is deprecated in v2.2. Use 'maintain' (action='export').");
|
||||
tools::maintenance::execute_export(&self.storage, request.arguments).await
|
||||
}
|
||||
"gc" => {
|
||||
warn!("Tool 'gc' is deprecated in v2.2. Use 'maintain' (action='gc').");
|
||||
tools::maintenance::execute_gc(&self.storage, request.arguments).await
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// AUTO-SAVE & DEDUP TOOLS (v1.3+)
|
||||
// ================================================================
|
||||
// DEPRECATED (v2.2): folded into `maintain` (action='importance_score').
|
||||
"importance_score" => {
|
||||
warn!("Tool 'importance_score' is deprecated in v2.2. Use 'maintain' (action='importance_score').");
|
||||
tools::importance::execute(&self.storage, &self.cognitive, request.arguments).await
|
||||
}
|
||||
// ================================================================
|
||||
|
|
@ -989,9 +997,11 @@ impl McpServer {
|
|||
}
|
||||
|
||||
// ================================================================
|
||||
// COGNITIVE TOOLS (v1.5+)
|
||||
// COGNITIVE TOOLS (v1.5+) — DEPRECATED (v2.2): dream folded into
|
||||
// `maintain` (action='dream'). Hidden alias; DreamStarted preserved.
|
||||
// ================================================================
|
||||
"dream" => {
|
||||
warn!("Tool 'dream' is deprecated in v2.2. Use 'maintain' (action='dream').");
|
||||
self.emit(VestigeEvent::DreamStarted {
|
||||
memory_count: self
|
||||
.storage
|
||||
|
|
@ -1018,7 +1028,11 @@ impl McpServer {
|
|||
warn!("Tool 'predict' is deprecated in v2.2. Use 'graph' (action='predict').");
|
||||
tools::predict::execute(&self.storage, &self.cognitive, request.arguments).await
|
||||
}
|
||||
"restore" => tools::restore::execute(&self.storage, request.arguments).await,
|
||||
// DEPRECATED (v2.2): folded into `maintain` (action='restore').
|
||||
"restore" => {
|
||||
warn!("Tool 'restore' is deprecated in v2.2. Use 'maintain' (action='restore').");
|
||||
tools::restore::execute(&self.storage, request.arguments).await
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// CONTEXT PACKETS (v1.8+) — `session_start` (renamed v2.2)
|
||||
|
|
@ -1300,6 +1314,19 @@ impl McpServer {
|
|||
}
|
||||
let now = Utc::now();
|
||||
|
||||
// v2.2: the unified `maintain` tool folds consolidate/dream/importance_score
|
||||
// (the three maintenance actions that emit). Normalize its name to the
|
||||
// effective action so the existing emit arms below fire unchanged. Old
|
||||
// standalone names still arrive verbatim and match directly.
|
||||
let tool_name = if tool_name == "maintain" {
|
||||
args.as_ref()
|
||||
.and_then(|a| a.get("action"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("maintain")
|
||||
} else {
|
||||
tool_name
|
||||
};
|
||||
|
||||
match tool_name {
|
||||
// -- smart_ingest: memory created/updated --
|
||||
"smart_ingest" | "ingest" | "session_checkpoint" => {
|
||||
|
|
@ -1830,8 +1857,8 @@ mod tests {
|
|||
// dispatchable as hidden back-compat aliases but drop off the advertised list.
|
||||
assert_eq!(
|
||||
tools.len(),
|
||||
21,
|
||||
"Expected exactly 21 tools after dedup + memory_status + graph consolidation"
|
||||
15,
|
||||
"Expected exactly 15 tools after dedup + memory_status + graph + maintain consolidation"
|
||||
);
|
||||
|
||||
let tool_names: Vec<&str> = tools.iter().map(|t| t["name"].as_str().unwrap()).collect();
|
||||
|
|
@ -1889,13 +1916,24 @@ mod tests {
|
|||
!tool_names.contains(&"stats"),
|
||||
"stats should be removed in v1.7"
|
||||
);
|
||||
assert!(tool_names.contains(&"consolidate"));
|
||||
assert!(tool_names.contains(&"backup"));
|
||||
assert!(tool_names.contains(&"export"));
|
||||
assert!(tool_names.contains(&"gc"));
|
||||
|
||||
// Auto-save tool (v1.3)
|
||||
assert!(tool_names.contains(&"importance_score"));
|
||||
// Maintenance / lifecycle — unified `maintain` tool (v2.2).
|
||||
// consolidate + dream + gc + importance_score + backup + export + restore
|
||||
// folded in; old names dispatch as hidden aliases but are off the list.
|
||||
assert!(tool_names.contains(&"maintain"));
|
||||
for old in [
|
||||
"consolidate",
|
||||
"dream",
|
||||
"gc",
|
||||
"importance_score",
|
||||
"backup",
|
||||
"export",
|
||||
"restore",
|
||||
] {
|
||||
assert!(
|
||||
!tool_names.contains(&old),
|
||||
"{old} should be folded into 'maintain' in v2.2"
|
||||
);
|
||||
}
|
||||
|
||||
// Dedup / merge / supersede — unified `dedup` tool (v2.2).
|
||||
// find_duplicates + the 7 Phase-3 merge tools folded in; still
|
||||
|
|
@ -1917,10 +1955,8 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
// Cognitive tools (v1.5)
|
||||
// (explore_connections + predict folded into `graph` in v2.2)
|
||||
assert!(tool_names.contains(&"dream"));
|
||||
assert!(tool_names.contains(&"restore"));
|
||||
// Cognitive tools (v1.5): explore_connections + predict → `graph`;
|
||||
// dream + restore → `maintain` (all v2.2). Nothing left advertised here.
|
||||
|
||||
// Context packets (v1.8) — renamed session_context → session_start (v2.2)
|
||||
assert!(tool_names.contains(&"session_start"));
|
||||
|
|
@ -2064,6 +2100,74 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// v2.2: the 7 tools folded into `maintain` must still dispatch, the new
|
||||
/// actions must resolve, gc must default to dry_run, and restore must keep
|
||||
/// path validation (a nonexistent path errors rather than silently no-op).
|
||||
#[tokio::test]
|
||||
async fn test_maintain_actions_and_safety() {
|
||||
let (mut server, _dir) = test_server().await;
|
||||
let init_request = make_request("initialize", Some(init_params()));
|
||||
server.handle_request(init_request).await;
|
||||
|
||||
// Aliases + safe new actions must dispatch (not unknown-tool).
|
||||
let dispatch_ok: Vec<(&str, serde_json::Value)> = vec![
|
||||
("consolidate", serde_json::json!({})),
|
||||
("backup", serde_json::json!({})),
|
||||
("dream", serde_json::json!({})),
|
||||
("maintain", serde_json::json!({"action": "consolidate"})),
|
||||
("maintain", serde_json::json!({"action": "gc"})),
|
||||
("maintain", serde_json::json!({"action": "backup"})),
|
||||
];
|
||||
for (name, args) in dispatch_ok {
|
||||
let request = make_request(
|
||||
"tools/call",
|
||||
Some(serde_json::json!({ "name": name, "arguments": args })),
|
||||
);
|
||||
let response = server.handle_request(request).await.unwrap();
|
||||
if let Some(err) = response.error {
|
||||
assert_ne!(err.code, -32602, "'{name}' {args} should dispatch: {}", err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// gc via maintain defaults to dry_run=true (no deletion).
|
||||
let gc_req = make_request(
|
||||
"tools/call",
|
||||
Some(serde_json::json!({ "name": "maintain", "arguments": {"action": "gc"} })),
|
||||
);
|
||||
let gc_resp = server.handle_request(gc_req).await.unwrap();
|
||||
let text = gc_resp.result.unwrap()["content"][0]["text"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
assert!(
|
||||
text.contains("\"dryRun\": true") || text.contains("\"dryRun\":true"),
|
||||
"maintain action=gc must default to dry_run=true; got: {text}"
|
||||
);
|
||||
|
||||
// restore keeps path validation: a missing file must error, not no-op.
|
||||
let restore_req = make_request(
|
||||
"tools/call",
|
||||
Some(serde_json::json!({
|
||||
"name": "maintain",
|
||||
"arguments": {"action": "restore", "path": "/nonexistent/vestige-backup-xyz.json"}
|
||||
})),
|
||||
);
|
||||
let restore_resp = server.handle_request(restore_req).await.unwrap();
|
||||
// Either a JSON-RPC error or an error envelope is acceptable; a silent
|
||||
// success is NOT (that would mean confinement/validation was bypassed).
|
||||
let validated = restore_resp.error.is_some()
|
||||
|| restore_resp
|
||||
.result
|
||||
.map(|r| {
|
||||
r["content"][0]["text"]
|
||||
.as_str()
|
||||
.map(|t| t.to_lowercase().contains("not found") || t.to_lowercase().contains("error"))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
assert!(validated, "maintain action=restore must validate a missing path");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_tools_have_descriptions_and_schemas() {
|
||||
let (mut server, _dir) = test_server().await;
|
||||
|
|
|
|||
134
crates/vestige-mcp/src/tools/maintain.rs
Normal file
134
crates/vestige-mcp/src/tools/maintain.rs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
//! Unified `maintain` Tool (v2.2 — Tool Consolidation)
|
||||
//!
|
||||
//! Folds the seven maintenance/lifecycle tools into one action-dispatched
|
||||
//! surface:
|
||||
//!
|
||||
//! action = consolidate | dream | gc | importance_score | backup | export | restore
|
||||
//!
|
||||
//! This is a thin facade: each action forwards the *same* args envelope to the
|
||||
//! existing handler. None of the underlying arg structs use
|
||||
//! `deny_unknown_fields`, so the `action` discriminator is ignored by each
|
||||
//! handler and per-action params validate as before. Safety defaults are
|
||||
//! preserved because they live inside the callees:
|
||||
//! - `gc` defaults `dry_run=true` (handler-internal),
|
||||
//! - `restore` keeps path-confinement (handler-internal),
|
||||
//! - `export` keeps its traversal guard (handler-internal).
|
||||
//!
|
||||
//! The `consolidate`/`dream` *Started* events and the
|
||||
//! `consolidate`/`dream`/`importance_score` *Completed* events are emitted by
|
||||
//! the server dispatch + `emit_tool_event` (which normalizes the `maintain`
|
||||
//! name to its effective action) — not here.
|
||||
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use vestige_core::Storage;
|
||||
|
||||
use crate::cognitive::CognitiveEngine;
|
||||
|
||||
/// Discriminated-union schema for the unified `maintain` tool.
|
||||
pub fn schema() -> Value {
|
||||
serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": ["consolidate", "dream", "gc", "importance_score", "backup", "export", "restore"],
|
||||
"description": "Maintenance op. 'consolidate' (run FSRS-6 decay/embedding cycle), 'dream' (replay memories → insights/connections), 'gc' (garbage-collect stale memories; dry_run=true by default), 'importance_score' (4-channel neuroscience score for 'content'), 'backup' (SQLite DB backup), 'export' (memories as JSON/JSONL with filters), 'restore' (restore from a JSON backup at 'path')."
|
||||
},
|
||||
// --- gc ---
|
||||
"min_retention": { "type": "number", "minimum": 0.0, "maximum": 1.0, "description": "[gc] Collect memories below this retention (default 0.1)." },
|
||||
"dry_run": { "type": "boolean", "description": "[gc] Preview only. Defaults to TRUE for safety." },
|
||||
// --- importance_score ---
|
||||
"content": { "type": "string", "description": "[importance_score] Content to score." },
|
||||
// --- export ---
|
||||
"format": { "type": "string", "enum": ["json", "jsonl"], "description": "[export] Output format." },
|
||||
"tags": { "type": "array", "items": { "type": "string" }, "description": "[export] Tag filter." },
|
||||
"start": { "type": "string", "description": "[export] Start date filter (ISO 8601)." },
|
||||
"end": { "type": "string", "description": "[export] End date filter (ISO 8601)." },
|
||||
// --- backup / restore ---
|
||||
"path": { "type": "string", "description": "[restore] Path to a JSON backup file (path-confined)." }
|
||||
},
|
||||
"required": ["action"]
|
||||
})
|
||||
}
|
||||
|
||||
/// Unified dispatcher for `maintain`. Routes on `action` (required).
|
||||
pub async fn execute(
|
||||
storage: &Arc<Storage>,
|
||||
cognitive: &Arc<Mutex<CognitiveEngine>>,
|
||||
args: Option<Value>,
|
||||
) -> Result<Value, String> {
|
||||
// Clone the discriminator out before the args envelope is moved into a callee.
|
||||
let action = args
|
||||
.as_ref()
|
||||
.and_then(|a| a.get("action"))
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or("Missing 'action'. Use consolidate|dream|gc|importance_score|backup|export|restore.")?
|
||||
.to_string();
|
||||
|
||||
match action.as_str() {
|
||||
"consolidate" => super::maintenance::execute_consolidate(storage, args).await,
|
||||
"dream" => super::dream::execute(storage, cognitive, args).await,
|
||||
"gc" => super::maintenance::execute_gc(storage, args).await,
|
||||
"importance_score" => super::importance::execute(storage, cognitive, args).await,
|
||||
"backup" => super::maintenance::execute_backup(storage, args).await,
|
||||
"export" => super::maintenance::execute_export(storage, args).await,
|
||||
"restore" => super::restore::execute(storage, args).await,
|
||||
other => Err(format!(
|
||||
"Unknown maintain action '{other}'. Use consolidate|dream|gc|importance_score|backup|export|restore."
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn test_storage() -> Arc<Storage> {
|
||||
let dir = tempfile::TempDir::new().unwrap();
|
||||
let storage = Storage::new(Some(dir.path().join("test.db"))).unwrap();
|
||||
std::mem::forget(dir);
|
||||
Arc::new(storage)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_schema_actions() {
|
||||
let s = schema();
|
||||
let actions = s["properties"]["action"]["enum"].as_array().unwrap();
|
||||
assert_eq!(actions.len(), 7);
|
||||
assert_eq!(s["required"][0], "action");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_missing_action_errors() {
|
||||
let storage = test_storage();
|
||||
let cognitive = Arc::new(Mutex::new(CognitiveEngine::new()));
|
||||
let r = execute(&storage, &cognitive, None).await;
|
||||
assert!(r.is_err(), "missing action must error");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_gc_defaults_dry_run() {
|
||||
let storage = test_storage();
|
||||
let cognitive = Arc::new(Mutex::new(CognitiveEngine::new()));
|
||||
// No dry_run passed → handler default true → nothing is actually deleted.
|
||||
let args = Some(serde_json::json!({ "action": "gc" }));
|
||||
let r = execute(&storage, &cognitive, args).await.unwrap();
|
||||
// gc's envelope reports dry_run; assert it stayed true.
|
||||
let dry = r
|
||||
.get("dryRun")
|
||||
.or(r.get("dry_run"))
|
||||
.and_then(|v| v.as_bool());
|
||||
assert_eq!(dry, Some(true), "gc must default to dry_run=true via maintain");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_consolidate_resolves() {
|
||||
let storage = test_storage();
|
||||
let cognitive = Arc::new(Mutex::new(CognitiveEngine::new()));
|
||||
let args = Some(serde_json::json!({ "action": "consolidate" }));
|
||||
assert!(execute(&storage, &cognitive, args).await.is_ok());
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,10 @@ pub mod timeline;
|
|||
// v1.2: Maintenance tools
|
||||
pub mod maintenance;
|
||||
|
||||
// v2.2: Unified maintenance surface — folds consolidate + dream + gc +
|
||||
// importance_score + backup + export + restore into one action-dispatched tool.
|
||||
pub mod maintain;
|
||||
|
||||
// v2.2: Unified status surface — folds system_status + memory_health +
|
||||
// memory_timeline + memory_changelog into one view-dispatched tool.
|
||||
pub mod memory_status;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue