@inerrata-corporation/errata 2.0.1-dev.99 → 2.0.2-dev.1024
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 +670 -288
- package/errata.mjs +8868 -2233
- package/package.json +1 -1
- package/pass-worker.mjs +1370 -326
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,
|
|
@@ -406,6 +421,13 @@ var init_castalia = __esm({
|
|
|
406
421
|
}
|
|
407
422
|
});
|
|
408
423
|
|
|
424
|
+
// ../../packages/shared/src/graph-events.ts
|
|
425
|
+
var init_graph_events = __esm({
|
|
426
|
+
"../../packages/shared/src/graph-events.ts"() {
|
|
427
|
+
"use strict";
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
|
|
409
431
|
// ../../packages/shared/src/canonical/hash.ts
|
|
410
432
|
import { createHash } from "node:crypto";
|
|
411
433
|
function sha256(input) {
|
|
@@ -447,13 +469,18 @@ function resolveCanonical(label) {
|
|
|
447
469
|
function resolveCanonicalId(label) {
|
|
448
470
|
return resolveCanonical(label)?.id;
|
|
449
471
|
}
|
|
472
|
+
function resolveUnambiguousCanonicalId(label) {
|
|
473
|
+
const key = label.trim().toLowerCase();
|
|
474
|
+
if (AMBIGUOUS_ALIASES.has(key)) return void 0;
|
|
475
|
+
return resolveCanonicalId(key);
|
|
476
|
+
}
|
|
450
477
|
function aliasesLongestFirst() {
|
|
451
478
|
const out2 = [];
|
|
452
479
|
for (const e of REGISTRY) for (const a of e.aliases) out2.push({ alias: a.toLowerCase(), entity: e });
|
|
453
480
|
out2.sort((x, y) => y.alias.length - x.alias.length);
|
|
454
481
|
return out2;
|
|
455
482
|
}
|
|
456
|
-
var REGISTRY, BY_ALIAS;
|
|
483
|
+
var REGISTRY, AMBIGUOUS_ALIASES, BY_ALIAS;
|
|
457
484
|
var init_taxonomy = __esm({
|
|
458
485
|
"../../packages/shared/src/nlp/taxonomy.ts"() {
|
|
459
486
|
"use strict";
|
|
@@ -502,6 +529,13 @@ var init_taxonomy = __esm({
|
|
|
502
529
|
{ id: "concept:retry", name: "retry", category: "concept", aliases: ["retry"] },
|
|
503
530
|
{ id: "concept:migration", name: "migration", category: "concept", aliases: ["migration"] }
|
|
504
531
|
];
|
|
532
|
+
AMBIGUOUS_ALIASES = /* @__PURE__ */ new Set([
|
|
533
|
+
"node",
|
|
534
|
+
"go",
|
|
535
|
+
"pool",
|
|
536
|
+
"spring",
|
|
537
|
+
"ws"
|
|
538
|
+
]);
|
|
505
539
|
BY_ALIAS = /* @__PURE__ */ new Map();
|
|
506
540
|
for (const e of REGISTRY) {
|
|
507
541
|
for (const a of e.aliases) {
|
|
@@ -563,7 +597,7 @@ function lemma(token) {
|
|
|
563
597
|
}
|
|
564
598
|
return token;
|
|
565
599
|
}
|
|
566
|
-
function
|
|
600
|
+
function tokenize(text, resolve) {
|
|
567
601
|
const matches = text.normalize("NFC").toLowerCase().match(TOKEN_RE) ?? [];
|
|
568
602
|
const out2 = /* @__PURE__ */ new Set();
|
|
569
603
|
for (const raw of matches) {
|
|
@@ -573,11 +607,14 @@ function conceptTokens(text) {
|
|
|
573
607
|
out2.add(token);
|
|
574
608
|
continue;
|
|
575
609
|
}
|
|
576
|
-
const canonical =
|
|
610
|
+
const canonical = resolve(token);
|
|
577
611
|
out2.add(canonical ?? lemma(token));
|
|
578
612
|
}
|
|
579
613
|
return [...out2].sort();
|
|
580
614
|
}
|
|
615
|
+
function retrievalTokens(text) {
|
|
616
|
+
return tokenize(text, resolveUnambiguousCanonicalId);
|
|
617
|
+
}
|
|
581
618
|
var STOPWORDS, NEGATION_TOKENS, TOKEN_RE;
|
|
582
619
|
var init_concept_bag = __esm({
|
|
583
620
|
"../../packages/shared/src/nlp/concept-bag.ts"() {
|
|
@@ -15049,13 +15086,21 @@ var init_edge_rules = __esm({
|
|
|
15049
15086
|
// NOT ruled here — the type pre-exists with broader extractor senses, and a
|
|
15050
15087
|
// new rule on an old type would reject legitimate live flows (reject-never-flip
|
|
15051
15088
|
// cuts both ways: only rule types you introduce or senses that are documented).
|
|
15052
|
-
|
|
15089
|
+
// `Component` joined the target set with OM-agent-anchors: an agent-named
|
|
15090
|
+
// component ("React Router") is the same knowledge→named-unit anchor shape as
|
|
15091
|
+
// a Tool — the knowledge is ABOUT it, not dependent on it.
|
|
15092
|
+
CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15053
15093
|
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15054
15094
|
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15055
15095
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15056
15096
|
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15057
15097
|
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15058
|
-
|
|
15098
|
+
// `AntiPattern` joined the target set 2026-08-02: it is one of the three motif
|
|
15099
|
+
// kinds (generalizer motifs.ts `layerOf`: pattern | technique | antipattern) and
|
|
15100
|
+
// the SUPPRESSED-close path mints `Problem ─INSTANCE_OF→ AntiPattern` as the
|
|
15101
|
+
// negative-knowledge binding ("silenced, not solved") — the original three-label
|
|
15102
|
+
// rule predates AntiPattern joining the motif layer and silently ate that edge.
|
|
15103
|
+
INSTANCE_OF: { to: ["Pattern", "AntiPattern", "Weakness", "Technique"] },
|
|
15059
15104
|
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15060
15105
|
MATCHES: { to: ["Pattern"] },
|
|
15061
15106
|
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
@@ -15156,13 +15201,35 @@ var init_wire = __esm({
|
|
|
15156
15201
|
/** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
|
|
15157
15202
|
description: external_exports.string().min(1).max(4e3),
|
|
15158
15203
|
attrs: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
15204
|
+
/** One-way origin key of the SESSION that minted this node (`ws_…`, a
|
|
15205
|
+
* truncated digest — never the raw session id). Stamped onto the created
|
|
15206
|
+
* node as `authoringSession`, which is the independence unit for the
|
|
15207
|
+
* evidence channels: the session that authored a claim may not corroborate
|
|
15208
|
+
* or refute it, while a DIFFERENT session on the same checkout may (alyssa,
|
|
15209
|
+
* 2026-07-31 — the workspace key made every witness on a single-checkout
|
|
15210
|
+
* deployment a self-corroboration). Optional + additive: absent leaves the
|
|
15211
|
+
* gate fail-open for that node, exactly today's behaviour. */
|
|
15212
|
+
originSession: external_exports.string().min(3).max(64).optional(),
|
|
15213
|
+
/** Epoch ms of the ORIGINATING local node's creation — when the knowledge was
|
|
15214
|
+
* actually captured, as opposed to when its public twin reached the cloud.
|
|
15215
|
+
* Stored as `observedAt`; the network board's chronological view orders on
|
|
15216
|
+
* it. Without this the wire carried NO timestamp at all, so a node captured
|
|
15217
|
+
* days ago surfaced as "new" the moment it was generalized and published —
|
|
15218
|
+
* the board showed ingest order wearing a chronology's clothes. Optional +
|
|
15219
|
+
* additive: absent keeps ingest-time ordering for that node. */
|
|
15220
|
+
originCreatedAtMs: external_exports.number().int().positive().optional(),
|
|
15159
15221
|
extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
|
|
15160
15222
|
validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
|
|
15161
15223
|
/** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
|
|
15162
15224
|
* knows which packages are private. The door does NOT trust a `public` claim
|
|
15163
15225
|
* blindly (it re-confirms on the spine); absent ⇒ unknown ⇒ fail-closed to
|
|
15164
15226
|
* org-private. Accepted-but-ignored until ORG_MEMBRANE_ENABLED flips. */
|
|
15165
|
-
anchorVisibility: external_exports.enum(["public", "private"]).optional()
|
|
15227
|
+
anchorVisibility: external_exports.enum(["public", "private"]).optional(),
|
|
15228
|
+
/** The spine-resolvable anchor id backing a `public` tag (OM-anchor-tag): a
|
|
15229
|
+
* Package purl or `languageCanonicalId`. The door validates THIS on the
|
|
15230
|
+
* public spine (falling back to `canonicalId` when absent — context stubs
|
|
15231
|
+
* are self-anchored). Never trusted without spine confirmation. */
|
|
15232
|
+
anchor: external_exports.string().min(1).max(300).optional()
|
|
15166
15233
|
});
|
|
15167
15234
|
RouteContextCountWireSchema = external_exports.object({
|
|
15168
15235
|
confirmed: external_exports.number().int().min(0),
|
|
@@ -15262,6 +15329,7 @@ var init_src = __esm({
|
|
|
15262
15329
|
"../../packages/shared/src/index.ts"() {
|
|
15263
15330
|
"use strict";
|
|
15264
15331
|
init_castalia();
|
|
15332
|
+
init_graph_events();
|
|
15265
15333
|
init_identity();
|
|
15266
15334
|
init_wire();
|
|
15267
15335
|
init_edge_rules();
|
|
@@ -15323,6 +15391,51 @@ var init_review = __esm({
|
|
|
15323
15391
|
}
|
|
15324
15392
|
});
|
|
15325
15393
|
|
|
15394
|
+
// ../../packages/local-shared/src/anchorable.ts
|
|
15395
|
+
function anchorableExtensionAlternation() {
|
|
15396
|
+
return BY_LENGTH.map((e) => e.slice(1).replace(/[+.]/g, (c) => `\\${c}`)).join("|");
|
|
15397
|
+
}
|
|
15398
|
+
var ANCHORABLE_EXTENSIONS, BY_LENGTH;
|
|
15399
|
+
var init_anchorable = __esm({
|
|
15400
|
+
"../../packages/local-shared/src/anchorable.ts"() {
|
|
15401
|
+
"use strict";
|
|
15402
|
+
ANCHORABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
15403
|
+
// typescript provider
|
|
15404
|
+
".ts",
|
|
15405
|
+
".tsx",
|
|
15406
|
+
".js",
|
|
15407
|
+
".jsx",
|
|
15408
|
+
".mjs",
|
|
15409
|
+
".cjs",
|
|
15410
|
+
// python / go / rust / ruby / csharp providers
|
|
15411
|
+
".py",
|
|
15412
|
+
".go",
|
|
15413
|
+
".rs",
|
|
15414
|
+
".rb",
|
|
15415
|
+
".cs",
|
|
15416
|
+
// cpp provider — every variant it parses, not just the three that were listed
|
|
15417
|
+
".c",
|
|
15418
|
+
".h",
|
|
15419
|
+
".cpp",
|
|
15420
|
+
".cc",
|
|
15421
|
+
".cxx",
|
|
15422
|
+
".c++",
|
|
15423
|
+
".hpp",
|
|
15424
|
+
".hh",
|
|
15425
|
+
".hxx",
|
|
15426
|
+
".h++",
|
|
15427
|
+
// CUDA — the cpp provider parses these too. Missed when this list was first
|
|
15428
|
+
// transcribed by hand; the parity test caught them on its very first run,
|
|
15429
|
+
// which is the argument for the test existing.
|
|
15430
|
+
".cu",
|
|
15431
|
+
".cuh",
|
|
15432
|
+
// no provider yet; the grammar ships. Inert, not wrong — see above.
|
|
15433
|
+
".java"
|
|
15434
|
+
]);
|
|
15435
|
+
BY_LENGTH = [...ANCHORABLE_EXTENSIONS].sort((a, b) => b.length - a.length);
|
|
15436
|
+
}
|
|
15437
|
+
});
|
|
15438
|
+
|
|
15326
15439
|
// ../../packages/local-shared/src/sqlite-adapter.ts
|
|
15327
15440
|
function openDatabase(path) {
|
|
15328
15441
|
const db = new DatabaseSync(path);
|
|
@@ -15335,6 +15448,7 @@ function openDatabase(path) {
|
|
|
15335
15448
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15336
15449
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15337
15450
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15451
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15338
15452
|
} catch {
|
|
15339
15453
|
}
|
|
15340
15454
|
}
|
|
@@ -15370,7 +15484,7 @@ function openDatabase(path) {
|
|
|
15370
15484
|
return db.prepare(`PRAGMA ${key}`).get();
|
|
15371
15485
|
},
|
|
15372
15486
|
transaction(fn) {
|
|
15373
|
-
db.exec("BEGIN");
|
|
15487
|
+
db.exec("BEGIN IMMEDIATE");
|
|
15374
15488
|
try {
|
|
15375
15489
|
const r = fn();
|
|
15376
15490
|
db.exec("COMMIT");
|
|
@@ -15424,10 +15538,92 @@ var init_src2 = __esm({
|
|
|
15424
15538
|
init_profile();
|
|
15425
15539
|
init_daemon_wire();
|
|
15426
15540
|
init_review();
|
|
15541
|
+
init_anchorable();
|
|
15427
15542
|
init_sqlite_adapter();
|
|
15428
15543
|
}
|
|
15429
15544
|
});
|
|
15430
15545
|
|
|
15546
|
+
// ../../packages/indexer/src/parse-cache.ts
|
|
15547
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
15548
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
15549
|
+
import { homedir } from "node:os";
|
|
15550
|
+
import { join } from "node:path";
|
|
15551
|
+
function parseCacheDir() {
|
|
15552
|
+
return process.env["ERRATA_PARSE_CACHE"] ?? join(homedir(), ".errata", "parse-cache");
|
|
15553
|
+
}
|
|
15554
|
+
function parseCacheKey(source, providerId) {
|
|
15555
|
+
return createHash2("sha256").update(`${PARSE_CACHE_VERSION}:${providerId}:${source}`).digest("hex");
|
|
15556
|
+
}
|
|
15557
|
+
function entryPath(dir, key) {
|
|
15558
|
+
return join(dir, key.slice(0, 2), `${key.slice(2)}.json`);
|
|
15559
|
+
}
|
|
15560
|
+
function readParseCache(dir, key) {
|
|
15561
|
+
const file2 = entryPath(dir, key);
|
|
15562
|
+
try {
|
|
15563
|
+
const raw = readFileSync(file2, "utf8");
|
|
15564
|
+
const parsed = JSON.parse(raw);
|
|
15565
|
+
if (parsed.v !== PARSE_CACHE_VERSION || !Array.isArray(parsed.symbols)) return null;
|
|
15566
|
+
return { symbols: parsed.symbols, reExports: parsed.reExports ?? [] };
|
|
15567
|
+
} catch {
|
|
15568
|
+
return null;
|
|
15569
|
+
}
|
|
15570
|
+
}
|
|
15571
|
+
function writeParseCache(dir, key, providerId, value) {
|
|
15572
|
+
try {
|
|
15573
|
+
const envelope = {
|
|
15574
|
+
v: PARSE_CACHE_VERSION,
|
|
15575
|
+
provider: providerId,
|
|
15576
|
+
symbols: value.symbols,
|
|
15577
|
+
reExports: value.reExports
|
|
15578
|
+
};
|
|
15579
|
+
const body2 = JSON.stringify(envelope);
|
|
15580
|
+
if (body2.length > MAX_ENTRY_BYTES) return;
|
|
15581
|
+
const file2 = entryPath(dir, key);
|
|
15582
|
+
mkdirSync(join(dir, key.slice(0, 2)), { recursive: true });
|
|
15583
|
+
writeFileSync(file2, body2);
|
|
15584
|
+
} catch {
|
|
15585
|
+
}
|
|
15586
|
+
}
|
|
15587
|
+
function sweepParseCacheOnce(dir, now = Date.now()) {
|
|
15588
|
+
if (sweptThisProcess) return 0;
|
|
15589
|
+
sweptThisProcess = true;
|
|
15590
|
+
let removed = 0;
|
|
15591
|
+
try {
|
|
15592
|
+
if (!existsSync(dir)) return 0;
|
|
15593
|
+
for (const bucket of readdirSync(dir)) {
|
|
15594
|
+
const bucketDir = join(dir, bucket);
|
|
15595
|
+
let names;
|
|
15596
|
+
try {
|
|
15597
|
+
names = readdirSync(bucketDir);
|
|
15598
|
+
} catch {
|
|
15599
|
+
continue;
|
|
15600
|
+
}
|
|
15601
|
+
for (const name2 of names) {
|
|
15602
|
+
const file2 = join(bucketDir, name2);
|
|
15603
|
+
try {
|
|
15604
|
+
if (now - statSync(file2).mtimeMs > ENTRY_TTL_MS) {
|
|
15605
|
+
rmSync(file2, { force: true });
|
|
15606
|
+
removed++;
|
|
15607
|
+
}
|
|
15608
|
+
} catch {
|
|
15609
|
+
}
|
|
15610
|
+
}
|
|
15611
|
+
}
|
|
15612
|
+
} catch {
|
|
15613
|
+
}
|
|
15614
|
+
return removed;
|
|
15615
|
+
}
|
|
15616
|
+
var PARSE_CACHE_VERSION, ENTRY_TTL_MS, MAX_ENTRY_BYTES, sweptThisProcess;
|
|
15617
|
+
var init_parse_cache = __esm({
|
|
15618
|
+
"../../packages/indexer/src/parse-cache.ts"() {
|
|
15619
|
+
"use strict";
|
|
15620
|
+
PARSE_CACHE_VERSION = 1;
|
|
15621
|
+
ENTRY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
15622
|
+
MAX_ENTRY_BYTES = 2 * 1024 * 1024;
|
|
15623
|
+
sweptThisProcess = false;
|
|
15624
|
+
}
|
|
15625
|
+
});
|
|
15626
|
+
|
|
15431
15627
|
// ../../packages/indexer/src/simhash.ts
|
|
15432
15628
|
function fnv1a64(s) {
|
|
15433
15629
|
let h = FNV_OFFSET;
|
|
@@ -15446,7 +15642,7 @@ function popcount(x) {
|
|
|
15446
15642
|
}
|
|
15447
15643
|
return c;
|
|
15448
15644
|
}
|
|
15449
|
-
function
|
|
15645
|
+
function tokenize2(text) {
|
|
15450
15646
|
return text.match(/[A-Za-z_$][A-Za-z0-9_$]*|\d+|[^\s\w]/g) ?? [];
|
|
15451
15647
|
}
|
|
15452
15648
|
function shingle(tokens, n = SHINGLE_N) {
|
|
@@ -15475,7 +15671,7 @@ function simhashFeatures(features) {
|
|
|
15475
15671
|
return out2;
|
|
15476
15672
|
}
|
|
15477
15673
|
function simhash(text) {
|
|
15478
|
-
return simhashFeatures(shingle(
|
|
15674
|
+
return simhashFeatures(shingle(tokenize2(text)));
|
|
15479
15675
|
}
|
|
15480
15676
|
function hammingDistance(a, b) {
|
|
15481
15677
|
return popcount((a ^ b) & MASK64);
|
|
@@ -15651,10 +15847,10 @@ var init_identity2 = __esm({
|
|
|
15651
15847
|
});
|
|
15652
15848
|
|
|
15653
15849
|
// ../../packages/indexer/src/pipeline.ts
|
|
15654
|
-
import { appendFileSync, readFileSync, statSync } from "node:fs";
|
|
15850
|
+
import { appendFileSync, readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
15655
15851
|
import { readdir } from "node:fs/promises";
|
|
15656
|
-
import { extname, join, relative, sep } from "node:path";
|
|
15657
|
-
import { createHash as
|
|
15852
|
+
import { extname, join as join2, relative, sep } from "node:path";
|
|
15853
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15658
15854
|
import { execFileSync } from "node:child_process";
|
|
15659
15855
|
function nowTs() {
|
|
15660
15856
|
return indexNow ?? Date.now();
|
|
@@ -15663,7 +15859,7 @@ function fingerprintOf(signature, bodyHash) {
|
|
|
15663
15859
|
return `${signature ?? ""}|${bodyHash ?? ""}`;
|
|
15664
15860
|
}
|
|
15665
15861
|
function sha(s) {
|
|
15666
|
-
return
|
|
15862
|
+
return createHash3("sha256").update(s).digest("hex").slice(0, 16);
|
|
15667
15863
|
}
|
|
15668
15864
|
function fileNodeId(workspaceId, relPath) {
|
|
15669
15865
|
return `file_${sha(workspaceId + ":" + relPath.replace(/\\/g, "/"))}`;
|
|
@@ -15783,10 +15979,11 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15783
15979
|
};
|
|
15784
15980
|
}
|
|
15785
15981
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
15982
|
+
const contentMovedPaths = /* @__PURE__ */ new Set();
|
|
15786
15983
|
for (const rel of [...changedRelPaths]) {
|
|
15787
15984
|
let h;
|
|
15788
15985
|
try {
|
|
15789
|
-
h =
|
|
15986
|
+
h = createHash3("sha256").update(readFileSync2(join2(rootPath, rel))).digest("hex");
|
|
15790
15987
|
} catch {
|
|
15791
15988
|
continue;
|
|
15792
15989
|
}
|
|
@@ -15795,6 +15992,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15795
15992
|
if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id)) {
|
|
15796
15993
|
store2.updateNode(fnode.id, { lastUpdatedAt: t });
|
|
15797
15994
|
changedRelPaths.delete(rel);
|
|
15995
|
+
} else if (!fnode || fnode.attrs["contentHash"] !== h) {
|
|
15996
|
+
contentMovedPaths.add(rel);
|
|
15798
15997
|
}
|
|
15799
15998
|
}
|
|
15800
15999
|
if (changedRelPaths.size === 0) {
|
|
@@ -15833,13 +16032,13 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15833
16032
|
let parsedFiles = 0;
|
|
15834
16033
|
for (const rel of changedRelPaths) {
|
|
15835
16034
|
if (parsedFiles++ > 0) await new Promise((r2) => setImmediate(r2));
|
|
15836
|
-
const abs =
|
|
16035
|
+
const abs = join2(rootPath, ...rel.split("/"));
|
|
15837
16036
|
const ext = extname(abs).toLowerCase();
|
|
15838
16037
|
const provider = providers.find((p) => p.fileExtensions.includes(ext));
|
|
15839
16038
|
if (!provider) continue;
|
|
15840
16039
|
let symbols;
|
|
15841
16040
|
try {
|
|
15842
|
-
symbols = provider.extractSymbols(
|
|
16041
|
+
symbols = provider.extractSymbols(readFileSync2(abs, "utf8"), abs);
|
|
15843
16042
|
} catch {
|
|
15844
16043
|
continue;
|
|
15845
16044
|
}
|
|
@@ -15929,6 +16128,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15929
16128
|
changedRelPaths
|
|
15930
16129
|
});
|
|
15931
16130
|
store2.transaction(() => {
|
|
16131
|
+
store2.reviveReobserved([...changedRelPaths], workspaceId, t);
|
|
15932
16132
|
const versionedAndFile = /* @__PURE__ */ new Set([...VERSIONED_LABELS, "File"]);
|
|
15933
16133
|
const ownedLive = [];
|
|
15934
16134
|
for (const n of store2.nodesByRelPath([...changedRelPaths])) {
|
|
@@ -16037,7 +16237,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
16037
16237
|
const h = fileHashes.get(rel);
|
|
16038
16238
|
if (!h) continue;
|
|
16039
16239
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
16040
|
-
if (fnode)
|
|
16240
|
+
if (!fnode) continue;
|
|
16241
|
+
store2.updateNode(fnode.id, {
|
|
16242
|
+
attrs: {
|
|
16243
|
+
...fnode.attrs,
|
|
16244
|
+
contentHash: h,
|
|
16245
|
+
...contentMovedPaths.has(rel) ? { contentChangedAt: t } : {}
|
|
16246
|
+
}
|
|
16247
|
+
});
|
|
16041
16248
|
}
|
|
16042
16249
|
return {
|
|
16043
16250
|
filesReindexed: changedRelPaths.size,
|
|
@@ -16085,8 +16292,13 @@ async function runIndexer(store2, opts) {
|
|
|
16085
16292
|
byLanguage: {},
|
|
16086
16293
|
durationMs: 0,
|
|
16087
16294
|
nodesPurged: 0,
|
|
16088
|
-
edgesPurged: 0
|
|
16295
|
+
edgesPurged: 0,
|
|
16296
|
+
parseCacheHits: 0,
|
|
16297
|
+
parseCacheMisses: 0
|
|
16089
16298
|
};
|
|
16299
|
+
const cacheDir = parseCacheDir();
|
|
16300
|
+
const parseCacheEnabled = cacheDir !== "";
|
|
16301
|
+
if (parseCacheEnabled) sweepParseCacheOnce(cacheDir);
|
|
16090
16302
|
if (opts.clean) {
|
|
16091
16303
|
const purge = purgeWorkspaceCodeGraph(store2, opts.workspaceId);
|
|
16092
16304
|
report.nodesPurged = purge.nodes;
|
|
@@ -16140,25 +16352,38 @@ async function runIndexer(store2, opts) {
|
|
|
16140
16352
|
}
|
|
16141
16353
|
let source;
|
|
16142
16354
|
try {
|
|
16143
|
-
const st =
|
|
16355
|
+
const st = statSync2(f.absPath);
|
|
16144
16356
|
if (st.size > maxBytes) {
|
|
16145
16357
|
report.filesSkipped++;
|
|
16146
16358
|
continue;
|
|
16147
16359
|
}
|
|
16148
|
-
source =
|
|
16360
|
+
source = readFileSync2(f.absPath, "utf8");
|
|
16149
16361
|
} catch {
|
|
16150
16362
|
report.filesSkipped++;
|
|
16151
16363
|
continue;
|
|
16152
16364
|
}
|
|
16365
|
+
const cacheKey = parseCacheKey(source, f.provider.id);
|
|
16366
|
+
const cached2 = parseCacheEnabled ? readParseCache(cacheDir, cacheKey) : null;
|
|
16153
16367
|
let symbols;
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16368
|
+
let reExports;
|
|
16369
|
+
if (cached2) {
|
|
16370
|
+
symbols = cached2.symbols;
|
|
16371
|
+
reExports = cached2.reExports;
|
|
16372
|
+
report.parseCacheHits++;
|
|
16373
|
+
} else {
|
|
16374
|
+
try {
|
|
16375
|
+
symbols = f.provider.extractSymbols(source, f.absPath);
|
|
16376
|
+
} catch {
|
|
16377
|
+
report.filesSkipped++;
|
|
16378
|
+
continue;
|
|
16379
|
+
}
|
|
16380
|
+
reExports = scanReExports(source);
|
|
16381
|
+
report.parseCacheMisses++;
|
|
16382
|
+
if (parseCacheEnabled) {
|
|
16383
|
+
writeParseCache(cacheDir, cacheKey, f.provider.id, { symbols, reExports });
|
|
16384
|
+
}
|
|
16159
16385
|
}
|
|
16160
16386
|
symbolsByFile.set(f.relPath, symbols);
|
|
16161
|
-
const reExports = scanReExports(source);
|
|
16162
16387
|
if (reExports.length > 0) reExportsByFile.set(f.relPath, reExports);
|
|
16163
16388
|
report.filesParsed++;
|
|
16164
16389
|
report.byLanguage[f.provider.id] = (report.byLanguage[f.provider.id] ?? 0) + 1;
|
|
@@ -16203,7 +16428,7 @@ async function runIndexer(store2, opts) {
|
|
|
16203
16428
|
const depth = fileNode.relPath.split("/").length;
|
|
16204
16429
|
if (depth !== 3) continue;
|
|
16205
16430
|
try {
|
|
16206
|
-
const pkg = JSON.parse(
|
|
16431
|
+
const pkg = JSON.parse(readFileSync2(fileNode.absPath, "utf8"));
|
|
16207
16432
|
if (!pkg.name) continue;
|
|
16208
16433
|
const pkgDir = fileNode.relPath.replace(/\/package\.json$/, "");
|
|
16209
16434
|
const candidates = [
|
|
@@ -16585,7 +16810,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16585
16810
|
for (const ent of entries) {
|
|
16586
16811
|
if (ignores.has(ent.name)) continue;
|
|
16587
16812
|
if (ent.name.startsWith(".") && ent.name !== ".") continue;
|
|
16588
|
-
const abs =
|
|
16813
|
+
const abs = join2(current, ent.name);
|
|
16589
16814
|
if (ent.isDirectory()) {
|
|
16590
16815
|
await scan(root, abs, ignores, providers, out2);
|
|
16591
16816
|
} else if (ent.isFile()) {
|
|
@@ -16594,7 +16819,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16594
16819
|
const rel = relative(root, abs).split(sep).join("/");
|
|
16595
16820
|
let size = 0;
|
|
16596
16821
|
try {
|
|
16597
|
-
size =
|
|
16822
|
+
size = statSync2(abs).size;
|
|
16598
16823
|
} catch {
|
|
16599
16824
|
continue;
|
|
16600
16825
|
}
|
|
@@ -16608,7 +16833,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16608
16833
|
}
|
|
16609
16834
|
}
|
|
16610
16835
|
}
|
|
16611
|
-
function
|
|
16836
|
+
function gitListRelPaths(root, ignores) {
|
|
16612
16837
|
let stdout;
|
|
16613
16838
|
try {
|
|
16614
16839
|
stdout = execFileSync(
|
|
@@ -16631,10 +16856,19 @@ function gitListFiles(root, ignores, providers) {
|
|
|
16631
16856
|
if (segs.some((s) => ignores.has(s) || s.startsWith(".") && s.length > 1)) {
|
|
16632
16857
|
continue;
|
|
16633
16858
|
}
|
|
16634
|
-
|
|
16859
|
+
out2.push(rel);
|
|
16860
|
+
}
|
|
16861
|
+
return out2;
|
|
16862
|
+
}
|
|
16863
|
+
function gitListFiles(root, ignores, providers) {
|
|
16864
|
+
const rels = gitListRelPaths(root, ignores);
|
|
16865
|
+
if (rels === null) return null;
|
|
16866
|
+
const out2 = [];
|
|
16867
|
+
for (const rel of rels) {
|
|
16868
|
+
const abs = join2(root, rel);
|
|
16635
16869
|
let size;
|
|
16636
16870
|
try {
|
|
16637
|
-
size =
|
|
16871
|
+
size = statSync2(abs).size;
|
|
16638
16872
|
} catch {
|
|
16639
16873
|
continue;
|
|
16640
16874
|
}
|
|
@@ -16654,7 +16888,7 @@ function upsertFile(store2, id, f, workspaceId) {
|
|
|
16654
16888
|
const now = nowTs();
|
|
16655
16889
|
let contentHash;
|
|
16656
16890
|
try {
|
|
16657
|
-
contentHash =
|
|
16891
|
+
contentHash = createHash3("sha256").update(readFileSync2(f.absPath)).digest("hex");
|
|
16658
16892
|
} catch {
|
|
16659
16893
|
}
|
|
16660
16894
|
const node = {
|
|
@@ -16853,6 +17087,7 @@ var init_pipeline = __esm({
|
|
|
16853
17087
|
"../../packages/indexer/src/pipeline.ts"() {
|
|
16854
17088
|
"use strict";
|
|
16855
17089
|
init_src2();
|
|
17090
|
+
init_parse_cache();
|
|
16856
17091
|
init_identity2();
|
|
16857
17092
|
DEFAULT_IGNORES = /* @__PURE__ */ new Set([
|
|
16858
17093
|
"node_modules",
|
|
@@ -21017,8 +21252,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
21017
21252
|
|
|
21018
21253
|
// ../../packages/indexer/src/languages/tree-sitter-loader.ts
|
|
21019
21254
|
import { fileURLToPath } from "node:url";
|
|
21020
|
-
import { dirname, join as
|
|
21021
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
21255
|
+
import { dirname, join as join3 } from "node:path";
|
|
21256
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
21022
21257
|
import { createRequire } from "node:module";
|
|
21023
21258
|
function entryDir() {
|
|
21024
21259
|
try {
|
|
@@ -21032,27 +21267,27 @@ function entryDir() {
|
|
|
21032
21267
|
}
|
|
21033
21268
|
function findWasmDir() {
|
|
21034
21269
|
const here = entryDir();
|
|
21035
|
-
const seaWasm =
|
|
21036
|
-
if (
|
|
21270
|
+
const seaWasm = join3(here, "resources", "wasm");
|
|
21271
|
+
if (existsSync2(join3(seaWasm, "tree-sitter-typescript.wasm"))) return seaWasm;
|
|
21037
21272
|
let dir = here;
|
|
21038
21273
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21039
|
-
const flat =
|
|
21274
|
+
const flat = join3(
|
|
21040
21275
|
dir,
|
|
21041
21276
|
"node_modules",
|
|
21042
21277
|
"@vscode",
|
|
21043
21278
|
"tree-sitter-wasm",
|
|
21044
21279
|
"wasm"
|
|
21045
21280
|
);
|
|
21046
|
-
if (
|
|
21281
|
+
if (existsSync2(join3(flat, "tree-sitter-typescript.wasm"))) return flat;
|
|
21047
21282
|
dir = dirname(dir);
|
|
21048
21283
|
}
|
|
21049
21284
|
let root = here;
|
|
21050
21285
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21051
|
-
const pnpmDir =
|
|
21052
|
-
if (
|
|
21053
|
-
for (const entry of
|
|
21286
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21287
|
+
if (existsSync2(pnpmDir)) {
|
|
21288
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21054
21289
|
if (entry.startsWith("@vscode+tree-sitter-wasm@")) {
|
|
21055
|
-
const candidate =
|
|
21290
|
+
const candidate = join3(
|
|
21056
21291
|
pnpmDir,
|
|
21057
21292
|
entry,
|
|
21058
21293
|
"node_modules",
|
|
@@ -21060,7 +21295,7 @@ function findWasmDir() {
|
|
|
21060
21295
|
"tree-sitter-wasm",
|
|
21061
21296
|
"wasm"
|
|
21062
21297
|
);
|
|
21063
|
-
if (
|
|
21298
|
+
if (existsSync2(join3(candidate, "tree-sitter-typescript.wasm"))) {
|
|
21064
21299
|
return candidate;
|
|
21065
21300
|
}
|
|
21066
21301
|
}
|
|
@@ -21074,27 +21309,27 @@ function findWasmDir() {
|
|
|
21074
21309
|
}
|
|
21075
21310
|
function findRuntimeDir() {
|
|
21076
21311
|
const here = entryDir();
|
|
21077
|
-
const seaRuntime =
|
|
21078
|
-
if (
|
|
21312
|
+
const seaRuntime = join3(here, "resources", "wasm");
|
|
21313
|
+
if (existsSync2(join3(seaRuntime, "web-tree-sitter.wasm"))) return seaRuntime;
|
|
21079
21314
|
let dir = here;
|
|
21080
21315
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21081
|
-
const flat =
|
|
21082
|
-
if (
|
|
21316
|
+
const flat = join3(dir, "node_modules", "web-tree-sitter");
|
|
21317
|
+
if (existsSync2(join3(flat, "web-tree-sitter.wasm"))) return flat;
|
|
21083
21318
|
dir = dirname(dir);
|
|
21084
21319
|
}
|
|
21085
21320
|
let root = here;
|
|
21086
21321
|
for (let i2 = 0; i2 < 6; i2++) {
|
|
21087
|
-
const pnpmDir =
|
|
21088
|
-
if (
|
|
21089
|
-
for (const entry of
|
|
21322
|
+
const pnpmDir = join3(root, "node_modules", ".pnpm");
|
|
21323
|
+
if (existsSync2(pnpmDir)) {
|
|
21324
|
+
for (const entry of readdirSync2(pnpmDir)) {
|
|
21090
21325
|
if (entry.startsWith("web-tree-sitter@")) {
|
|
21091
|
-
const candidate =
|
|
21326
|
+
const candidate = join3(
|
|
21092
21327
|
pnpmDir,
|
|
21093
21328
|
entry,
|
|
21094
21329
|
"node_modules",
|
|
21095
21330
|
"web-tree-sitter"
|
|
21096
21331
|
);
|
|
21097
|
-
if (
|
|
21332
|
+
if (existsSync2(join3(candidate, "web-tree-sitter.wasm"))) {
|
|
21098
21333
|
return candidate;
|
|
21099
21334
|
}
|
|
21100
21335
|
}
|
|
@@ -21111,7 +21346,7 @@ async function loadWebTreeSitter() {
|
|
|
21111
21346
|
void err2;
|
|
21112
21347
|
}
|
|
21113
21348
|
const here = entryDir();
|
|
21114
|
-
const seaResourceBase =
|
|
21349
|
+
const seaResourceBase = join3(here, "resources", "_resolve.js");
|
|
21115
21350
|
const resourceRequire = createRequire(seaResourceBase);
|
|
21116
21351
|
return resourceRequire("web-tree-sitter");
|
|
21117
21352
|
}
|
|
@@ -21126,9 +21361,9 @@ async function ensureTreeSitterReady() {
|
|
|
21126
21361
|
await Parser2.init({
|
|
21127
21362
|
locateFile: (name2) => {
|
|
21128
21363
|
if (name2 === "tree-sitter.wasm" || name2 === "web-tree-sitter.wasm") {
|
|
21129
|
-
return
|
|
21364
|
+
return join3(runtime, name2);
|
|
21130
21365
|
}
|
|
21131
|
-
return
|
|
21366
|
+
return join3(grammars, name2);
|
|
21132
21367
|
}
|
|
21133
21368
|
});
|
|
21134
21369
|
})();
|
|
@@ -21140,7 +21375,7 @@ async function loadGrammar(name2) {
|
|
|
21140
21375
|
if (cached2) return cached2;
|
|
21141
21376
|
if (!languageClass) throw new Error("tree-sitter not initialized");
|
|
21142
21377
|
const grammars = findWasmDir();
|
|
21143
|
-
const lang = await languageClass.load(
|
|
21378
|
+
const lang = await languageClass.load(join3(grammars, `${name2}.wasm`));
|
|
21144
21379
|
grammarCache.set(name2, lang);
|
|
21145
21380
|
return lang;
|
|
21146
21381
|
}
|
|
@@ -21163,7 +21398,7 @@ var init_tree_sitter_loader = __esm({
|
|
|
21163
21398
|
});
|
|
21164
21399
|
|
|
21165
21400
|
// ../../packages/indexer/src/languages/typescript-treesitter.ts
|
|
21166
|
-
import { createHash as
|
|
21401
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
21167
21402
|
function extractNode(node, source, containerQname, containerKind) {
|
|
21168
21403
|
switch (node.type) {
|
|
21169
21404
|
case "function_declaration":
|
|
@@ -21376,7 +21611,7 @@ function scopeRecord(kind, name2, qname, node, body2, source) {
|
|
|
21376
21611
|
const sig = header.replace(/\s+/g, " ").trim();
|
|
21377
21612
|
if (sig) rec.signature = sig;
|
|
21378
21613
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21379
|
-
rec.bodyHash =
|
|
21614
|
+
rec.bodyHash = createHash4("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21380
21615
|
rec.bodySimhash = toHex(simhash(bodyText));
|
|
21381
21616
|
}
|
|
21382
21617
|
}
|
|
@@ -21818,14 +22053,14 @@ var init_typescript_treesitter = __esm({
|
|
|
21818
22053
|
});
|
|
21819
22054
|
|
|
21820
22055
|
// ../../packages/indexer/src/languages/python-treesitter.ts
|
|
21821
|
-
import { createHash as
|
|
22056
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21822
22057
|
function sigAndHash(node, body2, source) {
|
|
21823
22058
|
if (!body2) return {};
|
|
21824
22059
|
const out2 = {};
|
|
21825
22060
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
21826
22061
|
if (sig) out2.signature = sig;
|
|
21827
22062
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
21828
|
-
out2.bodyHash =
|
|
22063
|
+
out2.bodyHash = createHash5("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
21829
22064
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
21830
22065
|
return out2;
|
|
21831
22066
|
}
|
|
@@ -22238,7 +22473,7 @@ var init_python_treesitter = __esm({
|
|
|
22238
22473
|
});
|
|
22239
22474
|
|
|
22240
22475
|
// ../../packages/indexer/src/languages/cpp-treesitter.ts
|
|
22241
|
-
import { createHash as
|
|
22476
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22242
22477
|
function extractNode3(node, containerQname, containerIsClass, source) {
|
|
22243
22478
|
switch (node.type) {
|
|
22244
22479
|
case "function_definition":
|
|
@@ -22422,7 +22657,7 @@ function sigAndHash2(node, body2, source) {
|
|
|
22422
22657
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22423
22658
|
if (sig) out2.signature = sig;
|
|
22424
22659
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22425
|
-
out2.bodyHash =
|
|
22660
|
+
out2.bodyHash = createHash6("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22426
22661
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22427
22662
|
return out2;
|
|
22428
22663
|
}
|
|
@@ -22583,7 +22818,7 @@ var init_cpp_treesitter = __esm({
|
|
|
22583
22818
|
});
|
|
22584
22819
|
|
|
22585
22820
|
// ../../packages/indexer/src/languages/go-treesitter.ts
|
|
22586
|
-
import { createHash as
|
|
22821
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
22587
22822
|
function extractNode4(node, containerQname, source) {
|
|
22588
22823
|
switch (node.type) {
|
|
22589
22824
|
case "function_declaration": {
|
|
@@ -22745,7 +22980,7 @@ function sigAndHash3(node, body2, source) {
|
|
|
22745
22980
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
22746
22981
|
if (sig) out2.signature = sig;
|
|
22747
22982
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
22748
|
-
out2.bodyHash =
|
|
22983
|
+
out2.bodyHash = createHash7("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
22749
22984
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
22750
22985
|
return out2;
|
|
22751
22986
|
}
|
|
@@ -22889,7 +23124,7 @@ var init_go_treesitter = __esm({
|
|
|
22889
23124
|
});
|
|
22890
23125
|
|
|
22891
23126
|
// ../../packages/indexer/src/languages/rust-treesitter.ts
|
|
22892
|
-
import { createHash as
|
|
23127
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22893
23128
|
function extractNode5(node, containerQname, containerIsClass, source) {
|
|
22894
23129
|
switch (node.type) {
|
|
22895
23130
|
case "function_item":
|
|
@@ -23104,7 +23339,7 @@ function sigAndHash4(node, body2, source) {
|
|
|
23104
23339
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23105
23340
|
if (sig) out2.signature = sig;
|
|
23106
23341
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23107
|
-
out2.bodyHash =
|
|
23342
|
+
out2.bodyHash = createHash8("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23108
23343
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23109
23344
|
return out2;
|
|
23110
23345
|
}
|
|
@@ -23255,7 +23490,7 @@ var init_rust_treesitter = __esm({
|
|
|
23255
23490
|
});
|
|
23256
23491
|
|
|
23257
23492
|
// ../../packages/indexer/src/languages/ruby-treesitter.ts
|
|
23258
|
-
import { createHash as
|
|
23493
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
23259
23494
|
function sigAndHash5(node, body2, source) {
|
|
23260
23495
|
if (!body2) {
|
|
23261
23496
|
const sig2 = source.slice(node.startIndex, node.endIndex).replace(/\s+/g, " ").trim();
|
|
@@ -23265,7 +23500,7 @@ function sigAndHash5(node, body2, source) {
|
|
|
23265
23500
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23266
23501
|
if (sig) out2.signature = sig;
|
|
23267
23502
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23268
|
-
out2.bodyHash =
|
|
23503
|
+
out2.bodyHash = createHash9("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23269
23504
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23270
23505
|
return out2;
|
|
23271
23506
|
}
|
|
@@ -23611,7 +23846,7 @@ var init_ruby_treesitter = __esm({
|
|
|
23611
23846
|
});
|
|
23612
23847
|
|
|
23613
23848
|
// ../../packages/indexer/src/languages/csharp-treesitter.ts
|
|
23614
|
-
import { createHash as
|
|
23849
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23615
23850
|
function extractNode7(node, containerQname, containerIsType, source, eof) {
|
|
23616
23851
|
switch (node.type) {
|
|
23617
23852
|
case "method_declaration":
|
|
@@ -23813,7 +24048,7 @@ function sigAndHash6(node, body2, source) {
|
|
|
23813
24048
|
const sig = source.slice(node.startIndex, body2.startIndex).replace(/\s+/g, " ").trim();
|
|
23814
24049
|
if (sig) out2.signature = sig;
|
|
23815
24050
|
const bodyText = source.slice(body2.startIndex, body2.endIndex);
|
|
23816
|
-
out2.bodyHash =
|
|
24051
|
+
out2.bodyHash = createHash10("sha256").update(bodyText).digest("hex").slice(0, 16);
|
|
23817
24052
|
out2.bodySimhash = toHex(simhash(bodyText));
|
|
23818
24053
|
return out2;
|
|
23819
24054
|
}
|
|
@@ -23970,6 +24205,7 @@ var init_csharp_treesitter = __esm({
|
|
|
23970
24205
|
// ../../packages/indexer/src/index.ts
|
|
23971
24206
|
var src_exports = {};
|
|
23972
24207
|
__export(src_exports, {
|
|
24208
|
+
DEFAULT_IGNORES: () => DEFAULT_IGNORES,
|
|
23973
24209
|
DRIFT_FORK_RATIO: () => DRIFT_FORK_RATIO,
|
|
23974
24210
|
TreeSitterCSharpProvider: () => TreeSitterCSharpProvider,
|
|
23975
24211
|
TreeSitterCppProvider: () => TreeSitterCppProvider,
|
|
@@ -23986,6 +24222,7 @@ __export(src_exports, {
|
|
|
23986
24222
|
findInnermostScope: () => findInnermostScope,
|
|
23987
24223
|
folderNodeId: () => folderNodeId,
|
|
23988
24224
|
fromHex: () => fromHex,
|
|
24225
|
+
gitListRelPaths: () => gitListRelPaths,
|
|
23989
24226
|
hammingDistance: () => hammingDistance,
|
|
23990
24227
|
hasForkedDrift: () => hasForkedDrift,
|
|
23991
24228
|
incrementalReindex: () => incrementalReindex,
|
|
@@ -23998,7 +24235,7 @@ __export(src_exports, {
|
|
|
23998
24235
|
simhashFeatures: () => simhashFeatures,
|
|
23999
24236
|
symbolNodeId: () => symbolNodeId,
|
|
24000
24237
|
toHex: () => toHex,
|
|
24001
|
-
tokenize: () =>
|
|
24238
|
+
tokenize: () => tokenize2
|
|
24002
24239
|
});
|
|
24003
24240
|
function defaultProviders() {
|
|
24004
24241
|
if (providersCache) return providersCache;
|
|
@@ -24051,9 +24288,32 @@ var LOCAL_RULE_OVERRIDES = {
|
|
|
24051
24288
|
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24052
24289
|
},
|
|
24053
24290
|
REVEALED_BY: null,
|
|
24054
|
-
PRODUCED: null
|
|
24291
|
+
PRODUCED: null,
|
|
24292
|
+
// FIXED_BY locally ALSO carries the fix-provenance sense: `resolveProblem`
|
|
24293
|
+
// attributes a closed Problem to the fixing `Episode` (PLAN_PLASTICITY §2.1)
|
|
24294
|
+
// alongside the cloud's Problem→Solution knowledge claim. Same shape as
|
|
24295
|
+
// PRODUCED/REVEALED_BY above — an Episode is code-layer and never drains, so
|
|
24296
|
+
// the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
|
|
24297
|
+
// stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
|
|
24298
|
+
// this rule exists at all.
|
|
24299
|
+
FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] }
|
|
24055
24300
|
};
|
|
24056
|
-
|
|
24301
|
+
function localEdgeViolation(fromLabel, type, toLabel) {
|
|
24302
|
+
if (type in LOCAL_RULE_OVERRIDES) {
|
|
24303
|
+
const rule = LOCAL_RULE_OVERRIDES[type];
|
|
24304
|
+
if (!rule) return null;
|
|
24305
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
24306
|
+
return `${type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
24307
|
+
}
|
|
24308
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
24309
|
+
return `${type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24310
|
+
}
|
|
24311
|
+
return null;
|
|
24312
|
+
}
|
|
24313
|
+
const verdict = isValidEdge(fromLabel, type, toLabel);
|
|
24314
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
24315
|
+
}
|
|
24316
|
+
var SCHEMA_VERSION = 6;
|
|
24057
24317
|
var SCHEMA_SQL = `
|
|
24058
24318
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
24059
24319
|
version INTEGER PRIMARY KEY
|
|
@@ -24068,6 +24328,21 @@ CREATE TABLE IF NOT EXISTS store_meta (
|
|
|
24068
24328
|
value TEXT NOT NULL
|
|
24069
24329
|
);
|
|
24070
24330
|
|
|
24331
|
+
-- Durable ledger of edges the ontology gate REFUSED, keyed by edge type.
|
|
24332
|
+
-- Durable rather than in-memory for one specific reason: the status command runs
|
|
24333
|
+
-- in a SEPARATE process and opens its own store handle, so a counter living on
|
|
24334
|
+
-- the instance reads 0 there forever. That is exactly how a producer rejecting
|
|
24335
|
+
-- 100% of its output stayed invisible for 17 days. Persisting it also survives
|
|
24336
|
+
-- the daemon restart that would otherwise erase the evidence.
|
|
24337
|
+
-- Keyed by type because a systematic producer bug shows up as ONE type
|
|
24338
|
+
-- dominating; sample keeps the latest reason so the count is actionable.
|
|
24339
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24340
|
+
type TEXT PRIMARY KEY,
|
|
24341
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24342
|
+
last_at INTEGER NOT NULL,
|
|
24343
|
+
sample TEXT
|
|
24344
|
+
);
|
|
24345
|
+
|
|
24071
24346
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
24072
24347
|
id TEXT PRIMARY KEY,
|
|
24073
24348
|
label TEXT NOT NULL,
|
|
@@ -24330,6 +24605,13 @@ var SqliteGraphStore = class {
|
|
|
24330
24605
|
findByLabel: this.db.prepare(
|
|
24331
24606
|
"SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
24332
24607
|
),
|
|
24608
|
+
// Scalar count twin of findByLabel — same live-row semantics, no row
|
|
24609
|
+
// hydration. Exists because status surfaces (daemon `/` + `/health`) used
|
|
24610
|
+
// findNodesByLabel(...).length, materializing every row's attrs JSON and
|
|
24611
|
+
// embedding blob per request — seconds of synchronous loop-hold per poll.
|
|
24612
|
+
countByLabel: this.db.prepare(
|
|
24613
|
+
"SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
|
|
24614
|
+
),
|
|
24333
24615
|
// ALL versions of a label — incl frozen/closed (valid_to set). Used by the
|
|
24334
24616
|
// clean-reindex purge so a true wipe removes history too, not just live rows.
|
|
24335
24617
|
findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
|
|
@@ -24437,6 +24719,16 @@ var SqliteGraphStore = class {
|
|
|
24437
24719
|
if (!cols.has(name2)) this.db.exec(ddl);
|
|
24438
24720
|
}
|
|
24439
24721
|
}
|
|
24722
|
+
if (from < 6) {
|
|
24723
|
+
this.db.exec(`
|
|
24724
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24725
|
+
type TEXT PRIMARY KEY,
|
|
24726
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24727
|
+
last_at INTEGER NOT NULL,
|
|
24728
|
+
sample TEXT
|
|
24729
|
+
)
|
|
24730
|
+
`);
|
|
24731
|
+
}
|
|
24440
24732
|
this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
|
|
24441
24733
|
});
|
|
24442
24734
|
}
|
|
@@ -24518,6 +24810,7 @@ var SqliteGraphStore = class {
|
|
|
24518
24810
|
const violation = this.edgeRuleViolation(edge);
|
|
24519
24811
|
if (violation) {
|
|
24520
24812
|
this.rejectedEdgeCount++;
|
|
24813
|
+
this.recordEdgeRejection(edge.type, violation, edge.lastSeenAt || edge.createdAt || 0);
|
|
24521
24814
|
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24522
24815
|
return;
|
|
24523
24816
|
}
|
|
@@ -24542,22 +24835,51 @@ var SqliteGraphStore = class {
|
|
|
24542
24835
|
* overlay consulted first. Returns the reason string on a documented-
|
|
24543
24836
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24544
24837
|
* next to the insert itself. */
|
|
24545
|
-
|
|
24546
|
-
|
|
24547
|
-
|
|
24548
|
-
|
|
24549
|
-
|
|
24550
|
-
|
|
24551
|
-
|
|
24552
|
-
|
|
24553
|
-
|
|
24554
|
-
|
|
24555
|
-
|
|
24556
|
-
|
|
24557
|
-
|
|
24838
|
+
/** Upsert one refusal into the durable ledger. Best-effort: a bookkeeping
|
|
24839
|
+
* failure must never turn a refused edge into a thrown write. */
|
|
24840
|
+
recordEdgeRejection(type, reason, at) {
|
|
24841
|
+
try {
|
|
24842
|
+
this.db.prepare(
|
|
24843
|
+
`INSERT INTO edge_rejections (type, count, last_at, sample) VALUES (?, 1, ?, ?)
|
|
24844
|
+
ON CONFLICT(type) DO UPDATE SET
|
|
24845
|
+
count = count + 1, last_at = excluded.last_at, sample = excluded.sample`
|
|
24846
|
+
).run(type, at, reason.slice(0, 200));
|
|
24847
|
+
} catch {
|
|
24848
|
+
}
|
|
24849
|
+
}
|
|
24850
|
+
/** Refusals recorded by the ontology gate, per edge type, newest activity first.
|
|
24851
|
+
* Durable across restarts and readable from any process (see the table note). */
|
|
24852
|
+
edgeRejections() {
|
|
24853
|
+
try {
|
|
24854
|
+
return this.db.prepare(
|
|
24855
|
+
"SELECT type, count, last_at AS lastAt, sample FROM edge_rejections ORDER BY count DESC, last_at DESC"
|
|
24856
|
+
).all();
|
|
24857
|
+
} catch {
|
|
24858
|
+
return [];
|
|
24859
|
+
}
|
|
24860
|
+
}
|
|
24861
|
+
/** Drop ledger entries whose last refusal predates `cutoff`. A still-misbehaving
|
|
24862
|
+
* producer keeps refreshing `last_at` and survives; a fixed one fades out. */
|
|
24863
|
+
pruneEdgeRejections(cutoff) {
|
|
24864
|
+
try {
|
|
24865
|
+
this.db.prepare("DELETE FROM edge_rejections WHERE last_at < ?").run(cutoff);
|
|
24866
|
+
} catch {
|
|
24867
|
+
}
|
|
24868
|
+
}
|
|
24869
|
+
/** Clear the ledger outright, whole or per type — operator escape hatch. */
|
|
24870
|
+
clearEdgeRejections(type) {
|
|
24871
|
+
try {
|
|
24872
|
+
if (type) this.db.prepare("DELETE FROM edge_rejections WHERE type = ?").run(type);
|
|
24873
|
+
else this.db.exec("DELETE FROM edge_rejections");
|
|
24874
|
+
} catch {
|
|
24558
24875
|
}
|
|
24559
|
-
|
|
24560
|
-
|
|
24876
|
+
}
|
|
24877
|
+
edgeRuleViolation(edge) {
|
|
24878
|
+
return localEdgeViolation(
|
|
24879
|
+
this.getNode(edge.from)?.label,
|
|
24880
|
+
edge.type,
|
|
24881
|
+
this.getNode(edge.to)?.label
|
|
24882
|
+
);
|
|
24561
24883
|
}
|
|
24562
24884
|
updateEdge(id, patch) {
|
|
24563
24885
|
this.stmts.updateEdge.run({
|
|
@@ -24583,6 +24905,13 @@ var SqliteGraphStore = class {
|
|
|
24583
24905
|
const rows = this.stmts.findByLabel.all(label);
|
|
24584
24906
|
return rows.map(rowToNode);
|
|
24585
24907
|
}
|
|
24908
|
+
/** Live-row count for a label — `findNodesByLabel(label).length` without the
|
|
24909
|
+
* per-row hydration (attrs JSON + embedding blob). Status surfaces poll this
|
|
24910
|
+
* per project per request; the materializing form held the daemon's event
|
|
24911
|
+
* loop for seconds at scale. */
|
|
24912
|
+
countNodesByLabel(label) {
|
|
24913
|
+
return Number(this.stmts.countByLabel.get(label).n);
|
|
24914
|
+
}
|
|
24586
24915
|
findAllVersionsByLabel(label) {
|
|
24587
24916
|
const rows = this.stmts.findByLabelAll.all(label);
|
|
24588
24917
|
return rows.map(rowToNode);
|
|
@@ -24687,6 +25016,163 @@ var SqliteGraphStore = class {
|
|
|
24687
25016
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
24688
25017
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
24689
25018
|
}
|
|
25019
|
+
getMeta(key) {
|
|
25020
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
25021
|
+
return r?.value ?? null;
|
|
25022
|
+
}
|
|
25023
|
+
setMeta(key, value) {
|
|
25024
|
+
this.db.prepare(
|
|
25025
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
25026
|
+
).run({ key, value });
|
|
25027
|
+
}
|
|
25028
|
+
dirtyNodeIdsSince(ts) {
|
|
25029
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
25030
|
+
return rows.map((r) => r.id);
|
|
25031
|
+
}
|
|
25032
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
25033
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
25034
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
25035
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
25036
|
+
edgeEndpointsTouchedSince(ts) {
|
|
25037
|
+
const rows = this.db.prepare(
|
|
25038
|
+
`SELECT from_id, to_id FROM edges
|
|
25039
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
25040
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
25041
|
+
).all({ ts });
|
|
25042
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
25043
|
+
}
|
|
25044
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
25045
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
25046
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
25047
|
+
outEdgesForMany(ids) {
|
|
25048
|
+
return this.edgesForMany(ids, "from_id");
|
|
25049
|
+
}
|
|
25050
|
+
inEdgesForMany(ids) {
|
|
25051
|
+
return this.edgesForMany(ids, "to_id");
|
|
25052
|
+
}
|
|
25053
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
25054
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
25055
|
+
ranksForMany(ids) {
|
|
25056
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
25057
|
+
const CHUNK = 400;
|
|
25058
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25059
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
25060
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
25061
|
+
const rows = this.db.prepare(
|
|
25062
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
25063
|
+
).all(...chunk);
|
|
25064
|
+
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
25065
|
+
}
|
|
25066
|
+
return out2;
|
|
25067
|
+
}
|
|
25068
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
25069
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
25070
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
25071
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
25072
|
+
* scanLiveNodes materialization. */
|
|
25073
|
+
landmarkSweepRows() {
|
|
25074
|
+
const rows = this.db.prepare(
|
|
25075
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
25076
|
+
WHERE valid_to IS NULL
|
|
25077
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
25078
|
+
).all();
|
|
25079
|
+
return rows.map((r) => ({
|
|
25080
|
+
id: r.id,
|
|
25081
|
+
label: r.label,
|
|
25082
|
+
pageRank: r.page_rank,
|
|
25083
|
+
isLandmark: r.is_landmark === 1,
|
|
25084
|
+
memoryTier: r.memory_tier,
|
|
25085
|
+
extractionSource: r.extraction_source
|
|
25086
|
+
}));
|
|
25087
|
+
}
|
|
25088
|
+
edgesForMany(ids, col) {
|
|
25089
|
+
const out2 = [];
|
|
25090
|
+
const CHUNK = 400;
|
|
25091
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25092
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
25093
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
25094
|
+
const rows = this.db.prepare(
|
|
25095
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
25096
|
+
).all(...chunk);
|
|
25097
|
+
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
25098
|
+
}
|
|
25099
|
+
return out2;
|
|
25100
|
+
}
|
|
25101
|
+
checkpointWal() {
|
|
25102
|
+
try {
|
|
25103
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
25104
|
+
return r ?? null;
|
|
25105
|
+
} catch {
|
|
25106
|
+
return null;
|
|
25107
|
+
}
|
|
25108
|
+
}
|
|
25109
|
+
reviveReobserved(relPaths, workspaceId, since) {
|
|
25110
|
+
let revived = 0;
|
|
25111
|
+
const CHUNK = 400;
|
|
25112
|
+
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
25113
|
+
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
25114
|
+
if (slice.length === 0) continue;
|
|
25115
|
+
const r = this.db.prepare(
|
|
25116
|
+
`UPDATE nodes SET valid_to = NULL
|
|
25117
|
+
WHERE valid_to IS NOT NULL
|
|
25118
|
+
AND last_updated_at >= ?
|
|
25119
|
+
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
25120
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
|
|
25121
|
+
).run(since, workspaceId, ...slice);
|
|
25122
|
+
revived += Number(r.changes);
|
|
25123
|
+
}
|
|
25124
|
+
if (revived > 0) this.mutations++;
|
|
25125
|
+
return revived;
|
|
25126
|
+
}
|
|
25127
|
+
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
25128
|
+
const r = this.db.prepare(
|
|
25129
|
+
`SELECT COUNT(*) AS total,
|
|
25130
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
25131
|
+
FROM (SELECT attrs_json FROM edges
|
|
25132
|
+
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
25133
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
25134
|
+
).get(attr, `%${marker}%`, limit);
|
|
25135
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
25136
|
+
}
|
|
25137
|
+
liveNodeIds(ids) {
|
|
25138
|
+
const live = /* @__PURE__ */ new Set();
|
|
25139
|
+
const CHUNK = 900;
|
|
25140
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25141
|
+
const slice = ids.slice(i2, i2 + CHUNK);
|
|
25142
|
+
if (slice.length === 0) continue;
|
|
25143
|
+
const rows = this.db.prepare(
|
|
25144
|
+
`SELECT id FROM nodes WHERE valid_to IS NULL AND id IN (${slice.map(() => "?").join(",")})`
|
|
25145
|
+
).all(...slice);
|
|
25146
|
+
for (const r of rows) live.add(r.id);
|
|
25147
|
+
}
|
|
25148
|
+
return live;
|
|
25149
|
+
}
|
|
25150
|
+
/**
|
|
25151
|
+
* Live edges WITH their endpoint labels and ids, resolved in ONE join.
|
|
25152
|
+
*
|
|
25153
|
+
* The ontology sweep needs (id, type, fromLabel, toLabel) for every live edge.
|
|
25154
|
+
* Doing that as `scanLiveEdges()` + two `getNode()` calls is 2N node reads —
|
|
25155
|
+
* 550,000 on this store — and each one deserializes the node's embedding blob.
|
|
25156
|
+
* Measured: the sweep did not finish in 10 minutes. As a single join it is one
|
|
25157
|
+
* query over an index-covered scan. Labels only; nothing here touches embeddings.
|
|
25158
|
+
*/
|
|
25159
|
+
scanLiveEdgeRows() {
|
|
25160
|
+
const rows = this.db.prepare(
|
|
25161
|
+
`SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
|
|
25162
|
+
FROM edges e
|
|
25163
|
+
LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
|
|
25164
|
+
LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
|
|
25165
|
+
WHERE e.valid_to IS NULL`
|
|
25166
|
+
).all();
|
|
25167
|
+
return rows.map((r) => ({
|
|
25168
|
+
id: r.id,
|
|
25169
|
+
from: r.from_id,
|
|
25170
|
+
to: r.to_id,
|
|
25171
|
+
type: r.type,
|
|
25172
|
+
fromLabel: r.from_label ?? void 0,
|
|
25173
|
+
toLabel: r.to_label ?? void 0
|
|
25174
|
+
}));
|
|
25175
|
+
}
|
|
24690
25176
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
24691
25177
|
* expression index so the incremental reindex fetches only the changed files'
|
|
24692
25178
|
* symbols instead of scanning every versioned node. */
|
|
@@ -24707,6 +25193,10 @@ var SqliteGraphStore = class {
|
|
|
24707
25193
|
if (!live) return null;
|
|
24708
25194
|
const version2 = live.version ?? 1;
|
|
24709
25195
|
const frozenId = `${liveId}@v${version2}`;
|
|
25196
|
+
if (this.getNode(frozenId)) {
|
|
25197
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
25198
|
+
return frozenId;
|
|
25199
|
+
}
|
|
24710
25200
|
this.stmts.freezeCopy.run({ frozen_id: frozenId, live_id: liveId, t });
|
|
24711
25201
|
this.mergeEdge({
|
|
24712
25202
|
id: `edge_superseded_${frozenId}`,
|
|
@@ -24765,6 +25255,7 @@ var CAUSAL_FAMILY = [
|
|
|
24765
25255
|
|
|
24766
25256
|
// ../../packages/local-graph/src/justification.ts
|
|
24767
25257
|
init_src();
|
|
25258
|
+
init_src2();
|
|
24768
25259
|
function addDependency(store2, opts) {
|
|
24769
25260
|
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
24770
25261
|
const attrs = { relation: opts.relation };
|
|
@@ -24787,13 +25278,72 @@ function addDependency(store2, opts) {
|
|
|
24787
25278
|
store2.mergeEdge(edge);
|
|
24788
25279
|
return edge;
|
|
24789
25280
|
}
|
|
24790
|
-
function markRevisit(store2, node, reason, ts) {
|
|
25281
|
+
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
24791
25282
|
const fresh = store2.getNode(node.id) ?? node;
|
|
24792
25283
|
store2.updateNode(node.id, {
|
|
24793
|
-
attrs: {
|
|
25284
|
+
attrs: {
|
|
25285
|
+
...fresh.attrs,
|
|
25286
|
+
revisit: true,
|
|
25287
|
+
revisitReason: reason,
|
|
25288
|
+
revisitSinceTs: ts,
|
|
25289
|
+
revisitAnsweredWhen: answeredWhen
|
|
25290
|
+
},
|
|
24794
25291
|
lastUpdatedAt: ts
|
|
24795
25292
|
});
|
|
24796
25293
|
}
|
|
25294
|
+
var LEGACY_AUTO_CLOSE_ASK = "auto-closed off a pre-provenance anchor";
|
|
25295
|
+
var KNOWN_CONDITIONS = {
|
|
25296
|
+
parentProblemOpen: true,
|
|
25297
|
+
dependentStale: true
|
|
25298
|
+
};
|
|
25299
|
+
function isRevisitCondition(v) {
|
|
25300
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(KNOWN_CONDITIONS, v);
|
|
25301
|
+
}
|
|
25302
|
+
function conditionOf(node) {
|
|
25303
|
+
const explicit = node.attrs["revisitAnsweredWhen"];
|
|
25304
|
+
if (isRevisitCondition(explicit)) return explicit;
|
|
25305
|
+
if (String(node.attrs["revisitReason"] ?? "").startsWith(LEGACY_AUTO_CLOSE_ASK)) {
|
|
25306
|
+
return "parentProblemOpen";
|
|
25307
|
+
}
|
|
25308
|
+
return void 0;
|
|
25309
|
+
}
|
|
25310
|
+
function clearAnsweredRevisits(store2, ts) {
|
|
25311
|
+
const report = { cleared: 0, standing: 0 };
|
|
25312
|
+
for (const label of SEMANTIC_NODE_LABELS) {
|
|
25313
|
+
for (const node of store2.findNodesByLabel(label)) {
|
|
25314
|
+
if (node.attrs["revisit"] !== true) continue;
|
|
25315
|
+
const condition = conditionOf(node);
|
|
25316
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
25317
|
+
report.standing++;
|
|
25318
|
+
continue;
|
|
25319
|
+
}
|
|
25320
|
+
clearRevisit(store2, node.id, ts);
|
|
25321
|
+
report.cleared++;
|
|
25322
|
+
}
|
|
25323
|
+
}
|
|
25324
|
+
return report;
|
|
25325
|
+
}
|
|
25326
|
+
function isRevisitAnswered(store2, node, condition) {
|
|
25327
|
+
switch (condition) {
|
|
25328
|
+
case "parentProblemOpen": {
|
|
25329
|
+
const parents = store2.inEdges(node.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.from)).filter((n) => n !== null && n.validTo == null);
|
|
25330
|
+
return parents.every((p) => p.attrs["resolvedAt"] == null);
|
|
25331
|
+
}
|
|
25332
|
+
case "dependentStale": {
|
|
25333
|
+
const since = Number(node.attrs["revisitSinceTs"] ?? 0);
|
|
25334
|
+
return since > 0 && node.lastUpdatedAt > since;
|
|
25335
|
+
}
|
|
25336
|
+
}
|
|
25337
|
+
}
|
|
25338
|
+
function clearRevisit(store2, nodeId, ts) {
|
|
25339
|
+
const n = store2.getNode(nodeId);
|
|
25340
|
+
if (!n) return;
|
|
25341
|
+
const attrs = { ...n.attrs };
|
|
25342
|
+
delete attrs["revisit"];
|
|
25343
|
+
delete attrs["revisitReason"];
|
|
25344
|
+
delete attrs["revisitSinceTs"];
|
|
25345
|
+
store2.updateNode(nodeId, { attrs, lastUpdatedAt: ts });
|
|
25346
|
+
}
|
|
24797
25347
|
function listNeedsRevisit(store2, asOf) {
|
|
24798
25348
|
return store2.nodesAsOf(asOf).filter((n) => n.attrs["revisit"] === true).map((n) => ({
|
|
24799
25349
|
id: n.id,
|
|
@@ -24926,7 +25476,6 @@ function markBoth(store2, a, b, patch, ts) {
|
|
|
24926
25476
|
// ../../packages/local-graph/src/design-problem.ts
|
|
24927
25477
|
init_src();
|
|
24928
25478
|
init_src();
|
|
24929
|
-
init_src2();
|
|
24930
25479
|
|
|
24931
25480
|
// ../../packages/local-graph/src/problem-package-link.ts
|
|
24932
25481
|
init_src();
|
|
@@ -25112,90 +25661,171 @@ function backfillProblemContext(store2, ts) {
|
|
|
25112
25661
|
}
|
|
25113
25662
|
|
|
25114
25663
|
// ../../packages/local-graph/src/design-problem.ts
|
|
25115
|
-
|
|
25116
|
-
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25120
|
-
|
|
25121
|
-
|
|
25122
|
-
|
|
25123
|
-
|
|
25124
|
-
|
|
25125
|
-
|
|
25126
|
-
|
|
25127
|
-
|
|
25128
|
-
|
|
25129
|
-
|
|
25130
|
-
pageRank: 0,
|
|
25131
|
-
isLandmark: false,
|
|
25132
|
-
community: null,
|
|
25133
|
-
stability: "unstable",
|
|
25134
|
-
attrs
|
|
25135
|
-
};
|
|
25664
|
+
init_src2();
|
|
25665
|
+
|
|
25666
|
+
// ../../packages/local-graph/src/problem-dedup.ts
|
|
25667
|
+
init_src();
|
|
25668
|
+
init_src();
|
|
25669
|
+
var REDIRECT_EDGES = [
|
|
25670
|
+
"CAUSED_BY",
|
|
25671
|
+
"SOLVED_BY",
|
|
25672
|
+
"FIXED_BY",
|
|
25673
|
+
"ANCHORED_AT",
|
|
25674
|
+
"MANIFESTED_IN",
|
|
25675
|
+
"EVIDENCED_BY"
|
|
25676
|
+
];
|
|
25677
|
+
function corroborations(n) {
|
|
25678
|
+
return Number(n.attrs["corroborations"] ?? 0);
|
|
25136
25679
|
}
|
|
25137
|
-
function
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
type,
|
|
25143
|
-
confidence: 0.4,
|
|
25144
|
-
extractionSource: "agent-observed",
|
|
25145
|
-
createdAt: ts,
|
|
25146
|
-
lastSeenAt: ts,
|
|
25147
|
-
navSuccesses: 0,
|
|
25148
|
-
navFailures: 0,
|
|
25149
|
-
attrs: { provisional: true }
|
|
25150
|
-
});
|
|
25680
|
+
function overlap(a, b) {
|
|
25681
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
25682
|
+
let inter = 0;
|
|
25683
|
+
for (const x of a) if (b.has(x)) inter++;
|
|
25684
|
+
return inter / Math.min(a.size, b.size);
|
|
25151
25685
|
}
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
"
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
25174
|
-
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
|
|
25182
|
-
|
|
25183
|
-
|
|
25184
|
-
|
|
25185
|
-
|
|
25186
|
-
|
|
25187
|
-
|
|
25188
|
-
|
|
25189
|
-
|
|
25190
|
-
|
|
25686
|
+
function mergeDuplicateProblems(store2, opts) {
|
|
25687
|
+
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25688
|
+
const minTokens = opts.minTokens ?? 4;
|
|
25689
|
+
const report = { clusters: 0, merged: 0 };
|
|
25690
|
+
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25691
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
25692
|
+
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
25693
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
25694
|
+
store2.transaction(() => {
|
|
25695
|
+
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25696
|
+
const a = open[i2];
|
|
25697
|
+
if (consumed.has(a.id)) continue;
|
|
25698
|
+
const cluster = [a];
|
|
25699
|
+
const ta = tokens.get(a.id);
|
|
25700
|
+
for (let j = i2 + 1; j < open.length; j++) {
|
|
25701
|
+
const b = open[j];
|
|
25702
|
+
if (consumed.has(b.id)) continue;
|
|
25703
|
+
const tb = tokens.get(b.id);
|
|
25704
|
+
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25705
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
25706
|
+
if (overlap(ta, tb) >= minOverlap) {
|
|
25707
|
+
cluster.push(b);
|
|
25708
|
+
consumed.add(b.id);
|
|
25709
|
+
}
|
|
25710
|
+
}
|
|
25711
|
+
if (cluster.length < 2) continue;
|
|
25712
|
+
report.clusters++;
|
|
25713
|
+
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25714
|
+
const survivor = cluster[0];
|
|
25715
|
+
for (const dup of cluster.slice(1)) {
|
|
25716
|
+
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25717
|
+
report.merged++;
|
|
25718
|
+
}
|
|
25719
|
+
}
|
|
25720
|
+
});
|
|
25721
|
+
return report;
|
|
25722
|
+
}
|
|
25723
|
+
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25724
|
+
const surv = store2.getNode(survivor.id);
|
|
25725
|
+
if (!surv) return;
|
|
25726
|
+
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25727
|
+
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25728
|
+
store2.updateNode(survivor.id, {
|
|
25729
|
+
attrs: {
|
|
25730
|
+
...surv.attrs,
|
|
25731
|
+
sources: [...sources],
|
|
25732
|
+
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25733
|
+
},
|
|
25734
|
+
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25735
|
+
lastUpdatedAt: ts
|
|
25736
|
+
});
|
|
25737
|
+
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25738
|
+
if (e.to === survivor.id) continue;
|
|
25739
|
+
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25740
|
+
if (store2.getEdge(id)) continue;
|
|
25741
|
+
const redirected = {
|
|
25742
|
+
...e,
|
|
25743
|
+
id,
|
|
25744
|
+
from: survivor.id,
|
|
25745
|
+
createdAt: ts,
|
|
25746
|
+
lastSeenAt: ts
|
|
25747
|
+
};
|
|
25748
|
+
store2.mergeEdge(redirected);
|
|
25749
|
+
}
|
|
25750
|
+
store2.updateNode(dup.id, {
|
|
25751
|
+
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25752
|
+
lastUpdatedAt: ts
|
|
25753
|
+
});
|
|
25754
|
+
store2.closeNode(dup.id, ts);
|
|
25755
|
+
}
|
|
25756
|
+
|
|
25757
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
25758
|
+
function isConstraintProblem(node) {
|
|
25759
|
+
return node.attrs["kind"] === "constraint";
|
|
25760
|
+
}
|
|
25761
|
+
var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
|
|
25762
|
+
"Solution",
|
|
25763
|
+
"RootCause",
|
|
25764
|
+
"Pattern",
|
|
25765
|
+
"Technique",
|
|
25766
|
+
"AntiPattern"
|
|
25767
|
+
]);
|
|
25768
|
+
function isAutoMinted(n) {
|
|
25769
|
+
return n.label === "Solution" && String(n.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25770
|
+
}
|
|
25771
|
+
function priorsForFile(store2, relPath) {
|
|
25772
|
+
const want = relPath.trim().replace(/^\.?\//, "");
|
|
25773
|
+
let file2 = null;
|
|
25774
|
+
for (const n of store2.findNodesByLabel("File")) {
|
|
25775
|
+
if (n.description === want || n.attrs["relPath"] === want) {
|
|
25776
|
+
file2 = n;
|
|
25777
|
+
break;
|
|
25778
|
+
}
|
|
25779
|
+
}
|
|
25780
|
+
if (!file2) return null;
|
|
25781
|
+
const openProblems = [];
|
|
25782
|
+
const constraints = [];
|
|
25783
|
+
const resolvedProblems = [];
|
|
25784
|
+
const seenProblem = /* @__PURE__ */ new Set();
|
|
25785
|
+
const related = /* @__PURE__ */ new Map();
|
|
25786
|
+
for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
25787
|
+
const n = store2.getNode(e.from);
|
|
25788
|
+
if (!n) continue;
|
|
25789
|
+
if (n.label === "Problem") {
|
|
25790
|
+
if (seenProblem.has(n.id)) continue;
|
|
25791
|
+
seenProblem.add(n.id);
|
|
25792
|
+
if (!n.attrs["resolvedAt"]) {
|
|
25793
|
+
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
25794
|
+
} else if (n.attrs["resolvedAs"] == null && n.attrs["mergedInto"] === void 0 && e.attrs?.["anchorProvenance"] !== "legacy") {
|
|
25795
|
+
resolvedProblems.push(n);
|
|
25796
|
+
}
|
|
25797
|
+
} else if (CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) {
|
|
25798
|
+
related.set(n.id, n);
|
|
25799
|
+
}
|
|
25800
|
+
}
|
|
25801
|
+
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
25802
|
+
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
25803
|
+
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
25804
|
+
const n = store2.getNode(e.to);
|
|
25805
|
+
if (n && CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) related.set(n.id, n);
|
|
25806
|
+
if (n && n.label === "Solution" && e.type === "SOLVED_BY" && !isAutoMinted(n)) {
|
|
25807
|
+
const list = solutionsByProblem.get(p.id) ?? [];
|
|
25191
25808
|
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
25192
25809
|
solutionsByProblem.set(p.id, list);
|
|
25193
25810
|
}
|
|
25194
25811
|
}
|
|
25195
25812
|
}
|
|
25196
|
-
if (openProblems.length === 0 && related.size === 0)
|
|
25197
|
-
|
|
25198
|
-
|
|
25813
|
+
if (openProblems.length === 0 && constraints.length === 0 && resolvedProblems.length === 0 && related.size === 0)
|
|
25814
|
+
return null;
|
|
25815
|
+
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
25816
|
+
openProblems.sort(recentFirst);
|
|
25817
|
+
constraints.sort(recentFirst);
|
|
25818
|
+
resolvedProblems.sort(
|
|
25819
|
+
(a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)
|
|
25820
|
+
);
|
|
25821
|
+
return {
|
|
25822
|
+
file: file2,
|
|
25823
|
+
openProblems,
|
|
25824
|
+
constraints,
|
|
25825
|
+
resolvedProblems,
|
|
25826
|
+
related: [...related.values()],
|
|
25827
|
+
solutionsByProblem
|
|
25828
|
+
};
|
|
25199
25829
|
}
|
|
25200
25830
|
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
25201
25831
|
const p = store2.getNode(problemId);
|
|
@@ -25206,18 +25836,49 @@ function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
|
25206
25836
|
});
|
|
25207
25837
|
return true;
|
|
25208
25838
|
}
|
|
25209
|
-
function
|
|
25839
|
+
function reopenAutoClosedProblems(store2, t) {
|
|
25840
|
+
const report = { reopened: 0, alreadySuspect: 0, agentDescribed: 0 };
|
|
25841
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25842
|
+
if (p.attrs["resolvedAt"] == null) continue;
|
|
25843
|
+
if (p.attrs["resolvedAs"] != null) continue;
|
|
25844
|
+
if (p.attrs["mergedInto"] !== void 0) continue;
|
|
25845
|
+
if (p.attrs["resolutionWitnessed"] === true) {
|
|
25846
|
+
report.agentDescribed++;
|
|
25847
|
+
continue;
|
|
25848
|
+
}
|
|
25849
|
+
const sols = store2.outEdges(p.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.to)).filter((n) => n !== null);
|
|
25850
|
+
if (sols.length === 0) continue;
|
|
25851
|
+
if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {
|
|
25852
|
+
report.agentDescribed++;
|
|
25853
|
+
continue;
|
|
25854
|
+
}
|
|
25855
|
+
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
25856
|
+
const attrs = { ...p.attrs };
|
|
25857
|
+
delete attrs["resolvedAt"];
|
|
25858
|
+
attrs["reopenedFrom"] = "auto-close";
|
|
25859
|
+
attrs["reopenedAt"] = t;
|
|
25860
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25861
|
+
report.reopened++;
|
|
25862
|
+
}
|
|
25863
|
+
return report;
|
|
25864
|
+
}
|
|
25865
|
+
var AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
25866
|
+
function markFixCandidates(store2, t) {
|
|
25210
25867
|
let resolved = 0;
|
|
25211
25868
|
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25212
25869
|
if (!p.id.startsWith("dprob_")) continue;
|
|
25213
25870
|
if (p.attrs["resolvedAt"]) continue;
|
|
25871
|
+
if (isConstraintProblem(p)) continue;
|
|
25214
25872
|
let symName = "";
|
|
25215
25873
|
let symRelPath;
|
|
25216
25874
|
let edited = false;
|
|
25875
|
+
const since = Math.max(p.createdAt, Number(p.attrs["fixCandidateClearedAt"] ?? 0));
|
|
25217
25876
|
for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
|
|
25218
25877
|
if (e.attrs?.["mention"] === true) continue;
|
|
25878
|
+
if (e.attrs?.["anchorProvenance"] === "legacy") continue;
|
|
25219
25879
|
const sym = store2.getNode(e.to);
|
|
25220
|
-
|
|
25880
|
+
const changedAt = sym?.label === "File" ? Number(sym.attrs["contentChangedAt"] ?? 0) : sym?.lastUpdatedAt ?? 0;
|
|
25881
|
+
if (sym && changedAt > since) {
|
|
25221
25882
|
edited = true;
|
|
25222
25883
|
symName = sym.description;
|
|
25223
25884
|
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
@@ -25225,36 +25886,172 @@ function resolveDesignProblems(store2, t) {
|
|
|
25225
25886
|
}
|
|
25226
25887
|
}
|
|
25227
25888
|
if (!edited) continue;
|
|
25228
|
-
if (
|
|
25229
|
-
|
|
25230
|
-
store2.
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
|
|
25234
|
-
// AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
|
|
25235
|
-
// just prose — the F2 join key downstream backfills and the viz read.
|
|
25236
|
-
resolvedSymbols: [symName],
|
|
25237
|
-
...symRelPath ? { resolvedRelPath: symRelPath } : {}
|
|
25238
|
-
})
|
|
25239
|
-
);
|
|
25240
|
-
mergeEdge(store2, p.id, solId, "SOLVED_BY", t);
|
|
25241
|
-
for (const ae of store2.outEdges(p.id, ["ANCHORED_AT"]))
|
|
25242
|
-
mergeEdge(store2, solId, ae.to, "ANCHORED_AT", t);
|
|
25243
|
-
}
|
|
25889
|
+
if (p.attrs["fixCandidateAt"] !== void 0) continue;
|
|
25890
|
+
if (store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25891
|
+
const s = store2.getNode(e.to);
|
|
25892
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25893
|
+
}))
|
|
25894
|
+
continue;
|
|
25244
25895
|
store2.updateNode(p.id, {
|
|
25245
|
-
attrs: {
|
|
25896
|
+
attrs: {
|
|
25897
|
+
...p.attrs,
|
|
25898
|
+
// The cue, kept as structured data so the render can name the file it is
|
|
25899
|
+
// asking about. Deliberately NOT `resolvedAt` — this is a question.
|
|
25900
|
+
fixCandidateAt: t,
|
|
25901
|
+
fixCandidateSymbol: symName,
|
|
25902
|
+
...symRelPath ? { fixCandidateFile: symRelPath } : {},
|
|
25903
|
+
// Snapshot the render-ledger counter so "shown since THIS ask" is
|
|
25904
|
+
// measurable. `shownCount` is the node's LIFETIME count across every
|
|
25905
|
+
// band, so comparing it raw would retire a fresh ask on a
|
|
25906
|
+
// frequently-surfaced problem before anyone had seen the question once.
|
|
25907
|
+
fixCandidateShownBase: Number(p.attrs["shownCount"] ?? 0)
|
|
25908
|
+
},
|
|
25246
25909
|
lastUpdatedAt: t
|
|
25247
25910
|
});
|
|
25248
25911
|
resolved++;
|
|
25249
25912
|
}
|
|
25250
25913
|
return resolved;
|
|
25251
25914
|
}
|
|
25915
|
+
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
25916
|
+
function clearSettledFixCandidates(store2, t) {
|
|
25917
|
+
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
25918
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25919
|
+
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
25920
|
+
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");
|
|
25921
|
+
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
25922
|
+
const answered = p.attrs["resolvedAt"] != null || store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25923
|
+
const s = store2.getNode(e.to);
|
|
25924
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25925
|
+
});
|
|
25926
|
+
const base = Number(
|
|
25927
|
+
p.attrs["fixCandidateShownBase"] ?? p.attrs["shownCount"] ?? 0
|
|
25928
|
+
);
|
|
25929
|
+
const shownSinceAsk = Number(p.attrs["shownCount"] ?? 0) - base;
|
|
25930
|
+
const ignored = shownSinceAsk >= FIX_CANDIDATE_ASK_LIMIT;
|
|
25931
|
+
if (!answered && !ignored && !unsubstantiated) {
|
|
25932
|
+
report.standing++;
|
|
25933
|
+
continue;
|
|
25934
|
+
}
|
|
25935
|
+
const attrs = { ...p.attrs };
|
|
25936
|
+
delete attrs["fixCandidateAt"];
|
|
25937
|
+
delete attrs["fixCandidateSymbol"];
|
|
25938
|
+
delete attrs["fixCandidateFile"];
|
|
25939
|
+
attrs["fixCandidateClearedAt"] = t;
|
|
25940
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25941
|
+
if (answered) report.answered++;
|
|
25942
|
+
else if (ignored) report.ignored++;
|
|
25943
|
+
else report.unsubstantiated++;
|
|
25944
|
+
}
|
|
25945
|
+
return report;
|
|
25946
|
+
}
|
|
25947
|
+
|
|
25948
|
+
// ../../packages/local-graph/src/intent.ts
|
|
25949
|
+
init_src();
|
|
25252
25950
|
|
|
25253
25951
|
// ../../packages/local-graph/src/anchor-backfill.ts
|
|
25254
|
-
|
|
25952
|
+
init_src();
|
|
25953
|
+
init_src2();
|
|
25255
25954
|
function isLegacyAnchor(attrs) {
|
|
25256
25955
|
return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
|
|
25257
25956
|
}
|
|
25957
|
+
var STATEMENT_PATH_RE = new RegExp(
|
|
25958
|
+
String.raw`(?:[\w.+-]+\/)+[\w.+-]+\.(?:${anchorableExtensionAlternation()})`,
|
|
25959
|
+
"g"
|
|
25960
|
+
);
|
|
25961
|
+
function repairLegacyAnchorsFromStatement(store2, ts) {
|
|
25962
|
+
const report = {
|
|
25963
|
+
repaired: [],
|
|
25964
|
+
retiredGuesses: 0,
|
|
25965
|
+
noPathNamed: 0,
|
|
25966
|
+
pathUnknown: 0,
|
|
25967
|
+
alreadyCorrect: 0
|
|
25968
|
+
};
|
|
25969
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
25970
|
+
for (const f of store2.findNodesByLabel("File")) {
|
|
25971
|
+
const path = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
25972
|
+
if (path) byPath.set(path, { id: f.id, path });
|
|
25973
|
+
}
|
|
25974
|
+
const resolvePath = (named) => {
|
|
25975
|
+
const exact = byPath.get(named);
|
|
25976
|
+
if (exact) return exact;
|
|
25977
|
+
const hits = [...byPath.values()].filter(
|
|
25978
|
+
(f) => f.path.endsWith(`/${named}`) || named.endsWith(`/${f.path}`)
|
|
25979
|
+
);
|
|
25980
|
+
return hits.length === 1 ? hits[0] : null;
|
|
25981
|
+
};
|
|
25982
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25983
|
+
try {
|
|
25984
|
+
const anchors = store2.outEdges(p.id, ["ANCHORED_AT"]);
|
|
25985
|
+
const legacy = anchors.filter((e) => e.attrs?.["anchorProvenance"] === "legacy");
|
|
25986
|
+
if (legacy.length === 0) continue;
|
|
25987
|
+
const better = anchors.filter((e) => {
|
|
25988
|
+
const prov = e.attrs?.["anchorProvenance"];
|
|
25989
|
+
return prov === "restated" || prov === "witnessed" || prov === "edited";
|
|
25990
|
+
});
|
|
25991
|
+
if (better.length > 0) {
|
|
25992
|
+
const betterTargets = new Set(better.map((e) => e.to));
|
|
25993
|
+
let retired = 0;
|
|
25994
|
+
for (const e of legacy) {
|
|
25995
|
+
if (!betterTargets.has(e.to)) {
|
|
25996
|
+
store2.closeEdge(e.id, ts);
|
|
25997
|
+
retired++;
|
|
25998
|
+
}
|
|
25999
|
+
}
|
|
26000
|
+
report.retiredGuesses += retired;
|
|
26001
|
+
continue;
|
|
26002
|
+
}
|
|
26003
|
+
const named = [...String(p.description ?? "").matchAll(STATEMENT_PATH_RE)].map((m) => m[0]);
|
|
26004
|
+
if (named.length === 0) {
|
|
26005
|
+
report.noPathNamed++;
|
|
26006
|
+
continue;
|
|
26007
|
+
}
|
|
26008
|
+
const guessedPaths = legacy.map((e) => {
|
|
26009
|
+
const f = store2.getNode(e.to);
|
|
26010
|
+
return String(f?.attrs["relPath"] ?? f?.description ?? "");
|
|
26011
|
+
});
|
|
26012
|
+
if (named.some((x) => guessedPaths.some((g) => g.endsWith(x) || x.endsWith(g)))) {
|
|
26013
|
+
report.alreadyCorrect++;
|
|
26014
|
+
continue;
|
|
26015
|
+
}
|
|
26016
|
+
const target = named.map(resolvePath).find((f) => f !== null);
|
|
26017
|
+
if (!target) {
|
|
26018
|
+
report.pathUnknown++;
|
|
26019
|
+
continue;
|
|
26020
|
+
}
|
|
26021
|
+
store2.mergeEdge({
|
|
26022
|
+
id: `edge_${digest({ from: p.id, type: "ANCHORED_AT", to: target.id })}`.slice(0, 24),
|
|
26023
|
+
from: p.id,
|
|
26024
|
+
to: target.id,
|
|
26025
|
+
type: "ANCHORED_AT",
|
|
26026
|
+
// Same confidence a capture-time anchor enters at: the source is the
|
|
26027
|
+
// agent's own statement either way, only the moment of reading differs.
|
|
26028
|
+
confidence: 0.4,
|
|
26029
|
+
extractionSource: "agent-observed",
|
|
26030
|
+
createdAt: ts,
|
|
26031
|
+
lastSeenAt: ts,
|
|
26032
|
+
navSuccesses: 0,
|
|
26033
|
+
navFailures: 0,
|
|
26034
|
+
attrs: {
|
|
26035
|
+
anchorProvenance: "restated",
|
|
26036
|
+
restatedFrom: guessedPaths[0] ?? "",
|
|
26037
|
+
restatedAt: ts
|
|
26038
|
+
}
|
|
26039
|
+
});
|
|
26040
|
+
for (const e of legacy) {
|
|
26041
|
+
if (e.to !== target.id) store2.closeEdge(e.id, ts);
|
|
26042
|
+
}
|
|
26043
|
+
report.retiredGuesses += legacy.filter((e) => e.to !== target.id).length;
|
|
26044
|
+
report.repaired.push({
|
|
26045
|
+
problemId: p.id,
|
|
26046
|
+
problem: p.description,
|
|
26047
|
+
guessed: guessedPaths[0] ?? "",
|
|
26048
|
+
restated: target.path
|
|
26049
|
+
});
|
|
26050
|
+
} catch {
|
|
26051
|
+
}
|
|
26052
|
+
}
|
|
26053
|
+
return report;
|
|
26054
|
+
}
|
|
25258
26055
|
function backfillLegacyAnchors(store2, ts) {
|
|
25259
26056
|
const report = {
|
|
25260
26057
|
demotedEdges: 0,
|
|
@@ -25294,7 +26091,11 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
25294
26091
|
store2,
|
|
25295
26092
|
sol,
|
|
25296
26093
|
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
25297
|
-
ts
|
|
26094
|
+
ts,
|
|
26095
|
+
// "or reopen it" is half the ask, and the nightly reopen takes that
|
|
26096
|
+
// branch — so record what would answer this, or the flag can never be
|
|
26097
|
+
// lowered and the band fills with settled questions.
|
|
26098
|
+
"parentProblemOpen"
|
|
25298
26099
|
);
|
|
25299
26100
|
store2.updateNode(p.id, {
|
|
25300
26101
|
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
@@ -25328,7 +26129,7 @@ function pagerank(input) {
|
|
|
25328
26129
|
const ids = input.nodeIds;
|
|
25329
26130
|
const n = ids.length;
|
|
25330
26131
|
if (n === 0) {
|
|
25331
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26132
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25332
26133
|
}
|
|
25333
26134
|
const index = /* @__PURE__ */ new Map();
|
|
25334
26135
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -25400,8 +26201,12 @@ function pagerank(input) {
|
|
|
25400
26201
|
}
|
|
25401
26202
|
}
|
|
25402
26203
|
const scores = /* @__PURE__ */ new Map();
|
|
25403
|
-
|
|
25404
|
-
|
|
26204
|
+
let danglingRank = 0;
|
|
26205
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26206
|
+
scores.set(ids[i2], score2[i2]);
|
|
26207
|
+
if (dangling[i2]) danglingRank += score2[i2];
|
|
26208
|
+
}
|
|
26209
|
+
return { scores, iterations: iter, converged, danglingRank };
|
|
25405
26210
|
}
|
|
25406
26211
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
25407
26212
|
const entries = [...scores.entries()];
|
|
@@ -25411,7 +26216,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
25411
26216
|
}
|
|
25412
26217
|
}
|
|
25413
26218
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
25414
|
-
entries.sort((a, b) => b[1] - a[1]);
|
|
26219
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
25415
26220
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
25416
26221
|
const out2 = /* @__PURE__ */ new Set();
|
|
25417
26222
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -25649,8 +26454,86 @@ function motifDecision(input) {
|
|
|
25649
26454
|
};
|
|
25650
26455
|
}
|
|
25651
26456
|
|
|
25652
|
-
// ../../packages/
|
|
25653
|
-
|
|
26457
|
+
// ../../packages/math/src/pagerank-local.ts
|
|
26458
|
+
function pagerankLocal(input) {
|
|
26459
|
+
const damping = input.damping ?? 0.85;
|
|
26460
|
+
const tol = input.tolerance ?? 1e-6;
|
|
26461
|
+
const maxIter = input.maxIterations ?? 100;
|
|
26462
|
+
const ids = input.regionIds;
|
|
26463
|
+
const n = ids.length;
|
|
26464
|
+
if (n === 0 || input.globalN === 0) {
|
|
26465
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26466
|
+
}
|
|
26467
|
+
const index = /* @__PURE__ */ new Map();
|
|
26468
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
26469
|
+
const base = (1 - damping) / input.globalN;
|
|
26470
|
+
const outSum = new Float64Array(n);
|
|
26471
|
+
const inflow = new Float64Array(n);
|
|
26472
|
+
let score2 = new Float64Array(n);
|
|
26473
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26474
|
+
const id = ids[i2];
|
|
26475
|
+
outSum[i2] = input.outSum.get(id) ?? 0;
|
|
26476
|
+
inflow[i2] = input.boundaryInflow.get(id) ?? 0;
|
|
26477
|
+
score2[i2] = input.rank0.get(id) ?? base;
|
|
26478
|
+
}
|
|
26479
|
+
const rowLen = new Int32Array(n);
|
|
26480
|
+
let edgeCount = 0;
|
|
26481
|
+
for (const [from, edges] of input.out) {
|
|
26482
|
+
const fi = index.get(from);
|
|
26483
|
+
if (fi === void 0) continue;
|
|
26484
|
+
let inSet = 0;
|
|
26485
|
+
for (const e of edges) if (index.has(e.to)) inSet++;
|
|
26486
|
+
rowLen[fi] = inSet;
|
|
26487
|
+
edgeCount += inSet;
|
|
26488
|
+
}
|
|
26489
|
+
const rowStart = new Int32Array(n + 1);
|
|
26490
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
26491
|
+
const colIdx = new Int32Array(edgeCount);
|
|
26492
|
+
const colW = new Float64Array(edgeCount);
|
|
26493
|
+
const cursor = rowStart.slice(0, n);
|
|
26494
|
+
for (const [from, edges] of input.out) {
|
|
26495
|
+
const fi = index.get(from);
|
|
26496
|
+
if (fi === void 0) continue;
|
|
26497
|
+
for (const e of edges) {
|
|
26498
|
+
const ti = index.get(e.to);
|
|
26499
|
+
if (ti === void 0) continue;
|
|
26500
|
+
const c = cursor[fi];
|
|
26501
|
+
cursor[fi] = c + 1;
|
|
26502
|
+
colIdx[c] = ti;
|
|
26503
|
+
colW[c] = e.weight;
|
|
26504
|
+
}
|
|
26505
|
+
}
|
|
26506
|
+
const externalDangling = input.externalDanglingRank ?? 0;
|
|
26507
|
+
let next = new Float64Array(n);
|
|
26508
|
+
let iter = 0;
|
|
26509
|
+
let converged = false;
|
|
26510
|
+
for (; iter < maxIter; iter++) {
|
|
26511
|
+
let internalDangling = 0;
|
|
26512
|
+
for (let i2 = 0; i2 < n; i2++) if (outSum[i2] <= 0) internalDangling += score2[i2];
|
|
26513
|
+
const danglingShare = damping * (internalDangling + externalDangling) / input.globalN;
|
|
26514
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] = base + danglingShare + damping * inflow[i2];
|
|
26515
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26516
|
+
const os2 = outSum[i2];
|
|
26517
|
+
if (os2 <= 0) continue;
|
|
26518
|
+
const f = damping * score2[i2] / os2;
|
|
26519
|
+
const end = rowStart[i2 + 1];
|
|
26520
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
26521
|
+
}
|
|
26522
|
+
let diff = 0;
|
|
26523
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
26524
|
+
const tmp = score2;
|
|
26525
|
+
score2 = next;
|
|
26526
|
+
next = tmp;
|
|
26527
|
+
if (diff < tol) {
|
|
26528
|
+
iter++;
|
|
26529
|
+
converged = true;
|
|
26530
|
+
break;
|
|
26531
|
+
}
|
|
26532
|
+
}
|
|
26533
|
+
const scores = /* @__PURE__ */ new Map();
|
|
26534
|
+
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
26535
|
+
return { scores, iterations: iter, converged };
|
|
26536
|
+
}
|
|
25654
26537
|
|
|
25655
26538
|
// ../../packages/local-graph/src/triage.ts
|
|
25656
26539
|
init_src();
|
|
@@ -25659,95 +26542,8 @@ init_src();
|
|
|
25659
26542
|
init_src2();
|
|
25660
26543
|
var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
|
|
25661
26544
|
|
|
25662
|
-
// ../../packages/local-graph/src/
|
|
25663
|
-
|
|
25664
|
-
init_src();
|
|
25665
|
-
var REDIRECT_EDGES = [
|
|
25666
|
-
"CAUSED_BY",
|
|
25667
|
-
"SOLVED_BY",
|
|
25668
|
-
"FIXED_BY",
|
|
25669
|
-
"ANCHORED_AT",
|
|
25670
|
-
"MANIFESTED_IN",
|
|
25671
|
-
"EVIDENCED_BY"
|
|
25672
|
-
];
|
|
25673
|
-
function corroborations(n) {
|
|
25674
|
-
return Number(n.attrs["corroborations"] ?? 0);
|
|
25675
|
-
}
|
|
25676
|
-
function overlap(a, b) {
|
|
25677
|
-
if (a.size === 0 || b.size === 0) return 0;
|
|
25678
|
-
let inter = 0;
|
|
25679
|
-
for (const x of a) if (b.has(x)) inter++;
|
|
25680
|
-
return inter / Math.min(a.size, b.size);
|
|
25681
|
-
}
|
|
25682
|
-
function mergeDuplicateProblems(store2, opts) {
|
|
25683
|
-
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25684
|
-
const minTokens = opts.minTokens ?? 4;
|
|
25685
|
-
const report = { clusters: 0, merged: 0 };
|
|
25686
|
-
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25687
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
25688
|
-
for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
|
|
25689
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
25690
|
-
store2.transaction(() => {
|
|
25691
|
-
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25692
|
-
const a = open[i2];
|
|
25693
|
-
if (consumed.has(a.id)) continue;
|
|
25694
|
-
const cluster = [a];
|
|
25695
|
-
const ta = tokens.get(a.id);
|
|
25696
|
-
for (let j = i2 + 1; j < open.length; j++) {
|
|
25697
|
-
const b = open[j];
|
|
25698
|
-
if (consumed.has(b.id)) continue;
|
|
25699
|
-
const tb = tokens.get(b.id);
|
|
25700
|
-
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25701
|
-
if (overlap(ta, tb) >= minOverlap) {
|
|
25702
|
-
cluster.push(b);
|
|
25703
|
-
consumed.add(b.id);
|
|
25704
|
-
}
|
|
25705
|
-
}
|
|
25706
|
-
if (cluster.length < 2) continue;
|
|
25707
|
-
report.clusters++;
|
|
25708
|
-
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25709
|
-
const survivor = cluster[0];
|
|
25710
|
-
for (const dup of cluster.slice(1)) {
|
|
25711
|
-
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25712
|
-
report.merged++;
|
|
25713
|
-
}
|
|
25714
|
-
}
|
|
25715
|
-
});
|
|
25716
|
-
return report;
|
|
25717
|
-
}
|
|
25718
|
-
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25719
|
-
const surv = store2.getNode(survivor.id);
|
|
25720
|
-
if (!surv) return;
|
|
25721
|
-
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25722
|
-
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25723
|
-
store2.updateNode(survivor.id, {
|
|
25724
|
-
attrs: {
|
|
25725
|
-
...surv.attrs,
|
|
25726
|
-
sources: [...sources],
|
|
25727
|
-
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25728
|
-
},
|
|
25729
|
-
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25730
|
-
lastUpdatedAt: ts
|
|
25731
|
-
});
|
|
25732
|
-
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25733
|
-
if (e.to === survivor.id) continue;
|
|
25734
|
-
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25735
|
-
if (store2.getEdge(id)) continue;
|
|
25736
|
-
const redirected = {
|
|
25737
|
-
...e,
|
|
25738
|
-
id,
|
|
25739
|
-
from: survivor.id,
|
|
25740
|
-
createdAt: ts,
|
|
25741
|
-
lastSeenAt: ts
|
|
25742
|
-
};
|
|
25743
|
-
store2.mergeEdge(redirected);
|
|
25744
|
-
}
|
|
25745
|
-
store2.updateNode(dup.id, {
|
|
25746
|
-
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25747
|
-
lastUpdatedAt: ts
|
|
25748
|
-
});
|
|
25749
|
-
store2.closeNode(dup.id, ts);
|
|
25750
|
-
}
|
|
26545
|
+
// ../../packages/local-graph/src/community.ts
|
|
26546
|
+
init_src2();
|
|
25751
26547
|
|
|
25752
26548
|
// ../../packages/local-graph/src/tools.ts
|
|
25753
26549
|
init_src();
|
|
@@ -25764,6 +26560,9 @@ function rankToolsForHandles(store2, limit = 5) {
|
|
|
25764
26560
|
// ../../packages/local-graph/src/principle-sync.ts
|
|
25765
26561
|
init_src2();
|
|
25766
26562
|
|
|
26563
|
+
// ../../packages/local-graph/src/mechanism-liveness.ts
|
|
26564
|
+
var STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
26565
|
+
|
|
25767
26566
|
// ../../packages/generalizer/src/generalizer.ts
|
|
25768
26567
|
init_src2();
|
|
25769
26568
|
init_src();
|
|
@@ -25883,7 +26682,164 @@ function promoteMotifs(store2, t) {
|
|
|
25883
26682
|
}
|
|
25884
26683
|
|
|
25885
26684
|
// ../../packages/generalizer/src/nightly.ts
|
|
25886
|
-
|
|
26685
|
+
var FULL_RESCORE_EVERY = 12;
|
|
26686
|
+
var INCREMENTAL_REGION_MAX_FRACTION = 0.2;
|
|
26687
|
+
var INCREMENTAL_HOPS = 3;
|
|
26688
|
+
function runGraphRescore(store2, opts = {}) {
|
|
26689
|
+
const lastRescoreAt = Number(store2.getMeta?.("lastRescoreAt") ?? 0);
|
|
26690
|
+
const sinceFull = Number(store2.getMeta?.("rescoresSinceFull") ?? 0);
|
|
26691
|
+
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;
|
|
26692
|
+
if (incrementalCapable) {
|
|
26693
|
+
const started = Date.now();
|
|
26694
|
+
const inc = tryIncrementalRescore(store2, lastRescoreAt, started);
|
|
26695
|
+
if (inc) {
|
|
26696
|
+
store2.setMeta("lastRescoreAt", String(started));
|
|
26697
|
+
store2.setMeta("rescoresSinceFull", String(sinceFull + 1));
|
|
26698
|
+
return inc;
|
|
26699
|
+
}
|
|
26700
|
+
}
|
|
26701
|
+
const report = runFullRescore(store2);
|
|
26702
|
+
store2.setMeta?.("lastRescoreAt", String(report.startedAt));
|
|
26703
|
+
store2.setMeta?.("rescoresSinceFull", "0");
|
|
26704
|
+
store2.setMeta?.("danglingRankShare", String(report.danglingRank));
|
|
26705
|
+
const { startedAt: _drop, danglingRank: _drop2, ...rest } = report;
|
|
26706
|
+
return rest;
|
|
26707
|
+
}
|
|
26708
|
+
function tryIncrementalRescore(store2, lastRescoreAt, started) {
|
|
26709
|
+
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
26710
|
+
const liveN = store2.nodeCount();
|
|
26711
|
+
const cap = Math.max(1e3, Math.floor(liveN * INCREMENTAL_REGION_MAX_FRACTION));
|
|
26712
|
+
const region = new Set(store2.dirtyNodeIdsSince(lastRescoreAt));
|
|
26713
|
+
for (const e of store2.edgeEndpointsTouchedSince(lastRescoreAt)) {
|
|
26714
|
+
region.add(e.from);
|
|
26715
|
+
region.add(e.to);
|
|
26716
|
+
}
|
|
26717
|
+
if (region.size > cap) return null;
|
|
26718
|
+
let frontier = [...region];
|
|
26719
|
+
for (let hop = 0; hop < INCREMENTAL_HOPS && frontier.length > 0; hop++) {
|
|
26720
|
+
const next = [];
|
|
26721
|
+
for (const e of store2.outEdgesForMany(frontier)) {
|
|
26722
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26723
|
+
if (!region.has(e.to)) {
|
|
26724
|
+
region.add(e.to);
|
|
26725
|
+
next.push(e.to);
|
|
26726
|
+
}
|
|
26727
|
+
}
|
|
26728
|
+
if (region.size > cap) return null;
|
|
26729
|
+
frontier = next;
|
|
26730
|
+
}
|
|
26731
|
+
const rank0 = store2.ranksForMany([...region]);
|
|
26732
|
+
const regionIds = [...rank0.keys()];
|
|
26733
|
+
if (regionIds.length === 0) {
|
|
26734
|
+
return {
|
|
26735
|
+
scoredNodes: 0,
|
|
26736
|
+
iterations: 0,
|
|
26737
|
+
converged: true,
|
|
26738
|
+
landmarks: 0,
|
|
26739
|
+
communities: 0,
|
|
26740
|
+
motifsPromoted: 0,
|
|
26741
|
+
techniques: 0,
|
|
26742
|
+
antipatterns: 0,
|
|
26743
|
+
pageRankWritten: 0,
|
|
26744
|
+
landmarkFlips: 0,
|
|
26745
|
+
mode: "incremental",
|
|
26746
|
+
regionSize: 0,
|
|
26747
|
+
durationMs: Date.now() - started
|
|
26748
|
+
};
|
|
26749
|
+
}
|
|
26750
|
+
const inRegion = new Set(regionIds);
|
|
26751
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
26752
|
+
const outSum = /* @__PURE__ */ new Map();
|
|
26753
|
+
for (const e of store2.outEdgesForMany(regionIds)) {
|
|
26754
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26755
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26756
|
+
if (w <= 0) continue;
|
|
26757
|
+
outSum.set(e.from, (outSum.get(e.from) ?? 0) + w);
|
|
26758
|
+
if (!inRegion.has(e.to)) continue;
|
|
26759
|
+
const l = out2.get(e.from);
|
|
26760
|
+
if (l) l.push({ to: e.to, weight: w });
|
|
26761
|
+
else out2.set(e.from, [{ to: e.to, weight: w }]);
|
|
26762
|
+
}
|
|
26763
|
+
const boundaryEdges = store2.inEdgesForMany(regionIds).filter((e) => allowedTypes.has(e.type) && !inRegion.has(e.from) && (EDGE_WEIGHT[e.type] ?? 1) > 0);
|
|
26764
|
+
const boundarySources = [...new Set(boundaryEdges.map((e) => e.from))];
|
|
26765
|
+
if (boundarySources.length > cap) return null;
|
|
26766
|
+
const boundaryRanks = store2.ranksForMany(boundarySources);
|
|
26767
|
+
const boundaryOutSum = /* @__PURE__ */ new Map();
|
|
26768
|
+
for (const e of store2.outEdgesForMany(boundarySources)) {
|
|
26769
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26770
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26771
|
+
if (w > 0) boundaryOutSum.set(e.from, (boundaryOutSum.get(e.from) ?? 0) + w);
|
|
26772
|
+
}
|
|
26773
|
+
const boundaryInflow = /* @__PURE__ */ new Map();
|
|
26774
|
+
for (const e of boundaryEdges) {
|
|
26775
|
+
const r = boundaryRanks.get(e.from);
|
|
26776
|
+
const os2 = boundaryOutSum.get(e.from);
|
|
26777
|
+
if (r === void 0 || !os2) continue;
|
|
26778
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26779
|
+
boundaryInflow.set(e.to, (boundaryInflow.get(e.to) ?? 0) + r * w / os2);
|
|
26780
|
+
}
|
|
26781
|
+
const globalDangling = Number(store2.getMeta("danglingRankShare") ?? 0);
|
|
26782
|
+
let regionDangling = 0;
|
|
26783
|
+
for (const id of regionIds) {
|
|
26784
|
+
if ((outSum.get(id) ?? 0) <= 0) regionDangling += rank0.get(id) ?? 0;
|
|
26785
|
+
}
|
|
26786
|
+
const result = pagerankLocal({
|
|
26787
|
+
regionIds,
|
|
26788
|
+
rank0,
|
|
26789
|
+
out: out2,
|
|
26790
|
+
outSum,
|
|
26791
|
+
boundaryInflow,
|
|
26792
|
+
globalN: liveN,
|
|
26793
|
+
externalDanglingRank: Math.max(0, globalDangling - regionDangling),
|
|
26794
|
+
damping: 0.85,
|
|
26795
|
+
tolerance: 1e-6,
|
|
26796
|
+
maxIterations: 100
|
|
26797
|
+
});
|
|
26798
|
+
let pageRankWritten = 0;
|
|
26799
|
+
store2.transaction(() => {
|
|
26800
|
+
for (const [id, score2] of result.scores) {
|
|
26801
|
+
if (Math.abs(score2 - (rank0.get(id) ?? 0)) < 1e-9) continue;
|
|
26802
|
+
store2.setPageRank(id, score2);
|
|
26803
|
+
pageRankWritten++;
|
|
26804
|
+
}
|
|
26805
|
+
});
|
|
26806
|
+
const sweep = store2.landmarkSweepRows();
|
|
26807
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
26808
|
+
for (const row of sweep) {
|
|
26809
|
+
if (row.label === "Pattern" || row.label === "RootCause") {
|
|
26810
|
+
candidates.set(row.id, result.scores.get(row.id) ?? row.pageRank);
|
|
26811
|
+
}
|
|
26812
|
+
}
|
|
26813
|
+
const want = markLandmarks(candidates, 0.1);
|
|
26814
|
+
for (const row of sweep) if (row.memoryTier === "persistent") want.add(row.id);
|
|
26815
|
+
let landmarkFlips = 0;
|
|
26816
|
+
store2.transaction(() => {
|
|
26817
|
+
for (const row of sweep) {
|
|
26818
|
+
if (row.extractionSource === "bko-inferred") continue;
|
|
26819
|
+
const should = want.has(row.id);
|
|
26820
|
+
if (row.isLandmark === should) continue;
|
|
26821
|
+
store2.setLandmark(row.id, should);
|
|
26822
|
+
landmarkFlips++;
|
|
26823
|
+
}
|
|
26824
|
+
});
|
|
26825
|
+
return {
|
|
26826
|
+
scoredNodes: regionIds.length,
|
|
26827
|
+
iterations: result.iterations,
|
|
26828
|
+
converged: result.converged,
|
|
26829
|
+
landmarks: want.size,
|
|
26830
|
+
communities: 0,
|
|
26831
|
+
// deferred to the full backstop — see mode docs
|
|
26832
|
+
motifsPromoted: 0,
|
|
26833
|
+
techniques: 0,
|
|
26834
|
+
antipatterns: 0,
|
|
26835
|
+
pageRankWritten,
|
|
26836
|
+
landmarkFlips,
|
|
26837
|
+
mode: "incremental",
|
|
26838
|
+
regionSize: regionIds.length,
|
|
26839
|
+
durationMs: Date.now() - started
|
|
26840
|
+
};
|
|
26841
|
+
}
|
|
26842
|
+
function runFullRescore(store2) {
|
|
25887
26843
|
const started = Date.now();
|
|
25888
26844
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
25889
26845
|
const nodeIds = [];
|
|
@@ -25909,10 +26865,12 @@ function runNightlyPipeline(store2) {
|
|
|
25909
26865
|
maxIterations: 100
|
|
25910
26866
|
});
|
|
25911
26867
|
const scored = result.scores.size;
|
|
26868
|
+
let pageRankWritten = 0;
|
|
25912
26869
|
store2.transaction(() => {
|
|
25913
26870
|
for (const [id, score2] of result.scores) {
|
|
25914
26871
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
25915
26872
|
store2.setPageRank(id, score2);
|
|
26873
|
+
pageRankWritten++;
|
|
25916
26874
|
}
|
|
25917
26875
|
});
|
|
25918
26876
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -25927,11 +26885,13 @@ function runNightlyPipeline(store2) {
|
|
|
25927
26885
|
for (const id of nodeIds) {
|
|
25928
26886
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
25929
26887
|
}
|
|
26888
|
+
let landmarkFlips = 0;
|
|
25930
26889
|
store2.transaction(() => {
|
|
25931
26890
|
for (const id of nodeIds) {
|
|
25932
26891
|
const want = allLandmarks.has(id);
|
|
25933
26892
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
25934
26893
|
store2.setLandmark(id, want);
|
|
26894
|
+
landmarkFlips++;
|
|
25935
26895
|
}
|
|
25936
26896
|
});
|
|
25937
26897
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -25966,8 +26926,6 @@ function runNightlyPipeline(store2) {
|
|
|
25966
26926
|
for (const [id, c] of comm.community) store2.updateNode(id, { community: c });
|
|
25967
26927
|
});
|
|
25968
26928
|
const motifs = promoteMotifs(store2, started);
|
|
25969
|
-
const designResolved = resolveDesignProblems(store2, started);
|
|
25970
|
-
const cry = crystallize(store2, { ts: started });
|
|
25971
26929
|
return {
|
|
25972
26930
|
scoredNodes: scored,
|
|
25973
26931
|
iterations: result.iterations,
|
|
@@ -25977,15 +26935,61 @@ function runNightlyPipeline(store2) {
|
|
|
25977
26935
|
motifsPromoted: motifs.promoted,
|
|
25978
26936
|
techniques: motifs.techniques,
|
|
25979
26937
|
antipatterns: motifs.antipatterns,
|
|
25980
|
-
|
|
25981
|
-
|
|
25982
|
-
|
|
26938
|
+
pageRankWritten,
|
|
26939
|
+
landmarkFlips,
|
|
26940
|
+
mode: "full",
|
|
26941
|
+
regionSize: 0,
|
|
26942
|
+
startedAt: started,
|
|
26943
|
+
danglingRank: result.danglingRank,
|
|
26944
|
+
durationMs: Date.now() - started
|
|
26945
|
+
};
|
|
26946
|
+
}
|
|
26947
|
+
function runSemanticMaintenance(store2) {
|
|
26948
|
+
const started = Date.now();
|
|
26949
|
+
reopenAutoClosedProblems(store2, started);
|
|
26950
|
+
const revisits = clearAnsweredRevisits(store2, started);
|
|
26951
|
+
const fixCandidates = clearSettledFixCandidates(store2, started);
|
|
26952
|
+
const designResolved = markFixCandidates(store2, started);
|
|
26953
|
+
const cry = crystallize(store2, { ts: started });
|
|
26954
|
+
return {
|
|
25983
26955
|
designResolved,
|
|
26956
|
+
revisitsCleared: revisits.cleared,
|
|
26957
|
+
revisitsStanding: revisits.standing,
|
|
26958
|
+
fixCandidatesSettled: fixCandidates.answered + fixCandidates.ignored + fixCandidates.unsubstantiated,
|
|
26959
|
+
fixCandidates,
|
|
25984
26960
|
claimsEvaluated: cry.evaluated,
|
|
25985
26961
|
claimsDerived: cry.derived.length,
|
|
25986
26962
|
durationMs: Date.now() - started
|
|
25987
26963
|
};
|
|
25988
26964
|
}
|
|
26965
|
+
function runNightlyPipeline(store2) {
|
|
26966
|
+
const started = Date.now();
|
|
26967
|
+
const rescore = runGraphRescore(store2);
|
|
26968
|
+
const semantic = runSemanticMaintenance(store2);
|
|
26969
|
+
return {
|
|
26970
|
+
scoredNodes: rescore.scoredNodes,
|
|
26971
|
+
iterations: rescore.iterations,
|
|
26972
|
+
converged: rescore.converged,
|
|
26973
|
+
landmarks: rescore.landmarks,
|
|
26974
|
+
communities: rescore.communities,
|
|
26975
|
+
motifsPromoted: rescore.motifsPromoted,
|
|
26976
|
+
techniques: rescore.techniques,
|
|
26977
|
+
antipatterns: rescore.antipatterns,
|
|
26978
|
+
skillsInduced: 0,
|
|
26979
|
+
// skills are cloud-induced now (see above)
|
|
26980
|
+
skillsRefreshed: 0,
|
|
26981
|
+
pageRankWritten: rescore.pageRankWritten,
|
|
26982
|
+
landmarkFlips: rescore.landmarkFlips,
|
|
26983
|
+
mode: rescore.mode,
|
|
26984
|
+
regionSize: rescore.regionSize,
|
|
26985
|
+
designResolved: semantic.designResolved,
|
|
26986
|
+
revisitsCleared: semantic.revisitsCleared,
|
|
26987
|
+
fixCandidatesSettled: semantic.fixCandidatesSettled,
|
|
26988
|
+
claimsEvaluated: semantic.claimsEvaluated,
|
|
26989
|
+
claimsDerived: semantic.claimsDerived,
|
|
26990
|
+
durationMs: Date.now() - started
|
|
26991
|
+
};
|
|
26992
|
+
}
|
|
25989
26993
|
function codeAnchorProjection(store2, semanticIds, opts = {}) {
|
|
25990
26994
|
const anchorToNodes = /* @__PURE__ */ new Map();
|
|
25991
26995
|
for (const id of semanticIds) {
|
|
@@ -26030,12 +27034,14 @@ var AGENTS_POINTER_BODY = [
|
|
|
26030
27034
|
init_src2();
|
|
26031
27035
|
function buildSnapshot(opts) {
|
|
26032
27036
|
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
|
|
26033
|
-
const
|
|
27037
|
+
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
27038
|
+
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
26034
27039
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
26035
27040
|
const recent = allProblems.slice(0, 8).map((p) => ({
|
|
26036
27041
|
node: p,
|
|
26037
27042
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
26038
27043
|
}));
|
|
27044
|
+
const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
|
|
26039
27045
|
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) => {
|
|
26040
27046
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
26041
27047
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
@@ -26063,10 +27069,13 @@ function buildSnapshot(opts) {
|
|
|
26063
27069
|
episodeId,
|
|
26064
27070
|
problems: problems2
|
|
26065
27071
|
}));
|
|
26066
|
-
const
|
|
26067
|
-
|
|
26068
|
-
|
|
26069
|
-
|
|
27072
|
+
const revisitSeen = /* @__PURE__ */ new Set();
|
|
27073
|
+
const needsRevisit = listNeedsRevisit(opts.store, (opts.now ?? /* @__PURE__ */ new Date()).getTime()).filter((r) => {
|
|
27074
|
+
const key = `${r.label}:${r.description.trim().toLowerCase()}`;
|
|
27075
|
+
if (revisitSeen.has(key)) return false;
|
|
27076
|
+
revisitSeen.add(key);
|
|
27077
|
+
return true;
|
|
27078
|
+
}).slice(0, 5);
|
|
26070
27079
|
const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
26071
27080
|
const pkgBase = (x) => {
|
|
26072
27081
|
const at = x.lastIndexOf("@");
|
|
@@ -26102,7 +27111,9 @@ function buildSnapshot(opts) {
|
|
|
26102
27111
|
profileContext,
|
|
26103
27112
|
recentProblems: recent,
|
|
26104
27113
|
recentResolved,
|
|
27114
|
+
recentConstraints,
|
|
26105
27115
|
...causalNudge ? { causalNudge } : {},
|
|
27116
|
+
...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
|
|
26106
27117
|
...domainNudge ? { domainNudge } : {},
|
|
26107
27118
|
motifs,
|
|
26108
27119
|
reviewCount: opts.reviewCount,
|
|
@@ -26153,10 +27164,16 @@ function sliceForFile(store2, relPath) {
|
|
|
26153
27164
|
}
|
|
26154
27165
|
const fp = priorsForFile(store2, relPath);
|
|
26155
27166
|
const priors = fp ? {
|
|
26156
|
-
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27167
|
+
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27168
|
+
id: p.id,
|
|
27169
|
+
description: p.description,
|
|
27170
|
+
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
27171
|
+
})),
|
|
27172
|
+
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
27173
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
26157
27174
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
26158
27175
|
solutionsByProblem: Object.fromEntries(
|
|
26159
|
-
fp.openProblems.slice(0, 3).map((p) => [
|
|
27176
|
+
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
26160
27177
|
p.id,
|
|
26161
27178
|
(fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
|
|
26162
27179
|
])
|
|
@@ -26193,37 +27210,37 @@ init_src3();
|
|
|
26193
27210
|
// ../../node_modules/.pnpm/env-paths@3.0.0/node_modules/env-paths/index.js
|
|
26194
27211
|
import os from "node:os";
|
|
26195
27212
|
import process3 from "node:process";
|
|
26196
|
-
var
|
|
27213
|
+
var homedir2 = os.homedir();
|
|
26197
27214
|
var tmpdir = os.tmpdir();
|
|
26198
27215
|
var { env } = process3;
|
|
26199
27216
|
|
|
26200
27217
|
// src/paths.ts
|
|
26201
|
-
import { dirname as dirname2, join as
|
|
27218
|
+
import { dirname as dirname2, join as join4 } from "node:path";
|
|
26202
27219
|
function workspaceDir(workspaceRoot) {
|
|
26203
|
-
return
|
|
27220
|
+
return join4(workspaceRoot, ".errata");
|
|
26204
27221
|
}
|
|
26205
27222
|
function workspacePaths(root) {
|
|
26206
27223
|
const dir = workspaceDir(root);
|
|
26207
27224
|
return {
|
|
26208
27225
|
root,
|
|
26209
27226
|
configDir: dir,
|
|
26210
|
-
workspaceJson:
|
|
26211
|
-
eventLog:
|
|
26212
|
-
castalia:
|
|
26213
|
-
reviewQueue:
|
|
26214
|
-
outbox:
|
|
26215
|
-
daemonLock:
|
|
26216
|
-
identityAudit:
|
|
26217
|
-
skillsDir:
|
|
26218
|
-
skillsManifest:
|
|
27227
|
+
workspaceJson: join4(dir, "workspace.json"),
|
|
27228
|
+
eventLog: join4(dir, "eventlog.sqlite"),
|
|
27229
|
+
castalia: join4(dir, "castalia.db"),
|
|
27230
|
+
reviewQueue: join4(dir, "review-queue.json"),
|
|
27231
|
+
outbox: join4(dir, "outbox"),
|
|
27232
|
+
daemonLock: join4(dir, "daemon.lock"),
|
|
27233
|
+
identityAudit: join4(dir, "identity-audit.log"),
|
|
27234
|
+
skillsDir: join4(dir, "skills"),
|
|
27235
|
+
skillsManifest: join4(dir, "skills.json")
|
|
26219
27236
|
};
|
|
26220
27237
|
}
|
|
26221
27238
|
|
|
26222
27239
|
// src/reconcile.ts
|
|
26223
27240
|
init_src3();
|
|
26224
27241
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
26225
|
-
import { readdirSync as
|
|
26226
|
-
import { join as
|
|
27242
|
+
import { readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
27243
|
+
import { join as join5, relative as relative2, sep as sep2 } from "node:path";
|
|
26227
27244
|
var IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
|
|
26228
27245
|
var SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
|
|
26229
27246
|
function gitSourceFiles(root) {
|
|
@@ -26240,7 +27257,7 @@ function gitSourceFiles(root) {
|
|
|
26240
27257
|
const out2 = [];
|
|
26241
27258
|
for (const rel of stdout.split("\0")) {
|
|
26242
27259
|
if (!rel || !SOURCE_RE.test(rel)) continue;
|
|
26243
|
-
const abs =
|
|
27260
|
+
const abs = join5(root, rel);
|
|
26244
27261
|
if (IGNORED.test(abs)) continue;
|
|
26245
27262
|
out2.push(abs);
|
|
26246
27263
|
}
|
|
@@ -26249,13 +27266,13 @@ function gitSourceFiles(root) {
|
|
|
26249
27266
|
function* walkSource(dir) {
|
|
26250
27267
|
let entries;
|
|
26251
27268
|
try {
|
|
26252
|
-
entries =
|
|
27269
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
26253
27270
|
} catch {
|
|
26254
27271
|
return;
|
|
26255
27272
|
}
|
|
26256
27273
|
for (const e of entries) {
|
|
26257
27274
|
const name2 = String(e.name);
|
|
26258
|
-
const full =
|
|
27275
|
+
const full = join5(dir, name2);
|
|
26259
27276
|
if (IGNORED.test(full)) continue;
|
|
26260
27277
|
if (e.isDirectory()) yield* walkSource(full);
|
|
26261
27278
|
else if (SOURCE_RE.test(name2)) yield full;
|
|
@@ -26264,12 +27281,18 @@ function* walkSource(dir) {
|
|
|
26264
27281
|
function listSourceFiles(root) {
|
|
26265
27282
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
26266
27283
|
}
|
|
26267
|
-
|
|
27284
|
+
var SCAN_YIELD_EVERY = 100;
|
|
27285
|
+
var LIVENESS_CHUNK = 4e3;
|
|
27286
|
+
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
26268
27287
|
const stale = [];
|
|
27288
|
+
const pending = [];
|
|
27289
|
+
const allTargets = [];
|
|
27290
|
+
let scanned = 0;
|
|
26269
27291
|
for (const abs of listSourceFiles(rootPath)) {
|
|
27292
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
26270
27293
|
let mtimeMs;
|
|
26271
27294
|
try {
|
|
26272
|
-
mtimeMs =
|
|
27295
|
+
mtimeMs = statSync3(abs).mtimeMs;
|
|
26273
27296
|
} catch {
|
|
26274
27297
|
continue;
|
|
26275
27298
|
}
|
|
@@ -26277,32 +27300,52 @@ function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
26277
27300
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
26278
27301
|
if (!fnode) {
|
|
26279
27302
|
stale.push(abs);
|
|
27303
|
+
} else if ((fnode.validTo ?? null) !== null) {
|
|
27304
|
+
stale.push(abs);
|
|
26280
27305
|
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
26281
27306
|
stale.push(abs);
|
|
26282
27307
|
} else {
|
|
26283
27308
|
const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
26284
27309
|
if (edges.length === 0) {
|
|
26285
27310
|
stale.push(abs);
|
|
26286
|
-
} else
|
|
26287
|
-
|
|
27311
|
+
} else {
|
|
27312
|
+
const targets = edges.map((e) => e.to);
|
|
27313
|
+
pending.push({ abs, targets });
|
|
27314
|
+
allTargets.push(...targets);
|
|
26288
27315
|
}
|
|
26289
27316
|
}
|
|
26290
27317
|
}
|
|
27318
|
+
const live = /* @__PURE__ */ new Set();
|
|
27319
|
+
for (let i2 = 0; i2 < allTargets.length; i2 += LIVENESS_CHUNK) {
|
|
27320
|
+
for (const id of store2.liveNodeIds(allTargets.slice(i2, i2 + LIVENESS_CHUNK))) live.add(id);
|
|
27321
|
+
if (i2 + LIVENESS_CHUNK < allTargets.length) await new Promise((r) => setImmediate(r));
|
|
27322
|
+
}
|
|
27323
|
+
for (const { abs, targets } of pending) {
|
|
27324
|
+
if (targets.some((id) => !live.has(id))) stale.push(abs);
|
|
27325
|
+
}
|
|
26291
27326
|
return stale;
|
|
26292
27327
|
}
|
|
26293
|
-
function isLive(n) {
|
|
26294
|
-
return n != null && (n.validTo ?? null) === null;
|
|
26295
|
-
}
|
|
26296
27328
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
26297
|
-
const stale = findStaleFiles(store2, rootPath, workspaceId);
|
|
27329
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId);
|
|
26298
27330
|
if (stale.length === 0) return 0;
|
|
26299
27331
|
let total = 0;
|
|
27332
|
+
let failedBatches = 0;
|
|
26300
27333
|
const BATCH = 20;
|
|
26301
27334
|
for (let i2 = 0; i2 < stale.length; i2 += BATCH) {
|
|
26302
|
-
|
|
26303
|
-
|
|
27335
|
+
try {
|
|
27336
|
+
const r = await incrementalReindex(store2, rootPath, workspaceId, stale.slice(i2, i2 + BATCH));
|
|
27337
|
+
total += r.filesReindexed;
|
|
27338
|
+
} catch (err2) {
|
|
27339
|
+
failedBatches++;
|
|
27340
|
+
console.warn(
|
|
27341
|
+
`[errata] reconcile: batch ${i2 / BATCH + 1} failed (${stale.slice(i2, i2 + BATCH).length} file(s) skipped): ${err2 instanceof Error ? err2.message : err2}`
|
|
27342
|
+
);
|
|
27343
|
+
}
|
|
26304
27344
|
if (i2 + BATCH < stale.length) await new Promise((res) => setImmediate(res));
|
|
26305
27345
|
}
|
|
27346
|
+
if (failedBatches > 0) {
|
|
27347
|
+
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
27348
|
+
}
|
|
26306
27349
|
return total;
|
|
26307
27350
|
}
|
|
26308
27351
|
|
|
@@ -26355,6 +27398,7 @@ function runNightly() {
|
|
|
26355
27398
|
const a = backfillLegacyAnchors(store, Date.now());
|
|
26356
27399
|
anchorsDemoted = a.demotedEdges;
|
|
26357
27400
|
resolutionsSuspect = a.suspects.length;
|
|
27401
|
+
repairLegacyAnchorsFromStatement(store, Date.now());
|
|
26358
27402
|
} catch {
|
|
26359
27403
|
}
|
|
26360
27404
|
const report = runNightlyPipeline(store);
|