Every removal is recorded in provenance — what was removed, by whom, why.
RetentionPolicy.record_removals is a property that is always True: no configuration turns
auditability off.Always plan first
Dropping canonical evidence is privileged: it cascades to everything derived from it, because a claim whose source was removed can no longer be justified.Drop
Batch invalidation
When a producer turns out to have been wrong — a bad model version, a broken pipeline run — drop everything it made:Producer records a version: without it, invalidating one model release would mean
invalidating every one.
Re-derivation instead of loss
Naming a replacement makes the difference between a deletion and a re-derivation:Supersede and demote
Two gentler options, which change accessibility rather than membership. The block stays in the composition and keeps proving into the root.Prune
Pruning never decides what to forget; a drop already did. It reclaims what no retained composition still needs.v1 keeps the manifest and layers v1 points at.
Redact
For law and safety, not for cleanup. Wrong or obsolete knowledge is dropped; redaction destroys bytes that a retained root still names.ModuleRef.tombstones list grows to name the destroyed identity. The SDK derives the redact
authorization scope from that signed difference; the removal record remains the attributable ledger entry
that explains who removed it and why.
The member is present exactly when a module has destroyed bytes to name — a module with none omits it,
rather than carrying an empty list, so that two implementations compute the same snapshot digest for the
same brain state.
For ordinary drops, the composition does change. A verifier compares a snapshot with its first parent and
requires every identity that disappeared to be named by a reachable removal record for that module. An
unexplained absence is rejected as RemovalInvariantError, including during pull. Together, tombstones
and the removal ledger distinguish intentional destruction from an incomplete or corrupted artifact.
The check depends on nothing in the snapshot being opted into, so nothing in a snapshot can turn it off.
When the first parent is not held the difference cannot be taken at all; that is reported as
REMOVAL_UNDECIDABLE and does not block — refusing would refuse every brain that pruned its history,
which the protocol permits, and passing silently would let a truncated history disable the check.
The same split is reported for the content a block names but does not
carry, because an
episode whose transcript is gone is not a whole episode:
This is the only reader that reports a datum a block names but the store no longer holds.
verify()
tolerates absent bytes by design — it answers whether what is present hashes to the identity it is filed
under — and a prune reclaims nothing, because a retained root still names the digest. Without
content_missing the brain looks intact until pack refuses to publish it.A drop travels. When two brains are reconciled, exclusion takes precedence — a block one side dropped does
not come back because the other still held it — and the removal record you wrote is what explains it on the
other side. The same holds in reverse: reconciling with a history that dropped evidence you derived from
takes your dependents with it, through this same cascade. See
Reconciliation.
RetentionPolicyError.