M8: doc_remove_hidden says when the listed categories hold nothing

Its error came from remove_hidden's refusal: "there are no redaction marks
to apply". It now checks the scan first, as redact_apply checks for marks,
and says there is nothing to remove. The scan counts what removal takes
(an AcroForm with no fields as 1), so the check refuses only what the
removal would. The tool description says it fails then.

The tool test also runs doc_optimize discarding categories that hold
nothing, end to end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmsdeUCdTaD72jkDw2pJKY
This commit is contained in:
Grzegorz Martens
2026-11-02 10:16:26 +01:00
co-authored by Claude Opus 5.5
parent 278a588bf2
commit ada2a1ccb7
3 changed files with 12 additions and 1 deletions
+6
View File
@@ -212,6 +212,12 @@ impl Automation {
if which.is_empty() {
return Err(ToolError::InvalidArgs("categories is empty".into()));
}
// The scan counts what removal takes, an AcroForm with no fields as 1, so none
// means the removal would refuse, with an error about redaction marks.
let info = self.doc(a)?.hidden_info();
if which.iter().all(|h| info.iter().find(|c| c.0 == *h).is_none_or(|c| c.1 == 0)) {
return Err(failed("nothing to remove: the listed categories hold nothing (see doc_hidden_info)"));
}
Edit::RemoveHidden { which }
}
};
+1 -1
View File
@@ -562,7 +562,7 @@ pub fn tools() -> Vec<ToolDef> {
t(
"doc_remove_hidden",
"Remove hidden information / Sanitize",
"Remove the listed categories (see doc_hidden_info), or every category when none are listed (Sanitize Document). Form fields are flattened so their values stay visible. The next save rewrites the whole file. Undoable until saved.",
"Remove the listed categories (see doc_hidden_info), or every category when none are listed (Sanitize Document). Fails when the listed categories hold nothing. Form fields are flattened so their values stay visible. The next save rewrites the whole file. Undoable until saved.",
)
.destructive()
.with(schema(
+5
View File
@@ -4670,6 +4670,11 @@ fn removing_hidden_information_through_tools() {
assert_eq!(r["undo"], "Remove hidden information");
assert_eq!(ok(&mut a, "comment_list", json!({ "doc": doc }))["count"], 0);
assert!(count(&ok(&mut a, "doc_hidden_info", json!({ "doc": doc })), "metadata") >= 1, "only comments went");
// With none left, removing them again says so, and Optimize discarding them still runs.
let again = a.call("doc_remove_hidden", &json!({ "doc": doc, "categories": ["comments"] }));
assert!(matches!(&again, Err(ToolError::Failed(m)) if m.starts_with("nothing to remove")), "{again:?}");
let optimized = ok(&mut a, "doc_optimize", json!({ "doc": doc, "path": "optimized.pdf", "discard": ["comments", "bookmarks"] }));
assert_eq!(optimized["discarded"], json!([]), "{optimized}");
let r = ok(&mut a, "doc_remove_hidden", json!({ "doc": doc }));
assert_eq!(r["undo"], "Sanitize document");
ok(&mut a, "doc_save", json!({ "doc": doc, "path": "clean.pdf" }));