@soulcraft/cor 3.0.0-rc.1

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.
Files changed (87) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +328 -0
  3. package/dist/aggregation/NativeAggregationEngine.d.ts +118 -0
  4. package/dist/aggregation/NativeAggregationEngine.js +186 -0
  5. package/dist/cli.d.ts +12 -0
  6. package/dist/cli.js +236 -0
  7. package/dist/graph/GraphAccelerationAdapter.d.ts +153 -0
  8. package/dist/graph/GraphAccelerationAdapter.js +326 -0
  9. package/dist/graph/NativeGraphAdjacencyIndex.d.ts +286 -0
  10. package/dist/graph/NativeGraphAdjacencyIndex.js +944 -0
  11. package/dist/hnsw/AdaptiveDiskAnnModeSelector.d.ts +187 -0
  12. package/dist/hnsw/AdaptiveDiskAnnModeSelector.js +313 -0
  13. package/dist/hnsw/NativeDiskAnnWrapper.d.ts +489 -0
  14. package/dist/hnsw/NativeDiskAnnWrapper.js +1456 -0
  15. package/dist/index.d.ts +28 -0
  16. package/dist/index.js +29 -0
  17. package/dist/legacyLayoutGuard.d.ts +93 -0
  18. package/dist/legacyLayoutGuard.js +237 -0
  19. package/dist/license/constants.d.ts +30 -0
  20. package/dist/license/constants.js +30 -0
  21. package/dist/license/onlineValidator.d.ts +43 -0
  22. package/dist/license/onlineValidator.js +225 -0
  23. package/dist/license/types.d.ts +39 -0
  24. package/dist/license/types.js +5 -0
  25. package/dist/license.d.ts +64 -0
  26. package/dist/license.js +165 -0
  27. package/dist/migration/MigrationCoordinator.d.ts +171 -0
  28. package/dist/migration/MigrationCoordinator.js +249 -0
  29. package/dist/migration/indexEpochGuard.d.ts +163 -0
  30. package/dist/migration/indexEpochGuard.js +202 -0
  31. package/dist/native/NativeEmbeddingEngine.d.ts +79 -0
  32. package/dist/native/NativeEmbeddingEngine.js +318 -0
  33. package/dist/native/NativeRoaringBitmap32.d.ts +114 -0
  34. package/dist/native/NativeRoaringBitmap32.js +221 -0
  35. package/dist/native/ffi.d.ts +20 -0
  36. package/dist/native/ffi.js +48 -0
  37. package/dist/native/idMapperTestSupport.d.ts +67 -0
  38. package/dist/native/idMapperTestSupport.js +112 -0
  39. package/dist/native/index.d.ts +49 -0
  40. package/dist/native/index.js +87 -0
  41. package/dist/native/napi.d.ts +21 -0
  42. package/dist/native/napi.js +88 -0
  43. package/dist/native/types.d.ts +1298 -0
  44. package/dist/native/types.js +16 -0
  45. package/dist/plugin.d.ts +50 -0
  46. package/dist/plugin.js +388 -0
  47. package/dist/providerContracts.d.ts +277 -0
  48. package/dist/providerContracts.js +38 -0
  49. package/dist/resource/OsMemoryProbe.d.ts +175 -0
  50. package/dist/resource/OsMemoryProbe.js +206 -0
  51. package/dist/resource/ResourceManager.d.ts +491 -0
  52. package/dist/resource/ResourceManager.js +960 -0
  53. package/dist/utils/ColumnManifest.d.ts +97 -0
  54. package/dist/utils/ColumnManifest.js +129 -0
  55. package/dist/utils/NativeColumnStore.d.ts +284 -0
  56. package/dist/utils/NativeColumnStore.js +685 -0
  57. package/dist/utils/NativeMetadataIndex.d.ts +882 -0
  58. package/dist/utils/NativeMetadataIndex.js +2631 -0
  59. package/dist/utils/NativeUnifiedCache.d.ts +87 -0
  60. package/dist/utils/NativeUnifiedCache.js +273 -0
  61. package/dist/utils/binaryIdMapperFactory.d.ts +59 -0
  62. package/dist/utils/binaryIdMapperFactory.js +94 -0
  63. package/dist/utils/collation.d.ts +30 -0
  64. package/dist/utils/collation.js +40 -0
  65. package/dist/utils/columnStoreTypes.d.ts +161 -0
  66. package/dist/utils/columnStoreTypes.js +29 -0
  67. package/dist/utils/nativeBinaryEntityIdMapper.d.ts +211 -0
  68. package/dist/utils/nativeBinaryEntityIdMapper.js +381 -0
  69. package/dist/utils/nativeEntityIdMapper.d.ts +111 -0
  70. package/dist/utils/nativeEntityIdMapper.js +170 -0
  71. package/docs/ADR-002-diskann-100-percent-rust.md +294 -0
  72. package/docs/ADR-003-semantic-time-travel.md +100 -0
  73. package/docs/aggregation.md +251 -0
  74. package/docs/comparison.md +162 -0
  75. package/docs/deployment-limits.md +87 -0
  76. package/docs/diskann.md +184 -0
  77. package/docs/migration-3.0.md +396 -0
  78. package/docs/performance-budget.md +274 -0
  79. package/docs/performance.md +117 -0
  80. package/docs/scaling.md +439 -0
  81. package/docs/snapshot-safety.md +246 -0
  82. package/docs/u64-id-space.md +214 -0
  83. package/docs/verification-report.md +670 -0
  84. package/native/brainy-native.node +0 -0
  85. package/native/index.d.ts +3916 -0
  86. package/package.json +92 -0
  87. package/scripts/migrate-cortex-2x-to-3x.mjs +970 -0
