🐾 fix(api): cascade plain library delete; case-insensitive name conflict #9
Reference in New Issue
Block a user
Delete Branch "fix/library-api-delete-and-name-case"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to mnemosyne#7, addressing the two gaps confirmed while answering the Spelunker team's provenance handover.
1. Plain API delete now cascades
DELETE /library/api/libraries/{uid}/called barelib.delete()— the only delete path not using the shared cascade — orphaning Collections/Items/Chunks/Images and skipping orphan-Concept GC. It now delegates todelete_library_cascade()like the HTML delete view and the workspace DELETE endpoint, and logs uid/name/item count/orphans like they do. (This — not global-vs-workspace scoping — was the actual cause of "deletes don't cascade"; the cascade is keyed onuidand always worked for global libraries via the other two paths.)2. Name conflicts are case-insensitive at create time
The Neo4j unique index is case-sensitive, so
"amazon connect"could silently coexist with"Amazon Connect"— and clients (Spelunker, humans) treat names case-insensitively, so near-duplicates confuse every name matcher. Both create endpoints (plain + workspace) now pre-check via a newfind_library_by_name_ci()and 409name_conflict, reporting the existing spelling. Implementation notes:toLower()comparison, deliberately not neomodel'siexact— that operator embeds the value in a regex, which breaks on names containing metacharacters (e.g. "C++ Notes").UniquePropertycatch for the pre-check/save race, mirroring the workspace path's existing backstop.Pre-deploy check for existing case-variants (should return nothing; if it doesn't, rename before deploy so 409s reference the intended library):
Testing
40 tests green across
test_managed_by(incl. new case-variant + save-race cases), newtest_library_api(cascade called; 404 skips cascade),test_workspaces_api,test_views. Fulllibrary+mcp_serversuite matches the known main baseline exactly (8F/1E pre-existing intest_search_api/test_tasks). Live Cypher behaviour offind_library_by_name_ciis on the manual e2e checklist per repo test policy.Not touched (flagged only): the API collection and item DELETE endpoints also call bare
.delete()and may deserve the same cascade treatment — separate decision.🤖 Generated with Claude Code
DELETE /library/api/libraries/{uid}/ called bare lib.delete(), orphaning Collections/Items/Chunks/Images and skipping Concept GC — the only delete path not using the shared cascade. It now delegates to delete_library_cascade like the HTML and workspace delete paths. Name-conflict checks on both create endpoints are now case-insensitive via find_library_by_name_ci (parameterised Cypher toLower comparison — not neomodel iexact, which embeds the value in a regex and breaks on names like "C++ Notes"). The Neo4j unique index is case-sensitive, so "amazon connect" previously coexisted silently with "Amazon Connect", confusing every name-matching client (Spelunker matches names case-insensitively). The 409 reports the existing spelling. The plain create also gains a UniqueProperty catch for the pre-check/save race, mirroring the workspace path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>