From 9ba14aa71b7129ce173175afc5eb7aa5b2d3d02e Mon Sep 17 00:00:00 2001 From: Weigang Geng <3356786+gengwg@users.noreply.github.com> Date: Mon, 7 Sep 2026 16:08:22 -0700 Subject: [PATCH] Write down the title-writer invariant where it is enforced sessions.title has exactly two writers: setTitle fills an empty title so a later, worse guess cannot clobber a good one, and renameStmt overwrites for a harness that names a session after the fact. Both mask, which makes the split the security boundary as well as the correctness rule - an adapter reaching for the column directly bypasses redaction, and one did until v0.12.5. Comment only. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NZDHk4yVYP4jMZkTUMQ1Lw --- packages/server/src/sources/emit.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/server/src/sources/emit.ts b/packages/server/src/sources/emit.ts index 7d24016..688f277 100644 --- a/packages/server/src/sources/emit.ts +++ b/packages/server/src/sources/emit.ts @@ -8,6 +8,15 @@ import { maskText } from "../redact-secrets.js"; export const MAX_TOOL_OUTPUT = 64 * 1024; const sessionExists = db.prepare(`SELECT 1 FROM sessions WHERE id = ?`); + +// Two writers for sessions.title, and only these two anywhere in the tree: +// setTitle fills an empty title, so a later, worse guess cannot clobber +// a good one (ensureSession runs on every poll of a session) +// renameStmt overwrites, for a harness that names a session after the +// fact (dsh's session/title, Claude Code's ai-title) +// Both mask, which makes that split the security boundary as well as the +// correctness rule: an adapter reaching for the column directly would bypass +// redaction, and one did until v0.12.5. Add a writer here or not at all. const setTitle = db.prepare(`UPDATE sessions SET title = ? WHERE id = ? AND (title IS NULL OR title = '')`); const setBranch = db.prepare(`UPDATE sessions SET branch = ? WHERE id = ? AND branch IS NULL`); const renameStmt = db.prepare(`UPDATE sessions SET title = ? WHERE id = ?`); @@ -56,8 +65,7 @@ export function ensureSession(s: { }); } -// For a harness that names a session after it has started (dsh). Masked like -// every other title: no adapter should write that column itself. +// The overwrite half of the pair above. export const renameSession = (sessionId: string, title: string) => renameStmt.run(maskText(title), sessionId); export const touch = (sessionId: string, at: string) => touchStmt.run(at, sessionId, at);