e2702ac673
Closes G9. DELETE on a folder now flips is_trashed on every descendant folder and file under it in a single CTE pipeline (lpath <@ root.lpath covers the whole subtree via the GiST index). Previously only the root row was flipped — descendants stayed live, directly addressable via their full path, and confused desktop-sync tree walks that expected the parent-collection 404 to imply the children were gone too. restore_from_trash mirrors the cascade: descendants where the original_*_parent_id column is NULL are the ones we cascade-trashed, so they get cascade-restored too. Descendants that were independently trashed before the parent went to trash have original_*_parent_id set, so they stay in trash and remain visible as top-level entries in storage.trash_items. No schema migration needed — both original_parent_id (folders) and original_folder_id (files) were already nullable and already encoded 'where this came from when it was independently trashed'; using NULL as the cascade-marker reuses that existing distinction cleanly. Test G9 flipped from KNOWN BUG to assert every descendant 404s after the parent DELETE; new G9b proves the inverse cascade by restoring the trashed root and verifying every descendant comes back at its original path.