rag-memory-epf-mcp 6.1.0 → 6.3.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.
package/README.md CHANGED
@@ -16,7 +16,7 @@ A **project-local RAG memory** MCP server — knowledge graph + multilingual vec
16
16
  - **38 MCP tools** — knowledge graph CRUD, observation lifecycle (correct / retract / history), document pipeline, hybrid search, multi-hop traversal, graph analytics (centrality / community detection / structure), export/import, temporal queries
17
17
  - **Observations that hold their history** — corrections supersede instead of overwrite, search returns only current facts, and every revision keeps its provenance
18
18
  - **Structure-anchored chunking (c1, v5)** — boundaries anchor to markdown structure (fence-aware, H1–H4 first, block-greedy, exact-token fallback), so editing the top of a file no longer re-embeds the whole document: unchanged text reuses its stored vectors at sync time. Chunk offsets are Unicode codepoints, language-neutral across SQL `substr`, Python slicing, and JS `[...str]` iteration; a publish-time invariant gate locks the gap-free partition. `overlap` is retired (omit or 0).
19
- - **SQLite optimized** — WAL mode, 32MB cache, 256MB mmap, FTS5 triggers, 7 indexes
19
+ - **SQLite optimized** — WAL mode, 32MB cache, FTS5 triggers, 7 indexes; mmap is opt-in (`RAG_MEMORY_MMAP_SIZE`, default 0 since 6.3.0 — see Environment Variables)
20
20
  - **MCP SDK 1.27.1** — Tool Annotations (readOnly/destructive/idempotent), latest protocol 2025-11-25
21
21
 
22
22
  ## Quick Start
@@ -145,7 +145,8 @@ storeDocument(id, content, metadata)
145
145
 
146
146
  | Variable | Default | Description |
147
147
  |----------|---------|-------------|
148
- | `DB_FILE_PATH` | `rag-memory.db` (server dir) | Path to project-local SQLite database |
148
+ | `DB_FILE_PATH` | `rag-memory.db` (server dir) | Path to project-local SQLite database. A **relative** path resolves against the server's working directory (since 6.3.0; before that it resolved against the package install directory, which under `npx` is the npm cache) |
149
+ | `RAG_MEMORY_MMAP_SIZE` | `0` | SQLite `mmap_size` in bytes. **Off by default since 6.3.0** — with mmap on, one writer + two readers on a Google Drive folder (Windows) produced `database disk image is malformed` reads; set e.g. `268435456` to restore the previous behaviour. The applied value is printed in the boot banner (`| mmap <n>`) |
149
150
  | `EMBEDDING_MODEL` | `Xenova/bge-m3` | HuggingFace model ID for embeddings |
150
151
  | `RAG_MEMORY_EMBEDDINGS` | `lazy` | Boot mode: `lazy` (connect instantly, model loads in background), `eager` (wait for model + reconciliation, pre-3.6 behavior), `off` (never load the model — FTS5-only, zero download) |
151
152
  | `RAG_MEMORY_MODEL_CACHE_DIR` | OS user cache | Version-independent model cache location (see `docs/UPDATING.md`) |
@@ -153,6 +154,60 @@ storeDocument(id, content, metadata)
153
154
 
154
155
  ## Changelog
155
156
 
