Skip to content

Collections

Collections turn groups of things into first-class entities. A Pair of two things is itself a thing — with its own shape, version history, and wref. Because collections are things, they can be the target of assertions via the standard about model.

TypeOrdered?Unique?# of ThingsField Names
Pairyesno2first, second
Tripleyesno3first, second, third
Setnoyes1+members
Listyesno1+items

The four built-in collection shapes (Pair, Triple, Set, List) are auto-created on first use. They cannot be created or revised manually. See Shapes — Built-in Shapes for the full list of core built-in shapes.

The about field on assertions accepts either a wref string or a tagged collection object:

about: "Location/A" // single thing
about: { pair: ["Location/A", "Location/B"] } // Pair
about: { triple: ["Player/alice", "Cell/1-1", "Item/g"] } // Triple
about: { set: ["Cell/0-0", "Cell/0-1", "Cell/1-0"] } // Set
about: { list: ["Cell/0-0", "Cell/0-1", "Cell/0-0"] } // List

The tag is always explicit. Each tagged object must have exactly one key.

Collections can be created without an assertion using kind: 'collection':

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

With a custom name:

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

If name is omitted, it’s auto-generated from the members.

Collection names are derived deterministically from their members. All members are version-pinned.

Pair/Location/Av1+Bv1 — Pair of Location/A@v1, Location/B@v1
Set/Cell/0-0v1+0-1v1+1-0v1 — Set of 3 Cells (sorted)
List/Location/Av1+Bv1+Cv1+Av1 — List with duplicates preserved

The wref starts with the collection type, then the shared member shape, then member names joined by + with version pins:

CollectionShape/MemberShape/bareNameVersionPin+joined+by+plus

Heterogeneous (members have different shapes)

Section titled “Heterogeneous (members have different shapes)”
Pair/Player+Location/alicev1+cavev1 — Player/alice@v1, Location/cave@v1
Triple/Player+Cell+Item/alicev1+cavev1+goldv1 — three different shapes

When members have different shapes, the shape names are joined by + before the /, followed by the member names in the same order:

CollectionShape/Shape1+Shape2+.../name1v1+name2v1+...

All collection members are always version-pinned. Bare wrefs and @HEAD are auto-resolved to the current HEAD version at commit time:

// Bare wrefs — auto-pinned to HEAD
about: { pair: ["Location/A", "Location/B"] }
// If A=v2, B=v1 → creates Pair/Location/Av2+Bv1
// Explicit version pins — pass through unchanged
about: { pair: ["Location/A@v1", "Location/B@v3"] }
// Creates Pair/Location/Av1+Bv3
// Intra-commit ADD — auto-pinned to @v1
operations: [
{ operation: "add", kind: "thing", name: "Location/X", data: { x: 1 } },
{ operation: "add", kind: "assertion", name: "D/x",
about: { pair: ["Location/X", "Location/Y"] }, data: { v: 1 } },
]
// Location/X (created in same commit) → auto-pinned to @v1
  • Set: members are deduplicated and sorted lexicographically. {B, A, A} becomes {A, B}.
  • List: order preserved, duplicates kept. [A, B, A] stays [A, B, A].
  • Pair: exact — (A, B) is not (B, A).
  • Triple: exact — order matters for all three positions.

Same members at the same versions produce the same collection thing:

// Commit 1: creates Pair/Location/Av1+Bv1
{ about: { pair: ["Location/A", "Location/B"] } }
// Same commit or later (members not revised): reuses the same Pair thing
{ about: { pair: ["Location/A", "Location/B"] } }
// After revising Location/B to v2: creates NEW Pair/Location/Av1+Bv2
{ about: { pair: ["Location/A", "Location/B"] } }

Collections are things — they can be members of other collections:

// Create a pair of pairs
{ about: { pair: ["Pair/Location/Av1+Bv1", "Pair/Location/Cv1+Dv1"] } }

Use Pair for directional relationships (order matters):

// alice → bob trust (different from bob → alice)
{ about: { pair: ["Player/alice", "Player/bob"] }, data: { trust: 0.8 } }

Use Set for symmetric relationships (order doesn’t matter):

// Adjacency is symmetric: {A,B} = {B,A}
{ about: { set: ["Cell/0-0", "Cell/0-1"] }, data: { direction: "north" } }

By default, querying --about Location/A only returns assertions that directly target Location/A. Assertions about a Pair or Set containing Location/A are not included.

Use --resolve-collections to also include assertions about collections containing the target:

Terminal window
# Direct assertions about Location/A only
wh assertion list Location/A
# Also includes assertions about Pair/Location/Av1+Bv1, Set/Location/Av1+Bv1+Cv1, etc.
wh assertion list Location/A --resolve-collections

Collection resolution is HEAD-only — it finds collections that currently contain the thing. It does not resolve historical memberships for versioned queries (@vN).

Supported on: wh thing query, wh thing history, wh thing search (text mode), wh assertion list.

Batch tokens ($N and #N) work in collection member wrefs, allowing you to create things and reference them in collection assertions within the same commit. Tokens are resolved before version pinning and collection expansion.