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++; } 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 ────────────────────────────────────────────────────────── });