157
+ ### v6.3.1
158
+
159
+ - **Changed — the engine empties the WAL while it is idle.** It runs `wal_checkpoint(TRUNCATE)` itself:
160
+ on a 1 s tick whenever the `-wal` file has bytes in it, ~200 ms after a tool call, first thing in the
161
+ signal handler, and before closing. SQLite folds the WAL only when the *last* connection closes and
162
+ only if that close gets to run; with several engines on one file, or a host that kills the engine
163
+ (measured: codex-cli 0.155.1 sends SIGTERM and SIGKILLs ~185 ms later; Windows terminates children
164
+ outright), frames stayed in the WAL — on 6.3.0 a fresh database sat at 4 KB main + 663 KB WAL for as
165
+ long as the engine ran. In a cloud-synced folder such a WAL can be replayed onto a main file written
166
+ by another machine. A write made in the last ~1 s before a hard kill can still be in the WAL; a 0-byte
167
+ `-wal` and a `-shm` left behind by a hard kill are harmless. Running engines on two machines against
168
+ the same synced file at the same time is still unsafe.
169
+ - **Changed — SIGHUP and SIGBREAK shut the engine down cleanly.** Closing the terminal or multiplexer
170
+ pane used to kill the process by default signal action, before the database was closed.
171
+ - No tool, argument, return shape or schema changed.
172
+
173
+ ### v6.3.0 (2026-09-16)
174
+
175
+ - **Changed — a relative `DB_FILE_PATH` now resolves against the server's working directory.** It used
176
+ to resolve against the package install directory, which under `npx` is the npm cache: a relative
177
+ value opened a database in a place nobody looks, with no error. Absolute paths and the unset default
178
+ are unchanged. (Framework spec 2026-09-14 §11-1.) If you relied on the old install-directory
179
+ resolution of a relative path, set an absolute path.
180
+ - **Changed — SQLite `mmap_size` is opt-in, default 0.** Measured 2026-09-15 on a Google Drive folder
181
+ (Windows, WAL): with mmap 256 MB, one writer + two readers produced 373,684 `database disk image is
182
+ malformed` reads in 20 s; with mmap 0, none. `RAG_MEMORY_MMAP_SIZE=<bytes>` restores it; the boot
183
+ banner reports the applied value. (Spec §11-2.)
184
+
185
+ ### v6.1.0
186
+
187
+ **Versioning note.** This content was tagged `v6.0.2` locally before release, but npm never received
188
+ a 6.0.2 — it published as **6.1.0**. Coming from any 6.0.x, upgrading to 6.1.0 brings exactly this
189
+ fix.
190
+
191
+ - **Fixed — a failure mid-deletion could leave partial state.** Deleting an entity ran its cleanup
192
+ steps (embeddings → chunk associations → relationships → the entity row) with no wrapping
193
+ transaction, so a failure in the middle committed the earlier steps: embeddings and links purged
194
+ while the entity itself survived. Each entity's deletion is now one transaction — any step fails,
195
+ that entity's whole sequence rolls back. Batch semantics are unchanged: other entities still
196
+ proceed when one fails.
197
+ - **Fixed — deleting an entity left its knowledge-graph chunks behind.** Two gaps compounded: the
198
+ delete path never invoked the entity-chunk sweeper, and KG relationship chunks are keyed by
199
+ `relationship_id`, so once the relationship rows were deleted their ids could no longer be found
200
+ and those chunks dangled forever — still vector-searchable after both of their endpoints were
201
+ gone. Deletion now captures relationship ids *before* removing the rows and sweeps both: stale
202
+ entity chunks via the existing `deleteStaleKgChunks`, captured relationship chunks via the new
203
+ `deleteKgRelationshipChunks` projection helper. Practical exposure today is bounded — the chunk
204
+ generation path is dormant (not tool-exposed) — but once seeded these chunks stay retrievable
205
+ unless swept here.
206
+ - Regression: `test/delete-entities-kg-hygiene.test.mjs` (registered in `verify:engine`) covers both,
207
+ verified RED before the fix; a mutation check confirms that removing the transaction reproduces the
208
+ partial state (`entities_alive=1` with `relationships_left=0`). Full suite green (`npm test`,
209
+ 46-call chain).
210
+
156
211
  ### v6.0.1
157
212
 
158
213
  **Versioning note.** Both changes below stop links from being created that should never have been
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ import { EmbeddingGate } from './src/embeddingGate.js';
4
4
  import type { EmbedPriority } from './src/embeddingGate.js';
5
5
  import { BackfillCoordinator } from './src/backfillCoordinator.js';
6
6
  export declare function compileFtsLiteralQuery(q: string): string | null;
