Overview#
A query is a line that starts with ?. It names a path into the state, optionally followed by a bracketed tail of read directives, and it returns the matching part of the state as TERSL. A query outside a declaration never changes anything. This page covers path expressions, what a result contains, and each read directive, with the answer each sample returns. After reading it you can retrieve exactly the part of a store you need and tell from the answer what was left out.
Every sample on this page runs against this state:
# System(online)
## Agents
Accounting
Coding(prefers @System.Models.Astra)
## Models
Astra(Astra_v2_5; cost_M_usd: 1.5)
Zemma 7(Zem7; cost_M_usd: 3.0)
# Runs(by id; in-progress)
## 5783(date: 2026-12-23; completed)
### Result
"The primary answer was 42"
Paths#
A path is a sequence of names separated by dots, from the top level down. Naming a node returns that node and its whole subtree. A bare ? returns the whole state.
? System.Agents
# System(*)
## Agents
Accounting
Coding(prefers @System.Models.Astra)
The result is real state at its true depth, with every container above the match included. Those containers are addresses: they show where the match sits and whether they carry attributes, and nothing more. Each container in a result carries one of three spellings:
| You see | It means |
|---|---|
## Agents |
The node has no attributes. |
# System(*) |
The node has attributes, and you did not ask about this node. |
# System(online) |
The node is one you asked about, shown with its full attribute list. |
A result never contains (), because in a declaration an empty list clears every attribute.
Selecting children#
A trailing .* selects the immediate children of the path node, objects and containers alike, and the node you named becomes an address. ? System.* returns Agents and Models with their subtrees under # System(*). The difference matters when a directive follows, because directives run per match: ? System.Models.* [WHERE …] tests each model, while ? System.Models [WHERE …] tests the Models container itself, which has no such attribute and so matches nothing.
? * selects everything at the top level of the store. A star anywhere other than the end of a pure query path, as in ? Places.*.Bucket, is refused with TERSE904. An object has no children, so a trailing .* after an object is a legal, empty answer.
What an answer leaves out#
When an answer omits part of a node, it says so with the same three tokens that declarations use to mean "keep the rest":
| Token | Position | Stands for |
|---|---|---|
* |
An object line | Objects of the node above that were left out. |
## * (any depth) |
A header at the child depth | Child containers that were left out. |
(*) |
An attribute list | Attributes that exist and are not shown. |
These marks appear only where something was omitted, last in their run. An answer that omitted nothing carries no marks. The containers above a match are not marked, so ? System.Agents does not mention Models; the one exception is the container directly above a matched object, whose left-out siblings are always marked.
Results also follow three further rules. Several ? lines in one request are answered as one union, and a node that any line reached in full is shown in full. No node appears twice. Applying a result back to the state it came from changes nothing, byte for byte.
Read directives#
Directives go in one bracketed tail at the end of the query line. Segments are separated by ; and may come in any order. Directive keywords are always in capitals.
DEPTH#
[DEPTH n] bounds the answer. The match is level 0 and is shown with its full attributes, levels 1 to n−1 are shown whole, and level n is the frontier: its headers appear, their attributes collapse to (*), and their contents are replaced by marks.
? Runs [DEPTH 1]
# Runs(by id; in-progress)
## 5783(*)
### *
5783 carries attributes and has a child container, so both are marked. ? [DEPTH 1] gives an overview of the whole store, and [DEPTH 0] asks only whether a node exists and what it carries.
WHERE#
[WHERE expr] keeps the matches whose attributes satisfy an expression. Its.name reads an identifier, Its["max hp"] reads one whose name contains a space, and Its HAS name tests whether a flag or identifier with that name exists. Comparisons are ==, !=, <, <=, > and >=, combined with AND, OR, NOT and parentheses. A text literal is quoted.
? System.Models.* [WHERE Its.cost_M_usd > 2]
# System(*)
## Models
Zemma 7(Zem7; cost_M_usd: 3.0)
*
The * under Zemma 7 tells you Models has other objects that did not match. Integers and floats compare as numbers, so > 2 matches 3.0. A missing attribute or a type mismatch makes a comparison false, with no error, and a quoted literal matches a bareword or a string with the same text. A predicate that cannot be compiled matches nothing and raises the warning TERSE931. Bare Its is an identity test: [WHERE Its == "Kell"] matches the node named Kell.
CONTAINS#
[CONTAINS "text"] is a case-insensitive substring search. The match and every node under it are tested against their own line and their own TEXT, and a container does not match because one of its descendants does. A matching container is returned without its subtree.
? Runs [CONTAINS "answer"]
# Runs(*)
## 5783(*)
### Result
"The primary answer was 42"
The argument is exactly one quoted string. Several CONTAINS segments in one tail must all match.
WHEN#
[WHEN spec] filters matches by their date attributes. The specification accepts a year, a month and year, MM/DD/YYYY, and ISO dates and date-times. ? Runs.* [WHEN 2026-12-23] returns run 5783 with its subtree. A [WHERE] predicate can also compare dates directly, as in [WHERE Its.date >= 2026-12-01].
FIRST n and LAST n#
[FIRST n] and [LAST n] keep the first or last n matches in state order, after any filters. The result is still emitted in state order.
? System.Models.* [FIRST 1]
# System(*)
## Models
Astra(Astra_v2_5; cost_M_usd: 1.5)
*
When a match set mixes kinds, objects count before containers. n must be a positive integer, and a tail may carry only one slice. Without a number, [FIRST] and [LAST] are placement directives for declarations, and on a pure query they are an error (TERSE_QUERY_ERR).
CONTAINERS and OBJECTS#
[CONTAINERS] leaves out object runs and shows only the container structure. [OBJECTS] is a pruning view: it drops every subtree that holds no objects, so the containers that remain show where the objects are. The two cannot be combined, and neither marks what it dropped.
? System [CONTAINERS]
# System(online)
## Agents
## Models
Composing a query#
Directives on different axes combine in one tail: [WHERE] selects, [DEPTH n] bounds, and the kind directives choose which runs to show.
? System.Models.* [WHERE Its.cost_M_usd > 1; FIRST 1]
? Runs.* [CONTAINS "answer"; DEPTH 2]
A line may carry only one tail; a second bracket group is refused with TERSE923. To ask several questions at once, write several ? lines. Their answers are merged into one document:
? System [DEPTH 1]
? System.Models
# System(online)
## Agents
*
## Models
Astra(Astra_v2_5; cost_M_usd: 1.5)
Zemma 7(Zem7; cost_M_usd: 3.0)
Agents is at the first line's frontier and is marked; Models was reached in full by the second line, so it is shown whole and unmarked.
Queries do not write#
A query outside a declaration is read-only. Mutation directives such as [MOVED] or [APPEND] on such a line are an error, and a capitalised keyword written after the path without brackets, as in ? T.probe MOVED, is TERSE036. The same ? syntax inside a declaration selects state to copy or move; Writing with declarations describes that use.