the store keeps a routing trace for fourteen days (V-629)

Migration #23 adds routing_traces, and internal/store/routingtraces.go writes,
lists and prunes it. Nothing calls it yet; the daemon side is the next commit.

The utterance is stored in clear. A 384-dimension vector of a short sentence is
substantially recoverable, so storing vectors instead would be a privacy claim
we cannot support. Retention is 14 days, enforced on write, and an age rather
than a row count so a busy Tuesday cannot push last Friday out. Store.Wipe
already deletes it with everything else, so explicit deletion needs no new
surface.

A correction is not covered by that bound. When the owner corrects a turn the
pair is promoted out into a seed-shaped row and kept, because a label is not a
transcript. What stays here is the transcript, and the transcript expires.

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:13:04 +04:00
parent 799cf5587d
commit 034d4b4359
3 changed files with 223 additions and 0 deletions
+30
View File
@@ -300,6 +300,36 @@ ALTER TABLE reminders ADD COLUMN next_fire_ts INTEGER;`, // #2
// here and no caller has to tell them apart.
`ALTER TABLE tasks ADD COLUMN done_when TEXT NOT NULL DEFAULT '';
ALTER TABLE tasks ADD COLUMN blocked_on TEXT NOT NULL DEFAULT '';`,
// #23 — the routing trace (V-629). internal/decision kept a 25-turn ring and
// persisted nothing, on the argument that a turn record is read minutes later
// or never. The owner reversed that on 06-08-2026: mode discovery and distance
// calibration need real utterances, and there is no other source of them.
// docs/plans/21-persisting-the-routing-trace.md carries the
// reversal.
//
// utterance holds his words in clear. A 384-dimension vector of a short
// sentence is substantially recoverable, so storing vectors instead would be a
// privacy claim we cannot support. What makes it safe is the same thing that
// makes the fact store safe: it never leaves the box, retention is bounded at
// routingTraceRetention, and Wipe drops it with everything else.
//
// correction is empty until the owner corrects a turn on /chat (V-630). A
// corrected pair is promoted out of here into a seed-shaped row and kept, so
// this column is a queue, not the durable label.
`CREATE TABLE IF NOT EXISTS routing_traces (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ts INTEGER NOT NULL,
utterance TEXT NOT NULL,
source TEXT NOT NULL DEFAULT '',
winner TEXT NOT NULL DEFAULT '',
intent TEXT NOT NULL DEFAULT '',
claimed_before_head INTEGER NOT NULL DEFAULT 0,
encoder_id TEXT NOT NULL DEFAULT '',
outcome TEXT NOT NULL DEFAULT '',
correction TEXT NOT NULL DEFAULT '',
claims TEXT NOT NULL DEFAULT '[]'
);
CREATE INDEX IF NOT EXISTS idx_routing_traces_ts ON routing_traces (ts DESC);`,
}
// migrate applies every migration with a number greater than the DB's current