grepmax 0.17.1 → 0.17.3

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
@@ -64,6 +64,7 @@ gmax log src/lib/auth.ts # Git commit history for a path or symb
64
64
  gmax test handleAuth # Find tests via reverse call graph
65
65
  gmax impact handleAuth # Dependents + affected tests
66
66
  gmax similar handleAuth # Find similar code patterns
67
+ gmax dead handleAuth # Unused-symbol check via call graph (DEAD / PUBLIC EXPORT / LIVE)
67
68
  gmax context "auth system" --budget 4000 # Token-budgeted topic summary
68
69
  ```
69
70
 
@@ -121,6 +122,7 @@ Plugins auto-update when you run `npm install -g grepmax@latest` — no need to
121
122
  | `trace_calls` | Call graph: importers, callers (multi-hop), callees with file:line. |
122
123
  | `extract_symbol` | Complete function/class body by symbol name. |
123
124
  | `peek_symbol` | Compact overview: signature + callers + callees. |
125
+ | `dead` | Unused-symbol check via call graph. Returns `DEAD`, `PUBLIC EXPORT`, or `LIVE` with caller count. |
124
126
  | `list_symbols` | Indexed symbols with role and export status. |
125
127
  | `index_status` | Index health: chunks, files, projects, watcher status. |
126
128
  | `summarize_project` | Project overview: languages, structure, key symbols, entry points. |
@@ -277,6 +279,7 @@ fixtures/
277
279
  | `GMAX_WORKER_THREADS` | Worker threads for embedding | 50% of cores |
278
280
  | `GMAX_DEBUG` | Debug logging | Off |
279
281
  | `GMAX_SUMMARIZER` | Enable summarizer auto-start (`1`) | Off |
282
+ | `GMAX_RERANK` | Enable ColBERT rerank (`1`) — off by default since v0.17.1 ([why](docs/known-limitations.md)) | Off |
280
283
 
281
284
  ## Troubleshooting
282
285
 
@@ -293,6 +296,20 @@ gmax watch stop && gmax watch --daemon -b # Restart daemon
293
296
 
294
297
  See [CLAUDE.md](CLAUDE.md) for development setup, commands, and architecture details.
295
298
 
299
+ ### Benchmarks
300
+
301
+ Two evaluation harnesses live in the repo. Both emit stable JSON via `:json` variants.
302
+
303
+ ```bash
304
+ pnpm bench:recall # 97-case internal eval against gmax's own repo
305
+ pnpm bench:recall:json
306
+ pnpm bench:oss # P1 definition-lookup across express, lodash, platform (sverklo-bench fixtures)
307
+ pnpm bench:oss:json
308
+ GMAX_EVAL_RERANK=1 pnpm bench:oss # toggle ColBERT rerank
309
+ ```
310
+
311
+ The OSS bench requires the fixture repos to be indexed first — see [`docs/known-limitations.md`](docs/known-limitations.md) for the most recent rerank-on-vs-off comparison across 4 datasets / 131 cases.
312
+
296
313
  ## Attribution
297
314
 
298
315
  grepmax is built upon the foundation of [mgrep](https://github.com/mixedbread-ai/mgrep) by MixedBread. See the [NOTICE](NOTICE) file for details.
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.dead = void 0;
46
+ const commander_1 = require("commander");
47
+ const graph_builder_1 = require("../lib/graph/graph-builder");
48
+ const vector_db_1 = require("../lib/store/vector-db");
49
+ const exit_1 = require("../lib/utils/exit");
50
+ const filter_builder_1 = require("../lib/utils/filter-builder");
51
+ const project_registry_1 = require("../lib/utils/project-registry");
52
+ const project_root_1 = require("../lib/utils/project-root");
53
+ const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
54
+ const style = {
55
+ bold: (s) => (useColors ? `\x1b[1m${s}\x1b[22m` : s),
56
+ dim: (s) => (useColors ? `\x1b[2m${s}\x1b[22m` : s),
57
+ red: (s) => (useColors ? `\x1b[31m${s}\x1b[39m` : s),
58
+ yellow: (s) => (useColors ? `\x1b[33m${s}\x1b[39m` : s),
59
+ green: (s) => (useColors ? `\x1b[32m${s}\x1b[39m` : s),
60
+ };
61
+ const TOP_CALLERS = 3;
62
+ function statusLabel(status) {
63
+ switch (status) {
64
+ case "DEAD":
65
+ return "DEAD";
66
+ case "PUBLIC_EXPORT":
67
+ return "PUBLIC EXPORT";
68
+ case "LIVE":
69
+ return "LIVE";
70
+ }
71
+ }
72
+ function formatHuman(r, projectRoot) {
73
+ const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
74
+ const defLoc = `${rel(r.defPath)}:${r.defLine + 1}`;
75
+ if (r.status === "DEAD") {
76
+ return `${style.red(style.bold("DEAD"))} ${defLoc} defines ${style.bold(r.symbol)}`;
77
+ }
78
+ if (r.status === "PUBLIC_EXPORT") {
79
+ return `${style.yellow(style.bold("PUBLIC EXPORT"))} ${defLoc} defines ${style.bold(r.symbol)} ${style.dim("— no internal callers found; check external usage")}`;
80
+ }
81
+ const header = `${style.green(style.bold("LIVE"))} ${defLoc} defines ${style.bold(r.symbol)} ${style.dim(`— ${r.callerCount} inbound caller${r.callerCount === 1 ? "" : "s"} (top ${Math.min(TOP_CALLERS, r.topCallers.length)}):`)}`;
82
+ const lines = [header];
83
+ for (const c of r.topCallers) {
84
+ lines.push(` ${rel(c.file)}:${c.line + 1}`);
85
+ }
86
+ return lines.join("\n");
87
+ }
88
+ function formatAgent(r, projectRoot) {
89
+ const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
90
+ const defLoc = `${rel(r.defPath)}:${r.defLine + 1}`;
91
+ const callerLocs = r.topCallers
92
+ .map((c) => `${rel(c.file)}:${c.line + 1}`)
93
+ .join(",");
94
+ return [
95
+ statusLabel(r.status),
96
+ defLoc,
97
+ String(r.callerCount),
98
+ callerLocs,
99
+ ].join("\t");
100
+ }
101
+ exports.dead = new commander_1.Command("dead")
102
+ .description("Report whether a symbol has zero inbound callers in the indexed call graph. " +
103
+ "The call graph reflects what tree-sitter chunked — dynamic dispatch, " +
104
+ "reflection, eval, and string-built call sites won't show up, so a 'DEAD' " +
105
+ "result is a hypothesis, not a proof. Exported public-API symbols " +
106
+ "legitimately have no in-project callers (reported as PUBLIC EXPORT).")
107
+ .argument("<symbol>", "The symbol to check")
108
+ .option("--root <dir>", "Project root directory")
109
+ .option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => prev ? [...prev, value] : [value])
110
+ .option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => prev ? [...prev, value] : [value])
111
+ .option("--agent", "Compact TSV output for AI agents", false)
112
+ .action((symbol, opts) => __awaiter(void 0, void 0, void 0, function* () {
113
+ var _a;
114
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
115
+ if (root === null)
116
+ return;
117
+ let vectorDb = null;
118
+ try {
119
+ const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
120
+ const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
121
+ vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
122
+ const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
123
+ const scope = resolveScope({
124
+ projectRoot,
125
+ in: opts.in,
126
+ exclude: opts.exclude,
127
+ });
128
+ // Resolve the defining chunk to get path, line, and is_exported.
129
+ const table = yield vectorDb.ensureTable();
130
+ const defRows = yield table
131
+ .query()
132
+ .select(["path", "start_line", "is_exported"])
133
+ .where(buildScopeWhere(scope, `array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}')`))
134
+ .limit(1)
135
+ .toArray();
136
+ if (defRows.length === 0) {
137
+ console.log(opts.agent ? "(not found)" : `Symbol not found: ${symbol}`);
138
+ process.exitCode = 1;
139
+ return;
140
+ }
141
+ const defRow = defRows[0];
142
+ const defPath = String(defRow.path || "");
143
+ const defLine = Number(defRow.start_line || 0);
144
+ const isExported = Boolean(defRow.is_exported);
145
+ const builder = new graph_builder_1.GraphBuilder(vectorDb, scope.pathPrefix, scope.excludePrefixes);
146
+ const callers = yield builder.getCallers(symbol);
147
+ const status = callers.length === 0
148
+ ? isExported
149
+ ? "PUBLIC_EXPORT"
150
+ : "DEAD"
151
+ : "LIVE";
152
+ const topCallers = callers
153
+ .slice(0, TOP_CALLERS)
154
+ .map((c) => ({ file: c.file, line: c.line }));
155
+ const result = {
156
+ status,
157
+ symbol,
158
+ defPath,
159
+ defLine,
160
+ callerCount: callers.length,
161
+ topCallers,
162
+ };
163
+ console.log(opts.agent
164
+ ? formatAgent(result, projectRoot)
165
+ : formatHuman(result, projectRoot));
166
+ }
167
+ catch (error) {
168
+ const message = error instanceof Error ? error.message : "Unknown error";
169
+ console.error("Dead check failed:", message);
170
+ process.exitCode = 1;
171
+ }
172
+ finally {
173
+ if (vectorDb) {
174
+ try {
175
+ yield vectorDb.close();
176
+ }
177
+ catch (err) {
178
+ console.error("Failed to close VectorDB:", err);
179
+ }
180
+ }
181
+ yield (0, exit_1.gracefulExit)();
182
+ }
183
+ }));
@@ -161,6 +161,18 @@ const TOOLS = [
161
161
  required: ["symbol"],
162
162
  },
163
163
  },
164
+ {
165
+ name: "dead",
166
+ description: "Report whether a symbol has zero inbound callers in the indexed call graph. Returns DEAD, PUBLIC EXPORT (exported with no internal callers), or LIVE with caller count. Hypothesis, not proof: dynamic dispatch, reflection, and string-built call sites won't show up.",
167
+ inputSchema: {
168
+ type: "object",
169
+ properties: {
170
+ symbol: { type: "string", description: "Symbol name to check" },
171
+ root: { type: "string", description: "Project root (absolute path)" },
172
+ },
173
+ required: ["symbol"],
174
+ },
175
+ },
164
176
  {
165
177
  name: "list_symbols",
166
178
  description: "List indexed symbols with role and export status.",
@@ -1113,6 +1125,57 @@ exports.mcp = new commander_1.Command("mcp")
1113
1125
  }
1114
1126
  });
1115
1127
  }
1128
+ function handleDead(args) {
1129
+ return __awaiter(this, void 0, void 0, function* () {
1130
+ ensureWatcher();
1131
+ const symbol = String(args.symbol || "");
1132
+ if (!symbol)
1133
+ return err("Missing required parameter: symbol");
1134
+ try {
1135
+ const root = typeof args.root === "string" && args.root
1136
+ ? args.root
1137
+ : projectRoot;
1138
+ const db = getVectorDb();
1139
+ const table = yield db.ensureTable();
1140
+ const prefix = root.endsWith("/") ? root : `${root}/`;
1141
+ const defRows = yield table
1142
+ .query()
1143
+ .select(["path", "start_line", "is_exported"])
1144
+ .where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
1145
+ .limit(1)
1146
+ .toArray();
1147
+ if (defRows.length === 0) {
1148
+ return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`);
1149
+ }
1150
+ const defRow = defRows[0];
1151
+ const defPath = String(defRow.path || "");
1152
+ const defLine = Number(defRow.start_line || 0);
1153
+ const isExported = Boolean(defRow.is_exported);
1154
+ const builder = new graph_builder_1.GraphBuilder(db, root);
1155
+ const callers = yield builder.getCallers(symbol);
1156
+ const rel = (p) => p.startsWith(root) ? p.slice(root.length + 1) : p;
1157
+ const defLoc = `${rel(defPath)}:${defLine + 1}`;
1158
+ if (callers.length === 0) {
1159
+ if (isExported) {
1160
+ return ok(`PUBLIC EXPORT ${defLoc} defines ${symbol} — no internal callers found; check external usage`);
1161
+ }
1162
+ return ok(`DEAD ${defLoc} defines ${symbol}`);
1163
+ }
1164
+ const top = callers.slice(0, 3);
1165
+ const lines = [
1166
+ `LIVE ${defLoc} defines ${symbol} — ${callers.length} inbound caller${callers.length === 1 ? "" : "s"} (top ${top.length}):`,
1167
+ ];
1168
+ for (const c of top) {
1169
+ lines.push(` ${rel(c.file)}:${c.line + 1}`);
1170
+ }
1171
+ return ok(lines.join("\n"));
1172
+ }
1173
+ catch (e) {
1174
+ const msg = e instanceof Error ? e.message : String(e);
1175
+ return err(`Dead check failed: ${msg}`);
1176
+ }
1177
+ });
1178
+ }
1116
1179
  function handleListSymbols(args) {
1117
1180
  return __awaiter(this, void 0, void 0, function* () {
1118
1181
  ensureWatcher();
@@ -1831,6 +1894,9 @@ exports.mcp = new commander_1.Command("mcp")
1831
1894
  case "peek_symbol":
1832
1895
  result = yield handlePeekSymbol(toolArgs);
1833
1896
  break;
1897
+ case "dead":
1898
+ result = yield handleDead(toolArgs);
1899
+ break;
1834
1900
  case "list_symbols":
1835
1901
  result = yield handleListSymbols(toolArgs);
1836
1902
  break;
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 0 sanity check for Bundle B G1' (graph-as-recall-recovery).
4
+ *
5
+ * For each platform hard-miss case (BeyondError, ErrorCodes, resolveActor,
6
+ * errorHandler), mirror the searcher's dense+FTS+RRF pipeline to produce
7
+ * the post-fusion top-200 candidate pool, then count how many of those
8
+ * 200 chunks have `referenced_symbols` containing the target symbol.
9
+ *
10
+ * ≥1 hit per case = 1-hop outbound graph walk can recover the target
11
+ * (the seed chunk's defined symbol points at the target via the call
12
+ * graph) → worth building the recovery layer.
13
+ *
14
+ * 0 hits across all 4 = the signal isn't in the graph at this seed
15
+ * depth → stop and report the negative result.
16
+ *
17
+ * Run: `npx tsx src/eval-graph-sanity.ts`
18
+ */
19
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ var desc = Object.getOwnPropertyDescriptor(m, k);
22
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
23
+ desc = { enumerable: true, get: function() { return m[k]; } };
24
+ }
25
+ Object.defineProperty(o, k2, desc);
26
+ }) : (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ o[k2] = m[k];
29
+ }));
30
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
31
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
32
+ }) : function(o, v) {
33
+ o["default"] = v;
34
+ });
35
+ var __importStar = (this && this.__importStar) || (function () {
36
+ var ownKeys = function(o) {
37
+ ownKeys = Object.getOwnPropertyNames || function (o) {
38
+ var ar = [];
39
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
40
+ return ar;
41
+ };
42
+ return ownKeys(o);
43
+ };
44
+ return function (mod) {
45
+ if (mod && mod.__esModule) return mod;
46
+ var result = {};
47
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
48
+ __setModuleDefault(result, mod);
49
+ return result;
50
+ };
51
+ })();
52
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
53
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
54
+ return new (P || (P = Promise))(function (resolve, reject) {
55
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
56
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
57
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
58
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
59
+ });
60
+ };
61
+ var _a, _b;
62
+ var _c;
63
+ Object.defineProperty(exports, "__esModule", { value: true });
64
+ (_a = (_c = process.env).GMAX_WORKER_COUNT) !== null && _a !== void 0 ? _a : (_c.GMAX_WORKER_COUNT = "1");
65
+ const path = __importStar(require("node:path"));
66
+ const config_1 = require("./config");
67
+ const vector_db_1 = require("./lib/store/vector-db");
68
+ const exit_1 = require("./lib/utils/exit");
69
+ const pool_1 = require("./lib/workers/pool");
70
+ const filter_builder_1 = require("./lib/utils/filter-builder");
71
+ const PLATFORM_ROOT = path.join((_b = process.env.HOME) !== null && _b !== void 0 ? _b : "", "Development/beyond/platform");
72
+ const HARD_MISS_TARGETS = [
73
+ "BeyondError",
74
+ "ErrorCodes",
75
+ "resolveActor",
76
+ "errorHandler",
77
+ ];
78
+ const PRE_K = 500;
79
+ const STAGE1_K = 200;
80
+ const RRF_K = 60;
81
+ function toStringArray(val) {
82
+ if (!val)
83
+ return [];
84
+ if (Array.isArray(val)) {
85
+ return val.filter((v) => typeof v === "string");
86
+ }
87
+ const maybe = val;
88
+ if (typeof maybe.toArray === "function") {
89
+ try {
90
+ const arr = maybe.toArray();
91
+ return Array.isArray(arr)
92
+ ? arr.filter((v) => typeof v === "string")
93
+ : [];
94
+ }
95
+ catch (_a) {
96
+ return [];
97
+ }
98
+ }
99
+ return [];
100
+ }
101
+ function probe(target) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ const db = new vector_db_1.VectorDB(config_1.PATHS.lancedbDir);
104
+ const table = yield db.ensureTable();
105
+ const pool = (0, pool_1.getWorkerPool)();
106
+ const { dense } = yield pool.encodeQuery(target);
107
+ const pathPrefix = PLATFORM_ROOT.endsWith("/") ? PLATFORM_ROOT : `${PLATFORM_ROOT}/`;
108
+ const where = `path LIKE '${(0, filter_builder_1.escapeSqlString)(pathPrefix)}%'`;
109
+ const columns = [
110
+ "id",
111
+ "path",
112
+ "chunk_index",
113
+ "start_line",
114
+ "end_line",
115
+ "defined_symbols",
116
+ "referenced_symbols",
117
+ ];
118
+ const vectorRows = (yield table
119
+ .vectorSearch(dense)
120
+ .select([...columns, "_distance"])
121
+ .where(where)
122
+ .limit(PRE_K)
123
+ .toArray());
124
+ let ftsRows = [];
125
+ try {
126
+ ftsRows = (yield table
127
+ .search(target)
128
+ .select([...columns, "_score"])
129
+ .where(where)
130
+ .limit(PRE_K)
131
+ .toArray());
132
+ }
133
+ catch (e) {
134
+ console.warn(`[sanity] FTS unavailable for "${target}": ${e.message}`);
135
+ }
136
+ const candidateScores = new Map();
137
+ const docMap = new Map();
138
+ vectorRows.forEach((doc, rank) => {
139
+ const key = doc.id ||
140
+ `${doc.path}:${doc.chunk_index}`;
141
+ docMap.set(key, doc);
142
+ candidateScores.set(key, (candidateScores.get(key) || 0) + 1.0 / (RRF_K + rank + 1));
143
+ });
144
+ ftsRows.forEach((doc, rank) => {
145
+ const key = doc.id ||
146
+ `${doc.path}:${doc.chunk_index}`;
147
+ if (!docMap.has(key))
148
+ docMap.set(key, doc);
149
+ candidateScores.set(key, (candidateScores.get(key) || 0) + 1.0 / (RRF_K + rank + 1));
150
+ });
151
+ const fused = Array.from(candidateScores.entries())
152
+ .sort((a, b) => b[1] - a[1])
153
+ .slice(0, STAGE1_K)
154
+ .map(([key]) => docMap.get(key))
155
+ .filter(Boolean);
156
+ let withRef = 0;
157
+ let withDef = 0;
158
+ let rankOfFirstRef = -1;
159
+ let rankOfFirstDef = -1;
160
+ const sampleRefHits = [];
161
+ fused.forEach((doc, i) => {
162
+ const refs = toStringArray(doc.referenced_symbols);
163
+ const defs = toStringArray(doc.defined_symbols);
164
+ if (refs.includes(target)) {
165
+ withRef++;
166
+ if (rankOfFirstRef < 0)
167
+ rankOfFirstRef = i + 1;
168
+ if (sampleRefHits.length < 3) {
169
+ sampleRefHits.push({ rank: i + 1, path: String(doc.path) });
170
+ }
171
+ }
172
+ if (defs.includes(target)) {
173
+ withDef++;
174
+ if (rankOfFirstDef < 0)
175
+ rankOfFirstDef = i + 1;
176
+ }
177
+ });
178
+ yield db.close();
179
+ return {
180
+ target,
181
+ poolSize: fused.length,
182
+ vectorRows: vectorRows.length,
183
+ ftsRows: ftsRows.length,
184
+ withRef,
185
+ withDef,
186
+ rankOfFirstRef,
187
+ rankOfFirstDef,
188
+ sampleRefHits,
189
+ };
190
+ });
191
+ }
192
+ function main() {
193
+ return __awaiter(this, void 0, void 0, function* () {
194
+ console.log(`Phase 0 sanity check — graph reachability on platform hard-miss cases`);
195
+ console.log(`pathPrefix: ${PLATFORM_ROOT}`);
196
+ console.log(`STAGE1_K=${STAGE1_K}, PRE_K=${PRE_K}\n`);
197
+ let anyReachable = false;
198
+ const summary = [];
199
+ for (const target of HARD_MISS_TARGETS) {
200
+ const res = yield probe(target);
201
+ summary.push(res);
202
+ const refOK = res.withRef > 0 ? "✓" : "✗";
203
+ const defOK = res.withDef > 0 ? "✓" : "✗";
204
+ console.log(`${refOK} ${target.padEnd(16)} pool=${String(res.poolSize).padStart(3)} ` +
205
+ `refs=${String(res.withRef).padStart(3)}/200 defs=${defOK} (${res.withDef}) ` +
206
+ `1st-ref@${res.rankOfFirstRef > 0 ? res.rankOfFirstRef : "—"} ` +
207
+ `1st-def@${res.rankOfFirstDef > 0 ? res.rankOfFirstDef : "—"}`);
208
+ for (const s of res.sampleRefHits) {
209
+ console.log(` ↳ rank ${s.rank}: ${s.path}`);
210
+ }
211
+ if (res.withRef > 0)
212
+ anyReachable = true;
213
+ }
214
+ console.log(`\nVerdict: ${anyReachable ? "BUILD (≥1 case has graph signal in top-200)" : "ABORT (graph is empty at this depth — pick a different mechanism)"}`);
215
+ console.log(`\nJSON:`);
216
+ console.log(JSON.stringify(summary, null, 2));
217
+ yield (0, exit_1.gracefulExit)(0);
218
+ });
219
+ }
220
+ if (require.main === module) {
221
+ main().catch((e) => {
222
+ console.error(e);
223
+ process.exit(1);
224
+ });
225
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Pulls the chunks for a known BeyondError-using file out of the index
4
+ * and prints their `referenced_symbols` arrays. Lets us see what the
5
+ * chunker actually extracts vs what's missing — whether BeyondError is
6
+ * absent due to chunker scope (e.g., only same-function refs are tagged)
7
+ * or whether the tagging is just sparse.
8
+ */
9
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
10
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
13
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
14
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
15
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
16
+ });
17
+ };
18
+ var _a;
19
+ var _b;
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ (_a = (_b = process.env).GMAX_WORKER_COUNT) !== null && _a !== void 0 ? _a : (_b.GMAX_WORKER_COUNT = "1");
22
+ const config_1 = require("./config");
23
+ const vector_db_1 = require("./lib/store/vector-db");
24
+ const exit_1 = require("./lib/utils/exit");
25
+ const filter_builder_1 = require("./lib/utils/filter-builder");
26
+ const CANDIDATES = [
27
+ "/Users/reoiv/Development/beyond/platform/packages/api/src/middleware/error.ts",
28
+ "/Users/reoiv/Development/beyond/platform/packages/api/src/graphql/shared/error-mapping.ts",
29
+ "/Users/reoiv/Development/beyond/platform/packages/api/src/lib/service-errors.ts",
30
+ ];
31
+ function toStrArr(v) {
32
+ if (!v)
33
+ return [];
34
+ if (Array.isArray(v))
35
+ return v.filter((x) => typeof x === "string");
36
+ const m = v;
37
+ if (typeof m.toArray === "function") {
38
+ try {
39
+ const a = m.toArray();
40
+ return Array.isArray(a) ? a.filter((x) => typeof x === "string") : [];
41
+ }
42
+ catch (_a) {
43
+ return [];
44
+ }
45
+ }
46
+ return [];
47
+ }
48
+ function main() {
49
+ return __awaiter(this, void 0, void 0, function* () {
50
+ const db = new vector_db_1.VectorDB(config_1.PATHS.lancedbDir);
51
+ const table = yield db.ensureTable();
52
+ for (const file of CANDIDATES) {
53
+ console.log(`\n── ${file}`);
54
+ const rows = (yield table
55
+ .query()
56
+ .select(["start_line", "end_line", "chunk_type", "defined_symbols", "referenced_symbols"])
57
+ .where(`path = '${(0, filter_builder_1.escapeSqlString)(file)}'`)
58
+ .limit(20)
59
+ .toArray());
60
+ if (rows.length === 0) {
61
+ console.log(" (no chunks indexed for this path)");
62
+ continue;
63
+ }
64
+ for (const r of rows) {
65
+ const defs = toStrArr(r.defined_symbols);
66
+ const refs = toStrArr(r.referenced_symbols);
67
+ const hasBE = refs.includes("BeyondError");
68
+ console.log(` lines ${r.start_line}-${r.end_line} (${r.chunk_type}) ` +
69
+ `defs=[${defs.slice(0, 4).join(",")}${defs.length > 4 ? "…" : ""}] ` +
70
+ `refs(${refs.length})=[${refs.slice(0, 8).join(",")}${refs.length > 8 ? "…" : ""}] ` +
71
+ `BeyondError-ref=${hasBE ? "✓" : "✗"}`);
72
+ }
73
+ }
74
+ yield db.close();
75
+ yield (0, exit_1.gracefulExit)(0);
76
+ });
77
+ }
78
+ if (require.main === module) {
79
+ main().catch((e) => {
80
+ console.error(e);
81
+ process.exit(1);
82
+ });
83
+ }