@inerrata-corporation/errata 2.0.2-dev.99 → 2.0.2
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/consolidate-worker.mjs +657 -287
- package/errata.mjs +10621 -2273
- package/package.json +1 -1
- package/pass-worker.mjs +1356 -296
package/pass-worker.mjs
CHANGED
|
@@ -144,7 +144,14 @@ var init_castalia = __esm({
|
|
|
144
144
|
"SOLVED_BY",
|
|
145
145
|
"MITIGATES",
|
|
146
146
|
"REPORTED_FAILURE",
|
|
147
|
-
"CONTRADICTS"
|
|
147
|
+
"CONTRADICTS",
|
|
148
|
+
// Motif twin-faces (KN-twin-faces): failure-face motif → remedy-face motif
|
|
149
|
+
// across layers (AntiPattern/Weakness ↔ Technique/Pattern). Same
|
|
150
|
+
// problem→fix direction as FIXED_BY/SOLVED_BY. Minted by the nightly LLM
|
|
151
|
+
// twin-face pass — these are the near-identical cross-layer pairs the
|
|
152
|
+
// polarity gate (finding 5) correctly refuses to FUSE; the link carries
|
|
153
|
+
// what fusion can't.
|
|
154
|
+
"REMEDIED_BY"
|
|
148
155
|
];
|
|
149
156
|
CONCEPTUAL_EDGES = [
|
|
150
157
|
"INSTANCE_OF",
|
|
@@ -205,7 +212,7 @@ var init_castalia = __esm({
|
|
|
205
212
|
DECOMPOSITION_EDGES = ["SPLIT_INTO"];
|
|
206
213
|
TRANSFER_EDGES = ["MAY_RESOLVE"];
|
|
207
214
|
SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
|
|
208
|
-
SUSPICION_EDGES = ["SUSPECTED_LINK"];
|
|
215
|
+
SUSPICION_EDGES = ["SUSPECTED_LINK", "REGRESSION_OF"];
|
|
209
216
|
TAXONOMY_EDGES = ["IS_A"];
|
|
210
217
|
TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
|
|
211
218
|
GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
|
|
@@ -259,6 +266,10 @@ var init_castalia = __esm({
|
|
|
259
266
|
// git topology, not signal-flow
|
|
260
267
|
"AUTHORED_BY",
|
|
261
268
|
// git authorship, not signal-flow
|
|
269
|
+
"PRODUCED",
|
|
270
|
+
// edit-episode provenance (Episode→symbol), not signal-flow — the family
|
|
271
|
+
// above was excluded together but this member slipped the net (LC-produced-pagerank);
|
|
272
|
+
// at 43% of all live edges it was the largest single edge population flowing rank
|
|
262
273
|
"CONTRIBUTED",
|
|
263
274
|
// agent attribution (Agent → knowledge), not signal-flow
|
|
264
275
|
"SUPERSEDES",
|
|
@@ -266,14 +277,20 @@ var init_castalia = __esm({
|
|
|
266
277
|
"ALTERNATIVE_TO",
|
|
267
278
|
// symmetric peer marker — weight would let alternatives inflate each other
|
|
268
279
|
// NB: BUILDS_ON is NOT excluded — it flows extension→base so foundational solutions rank up.
|
|
269
|
-
"SUSPECTED_LINK"
|
|
280
|
+
"SUSPECTED_LINK",
|
|
270
281
|
// Somnus hypothesis (PLAN_SOMNUS_V2 §4) — never flows rank; navigation-invisible.
|
|
282
|
+
"REGRESSION_OF"
|
|
283
|
+
// recurrence marker (RG-recognize) — a question about a close, not signal-flow.
|
|
271
284
|
]);
|
|
272
285
|
EDGE_WEIGHT = {
|
|
273
286
|
// Causal — high signal
|
|
274
287
|
CAUSED_BY: 3,
|
|
275
288
|
FIXED_BY: 3,
|
|
276
289
|
SOLVED_BY: 3,
|
|
290
|
+
// Twin-face link (failure motif → remedy motif, KN-twin-faces) — causal-grade
|
|
291
|
+
// but a notch below witnessed FIXED_BY/SOLVED_BY: the pairing is an LLM
|
|
292
|
+
// judgment over descriptions, not an agent-witnessed resolution.
|
|
293
|
+
REMEDIED_BY: 2.5,
|
|
277
294
|
MANIFESTS_AS: 2,
|
|
278
295
|
ESCALATES_TO: 1.5,
|
|
279
296
|
AFFECTS: 1.2,
|
|
@@ -376,6 +393,7 @@ var init_castalia = __esm({
|
|
|
376
393
|
ALTERNATIVE_TO: 0,
|
|
377
394
|
// Suspicion — a hypothesis, weightless + PageRank-excluded (never flows rank / EIG).
|
|
378
395
|
SUSPECTED_LINK: 0,
|
|
396
|
+
REGRESSION_OF: 0,
|
|
379
397
|
// Git — topology/authorship, weightless (not domain signal-flow)
|
|
380
398
|
POINTS_AT: 0,
|
|
381
399
|
PARENT: 0,
|
|
@@ -406,6 +424,13 @@ var init_castalia = __esm({
|
|
|
406
424
|
}
|
|
407
425
|
});
|
|
408
426
|
|
|
427
|
+
// ../../packages/shared/src/graph-events.ts
|
|
428
|
+
var init_graph_events = __esm({
|
|
429
|
+
"../../packages/shared/src/graph-events.ts"() {
|
|
430
|
+
"use strict";
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
409
434
|
// ../../packages/shared/src/canonical/hash.ts
|
|
410
435
|
import { createHash } from "node:crypto";
|
|
411
436
|
function sha256(input) {
|
|
@@ -447,13 +472,18 @@ function resolveCanonical(label) {
|
|
|
447
472
|
function resolveCanonicalId(label) {
|
|
448
473
|
return resolveCanonical(label)?.id;
|
|
449
474
|
}
|
|
475
|
+
function resolveUnambiguousCanonicalId(label) {
|
|
476
|
+
const key = label.trim().toLowerCase();
|
|
477
|
+
if (AMBIGUOUS_ALIASES.has(key)) return void 0;
|
|
478
|
+
return resolveCanonicalId(key);
|
|
479
|
+
}
|
|
450
480
|
function aliasesLongestFirst() {
|
|
451
481
|
const out2 = [];
|
|
452
482
|
for (const e of REGISTRY) for (const a of e.aliases) out2.push({ alias: a.toLowerCase(), entity: e });
|
|
453
483
|
out2.sort((x, y) => y.alias.length - x.alias.length);
|
|
454
484
|
return out2;
|
|
455
485
|
}
|
|
456
|
-
var REGISTRY, BY_ALIAS;
|
|
486
|
+
var REGISTRY, AMBIGUOUS_ALIASES, BY_ALIAS;
|
|
457
487
|
var init_taxonomy = __esm({
|
|
458
488
|
"../../packages/shared/src/nlp/taxonomy.ts"() {
|
|
459
489
|
"use strict";
|
|
@@ -502,6 +532,13 @@ var init_taxonomy = __esm({
|
|
|
502
532
|
{ id: "concept:retry", name: "retry", category: "concept", aliases: ["retry"] },
|
|
503
533
|
{ id: "concept:migration", name: "migration", category: "concept", aliases: ["migration"] }
|
|
504
534
|
];
|
|
535
|
+
AMBIGUOUS_ALIASES = /* @__PURE__ */ new Set([
|
|
536
|
+
"node",
|
|
537
|
+
"go",
|
|
538
|
+
"pool",
|
|
539
|
+
"spring",
|
|
540
|
+
"ws"
|
|
541
|
+
]);
|
|
505
542
|
BY_ALIAS = /* @__PURE__ */ new Map();
|
|
506
543
|
for (const e of REGISTRY) {
|
|
507
544
|
for (const a of e.aliases) {
|
|
@@ -563,7 +600,7 @@ function lemma(token) {
|
|
|
563
600
|
}
|
|
564
601
|
return token;
|
|
565
602
|
}
|
|
566
|
-
function
|
|
603
|
+
function tokenize(text, resolve) {
|
|
567
604
|
const matches = text.normalize("NFC").toLowerCase().match(TOKEN_RE) ?? [];
|
|
568
605
|
const out2 = /* @__PURE__ */ new Set();
|
|
569
606
|
for (const raw of matches) {
|
|
@@ -573,11 +610,14 @@ function conceptTokens(text) {
|
|
|
573
610
|
out2.add(token);
|
|
574
611
|
continue;
|
|
575
612
|
}
|
|
576
|
-
const canonical =
|
|
613
|
+
const canonical = resolve(token);
|
|
577
614
|
out2.add(canonical ?? lemma(token));
|
|
578
615
|
}
|
|
579
616
|
return [...out2].sort();
|
|
580
617
|
}
|
|
618
|
+
function retrievalTokens(text) {
|
|
619
|
+
return tokenize(text, resolveUnambiguousCanonicalId);
|
|
620
|
+
}
|
|
581
621
|
var STOPWORDS, NEGATION_TOKENS, TOKEN_RE;
|
|
582
622
|
var init_concept_bag = __esm({
|
|
583
623
|
"../../packages/shared/src/nlp/concept-bag.ts"() {
|
|
@@ -15058,7 +15098,12 @@ var init_edge_rules = __esm({
|
|
|
15058
15098
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15059
15099
|
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15060
15100
|
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15061
|
-
|
|
15101
|
+
// `AntiPattern` joined the target set 2026-08-02: it is one of the three motif
|
|
15102
|
+
// kinds (generalizer motifs.ts `layerOf`: pattern | technique | antipattern) and
|
|
15103
|
+
// the SUPPRESSED-close path mints `Problem ─INSTANCE_OF→ AntiPattern` as the
|
|
15104
|
+
// negative-knowledge binding ("silenced, not solved") — the original three-label
|
|
15105
|
+
// rule predates AntiPattern joining the motif layer and silently ate that edge.
|
|
15106
|
+
INSTANCE_OF: { to: ["Pattern", "AntiPattern", "Weakness", "Technique"] },
|
|
15062
15107
|
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15063
15108
|
MATCHES: { to: ["Pattern"] },
|
|
15064
15109
|
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
@@ -15159,6 +15204,23 @@ var init_wire = __esm({
|
|
|
15159
15204
|
/** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
|
|
15160
15205
|
description: external_exports.string().min(1).max(4e3),
|
|
15161
15206
|
attrs: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
15207
|
+
/** One-way origin key of the SESSION that minted this node (`ws_…`, a
|
|
15208
|
+
* truncated digest — never the raw session id). Stamped onto the created
|
|
15209
|
+
* node as `authoringSession`, which is the independence unit for the
|
|
15210
|
+
* evidence channels: the session that authored a claim may not corroborate
|
|
15211
|
+
* or refute it, while a DIFFERENT session on the same checkout may (alyssa,
|
|
15212
|
+
* 2026-07-31 — the workspace key made every witness on a single-checkout
|
|
15213
|
+
* deployment a self-corroboration). Optional + additive: absent leaves the
|
|
15214
|
+
* gate fail-open for that node, exactly today's behaviour. */
|
|
15215
|
+
originSession: external_exports.string().min(3).max(64).optional(),
|
|
15216
|
+
/** Epoch ms of the ORIGINATING local node's creation — when the knowledge was
|
|
15217
|
+
* actually captured, as opposed to when its public twin reached the cloud.
|
|
15218
|
+
* Stored as `observedAt`; the network board's chronological view orders on
|
|
15219
|
+
* it. Without this the wire carried NO timestamp at all, so a node captured
|
|
15220
|
+
* days ago surfaced as "new" the moment it was generalized and published —
|
|
15221
|
+
* the board showed ingest order wearing a chronology's clothes. Optional +
|
|
15222
|
+
* additive: absent keeps ingest-time ordering for that node. */
|
|
15223
|
+
originCreatedAtMs: external_exports.number().int().positive().optional(),
|
|
15162
15224
|
extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
|
|
15163
15225
|
validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
|
|
15164
15226
|
/** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
|
|
@@ -15270,6 +15332,7 @@ var init_src = __esm({
|
|
|
15270
15332
|
"../../packages/shared/src/index.ts"() {
|
|
15271
15333
|
"use strict";
|
|
15272
15334
|
init_castalia();
|
|
15335
|
+
init_graph_events();
|
|
15273
15336
|
init_identity();
|
|
15274
15337
|
init_wire();
|
|
15275
15338
|
init_edge_rules();
|
|
@@ -15331,6 +15394,51 @@ var init_review = __esm({
|
|
|
15331
15394
|
}
|
|
15332
15395
|
});
|
|
15333
15396
|
|
|
15397
|
+
// ../../packages/local-shared/src/anchorable.ts
|
|
15398
|
+
function anchorableExtensionAlternation() {
|
|
15399
|
+
return BY_LENGTH.map((e) => e.slice(1).replace(/[+.]/g, (c) => `\\${c}`)).join("|");
|
|
15400
|
+
}
|
|
15401
|
+
var ANCHORABLE_EXTENSIONS, BY_LENGTH;
|
|
15402
|
+
var init_anchorable = __esm({
|
|
15403
|
+
"../../packages/local-shared/src/anchorable.ts"() {
|
|
15404
|
+
"use strict";
|
|
15405
|
+
ANCHORABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
15406
|
+
// typescript provider
|
|
15407
|
+
".ts",
|
|
15408
|
+
".tsx",
|
|
15409
|
+
".js",
|
|
15410
|
+
".jsx",
|
|
15411
|
+
".mjs",
|
|
15412
|
+
".cjs",
|
|
15413
|
+
// python / go / rust / ruby / csharp providers
|
|
15414
|
+
".py",
|
|
15415
|
+
".go",
|
|
15416
|
+
".rs",
|
|
15417
|
+
".rb",
|
|
15418
|
+
".cs",
|
|
15419
|
+
// cpp provider — every variant it parses, not just the three that were listed
|
|
15420
|
+
".c",
|
|
15421
|
+
".h",
|
|
15422
|
+
".cpp",
|
|
15423
|
+
".cc",
|
|
15424
|
+
".cxx",
|
|
15425
|
+
".c++",
|
|
15426
|
+
".hpp",
|
|
15427
|
+
".hh",
|
|
15428
|
+
".hxx",
|
|
15429
|
+
".h++",
|
|
15430
|
+
// CUDA — the cpp provider parses these too. Missed when this list was first
|
|
15431
|
+
// transcribed by hand; the parity test caught them on its very first run,
|
|
15432
|
+
// which is the argument for the test existing.
|
|
15433
|
+
".cu",
|
|
15434
|
+
".cuh",
|
|
15435
|
+
// no provider yet; the grammar ships. Inert, not wrong — see above.
|
|
15436
|
+
".java"
|
|
15437
|
+
]);
|
|
15438
|
+
BY_LENGTH = [...ANCHORABLE_EXTENSIONS].sort((a, b) => b.length - a.length);
|
|
15439
|
+
}
|
|
15440
|
+
});
|
|
15441
|
+
|
|
15334
15442
|
// ../../packages/local-shared/src/sqlite-adapter.ts
|
|
15335
15443
|
function openDatabase(path) {
|
|
15336
15444
|
const db = new DatabaseSync(path);
|
|
@@ -15343,6 +15451,7 @@ function openDatabase(path) {
|
|
|
15343
15451
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15344
15452
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15345
15453
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15454
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15346
15455
|
} catch {
|
|
15347
15456
|
}
|
|
15348
15457
|
}
|
|
@@ -15378,7 +15487,7 @@ function openDatabase(path) {
|
|
|
15378
15487
|
return db.prepare(`PRAGMA ${key}`).get();
|
|
15379
15488
|
},
|
|
15380
15489
|
transaction(fn) {
|
|
15381
|
-
db.exec("BEGIN");
|
|
15490
|
+
db.exec("BEGIN IMMEDIATE");
|
|
15382
15491
|
try {
|
|
15383
15492
|
const r = fn();
|
|
15384
15493
|
db.exec("COMMIT");
|
|
@@ -15432,10 +15541,92 @@ var init_src2 = __esm({
|
|
|
15432
15541
|
init_profile();
|
|
15433
15542
|
init_daemon_wire();
|
|
15434
15543
|
init_review();
|
|
15544
|
+
init_anchorable();
|
|
15435
15545
|
init_sqlite_adapter();
|
|
15436
15546
|
}
|
|
15437
15547
|
});
|
|
15438
15548
|
|
|
15549
|
+
// ../../packages/indexer/src/parse-cache.ts
|
|
15550
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
15551
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
15552
|
+
import { homedir } from "node:os";
|
|
15553
|
+
import { join } from "node:path";
|
|
15554
|
+
function parseCacheDir() {
|
|
15555
|
+
return process.env["ERRATA_PARSE_CACHE"] ?? join(homedir(), ".errata", "parse-cache");
|
|
15556
|
+
}
|
|
15557
|
+
function parseCacheKey(source, providerId) {
|
|
15558
|
+
return createHash2("sha256").update(`${PARSE_CACHE_VERSION}:${providerId}:${source}`).digest("hex");
|
|
15559
|
+
}
|
|
15560
|
+
function entryPath(dir, key) {
|
|
15561
|
+
return join(dir, key.slice(0, 2), `${key.slice(2)}.json`);
|
|
15562
|
+
}
|
|
15563
|
+
function readParseCache(dir, key) {
|
|
15564
|
+
const file2 = entryPath(dir, key);
|
|
15565
|
+
try {
|
|
15566
|
+
const raw = readFileSync(file2, "utf8");
|
|
15567
|
+
const parsed = JSON.parse(raw);
|
|
15568
|
+
if (parsed.v !== PARSE_CACHE_VERSION || !Array.isArray(parsed.symbols)) return null;
|
|
15569
|
+
return { symbols: parsed.symbols, reExports: parsed.reExports ?? [] };
|
|
15570
|
+
} catch {
|
|
15571
|
+
return null;
|
|
15572
|
+
}
|
|
15573
|
+
}
|
|
15574
|
+
function writeParseCache(dir, key, providerId, value) {
|
|
15575
|
+
try {
|
|
15576
|
+
const envelope = {
|
|
15577
|
+
v: PARSE_CACHE_VERSION,
|
|
15578
|
+
provider: providerId,
|
|
15579
|
+
symbols: value.symbols,
|
|
15580
|
+
reExports: value.reExports
|
|
15581
|
+
};
|
|
15582
|
+
const body2 = JSON.stringify(envelope);
|
|
15583
|
+
if (body2.length > MAX_ENTRY_BYTES) return;
|
|
15584
|
+
const file2 = entryPath(dir, key);
|
|
15585
|
+
mkdirSync(join(dir, key.slice(0, 2)), { recursive: true });
|
|
15586
|
+
writeFileSync(file2, body2);
|
|
15587
|
+
} catch {
|
|
15588
|
+
}
|
|
15589
|
+
}
|
|
15590
|
+
function sweepParseCacheOnce(dir, now = Date.now()) {
|
|
15591
|
+
if (sweptThisProcess) return 0;
|
|
15592
|
+
sweptThisProcess = true;
|
|
15593
|
+
let removed = 0;
|
|
15594
|
+
try {
|
|
15595
|
+
if (!existsSync(dir)) return 0;
|
|
15596
|
+
for (const bucket of readdirSync(dir)) {
|
|
15597
|
+
const bucketDir = join(dir, bucket);
|
|
15598
|
+
let names;
|
|
15599
|
+
try {
|
|
15600
|
+
names = readdirSync(bucketDir);
|
|
15601
|
+
} catch {
|
|
15602
|
+
continue;
|
|
15603
|
+
}
|
|
15604
|
+
for (const name2 of names) {
|
|
15605
|
+
const file2 = join(bucketDir, name2);
|
|
15606
|
+
try {
|
|
15607
|
+
if (now - statSync(file2).mtimeMs > ENTRY_TTL_MS) {
|
|
15608
|
+
rmSync(file2, { force: true });
|
|
15609
|
+
removed++;
|
|
15610
|
+
}
|
|
15611
|
+
} catch {
|
|
15612
|
+
}
|
|
15613
|
+
}
|
|
15614
|
+
}
|
|
15615
|
+
} catch {
|
|
15616
|
+
}
|
|
15617
|
+
return removed;
|
|
15618
|
+
}
|
|
15619
|
+
var PARSE_CACHE_VERSION, ENTRY_TTL_MS, MAX_ENTRY_BYTES, sweptThisProcess;
|
|
15620
|
+
var init_parse_cache = __esm({
|
|
15621
|
+
"../../packages/indexer/src/parse-cache.ts"() {
|
|
15622
|
+
"use strict";
|
|
15623
|
+
PARSE_CACHE_VERSION = 1;
|
|
15624
|
+
ENTRY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
15625
|
+
MAX_ENTRY_BYTES = 2 * 1024 * 1024;
|
|
15626
|
+
sweptThisProcess = false;
|
|
15627
|
+
}
|
|
15628
|
+
});
|
|
15629
|
+
|
|
15439
15630
|
// ../../packages/indexer/src/simhash.ts
|
|
15440
15631
|
function fnv1a64(s) {
|
|
15441
15632
|
let h = FNV_OFFSET;
|
|
@@ -15454,7 +15645,7 @@ function popcount(x) {
|
|
|
15454
15645
|
}
|
|
15455
15646
|
return c;
|
|
15456
15647
|
}
|
|
15457
|
-
function
|
|
15648
|
+
function tokenize2(text) {
|
|
15458
15649
|
return text.match(/[A-Za-z_$][A-Za-z0-9_$]*|\d+|[^\s\w]/g) ?? [];
|
|
15459
15650
|
}
|
|
15460
15651
|
function shingle(tokens, n = SHINGLE_N) {
|
|
@@ -15483,7 +15674,7 @@ function simhashFeatures(features) {
|
|
|
15483
15674
|
return out2;
|
|
15484
15675
|
}
|
|
15485
15676
|
function simhash(text) {
|
|
15486
|
-
return simhashFeatures(shingle(
|
|
15677
|
+
return simhashFeatures(shingle(tokenize2(text)));
|
|
15487
15678
|
}
|
|
15488
15679
|
function hammingDistance(a, b) {
|
|
15489
15680
|
return popcount((a ^ b) & MASK64);
|
|
@@ -15659,10 +15850,10 @@ var init_identity2 = __esm({
|
|
|
15659
15850
|
});
|
|
15660
15851
|
|
|
15661
15852
|
// ../../packages/indexer/src/pipeline.ts
|
|
15662
|
-
import { appendFileSync, readFileSync, statSync } from "node:fs";
|
|
15853
|
+
import { appendFileSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
15663
15854
|
import { readdir } from "node:fs/promises";
|
|
15664
|
-
import { extname, join, relative, sep } from "node:path";
|
|
15665
|
-
import { createHash as
|
|
15855
|
+
import { extname, join as join2, relative, sep } from "node:path";
|
|
15856
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15666
15857
|
import { execFileSync } from "node:child_process";
|
|
15667
15858
|
function nowTs() {
|
|
15668
15859
|
return indexNow ?? Date.now();
|
|
@@ -15671,7 +15862,7 @@ function fingerprintOf(signature, bodyHash) {
|
|
|
15671
15862
|
return `${signature ?? ""}|${bodyHash ?? ""}`;
|
|
15672
15863
|
}
|
|
15673
15864
|
function sha(s) {
|
|
15674
|
-
return
|
|
15865
|
+
return createHash3("sha256").update(s).digest("hex").slice(0, 16);
|
|
15675
15866
|
}
|
|
15676
15867
|
function fileNodeId(workspaceId, relPath) {
|
|
15677
15868
|
return `file_${sha(workspaceId + ":" + relPath.replace(/\\/g, "/"))}`;
|
|
@@ -15791,10 +15982,11 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15791
15982
|
};
|
|
15792
15983
|
}
|
|
15793
15984
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
15985
|
+
const contentMovedPaths = /* @__PURE__ */ new Set();
|
|
15794
15986
|
for (const rel of [...changedRelPaths]) {
|
|
15795
15987
|
let h;
|
|
15796
15988
|
try {
|
|
15797
|
-
h =
|
|
15989
|
+
h = createHash3("sha256").update(readFileSync2(join2(rootPath, rel))).digest("hex");
|
|
15798
15990
|
} catch {
|
|
15799
15991
|
continue;
|
|
15800
15992
|
}
|
|
@@ -15803,6 +15995,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15803
15995
|
if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id)) {
|
|
15804
15996
|
store2.updateNode(fnode.id, { lastUpdatedAt: t });
|
|
15805
15997
|
changedRelPaths.delete(rel);
|
|
15998
|
+
} else if (!fnode || fnode.attrs["contentHash"] !== h) {
|
|
15999
|
+
contentMovedPaths.add(rel);
|
|
15806
16000
|
}
|
|
15807
16001
|
}
|
|
15808
16002
|
if (changedRelPaths.size === 0) {
|
|
@@ -15841,13 +16035,13 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15841
16035
|
let parsedFiles = 0;
|
|
15842
16036
|
for (const rel of changedRelPaths) {
|
|
15843
16037
|
if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
|
|
15844
|
-
const abs =
|
|
16038
|
+
const abs = join2(rootPath, ...rel.split("/"));
|
|
15845
16039
|
const ext = extname(abs).toLowerCase();
|
|
15846
16040
|
const provider = providers.find((p) => p.fileExtensions.includes(ext));
|
|
15847
16041
|
if (!provider) continue;
|
|
15848
16042
|
let symbols;
|
|
15849
16043
|
try {
|
|
15850
|
-
symbols = provider.extractSymbols(
|
|
16044
|
+
symbols = provider.extractSymbols(readFileSync2(abs, "utf8"), abs);
|
|
15851
16045
|
} catch {
|
|
15852
16046
|
continue;
|
|
15853
16047
|
}
|
|
@@ -15937,6 +16131,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15937
16131
|
changedRelPaths
|
|
15938
16132
|
});
|
|
15939
16133
|
store2.transaction(() => {
|
|
16134
|
+
store2.reviveReobserved([...changedRelPaths], workspaceId, t);
|
|
15940
16135
|
const versionedAndFile = /* @__PURE__ */ new Set([...VERSIONED_LABELS, "File"]);
|
|
15941
16136
|
const ownedLive = [];
|
|
15942
16137
|
for (const n of store2.nodesByRelPath([...changedRelPaths])) {
|
|
@@ -16045,7 +16240,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
16045
16240
|
const h = fileHashes.get(rel);
|
|
16046
16241
|
if (!h) continue;
|
|
16047
16242
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
16048
|
-
if (fnode)
|
|
16243
|
+
if (!fnode) continue;
|
|
16244
|
+
store2.updateNode(fnode.id, {
|
|
16245
|
+
attrs: {
|
|
16246
|
+
...fnode.attrs,
|
|
16247
|
+
contentHash: h,
|
|
16248
|
+
...contentMovedPaths.has(rel) ? { contentChangedAt: t } : {}
|
|
16249
|
+
}
|
|
16250
|
+
});
|
|
16049
16251
|
}
|
|
16050
16252
|
return {
|
|
16051
16253
|
filesReindexed: changedRelPaths.size,
|
|
@@ -16093,8 +16295,13 @@ async function runIndexer(store2, opts) {
|
|
|
16093
16295
|
byLanguage: {},
|
|
16094
16296
|
durationMs: 0,
|
|
16095
16297
|
nodesPurged: 0,
|
|
16096
|
-
edgesPurged: 0
|
|
16298
|
+
edgesPurged: 0,
|
|
16299
|
+
parseCacheHits: 0,
|
|
16300
|
+
parseCacheMisses: 0
|
|
16097
16301
|
};
|
|
16302
|
+
const cacheDir = parseCacheDir();
|
|
16303
|
+
const parseCacheEnabled = cacheDir !== "";
|
|
16304
|
+
if (parseCacheEnabled) sweepParseCacheOnce(cacheDir);
|
|
16098
16305
|
if (opts.clean) {
|
|
16099
16306
|
const purge = purgeWorkspaceCodeGraph(store2, opts.workspaceId);
|
|
16100
16307
|
report.nodesPurged = purge.nodes;
|
|
@@ -16148,25 +16355,38 @@ async function runIndexer(store2, opts) {
|
|
|
16148
16355
|
}
|
|
16149
16356
|
let source;
|
|
16150
16357
|
try {
|
|
16151
|
-
const st =
|
|
16358
|
+
const st = statSync2(f.absPath);
|
|
16152
16359
|
if (st.size > maxBytes) {
|
|
16153
16360
|
report.filesSkipped++;
|
|
16154
16361
|
continue;
|
|
16155
16362
|
}
|
|
16156
|
-
source =
|
|
16363
|
+
source = readFileSync2(f.absPath, "utf8");
|
|
16157
16364
|
} catch {
|
|
16158
16365
|
report.filesSkipped++;
|
|
16159
16366
|
continue;
|
|
16160
16367
|
}
|
|
16368
|
+
const cacheKey = parseCacheKey(source, f.provider.id);
|
|
16369
|
+
const cached2 = parseCacheEnabled ? readParseCache(cacheDir, cacheKey) : null;
|
|
16161
16370
|
let symbols;
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16371
|
+
let reExports;
|
|
16372
|
+
if (cached2) {
|
|
16373
|
+
symbols = cached2.symbols;
|
|
16374
|
+
reExports = cached2.reExports;
|
|
16375
|
+
report.parseCacheHits++;
|
|
16376
|
+
} else {
|
|
16377
|
+
try {
|
|
16378
|
+
symbols = f.provider.extractSymbols(source, f.absPath);
|
|
16379
|
+
} catch {
|
|
16380
|
+
report.filesSkipped++;
|
|
16381
|
+
continue;
|
|
16382
|
+
}
|
|
16383
|
+
reExports = scanReExports(source);
|
|
16384
|
+
report.parseCacheMisses++;
|
|
16385
|
+
if (parseCacheEnabled) {
|
|
16386
|
+
writeParseCache(cacheDir, cacheKey, f.provider.id, { symbols, reExports });
|
|
16387
|
+
}
|
|
16167
16388
|
}
|
|
16168
16389
|
symbolsByFile.set(f.relPath, symbols);
|
|
16169
|
-
const reExports = scanReExports(source);
|
|
16170
16390
|
if (reExports.length > 0) reExportsByFile.set(f.relPath, reExports);
|
|
16171
16391
|
report.filesParsed++;
|
|
16172
16392
|
report.byLanguage[f.provider.id] = (report.byLanguage[f.provider.id] ?? 0) + 1;
|
|
@@ -16211,7 +16431,7 @@ async function runIndexer(store2, opts) {
|
|
|
16211
16431
|
const depth = fileNode.relPath.split("/").length;
|
|
16212
16432
|
if (depth !== 3) continue;
|
|
16213
16433
|
try {
|
|
16214
|
-
const pkg = JSON.parse(
|
|
16434
|
+
const pkg = JSON.parse(readFileSync2(fileNode.absPath, "utf8"));
|
|
16215
16435
|
if (!pkg.name) continue;
|
|
16216
16436
|
const pkgDir = fileNode.relPath.replace(/\/package\.json$/, "");
|
|
16217
16437
|
const candidates = [
|
|
@@ -16593,7 +16813,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16593
16813
|
for (const ent of entries) {
|
|
16594
16814
|
if (ignores.has(ent.name)) continue;
|
|
16595
16815
|
if (ent.name.startsWith(".") && ent.name !== ".") continue;
|
|
16596
|
-
const abs =
|
|
16816
|
+
const abs = join2(current, ent.name);
|
|
16597
16817
|
if (ent.isDirectory()) {
|
|
16598
16818
|
await scan(root, abs, ignores, providers, out2);
|
|
16599
16819
|
} else if (ent.isFile()) {
|
|
@@ -16602,7 +16822,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16602
16822
|
const rel = relative(root, abs).split(sep).join("/");
|
|
16603
16823
|
let size = 0;
|
|
16604
16824
|
try {
|
|
16605
|
-
size =
|
|
16825
|
+
size = statSync2(abs).size;
|
|
16606
16826
|
} catch {
|
|
16607
16827
|
continue;
|
|
16608
16828
|
}
|
|
@@ -16616,7 +16836,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16616
16836
|
}
|
|
16617
16837
|
}
|
|
16618
16838
|
}
|
|
16619
|
-
function
|
|
16839
|
+
function gitListRelPaths(root, ignores) {
|
|
16620
16840
|
let stdout;
|
|
16621
16841
|
try {
|
|
16622
16842
|
stdout = execFileSync(
|
|
@@ -16639,10 +16859,19 @@ function gitListFiles(root, ignores, providers) {
|
|
|
16639
16859
|
if (segs.some((s) => ignores.has(s) || s.startsWith(".") && s.length > 1)) {
|
|
16640
16860
|
continue;
|
|
16641
16861
|
}
|
|
16642
|
-
|
|
16862
|
+
out2.push(rel);
|
|
16863
|
+
}
|
|
16864
|
+
return out2;
|
|
16865
|
+
}
|
|
16866
|
+
function gitListFiles(root, ignores, providers) {
|
|
16867
|
+
const rels = gitListRelPaths(root, ignores);
|
|
16868
|
+
if (rels === null) return null;
|
|
16869
|
+
const out2 = [];
|
|
16870
|
+
for (const rel of rels) {
|
|
16871
|
+
const abs = join2(root, rel);
|
|
16643
16872
|
let size;
|
|
16644
16873
|
try {
|
|
16645
|
-
size =
|
|
16874
|
+
size = statSync2(abs).size;
|
|
16646
16875
|
} catch {
|
|
16647
16876
|
continue;
|
|
16648
16877
|
}
|
|
@@ -16662,7 +16891,7 @@ function upsertFile(store2, id, f, workspaceId) {
|
|
|
16662
16891
|
const now = nowTs();
|
|
16663
16892
|
let contentHash;
|
|
16664
16893
|
try {
|
|
16665
|
-
contentHash =
|
|
16894
|
+
contentHash = createHash3("sha256").update(readFileSync2(f.absPath)).digest("hex");
|
|
16666
16895
|
} catch {
|
|
16667
16896
|
}
|
|
16668
16897
|
const node = {
|
|
@@ -16861,6 +17090,7 @@ var init_pipeline = __esm({
|
|
|
16861
17090
|
"../../packages/indexer/src/pipeline.ts"() {
|
|
16862
17091
|
"use strict";
|
|
16863
17092
|
init_src2();
|
|
17093
|
+
init_parse_cache();
|
|
16864
17094
|
init_identity2();
|
|
16865
17095
|
DEFAULT_IGNORES = /* @__PURE__ */ new Set([
|
|
16866
17096
|
"node_modules",
|
|
@@ -21025,8 +21255,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
21025
21255
|
|
|
21026
21256
|
// ../../packages/indexer/src/languages/tree-sitter-loader.ts
|
|
21027
21257
|
import { fileURLToPath } from "node:url";
|
|
21028
|
-
import { dirname, join as
|
|
21029
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
21258
|
+
import { dirname, join as join3 } from "node:path";
|
|
21259
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
21030
21260
|
import { createRequire } from "node:module";
|
|
21031
21261
|
function entryDir() {
|
|
21032
21262
|
try {
|
|
@@ -21040,27 +21270,27 @@ function entryDir() {
|
|
|
21040
21270
|
}
|
|
21041
21271
|
function findWasmDir() {
|
|
21042
21272
|
const here = entryDir();
|
|
21043
|
-
const seaWasm =
|
|
21044
|
-
if (
|
|
21273
|
+
const seaWasm = join3(here, "resources", "wasm");
|
|
21274
|
+
if (existsSync2(join3(seaWasm, "tree-sitter-typescript.wasm"))) return seaWasm;
|
|
21045
21275
|
let dir = here;
|
|
21046
21276
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21047
|
-
const flat =
|
|
21277
|
+
const flat = join3(
|
|
21048
21278
|
dir,
|
|
21049
21279
|
"node_modules",
|
|
21050
21280
|
"@vscode",
|
|
21051
21281
|
"tree-sitter-wasm",
|
|
21052
21282
|
"wasm"
|
|
21053
21283
|
);
|
|
21054
|
-
if (
|
|
21284
|
+
if (existsSync2(join3(flat, "tree-sitter-typescript.wasm"))) return flat;
|
|
21055
21285
|
dir = dirname(dir);
|
|
21056
21286
|
}
|
|
21057
21287
|
let root = here;
|
|
21058
21288
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21059
|
-
const pnpmDir =
|
|
21060
|
-
if (
|
|
21061
|
-
for (const entry of
|
|
21289
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21290
|
+
if (existsSync2(pnpmDir)) {
|
|
21291
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21062
21292
|
if (entry.startsWith("@vscode+tree-sitter-wasm@")) {
|
|
21063
|
-
const candidate =
|
|
21293
|
+
const candidate = join3(
|
|
21064
21294
|
pnpmDir,
|
|
21065
21295
|
entry,
|
|
21066
21296
|
"node_modules",
|
|
@@ -21068,7 +21298,7 @@ function findWasmDir() {
|
|
|
21068
21298
|
"tree-sitter-wasm",
|
|
21069
21299
|
"wasm"
|
|
21070
21300
|
);
|
|
21071
|
-
if (
|
|
21301
|
+
if (existsSync2(join3(candidate, "tree-sitter-typescript.wasm"))) {
|
|
21072
21302
|
return candidate;
|
|
21073
21303
|
}
|
|
21074
21304
|
}
|
|
@@ -21082,27 +21312,27 @@ function findWasmDir() {
|
|
|
21082
21312
|
}
|
|
21083
21313
|
function findRuntimeDir() {
|
|
21084
21314
|
const here = entryDir();
|
|
21085
|
-
const seaRuntime =
|
|
21086
|
-
if (
|
|
21315
|
+
const seaRuntime = join3(here, "resources", "wasm");
|
|
21316
|
+
if (existsSync2(join3(seaRuntime, "web-tree-sitter.wasm"))) return seaRuntime;
|
|
21087
21317
|
let dir = here;
|
|
21088
21318
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21089
|
-
const flat =
|
|
21090
|
-
if (
|
|
21319
|
+
const flat = join3(dir, "node_modules", "web-tree-sitter");
|
|
21320
|
+
if (existsSync2(join3(flat, "web-tree-sitter.wasm"))) return flat;
|
|
21091
21321
|
dir = dirname(dir);
|
|
21092
21322
|
}
|
|
21093
21323
|
let root = here;
|
|
21094
21324
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21095
|
-
const pnpmDir =
|
|
21096
|
-
if (
|
|
21097
|
-
for (const entry of
|
|
21325
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21326
|
+
if (existsSync2(pnpmDir)) {
|
|
21327
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21098
21328
|
if (entry.startsWith("web-tree-sitter@")) {
|
|
21099
|
-
const candidate =
|
|
21329
|
+
const candidate = join3(
|
|
21100
21330
|
pnpmDir,
|
|
21101
21331
|
entry,
|
|
21102
21332
|
"node_modules",
|
|
21103
21333
|
"web-tree-sitter"
|
|
21104
21334
|
);
|
|
21105
|
-
if (
|
|
21335
|
+
if (existsSync2(join3(candidate, "web-tree-sitter.wasm"))) {
|
|
21106
21336
|
return candidate;
|
|
21107
21337
|
}
|
|
21108
21338
|
}
|
|
@@ -21119,7 +21349,7 @@ async function loadWebTreeSitter() {
|
|
|
21119
21349
|
void err2;
|
|
21120
21350
|
}
|
|
21121
21351
|
const here = entryDir();
|
|
21122
|
-
const seaResourceBase =
|
|
21352
|
+
const seaResourceBase = join3(here, "resources", "_resolve.js");
|
|
21123
21353
|
const resourceRequire = createRequire(seaResourceBase);
|
|
21124
21354
|
return resourceRequire("web-tree-sitter");
|
|
21125
21355
|
}
|
|
@@ -21134,9 +21364,9 @@ async function ensureTreeSitterReady() {
|
|
|
21134
21364
|
await Parser2.init({
|
|
21135
21365
|
locateFile: (name2) => {
|
|
21136
21366
|
if (name2 === "tree-sitter.wasm" || name2 === "web-tree-sitter.wasm") {
|
|
21137
|
-
return
|
|
21367
|
+
return join3(runtime, name2);
|
|
21138
21368
|
}
|
|
21139
|
-
return
|
|
21369
|
+
return join3(grammars, name2);
|
|
21140
21370
|
}
|
|
21141
21371
|
});
|
|
21142
21372
|
})();
|
|
@@ -21148,7 +21378,7 @@ async function loadGrammar(name2) {
|
|
|
21148
21378
|
if (cached2) return cached2;
|
|
21149
21379
|
if (!languageClass) throw new Error("tree-sitter not initialized");
|
|
21150
21380
|
const grammars = findWasmDir();
|
|
21151
|
-
const lang = await languageClass.load(
|
|
21381
|
+
const lang = await languageClass.load(join3(grammars, `${name2}.wasm`));
|
|
21152
21382
|
grammarCache.set(name2, lang);
|
|
21153
21383
|
return lang;
|
|
21154
21384
|
}
|
|
@@ -21171,7 +21401,7 @@ var init_tree_sitter_loader = __esm({
|
|
|
21171
21401
|
});
|
|
21172
21402
|
|
|
21173
21403
|
// ../../packages/indexer/src/languages/typescript-treesitter.ts
|
|
21174
|
-
import { createHash as
|
|
21404
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
21175
21405
|
function extractNode(node, source, containerQname, containerKind) {
|
|
21176
21406
|
switch (node.type) {
|
|
21177
21407
|
case "function_declaration":
|
|
@@ -21384,7 +21614,7 @@ function scopeRecord(kind, name2, qname, node, body2, source) {
|
|
|
21384
21614
|
const sig = header.replace(/\s+/g, " ").trim();
|
|
21385
21615
|
if (sig) rec.signature = sig;
|
|
21386
21616
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21387
|
-
rec.bodyHash =
|
|
21617
|
+
rec.bodyHash = createHash4("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21388
21618
|
rec.bodySimhash = toHex(simhash(bodyText));
|
|
21389
21619
|
}
|
|
21390
21620
|
}
|
|
@@ -21826,14 +22056,14 @@ var init_typescript_treesitter = __esm({
|
|
|
21826
22056
|
});
|
|
21827
22057
|
|
|
21828
22058
|
// ../../packages/indexer/src/languages/python-treesitter.ts
|
|
21829
|
-
import { createHash as
|
|
22059
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21830
22060
|
function sigAndHash(node, body2, source) {
|
|
21831
22061
|
if (!body2) return {};
|
|
21832
22062
|
const out2 = {};
|
|
21833
22063
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
21834
22064
|
if (sig) out2.signature = sig;
|
|
21835
22065
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21836
|
-
out2.bodyHash =
|
|
22066
|
+
out2.bodyHash = createHash5("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21837
22067
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
21838
22068
|
return out2;
|
|
21839
22069
|
}
|
|
@@ -22246,7 +22476,7 @@ var init_python_treesitter = __esm({
|
|
|
22246
22476
|
});
|
|
22247
22477
|
|
|
22248
22478
|
// ../../packages/indexer/src/languages/cpp-treesitter.ts
|
|
22249
|
-
import { createHash as
|
|
22479
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22250
22480
|
function extractNode3(node, containerQname, containerIsClass, source) {
|
|
22251
22481
|
switch (node.type) {
|
|
22252
22482
|
case "function_definition":
|
|
@@ -22430,7 +22660,7 @@ function sigAndHash2(node, body2, source) {
|
|
|
22430
22660
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22431
22661
|
if (sig) out2.signature = sig;
|
|
22432
22662
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22433
|
-
out2.bodyHash =
|
|
22663
|
+
out2.bodyHash = createHash6("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22434
22664
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22435
22665
|
return out2;
|
|
22436
22666
|
}
|
|
@@ -22591,7 +22821,7 @@ var init_cpp_treesitter = __esm({
|
|
|
22591
22821
|
});
|
|
22592
22822
|
|
|
22593
22823
|
// ../../packages/indexer/src/languages/go-treesitter.ts
|
|
22594
|
-
import { createHash as
|
|
22824
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
22595
22825
|
function extractNode4(node, containerQname, source) {
|
|
22596
22826
|
switch (node.type) {
|
|
22597
22827
|
case "function_declaration": {
|
|
@@ -22753,7 +22983,7 @@ function sigAndHash3(node, body2, source) {
|
|
|
22753
22983
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22754
22984
|
if (sig) out2.signature = sig;
|
|
22755
22985
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22756
|
-
out2.bodyHash =
|
|
22986
|
+
out2.bodyHash = createHash7("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22757
22987
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22758
22988
|
return out2;
|
|
22759
22989
|
}
|
|
@@ -22897,7 +23127,7 @@ var init_go_treesitter = __esm({
|
|
|
22897
23127
|
});
|
|
22898
23128
|
|
|
22899
23129
|
// ../../packages/indexer/src/languages/rust-treesitter.ts
|
|
22900
|
-
import { createHash as
|
|
23130
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22901
23131
|
function extractNode5(node, containerQname, containerIsClass, source) {
|
|
22902
23132
|
switch (node.type) {
|
|
22903
23133
|
case "function_item":
|
|
@@ -23112,7 +23342,7 @@ function sigAndHash4(node, body2, source) {
|
|
|
23112
23342
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23113
23343
|
if (sig) out2.signature = sig;
|
|
23114
23344
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23115
|
-
out2.bodyHash =
|
|
23345
|
+
out2.bodyHash = createHash8("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23116
23346
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23117
23347
|
return out2;
|
|
23118
23348
|
}
|
|
@@ -23263,7 +23493,7 @@ var init_rust_treesitter = __esm({
|
|
|
23263
23493
|
});
|
|
23264
23494
|
|
|
23265
23495
|
// ../../packages/indexer/src/languages/ruby-treesitter.ts
|
|
23266
|
-
import { createHash as
|
|
23496
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
23267
23497
|
function sigAndHash5(node, body2, source) {
|
|
23268
23498
|
if (!body2) {
|
|
23269
23499
|
const sig2 = source.slice(node.startIndex, node.endIndex).replace(/\s+/g, " ").trim();
|
|
@@ -23273,7 +23503,7 @@ function sigAndHash5(node, body2, source) {
|
|
|
23273
23503
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23274
23504
|
if (sig) out2.signature = sig;
|
|
23275
23505
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23276
|
-
out2.bodyHash =
|
|
23506
|
+
out2.bodyHash = createHash9("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23277
23507
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23278
23508
|
return out2;
|
|
23279
23509
|
}
|
|
@@ -23619,7 +23849,7 @@ var init_ruby_treesitter = __esm({
|
|
|
23619
23849
|
});
|
|
23620
23850
|
|
|
23621
23851
|
// ../../packages/indexer/src/languages/csharp-treesitter.ts
|
|
23622
|
-
import { createHash as
|
|
23852
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23623
23853
|
function extractNode7(node, containerQname, containerIsType, source, eof) {
|
|
23624
23854
|
switch (node.type) {
|
|
23625
23855
|
case "method_declaration":
|
|
@@ -23821,7 +24051,7 @@ function sigAndHash6(node, body2, source) {
|
|
|
23821
24051
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23822
24052
|
if (sig) out2.signature = sig;
|
|
23823
24053
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23824
|
-
out2.bodyHash =
|
|
24054
|
+
out2.bodyHash = createHash10("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23825
24055
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23826
24056
|
return out2;
|
|
23827
24057
|
}
|
|
@@ -23978,6 +24208,7 @@ var init_csharp_treesitter = __esm({
|
|
|
23978
24208
|
// ../../packages/indexer/src/index.ts
|
|
23979
24209
|
var src_exports = {};
|
|
23980
24210
|
__export(src_exports, {
|
|
24211
|
+
DEFAULT_IGNORES: () => DEFAULT_IGNORES,
|
|
23981
24212
|
DRIFT_FORK_RATIO: () => DRIFT_FORK_RATIO,
|
|
23982
24213
|
TreeSitterCSharpProvider: () => TreeSitterCSharpProvider,
|
|
23983
24214
|
TreeSitterCppProvider: () => TreeSitterCppProvider,
|
|
@@ -23994,6 +24225,7 @@ __export(src_exports, {
|
|
|
23994
24225
|
findInnermostScope: () => findInnermostScope,
|
|
23995
24226
|
folderNodeId: () => folderNodeId,
|
|
23996
24227
|
fromHex: () => fromHex,
|
|
24228
|
+
gitListRelPaths: () => gitListRelPaths,
|
|
23997
24229
|
hammingDistance: () => hammingDistance,
|
|
23998
24230
|
hasForkedDrift: () => hasForkedDrift,
|
|
23999
24231
|
incrementalReindex: () => incrementalReindex,
|
|
@@ -24006,7 +24238,7 @@ __export(src_exports, {
|
|
|
24006
24238
|
simhashFeatures: () => simhashFeatures,
|
|
24007
24239
|
symbolNodeId: () => symbolNodeId,
|
|
24008
24240
|
toHex: () => toHex,
|
|
24009
|
-
tokenize: () =>
|
|
24241
|
+
tokenize: () => tokenize2
|
|
24010
24242
|
});
|
|
24011
24243
|
function defaultProviders() {
|
|
24012
24244
|
if (providersCache) return providersCache;
|
|
@@ -24059,9 +24291,38 @@ var LOCAL_RULE_OVERRIDES = {
|
|
|
24059
24291
|
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24060
24292
|
},
|
|
24061
24293
|
REVEALED_BY: null,
|
|
24062
|
-
PRODUCED: null
|
|
24294
|
+
PRODUCED: null,
|
|
24295
|
+
// FIXED_BY locally ALSO carries the fix-provenance sense: `resolveProblem`
|
|
24296
|
+
// attributes a closed Problem to the fixing `Episode` (PLAN_PLASTICITY §2.1)
|
|
24297
|
+
// alongside the cloud's Problem→Solution knowledge claim. Same shape as
|
|
24298
|
+
// PRODUCED/REVEALED_BY above — an Episode is code-layer and never drains, so
|
|
24299
|
+
// the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
|
|
24300
|
+
// stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
|
|
24301
|
+
// this rule exists at all.
|
|
24302
|
+
FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] },
|
|
24303
|
+
// RG-recognize: a NEW open Problem re-observing a RESOLVED one ("this is
|
|
24304
|
+
// back — the fix didn't hold"). Local-only for now: the instance drain's
|
|
24305
|
+
// INSTANCE_EDGES allowlist doesn't ship it, and the cloud door's matrix
|
|
24306
|
+
// doesn't know it — crossing the membrane is a deliberate later step, never
|
|
24307
|
+
// a side effect (the half-shipped-edge-type lesson).
|
|
24308
|
+
REGRESSION_OF: { from: ["Problem"], to: ["Problem"] }
|
|
24063
24309
|
};
|
|
24064
|
-
|
|
24310
|
+
function localEdgeViolation(fromLabel, type, toLabel) {
|
|
24311
|
+
if (type in LOCAL_RULE_OVERRIDES) {
|
|
24312
|
+
const rule = LOCAL_RULE_OVERRIDES[type];
|
|
24313
|
+
if (!rule) return null;
|
|
24314
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
24315
|
+
return `${type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
24316
|
+
}
|
|
24317
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
24318
|
+
return `${type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24319
|
+
}
|
|
24320
|
+
return null;
|
|
24321
|
+
}
|
|
24322
|
+
const verdict = isValidEdge(fromLabel, type, toLabel);
|
|
24323
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
24324
|
+
}
|
|
24325
|
+
var SCHEMA_VERSION = 6;
|
|
24065
24326
|
var SCHEMA_SQL = `
|
|
24066
24327
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
24067
24328
|
version INTEGER PRIMARY KEY
|
|
@@ -24076,6 +24337,21 @@ CREATE TABLE IF NOT EXISTS store_meta (
|
|
|
24076
24337
|
value TEXT NOT NULL
|
|
24077
24338
|
);
|
|
24078
24339
|
|
|
24340
|
+
-- Durable ledger of edges the ontology gate REFUSED, keyed by edge type.
|
|
24341
|
+
-- Durable rather than in-memory for one specific reason: the status command runs
|
|
24342
|
+
-- in a SEPARATE process and opens its own store handle, so a counter living on
|
|
24343
|
+
-- the instance reads 0 there forever. That is exactly how a producer rejecting
|
|
24344
|
+
-- 100% of its output stayed invisible for 17 days. Persisting it also survives
|
|
24345
|
+
-- the daemon restart that would otherwise erase the evidence.
|
|
24346
|
+
-- Keyed by type because a systematic producer bug shows up as ONE type
|
|
24347
|
+
-- dominating; sample keeps the latest reason so the count is actionable.
|
|
24348
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24349
|
+
type TEXT PRIMARY KEY,
|
|
24350
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24351
|
+
last_at INTEGER NOT NULL,
|
|
24352
|
+
sample TEXT
|
|
24353
|
+
);
|
|
24354
|
+
|
|
24079
24355
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
24080
24356
|
id TEXT PRIMARY KEY,
|
|
24081
24357
|
label TEXT NOT NULL,
|
|
@@ -24452,6 +24728,16 @@ var SqliteGraphStore = class {
|
|
|
24452
24728
|
if (!cols.has(name2)) this.db.exec(ddl);
|
|
24453
24729
|
}
|
|
24454
24730
|
}
|
|
24731
|
+
if (from < 6) {
|
|
24732
|
+
this.db.exec(`
|
|
24733
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24734
|
+
type TEXT PRIMARY KEY,
|
|
24735
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24736
|
+
last_at INTEGER NOT NULL,
|
|
24737
|
+
sample TEXT
|
|
24738
|
+
)
|
|
24739
|
+
`);
|
|
24740
|
+
}
|
|
24455
24741
|
this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
|
|
24456
24742
|
});
|
|
24457
24743
|
}
|
|
@@ -24533,6 +24819,7 @@ var SqliteGraphStore = class {
|
|
|
24533
24819
|
const violation = this.edgeRuleViolation(edge);
|
|
24534
24820
|
if (violation) {
|
|
24535
24821
|
this.rejectedEdgeCount++;
|
|
24822
|
+
this.recordEdgeRejection(edge.type, violation, edge.lastSeenAt || edge.createdAt || 0);
|
|
24536
24823
|
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24537
24824
|
return;
|
|
24538
24825
|
}
|
|
@@ -24557,22 +24844,51 @@ var SqliteGraphStore = class {
|
|
|
24557
24844
|
* overlay consulted first. Returns the reason string on a documented-
|
|
24558
24845
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24559
24846
|
* next to the insert itself. */
|
|
24560
|
-
|
|
24561
|
-
|
|
24562
|
-
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
|
|
24566
|
-
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24571
|
-
}
|
|
24572
|
-
return null;
|
|
24847
|
+
/** Upsert one refusal into the durable ledger. Best-effort: a bookkeeping
|
|
24848
|
+
* failure must never turn a refused edge into a thrown write. */
|
|
24849
|
+
recordEdgeRejection(type, reason, at) {
|
|
24850
|
+
try {
|
|
24851
|
+
this.db.prepare(
|
|
24852
|
+
`INSERT INTO edge_rejections (type, count, last_at, sample) VALUES (?, 1, ?, ?)
|
|
24853
|
+
ON CONFLICT(type) DO UPDATE SET
|
|
24854
|
+
count = count + 1, last_at = excluded.last_at, sample = excluded.sample`
|
|
24855
|
+
).run(type, at, reason.slice(0, 200));
|
|
24856
|
+
} catch {
|
|
24573
24857
|
}
|
|
24574
|
-
|
|
24575
|
-
|
|
24858
|
+
}
|
|
24859
|
+
/** Refusals recorded by the ontology gate, per edge type, newest activity first.
|
|
24860
|
+
* Durable across restarts and readable from any process (see the table note). */
|
|
24861
|
+
edgeRejections() {
|
|
24862
|
+
try {
|
|
24863
|
+
return this.db.prepare(
|
|
24864
|
+
"SELECT type, count, last_at AS lastAt, sample FROM edge_rejections ORDER BY count DESC, last_at DESC"
|
|
24865
|
+
).all();
|
|
24866
|
+
} catch {
|
|
24867
|
+
return [];
|
|
24868
|
+
}
|
|
24869
|
+
}
|
|
24870
|
+
/** Drop ledger entries whose last refusal predates `cutoff`. A still-misbehaving
|
|
24871
|
+
* producer keeps refreshing `last_at` and survives; a fixed one fades out. */
|
|
24872
|
+
pruneEdgeRejections(cutoff) {
|
|
24873
|
+
try {
|
|
24874
|
+
this.db.prepare("DELETE FROM edge_rejections WHERE last_at < ?").run(cutoff);
|
|
24875
|
+
} catch {
|
|
24876
|
+
}
|
|
24877
|
+
}
|
|
24878
|
+
/** Clear the ledger outright, whole or per type — operator escape hatch. */
|
|
24879
|
+
clearEdgeRejections(type) {
|
|
24880
|
+
try {
|
|
24881
|
+
if (type) this.db.prepare("DELETE FROM edge_rejections WHERE type = ?").run(type);
|
|
24882
|
+
else this.db.exec("DELETE FROM edge_rejections");
|
|
24883
|
+
} catch {
|
|
24884
|
+
}
|
|
24885
|
+
}
|
|
24886
|
+
edgeRuleViolation(edge) {
|
|
24887
|
+
return localEdgeViolation(
|
|
24888
|
+
this.getNode(edge.from)?.label,
|
|
24889
|
+
edge.type,
|
|
24890
|
+
this.getNode(edge.to)?.label
|
|
24891
|
+
);
|
|
24576
24892
|
}
|
|
24577
24893
|
updateEdge(id, patch) {
|
|
24578
24894
|
this.stmts.updateEdge.run({
|
|
@@ -24709,6 +25025,163 @@ var SqliteGraphStore = class {
|
|
|
24709
25025
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
24710
25026
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
24711
25027
|
}
|
|
25028
|
+
getMeta(key) {
|
|
25029
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
25030
|
+
return r?.value ?? null;
|
|
25031
|
+
}
|
|
25032
|
+
setMeta(key, value) {
|
|
25033
|
+
this.db.prepare(
|
|
25034
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
25035
|
+
).run({ key, value });
|
|
25036
|
+
}
|
|
25037
|
+
dirtyNodeIdsSince(ts) {
|
|
25038
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
25039
|
+
return rows.map((r) => r.id);
|
|
25040
|
+
}
|
|
25041
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
25042
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
25043
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
25044
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
25045
|
+
edgeEndpointsTouchedSince(ts) {
|
|
25046
|
+
const rows = this.db.prepare(
|
|
25047
|
+
`SELECT from_id, to_id FROM edges
|
|
25048
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
25049
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
25050
|
+
).all({ ts });
|
|
25051
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
25052
|
+
}
|
|
25053
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
25054
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
25055
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
25056
|
+
outEdgesForMany(ids) {
|
|
25057
|
+
return this.edgesForMany(ids, "from_id");
|
|
25058
|
+
}
|
|
25059
|
+
inEdgesForMany(ids) {
|
|
25060
|
+
return this.edgesForMany(ids, "to_id");
|
|
25061
|
+
}
|
|
25062
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
25063
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
25064
|
+
ranksForMany(ids) {
|
|
25065
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
25066
|
+
const CHUNK = 400;
|
|
25067
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25068
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
25069
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
25070
|
+
const rows = this.db.prepare(
|
|
25071
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
25072
|
+
).all(...chunk);
|
|
25073
|
+
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
25074
|
+
}
|
|
25075
|
+
return out2;
|
|
25076
|
+
}
|
|
25077
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
25078
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
25079
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
25080
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
25081
|
+
* scanLiveNodes materialization. */
|
|
25082
|
+
landmarkSweepRows() {
|
|
25083
|
+
const rows = this.db.prepare(
|
|
25084
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
25085
|
+
WHERE valid_to IS NULL
|
|
25086
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
25087
|
+
).all();
|
|
25088
|
+
return rows.map((r) => ({
|
|
25089
|
+
id: r.id,
|
|
25090
|
+
label: r.label,
|
|
25091
|
+
pageRank: r.page_rank,
|
|
25092
|
+
isLandmark: r.is_landmark === 1,
|
|
25093
|
+
memoryTier: r.memory_tier,
|
|
25094
|
+
extractionSource: r.extraction_source
|
|
25095
|
+
}));
|
|
25096
|
+
}
|
|
25097
|
+
edgesForMany(ids, col) {
|
|
25098
|
+
const out2 = [];
|
|
25099
|
+
const CHUNK = 400;
|
|
25100
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25101
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
25102
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
25103
|
+
const rows = this.db.prepare(
|
|
25104
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
25105
|
+
).all(...chunk);
|
|
25106
|
+
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
25107
|
+
}
|
|
25108
|
+
return out2;
|
|
25109
|
+
}
|
|
25110
|
+
checkpointWal() {
|
|
25111
|
+
try {
|
|
25112
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
25113
|
+
return r ?? null;
|
|
25114
|
+
} catch {
|
|
25115
|
+
return null;
|
|
25116
|
+
}
|
|
25117
|
+
}
|
|
25118
|
+
reviveReobserved(relPaths, workspaceId, since) {
|
|
25119
|
+
let revived = 0;
|
|
25120
|
+
const CHUNK = 400;
|
|
25121
|
+
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
25122
|
+
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
25123
|
+
if (slice.length === 0) continue;
|
|
25124
|
+
const r = this.db.prepare(
|
|
25125
|
+
`UPDATE nodes SET valid_to = NULL
|
|
25126
|
+
WHERE valid_to IS NOT NULL
|
|
25127
|
+
AND last_updated_at >= ?
|
|
25128
|
+
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
25129
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
|
|
25130
|
+
).run(since, workspaceId, ...slice);
|
|
25131
|
+
revived += Number(r.changes);
|
|
25132
|
+
}
|
|
25133
|
+
if (revived > 0) this.mutations++;
|
|
25134
|
+
return revived;
|
|
25135
|
+
}
|
|
25136
|
+
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
25137
|
+
const r = this.db.prepare(
|
|
25138
|
+
`SELECT COUNT(*) AS total,
|
|
25139
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
25140
|
+
FROM (SELECT attrs_json FROM edges
|
|
25141
|
+
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
25142
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
25143
|
+
).get(attr, `%${marker}%`, limit);
|
|
25144
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
25145
|
+
}
|
|
25146
|
+
liveNodeIds(ids) {
|
|
25147
|
+
const live = /* @__PURE__ */ new Set();
|
|
25148
|
+
const CHUNK = 900;
|
|
25149
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25150
|
+
const slice = ids.slice(i2, i2 + CHUNK);
|
|
25151
|
+
if (slice.length === 0) continue;
|
|
25152
|
+
const rows = this.db.prepare(
|
|
25153
|
+
`SELECT id FROM nodes WHERE valid_to IS NULL AND id IN (${slice.map(() => "?").join(",")})`
|
|
25154
|
+
).all(...slice);
|
|
25155
|
+
for (const r of rows) live.add(r.id);
|
|
25156
|
+
}
|
|
25157
|
+
return live;
|
|
25158
|
+
}
|
|
25159
|
+
/**
|
|
25160
|
+
* Live edges WITH their endpoint labels and ids, resolved in ONE join.
|
|
25161
|
+
*
|
|
25162
|
+
* The ontology sweep needs (id, type, fromLabel, toLabel) for every live edge.
|
|
25163
|
+
* Doing that as `scanLiveEdges()` + two `getNode()` calls is 2N node reads —
|
|
25164
|
+
* 550,000 on this store — and each one deserializes the node's embedding blob.
|
|
25165
|
+
* Measured: the sweep did not finish in 10 minutes. As a single join it is one
|
|
25166
|
+
* query over an index-covered scan. Labels only; nothing here touches embeddings.
|
|
25167
|
+
*/
|
|
25168
|
+
scanLiveEdgeRows() {
|
|
25169
|
+
const rows = this.db.prepare(
|
|
25170
|
+
`SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
|
|
25171
|
+
FROM edges e
|
|
25172
|
+
LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
|
|
25173
|
+
LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
|
|
25174
|
+
WHERE e.valid_to IS NULL`
|
|
25175
|
+
).all();
|
|
25176
|
+
return rows.map((r) => ({
|
|
25177
|
+
id: r.id,
|
|
25178
|
+
from: r.from_id,
|
|
25179
|
+
to: r.to_id,
|
|
25180
|
+
type: r.type,
|
|
25181
|
+
fromLabel: r.from_label ?? void 0,
|
|
25182
|
+
toLabel: r.to_label ?? void 0
|
|
25183
|
+
}));
|
|
25184
|
+
}
|
|
24712
25185
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
24713
25186
|
* expression index so the incremental reindex fetches only the changed files'
|
|
24714
25187
|
* symbols instead of scanning every versioned node. */
|
|
@@ -24729,6 +25202,10 @@ var SqliteGraphStore = class {
|
|
|
24729
25202
|
if (!live) return null;
|
|
24730
25203
|
const version2 = live.version ?? 1;
|
|
24731
25204
|
const frozenId = `${liveId}@v${version2}`;
|
|
25205
|
+
if (this.getNode(frozenId)) {
|
|
25206
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
25207
|
+
return frozenId;
|
|
25208
|
+
}
|
|
24732
25209
|
this.stmts.freezeCopy.run({ frozen_id: frozenId, live_id: liveId, t });
|
|
24733
25210
|
this.mergeEdge({
|
|
24734
25211
|
id: `edge_superseded_${frozenId}`,
|
|
@@ -24787,6 +25264,7 @@ var CAUSAL_FAMILY = [
|
|
|
24787
25264
|
|
|
24788
25265
|
// ../../packages/local-graph/src/justification.ts
|
|
24789
25266
|
init_src();
|
|
25267
|
+
init_src2();
|
|
24790
25268
|
function addDependency(store2, opts) {
|
|
24791
25269
|
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
24792
25270
|
const attrs = { relation: opts.relation };
|
|
@@ -24809,13 +25287,72 @@ function addDependency(store2, opts) {
|
|
|
24809
25287
|
store2.mergeEdge(edge);
|
|
24810
25288
|
return edge;
|
|
24811
25289
|
}
|
|
24812
|
-
function markRevisit(store2, node, reason, ts) {
|
|
25290
|
+
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
24813
25291
|
const fresh = store2.getNode(node.id) ?? node;
|
|
24814
25292
|
store2.updateNode(node.id, {
|
|
24815
|
-
attrs: {
|
|
25293
|
+
attrs: {
|
|
25294
|
+
...fresh.attrs,
|
|
25295
|
+
revisit: true,
|
|
25296
|
+
revisitReason: reason,
|
|
25297
|
+
revisitSinceTs: ts,
|
|
25298
|
+
revisitAnsweredWhen: answeredWhen
|
|
25299
|
+
},
|
|
24816
25300
|
lastUpdatedAt: ts
|
|
24817
25301
|
});
|
|
24818
25302
|
}
|
|
25303
|
+
var LEGACY_AUTO_CLOSE_ASK = "auto-closed off a pre-provenance anchor";
|
|
25304
|
+
var KNOWN_CONDITIONS = {
|
|
25305
|
+
parentProblemOpen: true,
|
|
25306
|
+
dependentStale: true
|
|
25307
|
+
};
|
|
25308
|
+
function isRevisitCondition(v) {
|
|
25309
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(KNOWN_CONDITIONS, v);
|
|
25310
|
+
}
|
|
25311
|
+
function conditionOf(node) {
|
|
25312
|
+
const explicit = node.attrs["revisitAnsweredWhen"];
|
|
25313
|
+
if (isRevisitCondition(explicit)) return explicit;
|
|
25314
|
+
if (String(node.attrs["revisitReason"] ?? "").startsWith(LEGACY_AUTO_CLOSE_ASK)) {
|
|
25315
|
+
return "parentProblemOpen";
|
|
25316
|
+
}
|
|
25317
|
+
return void 0;
|
|
25318
|
+
}
|
|
25319
|
+
function clearAnsweredRevisits(store2, ts) {
|
|
25320
|
+
const report = { cleared: 0, standing: 0 };
|
|
25321
|
+
for (const label of SEMANTIC_NODE_LABELS) {
|
|
25322
|
+
for (const node of store2.findNodesByLabel(label)) {
|
|
25323
|
+
if (node.attrs["revisit"] !== true) continue;
|
|
25324
|
+
const condition = conditionOf(node);
|
|
25325
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
25326
|
+
report.standing++;
|
|
25327
|
+
continue;
|
|
25328
|
+
}
|
|
25329
|
+
clearRevisit(store2, node.id, ts);
|
|
25330
|
+
report.cleared++;
|
|
25331
|
+
}
|
|
25332
|
+
}
|
|
25333
|
+
return report;
|
|
25334
|
+
}
|
|
25335
|
+
function isRevisitAnswered(store2, node, condition) {
|
|
25336
|
+
switch (condition) {
|
|
25337
|
+
case "parentProblemOpen": {
|
|
25338
|
+
const parents = store2.inEdges(node.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.from)).filter((n) => n !== null && n.validTo == null);
|
|
25339
|
+
return parents.every((p) => p.attrs["resolvedAt"] == null);
|
|
25340
|
+
}
|
|
25341
|
+
case "dependentStale": {
|
|
25342
|
+
const since = Number(node.attrs["revisitSinceTs"] ?? 0);
|
|
25343
|
+
return since > 0 && node.lastUpdatedAt > since;
|
|
25344
|
+
}
|
|
25345
|
+
}
|
|
25346
|
+
}
|
|
25347
|
+
function clearRevisit(store2, nodeId, ts) {
|
|
25348
|
+
const n = store2.getNode(nodeId);
|
|
25349
|
+
if (!n) return;
|
|
25350
|
+
const attrs = { ...n.attrs };
|
|
25351
|
+
delete attrs["revisit"];
|
|
25352
|
+
delete attrs["revisitReason"];
|
|
25353
|
+
delete attrs["revisitSinceTs"];
|
|
25354
|
+
store2.updateNode(nodeId, { attrs, lastUpdatedAt: ts });
|
|
25355
|
+
}
|
|
24819
25356
|
function listNeedsRevisit(store2, asOf) {
|
|
24820
25357
|
return store2.nodesAsOf(asOf).filter((n) => n.attrs["revisit"] === true).map((n) => ({
|
|
24821
25358
|
id: n.id,
|
|
@@ -24948,7 +25485,6 @@ function markBoth(store2, a, b, patch, ts) {
|
|
|
24948
25485
|
// ../../packages/local-graph/src/design-problem.ts
|
|
24949
25486
|
init_src();
|
|
24950
25487
|
init_src();
|
|
24951
|
-
init_src2();
|
|
24952
25488
|
|
|
24953
25489
|
// ../../packages/local-graph/src/problem-package-link.ts
|
|
24954
25490
|
init_src();
|
|
@@ -25134,50 +25670,119 @@ function backfillProblemContext(store2, ts) {
|
|
|
25134
25670
|
}
|
|
25135
25671
|
|
|
25136
25672
|
// ../../packages/local-graph/src/design-problem.ts
|
|
25137
|
-
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
pageRank: 0,
|
|
25153
|
-
isLandmark: false,
|
|
25154
|
-
community: null,
|
|
25155
|
-
stability: "unstable",
|
|
25156
|
-
attrs
|
|
25157
|
-
};
|
|
25673
|
+
init_src2();
|
|
25674
|
+
|
|
25675
|
+
// ../../packages/local-graph/src/problem-dedup.ts
|
|
25676
|
+
init_src();
|
|
25677
|
+
init_src();
|
|
25678
|
+
var REDIRECT_EDGES = [
|
|
25679
|
+
"CAUSED_BY",
|
|
25680
|
+
"SOLVED_BY",
|
|
25681
|
+
"FIXED_BY",
|
|
25682
|
+
"ANCHORED_AT",
|
|
25683
|
+
"MANIFESTED_IN",
|
|
25684
|
+
"EVIDENCED_BY"
|
|
25685
|
+
];
|
|
25686
|
+
function corroborations(n) {
|
|
25687
|
+
return Number(n.attrs["corroborations"] ?? 0);
|
|
25158
25688
|
}
|
|
25159
|
-
function
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25689
|
+
function overlap(a, b) {
|
|
25690
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
25691
|
+
let inter = 0;
|
|
25692
|
+
for (const x of a) if (b.has(x)) inter++;
|
|
25693
|
+
return inter / Math.min(a.size, b.size);
|
|
25694
|
+
}
|
|
25695
|
+
function mergeDuplicateProblems(store2, opts) {
|
|
25696
|
+
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25697
|
+
const minTokens = opts.minTokens ?? 4;
|
|
25698
|
+
const report = { clusters: 0, merged: 0 };
|
|
25699
|
+
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25700
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
25701
|
+
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
25702
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
25703
|
+
store2.transaction(() => {
|
|
25704
|
+
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25705
|
+
const a = open[i2];
|
|
25706
|
+
if (consumed.has(a.id)) continue;
|
|
25707
|
+
const cluster = [a];
|
|
25708
|
+
const ta = tokens.get(a.id);
|
|
25709
|
+
for (let j = i2 + 1; j < open.length; j++) {
|
|
25710
|
+
const b = open[j];
|
|
25711
|
+
if (consumed.has(b.id)) continue;
|
|
25712
|
+
const tb = tokens.get(b.id);
|
|
25713
|
+
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25714
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
25715
|
+
if (overlap(ta, tb) >= minOverlap) {
|
|
25716
|
+
cluster.push(b);
|
|
25717
|
+
consumed.add(b.id);
|
|
25718
|
+
}
|
|
25719
|
+
}
|
|
25720
|
+
if (cluster.length < 2) continue;
|
|
25721
|
+
report.clusters++;
|
|
25722
|
+
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25723
|
+
const survivor = cluster[0];
|
|
25724
|
+
for (const dup of cluster.slice(1)) {
|
|
25725
|
+
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25726
|
+
report.merged++;
|
|
25727
|
+
}
|
|
25728
|
+
}
|
|
25172
25729
|
});
|
|
25730
|
+
return report;
|
|
25173
25731
|
}
|
|
25174
|
-
|
|
25732
|
+
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25733
|
+
const surv = store2.getNode(survivor.id);
|
|
25734
|
+
if (!surv) return;
|
|
25735
|
+
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25736
|
+
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25737
|
+
const dupSeq = dup.attrs["createdAtSeq"] ?? store2.currentIngestSeq();
|
|
25738
|
+
const exposure = priorExposure(store2, survivor.id, dupSeq);
|
|
25739
|
+
const expField = `reinforce${exposure[0].toUpperCase()}${exposure.slice(1)}Count`;
|
|
25740
|
+
store2.updateNode(survivor.id, {
|
|
25741
|
+
attrs: {
|
|
25742
|
+
...surv.attrs,
|
|
25743
|
+
sources: [...sources],
|
|
25744
|
+
corroborations: corroborations(surv) + corroborations(dup) + 1,
|
|
25745
|
+
[expField]: (surv.attrs[expField] ?? 0) + 1,
|
|
25746
|
+
lastReinforceExposure: exposure
|
|
25747
|
+
},
|
|
25748
|
+
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25749
|
+
lastUpdatedAt: ts
|
|
25750
|
+
});
|
|
25751
|
+
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25752
|
+
if (e.to === survivor.id) continue;
|
|
25753
|
+
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25754
|
+
if (store2.getEdge(id)) continue;
|
|
25755
|
+
const redirected = {
|
|
25756
|
+
...e,
|
|
25757
|
+
id,
|
|
25758
|
+
from: survivor.id,
|
|
25759
|
+
createdAt: ts,
|
|
25760
|
+
lastSeenAt: ts
|
|
25761
|
+
};
|
|
25762
|
+
store2.mergeEdge(redirected);
|
|
25763
|
+
}
|
|
25764
|
+
store2.updateNode(dup.id, {
|
|
25765
|
+
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25766
|
+
lastUpdatedAt: ts
|
|
25767
|
+
});
|
|
25768
|
+
store2.closeNode(dup.id, ts);
|
|
25769
|
+
}
|
|
25770
|
+
|
|
25771
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
25772
|
+
function isConstraintProblem(node) {
|
|
25773
|
+
return node.attrs["kind"] === "constraint";
|
|
25774
|
+
}
|
|
25775
|
+
var RECURRENCE_DEBOUNCE_MS = 60 * 60 * 1e3;
|
|
25776
|
+
var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
|
|
25175
25777
|
"Solution",
|
|
25176
25778
|
"RootCause",
|
|
25177
25779
|
"Pattern",
|
|
25178
25780
|
"Technique",
|
|
25179
25781
|
"AntiPattern"
|
|
25180
25782
|
]);
|
|
25783
|
+
function isAutoMinted(n) {
|
|
25784
|
+
return n.label === "Solution" && String(n.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25785
|
+
}
|
|
25181
25786
|
function priorsForFile(store2, relPath) {
|
|
25182
25787
|
const want = relPath.trim().replace(/^\.?\//, "");
|
|
25183
25788
|
let file2 = null;
|
|
@@ -25189,35 +25794,53 @@ function priorsForFile(store2, relPath) {
|
|
|
25189
25794
|
}
|
|
25190
25795
|
if (!file2) return null;
|
|
25191
25796
|
const openProblems = [];
|
|
25797
|
+
const constraints = [];
|
|
25798
|
+
const resolvedProblems = [];
|
|
25192
25799
|
const seenProblem = /* @__PURE__ */ new Set();
|
|
25193
25800
|
const related = /* @__PURE__ */ new Map();
|
|
25194
25801
|
for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
25195
25802
|
const n = store2.getNode(e.from);
|
|
25196
25803
|
if (!n) continue;
|
|
25197
25804
|
if (n.label === "Problem") {
|
|
25198
|
-
if (
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25805
|
+
if (seenProblem.has(n.id)) continue;
|
|
25806
|
+
seenProblem.add(n.id);
|
|
25807
|
+
if (!n.attrs["resolvedAt"]) {
|
|
25808
|
+
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
25809
|
+
} else if (n.attrs["resolvedAs"] == null && n.attrs["mergedInto"] === void 0 && e.attrs?.["anchorProvenance"] !== "legacy") {
|
|
25810
|
+
resolvedProblems.push(n);
|
|
25811
|
+
}
|
|
25812
|
+
} else if (CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) {
|
|
25203
25813
|
related.set(n.id, n);
|
|
25204
25814
|
}
|
|
25205
25815
|
}
|
|
25206
25816
|
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
25207
|
-
for (const p of openProblems) {
|
|
25817
|
+
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
25208
25818
|
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
25209
25819
|
const n = store2.getNode(e.to);
|
|
25210
|
-
if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
|
|
25211
|
-
if (n && n.label === "Solution" && e.type === "SOLVED_BY") {
|
|
25820
|
+
if (n && CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) related.set(n.id, n);
|
|
25821
|
+
if (n && n.label === "Solution" && e.type === "SOLVED_BY" && !isAutoMinted(n)) {
|
|
25212
25822
|
const list = solutionsByProblem.get(p.id) ?? [];
|
|
25213
25823
|
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
25214
25824
|
solutionsByProblem.set(p.id, list);
|
|
25215
25825
|
}
|
|
25216
25826
|
}
|
|
25217
25827
|
}
|
|
25218
|
-
if (openProblems.length === 0 && related.size === 0)
|
|
25219
|
-
|
|
25220
|
-
|
|
25828
|
+
if (openProblems.length === 0 && constraints.length === 0 && resolvedProblems.length === 0 && related.size === 0)
|
|
25829
|
+
return null;
|
|
25830
|
+
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
25831
|
+
openProblems.sort(recentFirst);
|
|
25832
|
+
constraints.sort(recentFirst);
|
|
25833
|
+
resolvedProblems.sort(
|
|
25834
|
+
(a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)
|
|
25835
|
+
);
|
|
25836
|
+
return {
|
|
25837
|
+
file: file2,
|
|
25838
|
+
openProblems,
|
|
25839
|
+
constraints,
|
|
25840
|
+
resolvedProblems,
|
|
25841
|
+
related: [...related.values()],
|
|
25842
|
+
solutionsByProblem
|
|
25843
|
+
};
|
|
25221
25844
|
}
|
|
25222
25845
|
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
25223
25846
|
const p = store2.getNode(problemId);
|
|
@@ -25228,18 +25851,49 @@ function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
|
25228
25851
|
});
|
|
25229
25852
|
return true;
|
|
25230
25853
|
}
|
|
25231
|
-
function
|
|
25854
|
+
function reopenAutoClosedProblems(store2, t) {
|
|
25855
|
+
const report = { reopened: 0, alreadySuspect: 0, agentDescribed: 0 };
|
|
25856
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25857
|
+
if (p.attrs["resolvedAt"] == null) continue;
|
|
25858
|
+
if (p.attrs["resolvedAs"] != null) continue;
|
|
25859
|
+
if (p.attrs["mergedInto"] !== void 0) continue;
|
|
25860
|
+
if (p.attrs["resolutionWitnessed"] === true) {
|
|
25861
|
+
report.agentDescribed++;
|
|
25862
|
+
continue;
|
|
25863
|
+
}
|
|
25864
|
+
const sols = store2.outEdges(p.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.to)).filter((n) => n !== null);
|
|
25865
|
+
if (sols.length === 0) continue;
|
|
25866
|
+
if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {
|
|
25867
|
+
report.agentDescribed++;
|
|
25868
|
+
continue;
|
|
25869
|
+
}
|
|
25870
|
+
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
25871
|
+
const attrs = { ...p.attrs };
|
|
25872
|
+
delete attrs["resolvedAt"];
|
|
25873
|
+
attrs["reopenedFrom"] = "auto-close";
|
|
25874
|
+
attrs["reopenedAt"] = t;
|
|
25875
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25876
|
+
report.reopened++;
|
|
25877
|
+
}
|
|
25878
|
+
return report;
|
|
25879
|
+
}
|
|
25880
|
+
var AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
25881
|
+
function markFixCandidates(store2, t) {
|
|
25232
25882
|
let resolved = 0;
|
|
25233
25883
|
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25234
25884
|
if (!p.id.startsWith("dprob_")) continue;
|
|
25235
25885
|
if (p.attrs["resolvedAt"]) continue;
|
|
25886
|
+
if (isConstraintProblem(p)) continue;
|
|
25236
25887
|
let symName = "";
|
|
25237
25888
|
let symRelPath;
|
|
25238
25889
|
let edited = false;
|
|
25890
|
+
const since = Math.max(p.createdAt, Number(p.attrs["fixCandidateClearedAt"] ?? 0));
|
|
25239
25891
|
for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
|
|
25240
25892
|
if (e.attrs?.["mention"] === true) continue;
|
|
25893
|
+
if (e.attrs?.["anchorProvenance"] === "legacy") continue;
|
|
25241
25894
|
const sym = store2.getNode(e.to);
|
|
25242
|
-
|
|
25895
|
+
const changedAt = sym?.label === "File" ? Number(sym.attrs["contentChangedAt"] ?? 0) : sym?.lastUpdatedAt ?? 0;
|
|
25896
|
+
if (sym && changedAt > since) {
|
|
25243
25897
|
edited = true;
|
|
25244
25898
|
symName = sym.description;
|
|
25245
25899
|
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
@@ -25247,36 +25901,182 @@ function resolveDesignProblems(store2, t) {
|
|
|
25247
25901
|
}
|
|
25248
25902
|
}
|
|
25249
25903
|
if (!edited) continue;
|
|
25250
|
-
if (
|
|
25251
|
-
|
|
25252
|
-
store2.
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
// AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
|
|
25257
|
-
// just prose — the F2 join key downstream backfills and the viz read.
|
|
25258
|
-
resolvedSymbols: [symName],
|
|
25259
|
-
...symRelPath ? { resolvedRelPath: symRelPath } : {}
|
|
25260
|
-
})
|
|
25261
|
-
);
|
|
25262
|
-
mergeEdge(store2, p.id, solId, "SOLVED_BY", t);
|
|
25263
|
-
for (const ae of store2.outEdges(p.id, ["ANCHORED_AT"]))
|
|
25264
|
-
mergeEdge(store2, solId, ae.to, "ANCHORED_AT", t);
|
|
25265
|
-
}
|
|
25904
|
+
if (p.attrs["fixCandidateAt"] !== void 0) continue;
|
|
25905
|
+
if (store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25906
|
+
const s = store2.getNode(e.to);
|
|
25907
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25908
|
+
}))
|
|
25909
|
+
continue;
|
|
25266
25910
|
store2.updateNode(p.id, {
|
|
25267
|
-
attrs: {
|
|
25911
|
+
attrs: {
|
|
25912
|
+
...p.attrs,
|
|
25913
|
+
// The cue, kept as structured data so the render can name the file it is
|
|
25914
|
+
// asking about. Deliberately NOT `resolvedAt` — this is a question.
|
|
25915
|
+
fixCandidateAt: t,
|
|
25916
|
+
fixCandidateSymbol: symName,
|
|
25917
|
+
...symRelPath ? { fixCandidateFile: symRelPath } : {},
|
|
25918
|
+
// Snapshot the render-ledger counter so "shown since THIS ask" is
|
|
25919
|
+
// measurable. `shownCount` is the node's LIFETIME count across every
|
|
25920
|
+
// band, so comparing it raw would retire a fresh ask on a
|
|
25921
|
+
// frequently-surfaced problem before anyone had seen the question once.
|
|
25922
|
+
fixCandidateShownBase: Number(p.attrs["shownCount"] ?? 0)
|
|
25923
|
+
},
|
|
25268
25924
|
lastUpdatedAt: t
|
|
25269
25925
|
});
|
|
25270
25926
|
resolved++;
|
|
25271
25927
|
}
|
|
25272
25928
|
return resolved;
|
|
25273
25929
|
}
|
|
25930
|
+
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
25931
|
+
function clearSettledFixCandidates(store2, t) {
|
|
25932
|
+
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
25933
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25934
|
+
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
25935
|
+
const fileAnchors = store2.outEdges(p.id, ["ANCHORED_AT"]).filter((e) => e.attrs?.["mention"] !== true && e.attrs?.["anchorProvenance"] !== "legacy").map((e) => store2.getNode(e.to)).filter((n) => n !== null && n.label === "File");
|
|
25936
|
+
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
25937
|
+
const answered = p.attrs["resolvedAt"] != null || store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25938
|
+
const s = store2.getNode(e.to);
|
|
25939
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25940
|
+
});
|
|
25941
|
+
const base = Number(
|
|
25942
|
+
p.attrs["fixCandidateShownBase"] ?? p.attrs["shownCount"] ?? 0
|
|
25943
|
+
);
|
|
25944
|
+
const shownSinceAsk = Number(p.attrs["shownCount"] ?? 0) - base;
|
|
25945
|
+
const ignored = shownSinceAsk >= FIX_CANDIDATE_ASK_LIMIT;
|
|
25946
|
+
if (!answered && !ignored && !unsubstantiated) {
|
|
25947
|
+
report.standing++;
|
|
25948
|
+
continue;
|
|
25949
|
+
}
|
|
25950
|
+
const attrs = { ...p.attrs };
|
|
25951
|
+
delete attrs["fixCandidateAt"];
|
|
25952
|
+
delete attrs["fixCandidateSymbol"];
|
|
25953
|
+
delete attrs["fixCandidateFile"];
|
|
25954
|
+
attrs["fixCandidateClearedAt"] = t;
|
|
25955
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25956
|
+
if (answered) report.answered++;
|
|
25957
|
+
else if (ignored) report.ignored++;
|
|
25958
|
+
else report.unsubstantiated++;
|
|
25959
|
+
}
|
|
25960
|
+
return report;
|
|
25961
|
+
}
|
|
25962
|
+
function priorExposure(store2, nodeId, atSeq) {
|
|
25963
|
+
const n = store2.getNode(nodeId);
|
|
25964
|
+
if (!n || !n.attrs) return "unshown";
|
|
25965
|
+
const shownSeq = n.attrs["lastShownAtSeq"];
|
|
25966
|
+
if (shownSeq !== void 0 && shownSeq <= atSeq) return "shown";
|
|
25967
|
+
const evictedSeq = n.attrs["lastEvictedAtSeq"];
|
|
25968
|
+
if (evictedSeq !== void 0 && evictedSeq <= atSeq) return "evicted";
|
|
25969
|
+
if ((n.attrs["evictedCount"] ?? 0) > 0) return "evicted";
|
|
25970
|
+
return "unshown";
|
|
25971
|
+
}
|
|
25972
|
+
|
|
25973
|
+
// ../../packages/local-graph/src/intent.ts
|
|
25974
|
+
init_src();
|
|
25274
25975
|
|
|
25275
25976
|
// ../../packages/local-graph/src/anchor-backfill.ts
|
|
25276
|
-
|
|
25977
|
+
init_src();
|
|
25978
|
+
init_src2();
|
|
25277
25979
|
function isLegacyAnchor(attrs) {
|
|
25278
25980
|
return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
|
|
25279
25981
|
}
|
|
25982
|
+
var STATEMENT_PATH_RE = new RegExp(
|
|
25983
|
+
String.raw`(?:[\w.+-]+\/)+[\w.+-]+\.(?:${anchorableExtensionAlternation()})`,
|
|
25984
|
+
"g"
|
|
25985
|
+
);
|
|
25986
|
+
function repairLegacyAnchorsFromStatement(store2, ts) {
|
|
25987
|
+
const report = {
|
|
25988
|
+
repaired: [],
|
|
25989
|
+
retiredGuesses: 0,
|
|
25990
|
+
noPathNamed: 0,
|
|
25991
|
+
pathUnknown: 0,
|
|
25992
|
+
alreadyCorrect: 0
|
|
25993
|
+
};
|
|
25994
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
25995
|
+
for (const f of store2.findNodesByLabel("File")) {
|
|
25996
|
+
const path = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
25997
|
+
if (path) byPath.set(path, { id: f.id, path });
|
|
25998
|
+
}
|
|
25999
|
+
const resolvePath = (named) => {
|
|
26000
|
+
const exact = byPath.get(named);
|
|
26001
|
+
if (exact) return exact;
|
|
26002
|
+
const hits = [...byPath.values()].filter(
|
|
26003
|
+
(f) => f.path.endsWith(`/${named}`) || named.endsWith(`/${f.path}`)
|
|
26004
|
+
);
|
|
26005
|
+
return hits.length === 1 ? hits[0] : null;
|
|
26006
|
+
};
|
|
26007
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
26008
|
+
try {
|
|
26009
|
+
const anchors = store2.outEdges(p.id, ["ANCHORED_AT"]);
|
|
26010
|
+
const legacy = anchors.filter((e) => e.attrs?.["anchorProvenance"] === "legacy");
|
|
26011
|
+
if (legacy.length === 0) continue;
|
|
26012
|
+
const better = anchors.filter((e) => {
|
|
26013
|
+
const prov = e.attrs?.["anchorProvenance"];
|
|
26014
|
+
return prov === "restated" || prov === "witnessed" || prov === "edited";
|
|
26015
|
+
});
|
|
26016
|
+
if (better.length > 0) {
|
|
26017
|
+
const betterTargets = new Set(better.map((e) => e.to));
|
|
26018
|
+
let retired = 0;
|
|
26019
|
+
for (const e of legacy) {
|
|
26020
|
+
if (!betterTargets.has(e.to)) {
|
|
26021
|
+
store2.closeEdge(e.id, ts);
|
|
26022
|
+
retired++;
|
|
26023
|
+
}
|
|
26024
|
+
}
|
|
26025
|
+
report.retiredGuesses += retired;
|
|
26026
|
+
continue;
|
|
26027
|
+
}
|
|
26028
|
+
const named = [...String(p.description ?? "").matchAll(STATEMENT_PATH_RE)].map((m) => m[0]);
|
|
26029
|
+
if (named.length === 0) {
|
|
26030
|
+
report.noPathNamed++;
|
|
26031
|
+
continue;
|
|
26032
|
+
}
|
|
26033
|
+
const guessedPaths = legacy.map((e) => {
|
|
26034
|
+
const f = store2.getNode(e.to);
|
|
26035
|
+
return String(f?.attrs["relPath"] ?? f?.description ?? "");
|
|
26036
|
+
});
|
|
26037
|
+
if (named.some((x) => guessedPaths.some((g) => g.endsWith(x) || x.endsWith(g)))) {
|
|
26038
|
+
report.alreadyCorrect++;
|
|
26039
|
+
continue;
|
|
26040
|
+
}
|
|
26041
|
+
const target = named.map(resolvePath).find((f) => f !== null);
|
|
26042
|
+
if (!target) {
|
|
26043
|
+
report.pathUnknown++;
|
|
26044
|
+
continue;
|
|
26045
|
+
}
|
|
26046
|
+
store2.mergeEdge({
|
|
26047
|
+
id: `edge_${digest({ from: p.id, type: "ANCHORED_AT", to: target.id })}`.slice(0, 24),
|
|
26048
|
+
from: p.id,
|
|
26049
|
+
to: target.id,
|
|
26050
|
+
type: "ANCHORED_AT",
|
|
26051
|
+
// Same confidence a capture-time anchor enters at: the source is the
|
|
26052
|
+
// agent's own statement either way, only the moment of reading differs.
|
|
26053
|
+
confidence: 0.4,
|
|
26054
|
+
extractionSource: "agent-observed",
|
|
26055
|
+
createdAt: ts,
|
|
26056
|
+
lastSeenAt: ts,
|
|
26057
|
+
navSuccesses: 0,
|
|
26058
|
+
navFailures: 0,
|
|
26059
|
+
attrs: {
|
|
26060
|
+
anchorProvenance: "restated",
|
|
26061
|
+
restatedFrom: guessedPaths[0] ?? "",
|
|
26062
|
+
restatedAt: ts
|
|
26063
|
+
}
|
|
26064
|
+
});
|
|
26065
|
+
for (const e of legacy) {
|
|
26066
|
+
if (e.to !== target.id) store2.closeEdge(e.id, ts);
|
|
26067
|
+
}
|
|
26068
|
+
report.retiredGuesses += legacy.filter((e) => e.to !== target.id).length;
|
|
26069
|
+
report.repaired.push({
|
|
26070
|
+
problemId: p.id,
|
|
26071
|
+
problem: p.description,
|
|
26072
|
+
guessed: guessedPaths[0] ?? "",
|
|
26073
|
+
restated: target.path
|
|
26074
|
+
});
|
|
26075
|
+
} catch {
|
|
26076
|
+
}
|
|
26077
|
+
}
|
|
26078
|
+
return report;
|
|
26079
|
+
}
|
|
25280
26080
|
function backfillLegacyAnchors(store2, ts) {
|
|
25281
26081
|
const report = {
|
|
25282
26082
|
demotedEdges: 0,
|
|
@@ -25316,7 +26116,11 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
25316
26116
|
store2,
|
|
25317
26117
|
sol,
|
|
25318
26118
|
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
25319
|
-
ts
|
|
26119
|
+
ts,
|
|
26120
|
+
// "or reopen it" is half the ask, and the nightly reopen takes that
|
|
26121
|
+
// branch — so record what would answer this, or the flag can never be
|
|
26122
|
+
// lowered and the band fills with settled questions.
|
|
26123
|
+
"parentProblemOpen"
|
|
25320
26124
|
);
|
|
25321
26125
|
store2.updateNode(p.id, {
|
|
25322
26126
|
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
@@ -25350,7 +26154,7 @@ function pagerank(input) {
|
|
|
25350
26154
|
const ids = input.nodeIds;
|
|
25351
26155
|
const n = ids.length;
|
|
25352
26156
|
if (n === 0) {
|
|
25353
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26157
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25354
26158
|
}
|
|
25355
26159
|
const index = /* @__PURE__ */ new Map();
|
|
25356
26160
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -25422,8 +26226,12 @@ function pagerank(input) {
|
|
|
25422
26226
|
}
|
|
25423
26227
|
}
|
|
25424
26228
|
const scores = /* @__PURE__ */ new Map();
|
|
25425
|
-
|
|
25426
|
-
|
|
26229
|
+
let danglingRank = 0;
|
|
26230
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26231
|
+
scores.set(ids[i2], score2[i2]);
|
|
26232
|
+
if (dangling[i2]) danglingRank += score2[i2];
|
|
26233
|
+
}
|
|
26234
|
+
return { scores, iterations: iter, converged, danglingRank };
|
|
25427
26235
|
}
|
|
25428
26236
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
25429
26237
|
const entries = [...scores.entries()];
|
|
@@ -25433,7 +26241,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
25433
26241
|
}
|
|
25434
26242
|
}
|
|
25435
26243
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
25436
|
-
entries.sort((a, b) => b[1] - a[1]);
|
|
26244
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
25437
26245
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
25438
26246
|
const out2 = /* @__PURE__ */ new Set();
|
|
25439
26247
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -25671,8 +26479,86 @@ function motifDecision(input) {
|
|
|
25671
26479
|
};
|
|
25672
26480
|
}
|
|
25673
26481
|
|
|
25674
|
-
// ../../packages/
|
|
25675
|
-
|
|
26482
|
+
// ../../packages/math/src/pagerank-local.ts
|
|
26483
|
+
function pagerankLocal(input) {
|
|
26484
|
+
const damping = input.damping ?? 0.85;
|
|
26485
|
+
const tol = input.tolerance ?? 1e-6;
|
|
26486
|
+
const maxIter = input.maxIterations ?? 100;
|
|
26487
|
+
const ids = input.regionIds;
|
|
26488
|
+
const n = ids.length;
|
|
26489
|
+
if (n === 0 || input.globalN === 0) {
|
|
26490
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26491
|
+
}
|
|
26492
|
+
const index = /* @__PURE__ */ new Map();
|
|
26493
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
26494
|
+
const base = (1 - damping) / input.globalN;
|
|
26495
|
+
const outSum = new Float64Array(n);
|
|
26496
|
+
const inflow = new Float64Array(n);
|
|
26497
|
+
let score2 = new Float64Array(n);
|
|
26498
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26499
|
+
const id = ids[i2];
|
|
26500
|
+
outSum[i2] = input.outSum.get(id) ?? 0;
|
|
26501
|
+
inflow[i2] = input.boundaryInflow.get(id) ?? 0;
|
|
26502
|
+
score2[i2] = input.rank0.get(id) ?? base;
|
|
26503
|
+
}
|
|
26504
|
+
const rowLen = new Int32Array(n);
|
|
26505
|
+
let edgeCount = 0;
|
|
26506
|
+
for (const [from, edges] of input.out) {
|
|
26507
|
+
const fi = index.get(from);
|
|
26508
|
+
if (fi === void 0) continue;
|
|
26509
|
+
let inSet = 0;
|
|
26510
|
+
for (const e of edges) if (index.has(e.to)) inSet++;
|
|
26511
|
+
rowLen[fi] = inSet;
|
|
26512
|
+
edgeCount += inSet;
|
|
26513
|
+
}
|
|
26514
|
+
const rowStart = new Int32Array(n + 1);
|
|
26515
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
26516
|
+
const colIdx = new Int32Array(edgeCount);
|
|
26517
|
+
const colW = new Float64Array(edgeCount);
|
|
26518
|
+
const cursor = rowStart.slice(0, n);
|
|
26519
|
+
for (const [from, edges] of input.out) {
|
|
26520
|
+
const fi = index.get(from);
|
|
26521
|
+
if (fi === void 0) continue;
|
|
26522
|
+
for (const e of edges) {
|
|
26523
|
+
const ti = index.get(e.to);
|
|
26524
|
+
if (ti === void 0) continue;
|
|
26525
|
+
const c = cursor[fi];
|
|
26526
|
+
cursor[fi] = c + 1;
|
|
26527
|
+
colIdx[c] = ti;
|
|
26528
|
+
colW[c] = e.weight;
|
|
26529
|
+
}
|
|
26530
|
+
}
|
|
26531
|
+
const externalDangling = input.externalDanglingRank ?? 0;
|
|
26532
|
+
let next = new Float64Array(n);
|
|
26533
|
+
let iter = 0;
|
|
26534
|
+
let converged = false;
|
|
26535
|
+
for (; iter < maxIter; iter++) {
|
|
26536
|
+
let internalDangling = 0;
|
|
26537
|
+
for (let i2 = 0; i2 < n; i2++) if (outSum[i2] <= 0) internalDangling += score2[i2];
|
|
26538
|
+
const danglingShare = damping * (internalDangling + externalDangling) / input.globalN;
|
|
26539
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] = base + danglingShare + damping * inflow[i2];
|
|
26540
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26541
|
+
const os2 = outSum[i2];
|
|
26542
|
+
if (os2 <= 0) continue;
|
|
26543
|
+
const f = damping * score2[i2] / os2;
|
|
26544
|
+
const end = rowStart[i2 + 1];
|
|
26545
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
26546
|
+
}
|
|
26547
|
+
let diff = 0;
|
|
26548
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
26549
|
+
const tmp = score2;
|
|
26550
|
+
score2 = next;
|
|
26551
|
+
next = tmp;
|
|
26552
|
+
if (diff < tol) {
|
|
26553
|
+
iter++;
|
|
26554
|
+
converged = true;
|
|
26555
|
+
break;
|
|
26556
|
+
}
|
|
26557
|
+
}
|
|
26558
|
+
const scores = /* @__PURE__ */ new Map();
|
|
26559
|
+
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
26560
|
+
return { scores, iterations: iter, converged };
|
|
26561
|
+
}
|
|
25676
26562
|
|
|
25677
26563
|
// ../../packages/local-graph/src/triage.ts
|
|
25678
26564
|
init_src();
|
|
@@ -25681,95 +26567,8 @@ init_src();
|
|
|
25681
26567
|
init_src2();
|
|
25682
26568
|
var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
|
|
25683
26569
|
|
|
25684
|
-
// ../../packages/local-graph/src/
|
|
25685
|
-
|
|
25686
|
-
init_src();
|
|
25687
|
-
var REDIRECT_EDGES = [
|
|
25688
|
-
"CAUSED_BY",
|
|
25689
|
-
"SOLVED_BY",
|
|
25690
|
-
"FIXED_BY",
|
|
25691
|
-
"ANCHORED_AT",
|
|
25692
|
-
"MANIFESTED_IN",
|
|
25693
|
-
"EVIDENCED_BY"
|
|
25694
|
-
];
|
|
25695
|
-
function corroborations(n) {
|
|
25696
|
-
return Number(n.attrs["corroborations"] ?? 0);
|
|
25697
|
-
}
|
|
25698
|
-
function overlap(a, b) {
|
|
25699
|
-
if (a.size === 0 || b.size === 0) return 0;
|
|
25700
|
-
let inter = 0;
|
|
25701
|
-
for (const x of a) if (b.has(x)) inter++;
|
|
25702
|
-
return inter / Math.min(a.size, b.size);
|
|
25703
|
-
}
|
|
25704
|
-
function mergeDuplicateProblems(store2, opts) {
|
|
25705
|
-
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25706
|
-
const minTokens = opts.minTokens ?? 4;
|
|
25707
|
-
const report = { clusters: 0, merged: 0 };
|
|
25708
|
-
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25709
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
25710
|
-
for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
|
|
25711
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
25712
|
-
store2.transaction(() => {
|
|
25713
|
-
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25714
|
-
const a = open[i2];
|
|
25715
|
-
if (consumed.has(a.id)) continue;
|
|
25716
|
-
const cluster = [a];
|
|
25717
|
-
const ta = tokens.get(a.id);
|
|
25718
|
-
for (let j = i2 + 1; j < open.length; j++) {
|
|
25719
|
-
const b = open[j];
|
|
25720
|
-
if (consumed.has(b.id)) continue;
|
|
25721
|
-
const tb = tokens.get(b.id);
|
|
25722
|
-
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25723
|
-
if (overlap(ta, tb) >= minOverlap) {
|
|
25724
|
-
cluster.push(b);
|
|
25725
|
-
consumed.add(b.id);
|
|
25726
|
-
}
|
|
25727
|
-
}
|
|
25728
|
-
if (cluster.length < 2) continue;
|
|
25729
|
-
report.clusters++;
|
|
25730
|
-
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25731
|
-
const survivor = cluster[0];
|
|
25732
|
-
for (const dup of cluster.slice(1)) {
|
|
25733
|
-
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25734
|
-
report.merged++;
|
|
25735
|
-
}
|
|
25736
|
-
}
|
|
25737
|
-
});
|
|
25738
|
-
return report;
|
|
25739
|
-
}
|
|
25740
|
-
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25741
|
-
const surv = store2.getNode(survivor.id);
|
|
25742
|
-
if (!surv) return;
|
|
25743
|
-
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25744
|
-
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25745
|
-
store2.updateNode(survivor.id, {
|
|
25746
|
-
attrs: {
|
|
25747
|
-
...surv.attrs,
|
|
25748
|
-
sources: [...sources],
|
|
25749
|
-
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25750
|
-
},
|
|
25751
|
-
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25752
|
-
lastUpdatedAt: ts
|
|
25753
|
-
});
|
|
25754
|
-
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25755
|
-
if (e.to === survivor.id) continue;
|
|
25756
|
-
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25757
|
-
if (store2.getEdge(id)) continue;
|
|
25758
|
-
const redirected = {
|
|
25759
|
-
...e,
|
|
25760
|
-
id,
|
|
25761
|
-
from: survivor.id,
|
|
25762
|
-
createdAt: ts,
|
|
25763
|
-
lastSeenAt: ts
|
|
25764
|
-
};
|
|
25765
|
-
store2.mergeEdge(redirected);
|
|
25766
|
-
}
|
|
25767
|
-
store2.updateNode(dup.id, {
|
|
25768
|
-
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25769
|
-
lastUpdatedAt: ts
|
|
25770
|
-
});
|
|
25771
|
-
store2.closeNode(dup.id, ts);
|
|
25772
|
-
}
|
|
26570
|
+
// ../../packages/local-graph/src/community.ts
|
|
26571
|
+
init_src2();
|
|
25773
26572
|
|
|
25774
26573
|
// ../../packages/local-graph/src/tools.ts
|
|
25775
26574
|
init_src();
|
|
@@ -25786,6 +26585,9 @@ function rankToolsForHandles(store2, limit = 5) {
|
|
|
25786
26585
|
// ../../packages/local-graph/src/principle-sync.ts
|
|
25787
26586
|
init_src2();
|
|
25788
26587
|
|
|
26588
|
+
// ../../packages/local-graph/src/mechanism-liveness.ts
|
|
26589
|
+
var STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
26590
|
+
|
|
25789
26591
|
// ../../packages/generalizer/src/generalizer.ts
|
|
25790
26592
|
init_src2();
|
|
25791
26593
|
init_src();
|
|
@@ -25905,7 +26707,164 @@ function promoteMotifs(store2, t) {
|
|
|
25905
26707
|
}
|
|
25906
26708
|
|
|
25907
26709
|
// ../../packages/generalizer/src/nightly.ts
|
|
25908
|
-
|
|
26710
|
+
var FULL_RESCORE_EVERY = 12;
|
|
26711
|
+
var INCREMENTAL_REGION_MAX_FRACTION = 0.2;
|
|
26712
|
+
var INCREMENTAL_HOPS = 3;
|
|
26713
|
+
function runGraphRescore(store2, opts = {}) {
|
|
26714
|
+
const lastRescoreAt = Number(store2.getMeta?.("lastRescoreAt") ?? 0);
|
|
26715
|
+
const sinceFull = Number(store2.getMeta?.("rescoresSinceFull") ?? 0);
|
|
26716
|
+
const incrementalCapable = !opts.forceFull && lastRescoreAt > 0 && sinceFull < FULL_RESCORE_EVERY - 1 && !!store2.getMeta && !!store2.setMeta && !!store2.dirtyNodeIdsSince && !!store2.edgeEndpointsTouchedSince && !!store2.outEdgesForMany && !!store2.inEdgesForMany && !!store2.ranksForMany && !!store2.landmarkSweepRows;
|
|
26717
|
+
if (incrementalCapable) {
|
|
26718
|
+
const started = Date.now();
|
|
26719
|
+
const inc = tryIncrementalRescore(store2, lastRescoreAt, started);
|
|
26720
|
+
if (inc) {
|
|
26721
|
+
store2.setMeta("lastRescoreAt", String(started));
|
|
26722
|
+
store2.setMeta("rescoresSinceFull", String(sinceFull + 1));
|
|
26723
|
+
return inc;
|
|
26724
|
+
}
|
|
26725
|
+
}
|
|
26726
|
+
const report = runFullRescore(store2);
|
|
26727
|
+
store2.setMeta?.("lastRescoreAt", String(report.startedAt));
|
|
26728
|
+
store2.setMeta?.("rescoresSinceFull", "0");
|
|
26729
|
+
store2.setMeta?.("danglingRankShare", String(report.danglingRank));
|
|
26730
|
+
const { startedAt: _drop, danglingRank: _drop2, ...rest } = report;
|
|
26731
|
+
return rest;
|
|
26732
|
+
}
|
|
26733
|
+
function tryIncrementalRescore(store2, lastRescoreAt, started) {
|
|
26734
|
+
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
26735
|
+
const liveN = store2.nodeCount();
|
|
26736
|
+
const cap = Math.max(1e3, Math.floor(liveN * INCREMENTAL_REGION_MAX_FRACTION));
|
|
26737
|
+
const region = new Set(store2.dirtyNodeIdsSince(lastRescoreAt));
|
|
26738
|
+
for (const e of store2.edgeEndpointsTouchedSince(lastRescoreAt)) {
|
|
26739
|
+
region.add(e.from);
|
|
26740
|
+
region.add(e.to);
|
|
26741
|
+
}
|
|
26742
|
+
if (region.size > cap) return null;
|
|
26743
|
+
let frontier = [...region];
|
|
26744
|
+
for (let hop = 0; hop < INCREMENTAL_HOPS && frontier.length > 0; hop++) {
|
|
26745
|
+
const next = [];
|
|
26746
|
+
for (const e of store2.outEdgesForMany(frontier)) {
|
|
26747
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26748
|
+
if (!region.has(e.to)) {
|
|
26749
|
+
region.add(e.to);
|
|
26750
|
+
next.push(e.to);
|
|
26751
|
+
}
|
|
26752
|
+
}
|
|
26753
|
+
if (region.size > cap) return null;
|
|
26754
|
+
frontier = next;
|
|
26755
|
+
}
|
|
26756
|
+
const rank0 = store2.ranksForMany([...region]);
|
|
26757
|
+
const regionIds = [...rank0.keys()];
|
|
26758
|
+
if (regionIds.length === 0) {
|
|
26759
|
+
return {
|
|
26760
|
+
scoredNodes: 0,
|
|
26761
|
+
iterations: 0,
|
|
26762
|
+
converged: true,
|
|
26763
|
+
landmarks: 0,
|
|
26764
|
+
communities: 0,
|
|
26765
|
+
motifsPromoted: 0,
|
|
26766
|
+
techniques: 0,
|
|
26767
|
+
antipatterns: 0,
|
|
26768
|
+
pageRankWritten: 0,
|
|
26769
|
+
landmarkFlips: 0,
|
|
26770
|
+
mode: "incremental",
|
|
26771
|
+
regionSize: 0,
|
|
26772
|
+
durationMs: Date.now() - started
|
|
26773
|
+
};
|
|
26774
|
+
}
|
|
26775
|
+
const inRegion = new Set(regionIds);
|
|
26776
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
26777
|
+
const outSum = /* @__PURE__ */ new Map();
|
|
26778
|
+
for (const e of store2.outEdgesForMany(regionIds)) {
|
|
26779
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26780
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26781
|
+
if (w <= 0) continue;
|
|
26782
|
+
outSum.set(e.from, (outSum.get(e.from) ?? 0) + w);
|
|
26783
|
+
if (!inRegion.has(e.to)) continue;
|
|
26784
|
+
const l = out2.get(e.from);
|
|
26785
|
+
if (l) l.push({ to: e.to, weight: w });
|
|
26786
|
+
else out2.set(e.from, [{ to: e.to, weight: w }]);
|
|
26787
|
+
}
|
|
26788
|
+
const boundaryEdges = store2.inEdgesForMany(regionIds).filter((e) => allowedTypes.has(e.type) && !inRegion.has(e.from) && (EDGE_WEIGHT[e.type] ?? 1) > 0);
|
|
26789
|
+
const boundarySources = [...new Set(boundaryEdges.map((e) => e.from))];
|
|
26790
|
+
if (boundarySources.length > cap) return null;
|
|
26791
|
+
const boundaryRanks = store2.ranksForMany(boundarySources);
|
|
26792
|
+
const boundaryOutSum = /* @__PURE__ */ new Map();
|
|
26793
|
+
for (const e of store2.outEdgesForMany(boundarySources)) {
|
|
26794
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26795
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26796
|
+
if (w > 0) boundaryOutSum.set(e.from, (boundaryOutSum.get(e.from) ?? 0) + w);
|
|
26797
|
+
}
|
|
26798
|
+
const boundaryInflow = /* @__PURE__ */ new Map();
|
|
26799
|
+
for (const e of boundaryEdges) {
|
|
26800
|
+
const r = boundaryRanks.get(e.from);
|
|
26801
|
+
const os2 = boundaryOutSum.get(e.from);
|
|
26802
|
+
if (r === void 0 || !os2) continue;
|
|
26803
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26804
|
+
boundaryInflow.set(e.to, (boundaryInflow.get(e.to) ?? 0) + r * w / os2);
|
|
26805
|
+
}
|
|
26806
|
+
const globalDangling = Number(store2.getMeta("danglingRankShare") ?? 0);
|
|
26807
|
+
let regionDangling = 0;
|
|
26808
|
+
for (const id of regionIds) {
|
|
26809
|
+
if ((outSum.get(id) ?? 0) <= 0) regionDangling += rank0.get(id) ?? 0;
|
|
26810
|
+
}
|
|
26811
|
+
const result = pagerankLocal({
|
|
26812
|
+
regionIds,
|
|
26813
|
+
rank0,
|
|
26814
|
+
out: out2,
|
|
26815
|
+
outSum,
|
|
26816
|
+
boundaryInflow,
|
|
26817
|
+
globalN: liveN,
|
|
26818
|
+
externalDanglingRank: Math.max(0, globalDangling - regionDangling),
|
|
26819
|
+
damping: 0.85,
|
|
26820
|
+
tolerance: 1e-6,
|
|
26821
|
+
maxIterations: 100
|
|
26822
|
+
});
|
|
26823
|
+
let pageRankWritten = 0;
|
|
26824
|
+
store2.transaction(() => {
|
|
26825
|
+
for (const [id, score2] of result.scores) {
|
|
26826
|
+
if (Math.abs(score2 - (rank0.get(id) ?? 0)) < 1e-9) continue;
|
|
26827
|
+
store2.setPageRank(id, score2);
|
|
26828
|
+
pageRankWritten++;
|
|
26829
|
+
}
|
|
26830
|
+
});
|
|
26831
|
+
const sweep = store2.landmarkSweepRows();
|
|
26832
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
26833
|
+
for (const row of sweep) {
|
|
26834
|
+
if (row.label === "Pattern" || row.label === "RootCause") {
|
|
26835
|
+
candidates.set(row.id, result.scores.get(row.id) ?? row.pageRank);
|
|
26836
|
+
}
|
|
26837
|
+
}
|
|
26838
|
+
const want = markLandmarks(candidates, 0.1);
|
|
26839
|
+
for (const row of sweep) if (row.memoryTier === "persistent") want.add(row.id);
|
|
26840
|
+
let landmarkFlips = 0;
|
|
26841
|
+
store2.transaction(() => {
|
|
26842
|
+
for (const row of sweep) {
|
|
26843
|
+
if (row.extractionSource === "bko-inferred") continue;
|
|
26844
|
+
const should = want.has(row.id);
|
|
26845
|
+
if (row.isLandmark === should) continue;
|
|
26846
|
+
store2.setLandmark(row.id, should);
|
|
26847
|
+
landmarkFlips++;
|
|
26848
|
+
}
|
|
26849
|
+
});
|
|
26850
|
+
return {
|
|
26851
|
+
scoredNodes: regionIds.length,
|
|
26852
|
+
iterations: result.iterations,
|
|
26853
|
+
converged: result.converged,
|
|
26854
|
+
landmarks: want.size,
|
|
26855
|
+
communities: 0,
|
|
26856
|
+
// deferred to the full backstop — see mode docs
|
|
26857
|
+
motifsPromoted: 0,
|
|
26858
|
+
techniques: 0,
|
|
26859
|
+
antipatterns: 0,
|
|
26860
|
+
pageRankWritten,
|
|
26861
|
+
landmarkFlips,
|
|
26862
|
+
mode: "incremental",
|
|
26863
|
+
regionSize: regionIds.length,
|
|
26864
|
+
durationMs: Date.now() - started
|
|
26865
|
+
};
|
|
26866
|
+
}
|
|
26867
|
+
function runFullRescore(store2) {
|
|
25909
26868
|
const started = Date.now();
|
|
25910
26869
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
25911
26870
|
const nodeIds = [];
|
|
@@ -25931,10 +26890,12 @@ function runNightlyPipeline(store2) {
|
|
|
25931
26890
|
maxIterations: 100
|
|
25932
26891
|
});
|
|
25933
26892
|
const scored = result.scores.size;
|
|
26893
|
+
let pageRankWritten = 0;
|
|
25934
26894
|
store2.transaction(() => {
|
|
25935
26895
|
for (const [id, score2] of result.scores) {
|
|
25936
26896
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
25937
26897
|
store2.setPageRank(id, score2);
|
|
26898
|
+
pageRankWritten++;
|
|
25938
26899
|
}
|
|
25939
26900
|
});
|
|
25940
26901
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -25949,11 +26910,13 @@ function runNightlyPipeline(store2) {
|
|
|
25949
26910
|
for (const id of nodeIds) {
|
|
25950
26911
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
25951
26912
|
}
|
|
26913
|
+
let landmarkFlips = 0;
|
|
25952
26914
|
store2.transaction(() => {
|
|
25953
26915
|
for (const id of nodeIds) {
|
|
25954
26916
|
const want = allLandmarks.has(id);
|
|
25955
26917
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
25956
26918
|
store2.setLandmark(id, want);
|
|
26919
|
+
landmarkFlips++;
|
|
25957
26920
|
}
|
|
25958
26921
|
});
|
|
25959
26922
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -25988,8 +26951,6 @@ function runNightlyPipeline(store2) {
|
|
|
25988
26951
|
for (const [id, c] of comm.community) store2.updateNode(id, { community: c });
|
|
25989
26952
|
});
|
|
25990
26953
|
const motifs = promoteMotifs(store2, started);
|
|
25991
|
-
const designResolved = resolveDesignProblems(store2, started);
|
|
25992
|
-
const cry = crystallize(store2, { ts: started });
|
|
25993
26954
|
return {
|
|
25994
26955
|
scoredNodes: scored,
|
|
25995
26956
|
iterations: result.iterations,
|
|
@@ -25999,15 +26960,61 @@ function runNightlyPipeline(store2) {
|
|
|
25999
26960
|
motifsPromoted: motifs.promoted,
|
|
26000
26961
|
techniques: motifs.techniques,
|
|
26001
26962
|
antipatterns: motifs.antipatterns,
|
|
26002
|
-
|
|
26003
|
-
|
|
26004
|
-
|
|
26963
|
+
pageRankWritten,
|
|
26964
|
+
landmarkFlips,
|
|
26965
|
+
mode: "full",
|
|
26966
|
+
regionSize: 0,
|
|
26967
|
+
startedAt: started,
|
|
26968
|
+
danglingRank: result.danglingRank,
|
|
26969
|
+
durationMs: Date.now() - started
|
|
26970
|
+
};
|
|
26971
|
+
}
|
|
26972
|
+
function runSemanticMaintenance(store2) {
|
|
26973
|
+
const started = Date.now();
|
|
26974
|
+
reopenAutoClosedProblems(store2, started);
|
|
26975
|
+
const revisits = clearAnsweredRevisits(store2, started);
|
|
26976
|
+
const fixCandidates = clearSettledFixCandidates(store2, started);
|
|
26977
|
+
const designResolved = markFixCandidates(store2, started);
|
|
26978
|
+
const cry = crystallize(store2, { ts: started });
|
|
26979
|
+
return {
|
|
26005
26980
|
designResolved,
|
|
26981
|
+
revisitsCleared: revisits.cleared,
|
|
26982
|
+
revisitsStanding: revisits.standing,
|
|
26983
|
+
fixCandidatesSettled: fixCandidates.answered + fixCandidates.ignored + fixCandidates.unsubstantiated,
|
|
26984
|
+
fixCandidates,
|
|
26006
26985
|
claimsEvaluated: cry.evaluated,
|
|
26007
26986
|
claimsDerived: cry.derived.length,
|
|
26008
26987
|
durationMs: Date.now() - started
|
|
26009
26988
|
};
|
|
26010
26989
|
}
|
|
26990
|
+
function runNightlyPipeline(store2) {
|
|
26991
|
+
const started = Date.now();
|
|
26992
|
+
const rescore = runGraphRescore(store2);
|
|
26993
|
+
const semantic = runSemanticMaintenance(store2);
|
|
26994
|
+
return {
|
|
26995
|
+
scoredNodes: rescore.scoredNodes,
|
|
26996
|
+
iterations: rescore.iterations,
|
|
26997
|
+
converged: rescore.converged,
|
|
26998
|
+
landmarks: rescore.landmarks,
|
|
26999
|
+
communities: rescore.communities,
|
|
27000
|
+
motifsPromoted: rescore.motifsPromoted,
|
|
27001
|
+
techniques: rescore.techniques,
|
|
27002
|
+
antipatterns: rescore.antipatterns,
|
|
27003
|
+
skillsInduced: 0,
|
|
27004
|
+
// skills are cloud-induced now (see above)
|
|
27005
|
+
skillsRefreshed: 0,
|
|
27006
|
+
pageRankWritten: rescore.pageRankWritten,
|
|
27007
|
+
landmarkFlips: rescore.landmarkFlips,
|
|
27008
|
+
mode: rescore.mode,
|
|
27009
|
+
regionSize: rescore.regionSize,
|
|
27010
|
+
designResolved: semantic.designResolved,
|
|
27011
|
+
revisitsCleared: semantic.revisitsCleared,
|
|
27012
|
+
fixCandidatesSettled: semantic.fixCandidatesSettled,
|
|
27013
|
+
claimsEvaluated: semantic.claimsEvaluated,
|
|
27014
|
+
claimsDerived: semantic.claimsDerived,
|
|
27015
|
+
durationMs: Date.now() - started
|
|
27016
|
+
};
|
|
27017
|
+
}
|
|
26011
27018
|
function codeAnchorProjection(store2, semanticIds, opts = {}) {
|
|
26012
27019
|
const anchorToNodes = /* @__PURE__ */ new Map();
|
|
26013
27020
|
for (const id of semanticIds) {
|
|
@@ -26052,13 +27059,27 @@ var AGENTS_POINTER_BODY = [
|
|
|
26052
27059
|
init_src2();
|
|
26053
27060
|
function buildSnapshot(opts) {
|
|
26054
27061
|
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
|
|
26055
|
-
const
|
|
27062
|
+
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
27063
|
+
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
26056
27064
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
26057
27065
|
const recent = allProblems.slice(0, 8).map((p) => ({
|
|
26058
27066
|
node: p,
|
|
26059
27067
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
26060
27068
|
}));
|
|
26061
|
-
const
|
|
27069
|
+
const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
|
|
27070
|
+
const regressions = problems.filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0).flatMap(
|
|
27071
|
+
(open) => opts.store.outEdges(open.id, ["REGRESSION_OF"]).flatMap((e) => {
|
|
27072
|
+
const closed = opts.store.getNode(e.to);
|
|
27073
|
+
if (!closed || closed.attrs["resolutionSuspect"] !== true || closed.attrs["resolvedAt"] == null) {
|
|
27074
|
+
return [];
|
|
27075
|
+
}
|
|
27076
|
+
const solEdge = opts.store.outEdges(closed.id, ["SOLVED_BY"])[0];
|
|
27077
|
+
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
27078
|
+
return [{ open, closed, ...solution ? { solution } : {} }];
|
|
27079
|
+
})
|
|
27080
|
+
).sort((a, b) => Number(b.closed.attrs["recurredAt"] ?? 0) - Number(a.closed.attrs["recurredAt"] ?? 0)).slice(0, 2);
|
|
27081
|
+
const regressionClosedIds = new Set(regressions.map((r) => r.closed.id));
|
|
27082
|
+
const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).filter((p) => !regressionClosedIds.has(p.id)).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
|
|
26062
27083
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
26063
27084
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
26064
27085
|
return { node: p, ...solution ? { solution } : {} };
|
|
@@ -26085,10 +27106,13 @@ function buildSnapshot(opts) {
|
|
|
26085
27106
|
episodeId,
|
|
26086
27107
|
problems: problems2
|
|
26087
27108
|
}));
|
|
26088
|
-
const
|
|
26089
|
-
|
|
26090
|
-
|
|
26091
|
-
|
|
27109
|
+
const revisitSeen = /* @__PURE__ */ new Set();
|
|
27110
|
+
const needsRevisit = listNeedsRevisit(opts.store, (opts.now ?? /* @__PURE__ */ new Date()).getTime()).filter((r) => {
|
|
27111
|
+
const key = `${r.label}:${r.description.trim().toLowerCase()}`;
|
|
27112
|
+
if (revisitSeen.has(key)) return false;
|
|
27113
|
+
revisitSeen.add(key);
|
|
27114
|
+
return true;
|
|
27115
|
+
}).slice(0, 5);
|
|
26092
27116
|
const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
26093
27117
|
const pkgBase = (x) => {
|
|
26094
27118
|
const at = x.lastIndexOf("@");
|
|
@@ -26124,7 +27148,10 @@ function buildSnapshot(opts) {
|
|
|
26124
27148
|
profileContext,
|
|
26125
27149
|
recentProblems: recent,
|
|
26126
27150
|
recentResolved,
|
|
27151
|
+
recentConstraints,
|
|
27152
|
+
regressions,
|
|
26127
27153
|
...causalNudge ? { causalNudge } : {},
|
|
27154
|
+
...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
|
|
26128
27155
|
...domainNudge ? { domainNudge } : {},
|
|
26129
27156
|
motifs,
|
|
26130
27157
|
reviewCount: opts.reviewCount,
|
|
@@ -26175,10 +27202,16 @@ function sliceForFile(store2, relPath) {
|
|
|
26175
27202
|
}
|
|
26176
27203
|
const fp = priorsForFile(store2, relPath);
|
|
26177
27204
|
const priors = fp ? {
|
|
26178
|
-
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27205
|
+
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27206
|
+
id: p.id,
|
|
27207
|
+
description: p.description,
|
|
27208
|
+
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
27209
|
+
})),
|
|
27210
|
+
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
27211
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
26179
27212
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
26180
27213
|
solutionsByProblem: Object.fromEntries(
|
|
26181
|
-
fp.openProblems.slice(0, 3).map((p) => [
|
|
27214
|
+
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
26182
27215
|
p.id,
|
|
26183
27216
|
(fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
|
|
26184
27217
|
])
|
|
@@ -26215,37 +27248,37 @@ init_src3();
|
|
|
26215
27248
|
// ../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js
|
|
26216
27249
|
import os from "node:os";
|
|
26217
27250
|
import process3 from "node:process";
|
|
26218
|
-
var
|
|
27251
|
+
var homedir2 = os.homedir();
|
|
26219
27252
|
var tmpdir = os.tmpdir();
|
|
26220
27253
|
var { env } = process3;
|
|
26221
27254
|
|
|
26222
27255
|
// src/paths.ts
|
|
26223
|
-
import { dirname as dirname2, join as
|
|
27256
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
26224
27257
|
function workspaceDir(workspaceRoot) {
|
|
26225
|
-
return
|
|
27258
|
+
return join4(workspaceRoot, ".errata");
|
|
26226
27259
|
}
|
|
26227
27260
|
function workspacePaths(root) {
|
|
26228
27261
|
const dir = workspaceDir(root);
|
|
26229
27262
|
return {
|
|
26230
27263
|
root,
|
|
26231
27264
|
configDir: dir,
|
|
26232
|
-
workspaceJson:
|
|
26233
|
-
eventLog:
|
|
26234
|
-
castalia:
|
|
26235
|
-
reviewQueue:
|
|
26236
|
-
outbox:
|
|
26237
|
-
daemonLock:
|
|
26238
|
-
identityAudit:
|
|
26239
|
-
skillsDir:
|
|
26240
|
-
skillsManifest:
|
|
27265
|
+
workspaceJson: join4(dir, "workspace.json"),
|
|
27266
|
+
eventLog: join4(dir, "eventlog.sqlite"),
|
|
27267
|
+
castalia: join4(dir, "castalia.db"),
|
|
27268
|
+
reviewQueue: join4(dir, "review-queue.json"),
|
|
27269
|
+
outbox: join4(dir, "outbox"),
|
|
27270
|
+
daemonLock: join4(dir, "daemon.lock"),
|
|
27271
|
+
identityAudit: join4(dir, "identity-audit.log"),
|
|
27272
|
+
skillsDir: join4(dir, "skills"),
|
|
27273
|
+
skillsManifest: join4(dir, "skills.json")
|
|
26241
27274
|
};
|
|
26242
27275
|
}
|
|
26243
27276
|
|
|
26244
27277
|
// src/reconcile.ts
|
|
26245
27278
|
init_src3();
|
|
26246
27279
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
26247
|
-
import { readdirSync as
|
|
26248
|
-
import { join as
|
|
27280
|
+
import { readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
27281
|
+
import { join as join5, relative as relative2, sep as sep2 } from "node:path";
|
|
26249
27282
|
var IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
|
|
26250
27283
|
var SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
|
|
26251
27284
|
function gitSourceFiles(root) {
|
|
@@ -26262,7 +27295,7 @@ function gitSourceFiles(root) {
|
|
|
26262
27295
|
const out2 = [];
|
|
26263
27296
|
for (const rel of stdout.split("\0")) {
|
|
26264
27297
|
if (!rel || !SOURCE_RE.test(rel)) continue;
|
|
26265
|
-
const abs =
|
|
27298
|
+
const abs = join5(root, rel);
|
|
26266
27299
|
if (IGNORED.test(abs)) continue;
|
|
26267
27300
|
out2.push(abs);
|
|
26268
27301
|
}
|
|
@@ -26271,13 +27304,13 @@ function gitSourceFiles(root) {
|
|
|
26271
27304
|
function* walkSource(dir) {
|
|
26272
27305
|
let entries;
|
|
26273
27306
|
try {
|
|
26274
|
-
entries =
|
|
27307
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
26275
27308
|
} catch {
|
|
26276
27309
|
return;
|
|
26277
27310
|
}
|
|
26278
27311
|
for (const e of entries) {
|
|
26279
27312
|
const name2 = String(e.name);
|
|
26280
|
-
const full =
|
|
27313
|
+
const full = join5(dir, name2);
|
|
26281
27314
|
if (IGNORED.test(full)) continue;
|
|
26282
27315
|
if (e.isDirectory()) yield* walkSource(full);
|
|
26283
27316
|
else if (SOURCE_RE.test(name2)) yield full;
|
|
@@ -26286,12 +27319,18 @@ function* walkSource(dir) {
|
|
|
26286
27319
|
function listSourceFiles(root) {
|
|
26287
27320
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
26288
27321
|
}
|
|
26289
|
-
|
|
27322
|
+
var SCAN_YIELD_EVERY = 100;
|
|
27323
|
+
var LIVENESS_CHUNK = 4e3;
|
|
27324
|
+
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
26290
27325
|
const stale = [];
|
|
27326
|
+
const pending = [];
|
|
27327
|
+
const allTargets = [];
|
|
27328
|
+
let scanned = 0;
|
|
26291
27329
|
for (const abs of listSourceFiles(rootPath)) {
|
|
27330
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
26292
27331
|
let mtimeMs;
|
|
26293
27332
|
try {
|
|
26294
|
-
mtimeMs =
|
|
27333
|
+
mtimeMs = statSync3(abs).mtimeMs;
|
|
26295
27334
|
} catch {
|
|
26296
27335
|
continue;
|
|
26297
27336
|
}
|
|
@@ -26299,32 +27338,52 @@ function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
26299
27338
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
26300
27339
|
if (!fnode) {
|
|
26301
27340
|
stale.push(abs);
|
|
27341
|
+
} else if ((fnode.validTo ?? null) !== null) {
|
|
27342
|
+
stale.push(abs);
|
|
26302
27343
|
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
26303
27344
|
stale.push(abs);
|
|
26304
27345
|
} else {
|
|
26305
27346
|
const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
26306
27347
|
if (edges.length === 0) {
|
|
26307
27348
|
stale.push(abs);
|
|
26308
|
-
} else
|
|
26309
|
-
|
|
27349
|
+
} else {
|
|
27350
|
+
const targets = edges.map((e) => e.to);
|
|
27351
|
+
pending.push({ abs, targets });
|
|
27352
|
+
allTargets.push(...targets);
|
|
26310
27353
|
}
|
|
26311
27354
|
}
|
|
26312
27355
|
}
|
|
27356
|
+
const live = /* @__PURE__ */ new Set();
|
|
27357
|
+
for (let i2 = 0; i2 < allTargets.length; i2 += LIVENESS_CHUNK) {
|
|
27358
|
+
for (const id of store2.liveNodeIds(allTargets.slice(i2, i2 + LIVENESS_CHUNK))) live.add(id);
|
|
27359
|
+
if (i2 + LIVENESS_CHUNK < allTargets.length) await new Promise((r) => setImmediate(r));
|
|
27360
|
+
}
|
|
27361
|
+
for (const { abs, targets } of pending) {
|
|
27362
|
+
if (targets.some((id) => !live.has(id))) stale.push(abs);
|
|
27363
|
+
}
|
|
26313
27364
|
return stale;
|
|
26314
27365
|
}
|
|
26315
|
-
function isLive(n) {
|
|
26316
|
-
return n != null && (n.validTo ?? null) === null;
|
|
26317
|
-
}
|
|
26318
27366
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
26319
|
-
const stale = findStaleFiles(store2, rootPath, workspaceId);
|
|
27367
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId);
|
|
26320
27368
|
if (stale.length === 0) return 0;
|
|
26321
27369
|
let total = 0;
|
|
27370
|
+
let failedBatches = 0;
|
|
26322
27371
|
const BATCH = 20;
|
|
26323
27372
|
for (let i2 = 0; i2 < stale.length; i2 += BATCH) {
|
|
26324
|
-
|
|
26325
|
-
|
|
27373
|
+
try {
|
|
27374
|
+
const r = await incrementalReindex(store2, rootPath, workspaceId, stale.slice(i2, i2 + BATCH));
|
|
27375
|
+
total += r.filesReindexed;
|
|
27376
|
+
} catch (err2) {
|
|
27377
|
+
failedBatches++;
|
|
27378
|
+
console.warn(
|
|
27379
|
+
`[errata] reconcile: batch ${i2 / BATCH + 1} failed (${stale.slice(i2, i2 + BATCH).length} file(s) skipped): ${err2 instanceof Error ? err2.message : err2}`
|
|
27380
|
+
);
|
|
27381
|
+
}
|
|
26326
27382
|
if (i2 + BATCH < stale.length) await new Promise((res) => setImmediate(res));
|
|
26327
27383
|
}
|
|
27384
|
+
if (failedBatches > 0) {
|
|
27385
|
+
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
27386
|
+
}
|
|
26328
27387
|
return total;
|
|
26329
27388
|
}
|
|
26330
27389
|
|
|
@@ -26377,6 +27436,7 @@ function runNightly() {
|
|
|
26377
27436
|
const a = backfillLegacyAnchors(store, Date.now());
|
|
26378
27437
|
anchorsDemoted = a.demotedEdges;
|
|
26379
27438
|
resolutionsSuspect = a.suspects.length;
|
|
27439
|
+
repairLegacyAnchorsFromStatement(store, Date.now());
|
|
26380
27440
|
} catch {
|
|
26381
27441
|
}
|
|
26382
27442
|
const report = runNightlyPipeline(store);
|