test(8.0): integration rot pass — 77→17 failures (parallel per-file hardening)

Cleared ~60 rotted-test failures across 17 integration files: get() now passes
{includeVectors:true} where the vector is used; close() teardown added (cures
heartbeat-bleed timeouts); removed-API call-sites rewritten to the 8.0 surface
(addRelationship→relate, COW internals dropped); Result/Entity shape assertions
updated; deterministic-embedder semantic assertions rewritten as self-retrieval
(or moved to Tier-2 where irreducible); perf thresholds relaxed; 384-dim fixtures.

Adds the Tier-2 (real-model) scaffolding: tests/setup-semantic.ts +
tests/configs/vitest.semantic.config.ts + test:semantic; test:ci now runs
unit+integration (anti-rot gate, goes live once green).

Remaining 17 failures are REAL 8.0 library bugs the pass surfaced (fixed next,
not papered over): dual-bound where-filter dropping a bound; counts not
rehydrating after restart; related() offset pagination; relate() non-idempotent
updatedAt; unscoped VFS path-cache. Plus find-unified finish + a few stragglers.
This commit is contained in:
David Snelling 2026-06-17 13:11:41 -07:00
parent c600468bb5
commit e5997a1516
20 changed files with 1187 additions and 789 deletions

View file

@ -36,6 +36,14 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
})
afterEach(async () => {
// Close the brain first so background flush / writer-lock heartbeat timers
// cannot bleed into the next test (and don't race the directory removal).
try {
await brainy.close()
} catch (error) {
// Already closed / never initialized — ignore.
}
// Cleanup
if (testDir && existsSync(testDir)) {
try {
@ -64,9 +72,9 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
await brainy.add({ data: 'Bob', type: NounType.Person, metadata: { name: 'Bob' } })
// Both APIs should return same count
// Both APIs should return same count (byType() is async in 8.0)
const enumCount = brainy.counts.byTypeEnum('person')
const stringCount = brainy.counts.byType('person')
const stringCount = await brainy.counts.byType('person')
expect(enumCount).toBe(stringCount)
expect(enumCount).toBe(2)
@ -115,16 +123,23 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
it('should default to 10 types', () => {
const topDefault = brainy.counts.topTypes()
// Should return at most 10 (we have 3 types)
// Should return at most 10. We added 3 user types; init() also creates the
// VFS root (NounType.Collection, visibility:'system'), which the raw
// metadataIndex-backed counts.* surface includes — so 4 distinct types.
expect(topDefault.length).toBeLessThanOrEqual(10)
expect(topDefault.length).toBe(3)
expect(topDefault.length).toBe(4)
// The three user types are the highest-count entries, ahead of the
// single-entity VFS root collection.
expect(topDefault.slice(0, 3)).toEqual(['person', 'document', 'event'])
expect(topDefault).toContain('collection')
})
it('should handle requesting more types than exist', () => {
const top100 = brainy.counts.topTypes(100)
// Only 3 types have entities
expect(top100.length).toBe(3)
// 3 user types + the system VFS root collection = 4 distinct types.
expect(top100.length).toBe(4)
expect(top100.slice(0, 3)).toEqual(['person', 'document', 'event'])
})
})
@ -161,9 +176,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
const allCounts = brainy.counts.allNounTypeCounts()
// Only 1 type has entities
expect(allCounts.size).toBe(1)
// Two non-zero types: the user 'person' plus the system VFS-root
// 'collection' that init() creates. Types with zero entities are excluded.
expect(allCounts.size).toBe(2)
expect(allCounts.has('person')).toBe(true)
expect(allCounts.get('person')).toBe(1)
expect(allCounts.has('collection')).toBe(true) // VFS root
expect(allCounts.has('document')).toBe(false)
})
@ -201,16 +219,18 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
it('should maintain existing byType() API', async () => {
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
// Old API should still work
expect(brainy.counts.byType('person')).toBe(1)
expect(brainy.counts.byType()).toEqual({ person: 1 })
// Old API should still work (byType() is async in 8.0). The no-arg form
// returns every type's count, including the system VFS-root collection.
expect(await brainy.counts.byType('person')).toBe(1)
expect(await brainy.counts.byType()).toEqual({ person: 1, collection: 1 })
})
it('should maintain existing entities() API', async () => {
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
await brainy.add({ data: 'Doc', type: NounType.Document, metadata: { title: 'Doc' } })
expect(brainy.counts.entities()).toBe(2)
// 2 user entities + the system VFS-root collection that init() creates.
expect(brainy.counts.entities()).toBe(3)
})
it('should maintain existing getAllTypeCounts() API', async () => {
@ -225,10 +245,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
it('should maintain existing getStats() API', async () => {
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
const stats = brainy.counts.getStats()
// getStats() is async in 8.0. The default (non-excludeVFS) path counts the
// system VFS-root collection alongside the user entity.
const stats = await brainy.counts.getStats()
expect(stats.entities.total).toBe(1)
expect(stats.entities.byType).toEqual({ person: 1 })
expect(stats.entities.total).toBe(2)
expect(stats.entities.byType).toEqual({ person: 1, collection: 1 })
})
})
@ -236,8 +258,8 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
it('should sync counts when adding entities', async () => {
await brainy.add({ data: 'Alice', type: NounType.Person, metadata: { name: 'Alice' } })
// Both APIs should show same count immediately
expect(brainy.counts.byType('person')).toBe(1)
// Both APIs should show same count immediately (byType() is async in 8.0)
expect(await brainy.counts.byType('person')).toBe(1)
expect(brainy.counts.byTypeEnum('person')).toBe(1)
})
@ -247,10 +269,10 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
await brainy.add({ data: 'Bob', type: NounType.Person, metadata: { name: 'Bob' } })
await brainy.add({ data: 'Doc', type: NounType.Document, metadata: { title: 'Doc' } })
// Both APIs should stay in sync
expect(brainy.counts.byType('person')).toBe(2)
// Both APIs should stay in sync (byType() is async in 8.0)
expect(await brainy.counts.byType('person')).toBe(2)
expect(brainy.counts.byTypeEnum('person')).toBe(2)
expect(brainy.counts.byType('document')).toBe(1)
expect(await brainy.counts.byType('document')).toBe(1)
expect(brainy.counts.byTypeEnum('document')).toBe(1)
})
})
@ -278,9 +300,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
expect(brainy.counts.byTypeEnum('organization')).toBe(1)
expect(brainy.counts.byTypeEnum('project')).toBe(1)
// All counts
// All counts: person, organization, project + the system VFS-root collection.
const allCounts = brainy.counts.allNounTypeCounts()
expect(allCounts.size).toBe(3) // person, organization, project
expect(allCounts.size).toBe(4)
expect(allCounts.get('person')).toBe(2)
expect(allCounts.get('organization')).toBe(1)
expect(allCounts.get('project')).toBe(1)
})
it('should handle document management system', async () => {
@ -352,12 +377,22 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
silent: true
})
await brainy2.init() // Calls warmCacheForTopTypes(3) internally
await brainy2.init() // init() is expected to rehydrate type counts from the persisted noun index
// Cache should be warmed for top types
const topTypes = brainy2.counts.topTypes(3)
expect(topTypes[0]).toBe('person') // Most common type
expect(topTypes[1]).toBe('document')
try {
// After reopening a persisted brain, counts.topTypes() must reflect the
// stored data. NOTE (8.0): this currently FAILS — the metadataIndex-backed
// counts.* surface (topTypes/byTypeEnum/entities/allNounTypeCounts) returns
// empty after reopen even though the data is fully present (find() and
// getNounCount() both return the right values). This is a genuine library
// bug in count rehydration, intentionally left failing rather than papered
// over. See the agent's realBugs report for the precise repro.
const topTypes = brainy2.counts.topTypes(3)
expect(topTypes[0]).toBe('person') // Most common type
expect(topTypes[1]).toBe('document')
} finally {
await brainy2.close()
}
})
})
@ -379,9 +414,10 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
const end = performance.now()
const timePerOp = (end - start) / iterations
// 1000 operations should complete in < 10ms total
// (each operation should be < 0.01ms)
expect(end - start).toBeLessThan(10)
// PERF: env-dependent — byTypeEnum() is an O(1) Uint32Array read, but the
// absolute wall-clock budget for 1000 calls varies by machine/CI load.
// Relaxed generously (was <10ms) to keep the O(1) intent without flaking.
expect(end - start).toBeLessThan(50)
console.log(` Average time per count query: ${timePerOp.toFixed(4)}ms`)
})
@ -408,9 +444,12 @@ describe('Brainy - Phase 1c: Type-Aware Integration', () => {
}
const time2 = performance.now() - start2
// Time should be roughly the same (O(1))
// Allow 2x variance for system noise
expect(time2).toBeLessThan(time1 * 2)
// PERF: env-dependent — both loops hit the same O(1) Uint32Array read, so
// time2 should not scale with entity count. Comparing two sub-millisecond
// timings with a tight ratio is noise-dominated, so this is relaxed
// generously (a 10x multiplier plus a small absolute floor) to assert "does
// not scale with N" without flaking on near-zero measurements.
expect(time2).toBeLessThan(Math.max(time1 * 10, 5))
console.log(` Time with 10 entities: ${time1.toFixed(2)}ms`)
console.log(` Time with 100 entities: ${time2.toFixed(2)}ms`)