@inerrata-corporation/errata 2.0.2-dev.95 → 2.0.2-dev.981
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 +590 -238
- package/errata.mjs +8489 -2287
- package/package.json +1 -1
- package/pass-worker.mjs +1311 -297
package/pass-worker.mjs
CHANGED
|
@@ -144,7 +144,14 @@ var init_castalia = __esm({
|
|
|
144
144
|
"SOLVED_BY",
|
|
145
145
|
"MITIGATES",
|
|
146
146
|
"REPORTED_FAILURE",
|
|
147
|
-
"CONTRADICTS"
|
|
147
|
+
"CONTRADICTS",
|
|
148
|
+
// Motif twin-faces (KN-twin-faces): failure-face motif → remedy-face motif
|
|
149
|
+
// across layers (AntiPattern/Weakness ↔ Technique/Pattern). Same
|
|
150
|
+
// problem→fix direction as FIXED_BY/SOLVED_BY. Minted by the nightly LLM
|
|
151
|
+
// twin-face pass — these are the near-identical cross-layer pairs the
|
|
152
|
+
// polarity gate (finding 5) correctly refuses to FUSE; the link carries
|
|
153
|
+
// what fusion can't.
|
|
154
|
+
"REMEDIED_BY"
|
|
148
155
|
];
|
|
149
156
|
CONCEPTUAL_EDGES = [
|
|
150
157
|
"INSTANCE_OF",
|
|
@@ -259,6 +266,10 @@ var init_castalia = __esm({
|
|
|
259
266
|
// git topology, not signal-flow
|
|
260
267
|
"AUTHORED_BY",
|
|
261
268
|
// git authorship, not signal-flow
|
|
269
|
+
"PRODUCED",
|
|
270
|
+
// edit-episode provenance (Episode→symbol), not signal-flow — the family
|
|
271
|
+
// above was excluded together but this member slipped the net (LC-produced-pagerank);
|
|
272
|
+
// at 43% of all live edges it was the largest single edge population flowing rank
|
|
262
273
|
"CONTRIBUTED",
|
|
263
274
|
// agent attribution (Agent → knowledge), not signal-flow
|
|
264
275
|
"SUPERSEDES",
|
|
@@ -274,6 +285,10 @@ var init_castalia = __esm({
|
|
|
274
285
|
CAUSED_BY: 3,
|
|
275
286
|
FIXED_BY: 3,
|
|
276
287
|
SOLVED_BY: 3,
|
|
288
|
+
// Twin-face link (failure motif → remedy motif, KN-twin-faces) — causal-grade
|
|
289
|
+
// but a notch below witnessed FIXED_BY/SOLVED_BY: the pairing is an LLM
|
|
290
|
+
// judgment over descriptions, not an agent-witnessed resolution.
|
|
291
|
+
REMEDIED_BY: 2.5,
|
|
277
292
|
MANIFESTS_AS: 2,
|
|
278
293
|
ESCALATES_TO: 1.5,
|
|
279
294
|
AFFECTS: 1.2,
|
|
@@ -447,13 +462,18 @@ function resolveCanonical(label) {
|
|
|
447
462
|
function resolveCanonicalId(label) {
|
|
448
463
|
return resolveCanonical(label)?.id;
|
|
449
464
|
}
|
|
465
|
+
function resolveUnambiguousCanonicalId(label) {
|
|
466
|
+
const key = label.trim().toLowerCase();
|
|
467
|
+
if (AMBIGUOUS_ALIASES.has(key)) return void 0;
|
|
468
|
+
return resolveCanonicalId(key);
|
|
469
|
+
}
|
|
450
470
|
function aliasesLongestFirst() {
|
|
451
471
|
const out2 = [];
|
|
452
472
|
for (const e of REGISTRY) for (const a of e.aliases) out2.push({ alias: a.toLowerCase(), entity: e });
|
|
453
473
|
out2.sort((x, y) => y.alias.length - x.alias.length);
|
|
454
474
|
return out2;
|
|
455
475
|
}
|
|
456
|
-
var REGISTRY, BY_ALIAS;
|
|
476
|
+
var REGISTRY, AMBIGUOUS_ALIASES, BY_ALIAS;
|
|
457
477
|
var init_taxonomy = __esm({
|
|
458
478
|
"../../packages/shared/src/nlp/taxonomy.ts"() {
|
|
459
479
|
"use strict";
|
|
@@ -502,6 +522,13 @@ var init_taxonomy = __esm({
|
|
|
502
522
|
{ id: "concept:retry", name: "retry", category: "concept", aliases: ["retry"] },
|
|
503
523
|
{ id: "concept:migration", name: "migration", category: "concept", aliases: ["migration"] }
|
|
504
524
|
];
|
|
525
|
+
AMBIGUOUS_ALIASES = /* @__PURE__ */ new Set([
|
|
526
|
+
"node",
|
|
527
|
+
"go",
|
|
528
|
+
"pool",
|
|
529
|
+
"spring",
|
|
530
|
+
"ws"
|
|
531
|
+
]);
|
|
505
532
|
BY_ALIAS = /* @__PURE__ */ new Map();
|
|
506
533
|
for (const e of REGISTRY) {
|
|
507
534
|
for (const a of e.aliases) {
|
|
@@ -563,7 +590,7 @@ function lemma(token) {
|
|
|
563
590
|
}
|
|
564
591
|
return token;
|
|
565
592
|
}
|
|
566
|
-
function
|
|
593
|
+
function tokenize(text, resolve) {
|
|
567
594
|
const matches = text.normalize("NFC").toLowerCase().match(TOKEN_RE) ?? [];
|
|
568
595
|
const out2 = /* @__PURE__ */ new Set();
|
|
569
596
|
for (const raw of matches) {
|
|
@@ -573,11 +600,14 @@ function conceptTokens(text) {
|
|
|
573
600
|
out2.add(token);
|
|
574
601
|
continue;
|
|
575
602
|
}
|
|
576
|
-
const canonical =
|
|
603
|
+
const canonical = resolve(token);
|
|
577
604
|
out2.add(canonical ?? lemma(token));
|
|
578
605
|
}
|
|
579
606
|
return [...out2].sort();
|
|
580
607
|
}
|
|
608
|
+
function retrievalTokens(text) {
|
|
609
|
+
return tokenize(text, resolveUnambiguousCanonicalId);
|
|
610
|
+
}
|
|
581
611
|
var STOPWORDS, NEGATION_TOKENS, TOKEN_RE;
|
|
582
612
|
var init_concept_bag = __esm({
|
|
583
613
|
"../../packages/shared/src/nlp/concept-bag.ts"() {
|
|
@@ -15058,7 +15088,12 @@ var init_edge_rules = __esm({
|
|
|
15058
15088
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15059
15089
|
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15060
15090
|
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15061
|
-
|
|
15091
|
+
// `AntiPattern` joined the target set 2026-08-02: it is one of the three motif
|
|
15092
|
+
// kinds (generalizer motifs.ts `layerOf`: pattern | technique | antipattern) and
|
|
15093
|
+
// the SUPPRESSED-close path mints `Problem ─INSTANCE_OF→ AntiPattern` as the
|
|
15094
|
+
// negative-knowledge binding ("silenced, not solved") — the original three-label
|
|
15095
|
+
// rule predates AntiPattern joining the motif layer and silently ate that edge.
|
|
15096
|
+
INSTANCE_OF: { to: ["Pattern", "AntiPattern", "Weakness", "Technique"] },
|
|
15062
15097
|
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15063
15098
|
MATCHES: { to: ["Pattern"] },
|
|
15064
15099
|
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
@@ -15159,6 +15194,23 @@ var init_wire = __esm({
|
|
|
15159
15194
|
/** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
|
|
15160
15195
|
description: external_exports.string().min(1).max(4e3),
|
|
15161
15196
|
attrs: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
15197
|
+
/** One-way origin key of the SESSION that minted this node (`ws_…`, a
|
|
15198
|
+
* truncated digest — never the raw session id). Stamped onto the created
|
|
15199
|
+
* node as `authoringSession`, which is the independence unit for the
|
|
15200
|
+
* evidence channels: the session that authored a claim may not corroborate
|
|
15201
|
+
* or refute it, while a DIFFERENT session on the same checkout may (alyssa,
|
|
15202
|
+
* 2026-07-31 — the workspace key made every witness on a single-checkout
|
|
15203
|
+
* deployment a self-corroboration). Optional + additive: absent leaves the
|
|
15204
|
+
* gate fail-open for that node, exactly today's behaviour. */
|
|
15205
|
+
originSession: external_exports.string().min(3).max(64).optional(),
|
|
15206
|
+
/** Epoch ms of the ORIGINATING local node's creation — when the knowledge was
|
|
15207
|
+
* actually captured, as opposed to when its public twin reached the cloud.
|
|
15208
|
+
* Stored as `observedAt`; the network board's chronological view orders on
|
|
15209
|
+
* it. Without this the wire carried NO timestamp at all, so a node captured
|
|
15210
|
+
* days ago surfaced as "new" the moment it was generalized and published —
|
|
15211
|
+
* the board showed ingest order wearing a chronology's clothes. Optional +
|
|
15212
|
+
* additive: absent keeps ingest-time ordering for that node. */
|
|
15213
|
+
originCreatedAtMs: external_exports.number().int().positive().optional(),
|
|
15162
15214
|
extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
|
|
15163
15215
|
validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
|
|
15164
15216
|
/** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
|
|
@@ -15331,6 +15383,51 @@ var init_review = __esm({
|
|
|
15331
15383
|
}
|
|
15332
15384
|
});
|
|
15333
15385
|
|
|
15386
|
+
// ../../packages/local-shared/src/anchorable.ts
|
|
15387
|
+
function anchorableExtensionAlternation() {
|
|
15388
|
+
return BY_LENGTH.map((e) => e.slice(1).replace(/[+.]/g, (c) => `\\${c}`)).join("|");
|
|
15389
|
+
}
|
|
15390
|
+
var ANCHORABLE_EXTENSIONS, BY_LENGTH;
|
|
15391
|
+
var init_anchorable = __esm({
|
|
15392
|
+
"../../packages/local-shared/src/anchorable.ts"() {
|
|
15393
|
+
"use strict";
|
|
15394
|
+
ANCHORABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
15395
|
+
// typescript provider
|
|
15396
|
+
".ts",
|
|
15397
|
+
".tsx",
|
|
15398
|
+
".js",
|
|
15399
|
+
".jsx",
|
|
15400
|
+
".mjs",
|
|
15401
|
+
".cjs",
|
|
15402
|
+
// python / go / rust / ruby / csharp providers
|
|
15403
|
+
".py",
|
|
15404
|
+
".go",
|
|
15405
|
+
".rs",
|
|
15406
|
+
".rb",
|
|
15407
|
+
".cs",
|
|
15408
|
+
// cpp provider — every variant it parses, not just the three that were listed
|
|
15409
|
+
".c",
|
|
15410
|
+
".h",
|
|
15411
|
+
".cpp",
|
|
15412
|
+
".cc",
|
|
15413
|
+
".cxx",
|
|
15414
|
+
".c++",
|
|
15415
|
+
".hpp",
|
|
15416
|
+
".hh",
|
|
15417
|
+
".hxx",
|
|
15418
|
+
".h++",
|
|
15419
|
+
// CUDA — the cpp provider parses these too. Missed when this list was first
|
|
15420
|
+
// transcribed by hand; the parity test caught them on its very first run,
|
|
15421
|
+
// which is the argument for the test existing.
|
|
15422
|
+
".cu",
|
|
15423
|
+
".cuh",
|
|
15424
|
+
// no provider yet; the grammar ships. Inert, not wrong — see above.
|
|
15425
|
+
".java"
|
|
15426
|
+
]);
|
|
15427
|
+
BY_LENGTH = [...ANCHORABLE_EXTENSIONS].sort((a, b) => b.length - a.length);
|
|
15428
|
+
}
|
|
15429
|
+
});
|
|
15430
|
+
|
|
15334
15431
|
// ../../packages/local-shared/src/sqlite-adapter.ts
|
|
15335
15432
|
function openDatabase(path) {
|
|
15336
15433
|
const db = new DatabaseSync(path);
|
|
@@ -15343,6 +15440,7 @@ function openDatabase(path) {
|
|
|
15343
15440
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15344
15441
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15345
15442
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15443
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15346
15444
|
} catch {
|
|
15347
15445
|
}
|
|
15348
15446
|
}
|
|
@@ -15378,7 +15476,7 @@ function openDatabase(path) {
|
|
|
15378
15476
|
return db.prepare(`PRAGMA ${key}`).get();
|
|
15379
15477
|
},
|
|
15380
15478
|
transaction(fn) {
|
|
15381
|
-
db.exec("BEGIN");
|
|
15479
|
+
db.exec("BEGIN IMMEDIATE");
|
|
15382
15480
|
try {
|
|
15383
15481
|
const r = fn();
|
|
15384
15482
|
db.exec("COMMIT");
|
|
@@ -15432,10 +15530,92 @@ var init_src2 = __esm({
|
|
|
15432
15530
|
init_profile();
|
|
15433
15531
|
init_daemon_wire();
|
|
15434
15532
|
init_review();
|
|
15533
|
+
init_anchorable();
|
|
15435
15534
|
init_sqlite_adapter();
|
|
15436
15535
|
}
|
|
15437
15536
|
});
|
|
15438
15537
|
|
|
15538
|
+
// ../../packages/indexer/src/parse-cache.ts
|
|
15539
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
15540
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
15541
|
+
import { homedir } from "node:os";
|
|
15542
|
+
import { join } from "node:path";
|
|
15543
|
+
function parseCacheDir() {
|
|
15544
|
+
return process.env["ERRATA_PARSE_CACHE"] ?? join(homedir(), ".errata", "parse-cache");
|
|
15545
|
+
}
|
|
15546
|
+
function parseCacheKey(source, providerId) {
|
|
15547
|
+
return createHash2("sha256").update(`${PARSE_CACHE_VERSION}:${providerId}:${source}`).digest("hex");
|
|
15548
|
+
}
|
|
15549
|
+
function entryPath(dir, key) {
|
|
15550
|
+
return join(dir, key.slice(0, 2), `${key.slice(2)}.json`);
|
|
15551
|
+
}
|
|
15552
|
+
function readParseCache(dir, key) {
|
|
15553
|
+
const file2 = entryPath(dir, key);
|
|
15554
|
+
try {
|
|
15555
|
+
const raw = readFileSync(file2, "utf8");
|
|
15556
|
+
const parsed = JSON.parse(raw);
|
|
15557
|
+
if (parsed.v !== PARSE_CACHE_VERSION || !Array.isArray(parsed.symbols)) return null;
|
|
15558
|
+
return { symbols: parsed.symbols, reExports: parsed.reExports ?? [] };
|
|
15559
|
+
} catch {
|
|
15560
|
+
return null;
|
|
15561
|
+
}
|
|
15562
|
+
}
|
|
15563
|
+
function writeParseCache(dir, key, providerId, value) {
|
|
15564
|
+
try {
|
|
15565
|
+
const envelope = {
|
|
15566
|
+
v: PARSE_CACHE_VERSION,
|
|
15567
|
+
provider: providerId,
|
|
15568
|
+
symbols: value.symbols,
|
|
15569
|
+
reExports: value.reExports
|
|
15570
|
+
};
|
|
15571
|
+
const body2 = JSON.stringify(envelope);
|
|
15572
|
+
if (body2.length > MAX_ENTRY_BYTES) return;
|
|
15573
|
+
const file2 = entryPath(dir, key);
|
|
15574
|
+
mkdirSync(join(dir, key.slice(0, 2)), { recursive: true });
|
|
15575
|
+
writeFileSync(file2, body2);
|
|
15576
|
+
} catch {
|
|
15577
|
+
}
|
|
15578
|
+
}
|
|
15579
|
+
function sweepParseCacheOnce(dir, now = Date.now()) {
|
|
15580
|
+
if (sweptThisProcess) return 0;
|
|
15581
|
+
sweptThisProcess = true;
|
|
15582
|
+
let removed = 0;
|
|
15583
|
+
try {
|
|
15584
|
+
if (!existsSync(dir)) return 0;
|
|
15585
|
+
for (const bucket of readdirSync(dir)) {
|
|
15586
|
+
const bucketDir = join(dir, bucket);
|
|
15587
|
+
let names;
|
|
15588
|
+
try {
|
|
15589
|
+
names = readdirSync(bucketDir);
|
|
15590
|
+
} catch {
|
|
15591
|
+
continue;
|
|
15592
|
+
}
|
|
15593
|
+
for (const name2 of names) {
|
|
15594
|
+
const file2 = join(bucketDir, name2);
|
|
15595
|
+
try {
|
|
15596
|
+
if (now - statSync(file2).mtimeMs > ENTRY_TTL_MS) {
|
|
15597
|
+
rmSync(file2, { force: true });
|
|
15598
|
+
removed++;
|
|
15599
|
+
}
|
|
15600
|
+
} catch {
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15603
|
+
}
|
|
15604
|
+
} catch {
|
|
15605
|
+
}
|
|
15606
|
+
return removed;
|
|
15607
|
+
}
|
|
15608
|
+
var PARSE_CACHE_VERSION, ENTRY_TTL_MS, MAX_ENTRY_BYTES, sweptThisProcess;
|
|
15609
|
+
var init_parse_cache = __esm({
|
|
15610
|
+
"../../packages/indexer/src/parse-cache.ts"() {
|
|
15611
|
+
"use strict";
|
|
15612
|
+
PARSE_CACHE_VERSION = 1;
|
|
15613
|
+
ENTRY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
15614
|
+
MAX_ENTRY_BYTES = 2 * 1024 * 1024;
|
|
15615
|
+
sweptThisProcess = false;
|
|
15616
|
+
}
|
|
15617
|
+
});
|
|
15618
|
+
|
|
15439
15619
|
// ../../packages/indexer/src/simhash.ts
|
|
15440
15620
|
function fnv1a64(s) {
|
|
15441
15621
|
let h = FNV_OFFSET;
|
|
@@ -15454,7 +15634,7 @@ function popcount(x) {
|
|
|
15454
15634
|
}
|
|
15455
15635
|
return c;
|
|
15456
15636
|
}
|
|
15457
|
-
function
|
|
15637
|
+
function tokenize2(text) {
|
|
15458
15638
|
return text.match(/[A-Za-z_$][A-Za-z0-9_$]*|\d+|[^\s\w]/g) ?? [];
|
|
15459
15639
|
}
|
|
15460
15640
|
function shingle(tokens, n = SHINGLE_N) {
|
|
@@ -15483,7 +15663,7 @@ function simhashFeatures(features) {
|
|
|
15483
15663
|
return out2;
|
|
15484
15664
|
}
|
|
15485
15665
|
function simhash(text) {
|
|
15486
|
-
return simhashFeatures(shingle(
|
|
15666
|
+
return simhashFeatures(shingle(tokenize2(text)));
|
|
15487
15667
|
}
|
|
15488
15668
|
function hammingDistance(a, b) {
|
|
15489
15669
|
return popcount((a ^ b) & MASK64);
|
|
@@ -15659,10 +15839,10 @@ var init_identity2 = __esm({
|
|
|
15659
15839
|
});
|
|
15660
15840
|
|
|
15661
15841
|
// ../../packages/indexer/src/pipeline.ts
|
|
15662
|
-
import { appendFileSync, readFileSync, statSync } from "node:fs";
|
|
15842
|
+
import { appendFileSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
15663
15843
|
import { readdir } from "node:fs/promises";
|
|
15664
|
-
import { extname, join, relative, sep } from "node:path";
|
|
15665
|
-
import { createHash as
|
|
15844
|
+
import { extname, join as join2, relative, sep } from "node:path";
|
|
15845
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15666
15846
|
import { execFileSync } from "node:child_process";
|
|
15667
15847
|
function nowTs() {
|
|
15668
15848
|
return indexNow ?? Date.now();
|
|
@@ -15671,7 +15851,7 @@ function fingerprintOf(signature, bodyHash) {
|
|
|
15671
15851
|
return `${signature ?? ""}|${bodyHash ?? ""}`;
|
|
15672
15852
|
}
|
|
15673
15853
|
function sha(s) {
|
|
15674
|
-
return
|
|
15854
|
+
return createHash3("sha256").update(s).digest("hex").slice(0, 16);
|
|
15675
15855
|
}
|
|
15676
15856
|
function fileNodeId(workspaceId, relPath) {
|
|
15677
15857
|
return `file_${sha(workspaceId + ":" + relPath.replace(/\\/g, "/"))}`;
|
|
@@ -15791,10 +15971,11 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15791
15971
|
};
|
|
15792
15972
|
}
|
|
15793
15973
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
15974
|
+
const contentMovedPaths = /* @__PURE__ */ new Set();
|
|
15794
15975
|
for (const rel of [...changedRelPaths]) {
|
|
15795
15976
|
let h;
|
|
15796
15977
|
try {
|
|
15797
|
-
h =
|
|
15978
|
+
h = createHash3("sha256").update(readFileSync2(join2(rootPath, rel))).digest("hex");
|
|
15798
15979
|
} catch {
|
|
15799
15980
|
continue;
|
|
15800
15981
|
}
|
|
@@ -15803,6 +15984,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15803
15984
|
if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id)) {
|
|
15804
15985
|
store2.updateNode(fnode.id, { lastUpdatedAt: t });
|
|
15805
15986
|
changedRelPaths.delete(rel);
|
|
15987
|
+
} else if (!fnode || fnode.attrs["contentHash"] !== h) {
|
|
15988
|
+
contentMovedPaths.add(rel);
|
|
15806
15989
|
}
|
|
15807
15990
|
}
|
|
15808
15991
|
if (changedRelPaths.size === 0) {
|
|
@@ -15841,13 +16024,13 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15841
16024
|
let parsedFiles = 0;
|
|
15842
16025
|
for (const rel of changedRelPaths) {
|
|
15843
16026
|
if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
|
|
15844
|
-
const abs =
|
|
16027
|
+
const abs = join2(rootPath, ...rel.split("/"));
|
|
15845
16028
|
const ext = extname(abs).toLowerCase();
|
|
15846
16029
|
const provider = providers.find((p) => p.fileExtensions.includes(ext));
|
|
15847
16030
|
if (!provider) continue;
|
|
15848
16031
|
let symbols;
|
|
15849
16032
|
try {
|
|
15850
|
-
symbols = provider.extractSymbols(
|
|
16033
|
+
symbols = provider.extractSymbols(readFileSync2(abs, "utf8"), abs);
|
|
15851
16034
|
} catch {
|
|
15852
16035
|
continue;
|
|
15853
16036
|
}
|
|
@@ -15937,6 +16120,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15937
16120
|
changedRelPaths
|
|
15938
16121
|
});
|
|
15939
16122
|
store2.transaction(() => {
|
|
16123
|
+
store2.reviveReobserved([...changedRelPaths], workspaceId, t);
|
|
15940
16124
|
const versionedAndFile = /* @__PURE__ */ new Set([...VERSIONED_LABELS, "File"]);
|
|
15941
16125
|
const ownedLive = [];
|
|
15942
16126
|
for (const n of store2.nodesByRelPath([...changedRelPaths])) {
|
|
@@ -16045,7 +16229,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
16045
16229
|
const h = fileHashes.get(rel);
|
|
16046
16230
|
if (!h) continue;
|
|
16047
16231
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
16048
|
-
if (fnode)
|
|
16232
|
+
if (!fnode) continue;
|
|
16233
|
+
store2.updateNode(fnode.id, {
|
|
16234
|
+
attrs: {
|
|
16235
|
+
...fnode.attrs,
|
|
16236
|
+
contentHash: h,
|
|
16237
|
+
...contentMovedPaths.has(rel) ? { contentChangedAt: t } : {}
|
|
16238
|
+
}
|
|
16239
|
+
});
|
|
16049
16240
|
}
|
|
16050
16241
|
return {
|
|
16051
16242
|
filesReindexed: changedRelPaths.size,
|
|
@@ -16093,8 +16284,13 @@ async function runIndexer(store2, opts) {
|
|
|
16093
16284
|
byLanguage: {},
|
|
16094
16285
|
durationMs: 0,
|
|
16095
16286
|
nodesPurged: 0,
|
|
16096
|
-
edgesPurged: 0
|
|
16287
|
+
edgesPurged: 0,
|
|
16288
|
+
parseCacheHits: 0,
|
|
16289
|
+
parseCacheMisses: 0
|
|
16097
16290
|
};
|
|
16291
|
+
const cacheDir = parseCacheDir();
|
|
16292
|
+
const parseCacheEnabled = cacheDir !== "";
|
|
16293
|
+
if (parseCacheEnabled) sweepParseCacheOnce(cacheDir);
|
|
16098
16294
|
if (opts.clean) {
|
|
16099
16295
|
const purge = purgeWorkspaceCodeGraph(store2, opts.workspaceId);
|
|
16100
16296
|
report.nodesPurged = purge.nodes;
|
|
@@ -16148,25 +16344,38 @@ async function runIndexer(store2, opts) {
|
|
|
16148
16344
|
}
|
|
16149
16345
|
let source;
|
|
16150
16346
|
try {
|
|
16151
|
-
const st =
|
|
16347
|
+
const st = statSync2(f.absPath);
|
|
16152
16348
|
if (st.size > maxBytes) {
|
|
16153
16349
|
report.filesSkipped++;
|
|
16154
16350
|
continue;
|
|
16155
16351
|
}
|
|
16156
|
-
source =
|
|
16352
|
+
source = readFileSync2(f.absPath, "utf8");
|
|
16157
16353
|
} catch {
|
|
16158
16354
|
report.filesSkipped++;
|
|
16159
16355
|
continue;
|
|
16160
16356
|
}
|
|
16357
|
+
const cacheKey = parseCacheKey(source, f.provider.id);
|
|
16358
|
+
const cached2 = parseCacheEnabled ? readParseCache(cacheDir, cacheKey) : null;
|
|
16161
16359
|
let symbols;
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16360
|
+
let reExports;
|
|
16361
|
+
if (cached2) {
|
|
16362
|
+
symbols = cached2.symbols;
|
|
16363
|
+
reExports = cached2.reExports;
|
|
16364
|
+
report.parseCacheHits++;
|
|
16365
|
+
} else {
|
|
16366
|
+
try {
|
|
16367
|
+
symbols = f.provider.extractSymbols(source, f.absPath);
|
|
16368
|
+
} catch {
|
|
16369
|
+
report.filesSkipped++;
|
|
16370
|
+
continue;
|
|
16371
|
+
}
|
|
16372
|
+
reExports = scanReExports(source);
|
|
16373
|
+
report.parseCacheMisses++;
|
|
16374
|
+
if (parseCacheEnabled) {
|
|
16375
|
+
writeParseCache(cacheDir, cacheKey, f.provider.id, { symbols, reExports });
|
|
16376
|
+
}
|
|
16167
16377
|
}
|
|
16168
16378
|
symbolsByFile.set(f.relPath, symbols);
|
|
16169
|
-
const reExports = scanReExports(source);
|
|
16170
16379
|
if (reExports.length > 0) reExportsByFile.set(f.relPath, reExports);
|
|
16171
16380
|
report.filesParsed++;
|
|
16172
16381
|
report.byLanguage[f.provider.id] = (report.byLanguage[f.provider.id] ?? 0) + 1;
|
|
@@ -16211,7 +16420,7 @@ async function runIndexer(store2, opts) {
|
|
|
16211
16420
|
const depth = fileNode.relPath.split("/").length;
|
|
16212
16421
|
if (depth !== 3) continue;
|
|
16213
16422
|
try {
|
|
16214
|
-
const pkg = JSON.parse(
|
|
16423
|
+
const pkg = JSON.parse(readFileSync2(fileNode.absPath, "utf8"));
|
|
16215
16424
|
if (!pkg.name) continue;
|
|
16216
16425
|
const pkgDir = fileNode.relPath.replace(/\/package\.json$/, "");
|
|
16217
16426
|
const candidates = [
|
|
@@ -16593,7 +16802,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16593
16802
|
for (const ent of entries) {
|
|
16594
16803
|
if (ignores.has(ent.name)) continue;
|
|
16595
16804
|
if (ent.name.startsWith(".") && ent.name !== ".") continue;
|
|
16596
|
-
const abs =
|
|
16805
|
+
const abs = join2(current, ent.name);
|
|
16597
16806
|
if (ent.isDirectory()) {
|
|
16598
16807
|
await scan(root, abs, ignores, providers, out2);
|
|
16599
16808
|
} else if (ent.isFile()) {
|
|
@@ -16602,7 +16811,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16602
16811
|
const rel = relative(root, abs).split(sep).join("/");
|
|
16603
16812
|
let size = 0;
|
|
16604
16813
|
try {
|
|
16605
|
-
size =
|
|
16814
|
+
size = statSync2(abs).size;
|
|
16606
16815
|
} catch {
|
|
16607
16816
|
continue;
|
|
16608
16817
|
}
|
|
@@ -16616,7 +16825,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16616
16825
|
}
|
|
16617
16826
|
}
|
|
16618
16827
|
}
|
|
16619
|
-
function
|
|
16828
|
+
function gitListRelPaths(root, ignores) {
|
|
16620
16829
|
let stdout;
|
|
16621
16830
|
try {
|
|
16622
16831
|
stdout = execFileSync(
|
|
@@ -16639,10 +16848,19 @@ function gitListFiles(root, ignores, providers) {
|
|
|
16639
16848
|
if (segs.some((s) => ignores.has(s) || s.startsWith(".") && s.length > 1)) {
|
|
16640
16849
|
continue;
|
|
16641
16850
|
}
|
|
16642
|
-
|
|
16851
|
+
out2.push(rel);
|
|
16852
|
+
}
|
|
16853
|
+
return out2;
|
|
16854
|
+
}
|
|
16855
|
+
function gitListFiles(root, ignores, providers) {
|
|
16856
|
+
const rels = gitListRelPaths(root, ignores);
|
|
16857
|
+
if (rels === null) return null;
|
|
16858
|
+
const out2 = [];
|
|
16859
|
+
for (const rel of rels) {
|
|
16860
|
+
const abs = join2(root, rel);
|
|
16643
16861
|
let size;
|
|
16644
16862
|
try {
|
|
16645
|
-
size =
|
|
16863
|
+
size = statSync2(abs).size;
|
|
16646
16864
|
} catch {
|
|
16647
16865
|
continue;
|
|
16648
16866
|
}
|
|
@@ -16662,7 +16880,7 @@ function upsertFile(store2, id, f, workspaceId) {
|
|
|
16662
16880
|
const now = nowTs();
|
|
16663
16881
|
let contentHash;
|
|
16664
16882
|
try {
|
|
16665
|
-
contentHash =
|
|
16883
|
+
contentHash = createHash3("sha256").update(readFileSync2(f.absPath)).digest("hex");
|
|
16666
16884
|
} catch {
|
|
16667
16885
|
}
|
|
16668
16886
|
const node = {
|
|
@@ -16861,6 +17079,7 @@ var init_pipeline = __esm({
|
|
|
16861
17079
|
"../../packages/indexer/src/pipeline.ts"() {
|
|
16862
17080
|
"use strict";
|
|
16863
17081
|
init_src2();
|
|
17082
|
+
init_parse_cache();
|
|
16864
17083
|
init_identity2();
|
|
16865
17084
|
DEFAULT_IGNORES = /* @__PURE__ */ new Set([
|
|
16866
17085
|
"node_modules",
|
|
@@ -21025,8 +21244,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
21025
21244
|
|
|
21026
21245
|
// ../../packages/indexer/src/languages/tree-sitter-loader.ts
|
|
21027
21246
|
import { fileURLToPath } from "node:url";
|
|
21028
|
-
import { dirname, join as
|
|
21029
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
21247
|
+
import { dirname, join as join3 } from "node:path";
|
|
21248
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
21030
21249
|
import { createRequire } from "node:module";
|
|
21031
21250
|
function entryDir() {
|
|
21032
21251
|
try {
|
|
@@ -21040,27 +21259,27 @@ function entryDir() {
|
|
|
21040
21259
|
}
|
|
21041
21260
|
function findWasmDir() {
|
|
21042
21261
|
const here = entryDir();
|
|
21043
|
-
const seaWasm =
|
|
21044
|
-
if (
|
|
21262
|
+
const seaWasm = join3(here, "resources", "wasm");
|
|
21263
|
+
if (existsSync2(join3(seaWasm, "tree-sitter-typescript.wasm"))) return seaWasm;
|
|
21045
21264
|
let dir = here;
|
|
21046
21265
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21047
|
-
const flat =
|
|
21266
|
+
const flat = join3(
|
|
21048
21267
|
dir,
|
|
21049
21268
|
"node_modules",
|
|
21050
21269
|
"@vscode",
|
|
21051
21270
|
"tree-sitter-wasm",
|
|
21052
21271
|
"wasm"
|
|
21053
21272
|
);
|
|
21054
|
-
if (
|
|
21273
|
+
if (existsSync2(join3(flat, "tree-sitter-typescript.wasm"))) return flat;
|
|
21055
21274
|
dir = dirname(dir);
|
|
21056
21275
|
}
|
|
21057
21276
|
let root = here;
|
|
21058
21277
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21059
|
-
const pnpmDir =
|
|
21060
|
-
if (
|
|
21061
|
-
for (const entry of
|
|
21278
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21279
|
+
if (existsSync2(pnpmDir)) {
|
|
21280
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21062
21281
|
if (entry.startsWith("@vscode+tree-sitter-wasm@")) {
|
|
21063
|
-
const candidate =
|
|
21282
|
+
const candidate = join3(
|
|
21064
21283
|
pnpmDir,
|
|
21065
21284
|
entry,
|
|
21066
21285
|
"node_modules",
|
|
@@ -21068,7 +21287,7 @@ function findWasmDir() {
|
|
|
21068
21287
|
"tree-sitter-wasm",
|
|
21069
21288
|
"wasm"
|
|
21070
21289
|
);
|
|
21071
|
-
if (
|
|
21290
|
+
if (existsSync2(join3(candidate, "tree-sitter-typescript.wasm"))) {
|
|
21072
21291
|
return candidate;
|
|
21073
21292
|
}
|
|
21074
21293
|
}
|
|
@@ -21082,27 +21301,27 @@ function findWasmDir() {
|
|
|
21082
21301
|
}
|
|
21083
21302
|
function findRuntimeDir() {
|
|
21084
21303
|
const here = entryDir();
|
|
21085
|
-
const seaRuntime =
|
|
21086
|
-
if (
|
|
21304
|
+
const seaRuntime = join3(here, "resources", "wasm");
|
|
21305
|
+
if (existsSync2(join3(seaRuntime, "web-tree-sitter.wasm"))) return seaRuntime;
|
|
21087
21306
|
let dir = here;
|
|
21088
21307
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21089
|
-
const flat =
|
|
21090
|
-
if (
|
|
21308
|
+
const flat = join3(dir, "node_modules", "web-tree-sitter");
|
|
21309
|
+
if (existsSync2(join3(flat, "web-tree-sitter.wasm"))) return flat;
|
|
21091
21310
|
dir = dirname(dir);
|
|
21092
21311
|
}
|
|
21093
21312
|
let root = here;
|
|
21094
21313
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21095
|
-
const pnpmDir =
|
|
21096
|
-
if (
|
|
21097
|
-
for (const entry of
|
|
21314
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21315
|
+
if (existsSync2(pnpmDir)) {
|
|
21316
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21098
21317
|
if (entry.startsWith("web-tree-sitter@")) {
|
|
21099
|
-
const candidate =
|
|
21318
|
+
const candidate = join3(
|
|
21100
21319
|
pnpmDir,
|
|
21101
21320
|
entry,
|
|
21102
21321
|
"node_modules",
|
|
21103
21322
|
"web-tree-sitter"
|
|
21104
21323
|
);
|
|
21105
|
-
if (
|
|
21324
|
+
if (existsSync2(join3(candidate, "web-tree-sitter.wasm"))) {
|
|
21106
21325
|
return candidate;
|
|
21107
21326
|
}
|
|
21108
21327
|
}
|
|
@@ -21119,7 +21338,7 @@ async function loadWebTreeSitter() {
|
|
|
21119
21338
|
void err2;
|
|
21120
21339
|
}
|
|
21121
21340
|
const here = entryDir();
|
|
21122
|
-
const seaResourceBase =
|
|
21341
|
+
const seaResourceBase = join3(here, "resources", "_resolve.js");
|
|
21123
21342
|
const resourceRequire = createRequire(seaResourceBase);
|
|
21124
21343
|
return resourceRequire("web-tree-sitter");
|
|
21125
21344
|
}
|
|
@@ -21134,9 +21353,9 @@ async function ensureTreeSitterReady() {
|
|
|
21134
21353
|
await Parser2.init({
|
|
21135
21354
|
locateFile: (name2) => {
|
|
21136
21355
|
if (name2 === "tree-sitter.wasm" || name2 === "web-tree-sitter.wasm") {
|
|
21137
|
-
return
|
|
21356
|
+
return join3(runtime, name2);
|
|
21138
21357
|
}
|
|
21139
|
-
return
|
|
21358
|
+
return join3(grammars, name2);
|
|
21140
21359
|
}
|
|
21141
21360
|
});
|
|
21142
21361
|
})();
|
|
@@ -21148,7 +21367,7 @@ async function loadGrammar(name2) {
|
|
|
21148
21367
|
if (cached2) return cached2;
|
|
21149
21368
|
if (!languageClass) throw new Error("tree-sitter not initialized");
|
|
21150
21369
|
const grammars = findWasmDir();
|
|
21151
|
-
const lang = await languageClass.load(
|
|
21370
|
+
const lang = await languageClass.load(join3(grammars, `${name2}.wasm`));
|
|
21152
21371
|
grammarCache.set(name2, lang);
|
|
21153
21372
|
return lang;
|
|
21154
21373
|
}
|
|
@@ -21171,7 +21390,7 @@ var init_tree_sitter_loader = __esm({
|
|
|
21171
21390
|
});
|
|
21172
21391
|
|
|
21173
21392
|
// ../../packages/indexer/src/languages/typescript-treesitter.ts
|
|
21174
|
-
import { createHash as
|
|
21393
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
21175
21394
|
function extractNode(node, source, containerQname, containerKind) {
|
|
21176
21395
|
switch (node.type) {
|
|
21177
21396
|
case "function_declaration":
|
|
@@ -21384,7 +21603,7 @@ function scopeRecord(kind, name2, qname, node, body2, source) {
|
|
|
21384
21603
|
const sig = header.replace(/\s+/g, " ").trim();
|
|
21385
21604
|
if (sig) rec.signature = sig;
|
|
21386
21605
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21387
|
-
rec.bodyHash =
|
|
21606
|
+
rec.bodyHash = createHash4("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21388
21607
|
rec.bodySimhash = toHex(simhash(bodyText));
|
|
21389
21608
|
}
|
|
21390
21609
|
}
|
|
@@ -21826,14 +22045,14 @@ var init_typescript_treesitter = __esm({
|
|
|
21826
22045
|
});
|
|
21827
22046
|
|
|
21828
22047
|
// ../../packages/indexer/src/languages/python-treesitter.ts
|
|
21829
|
-
import { createHash as
|
|
22048
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21830
22049
|
function sigAndHash(node, body2, source) {
|
|
21831
22050
|
if (!body2) return {};
|
|
21832
22051
|
const out2 = {};
|
|
21833
22052
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
21834
22053
|
if (sig) out2.signature = sig;
|
|
21835
22054
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21836
|
-
out2.bodyHash =
|
|
22055
|
+
out2.bodyHash = createHash5("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21837
22056
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
21838
22057
|
return out2;
|
|
21839
22058
|
}
|
|
@@ -22246,7 +22465,7 @@ var init_python_treesitter = __esm({
|
|
|
22246
22465
|
});
|
|
22247
22466
|
|
|
22248
22467
|
// ../../packages/indexer/src/languages/cpp-treesitter.ts
|
|
22249
|
-
import { createHash as
|
|
22468
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22250
22469
|
function extractNode3(node, containerQname, containerIsClass, source) {
|
|
22251
22470
|
switch (node.type) {
|
|
22252
22471
|
case "function_definition":
|
|
@@ -22430,7 +22649,7 @@ function sigAndHash2(node, body2, source) {
|
|
|
22430
22649
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22431
22650
|
if (sig) out2.signature = sig;
|
|
22432
22651
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22433
|
-
out2.bodyHash =
|
|
22652
|
+
out2.bodyHash = createHash6("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22434
22653
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22435
22654
|
return out2;
|
|
22436
22655
|
}
|
|
@@ -22591,7 +22810,7 @@ var init_cpp_treesitter = __esm({
|
|
|
22591
22810
|
});
|
|
22592
22811
|
|
|
22593
22812
|
// ../../packages/indexer/src/languages/go-treesitter.ts
|
|
22594
|
-
import { createHash as
|
|
22813
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
22595
22814
|
function extractNode4(node, containerQname, source) {
|
|
22596
22815
|
switch (node.type) {
|
|
22597
22816
|
case "function_declaration": {
|
|
@@ -22753,7 +22972,7 @@ function sigAndHash3(node, body2, source) {
|
|
|
22753
22972
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22754
22973
|
if (sig) out2.signature = sig;
|
|
22755
22974
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22756
|
-
out2.bodyHash =
|
|
22975
|
+
out2.bodyHash = createHash7("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22757
22976
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22758
22977
|
return out2;
|
|
22759
22978
|
}
|
|
@@ -22897,7 +23116,7 @@ var init_go_treesitter = __esm({
|
|
|
22897
23116
|
});
|
|
22898
23117
|
|
|
22899
23118
|
// ../../packages/indexer/src/languages/rust-treesitter.ts
|
|
22900
|
-
import { createHash as
|
|
23119
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22901
23120
|
function extractNode5(node, containerQname, containerIsClass, source) {
|
|
22902
23121
|
switch (node.type) {
|
|
22903
23122
|
case "function_item":
|
|
@@ -23112,7 +23331,7 @@ function sigAndHash4(node, body2, source) {
|
|
|
23112
23331
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23113
23332
|
if (sig) out2.signature = sig;
|
|
23114
23333
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23115
|
-
out2.bodyHash =
|
|
23334
|
+
out2.bodyHash = createHash8("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23116
23335
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23117
23336
|
return out2;
|
|
23118
23337
|
}
|
|
@@ -23263,7 +23482,7 @@ var init_rust_treesitter = __esm({
|
|
|
23263
23482
|
});
|
|
23264
23483
|
|
|
23265
23484
|
// ../../packages/indexer/src/languages/ruby-treesitter.ts
|
|
23266
|
-
import { createHash as
|
|
23485
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
23267
23486
|
function sigAndHash5(node, body2, source) {
|
|
23268
23487
|
if (!body2) {
|
|
23269
23488
|
const sig2 = source.slice(node.startIndex, node.endIndex).replace(/\s+/g, " ").trim();
|
|
@@ -23273,7 +23492,7 @@ function sigAndHash5(node, body2, source) {
|
|
|
23273
23492
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23274
23493
|
if (sig) out2.signature = sig;
|
|
23275
23494
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23276
|
-
out2.bodyHash =
|
|
23495
|
+
out2.bodyHash = createHash9("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23277
23496
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23278
23497
|
return out2;
|
|
23279
23498
|
}
|
|
@@ -23619,7 +23838,7 @@ var init_ruby_treesitter = __esm({
|
|
|
23619
23838
|
});
|
|
23620
23839
|
|
|
23621
23840
|
// ../../packages/indexer/src/languages/csharp-treesitter.ts
|
|
23622
|
-
import { createHash as
|
|
23841
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23623
23842
|
function extractNode7(node, containerQname, containerIsType, source, eof) {
|
|
23624
23843
|
switch (node.type) {
|
|
23625
23844
|
case "method_declaration":
|
|
@@ -23821,7 +24040,7 @@ function sigAndHash6(node, body2, source) {
|
|
|
23821
24040
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23822
24041
|
if (sig) out2.signature = sig;
|
|
23823
24042
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23824
|
-
out2.bodyHash =
|
|
24043
|
+
out2.bodyHash = createHash10("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23825
24044
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23826
24045
|
return out2;
|
|
23827
24046
|
}
|
|
@@ -23978,6 +24197,7 @@ var init_csharp_treesitter = __esm({
|
|
|
23978
24197
|
// ../../packages/indexer/src/index.ts
|
|
23979
24198
|
var src_exports = {};
|
|
23980
24199
|
__export(src_exports, {
|
|
24200
|
+
DEFAULT_IGNORES: () => DEFAULT_IGNORES,
|
|
23981
24201
|
DRIFT_FORK_RATIO: () => DRIFT_FORK_RATIO,
|
|
23982
24202
|
TreeSitterCSharpProvider: () => TreeSitterCSharpProvider,
|
|
23983
24203
|
TreeSitterCppProvider: () => TreeSitterCppProvider,
|
|
@@ -23994,6 +24214,7 @@ __export(src_exports, {
|
|
|
23994
24214
|
findInnermostScope: () => findInnermostScope,
|
|
23995
24215
|
folderNodeId: () => folderNodeId,
|
|
23996
24216
|
fromHex: () => fromHex,
|
|
24217
|
+
gitListRelPaths: () => gitListRelPaths,
|
|
23997
24218
|
hammingDistance: () => hammingDistance,
|
|
23998
24219
|
hasForkedDrift: () => hasForkedDrift,
|
|
23999
24220
|
incrementalReindex: () => incrementalReindex,
|
|
@@ -24006,7 +24227,7 @@ __export(src_exports, {
|
|
|
24006
24227
|
simhashFeatures: () => simhashFeatures,
|
|
24007
24228
|
symbolNodeId: () => symbolNodeId,
|
|
24008
24229
|
toHex: () => toHex,
|
|
24009
|
-
tokenize: () =>
|
|
24230
|
+
tokenize: () => tokenize2
|
|
24010
24231
|
});
|
|
24011
24232
|
function defaultProviders() {
|
|
24012
24233
|
if (providersCache) return providersCache;
|
|
@@ -24059,9 +24280,32 @@ var LOCAL_RULE_OVERRIDES = {
|
|
|
24059
24280
|
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24060
24281
|
},
|
|
24061
24282
|
REVEALED_BY: null,
|
|
24062
|
-
PRODUCED: null
|
|
24283
|
+
PRODUCED: null,
|
|
24284
|
+
// FIXED_BY locally ALSO carries the fix-provenance sense: `resolveProblem`
|
|
24285
|
+
// attributes a closed Problem to the fixing `Episode` (PLAN_PLASTICITY §2.1)
|
|
24286
|
+
// alongside the cloud's Problem→Solution knowledge claim. Same shape as
|
|
24287
|
+
// PRODUCED/REVEALED_BY above — an Episode is code-layer and never drains, so
|
|
24288
|
+
// the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
|
|
24289
|
+
// stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
|
|
24290
|
+
// this rule exists at all.
|
|
24291
|
+
FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] }
|
|
24063
24292
|
};
|
|
24064
|
-
|
|
24293
|
+
function localEdgeViolation(fromLabel, type, toLabel) {
|
|
24294
|
+
if (type in LOCAL_RULE_OVERRIDES) {
|
|
24295
|
+
const rule = LOCAL_RULE_OVERRIDES[type];
|
|
24296
|
+
if (!rule) return null;
|
|
24297
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
24298
|
+
return `${type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
24299
|
+
}
|
|
24300
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
24301
|
+
return `${type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24302
|
+
}
|
|
24303
|
+
return null;
|
|
24304
|
+
}
|
|
24305
|
+
const verdict = isValidEdge(fromLabel, type, toLabel);
|
|
24306
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
24307
|
+
}
|
|
24308
|
+
var SCHEMA_VERSION = 6;
|
|
24065
24309
|
var SCHEMA_SQL = `
|
|
24066
24310
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
24067
24311
|
version INTEGER PRIMARY KEY
|
|
@@ -24076,6 +24320,21 @@ CREATE TABLE IF NOT EXISTS store_meta (
|
|
|
24076
24320
|
value TEXT NOT NULL
|
|
24077
24321
|
);
|
|
24078
24322
|
|
|
24323
|
+
-- Durable ledger of edges the ontology gate REFUSED, keyed by edge type.
|
|
24324
|
+
-- Durable rather than in-memory for one specific reason: the status command runs
|
|
24325
|
+
-- in a SEPARATE process and opens its own store handle, so a counter living on
|
|
24326
|
+
-- the instance reads 0 there forever. That is exactly how a producer rejecting
|
|
24327
|
+
-- 100% of its output stayed invisible for 17 days. Persisting it also survives
|
|
24328
|
+
-- the daemon restart that would otherwise erase the evidence.
|
|
24329
|
+
-- Keyed by type because a systematic producer bug shows up as ONE type
|
|
24330
|
+
-- dominating; sample keeps the latest reason so the count is actionable.
|
|
24331
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24332
|
+
type TEXT PRIMARY KEY,
|
|
24333
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24334
|
+
last_at INTEGER NOT NULL,
|
|
24335
|
+
sample TEXT
|
|
24336
|
+
);
|
|
24337
|
+
|
|
24079
24338
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
24080
24339
|
id TEXT PRIMARY KEY,
|
|
24081
24340
|
label TEXT NOT NULL,
|
|
@@ -24452,6 +24711,16 @@ var SqliteGraphStore = class {
|
|
|
24452
24711
|
if (!cols.has(name2)) this.db.exec(ddl);
|
|
24453
24712
|
}
|
|
24454
24713
|
}
|
|
24714
|
+
if (from < 6) {
|
|
24715
|
+
this.db.exec(`
|
|
24716
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24717
|
+
type TEXT PRIMARY KEY,
|
|
24718
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24719
|
+
last_at INTEGER NOT NULL,
|
|
24720
|
+
sample TEXT
|
|
24721
|
+
)
|
|
24722
|
+
`);
|
|
24723
|
+
}
|
|
24455
24724
|
this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
|
|
24456
24725
|
});
|
|
24457
24726
|
}
|
|
@@ -24533,6 +24802,7 @@ var SqliteGraphStore = class {
|
|
|
24533
24802
|
const violation = this.edgeRuleViolation(edge);
|
|
24534
24803
|
if (violation) {
|
|
24535
24804
|
this.rejectedEdgeCount++;
|
|
24805
|
+
this.recordEdgeRejection(edge.type, violation, edge.lastSeenAt || edge.createdAt || 0);
|
|
24536
24806
|
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24537
24807
|
return;
|
|
24538
24808
|
}
|
|
@@ -24557,22 +24827,51 @@ var SqliteGraphStore = class {
|
|
|
24557
24827
|
* overlay consulted first. Returns the reason string on a documented-
|
|
24558
24828
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24559
24829
|
* next to the insert itself. */
|
|
24560
|
-
|
|
24561
|
-
|
|
24562
|
-
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
|
|
24566
|
-
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
|
|
24571
|
-
|
|
24572
|
-
|
|
24830
|
+
/** Upsert one refusal into the durable ledger. Best-effort: a bookkeeping
|
|
24831
|
+
* failure must never turn a refused edge into a thrown write. */
|
|
24832
|
+
recordEdgeRejection(type, reason, at) {
|
|
24833
|
+
try {
|
|
24834
|
+
this.db.prepare(
|
|
24835
|
+
`INSERT INTO edge_rejections (type, count, last_at, sample) VALUES (?, 1, ?, ?)
|
|
24836
|
+
ON CONFLICT(type) DO UPDATE SET
|
|
24837
|
+
count = count + 1, last_at = excluded.last_at, sample = excluded.sample`
|
|
24838
|
+
).run(type, at, reason.slice(0, 200));
|
|
24839
|
+
} catch {
|
|
24840
|
+
}
|
|
24841
|
+
}
|
|
24842
|
+
/** Refusals recorded by the ontology gate, per edge type, newest activity first.
|
|
24843
|
+
* Durable across restarts and readable from any process (see the table note). */
|
|
24844
|
+
edgeRejections() {
|
|
24845
|
+
try {
|
|
24846
|
+
return this.db.prepare(
|
|
24847
|
+
"SELECT type, count, last_at AS lastAt, sample FROM edge_rejections ORDER BY count DESC, last_at DESC"
|
|
24848
|
+
).all();
|
|
24849
|
+
} catch {
|
|
24850
|
+
return [];
|
|
24851
|
+
}
|
|
24852
|
+
}
|
|
24853
|
+
/** Drop ledger entries whose last refusal predates `cutoff`. A still-misbehaving
|
|
24854
|
+
* producer keeps refreshing `last_at` and survives; a fixed one fades out. */
|
|
24855
|
+
pruneEdgeRejections(cutoff) {
|
|
24856
|
+
try {
|
|
24857
|
+
this.db.prepare("DELETE FROM edge_rejections WHERE last_at < ?").run(cutoff);
|
|
24858
|
+
} catch {
|
|
24859
|
+
}
|
|
24860
|
+
}
|
|
24861
|
+
/** Clear the ledger outright, whole or per type — operator escape hatch. */
|
|
24862
|
+
clearEdgeRejections(type) {
|
|
24863
|
+
try {
|
|
24864
|
+
if (type) this.db.prepare("DELETE FROM edge_rejections WHERE type = ?").run(type);
|
|
24865
|
+
else this.db.exec("DELETE FROM edge_rejections");
|
|
24866
|
+
} catch {
|
|
24573
24867
|
}
|
|
24574
|
-
|
|
24575
|
-
|
|
24868
|
+
}
|
|
24869
|
+
edgeRuleViolation(edge) {
|
|
24870
|
+
return localEdgeViolation(
|
|
24871
|
+
this.getNode(edge.from)?.label,
|
|
24872
|
+
edge.type,
|
|
24873
|
+
this.getNode(edge.to)?.label
|
|
24874
|
+
);
|
|
24576
24875
|
}
|
|
24577
24876
|
updateEdge(id, patch) {
|
|
24578
24877
|
this.stmts.updateEdge.run({
|
|
@@ -24709,6 +25008,163 @@ var SqliteGraphStore = class {
|
|
|
24709
25008
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
24710
25009
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
24711
25010
|
}
|
|
25011
|
+
getMeta(key) {
|
|
25012
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
25013
|
+
return r?.value ?? null;
|
|
25014
|
+
}
|
|
25015
|
+
setMeta(key, value) {
|
|
25016
|
+
this.db.prepare(
|
|
25017
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
25018
|
+
).run({ key, value });
|
|
25019
|
+
}
|
|
25020
|
+
dirtyNodeIdsSince(ts) {
|
|
25021
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
25022
|
+
return rows.map((r) => r.id);
|
|
25023
|
+
}
|
|
25024
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
25025
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
25026
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
25027
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
25028
|
+
edgeEndpointsTouchedSince(ts) {
|
|
25029
|
+
const rows = this.db.prepare(
|
|
25030
|
+
`SELECT from_id, to_id FROM edges
|
|
25031
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
25032
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
25033
|
+
).all({ ts });
|
|
25034
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
25035
|
+
}
|
|
25036
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
25037
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
25038
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
25039
|
+
outEdgesForMany(ids) {
|
|
25040
|
+
return this.edgesForMany(ids, "from_id");
|
|
25041
|
+
}
|
|
25042
|
+
inEdgesForMany(ids) {
|
|
25043
|
+
return this.edgesForMany(ids, "to_id");
|
|
25044
|
+
}
|
|
25045
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
25046
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
25047
|
+
ranksForMany(ids) {
|
|
25048
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
25049
|
+
const CHUNK = 400;
|
|
25050
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25051
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
25052
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
25053
|
+
const rows = this.db.prepare(
|
|
25054
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
25055
|
+
).all(...chunk);
|
|
25056
|
+
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
25057
|
+
}
|
|
25058
|
+
return out2;
|
|
25059
|
+
}
|
|
25060
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
25061
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
25062
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
25063
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
25064
|
+
* scanLiveNodes materialization. */
|
|
25065
|
+
landmarkSweepRows() {
|
|
25066
|
+
const rows = this.db.prepare(
|
|
25067
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
25068
|
+
WHERE valid_to IS NULL
|
|
25069
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
25070
|
+
).all();
|
|
25071
|
+
return rows.map((r) => ({
|
|
25072
|
+
id: r.id,
|
|
25073
|
+
label: r.label,
|
|
25074
|
+
pageRank: r.page_rank,
|
|
25075
|
+
isLandmark: r.is_landmark === 1,
|
|
25076
|
+
memoryTier: r.memory_tier,
|
|
25077
|
+
extractionSource: r.extraction_source
|
|
25078
|
+
}));
|
|
25079
|
+
}
|
|
25080
|
+
edgesForMany(ids, col) {
|
|
25081
|
+
const out2 = [];
|
|
25082
|
+
const CHUNK = 400;
|
|
25083
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25084
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
25085
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
25086
|
+
const rows = this.db.prepare(
|
|
25087
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
25088
|
+
).all(...chunk);
|
|
25089
|
+
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
25090
|
+
}
|
|
25091
|
+
return out2;
|
|
25092
|
+
}
|
|
25093
|
+
checkpointWal() {
|
|
25094
|
+
try {
|
|
25095
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
25096
|
+
return r ?? null;
|
|
25097
|
+
} catch {
|
|
25098
|
+
return null;
|
|
25099
|
+
}
|
|
25100
|
+
}
|
|
25101
|
+
reviveReobserved(relPaths, workspaceId, since) {
|
|
25102
|
+
let revived = 0;
|
|
25103
|
+
const CHUNK = 400;
|
|
25104
|
+
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
25105
|
+
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
25106
|
+
if (slice.length === 0) continue;
|
|
25107
|
+
const r = this.db.prepare(
|
|
25108
|
+
`UPDATE nodes SET valid_to = NULL
|
|
25109
|
+
WHERE valid_to IS NOT NULL
|
|
25110
|
+
AND last_updated_at >= ?
|
|
25111
|
+
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
25112
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
|
|
25113
|
+
).run(since, workspaceId, ...slice);
|
|
25114
|
+
revived += Number(r.changes);
|
|
25115
|
+
}
|
|
25116
|
+
if (revived > 0) this.mutations++;
|
|
25117
|
+
return revived;
|
|
25118
|
+
}
|
|
25119
|
+
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
25120
|
+
const r = this.db.prepare(
|
|
25121
|
+
`SELECT COUNT(*) AS total,
|
|
25122
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
25123
|
+
FROM (SELECT attrs_json FROM edges
|
|
25124
|
+
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
25125
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
25126
|
+
).get(attr, `%${marker}%`, limit);
|
|
25127
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
25128
|
+
}
|
|
25129
|
+
liveNodeIds(ids) {
|
|
25130
|
+
const live = /* @__PURE__ */ new Set();
|
|
25131
|
+
const CHUNK = 900;
|
|
25132
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25133
|
+
const slice = ids.slice(i2, i2 + CHUNK);
|
|
25134
|
+
if (slice.length === 0) continue;
|
|
25135
|
+
const rows = this.db.prepare(
|
|
25136
|
+
`SELECT id FROM nodes WHERE valid_to IS NULL AND id IN (${slice.map(() => "?").join(",")})`
|
|
25137
|
+
).all(...slice);
|
|
25138
|
+
for (const r of rows) live.add(r.id);
|
|
25139
|
+
}
|
|
25140
|
+
return live;
|
|
25141
|
+
}
|
|
25142
|
+
/**
|
|
25143
|
+
* Live edges WITH their endpoint labels and ids, resolved in ONE join.
|
|
25144
|
+
*
|
|
25145
|
+
* The ontology sweep needs (id, type, fromLabel, toLabel) for every live edge.
|
|
25146
|
+
* Doing that as `scanLiveEdges()` + two `getNode()` calls is 2N node reads —
|
|
25147
|
+
* 550,000 on this store — and each one deserializes the node's embedding blob.
|
|
25148
|
+
* Measured: the sweep did not finish in 10 minutes. As a single join it is one
|
|
25149
|
+
* query over an index-covered scan. Labels only; nothing here touches embeddings.
|
|
25150
|
+
*/
|
|
25151
|
+
scanLiveEdgeRows() {
|
|
25152
|
+
const rows = this.db.prepare(
|
|
25153
|
+
`SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
|
|
25154
|
+
FROM edges e
|
|
25155
|
+
LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
|
|
25156
|
+
LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
|
|
25157
|
+
WHERE e.valid_to IS NULL`
|
|
25158
|
+
).all();
|
|
25159
|
+
return rows.map((r) => ({
|
|
25160
|
+
id: r.id,
|
|
25161
|
+
from: r.from_id,
|
|
25162
|
+
to: r.to_id,
|
|
25163
|
+
type: r.type,
|
|
25164
|
+
fromLabel: r.from_label ?? void 0,
|
|
25165
|
+
toLabel: r.to_label ?? void 0
|
|
25166
|
+
}));
|
|
25167
|
+
}
|
|
24712
25168
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
24713
25169
|
* expression index so the incremental reindex fetches only the changed files'
|
|
24714
25170
|
* symbols instead of scanning every versioned node. */
|
|
@@ -24729,6 +25185,10 @@ var SqliteGraphStore = class {
|
|
|
24729
25185
|
if (!live) return null;
|
|
24730
25186
|
const version2 = live.version ?? 1;
|
|
24731
25187
|
const frozenId = `${liveId}@v${version2}`;
|
|
25188
|
+
if (this.getNode(frozenId)) {
|
|
25189
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
25190
|
+
return frozenId;
|
|
25191
|
+
}
|
|
24732
25192
|
this.stmts.freezeCopy.run({ frozen_id: frozenId, live_id: liveId, t });
|
|
24733
25193
|
this.mergeEdge({
|
|
24734
25194
|
id: `edge_superseded_${frozenId}`,
|
|
@@ -24787,6 +25247,7 @@ var CAUSAL_FAMILY = [
|
|
|
24787
25247
|
|
|
24788
25248
|
// ../../packages/local-graph/src/justification.ts
|
|
24789
25249
|
init_src();
|
|
25250
|
+
init_src2();
|
|
24790
25251
|
function addDependency(store2, opts) {
|
|
24791
25252
|
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
24792
25253
|
const attrs = { relation: opts.relation };
|
|
@@ -24809,13 +25270,72 @@ function addDependency(store2, opts) {
|
|
|
24809
25270
|
store2.mergeEdge(edge);
|
|
24810
25271
|
return edge;
|
|
24811
25272
|
}
|
|
24812
|
-
function markRevisit(store2, node, reason, ts) {
|
|
25273
|
+
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
24813
25274
|
const fresh = store2.getNode(node.id) ?? node;
|
|
24814
25275
|
store2.updateNode(node.id, {
|
|
24815
|
-
attrs: {
|
|
25276
|
+
attrs: {
|
|
25277
|
+
...fresh.attrs,
|
|
25278
|
+
revisit: true,
|
|
25279
|
+
revisitReason: reason,
|
|
25280
|
+
revisitSinceTs: ts,
|
|
25281
|
+
revisitAnsweredWhen: answeredWhen
|
|
25282
|
+
},
|
|
24816
25283
|
lastUpdatedAt: ts
|
|
24817
25284
|
});
|
|
24818
25285
|
}
|
|
25286
|
+
var LEGACY_AUTO_CLOSE_ASK = "auto-closed off a pre-provenance anchor";
|
|
25287
|
+
var KNOWN_CONDITIONS = {
|
|
25288
|
+
parentProblemOpen: true,
|
|
25289
|
+
dependentStale: true
|
|
25290
|
+
};
|
|
25291
|
+
function isRevisitCondition(v) {
|
|
25292
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(KNOWN_CONDITIONS, v);
|
|
25293
|
+
}
|
|
25294
|
+
function conditionOf(node) {
|
|
25295
|
+
const explicit = node.attrs["revisitAnsweredWhen"];
|
|
25296
|
+
if (isRevisitCondition(explicit)) return explicit;
|
|
25297
|
+
if (String(node.attrs["revisitReason"] ?? "").startsWith(LEGACY_AUTO_CLOSE_ASK)) {
|
|
25298
|
+
return "parentProblemOpen";
|
|
25299
|
+
}
|
|
25300
|
+
return void 0;
|
|
25301
|
+
}
|
|
25302
|
+
function clearAnsweredRevisits(store2, ts) {
|
|
25303
|
+
const report = { cleared: 0, standing: 0 };
|
|
25304
|
+
for (const label of SEMANTIC_NODE_LABELS) {
|
|
25305
|
+
for (const node of store2.findNodesByLabel(label)) {
|
|
25306
|
+
if (node.attrs["revisit"] !== true) continue;
|
|
25307
|
+
const condition = conditionOf(node);
|
|
25308
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
25309
|
+
report.standing++;
|
|
25310
|
+
continue;
|
|
25311
|
+
}
|
|
25312
|
+
clearRevisit(store2, node.id, ts);
|
|
25313
|
+
report.cleared++;
|
|
25314
|
+
}
|
|
25315
|
+
}
|
|
25316
|
+
return report;
|
|
25317
|
+
}
|
|
25318
|
+
function isRevisitAnswered(store2, node, condition) {
|
|
25319
|
+
switch (condition) {
|
|
25320
|
+
case "parentProblemOpen": {
|
|
25321
|
+
const parents = store2.inEdges(node.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.from)).filter((n) => n !== null && n.validTo == null);
|
|
25322
|
+
return parents.every((p) => p.attrs["resolvedAt"] == null);
|
|
25323
|
+
}
|
|
25324
|
+
case "dependentStale": {
|
|
25325
|
+
const since = Number(node.attrs["revisitSinceTs"] ?? 0);
|
|
25326
|
+
return since > 0 && node.lastUpdatedAt > since;
|
|
25327
|
+
}
|
|
25328
|
+
}
|
|
25329
|
+
}
|
|
25330
|
+
function clearRevisit(store2, nodeId, ts) {
|
|
25331
|
+
const n = store2.getNode(nodeId);
|
|
25332
|
+
if (!n) return;
|
|
25333
|
+
const attrs = { ...n.attrs };
|
|
25334
|
+
delete attrs["revisit"];
|
|
25335
|
+
delete attrs["revisitReason"];
|
|
25336
|
+
delete attrs["revisitSinceTs"];
|
|
25337
|
+
store2.updateNode(nodeId, { attrs, lastUpdatedAt: ts });
|
|
25338
|
+
}
|
|
24819
25339
|
function listNeedsRevisit(store2, asOf) {
|
|
24820
25340
|
return store2.nodesAsOf(asOf).filter((n) => n.attrs["revisit"] === true).map((n) => ({
|
|
24821
25341
|
id: n.id,
|
|
@@ -24948,7 +25468,6 @@ function markBoth(store2, a, b, patch, ts) {
|
|
|
24948
25468
|
// ../../packages/local-graph/src/design-problem.ts
|
|
24949
25469
|
init_src();
|
|
24950
25470
|
init_src();
|
|
24951
|
-
init_src2();
|
|
24952
25471
|
|
|
24953
25472
|
// ../../packages/local-graph/src/problem-package-link.ts
|
|
24954
25473
|
init_src();
|
|
@@ -25134,50 +25653,113 @@ function backfillProblemContext(store2, ts) {
|
|
|
25134
25653
|
}
|
|
25135
25654
|
|
|
25136
25655
|
// ../../packages/local-graph/src/design-problem.ts
|
|
25137
|
-
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
pageRank: 0,
|
|
25153
|
-
isLandmark: false,
|
|
25154
|
-
community: null,
|
|
25155
|
-
stability: "unstable",
|
|
25156
|
-
attrs
|
|
25157
|
-
};
|
|
25656
|
+
init_src2();
|
|
25657
|
+
|
|
25658
|
+
// ../../packages/local-graph/src/problem-dedup.ts
|
|
25659
|
+
init_src();
|
|
25660
|
+
init_src();
|
|
25661
|
+
var REDIRECT_EDGES = [
|
|
25662
|
+
"CAUSED_BY",
|
|
25663
|
+
"SOLVED_BY",
|
|
25664
|
+
"FIXED_BY",
|
|
25665
|
+
"ANCHORED_AT",
|
|
25666
|
+
"MANIFESTED_IN",
|
|
25667
|
+
"EVIDENCED_BY"
|
|
25668
|
+
];
|
|
25669
|
+
function corroborations(n) {
|
|
25670
|
+
return Number(n.attrs["corroborations"] ?? 0);
|
|
25158
25671
|
}
|
|
25159
|
-
function
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25672
|
+
function overlap(a, b) {
|
|
25673
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
25674
|
+
let inter = 0;
|
|
25675
|
+
for (const x of a) if (b.has(x)) inter++;
|
|
25676
|
+
return inter / Math.min(a.size, b.size);
|
|
25677
|
+
}
|
|
25678
|
+
function mergeDuplicateProblems(store2, opts) {
|
|
25679
|
+
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25680
|
+
const minTokens = opts.minTokens ?? 4;
|
|
25681
|
+
const report = { clusters: 0, merged: 0 };
|
|
25682
|
+
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25683
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
25684
|
+
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
25685
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
25686
|
+
store2.transaction(() => {
|
|
25687
|
+
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25688
|
+
const a = open[i2];
|
|
25689
|
+
if (consumed.has(a.id)) continue;
|
|
25690
|
+
const cluster = [a];
|
|
25691
|
+
const ta = tokens.get(a.id);
|
|
25692
|
+
for (let j = i2 + 1; j < open.length; j++) {
|
|
25693
|
+
const b = open[j];
|
|
25694
|
+
if (consumed.has(b.id)) continue;
|
|
25695
|
+
const tb = tokens.get(b.id);
|
|
25696
|
+
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25697
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
25698
|
+
if (overlap(ta, tb) >= minOverlap) {
|
|
25699
|
+
cluster.push(b);
|
|
25700
|
+
consumed.add(b.id);
|
|
25701
|
+
}
|
|
25702
|
+
}
|
|
25703
|
+
if (cluster.length < 2) continue;
|
|
25704
|
+
report.clusters++;
|
|
25705
|
+
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25706
|
+
const survivor = cluster[0];
|
|
25707
|
+
for (const dup of cluster.slice(1)) {
|
|
25708
|
+
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25709
|
+
report.merged++;
|
|
25710
|
+
}
|
|
25711
|
+
}
|
|
25172
25712
|
});
|
|
25713
|
+
return report;
|
|
25173
25714
|
}
|
|
25174
|
-
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
"
|
|
25178
|
-
"
|
|
25715
|
+
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25716
|
+
const surv = store2.getNode(survivor.id);
|
|
25717
|
+
if (!surv) return;
|
|
25718
|
+
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25719
|
+
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25720
|
+
store2.updateNode(survivor.id, {
|
|
25721
|
+
attrs: {
|
|
25722
|
+
...surv.attrs,
|
|
25723
|
+
sources: [...sources],
|
|
25724
|
+
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25725
|
+
},
|
|
25726
|
+
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25727
|
+
lastUpdatedAt: ts
|
|
25728
|
+
});
|
|
25729
|
+
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25730
|
+
if (e.to === survivor.id) continue;
|
|
25731
|
+
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25732
|
+
if (store2.getEdge(id)) continue;
|
|
25733
|
+
const redirected = {
|
|
25734
|
+
...e,
|
|
25735
|
+
id,
|
|
25736
|
+
from: survivor.id,
|
|
25737
|
+
createdAt: ts,
|
|
25738
|
+
lastSeenAt: ts
|
|
25739
|
+
};
|
|
25740
|
+
store2.mergeEdge(redirected);
|
|
25741
|
+
}
|
|
25742
|
+
store2.updateNode(dup.id, {
|
|
25743
|
+
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25744
|
+
lastUpdatedAt: ts
|
|
25745
|
+
});
|
|
25746
|
+
store2.closeNode(dup.id, ts);
|
|
25747
|
+
}
|
|
25748
|
+
|
|
25749
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
25750
|
+
function isConstraintProblem(node) {
|
|
25751
|
+
return node.attrs["kind"] === "constraint";
|
|
25752
|
+
}
|
|
25753
|
+
var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
|
|
25754
|
+
"Solution",
|
|
25755
|
+
"RootCause",
|
|
25756
|
+
"Pattern",
|
|
25757
|
+
"Technique",
|
|
25179
25758
|
"AntiPattern"
|
|
25180
25759
|
]);
|
|
25760
|
+
function isAutoMinted(n) {
|
|
25761
|
+
return n.label === "Solution" && String(n.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25762
|
+
}
|
|
25181
25763
|
function priorsForFile(store2, relPath) {
|
|
25182
25764
|
const want = relPath.trim().replace(/^\.?\//, "");
|
|
25183
25765
|
let file2 = null;
|
|
@@ -25189,35 +25771,53 @@ function priorsForFile(store2, relPath) {
|
|
|
25189
25771
|
}
|
|
25190
25772
|
if (!file2) return null;
|
|
25191
25773
|
const openProblems = [];
|
|
25774
|
+
const constraints = [];
|
|
25775
|
+
const resolvedProblems = [];
|
|
25192
25776
|
const seenProblem = /* @__PURE__ */ new Set();
|
|
25193
25777
|
const related = /* @__PURE__ */ new Map();
|
|
25194
25778
|
for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
25195
25779
|
const n = store2.getNode(e.from);
|
|
25196
25780
|
if (!n) continue;
|
|
25197
25781
|
if (n.label === "Problem") {
|
|
25198
|
-
if (
|
|
25199
|
-
|
|
25200
|
-
|
|
25201
|
-
|
|
25202
|
-
|
|
25782
|
+
if (seenProblem.has(n.id)) continue;
|
|
25783
|
+
seenProblem.add(n.id);
|
|
25784
|
+
if (!n.attrs["resolvedAt"]) {
|
|
25785
|
+
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
25786
|
+
} else if (n.attrs["resolvedAs"] == null && n.attrs["mergedInto"] === void 0 && e.attrs?.["anchorProvenance"] !== "legacy") {
|
|
25787
|
+
resolvedProblems.push(n);
|
|
25788
|
+
}
|
|
25789
|
+
} else if (CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) {
|
|
25203
25790
|
related.set(n.id, n);
|
|
25204
25791
|
}
|
|
25205
25792
|
}
|
|
25206
25793
|
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
25207
|
-
for (const p of openProblems) {
|
|
25794
|
+
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
25208
25795
|
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
25209
25796
|
const n = store2.getNode(e.to);
|
|
25210
|
-
if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
|
|
25211
|
-
if (n && n.label === "Solution" && e.type === "SOLVED_BY") {
|
|
25797
|
+
if (n && CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) related.set(n.id, n);
|
|
25798
|
+
if (n && n.label === "Solution" && e.type === "SOLVED_BY" && !isAutoMinted(n)) {
|
|
25212
25799
|
const list = solutionsByProblem.get(p.id) ?? [];
|
|
25213
25800
|
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
25214
25801
|
solutionsByProblem.set(p.id, list);
|
|
25215
25802
|
}
|
|
25216
25803
|
}
|
|
25217
25804
|
}
|
|
25218
|
-
if (openProblems.length === 0 && related.size === 0)
|
|
25219
|
-
|
|
25220
|
-
|
|
25805
|
+
if (openProblems.length === 0 && constraints.length === 0 && resolvedProblems.length === 0 && related.size === 0)
|
|
25806
|
+
return null;
|
|
25807
|
+
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
25808
|
+
openProblems.sort(recentFirst);
|
|
25809
|
+
constraints.sort(recentFirst);
|
|
25810
|
+
resolvedProblems.sort(
|
|
25811
|
+
(a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)
|
|
25812
|
+
);
|
|
25813
|
+
return {
|
|
25814
|
+
file: file2,
|
|
25815
|
+
openProblems,
|
|
25816
|
+
constraints,
|
|
25817
|
+
resolvedProblems,
|
|
25818
|
+
related: [...related.values()],
|
|
25819
|
+
solutionsByProblem
|
|
25820
|
+
};
|
|
25221
25821
|
}
|
|
25222
25822
|
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
25223
25823
|
const p = store2.getNode(problemId);
|
|
@@ -25228,18 +25828,49 @@ function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
|
25228
25828
|
});
|
|
25229
25829
|
return true;
|
|
25230
25830
|
}
|
|
25231
|
-
function
|
|
25831
|
+
function reopenAutoClosedProblems(store2, t) {
|
|
25832
|
+
const report = { reopened: 0, alreadySuspect: 0, agentDescribed: 0 };
|
|
25833
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25834
|
+
if (p.attrs["resolvedAt"] == null) continue;
|
|
25835
|
+
if (p.attrs["resolvedAs"] != null) continue;
|
|
25836
|
+
if (p.attrs["mergedInto"] !== void 0) continue;
|
|
25837
|
+
if (p.attrs["resolutionWitnessed"] === true) {
|
|
25838
|
+
report.agentDescribed++;
|
|
25839
|
+
continue;
|
|
25840
|
+
}
|
|
25841
|
+
const sols = store2.outEdges(p.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.to)).filter((n) => n !== null);
|
|
25842
|
+
if (sols.length === 0) continue;
|
|
25843
|
+
if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {
|
|
25844
|
+
report.agentDescribed++;
|
|
25845
|
+
continue;
|
|
25846
|
+
}
|
|
25847
|
+
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
25848
|
+
const attrs = { ...p.attrs };
|
|
25849
|
+
delete attrs["resolvedAt"];
|
|
25850
|
+
attrs["reopenedFrom"] = "auto-close";
|
|
25851
|
+
attrs["reopenedAt"] = t;
|
|
25852
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25853
|
+
report.reopened++;
|
|
25854
|
+
}
|
|
25855
|
+
return report;
|
|
25856
|
+
}
|
|
25857
|
+
var AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
25858
|
+
function markFixCandidates(store2, t) {
|
|
25232
25859
|
let resolved = 0;
|
|
25233
25860
|
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25234
25861
|
if (!p.id.startsWith("dprob_")) continue;
|
|
25235
25862
|
if (p.attrs["resolvedAt"]) continue;
|
|
25863
|
+
if (isConstraintProblem(p)) continue;
|
|
25236
25864
|
let symName = "";
|
|
25237
25865
|
let symRelPath;
|
|
25238
25866
|
let edited = false;
|
|
25867
|
+
const since = Math.max(p.createdAt, Number(p.attrs["fixCandidateClearedAt"] ?? 0));
|
|
25239
25868
|
for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
|
|
25240
25869
|
if (e.attrs?.["mention"] === true) continue;
|
|
25870
|
+
if (e.attrs?.["anchorProvenance"] === "legacy") continue;
|
|
25241
25871
|
const sym = store2.getNode(e.to);
|
|
25242
|
-
|
|
25872
|
+
const changedAt = sym?.label === "File" ? Number(sym.attrs["contentChangedAt"] ?? 0) : sym?.lastUpdatedAt ?? 0;
|
|
25873
|
+
if (sym && changedAt > since) {
|
|
25243
25874
|
edited = true;
|
|
25244
25875
|
symName = sym.description;
|
|
25245
25876
|
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
@@ -25247,36 +25878,172 @@ function resolveDesignProblems(store2, t) {
|
|
|
25247
25878
|
}
|
|
25248
25879
|
}
|
|
25249
25880
|
if (!edited) continue;
|
|
25250
|
-
if (
|
|
25251
|
-
|
|
25252
|
-
store2.
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
// AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
|
|
25257
|
-
// just prose — the F2 join key downstream backfills and the viz read.
|
|
25258
|
-
resolvedSymbols: [symName],
|
|
25259
|
-
...symRelPath ? { resolvedRelPath: symRelPath } : {}
|
|
25260
|
-
})
|
|
25261
|
-
);
|
|
25262
|
-
mergeEdge(store2, p.id, solId, "SOLVED_BY", t);
|
|
25263
|
-
for (const ae of store2.outEdges(p.id, ["ANCHORED_AT"]))
|
|
25264
|
-
mergeEdge(store2, solId, ae.to, "ANCHORED_AT", t);
|
|
25265
|
-
}
|
|
25881
|
+
if (p.attrs["fixCandidateAt"] !== void 0) continue;
|
|
25882
|
+
if (store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25883
|
+
const s = store2.getNode(e.to);
|
|
25884
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25885
|
+
}))
|
|
25886
|
+
continue;
|
|
25266
25887
|
store2.updateNode(p.id, {
|
|
25267
|
-
attrs: {
|
|
25888
|
+
attrs: {
|
|
25889
|
+
...p.attrs,
|
|
25890
|
+
// The cue, kept as structured data so the render can name the file it is
|
|
25891
|
+
// asking about. Deliberately NOT `resolvedAt` — this is a question.
|
|
25892
|
+
fixCandidateAt: t,
|
|
25893
|
+
fixCandidateSymbol: symName,
|
|
25894
|
+
...symRelPath ? { fixCandidateFile: symRelPath } : {},
|
|
25895
|
+
// Snapshot the render-ledger counter so "shown since THIS ask" is
|
|
25896
|
+
// measurable. `shownCount` is the node's LIFETIME count across every
|
|
25897
|
+
// band, so comparing it raw would retire a fresh ask on a
|
|
25898
|
+
// frequently-surfaced problem before anyone had seen the question once.
|
|
25899
|
+
fixCandidateShownBase: Number(p.attrs["shownCount"] ?? 0)
|
|
25900
|
+
},
|
|
25268
25901
|
lastUpdatedAt: t
|
|
25269
25902
|
});
|
|
25270
25903
|
resolved++;
|
|
25271
25904
|
}
|
|
25272
25905
|
return resolved;
|
|
25273
25906
|
}
|
|
25907
|
+
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
25908
|
+
function clearSettledFixCandidates(store2, t) {
|
|
25909
|
+
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
25910
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25911
|
+
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
25912
|
+
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");
|
|
25913
|
+
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
25914
|
+
const answered = p.attrs["resolvedAt"] != null || store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25915
|
+
const s = store2.getNode(e.to);
|
|
25916
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25917
|
+
});
|
|
25918
|
+
const base = Number(
|
|
25919
|
+
p.attrs["fixCandidateShownBase"] ?? p.attrs["shownCount"] ?? 0
|
|
25920
|
+
);
|
|
25921
|
+
const shownSinceAsk = Number(p.attrs["shownCount"] ?? 0) - base;
|
|
25922
|
+
const ignored = shownSinceAsk >= FIX_CANDIDATE_ASK_LIMIT;
|
|
25923
|
+
if (!answered && !ignored && !unsubstantiated) {
|
|
25924
|
+
report.standing++;
|
|
25925
|
+
continue;
|
|
25926
|
+
}
|
|
25927
|
+
const attrs = { ...p.attrs };
|
|
25928
|
+
delete attrs["fixCandidateAt"];
|
|
25929
|
+
delete attrs["fixCandidateSymbol"];
|
|
25930
|
+
delete attrs["fixCandidateFile"];
|
|
25931
|
+
attrs["fixCandidateClearedAt"] = t;
|
|
25932
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25933
|
+
if (answered) report.answered++;
|
|
25934
|
+
else if (ignored) report.ignored++;
|
|
25935
|
+
else report.unsubstantiated++;
|
|
25936
|
+
}
|
|
25937
|
+
return report;
|
|
25938
|
+
}
|
|
25939
|
+
|
|
25940
|
+
// ../../packages/local-graph/src/intent.ts
|
|
25941
|
+
init_src();
|
|
25274
25942
|
|
|
25275
25943
|
// ../../packages/local-graph/src/anchor-backfill.ts
|
|
25276
|
-
|
|
25944
|
+
init_src();
|
|
25945
|
+
init_src2();
|
|
25277
25946
|
function isLegacyAnchor(attrs) {
|
|
25278
25947
|
return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
|
|
25279
25948
|
}
|
|
25949
|
+
var STATEMENT_PATH_RE = new RegExp(
|
|
25950
|
+
String.raw`(?:[\w.+-]+\/)+[\w.+-]+\.(?:${anchorableExtensionAlternation()})`,
|
|
25951
|
+
"g"
|
|
25952
|
+
);
|
|
25953
|
+
function repairLegacyAnchorsFromStatement(store2, ts) {
|
|
25954
|
+
const report = {
|
|
25955
|
+
repaired: [],
|
|
25956
|
+
retiredGuesses: 0,
|
|
25957
|
+
noPathNamed: 0,
|
|
25958
|
+
pathUnknown: 0,
|
|
25959
|
+
alreadyCorrect: 0
|
|
25960
|
+
};
|
|
25961
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
25962
|
+
for (const f of store2.findNodesByLabel("File")) {
|
|
25963
|
+
const path = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
25964
|
+
if (path) byPath.set(path, { id: f.id, path });
|
|
25965
|
+
}
|
|
25966
|
+
const resolvePath = (named) => {
|
|
25967
|
+
const exact = byPath.get(named);
|
|
25968
|
+
if (exact) return exact;
|
|
25969
|
+
const hits = [...byPath.values()].filter(
|
|
25970
|
+
(f) => f.path.endsWith(`/${named}`) || named.endsWith(`/${f.path}`)
|
|
25971
|
+
);
|
|
25972
|
+
return hits.length === 1 ? hits[0] : null;
|
|
25973
|
+
};
|
|
25974
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25975
|
+
try {
|
|
25976
|
+
const anchors = store2.outEdges(p.id, ["ANCHORED_AT"]);
|
|
25977
|
+
const legacy = anchors.filter((e) => e.attrs?.["anchorProvenance"] === "legacy");
|
|
25978
|
+
if (legacy.length === 0) continue;
|
|
25979
|
+
const better = anchors.filter((e) => {
|
|
25980
|
+
const prov = e.attrs?.["anchorProvenance"];
|
|
25981
|
+
return prov === "restated" || prov === "witnessed" || prov === "edited";
|
|
25982
|
+
});
|
|
25983
|
+
if (better.length > 0) {
|
|
25984
|
+
const betterTargets = new Set(better.map((e) => e.to));
|
|
25985
|
+
let retired = 0;
|
|
25986
|
+
for (const e of legacy) {
|
|
25987
|
+
if (!betterTargets.has(e.to)) {
|
|
25988
|
+
store2.closeEdge(e.id, ts);
|
|
25989
|
+
retired++;
|
|
25990
|
+
}
|
|
25991
|
+
}
|
|
25992
|
+
report.retiredGuesses += retired;
|
|
25993
|
+
continue;
|
|
25994
|
+
}
|
|
25995
|
+
const named = [...String(p.description ?? "").matchAll(STATEMENT_PATH_RE)].map((m) => m[0]);
|
|
25996
|
+
if (named.length === 0) {
|
|
25997
|
+
report.noPathNamed++;
|
|
25998
|
+
continue;
|
|
25999
|
+
}
|
|
26000
|
+
const guessedPaths = legacy.map((e) => {
|
|
26001
|
+
const f = store2.getNode(e.to);
|
|
26002
|
+
return String(f?.attrs["relPath"] ?? f?.description ?? "");
|
|
26003
|
+
});
|
|
26004
|
+
if (named.some((x) => guessedPaths.some((g) => g.endsWith(x) || x.endsWith(g)))) {
|
|
26005
|
+
report.alreadyCorrect++;
|
|
26006
|
+
continue;
|
|
26007
|
+
}
|
|
26008
|
+
const target = named.map(resolvePath).find((f) => f !== null);
|
|
26009
|
+
if (!target) {
|
|
26010
|
+
report.pathUnknown++;
|
|
26011
|
+
continue;
|
|
26012
|
+
}
|
|
26013
|
+
store2.mergeEdge({
|
|
26014
|
+
id: `edge_${digest({ from: p.id, type: "ANCHORED_AT", to: target.id })}`.slice(0, 24),
|
|
26015
|
+
from: p.id,
|
|
26016
|
+
to: target.id,
|
|
26017
|
+
type: "ANCHORED_AT",
|
|
26018
|
+
// Same confidence a capture-time anchor enters at: the source is the
|
|
26019
|
+
// agent's own statement either way, only the moment of reading differs.
|
|
26020
|
+
confidence: 0.4,
|
|
26021
|
+
extractionSource: "agent-observed",
|
|
26022
|
+
createdAt: ts,
|
|
26023
|
+
lastSeenAt: ts,
|
|
26024
|
+
navSuccesses: 0,
|
|
26025
|
+
navFailures: 0,
|
|
26026
|
+
attrs: {
|
|
26027
|
+
anchorProvenance: "restated",
|
|
26028
|
+
restatedFrom: guessedPaths[0] ?? "",
|
|
26029
|
+
restatedAt: ts
|
|
26030
|
+
}
|
|
26031
|
+
});
|
|
26032
|
+
for (const e of legacy) {
|
|
26033
|
+
if (e.to !== target.id) store2.closeEdge(e.id, ts);
|
|
26034
|
+
}
|
|
26035
|
+
report.retiredGuesses += legacy.filter((e) => e.to !== target.id).length;
|
|
26036
|
+
report.repaired.push({
|
|
26037
|
+
problemId: p.id,
|
|
26038
|
+
problem: p.description,
|
|
26039
|
+
guessed: guessedPaths[0] ?? "",
|
|
26040
|
+
restated: target.path
|
|
26041
|
+
});
|
|
26042
|
+
} catch {
|
|
26043
|
+
}
|
|
26044
|
+
}
|
|
26045
|
+
return report;
|
|
26046
|
+
}
|
|
25280
26047
|
function backfillLegacyAnchors(store2, ts) {
|
|
25281
26048
|
const report = {
|
|
25282
26049
|
demotedEdges: 0,
|
|
@@ -25316,7 +26083,11 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
25316
26083
|
store2,
|
|
25317
26084
|
sol,
|
|
25318
26085
|
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
25319
|
-
ts
|
|
26086
|
+
ts,
|
|
26087
|
+
// "or reopen it" is half the ask, and the nightly reopen takes that
|
|
26088
|
+
// branch — so record what would answer this, or the flag can never be
|
|
26089
|
+
// lowered and the band fills with settled questions.
|
|
26090
|
+
"parentProblemOpen"
|
|
25320
26091
|
);
|
|
25321
26092
|
store2.updateNode(p.id, {
|
|
25322
26093
|
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
@@ -25350,7 +26121,7 @@ function pagerank(input) {
|
|
|
25350
26121
|
const ids = input.nodeIds;
|
|
25351
26122
|
const n = ids.length;
|
|
25352
26123
|
if (n === 0) {
|
|
25353
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26124
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25354
26125
|
}
|
|
25355
26126
|
const index = /* @__PURE__ */ new Map();
|
|
25356
26127
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -25422,8 +26193,12 @@ function pagerank(input) {
|
|
|
25422
26193
|
}
|
|
25423
26194
|
}
|
|
25424
26195
|
const scores = /* @__PURE__ */ new Map();
|
|
25425
|
-
|
|
25426
|
-
|
|
26196
|
+
let danglingRank = 0;
|
|
26197
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26198
|
+
scores.set(ids[i2], score2[i2]);
|
|
26199
|
+
if (dangling[i2]) danglingRank += score2[i2];
|
|
26200
|
+
}
|
|
26201
|
+
return { scores, iterations: iter, converged, danglingRank };
|
|
25427
26202
|
}
|
|
25428
26203
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
25429
26204
|
const entries = [...scores.entries()];
|
|
@@ -25433,7 +26208,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
25433
26208
|
}
|
|
25434
26209
|
}
|
|
25435
26210
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
25436
|
-
entries.sort((a, b) => b[1] - a[1]);
|
|
26211
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
25437
26212
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
25438
26213
|
const out2 = /* @__PURE__ */ new Set();
|
|
25439
26214
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -25671,8 +26446,86 @@ function motifDecision(input) {
|
|
|
25671
26446
|
};
|
|
25672
26447
|
}
|
|
25673
26448
|
|
|
25674
|
-
// ../../packages/
|
|
25675
|
-
|
|
26449
|
+
// ../../packages/math/src/pagerank-local.ts
|
|
26450
|
+
function pagerankLocal(input) {
|
|
26451
|
+
const damping = input.damping ?? 0.85;
|
|
26452
|
+
const tol = input.tolerance ?? 1e-6;
|
|
26453
|
+
const maxIter = input.maxIterations ?? 100;
|
|
26454
|
+
const ids = input.regionIds;
|
|
26455
|
+
const n = ids.length;
|
|
26456
|
+
if (n === 0 || input.globalN === 0) {
|
|
26457
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26458
|
+
}
|
|
26459
|
+
const index = /* @__PURE__ */ new Map();
|
|
26460
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
26461
|
+
const base = (1 - damping) / input.globalN;
|
|
26462
|
+
const outSum = new Float64Array(n);
|
|
26463
|
+
const inflow = new Float64Array(n);
|
|
26464
|
+
let score2 = new Float64Array(n);
|
|
26465
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26466
|
+
const id = ids[i2];
|
|
26467
|
+
outSum[i2] = input.outSum.get(id) ?? 0;
|
|
26468
|
+
inflow[i2] = input.boundaryInflow.get(id) ?? 0;
|
|
26469
|
+
score2[i2] = input.rank0.get(id) ?? base;
|
|
26470
|
+
}
|
|
26471
|
+
const rowLen = new Int32Array(n);
|
|
26472
|
+
let edgeCount = 0;
|
|
26473
|
+
for (const [from, edges] of input.out) {
|
|
26474
|
+
const fi = index.get(from);
|
|
26475
|
+
if (fi === void 0) continue;
|
|
26476
|
+
let inSet = 0;
|
|
26477
|
+
for (const e of edges) if (index.has(e.to)) inSet++;
|
|
26478
|
+
rowLen[fi] = inSet;
|
|
26479
|
+
edgeCount += inSet;
|
|
26480
|
+
}
|
|
26481
|
+
const rowStart = new Int32Array(n + 1);
|
|
26482
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
26483
|
+
const colIdx = new Int32Array(edgeCount);
|
|
26484
|
+
const colW = new Float64Array(edgeCount);
|
|
26485
|
+
const cursor = rowStart.slice(0, n);
|
|
26486
|
+
for (const [from, edges] of input.out) {
|
|
26487
|
+
const fi = index.get(from);
|
|
26488
|
+
if (fi === void 0) continue;
|
|
26489
|
+
for (const e of edges) {
|
|
26490
|
+
const ti = index.get(e.to);
|
|
26491
|
+
if (ti === void 0) continue;
|
|
26492
|
+
const c = cursor[fi];
|
|
26493
|
+
cursor[fi] = c + 1;
|
|
26494
|
+
colIdx[c] = ti;
|
|
26495
|
+
colW[c] = e.weight;
|
|
26496
|
+
}
|
|
26497
|
+
}
|
|
26498
|
+
const externalDangling = input.externalDanglingRank ?? 0;
|
|
26499
|
+
let next = new Float64Array(n);
|
|
26500
|
+
let iter = 0;
|
|
26501
|
+
let converged = false;
|
|
26502
|
+
for (; iter < maxIter; iter++) {
|
|
26503
|
+
let internalDangling = 0;
|
|
26504
|
+
for (let i2 = 0; i2 < n; i2++) if (outSum[i2] <= 0) internalDangling += score2[i2];
|
|
26505
|
+
const danglingShare = damping * (internalDangling + externalDangling) / input.globalN;
|
|
26506
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] = base + danglingShare + damping * inflow[i2];
|
|
26507
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26508
|
+
const os2 = outSum[i2];
|
|
26509
|
+
if (os2 <= 0) continue;
|
|
26510
|
+
const f = damping * score2[i2] / os2;
|
|
26511
|
+
const end = rowStart[i2 + 1];
|
|
26512
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
26513
|
+
}
|
|
26514
|
+
let diff = 0;
|
|
26515
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
26516
|
+
const tmp = score2;
|
|
26517
|
+
score2 = next;
|
|
26518
|
+
next = tmp;
|
|
26519
|
+
if (diff < tol) {
|
|
26520
|
+
iter++;
|
|
26521
|
+
converged = true;
|
|
26522
|
+
break;
|
|
26523
|
+
}
|
|
26524
|
+
}
|
|
26525
|
+
const scores = /* @__PURE__ */ new Map();
|
|
26526
|
+
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
26527
|
+
return { scores, iterations: iter, converged };
|
|
26528
|
+
}
|
|
25676
26529
|
|
|
25677
26530
|
// ../../packages/local-graph/src/triage.ts
|
|
25678
26531
|
init_src();
|
|
@@ -25681,95 +26534,8 @@ init_src();
|
|
|
25681
26534
|
init_src2();
|
|
25682
26535
|
var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
|
|
25683
26536
|
|
|
25684
|
-
// ../../packages/local-graph/src/
|
|
25685
|
-
|
|
25686
|
-
init_src();
|
|
25687
|
-
var REDIRECT_EDGES = [
|
|
25688
|
-
"CAUSED_BY",
|
|
25689
|
-
"SOLVED_BY",
|
|
25690
|
-
"FIXED_BY",
|
|
25691
|
-
"ANCHORED_AT",
|
|
25692
|
-
"MANIFESTED_IN",
|
|
25693
|
-
"EVIDENCED_BY"
|
|
25694
|
-
];
|
|
25695
|
-
function corroborations(n) {
|
|
25696
|
-
return Number(n.attrs["corroborations"] ?? 0);
|
|
25697
|
-
}
|
|
25698
|
-
function overlap(a, b) {
|
|
25699
|
-
if (a.size === 0 || b.size === 0) return 0;
|
|
25700
|
-
let inter = 0;
|
|
25701
|
-
for (const x of a) if (b.has(x)) inter++;
|
|
25702
|
-
return inter / Math.min(a.size, b.size);
|
|
25703
|
-
}
|
|
25704
|
-
function mergeDuplicateProblems(store2, opts) {
|
|
25705
|
-
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25706
|
-
const minTokens = opts.minTokens ?? 4;
|
|
25707
|
-
const report = { clusters: 0, merged: 0 };
|
|
25708
|
-
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25709
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
25710
|
-
for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
|
|
25711
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
25712
|
-
store2.transaction(() => {
|
|
25713
|
-
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25714
|
-
const a = open[i2];
|
|
25715
|
-
if (consumed.has(a.id)) continue;
|
|
25716
|
-
const cluster = [a];
|
|
25717
|
-
const ta = tokens.get(a.id);
|
|
25718
|
-
for (let j = i2 + 1; j < open.length; j++) {
|
|
25719
|
-
const b = open[j];
|
|
25720
|
-
if (consumed.has(b.id)) continue;
|
|
25721
|
-
const tb = tokens.get(b.id);
|
|
25722
|
-
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25723
|
-
if (overlap(ta, tb) >= minOverlap) {
|
|
25724
|
-
cluster.push(b);
|
|
25725
|
-
consumed.add(b.id);
|
|
25726
|
-
}
|
|
25727
|
-
}
|
|
25728
|
-
if (cluster.length < 2) continue;
|
|
25729
|
-
report.clusters++;
|
|
25730
|
-
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25731
|
-
const survivor = cluster[0];
|
|
25732
|
-
for (const dup of cluster.slice(1)) {
|
|
25733
|
-
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25734
|
-
report.merged++;
|
|
25735
|
-
}
|
|
25736
|
-
}
|
|
25737
|
-
});
|
|
25738
|
-
return report;
|
|
25739
|
-
}
|
|
25740
|
-
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25741
|
-
const surv = store2.getNode(survivor.id);
|
|
25742
|
-
if (!surv) return;
|
|
25743
|
-
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25744
|
-
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25745
|
-
store2.updateNode(survivor.id, {
|
|
25746
|
-
attrs: {
|
|
25747
|
-
...surv.attrs,
|
|
25748
|
-
sources: [...sources],
|
|
25749
|
-
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25750
|
-
},
|
|
25751
|
-
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25752
|
-
lastUpdatedAt: ts
|
|
25753
|
-
});
|
|
25754
|
-
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25755
|
-
if (e.to === survivor.id) continue;
|
|
25756
|
-
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25757
|
-
if (store2.getEdge(id)) continue;
|
|
25758
|
-
const redirected = {
|
|
25759
|
-
...e,
|
|
25760
|
-
id,
|
|
25761
|
-
from: survivor.id,
|
|
25762
|
-
createdAt: ts,
|
|
25763
|
-
lastSeenAt: ts
|
|
25764
|
-
};
|
|
25765
|
-
store2.mergeEdge(redirected);
|
|
25766
|
-
}
|
|
25767
|
-
store2.updateNode(dup.id, {
|
|
25768
|
-
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25769
|
-
lastUpdatedAt: ts
|
|
25770
|
-
});
|
|
25771
|
-
store2.closeNode(dup.id, ts);
|
|
25772
|
-
}
|
|
26537
|
+
// ../../packages/local-graph/src/community.ts
|
|
26538
|
+
init_src2();
|
|
25773
26539
|
|
|
25774
26540
|
// ../../packages/local-graph/src/tools.ts
|
|
25775
26541
|
init_src();
|
|
@@ -25786,6 +26552,9 @@ function rankToolsForHandles(store2, limit = 5) {
|
|
|
25786
26552
|
// ../../packages/local-graph/src/principle-sync.ts
|
|
25787
26553
|
init_src2();
|
|
25788
26554
|
|
|
26555
|
+
// ../../packages/local-graph/src/mechanism-liveness.ts
|
|
26556
|
+
var STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
26557
|
+
|
|
25789
26558
|
// ../../packages/generalizer/src/generalizer.ts
|
|
25790
26559
|
init_src2();
|
|
25791
26560
|
init_src();
|
|
@@ -25905,7 +26674,164 @@ function promoteMotifs(store2, t) {
|
|
|
25905
26674
|
}
|
|
25906
26675
|
|
|
25907
26676
|
// ../../packages/generalizer/src/nightly.ts
|
|
25908
|
-
|
|
26677
|
+
var FULL_RESCORE_EVERY = 12;
|
|
26678
|
+
var INCREMENTAL_REGION_MAX_FRACTION = 0.2;
|
|
26679
|
+
var INCREMENTAL_HOPS = 3;
|
|
26680
|
+
function runGraphRescore(store2, opts = {}) {
|
|
26681
|
+
const lastRescoreAt = Number(store2.getMeta?.("lastRescoreAt") ?? 0);
|
|
26682
|
+
const sinceFull = Number(store2.getMeta?.("rescoresSinceFull") ?? 0);
|
|
26683
|
+
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;
|
|
26684
|
+
if (incrementalCapable) {
|
|
26685
|
+
const started = Date.now();
|
|
26686
|
+
const inc = tryIncrementalRescore(store2, lastRescoreAt, started);
|
|
26687
|
+
if (inc) {
|
|
26688
|
+
store2.setMeta("lastRescoreAt", String(started));
|
|
26689
|
+
store2.setMeta("rescoresSinceFull", String(sinceFull + 1));
|
|
26690
|
+
return inc;
|
|
26691
|
+
}
|
|
26692
|
+
}
|
|
26693
|
+
const report = runFullRescore(store2);
|
|
26694
|
+
store2.setMeta?.("lastRescoreAt", String(report.startedAt));
|
|
26695
|
+
store2.setMeta?.("rescoresSinceFull", "0");
|
|
26696
|
+
store2.setMeta?.("danglingRankShare", String(report.danglingRank));
|
|
26697
|
+
const { startedAt: _drop, danglingRank: _drop2, ...rest } = report;
|
|
26698
|
+
return rest;
|
|
26699
|
+
}
|
|
26700
|
+
function tryIncrementalRescore(store2, lastRescoreAt, started) {
|
|
26701
|
+
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
26702
|
+
const liveN = store2.nodeCount();
|
|
26703
|
+
const cap = Math.max(1e3, Math.floor(liveN * INCREMENTAL_REGION_MAX_FRACTION));
|
|
26704
|
+
const region = new Set(store2.dirtyNodeIdsSince(lastRescoreAt));
|
|
26705
|
+
for (const e of store2.edgeEndpointsTouchedSince(lastRescoreAt)) {
|
|
26706
|
+
region.add(e.from);
|
|
26707
|
+
region.add(e.to);
|
|
26708
|
+
}
|
|
26709
|
+
if (region.size > cap) return null;
|
|
26710
|
+
let frontier = [...region];
|
|
26711
|
+
for (let hop = 0; hop < INCREMENTAL_HOPS && frontier.length > 0; hop++) {
|
|
26712
|
+
const next = [];
|
|
26713
|
+
for (const e of store2.outEdgesForMany(frontier)) {
|
|
26714
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26715
|
+
if (!region.has(e.to)) {
|
|
26716
|
+
region.add(e.to);
|
|
26717
|
+
next.push(e.to);
|
|
26718
|
+
}
|
|
26719
|
+
}
|
|
26720
|
+
if (region.size > cap) return null;
|
|
26721
|
+
frontier = next;
|
|
26722
|
+
}
|
|
26723
|
+
const rank0 = store2.ranksForMany([...region]);
|
|
26724
|
+
const regionIds = [...rank0.keys()];
|
|
26725
|
+
if (regionIds.length === 0) {
|
|
26726
|
+
return {
|
|
26727
|
+
scoredNodes: 0,
|
|
26728
|
+
iterations: 0,
|
|
26729
|
+
converged: true,
|
|
26730
|
+
landmarks: 0,
|
|
26731
|
+
communities: 0,
|
|
26732
|
+
motifsPromoted: 0,
|
|
26733
|
+
techniques: 0,
|
|
26734
|
+
antipatterns: 0,
|
|
26735
|
+
pageRankWritten: 0,
|
|
26736
|
+
landmarkFlips: 0,
|
|
26737
|
+
mode: "incremental",
|
|
26738
|
+
regionSize: 0,
|
|
26739
|
+
durationMs: Date.now() - started
|
|
26740
|
+
};
|
|
26741
|
+
}
|
|
26742
|
+
const inRegion = new Set(regionIds);
|
|
26743
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
26744
|
+
const outSum = /* @__PURE__ */ new Map();
|
|
26745
|
+
for (const e of store2.outEdgesForMany(regionIds)) {
|
|
26746
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26747
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26748
|
+
if (w <= 0) continue;
|
|
26749
|
+
outSum.set(e.from, (outSum.get(e.from) ?? 0) + w);
|
|
26750
|
+
if (!inRegion.has(e.to)) continue;
|
|
26751
|
+
const l = out2.get(e.from);
|
|
26752
|
+
if (l) l.push({ to: e.to, weight: w });
|
|
26753
|
+
else out2.set(e.from, [{ to: e.to, weight: w }]);
|
|
26754
|
+
}
|
|
26755
|
+
const boundaryEdges = store2.inEdgesForMany(regionIds).filter((e) => allowedTypes.has(e.type) && !inRegion.has(e.from) && (EDGE_WEIGHT[e.type] ?? 1) > 0);
|
|
26756
|
+
const boundarySources = [...new Set(boundaryEdges.map((e) => e.from))];
|
|
26757
|
+
if (boundarySources.length > cap) return null;
|
|
26758
|
+
const boundaryRanks = store2.ranksForMany(boundarySources);
|
|
26759
|
+
const boundaryOutSum = /* @__PURE__ */ new Map();
|
|
26760
|
+
for (const e of store2.outEdgesForMany(boundarySources)) {
|
|
26761
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26762
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26763
|
+
if (w > 0) boundaryOutSum.set(e.from, (boundaryOutSum.get(e.from) ?? 0) + w);
|
|
26764
|
+
}
|
|
26765
|
+
const boundaryInflow = /* @__PURE__ */ new Map();
|
|
26766
|
+
for (const e of boundaryEdges) {
|
|
26767
|
+
const r = boundaryRanks.get(e.from);
|
|
26768
|
+
const os2 = boundaryOutSum.get(e.from);
|
|
26769
|
+
if (r === void 0 || !os2) continue;
|
|
26770
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26771
|
+
boundaryInflow.set(e.to, (boundaryInflow.get(e.to) ?? 0) + r * w / os2);
|
|
26772
|
+
}
|
|
26773
|
+
const globalDangling = Number(store2.getMeta("danglingRankShare") ?? 0);
|
|
26774
|
+
let regionDangling = 0;
|
|
26775
|
+
for (const id of regionIds) {
|
|
26776
|
+
if ((outSum.get(id) ?? 0) <= 0) regionDangling += rank0.get(id) ?? 0;
|
|
26777
|
+
}
|
|
26778
|
+
const result = pagerankLocal({
|
|
26779
|
+
regionIds,
|
|
26780
|
+
rank0,
|
|
26781
|
+
out: out2,
|
|
26782
|
+
outSum,
|
|
26783
|
+
boundaryInflow,
|
|
26784
|
+
globalN: liveN,
|
|
26785
|
+
externalDanglingRank: Math.max(0, globalDangling - regionDangling),
|
|
26786
|
+
damping: 0.85,
|
|
26787
|
+
tolerance: 1e-6,
|
|
26788
|
+
maxIterations: 100
|
|
26789
|
+
});
|
|
26790
|
+
let pageRankWritten = 0;
|
|
26791
|
+
store2.transaction(() => {
|
|
26792
|
+
for (const [id, score2] of result.scores) {
|
|
26793
|
+
if (Math.abs(score2 - (rank0.get(id) ?? 0)) < 1e-9) continue;
|
|
26794
|
+
store2.setPageRank(id, score2);
|
|
26795
|
+
pageRankWritten++;
|
|
26796
|
+
}
|
|
26797
|
+
});
|
|
26798
|
+
const sweep = store2.landmarkSweepRows();
|
|
26799
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
26800
|
+
for (const row of sweep) {
|
|
26801
|
+
if (row.label === "Pattern" || row.label === "RootCause") {
|
|
26802
|
+
candidates.set(row.id, result.scores.get(row.id) ?? row.pageRank);
|
|
26803
|
+
}
|
|
26804
|
+
}
|
|
26805
|
+
const want = markLandmarks(candidates, 0.1);
|
|
26806
|
+
for (const row of sweep) if (row.memoryTier === "persistent") want.add(row.id);
|
|
26807
|
+
let landmarkFlips = 0;
|
|
26808
|
+
store2.transaction(() => {
|
|
26809
|
+
for (const row of sweep) {
|
|
26810
|
+
if (row.extractionSource === "bko-inferred") continue;
|
|
26811
|
+
const should = want.has(row.id);
|
|
26812
|
+
if (row.isLandmark === should) continue;
|
|
26813
|
+
store2.setLandmark(row.id, should);
|
|
26814
|
+
landmarkFlips++;
|
|
26815
|
+
}
|
|
26816
|
+
});
|
|
26817
|
+
return {
|
|
26818
|
+
scoredNodes: regionIds.length,
|
|
26819
|
+
iterations: result.iterations,
|
|
26820
|
+
converged: result.converged,
|
|
26821
|
+
landmarks: want.size,
|
|
26822
|
+
communities: 0,
|
|
26823
|
+
// deferred to the full backstop — see mode docs
|
|
26824
|
+
motifsPromoted: 0,
|
|
26825
|
+
techniques: 0,
|
|
26826
|
+
antipatterns: 0,
|
|
26827
|
+
pageRankWritten,
|
|
26828
|
+
landmarkFlips,
|
|
26829
|
+
mode: "incremental",
|
|
26830
|
+
regionSize: regionIds.length,
|
|
26831
|
+
durationMs: Date.now() - started
|
|
26832
|
+
};
|
|
26833
|
+
}
|
|
26834
|
+
function runFullRescore(store2) {
|
|
25909
26835
|
const started = Date.now();
|
|
25910
26836
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
25911
26837
|
const nodeIds = [];
|
|
@@ -25931,10 +26857,12 @@ function runNightlyPipeline(store2) {
|
|
|
25931
26857
|
maxIterations: 100
|
|
25932
26858
|
});
|
|
25933
26859
|
const scored = result.scores.size;
|
|
26860
|
+
let pageRankWritten = 0;
|
|
25934
26861
|
store2.transaction(() => {
|
|
25935
26862
|
for (const [id, score2] of result.scores) {
|
|
25936
26863
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
25937
26864
|
store2.setPageRank(id, score2);
|
|
26865
|
+
pageRankWritten++;
|
|
25938
26866
|
}
|
|
25939
26867
|
});
|
|
25940
26868
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -25949,11 +26877,13 @@ function runNightlyPipeline(store2) {
|
|
|
25949
26877
|
for (const id of nodeIds) {
|
|
25950
26878
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
25951
26879
|
}
|
|
26880
|
+
let landmarkFlips = 0;
|
|
25952
26881
|
store2.transaction(() => {
|
|
25953
26882
|
for (const id of nodeIds) {
|
|
25954
26883
|
const want = allLandmarks.has(id);
|
|
25955
26884
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
25956
26885
|
store2.setLandmark(id, want);
|
|
26886
|
+
landmarkFlips++;
|
|
25957
26887
|
}
|
|
25958
26888
|
});
|
|
25959
26889
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -25988,8 +26918,6 @@ function runNightlyPipeline(store2) {
|
|
|
25988
26918
|
for (const [id, c] of comm.community) store2.updateNode(id, { community: c });
|
|
25989
26919
|
});
|
|
25990
26920
|
const motifs = promoteMotifs(store2, started);
|
|
25991
|
-
const designResolved = resolveDesignProblems(store2, started);
|
|
25992
|
-
const cry = crystallize(store2, { ts: started });
|
|
25993
26921
|
return {
|
|
25994
26922
|
scoredNodes: scored,
|
|
25995
26923
|
iterations: result.iterations,
|
|
@@ -25999,15 +26927,61 @@ function runNightlyPipeline(store2) {
|
|
|
25999
26927
|
motifsPromoted: motifs.promoted,
|
|
26000
26928
|
techniques: motifs.techniques,
|
|
26001
26929
|
antipatterns: motifs.antipatterns,
|
|
26002
|
-
|
|
26003
|
-
|
|
26004
|
-
|
|
26930
|
+
pageRankWritten,
|
|
26931
|
+
landmarkFlips,
|
|
26932
|
+
mode: "full",
|
|
26933
|
+
regionSize: 0,
|
|
26934
|
+
startedAt: started,
|
|
26935
|
+
danglingRank: result.danglingRank,
|
|
26936
|
+
durationMs: Date.now() - started
|
|
26937
|
+
};
|
|
26938
|
+
}
|
|
26939
|
+
function runSemanticMaintenance(store2) {
|
|
26940
|
+
const started = Date.now();
|
|
26941
|
+
reopenAutoClosedProblems(store2, started);
|
|
26942
|
+
const revisits = clearAnsweredRevisits(store2, started);
|
|
26943
|
+
const fixCandidates = clearSettledFixCandidates(store2, started);
|
|
26944
|
+
const designResolved = markFixCandidates(store2, started);
|
|
26945
|
+
const cry = crystallize(store2, { ts: started });
|
|
26946
|
+
return {
|
|
26005
26947
|
designResolved,
|
|
26948
|
+
revisitsCleared: revisits.cleared,
|
|
26949
|
+
revisitsStanding: revisits.standing,
|
|
26950
|
+
fixCandidatesSettled: fixCandidates.answered + fixCandidates.ignored + fixCandidates.unsubstantiated,
|
|
26951
|
+
fixCandidates,
|
|
26006
26952
|
claimsEvaluated: cry.evaluated,
|
|
26007
26953
|
claimsDerived: cry.derived.length,
|
|
26008
26954
|
durationMs: Date.now() - started
|
|
26009
26955
|
};
|
|
26010
26956
|
}
|
|
26957
|
+
function runNightlyPipeline(store2) {
|
|
26958
|
+
const started = Date.now();
|
|
26959
|
+
const rescore = runGraphRescore(store2);
|
|
26960
|
+
const semantic = runSemanticMaintenance(store2);
|
|
26961
|
+
return {
|
|
26962
|
+
scoredNodes: rescore.scoredNodes,
|
|
26963
|
+
iterations: rescore.iterations,
|
|
26964
|
+
converged: rescore.converged,
|
|
26965
|
+
landmarks: rescore.landmarks,
|
|
26966
|
+
communities: rescore.communities,
|
|
26967
|
+
motifsPromoted: rescore.motifsPromoted,
|
|
26968
|
+
techniques: rescore.techniques,
|
|
26969
|
+
antipatterns: rescore.antipatterns,
|
|
26970
|
+
skillsInduced: 0,
|
|
26971
|
+
// skills are cloud-induced now (see above)
|
|
26972
|
+
skillsRefreshed: 0,
|
|
26973
|
+
pageRankWritten: rescore.pageRankWritten,
|
|
26974
|
+
landmarkFlips: rescore.landmarkFlips,
|
|
26975
|
+
mode: rescore.mode,
|
|
26976
|
+
regionSize: rescore.regionSize,
|
|
26977
|
+
designResolved: semantic.designResolved,
|
|
26978
|
+
revisitsCleared: semantic.revisitsCleared,
|
|
26979
|
+
fixCandidatesSettled: semantic.fixCandidatesSettled,
|
|
26980
|
+
claimsEvaluated: semantic.claimsEvaluated,
|
|
26981
|
+
claimsDerived: semantic.claimsDerived,
|
|
26982
|
+
durationMs: Date.now() - started
|
|
26983
|
+
};
|
|
26984
|
+
}
|
|
26011
26985
|
function codeAnchorProjection(store2, semanticIds, opts = {}) {
|
|
26012
26986
|
const anchorToNodes = /* @__PURE__ */ new Map();
|
|
26013
26987
|
for (const id of semanticIds) {
|
|
@@ -26052,12 +27026,14 @@ var AGENTS_POINTER_BODY = [
|
|
|
26052
27026
|
init_src2();
|
|
26053
27027
|
function buildSnapshot(opts) {
|
|
26054
27028
|
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
|
|
26055
|
-
const
|
|
27029
|
+
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
27030
|
+
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
26056
27031
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
26057
27032
|
const recent = allProblems.slice(0, 8).map((p) => ({
|
|
26058
27033
|
node: p,
|
|
26059
27034
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
26060
27035
|
}));
|
|
27036
|
+
const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
|
|
26061
27037
|
const recentResolved = problems.filter((p) => p.attrs["resolvedAt"] != null && p.attrs["resolvedAs"] == null).sort((a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)).slice(0, 4).map((p) => {
|
|
26062
27038
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
26063
27039
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
@@ -26085,10 +27061,13 @@ function buildSnapshot(opts) {
|
|
|
26085
27061
|
episodeId,
|
|
26086
27062
|
problems: problems2
|
|
26087
27063
|
}));
|
|
26088
|
-
const
|
|
26089
|
-
|
|
26090
|
-
|
|
26091
|
-
|
|
27064
|
+
const revisitSeen = /* @__PURE__ */ new Set();
|
|
27065
|
+
const needsRevisit = listNeedsRevisit(opts.store, (opts.now ?? /* @__PURE__ */ new Date()).getTime()).filter((r) => {
|
|
27066
|
+
const key = `${r.label}:${r.description.trim().toLowerCase()}`;
|
|
27067
|
+
if (revisitSeen.has(key)) return false;
|
|
27068
|
+
revisitSeen.add(key);
|
|
27069
|
+
return true;
|
|
27070
|
+
}).slice(0, 5);
|
|
26092
27071
|
const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
26093
27072
|
const pkgBase = (x) => {
|
|
26094
27073
|
const at = x.lastIndexOf("@");
|
|
@@ -26124,7 +27103,9 @@ function buildSnapshot(opts) {
|
|
|
26124
27103
|
profileContext,
|
|
26125
27104
|
recentProblems: recent,
|
|
26126
27105
|
recentResolved,
|
|
27106
|
+
recentConstraints,
|
|
26127
27107
|
...causalNudge ? { causalNudge } : {},
|
|
27108
|
+
...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
|
|
26128
27109
|
...domainNudge ? { domainNudge } : {},
|
|
26129
27110
|
motifs,
|
|
26130
27111
|
reviewCount: opts.reviewCount,
|
|
@@ -26175,10 +27156,16 @@ function sliceForFile(store2, relPath) {
|
|
|
26175
27156
|
}
|
|
26176
27157
|
const fp = priorsForFile(store2, relPath);
|
|
26177
27158
|
const priors = fp ? {
|
|
26178
|
-
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27159
|
+
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27160
|
+
id: p.id,
|
|
27161
|
+
description: p.description,
|
|
27162
|
+
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
27163
|
+
})),
|
|
27164
|
+
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
27165
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
26179
27166
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
26180
27167
|
solutionsByProblem: Object.fromEntries(
|
|
26181
|
-
fp.openProblems.slice(0, 3).map((p) => [
|
|
27168
|
+
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
26182
27169
|
p.id,
|
|
26183
27170
|
(fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
|
|
26184
27171
|
])
|
|
@@ -26215,37 +27202,37 @@ init_src3();
|
|
|
26215
27202
|
// ../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js
|
|
26216
27203
|
import os from "node:os";
|
|
26217
27204
|
import process3 from "node:process";
|
|
26218
|
-
var
|
|
27205
|
+
var homedir2 = os.homedir();
|
|
26219
27206
|
var tmpdir = os.tmpdir();
|
|
26220
27207
|
var { env } = process3;
|
|
26221
27208
|
|
|
26222
27209
|
// src/paths.ts
|
|
26223
|
-
import { dirname as dirname2, join as
|
|
27210
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
26224
27211
|
function workspaceDir(workspaceRoot) {
|
|
26225
|
-
return
|
|
27212
|
+
return join4(workspaceRoot, ".errata");
|
|
26226
27213
|
}
|
|
26227
27214
|
function workspacePaths(root) {
|
|
26228
27215
|
const dir = workspaceDir(root);
|
|
26229
27216
|
return {
|
|
26230
27217
|
root,
|
|
26231
27218
|
configDir: dir,
|
|
26232
|
-
workspaceJson:
|
|
26233
|
-
eventLog:
|
|
26234
|
-
castalia:
|
|
26235
|
-
reviewQueue:
|
|
26236
|
-
outbox:
|
|
26237
|
-
daemonLock:
|
|
26238
|
-
identityAudit:
|
|
26239
|
-
skillsDir:
|
|
26240
|
-
skillsManifest:
|
|
27219
|
+
workspaceJson: join4(dir, "workspace.json"),
|
|
27220
|
+
eventLog: join4(dir, "eventlog.sqlite"),
|
|
27221
|
+
castalia: join4(dir, "castalia.db"),
|
|
27222
|
+
reviewQueue: join4(dir, "review-queue.json"),
|
|
27223
|
+
outbox: join4(dir, "outbox"),
|
|
27224
|
+
daemonLock: join4(dir, "daemon.lock"),
|
|
27225
|
+
identityAudit: join4(dir, "identity-audit.log"),
|
|
27226
|
+
skillsDir: join4(dir, "skills"),
|
|
27227
|
+
skillsManifest: join4(dir, "skills.json")
|
|
26241
27228
|
};
|
|
26242
27229
|
}
|
|
26243
27230
|
|
|
26244
27231
|
// src/reconcile.ts
|
|
26245
27232
|
init_src3();
|
|
26246
27233
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
26247
|
-
import { readdirSync as
|
|
26248
|
-
import { join as
|
|
27234
|
+
import { readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
27235
|
+
import { join as join5, relative as relative2, sep as sep2 } from "node:path";
|
|
26249
27236
|
var IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
|
|
26250
27237
|
var SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
|
|
26251
27238
|
function gitSourceFiles(root) {
|
|
@@ -26262,7 +27249,7 @@ function gitSourceFiles(root) {
|
|
|
26262
27249
|
const out2 = [];
|
|
26263
27250
|
for (const rel of stdout.split("\0")) {
|
|
26264
27251
|
if (!rel || !SOURCE_RE.test(rel)) continue;
|
|
26265
|
-
const abs =
|
|
27252
|
+
const abs = join5(root, rel);
|
|
26266
27253
|
if (IGNORED.test(abs)) continue;
|
|
26267
27254
|
out2.push(abs);
|
|
26268
27255
|
}
|
|
@@ -26271,13 +27258,13 @@ function gitSourceFiles(root) {
|
|
|
26271
27258
|
function* walkSource(dir) {
|
|
26272
27259
|
let entries;
|
|
26273
27260
|
try {
|
|
26274
|
-
entries =
|
|
27261
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
26275
27262
|
} catch {
|
|
26276
27263
|
return;
|
|
26277
27264
|
}
|
|
26278
27265
|
for (const e of entries) {
|
|
26279
27266
|
const name2 = String(e.name);
|
|
26280
|
-
const full =
|
|
27267
|
+
const full = join5(dir, name2);
|
|
26281
27268
|
if (IGNORED.test(full)) continue;
|
|
26282
27269
|
if (e.isDirectory()) yield* walkSource(full);
|
|
26283
27270
|
else if (SOURCE_RE.test(name2)) yield full;
|
|
@@ -26286,12 +27273,18 @@ function* walkSource(dir) {
|
|
|
26286
27273
|
function listSourceFiles(root) {
|
|
26287
27274
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
26288
27275
|
}
|
|
26289
|
-
|
|
27276
|
+
var SCAN_YIELD_EVERY = 100;
|
|
27277
|
+
var LIVENESS_CHUNK = 4e3;
|
|
27278
|
+
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
26290
27279
|
const stale = [];
|
|
27280
|
+
const pending = [];
|
|
27281
|
+
const allTargets = [];
|
|
27282
|
+
let scanned = 0;
|
|
26291
27283
|
for (const abs of listSourceFiles(rootPath)) {
|
|
27284
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
26292
27285
|
let mtimeMs;
|
|
26293
27286
|
try {
|
|
26294
|
-
mtimeMs =
|
|
27287
|
+
mtimeMs = statSync3(abs).mtimeMs;
|
|
26295
27288
|
} catch {
|
|
26296
27289
|
continue;
|
|
26297
27290
|
}
|
|
@@ -26299,32 +27292,52 @@ function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
26299
27292
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
26300
27293
|
if (!fnode) {
|
|
26301
27294
|
stale.push(abs);
|
|
27295
|
+
} else if ((fnode.validTo ?? null) !== null) {
|
|
27296
|
+
stale.push(abs);
|
|
26302
27297
|
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
26303
27298
|
stale.push(abs);
|
|
26304
27299
|
} else {
|
|
26305
27300
|
const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
26306
27301
|
if (edges.length === 0) {
|
|
26307
27302
|
stale.push(abs);
|
|
26308
|
-
} else
|
|
26309
|
-
|
|
27303
|
+
} else {
|
|
27304
|
+
const targets = edges.map((e) => e.to);
|
|
27305
|
+
pending.push({ abs, targets });
|
|
27306
|
+
allTargets.push(...targets);
|
|
26310
27307
|
}
|
|
26311
27308
|
}
|
|
26312
27309
|
}
|
|
27310
|
+
const live = /* @__PURE__ */ new Set();
|
|
27311
|
+
for (let i2 = 0; i2 < allTargets.length; i2 += LIVENESS_CHUNK) {
|
|
27312
|
+
for (const id of store2.liveNodeIds(allTargets.slice(i2, i2 + LIVENESS_CHUNK))) live.add(id);
|
|
27313
|
+
if (i2 + LIVENESS_CHUNK < allTargets.length) await new Promise((r) => setImmediate(r));
|
|
27314
|
+
}
|
|
27315
|
+
for (const { abs, targets } of pending) {
|
|
27316
|
+
if (targets.some((id) => !live.has(id))) stale.push(abs);
|
|
27317
|
+
}
|
|
26313
27318
|
return stale;
|
|
26314
27319
|
}
|
|
26315
|
-
function isLive(n) {
|
|
26316
|
-
return n != null && (n.validTo ?? null) === null;
|
|
26317
|
-
}
|
|
26318
27320
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
26319
|
-
const stale = findStaleFiles(store2, rootPath, workspaceId);
|
|
27321
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId);
|
|
26320
27322
|
if (stale.length === 0) return 0;
|
|
26321
27323
|
let total = 0;
|
|
27324
|
+
let failedBatches = 0;
|
|
26322
27325
|
const BATCH = 20;
|
|
26323
27326
|
for (let i2 = 0; i2 < stale.length; i2 += BATCH) {
|
|
26324
|
-
|
|
26325
|
-
|
|
27327
|
+
try {
|
|
27328
|
+
const r = await incrementalReindex(store2, rootPath, workspaceId, stale.slice(i2, i2 + BATCH));
|
|
27329
|
+
total += r.filesReindexed;
|
|
27330
|
+
} catch (err2) {
|
|
27331
|
+
failedBatches++;
|
|
27332
|
+
console.warn(
|
|
27333
|
+
`[errata] reconcile: batch ${i2 / BATCH + 1} failed (${stale.slice(i2, i2 + BATCH).length} file(s) skipped): ${err2 instanceof Error ? err2.message : err2}`
|
|
27334
|
+
);
|
|
27335
|
+
}
|
|
26326
27336
|
if (i2 + BATCH < stale.length) await new Promise((res) => setImmediate(res));
|
|
26327
27337
|
}
|
|
27338
|
+
if (failedBatches > 0) {
|
|
27339
|
+
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
27340
|
+
}
|
|
26328
27341
|
return total;
|
|
26329
27342
|
}
|
|
26330
27343
|
|
|
@@ -26377,6 +27390,7 @@ function runNightly() {
|
|
|
26377
27390
|
const a = backfillLegacyAnchors(store, Date.now());
|
|
26378
27391
|
anchorsDemoted = a.demotedEdges;
|
|
26379
27392
|
resolutionsSuspect = a.suspects.length;
|
|
27393
|
+
repairLegacyAnchorsFromStatement(store, Date.now());
|
|
26380
27394
|
} catch {
|
|
26381
27395
|
}
|
|
26382
27396
|
const report = runNightlyPipeline(store);
|