# Copy and voice

A style prompt tells a model how you hope someone writes. A corpus shows how they actually write. `june/copy` builds a voice from that evidence: for each request it retrieves the person's most relevant writing as examples, drafts several candidates, scores them against those examples, and keeps the best. When the person edits a draft, the edit becomes evidence for the next version of the voice.

The request and the examples have different jobs. The request owns the facts, the intent and the form: what to say, to whom, and how long. The examples only show how this person says things. They aren't meant to be a source of facts, and when they disagree with the request's form, the request should win; [Writing with a voice](https://june.dev/docs/copy/voices.md#form-and-length) says how to make sure it does.

## A first voice

A corpus whose samples already say what they are (`type`, `audience` and so on) can write straight away, with no preparation and no training:

```ts
import { openai } from "june/sdk/openai";
import { copyCorpus, voice } from "june/copy";

const fonzie = voice({
  name: "fonzie",
  corpus: copyCorpus("./fonzie.jsonl"),
  model: openai("gpt-5-mini"),
});

const result = await fonzie.write({
  type: "homepage-hero",
  audience: "startup founders",
  brief: "Introduce the recruiting agent. Two lines, under 25 words.",
});

result.text; // the best of three drafts
result.exemplars; // the samples the model saw, and why each was picked
result.score; // how much it sounds like them, overall and by dimension
```

`fonzie.jsonl` has one sample per line, such as `{"text":"…","metadata":{"type":"homepage-hero","audience":"startup founders"}}`; [Corpora and authorship](https://june.dev/docs/copy/corpora.md#input-formats) has the format.

That's a working voice: one call, three drafts, the best one back, and the record of why it was chosen. It relies on a corpus whose samples are labelled. Most archives aren't, and some of what's in them isn't the person's writing. [`prepareVoice()`](https://june.dev/docs/copy/preparation.md) cleans the corpus and has a teacher model label it, and its result is what you pass as `corpus` from then on.

## Exemplars, training and evaluation are different things

It helps to keep four ideas apart, because they're easy to blur:

- **Preparation** changes nothing about the writing. It decides which samples are in, and adds synthetic prompts and labels next to them. Its output is a dataset.
- **Exemplars** are what a voice shows the writer at request time: a handful of the person's real samples, chosen by retrieval. This is how a base model writes in their voice, and it's the part of a voice you'll tune most.
- **Evaluation** tells you whether the voice is any good, against writing it didn't see or a model without the voice. The scores `voice.write()` uses to pick a draft are not an evaluation.
- **Fine-tuning** is optional, and it isn't the default way to improve a voice. It teaches a model the invariant parts of a voice from the prepared dataset, and it's worth it only when an evaluation shows a gap that better examples don't close. Exemplars still supply what fits the situation, since a support email and a homepage hero shouldn't sound identical.

`june/copy` is built on the generic [datasets](https://june.dev/docs/training/datasets.md), [evals](https://june.dev/docs/training/evals.md) and [training](https://june.dev/docs/training/fine-tuning.md) primitives; nothing in `june/train` knows about copy.

## How it fits together

```text
real writing ──copyCorpus()──▶ corpus ──prepareVoice()──▶ prepared voice
                                                                │
                      ┌─────────────────────────────────────────┘
                      ▼
request ──▶ voice.write():  retrieve exemplars ─▶ draft candidates ─▶ score ─▶ best draft
                                                                                 │
                                    a person edits it ◀──────────────────────────┘
                                          │
                     voice.edit() ──▶ preference example, or a new sample in the corpus
```

Each stage is a separate step you can run, inspect and change on its own, and none of them changes a voice behind your back:

| Stage                                              | What it does                                                                                 | API                                           | Model calls                          |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------- | ------------------------------------ |
| [Corpus](https://june.dev/docs/copy/corpora.md)                        | holds the person's writing, byte for byte, with metadata and a fingerprint                   | `copyCorpus()`                                | none                                 |
| [Preparation](https://june.dev/docs/copy/preparation.md)               | drops samples marked as not theirs and near-duplicates, reconstructs briefs, fills in labels | `prepareVoice()`                              | one teacher call per sample          |
| [Retrieval](https://june.dev/docs/copy/retrieval.md)                   | picks the samples most like a request in form, to show the writer                            | `metadataRetriever()`, `voice.retrieve()`     | none                                 |
| [Writing](https://june.dev/docs/copy/voices.md)                        | drafts candidates with the exemplars, scores them, keeps the best, maybe revises             | `voice()`, `voice.write()`, `<Write>`         | one per candidate, and a revision    |
| [Scoring](https://june.dev/docs/copy/scoring.md)                       | measures how much a text sounds like the exemplars                                           | `stylometry()`, `judge()`                     | none, or one per draft for `judge()` |
| [Evaluation](https://june.dev/docs/copy/scoring.md#evaluating-a-voice) | checks the voice against held-out writing or a baseline, before you rely on it               | `evalSuite()`, `evaluate()` from `june/train` | the writer and graders               |
| [Saving](https://june.dev/docs/copy/training.md#manifests)             | records a voice as JSON and loads it again, refusing a different corpus                      | `voice.manifest()`, `loadVoice()`             | none                                 |
| [Training](https://june.dev/docs/copy/training.md#fine-tuning-a-voice) | optional: fine-tunes a model on the prepared corpus                                          | `voice.train()`                               | a trainer                            |
| [Learning](https://june.dev/docs/copy/learning.md)                     | turns a person's edit of a draft into training or evaluation material                        | `voice.edit()`                                | none                                 |

## What June does, and what your app decides

`june/copy` holds the primitives. The policy around them, such as where writing comes from, when a voice changes, and who decides, belongs to the application. The [Copy Agent](https://github.com/johno/june/blob/main/apps/copy-agent/README.md) is a complete application built only on June's public packages, and a reference for how the two divide:

| `june/copy` and `june/train` provide                                    | The Copy Agent decides                                                                 |
| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| a corpus from an array or JSONL                                         | reading a folder of Markdown and text files, with front matter as metadata             |
| exclusion, deduplication, reverse instructions, inferred labels, splits | which writing to collect, and keeping prepared samples on disk between processes       |
| retrieval by request labels                                             | turning a conversation into a request, with a `type` picked from the voice's own types |
| candidates, scoring, one revision, `<Write>` in a workflow              | what each reply records, and the chat around it                                        |
| `voice.edit()`, and datasets and eval cases from chat feedback          | which edits are learned from and which are held out for evaluation                     |
| `evaluate()` and `compareEvaluations()`                                 | its graders, its benchmark, and what counts as better                                  |
| voice manifests, `loadVoice()`, optional `voice.train()`                | candidates, an active agent, and promotion by a person, never automatically            |

Candidates, active agents and promotion aren't June concepts. A voice in June is a value: it never changes itself or replaces another. `june/copy` doesn't learn a brand on its own; a person decides what the next voice is.

## A worked example: June.dev's own copy

June.dev's site copy is drafted in its author's voice with this package and the Copy Agent, and each step is one you'd take for any person or brand:

1. **A reviewed list of sources.** Every piece of writing the voice may learn from is listed with where it lives, a pinned revision, its type, who wrote it, and whether it's in or out and why. Nothing is scraped, and when authorship is unclear, it's out. See [building a corpus from reviewed sources](https://june.dev/docs/copy/corpora.md#building-a-corpus-from-reviewed-sources).
2. **A corpus of prose.** Each included source becomes one sample, with code, quotes and markup removed, and its origin kept in metadata. The corpus has about 130 samples, labelled with a small vocabulary of types such as `essay`, `tutorial`, `readme` and `docs`.
3. **Preparation, then a review of it.** `prepareVoice()` kept every sample (the source list had already excluded what didn't belong) and found no duplicates. Reading a sample of the reconstructed briefs and labels, and counting samples by type, is how you know what the voice will retrieve.
4. **An evaluation before use.** A fixed set of real site-copy requests ran through the voice and through the same writer model with no voice, graded on sounding like the author, following the brief, and keeping to the facts. The voice sounded clearly more like the author; the plain writer followed briefs better, and nearly every difference was about [form and length](https://june.dev/docs/copy/voices.md#form-and-length).
5. **Drafts a person reviews.** Each request states its facts, its length and its form. The voice drafts, a person approves or edits every piece, and the edits go back as [evidence](https://june.dev/docs/copy/learning.md).

Two of the most useful lessons came from this run, and both are explained as package behavior in these guides: retrieval matches labels by shared words, so [requests should use the corpus's own type vocabulary](https://june.dev/docs/copy/retrieval.md#use-the-corpuss-own-labels); and the writer follows its exemplars' form, so [say the length and form in the request](https://june.dev/docs/copy/voices.md#form-and-length). The [site copy's README](https://github.com/johno/june/blob/main/apps/www/voice/site-copy/README.md) and [the evaluation](https://github.com/johno/june/blob/main/apps/www/voice/EVALUATION.md) have the details.

## Guides

1. [Corpora and authorship](https://june.dev/docs/copy/corpora.md): input formats, metadata and provenance, authorship hygiene, voice drift
2. [Voice preparation](https://june.dev/docs/copy/preparation.md): exclusion, deduplication, reverse instructions, inferred labels, splits
3. [Writing with a voice](https://june.dev/docs/copy/voices.md): `voice()`, requests, form and length, candidates and revision, `<Write>`
4. [Retrieval](https://june.dev/docs/copy/retrieval.md): why structure matters more than topic, weights, labels
5. [Scoring and evaluation](https://june.dev/docs/copy/scoring.md): `stylometry()`, `judge()`, custom evaluators, evaluating a voice
6. [Saving and training](https://june.dev/docs/copy/training.md): manifests, `loadVoice()`, optional fine-tuning
7. [Learning from edits](https://june.dev/docs/copy/learning.md): preference examples, new samples, held-out edits

Before you collect anyone's writing, read [corpora and consent](https://june.dev/docs/reference/security.md#training-on-what-people-wrote).
