@soulcraft/brainy 6.2.9 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/brainy.d.ts +55 -0
  3. package/dist/brainy.js +104 -7
  4. package/dist/graph/graphAdjacencyIndex.d.ts +7 -0
  5. package/dist/graph/graphAdjacencyIndex.js +42 -0
  6. package/dist/storage/baseStorage.d.ts +7 -0
  7. package/dist/storage/baseStorage.js +31 -38
  8. package/dist/versioning/VersionIndex.d.ts +42 -47
  9. package/dist/versioning/VersionIndex.js +141 -166
  10. package/dist/versioning/VersionManager.d.ts +12 -6
  11. package/dist/versioning/VersionManager.js +26 -8
  12. package/dist/versioning/VersionStorage.d.ts +25 -15
  13. package/dist/versioning/VersionStorage.js +49 -65
  14. package/dist/vfs/PathResolver.d.ts +6 -0
  15. package/dist/vfs/PathResolver.js +20 -0
  16. package/dist/vfs/semantic/SemanticPathResolver.d.ts +6 -0
  17. package/dist/vfs/semantic/SemanticPathResolver.js +11 -0
  18. package/package.json +1 -1
  19. package/dist/augmentations/KnowledgeAugmentation.d.ts +0 -40
  20. package/dist/augmentations/KnowledgeAugmentation.js +0 -251
  21. package/dist/importManager.d.ts +0 -78
  22. package/dist/importManager.js +0 -267
  23. package/dist/query/typeInference.d.ts +0 -158
  24. package/dist/query/typeInference.js +0 -760
  25. package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +0 -252
  26. package/dist/storage/adapters/typeAwareStorageAdapter.js +0 -814
  27. package/dist/types/brainyDataInterface.d.ts +0 -52
  28. package/dist/types/brainyDataInterface.js +0 -10
  29. package/dist/vfs/ConceptSystem.d.ts +0 -203
  30. package/dist/vfs/ConceptSystem.js +0 -545
  31. package/dist/vfs/EntityManager.d.ts +0 -75
  32. package/dist/vfs/EntityManager.js +0 -216
  33. package/dist/vfs/EventRecorder.d.ts +0 -84
  34. package/dist/vfs/EventRecorder.js +0 -269
  35. package/dist/vfs/GitBridge.d.ts +0 -167
  36. package/dist/vfs/GitBridge.js +0 -537
  37. package/dist/vfs/KnowledgeLayer.d.ts +0 -35
  38. package/dist/vfs/KnowledgeLayer.js +0 -443
  39. package/dist/vfs/PersistentEntitySystem.d.ts +0 -165
  40. package/dist/vfs/PersistentEntitySystem.js +0 -503
  41. package/dist/vfs/SemanticVersioning.d.ts +0 -105
  42. package/dist/vfs/SemanticVersioning.js +0 -309
