mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-10 23:15:40 +00:00
7680919dbd
An attachment referenced only from soft-deleted content was reclaimable by the orphan GC: AttachmentReferenced scans LIVE rows only, so the archived reference is invisible to the sweep, and past the grace period the claim reclaims the blob. On restore the reference is live again and dangles. The reachable case is a never-attached upload (item_id NULL) referenced from a document — documents have no document_id column, so such a row is necessarily never-attached and the ClaimNeverAttachedAttachment predicate applies with nothing else standing in its way — or from an item's content where the attachment was uploaded unattached. RestoreItem and RestoreDocument now call stampAttachmentRefsTx before clearing deleted_at, inside the restoring transaction, per that helper's ORDERING contract: the stamp row-locks the attachment, so a GC claim racing the restore blocks until commit and re-evaluates last_referenced_at against the fresh stamp — refusing. RestoreItem stamps content + fields; RestoreDocument (previously a bare Exec) is wrapped in a transaction that reads the soft-deleted content + workspace and stamps content. This is prevention only: a blob already reclaimed before the restore is gone (the claim is irrevocable by design), and the restore-time stamp matches zero rows. Surfacing an already-dangling reference to the user on restore is tracked separately as IDEA-2646. Regression tests archive content holding the only reference, age the stamp past the claim's stale window, restore, then run the claim directly (the sweep's live scan would protect now-live content and pass for the wrong reason). All three legs fail on unfixed code. Claude-Session: https://claude.ai/code/session_017jD6t1zjxGSq47SQpZfp1V