# Correcting a turn

Last verified: 06-08-2026 @ 0d5bd0a

V-630, under V-628. Reads with `21-persisting-the-routing-trace.md`.

## Why a gesture and not a form

The routing trace (V-629) stores every turn. Almost all of them routed correctly, so
almost all of them teach nothing. A correction is the only high-value supervised signal
the box produces. It is also the only one that costs the owner something to give.

So the design constraint is the cost, not the schema. One gesture beside the reply. No
form and no separate page.

It is step-up gated like the chat POST beside it, which costs nothing: he tapped to send
the turn he is correcting. It is gated because trace ids are sequential integers, and this
is the one table the routing heads will be fitted on.

## Two things to capture, and only one of them is required

A correction has two halves.

- This turn was wrong.
- It should have been *this*.

The second is worth much more. It names which boundary moved, and it is what a fitted
head trains against. But requiring it would price out the first, and a turn marked wrong
with no target is still a usable negative. So the target is optional. The trace carries
`wrong` when he did not say.

The target is one of the seven intents and never free text. V-632 fits prototypes from
that table. An unroutable label would enter it, and a label nothing can score is worse
than no label.

## Where the label lives

`routing_labels`, migration #24, keyed unique on the utterance. A second correction of
the same sentence replaces the first, because his later answer is the one he meant.

It is a separate table from `routing_traces` on purpose. The transcript expires after 14
days. The label does not. A label is a sentence, an intent and an encoder id. That is not
a transcript, and the reversal in doc 21 rests on the distinction.

`was` is stored beside `should_be`. The pair is what names the confusion. A label with no
`was` cannot say which boundary moved.

## Reach

`CorrectTurn(traceID, shouldBe)` takes no browser and no session. The trace id rides back
on `ipc.ChatReply` through the same context sink the query source badge uses. Nothing in
the seam assumes the web.

Only `/chat` offers the gesture today. That is a gap, named rather than closed. If the web
is the only place to correct a turn, the sample skews to whatever the owner types at. Voice
is where the hard cases are. Telegram has the obvious shape, an inline keyboard on the
reply. Voice does not. Inventing a spoken correction grammar would put a recogniser in
front of the one signal that exists to fix recognisers. Both are follow-on work.

## What is not decided

Whether the owner ever wants to see the labels he gave. Nothing reads the table outward
yet. `/trace` shows the ring, which is 25 turns and in memory, and a labels view is a
different page with a different question.
