+66
−11
+305
−0
Loading
Recursive metadata removal reused the selected collection value's row ID for descendant collections and resources. These rows have independent IDs, so matching descendant values were retained while an unrelated resource value with the same numeric ID could be deleted. Resolve the selected value on the source collection and match both its metadata field and exact text throughout the active subtree, including direct resources. Preserve other fields, different values, deleted items, and values outside the subtree. Missing or foreign collection value IDs remain a no-op. Check update access to all traversed collections and resources before bulk deletion, retain EXIF protection, and make removal atomic so errors cannot leave partial deletions. Batch collection traversal by tree level, cache parent relations and resource ACL ancestry, and deduplicate shared resources to avoid per-object lookup queries. Add source comments explaining these matching, permission, performance, and rollback decisions. Add 11 regression tests covering overlapping row IDs, recursive and local removal, missing values, denied and inherited permissions, EXIF protection, deleted items, rollback, and constant SELECT counts for 1 versus 21 children at the same depth. Validation: Ruff passed for both changed Python files. All 227 resource, RPC, and annotation tests passed using an isolated in-memory SQLite database; the 11 focused tests also passed after adding the explanatory comments.