@inerrata-corporation/errata 2.0.2-dev.161 → 2.0.2-dev.1641
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 +645 -286
- package/errata.mjs +10215 -2103
- package/package.json +1 -1
- package/pass-worker.mjs +1344 -295
package/pass-worker.mjs
CHANGED
|
@@ -212,7 +212,7 @@ var init_castalia = __esm({
|
|
|
212
212
|
DECOMPOSITION_EDGES = ["SPLIT_INTO"];
|
|
213
213
|
TRANSFER_EDGES = ["MAY_RESOLVE"];
|
|
214
214
|
SOLUTION_STRUCTURE_EDGES = ["BUILDS_ON", "SUPERSEDES", "ALTERNATIVE_TO"];
|
|
215
|
-
SUSPICION_EDGES = ["SUSPECTED_LINK"];
|
|
215
|
+
SUSPICION_EDGES = ["SUSPECTED_LINK", "REGRESSION_OF"];
|
|
216
216
|
TAXONOMY_EDGES = ["IS_A"];
|
|
217
217
|
TRIAGE_EDGES = ["TRIAGED_BY", "CONFIRMS", "INDICATES", "ROUTES_TO"];
|
|
218
218
|
GIT_EDGES = ["POINTS_AT", "PARENT", "AUTHORED_BY"];
|
|
@@ -266,6 +266,10 @@ var init_castalia = __esm({
|
|
|
266
266
|
// git topology, not signal-flow
|
|
267
267
|
"AUTHORED_BY",
|
|
268
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
|
|
269
273
|
"CONTRIBUTED",
|
|
270
274
|
// agent attribution (Agent → knowledge), not signal-flow
|
|
271
275
|
"SUPERSEDES",
|
|
@@ -273,8 +277,10 @@ var init_castalia = __esm({
|
|
|
273
277
|
"ALTERNATIVE_TO",
|
|
274
278
|
// symmetric peer marker — weight would let alternatives inflate each other
|
|
275
279
|
// NB: BUILDS_ON is NOT excluded — it flows extension→base so foundational solutions rank up.
|
|
276
|
-
"SUSPECTED_LINK"
|
|
280
|
+
"SUSPECTED_LINK",
|
|
277
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.
|
|
278
284
|
]);
|
|
279
285
|
EDGE_WEIGHT = {
|
|
280
286
|
// Causal — high signal
|
|
@@ -387,6 +393,7 @@ var init_castalia = __esm({
|
|
|
387
393
|
ALTERNATIVE_TO: 0,
|
|
388
394
|
// Suspicion — a hypothesis, weightless + PageRank-excluded (never flows rank / EIG).
|
|
389
395
|
SUSPECTED_LINK: 0,
|
|
396
|
+
REGRESSION_OF: 0,
|
|
390
397
|
// Git — topology/authorship, weightless (not domain signal-flow)
|
|
391
398
|
POINTS_AT: 0,
|
|
392
399
|
PARENT: 0,
|
|
@@ -417,6 +424,13 @@ var init_castalia = __esm({
|
|
|
417
424
|
}
|
|
418
425
|
});
|
|
419
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
|
+
|
|
420
434
|
// ../../packages/shared/src/canonical/hash.ts
|
|
421
435
|
import { createHash } from "node:crypto";
|
|
422
436
|
function sha256(input) {
|
|
@@ -458,13 +472,18 @@ function resolveCanonical(label) {
|
|
|
458
472
|
function resolveCanonicalId(label) {
|
|
459
473
|
return resolveCanonical(label)?.id;
|
|
460
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
|
+
}
|
|
461
480
|
function aliasesLongestFirst() {
|
|
462
481
|
const out2 = [];
|
|
463
482
|
for (const e of REGISTRY) for (const a of e.aliases) out2.push({ alias: a.toLowerCase(), entity: e });
|
|
464
483
|
out2.sort((x, y) => y.alias.length - x.alias.length);
|
|
465
484
|
return out2;
|
|
466
485
|
}
|
|
467
|
-
var REGISTRY, BY_ALIAS;
|
|
486
|
+
var REGISTRY, AMBIGUOUS_ALIASES, BY_ALIAS;
|
|
468
487
|
var init_taxonomy = __esm({
|
|
469
488
|
"../../packages/shared/src/nlp/taxonomy.ts"() {
|
|
470
489
|
"use strict";
|
|
@@ -513,6 +532,13 @@ var init_taxonomy = __esm({
|
|
|
513
532
|
{ id: "concept:retry", name: "retry", category: "concept", aliases: ["retry"] },
|
|
514
533
|
{ id: "concept:migration", name: "migration", category: "concept", aliases: ["migration"] }
|
|
515
534
|
];
|
|
535
|
+
AMBIGUOUS_ALIASES = /* @__PURE__ */ new Set([
|
|
536
|
+
"node",
|
|
537
|
+
"go",
|
|
538
|
+
"pool",
|
|
539
|
+
"spring",
|
|
540
|
+
"ws"
|
|
541
|
+
]);
|
|
516
542
|
BY_ALIAS = /* @__PURE__ */ new Map();
|
|
517
543
|
for (const e of REGISTRY) {
|
|
518
544
|
for (const a of e.aliases) {
|
|
@@ -574,7 +600,7 @@ function lemma(token) {
|
|
|
574
600
|
}
|
|
575
601
|
return token;
|
|
576
602
|
}
|
|
577
|
-
function
|
|
603
|
+
function tokenize(text, resolve) {
|
|
578
604
|
const matches = text.normalize("NFC").toLowerCase().match(TOKEN_RE) ?? [];
|
|
579
605
|
const out2 = /* @__PURE__ */ new Set();
|
|
580
606
|
for (const raw of matches) {
|
|
@@ -584,11 +610,14 @@ function conceptTokens(text) {
|
|
|
584
610
|
out2.add(token);
|
|
585
611
|
continue;
|
|
586
612
|
}
|
|
587
|
-
const canonical =
|
|
613
|
+
const canonical = resolve(token);
|
|
588
614
|
out2.add(canonical ?? lemma(token));
|
|
589
615
|
}
|
|
590
616
|
return [...out2].sort();
|
|
591
617
|
}
|
|
618
|
+
function retrievalTokens(text) {
|
|
619
|
+
return tokenize(text, resolveUnambiguousCanonicalId);
|
|
620
|
+
}
|
|
592
621
|
var STOPWORDS, NEGATION_TOKENS, TOKEN_RE;
|
|
593
622
|
var init_concept_bag = __esm({
|
|
594
623
|
"../../packages/shared/src/nlp/concept-bag.ts"() {
|
|
@@ -15069,7 +15098,12 @@ var init_edge_rules = __esm({
|
|
|
15069
15098
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15070
15099
|
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15071
15100
|
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15072
|
-
|
|
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"] },
|
|
15073
15107
|
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15074
15108
|
MATCHES: { to: ["Pattern"] },
|
|
15075
15109
|
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
@@ -15170,6 +15204,23 @@ var init_wire = __esm({
|
|
|
15170
15204
|
/** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
|
|
15171
15205
|
description: external_exports.string().min(1).max(4e3),
|
|
15172
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(),
|
|
15173
15224
|
extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
|
|
15174
15225
|
validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
|
|
15175
15226
|
/** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
|
|
@@ -15281,6 +15332,7 @@ var init_src = __esm({
|
|
|
15281
15332
|
"../../packages/shared/src/index.ts"() {
|
|
15282
15333
|
"use strict";
|
|
15283
15334
|
init_castalia();
|
|
15335
|
+
init_graph_events();
|
|
15284
15336
|
init_identity();
|
|
15285
15337
|
init_wire();
|
|
15286
15338
|
init_edge_rules();
|
|
@@ -15342,6 +15394,51 @@ var init_review = __esm({
|
|
|
15342
15394
|
}
|
|
15343
15395
|
});
|
|
15344
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
|
+
|
|
15345
15442
|
// ../../packages/local-shared/src/sqlite-adapter.ts
|
|
15346
15443
|
function openDatabase(path) {
|
|
15347
15444
|
const db = new DatabaseSync(path);
|
|
@@ -15354,6 +15451,7 @@ function openDatabase(path) {
|
|
|
15354
15451
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15355
15452
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15356
15453
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15454
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15357
15455
|
} catch {
|
|
15358
15456
|
}
|
|
15359
15457
|
}
|
|
@@ -15389,7 +15487,7 @@ function openDatabase(path) {
|
|
|
15389
15487
|
return db.prepare(`PRAGMA ${key}`).get();
|
|
15390
15488
|
},
|
|
15391
15489
|
transaction(fn) {
|
|
15392
|
-
db.exec("BEGIN");
|
|
15490
|
+
db.exec("BEGIN IMMEDIATE");
|
|
15393
15491
|
try {
|
|
15394
15492
|
const r = fn();
|
|
15395
15493
|
db.exec("COMMIT");
|
|
@@ -15443,10 +15541,92 @@ var init_src2 = __esm({
|
|
|
15443
15541
|
init_profile();
|
|
15444
15542
|
init_daemon_wire();
|
|
15445
15543
|
init_review();
|
|
15544
|
+
init_anchorable();
|
|
15446
15545
|
init_sqlite_adapter();
|
|
15447
15546
|
}
|
|
15448
15547
|
});
|
|
15449
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
|
+
|
|
15450
15630
|
// ../../packages/indexer/src/simhash.ts
|
|
15451
15631
|
function fnv1a64(s) {
|
|
15452
15632
|
let h = FNV_OFFSET;
|
|
@@ -15465,7 +15645,7 @@ function popcount(x) {
|
|
|
15465
15645
|
}
|
|
15466
15646
|
return c;
|
|
15467
15647
|
}
|
|
15468
|
-
function
|
|
15648
|
+
function tokenize2(text) {
|
|
15469
15649
|
return text.match(/[A-Za-z_$][A-Za-z0-9_$]*|\d+|[^\s\w]/g) ?? [];
|
|
15470
15650
|
}
|
|
15471
15651
|
function shingle(tokens, n = SHINGLE_N) {
|
|
@@ -15494,7 +15674,7 @@ function simhashFeatures(features) {
|
|
|
15494
15674
|
return out2;
|
|
15495
15675
|
}
|
|
15496
15676
|
function simhash(text) {
|
|
15497
|
-
return simhashFeatures(shingle(
|
|
15677
|
+
return simhashFeatures(shingle(tokenize2(text)));
|
|
15498
15678
|
}
|
|
15499
15679
|
function hammingDistance(a, b) {
|
|
15500
15680
|
return popcount((a ^ b) & MASK64);
|
|
@@ -15670,10 +15850,10 @@ var init_identity2 = __esm({
|
|
|
15670
15850
|
});
|
|
15671
15851
|
|
|
15672
15852
|
// ../../packages/indexer/src/pipeline.ts
|
|
15673
|
-
import { appendFileSync, readFileSync, statSync } from "node:fs";
|
|
15853
|
+
import { appendFileSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
15674
15854
|
import { readdir } from "node:fs/promises";
|
|
15675
|
-
import { extname, join, relative, sep } from "node:path";
|
|
15676
|
-
import { createHash as
|
|
15855
|
+
import { extname, join as join2, relative, sep } from "node:path";
|
|
15856
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15677
15857
|
import { execFileSync } from "node:child_process";
|
|
15678
15858
|
function nowTs() {
|
|
15679
15859
|
return indexNow ?? Date.now();
|
|
@@ -15682,7 +15862,7 @@ function fingerprintOf(signature, bodyHash) {
|
|
|
15682
15862
|
return `${signature ?? ""}|${bodyHash ?? ""}`;
|
|
15683
15863
|
}
|
|
15684
15864
|
function sha(s) {
|
|
15685
|
-
return
|
|
15865
|
+
return createHash3("sha256").update(s).digest("hex").slice(0, 16);
|
|
15686
15866
|
}
|
|
15687
15867
|
function fileNodeId(workspaceId, relPath) {
|
|
15688
15868
|
return `file_${sha(workspaceId + ":" + relPath.replace(/\\/g, "/"))}`;
|
|
@@ -15802,10 +15982,11 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15802
15982
|
};
|
|
15803
15983
|
}
|
|
15804
15984
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
15985
|
+
const contentMovedPaths = /* @__PURE__ */ new Set();
|
|
15805
15986
|
for (const rel of [...changedRelPaths]) {
|
|
15806
15987
|
let h;
|
|
15807
15988
|
try {
|
|
15808
|
-
h =
|
|
15989
|
+
h = createHash3("sha256").update(readFileSync2(join2(rootPath, rel))).digest("hex");
|
|
15809
15990
|
} catch {
|
|
15810
15991
|
continue;
|
|
15811
15992
|
}
|
|
@@ -15814,6 +15995,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15814
15995
|
if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id)) {
|
|
15815
15996
|
store2.updateNode(fnode.id, { lastUpdatedAt: t });
|
|
15816
15997
|
changedRelPaths.delete(rel);
|
|
15998
|
+
} else if (!fnode || fnode.attrs["contentHash"] !== h) {
|
|
15999
|
+
contentMovedPaths.add(rel);
|
|
15817
16000
|
}
|
|
15818
16001
|
}
|
|
15819
16002
|
if (changedRelPaths.size === 0) {
|
|
@@ -15852,13 +16035,13 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15852
16035
|
let parsedFiles = 0;
|
|
15853
16036
|
for (const rel of changedRelPaths) {
|
|
15854
16037
|
if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
|
|
15855
|
-
const abs =
|
|
16038
|
+
const abs = join2(rootPath, ...rel.split("/"));
|
|
15856
16039
|
const ext = extname(abs).toLowerCase();
|
|
15857
16040
|
const provider = providers.find((p) => p.fileExtensions.includes(ext));
|
|
15858
16041
|
if (!provider) continue;
|
|
15859
16042
|
let symbols;
|
|
15860
16043
|
try {
|
|
15861
|
-
symbols = provider.extractSymbols(
|
|
16044
|
+
symbols = provider.extractSymbols(readFileSync2(abs, "utf8"), abs);
|
|
15862
16045
|
} catch {
|
|
15863
16046
|
continue;
|
|
15864
16047
|
}
|
|
@@ -15948,6 +16131,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15948
16131
|
changedRelPaths
|
|
15949
16132
|
});
|
|
15950
16133
|
store2.transaction(() => {
|
|
16134
|
+
store2.reviveReobserved([...changedRelPaths], workspaceId, t);
|
|
15951
16135
|
const versionedAndFile = /* @__PURE__ */ new Set([...VERSIONED_LABELS, "File"]);
|
|
15952
16136
|
const ownedLive = [];
|
|
15953
16137
|
for (const n of store2.nodesByRelPath([...changedRelPaths])) {
|
|
@@ -16056,7 +16240,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
16056
16240
|
const h = fileHashes.get(rel);
|
|
16057
16241
|
if (!h) continue;
|
|
16058
16242
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
16059
|
-
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
|
+
});
|
|
16060
16251
|
}
|
|
16061
16252
|
return {
|
|
16062
16253
|
filesReindexed: changedRelPaths.size,
|
|
@@ -16104,8 +16295,13 @@ async function runIndexer(store2, opts) {
|
|
|
16104
16295
|
byLanguage: {},
|
|
16105
16296
|
durationMs: 0,
|
|
16106
16297
|
nodesPurged: 0,
|
|
16107
|
-
edgesPurged: 0
|
|
16298
|
+
edgesPurged: 0,
|
|
16299
|
+
parseCacheHits: 0,
|
|
16300
|
+
parseCacheMisses: 0
|
|
16108
16301
|
};
|
|
16302
|
+
const cacheDir = parseCacheDir();
|
|
16303
|
+
const parseCacheEnabled = cacheDir !== "";
|
|
16304
|
+
if (parseCacheEnabled) sweepParseCacheOnce(cacheDir);
|
|
16109
16305
|
if (opts.clean) {
|
|
16110
16306
|
const purge = purgeWorkspaceCodeGraph(store2, opts.workspaceId);
|
|
16111
16307
|
report.nodesPurged = purge.nodes;
|
|
@@ -16159,25 +16355,38 @@ async function runIndexer(store2, opts) {
|
|
|
16159
16355
|
}
|
|
16160
16356
|
let source;
|
|
16161
16357
|
try {
|
|
16162
|
-
const st =
|
|
16358
|
+
const st = statSync2(f.absPath);
|
|
16163
16359
|
if (st.size > maxBytes) {
|
|
16164
16360
|
report.filesSkipped++;
|
|
16165
16361
|
continue;
|
|
16166
16362
|
}
|
|
16167
|
-
source =
|
|
16363
|
+
source = readFileSync2(f.absPath, "utf8");
|
|
16168
16364
|
} catch {
|
|
16169
16365
|
report.filesSkipped++;
|
|
16170
16366
|
continue;
|
|
16171
16367
|
}
|
|
16368
|
+
const cacheKey = parseCacheKey(source, f.provider.id);
|
|
16369
|
+
const cached2 = parseCacheEnabled ? readParseCache(cacheDir, cacheKey) : null;
|
|
16172
16370
|
let symbols;
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
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
|
+
}
|
|
16178
16388
|
}
|
|
16179
16389
|
symbolsByFile.set(f.relPath, symbols);
|
|
16180
|
-
const reExports = scanReExports(source);
|
|
16181
16390
|
if (reExports.length > 0) reExportsByFile.set(f.relPath, reExports);
|
|
16182
16391
|
report.filesParsed++;
|
|
16183
16392
|
report.byLanguage[f.provider.id] = (report.byLanguage[f.provider.id] ?? 0) + 1;
|
|
@@ -16222,7 +16431,7 @@ async function runIndexer(store2, opts) {
|
|
|
16222
16431
|
const depth = fileNode.relPath.split("/").length;
|
|
16223
16432
|
if (depth !== 3) continue;
|
|
16224
16433
|
try {
|
|
16225
|
-
const pkg = JSON.parse(
|
|
16434
|
+
const pkg = JSON.parse(readFileSync2(fileNode.absPath, "utf8"));
|
|
16226
16435
|
if (!pkg.name) continue;
|
|
16227
16436
|
const pkgDir = fileNode.relPath.replace(/\/package\.json$/, "");
|
|
16228
16437
|
const candidates = [
|
|
@@ -16604,7 +16813,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16604
16813
|
for (const ent of entries) {
|
|
16605
16814
|
if (ignores.has(ent.name)) continue;
|
|
16606
16815
|
if (ent.name.startsWith(".") && ent.name !== ".") continue;
|
|
16607
|
-
const abs =
|
|
16816
|
+
const abs = join2(current, ent.name);
|
|
16608
16817
|
if (ent.isDirectory()) {
|
|
16609
16818
|
await scan(root, abs, ignores, providers, out2);
|
|
16610
16819
|
} else if (ent.isFile()) {
|
|
@@ -16613,7 +16822,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16613
16822
|
const rel = relative(root, abs).split(sep).join("/");
|
|
16614
16823
|
let size = 0;
|
|
16615
16824
|
try {
|
|
16616
|
-
size =
|
|
16825
|
+
size = statSync2(abs).size;
|
|
16617
16826
|
} catch {
|
|
16618
16827
|
continue;
|
|
16619
16828
|
}
|
|
@@ -16627,7 +16836,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16627
16836
|
}
|
|
16628
16837
|
}
|
|
16629
16838
|
}
|
|
16630
|
-
function
|
|
16839
|
+
function gitListRelPaths(root, ignores) {
|
|
16631
16840
|
let stdout;
|
|
16632
16841
|
try {
|
|
16633
16842
|
stdout = execFileSync(
|
|
@@ -16650,10 +16859,19 @@ function gitListFiles(root, ignores, providers) {
|
|
|
16650
16859
|
if (segs.some((s) => ignores.has(s) || s.startsWith(".") && s.length > 1)) {
|
|
16651
16860
|
continue;
|
|
16652
16861
|
}
|
|
16653
|
-
|
|
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);
|
|
16654
16872
|
let size;
|
|
16655
16873
|
try {
|
|
16656
|
-
size =
|
|
16874
|
+
size = statSync2(abs).size;
|
|
16657
16875
|
} catch {
|
|
16658
16876
|
continue;
|
|
16659
16877
|
}
|
|
@@ -16673,7 +16891,7 @@ function upsertFile(store2, id, f, workspaceId) {
|
|
|
16673
16891
|
const now = nowTs();
|
|
16674
16892
|
let contentHash;
|
|
16675
16893
|
try {
|
|
16676
|
-
contentHash =
|
|
16894
|
+
contentHash = createHash3("sha256").update(readFileSync2(f.absPath)).digest("hex");
|
|
16677
16895
|
} catch {
|
|
16678
16896
|
}
|
|
16679
16897
|
const node = {
|
|
@@ -16872,6 +17090,7 @@ var init_pipeline = __esm({
|
|
|
16872
17090
|
"../../packages/indexer/src/pipeline.ts"() {
|
|
16873
17091
|
"use strict";
|
|
16874
17092
|
init_src2();
|
|
17093
|
+
init_parse_cache();
|
|
16875
17094
|
init_identity2();
|
|
16876
17095
|
DEFAULT_IGNORES = /* @__PURE__ */ new Set([
|
|
16877
17096
|
"node_modules",
|
|
@@ -21036,8 +21255,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
21036
21255
|
|
|
21037
21256
|
// ../../packages/indexer/src/languages/tree-sitter-loader.ts
|
|
21038
21257
|
import { fileURLToPath } from "node:url";
|
|
21039
|
-
import { dirname, join as
|
|
21040
|
-
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";
|
|
21041
21260
|
import { createRequire } from "node:module";
|
|
21042
21261
|
function entryDir() {
|
|
21043
21262
|
try {
|
|
@@ -21051,27 +21270,27 @@ function entryDir() {
|
|
|
21051
21270
|
}
|
|
21052
21271
|
function findWasmDir() {
|
|
21053
21272
|
const here = entryDir();
|
|
21054
|
-
const seaWasm =
|
|
21055
|
-
if (
|
|
21273
|
+
const seaWasm = join3(here, "resources", "wasm");
|
|
21274
|
+
if (existsSync2(join3(seaWasm, "tree-sitter-typescript.wasm"))) return seaWasm;
|
|
21056
21275
|
let dir = here;
|
|
21057
21276
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21058
|
-
const flat =
|
|
21277
|
+
const flat = join3(
|
|
21059
21278
|
dir,
|
|
21060
21279
|
"node_modules",
|
|
21061
21280
|
"@vscode",
|
|
21062
21281
|
"tree-sitter-wasm",
|
|
21063
21282
|
"wasm"
|
|
21064
21283
|
);
|
|
21065
|
-
if (
|
|
21284
|
+
if (existsSync2(join3(flat, "tree-sitter-typescript.wasm"))) return flat;
|
|
21066
21285
|
dir = dirname(dir);
|
|
21067
21286
|
}
|
|
21068
21287
|
let root = here;
|
|
21069
21288
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21070
|
-
const pnpmDir =
|
|
21071
|
-
if (
|
|
21072
|
-
for (const entry of
|
|
21289
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21290
|
+
if (existsSync2(pnpmDir)) {
|
|
21291
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21073
21292
|
if (entry.startsWith("@vscode+tree-sitter-wasm@")) {
|
|
21074
|
-
const candidate =
|
|
21293
|
+
const candidate = join3(
|
|
21075
21294
|
pnpmDir,
|
|
21076
21295
|
entry,
|
|
21077
21296
|
"node_modules",
|
|
@@ -21079,7 +21298,7 @@ function findWasmDir() {
|
|
|
21079
21298
|
"tree-sitter-wasm",
|
|
21080
21299
|
"wasm"
|
|
21081
21300
|
);
|
|
21082
|
-
if (
|
|
21301
|
+
if (existsSync2(join3(candidate, "tree-sitter-typescript.wasm"))) {
|
|
21083
21302
|
return candidate;
|
|
21084
21303
|
}
|
|
21085
21304
|
}
|
|
@@ -21093,27 +21312,27 @@ function findWasmDir() {
|
|
|
21093
21312
|
}
|
|
21094
21313
|
function findRuntimeDir() {
|
|
21095
21314
|
const here = entryDir();
|
|
21096
|
-
const seaRuntime =
|
|
21097
|
-
if (
|
|
21315
|
+
const seaRuntime = join3(here, "resources", "wasm");
|
|
21316
|
+
if (existsSync2(join3(seaRuntime, "web-tree-sitter.wasm"))) return seaRuntime;
|
|
21098
21317
|
let dir = here;
|
|
21099
21318
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21100
|
-
const flat =
|
|
21101
|
-
if (
|
|
21319
|
+
const flat = join3(dir, "node_modules", "web-tree-sitter");
|
|
21320
|
+
if (existsSync2(join3(flat, "web-tree-sitter.wasm"))) return flat;
|
|
21102
21321
|
dir = dirname(dir);
|
|
21103
21322
|
}
|
|
21104
21323
|
let root = here;
|
|
21105
21324
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21106
|
-
const pnpmDir =
|
|
21107
|
-
if (
|
|
21108
|
-
for (const entry of
|
|
21325
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21326
|
+
if (existsSync2(pnpmDir)) {
|
|
21327
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21109
21328
|
if (entry.startsWith("web-tree-sitter@")) {
|
|
21110
|
-
const candidate =
|
|
21329
|
+
const candidate = join3(
|
|
21111
21330
|
pnpmDir,
|
|
21112
21331
|
entry,
|
|
21113
21332
|
"node_modules",
|
|
21114
21333
|
"web-tree-sitter"
|
|
21115
21334
|
);
|
|
21116
|
-
if (
|
|
21335
|
+
if (existsSync2(join3(candidate, "web-tree-sitter.wasm"))) {
|
|
21117
21336
|
return candidate;
|
|
21118
21337
|
}
|
|
21119
21338
|
}
|
|
@@ -21130,7 +21349,7 @@ async function loadWebTreeSitter() {
|
|
|
21130
21349
|
void err2;
|
|
21131
21350
|
}
|
|
21132
21351
|
const here = entryDir();
|
|
21133
|
-
const seaResourceBase =
|
|
21352
|
+
const seaResourceBase = join3(here, "resources", "_resolve.js");
|
|
21134
21353
|
const resourceRequire = createRequire(seaResourceBase);
|
|
21135
21354
|
return resourceRequire("web-tree-sitter");
|
|
21136
21355
|
}
|
|
@@ -21145,9 +21364,9 @@ async function ensureTreeSitterReady() {
|
|
|
21145
21364
|
await Parser2.init({
|
|
21146
21365
|
locateFile: (name2) => {
|
|
21147
21366
|
if (name2 === "tree-sitter.wasm" || name2 === "web-tree-sitter.wasm") {
|
|
21148
|
-
return
|
|
21367
|
+
return join3(runtime, name2);
|
|
21149
21368
|
}
|
|
21150
|
-
return
|
|
21369
|
+
return join3(grammars, name2);
|
|
21151
21370
|
}
|
|
21152
21371
|
});
|
|
21153
21372
|
})();
|
|
@@ -21159,7 +21378,7 @@ async function loadGrammar(name2) {
|
|
|
21159
21378
|
if (cached2) return cached2;
|
|
21160
21379
|
if (!languageClass) throw new Error("tree-sitter not initialized");
|
|
21161
21380
|
const grammars = findWasmDir();
|
|
21162
|
-
const lang = await languageClass.load(
|
|
21381
|
+
const lang = await languageClass.load(join3(grammars, `${name2}.wasm`));
|
|
21163
21382
|
grammarCache.set(name2, lang);
|
|
21164
21383
|
return lang;
|
|
21165
21384
|
}
|
|
@@ -21182,7 +21401,7 @@ var init_tree_sitter_loader = __esm({
|
|
|
21182
21401
|
});
|
|
21183
21402
|
|
|
21184
21403
|
// ../../packages/indexer/src/languages/typescript-treesitter.ts
|
|
21185
|
-
import { createHash as
|
|
21404
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
21186
21405
|
function extractNode(node, source, containerQname, containerKind) {
|
|
21187
21406
|
switch (node.type) {
|
|
21188
21407
|
case "function_declaration":
|
|
@@ -21395,7 +21614,7 @@ function scopeRecord(kind, name2, qname, node, body2, source) {
|
|
|
21395
21614
|
const sig = header.replace(/\s+/g, " ").trim();
|
|
21396
21615
|
if (sig) rec.signature = sig;
|
|
21397
21616
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21398
|
-
rec.bodyHash =
|
|
21617
|
+
rec.bodyHash = createHash4("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21399
21618
|
rec.bodySimhash = toHex(simhash(bodyText));
|
|
21400
21619
|
}
|
|
21401
21620
|
}
|
|
@@ -21837,14 +22056,14 @@ var init_typescript_treesitter = __esm({
|
|
|
21837
22056
|
});
|
|
21838
22057
|
|
|
21839
22058
|
// ../../packages/indexer/src/languages/python-treesitter.ts
|
|
21840
|
-
import { createHash as
|
|
22059
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21841
22060
|
function sigAndHash(node, body2, source) {
|
|
21842
22061
|
if (!body2) return {};
|
|
21843
22062
|
const out2 = {};
|
|
21844
22063
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
21845
22064
|
if (sig) out2.signature = sig;
|
|
21846
22065
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21847
|
-
out2.bodyHash =
|
|
22066
|
+
out2.bodyHash = createHash5("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21848
22067
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
21849
22068
|
return out2;
|
|
21850
22069
|
}
|
|
@@ -22257,7 +22476,7 @@ var init_python_treesitter = __esm({
|
|
|
22257
22476
|
});
|
|
22258
22477
|
|
|
22259
22478
|
// ../../packages/indexer/src/languages/cpp-treesitter.ts
|
|
22260
|
-
import { createHash as
|
|
22479
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22261
22480
|
function extractNode3(node, containerQname, containerIsClass, source) {
|
|
22262
22481
|
switch (node.type) {
|
|
22263
22482
|
case "function_definition":
|
|
@@ -22441,7 +22660,7 @@ function sigAndHash2(node, body2, source) {
|
|
|
22441
22660
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22442
22661
|
if (sig) out2.signature = sig;
|
|
22443
22662
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22444
|
-
out2.bodyHash =
|
|
22663
|
+
out2.bodyHash = createHash6("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22445
22664
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22446
22665
|
return out2;
|
|
22447
22666
|
}
|
|
@@ -22602,7 +22821,7 @@ var init_cpp_treesitter = __esm({
|
|
|
22602
22821
|
});
|
|
22603
22822
|
|
|
22604
22823
|
// ../../packages/indexer/src/languages/go-treesitter.ts
|
|
22605
|
-
import { createHash as
|
|
22824
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
22606
22825
|
function extractNode4(node, containerQname, source) {
|
|
22607
22826
|
switch (node.type) {
|
|
22608
22827
|
case "function_declaration": {
|
|
@@ -22764,7 +22983,7 @@ function sigAndHash3(node, body2, source) {
|
|
|
22764
22983
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22765
22984
|
if (sig) out2.signature = sig;
|
|
22766
22985
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22767
|
-
out2.bodyHash =
|
|
22986
|
+
out2.bodyHash = createHash7("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22768
22987
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22769
22988
|
return out2;
|
|
22770
22989
|
}
|
|
@@ -22908,7 +23127,7 @@ var init_go_treesitter = __esm({
|
|
|
22908
23127
|
});
|
|
22909
23128
|
|
|
22910
23129
|
// ../../packages/indexer/src/languages/rust-treesitter.ts
|
|
22911
|
-
import { createHash as
|
|
23130
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22912
23131
|
function extractNode5(node, containerQname, containerIsClass, source) {
|
|
22913
23132
|
switch (node.type) {
|
|
22914
23133
|
case "function_item":
|
|
@@ -23123,7 +23342,7 @@ function sigAndHash4(node, body2, source) {
|
|
|
23123
23342
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23124
23343
|
if (sig) out2.signature = sig;
|
|
23125
23344
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23126
|
-
out2.bodyHash =
|
|
23345
|
+
out2.bodyHash = createHash8("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23127
23346
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23128
23347
|
return out2;
|
|
23129
23348
|
}
|
|
@@ -23274,7 +23493,7 @@ var init_rust_treesitter = __esm({
|
|
|
23274
23493
|
});
|
|
23275
23494
|
|
|
23276
23495
|
// ../../packages/indexer/src/languages/ruby-treesitter.ts
|
|
23277
|
-
import { createHash as
|
|
23496
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
23278
23497
|
function sigAndHash5(node, body2, source) {
|
|
23279
23498
|
if (!body2) {
|
|
23280
23499
|
const sig2 = source.slice(node.startIndex, node.endIndex).replace(/\s+/g, " ").trim();
|
|
@@ -23284,7 +23503,7 @@ function sigAndHash5(node, body2, source) {
|
|
|
23284
23503
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23285
23504
|
if (sig) out2.signature = sig;
|
|
23286
23505
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23287
|
-
out2.bodyHash =
|
|
23506
|
+
out2.bodyHash = createHash9("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23288
23507
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23289
23508
|
return out2;
|
|
23290
23509
|
}
|
|
@@ -23630,7 +23849,7 @@ var init_ruby_treesitter = __esm({
|
|
|
23630
23849
|
});
|
|
23631
23850
|
|
|
23632
23851
|
// ../../packages/indexer/src/languages/csharp-treesitter.ts
|
|
23633
|
-
import { createHash as
|
|
23852
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23634
23853
|
function extractNode7(node, containerQname, containerIsType, source, eof) {
|
|
23635
23854
|
switch (node.type) {
|
|
23636
23855
|
case "method_declaration":
|
|
@@ -23832,7 +24051,7 @@ function sigAndHash6(node, body2, source) {
|
|
|
23832
24051
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23833
24052
|
if (sig) out2.signature = sig;
|
|
23834
24053
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23835
|
-
out2.bodyHash =
|
|
24054
|
+
out2.bodyHash = createHash10("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23836
24055
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23837
24056
|
return out2;
|
|
23838
24057
|
}
|
|
@@ -23989,6 +24208,7 @@ var init_csharp_treesitter = __esm({
|
|
|
23989
24208
|
// ../../packages/indexer/src/index.ts
|
|
23990
24209
|
var src_exports = {};
|
|
23991
24210
|
__export(src_exports, {
|
|
24211
|
+
DEFAULT_IGNORES: () => DEFAULT_IGNORES,
|
|
23992
24212
|
DRIFT_FORK_RATIO: () => DRIFT_FORK_RATIO,
|
|
23993
24213
|
TreeSitterCSharpProvider: () => TreeSitterCSharpProvider,
|
|
23994
24214
|
TreeSitterCppProvider: () => TreeSitterCppProvider,
|
|
@@ -24005,6 +24225,7 @@ __export(src_exports, {
|
|
|
24005
24225
|
findInnermostScope: () => findInnermostScope,
|
|
24006
24226
|
folderNodeId: () => folderNodeId,
|
|
24007
24227
|
fromHex: () => fromHex,
|
|
24228
|
+
gitListRelPaths: () => gitListRelPaths,
|
|
24008
24229
|
hammingDistance: () => hammingDistance,
|
|
24009
24230
|
hasForkedDrift: () => hasForkedDrift,
|
|
24010
24231
|
incrementalReindex: () => incrementalReindex,
|
|
@@ -24017,7 +24238,7 @@ __export(src_exports, {
|
|
|
24017
24238
|
simhashFeatures: () => simhashFeatures,
|
|
24018
24239
|
symbolNodeId: () => symbolNodeId,
|
|
24019
24240
|
toHex: () => toHex,
|
|
24020
|
-
tokenize: () =>
|
|
24241
|
+
tokenize: () => tokenize2
|
|
24021
24242
|
});
|
|
24022
24243
|
function defaultProviders() {
|
|
24023
24244
|
if (providersCache) return providersCache;
|
|
@@ -24070,9 +24291,38 @@ var LOCAL_RULE_OVERRIDES = {
|
|
|
24070
24291
|
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24071
24292
|
},
|
|
24072
24293
|
REVEALED_BY: null,
|
|
24073
|
-
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"] }
|
|
24074
24309
|
};
|
|
24075
|
-
|
|
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;
|
|
24076
24326
|
var SCHEMA_SQL = `
|
|
24077
24327
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
24078
24328
|
version INTEGER PRIMARY KEY
|
|
@@ -24087,6 +24337,21 @@ CREATE TABLE IF NOT EXISTS store_meta (
|
|
|
24087
24337
|
value TEXT NOT NULL
|
|
24088
24338
|
);
|
|
24089
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
|
+
|
|
24090
24355
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
24091
24356
|
id TEXT PRIMARY KEY,
|
|
24092
24357
|
label TEXT NOT NULL,
|
|
@@ -24463,6 +24728,16 @@ var SqliteGraphStore = class {
|
|
|
24463
24728
|
if (!cols.has(name2)) this.db.exec(ddl);
|
|
24464
24729
|
}
|
|
24465
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
|
+
}
|
|
24466
24741
|
this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
|
|
24467
24742
|
});
|
|
24468
24743
|
}
|
|
@@ -24544,6 +24819,7 @@ var SqliteGraphStore = class {
|
|
|
24544
24819
|
const violation = this.edgeRuleViolation(edge);
|
|
24545
24820
|
if (violation) {
|
|
24546
24821
|
this.rejectedEdgeCount++;
|
|
24822
|
+
this.recordEdgeRejection(edge.type, violation, edge.lastSeenAt || edge.createdAt || 0);
|
|
24547
24823
|
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24548
24824
|
return;
|
|
24549
24825
|
}
|
|
@@ -24568,22 +24844,51 @@ var SqliteGraphStore = class {
|
|
|
24568
24844
|
* overlay consulted first. Returns the reason string on a documented-
|
|
24569
24845
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24570
24846
|
* next to the insert itself. */
|
|
24571
|
-
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24575
|
-
|
|
24576
|
-
|
|
24577
|
-
|
|
24578
|
-
|
|
24579
|
-
|
|
24580
|
-
|
|
24581
|
-
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24582
|
-
}
|
|
24583
|
-
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 {
|
|
24584
24857
|
}
|
|
24585
|
-
|
|
24586
|
-
|
|
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
|
+
);
|
|
24587
24892
|
}
|
|
24588
24893
|
updateEdge(id, patch) {
|
|
24589
24894
|
this.stmts.updateEdge.run({
|
|
@@ -24720,6 +25025,163 @@ var SqliteGraphStore = class {
|
|
|
24720
25025
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
24721
25026
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
24722
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
|
+
}
|
|
24723
25185
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
24724
25186
|
* expression index so the incremental reindex fetches only the changed files'
|
|
24725
25187
|
* symbols instead of scanning every versioned node. */
|
|
@@ -24740,6 +25202,10 @@ var SqliteGraphStore = class {
|
|
|
24740
25202
|
if (!live) return null;
|
|
24741
25203
|
const version2 = live.version ?? 1;
|
|
24742
25204
|
const frozenId = `${liveId}@v${version2}`;
|
|
25205
|
+
if (this.getNode(frozenId)) {
|
|
25206
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
25207
|
+
return frozenId;
|
|
25208
|
+
}
|
|
24743
25209
|
this.stmts.freezeCopy.run({ frozen_id: frozenId, live_id: liveId, t });
|
|
24744
25210
|
this.mergeEdge({
|
|
24745
25211
|
id: `edge_superseded_${frozenId}`,
|
|
@@ -24798,6 +25264,7 @@ var CAUSAL_FAMILY = [
|
|
|
24798
25264
|
|
|
24799
25265
|
// ../../packages/local-graph/src/justification.ts
|
|
24800
25266
|
init_src();
|
|
25267
|
+
init_src2();
|
|
24801
25268
|
function addDependency(store2, opts) {
|
|
24802
25269
|
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
24803
25270
|
const attrs = { relation: opts.relation };
|
|
@@ -24820,13 +25287,72 @@ function addDependency(store2, opts) {
|
|
|
24820
25287
|
store2.mergeEdge(edge);
|
|
24821
25288
|
return edge;
|
|
24822
25289
|
}
|
|
24823
|
-
function markRevisit(store2, node, reason, ts) {
|
|
25290
|
+
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
24824
25291
|
const fresh = store2.getNode(node.id) ?? node;
|
|
24825
25292
|
store2.updateNode(node.id, {
|
|
24826
|
-
attrs: {
|
|
25293
|
+
attrs: {
|
|
25294
|
+
...fresh.attrs,
|
|
25295
|
+
revisit: true,
|
|
25296
|
+
revisitReason: reason,
|
|
25297
|
+
revisitSinceTs: ts,
|
|
25298
|
+
revisitAnsweredWhen: answeredWhen
|
|
25299
|
+
},
|
|
24827
25300
|
lastUpdatedAt: ts
|
|
24828
25301
|
});
|
|
24829
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
|
+
}
|
|
24830
25356
|
function listNeedsRevisit(store2, asOf) {
|
|
24831
25357
|
return store2.nodesAsOf(asOf).filter((n) => n.attrs["revisit"] === true).map((n) => ({
|
|
24832
25358
|
id: n.id,
|
|
@@ -24959,7 +25485,6 @@ function markBoth(store2, a, b, patch, ts) {
|
|
|
24959
25485
|
// ../../packages/local-graph/src/design-problem.ts
|
|
24960
25486
|
init_src();
|
|
24961
25487
|
init_src();
|
|
24962
|
-
init_src2();
|
|
24963
25488
|
|
|
24964
25489
|
// ../../packages/local-graph/src/problem-package-link.ts
|
|
24965
25490
|
init_src();
|
|
@@ -25145,50 +25670,119 @@ function backfillProblemContext(store2, ts) {
|
|
|
25145
25670
|
}
|
|
25146
25671
|
|
|
25147
25672
|
// ../../packages/local-graph/src/design-problem.ts
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
pageRank: 0,
|
|
25164
|
-
isLandmark: false,
|
|
25165
|
-
community: null,
|
|
25166
|
-
stability: "unstable",
|
|
25167
|
-
attrs
|
|
25168
|
-
};
|
|
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);
|
|
25169
25688
|
}
|
|
25170
|
-
function
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
25174
|
-
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
|
|
25182
|
-
|
|
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
|
+
}
|
|
25183
25729
|
});
|
|
25730
|
+
return report;
|
|
25184
25731
|
}
|
|
25185
|
-
|
|
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([
|
|
25186
25777
|
"Solution",
|
|
25187
25778
|
"RootCause",
|
|
25188
25779
|
"Pattern",
|
|
25189
25780
|
"Technique",
|
|
25190
25781
|
"AntiPattern"
|
|
25191
25782
|
]);
|
|
25783
|
+
function isAutoMinted(n) {
|
|
25784
|
+
return n.label === "Solution" && String(n.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25785
|
+
}
|
|
25192
25786
|
function priorsForFile(store2, relPath) {
|
|
25193
25787
|
const want = relPath.trim().replace(/^\.?\//, "");
|
|
25194
25788
|
let file2 = null;
|
|
@@ -25200,35 +25794,53 @@ function priorsForFile(store2, relPath) {
|
|
|
25200
25794
|
}
|
|
25201
25795
|
if (!file2) return null;
|
|
25202
25796
|
const openProblems = [];
|
|
25797
|
+
const constraints = [];
|
|
25798
|
+
const resolvedProblems = [];
|
|
25203
25799
|
const seenProblem = /* @__PURE__ */ new Set();
|
|
25204
25800
|
const related = /* @__PURE__ */ new Map();
|
|
25205
25801
|
for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
25206
25802
|
const n = store2.getNode(e.from);
|
|
25207
25803
|
if (!n) continue;
|
|
25208
25804
|
if (n.label === "Problem") {
|
|
25209
|
-
if (
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
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)) {
|
|
25214
25813
|
related.set(n.id, n);
|
|
25215
25814
|
}
|
|
25216
25815
|
}
|
|
25217
25816
|
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
25218
|
-
for (const p of openProblems) {
|
|
25817
|
+
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
25219
25818
|
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
25220
25819
|
const n = store2.getNode(e.to);
|
|
25221
|
-
if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
|
|
25222
|
-
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)) {
|
|
25223
25822
|
const list = solutionsByProblem.get(p.id) ?? [];
|
|
25224
25823
|
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
25225
25824
|
solutionsByProblem.set(p.id, list);
|
|
25226
25825
|
}
|
|
25227
25826
|
}
|
|
25228
25827
|
}
|
|
25229
|
-
if (openProblems.length === 0 && related.size === 0)
|
|
25230
|
-
|
|
25231
|
-
|
|
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
|
+
};
|
|
25232
25844
|
}
|
|
25233
25845
|
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
25234
25846
|
const p = store2.getNode(problemId);
|
|
@@ -25239,18 +25851,49 @@ function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
|
25239
25851
|
});
|
|
25240
25852
|
return true;
|
|
25241
25853
|
}
|
|
25242
|
-
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) {
|
|
25243
25882
|
let resolved = 0;
|
|
25244
25883
|
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25245
25884
|
if (!p.id.startsWith("dprob_")) continue;
|
|
25246
25885
|
if (p.attrs["resolvedAt"]) continue;
|
|
25886
|
+
if (isConstraintProblem(p)) continue;
|
|
25247
25887
|
let symName = "";
|
|
25248
25888
|
let symRelPath;
|
|
25249
25889
|
let edited = false;
|
|
25890
|
+
const since = Math.max(p.createdAt, Number(p.attrs["fixCandidateClearedAt"] ?? 0));
|
|
25250
25891
|
for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
|
|
25251
25892
|
if (e.attrs?.["mention"] === true) continue;
|
|
25893
|
+
if (e.attrs?.["anchorProvenance"] === "legacy") continue;
|
|
25252
25894
|
const sym = store2.getNode(e.to);
|
|
25253
|
-
|
|
25895
|
+
const changedAt = sym?.label === "File" ? Number(sym.attrs["contentChangedAt"] ?? 0) : sym?.lastUpdatedAt ?? 0;
|
|
25896
|
+
if (sym && changedAt > since) {
|
|
25254
25897
|
edited = true;
|
|
25255
25898
|
symName = sym.description;
|
|
25256
25899
|
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
@@ -25258,36 +25901,182 @@ function resolveDesignProblems(store2, t) {
|
|
|
25258
25901
|
}
|
|
25259
25902
|
}
|
|
25260
25903
|
if (!edited) continue;
|
|
25261
|
-
if (
|
|
25262
|
-
|
|
25263
|
-
store2.
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
// AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
|
|
25268
|
-
// just prose — the F2 join key downstream backfills and the viz read.
|
|
25269
|
-
resolvedSymbols: [symName],
|
|
25270
|
-
...symRelPath ? { resolvedRelPath: symRelPath } : {}
|
|
25271
|
-
})
|
|
25272
|
-
);
|
|
25273
|
-
mergeEdge(store2, p.id, solId, "SOLVED_BY", t);
|
|
25274
|
-
for (const ae of store2.outEdges(p.id, ["ANCHORED_AT"]))
|
|
25275
|
-
mergeEdge(store2, solId, ae.to, "ANCHORED_AT", t);
|
|
25276
|
-
}
|
|
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;
|
|
25277
25910
|
store2.updateNode(p.id, {
|
|
25278
|
-
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
|
+
},
|
|
25279
25924
|
lastUpdatedAt: t
|
|
25280
25925
|
});
|
|
25281
25926
|
resolved++;
|
|
25282
25927
|
}
|
|
25283
25928
|
return resolved;
|
|
25284
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();
|
|
25285
25975
|
|
|
25286
25976
|
// ../../packages/local-graph/src/anchor-backfill.ts
|
|
25287
|
-
|
|
25977
|
+
init_src();
|
|
25978
|
+
init_src2();
|
|
25288
25979
|
function isLegacyAnchor(attrs) {
|
|
25289
25980
|
return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
|
|
25290
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
|
+
}
|
|
25291
26080
|
function backfillLegacyAnchors(store2, ts) {
|
|
25292
26081
|
const report = {
|
|
25293
26082
|
demotedEdges: 0,
|
|
@@ -25327,7 +26116,11 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
25327
26116
|
store2,
|
|
25328
26117
|
sol,
|
|
25329
26118
|
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
25330
|
-
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"
|
|
25331
26124
|
);
|
|
25332
26125
|
store2.updateNode(p.id, {
|
|
25333
26126
|
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
@@ -25361,7 +26154,7 @@ function pagerank(input) {
|
|
|
25361
26154
|
const ids = input.nodeIds;
|
|
25362
26155
|
const n = ids.length;
|
|
25363
26156
|
if (n === 0) {
|
|
25364
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26157
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25365
26158
|
}
|
|
25366
26159
|
const index = /* @__PURE__ */ new Map();
|
|
25367
26160
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -25433,8 +26226,12 @@ function pagerank(input) {
|
|
|
25433
26226
|
}
|
|
25434
26227
|
}
|
|
25435
26228
|
const scores = /* @__PURE__ */ new Map();
|
|
25436
|
-
|
|
25437
|
-
|
|
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 };
|
|
25438
26235
|
}
|
|
25439
26236
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
25440
26237
|
const entries = [...scores.entries()];
|
|
@@ -25444,7 +26241,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
25444
26241
|
}
|
|
25445
26242
|
}
|
|
25446
26243
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
25447
|
-
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));
|
|
25448
26245
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
25449
26246
|
const out2 = /* @__PURE__ */ new Set();
|
|
25450
26247
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -25682,8 +26479,86 @@ function motifDecision(input) {
|
|
|
25682
26479
|
};
|
|
25683
26480
|
}
|
|
25684
26481
|
|
|
25685
|
-
// ../../packages/
|
|
25686
|
-
|
|
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
|
+
}
|
|
25687
26562
|
|
|
25688
26563
|
// ../../packages/local-graph/src/triage.ts
|
|
25689
26564
|
init_src();
|
|
@@ -25692,95 +26567,8 @@ init_src();
|
|
|
25692
26567
|
init_src2();
|
|
25693
26568
|
var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
|
|
25694
26569
|
|
|
25695
|
-
// ../../packages/local-graph/src/
|
|
25696
|
-
|
|
25697
|
-
init_src();
|
|
25698
|
-
var REDIRECT_EDGES = [
|
|
25699
|
-
"CAUSED_BY",
|
|
25700
|
-
"SOLVED_BY",
|
|
25701
|
-
"FIXED_BY",
|
|
25702
|
-
"ANCHORED_AT",
|
|
25703
|
-
"MANIFESTED_IN",
|
|
25704
|
-
"EVIDENCED_BY"
|
|
25705
|
-
];
|
|
25706
|
-
function corroborations(n) {
|
|
25707
|
-
return Number(n.attrs["corroborations"] ?? 0);
|
|
25708
|
-
}
|
|
25709
|
-
function overlap(a, b) {
|
|
25710
|
-
if (a.size === 0 || b.size === 0) return 0;
|
|
25711
|
-
let inter = 0;
|
|
25712
|
-
for (const x of a) if (b.has(x)) inter++;
|
|
25713
|
-
return inter / Math.min(a.size, b.size);
|
|
25714
|
-
}
|
|
25715
|
-
function mergeDuplicateProblems(store2, opts) {
|
|
25716
|
-
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25717
|
-
const minTokens = opts.minTokens ?? 4;
|
|
25718
|
-
const report = { clusters: 0, merged: 0 };
|
|
25719
|
-
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25720
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
25721
|
-
for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
|
|
25722
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
25723
|
-
store2.transaction(() => {
|
|
25724
|
-
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25725
|
-
const a = open[i2];
|
|
25726
|
-
if (consumed.has(a.id)) continue;
|
|
25727
|
-
const cluster = [a];
|
|
25728
|
-
const ta = tokens.get(a.id);
|
|
25729
|
-
for (let j = i2 + 1; j < open.length; j++) {
|
|
25730
|
-
const b = open[j];
|
|
25731
|
-
if (consumed.has(b.id)) continue;
|
|
25732
|
-
const tb = tokens.get(b.id);
|
|
25733
|
-
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25734
|
-
if (overlap(ta, tb) >= minOverlap) {
|
|
25735
|
-
cluster.push(b);
|
|
25736
|
-
consumed.add(b.id);
|
|
25737
|
-
}
|
|
25738
|
-
}
|
|
25739
|
-
if (cluster.length < 2) continue;
|
|
25740
|
-
report.clusters++;
|
|
25741
|
-
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25742
|
-
const survivor = cluster[0];
|
|
25743
|
-
for (const dup of cluster.slice(1)) {
|
|
25744
|
-
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25745
|
-
report.merged++;
|
|
25746
|
-
}
|
|
25747
|
-
}
|
|
25748
|
-
});
|
|
25749
|
-
return report;
|
|
25750
|
-
}
|
|
25751
|
-
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25752
|
-
const surv = store2.getNode(survivor.id);
|
|
25753
|
-
if (!surv) return;
|
|
25754
|
-
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25755
|
-
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25756
|
-
store2.updateNode(survivor.id, {
|
|
25757
|
-
attrs: {
|
|
25758
|
-
...surv.attrs,
|
|
25759
|
-
sources: [...sources],
|
|
25760
|
-
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25761
|
-
},
|
|
25762
|
-
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25763
|
-
lastUpdatedAt: ts
|
|
25764
|
-
});
|
|
25765
|
-
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25766
|
-
if (e.to === survivor.id) continue;
|
|
25767
|
-
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25768
|
-
if (store2.getEdge(id)) continue;
|
|
25769
|
-
const redirected = {
|
|
25770
|
-
...e,
|
|
25771
|
-
id,
|
|
25772
|
-
from: survivor.id,
|
|
25773
|
-
createdAt: ts,
|
|
25774
|
-
lastSeenAt: ts
|
|
25775
|
-
};
|
|
25776
|
-
store2.mergeEdge(redirected);
|
|
25777
|
-
}
|
|
25778
|
-
store2.updateNode(dup.id, {
|
|
25779
|
-
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25780
|
-
lastUpdatedAt: ts
|
|
25781
|
-
});
|
|
25782
|
-
store2.closeNode(dup.id, ts);
|
|
25783
|
-
}
|
|
26570
|
+
// ../../packages/local-graph/src/community.ts
|
|
26571
|
+
init_src2();
|
|
25784
26572
|
|
|
25785
26573
|
// ../../packages/local-graph/src/tools.ts
|
|
25786
26574
|
init_src();
|
|
@@ -25797,6 +26585,9 @@ function rankToolsForHandles(store2, limit = 5) {
|
|
|
25797
26585
|
// ../../packages/local-graph/src/principle-sync.ts
|
|
25798
26586
|
init_src2();
|
|
25799
26587
|
|
|
26588
|
+
// ../../packages/local-graph/src/mechanism-liveness.ts
|
|
26589
|
+
var STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
26590
|
+
|
|
25800
26591
|
// ../../packages/generalizer/src/generalizer.ts
|
|
25801
26592
|
init_src2();
|
|
25802
26593
|
init_src();
|
|
@@ -25916,7 +26707,164 @@ function promoteMotifs(store2, t) {
|
|
|
25916
26707
|
}
|
|
25917
26708
|
|
|
25918
26709
|
// ../../packages/generalizer/src/nightly.ts
|
|
25919
|
-
|
|
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) {
|
|
25920
26868
|
const started = Date.now();
|
|
25921
26869
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
25922
26870
|
const nodeIds = [];
|
|
@@ -25942,10 +26890,12 @@ function runNightlyPipeline(store2) {
|
|
|
25942
26890
|
maxIterations: 100
|
|
25943
26891
|
});
|
|
25944
26892
|
const scored = result.scores.size;
|
|
26893
|
+
let pageRankWritten = 0;
|
|
25945
26894
|
store2.transaction(() => {
|
|
25946
26895
|
for (const [id, score2] of result.scores) {
|
|
25947
26896
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
25948
26897
|
store2.setPageRank(id, score2);
|
|
26898
|
+
pageRankWritten++;
|
|
25949
26899
|
}
|
|
25950
26900
|
});
|
|
25951
26901
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -25960,11 +26910,13 @@ function runNightlyPipeline(store2) {
|
|
|
25960
26910
|
for (const id of nodeIds) {
|
|
25961
26911
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
25962
26912
|
}
|
|
26913
|
+
let landmarkFlips = 0;
|
|
25963
26914
|
store2.transaction(() => {
|
|
25964
26915
|
for (const id of nodeIds) {
|
|
25965
26916
|
const want = allLandmarks.has(id);
|
|
25966
26917
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
25967
26918
|
store2.setLandmark(id, want);
|
|
26919
|
+
landmarkFlips++;
|
|
25968
26920
|
}
|
|
25969
26921
|
});
|
|
25970
26922
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -25999,8 +26951,6 @@ function runNightlyPipeline(store2) {
|
|
|
25999
26951
|
for (const [id, c] of comm.community) store2.updateNode(id, { community: c });
|
|
26000
26952
|
});
|
|
26001
26953
|
const motifs = promoteMotifs(store2, started);
|
|
26002
|
-
const designResolved = resolveDesignProblems(store2, started);
|
|
26003
|
-
const cry = crystallize(store2, { ts: started });
|
|
26004
26954
|
return {
|
|
26005
26955
|
scoredNodes: scored,
|
|
26006
26956
|
iterations: result.iterations,
|
|
@@ -26010,15 +26960,61 @@ function runNightlyPipeline(store2) {
|
|
|
26010
26960
|
motifsPromoted: motifs.promoted,
|
|
26011
26961
|
techniques: motifs.techniques,
|
|
26012
26962
|
antipatterns: motifs.antipatterns,
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
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 {
|
|
26016
26980
|
designResolved,
|
|
26981
|
+
revisitsCleared: revisits.cleared,
|
|
26982
|
+
revisitsStanding: revisits.standing,
|
|
26983
|
+
fixCandidatesSettled: fixCandidates.answered + fixCandidates.ignored + fixCandidates.unsubstantiated,
|
|
26984
|
+
fixCandidates,
|
|
26017
26985
|
claimsEvaluated: cry.evaluated,
|
|
26018
26986
|
claimsDerived: cry.derived.length,
|
|
26019
26987
|
durationMs: Date.now() - started
|
|
26020
26988
|
};
|
|
26021
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
|
+
}
|
|
26022
27018
|
function codeAnchorProjection(store2, semanticIds, opts = {}) {
|
|
26023
27019
|
const anchorToNodes = /* @__PURE__ */ new Map();
|
|
26024
27020
|
for (const id of semanticIds) {
|
|
@@ -26063,13 +27059,27 @@ var AGENTS_POINTER_BODY = [
|
|
|
26063
27059
|
init_src2();
|
|
26064
27060
|
function buildSnapshot(opts) {
|
|
26065
27061
|
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
|
|
26066
|
-
const
|
|
27062
|
+
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
27063
|
+
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
26067
27064
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
26068
27065
|
const recent = allProblems.slice(0, 8).map((p) => ({
|
|
26069
27066
|
node: p,
|
|
26070
27067
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
26071
27068
|
}));
|
|
26072
|
-
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) => {
|
|
26073
27083
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
26074
27084
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
26075
27085
|
return { node: p, ...solution ? { solution } : {} };
|
|
@@ -26096,10 +27106,13 @@ function buildSnapshot(opts) {
|
|
|
26096
27106
|
episodeId,
|
|
26097
27107
|
problems: problems2
|
|
26098
27108
|
}));
|
|
26099
|
-
const
|
|
26100
|
-
|
|
26101
|
-
|
|
26102
|
-
|
|
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);
|
|
26103
27116
|
const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
26104
27117
|
const pkgBase = (x) => {
|
|
26105
27118
|
const at = x.lastIndexOf("@");
|
|
@@ -26135,7 +27148,10 @@ function buildSnapshot(opts) {
|
|
|
26135
27148
|
profileContext,
|
|
26136
27149
|
recentProblems: recent,
|
|
26137
27150
|
recentResolved,
|
|
27151
|
+
recentConstraints,
|
|
27152
|
+
regressions,
|
|
26138
27153
|
...causalNudge ? { causalNudge } : {},
|
|
27154
|
+
...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
|
|
26139
27155
|
...domainNudge ? { domainNudge } : {},
|
|
26140
27156
|
motifs,
|
|
26141
27157
|
reviewCount: opts.reviewCount,
|
|
@@ -26186,10 +27202,16 @@ function sliceForFile(store2, relPath) {
|
|
|
26186
27202
|
}
|
|
26187
27203
|
const fp = priorsForFile(store2, relPath);
|
|
26188
27204
|
const priors = fp ? {
|
|
26189
|
-
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 })),
|
|
26190
27212
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
26191
27213
|
solutionsByProblem: Object.fromEntries(
|
|
26192
|
-
fp.openProblems.slice(0, 3).map((p) => [
|
|
27214
|
+
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
26193
27215
|
p.id,
|
|
26194
27216
|
(fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
|
|
26195
27217
|
])
|
|
@@ -26226,37 +27248,37 @@ init_src3();
|
|
|
26226
27248
|
// ../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js
|
|
26227
27249
|
import os from "node:os";
|
|
26228
27250
|
import process3 from "node:process";
|
|
26229
|
-
var
|
|
27251
|
+
var homedir2 = os.homedir();
|
|
26230
27252
|
var tmpdir = os.tmpdir();
|
|
26231
27253
|
var { env } = process3;
|
|
26232
27254
|
|
|
26233
27255
|
// src/paths.ts
|
|
26234
|
-
import { dirname as dirname2, join as
|
|
27256
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
26235
27257
|
function workspaceDir(workspaceRoot) {
|
|
26236
|
-
return
|
|
27258
|
+
return join4(workspaceRoot, ".errata");
|
|
26237
27259
|
}
|
|
26238
27260
|
function workspacePaths(root) {
|
|
26239
27261
|
const dir = workspaceDir(root);
|
|
26240
27262
|
return {
|
|
26241
27263
|
root,
|
|
26242
27264
|
configDir: dir,
|
|
26243
|
-
workspaceJson:
|
|
26244
|
-
eventLog:
|
|
26245
|
-
castalia:
|
|
26246
|
-
reviewQueue:
|
|
26247
|
-
outbox:
|
|
26248
|
-
daemonLock:
|
|
26249
|
-
identityAudit:
|
|
26250
|
-
skillsDir:
|
|
26251
|
-
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")
|
|
26252
27274
|
};
|
|
26253
27275
|
}
|
|
26254
27276
|
|
|
26255
27277
|
// src/reconcile.ts
|
|
26256
27278
|
init_src3();
|
|
26257
27279
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
26258
|
-
import { readdirSync as
|
|
26259
|
-
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";
|
|
26260
27282
|
var IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
|
|
26261
27283
|
var SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
|
|
26262
27284
|
function gitSourceFiles(root) {
|
|
@@ -26273,7 +27295,7 @@ function gitSourceFiles(root) {
|
|
|
26273
27295
|
const out2 = [];
|
|
26274
27296
|
for (const rel of stdout.split("\0")) {
|
|
26275
27297
|
if (!rel || !SOURCE_RE.test(rel)) continue;
|
|
26276
|
-
const abs =
|
|
27298
|
+
const abs = join5(root, rel);
|
|
26277
27299
|
if (IGNORED.test(abs)) continue;
|
|
26278
27300
|
out2.push(abs);
|
|
26279
27301
|
}
|
|
@@ -26282,13 +27304,13 @@ function gitSourceFiles(root) {
|
|
|
26282
27304
|
function* walkSource(dir) {
|
|
26283
27305
|
let entries;
|
|
26284
27306
|
try {
|
|
26285
|
-
entries =
|
|
27307
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
26286
27308
|
} catch {
|
|
26287
27309
|
return;
|
|
26288
27310
|
}
|
|
26289
27311
|
for (const e of entries) {
|
|
26290
27312
|
const name2 = String(e.name);
|
|
26291
|
-
const full =
|
|
27313
|
+
const full = join5(dir, name2);
|
|
26292
27314
|
if (IGNORED.test(full)) continue;
|
|
26293
27315
|
if (e.isDirectory()) yield* walkSource(full);
|
|
26294
27316
|
else if (SOURCE_RE.test(name2)) yield full;
|
|
@@ -26297,12 +27319,18 @@ function* walkSource(dir) {
|
|
|
26297
27319
|
function listSourceFiles(root) {
|
|
26298
27320
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
26299
27321
|
}
|
|
26300
|
-
|
|
27322
|
+
var SCAN_YIELD_EVERY = 100;
|
|
27323
|
+
var LIVENESS_CHUNK = 4e3;
|
|
27324
|
+
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
26301
27325
|
const stale = [];
|
|
27326
|
+
const pending = [];
|
|
27327
|
+
const allTargets = [];
|
|
27328
|
+
let scanned = 0;
|
|
26302
27329
|
for (const abs of listSourceFiles(rootPath)) {
|
|
27330
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
26303
27331
|
let mtimeMs;
|
|
26304
27332
|
try {
|
|
26305
|
-
mtimeMs =
|
|
27333
|
+
mtimeMs = statSync3(abs).mtimeMs;
|
|
26306
27334
|
} catch {
|
|
26307
27335
|
continue;
|
|
26308
27336
|
}
|
|
@@ -26310,32 +27338,52 @@ function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
26310
27338
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
26311
27339
|
if (!fnode) {
|
|
26312
27340
|
stale.push(abs);
|
|
27341
|
+
} else if ((fnode.validTo ?? null) !== null) {
|
|
27342
|
+
stale.push(abs);
|
|
26313
27343
|
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
26314
27344
|
stale.push(abs);
|
|
26315
27345
|
} else {
|
|
26316
27346
|
const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
26317
27347
|
if (edges.length === 0) {
|
|
26318
27348
|
stale.push(abs);
|
|
26319
|
-
} else
|
|
26320
|
-
|
|
27349
|
+
} else {
|
|
27350
|
+
const targets = edges.map((e) => e.to);
|
|
27351
|
+
pending.push({ abs, targets });
|
|
27352
|
+
allTargets.push(...targets);
|
|
26321
27353
|
}
|
|
26322
27354
|
}
|
|
26323
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
|
+
}
|
|
26324
27364
|
return stale;
|
|
26325
27365
|
}
|
|
26326
|
-
function isLive(n) {
|
|
26327
|
-
return n != null && (n.validTo ?? null) === null;
|
|
26328
|
-
}
|
|
26329
27366
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
26330
|
-
const stale = findStaleFiles(store2, rootPath, workspaceId);
|
|
27367
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId);
|
|
26331
27368
|
if (stale.length === 0) return 0;
|
|
26332
27369
|
let total = 0;
|
|
27370
|
+
let failedBatches = 0;
|
|
26333
27371
|
const BATCH = 20;
|
|
26334
27372
|
for (let i2 = 0; i2 < stale.length; i2 += BATCH) {
|
|
26335
|
-
|
|
26336
|
-
|
|
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
|
+
}
|
|
26337
27382
|
if (i2 + BATCH < stale.length) await new Promise((res) => setImmediate(res));
|
|
26338
27383
|
}
|
|
27384
|
+
if (failedBatches > 0) {
|
|
27385
|
+
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
27386
|
+
}
|
|
26339
27387
|
return total;
|
|
26340
27388
|
}
|
|
26341
27389
|
|
|
@@ -26388,6 +27436,7 @@ function runNightly() {
|
|
|
26388
27436
|
const a = backfillLegacyAnchors(store, Date.now());
|
|
26389
27437
|
anchorsDemoted = a.demotedEdges;
|
|
26390
27438
|
resolutionsSuspect = a.suspects.length;
|
|
27439
|
+
repairLegacyAnchorsFromStatement(store, Date.now());
|
|
26391
27440
|
} catch {
|
|
26392
27441
|
}
|
|
26393
27442
|
const report = runNightlyPipeline(store);
|