Skip to content

Collections

Collections turn groups of things into first-class things. Because collection members are untyped wrefs, any thing can be a member — including shapes and shaped things. A collection is an ordinary named thing with a built-in shape, version history, and wref. Because collections are things, they can be the target of assertions through the standard about field.

TypeOrdered?Unique?# of MembersField Names
Arcyesyesexactly 2from, to
Bondnoyesexactly 2ends
Setnoyes1+members
Listyesno1+items

The built-in collection shapes (Arc, Bond, Set, List) are auto-created on first use. You create and revise collection instances as ordinary named things.

Every collection needs a user-chosen name. Names are not generated from the members.

Terminal window
wh collection create --type arc --name cave-route \
--members Location/A,Location/B \
-m "Create directed cave route" \
--repo myorg/world

The same operation can be submitted through the generic commit surface:

Terminal window
wh commit submit --ops '[
{
"operation": "add",
"kind": "collection",
"type": "arc",
"name": "cave-route",
"members": ["Location/A", "Location/B"]
}
]' -m "Create directed cave route" --repo myorg/world

SDK and MCP callers use the same model: add a named collection thing, then refer to that collection by wref.

const receipt = await client.collection.create("myorg", "world", {
type: "arc",
name: "cave-route",
members: ["Location/A", "Location/B"],
message: "Create directed cave route",
eventRequestId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
})

Both surfaces require an eventRequestId on create — a UUID that makes the call idempotent, so reuse it when retrying rather than minting a new one — and both return a receipt for the submitted operation. Over MCP the equivalent call is warmhub_collection_create, taking the same type, name, members, and eventRequestId arguments. See the SDK reference for full type signatures and the MCP Tools Reference for the tool’s argument schema.

Set collections can be created from the same query mechanics used by thing queries. This is useful when the membership is “everything matching this selector” at a point in time.

Replace your-org/your-repo in the examples below with the slug of the repo you are writing into.

Terminal window
wh collection create --type set --name wake-voters \
--shape Voter \
--where county=Wake \
--where state=NC \
-m "Snapshot Wake County voters" \
--repo your-org/your-repo

To build a collection in one repo from things in another repo, pass the source repo. The stored members are pinned refs to the source things.

Terminal window
wh collection create --type set --name wake-voters \
--source-repo your-org/source-repo \
--shape Voter \
--where county=Wake \
--repo your-org/your-repo

Selector-backed creation is set-only. Arc, Bond, and List require explicit members because their roles or arity are part of their meaning.

about accepts a wref. It does not accept inline collection objects.

Create the collection first, then point the assertion at it:

Terminal window
wh commit submit --ops '[
{
"operation": "add",
"kind": "collection",
"type": "arc",
"name": "cave-route",
"members": ["Location/A", "Location/B"]
},
{
"operation": "add",
"kind": "assertion",
"name": "Distance/cave-route",
"about": "Arc/cave-route",
"data": { "value": 5 }
}
]' -m "Record route distance" --repo myorg/world

You can also create the collection in one command and later assert about Arc/cave-route.

Collection members are pinned at write time. Bare member wrefs and @HEAD resolve to exact versions when the collection is created or revised:

{
"operation": "add",
"kind": "collection",
"type": "set",
"name": "audit-scope",
"members": ["Location/A", "Location/B@HEAD", "Location/C@v2"]
}

The collection’s body stores pinned member refs. Retracting or renaming a member does not change the snapshot.

Collections are revisable because they are ordinary named things. A new version of the collection records the new membership.

Terminal window
wh collection revise Set/audit-scope \
--members Location/A,Location/C \
-m "Refresh audit scope" \
--repo myorg/world

Pinned collection wrefs act as revision guards. Revising Set/audit-scope@v1 fails if the live collection is already at a different version.

Use the collection domain for collection operations:

Terminal window
wh collection members Set/audit-scope --repo myorg/world
wh collection contains Set/audit-scope Location/A --repo myorg/world
wh collection diff Set/old-scope Set/new-scope --repo myorg/world
wh collection stats Set/audit-scope --repo myorg/world

wh thing view still works because a collection is a thing. For large Set/List bodies, thing reads may return a compact collection summary instead of the full member array. Use --data-mode full when you need the canonical JSON body:

Terminal window
wh thing view Set/audit-scope --repo myorg/world
wh thing view Set/audit-scope --data-mode full --repo myorg/world
  • Set: members are deduplicated and sorted lexicographically.
  • List: order is preserved and duplicates are kept.
  • Arc: exactly two distinct endpoints; order is preserved as fromto.
  • Bond: exactly two distinct endpoints; endpoints are sorted so input order does not matter.

Anchor an assertion about an Arc or Bond when the assertion describes a relationship that only makes sense with both endpoints. Use a single thing when the assertion is only about that thing.

Use Arc for directional relationships:

{
"operation": "add",
"kind": "collection",
"type": "arc",
"name": "alice-trusts-bob",
"members": ["Player/alice", "Player/bob"]
}

Use Bond for symmetric binary relationships:

{
"operation": "add",
"kind": "collection",
"type": "bond",
"name": "alice-and-bob-are-teammates",
"members": ["Player/alice", "Player/bob"]
}

Use Set when membership matters but order does not. Use List when order or duplicate membership is meaningful.