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

@ -19,6 +19,7 @@ import { insightsCommands } from './commands/insights.js'
import { importCommands } from './commands/import.js'
import { snapshotCommands } from './commands/snapshot.js'
import { inspectCommands } from './commands/inspect.js'
import { types as typesCommand, validate as validateCommand } from './commands/types.js'
import { readFileSync } from 'fs'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
@ -70,14 +71,9 @@ ${chalk.cyan('Examples:')}
$ brainy vfs similar /code/Button.tsx
${chalk.dim('# Storage management')}
$ brainy storage status --quota
$ brainy storage lifecycle set ${chalk.dim('# Interactive mode')}
$ brainy storage cost-estimate
$ brainy storage status
$ brainy storage batch-delete old-ids.txt
${chalk.dim('# Interactive mode')}
$ brainy interactive
${chalk.cyan('Documentation:')}
${chalk.dim('Full docs:')} https://github.com/soulcraftlabs/brainy
${chalk.dim('Report issues:')} https://github.com/soulcraftlabs/brainy/issues
@ -109,7 +105,7 @@ program
.description('Advanced search with Triple Intelligence™ (interactive if no query)')
.option('-k, --limit <number>', 'Number of results', '10')
.option('--offset <number>', 'Skip N results (pagination)')
.option('-t, --threshold <number>', 'Similarity threshold (0-1)', '0.7')
.option('-t, --threshold <number>', 'Minimum similarity score (0-1); with --near, the proximity threshold')
.option('--type <types>', 'Filter by type(s) - comma separated')
.option('--where <json>', 'Metadata filters (JSON)')
.option('--near <id>', 'Find items near this ID')
@ -181,6 +177,23 @@ program
.option('--pretty', 'Pretty print JSON')
.action(coreCommands.diagnostics)
// ===== Type Commands =====
program
.command('types')
.description('List all NounType and VerbType values')
.option('--noun', 'Show noun types only')
.option('--verb', 'Show verb types only')
.option('--json', 'Output as JSON')
.action(typesCommand)
program
.command('validate [type]')
.description('Check whether a type string is a valid NounType or VerbType (interactive if no type)')
.option('--verb', 'Validate as a verb type (default: noun type)')
.option('--json', 'Output as JSON')
.action(validateCommand)
// ===== Neural Commands =====
program
@ -222,17 +235,6 @@ program
.option('--children-only', 'Show only child hierarchy')
.action(neuralCommands.hierarchy)
program
.command('path <from> <to>')
.description('Find semantic path between items')
.option('--steps', 'Show step-by-step path')
.option('--max-hops <number>', 'Maximum path length', '5')
.action(() => {
console.log(chalk.yellow('\n⚠ Semantic path finding coming soon'))
console.log(chalk.dim('This feature requires implementing graph traversal algorithms'))
console.log(chalk.dim('Use "brainy neighbors" and "brainy hierarchy" to explore connections'))
})
program
.command('outliers')
.alias('anomalies')
@ -457,89 +459,26 @@ program
program
.command('storage')
.description('💾 Storage management and cost optimization')
.description('💾 Storage management (filesystem + memory backends)')
.addCommand(
new Command('status')
.description('Show storage status and health')
.option('--detailed', 'Show detailed information')
.option('--quota', 'Show quota information (OPFS)')
.description('Show storage backend, counts, root directory, and writer lock')
.option('--json', 'Output as JSON')
.option('--pretty', 'Pretty-print JSON')
.action((options) => {
storageCommands.status(options)
})
)
.addCommand(
new Command('lifecycle')
.description('Lifecycle policy management')
.addCommand(
new Command('set')
.argument('[config-file]', 'Policy configuration file (JSON)')
.description('Set lifecycle policy (interactive if no file)')
.option('--validate', 'Validate before applying')
.action((configFile, options) => {
storageCommands.lifecycle.set(configFile, options)
})
)
.addCommand(
new Command('get')
.description('Get current lifecycle policy')
.option('-f, --format <type>', 'Output format (json|yaml)', 'json')
.action((options) => {
storageCommands.lifecycle.get(options)
})
)
.addCommand(
new Command('remove')
.description('Remove lifecycle policy')
.action((options) => {
storageCommands.lifecycle.remove(options)
})
)
)
.addCommand(
new Command('compression')
.description('Compression management (FileSystem)')
.addCommand(
new Command('enable')
.description('Enable gzip compression')
.action((options) => {
storageCommands.compression.enable(options)
})
)
.addCommand(
new Command('disable')
.description('Disable compression')
.action((options) => {
storageCommands.compression.disable(options)
})
)
.addCommand(
new Command('status')
.description('Show compression status')
.action((options) => {
storageCommands.compression.status(options)
})
)
)
.addCommand(
new Command('batch-delete')
.argument('<file>', 'File containing entity IDs (one per line)')
.description('Batch delete with retry logic')
.option('--max-retries <n>', 'Maximum retry attempts', '3')
.option('--continue-on-error', 'Continue if some deletes fail')
.description('Delete a list of entities through the public delete path (with retry)')
.option('--max-retries <n>', 'Maximum retry attempts per ID', '3')
.option('--continue-on-error', 'Continue past IDs that still fail after retries')
.action((file, options) => {
storageCommands.batchDelete(file, options)
})
)
.addCommand(
new Command('cost-estimate')
.description('Estimate cloud storage costs')
.option('--provider <type>', 'Cloud provider (aws|gcs|azure|r2)')
.option('--size <gb>', 'Data size in GB')
.option('--operations <n>', 'Monthly operations')
.action((options) => {
storageCommands.costEstimate(options)
})
)
// ===== Data Management Commands =====
@ -742,9 +681,8 @@ program
program
.command('clean')
.description('Clean and optimize database')
.option('--remove-orphans', 'Remove orphaned items')
.option('--rebuild-index', 'Rebuild search index')
.description('Clear the database — ALL entities, relationships, and indexes (asks for confirmation)')
.option('-f, --force', 'Skip confirmation prompt')
.action(utilityCommands.clean)
program
@ -785,17 +723,6 @@ program
.description('Show the store\'s current generation watermark')
.action(snapshotCommands.generation)
// ===== Interactive Mode =====
program
.command('interactive')
.alias('i')
.description('Start interactive REPL mode')
.action(async () => {
const { startInteractiveMode } = await import('./interactive.js')
await startInteractiveMode()
})
// ===== Error Handling =====
program.exitOverride()