feat(8.0): no-freeze auto-upgrade hooks — isMigrating() deference + stampBrainFormat() + brain-format export

Three hooks so a native provider can run a non-blocking, online, background
build-new→verify→swap index migration on a large-brain upgrade while brainy stays
out of the way — no minutes-long blocking rebuild-on-open/first-query.

- isMigrating?(): boolean — OPTIONAL on MetadataIndexProvider / GraphIndexProvider /
  VectorIndexProvider (mirrors isReady?()/init?(), feature-detected). While a
  provider reports migrating, brainy SKIPS its rebuild for that index — per-index,
  so a non-migrating sibling still rebuilds; skipped even under epoch-drift or
  size()===0 — in both rebuildIndexesIfNeeded and the lazy first-query force path.
  The provider serves correct reads from canonical until it verifies-and-swaps.
- brain.stampBrainFormat() — public; the provider calls it once its background swap
  verifies, so brainy authors dataFormat (the provider never does). brainy withholds
  its own marker stamp while any provider is migrating, so the shared epoch is never
  advanced ahead of a deferred index.
- ./brain-format export — the marker module's EXPECTED_INDEX_EPOCH / CURRENT_DATA_FORMAT
  are importable so a native provider shares the constant (no duplicate = no
  lockstep-drift). 8-case test; unit 1743 green.
This commit is contained in:
David Snelling 2026-06-30 13:37:23 -07:00
parent bd6faf7499
commit b6b919890c
4 changed files with 378 additions and 18 deletions

View file

@ -125,6 +125,17 @@ export interface MetadataIndexProvider {
flush(): Promise<void>
rebuild(): Promise<void>
/**
* @description OPTIONAL. A native provider returns true from the moment its
* `init()` detects a large epoch-drift until its background
* build-newverifyswap has verified-and-swapped. While true, brainy SKIPS its
* own rebuild for this provider and lets the provider's non-blocking background
* migration own the index (the no-freeze path); the provider serves correct
* reads from canonical meanwhile. Mirrors {@link MetadataIndexProvider.init}
* (and `isReady?()` on the graph provider).
*/
isMigrating?(): boolean
addToIndex(id: string, entityOrMetadata: any, skipFlush?: boolean, deferWrites?: boolean): Promise<void>
removeFromIndex(id: string, metadata?: any): Promise<void>
@ -276,6 +287,16 @@ export interface GraphIndexProvider {
*/
init?(): Promise<void>
/**
* @description OPTIONAL. A native provider returns true from the moment its
* `init()` detects a large epoch-drift until its background
* build-newverifyswap has verified-and-swapped. While true, brainy SKIPS its
* own rebuild for this provider and lets the provider's non-blocking background
* migration own the index (the no-freeze path); the provider serves correct
* reads from canonical meanwhile. Mirrors `isReady?()` / `init?()`.
*/
isMigrating?(): boolean
/**
* @description Entity ints reachable from `id` (1 hop), deduped.
* @param id - The entity's interned int (from the shared idMapper).
@ -880,6 +901,17 @@ export interface VectorIndexProvider {
rebuild(options?: any): Promise<void>
flush(): Promise<number>
getPersistMode(): 'immediate' | 'deferred'
/**
* @description OPTIONAL. A native provider returns true from the moment its
* `init()` detects a large epoch-drift until its background
* build-newverifyswap has verified-and-swapped. While true, brainy SKIPS its
* own rebuild for this provider and lets the provider's non-blocking background
* migration own the index (the no-freeze path); the provider serves correct
* reads from canonical meanwhile. Mirrors `isReady?()` / `init?()` on the
* other index providers.
*/
isMigrating?(): boolean
}