Skip to content

Operations

Every write request contains one or more operations. There are three operation types — add, revise, and retract — across shape, thing, assertion, and collection records. Collections provide inline syntax for creating grouped things.

A write request can include multiple operations. CLI (wh commit submit), SDK (client.commit.apply), and MCP (warmhub_commit_submit) writes apply operations one at a time — if a later operation fails, earlier ones are still in place, and you can retry just the failed ones. See Writes.

Create a new shape with field definitions:

{
"operation": "add",
"kind": "shape",
"name": "Location",
"data": { "fields": { "x": "number", "y": "number", "label": "string" } }
}

Create a new thing under an existing shape:

{
"operation": "add",
"kind": "thing",
"name": "Location/cave",
"data": { "x": 3, "y": 7, "label": "Dark Cave" }
}

Create an assertion about another thing. The about field is required:

{
"operation": "add",
"kind": "assertion",
"name": "Belief/cave-safe",
"about": "Location/cave",
"data": { "safe": true, "confidence": 0.8 }
}

about accepts local wrefs, canonical wrefs, or inline collection syntax.

Revise only changes data. It does not accept an active field — to mark an entity inactive, use a retract operation instead. TypeScript SDK users see this as a compile error because ReviseOperation declares active?: never.

Update a shape’s field definitions:

{
"operation": "revise",
"kind": "shape",
"name": "Location",
"data": { "fields": { "x": "number", "y": "number", "z": "number" } }
}

Update a thing’s data:

{
"operation": "revise",
"kind": "thing",
"name": "Location/cave",
"data": { "x": 5, "y": 3, "label": "Bright Cave" }
}

Update an assertion’s data. The about target cannot be changed:

{
"operation": "revise",
"kind": "assertion",
"name": "Belief/cave-safe",
"data": { "safe": false, "confidence": 0.2 }
}

Collections are a convenience for creating grouped things (pairs, triples, sets, lists). Under the hood, the commit pipeline expands collection operations into standard thing operations — collections are things with built-in shapes.

Create a collection thing explicitly:

{
"operation": "add",
"kind": "collection",
"type": "pair",
"members": ["Location/A", "Location/B"]
}

The name field is optional — if omitted, a canonical name is derived from the members (e.g., Pair/Location/Av1+Bv1).

You can also create collections inline within an assertion’s about field. See Collections for the full syntax.

To mark a collection inactive, use a RETRACT operation (see RETRACT Operations):

{
"operation": "retract",
"name": "Pair/Location/Av1+Bv1"
}

Mark an entity inactive. The entity’s data and version history are preserved; it is hidden from default HEAD queries.

{
"operation": "retract",
"name": "Location/cave",
"reason": "replaced by Location/dungeon"
}

The reason field is optional (max 500 chars). The kind field is an optional safety hint — the operation will error if the resolved entity’s kind doesn’t match.

Retract is the only path to setting an entity inactive. Once retracted, you can add a new entity at the same name to create a fresh identity.

  • data is required on revise for shape, thing, and assertion
  • Revise is a full replacement — you must include all fields in data, not just the ones that changed
  • Omitted fields will be absent in the new version, which will fail shape validation if they are required
  • about is required on ADD assertion, immutable on REVISE assertion

If a revise produces the same data hash as the current version, it returns operation: "noop" — no new version is created.

Within a single chunk, the following sequences on the same target are rejected by preflight:

SequenceAllowed?
ADD + ADDNo — duplicate add
REVISE + ADDNo — can’t add something that already exists
ADD + REVISEYes — create then immediately update
REVISE + REVISEYes — multiple updates in sequence

TypeScript callers binding an inline operation literal to a variable should either annotate it : Operation[] or use satisfies Operation[] to keep the operation discriminant narrowed — see SDK Write Methods — Typing Operation arrays.

The --file flag on wh commit submit accepts a JSON file containing an array of operations. For large datasets, prefer JSONL streaming (one operation per line):

Terminal window
# JSON array (small/medium batches)
wh commit submit --file operations.json --message "batch update" --repo org/repo
# JSONL stream (large datasets — chunked automatically)
wh commit submit --file dataset.jsonl --progress -m "bulk import" --repo org/repo

The JSON file must contain a JSON array of operation objects:

[
{
"operation": "add",
"kind": "thing",
"name": "Location/cave",
"data": { "x": 3, "y": 7, "label": "Dark Cave" }
},
{
"operation": "add",
"kind": "assertion",
"name": "Belief/cave-safe",
"about": "Location/cave",
"data": { "safe": true, "confidence": 0.8 }
}
]

Use wh shape template to scaffold operations from shape definitions (template generation lives in the shape domain):

Terminal window
# Single shape -> thing scaffold
wh shape template Hypothesis --repo org/repo
# Multiple shapes at once
wh shape template Hypothesis Evidence Decision --repo org/repo
# Assertion scaffold for a shaped claim about a target
wh shape template Hypothesis --kind assertion \
--about ResearchTopic/example --repo org/repo
# Write to file, then edit and commit
wh shape template Hypothesis Evidence -o experiment.json --repo org/repo
$EDITOR experiment.json
wh commit submit --file experiment.json -m "add experiment" --repo org/repo

The template fills fields with placeholder values ("", 0, false) and FILL_IN: hints for fields with descriptions. Replace placeholders with real data before writing.

Shapes define the payload schema, not whether an operation is a thing or an assertion. To scaffold an assertion, pass --kind assertion; add --about <TargetShape/name> when you want a concrete target instead of the default placeholder.

For large datasets, use JSONL format (one operation per line) with streaming:

Terminal window
wh commit submit --file dataset.jsonl --progress -m "bulk ingest" --repo org/repo

The $N (allocate) and #N (reference) tokens let you create entities and reference them within the same stream — for example, adding a thing and an assertion about it in the same chunk, or in a later chunk of the same stream. Token state is scoped to the streamId and is echoed back on each stream.append response so the next chunk can resolve #N references against earlier allocations.

#N cannot reference future tokens — order operations so adds precede the references that depend on them.

See Wrefs: Batch Tokens for the full syntax, rules, and examples.