Skip to content

Naming as Navigation

Every thing and assertion in a repository has a name, and all names share one namespace. A name can contain /, as in catalog/electronics/phones/pixel-9. The slashes give the name segments, and glob patterns match against those segments. A good naming scheme lets an agent list part of a repository, guess the names of things it has not seen, and subscribe to changes in one part of it.

A name says where a Thing sits in the repository. Its Shapes say what it is. The two are stated separately:

{
"operation": "add",
"name": "catalog/electronics/phones/pixel-9",
"shapes": ["Product"],
"data": { "title": "Pixel 9" }
}
  • The first segment of a name is not a Shape. In catalog/electronics/phones/pixel-9, catalog is just the first part of the name. The Thing’s Shape is Product because the write says so in shapes.
  • Names are unique across the repository, whatever the Shape. Once brands/google is a Brand, an add of brands/google under another Shape is refused. Plan names so that things of different Shapes do not need the same name. For example, a pull request pr-456 and an assertion about it cannot both be called pr-456.
  • A Shape is itself a Thing, named hub/shapes/<Name>. The Product Shape is the Thing hub/shapes/Product. The hub/ prefix is reserved: only Shapes live there, and a Thing named under hub/ is refused.

The web app uses the same name in its URL, and the short wref.ai form mirrors it:

app.warmhub.ai/orgs/acme/repos/catalog/things/catalog/electronics/phones/pixel-9
─┬── ──┬──── ───────────────┬──────────────────
org repo thing name
wref.ai/acme/catalog/catalog/electronics/phones/pixel-9
─┬── ──┬─── ───────────────┬──────────────────
org repo thing name

Both forms resolve to the same Thing. The name is the address.

