---
title: "What is recorded"
description: "Spans, turns, sittings, overlays, and how they connect."
---

Assay stores a few kinds of row. Labels hang on them. They do not invent a new unit.

## The units

| Unit | What it is | Typical id |
|---|---|---|
| **Span** | One metered event | minted on capture |
| **Turn** | One sealed transcript line | minted on capture |
| **Task** | One unit of intent (often one human turn) | `task_id` |
| **Session / sitting** | One harness conversation | `session_id` — **the spine** |
| **Episode** | A range of turns inside one sitting, with one primary on the tree | an objective window |

There is **no per-turn objective label**. Join a turn to an episode by range at read time.

`principal` is **who directed**. It is a dimension. It is never the join. Joins use `session_id`, then `task_id`, then the exact span.

## The span

Written once. No update. No delete.

It carries:

- **Attribution** — principal, session, task, optional parent span, optional tool name, optional project (seating: where the harness was pointed)
- **Model** — provider, model, lane (`frontier` / `offload` / `local`)
- **Tokens by phase** — input, cache read, cache write, output, reasoning, tool I/O
- **Cost** — integer micro-USD when priced; absent when not
- **Producer ref** — the idempotency key

**Project is seating, not product.** Where the session was opened is not what it built.

## The turn

One conversation line: human, agent, reasoning, or tool.

Almost every column is readable without the key: time, session, sequence, kind, tool, counts, the link to the span.

**Exactly one column is sealed: the message text.**

A shredded principal keeps the rows. The text is gone. Surfaces say `[shredded]`, not an empty message.

## Overlays

Overlays are extra rows. They point at a span or a sitting. They never edit the original.

| Overlay | Claim |
|---|---|
| Classification | Span work-type (mechanical, tooling, routing, reasoning, generation) |
| Valuation | A value score on a span |
| Cost correction | A later, versioned cost — original stays |
| Outcome attribution | This outcome was produced by this spend |
| Product attribution | Path mentions in the sitting's own documents |
| Session annotation | **Type** — kind of work |
| Objective window | **Episode** — this stretch of turns was for this tree node |
| Objective touch | This sitting also affected that node — **no dollars** |
| Scope window | Writes landed in `repo:<slug>` |

Each overlay is idempotent on its own key. Running a pass twice does not double-count.

## Outcomes

A `task_outcome` is a result: something happened, optionally with a declared dollar value.

ROI joins outcomes to spend through the attribution overlay. Missing declared value means ROI is **undefined**, not −100%.

## How a dollar finds a sentence

1. Capture writes spans and turns for the same sitting.
2. A turn may carry `span_ref` to the span that paid for it.
3. `assay turns` and the session page print cost on that join.
4. Labels on the sitting (type, episode, product) group those dollars.

If a sitting has spend and no transcript, you still have cost. You do not have words.

If a sitting has words and no price (unknown model), you still have the conversation. Totals keep that hole visible.
