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.

await client.collection.create("myorg", "world", {
type: "arc",
name: "cave-route",
members: ["Location/A", "Location/B"],
message: "Create directed cave route",
})

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.