feat(namespace): NO SPECIAL NAMES + storage fidelity — the ruled completion of the field-addressing law
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).
This commit is contained in:
parent
48a6130a50
commit
24bf6cdbc5
32 changed files with 1355 additions and 1905 deletions
307
tests/conformance/collider-fidelity.test.ts
Normal file
307
tests/conformance/collider-fidelity.test.ts
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
/**
|
||||
* @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()
|
||||
})
|
||||
})
|
||||
Reference in a new issue