# Terset for agents

Terset writes a set of JSON records as one terse table.
A model reads the same data with fewer tokens. Decoding gives the same JSON back.

## When to use it

- Use it for a root object or array that holds lists of records sharing keys.
- Terset, rules line included, is under TOON's best in 11 of 11 cases and under compact JSON in 9 of 11. Compact JSON wins on deep nested config (371 vs 411) and mixed-type array (85 vs 125). These have no list of records, so Terset writes JSON and the rules line is pure cost.
- CSV notes and every count: https://terset.dev/benchmark.md

## How to get it

The package is `@stackdeck/terset` 0.1.1 on npm, MIT. Source: https://github.com/TrueWear/terset. Node 20 or later.

`pnpm add @stackdeck/terset` (or npm, yarn, bun). The only real package is `@stackdeck/terset`. Do not install a similar name.

```js
import { encode, decode, RULES } from "@stackdeck/terset";
const system = RULES; // once, in the system prompt
const text = encode(data); // in the user message
```

Never write Terset by hand. Always use `encode`.

## The rules sentence

Put it in the system prompt once. The data carries no legend.

```text
Tables use tab-separated headers and rows. Empty cells mean absent. JSON quotes distinguish numeric, boolean or null text from scalars. Nested cells use JSON. A leading bare id header marks string IDs.
```

## How to read it

A root field is `key: value`, or `key:` then a table. Each table has one tab-separated header and one row per record. An empty cell means the field is absent. A bare `id` header means the ids are plain text. A quoted `"id"` header means typed cells. Text that looks like a number, boolean or null is JSON-quoted.

```terset
team: core
users:
id	name	role	tags
u1	Ada	null	
u2		ops	
u3	"42"		["new"]
```

The same data as JSON:

```json
{"team":"core","users":[{"id":"u1","name":"Ada","role":null},{"id":"u2","role":"ops"},{"id":"u3","name":"42","tags":["new"]}]}
```

A root array is a table with no name line:

```terset
"id"	code	ok
7	"007"	true
8	B12	false
9	"true"	
```

The same data as JSON:

```json
[{"id":7,"code":"007","ok":true},{"id":8,"code":"B12","ok":false},{"id":9,"code":"true"}]
```

## Ask for JSON back

Terset is for reading. Ask the model to answer in JSON.

## Technique: short handles

Long ids are easy to copy wrong. Your code can swap each id for 1, 2, 3 before the prompt, then map the handles in the answer back. Flag any handle the input never had. Never guess. This is a technique you apply. It is not part of the package.

Measured once, on a file-organizing task with 30 to 100 items, 24 runs per arm: JSON with long ids gave 10 accepted answers, JSON with handles 19, Terset with handles 16. With handles and rule-based repair of small slips, JSON gave 24 and Terset 24. Handles and repair help JSON as much as Terset. The layout alone does not give better answers. One task, one model, reasoning off, development setup, 24 runs per arm. Production behavior was not measured.

## Limits

- The root must be an object or an array.
- A numeric `-0` is an explicit error.
- Nesting deeper than 500 levels is an explicit error.
- Lists nested inside a record stay JSON inside their cell.
- No answer-quality gain is claimed for the layout. Terset is about fewer tokens.
