Skip to main content
Removal is not one operation. The protocol keeps four mechanisms distinct because they answer different questions, and collapsing them would make an audit unanswerable.
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.
Nothing is written. The cascade is computed from the provenance ledger, so it is exact rather than estimated.

Drop

Older retained roots keep verifying exactly as before, because nothing about them changed. A canonical drop is off by default, since excluding evidence forfeits re-derivation from it:

Batch invalidation

When a producer turns out to have been wrong — a bad model version, a broken pipeline run — drop everything it made:
This is why 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:
Then run a re-derivation task — see Ingestion.

Supersede and demote

Two gentler options, which change accessibility rather than membership. The block stays in the composition and keeps proving into the root.
Demotion is the only option for episodic memory. The chronological record is append-only by protocol, not by policy: an episode is a record of what happened and cannot be rewritten. drop raises AppendOnlyViolationError, and no policy can permit it.
The decay function that governs demotion is a policy decision, not a protocol one.

Prune

Pruning never decides what to forget; a drop already did. It reclaims what no retained composition still needs.
Nothing a retained root names is touched, and neither is anything a published tag names: a layout that published 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.
Membership still verifies afterwards, but reconstruction of that block is forfeited. The block becomes a tombstone, which is distinguishable from missing — a removed block must never look like a corrupted one:
Redaction creates a successor snapshot. Its module composition and Merkle root stay unchanged, while the signed 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.
Redaction requires explicit policy, or it raises RetentionPolicyError.

The policy

The policy holds the deployment’s answers to the questions the protocol leaves open — thresholds and permissions, never invariants.