/** * @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 2 (the paired level-indexability rebuild)', () => { expect(EXPECTED_INDEX_EPOCH).toBe(2) }) })