a corrected turn is promoted to a label that outlives the trace (V-630)

Migration #24 adds routing_labels, and CorrectTurn writes it. Nothing calls it
yet; the wire and the surface are the next commits.

Separate table, and that is the whole retention argument. A trace is a
transcript and expires in 14 days. A correction is a label the owner wrote by
hand, and it is the only supervised signal this box will ever get, so it is
promoted out at the moment he writes it and kept.

should_be may be empty. "That was wrong" with no target is a usable negative and
must not cost more to give than the full answer. UNIQUE(utterance) so a second
correction replaces the first, because his second answer is the one he meant.
The label and the trace stamp go in one transaction: a stamp with no label loses
the signal when the trace expires.

ErrNoSuchTrace is held apart from a write failure. Correcting a turn older than
the bound is the expected case, and the surface should say so rather than report
a broken database.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117tgnmbgZpHVV3XSNw8Qua
This commit is contained in:
2026-08-06 19:30:46 +04:00
parent 7688dfde66
commit e5ec4abe04
3 changed files with 250 additions and 0 deletions
+21
View File
@@ -330,6 +330,27 @@ ALTER TABLE reminders ADD COLUMN next_fire_ts INTEGER;`, // #2
claims TEXT NOT NULL DEFAULT '[]'
);
CREATE INDEX IF NOT EXISTS idx_routing_traces_ts ON routing_traces (ts DESC);`,
// #24 — the corrected pairs (V-630). Separate from routing_traces on
// purpose, and this is the whole retention argument: a trace is a transcript
// and expires in 14 days, while a correction is a label the owner wrote by
// hand and is the only supervised signal the box will ever get. Promoting it
// out at the moment he writes it means the label survives the transcript
// that carried it.
//
// should_be may be empty. "That was wrong" with no target is a usable
// negative and must not cost more to give than the full answer would.
//
// UNIQUE(utterance) so correcting the same sentence twice replaces the
// label rather than stacking two. His second answer is the one he meant.
`CREATE TABLE IF NOT EXISTS routing_labels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER NOT NULL,
utterance TEXT NOT NULL UNIQUE,
was TEXT NOT NULL DEFAULT '',
should_be TEXT NOT NULL DEFAULT '',
source TEXT NOT NULL DEFAULT '',
encoder_id TEXT NOT NULL DEFAULT ''
);`,
}
// migrate applies every migration with a number greater than the DB's current