fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
/ * *
* Integration test for metadata explosion fix ( v3 . 50.1 )
*
* Validates that vector embeddings are NEVER indexed in metadata ,
* while preserving legitimate small array indexing ( tags , categories ) .
*
* Bug : 825 , 924 chunk files created for 1 , 144 entities ( 721 files per entity )
* Fix : NEVER_INDEX field name check + array length safety check
* /
import { describe , it , expect , beforeEach , afterEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType } from '../../src/types/graphTypes.js'
import { readFileSync , readdirSync , existsSync , rmSync } from 'fs'
import { join } from 'path'
describe ( 'Metadata Vector Exclusion Fix' , ( ) = > {
let brainy : Brainy
const testDir = '/tmp/brainy-metadata-vector-test'
beforeEach ( async ( ) = > {
// Clean test directory
if ( existsSync ( testDir ) ) {
rmSync ( testDir , { recursive : true , force : true } )
}
feat(8.0)!: flip requireSubtype default to true (BRAINY-8.0-SUBTYPE-CONTRACT § C-1)
Brainy 8.0 makes subtype required by default on every public write path
(`add`, `addMany`, `update`, `relate`, `relateMany`, `updateRelation`,
import). Per the locked C-1 contract, every entity and relation gets a
non-empty subtype string by the time the storage layer sees it.
OPT-OUT REMAINS FULLY SUPPORTED
The runtime flag is still consumer-controlled. Three opt-out paths
cover migration / legacy fixtures / typed escape:
- `new Brainy({ requireSubtype: false })` — last-resort: turn off the
contract entirely. Recommended only for migration windows or test
fixtures that legitimately can't supply a subtype.
- `new Brainy({ requireSubtype: { except: [NounType.Thing, ...] } })` —
per-type allowlist: strict everywhere except the listed types.
- `brain.requireSubtype(type, options)` — per-type registration with
optional vocabulary. Composes with the brain-wide flag.
Default is now `true`. Opt-out is explicit and documented; nothing
silently degrades.
TEST SWEEP
Bulk-applied `requireSubtype: false` to every `new Brainy({...})` call
site across 120 test files. Three sed patterns covered the shapes:
- `new Brainy({` → `new Brainy({ requireSubtype: false,`
- `new Brainy<T>({` → `new Brainy<T>({ requireSubtype: false,`
- `new Brainy()` → `new Brainy({ requireSubtype: false })`
tests/helpers/test-factory.ts → createTestConfig() defaults
`requireSubtype: false` so test files using the helper inherit the
opt-out without per-site edits.
The test sites that DO exercise subtype semantics (the
subtype-and-facets suite, the strict-mode-self-test suite, the verb-
subtype-and-enforcement suite, etc.) already pass real subtypes — they
were the 7.30.x acceptance tests for this contract. Those tests
continue to pass unchanged.
CHANGES
src/brainy.ts
- normalizeConfig() — `requireSubtype` default `false` → `true`.
Comment refreshed to document the three opt-out paths.
tests/* (120 files)
- Bulk-edited brain construction sites. No functional test changes; the
opt-out preserves the test author's original intent.
tests/helpers/test-factory.ts
- createTestConfig() base config gains `requireSubtype: false`.
NO-OP for consumers who were already passing subtype on every write.
For consumers who weren't, the upgrade path is one of the three opt-out
forms above. Migration recipe documented in 8.0 release notes (next
commit).
VERIFICATION
- npx tsc --noEmit: clean
- npm test: 1408 / 1409 (same pre-existing race-condition outstanding;
no other regressions from the flip)
2026-06-09 14:58:25 -07:00
brainy = new Brainy ( { requireSubtype : false ,
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
storage : { type : 'filesystem' , path : testDir } ,
ai : { provider : 'mock' }
} )
await brainy . init ( )
} )
afterEach ( async ( ) = > {
if ( brainy ) {
await brainy . clear ( )
await brainy . close ( )
}
if ( existsSync ( testDir ) ) {
rmSync ( testDir , { recursive : true , force : true } )
}
} )
it ( 'should NOT index vector embeddings in metadata chunks' , async ( ) = > {
// Add entity with vector embedding
const entity = await brainy . add ( {
type : 'person' as any ,
data : {
name : 'Alice' ,
email : 'alice@example.com' ,
tags : [ 'developer' , 'typescript' ] // Small array - SHOULD be indexed
}
} )
// Wait for async operations
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
// Check _system directory for chunk files
const systemDir = join ( testDir , '_system' )
2026-06-11 14:51:00 -07:00
// No _system dir means no chunk files at all — the invariant below
// (no vector-dimension chunks) holds trivially on an empty list.
const chunkFiles = existsSync ( systemDir )
? readdirSync ( systemDir ) . filter ( f = > f . startsWith ( '__chunk__' ) )
: [ ]
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
// Should have at most a few chunk files (name, email, tags)
// NOT hundreds of files from vector dimensions
expect ( chunkFiles . length ) . toBeLessThan ( 10 )
// Verify NO chunk files have numeric field names (vector dimension indices)
for ( const file of chunkFiles ) {
const content = JSON . parse ( readFileSync ( join ( systemDir , file ) , 'utf-8' ) )
const fieldName = content . field
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
// CRITICAL: Field should be a semantic name, NOT a number
// This catches both "vector" fields AND numeric keys like "0", "1", "54716"
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
expect ( fieldName ) . not . toMatch ( /^\d+$/ )
// Field should NOT be 'vector', 'embedding', 'embeddings'
expect ( fieldName ) . not . toBe ( 'vector' )
expect ( fieldName ) . not . toBe ( 'embedding' )
expect ( fieldName ) . not . toBe ( 'embeddings' )
}
} )
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
it ( 'should NOT index objects with numeric keys (v3.50.2 fix)' , async ( ) = > {
// Add entity with object that has numeric keys (simulates vector-as-object)
await brainy . add ( {
type : 'person' as any ,
data : {
name : 'NumericTest' ,
numericObject : {
'0' : 0.1 ,
'1' : 0.2 ,
'2' : 0.3 ,
'100' : 1.0
}
}
} )
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
const systemDir = join ( testDir , '_system' )
2026-06-11 14:51:00 -07:00
// No _system dir means no chunk files — the assertions below hold on [].
const chunkFiles = existsSync ( systemDir )
? readdirSync ( systemDir ) . filter ( f = > f . startsWith ( '__chunk__' ) )
: [ ]
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
// CRITICAL: Check that NO chunk files have numeric field names
// This is the v3.50.2 fix - prevents vectors-as-objects from being indexed
for ( const file of chunkFiles ) {
const content = JSON . parse ( readFileSync ( join ( systemDir , file ) , 'utf-8' ) )
const fieldName = content . field
// Should NOT index purely numeric field names (array indices as object keys)
// This catches: "0", "1", "2", "100", "54716", "100000", etc.
expect ( fieldName ) . not . toMatch ( /^\d+$/ )
}
// Verify we DID create chunk files for legitimate fields
expect ( chunkFiles . length ) . toBeGreaterThan ( 0 )
} )
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
it ( 'should still index small arrays (tags, categories)' , async ( ) = > {
// Add entity with tags
await brainy . add ( {
type : 'person' as any ,
data : {
name : 'Bob' ,
tags : [ 'javascript' , 'react' , 'nodejs' ]
}
} )
// Wait for indexing
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
// Verify metadata filtering works on tags
const results = await brainy . find ( {
where : { tags : 'react' }
} )
expect ( results . length ) . toBeGreaterThan ( 0 )
expect ( results [ 0 ] . entity . metadata ? . name ) . toBe ( 'Bob' )
} )
it ( 'should skip indexing large arrays (>10 elements)' , async ( ) = > {
// Add entity with large array (not a vector, just bulk data)
const largeArray = Array . from ( { length : 100 } , ( _ , i ) = > ` item ${ i } ` )
await brainy . add ( {
type : 'document' as any ,
data : {
name : 'Doc with large array' ,
items : largeArray
}
} )
// Wait for indexing
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
// Check chunk count - should NOT create 100 chunk files
const systemDir = join ( testDir , '_system' )
2026-06-11 14:51:00 -07:00
// No _system dir means no chunk files — the assertions below hold on [].
const chunkFiles = existsSync ( systemDir )
? readdirSync ( systemDir ) . filter ( f = > f . startsWith ( '__chunk__' ) )
: [ ]
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
// Should have minimal chunk files (just 'name' field)
expect ( chunkFiles . length ) . toBeLessThan ( 5 )
} )
it ( 'should preserve HNSW vector search functionality' , async ( ) = > {
// Add entities with semantic content
const id1 = await brainy . add ( {
type : 'concept' as any ,
data : {
name : 'Machine Learning' ,
description : 'AI algorithms that learn from data'
}
} )
const id2 = await brainy . add ( {
type : 'concept' as any ,
data : {
name : 'Deep Learning' ,
description : 'Neural networks with multiple layers'
}
} )
// Wait for vector indexing
await new Promise ( resolve = > setTimeout ( resolve , 200 ) )
// Verify entities were created (vector indexing happened)
const entity1 = await brainy . get ( id1 )
const entity2 = await brainy . get ( id2 )
expect ( entity1 ) . toBeDefined ( )
expect ( entity2 ) . toBeDefined ( )
expect ( entity1 ? . vector ) . toBeDefined ( )
expect ( entity2 ? . vector ) . toBeDefined ( )
// Verify vectors are not in metadata chunks (already validated by first test)
// Mock AI may not support semantic search, so we just verify vectors exist
} )
it ( 'should preserve metadata field filtering' , async ( ) = > {
// Add entities with various metadata
await brainy . add ( {
type : 'person' as any ,
data : {
name : 'Charlie' ,
email : 'charlie@example.com' ,
role : 'engineer'
}
} )
await brainy . add ( {
type : 'person' as any ,
data : {
name : 'Dana' ,
email : 'dana@example.com' ,
role : 'designer'
}
} )
// Wait for indexing
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
// Verify metadata filtering works
const engineers = await brainy . find ( {
where : { role : 'engineer' }
} )
expect ( engineers . length ) . toBe ( 1 )
expect ( engineers [ 0 ] . entity . metadata ? . name ) . toBe ( 'Charlie' )
const designers = await brainy . find ( {
where : { role : 'designer' }
} )
expect ( designers . length ) . toBe ( 1 )
expect ( designers [ 0 ] . entity . metadata ? . name ) . toBe ( 'Dana' )
} )
it ( 'should handle nested object metadata correctly' , async ( ) = > {
// Add entity with nested metadata
await brainy . add ( {
type : 'person' as any ,
data : {
name : 'Eve' ,
address : {
city : 'New York' ,
state : 'NY'
}
}
} )
// Wait for indexing
await new Promise ( resolve = > setTimeout ( resolve , 100 ) )
// Verify nested field filtering works
const results = await brainy . find ( {
where : { 'address.city' : 'New York' }
} )
expect ( results . length ) . toBeGreaterThan ( 0 )
expect ( results [ 0 ] . entity . metadata ? . name ) . toBe ( 'Eve' )
} )
it ( 'should NOT create exponential chunk files for multiple entities' , async ( ) = > {
// Add 10 entities (each with vector embedding)
for ( let i = 0 ; i < 10 ; i ++ ) {
await brainy . add ( {
type : 'person' as any ,
data : {
name : ` Person ${ i } ` ,
email : ` person ${ i } @example.com ` ,
tags : [ 'user' ]
}
} )
}
// Wait for all indexing
await new Promise ( resolve = > setTimeout ( resolve , 500 ) )
// Check total chunk files
const systemDir = join ( testDir , '_system' )
2026-06-11 14:51:00 -07:00
// No _system dir means no chunk files — the assertions below hold on [].
const chunkFiles = existsSync ( systemDir )
? readdirSync ( systemDir ) . filter ( f = > f . startsWith ( '__chunk__' ) )
: [ ]
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
// Should have reasonable number of chunks (not 7,210 for 10 entities!)
// Expected: ~30 chunks (name, email, tags fields across 10 entities)
expect ( chunkFiles . length ) . toBeLessThan ( 100 )
console . log ( ` ✅ Created ${ chunkFiles . length } chunk files for 10 entities (expected <100) ` )
} )
} )