Merge branch 'worktree-agent-ad3aff0dffd17a6eb'
Some checks failed
CI / Node 22 (push) Successful in 12m19s
CI / Node 24 (push) Has been cancelled
CI / Integration + conformance (Node 22) (push) Has been cancelled
CI / Bun (latest) (push) Has been cancelled

This commit is contained in:
David Snelling 2026-08-25 11:47:25 -07:00
commit f14da34b27
12 changed files with 726 additions and 80 deletions

View file

@ -742,6 +742,15 @@ export class Brainy<T = any> implements BrainyInterface<T> {
private _pendingEmbedIds = new Set<string>()
private _embedWorkerFlight: Promise<void> | null = null
// OPEN-PATH FIX: the background embedding-engine warm kicked off (never
// awaited) by `performInit()` when `eagerEmbeddings` resolves true. Stored
// for observability only — `embed()`/`embeddingManager.embed()` already
// await the engine's OWN singleton init promise internally, so nothing
// needs to explicitly await this field for correctness. Never rejects on
// its own: a `.catch` narrates the failure and swallows it so a failed
// warm never surfaces as an unhandled rejection.
private _embeddingWarmPromise: Promise<void> | null = null
/** The stored log-authority switch, read once at open (default: tree). */
private _logAuthority: LogAuthorityRecord = { authority: 'tree' }
// A failed walk latches its error: retries within the cooldown rethrow it
@ -1079,6 +1088,23 @@ export class Brainy<T = any> implements BrainyInterface<T> {
configureLogger({ level: LogLevel.DEBUG }) // Enable verbose logging
}
// OPEN-PATH NARRATION: lightweight phase timing across the five
// named stretches of init — storage init / generation-store open+fold /
// index init+gate / VFS bootstrap / embedding-warm-started. Each
// `markPhase()` call records elapsed ms SINCE THE PREVIOUS checkpoint,
// so the buckets always sum to the pre-integration/warmOnOpen total.
// Silent under 2s; one `prodLog.warn` line naming every phase's ms
// above it, so the operator's next restart storm names its own slow
// phase instead of re-deriving it from a stack of raw timestamps.
const initStart = Date.now()
let lastPhaseCheckpoint = initStart
const phaseTimingsMs: Record<string, number> = {}
const markPhase = (name: string): void => {
const now = Date.now()
phaseTimingsMs[name] = now - lastPhaseCheckpoint
lastPhaseCheckpoint = now
}
try {
// Auto-detect and activate plugins BEFORE storage setup
// so plugin-provided storage factories (e.g., filesystem override from cor) are available
@ -1150,6 +1176,12 @@ export class Brainy<T = any> implements BrainyInterface<T> {
}
}
// PHASE 1 of 5 — "storage init": plugin/legacy-layout bootstrap,
// storage adapter construction+init, the OS-limit check, and the
// writer-lock claim, all folded into one bucket (everything above this
// line since performInit started).
markPhase('storage-init')
// 8.0 generational MVCC: open the record layer BEFORE any index is
// created or loaded. Crash recovery may rewrite canonical entity files
// (restoring before-images of an uncommitted transaction), and every
@ -1224,6 +1256,12 @@ export class Brainy<T = any> implements BrainyInterface<T> {
await this.createMigrationBackupIfNeeded()
}
// PHASE 2 of 5 — "generation-store open+fold": GenerationStore
// construction+open (crash-recovery replay/rollback fold), the
// derived-family registration, the fact-scan seam, the entity-tree
// stamp check, the brain-format handshake, and the pre-upgrade backup.
markPhase('generation-store-open-fold')
// Provider: embeddings (reassign embedder if plugin provides one)
const embeddingProvider = this.pluginRegistry.getProvider<EmbeddingFunction>('embeddings')
if (embeddingProvider) {
@ -1475,6 +1513,14 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// Check for pending data migrations
await this.checkMigrations()
// PHASE 3 of 5 — "index init+gate": provider wiring (embeddings,
// cache, roaring, msgpack, sort:topK, distance), HNSW/metadata/graph
// index construction, the eager cold-load, id-resolver + connections-
// codec wiring, crash-recovery index rebuild, the replay-gap check,
// legacy VFS blob adoption, blob-history backfill, and the
// rebuildIndexesIfNeeded() gate + migration check.
markPhase('index-init-gate')
// Register shutdown hooks for graceful count flushing (once globally)
if (!Brainy.shutdownHooksRegisteredGlobally) {
this.registerShutdownHooks()
@ -1626,15 +1672,40 @@ export class Brainy<T = any> implements BrainyInterface<T> {
}
}
// Eager embedding initialization.
// PHASE 4 of 5 — "VFS bootstrap": shutdown-hook registration, blob
// storage init, the provider-summary log, flipping `initialized`,
// the migration-lock wait, VFS construction+init, flipping generation
// stamping active, the log-authority adopt/oracle check, and
// pending-embed crash recovery.
markPhase('vfs-bootstrap')
// Eager embedding initialization — BACKGROUND WARM (open-path fix).
//
// Adaptive default (8.0): the WASM embedding engine eagerly initializes
// Adaptive default (8.0): the WASM embedding engine eagerly WARMS
// during init() WHENEVER it is the active embedder — i.e. no native
// 'embeddings' provider has taken over — and the instance is a writer
// (not reader-mode) outside of unit tests. The WASM module (≈93MB with
// the embedded model) takes 90-140s to compile on throttled CPUs; paying
// that during boot rather than on the first embed()-driven call is the
// right default for the overwhelmingly common single-process server.
// the embedded model) takes 90-140s to compile on throttled CPUs.
//
// Historically this AWAITED `embeddingManager.init()` INLINE, so every
// writer's open() blocked on the compile — N concurrent opens all
// queued on the ONE process-global singleton (an ~80x contention
// multiplier measured in a production restart storm: 90,017ms busy vs
// 1,117ms quiet). The engine only needs to be ready before the FIRST
// REAL embed() call, not before init() returns, so this now only
// STARTS the warm and moves on — init() never waits for it.
//
// No double-await needed for correctness: `this.embed()` (~line 15420)
// delegates to `this.embedder`, which for the default engine is
// `embeddingManager.getEmbeddingFunction()` → `embeddingManager.embed()`
// (src/embeddings/EmbeddingManager.ts). That method calls `await
// this.init()` FIRST, and `init()` itself serializes every concurrent
// caller onto ONE shared `globalInitPromise` — so the first real
// embed() automatically waits for whichever finishes first: this
// background warm (if still running) or a fresh init() (if the warm
// hasn't reached this code yet, e.g. `eagerEmbeddings: false`).
// Verified by reading both call sites; `_embeddingWarmPromise` below
// is stored for observability only, never re-awaited by embed().
//
// Skipped automatically when:
// - a native 'embeddings' provider is registered (it owns embeddings;
@ -1642,8 +1713,8 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// - reader-mode (readers don't embed — they query existing vectors),
// - unit-test mode (tests must stay fast and use the mock embedder).
//
// `eagerEmbeddings: false` is the explicit override to force lazy init
// (first-embed) even when this instance is the active embedder.
// `eagerEmbeddings: false` keeps meaning "no warm at all" — fully lazy,
// the first embed() call pays the full cost inline, same as before.
const isUnitTestMode = isDeterministicEmbedMode()
const eager = this.config.eagerEmbeddings ?? true
if (
@ -1652,9 +1723,45 @@ export class Brainy<T = any> implements BrainyInterface<T> {
this.config.mode !== 'reader' &&
!isUnitTestMode
) {
console.log('Eager embedding initialization enabled...')
await embeddingManager.init()
console.log('Embedding engine ready')
const warmStart = Date.now()
console.log('Background embedding-engine warm started (init() does not wait for it)...')
this._embeddingWarmPromise = embeddingManager
.init()
.then(() => {
prodLog.info(
`[Brainy] background embedding-engine warm complete in ${Date.now() - warmStart}ms`
)
})
.catch((err) => {
// Loud, never silent: a warm that fails to compile must be
// heard NOW, not discovered as a mystery latency spike on
// whichever request happens to trigger the first real embed().
// That first embed() call still retries init() itself (the
// singleton promise contract above) and surfaces its own typed
// error to its caller — this is the immediate, background echo.
prodLog.warn(
`[Brainy] background embedding-engine warm FAILED: ` +
`${(err as Error).message} — the first embed() call will retry ` +
`initialization and surface the error there`
)
})
}
// PHASE 5 of 5 — "embedding-warm-started": just the synchronous cost
// of kicking off the background warm above (the warm's own compile
// time is NOT included — that's the whole point of backgrounding it).
markPhase('embedding-warm-started')
{
const totalOpenMs = Date.now() - initStart
if (totalOpenMs > 2000) {
const phaseList = Object.entries(phaseTimingsMs)
.map(([name, ms]) => `${name}=${ms}ms`)
.join(', ')
prodLog.warn(
`[Brainy] slow open: ${totalOpenMs}ms total (${phaseList}) — see the ` +
`phase breakdown above to find which one to investigate first`
)
}
}
// Integration Hub initialization
@ -3469,11 +3576,20 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// new `data`); otherwise new `data` re-embeds; otherwise the existing
// vector is kept. Any vector change re-indexes HNSW below.
let vector = existing.vector
// 'data' is a real new value whenever it's not null/undefined — an
// empty string ('') is legitimate content (e.g. truncating a file to
// empty via overwrite), matching validateUpdateParams's absent-vs-empty
// distinction. Using `Boolean(params.data)` here would treat '' as "no
// new data", silently skipping BOTH the deferred marker and the eager
// re-embed below — a stale vector left behind with no path to ever
// correct itself (a quiet loss, not the deferred-but-eventually-
// correct flicker the deferEmbedding contract promises).
const hasNewData = params.data !== undefined && params.data !== null
// MT5 deferred re-embedding: the OLD vector keeps serving semantic
// search — stale-but-present, never absent (the flicker law) — until
// the background worker embeds the new data and swaps it atomically.
const deferringEmbed =
params.deferEmbedding === true && Boolean(params.data) && !params.vector
params.deferEmbedding === true && hasNewData && !params.vector
if (params.vector) {
if (this.dimensions && params.vector.length !== this.dimensions) {
throw new Error(
@ -3481,13 +3597,13 @@ export class Brainy<T = any> implements BrainyInterface<T> {
)
}
vector = params.vector
} else if (params.data && !deferringEmbed) {
} else if (hasNewData && !deferringEmbed) {
vector = await this.embed(params.data)
}
// A deferred data change does NOT reindex now (the vector is unchanged;
// the worker's atomic swap carries the real reindex later).
const needsReindexing = Boolean(
(params.data && !deferringEmbed) || params.type || params.vector
(hasNewData && !deferringEmbed) || params.type || params.vector
)
// Always update the noun with new metadata
@ -10495,6 +10611,11 @@ export class Brainy<T = any> implements BrainyInterface<T> {
// Resolve the updated vector — mirror of update(): an explicit `vector`
// always wins, new `data` re-embeds, otherwise the existing vector is
// kept. Any vector change re-indexes HNSW below.
// 'data' is present whenever it's not null/undefined — '' is real
// content (see the identical hasNewData in update()); a plain truthy
// check would silently skip re-embedding an emptied value and leave a
// stale vector with no path to ever correct itself.
const hasNewData = params.data !== undefined && params.data !== null
let vector = existing.vector
if (params.vector) {
if (this.dimensions && params.vector.length !== this.dimensions) {
@ -10503,10 +10624,10 @@ export class Brainy<T = any> implements BrainyInterface<T> {
)
}
vector = params.vector
} else if (params.data) {
} else if (hasNewData) {
vector = await this.embed(params.data)
}
const needsReindexing = Boolean(params.data || params.type || params.vector)
const needsReindexing = Boolean(hasNewData || params.type || params.vector)
const newMetadata =
params.merge !== false
@ -15826,6 +15947,25 @@ export class Brainy<T = any> implements BrainyInterface<T> {
return embeddingManager.isInitialized()
}
/**
* Whether the process-global WASM embedding engine (all-MiniLM-L6-v2,
* fixed 384-dim output, 93MB with the bundled model, 90-140s cold compile
* on throttled CPUs) is this instance's active embedder `false` when a
* plugin has replaced it via the `'embeddings'` provider key. A native
* provider has no such cold-start cost and may use a different output
* dimension, so it is never worth avoiding.
*
* Used by init-path bootstrap writes (the VFS root see
* `VirtualFileSystem.doInitializeRoot()`) to decide whether embedding a
* value during `init()` risks paying the WASM engine's cold compile.
*
* @returns true when the default WASM engine is active (no native
* `'embeddings'` provider registered).
*/
usesDefaultWasmEmbedder(): boolean {
return !this.pluginRegistry.hasProvider('embeddings')
}
/**
* Setup embedder
*/