TERSL — the language

What TERSL is

TERSL is a human-readable, AI-first language for hierarchical state, with a matching syntax for reading it with queries and changing it with declarations.

Overview#

TERSL is a human-readable, AI-first format for hierarchical state, together with a matching syntax for querying that state and for declaring changes to it. All three live in one plain text file, conventionally with the .terse extension. A TERSL file holds the state itself: read into an empty store, the file reproduces exactly what it describes, and the same text can be queried precisely to any depth and patched by declaration.

This page explains what kind of language TERSL is, what it is designed for, and how its three operations fit together. The other pages in this section cover the shape of state, reading with queries, writing with declarations and the open-source tooling.

What it is for#

TERSL is meant for state that people and language models both read and write. Typical uses are an agent's long-term memory, configuration, the world state of a simulation or game, and working records such as case files, run logs and build pipelines. In each of these, a model has to keep the state in context, find one part of it by name, and change a few lines without rewriting the rest. A person has to be able to open the same file and understand it.

The language is designed to be understood with very little instruction. It borrows notation that readers already know: # headings for hierarchy, key: value pairs for fields, and @ for references. To teach it to a model, you place the written guide or one of the short primers from the specification's prompt-reference directory in the system prompt; the primers are themselves written in TERSL.

Design priorities#

The specification states its design priority as "AI & Human-First, Machine-Second". Where readability and machine convenience conflict, the specification chooses readability and leaves the extra work to the parser. Names are natural language with spaces, and machine-friendly identifiers are derived from them by fixed rules when a program needs one.

Names are the one place where the specification restricts characters. A name may contain only letters of any script, digits, underscores, spaces, hyphens and apostrophes. Other punctuation is refused in a name, because a character that silently disappeared from a name would make two different names the same node. Punctuation remains free in attribute values, flag text and quoted strings.

Three further principles shape the rest of the language. Tokens are treated as a resource, so the syntax carries little punctuation that a reader does not need. Query and mutation are first-class: an agent changes state by declaring what should be different, instead of loading the whole document and writing it back. The modelling style favours names that carry identity, attributes that each describe one property, and hierarchy that reflects real parenthood.

The shape in one example#

This file, taken from the open-source repository's README, describes a small system with two agents, two models and one run:

tersl
# 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"

Lines that start with # are containers, and the number of # characters gives the depth: System and Runs are at the top level, Agents and Models sit inside System, and Result sits inside run 5783. Lines without a # are objects, which belong to the container above them, so Accounting and Coding are the two agents.

The parentheses after a name hold attributes, separated by semicolons. A bare word such as online or completed is a flag, a human-readable descriptor. A pair such as cost_M_usd: 1.5 is an identifier, used when a program or a query will read the value. Astra_v2_5 is a raw value, an unkeyed note for human context.

@System.Models.Astra is a reference that points to another node by its path. The quoted line under Result is the container's TEXT, its free-text payload. Every one of these constructs is described in detail on The shape of state.

The three operations#

State is the text above. Applied to an empty store, a TERSL document becomes the state, with its order preserved. Order is part of the state: siblings stay in the order they were written, and nothing sorts them.

Queries are lines that start with ?. They are read-only, and their results are themselves TERSL: the matching nodes at their true depth, with the containers above them included so that the location of every match is clear. ? System.Agents returns the agents with System above them, and a tail such as [WHERE Its.cost_M_usd > 2], [CONTAINS "answer"] or [DEPTH 1] filters or bounds the answer. A result can be applied back to the state it came from and changes nothing. See Reading with queries.

Declarations are how state changes. You write the part of the shape that should be different, and everything you do not mention stays as it is: restating a line does not move it, and leaving out an attribute does not clear it. Directives in a bracketed tail at the end of a line say what else to do, such as [FIRST] to place a line, [WAS old name] to rename, [REMOVED] to delete, or [MOVED] on an embedded query to relocate what it matches. See Writing with declarations.

Where the language lives#

The language is defined in the open-source repository terse-lang/terse. Its terse-spec directory holds the written-form guide, the normative specification and the implementer contract. The specification is at version 0.5.1, a pre-1.0 draft, and its details may change before 1.0. Where the guide and the specification differ, the specification is authoritative. The repository also contains implementations in Python and TypeScript, an MCP server and applications built on the language; Open-source tooling describes each of them.

TERSL/DB, the hosted database from Tourbillon.ai, stores and serves TERSL state and is described in What TERSL/DB is.