# Meridian tools

Meridian is the engine under every session record. It reads a folder of Obsidian-flavoured markdown into a world model, serves byte-exact section reads, and applies guarded writes that either land whole or do not land at all. It ships as one binary, `mrd`, with two faces over the same engine: the tools your agent calls (`read`, `put`, `sql`, `script`, `walk`, `resolve`, and the `pin` field on `put`), and the `mrd` command line. The MCP server chapter covers how those tools reach your agent; this chapter covers what they do and how a write is shaped.

The one idea to carry away: **a write is a plan against something you read**. Every read hands you a token naming what you saw. Every write carries that token back. If the world still matches, the write lands; if it moved, the write is refused and the file is byte-untouched. That is how several agents share one tree with no merge step and no lost paragraph.

## The shape of every write

```mermaid
flowchart LR
  R["read<br/>toc, then sections"] --> M["tokens<br/>mint · fingerprint · rev"]
  M --> P["put<br/>if_fingerprint + scope"]
  P -->|"premise held"| W["receipt<br/>wrote · born"]
  P -->|"premise moved"| X["refused<br/>nothing landed"]
  X -. "re-read, re-plan" .-> R
```

## The tools

**`read` — the map before the bytes.** A read serves either a record's section map (the toc: heading tree, a word count and a `rev` per section) or exactly the sections you name. Start with `depth: 2` on anything large; the toc prices every section before you pay for one. Selectors are a heading path root-anchored at the file's top heading (`Task: fix-auth/Goal`, never a bare `Goal`), a dewey ordinal from the toc's first column (`1.4`), a block id (`^id`), or the `hpath` array copied from a toc row. A sections answer serves at most 20,000 words and 64 selectors — over either it refuses and serves nothing, never truncates; narrow and ask again.

Three lines in a read header matter later:

- `corpus_fingerprint:` — the whole root's version.
- `mint {path}: <token>` — the version of exactly that file. This is the token you carry into a write.
- `fingerprint:fp1.…` on each section of a sections read — the proof a `pin` needs.

**`put` — every write, one guarded call.** One record, one call: a section edit (a single `op`, or a batch in `edits[]`), a frontmatter `properties` patch, and a `pin`, in any combination. `ref` plus `body` on a path that does not exist is a birth; the same call on an existing record refuses. Four ops: `replace` (an exact `find` anchor that matches once), `append`, `replace_section` (needs a fresh section `rev`), `create_section`. A `properties` patch touches only the keys you name and never the body.

The receipt's first word is the verdict: `wrote`, `born`, `unchanged`, `skipped`, `partial`. The last two are not errors — they name edits whose bytes already landed within the ten-minute replay window. Verify; do not resend.

**`pin` — attestation, a field on `put`.** A pin records in this record that it draws from a section of another record, at that section's content fingerprint. It needs proof of read: the `fp1.…` token from a sections read you made in this session, copied untrimmed. A pin also mints an anchor in the target, so the target's `file_rev` moves while its content fingerprint does not — that is not drift.

**`sql` — the corpus as a table.** One DuckDB statement over one root: `doc`, `section`, `body`, `link`, `backlink`, `tag`, `frontmatter`, `record`, `task`. Read-only in effect — a mutation is visible to your own statement and rolled back when the call ends. The full result is written to a TSV named on the answer's last line; the inline answer shows the first rows. The freshness banner's `as_of_fingerprint` is a legal `if_fingerprint` for a following write.

**`script` — read, decide, write, as one commit.** A Starlark program with three calls: `read(path[, section=…])`, `put(path, props={…})` or `put(path, section=…, append=…)`, and `me()`. Everything it writes seals as one commit — one fingerprint advance, one receipt naming every file — or nothing lands. `dry: true` rehearses without applying. This is the door for carrying content from one record into another atomically.

**`walk` and `resolve`.** `walk` follows a page's pin graph — up for what it rests on, `down: true` for who rests on it — coloured green (verified), red (drifted), grey (unverifiable). It lists no frontmatter edge: `spawned-by`, `handoff-from` are `sql` over `frontmatter`, so an empty walk is not "no successors". `resolve` answers where a ref would land — the physical path, the addressing kind (`rooted`, `ambient`, `absolute`) and the serving root — without reading it.

**`mrd` — the same engine from a shell.** `mrd read <ref>`, `mrd sql "<statement>" --root <name>`, `mrd resolve`, `mrd script`. A `!` in its help gutter marks a verb that changes something; everything else is a read. `mrd --help` is the authority on its verbs.

## The tokens and their grain

| Token | Grain | Where you get it | What a write carrying it says |
|---|---|---|---|
| section `rev` | one section | a toc row or a sections header | "this section reads as I saw it" |
| `file_rev` | one file | a read header, every put receipt | the document's version |
| mint token + `scope` | one file (or folder) | the `mint {path}:` line of a read | "I planned against this file; I do not care what happened elsewhere" |
| `corpus_fingerprint` | the whole root | a read header, a put receipt, `sql`'s `as_of_fingerprint`, a script's `entry_fingerprint` | "I planned against the world; tell me if anything moved" |

