feat: comprehensive metadata namespace architecture and cleanup system

BREAKING CHANGE: Remove hard delete option from deleteVerb() for consistent API

- Add complete metadata namespace architecture with O(1) soft delete performance
- Implement periodic cleanup system for old soft-deleted items
- Add restore methods for both nouns and verbs
- Require metadata contracts for all augmentations
- Eliminate namespace collisions with clean separation (_brainy, _augmentations, _audit)
- Optimize index performance using flattened dot-notation for O(1) lookups
- Add comprehensive augmentation safety system with type-safe access control
- Maintain full backward compatibility for existing data
- Add enterprise-grade cleanup with configurable age thresholds and batch processing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
David Snelling 2025-08-27 15:38:48 -07:00
parent 9220ca6748
commit 7e243b6f2b
35 changed files with 2700 additions and 125 deletions

View file

@ -518,6 +518,38 @@ export class MetadataIndexManager {
}
}
/**
* Get all IDs in the index
*/
async getAllIds(): Promise<string[]> {
// Collect all unique IDs from all index entries
const allIds = new Set<string>()
// First, add all IDs from the in-memory cache
for (const entry of this.indexCache.values()) {
entry.ids.forEach(id => allIds.add(id))
}
// If storage has a method to get all nouns, use it as the source of truth
// This ensures we include items that might not be indexed yet
if (this.storage && typeof (this.storage as any).getNouns === 'function') {
try {
const result = await (this.storage as any).getNouns({
pagination: { limit: 100000 }
})
if (result && result.items) {
result.items.forEach((item: any) => {
if (item.id) allIds.add(item.id)
})
}
} catch (e) {
// Fall back to using only indexed IDs
}
}
return Array.from(allIds)
}
/**
* Get IDs for a specific field-value combination with caching
*/
@ -782,6 +814,25 @@ export class MetadataIndexManager {
fieldResults = Array.from(allIds)
}
break
// Negation operators
case 'notEquals':
case 'isNot':
case 'ne':
// For notEquals, we need all IDs EXCEPT those matching the value
// This is especially important for soft delete: deleted !== true
// should include items without a deleted field
// First, get all IDs in the database
const allItemIds = await this.getAllIds()
// Then get IDs that match the value we want to exclude
const excludeIds = await this.getIds(field, operand)
const excludeSet = new Set(excludeIds)
// Return all IDs except those to exclude
fieldResults = allItemIds.filter(id => !excludeSet.has(id))
break
}
}
} else {