ADR-0043: Disposal blob deletion is post-commit, GC-backstopped¶
- Status: accepted
- Date: 2026-07-02
- Refines: ADR-0025 (its "blob deletion + DB tombstoning + index purge as one audited operation" wording), ADR-0042
Context¶
The 2026-07-02 review round found the shipped disposal path deviating from ADR-0025's phrasing: blob deletion runs after the tombstoning transaction commits, best-effort with logged failures — not inside "one audited operation".
Decision¶
This is deliberate and now the binding contract: the database transaction (gates, content severance, tombstone stamps, disposal record, audit event, index-purge hook) is the atomic, audited unit and the source of truth; blob deletion follows post-commit, manifest-aware and best-effort. A transient storage error must not roll back a committed, audited disposal; blobs that survive a failed deletion are unreachable (their DB references are gone) and are reclaimed by orphan garbage collection. Consequently a "successful" disposal can leave a blob on disk until GC — the same bounded-residual shape ADR-0025 already accepts for backups.
Two adjacent policy decisions the review found undocumented are also recorded:
- Unconfigured retention blocks disposal (fail-closed): a document whose type has neither a
(scope, type) retention rule nor a type default can never be disposed — consistent with the
sweep, which never flags such documents.
- The index-purge guarantee (REQ-DPR-011/REQ-SRCH-011) is not yet effective: the hook is
called in-transaction but its implementation is a no-op until the search phase — deferred per
ADR-0042 X3; recorded here so the requirement trace shows the pending half explicitly.
- emailed_at backlog semantics (ADR-0041): with the email channel disabled, rows stay
unstamped by design; enabling the channel later delivers the accumulated backlog (no age cap) —
an accepted operational behavior to consider before first enablement on a long-running instance.