/** * @module tests/integration/level-field-shadow * @description The reserved-name shadow fix (VENUE-BRAINY-ORDERBY-NOOP, * 2026-08-03): `level` is HNSW plumbing, not an entity field — it must never * shadow user metadata of the same name. Pre-fix, STANDARD_ENTITY_FIELDS * listed `level`, so every by-name read returned the engine's internal 0 * (all-equal → stable sort → insertion order, silently), and the indexing * views stamped level:0 into the same flattened column as user values * (multi-valued [0, real] poison). Laws: * (1) venue's exact repro sorts: three adds with metadata.level 3/9/6 → * find({orderBy:'level'}) returns 9,6,3 desc and 3,6,9 asc; * (2) where {level: N} matches through filter AND egress guard; * (3) the index column carries the user value only (no 0 poison); * (4) update() keeps `level` readable (the update indexing view is clean too); * (5) the transact() update path never rewrites the noun record on a * metadata-only patch (the planUpdate granularity completion). */ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { Brainy } from '../../src/brainy.js' import { NounType } from '../../src/types/graphTypes.js' import { EXPECTED_INDEX_EPOCH } from '../../src/storage/brainFormat.js' const stubEmbedding = async (text: string): Promise => { const hash = text.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) return new Array(384).fill(0).map((_, i) => Math.sin(hash + i)) } describe('level field shadow — user metadata named level is a real field', () => { let brain: Brainy beforeEach(async () => { brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' as const }, embeddingFunction: stubEmbedding }) await brain.init() }) afterEach(async () => { await brain.close() }) async function addProbeRows(): Promise { const ids: string[] = [] for (const level of [3, 9, 6]) { ids.push( await brain.add({ data: `probe character level ${level}`, type: NounType.Person, subtype: 'probe-char', metadata: { name: `char-${level}`, level } }) ) } return ids } it("venue's exact repro: orderBy 'level' sorts desc and asc", async () => { await addProbeRows() const desc = await brain.find({ type: NounType.Person, subtype: 'probe-char', orderBy: 'level', order: 'desc', limit: 100 }) expect(desc.map((r: any) => r.metadata?.level)).toEqual([9, 6, 3]) const asc = await brain.find({ type: NounType.Person, subtype: 'probe-char', orderBy: 'level', order: 'asc', limit: 100 }) expect(asc.map((r: any) => r.metadata?.level)).toEqual([3, 6, 9]) }) it('ordered reads are COMPLETE — no row dropped (the 2-of-3 face)', async () => { const ids = await addProbeRows() const desc = await brain.find({ type: NounType.Person, subtype: 'probe-char', orderBy: 'level', order: 'desc', limit: 100 }) expect(desc).toHaveLength(3) expect(new Set(desc.map((r: any) => r.id))).toEqual(new Set(ids)) }) it('where {level: N} matches through the filter and the egress guard', async () => { const ids = await addProbeRows() const hit = await brain.find({ where: { level: 9 } }) expect(hit).toHaveLength(1) expect(hit[0].id).toBe(ids[1]) expect(hit[0].metadata?.level).toBe(9) }) it('the index column carries ONLY the user value (no 0 poison)', async () => { const ids = await addProbeRows() const metadataIndex = (brain as any).metadataIndex const value = await metadataIndex.getFieldValueForEntity(ids[1], 'level') expect(value).toBe(9) // Zero must not match anything — pre-fix every entity carried a phantom 0. const phantom = await brain.find({ where: { level: 0 } }) expect(phantom).toHaveLength(0) }) it('update() keeps level readable (the update indexing view is clean)', async () => { const ids = await addProbeRows() await brain.update({ id: ids[0], metadata: { level: 12 } }) const desc = await brain.find({ type: NounType.Person, subtype: 'probe-char', orderBy: 'level', order: 'desc', limit: 100 }) expect(desc.map((r: any) => r.metadata?.level)).toEqual([12, 9, 6]) }) it('transact() metadata-only update never rewrites the noun record', async () => { const ids = await addProbeRows() const storage = (brain as any).storage const saveNounSpy = vi.spyOn(storage, 'saveNoun') await brain.transact([ { op: 'update', id: ids[0], metadata: { level: 4 } }, { op: 'update', id: ids[2], metadata: { level: 7 } } ]) expect(saveNounSpy).not.toHaveBeenCalled() saveNounSpy.mockRestore() const after = await brain.get(ids[0], { includeVectors: true }) expect(after?.metadata?.level).toBe(4) expect(Array.isArray(after?.vector) && after!.vector!.length).toBe(384) }) it('this build runs index epoch 3 (the namespace-law key split rebuild)', () => { expect(EXPECTED_INDEX_EPOCH).toBe(3) }) }) describe('noun-record writes never stamp over stored graph state', () => { let brain: Brainy beforeEach(async () => { brain = new Brainy({ requireSubtype: false, storage: { type: 'memory' as const }, embeddingFunction: stubEmbedding }) await brain.init() }) afterEach(async () => { await brain.close() }) it('a data-changing update preserves LEGACY inline connections in the record', async () => { // Codec-era records carry an EMPTY connections field by design (the // adjacency lives in a separate compressed blob) — the clobber window // exists only for legacy pre-codec records whose adjacency is inline. // Simulate one: write the record with inline connections directly. const id = await brain.add({ data: 'legacy-shaped node', type: NounType.Concept, metadata: { n: 1 } }) const storage = (brain as any).storage const rec = await storage.getNoun(id) const legacy = { ...rec, connections: new Map([[0, new Set(['00000000-0000-4000-8000-00000000aaaa'])]]), level: 1 } await storage.saveNoun(legacy) const before = await storage.getNoun(id) expect(before.connections.size).toBeGreaterThan(0) // A data-changing update stages SaveNounOperation with placeholder // adjacency — the legacy inline connections must survive the write. await brain.update({ id, data: 'completely re-embedded text' }) const after = await storage.getNoun(id) expect(after.connections.size).toBeGreaterThan(0) expect(after.level).toBe(1) }) })