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
|
|
@ -320,15 +320,18 @@ export interface AddParams<T = any> {
|
|||
*/
|
||||
visibility?: 'public' | 'internal'
|
||||
/**
|
||||
* Structured queryable fields — indexed by MetadataIndex, used in `where` filters.
|
||||
* Structured queryable fields — indexed by MetadataIndex, used in `where`
|
||||
* filters, `orderBy`, and aggregation.
|
||||
*
|
||||
* Reserved entity fields (`RESERVED_ENTITY_FIELDS` — `noun`, `subtype`, `visibility`,
|
||||
* `createdAt`, `updatedAt`, `confidence`, `weight`, `service`, `data`, `createdBy`,
|
||||
* `_rev`) may NOT appear here — they have dedicated top-level params and the type makes
|
||||
* a literal reserved key a compile error. Untyped (JavaScript) callers that pass one
|
||||
* anyway are normalized at write time: user-settable fields remap to their top-level
|
||||
* param (top-level wins when both are supplied), system-managed fields are dropped with
|
||||
* a one-shot warning.
|
||||
* THE FIELD-ADDRESSING LAW: every name here is YOURS. There are no
|
||||
* reserved metadata names — `confidence`, `type`, `id`, `level`, `data`,
|
||||
* `content`, … are ordinary user fields that index, filter, sort, and
|
||||
* aggregate like any other, and survive faithfully across restarts and
|
||||
* rebuilds. Engine scalars are set only via their dedicated params
|
||||
* (`confidence`, `weight`, `subtype`, …) and are queried explicitly as
|
||||
* `system.<field>` (`where: { 'system.confidence': … }`). The ONE illegal
|
||||
* spelling is a key starting `'system.'` — the engine's explicit address
|
||||
* namespace cannot be forged; such a write refuses with a typed error.
|
||||
*/
|
||||
metadata?: EntityMetadataInput<T>
|
||||
/** Custom entity ID. When omitted, a time-ordered UUID v7 is generated; a supplied natural-key string is normalized to a stable UUID v5. */
|
||||
|
|
@ -386,12 +389,11 @@ export interface UpdateParams<T = any> {
|
|||
*/
|
||||
visibility?: EntityVisibility
|
||||
/**
|
||||
* Metadata fields to merge (or replace when `merge: false`). Reserved entity
|
||||
* fields (`RESERVED_ENTITY_FIELDS`) may NOT appear here — `confidence` /
|
||||
* `weight` / `subtype` / `visibility` have dedicated params on this call, and the rest
|
||||
* are system-managed. A literal reserved key is a compile error; untyped callers
|
||||
* are normalized at write time (remap user-settable, drop system-managed
|
||||
* with a one-shot warning).
|
||||
* Metadata fields to merge (or replace when `merge: false`). Every name is
|
||||
* the user's (the field-addressing law) — a patch field named `confidence`
|
||||
* updates YOUR field of that name, never the engine scalar (use the
|
||||
* dedicated `confidence` param for that). Keys spelled `'system.…'` refuse
|
||||
* with a typed error (namespace forgery).
|
||||
*/
|
||||
metadata?: EntityMetadataPatch<T>
|
||||
merge?: boolean // Merge or replace metadata (default: true)
|
||||
|
|
@ -444,11 +446,11 @@ export interface RelateParams<T = any> {
|
|||
/** Content for the relationship (optional — overrides auto-computed vector) */
|
||||
data?: any
|
||||
/**
|
||||
* Structured queryable fields on the edge. Reserved relationship fields
|
||||
* (`RESERVED_RELATION_FIELDS` — `verb`, `subtype`, `visibility`, `createdAt`,
|
||||
* `updatedAt`, `confidence`, `weight`, `service`, `data`, `createdBy`, `_rev`) may NOT
|
||||
* appear here — they have dedicated params. A literal reserved key is a
|
||||
* compile error; untyped callers are normalized at write time.
|
||||
* Structured queryable fields on the edge. Every name is the user's (the
|
||||
* field-addressing law) — `verb`, `confidence`, `weight`, … in this bag are
|
||||
* ordinary user fields; engine scalars ride their dedicated params and are
|
||||
* addressed as `system.<field>`. Keys spelled `'system.…'` refuse with a
|
||||
* typed error (namespace forgery).
|
||||
*/
|
||||
metadata?: RelationMetadataInput<T>
|
||||
/** Create reverse edge too (default: false) */
|
||||
|
|
@ -478,10 +480,9 @@ export interface UpdateRelationParams<T = any> {
|
|||
confidence?: number // New confidence (0-1)
|
||||
data?: any // New content
|
||||
/**
|
||||
* Metadata fields to merge (or replace when `merge: false`). Reserved
|
||||
* relationship fields (`RESERVED_RELATION_FIELDS`) may NOT appear here —
|
||||
* a literal reserved key is a compile error; untyped callers are
|
||||
* normalized at write time.
|
||||
* Metadata fields to merge (or replace when `merge: false`). Every name is
|
||||
* the user's (the field-addressing law); engine scalars ride their
|
||||
* dedicated params. Keys spelled `'system.…'` refuse with a typed error.
|
||||
*/
|
||||
metadata?: RelationMetadataPatch<T>
|
||||
merge?: boolean // Merge or replace metadata
|
||||
|
|
@ -2027,32 +2028,6 @@ export interface BrainyConfig {
|
|||
*/
|
||||
force?: boolean
|
||||
|
||||
/**
|
||||
* How write paths react when an untyped (JavaScript) caller smuggles a
|
||||
* Brainy-reserved field (`RESERVED_ENTITY_FIELDS` / `RESERVED_RELATION_FIELDS`
|
||||
* — `confidence`, `weight`, `subtype`, `visibility`, `service`, `createdBy`,
|
||||
* `noun`/`verb`, `data`, `createdAt`, `updatedAt`, `_rev`) **inside the
|
||||
* `metadata` bag** of `add()` / `update()` / `relate()` / `updateRelation()`
|
||||
* (and their `transact()` / `with()` mirrors). TypeScript callers can't write
|
||||
* these shapes at all — the compile-time guard on the metadata param types
|
||||
* (`NoReservedEntityKeys` / `NoReservedRelationKeys`) rejects a literal
|
||||
* reserved key — so this policy only governs untyped callers that slip one
|
||||
* past the compiler.
|
||||
*
|
||||
* - `'throw'` (**default, 8.0**): a reserved key in the bag throws a clear
|
||||
* `Error` naming the offending key(s) and the correct write path. No silent
|
||||
* remap, no data loss, no surprise. This is the 8.0 "no silent failures"
|
||||
* contract.
|
||||
* - `'warn'`: legacy remapping with a loud, one-shot (per key, per process)
|
||||
* warning for EVERY reserved key found — user-mutable fields are remapped to
|
||||
* their dedicated top-level param (top-level wins when both are supplied),
|
||||
* system-managed fields are dropped. Use while migrating untyped call sites.
|
||||
* - `'remap'`: the pre-8.0 silent remapping, no warning. Last-resort
|
||||
* compatibility hatch for code that intentionally relies on the bag path.
|
||||
*
|
||||
* @default 'throw'
|
||||
*/
|
||||
reservedFieldPolicy?: 'throw' | 'warn' | 'remap'
|
||||
}
|
||||
|
||||
// ============= Neural API Types =============
|
||||
|
|
|
|||
Reference in a new issue