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.
Collection Types
Section titled “Collection Types”| Type | Ordered? | Unique? | # of Members | Field Names |
|---|---|---|---|---|
| Arc | yes | yes | exactly 2 | from, to |
| Bond | no | yes | exactly 2 | ends |
| Set | no | yes | 1+ | members |
| List | yes | no | 1+ | 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.
Creating Collections
Section titled “Creating Collections”Every collection needs a user-chosen name. Names are not generated from the members.
wh collection create --type arc --name cave-route \ --members Location/A,Location/B \ -m "Create directed cave route" \ --repo myorg/worldThe same operation can be submitted through the generic commit surface:
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/worldSDK 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",})Selector-Backed Sets
Section titled “Selector-Backed Sets”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.
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-repoTo 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.
wh collection create --type set --name wake-voters \ --source-repo your-org/source-repo \ --shape Voter \ --where county=Wake \ --repo your-org/your-repoSelector-backed creation is set-only. Arc, Bond, and List require explicit members because their roles or arity are part of their meaning.
Asserting About Collections
Section titled “Asserting About Collections”about accepts a wref. It does not accept inline collection objects.
Create the collection first, then point the assertion at it:
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/worldYou can also create the collection in one command and later assert about Arc/cave-route.
Version Pinning
Section titled “Version Pinning”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.
Revising Collections
Section titled “Revising Collections”Collections are revisable because they are ordinary named things. A new version of the collection records the new membership.
wh collection revise Set/audit-scope \ --members Location/A,Location/C \ -m "Refresh audit scope" \ --repo myorg/worldPinned collection wrefs act as revision guards. Revising Set/audit-scope@v1 fails if the live collection is already at a different version.
Reading Collections
Section titled “Reading Collections”Use the collection domain for collection operations:
wh collection members Set/audit-scope --repo myorg/worldwh collection contains Set/audit-scope Location/A --repo myorg/worldwh collection diff Set/old-scope Set/new-scope --repo myorg/worldwh collection stats Set/audit-scope --repo myorg/worldwh 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:
wh thing view Set/audit-scope --repo myorg/worldwh thing view Set/audit-scope --data-mode full --repo myorg/worldNormalization Rules
Section titled “Normalization Rules”- Set: members are deduplicated and sorted lexicographically.
- List: order is preserved and duplicates are kept.
- Arc: exactly two distinct endpoints; order is preserved as
from→to. - Bond: exactly two distinct endpoints; endpoints are sorted so input order does not matter.
Modeling Guidance
Section titled “Modeling Guidance”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.