grepmax 0.17.23 → 0.18.0

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.
@@ -1,20 +1,28 @@
1
1
  "use strict";
2
2
  /**
3
- * Phase 0 sanity check for Bundle B G1' (graph-as-recall-recovery).
3
+ * Graph sanity checks.
4
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.
5
+ * Default mode caller-count guard (regression net for the chunker-v2 /
6
+ * call-site-dedup fix). Before that fix, `splitIfTooBig` copied a parent
7
+ * chunk's full `referenced_symbols` onto every sub-chunk, so `getCallers`
8
+ * multiplied chunk rows: a symbol with 3 real call sites reported 66 callers
9
+ * (22x). For a set of known gmax symbols this asserts `getCallers`' raw count
10
+ * stays within a small multiple of grep-truth (word-boundary line matches
11
+ * across tracked source). Exits nonzero if any symbol blows past the ceiling.
12
+ * Run after a reindex so the index matches the working tree.
9
13
  *
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.
14
+ * npx tsx src/eval-graph-sanity.ts
13
15
  *
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
+ * Platform mode (--platform) the original Phase 0 probe for Bundle B G1'
17
+ * (graph-as-recall-recovery). For each platform hard-miss case (BeyondError,
18
+ * ErrorCodes, resolveActor, errorHandler), mirror the searcher's dense+FTS+RRF
19
+ * pipeline to the post-fusion top-200 pool, then count how many of those chunks
20
+ * carry the target in `referenced_symbols`. ≥1 hit = a 1-hop outbound graph
21
+ * walk can recover the target → worth building the recovery layer; 0 across all
22
+ * 4 = the signal isn't in the graph at this depth. Requires
23
+ * ~/Development/beyond/platform to be indexed.
16
24
  *
17
- * Run: `npx tsx src/eval-graph-sanity.ts`
25
+ * npx tsx src/eval-graph-sanity.ts --platform
18
26
  */
19
27
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
20
28
  if (k2 === undefined) k2 = k;
@@ -62,12 +70,15 @@ var _a, _b;
62
70
  var _c;
63
71
  Object.defineProperty(exports, "__esModule", { value: true });
64
72
  (_a = (_c = process.env).GMAX_WORKER_COUNT) !== null && _a !== void 0 ? _a : (_c.GMAX_WORKER_COUNT = "1");
73
+ const node_child_process_1 = require("node:child_process");
65
74
  const path = __importStar(require("node:path"));
66
75
  const config_1 = require("./config");
76
+ const callsites_1 = require("./lib/graph/callsites");
77
+ const graph_builder_1 = require("./lib/graph/graph-builder");
67
78
  const vector_db_1 = require("./lib/store/vector-db");
68
79
  const exit_1 = require("./lib/utils/exit");
69
- const pool_1 = require("./lib/workers/pool");
70
80
  const filter_builder_1 = require("./lib/utils/filter-builder");
81
+ const pool_1 = require("./lib/workers/pool");
71
82
  const PLATFORM_ROOT = path.join((_b = process.env.HOME) !== null && _b !== void 0 ? _b : "", "Development/beyond/platform");
