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.
When to Use Things
Section titled “When to Use Things”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.
Creating Things
Section titled “Creating Things”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.
# CLIwh commit submit --add acme --shape Company --data '{"industry": "fintech", "stage": "series-b"}' -m "Add company"// SDKawait client.commit.apply("myorg", "world", "Add company", [ { operation: "add", kind: "thing", name: "Company/acme", data: { industry: "fintech", stage: "series-b" } },])// MCP — warmhub_commit_submit{ "name": "warmhub_commit_submit", "arguments": { "orgName": "myorg", "repoName": "world", "operations": [ { "operation": "add", "kind": "thing", "name": "Company/acme", "data": { "industry": "fintech", "stage": "series-b" } } ] }}The thing’s wref is Company/acme — the shape name followed by the thing name.
Naming
Section titled “Naming”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.
Location/dungeon/room-1Location/dungeon/room-2GameState/round-1/stateThe first segment is always the shape name. Everything after the first / is the thing name. Names cannot start or end with /, and cannot contain //.
Versioning
Section titled “Versioning”Every mutation that changes a thing creates a new version. Versions are numbered sequentially starting at v1:
# CLI — v1 initial creation, then v2 revised datawh commit submit --add cave --shape Location --data '{"x": 3, "y": 7}'wh thing revise Location/cave --data '{"x": 5, "y": 3}' -m "Move cave"// SDK — revise produces v2await client.commit.apply("myorg", "world", "Move cave", [ { operation: "revise", kind: "thing", name: "Location/cave", data: { x: 5, y: 3 } },])// MCP — warmhub_commit_submit{ "name": "warmhub_commit_submit", "arguments": { "orgName": "myorg", "repoName": "world", "operations": [ { "operation": "revise", "kind": "thing", "name": "Location/cave", "data": { "x": 5, "y": 3 } } ] }}Each version is immutable — old versions are preserved and queryable:
# View specific versionwh thing view Location/cave --version 1
# View current (HEAD) versionwh thing view Location/caveRevising a thing with data identical to its current HEAD is a no-op — no new version is created. See Operations for details.
Active/Inactive Lifecycle
Section titled “Active/Inactive Lifecycle”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. A retracted thing retains its name and can still 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.
# CLI — retract marks inactive, records optional reasonwh thing retract Location/cave --reason "superseded by Location/cave-v2" -m "retract"// SDKawait client.commit.apply("myorg", "world", "Retract cave", [ { operation: "retract", name: "Location/cave", reason: "superseded by Location/cave-v2" },])// MCP — warmhub_commit_submit{ "name": "warmhub_commit_submit", "arguments": { "orgName": "myorg", "repoName": "world", "operations": [ { "operation": "retract", "name": "Location/cave", "reason": "superseded by Location/cave-v2" } ] }}Querying Things
Section titled “Querying Things”# 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 oneswh thing list --shape Location
# Hide all component-owned records (stricter than default)wh thing list --exclude-components
# View a specific thingwh thing view Location/cave
# Version historywh thing history Location/cave --limit 10
# Filtered querywh thing query --shape Location --limit 50
# Query only component-owned recordswh thing query --component com.acme.research --limit 50Reading Thing Data in TypeScript
Section titled “Reading Thing Data in TypeScript”ThingDetail.data is typed as unknown on the SDK so the compiler refuses
property access until the caller narrows the payload to its shape. There is no
Thing<MyShape> generic — narrow with a cast or a runtime parse before
reaching for fields:
import type { ThingDetail } from "@warmhub/sdk-ts";
type LocationData = { x: number; y: number; label?: string };
const detail = await client.thing.get("acme", "world", "Location/cave");if (!detail) throw new Error("not found");
// Option 1 — trust the shape and cast (cheapest)const data = detail.data as LocationData;console.log(data.x, data.y);
// Option 2 — validate at the boundary (e.g. with zod)// const data = LocationSchema.parse(detail.data);
// History items carry the same `data?: unknown` field on each version row.const history = await client.thing.history("acme", "world", "Location/cave");const v1 = history.versions[0];const previous = v1?.data as LocationData | undefined;If the shape is known statically, prefer the cast for ergonomics. If the payload is untrusted or shape evolution is in play, parse it through a schema (zod, valibot, ajv) so the runtime check stays close to the read.
Every entity in a repo is one of four kinds, set by the kind field on a write:
| Kind | Description |
|---|---|
shape | Schema definition — fields and types |
thing | Named entity with data |
assertion | Claim about another thing (has about reference) |
collection | Built-in grouping of things — a Pair, Triple, Set, or List |
They share one lifecycle: each has a wref, is versioned, and is created or modified through writes.
Hit a problem or have a question? Get in touch.