fix: VFS readdir() no longer returns duplicate entries

- PathResolver.getChildren() now deduplicates by entity ID (v7.4.1)
  This handles duplicate relationship records that can occur when multiple
  Brainy instances create relationships concurrently for the same storage path.

- brain.clear() now invalidates GraphAdjacencyIndex (v7.4.1)
  Prevents stale in-memory index data after clearing storage, which could
  cause relate()'s duplicate check to fail.

Fixes: Workshop bug where readdir('/') returned same directory 13+ times
This commit is contained in:
David Snelling 2026-01-20 17:37:27 -08:00
parent 311f165533
commit 2bd4031f9c
2 changed files with 21 additions and 0 deletions

View file

@ -2636,6 +2636,15 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// Clear storage
await this.storage.clear()
// v7.4.1: Invalidate GraphAdjacencyIndex to prevent stale in-memory data
// The index has LSMTree data and verbIdSet pointing to deleted entities.
// Without this, relate()'s duplicate check uses stale data, potentially
// allowing duplicate relationships or missing valid duplicates.
if (typeof (this.storage as any).invalidateGraphIndex === 'function') {
;(this.storage as any).invalidateGraphIndex()
}
this.graphIndex = undefined as any
// Reset index
if ('clear' in this.index && typeof this.index.clear === 'function') {
await this.index.clear()

View file

@ -307,8 +307,20 @@ export class PathResolver {
const childIds = relations.map(r => r.to)
const childrenMap = await this.brain.batchGet(childIds)
// v7.4.1: Deduplicate by entity ID to handle duplicate relationship records
// This can occur when multiple Brainy instances create relationships concurrently
// for the same storage path (each instance has its own in-memory GraphAdjacencyIndex).
// The Set lookup is O(1), adding negligible overhead.
const seenEntityIds = new Set<string>()
// Process batched results
for (const relation of relations) {
// Skip if we've already processed this entity
if (seenEntityIds.has(relation.to)) {
continue
}
seenEntityIds.add(relation.to)
const entity = childrenMap.get(relation.to)
if (entity && entity.metadata?.vfsType && entity.metadata?.name) {
validChildren.push(entity as VFSEntity)