72
83
  const HARD_MISS_TARGETS = [
73
84
  "BeyondError",
@@ -104,7 +115,9 @@ function probe(target) {
104
115
  const table = yield db.ensureTable();
105
116
  const pool = (0, pool_1.getWorkerPool)();
106
117
  const { dense } = yield pool.encodeQuery(target);
107
- const pathPrefix = PLATFORM_ROOT.endsWith("/") ? PLATFORM_ROOT : `${PLATFORM_ROOT}/`;
118
+ const pathPrefix = PLATFORM_ROOT.endsWith("/")
119
+ ? PLATFORM_ROOT
120
+ : `${PLATFORM_ROOT}/`;
108
121
  const where = `path LIKE '${(0, filter_builder_1.escapeSqlString)(pathPrefix)}%'`;
109
122
  const columns = [
110
123
  "id",
@@ -136,14 +149,12 @@ function probe(target) {
136
149
  const candidateScores = new Map();
137
150
  const docMap = new Map();
138
151
  vectorRows.forEach((doc, rank) => {
139
- const key = doc.id ||
140
- `${doc.path}:${doc.chunk_index}`;
152
+ const key = doc.id || `${doc.path}:${doc.chunk_index}`;
141
153
  docMap.set(key, doc);
142
154
  candidateScores.set(key, (candidateScores.get(key) || 0) + 1.0 / (RRF_K + rank + 1));
143
155
  });
144
156
  ftsRows.forEach((doc, rank) => {
145
- const key = doc.id ||
146
- `${doc.path}:${doc.chunk_index}`;
157
+ const key = doc.id || `${doc.path}:${doc.chunk_index}`;
147
158
  if (!docMap.has(key))
148
159
  docMap.set(key, doc);
149
160
  candidateScores.set(key, (candidateScores.get(key) || 0) + 1.0 / (RRF_K + rank + 1));
@@ -189,7 +200,7 @@ function probe(target) {
189
200
  };
190
201
  });
191
202
  }
192
- function main() {
203
+ function platformProbe() {
193
204
  return __awaiter(this, void 0, void 0, function* () {
194
205
  console.log(`Phase 0 sanity check — graph reachability on platform hard-miss cases`);
195
206
  console.log(`pathPrefix: ${PLATFORM_ROOT}`);
@@ -214,7 +225,115 @@ function main() {
214
225
  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
226
  console.log(`\nJSON:`);
216
227
  console.log(JSON.stringify(summary, null, 2));
217
- yield (0, exit_1.gracefulExit)(0);
228
+ });
229
+ }
230
+ // ---------------------------------------------------------------------------
231
+ // Caller-count guard (default mode)
232
+ // ---------------------------------------------------------------------------
233
+ const GMAX_ROOT = path.resolve(__dirname, "..");
234
+ /**
235
+ * Distinctive gmax symbols with a spread of real usage. All sit comfortably
236
+ * below getCallers' internal `.limit(100)`, so the cap can never silently mask
237
+ * an explosion — if the chunker regresses, the raw count blows past the
238
+ * ceiling rather than getting clamped to 100. Names are intentionally unusual
239
+ * (no bare words) so `git grep -w` truth isn't polluted by unrelated matches.
240
+ */
241
+ const GUARD_SYMBOLS = [
242
+ "resolveCallSites",
243
+ "findCallSiteSnippet",
244
+ "withQueryTimeout",
245
+ "isFileCached",
246
+ "getWorkerPool",
247
+ "isBuiltinCallee",
248
+ "findDependents",
249
+ "buildFileSubgraph",
250
+ ];
251
+ /**
252
+ * getCallers returns one chunk-row per referencing chunk. Healthy, that count
253
+ * is ≤ the number of source lines mentioning the symbol (several mentions in
254
+ * one chunk collapse to one row; def/import lines aren't call references). So
255
+ * the healthy ratio is ≤ ~1. A small multiple absorbs index-vs-worktree skew
256
+ * and chunk-boundary splits while still catching the 22x chunker-v2 blowup.
257
+ */
258
+ const CEILING_MULTIPLE = 3;
259
+ /** Mirror of getCallers' internal `.limit(100)` — at the cap the guard is blind. */
260
+ const CALLER_LIMIT = 100;
261
+ /**
262
+ * grep-truth: word-boundary line + file counts for `symbol` across tracked
263
+ * `.ts` (repo-wide, so experiments/ and scripts/ count too — they're indexed).
264
+ * `git grep` skips gitignored paths (dist/), matching the indexed corpus.
265
+ */
266
+ function grepTruth(symbol) {
267
+ let out;
268
+ try {
269
+ out = (0, node_child_process_1.execFileSync)("git", ["grep", "-Fwc", symbol, "--", "*.ts"], {
270
+ cwd: GMAX_ROOT,
271
+ encoding: "utf-8",
272
+ });
273
+ }
274
+ catch (_a) {
275
+ // git grep exits 1 when there are zero matches.
276
+ return { lines: 0, files: 0 };
277
+ }
278
+ let lines = 0;
279
+ let files = 0;
280
+ for (const row of out.split("\n")) {
281
+ const idx = row.lastIndexOf(":");
282
+ if (idx < 0)
283
+ continue;
284
+ const n = Number(row.slice(idx + 1));
285
+ if (Number.isFinite(n) && n > 0) {
286
+ lines += n;
287
+ files++;
288
+ }
289
+ }
290
+ return { lines, files };
291
+ }
292
+ function callerCountGuard() {
293
+ return __awaiter(this, void 0, void 0, function* () {
294
+ const db = new vector_db_1.VectorDB(config_1.PATHS.lancedbDir);
295
+ yield db.ensureTable();
296
+ const builder = new graph_builder_1.GraphBuilder(db, GMAX_ROOT);
297
+ const fileCache = new Map();
298
+ console.log(`Caller-count guard — getCallers vs grep-truth call sites`);
299
+ console.log(`root: ${GMAX_ROOT}`);
300
+ console.log(`ceiling = ${CEILING_MULTIPLE}x grep lines · getCallers cap = ${CALLER_LIMIT}`);
301
+ console.log(`(run after a reindex so the index matches the working tree)\n`);
302
+ let allOK = true;
303
+ for (const sym of GUARD_SYMBOLS) {
304
+ const { lines: grepLines, files: grepFiles } = grepTruth(sym);
305
+ const callers = yield builder.getCallers(sym);
306
+ const raw = callers.length;
307
+ const deduped = (0, callsites_1.resolveCallSites)(callers.map((c) => ({ symbol: c.symbol, file: c.file, line: c.line })), sym, fileCache).length;
308
+ const ceiling = CEILING_MULTIPLE * Math.max(1, grepLines);
309
+ const capped = raw >= CALLER_LIMIT;
310
+ const ratio = grepLines > 0 ? raw / grepLines : raw;
311
+ // capped is a failure too: the fixture grew too common, so the cap now
312
+ // hides explosions — swap it for a lower-usage symbol.
313
+ const ok = !capped && raw <= ceiling;
314
+ if (!ok)
315
+ allOK = false;
316
+ console.log(`${ok ? "✓" : "✗"} ${sym.padEnd(20)} ` +
317
+ `callers=${String(raw).padStart(3)} (dedup ${String(deduped).padStart(3)}) ` +
318
+ `grep=${String(grepLines).padStart(3)}L/${String(grepFiles).padStart(2)}f ` +
319
+ `ratio=${ratio.toFixed(2)} ceiling=${ceiling}` +
320
+ (capped ? " [CAPPED — fixture too common]" : ""));
321
+ }
322
+ console.log(`\nVerdict: ${allOK ? "PASS (no caller-count explosion)" : "FAIL (getCallers exceeds grep-truth ceiling — chunker symbol dedup regressed)"}`);
323
+ yield db.close();
324
+ return allOK;
325
+ });
326
+ }
327
+ function main() {
328
+ return __awaiter(this, void 0, void 0, function* () {
329
+ const platform = process.argv.includes("--platform");
330
+ if (platform) {
331
+ yield platformProbe();
332
+ yield (0, exit_1.gracefulExit)(0);
333
+ return;
334
+ }
335
+ const ok = yield callerCountGuard();
336
+ yield (0, exit_1.gracefulExit)(ok ? 0 : 1);
218
337
  });
219
338
  }
220
339
  if (require.main === module) {