HEAD Queries
A HEAD query returns all active items in a repository — things, assertions, and shapes — at their current version. It’s the fastest way to orient yourself in a repository.
The CLI names these snapshot reads list for consistency across domains; the command returns the repository HEAD projection.
# All active itemswh thing list
# Filter by shapewh thing list --shape Location
# Filter by declared shape (direct declaration only)wh thing list --declared-shape Location
# Expand each row with its validated-shape closure (opt-in projection)wh thing list --declared-shape Location --include-validated-shapes
# Filter by kind: thing, shape, assertion, or collection (deprecated).# Rows only ever report kind "thing" or "shape"wh thing list --kind assertion
# Limit results (default: 50, max: 500)wh thing list --limit 50
# Glob filter on Thing names (* = one segment, ** = zero or more)wh thing list --shape Location --match "north/*"
# JSON outputwh thing list --json--match matches the Thing’s name, not its Shape: --match "Location/*" finds Things whose names start with Location/, not Things of the Shape Location. Select by Shape with --shape.
Both wh thing list and wh assertion list return one page by default (up to 50 items). Use --limit to request up to 500 items per page, --cursor to advance to a specific page, or --all to automatically exhaust all pages:
# Fetch all pages automaticallywh thing list --all
# Advance to a specific page using a cursor from a previous responsewh thing list --limit 50 --cursor <cursor-value>In human-readable output, when a result set is truncated the CLI prints continuation guidance so you know more pages are available. With --json, that guidance is instead carried structurally as page.hasMore and page.nextCursor on the response envelope.
--declared-shape and --include-validated-shapes
Section titled “--declared-shape and --include-validated-shapes”--declared-shape matches only the shape a thing was actually written under. --shape is broader — it also matches things that were validated against that shape indirectly, such as through a composite that includes it — so for the same name --declared-shape returns a subset of what --shape returns.
--include-validated-shapes does not change which items come back. It adds a field: returned items gain a validatedShapes array listing each shape that validated them. Shape rows carry an empty array, so read the field as possibly empty rather than always populated. It is opt-in because that list is not in the default payload, and it works with or without --declared-shape.
# Only items that directly declare Locationwh thing list --declared-shape Location
# The same items, each row expanded with its full validatedShapes closurewh thing list --declared-shape Location --include-validated-shapes
# Polling read with declared-shape filterwh thing list --live --declared-shape Location
# Polling read, rows expanded with the validated closurewh thing list --live --declared-shape Location --include-validated-shapesRestrictions:
--include-validated-shapescannot be combined with--count.--since-repo-seqcannot be combined with--live.- Neither
--declared-shapenor--include-validated-shapescan be combined with--since-repo-seq: an incremental read rejects both withVALIDATION_ERROR. --declared-shapecannot be combined with--match, and cannot be used with a token narrowed to a subset of refs. Either case is rejected withVALIDATION_ERROR.
Reading collections
Section titled “Reading collections”A collection is a thing. It reads as kind: "thing" and says what it is through a collections array, derived from the Shapes its version actually satisfies:
{ "wref": "team", "kind": "thing", "declaredShapes": ["Set@v2"], "collections": [ { "shape": "Set@v2", "memberCount": 3 } ]}So --kind thing includes collections, and to find only collections you filter by shape (--shape Set) or look for a collections array on the rows you get back. A thing that is not a collection has no collections field at all.
Because the array is derived from the certificate closure rather than from the shape name, a thing under your own Shape that composes Arc is an Arc here — it carries an Arc entry and the collection reads work on it. A thing composing two collection Shapes carries two entries, and the collection read procedures take a shape argument to say which one to read.
For the full inventory of current collection shapes, see the Collections page.
Counting
Section titled “Counting”Use --count to get just the number of matching items instead of the full result list:
# Count all active itemswh thing list --count
# Count by shapewh thing list --shape Location --count
# Count assertionswh assertion list --countwh assertion list --shape Observation --count--count cannot be combined with --limit, --cursor, --all, --live, or --include-validated-shapes.
For assertions specifically:
# Equivalent to: wh thing list --kind assertionwh assertion list
# With shape filterwh assertion list --shape ObservationLike wh thing list, wh assertion list returns one page by default (up to 50 items, max 500 per page). Use --all to fetch all pages or --limit <n> --cursor <cursor-value> to paginate manually.
{ "name": "warmhub_thing_head", "arguments": { "shape": "Location", "kind": "thing", "match": "dungeon/*", "limit": 100 }}Count via MCP:
{ "name": "warmhub_thing_head", "arguments": { "shape": "Location", "count": true }}orgName and repoName are required.
The kind parameter controls which rows are returned. kind: "thing" includes collections, because a collection is a thing; the deprecated kind: "collection" narrows to the built-in collection shapes. See Reading collections above.
The warmhub_thing_head tool accepts shape, shapeless, kind, match, excludeInfraShapes, limit, cursor, sinceRepoSeq, count, and where. shapeless: true returns only things no Shape certifies; see Shapeless filter. The declaredShape and includeValidatedShapes filters are available on the CLI and SDK but are not exposed on the MCP tool surface.
MCP incremental reads with sinceRepoSeq
Section titled “MCP incremental reads with sinceRepoSeq”MCP callers can pass sinceRepoSeq to read only the items that have changed since a known point in the repo’s sequence. This lets your agent avoid re-fetching the full snapshot on every call — it receives only the delta since the last read.
Seeding the first snapshot. Pass sinceRepoSeq: -1 on the first call. The tool treats -1 as “start from the beginning of the repo’s sequence,” which begins a full scan of current HEAD state. That first call returns one page, not the whole repo — follow nextCursor through every page as described below, and you hold the complete snapshot only once you reach the terminal page.
{ "name": "warmhub_thing_head", "arguments": { "sinceRepoSeq": -1, "limit": 100 }}Paginating an incremental read. When the response includes a nextCursor field, more pages are available. Advance through them by passing the same sinceRepoSeq lower bound and the same limit you used on the first call — do not update either value mid-pagination. The lower bound and limit must stay constant across all pages of a single incremental read.
{ "name": "warmhub_thing_head", "arguments": { "sinceRepoSeq": -1, "limit": 100, "cursor": "<cursor-from-previous-page>" }}Persisting the sequence position. repoSeq appears at the top level of the response only on the terminal page — the last page, which has no nextCursor. (nextCursor and repoSeq are mutually exclusive: they never appear together on the same response.) Persist repoSeq only from that terminal page. Persisting an intermediate page’s value risks missing items that arrived between pages. On the next incremental read, pass the persisted value as sinceRepoSeq to receive only the items that changed after that point.
Count with sinceRepoSeq. You can combine sinceRepoSeq with count: true to get the number of changed items without fetching the full result list. Count responses also return repoSeq at the top level (there is no nextCursor on a count response). Persist that repoSeq to checkpoint your position.
{ "name": "warmhub_thing_head", "arguments": { "sinceRepoSeq": 4821, "count": true }}Summary of the contract:
| Step | What to do |
|---|---|
| First call | Pass sinceRepoSeq: -1 to seed a full snapshot |
| Paginating | Keep the same sinceRepoSeq lower bound and the same limit on every page of the same read; continue while the response includes nextCursor |
| Persisting position | Save repoSeq only from the terminal page (no nextCursor present) or from a count response |
| Next incremental read | Pass the persisted repoSeq as sinceRepoSeq |
| Retractions | Not surfaced by this tool — see Deletions are not reported below |
When an incremental read is refused
Section titled “When an incremental read is refused”Not every request shape can be served incrementally, and the two ways a request is turned away report differently:
- Your token must have read authority over the whole repository. A token narrowed to a subset of refs is refused with
FORBIDDEN. - The query must be one the backend can serve exactly. That rules out combining
sinceRepoSeqwith a globmatchfilter. A query that does not qualify, or a repo whose incremental machinery is not ready yet, is refused withINCREMENTAL_READ_UNAVAILABLE. That error deliberately does not say which of the two it was.
Deletions are not reported
Section titled “Deletions are not reported”warmhub_thing_head does not expose an includeRetracted parameter, so an incremental read returns only rows that are currently active. A deleted or retracted item is simply absent from the response — it does not come back as an active: false row. So a reader that stores its last sequence position cannot tell “this item was deleted” apart from “this item did not change.” Do not use this surface alone to drive a local copy that has to remove rows.
The SDK helper client.thing.headChanges(...) does report deletions. Call it with the same lower bound you would pass to MCP:
const { items, repoSeq } = await client.thing.headChanges("my-org", "my-repo", { sinceRepoSeq: -1,})// Deleted rows arrive as `active: false`.// It pages internally, so `repoSeq` is already the final position — save it// and pass it as `sinceRepoSeq` next time.Key your local state by item.metadata.durableId — the durable id is a stable identifier that survives rename and retraction — rather than by wref, the name reference, which changes when a thing is renamed and would duplicate rows in your copy.
One limit remains: this helper is not a repo-wide deletion feed covering every writer, so if you need to catch deletions made by anyone, re-read the full snapshot periodically rather than trusting the delta.
import type { Page, ThingItem } from "@warmhub/sdk-ts";
// Optionally filter by shape, kind, or a wref globconst page: Page<ThingItem> = await client.thing.head("my-org", "my-repo", { shape: "Location", kind: "thing", match: "dungeon/*", limit: 100,});for (const item of page.items) console.log(item.wref);
// Filter by declared shape, with optional validated-closure expansionconst declaredPage: Page<ThingItem> = await client.thing.head("my-org", "my-repo", { declaredShape: "Location", includeValidatedShapes: true, limit: 100,});
// Page through results with the returned cursorif (page.nextCursor) { // pass page.nextCursor back as `cursor` on the next call}All filters are optional. A single call returns one Page<ThingItem> — follow nextCursor, or use client.thing.headAll / headIter, to read past the first page. The example assumes a configured client; see the SDK Quickstart to create one, and Read Semantics for filters, glob match, and pagination.
The kind parameter controls which rows are returned. kind: "thing" includes collections, because a collection is a thing; the deprecated kind: "collection" narrows to the built-in collection shapes. See Reading collections above.
includeValidatedShapes cannot be combined with count-only reads.
HTTP API
Section titled “HTTP API”The HTTP examples below use the public warmhub-data/us.congress.trades repo so you can copy-paste them directly; the CLI and MCP blocks above use generic Location names that you’d swap for your repo’s own shapes.
GET /api/repos/warmhub-data/us.congress.trades/headGET /api/repos/warmhub-data/us.congress.trades/head?shape=CongressTrade&kind=thing&limit=25GET /api/repos/warmhub-data/us.congress.trades/head?match=CongressTrade%2F20034954%2F*&limit=25Anonymous callers reading public repos are capped at limit=25 per page; authenticated callers can request up to limit=500. See Anonymous Pagination Caps.
The HTTP API does not currently mount a count-only route. Use CLI/MCP count
surfaces for count-only reads, or page through /head when using HTTP.
Response
Section titled “Response”The response contains an items array. Each item includes:
| Field | Description |
|---|---|
name | Item name |
wref | Full wref (the name for things, assertions, and collections; hub/shapes/<Name> for shape rows) |
shapeName | Shape name (omitted for shape rows) |
kind | Entity kind (shape or thing). A collection is a thing; see collections. Assertion rows report kind: "thing"; you can tell a row is an assertion because it includes aboutWref and affirmedWrefs |
version | Current version number |
data | Current data payload |
aboutWref | About target wref (assertions only) |
affirmedWrefs | Affirmed wrefs (assertions only) |
declaredShapes | Array of version-pinned shape wrefs (e.g. Shape@vN) that this item directly declares membership in — present only when membership projection is available |
validatedShapes | Array of version-pinned shape wrefs in the validated closure for this item — present only when includeValidatedShapes was requested (SDK and CLI only; not available on the HTTP or MCP surfaces) |
metadata | Metadata envelope (see below) |
declaredShapes is included when membership projection is available for the query; it is not guaranteed on every HEAD row. validatedShapes is included only when the caller opts in via includeValidatedShapes on the SDK or CLI; it covers the full validated closure and will be absent from the row when the option was not requested or is not supported on the surface being used.
Metadata envelope
Section titled “Metadata envelope”Every HEAD row includes a metadata object with the following fields:
| Field | Description |
|---|---|
metadata.durableId | Stable identifier for the item that does not change across versions |
metadata.createdOn | Unix timestamp (milliseconds since epoch) of when the item was first created |
metadata.revisedOn | Unix timestamp (milliseconds since epoch) of when the current version was created |
Use durableId when you need a stable reference that survives updates. Use createdOn and revisedOn to track when items were created and last changed.
Reactive Mode
Section titled “Reactive Mode”Watch HEAD in real-time:
wh thing list --liveThis re-runs the query periodically and re-renders the display whenever the data changes. On TTY terminals, the output refreshes in place. Combine with filters:
wh thing list --live --shape Location --kind thing
# Polling read with declared-shape filterwh thing list --live --declared-shape Location
# Polling read with declared-shape filter and validated closurewh thing list --live --declared-shape Location --include-validated-shapesWhen to Use HEAD
Section titled “When to Use HEAD”HEAD queries are best for:
- Orientation — understanding what’s in a repo before targeted queries
- Monitoring — watching for changes with
--live - Broad snapshots — getting everything of a particular shape or kind
For targeted lookups, prefer filtering queries which accept more specific criteria.