@@ -0,0 +1,396 @@
1
+ ---
2
+ title: Upgrading to Brainy 8.0 + Cor 3.0
3
+ slug: cor/migration-3.0
4
+ public: true
5
+ category: cor
6
+ template: guide
7
+ order: 4
8
+ description: Step-by-step upgrade guide for consumers moving from Brainy 7.x + Cortex 2.x to Brainy 8.0 + Cor 3.0. Covers breaking changes, data-format transitions, the safe backup/restore pattern, and a per-deployment checklist (Memory team gets a worked example).
9
+ next:
10
+ - cor/snapshot-safety
11
+ - cor/diskann
12
+ - cor/u64-id-space
13
+ ---
14
+
15
+ # Upgrading to Brainy 8.0 + Cor 3.0
16
+
17
+ This guide is for anyone running **Brainy 7.x + Cortex 2.x in production** and upgrading to **Brainy 8.0 + Cor 3.0**. It covers what changes on disk, what the safe upgrade flow looks like, what zero-config defaults flip, and what the rollback story is if something goes wrong.
18
+
19
+ > **Scope.** Cor 3.0 + Brainy 8.0 are a coordinated, lock-step release. Don't upgrade one without the other — the napi contracts and on-disk formats are tied. The peerDependency in cor 3.0 is `@soulcraft/brainy@>=8.0.0`.
20
+
21
+ > **Soulcraft posture.** Soulcraft is the only customer of this stack today. Per the 2026-06-10 mandate, data formats are freely broken in 3.0. **The safe upgrade pattern is backup → wipe → reinstall → restore**, NOT in-place migration. Reading raw cortex 2.x on-disk files in cor 3.0 is explicitly NOT supported; the new file formats (shadow-page LSM, memtable log V2, delta log V2) reject pre-3.0 inputs at open. Brainy's `persist()` snapshot format is the format-independent bridge.
22
+
23
+ ---
24
+
25
+ ## TL;DR — automated migration script (recommended for production)
26
+
27
+ Cor 3.0 ships **`scripts/migrate-cortex-2x-to-3x.mjs`** — a checkpointed, idempotent, online-friendly migration tool. Use this for any deployment with real data. Don't hand-roll the four-step flow below unless you're migrating a throwaway dev brain.
28
+
29
+ ```bash
30
+ # Single-brain layout (Venue, Workshop, single-tenant services):
31
+ node node_modules/@soulcraft/cor/scripts/migrate-cortex-2x-to-3x.mjs \
32
+ --brain-dir /opt/venue/data/brain \
33
+ --backup-dir /opt/venue/backups/pre-3.0
34
+
35
+ # Multi-tenant layout (Memory, per-user/per-org brain subdirs):
36
+ node node_modules/@soulcraft/cor/scripts/migrate-cortex-2x-to-3x.mjs \
37
+ --tenants-dir /opt/memory/data/memory/users \
38
+ --backup-dir /opt/memory/backups/pre-3.0 \
39
+ --concurrency 8
40
+
41
+ # Dry-run first to see what will happen:
42
+ node ... --brain-dir /opt/venue/data/brain --backup-dir /tmp/test --dry-run
43
+
44
+ # Resume after a failure:
45
+ node ... --brain-dir /opt/venue/data/brain --backup-dir /opt/venue/backups/pre-3.0 --resume
46
+
47
+ # Rollback to the filesystem backup:
48
+ node ... --brain-dir /opt/venue/data/brain --backup-dir /opt/venue/backups/pre-3.0 --rollback
49
+ ```
50
+
51
+ **The script's guarantees:**
52
+
53
+ - **Zero data loss.** Phase 2 takes a bit-identical `rsync` filesystem backup BEFORE anything else. Phase 6 (wipe) refuses to run unless both the filesystem backup and the logical snapshot have completed and been verified.
54
+ - **Bounded downtime.** Phases 1–4 (preflight, fs-backup, logical snapshot, verify) run while your service is LIVE. Only phases 5–9 (pause-writes → restore) require writes to be quiesced. For a 100 K-entity brain, the writes-paused window is typically < 60 seconds.
55
+ - **Idempotent.** Every phase checkpoints its completion to `<brain-root>/.cor-3.0-migration-state.json`. Crashes are recoverable; re-run with `--resume`.
56
+ - **One-command rollback.** `--rollback` restores from the filesystem backup. Combined with `npm install @soulcraft/brainy@7.31.6 @soulcraft/cortex@2.7.0`, you're back on the old stack in ~5 minutes.
57
+
58
+ Run `node node_modules/@soulcraft/cor/scripts/migrate-cortex-2x-to-3x.mjs --help` for the full flag list.
59
+
60
+ ## The 4-step upgrade (manual, for reference)
61
+
62
+ If you can't run the script (e.g., your deployment environment doesn't have the cor package installed yet, or you're doing a small-scale test migration), the manual flow is:
63
+
64
+ ```bash
65
+ # 1. Snapshot every brain on the old stack (Brainy 7.x + Cortex 2.x)
66
+ node -e "
67
+ const { BrainyData } = require('@soulcraft/brainy')
68
+ for (const path of YOUR_BRAIN_DIRECTORIES) {
69
+ const b = new BrainyData({ storage: { type: 'filesystem', rootDirectory: path } })
70
+ await b.init()
71
+ await b.persist('/backups/' + path.replace(/\\//g, '_') + '.brainy-snapshot')
72
+ }
73
+ "
74
+
75
+ # 2. Upgrade your dependencies
76
+ npm install @soulcraft/brainy@^8.0.0 @soulcraft/cor@^3.0.0
77
+ # (or `bun add ...`)
78
+
79
+ # 3. WIPE the old brain directories (their on-disk format is incompatible)
80
+ rm -rf YOUR_BRAIN_DIRECTORIES
81
+
82
+ # 4. Restore each snapshot through the new stack
83
+ node -e "
84
+ const { BrainyData } = require('@soulcraft/brainy')
85
+ for (const path of YOUR_BRAIN_DIRECTORIES) {
86
+ const b = new BrainyData({ storage: { type: 'filesystem', rootDirectory: path } })
87
+ await b.init()
88
+ await b.restore('/backups/' + path.replace(/\\//g, '_') + '.brainy-snapshot')
89
+ }
90
+ "
91
+ ```
92
+
93
+ The snapshot is JSON-shaped and format-independent. Restore replays every entity through the 3.0 write path, producing 3.0-native files. **No in-place data conversion runs**; the data simply re-lands in the new format because every record was re-written.
94
+
95
+ Time cost: roughly **one minute per 100 K entities** on commodity NVMe — restore is bottlenecked by re-embedding if your Brainy config rebuilds embeddings on write. If your snapshot includes pre-computed vectors, restore is ~5-10× faster.
96
+
97
+ ---
98
+
99
+ ## Breaking changes that matter for the upgrade
100
+
101
+ ### Cor 3.0 — on-disk format changes
102
+
103
+ | Subsystem | Cortex 2.x format | Cor 3.0 format | Compat |
104
+ |---|---|---|---|
105
+ | **Entity ID mapper — `uuid_to_int.mkv`** | Plain `MmapKv` extendible-hash | **Shadow-page LSM** (frozen base + delta log + atomic head pointer) | 3.0 **rejects** 2.x files at open |
106
+ | **Entity ID mapper — `int_to_uuid.bin`** | Plain `I2uStore` mmap'd packed array | **Shadow-page LSM** with `IntToUuidDeltaLog V2` | 3.0 **rejects** 2.x files at open |
107
+ | **Verb endpoints — `verb_endpoints.bin`** | Plain `MmapEndpointsStore` | **Shadow-page LSM** with V2 delta log | 3.0 **rejects** 2.x files at open |
108
+ | **Metadata-index memtable log** | V1 (no generation field) | **V2** (per-record `u64 generation`) | 3.0 **rejects** V1 files at open |
109
+ | **MmapKv delta log** | V1 (no generation field) | **V2** (per-record `u64 generation`) | 3.0 **rejects** V1 files at open |
110
+ | **DiskANN index file (`.diskann`)** | n/a (HNSW only in 2.x) | Adaptive DiskANN — new file | Existing HNSW files keep working through cor 3.0's HNSW provider; auto-engagement to DiskANN happens on first `brain.init()` only if the storage is local filesystem |
111
+ | **SSTable file format** | Cortex 2.4+ stable format | Same | Existing SSTables remain readable |
112
+
113
+ **Why all the rejections?** Cor 3.0 ships *self-contained snapshot safety* (`docs/snapshot-safety.md`) — every persistent file is hard-link-snapshot-safe by construction. That property requires the new file structure (frozen base + atomic head + append-only delta with rotation). The 2.x in-place mutation patterns leak live state into snapshots and can't be patched. The mandate explicitly chose "break the format and migrate" over "carry compatibility code forever."
114
+
115
+ ### Cor 3.0 — default-flip changes (zero-config)
116
+
117
+ | Knob | 2.x default | 3.0 default | Why |
118
+ |---|---|---|---|
119
+ | `idSpace` (entity int wire-width) | `'u32'` (4.29 B cap) | **`'u64'`** (1.84 × 10¹⁹ cap) | Cor 3.0's design floor is 1 B+ entities. u64 adds 4 bytes per slot, no measurable perf cost (`findPerfGates.test.ts F2` still passes). u32 stays available via explicit opt-in for Brainy 7.x-compatible brains. |
120
+ | Verb-id wire-width | `'U32'` (4.29 B verb-IDs) | **`'U64'`** | Same rationale; verbs scale faster than entities under multi-hop graphs. |
121
+ | Metadata-index LSM mode (`useLsm`) | `false` (JSON dispatch) | `useLsm: true` explicit by wrapper | LSM is the verified production path now. |
122
+ | DiskANN mode | `'hybrid'` at raw napi | **`'auto'`** at wrapper layer | `AdaptiveDiskAnnModeSelector` picks in-memory / hybrid / on-disk from RAM + dim + dataset size. |
123
+ | Bloom filter | on at 10 bits/element | unchanged | Production default. |
124
+
125
+ These defaults change ONLY for new brains created on cor 3.0. **Brains restored from a 2.x snapshot retain their pre-existing IdSpace** if the snapshot recorded one — the on-disk file header is authoritative on reopen (per the G4.4 wrapper fix).
126
+
127
+ ### Brainy 8.0 — what the SDK changed
128
+
129
+ Brainy 8.0 has its own `RELEASES.md` entry documenting:
130
+ - `DataAPI` deletion (use `brain.now().persist()` / `restore` / `stats` instead)
131
+ - `requireSubtype: true` by default — existing entities without a subtype need to be migrated via `brain.fillSubtypes()` (helper shipped in `c446783`)
132
+ - Reserved-field contract — `confidence`, etc. now live at the top-level entity, not in `metadata` (auto-remapped on `update`)
133
+ - Full historical query surface (`db.asOf(g)` / `db.with(...)`) backed by cor's pin lifecycle
134
+
135
+ See brainy's `RELEASES.md` for the exhaustive list — that's the canonical source.
136
+
137
+ ---
138
+
139
+ ## The safe upgrade flow, in detail
140
+
141
+ ### Phase 0 — Pre-flight (15 min, on the OLD stack)
142
+
143
+ 1. **Run `npm test` / your equivalent.** Confirm everything is green on 7.x + 2.x before changing anything.
144
+ 2. **Take a filesystem-level snapshot of every brain directory** (`tar`, `rsync`, ZFS snapshot, whatever you trust). This is your atomic rollback if step 3+ goes wrong. The Brainy `persist()` snapshot in step 1 is a *logical* backup; the filesystem snapshot is a *bit-identical* backup.
145
+ 3. **Read brainy's `RELEASES.md` 8.0 entry** (in their repo) for any SDK-shape changes affecting your call sites. Cor 3.0 doesn't change Brainy's API surface — that's brainy's responsibility — but new defaults may affect entities you create.
146
+
147
+ ### Phase 1 — Snapshot (1 min per 100 K entities, on the OLD stack)
148
+
149
+ Run `brain.persist(path)` for every brain. The snapshot is a JSON-shaped file (gzipped) containing every entity, every relation, every metadata field, every vector — format-independent.
150
+
151
+ ```typescript
152
+ import { BrainyData } from '@soulcraft/brainy'
153
+
154
+ const brain = new BrainyData({ storage: { type: 'filesystem', rootDirectory: BRAIN_DIR } })
155
+ await brain.init()
156
+ await brain.persist('/backups/brain.snapshot')
157
+ console.log('snapshot stats:', brain.now().stats())
158
+ ```
159
+
160
+ For multi-tenant deployments (e.g., per-user brains via `BrainyInstancePool`), iterate every tenant's brain directory and snapshot each one independently. Snapshots can run in parallel across tenants — they each touch only their own files.
161
+
162
+ ### Phase 2 — Upgrade dependencies (2 min)
163
+
164
+ ```bash
165
+ # Pin to exact RC1 versions during early adoption; relax to ^8.0.0 / ^3.0.0 after GA.
166
+ npm install @soulcraft/brainy@8.0.0-rc.1 @soulcraft/cor@3.0.0-rc.1
167
+
168
+ # Verify peer-dep satisfied:
169
+ npm ls @soulcraft/brainy @soulcraft/cor
170
+ ```
171
+
172
+ If your environment has multiple lockfiles (monorepo, multiple services), upgrade every one in the same change so a cross-service deploy doesn't mix major versions.
173
+
174
+ ### Phase 3 — Wipe (1 second)
175
+
176
+ ```bash
177
+ rm -rf /data/idx/_id_mapper /data/idx/_metadata /data/idx/_diskann /data/idx/_graph_adjacency
178
+ # If you have other cor-managed paths under /data/idx, see the "cor-managed paths" appendix below.
179
+ ```
180
+
181
+ Why wipe instead of letting the new code overwrite? **The shadow-page LSM stores recognize their own `head` pointer file. A stale 2.x `head` pointer + new 3.0 binary may produce undefined behavior.** Wiping the cor-managed subdirectories is faster than the alternative.
182
+
183
+ If you can't tolerate the wipe (e.g., a brain you can't snapshot for some operational reason), see the **"Read-only museum mode"** appendix at the end.
184
+
185
+ ### Phase 4 — Restore (5-10× faster than the original write, on the NEW stack)
186
+
187
+ ```typescript
188
+ import { BrainyData } from '@soulcraft/brainy'
189
+
190
+ const brain = new BrainyData({ storage: { type: 'filesystem', rootDirectory: BRAIN_DIR } })
191
+ await brain.init() // creates the 3.0-native empty files
192
+ await brain.restore('/backups/brain.snapshot')
193
+
194
+ // Optional: confirm the restored stats match the snapshot stats.
195
+ const stats = brain.now().stats()
196
+ console.log('restored:', stats)
197
+ ```
198
+
199
+ Restore replays every entity through brainy's normal `add()` path. Vectors are NOT recomputed — they ride through the snapshot. Metadata-index posting lists, graph adjacency LSM trees, and the DiskANN index are all built from scratch on the new disk format.
200
+
201
+ For per-tenant brains, run restore in batches matching your operational concurrency cap (typically 4-8 in parallel — each restore is CPU-bound on JSON parsing).
202
+
203
+ ### Phase 5 — Verify (5 min)
204
+
205
+ 1. **Spot-check entity counts** match between snapshot stats and restored stats (per tenant).
206
+ 2. **Run a small set of read queries** with known-good answers — search, getRelations, getAllConnected, getByMetadata. Confirm the results match the pre-upgrade behaviour.
207
+ 3. **Run your production test suite** against the new stack. If you have integration tests that exercise `find()` with metadata + graph + vector composition, this is the moment to verify they still pass — cor 3.0's triple-intelligence find() is sub-millisecond at 1 M (`docs/verification-report.md`), so anything slower than the 2.x baseline is a config problem to investigate before flipping production traffic.
208
+ 4. **Restart the process** (no, don't skip this). Confirm the new on-disk format reopens cleanly, restore-state survives the restart, and the engine reports the expected providers via `brain.diagnostics().providers` (which should show `vectorIndex: { source: 'plugin' }` — DiskANN is engaged).
209
+
210
+ ### Phase 6 — Cutover (operational)
211
+
212
+ Standard blue-green or canary cutover. Cor 3.0 + Brainy 8.0 is the same JS API surface as 7.x / 2.x for the read path; the breaking changes are constructor-time and on-disk-format-time. Once the new stack is green, route traffic.
213
+
214
+ ---
215
+
216
+ ## What if it goes wrong?
217
+
218
+ ### Rollback (any time before Phase 6)
219
+
220
+ ```bash
221
+ # Restore the filesystem-level backup from Phase 0:
222
+ rsync -a /backups/brain-fs-snapshot/ /data/idx/
223
+ # Downgrade dependencies:
224
+ npm install @soulcraft/brainy@7.31.6 @soulcraft/cortex@2.7.0
225
+ # Restart the process.
226
+ ```
227
+
228
+ The filesystem-level backup is bit-identical to the pre-upgrade state. If you skipped Phase 0's tar/rsync snapshot, the logical snapshot from Phase 1 is your fallback — but you'd be restoring an empty 7.x brain and re-importing, which is a different flow than rollback.
229
+
230
+ ### "I upgraded a brain in place and now nothing reads"
231
+
232
+ That's the format-mismatch case. Brainy 8.0 + Cor 3.0 reading a Cortex 2.x file directory will surface an error like:
233
+
234
+ ```
235
+ Error: shadow_page_mmap_kv invariant: head missing or unparseable current_generation
236
+ Error: MmapKvDeltaLog: unsupported version 1
237
+ Error: MemtableLog: unsupported version 1
238
+ ```
239
+
240
+ Fix: shut down the process, wipe the cor-managed subdirectories, restore from the Phase 1 logical snapshot. If you don't have a Phase 1 snapshot, restore from the Phase 0 filesystem snapshot and try the upgrade again with all the phases.
241
+
242
+ ### "Restore is slow"
243
+
244
+ A 50 K-entity brain restoring in more than 10 minutes suggests either: (a) embeddings are being recomputed on write (verify your snapshot includes pre-computed vectors), (b) network storage instead of local NVMe (cor 3.0's DiskANN auto-engagement requires local FS — cloud adapters fall back to the slower HNSW path), or (c) Brainy's `BrainyInstancePool` is evicting before restore finishes (raise the eviction window for the migration window).
245
+
246
+ ### "My idSpace is wrong"
247
+
248
+ If your old brain was `u32` and you accidentally created the new brain with the cor 3.0 default `u64` and restored into it — that's actually fine. The on-disk format upgrades transparently (each u32 int sign-extends into a u64). The reverse (`u64` brain restored into a `u32` brain) errors out at restore time with `EntityIdSpaceExceeded` for any int above 2^53.
249
+
250
+ If you specifically need to keep a brain on `u32` (e.g., interop with a Brainy 7.x-compatible consumer), pass `entityIdMapper: { idSpace: 'u32' }` to the `BrainyData` constructor explicitly. Cor 3.0 honours the explicit choice and rolls back the production default.
251
+
252
+ ---
253
+
254
+ ## Worked example — Venue upgrade (live customer data)
255
+
256
+ Venue is the canonical "real customer data, can't lose anything" deployment. Production runs `^7.30.0` brainy + `^2.7.0` cor via SDK 3.20.1 on `venue-vm` (GCE). The brain holds bookings, customer records, kit configurations, deployment state — none of which can be lost.
257
+
258
+ **Use the automated script. Do not hand-roll.**
259
+
260
+ ```bash
261
+ # On venue-vm, during the morning low-traffic window:
262
+
263
+ # Phase 0: pre-flight check on the running service.
264
+ curl -fsS https://venue.example.com/api/health | jq '.brainy.ok'
265
+ # Expected: true. If false, fix BEFORE upgrading.
266
+
267
+ # Take an out-of-band filesystem backup as belt-and-suspenders.
268
+ # (The script will take its own, but this one is yours to verify
269
+ # manually before the script touches anything.)
270
+ sudo zfs snapshot tank/opt/venue@manual-pre-3.0-2026-06-11
271
+ # OR if no ZFS:
272
+ sudo rsync -a /opt/venue/data/ /opt/venue/backups/manual-pre-3.0-2026-06-11/
273
+
274
+ # Phase 1-4: snapshot + verify (CAN run while service is live;
275
+ # Venue's writes during the snapshot window will land in 7.x but
276
+ # won't make it to the snapshot — see "online-snapshot caveats" below).
277
+ sudo -u venue node /opt/venue/node_modules/@soulcraft/cor/scripts/migrate-cortex-2x-to-3x.mjs \
278
+ --brain-dir /opt/venue/data/brain \
279
+ --backup-dir /opt/venue/backups/cor-3.0-migration
280
+
281
+ # At this point the script will prompt you to confirm "have writes
282
+ # been paused?" before phase 5. ALSO STOP THE SERVICE HERE for safety:
283
+ sudo systemctl stop venue-service
284
+
285
+ # Phase 5-6 will then run (pause-writes confirmation, wipe).
286
+ # Phase 7 will prompt you to upgrade dependencies — do it in ANOTHER
287
+ # terminal:
288
+
289
+ # On venue-vm in a second terminal:
290
+ cd /opt/venue
291
+ sudo -u venue bun install @soulcraft/brainy@^8.0.0-rc.1 @soulcraft/cor@^3.0.0-rc.1
292
+ # Confirm 'y' back in the first terminal.
293
+
294
+ # Phase 8-9 (restore + verify) will then run automatically.
295
+ # Phase 10 writes the completion marker.
296
+
297
+ # Start the service back up.
298
+ sudo systemctl start venue-service
299
+
300
+ # Verify production traffic.
301
+ curl -fsS https://venue.example.com/api/health | jq '.brainy.ok'
302
+ # Expected: true. Confirm a few read paths work end-to-end:
303
+ # - load a customer record
304
+ # - list current bookings
305
+ # - render a published page
306
+ ```
307
+
308
+ **Total operational window for Venue**: typically ~10 minutes for a brain with ≤ 100 K entities. Most of that is in Phase 2 (filesystem backup) which runs while the service is live. Actual downtime — from `systemctl stop venue-service` to `systemctl start` — is the wipe + npm install + restore window, normally < 2 minutes for a Venue-scale brain.
309
+
310
+ **Online-snapshot caveats (important for Venue):**
311
+
312
+ Brainy 7.x's `persist()` captures a consistent snapshot of entities visible at call time. **Concurrent writes during persist() are NOT included in the snapshot.** For Venue, this means: any booking created or customer record updated between `persist()` start and `persist()` end will be LOST if the service is left running during the snapshot.
313
+
314
+ For zero-loss migration, **stop the service at the start of Phase 3 (logical-snapshot)** — don't wait until Phase 5 (pause-writes). The script will accept early write-pausing; just answer `y` to the prompt before Phase 3 too.
315
+
316
+ If your acceptable-loss tolerance is "any write in the last 30 seconds is fine to lose" (typical for blog-shape data, not for booking/financial data), pass `--online-snapshot` and skip the early pause. **Venue should NOT pass `--online-snapshot`.** Memory + Workshop probably can.
317
+
318
+ Brainy 8.0 + Cor 3.0's pin lifecycle makes TRUE online migration possible (Phase 3 captures a consistent snapshot WITHOUT pausing writes, because brainy holds a generation-pin during the snapshot and writes that land after the pin are filtered out at read time). But that's the FUTURE state — you can't use it to migrate TO 3.0, only between 3.0.x versions later. For 2.x → 3.0, the safe path is: stop service → snapshot → wipe → upgrade → restore → start service.
319
+
320
+ **Rollback (if anything breaks):**
321
+
322
+ ```bash
323
+ sudo systemctl stop venue-service
324
+ sudo -u venue node /opt/venue/node_modules/@soulcraft/cor/scripts/migrate-cortex-2x-to-3x.mjs \
325
+ --brain-dir /opt/venue/data/brain \
326
+ --backup-dir /opt/venue/backups/cor-3.0-migration \
327
+ --rollback
328
+ cd /opt/venue && sudo -u venue bun install @soulcraft/brainy@7.31.6 @soulcraft/cortex@2.7.0
329
+ sudo systemctl start venue-service
330
+ ```
331
+
332
+ Total rollback time: ~5 minutes. The filesystem backup is bit-identical; downgrade puts you on the exact pre-migration state.
333
+
334
+ ## Worked example — Workshop upgrade
335
+
336
+ Workshop runs the same dependency stack as Venue (SDK 3.20.1 → `^7.30.0` brainy + `^2.7.0` cor). The migration flow is **identical to Venue's above** with these differences:
337
+
338
+ - Workshop has **per-workspace brain directories** under `{BRAINY_DATA_PATH}/{hash8}/{workspaceId}` rather than a single brain. Use `--tenants-dir` with the workspace root, not `--brain-dir`.
339
+ - Workshop's data is **less write-heavy than Venue's** (workshops are typically authoring sessions, not transactional). The `--online-snapshot` flag is safer to use here than for Venue.
340
+ - Workshop is **planning to migrate to Memory's unified personal data layer** per the MEM-WORKSHOP-VFS-MIGRATION thread. If that migration happens BEFORE the cor 3.0 upgrade, Workshop's per-workspace brains become Memory's responsibility and Workshop's part of the cor 3.0 upgrade trivializes (just bump the SDK peer-dep range).
341
+
342
+ ## Worked example — Memory team upgrade
343
+
344
+ Memory is the canonical first 3.0 consumer (production on Brainy 7.31.6 + Cortex 2.7.0 as of 2026-06-11; per-tenant brains via `BrainyInstancePool` with `strategy: 'per-tenant'`, mmap-filesystem storage at `/opt/memory/data/memory/users/{emailHash}`).
345
+
346
+ | Phase | Action | Memory-specific notes |
347
+ |---|---|---|
348
+ | 0 — Pre-flight | Filesystem-level backup of `/opt/memory/data/` | Use ZFS snapshot if available; `tar` if not. The path tree is ~50 GB at current production scale. |
349
+ | 1 — Snapshot | Iterate `/opt/memory/data/memory/users/*/` and `persist()` each | Use a small concurrency limit (`p-limit(4)`); Memory's per-tenant pool will swap brains in and out as you iterate, which is fine — `persist` only touches the local brain instance. |
350
+ | 2 — Upgrade | `bun add @soulcraft/brainy@8.0.0-rc.1 @soulcraft/cor@3.0.0-rc.1` | Pinned RC1 during early adoption; flip to `^8.0.0 / ^3.0.0` after the GA tags land. Memory's existing `^7.31.0 / ^2.7.0` ranges in `package.json` need a major-version bump in the same change. |
351
+ | 3 — Wipe | `rm -rf /opt/memory/data/memory/users/` (after Phase 1 snapshots verified) | The `getContext` cache + sleep-cycle artifacts are not under this path; they're separate. Memory's `restore()` path doesn't touch them. |
352
+ | 4 — Restore | Iterate snapshots, `restore()` each into the new pool | The sleep cycle will re-consolidate overnight after restore; that's expected — restore re-imports the raw entities, sleep re-derives semantic memories. |
353
+ | 5 — Verify | Per-tenant: stats match, `getContext()` returns sensible bundles, `recall()` on a known memory returns the expected entity | Memory has an `/api/health` endpoint that includes per-tenant brain stats — wire that into the verify step. |
354
+ | 6 — Cutover | Memory is a single Node service; standard blue-green via the `memory-vm` GCE instance group | Memory's import-RPC contract (workshop-vfs-migration spec) continues to work post-upgrade unchanged. |
355
+
356
+ **Memory-specific gotchas:**
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.
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
+ - **`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
+ - **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.
362
+
363
+ ---
364
+
365
+ ## Cor-managed paths (appendix)
366
+
367
+ Cor 3.0 owns the following subdirectories under your storage root (`{rootDirectory}` in your `BrainyData` storage config):
368
+
369
+ ```
370
+ {rootDirectory}/
371
+ ├── _id_mapper/
372
+ │ ├── uuid_to_int/ # ShadowPageMmapKv directory (G4.1)
373
+ │ ├── int_to_uuid/ # ShadowPageI2uStore directory (G4.4)
374
+ │ ├── verb_endpoints/ # ShadowPageEndpointsStore (G4.3)
375
+ │ └── verb-uuid-to-int/ # Verb-id ShadowPageMmapKv (Piece 12)
376
+ ├── _metadata/ # LSM metadata index — SSTables + memtable shards (Piece 11)
377
+ ├── _diskann/ # Adaptive DiskANN index file(s)
378
+ └── _graph_adjacency/ # 4 LSM trees for typed verb tracking (Piece D)
379
+ ```
380
+
381
+ When upgrading, wipe THESE subdirectories. Anything else under `{rootDirectory}` (Brainy's own state, app-level files, the VFS tree) is Brainy's or your app's — leave it alone.
382
+
383
+ ## Read-only museum mode (appendix)
384
+
385
+ If a brain is permanently read-only and you can't tolerate the wipe (e.g., a regulatory-archive brain that can't be re-written for compliance reasons), the supported pattern is **mount the old brain on a Brainy 7.x + Cortex 2.x sidecar process, expose its read path via your service's RPC, and have the new Brainy 8.0 + Cor 3.0 service proxy reads to it for those specific brain ids.** Cor 3.0 does not read 2.x on-disk files in-process.
386
+
387
+ This pattern is operationally heavier than the upgrade — only use it for genuinely-immutable archives.
388
+
389
+ ---
390
+
391
+ ## What I'd watch for in the first week post-upgrade
392
+
393
+ - **`brain.diagnostics().providers`** should report `vectorIndex: { source: 'plugin' }` (DiskANN engaged, not the fallback HNSW). If it reports HNSW, your storage adapter isn't returning a local filesystem path from `getBinaryBlobPath()` — switch to `{ type: 'filesystem', rootDirectory: '/path' }` per `docs/deploy.md`.
394
+ - **First read after a process restart**. Should be sub-millisecond at < 100 K entities. If it's > 5 ms, the engine is in cold-cache + warming mode — second read should be hot. Anything still slow is a config problem; check `idSpace` (should be `'u64'` per `getIdSpace()`) and verify the DiskANN file resides on the same NVMe as the process.
395
+ - **Memory pressure under multi-tenant load**. `AdaptiveDiskAnnModeSelector` auto-demotes from in-memory to hybrid under sustained pressure — observe `brain.diagnostics().memory` and `brain.diagnostics().diskannMode` to confirm.
396
+ - **`find()` composed latency**. Cor 3.0 ships triple-intelligence find() at p50 0.230 ms / p99 0.355 ms at 1 M (`docs/verification-report.md`). If your production latency is meaningfully worse than that at comparable scale, the composition is broken somewhere — most often a metadata filter that the LSM index doesn't have a posting list for, falling back to a sequential scan. Run `brain.diagnostics().lsm.fields` to confirm every filterable field has a built posting list.