308 lines
11 KiB
TypeScript
308 lines
11 KiB
TypeScript
|
|
/**
|
|||
|
|
* @module tests/conformance/collider-fidelity
|
|||
|
|
* @description THE REOPEN-COLLIDER CONFORMANCE CASE (required cross-engine
|
|||
|
|
* before any RC counts as gates-green — ruled 2026-08-03). The
|
|||
|
|
* field-addressing law's fidelity half: user metadata may carry ANY name —
|
|||
|
|
* including every engine spelling (`confidence`, `type`, `id`, `createdAt`,
|
|||
|
|
* …) and every plumbing name (`level`, `data`, `vector`, `_rev`) — and the
|
|||
|
|
* value survives, verbatim and reachable, across the FULL lifecycle: live
|
|||
|
|
* reads, where/orderBy, flush, close+reopen, a forced epoch rebuild, and
|
|||
|
|
* time travel. The engine scalars stay separately reachable at `system.*`
|
|||
|
|
* the whole way. No halfway states.
|
|||
|
|
*
|
|||
|
|
* Self-arming like the namespace-law suite: skips loudly until the arming
|
|||
|
|
* exports are present, so the suite can sit on a branch ahead of the build.
|
|||
|
|
*/
|
|||
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
|||
|
|
import { mkdtempSync, rmSync } from 'node:fs'
|
|||
|
|
import { tmpdir } from 'node:os'
|
|||
|
|
import { join } from 'node:path'
|
|||
|
|
import * as brainyExports from '../../src/index.js'
|
|||
|
|
import { Brainy, NounType, VerbType } from '../../src/index.js'
|
|||
|
|
import {
|
|||
|
|
BRAIN_FORMAT_PATH,
|
|||
|
|
EXPECTED_INDEX_EPOCH
|
|||
|
|
} from '../../src/storage/brainFormat.js'
|
|||
|
|
|
|||
|
|
const ARMED = 'UnresolvableFieldError' in brainyExports
|
|||
|
|
const suite = ARMED ? describe : describe.skip
|
|||
|
|
if (!ARMED) {
|
|||
|
|
// eslint-disable-next-line no-console
|
|||
|
|
console.warn(
|
|||
|
|
'[collider-fidelity] SKIPPING: package root does not export the ' +
|
|||
|
|
'field-addressing law surface yet (UnresolvableFieldError absent).'
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** Every entity system scalar name written as a USER metadata field, with
|
|||
|
|
* unmistakable user values, plus the plumbing names and naturals. */
|
|||
|
|
const COLLIDER_BAG = {
|
|||
|
|
// the ten entity system scalars, as user fields
|
|||
|
|
id: 'user-id',
|
|||
|
|
type: 'user-type',
|
|||
|
|
subtype: 'user-subtype',
|
|||
|
|
createdAt: 'user-createdAt',
|
|||
|
|
updatedAt: 'user-updatedAt',
|
|||
|
|
confidence: 'user-confidence',
|
|||
|
|
weight: 'user-weight',
|
|||
|
|
visibility: 'user-visibility',
|
|||
|
|
service: 'user-service',
|
|||
|
|
createdBy: 'user-createdBy',
|
|||
|
|
// plumbing names, as user fields
|
|||
|
|
level: 7,
|
|||
|
|
data: 'user-data',
|
|||
|
|
vector: 'user-vector',
|
|||
|
|
_rev: 'user-rev',
|
|||
|
|
// naturals previously silently un-indexed by name
|
|||
|
|
content: 'user-content',
|
|||
|
|
// a plain control field
|
|||
|
|
plain: 'control'
|
|||
|
|
} as const
|
|||
|
|
|
|||
|
|
|
|||
|
|
suite('collider fidelity — the reopen-collider case (both suites, ruled)', () => {
|
|||
|
|
let dir: string
|
|||
|
|
let brain: Brainy
|
|||
|
|
let colliderId: string
|
|||
|
|
|
|||
|
|
const open = async (): Promise<Brainy> => {
|
|||
|
|
const b = new Brainy({
|
|||
|
|
storage: { type: 'filesystem', path: dir },
|
|||
|
|
requireSubtype: false
|
|||
|
|
})
|
|||
|
|
await b.init()
|
|||
|
|
return b
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** The full read battery — run at every lifecycle boundary. */
|
|||
|
|
const verifyColliderTruth = async (label: string): Promise<void> => {
|
|||
|
|
// 1. get(): the bag comes back verbatim; engine scalars stay engine.
|
|||
|
|
const entity = await brain.get(colliderId)
|
|||
|
|
expect(entity, `${label}: entity readable`).toBeTruthy()
|
|||
|
|
for (const [k, v] of Object.entries(COLLIDER_BAG)) {
|
|||
|
|
expect(
|
|||
|
|
(entity!.metadata as Record<string, unknown>)[k],
|
|||
|
|
`${label}: bag.${k} verbatim`
|
|||
|
|
).toEqual(v)
|
|||
|
|
}
|
|||
|
|
expect(entity!.type, `${label}: engine type intact`).toBe(NounType.Document)
|
|||
|
|
expect(entity!.confidence, `${label}: engine confidence intact`).toBe(0.25)
|
|||
|
|
|
|||
|
|
// 2. where on collider names (bare = the user's field, always).
|
|||
|
|
for (const [k, v] of [
|
|||
|
|
['confidence', 'user-confidence'],
|
|||
|
|
['type', 'user-type'],
|
|||
|
|
['id', 'user-id'],
|
|||
|
|
['content', 'user-content'],
|
|||
|
|
['data', 'user-data'],
|
|||
|
|
['level', 7]
|
|||
|
|
] as const) {
|
|||
|
|
const rows = await brain.find({ where: { [k]: v }, limit: 10 })
|
|||
|
|
expect(
|
|||
|
|
rows.map((r) => r.id),
|
|||
|
|
`${label}: where {${k}} finds the collider row`
|
|||
|
|
).toContain(colliderId)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. system.* keeps reading the ENGINE values.
|
|||
|
|
const byEngine = await brain.find({
|
|||
|
|
where: { 'system.confidence': 0.25 },
|
|||
|
|
limit: 10
|
|||
|
|
})
|
|||
|
|
expect(
|
|||
|
|
byEngine.map((r) => r.id),
|
|||
|
|
`${label}: system.confidence reads the engine scalar`
|
|||
|
|
).toContain(colliderId)
|
|||
|
|
const byUserSpelledSystem = await brain.find({
|
|||
|
|
where: { 'system.confidence': 'user-confidence' },
|
|||
|
|
limit: 10
|
|||
|
|
})
|
|||
|
|
expect(
|
|||
|
|
byUserSpelledSystem.map((r) => r.id),
|
|||
|
|
`${label}: the user's value is NOT reachable via system.*`
|
|||
|
|
).not.toContain(colliderId)
|
|||
|
|
|
|||
|
|
// 4. orderBy a collider name orders by the USER values.
|
|||
|
|
const ordered = await brain.find({
|
|||
|
|
type: NounType.Document,
|
|||
|
|
orderBy: 'level',
|
|||
|
|
order: 'desc',
|
|||
|
|
limit: 10
|
|||
|
|
})
|
|||
|
|
expect(ordered.length, `${label}: ordered read complete`).toBe(3)
|
|||
|
|
expect(
|
|||
|
|
(ordered[0].metadata as Record<string, unknown>).plain,
|
|||
|
|
`${label}: user level orders desc (7 first)`
|
|||
|
|
).toBe('control')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
beforeAll(async () => {
|
|||
|
|
dir = mkdtempSync(join(tmpdir(), 'brainy-collider-'))
|
|||
|
|
brain = await open()
|
|||
|
|
|
|||
|
|
colliderId = await brain.add({
|
|||
|
|
data: 'the collider probe document',
|
|||
|
|
type: NounType.Document,
|
|||
|
|
confidence: 0.25,
|
|||
|
|
metadata: { ...COLLIDER_BAG }
|
|||
|
|
})
|
|||
|
|
// two ordering companions with smaller user `level`s
|
|||
|
|
await brain.add({
|
|||
|
|
data: 'ordering companion low',
|
|||
|
|
type: NounType.Document,
|
|||
|
|
metadata: { level: 3, plain: 'low' }
|
|||
|
|
})
|
|||
|
|
await brain.add({
|
|||
|
|
data: 'ordering companion mid',
|
|||
|
|
type: NounType.Document,
|
|||
|
|
metadata: { level: 5, plain: 'mid' }
|
|||
|
|
})
|
|||
|
|
}, 120000)
|
|||
|
|
|
|||
|
|
afterAll(async () => {
|
|||
|
|
await brain.close().catch(() => {})
|
|||
|
|
rmSync(dir, { recursive: true, force: true })
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('LIVE: colliders are the user’s, verbatim and fully queryable', async () => {
|
|||
|
|
await verifyColliderTruth('live')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('REOPEN: the restart boundary loses nothing', async () => {
|
|||
|
|
await brain.flush()
|
|||
|
|
await brain.close()
|
|||
|
|
brain = await open()
|
|||
|
|
await verifyColliderTruth('reopen')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('REBUILD: a forced epoch rebuild re-indexes the colliders from canonical', async () => {
|
|||
|
|
await brain.close()
|
|||
|
|
// Simulate epoch drift: a missing marker forces the full derived-index
|
|||
|
|
// rebuild at open — the exact path every pre-law brain takes once.
|
|||
|
|
rmSync(join(dir, BRAIN_FORMAT_PATH), { force: true })
|
|||
|
|
brain = await open()
|
|||
|
|
await verifyColliderTruth('rebuild')
|
|||
|
|
// And the rebuild re-stamps the current epoch.
|
|||
|
|
const marker = await (
|
|||
|
|
brain as unknown as {
|
|||
|
|
storage: { readRawObject(p: string): Promise<{ indexEpoch?: number } | null> }
|
|||
|
|
}
|
|||
|
|
).storage.readRawObject(BRAIN_FORMAT_PATH)
|
|||
|
|
expect(marker?.indexEpoch).toBe(EXPECTED_INDEX_EPOCH)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('TIME TRAVEL: asOf reads historical collider values faithfully', async () => {
|
|||
|
|
const gen = brain.generation()
|
|||
|
|
await brain.update({ id: colliderId, metadata: { confidence: 'user-confidence-v2' } })
|
|||
|
|
const now = await brain.get(colliderId)
|
|||
|
|
expect((now!.metadata as Record<string, unknown>).confidence).toBe('user-confidence-v2')
|
|||
|
|
|
|||
|
|
const past = await brain.asOf(gen)
|
|||
|
|
try {
|
|||
|
|
const then = await past.get(colliderId)
|
|||
|
|
expect(
|
|||
|
|
(then!.metadata as Record<string, unknown>).confidence,
|
|||
|
|
'asOf reads the pre-update USER value'
|
|||
|
|
).toBe('user-confidence')
|
|||
|
|
} finally {
|
|||
|
|
await past.release()
|
|||
|
|
}
|
|||
|
|
// engine scalar untouched throughout
|
|||
|
|
expect(now!.confidence).toBe(0.25)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('RELATION MIRROR: edge collider bags survive write → read → reopen', async () => {
|
|||
|
|
const a = await brain.add({ data: 'edge endpoint a', type: NounType.Person, metadata: { plain: 'a' } })
|
|||
|
|
const b = await brain.add({ data: 'edge endpoint b', type: NounType.Person, metadata: { plain: 'b' } })
|
|||
|
|
const edgeBag = {
|
|||
|
|
verb: 'user-verb',
|
|||
|
|
confidence: 'user-edge-confidence',
|
|||
|
|
weight: 'user-edge-weight',
|
|||
|
|
subtype: 'user-edge-subtype',
|
|||
|
|
createdAt: 'user-edge-createdAt',
|
|||
|
|
service: 'user-edge-service'
|
|||
|
|
}
|
|||
|
|
const relId = await brain.relate({
|
|||
|
|
from: a,
|
|||
|
|
to: b,
|
|||
|
|
type: VerbType.RelatedTo,
|
|||
|
|
confidence: 0.5,
|
|||
|
|
metadata: { ...edgeBag }
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const check = async (label: string): Promise<void> => {
|
|||
|
|
const rels = await brain.related({ from: a, type: VerbType.RelatedTo })
|
|||
|
|
const rel = rels.find((r) => r.id === relId)
|
|||
|
|
expect(rel, `${label}: relation readable`).toBeTruthy()
|
|||
|
|
for (const [k, v] of Object.entries(edgeBag)) {
|
|||
|
|
expect(
|
|||
|
|
(rel!.metadata as Record<string, unknown>)[k],
|
|||
|
|
`${label}: edge bag.${k} verbatim`
|
|||
|
|
).toEqual(v)
|
|||
|
|
}
|
|||
|
|
expect(rel!.confidence, `${label}: engine edge confidence intact`).toBe(0.5)
|
|||
|
|
expect(rel!.type, `${label}: engine verb intact`).toBe(VerbType.RelatedTo)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await check('live')
|
|||
|
|
await brain.flush()
|
|||
|
|
await brain.close()
|
|||
|
|
brain = await open()
|
|||
|
|
await check('reopen')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('FORGERY: user metadata keys spelled system.* refuse at every write door', async () => {
|
|||
|
|
await expect(
|
|||
|
|
brain.add({ data: 'forged', type: NounType.Document, metadata: { 'system.confidence': 1 } })
|
|||
|
|
).rejects.toThrow(/system\./)
|
|||
|
|
await expect(
|
|||
|
|
brain.update({ id: colliderId, metadata: { 'system.type': 'x' } })
|
|||
|
|
).rejects.toThrow(/system\./)
|
|||
|
|
const a = await brain.add({ data: 'forgery endpoint a', type: NounType.Person, metadata: {} })
|
|||
|
|
const b = await brain.add({ data: 'forgery endpoint b', type: NounType.Person, metadata: {} })
|
|||
|
|
await expect(
|
|||
|
|
brain.relate({ from: a, to: b, type: VerbType.RelatedTo, metadata: { 'system.verb': 'x' } })
|
|||
|
|
).rejects.toThrow(/system\./)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('CONFIG: the dead reservedFieldPolicy option refuses loudly, never ignored', () => {
|
|||
|
|
expect(
|
|||
|
|
() => new Brainy({ storage: { type: 'memory' }, reservedFieldPolicy: 'throw' } as never)
|
|||
|
|
).toThrow(/field-addressing law/)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('LEGACY: a pre-law flat record still reads with engine fields top-level', async () => {
|
|||
|
|
const storage = (
|
|||
|
|
brain as unknown as {
|
|||
|
|
storage: {
|
|||
|
|
saveNoun(n: unknown): Promise<void>
|
|||
|
|
saveNounMetadata(id: string, m: Record<string, unknown>): Promise<void>
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
).storage
|
|||
|
|
const legacyId = '00000000-0000-4000-8000-00000000f1a7'
|
|||
|
|
await storage.saveNoun({ id: legacyId, vector: new Array(384).fill(0.01), connections: new Map(), level: 0 })
|
|||
|
|
// Legacy FLAT shape: engine + user keys mixed at one level, NO _fmt stamp.
|
|||
|
|
// Sound to split by name — the pre-law door refused user colliders.
|
|||
|
|
await storage.saveNounMetadata(legacyId, {
|
|||
|
|
noun: NounType.Document,
|
|||
|
|
confidence: 0.75,
|
|||
|
|
createdAt: 1700000000000,
|
|||
|
|
updatedAt: 1700000000000,
|
|||
|
|
_rev: 1,
|
|||
|
|
legacyField: 'legacy-value'
|
|||
|
|
})
|
|||
|
|
const entity = await brain.get(legacyId)
|
|||
|
|
expect(entity).toBeTruthy()
|
|||
|
|
expect(entity!.confidence, 'legacy flat confidence = engine').toBe(0.75)
|
|||
|
|
expect(
|
|||
|
|
(entity!.metadata as Record<string, unknown>).legacyField,
|
|||
|
|
'legacy custom field = user bag'
|
|||
|
|
).toBe('legacy-value')
|
|||
|
|
expect(
|
|||
|
|
(entity!.metadata as Record<string, unknown>).confidence,
|
|||
|
|
'legacy flat engine key never leaks into the bag'
|
|||
|
|
).toBeUndefined()
|
|||
|
|
})
|
|||
|
|
})
|