7
+ export declare function resolveDbFilePath(envValue: string | undefined, cwd: string, serverDir: string): string;
8
+ export declare function parseMmapSize(raw: string | undefined): number;
7
9
  interface Entity {
8
10
  name: string;
9
11
  entityType: string;
@@ -71,6 +73,7 @@ export declare class RAGKnowledgeGraphManager {
71
73
  grandfatherAllowed: boolean;
72
74
  coordinator: BackfillCoordinator | null;
73
75
  readonly calendarTimeZone: string;
76
+ mmapApplied: number;
74
77
  private embeddingCache;
75
78
  private readonly EMBEDDING_CACHE_MAX;
76
79
  private dictionaryCache;
@@ -98,6 +101,11 @@ export declare class RAGKnowledgeGraphManager {
98
101
  description: string;
99
102
  }>;
100
103
  }>;
104
+ checkpointWal(): boolean;
105
+ private walTimer;
106
+ private walSoon;
107
+ startWalKeeper(tickMs?: number): void;
108
+ noteActivity(delayMs?: number): void;
101
109
  cleanup(): void;
102
110
  private _timestampObservation;
103
111
  createEntities(entities: Array<Entity & {
package/dist/index.js CHANGED
@@ -59,7 +59,7 @@ function sanitizeErrorMessage(msg) {
59
59
  }
60
60
  // v3.6: startup self-report banner (version reliability — spec §8).
61
61
  function printBanner(opts) {
62
- console.error(`🚀 rag-memory-epf-mcp v${PKG_VERSION} | node v${process.versions.node} | model ${opts.model}@${opts.revision} (${opts.dtype}) | cache ${opts.cachePath} | db ${opts.dbPath}`);
62
+ console.error(`🚀 rag-memory-epf-mcp v${PKG_VERSION} | node v${process.versions.node} | model ${opts.model}@${opts.revision} (${opts.dtype}) | cache ${opts.cachePath} | db ${opts.dbPath} | mmap ${opts.mmap}`);
63
63
  }
64
64
  // v3.6 (spec §5): ONE FTS5 literal-query compiler shared by chunk and entity
65
65
  // search — raw user input can never produce MATCH syntax errors or trigger
@@ -79,13 +79,36 @@ export function compileFtsLiteralQuery(q) {
79
79
  if (env.backends?.onnx?.wasm) {
80
80
  env.backends.onnx.wasm.wasmPaths = './node_modules/@huggingface/transformers/dist/';
81
81
  }
82
- // Define database file path using environment variable with fallback
83
- const defaultDbPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'rag-memory.db');
84
- const DB_FILE_PATH = process.env.DB_FILE_PATH
85
- ? path.isAbsolute(process.env.DB_FILE_PATH)
86
- ? process.env.DB_FILE_PATH
87
- : path.join(path.dirname(fileURLToPath(import.meta.url)), process.env.DB_FILE_PATH)
88
- : defaultDbPath;
82
+ // Define database file path using environment variable with fallback.
83
+ // Next release (major candidate; the version is set at release time — framework spec
84
+ // 2026-09-14-universal-mcp-config-design §11-1): a RELATIVE DB_FILE_PATH
85
+ // resolves against the server's working directory (process.cwd()), the way every other CLI tool
86
+ // reads a relative path. Before this it resolved against the package's own install directory —
87
+ // under npx that is the npm cache, so a relative value silently opened a database nobody could
88
+ // find (measured 2026-09-14 on 6.1.0 dist/index.js:83-88) and every framework config had to carry
89
+ // a machine-specific absolute path. The UNSET default is deliberately unchanged (README contract:
90
+ // `rag-memory.db` next to the server); the framework always sets DB_FILE_PATH explicitly instead.
91
+ // Exported so the contract is unit-testable without writing a database into dist/.
92
+ export function resolveDbFilePath(envValue, cwd, serverDir) {
93
+ if (!envValue)
94
+ return path.join(serverDir, 'rag-memory.db');
95
+ return path.isAbsolute(envValue) ? envValue : path.resolve(cwd, envValue);
96
+ }
97
+ const DB_FILE_PATH = resolveDbFilePath(process.env.DB_FILE_PATH, process.cwd(), path.dirname(fileURLToPath(import.meta.url)));
98
+ // Next release (spec §11-2): SQLite mmap is opt-in, default 0 (off). Measured 2026-09-15 (Windows, Google
99
+ // Drive G:, WAL, mmap 256 MB): one writer + two readers -> 373,684 `database disk image is
100
+ // malformed` reads in 20 s; mmap 0 -> 0 errors; local disk -> 0 either way. On macOS Drive the same
101
+ // probe read clean but the WAL grew to ~4 GB in 20 s with mmap on (n=1). The framework opens one
102
+ // DB from several CLIs on synced folders, so correctness wins over an unmeasured read-speed gain.
103
+ // RAG_MEMORY_MMAP_SIZE=<bytes> turns it back on; garbage or negative values count as 0. The value
104
+ // SQLite actually applied is read back and printed in the boot banner (`| mmap <n>`).
105
+ export function parseMmapSize(raw) {
106
+ if (raw === undefined || raw.trim() === '')
107
+ return 0;
108
+ const n = Number(raw);
109
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;
110
+ }
111
+ const MMAP_SIZE = parseMmapSize(process.env.RAG_MEMORY_MMAP_SIZE);
89
112
  const EMBEDDING_MODEL = process.env.EMBEDDING_MODEL || 'Xenova/bge-m3';
90
113
  // v3.5 default model config — grandfathering legacy vectors is only automatic
91
114
  // when the current config matches this (spec §6b custom-model guard). An
@@ -183,6 +206,8 @@ export class RAGKnowledgeGraphManager {
183
206
  // The calendar that date-only human labels are written in. Resolved once, here, so an invalid
184
207
  // zone fails at construction instead of quietly writing wrong days for weeks.
185
208
  calendarTimeZone = resolveCalendarTimeZone(process.env.RAG_MEMORY_CALENDAR_TZ);
209
+ // The mmap_size SQLite actually applied at initialize() (-1 until then). Banner-only.
210
+ mmapApplied = -1;
186
211
  embeddingCache = new Map();
187
212
  EMBEDDING_CACHE_MAX = 500;
188
213
  dictionaryCache = null;
@@ -201,7 +226,10 @@ export class RAGKnowledgeGraphManager {
201
226
  this.db.pragma('busy_timeout = 5000');
202
227
  this.db.pragma('cache_size = -32000');
203
228
  this.db.pragma('temp_store = MEMORY');
204
- this.db.pragma('mmap_size = 268435456');
229
+ // Opt-in mmap (see MMAP_SIZE above). Read the applied value back so the banner reports
230
+ // what SQLite did, not what we asked for (a compile-time cap can lower it silently).
231
+ this.db.pragma(`mmap_size = ${MMAP_SIZE}`);
232
+ this.mmapApplied = Number(this.db.pragma('mmap_size', { simple: true }));
205
233
  this.db.pragma('foreign_keys = ON');
206
234
  // spec §5.2: 관찰 lifecycle 의 무결성은 전부 FK CASCADE 를 전제한다 — root 를 지우면
207
235
  // revision 이, revision 을 지우면 source 가 따라가야 history 가 고아로 남지 않는다.
@@ -565,13 +593,78 @@ export class RAGKnowledgeGraphManager {
565
593
  }))
566
594
  };
567
595
  }
596
+ // Fold the WAL into the main file and empty it. Returns true when the WAL holds no
597
+ // frames afterwards.
598
+ //
599
+ // Why the engine does this itself instead of leaving it to SQLite: SQLite folds the WAL
600
+ // only when the LAST connection closes, and only if that close gets to run. Neither holds
601
+ // here — several engines keep the same file open (one per CLI), and some hosts end the
602
+ // engine without letting it finish (codex-cli 0.155.1, measured: SIGTERM, then SIGKILL
603
+ // ~185 ms later; on Windows a child is simply terminated). A WAL with frames that outlives
604
+ // its process is replayed at the next open against whatever main file is there by then, and
605
+ // in a cloud-synced folder that can be a main file written by another machine. SQLite does
606
+ // not check that a WAL belongs to the main file next to it. That corrupted a live database
607
+ // twice. So the invariant is kept continuously: while idle, the WAL is empty.
608
+ //
609
+ // TRUNCATE, not PASSIVE: PASSIVE copies the frames into the main file but leaves them in
610
+ // the WAL, still valid, still replayable onto a foreign main file.
611
+ // busy_timeout is dropped to 0 around the call: TRUNCATE runs the busy handler, and a 5 s
612
+ // stall inside a signal handler or a timer tick is worse than retrying on the next tick.
613
+ checkpointWal() {
614
+ if (!this.db)
615
+ return true;
616
+ try {
617
+ const st = fsSync.statSync(DB_FILE_PATH + '-wal', { throwIfNoEntry: false });
618
+ if (!st || st.size === 0)
619
+ return true;
620
+ this.db.pragma('busy_timeout = 0');
621
+ try {
622
+ const r = this.db.pragma('wal_checkpoint(TRUNCATE)');
623
+ return r?.[0]?.busy === 0;
624
+ }
625
+ finally {
626
+ this.db.pragma('busy_timeout = 5000');
627
+ }
628
+ }
629
+ catch {
630
+ return false; // inside a transaction, or the file is locked — the next tick retries
631
+ }
632
+ }
633
+ walTimer = null;
634
+ walSoon = null;
635
+ // Every write path ends up here without having to be listed: the periodic tick looks at
636
+ // the size of the -wal file, not at which tool ran, so background writers (reconciliation,
637
+ // backfill) are covered as well. noteActivity() only shortens the wait after a tool call.
638
+ startWalKeeper(tickMs = 1000) {
639
+ if (this.walTimer)
640
+ return;
641
+ this.walTimer = setInterval(() => { this.checkpointWal(); }, tickMs);
642
+ this.walTimer.unref();
643
+ }
644
+ noteActivity(delayMs = 200) {
645
+ if (this.walSoon)
646
+ return;
647
+ this.walSoon = setTimeout(() => { this.walSoon = null; this.checkpointWal(); }, delayMs);
648
+ this.walSoon.unref();
649
+ }
568
650
  cleanup() {
651
+ if (this.walTimer) {
652
+ clearInterval(this.walTimer);
653
+ this.walTimer = null;
654
+ }
655
+ if (this.walSoon) {
656
+ clearTimeout(this.walSoon);
657
+ this.walSoon = null;
658
+ }
569
659
  if (this.encoding) {
570
660
  this.encoding.free();
571
661
  this.encoding = null;
572
662
  }
573
663
  this.embeddingCache.clear();
574
664
  if (this.db) {
665
+ // close() folds the WAL only for the last connection; with other engines attached it
666
+ // would leave every frame behind.
667
+ this.checkpointWal();
575
668
  this.db.close();
576
669
  this.db = null;
577
670
  }
@@ -4033,6 +4126,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4033
4126
  try {
4034
4127
  // Validate arguments using our structured schema
4035
4128
  const validatedArgs = validateToolArgs(name, args);
4129
+ ragKgManager.noteActivity(); // fold the WAL shortly after this call, whatever it wrote
4036
4130
  switch (name) {
4037
4131
  // Original MCP tools
4038
4132
  case "createEntities":
@@ -4175,6 +4269,7 @@ async function main() {
4175
4269
  model: EMBEDDING_MODEL, revision: MODEL_REVISION, dtype: MODEL_DTYPE,
4176
4270
  cachePath: resolveModelCacheDir(process.env, process.platform, os.homedir()),
4177
4271
  dbPath: DB_FILE_PATH,
4272
+ mmap: ragKgManager.mmapApplied,
4178
4273
  });
4179
4274
  if (ragKgManager.embeddingsMode === 'eager') {
4180
4275
  // eager = wait for BOTH the first model load attempt and reconciliation to
@@ -4206,6 +4301,9 @@ async function main() {
4206
4301
  if (shuttingDown)
4207
4302
  return;
4208
4303
  shuttingDown = true;
4304
+ // Synchronously, before anything is awaited: a host may SIGKILL before the settle
4305
+ // sequence below completes (codex: ~185 ms after SIGTERM).
4306
+ ragKgManager.checkpointWal();
4209
4307
  void (async () => {
4210
4308
  try {
4211
4309
  await server.close();
@@ -4221,6 +4319,13 @@ async function main() {
4221
4319
  };
4222
4320
  process.on('SIGINT', shutdown);
4223
4321
  process.on('SIGTERM', shutdown);
4322
+ // SIGHUP = the terminal or multiplexer pane was closed. Without a handler the default
4323
+ // action kills the process before 'exit' listeners run, so the database is never closed.
4324
+ // On Windows, Node raises SIGHUP when the console window is closed and SIGBREAK on
4325
+ // Ctrl+Break; registering them is harmless where they never fire.
4326
+ process.on('SIGHUP', shutdown);
4327
+ process.on('SIGBREAK', shutdown);
4328
+ ragKgManager.startWalKeeper();
4224
4329
  process.on('exit', () => { try {
4225
4330
  ragKgManager.cleanup();
4226
4331
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "rag-memory-epf-mcp",
3
- "version": "6.1.0",
3
+ "version": "6.3.1",
4
4
  "engines": {
5
5
  "node": ">=24"
6
6
  },
7
- "description": "Project-local RAG memory MCP server \u2014 knowledge graph + multilingual vector + FTS5 in a single SQLite file. Per-project isolation, 38 MCP tools, codepoint-safe chunking (Korean/CJK/emoji).",
7
+ "description": "Project-local RAG memory MCP server knowledge graph + multilingual vector + FTS5 in a single SQLite file. Per-project isolation, 38 MCP tools, codepoint-safe chunking (Korean/CJK/emoji).",
8
8
  "keywords": [
9
9
  "mcp",
10
10
  "model-context-protocol",
@@ -45,7 +45,7 @@
45
45
  "prepare": "npm run build",
46
46
  "watch": "tsc --watch",
47
47
  "verify:invariants": "node test/chunk-invariants.test.mjs",
48
- "verify:engine": "node test/engine-smoke.test.mjs && node test/launch-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs && node test/entity-embed-cap.test.mjs && node test/migration12.test.mjs && node test/model-cache.test.mjs && node test/embedding-gate.test.mjs && node test/lazy-boot.test.mjs && node test/reconciliation.test.mjs && node test/backfill.test.mjs && node test/fts-query.test.mjs && node test/search-contracts.test.mjs && node test/tool-contracts.test.mjs && node test/bounded-exit.test.mjs && node test/observation-schema.test.mjs && node test/search-graph-default.test.mjs && node test/observation-migration.test.mjs && node test/observation-lifecycle.test.mjs && node test/observation-contracts.test.mjs && node test/observation-search.test.mjs && node test/observation-cascade.test.mjs && node test/observation-realdata.test.mjs && node test/chunker-c.test.mjs && node test/migration14.test.mjs && node test/chunk-params-validation.test.mjs && node test/vector-reuse.test.mjs && node test/entity-range-linking.test.mjs && node test/alias-link-gate.test.mjs && node test/entity-name-boundary.test.mjs && node test/stats-chunking.test.mjs && node test/migration14-realdata.test.mjs && node test/migration14-realdata-sync.test.mjs && node test/search-summaries-off.test.mjs && node test/document-return-contracts.test.mjs && node test/observation-date-prefix.test.mjs && node test/delete-entities-cascade.test.mjs && node test/delete-entities-kg-hygiene.test.mjs && node test/backup-publish-portable.test.mjs && node test/graph-context-explain.test.mjs && node test/eval-graph-role-libs.test.mjs && node test/eval-graph-role-t5b.test.mjs && node --test test/eval-graph-role-t8-fix.test.mjs && node --test test/eval-graph-role-t7-upstream.test.mjs && node --test test/eval-graph-role-t11-decision.test.mjs && node --test test/eval-graph-role-prereq-fix.test.mjs",
48
+ "verify:engine": "node test/engine-smoke.test.mjs && node test/launch-smoke.test.mjs && node test/sync-atomicity.test.mjs && node test/dedup.test.mjs && node test/search-degradation.test.mjs && node test/entity-embed-cap.test.mjs && node test/migration12.test.mjs && node test/model-cache.test.mjs && node test/embedding-gate.test.mjs && node test/lazy-boot.test.mjs && node test/reconciliation.test.mjs && node test/backfill.test.mjs && node test/fts-query.test.mjs && node test/search-contracts.test.mjs && node test/tool-contracts.test.mjs && node test/bounded-exit.test.mjs && node test/observation-schema.test.mjs && node test/search-graph-default.test.mjs && node test/observation-migration.test.mjs && node test/observation-lifecycle.test.mjs && node test/observation-contracts.test.mjs && node test/observation-search.test.mjs && node test/observation-cascade.test.mjs && node test/observation-realdata.test.mjs && node test/chunker-c.test.mjs && node test/migration14.test.mjs && node test/chunk-params-validation.test.mjs && node test/vector-reuse.test.mjs && node test/entity-range-linking.test.mjs && node test/alias-link-gate.test.mjs && node test/entity-name-boundary.test.mjs && node test/stats-chunking.test.mjs && node test/migration14-realdata.test.mjs && node test/migration14-realdata-sync.test.mjs && node test/search-summaries-off.test.mjs && node test/document-return-contracts.test.mjs && node test/observation-date-prefix.test.mjs && node test/delete-entities-cascade.test.mjs && node test/delete-entities-kg-hygiene.test.mjs && node test/backup-publish-portable.test.mjs && node test/graph-context-explain.test.mjs && node test/eval-graph-role-libs.test.mjs && node test/eval-graph-role-t5b.test.mjs && node --test test/eval-graph-role-t8-fix.test.mjs && node --test test/eval-graph-role-t7-upstream.test.mjs && node --test test/eval-graph-role-t11-decision.test.mjs && node --test test/eval-graph-role-prereq-fix.test.mjs && node test/db-path.test.mjs && node test/mmap-option.test.mjs && node test/wal-exit.test.mjs",
49
49
  "test": "npm run build && npm run verify:invariants && npm run verify:engine",
50
50
  "prepublishOnly": "npm run build && npm run verify:invariants && npm run verify:engine"
51
51
  },
@@ -68,4 +68,4 @@
68
68
  "shx": "^0.3.4",
69
69
  "typescript": "^5.6.2"
70
70
  }
71
- }
71
+ }