A segment does not have to exist as its own Thing. catalog/electronics/phones/pixel-9 can exist with no Thing named catalog/electronics or catalog/electronics/phones. A glob such as catalog/electronics/** matches against the name string, so it finds pixel-9 either way.

By name. Segments group related things. From catalog/electronics/phones/pixel-9, an agent can list every phone (catalog/electronics/phones/*), every electronics item (catalog/electronics/**), or the whole catalog (catalog/**).

By assertion. Assertions link a thing to another thing regardless of where their names sit. A Review assertion named reviews/pixel-9/agent-1 can be about catalog/electronics/phones/pixel-9, and a SuppliedBy assertion can link the same phone to brands/google.

catalog/ brands/
├── electronics/ ├── google ◄──── SuppliedBy ────┐
│ ├── phones/ ├── apple │
│ │ ├── pixel-9 ──── supplied-by/pixel-9 (SuppliedBy) ────────────┘
│ │ └── iphone-16 └── samsung
│ └── laptops/
│ └── macbook-air reviews/
└── home/ └── pixel-9/
└── kitchen/ └── agent-1 (about: catalog/electronics/phones/pixel-9)
└── instant-pot-duo

An agent can use names to find the right set of things, then follow assertions to related things whose names are elsewhere in the repository.

An agent cannot load a whole repository, but it can read one part of it:

Terminal window
# Everything under catalog/electronics/
wh thing list --match "catalog/electronics/**"
# Only the phones
wh thing list --match "catalog/electronics/phones/*"
# Phones in every top-level category
wh thing list --match "catalog/*/phones/*"

Add --shape to filter by Shape as well as by name. The two filters are independent. --shape includes Things that reach the Shape through composition; --declared-shape matches only Things that declare it directly:

Terminal window
# Every Product, wherever its name sits
wh thing list --shape Product
# Products whose names start with catalog/electronics/
wh thing list --shape Product --match "catalog/electronics/**"

If an agent sees sensors/hq/floor-3/conference-a/temp, it can guess that sensors/hq/floor-2/conference-a/temp may exist. If it sees reports/2024-q1/summary, it can guess at reports/2024-q2/summary. A consistent scheme lets an agent find things it has not seen yet by changing one segment.

A flat name such as sensor-hq-floor-3-temp holds the same information, but globs cannot select on its parts.

The wref articles/tech/2024/transformer-scaling tells an agent the category, year, and topic before it reads any data. The Shape, Article, says what fields the data has. Together they describe a thing without a read.

Subscriptions accept the same glob syntax in their match filter, so an action can run on changes to one part of a repository:

{
"shape": "Sensor",
"all": [
{ "operation": "add" },
{ "kind": "thing" },
{ "match": "sensors/hq/**" }
]
}

A subscription on Things names their Shape, in shape at the top of the filter or as the subscription’s shapeName. The match then narrows it by name: any Sensor added under sensors/hq/, on any floor, triggers the action. Patterns can use ** (sensors/**/temp), * for one segment (catalog/*/phones/*), or braces (sensors/hq/floor-{3,4}/**). An array of patterns matches any of them ({"match": ["sensors/hq/**", "sensors/warehouse/**"]}); put two match predicates in all to require both.

Order segments by how people and agents select the data. Ask: “What are the most common ways someone will want to select part of this?”

Product catalog. Category, then subcategory, then item:

catalog/electronics/phones/pixel-9
catalog/electronics/laptops/macbook-air
catalog/home/kitchen/instant-pot-duo
catalog/outdoor/camping/rei-half-dome

This supports catalog/electronics/** (one category), catalog/electronics/phones/* (one subcategory), and catalog/**/camping/* (one subcategory across categories).

Sensor network. Location, then sensor type:

sensors/hq/floor-3/conference-a/temp
sensors/hq/floor-3/conference-a/humidity
sensors/hq/floor-3/hallway/motion
sensors/warehouse/zone-b/temp

sensors/hq/floor-3/** selects one floor, and sensors/**/temp selects every temperature sensor.

Research corpus. Source, then year, then topic:

papers/arxiv/2024/attention-mechanisms-survey
papers/arxiv/2023/transformer-efficiency
papers/acl/2024/multilingual-rag

Lead with the segment you filter on most often. If most reads are by region, put region first. If most are by date, put date first.

# Category first: most reads browse by product type
catalog/electronics/phones/pixel-9
# Region first: most reads are limited to one geography
catalog/us/electronics/phones/pixel-9

Neither order is always right. Pick the one that matches how the data will be read.

Because all names share one namespace, a top-level segment per kind of record keeps names from colliding and makes each kind easy to select: catalog/ for products, brands/ for brands, reviews/ for review assertions. The prefix is a naming convention only. The Shape still comes from shapes, and --shape filters on it.

Each segment should carry information:

# Every segment says something
companies/acme/filings/10-k/2024
# "data" and "records" add nothing
companies/acme/data/records/filings/10-k/2024

Use stable identifiers rather than timestamps or UUIDs, unless you have a reason. Names can be changed with a rename. Stored references follow the Thing, but the old name stops resolving, so any wref text kept outside the repository (docs, scripts, other systems) breaks, and a producer that still computes the old name creates a second Thing. Keep names stable once others refer to them. See Retract, Rename & Schema Changes.

If a set of things has no natural grouping, flat names are fine:

currencies/usd
currencies/eur
currencies/jpy

Two levels below the prefix are often enough. At five levels, check whether each one helps an agent select data.

Use names for grouping and assertions for relationships

Section titled “Use names for grouping and assertions for relationships”

Segments express grouping: a product belongs to a category and subcategory. Assertions express relationships: a product is supplied by a brand, or one paper cites another.

Do not put a relationship in a name:

# Don't: the supplier relationship is in the name
catalog/electronics/made-by-samsung/phones/galaxy-s24
# Do: group by category, and record the supplier in an assertion
catalog/electronics/phones/galaxy-s24
└── supplied-by/galaxy-s24 (SuppliedBy assertion)
about: catalog/electronics/phones/galaxy-s24, supplier: brands/samsung

A name answers “where is this in the repository?” An assertion answers “what is this related to?”

--match on read commands takes a glob pattern:

PatternMatches
catalog/electronics/*Names exactly one segment below catalog/electronics/
catalog/electronics/**Names at any depth below catalog/electronics/
catalog/*/phones/*Phones in every top-level category
sensors/**/tempNames ending in temp at any depth below sensors/
catalog/{electronics,home}/**Names under either catalog/electronics/ or catalog/home/
companies/acme/filings/10-*/*One segment that starts with 10- (10-k, 10-q)
hub/shapes/*The repository’s Shapes

* never crosses a /. ** crosses any number of segments.

A pattern of ** alone matches every Thing but no Shape. To list Shapes, name the prefix: hub/shapes/*.

Terminal window
# One floor
wh thing list --match "sensors/hq/floor-3/**"
# Every temperature sensor
wh thing list --match "sensors/**/temp"
# Several categories
wh thing list --match "catalog/{electronics,home,outdoor}/**"

The same patterns work in MCP queries and in the SDK’s query methods.

A common sequence is to select by name, then read assertions:

Terminal window
# 1. Find the phones
wh thing list --match "catalog/electronics/phones/*"
# 2. Read what has been asserted about one of them
wh thing about catalog/electronics/phones/pixel-9
# 3. Read a related brand
wh thing view brands/google
# 4. Read what has been asserted about the brand
wh thing about brands/google

A scraper adds product listings by category:

catalog/electronics/phones/pixel-9
catalog/electronics/phones/iphone-16
catalog/electronics/laptops/macbook-air
catalog/home/kitchen/instant-pot-duo

A Product subscription with match: "catalog/electronics/**" runs a classification action for each new item. The action adds assertions such as reviews/..., prices/..., and similar/... about the product. Agents then select products by name and read the assertions for ratings, prices, and alternatives.

Three agents study companies from different angles:

# Things
companies/acme
companies/globex
pairs/acme-v-globex (a Bond with ends companies/acme and companies/globex)
filings/acme/10-k/2024
filings/globex/10-k/2024
# Assertions, one prefix per kind
theses/acme-growth (about: companies/acme) agent-1
risk-flags/acme-debt (about: companies/acme) agent-2
comparisons/acme-v-globex (about: pairs/acme-v-globex) agent-3

An agent reviewing Acme can:

  1. List filings/acme/** to see every filing.
  2. Read the assertions about companies/acme to see each agent’s view.
  3. List what refers to companies/acme (wh thing refs companies/acme) to find pairs/acme-v-globex, read the comparison about that pair, then list filings/globex/**.
sensors/hq/floor-3/conference-a/temp
sensors/hq/floor-3/conference-a/co2
alerts/hq/floor-3/conference-a/co2-high-2024-03-15

A Sensor subscription with match: "sensors/hq/**" runs a threshold check for any sensor data under HQ. When CO2 exceeds the limit, the action adds an Alert Thing whose name repeats the sensor’s location, so */hq/floor-3/conference-a/** finds both the sensors and the alert. A second subscription, on Alert with match: "alerts/**", sends notifications.

Treating the first segment as the Shape. Product/pixel-9 is a valid name, but Product in it does not make the Thing a Product; only shapes does. Use a lowercase prefix such as catalog/ so a name is not mistaken for a Shape.

Relationships in names. If one thing’s identity appears in another thing’s name (catalog/reviewed-by-agent-1/phones/pixel-9), use an assertion instead.

UUID-only names. catalog/f47ac10b-58cc-4372-a567-0e02b2c3d479 is valid, but an agent cannot learn anything from it. Prefer readable names when the domain has natural identifiers. Otherwise use deterministic source identifiers, such as upstream IDs, content slugs, or run-scoped sequence numbers.

Inconsistent depth. If most products are catalog/<category>/<subcategory>/<item> but some are catalog/<item>, globs select unpredictably. Keep things of the same kind at the same depth.

Name segments in place of Shapes. If segments separate different kinds of data (entities/person/jones and entities/company/acme), those kinds probably need separate Shapes (Person, Company), each with its own fields. Shapes say what a thing is; names say where it sits among things of that kind.

Too many segments. data/raw/ingested/pipeline-v2/batch-003/electronics/phones/pixel-9 has segments no read filters on. Keep only the segments that agents select by.