Skip to content

Things

A thing is a named, versioned entity within a repository. Things are the core data objects in WarmHub — they represent the entities in your domain.

Things are the foundation. Use them for:

  • Entities in your domain — the objects your system reasons about (locations, players, companies, documents)
  • Reference data — stable records that serve as targets for assertions
  • Anything with a single canonical state — if there’s one truth about this entity, it’s a thing

You don’t need assertions just to track changes over time — things are already versioned, so revise gives you full history on its own.

Things are created through writes. The same add thing operation works on every surface — the CLI offers a shorthand, while the SDK and MCP take the operation directly. See Write operations for the full contract.

Terminal window
wh commit submit --add acme --shape Company --data '{"industry": "fintech", "stage": "series-b"}' -m "Add company"

The thing’s wref is its name, acme. Its shapes are stated in shapes, not in the name.

Thing names can contain / for hierarchical organization. See Naming as Navigation for a deep dive on designing effective names — how hierarchy affects agent navigation, scoped queries, and subscription routing.

dungeon/room-1
dungeon/room-2
round-1/state

The whole string is the thing name, / included. Names cannot start or end with /, and cannot contain //.

Thing names are unique within a repository across all Shapes. Two things cannot share the same name even if they belong to different Shapes — a thing foo under Experiment and a thing foo under Evaluation cannot coexist in the same repo. A write that would reuse an active name under another Shape is refused with NAME_TAKEN. The refusal is terminal: choose another name. It identifies the holder by durable ID only, and only when you can read it; see error responses for the field contract. Only active names are claimed, so retracting a thing releases its name for a new thing to take.

Every mutation that changes a thing creates a new version. Versions are numbered sequentially starting at v1:

Terminal window
# v1 initial creation, then v2 revised data
wh commit submit --add cave --shape Location --data '{"x": 3, "y": 7}'
wh thing revise cave --shape Location --data '{"x": 5, "y": 3}' -m "Move cave"

Each version is immutable — old versions are preserved and queryable:

Terminal window
# View specific version
wh thing view cave --version 1
# View current (HEAD) version
wh thing view cave

Revising a thing with data identical to its current HEAD is a no-op — no new version is created. See Operations for details.

Things have an active flag. By default, things are active. Retracting a thing hides it from HEAD queries and other default queries, but preserves its name, data, and full version history. The retained name no longer counts as claimed, so a new thing may be created under it — see Naming. A retracted thing cannot be renamed. For exactly what retracting or renaming a thing does to the assertions, collections, and wrefs that point at it, see Retract, Rename & Schema Changes.

Terminal window
# retract marks inactive, records optional reason
wh thing retract cave --reason "superseded by cave-v2" -m "retract"
Terminal window
# Snapshot of active things and assertions.
# System-managed component infrastructure records are hidden by default.
wh thing list
# Filter by shape — shows all things in that shape, including component-seeded ones
wh thing list --shape Location
# Hide all component-owned records (stricter than default)
wh thing list --exclude-components
# View a specific thing
wh thing view cave
# Version history
wh thing history cave --limit 10
# Filtered query
wh thing query --shape Location --limit 50
# Query only component-owned records (the component's Org/Name ref)
wh thing query --component acme/research --limit 50

Reads return the thing’s data as a raw payload — the SDK cannot know your schema at read time. Each SDK lets you narrow or validate that payload on the client side:

  • TypeScript — data is typed as unknown; narrow with a cast or runtime parse (e.g. zod) before accessing fields.
  • Python — data is a JsonValue (mapping, scalar, or array); pass decode_as= a dataclass to decode and validate field names at the boundary.

See Read Semantics for full decode patterns, history traversal, and batch reads (currently TypeScript-focused; Python patterns follow the same structure with snake_case names).

Every entity in a repo is one of four kinds. A write does not state the kind: a hub/shapes/<Name> name is a shape, a thing one of whose declared shapes composes Assertion is an assertion, and a thing under Arc, Bond, Set, or List is a collection:

KindDescription
shapeSchema definition — fields and types
thingNamed entity with data
assertionClaim about a shape or shaped thing (has a data.about reference)
collectionBuilt-in grouping of things — an Arc, Bond, Set, or List

They share one lifecycle: each has a wref, is versioned, and is created or modified through writes.