From ddd5e71928e4ba826434ed384fe6fea05eca73f3 Mon Sep 17 00:00:00 2001 From: David Snelling Date: Tue, 25 Aug 2026 10:47:51 -0700 Subject: [PATCH] fix(storage): an unknown nested storage config can never silently land on the shared default root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The factory loudly rejects every REMOVED pre-8.0 path key, but an unknown nested `config` object (e.g. `storage: { config: { baseDir } }` — a shape that was never supported) fell through SILENTLY to the zero-config default directory. Every instance constructed with such a shape wrote to ONE shared on-disk root while its caller believed each had its own — found live when two integration tests' brains shared a store across an entire single-process CI run and a health probe refused on the foreign edges it sampled. A nested `config` carrying any path-shaped key now throws the same loud migration error, naming the canonical `path` rename. The two tests are repaired to the supported shape (and now actually test isolated stores, for the first time since 8.0). --- src/storage/storageFactory.ts | 15 +++++++++++++++ .../integration/batchImportWithRelations.test.ts | 8 +------- tests/integration/readAfterWrite.test.ts | 8 +------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/storage/storageFactory.ts b/src/storage/storageFactory.ts index 64b18dc1..46c67f44 100644 --- a/src/storage/storageFactory.ts +++ b/src/storage/storageFactory.ts @@ -154,6 +154,21 @@ export function resolveFilesystemRoot( ) { throwRemovedStorageKey('fileSystemStorage.path') } + // A nested `config` object carrying a path-shaped key is the same hazard in + // a shape nobody ever supported: it used to fall through SILENTLY to the + // shared default root — every instance writing one directory while its + // caller believed each had its own. (Found live: an integration test's + // brains shared one store across a whole single-process run and a health + // probe refused on the foreign edges it sampled.) Loud, with the rename. + const nested = (config as Record).config + if (nested && typeof nested === 'object') { + const pathish = ['path', 'baseDir', 'rootDirectory', 'rootDir', 'dir', 'directory'] + const hit = pathish.find( + (k) => typeof (nested as Record)[k] === 'string' && + ((nested as Record)[k] as string).length > 0 + ) + if (hit) throwRemovedStorageKey(`config.${hit}`) + } // 3. Zero-config default. A `type: 'filesystem'` with no path lands here // intentionally ("persist, default location"). diff --git a/tests/integration/batchImportWithRelations.test.ts b/tests/integration/batchImportWithRelations.test.ts index 7fe8e511..4095b51c 100644 --- a/tests/integration/batchImportWithRelations.test.ts +++ b/tests/integration/batchImportWithRelations.test.ts @@ -15,13 +15,7 @@ describe('Batch Import with Immediate Relations (v5.7.3 Fix)', () => { // Initialize brain brain = new Brainy({ requireSubtype: false, - storage: { - type: 'filesystem', - config: { - baseDir: testDir, - enableCompression: false // Faster tests - } - }, + storage: { type: 'filesystem', path: testDir }, dimensions: 384 }) diff --git a/tests/integration/readAfterWrite.test.ts b/tests/integration/readAfterWrite.test.ts index e0ab5863..cf1dc9ec 100644 --- a/tests/integration/readAfterWrite.test.ts +++ b/tests/integration/readAfterWrite.test.ts @@ -34,13 +34,7 @@ describe('Read-After-Write Consistency (v5.7.2 Bug Fix)', () => { testDir = join(tmpdir(), `brainy-consistency-${Date.now()}-${Math.random().toString(36).substring(7)}`) brain = new Brainy({ requireSubtype: false, - storage: { - type: 'filesystem', - config: { - baseDir: testDir, - enableCompression: false // Faster tests - } - }, + storage: { type: 'filesystem', path: testDir }, dimensions: 384 })