I36: #[non_exhaustive] for symmetry with DefragOptions and so a
future bench-friendly stat (e.g. wall-time elapsed inside the sweep)
is not a breaking change.
A non-zero chunk tag. “No tag” is the ABSENCE of a Tag (Option<Tag>) —
Tag(0) is unconstructable, which makes the untagged sentinel expressible
in the type (ISSUES.md I126). The engine still stores the tag as a u32
with 0 meaning untagged; the 0 <-> None mapping happens at the lib.rs
boundary.
Progress report from Chisel::delete_with_tag. deleted lists the handles
removed in this pass (the engine deletes their chunks); complete is true
when the tag has no remaining members. Produced only on the success path —
a mid-pass error returns Err and no progress (the per-delete state stays
consistent; only the reporting is lost).
I36 (ISSUES.md, 2026-05-22): #[non_exhaustive] so adding a new
error variant (a future operational signal, a new fatal-corruption
flavor) is not a breaking change. External match arms over
ChiselError need a _ => … catchall; the conventional path is
if e.is_fatal() { … } else { … } which never enumerates
variants and is therefore unaffected. The Display impl below is
exhaustive — adding a variant still requires updating its prose.
Client-supplied encryption credential. Raw is high-entropy key bytes
(derived via HKDF); Passphrase is a human secret (derived via Argon2id).
Both are zeroized on drop. Clone is needed because Options is consumed
by open while rotation APIs may also hold a key.
Why a superblock slot failed validation (I106). These are exactly the three
torn-slot causes deserialize checks. Copy and small so the failure-path
Vec<SlotDefect> is cheap.