@tekyzinc/gsd-t 4.13.12 → 4.14.10
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/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/bin/gsd-t-graph-freshness.cjs +8 -4
- package/bin/gsd-t-graph-index.cjs +7 -2
- package/bin/gsd-t-graph-k1-sqlite-stream.cjs +1 -1
- package/bin/gsd-t-graph-metrics-rollup.cjs +527 -0
- package/bin/gsd-t-graph-query-cli.cjs +131 -31
- package/bin/gsd-t-graph-scip-upgrade.cjs +14 -5
- package/bin/gsd-t-graph-store-bakeoff.cjs +1 -1
- package/bin/gsd-t-graph-store-resolver.cjs +484 -0
- package/bin/gsd-t-verify-gate.cjs +9 -2
- package/bin/gsd-t.js +46 -1
- package/package.json +1 -1
- package/scripts/gsd-t-graph-intercept.js +89 -8
- package/scripts/gsd-t-read-intercept.js +87 -18
- package/templates/workflows/gsd-t-debug.workflow.js +19 -0
- package/templates/workflows/gsd-t-integrate.workflow.js +19 -0
- package/templates/workflows/gsd-t-phase.workflow.js +19 -0
- package/templates/workflows/gsd-t-quick.workflow.js +19 -0
- package/templates/workflows/gsd-t-scan.workflow.js +33 -1
- package/templates/workflows/gsd-t-verify.workflow.js +19 -0
|
@@ -71,6 +71,10 @@ const fs = require("node:fs");
|
|
|
71
71
|
const path = require("node:path");
|
|
72
72
|
const os = require("node:os");
|
|
73
73
|
|
|
74
|
+
// ─── M99 D1: Single store resolver (routing ALL path derivations here) ────────
|
|
75
|
+
// [RULE] one-resolver-only — no raw .gsd-t/graph.db literals outside this module
|
|
76
|
+
const _resolver = require("./gsd-t-graph-store-resolver.cjs");
|
|
77
|
+
|
|
74
78
|
// ─── ANSI colours ────────────────────────────────────────────────────────────
|
|
75
79
|
const C = {
|
|
76
80
|
reset: "\x1b[0m",
|
|
@@ -87,25 +91,40 @@ function colorize(str, ...codes) {
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
// ─── Store location ───────────────────────────────────────────────────────────
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
+
// ALL path resolution routes through the M99 D1 resolver module.
|
|
95
|
+
// [RULE] one-resolver-only — no raw .gsd-t/graph.db literals in this file.
|
|
96
|
+
//
|
|
97
|
+
// Discovery order (per resolver contract):
|
|
98
|
+
// 1. GSD_T_GRAPH_STORE env var override
|
|
99
|
+
// 2. cwd-walk: .gsd-t/graphDB/graph.db (new layout post-M99)
|
|
100
|
+
// 3. cwd-walk: .gsd-t/graph.db (legacy — triggers self-heal migration via migrateGraphStore)
|
|
101
|
+
// 4. cwd-walk: .gsd-t/graph-index/ (JSONL baseline)
|
|
94
102
|
|
|
95
103
|
function resolveStorePath() {
|
|
96
104
|
if (process.env.GSD_T_GRAPH_STORE) {
|
|
97
105
|
return process.env.GSD_T_GRAPH_STORE;
|
|
98
106
|
}
|
|
99
|
-
// Walk up from cwd to find the store.
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// the query never discovered it.)
|
|
107
|
+
// Walk up from cwd to find the store.
|
|
108
|
+
// Check the NEW graphDB layout first; fall back to legacy for self-heal;
|
|
109
|
+
// then JSONL for the baseline store. This is THE primary discovery path —
|
|
110
|
+
// post-migration it MUST find graphDB/graph.db, not the legacy .gsd-t/graph.db.
|
|
111
|
+
// [RULE] discovery-loop-end-to-end (pre-mortem #1)
|
|
105
112
|
let dir = process.cwd();
|
|
106
113
|
for (let i = 0; i < 10; i++) {
|
|
107
|
-
|
|
108
|
-
|
|
114
|
+
// New layout: .gsd-t/graphDB/graph.db (M99+)
|
|
115
|
+
const newDbPath = _resolver.resolveStorePath(dir);
|
|
116
|
+
if (fs.existsSync(newDbPath)) return newDbPath;
|
|
117
|
+
// Legacy layout: check via resolver's legacy path helper (no raw literals here)
|
|
118
|
+
const legacyDbPath = _resolver.resolveLegacyStorePath(dir); // [RULE] one-resolver-only
|
|
119
|
+
if (fs.existsSync(legacyDbPath)) {
|
|
120
|
+
// Self-heal: migrate to graphDB/ on first touch (T2 wire-in)
|
|
121
|
+
try { _resolver.migrateGraphStore(dir); } catch (_e) { /* fail-open */ }
|
|
122
|
+
// After migration, new path should exist; fall through to next iteration
|
|
123
|
+
if (fs.existsSync(newDbPath)) return newDbPath;
|
|
124
|
+
// Migration didn't complete (or skipped) — return legacy for backward compat
|
|
125
|
+
return legacyDbPath;
|
|
126
|
+
}
|
|
127
|
+
// JSONL baseline (pre-SQLite projects)
|
|
109
128
|
const jsonlDir = path.join(dir, ".gsd-t", "graph-index");
|
|
110
129
|
if (fs.existsSync(jsonlDir)) return jsonlDir;
|
|
111
130
|
const parent = path.dirname(dir);
|
|
@@ -221,12 +240,14 @@ function loadJsonlStore(storePath) {
|
|
|
221
240
|
function loadSkippedFiles(storePath) {
|
|
222
241
|
if (!storePath) return new Set();
|
|
223
242
|
try {
|
|
224
|
-
//
|
|
225
|
-
//
|
|
243
|
+
// storePath may be .gsd-t/graph-index/ (JSONL) or a .db path itself.
|
|
244
|
+
// All db path resolution goes through the M99 resolver. [RULE] one-resolver-only
|
|
226
245
|
let dbPath = null;
|
|
227
|
-
// Try storePath as a directory: look for graph.db
|
|
246
|
+
// Try storePath as a directory: look for graph.db via resolver (new graphDB/ layout first)
|
|
228
247
|
if (fs.existsSync(storePath) && fs.statSync(storePath).isDirectory()) {
|
|
229
|
-
|
|
248
|
+
// Derive project root from the JSONL dir (2-up), then resolve via resolver
|
|
249
|
+
const projRoot = _resolver.deriveProjectRoot(storePath);
|
|
250
|
+
const candidate = _resolver.resolveStorePath(projRoot);
|
|
230
251
|
if (fs.existsSync(candidate)) dbPath = candidate;
|
|
231
252
|
} else if (storePath.endsWith(".db") && fs.existsSync(storePath)) {
|
|
232
253
|
dbPath = storePath;
|
|
@@ -456,8 +477,13 @@ function loadStore(storePath) {
|
|
|
456
477
|
if (!storePath) return { ok: false, reason: "graph-unavailable" };
|
|
457
478
|
|
|
458
479
|
// SQLite is the PICKED store (K1) — read it first; fall back to JSONL baseline.
|
|
480
|
+
// Sibling-db fallback uses resolver (not a raw path.join). [RULE] one-resolver-only
|
|
481
|
+
const _siblingDb = storePath.endsWith(".db") ? null : (() => {
|
|
482
|
+
const projRoot = _resolver.deriveProjectRoot(storePath);
|
|
483
|
+
return _resolver.resolveStorePath(projRoot);
|
|
484
|
+
})();
|
|
459
485
|
const loaded = (storePath.endsWith(".db") ? loadSqliteStore(storePath) : loadJsonlStore(storePath))
|
|
460
|
-
|| loadJsonlStore(storePath) || (
|
|
486
|
+
|| loadJsonlStore(storePath) || (_siblingDb && fs.existsSync(_siblingDb) ? loadSqliteStore(_siblingDb) : null);
|
|
461
487
|
if (!loaded) return { ok: false, reason: "graph-unavailable" };
|
|
462
488
|
|
|
463
489
|
try {
|
|
@@ -507,13 +533,10 @@ function runFreshnessCheck(storePath) {
|
|
|
507
533
|
|
|
508
534
|
try {
|
|
509
535
|
// D4's real signatures take (db, projectRoot, ...) — NOT (storePath). The
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
const projectRoot = storePath.endsWith(".db")
|
|
515
|
-
? path.dirname(path.dirname(storePath)) // .gsd-t/graph.db → repo root
|
|
516
|
-
: path.dirname(path.dirname(storePath)); // .gsd-t/graph-index → repo root
|
|
536
|
+
// projectRoot is derived via the M99 resolver (branch-aware depth fix).
|
|
537
|
+
// [RULE] projectroot-depth-corrected-with-move [RULE] jsonl-branch-depth-preserved
|
|
538
|
+
// Pre-mortem #3: .db branch is 3-up post-graphDB/ move; JSONL branch stays 2-up.
|
|
539
|
+
const projectRoot = _resolver.deriveProjectRoot(storePath);
|
|
517
540
|
// Pass the exact storePath (canonical `.gsd-t/graph.db`) so openDb opens the
|
|
518
541
|
// real store, not a re-derived nested path. projectRoot is still used for the
|
|
519
542
|
// working-tree walk.
|
|
@@ -529,7 +552,18 @@ function runFreshnessCheck(storePath) {
|
|
|
529
552
|
let parseAndPut = null;
|
|
530
553
|
try { parseAndPut = require(path.join(__dirname, "gsd-t-graph-index.cjs")).parse_and_put; } catch { /* optional */ }
|
|
531
554
|
freshnessModule.freshness_check_on_query(db, projectRoot, touched, parseAndPut);
|
|
532
|
-
|
|
555
|
+
// Surface the touched-set so the query CLI can record freshness telemetry:
|
|
556
|
+
// edits = indexed files whose content-hash drifted (re-indexed before answering —
|
|
557
|
+
// i.e. a STALE query that was refreshed), adds = new files, deletes = removed.
|
|
558
|
+
// [RULE] freshness-telemetry-surfaced
|
|
559
|
+
const t = touched || {};
|
|
560
|
+
return {
|
|
561
|
+
ok: true,
|
|
562
|
+
editsCount: Array.isArray(t.edits) ? t.edits.length : 0,
|
|
563
|
+
addsCount: Array.isArray(t.adds) ? t.adds.length : 0,
|
|
564
|
+
deletesCount: Array.isArray(t.deletes) ? t.deletes.length : 0,
|
|
565
|
+
reindexedFiles: Array.isArray(t.edits) ? t.edits.slice(0, 20) : [],
|
|
566
|
+
};
|
|
533
567
|
} catch (_e) {
|
|
534
568
|
return { ok: false, reason: "graph-unavailable" };
|
|
535
569
|
}
|
|
@@ -1216,7 +1250,60 @@ if (require.main === module) {
|
|
|
1216
1250
|
const verb = args[0];
|
|
1217
1251
|
const target = args[1];
|
|
1218
1252
|
|
|
1253
|
+
// ─── Graph usage telemetry (M99, layer 1) ─────────────────────────────────
|
|
1254
|
+
// Every graph read funnels through emit(); append ONE JSON line per query via
|
|
1255
|
+
// the resolver's append_ledger_line to graphDB/logs/graph-events-NNN.jsonl so
|
|
1256
|
+
// graph usage + health is observable (hit/miss/graph-unavailable, tier, latency,
|
|
1257
|
+
// reindex). This is the chokepoint: one ledger captures 100% of graph reads
|
|
1258
|
+
// regardless of consumer. (M99 moved the sink here from the retired
|
|
1259
|
+
// .gsd-t/metrics/graph-events.jsonl path — see gsd-t-graph-store-resolver.cjs.)
|
|
1260
|
+
// FAIL-OPEN — a telemetry write must NEVER block or alter a query answer.
|
|
1261
|
+
// Consumer label comes from GSDT_GRAPH_CONSUMER (set by the calling workflow/hook);
|
|
1262
|
+
// defaults to "cli" for a bare invocation. [RULE] graph-telemetry-fail-open
|
|
1263
|
+
const _t0 = Date.now();
|
|
1264
|
+
let _telemStorePath = null; // set when storePath resolves; avoids const TDZ in the logger
|
|
1265
|
+
let _telemFreshness = null; // { reindexedCount, staleOnQuery, addsCount, deletesCount, reindexedFiles }
|
|
1266
|
+
// ─── Layer-1 sink: moved to graphDB/logs/ via the M99 resolver sink ──────────
|
|
1267
|
+
// Record SHAPE is KEPT byte-for-byte (kind:"query" + all fields from graph-metrics-contract.md).
|
|
1268
|
+
// Only the sink path + rotation/toggle is the supersede (no Divergence flag).
|
|
1269
|
+
// [RULE] layer1-shape-kept [RULE] fail-open-telemetry
|
|
1270
|
+
function _logGraphEvent(envelope) {
|
|
1271
|
+
try {
|
|
1272
|
+
const sp = (typeof _telemStorePath === "string" && _telemStorePath) ? _telemStorePath : null;
|
|
1273
|
+
// Derive projectRoot via the resolver (branch-aware). If no storePath yet, use cwd.
|
|
1274
|
+
const projRoot = sp ? _resolver.deriveProjectRoot(sp) : process.cwd();
|
|
1275
|
+
const e = envelope || {};
|
|
1276
|
+
const outcome = e.ok
|
|
1277
|
+
? (Array.isArray(e.results) && e.results.length === 0 ? "hit-empty" : "hit")
|
|
1278
|
+
: (e.reason || "error");
|
|
1279
|
+
const rec = {
|
|
1280
|
+
kind: "query",
|
|
1281
|
+
ts: new Date().toISOString(),
|
|
1282
|
+
verb: verb || null,
|
|
1283
|
+
target: target || null,
|
|
1284
|
+
outcome,
|
|
1285
|
+
tier: e.tier || null,
|
|
1286
|
+
resultCount: Array.isArray(e.results) ? e.results.length
|
|
1287
|
+
: (typeof e.fileCount === "number" ? e.fileCount : null),
|
|
1288
|
+
candidateCount: Array.isArray(e.candidates) ? e.candidates.length : null,
|
|
1289
|
+
latencyMs: Date.now() - _t0,
|
|
1290
|
+
consumer: process.env.GSDT_GRAPH_CONSUMER || "cli",
|
|
1291
|
+
via: process.env.GSDT_GRAPH_VIA || null,
|
|
1292
|
+
// Freshness signal: did this query run against code that had changed?
|
|
1293
|
+
// reindexedCount>0 ⇒ a STALE query that was auto-refreshed before answering.
|
|
1294
|
+
staleOnQuery: _telemFreshness ? _telemFreshness.staleOnQuery : null,
|
|
1295
|
+
reindexedCount: _telemFreshness ? _telemFreshness.reindexedCount : null,
|
|
1296
|
+
addsCount: _telemFreshness ? _telemFreshness.addsCount : null,
|
|
1297
|
+
deletesCount: _telemFreshness ? _telemFreshness.deletesCount : null,
|
|
1298
|
+
reindexedFiles: (_telemFreshness && _telemFreshness.reindexedCount) ? _telemFreshness.reindexedFiles : null,
|
|
1299
|
+
};
|
|
1300
|
+
// Delegate to the shared sink (graphDB/logs/ + rotation + GSDT_GRAPH_TELEMETRY toggle)
|
|
1301
|
+
_resolver.append_ledger_line(rec, projRoot);
|
|
1302
|
+
} catch { /* fail-open: telemetry never blocks a query */ }
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1219
1305
|
function emit(envelope) {
|
|
1306
|
+
_logGraphEvent(envelope);
|
|
1220
1307
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
1221
1308
|
}
|
|
1222
1309
|
|
|
@@ -1239,9 +1326,23 @@ if (require.main === module) {
|
|
|
1239
1326
|
}
|
|
1240
1327
|
|
|
1241
1328
|
const storePath = resolveStorePath();
|
|
1329
|
+
_telemStorePath = storePath;
|
|
1242
1330
|
|
|
1243
1331
|
// ── Step 1: D4 freshness check INLINE — [RULE] stale-file-reindexed-before-answer ──
|
|
1244
1332
|
const freshnessResult = runFreshnessCheck(storePath);
|
|
1333
|
+
// Capture freshness telemetry: how many files were stale + re-indexed on THIS query.
|
|
1334
|
+
// This is the "freshness reindexing on code updates" + "stale query" signal — a query
|
|
1335
|
+
// that triggered a re-index means the caller queried against code that had changed.
|
|
1336
|
+
try {
|
|
1337
|
+
const r = freshnessResult || {};
|
|
1338
|
+
_telemFreshness = {
|
|
1339
|
+
reindexedCount: typeof r.editsCount === "number" ? r.editsCount : 0,
|
|
1340
|
+
staleOnQuery: (typeof r.editsCount === "number" ? r.editsCount : 0) > 0,
|
|
1341
|
+
addsCount: typeof r.addsCount === "number" ? r.addsCount : 0,
|
|
1342
|
+
deletesCount: typeof r.deletesCount === "number" ? r.deletesCount : 0,
|
|
1343
|
+
reindexedFiles: Array.isArray(r.reindexedFiles) ? r.reindexedFiles : [],
|
|
1344
|
+
};
|
|
1345
|
+
} catch { /* fail-open */ }
|
|
1245
1346
|
if (!freshnessResult.ok) {
|
|
1246
1347
|
fail({ ok: false, reason: "graph-unavailable" });
|
|
1247
1348
|
}
|
|
@@ -1265,20 +1366,21 @@ if (require.main === module) {
|
|
|
1265
1366
|
const queryResult = queryWhoCalls(index, target);
|
|
1266
1367
|
if (queryResult.ambiguous) {
|
|
1267
1368
|
// [RULE] who-calls-function-identity-disambiguated
|
|
1268
|
-
|
|
1369
|
+
emit({
|
|
1269
1370
|
ok: false,
|
|
1270
1371
|
reason: "ambiguous-function",
|
|
1271
1372
|
verb,
|
|
1272
1373
|
target,
|
|
1273
1374
|
candidates: queryResult.candidates,
|
|
1274
|
-
})
|
|
1375
|
+
});
|
|
1275
1376
|
process.exit(2);
|
|
1276
1377
|
}
|
|
1277
1378
|
emit({ ok: true, verb, target, results: queryResult.results, tier: queryResult.tier, coverage: queryResult.coverage });
|
|
1278
1379
|
|
|
1279
1380
|
} else if (verb === "body") {
|
|
1280
1381
|
if (!target) fail({ ok: false, reason: "missing-target", verb });
|
|
1281
|
-
|
|
1382
|
+
// [RULE] projectroot-depth-corrected-with-move — resolver is branch-aware
|
|
1383
|
+
const projectRoot = _resolver.deriveProjectRoot(storePath);
|
|
1282
1384
|
let bodyResult = queryBody(index, target, projectRoot);
|
|
1283
1385
|
|
|
1284
1386
|
// [RULE] body-end-line-required — a pre-M98 node lacks end_line and its file
|
|
@@ -1313,9 +1415,7 @@ if (require.main === module) {
|
|
|
1313
1415
|
|
|
1314
1416
|
if (bodyResult.ambiguous) {
|
|
1315
1417
|
// [RULE] body-ambiguous-never-merged
|
|
1316
|
-
|
|
1317
|
-
ok: false, reason: "ambiguous-function", verb, target, candidates: bodyResult.candidates,
|
|
1318
|
-
}) + "\n");
|
|
1418
|
+
emit({ ok: false, reason: "ambiguous-function", verb, target, candidates: bodyResult.candidates });
|
|
1319
1419
|
process.exit(2);
|
|
1320
1420
|
}
|
|
1321
1421
|
if (bodyResult.notFound) fail({ ok: false, reason: "not-found", verb, target, file: bodyResult.file });
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
const fs = require('fs');
|
|
45
45
|
const path = require('path');
|
|
46
46
|
const { execSync, spawnSync } = require('child_process');
|
|
47
|
+
// Red Team HIGH (M99 round 4 — SC#1): SCIP indexes must live under .gsd-t/graphDB/,
|
|
48
|
+
// not loose in .gsd-t/. Route every index.scip / index-python.scip path through the
|
|
49
|
+
// single resolver. [RULE] scip-index-under-graphdb
|
|
50
|
+
const { resolveScipPath } = require('./gsd-t-graph-store-resolver.cjs');
|
|
47
51
|
|
|
48
52
|
// ── ANSI helpers ─────────────────────────────────────────────────────────────
|
|
49
53
|
|
|
@@ -133,7 +137,11 @@ function _resetScipCache(override) {
|
|
|
133
137
|
function runScipTypescript(projectRoot, outPath) {
|
|
134
138
|
// M95: emit to a REAL file (outPath) so the index can be READ and call edges
|
|
135
139
|
// resolved — not /dev/null. The old /dev/null path only proved invocability.
|
|
136
|
-
const out = outPath ||
|
|
140
|
+
const out = outPath || resolveScipPath('index.scip', projectRoot);
|
|
141
|
+
// Ensure the output dir (now .gsd-t/graphDB/) exists — scip-typescript's
|
|
142
|
+
// --output open() fails if the parent dir is absent (the old .gsd-t/ always
|
|
143
|
+
// existed; graphDB/ may not on a fresh build). [RULE] scip-index-under-graphdb
|
|
144
|
+
try { fs.mkdirSync(path.dirname(out), { recursive: true }); } catch {}
|
|
137
145
|
// --infer-tsconfig: resolve a tsconfig even when one isn't at the repo root
|
|
138
146
|
// (monorepos / nested layouts — e.g. web/tsconfig.json). Without it, projects
|
|
139
147
|
// whose tsconfig lives in a subdir got 0 resolved call edges. [RULE] scip-infer-nested-tsconfig
|
|
@@ -149,7 +157,8 @@ function runScipTypescript(projectRoot, outPath) {
|
|
|
149
157
|
function runScipPython(projectRoot, outPath) {
|
|
150
158
|
// Emit to a REAL file (separate from the TS index) so it can be READ + merged.
|
|
151
159
|
// --project-name is required-ish for stable symbols; derive from the dir name.
|
|
152
|
-
const out = outPath ||
|
|
160
|
+
const out = outPath || resolveScipPath('index-python.scip', projectRoot);
|
|
161
|
+
try { fs.mkdirSync(path.dirname(out), { recursive: true }); } catch {} // graphDB/ may not exist yet
|
|
153
162
|
const projName = path.basename(projectRoot).replace(/[^A-Za-z0-9_-]/g, '-') || 'project';
|
|
154
163
|
// scip-python REQUIRES a project version — it crashes (makeModuleInit) when the
|
|
155
164
|
// version is undefined (no pyproject.toml). Pass an explicit fallback version.
|
|
@@ -275,13 +284,13 @@ function buildScipResolver(repoRoot, opts = {}) {
|
|
|
275
284
|
|
|
276
285
|
if (avail.typescript && langs.typescript) {
|
|
277
286
|
try {
|
|
278
|
-
const run = runScipTypescript(repoRoot,
|
|
287
|
+
const run = runScipTypescript(repoRoot, resolveScipPath('index.scip', repoRoot));
|
|
279
288
|
if (run && run.ok) { mergeRead(readScipIndex(run.scipPath)); ranIndexers.push('typescript'); }
|
|
280
289
|
} catch { /* degrade to floor for TS */ }
|
|
281
290
|
}
|
|
282
291
|
if (avail.python && langs.python) {
|
|
283
292
|
try {
|
|
284
|
-
const run = runScipPython(repoRoot,
|
|
293
|
+
const run = runScipPython(repoRoot, resolveScipPath('index-python.scip', repoRoot));
|
|
285
294
|
if (run && run.ok) { mergeRead(readScipIndex(run.scipPath)); ranIndexers.push('python'); }
|
|
286
295
|
} catch { /* degrade to floor for Python */ }
|
|
287
296
|
}
|
|
@@ -326,7 +335,7 @@ function buildScipResolver(repoRoot, opts = {}) {
|
|
|
326
335
|
return { edges: out, resolved };
|
|
327
336
|
}
|
|
328
337
|
|
|
329
|
-
return { ok: true, indexers: ranIndexers, scipPath:
|
|
338
|
+
return { ok: true, indexers: ranIndexers, scipPath: resolveScipPath('index.scip', repoRoot), resolveFileEdges };
|
|
330
339
|
}
|
|
331
340
|
|
|
332
341
|
/** No-op resolver used when SCIP is unavailable (floor mode). */
|
|
@@ -234,7 +234,7 @@ function candidateJsonl(dir, graph) {
|
|
|
234
234
|
|
|
235
235
|
// ─── Candidate: SQLite (better-sqlite3 recursive CTE) ───────────────────
|
|
236
236
|
function candidateSqlite(dir, graph) {
|
|
237
|
-
const dbPath = path.join(dir, "graph.db");
|
|
237
|
+
const dbPath = path.join(dir, "graph.db"); // spike-local-store: throwaway bench dir
|
|
238
238
|
|
|
239
239
|
const { mod: Database, err: sqliteErr } = tryRequire("better-sqlite3");
|
|
240
240
|
if (!Database) {
|