feat(8.0): brain.fillSubtypes migration helper + pre-RC1 gap closure

- brain.fillSubtypes(rules): idempotent subtype back-fill for pre-8.0 data.
  One rule per NounType/VerbType (literal default or per-entry function);
  fills only entries still missing a subtype through the public update()/
  updateRelation() paths; returns { scanned, filled, skipped, errors, byType }.
  Full unit suite in tests/unit/brainy/fill-subtypes.test.ts.
- Fix getNouns/getVerbs pagination hasMore (peek one past the window) —
  was permanently false, silently truncating every multi-page walk.
- find({ near }) without near.id now throws a teaching error instead of an
  opaque storage sharding failure; CLI --threshold without --near applies a
  plain score floor.
- CLI init/close audit: every one-shot command init()s, close()s, and exits
  explicitly; delete the unmaintained interactive REPL; replace the cloud-era
  storage subcommands with status/batch-delete; new types/validate commands.
- requireSubtype JSDoc now documents the 8.0 default-on contract; audit()
  recommendation points at fillSubtypes.
- Docs: data-storage-architecture rewritten to the real 8.0 on-disk layout;
  README storage section reflects filesystem+memory and snapshots; eli5 and
  SEMANTIC_VFS /as-of/ semantics corrected; internal tracker IDs and
  .strategy references scrubbed from published files.
This commit is contained in:
David Snelling 2026-06-11 10:42:34 -07:00
parent 9b0f4acd5b
commit c44678390e
30 changed files with 1517 additions and 3226 deletions

View file

@ -132,6 +132,7 @@ export const coreCommands = {
const spinner = ora('Adding to neural database...').start()
const brain = getBrainy()
await brain.init()
let metadata: any = {}
if (options.metadata) {
@ -204,6 +205,12 @@ export const coreCommands = {
} else {
formatOutput({ id: result, metadata, confidence: addParams.confidence, weight: addParams.weight }, options)
}
// close() releases the writer lock and indexes, but global timers
// (UnifiedCache bookkeeping, PathResolver stats) keep the event loop
// alive. CLI commands are one-shot — exit explicitly.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Failed to add data')
console.error(chalk.red('Failed to add data:', error.message))
@ -280,6 +287,7 @@ export const coreCommands = {
const spinner = ora('Searching with Triple Intelligence™...').start()
const brain = getBrainy()
await brain.init()
// Build comprehensive search params
const searchParams: any = {
@ -292,11 +300,6 @@ export const coreCommands = {
searchParams.offset = parseInt(options.offset)
}
// Vector Intelligence - similarity threshold
if (options.threshold) {
searchParams.near = { threshold: parseFloat(options.threshold) }
}
// Metadata Intelligence - type filtering
if (options.type) {
const types = options.type.split(',').map(t => t.trim())
@ -314,7 +317,9 @@ export const coreCommands = {
}
}
// Vector Intelligence - proximity search
// Vector Intelligence - proximity search around an anchor entity.
// `near` requires an id; a bare --threshold (no --near) is applied as a
// plain score floor on the fused results after find() returns.
if (options.near) {
searchParams.near = {
id: options.near,
@ -371,7 +376,14 @@ export const coreCommands = {
}
}
const results = await brain.find(searchParams)
let results = await brain.find(searchParams)
// Without --near there is no proximity anchor; apply --threshold as a
// minimum-score filter on the fused results instead.
if (!options.near && options.threshold) {
const minScore = parseFloat(options.threshold)
results = results.filter((r) => r.score === undefined || r.score >= minScore)
}
spinner.succeed(`Found ${results.length} results`)
@ -457,6 +469,10 @@ export const coreCommands = {
} else {
formatOutput(results, options)
}
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Search failed')
console.error(chalk.red('Search failed:', error.message))
@ -496,6 +512,7 @@ export const coreCommands = {
const spinner = ora('Fetching item...').start()
const brain = getBrainy()
await brain.init()
// Try to get the item
const item = await brain.get(id)
@ -529,6 +546,10 @@ export const coreCommands = {
} else {
formatOutput(item, options)
}
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Failed to get item')
console.error(chalk.red('Failed to get item:', error.message))
@ -589,6 +610,7 @@ export const coreCommands = {
const spinner = ora('Creating relationship...').start()
const brain = getBrainy()
await brain.init()
let metadata: any = {}
if (options.metadata) {
@ -627,6 +649,10 @@ export const coreCommands = {
} else {
formatOutput({ id: result, source, verb, target, metadata }, options)
}
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Failed to create relationship')
console.error(chalk.red('Failed to create relationship:', error.message))
@ -683,6 +709,7 @@ export const coreCommands = {
spinner = ora('Updating entity...').start()
const brain = getBrainy()
await brain.init()
// Get existing entity first
const existing = await brain.get(id)
@ -731,6 +758,10 @@ export const coreCommands = {
} else {
formatOutput({ id, updated: true }, options)
}
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Failed to update entity')
console.error(chalk.red('Update failed:', error.message))
@ -784,6 +815,7 @@ export const coreCommands = {
spinner = ora('Deleting entity...').start()
const brain = getBrainy()
await brain.init()
await brain.delete(id)
@ -794,6 +826,10 @@ export const coreCommands = {
} else {
formatOutput({ id, deleted: true }, options)
}
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Failed to delete entity')
console.error(chalk.red('Delete failed:', error.message))
@ -846,6 +882,7 @@ export const coreCommands = {
spinner = ora('Removing relationship...').start()
const brain = getBrainy()
await brain.init()
await brain.unrelate(id)
@ -856,6 +893,10 @@ export const coreCommands = {
} else {
formatOutput({ id, removed: true }, options)
}
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
if (spinner) spinner.fail('Failed to remove relationship')
console.error(chalk.red('Unrelate failed:', error.message))
@ -875,7 +916,9 @@ export const coreCommands = {
if (options.json) {
formatOutput(diag, options)
return
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
}
console.log(chalk.bold('\nBrainy Diagnostics'))
@ -907,6 +950,10 @@ export const coreCommands = {
console.log(` Metadata: ${diag.indexes.metadata.type} (initialized: ${diag.indexes.metadata.initialized})`)
console.log(` Graph: ${diag.indexes.graph.type} (initialized: ${diag.indexes.graph.initialized}, wired: ${diag.indexes.graph.wiredToStorage})`)
console.log()
// One-shot command — see add() for why the explicit close + exit.
await brain.close()
process.exit(0)
} catch (error: any) {
console.error(chalk.red('Diagnostics failed: ' + error.message))
process.exit(1)