HEAD Queries
A HEAD query returns all active items in a repository — things, assertions, shapes, and collections — 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 kind (thing, assertion, shape, collection)wh thing list --kind assertion
# Limit results (default: 50, max: 500)wh thing list --limit 50
# Glob filter on wrefs (* = one segment, ** = zero or more)wh thing list --match "Location/*"
# JSON outputwh thing list --jsonBoth 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.
Collection vs. thing reads
Section titled “Collection vs. thing reads”When you filter with --kind collection, the results include items whose shape is one of the built-in collection types. Each row carries kind: "collection". Conversely, --kind thing excludes all collection-typed rows — only non-collection thing rows are returned. Keep this split in mind when you expect to see collection items: they will not appear in a --kind thing result set. 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, or --live.
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": "Location/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: "collection" returns collection-typed rows; kind: "thing" excludes them. See Collection vs. thing reads above for the full breakdown.
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 — so thematchexample earlier on this page cannot be turned into an incremental read by addingsinceRepoSeq. 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 Shape/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: "Location/dungeon/*", limit: 100,});for (const item of page.items) console.log(item.wref);
// 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: "collection" returns collection-typed rows; kind: "thing" excludes them. See Collection vs. thing reads above for the full breakdown.
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 (Shape/name for things, assertions, and collections; the shape name itself for shape rows) |
shapeName | Shape name (omitted for shape rows) |
kind | Entity kind (shape, thing, assertion, collection) |
version | Current version number |
data | Current data payload |
aboutWref | About target wref (assertions only) |
metadata | Metadata envelope (see below) |
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 thingWhen 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.