Skip to content

Writes

All mutations in WarmHub flow through one operation pipeline. Operations arrive as a flat stream and apply one at a time, each in its own database transaction. There is no durable batch envelope — graph history lives on each thing’s version trail (wh thing history).

OperationEffect
addCreate a thing, shape, assertion, or collection at version 1
reviseAppend a new version with changed data
retractWithdraw a thing/shape/assertion/collection from default reads (name remains reserved)

Use retract for lifecycle changes. revise changes data only and does not accept active.

CLI, SDK, and MCP writes (wh commit submit, client.commit.apply, and warmhub_commit_submit) apply operations one at a time. If a later operation fails, earlier ones are still in place — the per-operation results array tells you what succeeded, what no-op’d, and what failed, so you can retry just the failed ones. Request-level failures such as authentication, malformed operation JSON, rate limits, or infrastructure errors still reject the whole request.

Use #N references or structured collection about to chain operations within a request — a dependent operation fails with a DEPENDENCY_FAILED or UNRESOLVED_TOKEN error when its prerequisite didn’t land.

Terminal window
# Single operation
wh commit submit --add temp-1 --shape Sensor \
--data '{"location": "Building A", "type": "temperature"}' \
-m "Add sensor" --committer Agent/bot-1
# Multiple operations — repeat --add paired with --data (≤20 ops)
wh commit submit \
--add Sensor/temp-1 --data '{"location":"A"}' \
--add Sensor/temp-2 --data '{"location":"B"}' \
-m "Two sensors"
# Batch from JSON file
wh commit submit --file ops.json -m "Bulk import"
# Stream large datasets from a JSONL file
wh commit submit --file dataset.jsonl --progress -m "100k import"
# Stream from stdin
producer | wh commit submit --stream -m "Pipe ops"
# Resume a partial seed — skip already-existing adds
wh commit submit --file dataset.jsonl --skip-existing -m "Resume seed"

The --committer flag is optional. The shorthand flags (--add, --data, --shape, --about, --kind, --retract, --reason) are repeatable and pair by index. See Commit Create Deep-Dive.

committer is a wref, not a free-form label — Agent/bot-1 resolves to the Agent shape’s bot-1 thing, and that thing must already exist in the repo. A bare name like eval-runner is rejected with a “Thing wref required” error. Use a cross-repo wref (wh:other-org/other-repo/Agent/bot-1) to attribute writes to an agent that lives in another repo.

{
"name": "warmhub_commit_submit",
"arguments": {
"committer": "Agent/claude",
"message": "Add sensor with reading",
"operations": [
{ "operation": "add", "kind": "thing", "name": "Sensor/temp-1", "data": { "location": "Building A", "type": "temperature" } },
{ "operation": "add", "kind": "assertion", "name": "Reading/temp-1-v1", "about": "Sensor/temp-1", "data": { "value": 72.5, "unit": "fahrenheit" } }
]
}
}
await client.commit.apply(orgName, repoName, "Add sensor", [
{ operation: "add", kind: "thing", name: "Sensor/temp-1", data: { location: "Building A" } },
])
// Direct streaming surface for large or chunked workloads
await client.stream.append({
orgName, repoName,
streamId: crypto.randomUUID(),
operations: [...],
})

client.stream.append() is the low-level stream wire API and returns backend entry statuses (success, noop, failed). The commit-oriented surfaces (client.commit.apply(), OperationBuilder.commit(), CLI, and MCP) normalize those to the public commit statuses shown below: applied, noop, and error.

WarmHub does not mount a REST endpoint for writes. HTTP clients should use the SDK, CLI, or MCP surfaces; see the HTTP API note.

When a chunk of operations is submitted, each one:

  1. Validates against shape and structural constraints (preflight)
  2. Resolves wref references and stream-local tokens ($N / #N)
  3. Pins version references (bare wrefs → current HEAD version)
  4. Computes data hashes (server-side only — clients never compute hashes)
  5. Records a new thing version in its own transaction

A failed operation is recorded in the per-op results array. Successful operations earlier in the same chunk remain.

A write request returns per-operation results:

{
"operationCount": 3,
"partial": true,
"statusCounts": { "applied": 2, "noop": 0, "error": 1 },
"operations": [
{ "opIndex": 0, "name": "Sensor/temp-1@v1", "operation": "add", "version": 1, "dataHash": "abc123", "status": "applied" },
{ "opIndex": 1, "name": "MissingShape/bad", "operation": "add", "status": "error", "error": { "code": "NOT_FOUND", "message": "Shape not found" } },
{ "opIndex": 2, "name": "Sensor/temp-2@v1", "operation": "add", "version": 1, "dataHash": "def456", "status": "applied" }
]
}

Every operation row carries status (applied, noop, or error) on all surfaces. Clean-success responses may omit the top-level partial and statusCounts fields for backward compatibility. MCP may also omit opIndex on rows from an ordinary clean-success response where result indexes match local array positions; resumed submissions and partial responses always include opIndex so callers can correlate every result with its submitted operation. Idempotent revises (same data hash) return operation: "noop". With skipExisting: true on an add, an existing target also returns operation: "noop".

There is no commitId — graph history lives in per-thing version trails, visible through wh thing history.

Successful operations emit per-operation write events. Subscriptions match against those events to fire webhook calls or scheduled actions. See Subscriptions.

  • Per-operation atomicity. Each operation succeeds or fails on its own.
  • Data hashes are server-computed. Clients store hashes from results and compare against DB values.
  • Convenience wrappers exist (wh thing revise, wh assertion create, wh thing retract) but they all call the same write pipeline.
  • Auditing is per-thing. Use wh thing history <wref> for the version trail and attribution for each version.