gitnexus 1.6.10-rc.13 → 1.6.10-rc.15
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/dist/cli/analyze.js +60 -2
- package/dist/cli/clean.js +17 -3
- package/dist/cli/cli-message.d.ts +1 -1
- package/dist/cli/doctor.d.ts +13 -0
- package/dist/cli/doctor.js +35 -0
- package/dist/cli/i18n/en.d.ts +5 -4
- package/dist/cli/i18n/en.js +5 -4
- package/dist/cli/i18n/resources.d.ts +6 -4
- package/dist/cli/i18n/zh-CN.d.ts +1 -0
- package/dist/cli/i18n/zh-CN.js +5 -4
- package/dist/cli/index.js +1 -1
- package/dist/core/augmentation/engine.js +4 -3
- package/dist/core/embeddings/embedding-pipeline.d.ts +20 -0
- package/dist/core/embeddings/embedding-pipeline.js +10 -2
- package/dist/core/incremental/escalation-gate.d.ts +38 -0
- package/dist/core/incremental/escalation-gate.js +48 -0
- package/dist/core/incremental/shadow-candidates.d.ts +3 -3
- package/dist/core/incremental/shadow-candidates.js +3 -3
- package/dist/core/incremental/subgraph-extract.d.ts +2 -2
- package/dist/core/incremental/subgraph-extract.js +2 -2
- package/dist/core/lbug/cypher-escape.d.ts +20 -0
- package/dist/core/lbug/cypher-escape.js +20 -0
- package/dist/core/lbug/lbug-adapter.d.ts +147 -1
- package/dist/core/lbug/lbug-adapter.js +384 -55
- package/dist/core/lbug/lbug-config.d.ts +28 -0
- package/dist/core/lbug/lbug-config.js +114 -4
- package/dist/core/lbug/sidecar-recovery.d.ts +140 -0
- package/dist/core/lbug/sidecar-recovery.js +327 -9
- package/dist/core/run-analyze.js +384 -106
- package/dist/core/wiki/graph-queries.js +6 -5
- package/dist/storage/repo-manager.d.ts +33 -0
- package/package.json +1 -1
- package/scripts/cross-platform-tests.ts +13 -0
package/dist/cli/analyze.js
CHANGED
|
@@ -12,13 +12,14 @@ import os from 'os';
|
|
|
12
12
|
import { spawn } from 'child_process';
|
|
13
13
|
import v8 from 'v8';
|
|
14
14
|
import cliProgress from 'cli-progress';
|
|
15
|
-
import { isLbugReady } from '../core/lbug/lbug-adapter.js';
|
|
15
|
+
import { isLbugReady, LbugWipeError } from '../core/lbug/lbug-adapter.js';
|
|
16
16
|
import { boundedCheckpointBeforeExit } from '../core/lbug/shutdown-helpers.js';
|
|
17
|
-
import { isLbugCheckpointIoError, isWalCorruptionError, parseWalCheckpointThreshold, WAL_RECOVERY_SUGGESTION, } from '../core/lbug/lbug-config.js';
|
|
17
|
+
import { getOsPageSize, isLbugCheckpointIoError, isLbugPageSizeFrameError, isPageSizeAwareLadybug, isWalCorruptionError, parseWalCheckpointThreshold, WAL_RECOVERY_SUGGESTION, } from '../core/lbug/lbug-config.js';
|
|
18
18
|
import { getStoragePaths, getGlobalRegistryPath, RegistryNameCollisionError, AnalysisNotFinalizedError, assertAnalysisFinalized, } from '../storage/repo-manager.js';
|
|
19
19
|
import { getGitRoot, hasGitDir, getDefaultBranch } from '../storage/git.js';
|
|
20
20
|
import { loadAnalyzeConfig, mergeAnalyzeOptions, resolveDefaultBranch, validateBranchName, GitNexusRcError, } from './analyze-config.js';
|
|
21
21
|
import { runFullAnalysis } from '../core/run-analyze.js';
|
|
22
|
+
import { getRuntimeFingerprint } from '../core/platform/capabilities.js';
|
|
22
23
|
import { getMaxFileSizeBannerMessage } from '../core/ingestion/utils/max-file-size.js';
|
|
23
24
|
import { warnMissingOptionalGrammars, getOptionalGrammarExtensions } from './optional-grammars.js';
|
|
24
25
|
import { glob } from 'glob';
|
|
@@ -1237,6 +1238,63 @@ const analyzeCommandImpl = async (inputPath, cliOptions) => {
|
|
|
1237
1238
|
process.exitCode = 1;
|
|
1238
1239
|
return;
|
|
1239
1240
|
}
|
|
1241
|
+
// DB-family wipe failure (#2409, tri-review 4669518496 P2-4): the rebuild
|
|
1242
|
+
// could not verify the LadybugDB file family was removed — usually another
|
|
1243
|
+
// process (MCP server, serve worker, antivirus) holding the index open.
|
|
1244
|
+
// Keyed on the error *type* (repo norm from #2385), never message text.
|
|
1245
|
+
// The message itself is fully self-contained (survivor paths + stop-MCP /
|
|
1246
|
+
// AV-exclusion / re-run guidance) because the serve worker forwards only
|
|
1247
|
+
// `err.message` over IPC — this branch just renders it without the
|
|
1248
|
+
// raw-stack fallback below.
|
|
1249
|
+
if (err instanceof LbugWipeError) {
|
|
1250
|
+
cliError(` ${msg.replace(/\n/g, '\n ')}\n`, {
|
|
1251
|
+
recoveryHint: 'lbug-wipe-failed',
|
|
1252
|
+
});
|
|
1253
|
+
process.exitCode = 1;
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
// Buffer-manager frame-release failure on non-4K-page kernels (#1231).
|
|
1257
|
+
// LadybugDB <= 0.17.x assumed 4 KiB OS pages when releasing evicted
|
|
1258
|
+
// frames; Raspberry Pi 5 (16 KiB kernel pages) and other arm64 systems
|
|
1259
|
+
// crash mid-COPY with a raw native message. 0.18.0 detects the page size
|
|
1260
|
+
// at runtime, so the actionable fix depends on which side of that
|
|
1261
|
+
// boundary the installed @ladybugdb/core is.
|
|
1262
|
+
if (isLbugPageSizeFrameError(err)) {
|
|
1263
|
+
const pageSize = getOsPageSize();
|
|
1264
|
+
const ladybug = getRuntimeFingerprint().ladybugdb;
|
|
1265
|
+
const pageLine = pageSize !== undefined && pageSize !== 4096
|
|
1266
|
+
? ` Detected OS page size: ${pageSize} bytes (non-4K — e.g. Raspberry Pi 5 16K kernel, Asahi Linux).\n`
|
|
1267
|
+
: '';
|
|
1268
|
+
// The upgrade variant must not assert version facts about an unknown
|
|
1269
|
+
// version — mirror the doctor-side wording rule (#2424 review R2).
|
|
1270
|
+
const upgradeIntro = ladybug === undefined
|
|
1271
|
+
? ` The installed @ladybugdb/core version is unknown — it may predate the\n` +
|
|
1272
|
+
` runtime OS-page-size detection added in 0.18.0.\n`
|
|
1273
|
+
: ` The installed @ladybugdb/core (${ladybug}) assumes 4 KiB pages in its buffer\n` +
|
|
1274
|
+
` manager.\n`;
|
|
1275
|
+
const guidance = isPageSizeAwareLadybug(ladybug)
|
|
1276
|
+
? ` The installed @ladybugdb/core (${ladybug}) already detects the OS page size at runtime,\n` +
|
|
1277
|
+
` so this configuration was expected to work. Please report it:\n` +
|
|
1278
|
+
` https://github.com/abhigyanpatwari/GitNexus/issues/1231\n` +
|
|
1279
|
+
` and include: gitnexus --version, node --version, getconf PAGE_SIZE, uname -a,\n` +
|
|
1280
|
+
` and the full error message above.\n`
|
|
1281
|
+
: upgradeIntro +
|
|
1282
|
+
` Upgrade GitNexus to a release that bundles @ladybugdb/core >= 0.18.0\n` +
|
|
1283
|
+
` (gitnexus >= 1.6.9), which detects the OS page size at runtime:\n` +
|
|
1284
|
+
` npm install -g gitnexus@latest\n` +
|
|
1285
|
+
` Last-resort workaround on Raspberry Pi 5: boot the 4 KiB-page kernel\n` +
|
|
1286
|
+
` (config.txt: kernel=kernel8.img), at the cost of Pi 5 optimizations.\n`;
|
|
1287
|
+
// Embed the raw native text (indented, no stack) so "the full error
|
|
1288
|
+
// message above" is fulfillable — same idiom as the LbugWipeError
|
|
1289
|
+
// branch. The errno suffix and the 0.18.0 guard's frame/granule numbers
|
|
1290
|
+
// are the discriminating triage content (#2424 review P2).
|
|
1291
|
+
cliError(` LadybugDB's buffer manager failed to release frame memory.\n` +
|
|
1292
|
+
` ${msg.replace(/\n/g, '\n ')}\n` +
|
|
1293
|
+
pageLine +
|
|
1294
|
+
guidance, { recoveryHint: 'lbug-page-size', pageSize, ladybugVersion: ladybug });
|
|
1295
|
+
process.exitCode = 1;
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1240
1298
|
// Local embedding runtime unsupported on this platform (macOS Intel ships no
|
|
1241
1299
|
// darwin/x64 ONNX native binding, #1515). The guard threw before importing
|
|
1242
1300
|
// transformers.js, so this is a clean, actionable GitNexus message. Checked
|
package/dist/cli/clean.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs from 'fs/promises';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { logger } from '../core/logger.js';
|
|
10
10
|
import { findRepo, unregisterRepo, listRegisteredRepos, assertSafeStoragePath, getStoragePaths, removeBranchIndex, UnsafeStoragePathError, } from '../storage/repo-manager.js';
|
|
11
|
-
import {
|
|
11
|
+
import { cleanParkedLbugSidecars, inspectLbugSidecars, listParkedLbugSidecars, } from '../core/lbug/sidecar-recovery.js';
|
|
12
12
|
import { t } from './i18n/index.js';
|
|
13
13
|
export const cleanCommand = async (options) => {
|
|
14
14
|
// --branch <name>: remove a single non-primary branch's index (#2106 R7).
|
|
@@ -62,7 +62,13 @@ export const cleanCommand = async (options) => {
|
|
|
62
62
|
}
|
|
63
63
|
const lbugPath = path.join(repo.storagePath, 'lbug');
|
|
64
64
|
const state = await inspectLbugSidecars(lbugPath);
|
|
65
|
-
|
|
65
|
+
// Single roster authority (this shipping review, FIX 5): the aggregate
|
|
66
|
+
// covers both parked-sidecar families — the timestamped missing-shadow
|
|
67
|
+
// WAL quarantines AND the fixed-name `.dirty-recovery` parks (`.next`
|
|
68
|
+
// residues included) left by a dirty-flag recovery rebuild (#2409). The
|
|
69
|
+
// previous inline concatenations here were how the `.next` residue
|
|
70
|
+
// stayed invisible to this surface.
|
|
71
|
+
const quarantined = await listParkedLbugSidecars(lbugPath);
|
|
66
72
|
console.log(t('clean.lbugSidecars.state', { state: state.kind }));
|
|
67
73
|
if (quarantined.length === 0) {
|
|
68
74
|
console.log(t('clean.lbugSidecars.none'));
|
|
@@ -76,8 +82,16 @@ export const cleanCommand = async (options) => {
|
|
|
76
82
|
console.log(`\n${t('common.runForceConfirm')}`);
|
|
77
83
|
return;
|
|
78
84
|
}
|
|
79
|
-
const deleted = await
|
|
85
|
+
const { deleted, failed } = await cleanParkedLbugSidecars(lbugPath);
|
|
80
86
|
console.log(t('clean.lbugSidecars.deleted', { count: deleted.length }));
|
|
87
|
+
// A locked parked file no longer crashes the clean mid-command (FIX 5)
|
|
88
|
+
// — the rest were deleted above; report what remains and why.
|
|
89
|
+
if (failed.length > 0) {
|
|
90
|
+
console.log(t('clean.lbugSidecars.failed', { count: failed.length }));
|
|
91
|
+
for (const file of failed) {
|
|
92
|
+
console.log(` - ${file}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
81
95
|
return;
|
|
82
96
|
}
|
|
83
97
|
// --all flag: clean all indexed repos
|
|
@@ -12,7 +12,7 @@ import { type CliMessageKey, type CliMessageVars } from './i18n/index.js';
|
|
|
12
12
|
* Consumers can import this type to narrow log-record `recoveryHint`
|
|
13
13
|
* fields without restating the literal list.
|
|
14
14
|
*/
|
|
15
|
-
export type RecoveryHint = 'wal-corruption' | 'wal-checkpoint-threshold' | 'heap-oom-respawn' | 'native-worker-abort' | 'hf-endpoint-unreachable' | 'http-embedding-endpoint-error' | 'embedding-dims-invalid' | 'local-embedding-unsupported' | 'local-embedding-stack-missing' | 'large-repo' | 'npm-resolution' | 'module-not-found' | 'gitnexusrc-invalid' | 'default-branch-invalid';
|
|
15
|
+
export type RecoveryHint = 'wal-corruption' | 'wal-checkpoint-threshold' | 'lbug-wipe-failed' | 'lbug-page-size' | 'heap-oom-respawn' | 'native-worker-abort' | 'hf-endpoint-unreachable' | 'http-embedding-endpoint-error' | 'embedding-dims-invalid' | 'local-embedding-unsupported' | 'local-embedding-stack-missing' | 'large-repo' | 'npm-resolution' | 'module-not-found' | 'gitnexusrc-invalid' | 'default-branch-invalid';
|
|
16
16
|
/**
|
|
17
17
|
* Common shape for the optional structured-field bag passed to
|
|
18
18
|
* `cliError`/`cliWarn`/`cliInfo`. Typed so the `recoveryHint` slot is
|
package/dist/cli/doctor.d.ts
CHANGED
|
@@ -23,4 +23,17 @@ export declare function localEmbeddingDoctorStatus(opts: {
|
|
|
23
23
|
status: string;
|
|
24
24
|
detail: string | null;
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Page-size lines for the `doctor` Runtime section (#1231). Pure so the
|
|
28
|
+
* warning gate can be unit-tested without running the whole command (the
|
|
29
|
+
* `localEmbeddingDoctorStatus` precedent above) — but takes the probed
|
|
30
|
+
* values as plain params rather than injectable probes, because `undefined`
|
|
31
|
+
* is a *meaningful* pageSize state here (probe unavailable / win32) and
|
|
32
|
+
* would collide with a "not provided → use default" DI convention.
|
|
33
|
+
*
|
|
34
|
+
* Returns 0 lines (page size unknown), 1 line (page size), or 2 lines
|
|
35
|
+
* (page size + non-4K warning when the installed @ladybugdb/core does not
|
|
36
|
+
* detect the OS page size at runtime).
|
|
37
|
+
*/
|
|
38
|
+
export declare function pageSizeDoctorLines(pageSize: number | undefined, ladybugVersion: string | undefined): string[];
|
|
26
39
|
export declare const doctorCommand: () => Promise<void>;
|
package/dist/cli/doctor.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getLocalEmbeddingRuntimeBlocker, localEmbeddingPrefixUnloadableMessage,
|
|
|
5
5
|
import { isPrefixRuntimeLoadable, resolveEmbeddingRuntime, } from '../core/embeddings/runtime-install.js';
|
|
6
6
|
import { cudaRedirectDoctorStatus } from '../core/embeddings/onnxruntime-node-resolver.js';
|
|
7
7
|
import { checkLbugNative, probeFtsExtensionLoad } from '../core/lbug/native-check.js';
|
|
8
|
+
import { getOsPageSize, isPageSizeAwareLadybug } from '../core/lbug/lbug-config.js';
|
|
8
9
|
import { diagnoseExtensionLoad } from '../core/lbug/extension-load-error.js';
|
|
9
10
|
import { getExtensionInstallPolicy } from '../core/lbug/extension-loader.js';
|
|
10
11
|
import { t } from './i18n/index.js';
|
|
@@ -87,6 +88,33 @@ export function localEmbeddingDoctorStatus(opts) {
|
|
|
87
88
|
}
|
|
88
89
|
return { status: '✓ local embeddings supported', detail: null };
|
|
89
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Page-size lines for the `doctor` Runtime section (#1231). Pure so the
|
|
93
|
+
* warning gate can be unit-tested without running the whole command (the
|
|
94
|
+
* `localEmbeddingDoctorStatus` precedent above) — but takes the probed
|
|
95
|
+
* values as plain params rather than injectable probes, because `undefined`
|
|
96
|
+
* is a *meaningful* pageSize state here (probe unavailable / win32) and
|
|
97
|
+
* would collide with a "not provided → use default" DI convention.
|
|
98
|
+
*
|
|
99
|
+
* Returns 0 lines (page size unknown), 1 line (page size), or 2 lines
|
|
100
|
+
* (page size + non-4K warning when the installed @ladybugdb/core does not
|
|
101
|
+
* detect the OS page size at runtime).
|
|
102
|
+
*/
|
|
103
|
+
export function pageSizeDoctorLines(pageSize, ladybugVersion) {
|
|
104
|
+
if (pageSize === undefined)
|
|
105
|
+
return [];
|
|
106
|
+
const lines = [` ${padDisplayEnd('page size', 10)}${pageSize}`];
|
|
107
|
+
if (pageSize > 4096 && !isPageSizeAwareLadybug(ladybugVersion)) {
|
|
108
|
+
// Don't assert "< 0.18.0" as fact when the version is unresolvable
|
|
109
|
+
// (#2424 review R2) — name the unknown state instead.
|
|
110
|
+
const versionClause = ladybugVersion === undefined
|
|
111
|
+
? 'an unknown @ladybugdb/core version (may predate 0.18.0)'
|
|
112
|
+
: `@ladybugdb/core < 0.18.0`;
|
|
113
|
+
lines.push(` ${padDisplayEnd('', 10)}⚠ non-4K page size with ${versionClause} — ` +
|
|
114
|
+
`'gitnexus analyze' may fail during COPY (#1231). Upgrade gitnexus (npm install -g gitnexus@latest).`);
|
|
115
|
+
}
|
|
116
|
+
return lines;
|
|
117
|
+
}
|
|
90
118
|
export const doctorCommand = async () => {
|
|
91
119
|
const fingerprint = getRuntimeFingerprint();
|
|
92
120
|
const capabilities = getRuntimeCapabilities();
|
|
@@ -97,6 +125,13 @@ export const doctorCommand = async () => {
|
|
|
97
125
|
console.log(` ${label('doctor.labels.node', 10)}${fingerprint.node}`);
|
|
98
126
|
console.log(` ${label('doctor.labels.gitnexus', 10)}${fingerprint.gitnexus}`);
|
|
99
127
|
console.log(` ${label('doctor.labels.ladybugdb', 10)}${fingerprint.ladybugdb ?? 'unknown'}`);
|
|
128
|
+
// OS page size next to the LadybugDB version because the two interact:
|
|
129
|
+
// @ladybugdb/core < 0.18.0 assumed 4 KiB pages in its buffer manager and
|
|
130
|
+
// crashes mid-COPY on 16 KiB/64 KiB-page kernels (#1231). Literal label
|
|
131
|
+
// (like the 'native' line below) to avoid adding i18n keys.
|
|
132
|
+
for (const line of pageSizeDoctorLines(getOsPageSize(), fingerprint.ladybugdb)) {
|
|
133
|
+
console.log(line);
|
|
134
|
+
}
|
|
100
135
|
const nativeCheck = checkLbugNative();
|
|
101
136
|
if (nativeCheck.ok) {
|
|
102
137
|
console.log(` ${padDisplayEnd('native', 10)}✓ lbugjs.node loaded`);
|
package/dist/cli/i18n/en.d.ts
CHANGED
|
@@ -40,9 +40,10 @@ export declare const en: {
|
|
|
40
40
|
readonly 'clean.deleteBranch': "This will delete the branch index \"{{branch}}\" at: {{path}}";
|
|
41
41
|
readonly 'clean.deletedBranch': "Deleted branch index: {{branch}}";
|
|
42
42
|
readonly 'clean.lbugSidecars.state': "LadybugDB sidecar state: {{state}}";
|
|
43
|
-
readonly 'clean.lbugSidecars.none': "No
|
|
44
|
-
readonly 'clean.lbugSidecars.preview': "This will delete {{count}}
|
|
45
|
-
readonly 'clean.lbugSidecars.deleted': "Deleted {{count}}
|
|
43
|
+
readonly 'clean.lbugSidecars.none': "No parked LadybugDB recovery sidecars found (missing-shadow WAL quarantines or dirty-recovery parks).";
|
|
44
|
+
readonly 'clean.lbugSidecars.preview': "This will delete {{count}} parked LadybugDB recovery sidecar(s) (missing-shadow WAL quarantines and dirty-recovery parks):";
|
|
45
|
+
readonly 'clean.lbugSidecars.deleted': "Deleted {{count}} parked LadybugDB recovery sidecar(s).";
|
|
46
|
+
readonly 'clean.lbugSidecars.failed': "Could not delete {{count}} locked file(s) — stop the process holding them (GitNexus MCP/serve or an antivirus scan) and re-run:";
|
|
46
47
|
readonly 'remove.nothingToRemove': "Nothing to remove: {{message}}";
|
|
47
48
|
readonly 'remove.deleteTarget': "This will delete the GitNexus index for: {{name}}";
|
|
48
49
|
readonly 'remove.removed': "Removed: {{name}}";
|
|
@@ -175,7 +176,7 @@ export declare const en: {
|
|
|
175
176
|
readonly 'help.option.uninstall.force': "Apply the changes (default is a dry-run preview)";
|
|
176
177
|
readonly 'help.option.clean.all': "Clean all indexed repos";
|
|
177
178
|
readonly 'help.option.clean.branch': "Delete only the named branch index (not the workspace index)";
|
|
178
|
-
readonly 'help.option.clean.lbugSidecars': "Clean
|
|
179
|
+
readonly 'help.option.clean.lbugSidecars': "Clean parked LadybugDB recovery sidecars (missing-shadow WAL quarantines and dirty-recovery parks)";
|
|
179
180
|
readonly 'help.option.wiki.force': "Force full regeneration even if up to date";
|
|
180
181
|
readonly 'help.option.wiki.provider': "LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)";
|
|
181
182
|
readonly 'help.option.wiki.model': "LLM model or Azure deployment name (default: minimax/minimax-m2.5)";
|
package/dist/cli/i18n/en.js
CHANGED
|
@@ -40,9 +40,10 @@ export const en = {
|
|
|
40
40
|
'clean.deleteBranch': 'This will delete the branch index "{{branch}}" at: {{path}}',
|
|
41
41
|
'clean.deletedBranch': 'Deleted branch index: {{branch}}',
|
|
42
42
|
'clean.lbugSidecars.state': 'LadybugDB sidecar state: {{state}}',
|
|
43
|
-
'clean.lbugSidecars.none': 'No
|
|
44
|
-
'clean.lbugSidecars.preview': 'This will delete {{count}}
|
|
45
|
-
'clean.lbugSidecars.deleted': 'Deleted {{count}}
|
|
43
|
+
'clean.lbugSidecars.none': 'No parked LadybugDB recovery sidecars found (missing-shadow WAL quarantines or dirty-recovery parks).',
|
|
44
|
+
'clean.lbugSidecars.preview': 'This will delete {{count}} parked LadybugDB recovery sidecar(s) (missing-shadow WAL quarantines and dirty-recovery parks):',
|
|
45
|
+
'clean.lbugSidecars.deleted': 'Deleted {{count}} parked LadybugDB recovery sidecar(s).',
|
|
46
|
+
'clean.lbugSidecars.failed': 'Could not delete {{count}} locked file(s) — stop the process holding them (GitNexus MCP/serve or an antivirus scan) and re-run:',
|
|
46
47
|
'remove.nothingToRemove': 'Nothing to remove: {{message}}',
|
|
47
48
|
'remove.deleteTarget': 'This will delete the GitNexus index for: {{name}}',
|
|
48
49
|
'remove.removed': 'Removed: {{name}}',
|
|
@@ -175,7 +176,7 @@ export const en = {
|
|
|
175
176
|
'help.option.uninstall.force': 'Apply the changes (default is a dry-run preview)',
|
|
176
177
|
'help.option.clean.all': 'Clean all indexed repos',
|
|
177
178
|
'help.option.clean.branch': 'Delete only the named branch index (not the workspace index)',
|
|
178
|
-
'help.option.clean.lbugSidecars': 'Clean
|
|
179
|
+
'help.option.clean.lbugSidecars': 'Clean parked LadybugDB recovery sidecars (missing-shadow WAL quarantines and dirty-recovery parks)',
|
|
179
180
|
'help.option.wiki.force': 'Force full regeneration even if up to date',
|
|
180
181
|
'help.option.wiki.provider': 'LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)',
|
|
181
182
|
'help.option.wiki.model': 'LLM model or Azure deployment name (default: minimax/minimax-m2.5)',
|
|
@@ -41,9 +41,10 @@ export declare const cliResources: {
|
|
|
41
41
|
readonly 'clean.deleteBranch': "This will delete the branch index \"{{branch}}\" at: {{path}}";
|
|
42
42
|
readonly 'clean.deletedBranch': "Deleted branch index: {{branch}}";
|
|
43
43
|
readonly 'clean.lbugSidecars.state': "LadybugDB sidecar state: {{state}}";
|
|
44
|
-
readonly 'clean.lbugSidecars.none': "No
|
|
45
|
-
readonly 'clean.lbugSidecars.preview': "This will delete {{count}}
|
|
46
|
-
readonly 'clean.lbugSidecars.deleted': "Deleted {{count}}
|
|
44
|
+
readonly 'clean.lbugSidecars.none': "No parked LadybugDB recovery sidecars found (missing-shadow WAL quarantines or dirty-recovery parks).";
|
|
45
|
+
readonly 'clean.lbugSidecars.preview': "This will delete {{count}} parked LadybugDB recovery sidecar(s) (missing-shadow WAL quarantines and dirty-recovery parks):";
|
|
46
|
+
readonly 'clean.lbugSidecars.deleted': "Deleted {{count}} parked LadybugDB recovery sidecar(s).";
|
|
47
|
+
readonly 'clean.lbugSidecars.failed': "Could not delete {{count}} locked file(s) — stop the process holding them (GitNexus MCP/serve or an antivirus scan) and re-run:";
|
|
47
48
|
readonly 'remove.nothingToRemove': "Nothing to remove: {{message}}";
|
|
48
49
|
readonly 'remove.deleteTarget': "This will delete the GitNexus index for: {{name}}";
|
|
49
50
|
readonly 'remove.removed': "Removed: {{name}}";
|
|
@@ -176,7 +177,7 @@ export declare const cliResources: {
|
|
|
176
177
|
readonly 'help.option.uninstall.force': "Apply the changes (default is a dry-run preview)";
|
|
177
178
|
readonly 'help.option.clean.all': "Clean all indexed repos";
|
|
178
179
|
readonly 'help.option.clean.branch': "Delete only the named branch index (not the workspace index)";
|
|
179
|
-
readonly 'help.option.clean.lbugSidecars': "Clean
|
|
180
|
+
readonly 'help.option.clean.lbugSidecars': "Clean parked LadybugDB recovery sidecars (missing-shadow WAL quarantines and dirty-recovery parks)";
|
|
180
181
|
readonly 'help.option.wiki.force': "Force full regeneration even if up to date";
|
|
181
182
|
readonly 'help.option.wiki.provider': "LLM provider: openai, openrouter, azure, custom, cursor, claude, codex, or opencode (default: openai)";
|
|
182
183
|
readonly 'help.option.wiki.model': "LLM model or Azure deployment name (default: minimax/minimax-m2.5)";
|
|
@@ -290,6 +291,7 @@ export declare const cliResources: {
|
|
|
290
291
|
'clean.lbugSidecars.none': string;
|
|
291
292
|
'clean.lbugSidecars.preview': string;
|
|
292
293
|
'clean.lbugSidecars.deleted': string;
|
|
294
|
+
'clean.lbugSidecars.failed': string;
|
|
293
295
|
'remove.nothingToRemove': string;
|
|
294
296
|
'remove.deleteTarget': string;
|
|
295
297
|
'remove.removed': string;
|
package/dist/cli/i18n/zh-CN.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export declare const zhCN: {
|
|
|
43
43
|
'clean.lbugSidecars.none': string;
|
|
44
44
|
'clean.lbugSidecars.preview': string;
|
|
45
45
|
'clean.lbugSidecars.deleted': string;
|
|
46
|
+
'clean.lbugSidecars.failed': string;
|
|
46
47
|
'remove.nothingToRemove': string;
|
|
47
48
|
'remove.deleteTarget': string;
|
|
48
49
|
'remove.removed': string;
|
package/dist/cli/i18n/zh-CN.js
CHANGED
|
@@ -40,9 +40,10 @@ export const zhCN = {
|
|
|
40
40
|
'clean.deleteBranch': '将删除分支索引 “{{branch}}”,路径:{{path}}',
|
|
41
41
|
'clean.deletedBranch': '已删除分支索引:{{branch}}',
|
|
42
42
|
'clean.lbugSidecars.state': 'LadybugDB sidecar 状态:{{state}}',
|
|
43
|
-
'clean.lbugSidecars.none': '
|
|
44
|
-
'clean.lbugSidecars.preview': '将删除 {{count}}
|
|
45
|
-
'clean.lbugSidecars.deleted': '已删除 {{count}}
|
|
43
|
+
'clean.lbugSidecars.none': '未找到已暂存的 LadybugDB 恢复 sidecar(missing-shadow WAL 隔离文件或 dirty-recovery 暂存文件)。',
|
|
44
|
+
'clean.lbugSidecars.preview': '将删除 {{count}} 个已暂存的 LadybugDB 恢复 sidecar(missing-shadow WAL 隔离文件与 dirty-recovery 暂存文件):',
|
|
45
|
+
'clean.lbugSidecars.deleted': '已删除 {{count}} 个已暂存的 LadybugDB 恢复 sidecar。',
|
|
46
|
+
'clean.lbugSidecars.failed': '有 {{count}} 个文件被锁定而无法删除 — 请停止占用它们的进程(GitNexus MCP/serve 或杀毒软件扫描)后重试:',
|
|
46
47
|
'remove.nothingToRemove': '无需移除:{{message}}',
|
|
47
48
|
'remove.deleteTarget': '将删除该仓库的 GitNexus 索引:{{name}}',
|
|
48
49
|
'remove.removed': '已移除:{{name}}',
|
|
@@ -175,7 +176,7 @@ export const zhCN = {
|
|
|
175
176
|
'help.option.uninstall.force': '应用更改(默认仅为预演预览)',
|
|
176
177
|
'help.option.clean.all': '清理所有已索引仓库',
|
|
177
178
|
'help.option.clean.branch': '仅删除指定分支的索引(不影响工作区索引)',
|
|
178
|
-
'help.option.clean.lbugSidecars': '
|
|
179
|
+
'help.option.clean.lbugSidecars': '清理已暂存的 LadybugDB 恢复 sidecar(missing-shadow WAL 隔离文件与 dirty-recovery 暂存文件)',
|
|
179
180
|
'help.option.wiki.force': '即使已是最新也强制完整重新生成',
|
|
180
181
|
'help.option.wiki.provider': 'LLM 提供商:openai、openrouter、azure、custom、cursor、claude、codex 或 opencode(默认:openai)',
|
|
181
182
|
'help.option.wiki.model': 'LLM 模型或 Azure deployment 名称(默认:minimax/minimax-m2.5)',
|
package/dist/cli/index.js
CHANGED
|
@@ -170,7 +170,7 @@ program
|
|
|
170
170
|
.option('-f, --force', 'Skip confirmation prompt')
|
|
171
171
|
.option('--all', 'Clean all indexed repos')
|
|
172
172
|
.option('--branch <name>', 'Delete only the named branch index (not the workspace index)')
|
|
173
|
-
.option('--lbug-sidecars', 'Clean
|
|
173
|
+
.option('--lbug-sidecars', 'Clean parked LadybugDB recovery sidecars (missing-shadow WAL quarantines and dirty-recovery parks)')
|
|
174
174
|
.action(createLazyAction(() => import('./clean.js'), 'cleanCommand'));
|
|
175
175
|
program
|
|
176
176
|
.command('remove <target>')
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import { listRegisteredRepos } from '../../storage/repo-manager.js';
|
|
18
|
+
import { escapeCypherString } from '../lbug/cypher-escape.js';
|
|
18
19
|
/**
|
|
19
20
|
* Find the best matching repo for a given working directory.
|
|
20
21
|
* Matches by checking if cwd is within the repo's path.
|
|
@@ -76,7 +77,7 @@ async function findRepoForCwd(cwd) {
|
|
|
76
77
|
export async function augment(pattern, cwd) {
|
|
77
78
|
if (!pattern || pattern.length < 3)
|
|
78
79
|
return '';
|
|
79
|
-
const patternFirstWord = pattern.trim()
|
|
80
|
+
const patternFirstWord = escapeCypherString(pattern.trim()).split(/\s+/)[0];
|
|
80
81
|
if (!patternFirstWord || patternFirstWord.length < 2)
|
|
81
82
|
return '';
|
|
82
83
|
const workDir = cwd || process.cwd();
|
|
@@ -97,7 +98,7 @@ export async function augment(pattern, cwd) {
|
|
|
97
98
|
// Step 2: Map BM25 file results to symbols
|
|
98
99
|
const symbolMatches = [];
|
|
99
100
|
for (const result of bm25Results.slice(0, 5)) {
|
|
100
|
-
const escaped = result.filePath
|
|
101
|
+
const escaped = escapeCypherString(result.filePath);
|
|
101
102
|
try {
|
|
102
103
|
const symbols = await executeQuery(repoId, `
|
|
103
104
|
MATCH (n) WHERE n.filePath = '${escaped}'
|
|
@@ -147,7 +148,7 @@ export async function augment(pattern, cwd) {
|
|
|
147
148
|
.filter((sym, i, arr) => arr.findIndex((s) => s.nodeId === sym.nodeId) === i);
|
|
148
149
|
if (uniqueSymbols.length === 0)
|
|
149
150
|
return '';
|
|
150
|
-
const idList = uniqueSymbols.map((s) => `'${s.nodeId
|
|
151
|
+
const idList = uniqueSymbols.map((s) => `'${escapeCypherString(s.nodeId)}'`).join(', ');
|
|
151
152
|
// Batch fetch callers
|
|
152
153
|
const callersMap = new Map();
|
|
153
154
|
try {
|
|
@@ -51,6 +51,26 @@ export declare const batchInsertEmbeddings: (executeWithReusedStatement: (cypher
|
|
|
51
51
|
embedding: number[];
|
|
52
52
|
contentHash?: string;
|
|
53
53
|
}>) => Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Create the vector index for semantic search (indexes the CodeEmbedding table).
|
|
56
|
+
*
|
|
57
|
+
* Keeps the embedding-specific extension-install policy gate here
|
|
58
|
+
* (ensureVectorExtensionAvailable → resolveEmbeddingInstallPolicy, default
|
|
59
|
+
* `auto` for the analyze write path), then delegates the actual
|
|
60
|
+
* `CALL CREATE_VECTOR_INDEX(...)` to the adapter, which runs it through the
|
|
61
|
+
* unprepared `conn.query()` path. It must NOT go through the injected
|
|
62
|
+
* `executeQuery` (prepared `conn.prepare()`): LadybugDB cannot prepare that
|
|
63
|
+
* procedure and fails with "We do not support prepare multiple statements" —
|
|
64
|
+
* the silent degrade in #2114.
|
|
65
|
+
*
|
|
66
|
+
* Exported for run-analyze's wipe-and-restore seam (tri-review 4669518496
|
|
67
|
+
* P1): a full-rebuild/escalated write wipes the DB files — index included —
|
|
68
|
+
* and a preserve-only run restores embedding ROWS without ever reaching the
|
|
69
|
+
* pipeline call sites below, so the orchestrator recreates the index through
|
|
70
|
+
* this same policy-gated, warn-on-failure entry point. Consumed there via
|
|
71
|
+
* dynamic import only (lazy-embeddings convention, #2370).
|
|
72
|
+
*/
|
|
73
|
+
export declare const buildVectorIndex: () => Promise<boolean>;
|
|
54
74
|
export interface EmbeddingPipelineResult {
|
|
55
75
|
nodesProcessed: number;
|
|
56
76
|
chunksProcessed: number;
|
|
@@ -18,6 +18,7 @@ import { DEFAULT_VECTOR_MAX_DISTANCE, getVectorMaxDistance, resolveEmbeddingConf
|
|
|
18
18
|
import { rankExactEmbeddingRows } from './exact-search.js';
|
|
19
19
|
import { EMBEDDING_TABLE_NAME, EMBEDDING_INDEX_NAME, STALE_HASH_SENTINEL } from '../lbug/schema.js';
|
|
20
20
|
import { loadVectorExtension, createVectorIndex } from '../lbug/lbug-adapter.js';
|
|
21
|
+
import { escapeCypherString } from '../lbug/cypher-escape.js';
|
|
21
22
|
import { getExactScanLimit } from '../platform/capabilities.js';
|
|
22
23
|
import { logger } from '../logger.js';
|
|
23
24
|
const isDev = process.env.NODE_ENV === 'development';
|
|
@@ -163,8 +164,15 @@ export const batchInsertEmbeddings = async (executeWithReusedStatement, updates)
|
|
|
163
164
|
* `executeQuery` (prepared `conn.prepare()`): LadybugDB cannot prepare that
|
|
164
165
|
* procedure and fails with "We do not support prepare multiple statements" —
|
|
165
166
|
* the silent degrade in #2114.
|
|
167
|
+
*
|
|
168
|
+
* Exported for run-analyze's wipe-and-restore seam (tri-review 4669518496
|
|
169
|
+
* P1): a full-rebuild/escalated write wipes the DB files — index included —
|
|
170
|
+
* and a preserve-only run restores embedding ROWS without ever reaching the
|
|
171
|
+
* pipeline call sites below, so the orchestrator recreates the index through
|
|
172
|
+
* this same policy-gated, warn-on-failure entry point. Consumed there via
|
|
173
|
+
* dynamic import only (lazy-embeddings convention, #2370).
|
|
166
174
|
*/
|
|
167
|
-
const buildVectorIndex = async () => {
|
|
175
|
+
export const buildVectorIndex = async () => {
|
|
168
176
|
// This pre-check applies the embedding-specific install policy
|
|
169
177
|
// (resolveEmbeddingInstallPolicy, default `auto` for analyze) before reaching
|
|
170
178
|
// the adapter. The adapter's createVectorIndex() calls loadVectorExtension()
|
|
@@ -536,7 +544,7 @@ export const semanticSearch = async (executeQuery, query, k = 10, maxDistance =
|
|
|
536
544
|
// Batch-fetch metadata per label
|
|
537
545
|
const results = [];
|
|
538
546
|
for (const [label, items] of byLabel) {
|
|
539
|
-
const idList = items.map((i) => `'${i.nodeId
|
|
547
|
+
const idList = items.map((i) => `'${escapeCypherString(i.nodeId)}'`).join(', ');
|
|
540
548
|
try {
|
|
541
549
|
const nodeQuery = `
|
|
542
550
|
MATCH (n:\`${label}\`) WHERE n.id IN [${idList}]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escalation gate for the incremental DB writeback (#2409).
|
|
3
|
+
*
|
|
4
|
+
* When the effective write set covers most of the repo, per-file surgery is
|
|
5
|
+
* strictly worse than the proven wipe-and-bulk-COPY plan — the same data
|
|
6
|
+
* volume lands either way, but the surgical plan pays per-table deletes plus
|
|
7
|
+
* COPY-into-non-empty tables, and at that size it measured SLOWER than a full
|
|
8
|
+
* DB load. The orchestrator (`run-analyze.ts`) consults this predicate to
|
|
9
|
+
* decide which write plan to run; only the DB write plan changes on
|
|
10
|
+
* escalation — fileHashes/meta bookkeeping is identical.
|
|
11
|
+
*
|
|
12
|
+
* Extracted to a pure module (tri-review 4669518496) so the AND-gate's
|
|
13
|
+
* boundary corners are pinned by unit tests without multi-minute
|
|
14
|
+
* orchestration permutations — an `&&`→`||` mutation here can no longer
|
|
15
|
+
* survive CI.
|
|
16
|
+
*/
|
|
17
|
+
export declare const INCREMENTAL_MAX_WRITE_FRACTION = 0.5;
|
|
18
|
+
export declare const INCREMENTAL_ESCALATION_MIN_FILES = 50;
|
|
19
|
+
/**
|
|
20
|
+
* Should the incremental writeback escalate from per-file surgery to a full
|
|
21
|
+
* wipe-and-bulk-COPY write plan?
|
|
22
|
+
*
|
|
23
|
+
* @param deleteCount Files whose rows will be DETACH-DELETEd
|
|
24
|
+
* (effective write set ∪ deleted files, deduped).
|
|
25
|
+
* @param effectiveWriteCount Size of the effective write set (toWrite ∪
|
|
26
|
+
* importer-BFS expansion ∪ boundary-crossing files).
|
|
27
|
+
* @param totalFiles Current repo file count (denominator).
|
|
28
|
+
*
|
|
29
|
+
* POPULATION MISMATCH (tri-review 4669518496, documented not "fixed"): the
|
|
30
|
+
* numerator counts effective-write-set members, which include importer-BFS
|
|
31
|
+
* results read from the PRE-pipeline DB — those can be now-DELETED paths that
|
|
32
|
+
* the CURRENT file list (the denominator) no longer contains. The fraction is
|
|
33
|
+
* therefore not a true subset ratio and can exceed 1 on delete-heavy runs.
|
|
34
|
+
* That errs toward escalation, which is the safe direction (the full write
|
|
35
|
+
* plan is always correct); the valve's log line clamps the DISPLAYED
|
|
36
|
+
* percentage to 100 so operators aren't shown ">100% of the repo".
|
|
37
|
+
*/
|
|
38
|
+
export declare const shouldEscalateIncrementalWrite: (deleteCount: number, effectiveWriteCount: number, totalFiles: number) => boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escalation gate for the incremental DB writeback (#2409).
|
|
3
|
+
*
|
|
4
|
+
* When the effective write set covers most of the repo, per-file surgery is
|
|
5
|
+
* strictly worse than the proven wipe-and-bulk-COPY plan — the same data
|
|
6
|
+
* volume lands either way, but the surgical plan pays per-table deletes plus
|
|
7
|
+
* COPY-into-non-empty tables, and at that size it measured SLOWER than a full
|
|
8
|
+
* DB load. The orchestrator (`run-analyze.ts`) consults this predicate to
|
|
9
|
+
* decide which write plan to run; only the DB write plan changes on
|
|
10
|
+
* escalation — fileHashes/meta bookkeeping is identical.
|
|
11
|
+
*
|
|
12
|
+
* Extracted to a pure module (tri-review 4669518496) so the AND-gate's
|
|
13
|
+
* boundary corners are pinned by unit tests without multi-minute
|
|
14
|
+
* orchestration permutations — an `&&`→`||` mutation here can no longer
|
|
15
|
+
* survive CI.
|
|
16
|
+
*/
|
|
17
|
+
// Escalation cap (#2409): above this fraction of the repo's files, the
|
|
18
|
+
// surgical delete-and-COPY writeback is replaced by the full-rebuild write
|
|
19
|
+
// plan (wipe + bulk COPY of the already-built graph). 0.5 is a coarse
|
|
20
|
+
// crossover knob, not a tuned constant — lower it if surgical writebacks
|
|
21
|
+
// above ~30% ever measure slower than the full COPY.
|
|
22
|
+
export const INCREMENTAL_MAX_WRITE_FRACTION = 0.5;
|
|
23
|
+
// …but only at a scale where the surgical plan's overhead is real. Tiny
|
|
24
|
+
// repos hit huge fractions from a single edit (7 files → one touch can
|
|
25
|
+
// pull in 5) while both write plans finish in well under a second there —
|
|
26
|
+
// escalating would churn the DB files for nothing.
|
|
27
|
+
export const INCREMENTAL_ESCALATION_MIN_FILES = 50;
|
|
28
|
+
/**
|
|
29
|
+
* Should the incremental writeback escalate from per-file surgery to a full
|
|
30
|
+
* wipe-and-bulk-COPY write plan?
|
|
31
|
+
*
|
|
32
|
+
* @param deleteCount Files whose rows will be DETACH-DELETEd
|
|
33
|
+
* (effective write set ∪ deleted files, deduped).
|
|
34
|
+
* @param effectiveWriteCount Size of the effective write set (toWrite ∪
|
|
35
|
+
* importer-BFS expansion ∪ boundary-crossing files).
|
|
36
|
+
* @param totalFiles Current repo file count (denominator).
|
|
37
|
+
*
|
|
38
|
+
* POPULATION MISMATCH (tri-review 4669518496, documented not "fixed"): the
|
|
39
|
+
* numerator counts effective-write-set members, which include importer-BFS
|
|
40
|
+
* results read from the PRE-pipeline DB — those can be now-DELETED paths that
|
|
41
|
+
* the CURRENT file list (the denominator) no longer contains. The fraction is
|
|
42
|
+
* therefore not a true subset ratio and can exceed 1 on delete-heavy runs.
|
|
43
|
+
* That errs toward escalation, which is the safe direction (the full write
|
|
44
|
+
* plan is always correct); the valve's log line clamps the DISPLAYED
|
|
45
|
+
* percentage to 100 so operators aren't shown ">100% of the repo".
|
|
46
|
+
*/
|
|
47
|
+
export const shouldEscalateIncrementalWrite = (deleteCount, effectiveWriteCount, totalFiles) => deleteCount >= INCREMENTAL_ESCALATION_MIN_FILES &&
|
|
48
|
+
effectiveWriteCount / Math.max(1, totalFiles) > INCREMENTAL_MAX_WRITE_FRACTION;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Shadow-candidate path derivation for incremental indexing.
|
|
3
3
|
*
|
|
4
4
|
* Background — Bugbot review on PR #1479:
|
|
5
|
-
*
|
|
6
|
-
* pre-pipeline DB, because the new file's IMPORTS
|
|
7
|
-
* written yet. But pre-existing files may have IMPORTS edges that
|
|
5
|
+
* the importer BFS (queryImportersBatch) on a NEWLY ADDED file returns
|
|
6
|
+
* 0 importers in the pre-pipeline DB, because the new file's IMPORTS
|
|
7
|
+
* rows haven't been written yet. But pre-existing files may have IMPORTS edges that
|
|
8
8
|
* *resolved to a sibling path*, and the newcomer can now steal that
|
|
9
9
|
* resolution under standard JS/TS module-resolution rules. Without
|
|
10
10
|
* pulling those pre-existing files into the writable set, their
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Shadow-candidate path derivation for incremental indexing.
|
|
3
3
|
*
|
|
4
4
|
* Background — Bugbot review on PR #1479:
|
|
5
|
-
*
|
|
6
|
-
* pre-pipeline DB, because the new file's IMPORTS
|
|
7
|
-
* written yet. But pre-existing files may have IMPORTS edges that
|
|
5
|
+
* the importer BFS (queryImportersBatch) on a NEWLY ADDED file returns
|
|
6
|
+
* 0 importers in the pre-pipeline DB, because the new file's IMPORTS
|
|
7
|
+
* rows haven't been written yet. But pre-existing files may have IMPORTS edges that
|
|
8
8
|
* *resolved to a sibling path*, and the newcomer can now steal that
|
|
9
9
|
* resolution under standard JS/TS module-resolution rules. Without
|
|
10
10
|
* pulling those pre-existing files into the writable set, their
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* `extractChangedSubgraph` intentionally does NOT expand the set it is
|
|
24
24
|
* given — expansion is the orchestrator's job, so the SAME expanded set
|
|
25
|
-
* can be fed to both `
|
|
25
|
+
* can be fed to both `deleteNodesForFiles` and this function (asymmetry
|
|
26
26
|
* between the delete set and the write set silently corrupts the DB).
|
|
27
27
|
* `computeEffectiveWriteSet` below performs the boundary-crossing 1-hop
|
|
28
28
|
* walk; the orchestrator composes it with its importer-BFS expansion and
|
|
@@ -57,7 +57,7 @@ export declare const extractChangedSubgraph: (fullGraph: KnowledgeGraph, toWrite
|
|
|
57
57
|
* deleted + rewritten in lockstep with the changed side.
|
|
58
58
|
*
|
|
59
59
|
* Single pass over the edge list. Does NOT mutate `toWriteSet`. The
|
|
60
|
-
* orchestrator MUST feed the returned set to both `
|
|
60
|
+
* orchestrator MUST feed the returned set to both `deleteNodesForFiles`
|
|
61
61
|
* and `extractChangedSubgraph` — feeding the unexpanded set to either
|
|
62
62
|
* one leaves stale rows or PK-conflicts at COPY time.
|
|
63
63
|
*/
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*
|
|
23
23
|
* `extractChangedSubgraph` intentionally does NOT expand the set it is
|
|
24
24
|
* given — expansion is the orchestrator's job, so the SAME expanded set
|
|
25
|
-
* can be fed to both `
|
|
25
|
+
* can be fed to both `deleteNodesForFiles` and this function (asymmetry
|
|
26
26
|
* between the delete set and the write set silently corrupts the DB).
|
|
27
27
|
* `computeEffectiveWriteSet` below performs the boundary-crossing 1-hop
|
|
28
28
|
* walk; the orchestrator composes it with its importer-BFS expansion and
|
|
@@ -118,7 +118,7 @@ export const extractChangedSubgraph = (fullGraph, toWriteSet) => {
|
|
|
118
118
|
* deleted + rewritten in lockstep with the changed side.
|
|
119
119
|
*
|
|
120
120
|
* Single pass over the edge list. Does NOT mutate `toWriteSet`. The
|
|
121
|
-
* orchestrator MUST feed the returned set to both `
|
|
121
|
+
* orchestrator MUST feed the returned set to both `deleteNodesForFiles`
|
|
122
122
|
* and `extractChangedSubgraph` — feeding the unexpanded set to either
|
|
123
123
|
* one leaves stale rows or PK-conflicts at COPY time.
|
|
124
124
|
*/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape a value for embedding in a single-quoted Cypher string literal.
|
|
3
|
+
*
|
|
4
|
+
* LadybugDB's parser uses backslash escapes and REJECTS SQL-style `''`
|
|
5
|
+
* doubling — `'we''ird'` is a parser error, not an escaped quote (#2409
|
|
6
|
+
* review). Every call site that used doubling produced a query that never
|
|
7
|
+
* parsed; the failures were invisible wherever the site swallowed errors
|
|
8
|
+
* (per-file deletes skipping quoted paths, importer BFS returning [],
|
|
9
|
+
* augment/wiki batch lookups silently missing rows).
|
|
10
|
+
*
|
|
11
|
+
* Backslashes are escaped first, then quotes — reversing the order would
|
|
12
|
+
* double the backslash that the quote escape just introduced. For values
|
|
13
|
+
* inside single-quoted literals only; table/label names go through
|
|
14
|
+
* `escapeTableName` in lbug-adapter instead.
|
|
15
|
+
*
|
|
16
|
+
* NOT for CSV emission: the COPY path (csv-generator.ts) quotes fields for
|
|
17
|
+
* LadybugDB's CSV reader (`ESCAPE='"'`), a different grammar with its own
|
|
18
|
+
* rules — its `''` usages are not this bug.
|
|
19
|
+
*/
|
|
20
|
+
export declare const escapeCypherString: (value: string) => string;
|