Retract, Rename & Schema Changes
WarmHub preserves data by default. You retract things instead of deleting them, you rename things in place, and you evolve shapes over time — and each of these keeps the full version history. Destroying or overwriting data is possible, but only through deliberate, privileged operations: removal permanently deletes a thing and every version, redaction erases a thing’s content and former name while keeping its identity, and wh thing rewrite corrects the body of one existing version in place. Each of these changes ripples outward to the wrefs, assertions, and collections that point at what you changed.
This page covers exactly what happens to those references in each case — for retraction, removal, redaction, and rename. For wh thing rewrite, see its entry in the CLI reference. It assumes you already know things, wrefs, and assertions.
| Change | New version? | Effect on references |
|---|---|---|
| Retract a thing | Yes — a retract version | None — references keep pointing at the preserved version |
| Remove a thing | No — the thing and every version are deleted | Existing references survive, marked removed; a new write that points at it is refused |
| Rename a thing | No — an in-place identity edit | Identity-pinned references follow it; a wref using the old name breaks |
| Redact a thing | No — every existing version’s body becomes {} and its name becomes hub/redacted/<durableId> | Existing references survive and resolve to the redacted identity; a new reference to it is refused unless the field is explicitly typed to allow redacted things |
| Revise a shape (changed) | Yes — a new shape version | None — existing things are unchanged until their next write; things of a composite that composes the shape pick up the change only after the composite is revised |
| Revise a shape (identical to current) | No — a no-op; current version kept. A composite whose member has a newer version is the exception: it gets a new version | None — existing things are unchanged |
| Retract a shape | Yes — a retract version | Existing things stay readable; new floating write references to the shape fail, while existing pinned versions remain valid |
The throughline: assertions, wref data fields, and collection members written with @vN pin the exact version they referenced, and bare collection members follow their Thing by identity, so a retraction or a rename can never orphan them. Only an input wref that addresses an entity by its name is fragile, and only across a rename. Removal and redaction are the exceptions to all of this — both are irreversible. Even then existing references are not erased: they keep the thing’s durable id. A reference to a removed thing reads back marked as removed; a reference to a redacted thing resolves to an identity renamed hub/redacted/<durableId>, with an empty body and only the RedactedThing shape. What they can no longer do is reach the original content. A new write that points at a removed id is refused, and so is a new reference to a redacted id unless the field is explicitly typed to allow redacted things (see Redaction).
Retraction
Section titled “Retraction”Retracting a thing marks it inactive. Its name, data, and full version history are preserved, and it is hidden from default queries. Retraction is how you withdraw something while keeping its history; to destroy a thing outright, see Removal below; to erase its canonical content while preserving its durable identity, see Redaction below. See Operations — Retract for the operation itself and Things — Active/Inactive Lifecycle for the lifecycle.
What retraction does not do is cascade. Retracting a thing leaves everything that points at it in place:
| Reference to the retracted thing | What happens |
|---|---|
| Its own wref | Hidden from default reads and listings — a plain wh thing view cave returns not-found. Its data and full version history are preserved and stay readable with --include-retracted or a pinned @vN version. |
| An assertion about it | Untouched. The assertion still resolves and still points at the exact target version it was pinned to when created. |
| A collection that includes it | Untouched, with no new collection version. How the member points at it depends on how it was written: a member written with @vN is version-pinned and still resolves to that version; a bare member is an identity reference that follows the Thing. |
| A subscription | A retract is a write operation, so a subscription whose filter matches retract fires on it. See Subscription Filters. |
Because assertions and @vN collection members pin the exact version they referenced, and bare members point at the Thing’s identity, a retraction can never orphan them — the retracted Thing and its versions still exist.
Removal
Section titled “Removal”Removing a thing permanently deletes it and every one of its versions. This cannot be undone: unlike retraction, the thing itself is gone and no version of it can be read again. What survives is other people’s references to it, which keep its durable id and read back marked as removed.
Removal is deliberately hard to reach:
- It needs unrestricted repository admin access. A token narrowed to particular names is refused outright — there is no permission to delete only part of a repository. See Access reference.
- Removal is one of three privileged thing operations. The others are redaction and
wh thing rewrite, which corrects the body of one existing version in place; each needs the same unrestricted admin access. - Write access is not enough. Being able to create and revise things does not let you destroy them.
wh thing removeprompts for confirmation unless you pass--yes. Use--dry-runto preview, and--expected-versionto remove only if the thing is still at the version you read.
wh thing remove old-cave --dry-runwh thing remove old-cave --expected-version 3 --yesA retracted thing must be named by its durable ID rather than a wref, because retracted names are not unique — wh thing resolve <wref> prints it on the durableId: line before you retract. The MCP tool takes the durable ID in every case.
You can preview a removal before running it: the TypeScript SDK validates a remove operation against the server and reports what it would destroy, then client.thing.remove(...) carries it out. Preview is not available from the Python SDK today — there, use the CLI’s --dry-run. See SDK Write Methods.
Removal and subscriptions
Section titled “Removal and subscriptions”Removals do not trigger subscriptions. A subscription filtering on remove never fires, and nothing else announces the removal either — it leaves no subscription-visible trail at all. Do not build a workflow that expects to be told when something is destroyed: if you need a record of removals, write one yourself at the point you perform them.
Redaction
Section titled “Redaction”Redaction irreversibly erases one thing’s canonical content and former name while preserving its durable identity, version history topology, and existing inbound references. It adds no version: every existing version’s body becomes {} and every current and former name becomes hub/redacted/<durableId>. See Redaction for who can redact, how to run it, what you can still do with a redacted thing, and what it does not erase.
| Reference to the redacted thing | What happens |
|---|---|
| Its former name | No longer resolves to anything. Address the thing by its durable ID. |
| Its durable ID | Resolves to the redacted identity: name hub/redacted/<durableId>, empty body, RedactedThing shape. |
| An assertion about it | Survives. It keeps the durable ID, which now resolves to the redacted identity. |
| A collection that includes it | Survives. The member keeps the durable ID, which now resolves to the redacted identity. |
| A new reference to it | Refused by default. A field explicitly typed to allow redacted things (typed as wref<RedactedThing> in the schema) may still accept a reference to the redacted identity. |
A remove, reasonless retract, or name-preserving restore | Allowed. These operations act on the identity itself, by durable ID, so redaction does not block them. |
| A subscription | Does not fire. Redaction is not delivered through commit.applied subscriptions, so like removal it leaves no subscription-visible trail. |
Rename
Section titled “Rename”Renaming changes a thing’s name in place — wh thing rename cave cavern, or client.thing.rename in the SDK. Both arguments are plain thing names, and the shapes the thing declares are unaffected by the rename. It does not create a new version, so a rename does not appear in wh thing history and the old name is not readable back through the history commands today. Plan as though the old name is not recoverable: if you need to know a thing’s former names, record them yourself.
Rename is only available for active things. Attempting to rename a retracted thing is rejected with ALREADY_RETRACTED. To rename a retracted thing, restore it first — optionally under a new name with wh thing restore <durableId> --name <newName>.
That makes the rule simple: references that point by identity follow the rename; references that use the old name break.
| Reference to the renamed thing | What happens |
|---|---|
| An assertion about it | Follows the rename. Assertions link to their target by identity, so reading the assertion — or querying what it is about — resolves to the thing under its new name automatically. |
| A collection that includes it | Follows the rename. Collection members are pinned by identity, so the member resolves to the new name automatically. Nothing dangles. |
| Its durable id | Unaffected. A durable id names a thing by identity, so it survives every rename. |
| A wref that uses the old name | Breaks. Every old spelling, including @HEAD and @vN, returns not-found. There is no redirect; use the new name to address both current and historical versions. |
| A subscription | Not re-evaluated. A rename is not a write operation, so it never triggers subscription matching (see below). |
Shapes rename the same way: wh shape rename (or client.shape.rename) patches the shape’s name in place, preserves its history, and creates no new version. Things keep validating against it, because they reference their shape by identity. If Player becomes Participant, old spellings such as Player and Player@v2 stop resolving; Participant and Participant@v2 resolve the current and historical definitions.
Rename and subscriptions
Section titled “Rename and subscriptions”Subscriptions match write operations as they happen — an add, a revise, or a retract carrying a name, shape, and kind. A rename is not a write operation, so it produces nothing for a subscription to match.
The practical consequence: renaming a thing into or out of a subscription’s name pattern — a glob match like Product/electronics/** — does not notify that subscription. The renamed thing matches it again only the next time it is written (its next revise or retract, under the new name).
Renames are not invisible everywhere, though. A repository can subscribe to the thing.renamed and shape.renamed metadata events, which do fire on a rename and carry the old and new names. Use those if you need to react to renames; a name-pattern subscription on write operations will not do it. Removal has no equivalent — see Removal and subscriptions.
Schema changes
Section titled “Schema changes”Revising a shape
Section titled “Revising a shape”Revising a shape replaces its field definitions — it is a full replacement, like any revise. You can add fields (optional or required), remove fields, or rename them.
If the revised shape is identical to the current version, the operation succeeds as a no-op and no new version is created. Only a changed revise produces a new shape version.
Existing things are not re-validated when the shape changes. They keep their data and stay valid against the shape version they were written under. The new shape is enforced the next time each thing is written.
A shape that a composite shape composes is the exception: things written under the composite validate against the member version the composite captured. They pick up the revised member only after the composite itself is revised, because every composite revise re-captures its members at their current versions. That composite revise is refused if the revised member now conflicts with the composite.
Adding a required field is therefore backwards-incompatible in a specific way: existing things stay valid until you next revise one, and that next revise must satisfy the new shape. Shapes — Back-Filling Required Fields covers the two migration strategies: back-fill on next revise, or mass-revise every thing immediately. To avoid the incompatibility entirely, add the field as optional.
Retracting a shape
Section titled “Retracting a shape”Shapes can be retracted, with two restrictions: the built-in shapes (Arc, Bond, Pair, Set, List, Content, View, LicenseSubject, LicenseDeclaration) cannot be — View, LicenseSubject, and LicenseDeclaration are materialized lazily on first write, so the name is reserved whether or not the shape exists yet — and the retired Triple shape namespace is read-only and also cannot be retracted. Retracting a shape you defined marks the shape inactive. The things validated against it are not deleted or changed, and they still resolve. On write paths, a floating wref to the retracted shape (Player or Player@HEAD) fails; an existing pinned version (Player@vN) remains valid. As with any name, you can add a new shape at the same name afterward — a fresh identity.
Idempotent revise
Section titled “Idempotent revise”One change deliberately does nothing: revising a thing with data identical to its current version. WarmHub returns a no-op and creates no new version, so re-submitting an unchanged revise is always safe. See Operations — Idempotent revise for details.