feat(8.0): visibility field (public/internal/system) on nouns + verbs
Adds a reserved, top-level `visibility` field (mirrors the subtype rollout): 'public' (default, surfaced) | 'internal' (developer app-internal — hidden from default find/count/stats, opt-in via includeInternal) | 'system' (Brainy plumbing, library-set only). Fixes a real leak: the VFS root entity counted in getNounCount() and appeared in find() (a fresh brain reported 1 entity). It is now visibility:'system' → excluded from every user-facing surface. Developers also get a first-class hidden-unless-asked tier (e.g. learned internals vs user-exposed data). - Reserved (RESERVED_ENTITY_FIELDS / RESERVED_RELATION_FIELDS) — spoof-proof from metadata. - Threaded through add/relate/update/transact; surfaced top-level on reads. - Default exclusion in counts (baseStorage), find()/related() (hard candidate filter via excludeVisibility — keeps topK/limit correct), and stats; includeInternal/includeSystem opt-ins. - VFS root marked 'system'. Tests: visibility.test.ts 17/17 (fresh-brain getNounCount()===0, internal hidden + opt-in, verb symmetry, top-level surfacing, metadata-spoof rejection). Unit 1431 green; count-synchronization integration now passes (off-by-one fixed).
This commit is contained in:
parent
0ca0e5c6cc
commit
f4dea80176
10 changed files with 777 additions and 41 deletions
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { StorageAdapter, Vector } from '../coreTypes.js'
|
||||
import type { EntityVisibility } from '../coreTypes.js'
|
||||
import { NounType, VerbType } from './graphTypes.js'
|
||||
import type {
|
||||
EntityMetadataInput,
|
||||
|
|
@ -39,6 +40,13 @@ export interface Entity<T = any> {
|
|||
* rolled into per-NounType statistics.
|
||||
*/
|
||||
subtype?: string
|
||||
/**
|
||||
* Visibility tier (reserved top-level field). Absent === `'public'` (counted and
|
||||
* returned everywhere). `'internal'` hides the entity from default `find()` / counts /
|
||||
* `stats()` (opt back in with `find({ includeInternal: true })`); `'system'` is Brainy
|
||||
* plumbing, hidden unless `find({ includeSystem: true })`. See {@link EntityVisibility}.
|
||||
*/
|
||||
visibility?: EntityVisibility
|
||||
/** Opaque content — used for embeddings and semantic search. Not indexed by MetadataIndex. */
|
||||
data?: any
|
||||
/** User-defined structured fields — indexed and queryable via `where` filters. */
|
||||
|
|
@ -90,6 +98,14 @@ export interface Relation<T = any> {
|
|||
* the column-store directly.
|
||||
*/
|
||||
subtype?: string
|
||||
/**
|
||||
* Visibility tier (reserved top-level field), the verb mirror of `Entity.visibility`.
|
||||
* Absent === `'public'` (counted and returned everywhere). `'internal'` hides the edge
|
||||
* from default `related()` / counts / `stats()` (opt back in with
|
||||
* `related({ includeInternal: true })`); `'system'` is Brainy plumbing. See
|
||||
* {@link EntityVisibility}.
|
||||
*/
|
||||
visibility?: EntityVisibility
|
||||
/** Connection strength (0-1, default: 1.0) */
|
||||
weight?: number
|
||||
/** Opaque content for the relationship (overrides auto-computed vector if provided) */
|
||||
|
|
@ -135,6 +151,7 @@ export interface Result<T = any> {
|
|||
// Convenience: Common entity fields flattened to top level
|
||||
type?: NounType // Entity type (from entity.type)
|
||||
subtype?: string // Per-product sub-classification (from entity.subtype)
|
||||
visibility?: EntityVisibility // Visibility tier (from entity.visibility); absent === 'public'
|
||||
metadata?: T // Entity metadata (from entity.metadata)
|
||||
data?: any // Entity data (from entity.data)
|
||||
confidence?: number // Type classification confidence (from entity.confidence)
|
||||
|
|
@ -289,16 +306,25 @@ export interface AddParams<T = any> {
|
|||
* aggregation on the standard-field fast path.
|
||||
*/
|
||||
subtype?: string
|
||||
/**
|
||||
* Visibility tier (reserved top-level field). Omit (or pass `'public'`) for normal
|
||||
* data that is counted and returned everywhere. Pass `'internal'` for app-internal
|
||||
* data that should be hidden from default `find()` / counts / `stats()` but stay
|
||||
* retrievable via `find({ includeInternal: true })`. The `'system'` tier is reserved
|
||||
* for Brainy's own plumbing and is intentionally not accepted here. Stored only when
|
||||
* not `'public'`.
|
||||
*/
|
||||
visibility?: 'public' | 'internal'
|
||||
/**
|
||||
* Structured queryable fields — indexed by MetadataIndex, used in `where` filters.
|
||||
*
|
||||
* Reserved entity fields (`RESERVED_ENTITY_FIELDS` — `noun`, `subtype`, `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.
|
||||
* 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.
|
||||
*/
|
||||
metadata?: EntityMetadataInput<T>
|
||||
/** Custom entity ID (auto-generated UUID v4 if not provided) */
|
||||
|
|
@ -330,11 +356,18 @@ export interface UpdateParams<T = any> {
|
|||
data?: any // New content to re-embed
|
||||
type?: NounType // Change type
|
||||
subtype?: string // Change subtype (set to '' or null-equivalent via dedicated unset is future work)
|
||||
/**
|
||||
* Change the visibility tier. Omit to preserve the existing value. Toggling between
|
||||
* `'public'` and `'internal'` moves the entity in/out of the default-visible counts and
|
||||
* `find()` results. The `'system'` tier is Brainy-internal (e.g. re-asserted on the VFS
|
||||
* root during repair) — consumer code should only ever use `'public'` / `'internal'`.
|
||||
*/
|
||||
visibility?: EntityVisibility
|
||||
/**
|
||||
* Metadata fields to merge (or replace when `merge: false`). Reserved entity
|
||||
* fields (`RESERVED_ENTITY_FIELDS`) may NOT appear here — `confidence` /
|
||||
* `weight` / `subtype` have dedicated params on this call, and the rest are
|
||||
* system-managed. A literal reserved key is a compile error; untyped callers
|
||||
* `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).
|
||||
*/
|
||||
|
|
@ -375,14 +408,23 @@ export interface RelateParams<T = any> {
|
|||
* (`related({ verb, subtype })`) and aggregation (`groupBy:['subtype']`).
|
||||
*/
|
||||
subtype?: string
|
||||
/**
|
||||
* Visibility tier (reserved top-level field), the verb mirror of `AddParams.visibility`.
|
||||
* Omit (or pass `'public'`) for normal edges that are counted and returned everywhere.
|
||||
* Pass `'internal'` for app-internal edges hidden from default `related()` / counts /
|
||||
* `stats()` but retrievable via `related({ includeInternal: true })`. The `'system'`
|
||||
* tier is reserved for Brainy's own plumbing and is not accepted here. Stored only when
|
||||
* not `'public'`.
|
||||
*/
|
||||
visibility?: 'public' | 'internal'
|
||||
/** Connection strength (0-1, default: 1.0) */
|
||||
weight?: number
|
||||
/** 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`, `createdAt`, `updatedAt`,
|
||||
* `confidence`, `weight`, `service`, `data`, `createdBy`, `_rev`) may NOT
|
||||
* (`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.
|
||||
*/
|
||||
|
|
@ -404,6 +446,12 @@ export interface UpdateRelationParams<T = any> {
|
|||
id: string // Relation to update
|
||||
type?: VerbType // Change verb type
|
||||
subtype?: string // Change sub-classification (omit to preserve existing)
|
||||
/**
|
||||
* Change the visibility tier. Omit to preserve the existing value. The verb mirror of
|
||||
* `UpdateParams.visibility`; `'system'` is Brainy-internal — consumer code should only
|
||||
* use `'public'` / `'internal'`.
|
||||
*/
|
||||
visibility?: EntityVisibility
|
||||
weight?: number // New weight
|
||||
confidence?: number // New confidence (0-1)
|
||||
data?: any // New content
|
||||
|
|
@ -448,7 +496,24 @@ export interface FindParams<T = any> {
|
|||
subtype?: string | string[]
|
||||
/** Metadata filters using BFO operators (e.g., `{ year: { greaterThan: 2020 } }`) */
|
||||
where?: Partial<T>
|
||||
|
||||
|
||||
// Visibility
|
||||
/**
|
||||
* Also return `visibility: 'internal'` entities. By default `find()` returns ONLY
|
||||
* public entities (those with no `visibility` field, or `visibility: 'public'`);
|
||||
* app-internal entities are hidden. Set `true` to include them. Applied as a hard
|
||||
* candidate filter, so `limit` / `offset` stay correct. Does NOT include `'system'`
|
||||
* entities — use `includeSystem` for those.
|
||||
*/
|
||||
includeInternal?: boolean
|
||||
/**
|
||||
* Also return `visibility: 'system'` entities (Brainy's own plumbing, e.g. the VFS
|
||||
* root). Hidden by default and even when `includeInternal` is set. Set `true` only
|
||||
* when you specifically need to see system entities. Applied as a hard candidate
|
||||
* filter, so `limit` / `offset` stay correct.
|
||||
*/
|
||||
includeSystem?: boolean
|
||||
|
||||
// Graph Intelligence
|
||||
connected?: GraphConstraints
|
||||
|
||||
|
|
@ -609,6 +674,25 @@ export interface RelatedParams {
|
|||
*/
|
||||
subtype?: string | string[]
|
||||
|
||||
/**
|
||||
* Also return `visibility: 'internal'` relationships.
|
||||
*
|
||||
* By default `related()` returns ONLY public relationships (those with no
|
||||
* `visibility` field, or `visibility: 'public'`); app-internal edges are hidden.
|
||||
* Set `true` to include them. Applied as a hard candidate filter so `limit` /
|
||||
* `offset` stay correct. Does NOT include `'system'` edges — use `includeSystem`.
|
||||
*/
|
||||
includeInternal?: boolean
|
||||
|
||||
/**
|
||||
* Also return `visibility: 'system'` relationships (Brainy's own plumbing).
|
||||
*
|
||||
* Hidden by default and even when `includeInternal` is set. Set `true` only when
|
||||
* you specifically need system edges. Applied as a hard candidate filter so
|
||||
* `limit` / `offset` stay correct.
|
||||
*/
|
||||
includeSystem?: boolean
|
||||
|
||||
/**
|
||||
* Maximum number of results to return
|
||||
*
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
* |-----|----------------------|
|
||||
* | `noun` | the `type` param of `add()` / `update()` (stored under the key `noun`) |
|
||||
* | `subtype` | the `subtype` param |
|
||||
* | `visibility` | the `visibility` param (`'public'` \| `'internal'`; `'system'` is Brainy-only) |
|
||||
* | `createdAt` | system-managed — set once at `add()` time |
|
||||
* | `updatedAt` | system-managed — set on every write |
|
||||
* | `confidence` | the `confidence` param |
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
export const RESERVED_ENTITY_FIELDS = [
|
||||
'noun',
|
||||
'subtype',
|
||||
'visibility',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'confidence',
|
||||
|
|
@ -78,6 +80,7 @@ export type ReservedEntityField = (typeof RESERVED_ENTITY_FIELDS)[number]
|
|||
* |-----|----------------------|
|
||||
* | `verb` | the `type` param of `relate()` / `updateRelation()` (stored under the key `verb`) |
|
||||
* | `subtype` | the `subtype` param |
|
||||
* | `visibility` | the `visibility` param (`'public'` \| `'internal'`; `'system'` is Brainy-only) |
|
||||
* | `createdAt` | system-managed — set once at `relate()` time |
|
||||
* | `updatedAt` | system-managed — set on every write |
|
||||
* | `confidence` | the `confidence` param |
|
||||
|
|
@ -90,6 +93,7 @@ export type ReservedEntityField = (typeof RESERVED_ENTITY_FIELDS)[number]
|
|||
export const RESERVED_RELATION_FIELDS = [
|
||||
'verb',
|
||||
'subtype',
|
||||
'visibility',
|
||||
'createdAt',
|
||||
'updatedAt',
|
||||
'confidence',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue