From 96490d269d5009643b887aced0a02cdb4b14d3e4 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 10 Apr 2026 08:16:29 -0500 Subject: [PATCH 1/2] fix(state): prevent false degraded-mode warning when DB not yet initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deriveState() is called during before_agent_start context injection, before any tool invocation has had a chance to open the DB. Previously, isDbAvailable() returning false in this path triggered a misleading "DB unavailable — using filesystem state derivation (degraded mode)" warning, even though the DB was simply not yet initialized (not failed). Add a _dbOpenAttempted flag in gsd-db.ts that tracks whether openDatabase() has been called at least once. The degraded-mode warning now only fires when the DB was actually attempted and failed to open, not when it hasn't been initialized yet. Supersedes #3922. --- src/resources/extensions/gsd/gsd-db.ts | 12 ++++++++++++ src/resources/extensions/gsd/state.ts | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/gsd/gsd-db.ts b/src/resources/extensions/gsd/gsd-db.ts index b25b820f9..e440bdb44 100644 --- a/src/resources/extensions/gsd/gsd-db.ts +++ b/src/resources/extensions/gsd/gsd-db.ts @@ -778,6 +778,7 @@ let currentDb: DbAdapter | null = null; let currentPath: string | null = null; let currentPid: number = 0; let _exitHandlerRegistered = false; +let _dbOpenAttempted = false; export function getDbProvider(): ProviderName | null { loadProvider(); @@ -788,7 +789,18 @@ export function isDbAvailable(): boolean { return currentDb !== null; } +/** + * Returns true if openDatabase() has been called at least once this session. + * Used to distinguish "DB not yet initialized" from "DB genuinely unavailable" + * so that early callers (e.g. before_agent_start context injection) don't + * trigger a false degraded-mode warning. + */ +export function wasDbOpenAttempted(): boolean { + return _dbOpenAttempted; +} + export function openDatabase(path: string): boolean { + _dbOpenAttempted = true; if (currentDb && currentPath !== path) closeDatabase(); if (currentDb && currentPath === path) return true; diff --git a/src/resources/extensions/gsd/state.ts b/src/resources/extensions/gsd/state.ts index f6170522b..9dddc53e6 100644 --- a/src/resources/extensions/gsd/state.ts +++ b/src/resources/extensions/gsd/state.ts @@ -47,6 +47,7 @@ import { extractVerdict } from './verdict-parser.js'; import { isDbAvailable, + wasDbOpenAttempted, getAllMilestones, getMilestone, getMilestoneSlices, @@ -271,7 +272,12 @@ export async function deriveState(basePath: string): Promise { _telemetry.markdownDeriveCount++; } } else { - logWarning("state", "DB unavailable — using filesystem state derivation (degraded mode)"); + // Only warn when DB initialization was attempted and failed — not when + // the DB simply hasn't been opened yet (e.g. during before_agent_start + // context injection which runs before any tool invocation opens the DB). + if (wasDbOpenAttempted()) { + logWarning("state", "DB unavailable — using filesystem state derivation (degraded mode)"); + } result = await _deriveStateImpl(basePath); _telemetry.markdownDeriveCount++; } From be2032794bd1eca8770d47cd99fd6d3e6a767f36 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 10 Apr 2026 08:19:58 -0500 Subject: [PATCH 2/2] test: add regression test for wasDbOpenAttempted flag Verifies the flag returns true after openDatabase() is called and persists after closeDatabase(), ensuring the "not yet initialized" vs "genuinely unavailable" distinction works correctly. --- src/resources/extensions/gsd/tests/gsd-db.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/resources/extensions/gsd/tests/gsd-db.test.ts b/src/resources/extensions/gsd/tests/gsd-db.test.ts index 5fb66a81b..0987b9ad3 100644 --- a/src/resources/extensions/gsd/tests/gsd-db.test.ts +++ b/src/resources/extensions/gsd/tests/gsd-db.test.ts @@ -7,6 +7,7 @@ import { openDatabase, closeDatabase, isDbAvailable, + wasDbOpenAttempted, getDbProvider, insertDecision, getDecisionById, @@ -346,6 +347,17 @@ describe('gsd-db', () => { assert.deepStrictEqual(ar, [], 'getActiveRequirements returns [] when DB closed'); }); + test('gsd-db: wasDbOpenAttempted tracks openDatabase calls', () => { + // wasDbOpenAttempted should return true once openDatabase has been called + // (previous tests in this suite already called openDatabase, so the flag is set) + assert.ok(wasDbOpenAttempted(), 'wasDbOpenAttempted should be true after openDatabase was called'); + + // Verify the flag persists even after closeDatabase + closeDatabase(); + assert.ok(!isDbAvailable(), 'DB should not be available after close'); + assert.ok(wasDbOpenAttempted(), 'wasDbOpenAttempted should remain true after closeDatabase'); + }); + // ─── Final Report ────────────────────────────────────────────────────────── });