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:
parent
9b0f4acd5b
commit
c44678390e
30 changed files with 1517 additions and 3226 deletions
|
|
@ -22,8 +22,7 @@ interface StatsOptions extends UtilityOptions {
|
|||
}
|
||||
|
||||
interface CleanOptions extends UtilityOptions {
|
||||
removeOrphans?: boolean
|
||||
rebuildIndex?: boolean
|
||||
force?: boolean
|
||||
}
|
||||
|
||||
interface BenchmarkOptions extends UtilityOptions {
|
||||
|
|
@ -63,6 +62,7 @@ export const utilityCommands = {
|
|||
|
||||
try {
|
||||
const brain = getBrainy()
|
||||
await brain.init()
|
||||
const nounCount = await brain.getNounCount()
|
||||
const verbCount = await brain.getVerbCount()
|
||||
const memUsage = process.memoryUsage()
|
||||
|
|
@ -77,7 +77,11 @@ export const utilityCommands = {
|
|||
|
||||
if (options.json) {
|
||||
formatOutput(stats, options)
|
||||
return
|
||||
// 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)
|
||||
}
|
||||
|
||||
console.log(chalk.cyan('\n📊 Database Statistics\n'))
|
||||
|
|
@ -112,7 +116,10 @@ export const utilityCommands = {
|
|||
)
|
||||
|
||||
console.log(memTable.toString())
|
||||
|
||||
|
||||
// One-shot command — see the --json branch for why the explicit exit.
|
||||
await brain.close()
|
||||
process.exit(0)
|
||||
} catch (error: any) {
|
||||
spinner.fail('Failed to gather statistics')
|
||||
console.error(chalk.red(error.message))
|
||||
|
|
@ -121,30 +128,33 @@ export const utilityCommands = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Clean and optimize database
|
||||
* Clear the database (all entities, relationships, and indexes).
|
||||
* Destructive — asks for confirmation unless --force is passed.
|
||||
*/
|
||||
async clean(options: CleanOptions) {
|
||||
const spinner = ora('Cleaning database...').start()
|
||||
let spinner: ReturnType<typeof ora> | null = null
|
||||
|
||||
try {
|
||||
const brain = getBrainy()
|
||||
// Destructive operation — confirm first (skipped with --force).
|
||||
if (!options.force) {
|
||||
const inquirer = (await import('inquirer')).default
|
||||
const { confirm } = await inquirer.prompt([{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: chalk.yellow('⚠️ Permanently delete ALL data (entities, relationships, indexes)?'),
|
||||
default: false
|
||||
}])
|
||||
|
||||
// For now, only support full clear
|
||||
// removeOrphans and rebuildIndex would require new Brainy APIs
|
||||
if (options.removeOrphans || options.rebuildIndex) {
|
||||
spinner.warn('Advanced cleanup options not yet implemented')
|
||||
console.log(chalk.yellow('\n⚠️ Advanced cleanup features coming soon:'))
|
||||
console.log(chalk.dim(' • --remove-orphans: Remove disconnected items'))
|
||||
console.log(chalk.dim(' • --rebuild-index: Rebuild vector index'))
|
||||
console.log(chalk.dim('\nUse "brainy clean" without options to clear the database'))
|
||||
return
|
||||
if (!confirm) {
|
||||
console.log(chalk.yellow('Clean cancelled'))
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Show warning before clearing
|
||||
console.log(chalk.yellow('\n⚠️ WARNING: This will permanently delete ALL data!'))
|
||||
const brain = getBrainy()
|
||||
|
||||
// Clear all data (entities, relationships, and every index)
|
||||
spinner.text = 'Clearing all data...'
|
||||
spinner = ora('Clearing all data...').start()
|
||||
await brain.init()
|
||||
await brain.clear()
|
||||
|
||||
|
|
@ -163,7 +173,7 @@ export const utilityCommands = {
|
|||
await brain.close()
|
||||
process.exit(0)
|
||||
} catch (error: any) {
|
||||
spinner.fail('Cleanup failed')
|
||||
if (spinner) spinner.fail('Cleanup failed')
|
||||
console.error(chalk.red(error.message))
|
||||
process.exit(1)
|
||||
}
|
||||
|
|
@ -185,7 +195,8 @@ export const utilityCommands = {
|
|||
|
||||
try {
|
||||
const brain = getBrainy()
|
||||
|
||||
await brain.init()
|
||||
|
||||
// Benchmark different operations
|
||||
const benchmarks = [
|
||||
{ name: 'add', enabled: operations === 'all' || operations.includes('add') },
|
||||
|
|
@ -205,7 +216,8 @@ export const utilityCommands = {
|
|||
|
||||
switch (bench.name) {
|
||||
case 'add':
|
||||
await brain.add({ data: `Test item ${i}`, type: NounType.Thing, metadata: { benchmark: true } })
|
||||
// 8.0 requires a subtype on every write by default.
|
||||
await brain.add({ data: `Test item ${i}`, type: NounType.Thing, subtype: 'benchmark', metadata: { benchmark: true } })
|
||||
break
|
||||
case 'search':
|
||||
await brain.find({ query: 'test', limit: 10 })
|
||||
|
|
@ -284,7 +296,10 @@ export const utilityCommands = {
|
|||
} else {
|
||||
formatOutput(results, options)
|
||||
}
|
||||
|
||||
|
||||
// One-shot command — see stats() for why the explicit close + exit.
|
||||
await brain.close()
|
||||
process.exit(0)
|
||||
} catch (error: any) {
|
||||
console.error(chalk.red('Benchmark failed:'), error.message)
|
||||
process.exit(1)
|
||||
|
|
|
|||
Reference in a new issue