---
title: "Labelling"
description: "Place sittings on the objective tree and set type, using the AI you already pay for."
---

Assay is one store. Labelling is not always in scope.

| Mode | What Assay is | Labelling |
|---|---|---|
| **Corpus** (`mode: "corpus"`) | Track a person or a business using AI to build | **Core.** `assay labelling on`. |
| **Observability** (`mode: "observability"`) | Meter + transcripts on an agent loop | **Optional.** `assay sweep on` without labelling. |

`assay init` writes `mode: "corpus"` and both switches **off**.

## The two switches

| Switch | Command | What it does |
|---|---|---|
| Capture | `assay sweep on` / `off` | File-log ingest on a 30-minute timer |
| Labelling | `assay labelling on` / `off` | Place unplaced sittings using **their** AI |

`labelling on` does not require `sweep on` (an embedded or OTLP host already fills the store). Both off unloads the timer.

`on` is the consent moment. It records the harness binary it found. It copies enough environment into the timer so the job sees the same AI you do. For Claude Code it also merges a Stop hook: `assay labelling session-end`.

`off` reverses config, copied skill files, the timer, and that marked hook. Overlays already written stay.

## Do not buy a second model

The runner uses the AI they already pay for:

1. A coding-harness CLI on `PATH` with print mode, or
2. `labelling.api` in the config (url, model, and the **name** of an env var for the key — never the secret itself).

No driver → `on` refuses and writes nothing.

Assay owns: the tree, the type vocabulary, the write door, rank (`declared > judge > heuristic`), retrieve-by-write, the timer.

The model returns JSON. Assay parses and writes through the same door as `assay declare`.

## Reliability path

**Clean exit:** `assay labelling session-end [--session <id>]`.

1. Retrieve-only objective placement. No judge.
2. If that sitting is still unplaced, run the labeler for **that sitting only**.
3. No session id → retrieve only. Do not guess the newest sitting.
4. Always exits 0 so the host sitting never fails.

**Backup:** the sweep timer (see [Capture](/capture)).

The portable skill is still copied into detected harness dirs so a same-sitting declare can happen as a byproduct. It is not what makes `on` true. Session-end does not capture. Sweep still does.

## What you do by hand

```bash
assay unplaced                 # sittings still missing an objective
assay labelling run            # batch of unplaced sittings
assay declare --type build:feature --session <id>
assay declare --objective <node> --session <id>
```

On the session page in the console, click the type value and type. Enter saves. Escape abandons. That write is `source=user` and outranks later guesses.

A sitting that **changed job** needs episodes, not one label. A sitting that **also touched** another product needs a touch, not a split. See [Framing](/framing).

Proposed nodes from a judge wait for accept. Do not treat a proposal as an accepted root.

## Observability

Labelling is optional. `assay sweep on` captures without it. A local `--judge` stays behind the sovereignty gate. Do not treat that pass as corpus quality.
