feat(1001): story_ids filter for recover_half_written_items

The first dry-run against the live pipeline surfaced 735 orphans (35
tombstoned half-writes, 700 stale content rows with no CRDT entry —
mostly artefacts of the pre-numeric-id era). Bulk-recovering would
resurrect a lot of stories the user deliberately purged in the past.

Add an optional `story_ids` filter that restricts both discovery (in
dry-run) and recovery to a named subset, so the operator can target
the specific recent half-writes without touching anything else. The
new test asserts the filter is honoured.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-05-13 19:26:07 +01:00
parent cd411ba443
commit 92b1744c3a
3 changed files with 74 additions and 9 deletions
+21 -5
View File
@@ -131,21 +131,37 @@ pub(crate) fn tool_recover_half_written_items(args: &Value) -> Result<String, St
.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 half = crate::db::find_half_written_items();
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": half.len(),
"count": count,
"message": format!(
"Discovered {} half-written item(s). Re-run with dry_run=false to recover them.",
half.len()
"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();
let results = crate::db::recover_half_written_items(only.as_deref());
serde_json::to_string_pretty(&json!({
"dry_run": false,
"recovered": results,