From af308ea5c89ac50ae1875b245319fd12172a7df2 Mon Sep 17 00:00:00 2001 From: Michael Lipscombe Date: Wed, 4 Feb 2026 16:19:04 -0700 Subject: [PATCH] fix: Backfill project field on SDK session creation to prevent race condition PostToolUse hook can create the session before UserPromptSubmit's session-init sets the project, leaving it empty. Add an UPDATE after INSERT OR IGNORE to backfill the project when a later hook provides it. Closes #939 Co-Authored-By: Claude Opus 4.5 --- src/services/sqlite/SessionStore.ts | 10 +++++++++- src/services/sqlite/sessions/create.ts | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/services/sqlite/SessionStore.ts b/src/services/sqlite/SessionStore.ts index e536edec..b17a569c 100644 --- a/src/services/sqlite/SessionStore.ts +++ b/src/services/sqlite/SessionStore.ts @@ -1165,7 +1165,7 @@ export class SessionStore { const now = new Date(); const nowEpoch = now.getTime(); - // Pure INSERT OR IGNORE - no updates, no complexity + // INSERT OR IGNORE to create session, then backfill project if it was created empty // NOTE: memory_session_id starts as NULL. It is captured by SDKAgent from the first SDK // response and stored via updateMemorySessionId(). CRITICAL: memory_session_id must NEVER // equal contentSessionId - that would inject memory messages into the user's transcript! @@ -1175,6 +1175,14 @@ export class SessionStore { VALUES (?, NULL, ?, ?, ?, ?, 'active') `).run(contentSessionId, project, userPrompt, now.toISOString(), nowEpoch); + // Backfill project if session was created by another hook with empty project + if (project) { + this.db.prepare(` + UPDATE sdk_sessions SET project = ? + WHERE content_session_id = ? AND (project IS NULL OR project = '') + `).run(project, contentSessionId); + } + // Return existing or new ID const row = this.db.prepare('SELECT id FROM sdk_sessions WHERE content_session_id = ?') .get(contentSessionId) as { id: number }; diff --git a/src/services/sqlite/sessions/create.ts b/src/services/sqlite/sessions/create.ts index d2bf773e..ffc5cb13 100644 --- a/src/services/sqlite/sessions/create.ts +++ b/src/services/sqlite/sessions/create.ts @@ -30,7 +30,7 @@ export function createSDKSession( const now = new Date(); const nowEpoch = now.getTime(); - // Pure INSERT OR IGNORE - no updates, no complexity + // INSERT OR IGNORE to create session, then backfill project if it was created empty // NOTE: memory_session_id starts as NULL. It is captured by SDKAgent from the first SDK // response and stored via updateMemorySessionId(). CRITICAL: memory_session_id must NEVER // equal contentSessionId - that would inject memory messages into the user's transcript! @@ -40,6 +40,14 @@ export function createSDKSession( VALUES (?, NULL, ?, ?, ?, ?, 'active') `).run(contentSessionId, project, userPrompt, now.toISOString(), nowEpoch); + // Backfill project if session was created by another hook with empty project + if (project) { + db.prepare(` + UPDATE sdk_sessions SET project = ? + WHERE content_session_id = ? AND (project IS NULL OR project = '') + `).run(project, contentSessionId); + } + // Return existing or new ID const row = db.prepare('SELECT id FROM sdk_sessions WHERE content_session_id = ?') .get(contentSessionId) as { id: number };