@soulcraft/cor 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -470,7 +470,7 @@ export class NativeDiskAnnWrapper {
470
470
  */
471
471
  async hydrateDeltaFromStorage(dim) {
472
472
  // Best-effort: only when the storage adapter supports bulk noun reads
473
- // (production FileSystemStorage/MmapFileSystemStorage do; some partial
473
+ // (production brainy FileSystemStorage does; some partial
474
474
  // adapters used in direct-construction tests do not). When absent, the
475
475
  // caller falls back to the in-memory rebuild.
476
476
  if (typeof this.storage?.getNouns !== 'function')
@@ -35,7 +35,8 @@ import type { ColumnStoreProvider, EntityIdMapperLike } from './columnStoreTypes
35
35
  import { ValueType } from './columnStoreTypes.js';
36
36
  import { RoaringBitmap32 } from '../native/NativeRoaringBitmap32.js';
37
37
  /**
38
- * @description The subset of cor's `MmapFileSystemStorage` that the column
38
+ * @description The subset of brainy's `FileSystemStorage` (whose index files cor
39
+ * memory-maps) that the column
39
40
  * store needs for billion-scale raw segment persistence. cor builds against
40
41
  * published `@soulcraft/brainy`, whose `StorageAdapter` type does not yet
41
42
  * declare these binary-blob methods, so we narrow the adapter to this local
@@ -130,7 +130,7 @@ export class NativeColumnStore {
130
130
  if (!isBinaryBlobStorage(storage)) {
131
131
  throw new Error('NativeColumnStore requires a storage adapter with binary-blob support ' +
132
132
  '(saveBinaryBlob/loadBinaryBlob/deleteBinaryBlob/getBinaryBlobPath). ' +
133
- 'Use cor MmapFileSystemStorage or another adapter implementing BinaryBlobStorage.');
133
+ 'Use brainy FileSystemStorage or another adapter implementing BinaryBlobStorage.');
134
134
  }
135
135
  this.storage = storage;
136
136
  // Discover fields by listing manifest files under the column-index prefix.
@@ -405,13 +405,31 @@ export class MetadataIndexManager {
405
405
  // Resolve the metadata directory for the persistent engine, mirroring
406
406
  // wireIdMapper's filesystem guard. `getBinaryBlobPath` returns null on
407
407
  // non-local adapters → fall back to the in-RAM engine.
408
- const metaDir = typeof blobStorage.getBinaryBlobPath === 'function'
409
- ? blobStorage.getBinaryBlobPath('_metadata')
410
- : null;
408
+ const getBlobPath = blobStorage.getBinaryBlobPath;
409
+ const hasBlobPath = typeof getBlobPath === 'function';
410
+ const metaDir = hasBlobPath ? getBlobPath.call(blobStorage, '_metadata') : null;
411
411
  if (metaDir === null) {
412
412
  // Fallback: non-persistent in-RAM engine (the legacy `new` path).
413
413
  // U64 IdSpace matches the cor 3.0 production default + the metadata
414
414
  // index core constructed above.
415
+ //
416
+ // Two very different situations land here — do NOT let the durable one
417
+ // pass silently (the invisible-degrade the whole GA bar forbids):
418
+ // - method ABSENT → a genuinely non-local adapter (cloud/in-memory);
419
+ // an in-RAM metadata engine is correct. Quiet.
420
+ // - method PRESENT but returned null for '_metadata' → a filesystem-
421
+ // class adapter that should have yielded a path but didn't. The
422
+ // metadata index will run WITHOUT on-disk durability — every restart
423
+ // rebuilds from canonical (the 48s-per-boot class). Announce it LOUD
424
+ // so an operator sees it instead of silently paying the cost forever.
425
+ if (hasBlobPath) {
426
+ prodLog.error('[NativeMetadataIndex] getBinaryBlobPath("_metadata") returned null on ' +
427
+ 'an adapter that implements it — the metadata LSM is running IN-RAM, ' +
428
+ 'NOT persisted. Every restart will rebuild the index from canonical ' +
429
+ '(slow cold opens). This is a durability misconfiguration, not normal ' +
430
+ 'operation — check the storage adapter/path. Reachable durable state is ' +
431
+ 'the "restart = warm" contract.');
432
+ }
415
433
  this.lsmEngine = new bindings.NativeLsmEngine({ idSpace: 'u64' });
416
434
  this.lsmManifest = null;
417
435
  this.lsmMetaDir = null;
@@ -1040,6 +1058,30 @@ export class MetadataIndexManager {
1040
1058
  'NO rebuild.');
1041
1059
  return;
1042
1060
  }
1061
+ // **Tripwire — a rebuild here on a PREVIOUSLY-HEALTHY brain is a bug,
1062
+ // not routine.** A rebuild is legitimate exactly twice: a fresh brain
1063
+ // (no marker) and a 7.x→8.0 upgrade (stale marker) — both have no
1064
+ // durable derived state yet. But if the on-disk brain-format marker is
1065
+ // present AND current, an 8.x/3.x already fully built and STAMPED this
1066
+ // brain's indexes; their absence now means the field registry + LSM
1067
+ // both failed to persist/reload (the "restart = milliseconds" contract
1068
+ // broken — every boot pays an O(N) rebuild). That must never be
1069
+ // silent: surface it at ERROR with the entity count so it's caught the
1070
+ // first time, not after a customer measures a 48s boot.
1071
+ try {
1072
+ const marker = await readOnDiskBrainFormat(this.storage);
1073
+ if (!isEpochStale(marker)) {
1074
+ prodLog.error(`[NativeMetadataIndex] DURABILITY TRIPWIRE: rebuilding ${probe.totalCount ?? 'unknown'} ` +
1075
+ 'entities on a brain whose format marker is present + current — the ' +
1076
+ 'derived indexes (field registry AND LSM) should already be on disk. ' +
1077
+ 'This is an unexpected full rebuild on a previously-healthy brain ' +
1078
+ '(the every-boot-rebuild class); please report it with the brain layout. ' +
1079
+ 'Data is safe (canonical entities intact); only startup time is affected.');
1080
+ }
1081
+ }
1082
+ catch {
1083
+ // marker read failed — fall through to the normal rebuild path
1084
+ }
1043
1085
  console.warn(`[NativeMetadataIndex] Field registry missing but ${probe.totalCount ?? 'unknown'} entities exist — rebuilding index`);
1044
1086
  await this.rebuild();
1045
1087
  return;
@@ -355,7 +355,7 @@ Memory is the canonical first 3.0 consumer (production on Brainy 7.31.6 + Cortex
355
355
 
356
356
  **Memory-specific gotchas:**
357
357
 
358
- - **`mmap-filesystem` storage adapter** is filesystem-backed via Brainy's `MmapFileSystemStorage` — cor 3.0's DiskANN auto-engagement WILL fire here, which is what you want.
358
+ - **Storage** is Brainy's standard `filesystem` adapter (there is no separate "mmap" storage class to configure — cor memory-maps its own index files under whatever filesystem path you give Brainy). cor 3.0's DiskANN auto-engagement WILL fire here, which is what you want.
359
359
  - **Per-user brain cold start** was 8.3 s on Brainy 7.31.3 + Cortex 2.7.0 (per the BRAINY-COR-MMAP-VECTOR-NOT-WIRED thread). On 8.0 + 3.0 with DiskANN + the cor shadow-page LSM, cold start should drop substantially — Adaptive DiskANN auto-selects `in-memory` mode at the per-user scale (each user is currently ≤ 1 K memories), giving sub-millisecond reads with no warm-up. Re-measure after upgrade and update the production-impact section of that thread.
360
360
  - **`db.asOf(g)` / `db.with(...)`** time-travel reads are now available — if Memory wants to surface "what did this user remember last week", that's a feature you can build on top of brainy 8.0's historical query surface without any cor-side work.
361
361
  - **No external Memory user has 1 M+ memories yet**, so the migration window is operationally cheap; do it during the morning low-traffic window per current ops practice.
package/docs/scaling.md CHANGED
@@ -104,9 +104,11 @@ are evicted before small active ones).
104
104
 
105
105
  ### Memory-mapped storage
106
106
 
107
- Cor's `MmapFileSystemStorage` extends Brainy's filesystem storage with
108
- zero-copy binary I/O. The Rust native layer memory-maps data files via
109
- `memmap2`, letting the Linux kernel manage which pages stay in RAM.
107
+ You use Brainy's standard `filesystem` storage — there is no separate
108
+ storage class to choose. Cor's native engines memory-map their own index
109
+ files (vectors, graph adjacency, metadata LSM) via `memmap2`, letting the
110
+ Linux kernel manage which pages stay in RAM. Zero-copy binary I/O comes
111
+ from the native layer, not from a special adapter.
110
112
 
111
113
  What mmap provides:
112
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/cor",
3
- "version": "3.0.3",
3
+ "version": "3.0.4",
4
4
  "description": "Native Rust acceleration for Brainy \u2014 SIMD distance, vector quantization, zero-copy mmap, native embeddings. Free tier for storage, Pro license for compute acceleration.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",