chore(1001): retire recover_half_written_items from MCP surface

The recovery tool was a one-shot migration aid for the half-written
items that existed before the Stage 1 allocator fix. The three live
orphans (989/1000/1001) have been migrated; the Stage 1 fix prevents
new half-writes; the tool's job is done.

Removes the MCP wrapper, schema, dispatch case, and tools-list
assertion. The db::recover module itself stays in-process (under
`#[allow(dead_code)]`) so it can be re-exposed quickly if the bug
ever resurfaces — its regression tests still run as part of the
default suite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-05-13 19:36:02 +01:00
parent 92b1744c3a
commit b6898886d7
5 changed files with 6 additions and 80 deletions
-56
View File
@@ -115,62 +115,6 @@ pub(crate) fn tool_dump_crdt(args: &Value) -> Result<String, String> {
.map_err(|e| format!("Serialization error: {e}"))
}
/// MCP tool: discover or recover half-written pipeline items (bug 1001).
///
/// A half-written item is one whose content row is in the content store /
/// SQLite shadow but whose CRDT entry is absent or tombstoned. Such items
/// are invisible to every CRDT-driven read path and to `delete_story` /
/// `purge_story`, so they need explicit recovery.
///
/// With `dry_run = true` (default), returns the list of discovered half-
/// writes without mutating anything. With `dry_run = false`, lifts each
/// orphan onto a fresh non-tombstoned id and returns the old→new mapping.
pub(crate) fn tool_recover_half_written_items(args: &Value) -> Result<String, String> {
let dry_run = args
.get("dry_run")
.and_then(|v| v.as_bool())
.unwrap_or(true);
// Optional id filter — when provided, recovery (or the dry-run report) is
// restricted to these ids. This is the safe choice for live systems
// where the orphan set may include many historic purged stories that
// should stay dead.
let only: Option<Vec<String>> = args.get("story_ids").and_then(|v| {
v.as_array().map(|arr| {
arr.iter()
.filter_map(|x| x.as_str().map(str::to_string))
.collect()
})
});
if dry_run {
let mut half = crate::db::find_half_written_items();
if let Some(filter) = &only {
half.retain(|h| filter.iter().any(|f| f == &h.story_id));
}
let count = half.len();
return serde_json::to_string_pretty(&json!({
"dry_run": true,
"found": half,
"count": count,
"message": format!(
"Discovered {count} half-written item(s){scope}. Re-run with dry_run=false to recover them.",
scope = if only.is_some() { " matching the filter" } else { "" },
),
}))
.map_err(|e| format!("Serialization error: {e}"));
}
let results = crate::db::recover_half_written_items(only.as_deref());
serde_json::to_string_pretty(&json!({
"dry_run": false,
"recovered": results,
"count": results.len(),
"message": format!("Recovered {} half-written item(s).", results.len()),
}))
.map_err(|e| format!("Serialization error: {e}"))
}
/// MCP tool: return the server version, build hash, and running port.
pub(crate) fn tool_get_version(ctx: &AppContext) -> Result<String, String> {
let build_hash =