The write side of the law, ruled 2026-08-03: data is either in main space where developers can use anything, or it is in system.*. - The reserved-name write door DIES: add/update/relate/updateRelation metadata bags accept EVERY name (confidence, type, id, data, level, content, ...) as ordinary user fields — indexed, filterable, sortable, aggregatable, identical to any other field. The remap/enforce/warn machinery, the reservedFieldPolicy config (now a typed init refusal), and the compile-time metadata key bans are all removed. The one write refusal left: keys spelled 'system.*' (namespace forgery), now enforced on all four write doors. - STORED RECORDS GO NESTED (v2): engine fields top-level, the user bag nested verbatim under 'metadata', sealed by a format stamp — by-name storage discrimination is unsound once colliders are admitted. Legacy flat records stay readable forever through the shape-aware splitters (sound for them: the old door refused colliders). Time travel rides the same split (generation store snapshots whole records). - Name-based index exclusions DIE: user frame indexes every name; the excludeFields/indexedFields knobs and their silent-[] holes are gone; bulk-payload protection is value-shape only, uniform across names. - Consumer-sweep findings fixed in the same wave: per-type counts read the frozen 'system.type' column (addToIndex sort, affinity tracking, cold-count rehydration, VFS type bitmaps — legacy 'noun' fallback for pre-rebuild reads); resolveHiddenIds addresses 'system.visibility' (bare 'visibility' was a silent no-op under the law — VFS/system entities leaked into default reads). - Fidelity fallout fixed in the owning layers: readEntityFieldAddress reads the bag first (colliders were absent-shadowed by its own guard) and never serves system addresses from the bag; blob history refs read the bag shape-aware; migration transforms now receive ONE normalized view (engine fields + nested bag) regardless of stored era, and stray flat-habit keys refuse with the fix in the message. - THE REOPEN-COLLIDER CONFORMANCE CASE (required before any RC counts as gates-green): all ten collider names + plumbing names written as user fields, verified verbatim + queryable across live reads, flush+reopen, a forced epoch rebuild, and asOf time travel; relation mirror; forgery refusals; legacy flat-record compat. 8/8 green. Gates: unit 1901/1901 (exit 0) · integration 758 (exit 0) · conformance 27/27 (exit 0) · consumer test sweep migrated (10 files).
307 lines
11 KiB
TypeScript
307 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()
|
||
})
|
||
})
|