2026-01-20 16:21:11 -08:00
/ * *
* Google Sheets Integration
*
* Provides REST API endpoints optimized for Google Apps Script to enable
* two - way sync between Brainy and Google Sheets .
*
* Features :
* - Custom function support ( = BRAINY_QUERY ( ) , = BRAINY_ADD ( ) )
* - Real - time updates via SSE subscription
* - Batch operations for performance
* - Simple authentication ( API key or Bearer token )
*
* Zero external dependencies - works in all environments .
* /
import { IntegrationBase , HTTPIntegration } from '../core/IntegrationBase.js'
import { IntegrationConfig } from '../core/types.js'
import { Entity , FindParams } from '../../types/brainy.types.js'
import { NounType , VerbType } from '../../types/graphTypes.js'
/ * *
* Google Sheets integration configuration
* /
export interface GoogleSheetsConfig extends IntegrationConfig {
/** Base path for API routes (default: '/sheets') */
basePath? : string
/** Port to listen on (only for standalone) */
port? : number
/** Maximum results per query (default: 1000) */
maxResults? : number
/** Default query limit (default: 100) */
defaultLimit? : number
/** Allow write operations (default: true) */
allowWrite? : boolean
/** CORS origins to allow (default: Google Sheets) */
allowedOrigins? : string [ ]
}
/ * *
* Sheets API request
* /
interface SheetsRequest {
method : string
path : string
query : Record < string , string >
body? : any
headers : Record < string , string >
}
/ * *
* Sheets API response
* /
interface SheetsResponse {
status : number
headers : Record < string , string >
body : any
}
/ * *
* Google Sheets Integration
*
* Provides a simple REST API optimized for Google Apps Script custom functions .
*
* Endpoints :
* - GET / sheets / query - Query entities ( for = BRAINY_QUERY ( ) )
* - GET / sheets / entity / : id - Get single entity ( for = BRAINY_GET ( ) )
* - GET / sheets / similar - Semantic search ( for = BRAINY_SIMILAR ( ) )
* - GET / sheets / relations - Get relationships ( for = BRAINY_RELATIONS ( ) )
* - POST / sheets / add - Add entity ( for sidebar )
* - POST / sheets / batch - Batch operations ( for range sync )
* - GET / sheets / schema - Get type schema ( for sidebar dropdown )
* - GET / sheets / stream - SSE for real - time updates
*
* Response Format ( optimized for Sheets ) :
* ` ` ` json
* {
* "headers" : [ "Id" , "Type" , "Name" , "Email" ] ,
* "rows" : [
* [ "uuid-1" , "person" , "John Doe" , "john@example.com" ] ,
* [ "uuid-2" , "person" , "Jane Doe" , "jane@example.com" ]
* ] ,
* "count" : 2 ,
* "hasMore" : false
* }
* ` ` `
*
* @example
* ` ` ` typescript
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
* const sheets = new GoogleSheetsIntegration ( {
2026-01-20 16:21:11 -08:00
* basePath : '/sheets' ,
* maxResults : 1000
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
* } )
* await sheets . initialize ( )
2026-01-20 16:21:11 -08:00
* ` ` `
* /
export class GoogleSheetsIntegration
extends IntegrationBase
implements HTTPIntegration
{
readonly name = 'sheets'
port : number
basePath : string
private sheetsConfig : GoogleSheetsConfig & {
enabled : boolean
basePath : string
port : number
maxResults : number
defaultLimit : number
allowWrite : boolean
allowedOrigins : string [ ]
}
private sseClients : Map < string , ( event : string , data : any ) = > void > =
new Map ( )
constructor ( config? : GoogleSheetsConfig ) {
super ( config )
this . sheetsConfig = {
enabled : config?.enabled ? ? true ,
basePath : config?.basePath ? ? '/sheets' ,
port : config?.port ? ? 0 ,
maxResults : config?.maxResults ? ? 1000 ,
defaultLimit : config?.defaultLimit ? ? 100 ,
allowWrite : config?.allowWrite ? ? true ,
allowedOrigins : config?.allowedOrigins ? ? [
'https://docs.google.com' ,
'https://script.google.com'
] ,
rateLimit : config?.rateLimit ,
auth : config?.auth ,
cors : config?.cors ? ? {
origin : [
'https://docs.google.com' ,
'https://script.google.com' ,
'*'
] ,
methods : [ 'GET' , 'POST' , 'OPTIONS' ] ,
credentials : true
}
}
this . port = this . sheetsConfig . port
this . basePath = this . sheetsConfig . basePath
}
protected async onStart ( ) : Promise < void > {
// Subscribe to changes for real-time sync
this . subscribeToChanges (
{ entityTypes : [ 'noun' , 'verb' ] } ,
( event ) = > {
// Broadcast to all SSE clients
this . broadcastSSE ( 'change' , {
type : event . entityType ,
operation : event.operation ,
entityId : event.entityId ,
timestamp : event.timestamp
} )
}
)
this . log ( 'Google Sheets integration started' )
}
protected async onStop ( ) : Promise < void > {
// Close all SSE connections
this . sseClients . clear ( )
this . log ( 'Google Sheets integration stopped' )
}
/ * *
* Handle a Sheets API request
* /
async handleRequest ( request : SheetsRequest ) : Promise < SheetsResponse > {
this . recordRequest ( )
try {
const { method , path } = request
const relativePath = path . startsWith ( this . basePath )
? path . slice ( this . basePath . length )
: path
// CORS preflight
if ( method === 'OPTIONS' ) {
return this . corsResponse ( )
}
// Route the request
if ( method === 'GET' ) {
if ( relativePath === '/query' || relativePath === '' ) {
return this . handleQuery ( request )
}
if ( relativePath . startsWith ( '/entity/' ) ) {
const id = relativePath . slice ( '/entity/' . length )
return this . handleGetEntity ( id )
}
if ( relativePath === '/similar' ) {
return this . handleSimilar ( request )
}
if ( relativePath === '/relations' ) {
return this . handleRelations ( request )
}
if ( relativePath === '/schema' ) {
return this . handleSchema ( )
}
if ( relativePath === '/stream' ) {
return this . handleStream ( request )
}
if ( relativePath === '/health' ) {
return this . handleHealth ( )
}
}
if ( method === 'POST' && this . sheetsConfig . allowWrite ) {
if ( relativePath === '/add' ) {
return this . handleAdd ( request )
}
if ( relativePath === '/update' ) {
return this . handleUpdate ( request )
}
if ( relativePath === '/delete' ) {
return this . handleDelete ( request )
}
if ( relativePath === '/batch' ) {
return this . handleBatch ( request )
}
if ( relativePath === '/relate' ) {
return this . handleRelate ( request )
}
}
return this . errorResponse ( 404 , 'Not Found' )
} catch ( error : any ) {
this . recordError ( error )
return this . errorResponse ( 500 , error . message )
}
}
/ * *
* Get registered routes
* /
getRoutes ( ) : Array < { method : string ; path : string ; description : string } > {
const routes = [
{ method : 'GET' , path : ` ${ this . basePath } /query ` , description : 'Query entities' } ,
{ method : 'GET' , path : ` ${ this . basePath } /entity/:id ` , description : 'Get entity by ID' } ,
{ method : 'GET' , path : ` ${ this . basePath } /similar ` , description : 'Semantic search' } ,
{ method : 'GET' , path : ` ${ this . basePath } /relations ` , description : 'Get relationships' } ,
{ method : 'GET' , path : ` ${ this . basePath } /schema ` , description : 'Get schema' } ,
{ method : 'GET' , path : ` ${ this . basePath } /stream ` , description : 'SSE stream' } ,
{ method : 'GET' , path : ` ${ this . basePath } /health ` , description : 'Health check' }
]
if ( this . sheetsConfig . allowWrite ) {
routes . push (
{ method : 'POST' , path : ` ${ this . basePath } /add ` , description : 'Add entity' } ,
{ method : 'POST' , path : ` ${ this . basePath } /update ` , description : 'Update entity' } ,
{ method : 'POST' , path : ` ${ this . basePath } /delete ` , description : 'Delete entity' } ,
{ method : 'POST' , path : ` ${ this . basePath } /batch ` , description : 'Batch operations' } ,
{ method : 'POST' , path : ` ${ this . basePath } /relate ` , description : 'Create relationship' }
)
}
return routes
}
/ * *
* Register an SSE client
* /
registerSSEClient (
clientId : string ,
sendFn : ( event : string , data : any ) = > void
) : ( ) = > void {
this . sseClients . set ( clientId , sendFn )
return ( ) = > this . sseClients . delete ( clientId )
}
/ * *
* Get manifest
* /
refactor: remove augmentation system and semantic type matching
Remove the entire augmentation pipeline infrastructure (52 files,
~15,000 lines) and the semantic type matching system. These were
unused middleware layers adding complexity without value.
What was removed:
- src/augmentations/ directory (all augmentation implementations)
- src/augmentationManager.ts (pipeline orchestrator)
- src/types/augmentations.ts, src/types/pipelineTypes.ts
- src/shared/default-augmentations.ts
- Semantic type suggestion (BrainyTypes.suggestNoun/suggestVerb)
- src/utils/typeMatching/ (embedding-based type matcher)
What was preserved by relocating:
- Import handlers (CSV, PDF, Excel) -> src/importers/handlers/
- NeuralImportAugmentation -> src/cortex/neuralImportAugmentation.ts
- Type matching utilities -> heuristic inference in consumers
What was simplified:
- brainy.ts: operations call storage directly (no execute() wrapper)
- IntegrationBase: standalone class (no BaseAugmentation parent)
- BrainyTypes: validation-only (nouns, verbs, isValid*, get*)
- Pipeline: direct execution (no augmentation interception)
- index.ts: removed TypeSuggestion, suggestType exports
- package.json: removed stale types/augmentations export
Build passes, 1176 tests pass, 0 failures.
2026-02-01 10:48:56 -08:00
getManifest ( ) : Record < string , any > {
2026-01-20 16:21:11 -08:00
return {
id : 'sheets' ,
name : 'Google Sheets Integration' ,
version : '1.0.0' ,
description : 'Two-way sync between Brainy and Google Sheets' ,
longDescription :
'Enables real-time bidirectional synchronization with Google Sheets. Use custom functions like =BRAINY_QUERY() directly in cells, or the sidebar for browsing and editing.' ,
category : 'integration' ,
status : 'stable' ,
configSchema : {
type : 'object' ,
properties : {
enabled : { type : 'boolean' , default : true } ,
basePath : { type : 'string' , default : '/sheets' } ,
maxResults : { type : 'number' , default : 1000 } ,
defaultLimit : { type : 'number' , default : 100 } ,
allowWrite : { type : 'boolean' , default : true }
}
} ,
configDefaults : {
enabled : true ,
basePath : '/sheets' ,
maxResults : 1000 ,
defaultLimit : 100 ,
allowWrite : true
} ,
features : [
'Custom functions (=BRAINY_QUERY)' ,
'Real-time sync via SSE' ,
'Batch operations' ,
'Semantic search support' ,
'Type schema discovery'
] ,
keywords : [ 'google-sheets' , 'spreadsheet' , 'sync' , 'real-time' ]
}
}
// Route handlers
private async handleQuery ( request : SheetsRequest ) : Promise < SheetsResponse > {
const { query } = request
// Build find params
const findParams : FindParams = {
limit : Math.min (
parseInt ( query . limit ) || this . sheetsConfig . defaultLimit ,
this . sheetsConfig . maxResults
)
}
// Query string (semantic search)
if ( query . q ) {
findParams . query = query . q
}
// Type filter
if ( query . type ) {
const types = query . type . split ( ',' ) as NounType [ ]
findParams . type = types . length === 1 ? types [ 0 ] : types
}
// Offset pagination
if ( query . offset ) {
findParams . offset = parseInt ( query . offset )
}
// Sort
if ( query . orderBy ) {
findParams . orderBy = query . orderBy
findParams . order = ( query . order as 'asc' | 'desc' ) || 'desc'
}
// Execute query
const entities = await this . queryEntities ( findParams )
// Convert to sheets format
return this . entitiesToSheetsResponse ( entities )
}
private async handleGetEntity ( id : string ) : Promise < SheetsResponse > {
const entity = await this . getEntity ( id )
if ( ! entity ) {
return this . errorResponse ( 404 , 'Entity not found' )
}
return this . entitiesToSheetsResponse ( [ entity ] )
}
private async handleSimilar ( request : SheetsRequest ) : Promise < SheetsResponse > {
if ( ! this . context ) {
return this . errorResponse ( 500 , 'Not initialized' )
}
const { query } = request
if ( ! query . q && ! query . to ) {
return this . errorResponse ( 400 , 'Missing q or to parameter' )
}
const limit = Math . min (
parseInt ( query . limit ) || 10 ,
this . sheetsConfig . maxResults
)
// Semantic similarity search
let results : any [ ]
if ( query . to ) {
// Similar to entity
results = await this . context . brain . similar ( {
to : query.to ,
limit ,
threshold : query.threshold ? parseFloat ( query . threshold ) : undefined
} )
} else {
// Similar to query text
results = await this . context . brain . find ( {
query : query.q ,
limit
} )
}
const entities = results . map ( ( r : any ) = > r . entity || r )
return this . entitiesToSheetsResponse ( entities )
}
private async handleRelations (
request : SheetsRequest
) : Promise < SheetsResponse > {
const { query } = request
const params : any = {
limit : Math.min (
parseInt ( query . limit ) || 100 ,
this . sheetsConfig . maxResults
)
}
if ( query . from ) params . from = query . from
if ( query . to ) params . to = query . to
if ( query . type ) params . type = query . type as VerbType
const relations = await this . queryRelations ( params )
// Convert to sheets format
const headers = [
'Id' ,
'FromId' ,
'ToId' ,
'Type' ,
'Weight' ,
'Confidence' ,
'CreatedAt'
]
const rows = relations . map ( ( r ) = > [
r . id ,
r . from ,
r . to ,
r . type ,
r . weight ? ? 1 ,
r . confidence ? ? 1 ,
new Date ( r . createdAt ) . toISOString ( )
] )
return this . jsonResponse ( {
headers ,
rows ,
count : rows.length ,
hasMore : false
} )
}
private handleSchema ( ) : SheetsResponse {
return this . jsonResponse ( {
nounTypes : Object.values ( NounType ) ,
verbTypes : Object.values ( VerbType ) ,
entityFields : [ 'id' , 'type' , 'data' , 'metadata' , 'confidence' , 'weight' , 'service' , 'createdAt' , 'updatedAt' ] ,
commonMetadataFields : [ 'name' , 'title' , 'description' , 'email' , 'url' , 'tags' , 'category' , 'status' , 'priority' ]
} )
}
private handleStream ( _request : SheetsRequest ) : SheetsResponse {
// This returns headers for SSE - actual streaming is handled by the HTTP server
return {
status : 200 ,
headers : {
'Content-Type' : 'text/event-stream' ,
'Cache-Control' : 'no-cache' ,
Connection : 'keep-alive' ,
'Access-Control-Allow-Origin' : '*'
} ,
body : 'sse' // Signal to HTTP server to handle as SSE
}
}
private handleHealth ( ) : SheetsResponse {
return this . jsonResponse ( {
status : 'ok' ,
integration : 'sheets' ,
uptime : this.startedAt ? Date . now ( ) - this . startedAt : 0 ,
requests : this.requestCount
} )
}
private async handleAdd ( request : SheetsRequest ) : Promise < SheetsResponse > {
if ( ! this . context ) {
return this . errorResponse ( 500 , 'Not initialized' )
}
const { body } = request
if ( ! body || ! body . type ) {
return this . errorResponse ( 400 , 'Missing required field: type' )
}
fix: internal subtype consistency + brain.audit() diagnostic + improved enforcement errors
Brainy 7.30 shipped opt-in subtype enforcement; SDK 3.20.0 then registered
SDK_CORE_VOCABULARY on every consumer's brain (Event, Collection, Message,
Contract, Media, Document NounTypes). On 2026-06-08 Venue's /book flow went 500
because their brain.add({ type: NounType.Event, ... }) call sites lacked
subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk internal write
paths that also omit subtype — any consumer running the same vocabulary would
have hit Brainy's infrastructure paths next. 7.30.1 closes both gaps before
8.0 makes strict mode the default.
Additive across the board. Zero behavior change for consumers not using strict
mode. Every change is JS-side — Cortex needs no work for 7.30.1.
NEW — brain.audit() diagnostic
- Read-only method walking storage.getNouns() / getVerbs() pagination
- Returns { entitiesWithoutSubtype: { type: count }, relationshipsWithoutSubtype,
total, scanned, recommendation }
- VFS infrastructure entities excluded by default (they bypass enforcement via
isVFSEntity marker); pass { includeVFS: true } to surface them
- The companion to migrateField (7.x) and fillSubtypes (8.0): tells consumers
exactly what would break under strict enforcement, deterministically
NEW — Improved enforcement error messages
- Caller's source location extracted from Error().stack so users see their own
call site, not a Brainy internal frame
- Specific guidance branches: registered vocabulary → "Pass one of: a, b, c";
brain-wide strict mode → mentions the except clause; otherwise → registration
recipe via brain.requireSubtype()
- Documentation link to the canonical migration recipe
- Same shape for noun and verb enforcement
NEW — CLI --subtype flag
- brainy add and brainy relate gain -s/--subtype <value>
- Defaults to 'cli-add' / 'cli-relate' so the CLI works against strict-mode
brains without the user needing to know the vocabulary in advance
INTERNAL — every Brainy write path now sets subtype
- VFS Contains edges (5 sites at lines 503/905/1694/1772/1886) → 'vfs-contains'
- VFS symlink entity → 'vfs-symlink' (NEW — distinct from 'vfs-file')
- VFS copy-file → preserves source subtype, falls back to 'vfs-file'
- VFS symlink also adopts the isVFSEntity infrastructure marker so it bypasses
enforcement in strict mode
- Aggregation materializer (Measurement entities) → 'materialized-aggregate'
- ImportCoordinator (3 sites): document → 'import-source'; entities →
options.defaultSubtype ?? 'imported'; placeholder → 'import-placeholder'
- SmartImportOrchestrator (4 entity sites + 2 batch relate sites): same
precedence (extractor → options.defaultSubtype → 'imported')
- EntityDeduplicator → candidate.subtype ?? 'imported'
- UniversalImportAPI → extractor → 'extracted' for both entities and relations
- NeuralImport → adds defaultSubtype to NeuralImportOptions; precedence same
- GoogleSheetsIntegration → request body 'subtype' ?? 'imported-from-sheets'
- ODataIntegration → request body 'Subtype' ?? 'imported-from-odata'
- MCP client message storage → 'mcp-message' (also fixes pre-existing missing
data field and missing type by aliasing from the prior text field)
Side-effect fix: storage.getNouns() paginated now surfaces subtype to top-level
- Single-noun getNoun() already did this in 7.30; the paginated path was missed
- Without this fix brain.audit() saw missing subtype on entities that actually
had one (caught by the strict-mode self-test before release)
NEW — tests/integration/strict-mode-self-test.test.ts (13 tests)
- Creates a brain under the exact SDK_CORE_VOCABULARY shape Venue hit + brain-
wide strict mode
- Exercises every internal Brainy path: VFS root + mkdir + writeFile + cp + mv
+ ln + symlink; aggregation engine; audit diagnostic with includeVFS toggle
- Validates error message UX: caller location, vocabulary guidance, brain-wide
strict mode guidance, off-vocabulary value reporting
Docs
- New "Strict mode in practice" section in docs/guides/subtypes-and-facets.md
covering the SDK_CORE_VOCABULARY pattern, 4-step migration recipe
(audit → migrateField → hand-fix → re-audit), the Brainy-internal label
reference table, and an 8.0 forward-look on fillSubtypes()
- docs/api/README.md: new audit() entry, strict-mode tips on add() and relate()
- RELEASES.md: full 7.30.1 entry
Cortex parity (forward-looking, not blocking 7.30.1)
- 6th open question added to .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md: native
fast path for audit() and fillSubtypes() via column-store null-subtype
bitmap for billion-scale brains
- Cortex should add a parity test mirroring strict-mode-self-test.test.ts
against their native paths to catch any latent bug where native writes
bypass JS validation
- Brainy-internal subtype labels become a documented part of the 8.0 contract
(useful for Cortex telemetry surfacing Brainy-managed infrastructure %)
Verification
- npx tsc --noEmit: clean
- npm test: 1468/1468 unit
- 7.29 noun integration suite: 26/26 (no regression)
- 7.30 verb subtype + enforcement integration suite: 30/30 (no regression)
- New strict-mode-self-test integration suite: 13/13
- npm run build: clean
- Closed-source product reference audit: clean
Addresses VE-SUBTYPE-MIGRATION (Venue's reported request) and ships internal
labels Venue did NOT ask for but that would have broken them next under their
own vocabulary registration.
2026-06-08 11:31:47 -07:00
// Honor caller-supplied `subtype` from the Sheets request; fall back to
// the integration-default `'imported-from-sheets'` so enforcement
// consumers don't get rejected on Sheets-driven writes (added 7.30.1).
2026-01-20 16:21:11 -08:00
const entity = await this . context . brain . add ( {
type : body . type as NounType ,
fix: internal subtype consistency + brain.audit() diagnostic + improved enforcement errors
Brainy 7.30 shipped opt-in subtype enforcement; SDK 3.20.0 then registered
SDK_CORE_VOCABULARY on every consumer's brain (Event, Collection, Message,
Contract, Media, Document NounTypes). On 2026-06-08 Venue's /book flow went 500
because their brain.add({ type: NounType.Event, ... }) call sites lacked
subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk internal write
paths that also omit subtype — any consumer running the same vocabulary would
have hit Brainy's infrastructure paths next. 7.30.1 closes both gaps before
8.0 makes strict mode the default.
Additive across the board. Zero behavior change for consumers not using strict
mode. Every change is JS-side — Cortex needs no work for 7.30.1.
NEW — brain.audit() diagnostic
- Read-only method walking storage.getNouns() / getVerbs() pagination
- Returns { entitiesWithoutSubtype: { type: count }, relationshipsWithoutSubtype,
total, scanned, recommendation }
- VFS infrastructure entities excluded by default (they bypass enforcement via
isVFSEntity marker); pass { includeVFS: true } to surface them
- The companion to migrateField (7.x) and fillSubtypes (8.0): tells consumers
exactly what would break under strict enforcement, deterministically
NEW — Improved enforcement error messages
- Caller's source location extracted from Error().stack so users see their own
call site, not a Brainy internal frame
- Specific guidance branches: registered vocabulary → "Pass one of: a, b, c";
brain-wide strict mode → mentions the except clause; otherwise → registration
recipe via brain.requireSubtype()
- Documentation link to the canonical migration recipe
- Same shape for noun and verb enforcement
NEW — CLI --subtype flag
- brainy add and brainy relate gain -s/--subtype <value>
- Defaults to 'cli-add' / 'cli-relate' so the CLI works against strict-mode
brains without the user needing to know the vocabulary in advance
INTERNAL — every Brainy write path now sets subtype
- VFS Contains edges (5 sites at lines 503/905/1694/1772/1886) → 'vfs-contains'
- VFS symlink entity → 'vfs-symlink' (NEW — distinct from 'vfs-file')
- VFS copy-file → preserves source subtype, falls back to 'vfs-file'
- VFS symlink also adopts the isVFSEntity infrastructure marker so it bypasses
enforcement in strict mode
- Aggregation materializer (Measurement entities) → 'materialized-aggregate'
- ImportCoordinator (3 sites): document → 'import-source'; entities →
options.defaultSubtype ?? 'imported'; placeholder → 'import-placeholder'
- SmartImportOrchestrator (4 entity sites + 2 batch relate sites): same
precedence (extractor → options.defaultSubtype → 'imported')
- EntityDeduplicator → candidate.subtype ?? 'imported'
- UniversalImportAPI → extractor → 'extracted' for both entities and relations
- NeuralImport → adds defaultSubtype to NeuralImportOptions; precedence same
- GoogleSheetsIntegration → request body 'subtype' ?? 'imported-from-sheets'
- ODataIntegration → request body 'Subtype' ?? 'imported-from-odata'
- MCP client message storage → 'mcp-message' (also fixes pre-existing missing
data field and missing type by aliasing from the prior text field)
Side-effect fix: storage.getNouns() paginated now surfaces subtype to top-level
- Single-noun getNoun() already did this in 7.30; the paginated path was missed
- Without this fix brain.audit() saw missing subtype on entities that actually
had one (caught by the strict-mode self-test before release)
NEW — tests/integration/strict-mode-self-test.test.ts (13 tests)
- Creates a brain under the exact SDK_CORE_VOCABULARY shape Venue hit + brain-
wide strict mode
- Exercises every internal Brainy path: VFS root + mkdir + writeFile + cp + mv
+ ln + symlink; aggregation engine; audit diagnostic with includeVFS toggle
- Validates error message UX: caller location, vocabulary guidance, brain-wide
strict mode guidance, off-vocabulary value reporting
Docs
- New "Strict mode in practice" section in docs/guides/subtypes-and-facets.md
covering the SDK_CORE_VOCABULARY pattern, 4-step migration recipe
(audit → migrateField → hand-fix → re-audit), the Brainy-internal label
reference table, and an 8.0 forward-look on fillSubtypes()
- docs/api/README.md: new audit() entry, strict-mode tips on add() and relate()
- RELEASES.md: full 7.30.1 entry
Cortex parity (forward-looking, not blocking 7.30.1)
- 6th open question added to .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md: native
fast path for audit() and fillSubtypes() via column-store null-subtype
bitmap for billion-scale brains
- Cortex should add a parity test mirroring strict-mode-self-test.test.ts
against their native paths to catch any latent bug where native writes
bypass JS validation
- Brainy-internal subtype labels become a documented part of the 8.0 contract
(useful for Cortex telemetry surfacing Brainy-managed infrastructure %)
Verification
- npx tsc --noEmit: clean
- npm test: 1468/1468 unit
- 7.29 noun integration suite: 26/26 (no regression)
- 7.30 verb subtype + enforcement integration suite: 30/30 (no regression)
- New strict-mode-self-test integration suite: 13/13
- npm run build: clean
- Closed-source product reference audit: clean
Addresses VE-SUBTYPE-MIGRATION (Venue's reported request) and ships internal
labels Venue did NOT ask for but that would have broken them next under their
own vocabulary registration.
2026-06-08 11:31:47 -07:00
subtype : ( body . subtype as string | undefined ) ? ? 'imported-from-sheets' ,
2026-01-20 16:21:11 -08:00
data : body.data ,
metadata : body.metadata ,
confidence : body.confidence ,
weight : body.weight ,
service : body.service
} )
return this . entitiesToSheetsResponse ( [ entity ] )
}
private async handleUpdate ( request : SheetsRequest ) : Promise < SheetsResponse > {
if ( ! this . context ) {
return this . errorResponse ( 500 , 'Not initialized' )
}
const { body } = request
if ( ! body || ! body . id ) {
return this . errorResponse ( 400 , 'Missing required field: id' )
}
await this . context . brain . update ( {
id : body.id ,
data : body.data ,
metadata : body.metadata ,
type : body . type as NounType ,
confidence : body.confidence ,
weight : body.weight ,
merge : body.merge ? ? true
} )
const updated = await this . getEntity ( body . id )
return this . entitiesToSheetsResponse ( updated ? [ updated ] : [ ] )
}
private async handleDelete ( request : SheetsRequest ) : Promise < SheetsResponse > {
if ( ! this . context ) {
return this . errorResponse ( 500 , 'Not initialized' )
}
const { body } = request
if ( ! body || ! body . id ) {
return this . errorResponse ( 400 , 'Missing required field: id' )
}
2026-06-11 14:51:00 -07:00
await this . context . brain . remove ( body . id )
2026-01-20 16:21:11 -08:00
return this . jsonResponse ( { success : true , deleted : body.id } )
}
private async handleBatch ( request : SheetsRequest ) : Promise < SheetsResponse > {
if ( ! this . context ) {
return this . errorResponse ( 500 , 'Not initialized' )
}
const { body } = request
if ( ! body || ! Array . isArray ( body . operations ) ) {
return this . errorResponse ( 400 , 'Missing operations array' )
}
const results : any [ ] = [ ]
for ( const op of body . operations ) {
try {
switch ( op . action ) {
case 'add' :
fix: internal subtype consistency + brain.audit() diagnostic + improved enforcement errors
Brainy 7.30 shipped opt-in subtype enforcement; SDK 3.20.0 then registered
SDK_CORE_VOCABULARY on every consumer's brain (Event, Collection, Message,
Contract, Media, Document NounTypes). On 2026-06-08 Venue's /book flow went 500
because their brain.add({ type: NounType.Event, ... }) call sites lacked
subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk internal write
paths that also omit subtype — any consumer running the same vocabulary would
have hit Brainy's infrastructure paths next. 7.30.1 closes both gaps before
8.0 makes strict mode the default.
Additive across the board. Zero behavior change for consumers not using strict
mode. Every change is JS-side — Cortex needs no work for 7.30.1.
NEW — brain.audit() diagnostic
- Read-only method walking storage.getNouns() / getVerbs() pagination
- Returns { entitiesWithoutSubtype: { type: count }, relationshipsWithoutSubtype,
total, scanned, recommendation }
- VFS infrastructure entities excluded by default (they bypass enforcement via
isVFSEntity marker); pass { includeVFS: true } to surface them
- The companion to migrateField (7.x) and fillSubtypes (8.0): tells consumers
exactly what would break under strict enforcement, deterministically
NEW — Improved enforcement error messages
- Caller's source location extracted from Error().stack so users see their own
call site, not a Brainy internal frame
- Specific guidance branches: registered vocabulary → "Pass one of: a, b, c";
brain-wide strict mode → mentions the except clause; otherwise → registration
recipe via brain.requireSubtype()
- Documentation link to the canonical migration recipe
- Same shape for noun and verb enforcement
NEW — CLI --subtype flag
- brainy add and brainy relate gain -s/--subtype <value>
- Defaults to 'cli-add' / 'cli-relate' so the CLI works against strict-mode
brains without the user needing to know the vocabulary in advance
INTERNAL — every Brainy write path now sets subtype
- VFS Contains edges (5 sites at lines 503/905/1694/1772/1886) → 'vfs-contains'
- VFS symlink entity → 'vfs-symlink' (NEW — distinct from 'vfs-file')
- VFS copy-file → preserves source subtype, falls back to 'vfs-file'
- VFS symlink also adopts the isVFSEntity infrastructure marker so it bypasses
enforcement in strict mode
- Aggregation materializer (Measurement entities) → 'materialized-aggregate'
- ImportCoordinator (3 sites): document → 'import-source'; entities →
options.defaultSubtype ?? 'imported'; placeholder → 'import-placeholder'
- SmartImportOrchestrator (4 entity sites + 2 batch relate sites): same
precedence (extractor → options.defaultSubtype → 'imported')
- EntityDeduplicator → candidate.subtype ?? 'imported'
- UniversalImportAPI → extractor → 'extracted' for both entities and relations
- NeuralImport → adds defaultSubtype to NeuralImportOptions; precedence same
- GoogleSheetsIntegration → request body 'subtype' ?? 'imported-from-sheets'
- ODataIntegration → request body 'Subtype' ?? 'imported-from-odata'
- MCP client message storage → 'mcp-message' (also fixes pre-existing missing
data field and missing type by aliasing from the prior text field)
Side-effect fix: storage.getNouns() paginated now surfaces subtype to top-level
- Single-noun getNoun() already did this in 7.30; the paginated path was missed
- Without this fix brain.audit() saw missing subtype on entities that actually
had one (caught by the strict-mode self-test before release)
NEW — tests/integration/strict-mode-self-test.test.ts (13 tests)
- Creates a brain under the exact SDK_CORE_VOCABULARY shape Venue hit + brain-
wide strict mode
- Exercises every internal Brainy path: VFS root + mkdir + writeFile + cp + mv
+ ln + symlink; aggregation engine; audit diagnostic with includeVFS toggle
- Validates error message UX: caller location, vocabulary guidance, brain-wide
strict mode guidance, off-vocabulary value reporting
Docs
- New "Strict mode in practice" section in docs/guides/subtypes-and-facets.md
covering the SDK_CORE_VOCABULARY pattern, 4-step migration recipe
(audit → migrateField → hand-fix → re-audit), the Brainy-internal label
reference table, and an 8.0 forward-look on fillSubtypes()
- docs/api/README.md: new audit() entry, strict-mode tips on add() and relate()
- RELEASES.md: full 7.30.1 entry
Cortex parity (forward-looking, not blocking 7.30.1)
- 6th open question added to .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md: native
fast path for audit() and fillSubtypes() via column-store null-subtype
bitmap for billion-scale brains
- Cortex should add a parity test mirroring strict-mode-self-test.test.ts
against their native paths to catch any latent bug where native writes
bypass JS validation
- Brainy-internal subtype labels become a documented part of the 8.0 contract
(useful for Cortex telemetry surfacing Brainy-managed infrastructure %)
Verification
- npx tsc --noEmit: clean
- npm test: 1468/1468 unit
- 7.29 noun integration suite: 26/26 (no regression)
- 7.30 verb subtype + enforcement integration suite: 30/30 (no regression)
- New strict-mode-self-test integration suite: 13/13
- npm run build: clean
- Closed-source product reference audit: clean
Addresses VE-SUBTYPE-MIGRATION (Venue's reported request) and ships internal
labels Venue did NOT ask for but that would have broken them next under their
own vocabulary registration.
2026-06-08 11:31:47 -07:00
// Same subtype-precedence as the single-entity handler (7.30.1).
2026-01-20 16:21:11 -08:00
const added = await this . context . brain . add ( {
type : op . type as NounType ,
fix: internal subtype consistency + brain.audit() diagnostic + improved enforcement errors
Brainy 7.30 shipped opt-in subtype enforcement; SDK 3.20.0 then registered
SDK_CORE_VOCABULARY on every consumer's brain (Event, Collection, Message,
Contract, Media, Document NounTypes). On 2026-06-08 Venue's /book flow went 500
because their brain.add({ type: NounType.Event, ... }) call sites lacked
subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk internal write
paths that also omit subtype — any consumer running the same vocabulary would
have hit Brainy's infrastructure paths next. 7.30.1 closes both gaps before
8.0 makes strict mode the default.
Additive across the board. Zero behavior change for consumers not using strict
mode. Every change is JS-side — Cortex needs no work for 7.30.1.
NEW — brain.audit() diagnostic
- Read-only method walking storage.getNouns() / getVerbs() pagination
- Returns { entitiesWithoutSubtype: { type: count }, relationshipsWithoutSubtype,
total, scanned, recommendation }
- VFS infrastructure entities excluded by default (they bypass enforcement via
isVFSEntity marker); pass { includeVFS: true } to surface them
- The companion to migrateField (7.x) and fillSubtypes (8.0): tells consumers
exactly what would break under strict enforcement, deterministically
NEW — Improved enforcement error messages
- Caller's source location extracted from Error().stack so users see their own
call site, not a Brainy internal frame
- Specific guidance branches: registered vocabulary → "Pass one of: a, b, c";
brain-wide strict mode → mentions the except clause; otherwise → registration
recipe via brain.requireSubtype()
- Documentation link to the canonical migration recipe
- Same shape for noun and verb enforcement
NEW — CLI --subtype flag
- brainy add and brainy relate gain -s/--subtype <value>
- Defaults to 'cli-add' / 'cli-relate' so the CLI works against strict-mode
brains without the user needing to know the vocabulary in advance
INTERNAL — every Brainy write path now sets subtype
- VFS Contains edges (5 sites at lines 503/905/1694/1772/1886) → 'vfs-contains'
- VFS symlink entity → 'vfs-symlink' (NEW — distinct from 'vfs-file')
- VFS copy-file → preserves source subtype, falls back to 'vfs-file'
- VFS symlink also adopts the isVFSEntity infrastructure marker so it bypasses
enforcement in strict mode
- Aggregation materializer (Measurement entities) → 'materialized-aggregate'
- ImportCoordinator (3 sites): document → 'import-source'; entities →
options.defaultSubtype ?? 'imported'; placeholder → 'import-placeholder'
- SmartImportOrchestrator (4 entity sites + 2 batch relate sites): same
precedence (extractor → options.defaultSubtype → 'imported')
- EntityDeduplicator → candidate.subtype ?? 'imported'
- UniversalImportAPI → extractor → 'extracted' for both entities and relations
- NeuralImport → adds defaultSubtype to NeuralImportOptions; precedence same
- GoogleSheetsIntegration → request body 'subtype' ?? 'imported-from-sheets'
- ODataIntegration → request body 'Subtype' ?? 'imported-from-odata'
- MCP client message storage → 'mcp-message' (also fixes pre-existing missing
data field and missing type by aliasing from the prior text field)
Side-effect fix: storage.getNouns() paginated now surfaces subtype to top-level
- Single-noun getNoun() already did this in 7.30; the paginated path was missed
- Without this fix brain.audit() saw missing subtype on entities that actually
had one (caught by the strict-mode self-test before release)
NEW — tests/integration/strict-mode-self-test.test.ts (13 tests)
- Creates a brain under the exact SDK_CORE_VOCABULARY shape Venue hit + brain-
wide strict mode
- Exercises every internal Brainy path: VFS root + mkdir + writeFile + cp + mv
+ ln + symlink; aggregation engine; audit diagnostic with includeVFS toggle
- Validates error message UX: caller location, vocabulary guidance, brain-wide
strict mode guidance, off-vocabulary value reporting
Docs
- New "Strict mode in practice" section in docs/guides/subtypes-and-facets.md
covering the SDK_CORE_VOCABULARY pattern, 4-step migration recipe
(audit → migrateField → hand-fix → re-audit), the Brainy-internal label
reference table, and an 8.0 forward-look on fillSubtypes()
- docs/api/README.md: new audit() entry, strict-mode tips on add() and relate()
- RELEASES.md: full 7.30.1 entry
Cortex parity (forward-looking, not blocking 7.30.1)
- 6th open question added to .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md: native
fast path for audit() and fillSubtypes() via column-store null-subtype
bitmap for billion-scale brains
- Cortex should add a parity test mirroring strict-mode-self-test.test.ts
against their native paths to catch any latent bug where native writes
bypass JS validation
- Brainy-internal subtype labels become a documented part of the 8.0 contract
(useful for Cortex telemetry surfacing Brainy-managed infrastructure %)
Verification
- npx tsc --noEmit: clean
- npm test: 1468/1468 unit
- 7.29 noun integration suite: 26/26 (no regression)
- 7.30 verb subtype + enforcement integration suite: 30/30 (no regression)
- New strict-mode-self-test integration suite: 13/13
- npm run build: clean
- Closed-source product reference audit: clean
Addresses VE-SUBTYPE-MIGRATION (Venue's reported request) and ships internal
labels Venue did NOT ask for but that would have broken them next under their
own vocabulary registration.
2026-06-08 11:31:47 -07:00
subtype : ( op . subtype as string | undefined ) ? ? 'imported-from-sheets' ,
2026-01-20 16:21:11 -08:00
data : op.data ,
metadata : op.metadata
} )
results . push ( { success : true , id : added.id , action : 'add' } )
break
case 'update' :
await this . context . brain . update ( {
id : op.id ,
data : op.data ,
metadata : op.metadata ,
merge : true
} )
results . push ( { success : true , id : op.id , action : 'update' } )
break
case 'delete' :
2026-06-11 14:51:00 -07:00
await this . context . brain . remove ( op . id )
2026-01-20 16:21:11 -08:00
results . push ( { success : true , id : op.id , action : 'delete' } )
break
default :
results . push ( {
success : false ,
error : ` Unknown action: ${ op . action } `
} )
}
} catch ( error : any ) {
results . push ( {
success : false ,
id : op.id ,
action : op.action ,
error : error.message
} )
}
}
return this . jsonResponse ( {
results ,
total : body.operations.length ,
successful : results.filter ( ( r ) = > r . success ) . length ,
failed : results.filter ( ( r ) = > ! r . success ) . length
} )
}
private async handleRelate ( request : SheetsRequest ) : Promise < SheetsResponse > {
if ( ! this . context ) {
return this . errorResponse ( 500 , 'Not initialized' )
}
const { body } = request
if ( ! body || ! body . from || ! body . to || ! body . type ) {
return this . errorResponse ( 400 , 'Missing required fields: from, to, type' )
}
fix: internal subtype consistency + brain.audit() diagnostic + improved enforcement errors
Brainy 7.30 shipped opt-in subtype enforcement; SDK 3.20.0 then registered
SDK_CORE_VOCABULARY on every consumer's brain (Event, Collection, Message,
Contract, Media, Document NounTypes). On 2026-06-08 Venue's /book flow went 500
because their brain.add({ type: NounType.Event, ... }) call sites lacked
subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk internal write
paths that also omit subtype — any consumer running the same vocabulary would
have hit Brainy's infrastructure paths next. 7.30.1 closes both gaps before
8.0 makes strict mode the default.
Additive across the board. Zero behavior change for consumers not using strict
mode. Every change is JS-side — Cortex needs no work for 7.30.1.
NEW — brain.audit() diagnostic
- Read-only method walking storage.getNouns() / getVerbs() pagination
- Returns { entitiesWithoutSubtype: { type: count }, relationshipsWithoutSubtype,
total, scanned, recommendation }
- VFS infrastructure entities excluded by default (they bypass enforcement via
isVFSEntity marker); pass { includeVFS: true } to surface them
- The companion to migrateField (7.x) and fillSubtypes (8.0): tells consumers
exactly what would break under strict enforcement, deterministically
NEW — Improved enforcement error messages
- Caller's source location extracted from Error().stack so users see their own
call site, not a Brainy internal frame
- Specific guidance branches: registered vocabulary → "Pass one of: a, b, c";
brain-wide strict mode → mentions the except clause; otherwise → registration
recipe via brain.requireSubtype()
- Documentation link to the canonical migration recipe
- Same shape for noun and verb enforcement
NEW — CLI --subtype flag
- brainy add and brainy relate gain -s/--subtype <value>
- Defaults to 'cli-add' / 'cli-relate' so the CLI works against strict-mode
brains without the user needing to know the vocabulary in advance
INTERNAL — every Brainy write path now sets subtype
- VFS Contains edges (5 sites at lines 503/905/1694/1772/1886) → 'vfs-contains'
- VFS symlink entity → 'vfs-symlink' (NEW — distinct from 'vfs-file')
- VFS copy-file → preserves source subtype, falls back to 'vfs-file'
- VFS symlink also adopts the isVFSEntity infrastructure marker so it bypasses
enforcement in strict mode
- Aggregation materializer (Measurement entities) → 'materialized-aggregate'
- ImportCoordinator (3 sites): document → 'import-source'; entities →
options.defaultSubtype ?? 'imported'; placeholder → 'import-placeholder'
- SmartImportOrchestrator (4 entity sites + 2 batch relate sites): same
precedence (extractor → options.defaultSubtype → 'imported')
- EntityDeduplicator → candidate.subtype ?? 'imported'
- UniversalImportAPI → extractor → 'extracted' for both entities and relations
- NeuralImport → adds defaultSubtype to NeuralImportOptions; precedence same
- GoogleSheetsIntegration → request body 'subtype' ?? 'imported-from-sheets'
- ODataIntegration → request body 'Subtype' ?? 'imported-from-odata'
- MCP client message storage → 'mcp-message' (also fixes pre-existing missing
data field and missing type by aliasing from the prior text field)
Side-effect fix: storage.getNouns() paginated now surfaces subtype to top-level
- Single-noun getNoun() already did this in 7.30; the paginated path was missed
- Without this fix brain.audit() saw missing subtype on entities that actually
had one (caught by the strict-mode self-test before release)
NEW — tests/integration/strict-mode-self-test.test.ts (13 tests)
- Creates a brain under the exact SDK_CORE_VOCABULARY shape Venue hit + brain-
wide strict mode
- Exercises every internal Brainy path: VFS root + mkdir + writeFile + cp + mv
+ ln + symlink; aggregation engine; audit diagnostic with includeVFS toggle
- Validates error message UX: caller location, vocabulary guidance, brain-wide
strict mode guidance, off-vocabulary value reporting
Docs
- New "Strict mode in practice" section in docs/guides/subtypes-and-facets.md
covering the SDK_CORE_VOCABULARY pattern, 4-step migration recipe
(audit → migrateField → hand-fix → re-audit), the Brainy-internal label
reference table, and an 8.0 forward-look on fillSubtypes()
- docs/api/README.md: new audit() entry, strict-mode tips on add() and relate()
- RELEASES.md: full 7.30.1 entry
Cortex parity (forward-looking, not blocking 7.30.1)
- 6th open question added to .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md: native
fast path for audit() and fillSubtypes() via column-store null-subtype
bitmap for billion-scale brains
- Cortex should add a parity test mirroring strict-mode-self-test.test.ts
against their native paths to catch any latent bug where native writes
bypass JS validation
- Brainy-internal subtype labels become a documented part of the 8.0 contract
(useful for Cortex telemetry surfacing Brainy-managed infrastructure %)
Verification
- npx tsc --noEmit: clean
- npm test: 1468/1468 unit
- 7.29 noun integration suite: 26/26 (no regression)
- 7.30 verb subtype + enforcement integration suite: 30/30 (no regression)
- New strict-mode-self-test integration suite: 13/13
- npm run build: clean
- Closed-source product reference audit: clean
Addresses VE-SUBTYPE-MIGRATION (Venue's reported request) and ships internal
labels Venue did NOT ask for but that would have broken them next under their
own vocabulary registration.
2026-06-08 11:31:47 -07:00
// Same subtype precedence as entity writes: caller-supplied → default
// `'imported-from-sheets'` (added 7.30.1).
2026-01-20 16:21:11 -08:00
const relation = await this . context . brain . relate ( {
from : body . from ,
to : body.to ,
type : body . type as VerbType ,
fix: internal subtype consistency + brain.audit() diagnostic + improved enforcement errors
Brainy 7.30 shipped opt-in subtype enforcement; SDK 3.20.0 then registered
SDK_CORE_VOCABULARY on every consumer's brain (Event, Collection, Message,
Contract, Media, Document NounTypes). On 2026-06-08 Venue's /book flow went 500
because their brain.add({ type: NounType.Event, ... }) call sites lacked
subtype. An audit of Brainy's OWN source revealed 14 HIGH-risk internal write
paths that also omit subtype — any consumer running the same vocabulary would
have hit Brainy's infrastructure paths next. 7.30.1 closes both gaps before
8.0 makes strict mode the default.
Additive across the board. Zero behavior change for consumers not using strict
mode. Every change is JS-side — Cortex needs no work for 7.30.1.
NEW — brain.audit() diagnostic
- Read-only method walking storage.getNouns() / getVerbs() pagination
- Returns { entitiesWithoutSubtype: { type: count }, relationshipsWithoutSubtype,
total, scanned, recommendation }
- VFS infrastructure entities excluded by default (they bypass enforcement via
isVFSEntity marker); pass { includeVFS: true } to surface them
- The companion to migrateField (7.x) and fillSubtypes (8.0): tells consumers
exactly what would break under strict enforcement, deterministically
NEW — Improved enforcement error messages
- Caller's source location extracted from Error().stack so users see their own
call site, not a Brainy internal frame
- Specific guidance branches: registered vocabulary → "Pass one of: a, b, c";
brain-wide strict mode → mentions the except clause; otherwise → registration
recipe via brain.requireSubtype()
- Documentation link to the canonical migration recipe
- Same shape for noun and verb enforcement
NEW — CLI --subtype flag
- brainy add and brainy relate gain -s/--subtype <value>
- Defaults to 'cli-add' / 'cli-relate' so the CLI works against strict-mode
brains without the user needing to know the vocabulary in advance
INTERNAL — every Brainy write path now sets subtype
- VFS Contains edges (5 sites at lines 503/905/1694/1772/1886) → 'vfs-contains'
- VFS symlink entity → 'vfs-symlink' (NEW — distinct from 'vfs-file')
- VFS copy-file → preserves source subtype, falls back to 'vfs-file'
- VFS symlink also adopts the isVFSEntity infrastructure marker so it bypasses
enforcement in strict mode
- Aggregation materializer (Measurement entities) → 'materialized-aggregate'
- ImportCoordinator (3 sites): document → 'import-source'; entities →
options.defaultSubtype ?? 'imported'; placeholder → 'import-placeholder'
- SmartImportOrchestrator (4 entity sites + 2 batch relate sites): same
precedence (extractor → options.defaultSubtype → 'imported')
- EntityDeduplicator → candidate.subtype ?? 'imported'
- UniversalImportAPI → extractor → 'extracted' for both entities and relations
- NeuralImport → adds defaultSubtype to NeuralImportOptions; precedence same
- GoogleSheetsIntegration → request body 'subtype' ?? 'imported-from-sheets'
- ODataIntegration → request body 'Subtype' ?? 'imported-from-odata'
- MCP client message storage → 'mcp-message' (also fixes pre-existing missing
data field and missing type by aliasing from the prior text field)
Side-effect fix: storage.getNouns() paginated now surfaces subtype to top-level
- Single-noun getNoun() already did this in 7.30; the paginated path was missed
- Without this fix brain.audit() saw missing subtype on entities that actually
had one (caught by the strict-mode self-test before release)
NEW — tests/integration/strict-mode-self-test.test.ts (13 tests)
- Creates a brain under the exact SDK_CORE_VOCABULARY shape Venue hit + brain-
wide strict mode
- Exercises every internal Brainy path: VFS root + mkdir + writeFile + cp + mv
+ ln + symlink; aggregation engine; audit diagnostic with includeVFS toggle
- Validates error message UX: caller location, vocabulary guidance, brain-wide
strict mode guidance, off-vocabulary value reporting
Docs
- New "Strict mode in practice" section in docs/guides/subtypes-and-facets.md
covering the SDK_CORE_VOCABULARY pattern, 4-step migration recipe
(audit → migrateField → hand-fix → re-audit), the Brainy-internal label
reference table, and an 8.0 forward-look on fillSubtypes()
- docs/api/README.md: new audit() entry, strict-mode tips on add() and relate()
- RELEASES.md: full 7.30.1 entry
Cortex parity (forward-looking, not blocking 7.30.1)
- 6th open question added to .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md: native
fast path for audit() and fillSubtypes() via column-store null-subtype
bitmap for billion-scale brains
- Cortex should add a parity test mirroring strict-mode-self-test.test.ts
against their native paths to catch any latent bug where native writes
bypass JS validation
- Brainy-internal subtype labels become a documented part of the 8.0 contract
(useful for Cortex telemetry surfacing Brainy-managed infrastructure %)
Verification
- npx tsc --noEmit: clean
- npm test: 1468/1468 unit
- 7.29 noun integration suite: 26/26 (no regression)
- 7.30 verb subtype + enforcement integration suite: 30/30 (no regression)
- New strict-mode-self-test integration suite: 13/13
- npm run build: clean
- Closed-source product reference audit: clean
Addresses VE-SUBTYPE-MIGRATION (Venue's reported request) and ships internal
labels Venue did NOT ask for but that would have broken them next under their
own vocabulary registration.
2026-06-08 11:31:47 -07:00
subtype : ( body . subtype as string | undefined ) ? ? 'imported-from-sheets' ,
2026-01-20 16:21:11 -08:00
weight : body.weight ,
metadata : body.metadata
} )
return this . jsonResponse ( {
success : true ,
relation : {
id : relation.id ,
from : relation . from ,
to : relation.to ,
type : relation . type
}
} )
}
// Helpers
private entitiesToSheetsResponse ( entities : Entity [ ] ) : SheetsResponse {
if ( entities . length === 0 ) {
return this . jsonResponse ( {
headers : [ 'Id' , 'Type' , 'CreatedAt' ] ,
rows : [ ] ,
count : 0 ,
hasMore : false
} )
}
// Collect all unique metadata keys
const metadataKeys = new Set < string > ( )
for ( const entity of entities ) {
if ( entity . metadata ) {
for ( const key of Object . keys ( entity . metadata ) ) {
metadataKeys . add ( key )
}
}
}
// Build headers
const baseHeaders = [ 'Id' , 'Type' , 'Confidence' , 'Weight' , 'CreatedAt' ]
const metaHeaders = Array . from ( metadataKeys ) . sort ( )
const headers = [ . . . baseHeaders , . . . metaHeaders , 'Data' ]
// Build rows
const rows = entities . map ( ( entity ) = > {
const baseValues = [
entity . id ,
entity . type ,
entity . confidence ? ? '' ,
entity . weight ? ? '' ,
new Date ( entity . createdAt ) . toISOString ( )
]
const metaValues = metaHeaders . map ( ( key ) = > {
const val = entity . metadata ? . [ key ]
if ( val === undefined || val === null ) return ''
if ( typeof val === 'object' ) return JSON . stringify ( val )
return val
} )
const dataValue = entity . data ? JSON . stringify ( entity . data ) : ''
return [ . . . baseValues , . . . metaValues , dataValue ]
} )
return this . jsonResponse ( {
headers ,
rows ,
count : rows.length ,
hasMore : false
} )
}
private broadcastSSE ( event : string , data : any ) : void {
for ( const [ _ , sendFn ] of this . sseClients ) {
try {
sendFn ( event , data )
} catch {
// Client disconnected
}
}
}
private jsonResponse ( data : any ) : SheetsResponse {
return {
status : 200 ,
headers : {
'Content-Type' : 'application/json' ,
'Access-Control-Allow-Origin' : '*' ,
'Access-Control-Allow-Methods' : 'GET, POST, OPTIONS' ,
'Access-Control-Allow-Headers' : 'Content-Type, Authorization'
} ,
body : JSON.stringify ( data )
}
}
private errorResponse ( status : number , message : string ) : SheetsResponse {
return {
status ,
headers : {
'Content-Type' : 'application/json' ,
'Access-Control-Allow-Origin' : '*'
} ,
body : JSON.stringify ( { error : message } )
}
}
private corsResponse ( ) : SheetsResponse {
return {
status : 204 ,
headers : {
'Access-Control-Allow-Origin' : '*' ,
'Access-Control-Allow-Methods' : 'GET, POST, OPTIONS' ,
'Access-Control-Allow-Headers' : 'Content-Type, Authorization' ,
'Access-Control-Max-Age' : '86400'
} ,
body : null
}
}
}
/ * *
* Package export for @soulcraft / brainy - sheets
* /
export const integration = {
name : 'sheets' ,
version : '1.0.0' ,
description : 'Google Sheets two-way sync with real-time updates' ,
environments : [ 'node' , 'browser' , 'deno' , 'cloudflare' , 'bun' ] ,
create : ( brain : any , config? : GoogleSheetsConfig ) = >
new GoogleSheetsIntegration ( config ) ,
defaultConfig : {
basePath : '/sheets' ,
maxResults : 1000 ,
defaultLimit : 100 ,
allowWrite : true
}
}