package/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [6.3.1](https://github.com/soulcraftlabs/brainy/compare/v6.3.0...v6.3.1) (2025-12-09)
6
+
7
+ - fix(versioning): clean architecture with index pollution prevention (f145fa1)
8
+ - chore(release): 6.3.0 - singleton GraphAdjacencyIndex architecture fix (292be1b)
9
+ - fix(architecture): singleton GraphAdjacencyIndex via storage.getGraphIndex() (v6.3.0) (c15892e)
10
+ - chore(release): 6.2.9 - fix critical VFS bugs (directory corruption) (810b756)
11
+ - fix(vfs): resolve two critical VFS bugs causing directory listing corruption (2ba69ec)
12
+ - chore(release): 6.2.8 - deferred HNSW persistence for 30-50× faster cloud adds (1da6048)
13
+ - perf(hnsw): deferred persistence mode for 30-50× faster cloud storage adds (4d1d567)
14
+ - chore(release): 6.2.7 - simplify cloud storage to always-on write buffering (a33b759)
15
+ - perf(storage): simplify cloud adapters to always-on write buffering (26510ce)
16
+ - chore(release): 6.2.6 - fix cloud storage read-after-write consistency (6449bb1)
17
+ - fix(storage): populate cache before write buffer for read-after-write consistency (2d27bd0)
18
+ - chore(release): 6.2.5 - fix counts.byType() accumulation bug (e4bbd7f)
19
+ - fix(counts): counts.byType() returns inflated values due to accumulation bug (9456c2c)
20
+ - chore(release): 6.2.4 - fix asOf() COW property name mismatch (ea53c11)
21
+ - fix(cow): asOf() fails with "COW not enabled" due to property name mismatch (b3ae18b)
22
+ - chore(release): 6.2.3 - fix counts.byType({ excludeVFS: true }) returning empty (0ba6da4)
23
+ - fix(counts): counts.byType({ excludeVFS: true }) now returns correct type counts (9b2ff2d)
24
+
25
+
5
26
  ### [6.2.2](https://github.com/soulcraftlabs/brainy/compare/v6.2.1...v6.2.2) (2025-11-25)
6
27
 
7
28
  - refactor: remove 3,700+ LOC of unused HNSW implementations (e3146ce)
package/dist/brainy.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * Beautiful, Professional, Planet-Scale, Fun to Use
5
5
  * NO STUBS, NO MOCKS, REAL IMPLEMENTATION
6
6
  */
7
+ import { BaseStorage } from './storage/baseStorage.js';
7
8
  import { Vector } from './coreTypes.js';
8
9
  import { ImprovedNeuralAPI } from './neural/improvedNeuralAPI.js';
9
10
  import { NaturalLanguageProcessor } from './neural/naturalLanguageProcessor.js';
@@ -1629,6 +1630,60 @@ export declare class Brainy<T = any> implements BrainyInterface<T> {
1629
1630
  };
1630
1631
  density: number;
1631
1632
  }>;
1633
+ /**
1634
+ * Search entities by metadata filters (internal API)
1635
+ * Used by versioning system for querying version metadata
1636
+ *
1637
+ * @param filters - Metadata filter object (same format as `where` in find())
1638
+ * @returns Array of matching entities
1639
+ * @internal
1640
+ */
1641
+ searchByMetadata(filters: Record<string, any>): Promise<any[]>;
1642
+ /**
1643
+ * Get raw noun metadata (internal API)
1644
+ * Used by versioning system for reading entity state
1645
+ *
1646
+ * @param id - Entity ID
1647
+ * @returns Noun metadata or null if not found
1648
+ * @internal
1649
+ */
1650
+ getNounMetadata(id: string): Promise<any | null>;
1651
+ /**
1652
+ * Save raw noun metadata (internal API)
1653
+ * Used by versioning system for storing version index entries
1654
+ *
1655
+ * @param id - Entity ID
1656
+ * @param data - Metadata to save
1657
+ * @internal
1658
+ */
1659
+ saveNounMetadata(id: string, data: any): Promise<void>;
1660
+ /**
1661
+ * Delete noun metadata (internal API)
1662
+ * Used by versioning system for removing version index entries
1663
+ *
1664
+ * @param id - Entity ID
1665
+ * @internal
1666
+ */
1667
+ deleteNounMetadata(id: string): Promise<void>;
1668
+ /**
1669
+ * Current branch name (internal API)
1670
+ * Used by versioning system for branch-aware operations
1671
+ * @internal
1672
+ */
1673
+ get currentBranch(): string;
1674
+ /**
1675
+ * Reference manager for COW commits (internal API)
1676
+ * Used by versioning system for commit operations
1677
+ * @internal
1678
+ */
1679
+ get refManager(): any;
1680
+ /**
1681
+ * Storage adapter (internal API)
1682
+ * Used by versioning system for direct storage access
1683
+ * Returns BaseStorage which has saveMetadata/getMetadata for key-value storage
1684
+ * @internal
1685
+ */
1686
+ get storageAdapter(): BaseStorage;
1632
1687
  /**
1633
1688
  * Parse natural language query using advanced NLP with 220+ patterns
1634
1689
  * The embedding model is always available as it's core to Brainy's functionality
package/dist/brainy.js CHANGED
@@ -18,7 +18,6 @@ import { TripleIntelligenceSystem } from './triple/TripleIntelligenceSystem.js';
18
18
  import { VirtualFileSystem } from './vfs/VirtualFileSystem.js';
19
19
  import { VersioningAPI } from './versioning/VersioningAPI.js';
20
20
  import { MetadataIndexManager } from './utils/metadataIndex.js';
21
- import { GraphAdjacencyIndex } from './graph/graphAdjacencyIndex.js';
22
21
  import { CommitBuilder } from './storage/cow/CommitObject.js';
23
22
  import { NULL_HASH } from './storage/cow/constants.js';
24
23
  import { createPipeline } from './streaming/pipeline.js';
@@ -127,8 +126,11 @@ export class Brainy {
127
126
  // Initialize core metadata index
128
127
  this.metadataIndex = new MetadataIndexManager(this.storage);
129
128
  await this.metadataIndex.init();
130
- // Initialize core graph index
131
- this.graphIndex = new GraphAdjacencyIndex(this.storage);
129
+ // v6.3.0: Get GraphAdjacencyIndex from storage (SINGLETON pattern)
130
+ // Storage owns the single instance, Brainy accesses it via getGraphIndex()
131
+ // This fixes the dual-ownership bug where Brainy and Storage had separate instances
132
+ // causing verbIdSet to be out of sync and VFS tree queries to fail
133
+ this.graphIndex = await this.storage.getGraphIndex();
132
134
  // Rebuild indexes if needed for existing data
133
135
  await this.rebuildIndexesIfNeeded();
134
136
  // Initialize augmentations
@@ -2198,8 +2200,10 @@ export class Brainy {
2198
2200
  // Fast rebuild for small indexes from COW storage (Metadata/Graph are fast)
2199
2201
  clone.metadataIndex = new MetadataIndexManager(clone.storage);
2200
2202
  await clone.metadataIndex.init();
2201
- clone.graphIndex = new GraphAdjacencyIndex(clone.storage);
2202
- await clone.graphIndex.rebuild();
2203
+ clone.storage.graphIndex = undefined;
2204
+ clone.storage.graphIndexPromise = undefined;
2205
+ clone.graphIndex = await clone.storage.getGraphIndex();
2206
+ // getGraphIndex() will rebuild automatically if data exists (via _initializeGraphIndex)
2203
2207
  // Setup augmentations
2204
2208
  clone.augmentationRegistry = this.setupAugmentations();
2205
2209
  await clone.augmentationRegistry.initializeAll({
@@ -2289,14 +2293,21 @@ export class Brainy {
2289
2293
  this.index = this.setupIndex();
2290
2294
  this.metadataIndex = new MetadataIndexManager(this.storage);
2291
2295
  await this.metadataIndex.init();
2292
- this.graphIndex = new GraphAdjacencyIndex(this.storage);
2296
+ this.storage.invalidateGraphIndex();
2297
+ this.graphIndex = await this.storage.getGraphIndex();
2293
2298
  // v5.7.7: Reset lazy loading state when switching branches
2294
2299
  // Indexes contain data from previous branch, must rebuild for new branch
2295
2300
  this.lazyRebuildCompleted = false;
2296
2301
  // Rebuild indexes from new branch data (force=true to override disableAutoRebuild)
2297
2302
  await this.rebuildIndexesIfNeeded(true);
2298
- // Re-initialize VFS for new branch
2303
+ // v6.3.0: Clear VFS caches before recreating VFS for new branch
2304
+ // UnifiedCache is global, so old branch's VFS path cache entries would persist
2299
2305
  if (this._vfs) {
2306
+ // Clear old PathResolver's caches including UnifiedCache entries
2307
+ if (this._vfs.pathResolver?.invalidateAllCaches) {
2308
+ this._vfs.pathResolver.invalidateAllCaches();
2309
+ }
2310
+ // Recreate VFS for new branch
2300
2311
  this._vfs = new VirtualFileSystem(this);
2301
2312
  await this._vfs.init();
2302
2313
  }
@@ -3543,6 +3554,92 @@ export class Brainy {
3543
3554
  async getStats(options) {
3544
3555
  return this.counts.getStats(options);
3545
3556
  }
3557
+ // ============= INTERNAL VERSIONING API =============
3558
+ // These methods are used by the versioning system (brain.versions.*)
3559
+ // They expose internal storage and index operations needed for entity versioning
3560
+ /**
3561
+ * Search entities by metadata filters (internal API)
3562
+ * Used by versioning system for querying version metadata
3563
+ *
3564
+ * @param filters - Metadata filter object (same format as `where` in find())
3565
+ * @returns Array of matching entities
3566
+ * @internal
3567
+ */
3568
+ async searchByMetadata(filters) {
3569
+ await this.ensureInitialized();
3570
+ const ids = await this.metadataIndex.getIdsForFilter(filters);
3571
+ if (ids.length === 0)
3572
+ return [];
3573
+ const results = [];
3574
+ const entitiesMap = await this.batchGet(ids);
3575
+ for (const id of ids) {
3576
+ const entity = entitiesMap.get(id);
3577
+ if (entity) {
3578
+ results.push(entity);
3579
+ }
3580
+ }
3581
+ return results;
3582
+ }
3583
+ /**
3584
+ * Get raw noun metadata (internal API)
3585
+ * Used by versioning system for reading entity state
3586
+ *
3587
+ * @param id - Entity ID
3588
+ * @returns Noun metadata or null if not found
3589
+ * @internal
3590
+ */
3591
+ async getNounMetadata(id) {
3592
+ await this.ensureInitialized();
3593
+ return this.storage.getNounMetadata(id);
3594
+ }
3595
+ /**
3596
+ * Save raw noun metadata (internal API)
3597
+ * Used by versioning system for storing version index entries
3598
+ *
3599
+ * @param id - Entity ID
3600
+ * @param data - Metadata to save
3601
+ * @internal
3602
+ */
3603
+ async saveNounMetadata(id, data) {
3604
+ await this.ensureInitialized();
3605
+ await this.storage.saveNounMetadata(id, data);
3606
+ }
3607
+ /**
3608
+ * Delete noun metadata (internal API)
3609
+ * Used by versioning system for removing version index entries
3610
+ *
3611
+ * @param id - Entity ID
3612
+ * @internal
3613
+ */
3614
+ async deleteNounMetadata(id) {
3615
+ await this.ensureInitialized();
3616
+ await this.storage.deleteNounMetadata(id);
3617
+ }
3618
+ /**
3619
+ * Current branch name (internal API)
3620
+ * Used by versioning system for branch-aware operations
3621
+ * @internal
3622
+ */
3623
+ get currentBranch() {
3624
+ return this.storage.currentBranch || 'main';
3625
+ }
3626
+ /**
3627
+ * Reference manager for COW commits (internal API)
3628
+ * Used by versioning system for commit operations
3629
+ * @internal
3630
+ */
3631
+ get refManager() {
3632
+ return this.storage.refManager;
3633
+ }
3634
+ /**
3635
+ * Storage adapter (internal API)
3636
+ * Used by versioning system for direct storage access
3637
+ * Returns BaseStorage which has saveMetadata/getMetadata for key-value storage
3638
+ * @internal
3639
+ */
3640
+ get storageAdapter() {
3641
+ return this.storage;
3642
+ }
3546
3643
  // ============= HELPER METHODS =============