Guard one file, pay one file; guard the world, pay the world. In a busy tree the world token refuses constantly, because someone is always writing somewhere; the mint token paired with `scope` is the everyday instrument. A batch `edits[]` requires `if_fingerprint` — the plan's premise rides with the plan.

## Refusals are the feature

A refusal is checked before anything commits, so there is never a half-written file to clean up. Each one names the fact and the recovery:

| Refusal | The fact it states | What you do |
|---|---|---|
| `fingerprint_mismatch` | the premise moved — the root, or the scoped file, is not what your read showed | re-read, re-plan the write against what the read shows |
| `cas_mismatch` | the section changed since your read; the answer prints the current bytes | re-apply your edit over the printed content |
| `write_conflict` | a pin's `sec_rev` is stale | re-read that section |
| `pin_proof_required` | the pin carries no fingerprint from a read in this session | read the section, copy the `fp1.…` token into the pin |
| `pin_target_missing` | the section you named does not exist in the target | list the target's sections with a toc read |
| a door's refusal with teaching | a session rule declined the bytes — a card outside your board, a status outside the seven columns, a memo missing its `type` | read the teaching; it names the missing thing |

The one law: **never resend the same bytes**. The engine says it in the refusal itself — "never resend it under the same premise, which refuses identically". A retry after a fresh read is, by construction, a new plan.

## Day one with records

Your agent does three things with records on its first working day. All three are `put` against a read.

**Read your card.** Your card is `sessions:agents/<short-id>/<short-id>.md`; `sessions:` is the alias every host declares for its sessions root, and the short id is in the banner your agent sees at boot.

```
read ref="sessions:agents/<short-id>/<short-id>.md" depth=1
```

The header line is the file's absolute path; `props:` are its frontmatter keys — role, session, seat, status.

**Claim a card.** Read the task card first (the same call, on `…/tasks/<slug>.md`); take the `mint {…}:` token and the brace path from the answer; hand both back:

```
put ref="sessions:year=<YYYY>/month=<MM>/<session>/tasks/<slug>.md"
    properties={"owner": "[[<short-id>]]", "status": "In Progress"}
    if_fingerprint="<the mint token>"
    scope="year=<YYYY>/month=<MM>/<session>/tasks/<slug>.md"
```

`wrote … props: owner=[[<short-id>]] status=In Progress` is the receipt. If a teammate claimed it between your read and your put, the answer is `fingerprint_mismatch` and the card still says what it said — read it again and you will see their name.

**Write a memo.** A finding worth keeping past the conversation is one born file in your agent home. Supply the meaning; the door stamps the rest.

```
put ref="sessions:agents/<short-id>/memos/<slug>.md"
    body="---\ntype: memo\ntags: [memo/gotcha]\n---\n# <slug in words>\n\n<the finding, one per file>\n"
```

`born …` is the receipt. A taken slug refuses at the door — pick another.

## Try it on your own card

Two calls the reader can run at once, with nothing at risk. First read your card's Notes section (a card has four top-level headings, so the section is addressed by its hpath, not by a bare name):

```
read ref="sessions:agents/<short-id>/<short-id>.md" sections=[[{"h":"Notes"}]]
```

The answer opens with a header carrying `mint {agents/<short-id>/<short-id>.md}: <token>` and a `== Notes (rev:… words:…) fingerprint:fp1.… ==` line. Then append one line under Notes, guarded by that mint token and its brace path:

```
put ref="sessions:agents/<short-id>/<short-id>.md"
    op="append" at="Notes" body="- first guarded write: landed\n"
    if_fingerprint="<the mint token>"
    scope="agents/<short-id>/<short-id>.md"
```

The receipt reads `wrote … §Notes rev:<old>→<new> file_rev:… corpus_fingerprint:…`. Send the identical call a second time and the receipt reads `skipped` with its explanation — the bytes are already there. That pair of receipts is the whole contract in miniature.

## Names you will meet

- `sessions:` — the root alias; `resolve ref="sessions:<path>"` shows the physical directory it names.
- `agents/<short-id>/<short-id>.md` — your card; `agents/<short-id>/memos/` — your memos.
- `year=YYYY/month=MM/<DD-HH-slug>/` — a session; `INTENT.md` inside it is what makes it one; `tasks/`, `rosters/`, `results/`, `decisions/` appear when something is first born in them.
- `MERIDIAN.md` — the file that declares a folder as a root; `mrd resolve` says which one answers for a path.
- `HOOKS.md` — where the session rules (the "doors") are armed; their refusals arrive through `put` with teaching.

## Where the detail lives

The installed **ccc-meridian** skill is the authority: `SKILL.md` for the tool surface and the addressing grammar, `references/mcp-tools.md` for every parameter and the full refusal vocabulary, `references/sql.md` for the table schema, `references/cli.md` for `mrd`. The session tree's own conventions — cards, boards, memos, the seven columns — are the **ccc-base** skill. The Sessions chapter of this handbook covers what those records mean; this one covers how they are read and written.