3547
3644
  /**
3548
3645
  * Parse natural language query using advanced NLP with 220+ patterns
@@ -51,8 +51,15 @@ export declare class GraphAdjacencyIndex {
51
51
  constructor(storage: StorageAdapter, config?: GraphIndexConfig);
52
52
  /**
53
53
  * Initialize the graph index (lazy initialization)
54
+ * v6.3.0: Added defensive auto-rebuild check for verbIdSet consistency
54
55
  */
55
56
  private ensureInitialized;
57
+ /**
58
+ * Populate verbIdSet from storage without full rebuild (v6.3.0)
59
+ * Lighter weight than full rebuild - only loads verb IDs, not all verb data
60
+ * @private
61
+ */
62
+ private populateVerbIdSetFromStorage;
56
63
  /**
57
64
  * Core API - Neighbor lookup with LSM-tree storage
58
65
  *
@@ -72,6 +72,7 @@ export class GraphAdjacencyIndex {
72
72
  }
73
73
  /**
74
74
  * Initialize the graph index (lazy initialization)
75
+ * v6.3.0: Added defensive auto-rebuild check for verbIdSet consistency
75
76
  */
76
77
  async ensureInitialized() {
77
78
  if (this.initialized) {
@@ -81,10 +82,51 @@ export class GraphAdjacencyIndex {
81
82
  await this.lsmTreeTarget.init();
82
83
  await this.lsmTreeVerbsBySource.init();
83
84
  await this.lsmTreeVerbsByTarget.init();
85
+ // v6.3.0: Defensive check - if LSM-trees have data but verbIdSet is empty,
86
+ // the index was created without proper rebuild (shouldn't happen with singleton
87
+ // pattern but protects against edge cases and future refactoring)
88
+ const lsmTreeSize = this.lsmTreeVerbsBySource.size();
89
+ if (lsmTreeSize > 0 && this.verbIdSet.size === 0) {
90
+ prodLog.warn(`GraphAdjacencyIndex: LSM-trees have ${lsmTreeSize} relationships but verbIdSet is empty. ` +
91
+ `Triggering auto-rebuild to restore consistency.`);
92
+ // Note: We don't await rebuild() here to avoid infinite loop
93
+ // (rebuild calls ensureInitialized). Instead, we'll populate verbIdSet
94
+ // by loading all verb IDs from storage.
95
+ await this.populateVerbIdSetFromStorage();
96
+ }
84
97
  // Start auto-flush timer after initialization
85
98
  this.startAutoFlush();
86
99
  this.initialized = true;
87
100
  }
101
+ /**
102
+ * Populate verbIdSet from storage without full rebuild (v6.3.0)
103
+ * Lighter weight than full rebuild - only loads verb IDs, not all verb data
104
+ * @private
105
+ */
106
+ async populateVerbIdSetFromStorage() {
107
+ prodLog.info('GraphAdjacencyIndex: Populating verbIdSet from storage...');
108
+ const startTime = Date.now();
109
+ // Use pagination to load all verb IDs
110
+ let hasMore = true;
111
+ let cursor = undefined;
112
+ let count = 0;
113
+ while (hasMore) {
114
+ const result = await this.storage.getVerbs({
115
+ pagination: { limit: 10000, cursor }
116
+ });
117
+ for (const verb of result.items) {
118
+ this.verbIdSet.add(verb.id);
119
+ // Also update counts
120
+ const verbType = verb.verb || 'unknown';
121
+ this.relationshipCountsByType.set(verbType, (this.relationshipCountsByType.get(verbType) || 0) + 1);
122
+ count++;
123
+ }
124
+ hasMore = result.hasMore;
125
+ cursor = result.nextCursor;
126
+ }
127
+ const elapsed = Date.now() - startTime;
128
+ prodLog.info(`GraphAdjacencyIndex: Populated verbIdSet with ${count} verb IDs in ${elapsed}ms`);
129
+ }
88
130
  /**
89
131
  * Core API - Neighbor lookup with LSM-tree storage
90
132
  *
@@ -82,6 +82,13 @@ export declare abstract class BaseStorage extends BaseStorageAdapter {
82
82
  * @public
83
83
  */
84
84
  rebuildGraphIndex(): Promise<void>;
85
+ /**
86
+ * Invalidate GraphAdjacencyIndex (v6.3.0)
87
+ * Call this when switching branches or clearing data to force re-creation
88
+ * The next getGraphIndex() call will create a fresh instance and rebuild
89
+ * @public
90
+ */
91
+ invalidateGraphIndex(): void;
85
92
  /**
86
93
  * Ensure the storage adapter is initialized
87
94
  */
@@ -185,18 +185,12 @@ export class BaseStorage extends BaseStorageAdapter {
185
185
  try {
186
186
  // Load type statistics from storage (if they exist)
187
187
  await this.loadTypeStatistics();
188
- // v6.0.0: Create GraphAdjacencyIndex (lazy-loaded, no rebuild)
189
- // LSM-trees are initialized on first use via ensureInitialized()
190
- // Index is populated incrementally as verbs are added via addVerb()
191
- try {
192
- prodLog.debug('[BaseStorage] Creating GraphAdjacencyIndex...');
193
- this.graphIndex = new GraphAdjacencyIndex(this);
194
- prodLog.debug(`[BaseStorage] GraphAdjacencyIndex instantiated (lazy-loaded), graphIndex=${!!this.graphIndex}`);
195
- }
196
- catch (error) {
197
- prodLog.error('[BaseStorage] Failed to create GraphAdjacencyIndex:', error);
198
- throw error;
199
- }
188
+ // v6.3.0: GraphAdjacencyIndex is now SINGLETON via getGraphIndex()
189
+ // - Removed direct creation here to fix dual-ownership bug
190
+ // - GraphAdjacencyIndex will be created lazily on first getGraphIndex() call
191
+ // - This ensures there's only ONE instance per storage adapter
192
+ // - See: https://github.com/soulcraftlabs/brainy/issues/vfs-corruption
193
+ prodLog.debug('[BaseStorage] init() complete - GraphAdjacencyIndex will be created via getGraphIndex()');
200
194
  }
201
195
  catch (error) {
202
196
  // Reset flag on failure to allow retry
@@ -210,13 +204,28 @@ export class BaseStorage extends BaseStorageAdapter {
210
204
  * @public
211
205
  */
212
206
  async rebuildGraphIndex() {
213
- if (!this.graphIndex) {
214
- throw new Error('GraphAdjacencyIndex not initialized');
215
- }
207
+ const index = await this.getGraphIndex();
216
208
  prodLog.info('[BaseStorage] Rebuilding graph index from existing data...');
217
- await this.graphIndex.rebuild();
209
+ await index.rebuild();
218
210
  prodLog.info('[BaseStorage] Graph index rebuild complete');
219
211
  }
212
+ /**
213
+ * Invalidate GraphAdjacencyIndex (v6.3.0)
214
+ * Call this when switching branches or clearing data to force re-creation
215
+ * The next getGraphIndex() call will create a fresh instance and rebuild
216
+ * @public
217
+ */
218
+ invalidateGraphIndex() {
219
+ if (this.graphIndex) {
220
+ prodLog.info('[BaseStorage] Invalidating GraphAdjacencyIndex for branch switch/clear');
221
+ // Stop any pending operations
222
+ if (typeof this.graphIndex.stopAutoFlush === 'function') {
223
+ this.graphIndex.stopAutoFlush();
224
+ }
225
+ this.graphIndex = undefined;
226
+ this.graphIndexPromise = undefined;
227
+ }
228
+ }
220
229
  /**
221
230
  * Ensure the storage adapter is initialized
222
231
  */
@@ -2311,28 +2320,12 @@ export class BaseStorage extends BaseStorageAdapter {
2311
2320
  this.verbCountsByType[typeIndex]++;
2312
2321
  // COW-aware write (v5.0.1): Use COW helper for branch isolation
2313
2322
  await this.writeObjectToBranch(path, verb);
2314
- // v6.0.0: Update GraphAdjacencyIndex incrementally (always available after init())
2315
- // GraphAdjacencyIndex.addVerb() calls ensureInitialized() automatically
2316
- if (this.graphIndex) {
2317
- prodLog.debug(`[BaseStorage] Updating GraphAdjacencyIndex with verb ${verb.id}`);
2318
- await this.graphIndex.addVerb({
2319
- id: verb.id,
2320
- sourceId: verb.sourceId,
2321
- targetId: verb.targetId,
2322
- vector: verb.vector,
2323
- source: verb.sourceId,
2324
- target: verb.targetId,
2325
- verb: verb.verb,
2326
- type: verb.verb,
2327
- createdAt: { seconds: Math.floor(Date.now() / 1000), nanoseconds: 0 },
2328
- updatedAt: { seconds: Math.floor(Date.now() / 1000), nanoseconds: 0 },
2329
- createdBy: { augmentation: 'storage', version: '6.0.0' }
2330
- });
2331
- prodLog.debug(`[BaseStorage] GraphAdjacencyIndex updated successfully`);
2332
- }
2333
- else {
2334
- prodLog.warn(`[BaseStorage] graphIndex is null, cannot update index for verb ${verb.id}`);
2335
- }
2323
+ // v6.3.0: GraphAdjacencyIndex updates are now handled EXCLUSIVELY by Brainy.relate()
2324
+ // via AddToGraphIndexOperation in the transaction system. This provides:
2325
+ // 1. Singleton pattern - only one graphIndex instance exists (via getGraphIndex())
2326
+ // 2. Transaction rollback - if relate() fails, index update is rolled back
2327
+ // 3. No double-counting - prevents duplicate addVerb() calls
2328
+ // REMOVED: Direct graphIndex.addVerb() call that caused dual-ownership bugs
2336
2329
  // Periodically save statistics
2337
2330
  // v6.2.9: Also save on first verb of each type to ensure low-count types are tracked
2338
2331
  // This prevents stale statistics after restart for types with < 100 verbs (common for VFS)
@@ -1,33 +1,24 @@
1
1
  /**
2
- * VersionIndex - Fast Version Lookup Using Existing Index Infrastructure (v5.3.0)
2
+ * VersionIndex - Pure Key-Value Version Storage (v6.3.0)
3
3
  *
4
- * Integrates with Brainy's existing index system:
5
- * - Uses MetadataIndexManager for field indexing
6
- * - Leverages UnifiedCache for memory management
7
- * - Uses EntityIdMapper for efficient ID handling
8
- * - Uses ChunkManager for adaptive chunking
9
- * - Leverages Roaring Bitmaps for fast set operations
4
+ * Stores version metadata using simple key-value storage:
5
+ * - Version list per entity: __version_meta_{entityId}_{branch}
6
+ * - Content stored separately by VersionStorage
10
7
  *
11
- * Version metadata is stored as regular entities with type='_version'
12
- * This allows us to use existing index infrastructure without modification!
13
- *
14
- * Fields indexed:
15
- * - versionEntityId: Entity being versioned
16
- * - versionBranch: Branch version was created on
17
- * - versionNumber: Version number
18
- * - versionTag: Optional user tag
19
- * - versionTimestamp: Creation timestamp
20
- * - versionCommitHash: Commit hash
8
+ * Key Design Decisions:
9
+ * - Versions are NOT entities (no brain.add())
10
+ * - Versions do NOT pollute find() results
11
+ * - Simple O(1) lookups per entity
12
+ * - Versions per entity is typically small (10-1000)
21
13
  *
22
14
  * NO MOCKS - Production implementation
23
15
  */
24
- import type { EntityVersion } from './VersionManager.js';
25
- import type { VersionQuery } from './VersionManager.js';
16
+ import type { EntityVersion, VersionQuery } from './VersionManager.js';
26
17
  /**
27
- * VersionIndex - Version lookup and querying using existing indexes
18
+ * VersionIndex - Pure key-value version metadata storage
28
19
  *
29
- * Strategy: Store version metadata as special entities with type='_version'
30
- * This leverages ALL existing index infrastructure automatically!
20
+ * Uses simple JSON storage instead of creating entities.
21
+ * This ensures versions never appear in find() results.
31
22
  */
32
23
  export declare class VersionIndex {
33
24
  private brain;
@@ -35,25 +26,21 @@ export declare class VersionIndex {
35
26
  constructor(brain: any);
36
27
  /**
37
28
  * Initialize version index
38
- *
39
- * No special setup needed - we use existing entity storage and indexes!
40
29
  */
41
30
  initialize(): Promise<void>;
42
31
  /**
43
32
  * Add version to index
44
33
  *
45
- * Stores version metadata as a special entity with type='_version'
46
- * This automatically indexes it using existing MetadataIndexManager!
34
+ * Stores version entry in key-value storage.
35
+ * Handles deduplication by content hash.
47
36
  *
48
- * @param version Version metadata
37
+ * @param version Version metadata to store
49
38
  */
50
39
  addVersion(version: EntityVersion): Promise<void>;
51
40
  /**
52
41
  * Get versions for an entity
53
42
  *
54
- * Uses existing MetadataIndexManager to query efficiently!
55
- *
56
- * @param query Version query
43
+ * @param query Version query with filters
57
44
  * @returns List of versions (newest first)
58
45
  */
59
46
  getVersions(query: VersionQuery): Promise<EntityVersion[]>;
@@ -92,35 +79,43 @@ export declare class VersionIndex {
92
79
  */
93
80
  removeVersion(entityId: string, version: number, branch: string): Promise<void>;
94
81
  /**
95
- * Convert entity to EntityVersion format
96
- *
97
- * @param entity Entity from storage
98
- * @returns EntityVersion or null if invalid
99
- */
100
- private entityToVersion;
101
- /**
102
- * Generate unique ID for version entity
103
- *
104
- * Format: _version:{entityId}:{version}:{branch}
82
+ * Clear all versions for an entity
105
83
  *
106
84
  * @param entityId Entity ID
107
- * @param version Version number
108
85
  * @param branch Branch name
109
- * @returns Version entity ID
86
+ * @returns Number of versions deleted
110
87
  */
111
- private getVersionEntityId;
88
+ clearVersions(entityId: string, branch: string): Promise<number>;
112
89
  /**
113
90
  * Get all versioned entities (for cleanup/debugging)
114
91
  *
115
- * @returns List of entity IDs that have versions
92
+ * Note: This is an expensive operation that requires scanning.
93
+ * In the simple key-value approach, we don't maintain a global index.
94
+ * This method returns an empty array - use storage-level scanning if needed.
95
+ *
96
+ * @returns Empty array (not supported in simple approach)
116
97
  */
117
98
  getVersionedEntities(): Promise<string[]>;
118
99
  /**
119
- * Clear all versions for an entity
100
+ * Generate storage key for version metadata
120
101
  *
121
102
  * @param entityId Entity ID
122
103
  * @param branch Branch name
123
- * @returns Number of versions deleted
104
+ * @returns Storage key
124
105
  */
125
- clearVersions(entityId: string, branch: string): Promise<number>;
106
+ private getMetaKey;
107
+ /**
108
+ * Load version store from storage
109
+ *
110
+ * @param key Storage key
111
+ * @returns Version store or null
112
+ */
113
+ private loadStore;
114
+ /**
115
+ * Save version store to storage
116
+ *
117
+ * @param key Storage key
118
+ * @param store Version store
119
+ */
120
+ private saveStore;
126
121
  }