@inerrata-corporation/errata 2.0.2-dev.81 → 2.0.2-dev.835
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 +583 -239
- package/errata.mjs +8111 -2067
- package/package.json +1 -1
- package/pass-worker.mjs +1132 -215
package/pass-worker.mjs
CHANGED
|
@@ -144,7 +144,14 @@ var init_castalia = __esm({
|
|
|
144
144
|
"SOLVED_BY",
|
|
145
145
|
"MITIGATES",
|
|
146
146
|
"REPORTED_FAILURE",
|
|
147
|
-
"CONTRADICTS"
|
|
147
|
+
"CONTRADICTS",
|
|
148
|
+
// Motif twin-faces (KN-twin-faces): failure-face motif → remedy-face motif
|
|
149
|
+
// across layers (AntiPattern/Weakness ↔ Technique/Pattern). Same
|
|
150
|
+
// problem→fix direction as FIXED_BY/SOLVED_BY. Minted by the nightly LLM
|
|
151
|
+
// twin-face pass — these are the near-identical cross-layer pairs the
|
|
152
|
+
// polarity gate (finding 5) correctly refuses to FUSE; the link carries
|
|
153
|
+
// what fusion can't.
|
|
154
|
+
"REMEDIED_BY"
|
|
148
155
|
];
|
|
149
156
|
CONCEPTUAL_EDGES = [
|
|
150
157
|
"INSTANCE_OF",
|
|
@@ -259,6 +266,10 @@ var init_castalia = __esm({
|
|
|
259
266
|
// git topology, not signal-flow
|
|
260
267
|
"AUTHORED_BY",
|
|
261
268
|
// git authorship, not signal-flow
|
|
269
|
+
"PRODUCED",
|
|
270
|
+
// edit-episode provenance (Episode→symbol), not signal-flow — the family
|
|
271
|
+
// above was excluded together but this member slipped the net (LC-produced-pagerank);
|
|
272
|
+
// at 43% of all live edges it was the largest single edge population flowing rank
|
|
262
273
|
"CONTRIBUTED",
|
|
263
274
|
// agent attribution (Agent → knowledge), not signal-flow
|
|
264
275
|
"SUPERSEDES",
|
|
@@ -274,6 +285,10 @@ var init_castalia = __esm({
|
|
|
274
285
|
CAUSED_BY: 3,
|
|
275
286
|
FIXED_BY: 3,
|
|
276
287
|
SOLVED_BY: 3,
|
|
288
|
+
// Twin-face link (failure motif → remedy motif, KN-twin-faces) — causal-grade
|
|
289
|
+
// but a notch below witnessed FIXED_BY/SOLVED_BY: the pairing is an LLM
|
|
290
|
+
// judgment over descriptions, not an agent-witnessed resolution.
|
|
291
|
+
REMEDIED_BY: 2.5,
|
|
277
292
|
MANIFESTS_AS: 2,
|
|
278
293
|
ESCALATES_TO: 1.5,
|
|
279
294
|
AFFECTS: 1.2,
|
|
@@ -447,13 +462,18 @@ function resolveCanonical(label) {
|
|
|
447
462
|
function resolveCanonicalId(label) {
|
|
448
463
|
return resolveCanonical(label)?.id;
|
|
449
464
|
}
|
|
465
|
+
function resolveUnambiguousCanonicalId(label) {
|
|
466
|
+
const key = label.trim().toLowerCase();
|
|
467
|
+
if (AMBIGUOUS_ALIASES.has(key)) return void 0;
|
|
468
|
+
return resolveCanonicalId(key);
|
|
469
|
+
}
|
|
450
470
|
function aliasesLongestFirst() {
|
|
451
471
|
const out2 = [];
|
|
452
472
|
for (const e of REGISTRY) for (const a of e.aliases) out2.push({ alias: a.toLowerCase(), entity: e });
|
|
453
473
|
out2.sort((x, y) => y.alias.length - x.alias.length);
|
|
454
474
|
return out2;
|
|
455
475
|
}
|
|
456
|
-
var REGISTRY, BY_ALIAS;
|
|
476
|
+
var REGISTRY, AMBIGUOUS_ALIASES, BY_ALIAS;
|
|
457
477
|
var init_taxonomy = __esm({
|
|
458
478
|
"../../packages/shared/src/nlp/taxonomy.ts"() {
|
|
459
479
|
"use strict";
|
|
@@ -502,6 +522,13 @@ var init_taxonomy = __esm({
|
|
|
502
522
|
{ id: "concept:retry", name: "retry", category: "concept", aliases: ["retry"] },
|
|
503
523
|
{ id: "concept:migration", name: "migration", category: "concept", aliases: ["migration"] }
|
|
504
524
|
];
|
|
525
|
+
AMBIGUOUS_ALIASES = /* @__PURE__ */ new Set([
|
|
526
|
+
"node",
|
|
527
|
+
"go",
|
|
528
|
+
"pool",
|
|
529
|
+
"spring",
|
|
530
|
+
"ws"
|
|
531
|
+
]);
|
|
505
532
|
BY_ALIAS = /* @__PURE__ */ new Map();
|
|
506
533
|
for (const e of REGISTRY) {
|
|
507
534
|
for (const a of e.aliases) {
|
|
@@ -563,7 +590,7 @@ function lemma(token) {
|
|
|
563
590
|
}
|
|
564
591
|
return token;
|
|
565
592
|
}
|
|
566
|
-
function
|
|
593
|
+
function tokenize(text, resolve) {
|
|
567
594
|
const matches = text.normalize("NFC").toLowerCase().match(TOKEN_RE) ?? [];
|
|
568
595
|
const out2 = /* @__PURE__ */ new Set();
|
|
569
596
|
for (const raw of matches) {
|
|
@@ -573,11 +600,14 @@ function conceptTokens(text) {
|
|
|
573
600
|
out2.add(token);
|
|
574
601
|
continue;
|
|
575
602
|
}
|
|
576
|
-
const canonical =
|
|
603
|
+
const canonical = resolve(token);
|
|
577
604
|
out2.add(canonical ?? lemma(token));
|
|
578
605
|
}
|
|
579
606
|
return [...out2].sort();
|
|
580
607
|
}
|
|
608
|
+
function retrievalTokens(text) {
|
|
609
|
+
return tokenize(text, resolveUnambiguousCanonicalId);
|
|
610
|
+
}
|
|
581
611
|
var STOPWORDS, NEGATION_TOKENS, TOKEN_RE;
|
|
582
612
|
var init_concept_bag = __esm({
|
|
583
613
|
"../../packages/shared/src/nlp/concept-bag.ts"() {
|
|
@@ -15049,13 +15079,21 @@ var init_edge_rules = __esm({
|
|
|
15049
15079
|
// NOT ruled here — the type pre-exists with broader extractor senses, and a
|
|
15050
15080
|
// new rule on an old type would reject legitimate live flows (reject-never-flip
|
|
15051
15081
|
// cuts both ways: only rule types you introduce or senses that are documented).
|
|
15052
|
-
|
|
15082
|
+
// `Component` joined the target set with OM-agent-anchors: an agent-named
|
|
15083
|
+
// component ("React Router") is the same knowledge→named-unit anchor shape as
|
|
15084
|
+
// a Tool — the knowledge is ABOUT it, not dependent on it.
|
|
15085
|
+
CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
|
|
15053
15086
|
OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
|
|
15054
15087
|
INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
|
|
15055
15088
|
// ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
|
|
15056
15089
|
IS_A: { from: ["Weakness"], to: ["Weakness"] },
|
|
15057
15090
|
// ── Conceptual (v1 taxonomy.ts: instance → motif/pattern reference) ──
|
|
15058
|
-
|
|
15091
|
+
// `AntiPattern` joined the target set 2026-08-02: it is one of the three motif
|
|
15092
|
+
// kinds (generalizer motifs.ts `layerOf`: pattern | technique | antipattern) and
|
|
15093
|
+
// the SUPPRESSED-close path mints `Problem ─INSTANCE_OF→ AntiPattern` as the
|
|
15094
|
+
// negative-knowledge binding ("silenced, not solved") — the original three-label
|
|
15095
|
+
// rule predates AntiPattern joining the motif layer and silently ate that edge.
|
|
15096
|
+
INSTANCE_OF: { to: ["Pattern", "AntiPattern", "Weakness", "Technique"] },
|
|
15059
15097
|
IMPLEMENTS: { from: ["Solution", "Language", "Component"], to: ["Pattern", "Technique"] },
|
|
15060
15098
|
MATCHES: { to: ["Pattern"] },
|
|
15061
15099
|
// ── Artifact evidence (v1 taxonomy.ts artifact rows) ──
|
|
@@ -15156,6 +15194,23 @@ var init_wire = __esm({
|
|
|
15156
15194
|
/** Canonical human-readable description (no raw paths — daemon scrubs; server rechecks). */
|
|
15157
15195
|
description: external_exports.string().min(1).max(4e3),
|
|
15158
15196
|
attrs: external_exports.record(external_exports.string(), external_exports.unknown()).default({}),
|
|
15197
|
+
/** One-way origin key of the SESSION that minted this node (`ws_…`, a
|
|
15198
|
+
* truncated digest — never the raw session id). Stamped onto the created
|
|
15199
|
+
* node as `authoringSession`, which is the independence unit for the
|
|
15200
|
+
* evidence channels: the session that authored a claim may not corroborate
|
|
15201
|
+
* or refute it, while a DIFFERENT session on the same checkout may (alyssa,
|
|
15202
|
+
* 2026-07-31 — the workspace key made every witness on a single-checkout
|
|
15203
|
+
* deployment a self-corroboration). Optional + additive: absent leaves the
|
|
15204
|
+
* gate fail-open for that node, exactly today's behaviour. */
|
|
15205
|
+
originSession: external_exports.string().min(3).max(64).optional(),
|
|
15206
|
+
/** Epoch ms of the ORIGINATING local node's creation — when the knowledge was
|
|
15207
|
+
* actually captured, as opposed to when its public twin reached the cloud.
|
|
15208
|
+
* Stored as `observedAt`; the network board's chronological view orders on
|
|
15209
|
+
* it. Without this the wire carried NO timestamp at all, so a node captured
|
|
15210
|
+
* days ago surfaced as "new" the moment it was generalized and published —
|
|
15211
|
+
* the board showed ingest order wearing a chronology's clothes. Optional +
|
|
15212
|
+
* additive: absent keeps ingest-time ordering for that node. */
|
|
15213
|
+
originCreatedAtMs: external_exports.number().int().positive().optional(),
|
|
15159
15214
|
extractionSource: external_exports.enum(INGEST_EXTRACTION_SOURCES),
|
|
15160
15215
|
validationSource: external_exports.enum(VALIDATION_SOURCES).optional(),
|
|
15161
15216
|
/** Org-membrane (M2): the daemon's anchor tag — it owns the lockfile, so it
|
|
@@ -15328,6 +15383,51 @@ var init_review = __esm({
|
|
|
15328
15383
|
}
|
|
15329
15384
|
});
|
|
15330
15385
|
|
|
15386
|
+
// ../../packages/local-shared/src/anchorable.ts
|
|
15387
|
+
function anchorableExtensionAlternation() {
|
|
15388
|
+
return BY_LENGTH.map((e) => e.slice(1).replace(/[+.]/g, (c) => `\\${c}`)).join("|");
|
|
15389
|
+
}
|
|
15390
|
+
var ANCHORABLE_EXTENSIONS, BY_LENGTH;
|
|
15391
|
+
var init_anchorable = __esm({
|
|
15392
|
+
"../../packages/local-shared/src/anchorable.ts"() {
|
|
15393
|
+
"use strict";
|
|
15394
|
+
ANCHORABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
15395
|
+
// typescript provider
|
|
15396
|
+
".ts",
|
|
15397
|
+
".tsx",
|
|
15398
|
+
".js",
|
|
15399
|
+
".jsx",
|
|
15400
|
+
".mjs",
|
|
15401
|
+
".cjs",
|
|
15402
|
+
// python / go / rust / ruby / csharp providers
|
|
15403
|
+
".py",
|
|
15404
|
+
".go",
|
|
15405
|
+
".rs",
|
|
15406
|
+
".rb",
|
|
15407
|
+
".cs",
|
|
15408
|
+
// cpp provider — every variant it parses, not just the three that were listed
|
|
15409
|
+
".c",
|
|
15410
|
+
".h",
|
|
15411
|
+
".cpp",
|
|
15412
|
+
".cc",
|
|
15413
|
+
".cxx",
|
|
15414
|
+
".c++",
|
|
15415
|
+
".hpp",
|
|
15416
|
+
".hh",
|
|
15417
|
+
".hxx",
|
|
15418
|
+
".h++",
|
|
15419
|
+
// CUDA — the cpp provider parses these too. Missed when this list was first
|
|
15420
|
+
// transcribed by hand; the parity test caught them on its very first run,
|
|
15421
|
+
// which is the argument for the test existing.
|
|
15422
|
+
".cu",
|
|
15423
|
+
".cuh",
|
|
15424
|
+
// no provider yet; the grammar ships. Inert, not wrong — see above.
|
|
15425
|
+
".java"
|
|
15426
|
+
]);
|
|
15427
|
+
BY_LENGTH = [...ANCHORABLE_EXTENSIONS].sort((a, b) => b.length - a.length);
|
|
15428
|
+
}
|
|
15429
|
+
});
|
|
15430
|
+
|
|
15331
15431
|
// ../../packages/local-shared/src/sqlite-adapter.ts
|
|
15332
15432
|
function openDatabase(path) {
|
|
15333
15433
|
const db = new DatabaseSync(path);
|
|
@@ -15340,6 +15440,7 @@ function openDatabase(path) {
|
|
|
15340
15440
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15341
15441
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15342
15442
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15443
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15343
15444
|
} catch {
|
|
15344
15445
|
}
|
|
15345
15446
|
}
|
|
@@ -15375,7 +15476,7 @@ function openDatabase(path) {
|
|
|
15375
15476
|
return db.prepare(`PRAGMA ${key}`).get();
|
|
15376
15477
|
},
|
|
15377
15478
|
transaction(fn) {
|
|
15378
|
-
db.exec("BEGIN");
|
|
15479
|
+
db.exec("BEGIN IMMEDIATE");
|
|
15379
15480
|
try {
|
|
15380
15481
|
const r = fn();
|
|
15381
15482
|
db.exec("COMMIT");
|
|
@@ -15429,6 +15530,7 @@ var init_src2 = __esm({
|
|
|
15429
15530
|
init_profile();
|
|
15430
15531
|
init_daemon_wire();
|
|
15431
15532
|
init_review();
|
|
15533
|
+
init_anchorable();
|
|
15432
15534
|
init_sqlite_adapter();
|
|
15433
15535
|
}
|
|
15434
15536
|
});
|
|
@@ -15451,7 +15553,7 @@ function popcount(x) {
|
|
|
15451
15553
|
}
|
|
15452
15554
|
return c;
|
|
15453
15555
|
}
|
|
15454
|
-
function
|
|
15556
|
+
function tokenize2(text) {
|
|
15455
15557
|
return text.match(/[A-Za-z_$][A-Za-z0-9_$]*|\d+|[^\s\w]/g) ?? [];
|
|
15456
15558
|
}
|
|
15457
15559
|
function shingle(tokens, n = SHINGLE_N) {
|
|
@@ -15480,7 +15582,7 @@ function simhashFeatures(features) {
|
|
|
15480
15582
|
return out2;
|
|
15481
15583
|
}
|
|
15482
15584
|
function simhash(text) {
|
|
15483
|
-
return simhashFeatures(shingle(
|
|
15585
|
+
return simhashFeatures(shingle(tokenize2(text)));
|
|
15484
15586
|
}
|
|
15485
15587
|
function hammingDistance(a, b) {
|
|
15486
15588
|
return popcount((a ^ b) & MASK64);
|
|
@@ -15788,6 +15890,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15788
15890
|
};
|
|
15789
15891
|
}
|
|
15790
15892
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
15893
|
+
const contentMovedPaths = /* @__PURE__ */ new Set();
|
|
15791
15894
|
for (const rel of [...changedRelPaths]) {
|
|
15792
15895
|
let h;
|
|
15793
15896
|
try {
|
|
@@ -15800,6 +15903,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15800
15903
|
if (fnode && fnode.attrs["contentHash"] === h && !fileHasStrandedSymbol(store2, fnode.id)) {
|
|
15801
15904
|
store2.updateNode(fnode.id, { lastUpdatedAt: t });
|
|
15802
15905
|
changedRelPaths.delete(rel);
|
|
15906
|
+
} else if (!fnode || fnode.attrs["contentHash"] !== h) {
|
|
15907
|
+
contentMovedPaths.add(rel);
|
|
15803
15908
|
}
|
|
15804
15909
|
}
|
|
15805
15910
|
if (changedRelPaths.size === 0) {
|
|
@@ -15934,6 +16039,7 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
15934
16039
|
changedRelPaths
|
|
15935
16040
|
});
|
|
15936
16041
|
store2.transaction(() => {
|
|
16042
|
+
store2.reviveReobserved([...changedRelPaths], workspaceId, t);
|
|
15937
16043
|
const versionedAndFile = /* @__PURE__ */ new Set([...VERSIONED_LABELS, "File"]);
|
|
15938
16044
|
const ownedLive = [];
|
|
15939
16045
|
for (const n of store2.nodesByRelPath([...changedRelPaths])) {
|
|
@@ -16042,7 +16148,14 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
16042
16148
|
const h = fileHashes.get(rel);
|
|
16043
16149
|
if (!h) continue;
|
|
16044
16150
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
16045
|
-
if (fnode)
|
|
16151
|
+
if (!fnode) continue;
|
|
16152
|
+
store2.updateNode(fnode.id, {
|
|
16153
|
+
attrs: {
|
|
16154
|
+
...fnode.attrs,
|
|
16155
|
+
contentHash: h,
|
|
16156
|
+
...contentMovedPaths.has(rel) ? { contentChangedAt: t } : {}
|
|
16157
|
+
}
|
|
16158
|
+
});
|
|
16046
16159
|
}
|
|
16047
16160
|
return {
|
|
16048
16161
|
filesReindexed: changedRelPaths.size,
|
|
@@ -16613,7 +16726,7 @@ async function scan(root, current, ignores, providers, out2) {
|
|
|
16613
16726
|
}
|
|
16614
16727
|
}
|
|
16615
16728
|
}
|
|
16616
|
-
function
|
|
16729
|
+
function gitListRelPaths(root, ignores) {
|
|
16617
16730
|
let stdout;
|
|
16618
16731
|
try {
|
|
16619
16732
|
stdout = execFileSync(
|
|
@@ -16636,6 +16749,15 @@ function gitListFiles(root, ignores, providers) {
|
|
|
16636
16749
|
if (segs.some((s) => ignores.has(s) || s.startsWith(".") && s.length > 1)) {
|
|
16637
16750
|
continue;
|
|
16638
16751
|
}
|
|
16752
|
+
out2.push(rel);
|
|
16753
|
+
}
|
|
16754
|
+
return out2;
|
|
16755
|
+
}
|
|
16756
|
+
function gitListFiles(root, ignores, providers) {
|
|
16757
|
+
const rels = gitListRelPaths(root, ignores);
|
|
16758
|
+
if (rels === null) return null;
|
|
16759
|
+
const out2 = [];
|
|
16760
|
+
for (const rel of rels) {
|
|
16639
16761
|
const abs = join(root, rel);
|
|
16640
16762
|
let size;
|
|
16641
16763
|
try {
|
|
@@ -23975,6 +24097,7 @@ var init_csharp_treesitter = __esm({
|
|
|
23975
24097
|
// ../../packages/indexer/src/index.ts
|
|
23976
24098
|
var src_exports = {};
|
|
23977
24099
|
__export(src_exports, {
|
|
24100
|
+
DEFAULT_IGNORES: () => DEFAULT_IGNORES,
|
|
23978
24101
|
DRIFT_FORK_RATIO: () => DRIFT_FORK_RATIO,
|
|
23979
24102
|
TreeSitterCSharpProvider: () => TreeSitterCSharpProvider,
|
|
23980
24103
|
TreeSitterCppProvider: () => TreeSitterCppProvider,
|
|
@@ -23991,6 +24114,7 @@ __export(src_exports, {
|
|
|
23991
24114
|
findInnermostScope: () => findInnermostScope,
|
|
23992
24115
|
folderNodeId: () => folderNodeId,
|
|
23993
24116
|
fromHex: () => fromHex,
|
|
24117
|
+
gitListRelPaths: () => gitListRelPaths,
|
|
23994
24118
|
hammingDistance: () => hammingDistance,
|
|
23995
24119
|
hasForkedDrift: () => hasForkedDrift,
|
|
23996
24120
|
incrementalReindex: () => incrementalReindex,
|
|
@@ -24003,7 +24127,7 @@ __export(src_exports, {
|
|
|
24003
24127
|
simhashFeatures: () => simhashFeatures,
|
|
24004
24128
|
symbolNodeId: () => symbolNodeId,
|
|
24005
24129
|
toHex: () => toHex,
|
|
24006
|
-
tokenize: () =>
|
|
24130
|
+
tokenize: () => tokenize2
|
|
24007
24131
|
});
|
|
24008
24132
|
function defaultProviders() {
|
|
24009
24133
|
if (providersCache) return providersCache;
|
|
@@ -24056,9 +24180,32 @@ var LOCAL_RULE_OVERRIDES = {
|
|
|
24056
24180
|
to: [...CODE_NODE_LABELS, "Symbol"]
|
|
24057
24181
|
},
|
|
24058
24182
|
REVEALED_BY: null,
|
|
24059
|
-
PRODUCED: null
|
|
24183
|
+
PRODUCED: null,
|
|
24184
|
+
// FIXED_BY locally ALSO carries the fix-provenance sense: `resolveProblem`
|
|
24185
|
+
// attributes a closed Problem to the fixing `Episode` (PLAN_PLASTICITY §2.1)
|
|
24186
|
+
// alongside the cloud's Problem→Solution knowledge claim. Same shape as
|
|
24187
|
+
// PRODUCED/REVEALED_BY above — an Episode is code-layer and never drains, so
|
|
24188
|
+
// the cloud door's `to: [Solution]` rule is untouched. The `from` constraint
|
|
24189
|
+
// stays: the reversed (Solution)-FIXED_BY->(...) splash bug is the reason
|
|
24190
|
+
// this rule exists at all.
|
|
24191
|
+
FIXED_BY: { from: EDGE_RULES["FIXED_BY"]?.from, to: ["Solution", "Episode"] }
|
|
24060
24192
|
};
|
|
24061
|
-
|
|
24193
|
+
function localEdgeViolation(fromLabel, type, toLabel) {
|
|
24194
|
+
if (type in LOCAL_RULE_OVERRIDES) {
|
|
24195
|
+
const rule = LOCAL_RULE_OVERRIDES[type];
|
|
24196
|
+
if (!rule) return null;
|
|
24197
|
+
if (fromLabel && rule.from && !rule.from.includes(fromLabel)) {
|
|
24198
|
+
return `${type} cannot originate from ${fromLabel} locally (allowed: ${rule.from.join(", ")})`;
|
|
24199
|
+
}
|
|
24200
|
+
if (toLabel && rule.to && !rule.to.includes(toLabel)) {
|
|
24201
|
+
return `${type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24202
|
+
}
|
|
24203
|
+
return null;
|
|
24204
|
+
}
|
|
24205
|
+
const verdict = isValidEdge(fromLabel, type, toLabel);
|
|
24206
|
+
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
24207
|
+
}
|
|
24208
|
+
var SCHEMA_VERSION = 6;
|
|
24062
24209
|
var SCHEMA_SQL = `
|
|
24063
24210
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
24064
24211
|
version INTEGER PRIMARY KEY
|
|
@@ -24073,6 +24220,21 @@ CREATE TABLE IF NOT EXISTS store_meta (
|
|
|
24073
24220
|
value TEXT NOT NULL
|
|
24074
24221
|
);
|
|
24075
24222
|
|
|
24223
|
+
-- Durable ledger of edges the ontology gate REFUSED, keyed by edge type.
|
|
24224
|
+
-- Durable rather than in-memory for one specific reason: the status command runs
|
|
24225
|
+
-- in a SEPARATE process and opens its own store handle, so a counter living on
|
|
24226
|
+
-- the instance reads 0 there forever. That is exactly how a producer rejecting
|
|
24227
|
+
-- 100% of its output stayed invisible for 17 days. Persisting it also survives
|
|
24228
|
+
-- the daemon restart that would otherwise erase the evidence.
|
|
24229
|
+
-- Keyed by type because a systematic producer bug shows up as ONE type
|
|
24230
|
+
-- dominating; sample keeps the latest reason so the count is actionable.
|
|
24231
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24232
|
+
type TEXT PRIMARY KEY,
|
|
24233
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24234
|
+
last_at INTEGER NOT NULL,
|
|
24235
|
+
sample TEXT
|
|
24236
|
+
);
|
|
24237
|
+
|
|
24076
24238
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
24077
24239
|
id TEXT PRIMARY KEY,
|
|
24078
24240
|
label TEXT NOT NULL,
|
|
@@ -24449,6 +24611,16 @@ var SqliteGraphStore = class {
|
|
|
24449
24611
|
if (!cols.has(name2)) this.db.exec(ddl);
|
|
24450
24612
|
}
|
|
24451
24613
|
}
|
|
24614
|
+
if (from < 6) {
|
|
24615
|
+
this.db.exec(`
|
|
24616
|
+
CREATE TABLE IF NOT EXISTS edge_rejections (
|
|
24617
|
+
type TEXT PRIMARY KEY,
|
|
24618
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
24619
|
+
last_at INTEGER NOT NULL,
|
|
24620
|
+
sample TEXT
|
|
24621
|
+
)
|
|
24622
|
+
`);
|
|
24623
|
+
}
|
|
24452
24624
|
this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
|
|
24453
24625
|
});
|
|
24454
24626
|
}
|
|
@@ -24530,6 +24702,7 @@ var SqliteGraphStore = class {
|
|
|
24530
24702
|
const violation = this.edgeRuleViolation(edge);
|
|
24531
24703
|
if (violation) {
|
|
24532
24704
|
this.rejectedEdgeCount++;
|
|
24705
|
+
this.recordEdgeRejection(edge.type, violation, edge.lastSeenAt || edge.createdAt || 0);
|
|
24533
24706
|
console.warn(`[local-graph] rejected edge ${edge.from}-[:${edge.type}]->${edge.to}: ${violation}`);
|
|
24534
24707
|
return;
|
|
24535
24708
|
}
|
|
@@ -24554,22 +24727,51 @@ var SqliteGraphStore = class {
|
|
|
24554
24727
|
* overlay consulted first. Returns the reason string on a documented-
|
|
24555
24728
|
* forbidden combination, else null. Point lookups on the id PK — negligible
|
|
24556
24729
|
* next to the insert itself. */
|
|
24557
|
-
|
|
24558
|
-
|
|
24559
|
-
|
|
24560
|
-
|
|
24561
|
-
|
|
24562
|
-
|
|
24563
|
-
|
|
24564
|
-
|
|
24565
|
-
|
|
24566
|
-
|
|
24567
|
-
return `${edge.type} cannot target ${toLabel} locally (allowed: ${rule.to.join(", ")})`;
|
|
24568
|
-
}
|
|
24569
|
-
return null;
|
|
24730
|
+
/** Upsert one refusal into the durable ledger. Best-effort: a bookkeeping
|
|
24731
|
+
* failure must never turn a refused edge into a thrown write. */
|
|
24732
|
+
recordEdgeRejection(type, reason, at) {
|
|
24733
|
+
try {
|
|
24734
|
+
this.db.prepare(
|
|
24735
|
+
`INSERT INTO edge_rejections (type, count, last_at, sample) VALUES (?, 1, ?, ?)
|
|
24736
|
+
ON CONFLICT(type) DO UPDATE SET
|
|
24737
|
+
count = count + 1, last_at = excluded.last_at, sample = excluded.sample`
|
|
24738
|
+
).run(type, at, reason.slice(0, 200));
|
|
24739
|
+
} catch {
|
|
24570
24740
|
}
|
|
24571
|
-
|
|
24572
|
-
|
|
24741
|
+
}
|
|
24742
|
+
/** Refusals recorded by the ontology gate, per edge type, newest activity first.
|
|
24743
|
+
* Durable across restarts and readable from any process (see the table note). */
|
|
24744
|
+
edgeRejections() {
|
|
24745
|
+
try {
|
|
24746
|
+
return this.db.prepare(
|
|
24747
|
+
"SELECT type, count, last_at AS lastAt, sample FROM edge_rejections ORDER BY count DESC, last_at DESC"
|
|
24748
|
+
).all();
|
|
24749
|
+
} catch {
|
|
24750
|
+
return [];
|
|
24751
|
+
}
|
|
24752
|
+
}
|
|
24753
|
+
/** Drop ledger entries whose last refusal predates `cutoff`. A still-misbehaving
|
|
24754
|
+
* producer keeps refreshing `last_at` and survives; a fixed one fades out. */
|
|
24755
|
+
pruneEdgeRejections(cutoff) {
|
|
24756
|
+
try {
|
|
24757
|
+
this.db.prepare("DELETE FROM edge_rejections WHERE last_at < ?").run(cutoff);
|
|
24758
|
+
} catch {
|
|
24759
|
+
}
|
|
24760
|
+
}
|
|
24761
|
+
/** Clear the ledger outright, whole or per type — operator escape hatch. */
|
|
24762
|
+
clearEdgeRejections(type) {
|
|
24763
|
+
try {
|
|
24764
|
+
if (type) this.db.prepare("DELETE FROM edge_rejections WHERE type = ?").run(type);
|
|
24765
|
+
else this.db.exec("DELETE FROM edge_rejections");
|
|
24766
|
+
} catch {
|
|
24767
|
+
}
|
|
24768
|
+
}
|
|
24769
|
+
edgeRuleViolation(edge) {
|
|
24770
|
+
return localEdgeViolation(
|
|
24771
|
+
this.getNode(edge.from)?.label,
|
|
24772
|
+
edge.type,
|
|
24773
|
+
this.getNode(edge.to)?.label
|
|
24774
|
+
);
|
|
24573
24775
|
}
|
|
24574
24776
|
updateEdge(id, patch) {
|
|
24575
24777
|
this.stmts.updateEdge.run({
|
|
@@ -24706,6 +24908,163 @@ var SqliteGraphStore = class {
|
|
|
24706
24908
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
24707
24909
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
24708
24910
|
}
|
|
24911
|
+
getMeta(key) {
|
|
24912
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
24913
|
+
return r?.value ?? null;
|
|
24914
|
+
}
|
|
24915
|
+
setMeta(key, value) {
|
|
24916
|
+
this.db.prepare(
|
|
24917
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
24918
|
+
).run({ key, value });
|
|
24919
|
+
}
|
|
24920
|
+
dirtyNodeIdsSince(ts) {
|
|
24921
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
24922
|
+
return rows.map((r) => r.id);
|
|
24923
|
+
}
|
|
24924
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
24925
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
24926
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
24927
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
24928
|
+
edgeEndpointsTouchedSince(ts) {
|
|
24929
|
+
const rows = this.db.prepare(
|
|
24930
|
+
`SELECT from_id, to_id FROM edges
|
|
24931
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
24932
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
24933
|
+
).all({ ts });
|
|
24934
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
24935
|
+
}
|
|
24936
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
24937
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
24938
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
24939
|
+
outEdgesForMany(ids) {
|
|
24940
|
+
return this.edgesForMany(ids, "from_id");
|
|
24941
|
+
}
|
|
24942
|
+
inEdgesForMany(ids) {
|
|
24943
|
+
return this.edgesForMany(ids, "to_id");
|
|
24944
|
+
}
|
|
24945
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
24946
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
24947
|
+
ranksForMany(ids) {
|
|
24948
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
24949
|
+
const CHUNK = 400;
|
|
24950
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
24951
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
24952
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
24953
|
+
const rows = this.db.prepare(
|
|
24954
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
24955
|
+
).all(...chunk);
|
|
24956
|
+
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
24957
|
+
}
|
|
24958
|
+
return out2;
|
|
24959
|
+
}
|
|
24960
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
24961
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
24962
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
24963
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
24964
|
+
* scanLiveNodes materialization. */
|
|
24965
|
+
landmarkSweepRows() {
|
|
24966
|
+
const rows = this.db.prepare(
|
|
24967
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
24968
|
+
WHERE valid_to IS NULL
|
|
24969
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
24970
|
+
).all();
|
|
24971
|
+
return rows.map((r) => ({
|
|
24972
|
+
id: r.id,
|
|
24973
|
+
label: r.label,
|
|
24974
|
+
pageRank: r.page_rank,
|
|
24975
|
+
isLandmark: r.is_landmark === 1,
|
|
24976
|
+
memoryTier: r.memory_tier,
|
|
24977
|
+
extractionSource: r.extraction_source
|
|
24978
|
+
}));
|
|
24979
|
+
}
|
|
24980
|
+
edgesForMany(ids, col) {
|
|
24981
|
+
const out2 = [];
|
|
24982
|
+
const CHUNK = 400;
|
|
24983
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
24984
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
24985
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
24986
|
+
const rows = this.db.prepare(
|
|
24987
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
24988
|
+
).all(...chunk);
|
|
24989
|
+
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
24990
|
+
}
|
|
24991
|
+
return out2;
|
|
24992
|
+
}
|
|
24993
|
+
checkpointWal() {
|
|
24994
|
+
try {
|
|
24995
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
24996
|
+
return r ?? null;
|
|
24997
|
+
} catch {
|
|
24998
|
+
return null;
|
|
24999
|
+
}
|
|
25000
|
+
}
|
|
25001
|
+
reviveReobserved(relPaths, workspaceId, since) {
|
|
25002
|
+
let revived = 0;
|
|
25003
|
+
const CHUNK = 400;
|
|
25004
|
+
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
25005
|
+
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
25006
|
+
if (slice.length === 0) continue;
|
|
25007
|
+
const r = this.db.prepare(
|
|
25008
|
+
`UPDATE nodes SET valid_to = NULL
|
|
25009
|
+
WHERE valid_to IS NOT NULL
|
|
25010
|
+
AND last_updated_at >= ?
|
|
25011
|
+
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
25012
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})`
|
|
25013
|
+
).run(since, workspaceId, ...slice);
|
|
25014
|
+
revived += Number(r.changes);
|
|
25015
|
+
}
|
|
25016
|
+
if (revived > 0) this.mutations++;
|
|
25017
|
+
return revived;
|
|
25018
|
+
}
|
|
25019
|
+
recentEdgeAttrCoverage(marker, attr, limit) {
|
|
25020
|
+
const r = this.db.prepare(
|
|
25021
|
+
`SELECT COUNT(*) AS total,
|
|
25022
|
+
COALESCE(SUM(CASE WHEN json_extract(attrs_json, '$.' || ?) = 1 THEN 1 ELSE 0 END), 0) AS count
|
|
25023
|
+
FROM (SELECT attrs_json FROM edges
|
|
25024
|
+
WHERE valid_to IS NULL AND attrs_json LIKE ?
|
|
25025
|
+
ORDER BY created_at DESC LIMIT ?)`
|
|
25026
|
+
).get(attr, `%${marker}%`, limit);
|
|
25027
|
+
return { count: Number(r.count), total: Number(r.total) };
|
|
25028
|
+
}
|
|
25029
|
+
liveNodeIds(ids) {
|
|
25030
|
+
const live = /* @__PURE__ */ new Set();
|
|
25031
|
+
const CHUNK = 900;
|
|
25032
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
25033
|
+
const slice = ids.slice(i2, i2 + CHUNK);
|
|
25034
|
+
if (slice.length === 0) continue;
|
|
25035
|
+
const rows = this.db.prepare(
|
|
25036
|
+
`SELECT id FROM nodes WHERE valid_to IS NULL AND id IN (${slice.map(() => "?").join(",")})`
|
|
25037
|
+
).all(...slice);
|
|
25038
|
+
for (const r of rows) live.add(r.id);
|
|
25039
|
+
}
|
|
25040
|
+
return live;
|
|
25041
|
+
}
|
|
25042
|
+
/**
|
|
25043
|
+
* Live edges WITH their endpoint labels and ids, resolved in ONE join.
|
|
25044
|
+
*
|
|
25045
|
+
* The ontology sweep needs (id, type, fromLabel, toLabel) for every live edge.
|
|
25046
|
+
* Doing that as `scanLiveEdges()` + two `getNode()` calls is 2N node reads —
|
|
25047
|
+
* 550,000 on this store — and each one deserializes the node's embedding blob.
|
|
25048
|
+
* Measured: the sweep did not finish in 10 minutes. As a single join it is one
|
|
25049
|
+
* query over an index-covered scan. Labels only; nothing here touches embeddings.
|
|
25050
|
+
*/
|
|
25051
|
+
scanLiveEdgeRows() {
|
|
25052
|
+
const rows = this.db.prepare(
|
|
25053
|
+
`SELECT e.id, e.from_id, e.to_id, e.type, a.label AS from_label, b.label AS to_label
|
|
25054
|
+
FROM edges e
|
|
25055
|
+
LEFT JOIN nodes a ON a.id = e.from_id AND a.valid_to IS NULL
|
|
25056
|
+
LEFT JOIN nodes b ON b.id = e.to_id AND b.valid_to IS NULL
|
|
25057
|
+
WHERE e.valid_to IS NULL`
|
|
25058
|
+
).all();
|
|
25059
|
+
return rows.map((r) => ({
|
|
25060
|
+
id: r.id,
|
|
25061
|
+
from: r.from_id,
|
|
25062
|
+
to: r.to_id,
|
|
25063
|
+
type: r.type,
|
|
25064
|
+
fromLabel: r.from_label ?? void 0,
|
|
25065
|
+
toLabel: r.to_label ?? void 0
|
|
25066
|
+
}));
|
|
25067
|
+
}
|
|
24709
25068
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
24710
25069
|
* expression index so the incremental reindex fetches only the changed files'
|
|
24711
25070
|
* symbols instead of scanning every versioned node. */
|
|
@@ -24726,6 +25085,10 @@ var SqliteGraphStore = class {
|
|
|
24726
25085
|
if (!live) return null;
|
|
24727
25086
|
const version2 = live.version ?? 1;
|
|
24728
25087
|
const frozenId = `${liveId}@v${version2}`;
|
|
25088
|
+
if (this.getNode(frozenId)) {
|
|
25089
|
+
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
25090
|
+
return frozenId;
|
|
25091
|
+
}
|
|
24729
25092
|
this.stmts.freezeCopy.run({ frozen_id: frozenId, live_id: liveId, t });
|
|
24730
25093
|
this.mergeEdge({
|
|
24731
25094
|
id: `edge_superseded_${frozenId}`,
|
|
@@ -24784,6 +25147,7 @@ var CAUSAL_FAMILY = [
|
|
|
24784
25147
|
|
|
24785
25148
|
// ../../packages/local-graph/src/justification.ts
|
|
24786
25149
|
init_src();
|
|
25150
|
+
init_src2();
|
|
24787
25151
|
function addDependency(store2, opts) {
|
|
24788
25152
|
const id = digest({ from: opts.fromId, type: "DEPENDS_ON", to: opts.toId });
|
|
24789
25153
|
const attrs = { relation: opts.relation };
|
|
@@ -24806,13 +25170,72 @@ function addDependency(store2, opts) {
|
|
|
24806
25170
|
store2.mergeEdge(edge);
|
|
24807
25171
|
return edge;
|
|
24808
25172
|
}
|
|
24809
|
-
function markRevisit(store2, node, reason, ts) {
|
|
25173
|
+
function markRevisit(store2, node, reason, ts, answeredWhen) {
|
|
24810
25174
|
const fresh = store2.getNode(node.id) ?? node;
|
|
24811
25175
|
store2.updateNode(node.id, {
|
|
24812
|
-
attrs: {
|
|
25176
|
+
attrs: {
|
|
25177
|
+
...fresh.attrs,
|
|
25178
|
+
revisit: true,
|
|
25179
|
+
revisitReason: reason,
|
|
25180
|
+
revisitSinceTs: ts,
|
|
25181
|
+
revisitAnsweredWhen: answeredWhen
|
|
25182
|
+
},
|
|
24813
25183
|
lastUpdatedAt: ts
|
|
24814
25184
|
});
|
|
24815
25185
|
}
|
|
25186
|
+
var LEGACY_AUTO_CLOSE_ASK = "auto-closed off a pre-provenance anchor";
|
|
25187
|
+
var KNOWN_CONDITIONS = {
|
|
25188
|
+
parentProblemOpen: true,
|
|
25189
|
+
dependentStale: true
|
|
25190
|
+
};
|
|
25191
|
+
function isRevisitCondition(v) {
|
|
25192
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(KNOWN_CONDITIONS, v);
|
|
25193
|
+
}
|
|
25194
|
+
function conditionOf(node) {
|
|
25195
|
+
const explicit = node.attrs["revisitAnsweredWhen"];
|
|
25196
|
+
if (isRevisitCondition(explicit)) return explicit;
|
|
25197
|
+
if (String(node.attrs["revisitReason"] ?? "").startsWith(LEGACY_AUTO_CLOSE_ASK)) {
|
|
25198
|
+
return "parentProblemOpen";
|
|
25199
|
+
}
|
|
25200
|
+
return void 0;
|
|
25201
|
+
}
|
|
25202
|
+
function clearAnsweredRevisits(store2, ts) {
|
|
25203
|
+
const report = { cleared: 0, standing: 0 };
|
|
25204
|
+
for (const label of SEMANTIC_NODE_LABELS) {
|
|
25205
|
+
for (const node of store2.findNodesByLabel(label)) {
|
|
25206
|
+
if (node.attrs["revisit"] !== true) continue;
|
|
25207
|
+
const condition = conditionOf(node);
|
|
25208
|
+
if (condition === void 0 || !isRevisitAnswered(store2, node, condition)) {
|
|
25209
|
+
report.standing++;
|
|
25210
|
+
continue;
|
|
25211
|
+
}
|
|
25212
|
+
clearRevisit(store2, node.id, ts);
|
|
25213
|
+
report.cleared++;
|
|
25214
|
+
}
|
|
25215
|
+
}
|
|
25216
|
+
return report;
|
|
25217
|
+
}
|
|
25218
|
+
function isRevisitAnswered(store2, node, condition) {
|
|
25219
|
+
switch (condition) {
|
|
25220
|
+
case "parentProblemOpen": {
|
|
25221
|
+
const parents = store2.inEdges(node.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.from)).filter((n) => n !== null && n.validTo == null);
|
|
25222
|
+
return parents.every((p) => p.attrs["resolvedAt"] == null);
|
|
25223
|
+
}
|
|
25224
|
+
case "dependentStale": {
|
|
25225
|
+
const since = Number(node.attrs["revisitSinceTs"] ?? 0);
|
|
25226
|
+
return since > 0 && node.lastUpdatedAt > since;
|
|
25227
|
+
}
|
|
25228
|
+
}
|
|
25229
|
+
}
|
|
25230
|
+
function clearRevisit(store2, nodeId, ts) {
|
|
25231
|
+
const n = store2.getNode(nodeId);
|
|
25232
|
+
if (!n) return;
|
|
25233
|
+
const attrs = { ...n.attrs };
|
|
25234
|
+
delete attrs["revisit"];
|
|
25235
|
+
delete attrs["revisitReason"];
|
|
25236
|
+
delete attrs["revisitSinceTs"];
|
|
25237
|
+
store2.updateNode(nodeId, { attrs, lastUpdatedAt: ts });
|
|
25238
|
+
}
|
|
24816
25239
|
function listNeedsRevisit(store2, asOf) {
|
|
24817
25240
|
return store2.nodesAsOf(asOf).filter((n) => n.attrs["revisit"] === true).map((n) => ({
|
|
24818
25241
|
id: n.id,
|
|
@@ -24945,7 +25368,6 @@ function markBoth(store2, a, b, patch, ts) {
|
|
|
24945
25368
|
// ../../packages/local-graph/src/design-problem.ts
|
|
24946
25369
|
init_src();
|
|
24947
25370
|
init_src();
|
|
24948
|
-
init_src2();
|
|
24949
25371
|
|
|
24950
25372
|
// ../../packages/local-graph/src/problem-package-link.ts
|
|
24951
25373
|
init_src();
|
|
@@ -25131,42 +25553,102 @@ function backfillProblemContext(store2, ts) {
|
|
|
25131
25553
|
}
|
|
25132
25554
|
|
|
25133
25555
|
// ../../packages/local-graph/src/design-problem.ts
|
|
25134
|
-
|
|
25135
|
-
|
|
25136
|
-
|
|
25137
|
-
|
|
25138
|
-
|
|
25139
|
-
|
|
25140
|
-
|
|
25141
|
-
|
|
25142
|
-
|
|
25143
|
-
|
|
25144
|
-
|
|
25145
|
-
|
|
25146
|
-
|
|
25147
|
-
|
|
25148
|
-
|
|
25149
|
-
pageRank: 0,
|
|
25150
|
-
isLandmark: false,
|
|
25151
|
-
community: null,
|
|
25152
|
-
stability: "unstable",
|
|
25153
|
-
attrs
|
|
25154
|
-
};
|
|
25556
|
+
init_src2();
|
|
25557
|
+
|
|
25558
|
+
// ../../packages/local-graph/src/problem-dedup.ts
|
|
25559
|
+
init_src();
|
|
25560
|
+
init_src();
|
|
25561
|
+
var REDIRECT_EDGES = [
|
|
25562
|
+
"CAUSED_BY",
|
|
25563
|
+
"SOLVED_BY",
|
|
25564
|
+
"FIXED_BY",
|
|
25565
|
+
"ANCHORED_AT",
|
|
25566
|
+
"MANIFESTED_IN",
|
|
25567
|
+
"EVIDENCED_BY"
|
|
25568
|
+
];
|
|
25569
|
+
function corroborations(n) {
|
|
25570
|
+
return Number(n.attrs["corroborations"] ?? 0);
|
|
25155
25571
|
}
|
|
25156
|
-
function
|
|
25157
|
-
|
|
25158
|
-
|
|
25159
|
-
|
|
25160
|
-
|
|
25161
|
-
|
|
25162
|
-
|
|
25163
|
-
|
|
25164
|
-
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25572
|
+
function overlap(a, b) {
|
|
25573
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
25574
|
+
let inter = 0;
|
|
25575
|
+
for (const x of a) if (b.has(x)) inter++;
|
|
25576
|
+
return inter / Math.min(a.size, b.size);
|
|
25577
|
+
}
|
|
25578
|
+
function mergeDuplicateProblems(store2, opts) {
|
|
25579
|
+
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25580
|
+
const minTokens = opts.minTokens ?? 4;
|
|
25581
|
+
const report = { clusters: 0, merged: 0 };
|
|
25582
|
+
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25583
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
25584
|
+
for (const p of open) tokens.set(p.id, new Set(retrievalTokens(p.description)));
|
|
25585
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
25586
|
+
store2.transaction(() => {
|
|
25587
|
+
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25588
|
+
const a = open[i2];
|
|
25589
|
+
if (consumed.has(a.id)) continue;
|
|
25590
|
+
const cluster = [a];
|
|
25591
|
+
const ta = tokens.get(a.id);
|
|
25592
|
+
for (let j = i2 + 1; j < open.length; j++) {
|
|
25593
|
+
const b = open[j];
|
|
25594
|
+
if (consumed.has(b.id)) continue;
|
|
25595
|
+
const tb = tokens.get(b.id);
|
|
25596
|
+
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25597
|
+
if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
|
|
25598
|
+
if (overlap(ta, tb) >= minOverlap) {
|
|
25599
|
+
cluster.push(b);
|
|
25600
|
+
consumed.add(b.id);
|
|
25601
|
+
}
|
|
25602
|
+
}
|
|
25603
|
+
if (cluster.length < 2) continue;
|
|
25604
|
+
report.clusters++;
|
|
25605
|
+
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25606
|
+
const survivor = cluster[0];
|
|
25607
|
+
for (const dup of cluster.slice(1)) {
|
|
25608
|
+
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25609
|
+
report.merged++;
|
|
25610
|
+
}
|
|
25611
|
+
}
|
|
25612
|
+
});
|
|
25613
|
+
return report;
|
|
25614
|
+
}
|
|
25615
|
+
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25616
|
+
const surv = store2.getNode(survivor.id);
|
|
25617
|
+
if (!surv) return;
|
|
25618
|
+
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25619
|
+
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25620
|
+
store2.updateNode(survivor.id, {
|
|
25621
|
+
attrs: {
|
|
25622
|
+
...surv.attrs,
|
|
25623
|
+
sources: [...sources],
|
|
25624
|
+
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25625
|
+
},
|
|
25626
|
+
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25627
|
+
lastUpdatedAt: ts
|
|
25169
25628
|
});
|
|
25629
|
+
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25630
|
+
if (e.to === survivor.id) continue;
|
|
25631
|
+
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25632
|
+
if (store2.getEdge(id)) continue;
|
|
25633
|
+
const redirected = {
|
|
25634
|
+
...e,
|
|
25635
|
+
id,
|
|
25636
|
+
from: survivor.id,
|
|
25637
|
+
createdAt: ts,
|
|
25638
|
+
lastSeenAt: ts
|
|
25639
|
+
};
|
|
25640
|
+
store2.mergeEdge(redirected);
|
|
25641
|
+
}
|
|
25642
|
+
store2.updateNode(dup.id, {
|
|
25643
|
+
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25644
|
+
lastUpdatedAt: ts
|
|
25645
|
+
});
|
|
25646
|
+
store2.closeNode(dup.id, ts);
|
|
25647
|
+
}
|
|
25648
|
+
|
|
25649
|
+
// ../../packages/local-graph/src/design-problem.ts
|
|
25650
|
+
function isConstraintProblem(node) {
|
|
25651
|
+
return node.attrs["kind"] === "constraint";
|
|
25170
25652
|
}
|
|
25171
25653
|
var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
|
|
25172
25654
|
"Solution",
|
|
@@ -25175,6 +25657,9 @@ var CITABLE_PRIOR_LABELS = /* @__PURE__ */ new Set([
|
|
|
25175
25657
|
"Technique",
|
|
25176
25658
|
"AntiPattern"
|
|
25177
25659
|
]);
|
|
25660
|
+
function isAutoMinted(n) {
|
|
25661
|
+
return n.label === "Solution" && String(n.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25662
|
+
}
|
|
25178
25663
|
function priorsForFile(store2, relPath) {
|
|
25179
25664
|
const want = relPath.trim().replace(/^\.?\//, "");
|
|
25180
25665
|
let file2 = null;
|
|
@@ -25186,35 +25671,53 @@ function priorsForFile(store2, relPath) {
|
|
|
25186
25671
|
}
|
|
25187
25672
|
if (!file2) return null;
|
|
25188
25673
|
const openProblems = [];
|
|
25674
|
+
const constraints = [];
|
|
25675
|
+
const resolvedProblems = [];
|
|
25189
25676
|
const seenProblem = /* @__PURE__ */ new Set();
|
|
25190
25677
|
const related = /* @__PURE__ */ new Map();
|
|
25191
25678
|
for (const e of store2.inEdges(file2.id, ["ANCHORED_AT"])) {
|
|
25192
25679
|
const n = store2.getNode(e.from);
|
|
25193
25680
|
if (!n) continue;
|
|
25194
25681
|
if (n.label === "Problem") {
|
|
25195
|
-
if (
|
|
25196
|
-
|
|
25197
|
-
|
|
25198
|
-
|
|
25199
|
-
|
|
25682
|
+
if (seenProblem.has(n.id)) continue;
|
|
25683
|
+
seenProblem.add(n.id);
|
|
25684
|
+
if (!n.attrs["resolvedAt"]) {
|
|
25685
|
+
(isConstraintProblem(n) ? constraints : openProblems).push(n);
|
|
25686
|
+
} else if (n.attrs["resolvedAs"] == null && n.attrs["mergedInto"] === void 0 && e.attrs?.["anchorProvenance"] !== "legacy") {
|
|
25687
|
+
resolvedProblems.push(n);
|
|
25688
|
+
}
|
|
25689
|
+
} else if (CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) {
|
|
25200
25690
|
related.set(n.id, n);
|
|
25201
25691
|
}
|
|
25202
25692
|
}
|
|
25203
25693
|
const solutionsByProblem = /* @__PURE__ */ new Map();
|
|
25204
|
-
for (const p of openProblems) {
|
|
25694
|
+
for (const p of [...openProblems, ...constraints, ...resolvedProblems]) {
|
|
25205
25695
|
for (const e of store2.outEdges(p.id, ["SOLVED_BY", "CAUSED_BY", "INSTANCE_OF"])) {
|
|
25206
25696
|
const n = store2.getNode(e.to);
|
|
25207
|
-
if (n && CITABLE_PRIOR_LABELS.has(n.label)) related.set(n.id, n);
|
|
25208
|
-
if (n && n.label === "Solution" && e.type === "SOLVED_BY") {
|
|
25697
|
+
if (n && CITABLE_PRIOR_LABELS.has(n.label) && !isAutoMinted(n)) related.set(n.id, n);
|
|
25698
|
+
if (n && n.label === "Solution" && e.type === "SOLVED_BY" && !isAutoMinted(n)) {
|
|
25209
25699
|
const list = solutionsByProblem.get(p.id) ?? [];
|
|
25210
25700
|
if (!list.some((s) => s.id === n.id)) list.push(n);
|
|
25211
25701
|
solutionsByProblem.set(p.id, list);
|
|
25212
25702
|
}
|
|
25213
25703
|
}
|
|
25214
25704
|
}
|
|
25215
|
-
if (openProblems.length === 0 && related.size === 0)
|
|
25216
|
-
|
|
25217
|
-
|
|
25705
|
+
if (openProblems.length === 0 && constraints.length === 0 && resolvedProblems.length === 0 && related.size === 0)
|
|
25706
|
+
return null;
|
|
25707
|
+
const recentFirst = (a, b) => (b.lastUpdatedAt ?? 0) - (a.lastUpdatedAt ?? 0);
|
|
25708
|
+
openProblems.sort(recentFirst);
|
|
25709
|
+
constraints.sort(recentFirst);
|
|
25710
|
+
resolvedProblems.sort(
|
|
25711
|
+
(a, b) => Number(b.attrs["resolvedAt"] ?? 0) - Number(a.attrs["resolvedAt"] ?? 0)
|
|
25712
|
+
);
|
|
25713
|
+
return {
|
|
25714
|
+
file: file2,
|
|
25715
|
+
openProblems,
|
|
25716
|
+
constraints,
|
|
25717
|
+
resolvedProblems,
|
|
25718
|
+
related: [...related.values()],
|
|
25719
|
+
solutionsByProblem
|
|
25720
|
+
};
|
|
25218
25721
|
}
|
|
25219
25722
|
function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
25220
25723
|
const p = store2.getNode(problemId);
|
|
@@ -25225,18 +25728,49 @@ function recordAnchorHint(store2, problemId, relPath, provenance, ts) {
|
|
|
25225
25728
|
});
|
|
25226
25729
|
return true;
|
|
25227
25730
|
}
|
|
25228
|
-
function
|
|
25731
|
+
function reopenAutoClosedProblems(store2, t) {
|
|
25732
|
+
const report = { reopened: 0, alreadySuspect: 0, agentDescribed: 0 };
|
|
25733
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25734
|
+
if (p.attrs["resolvedAt"] == null) continue;
|
|
25735
|
+
if (p.attrs["resolvedAs"] != null) continue;
|
|
25736
|
+
if (p.attrs["mergedInto"] !== void 0) continue;
|
|
25737
|
+
if (p.attrs["resolutionWitnessed"] === true) {
|
|
25738
|
+
report.agentDescribed++;
|
|
25739
|
+
continue;
|
|
25740
|
+
}
|
|
25741
|
+
const sols = store2.outEdges(p.id, ["SOLVED_BY"]).map((e) => store2.getNode(e.to)).filter((n) => n !== null);
|
|
25742
|
+
if (sols.length === 0) continue;
|
|
25743
|
+
if (!sols.every((s) => String(s.description ?? "").startsWith(AUTO_MINT_PREFIX))) {
|
|
25744
|
+
report.agentDescribed++;
|
|
25745
|
+
continue;
|
|
25746
|
+
}
|
|
25747
|
+
if (p.attrs["resolutionSuspect"] === true) report.alreadySuspect++;
|
|
25748
|
+
const attrs = { ...p.attrs };
|
|
25749
|
+
delete attrs["resolvedAt"];
|
|
25750
|
+
attrs["reopenedFrom"] = "auto-close";
|
|
25751
|
+
attrs["reopenedAt"] = t;
|
|
25752
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25753
|
+
report.reopened++;
|
|
25754
|
+
}
|
|
25755
|
+
return report;
|
|
25756
|
+
}
|
|
25757
|
+
var AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
25758
|
+
function markFixCandidates(store2, t) {
|
|
25229
25759
|
let resolved = 0;
|
|
25230
25760
|
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25231
25761
|
if (!p.id.startsWith("dprob_")) continue;
|
|
25232
25762
|
if (p.attrs["resolvedAt"]) continue;
|
|
25763
|
+
if (isConstraintProblem(p)) continue;
|
|
25233
25764
|
let symName = "";
|
|
25234
25765
|
let symRelPath;
|
|
25235
25766
|
let edited = false;
|
|
25767
|
+
const since = Math.max(p.createdAt, Number(p.attrs["fixCandidateClearedAt"] ?? 0));
|
|
25236
25768
|
for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
|
|
25237
25769
|
if (e.attrs?.["mention"] === true) continue;
|
|
25770
|
+
if (e.attrs?.["anchorProvenance"] === "legacy") continue;
|
|
25238
25771
|
const sym = store2.getNode(e.to);
|
|
25239
|
-
|
|
25772
|
+
const changedAt = sym?.label === "File" ? Number(sym.attrs["contentChangedAt"] ?? 0) : sym?.lastUpdatedAt ?? 0;
|
|
25773
|
+
if (sym && changedAt > since) {
|
|
25240
25774
|
edited = true;
|
|
25241
25775
|
symName = sym.description;
|
|
25242
25776
|
symRelPath = sym.attrs["relPath"] ?? void 0;
|
|
@@ -25244,36 +25778,172 @@ function resolveDesignProblems(store2, t) {
|
|
|
25244
25778
|
}
|
|
25245
25779
|
}
|
|
25246
25780
|
if (!edited) continue;
|
|
25247
|
-
if (
|
|
25248
|
-
|
|
25249
|
-
store2.
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
// AC-resolution-attrs: the resolving symbol/file as STRUCTURED data, not
|
|
25254
|
-
// just prose — the F2 join key downstream backfills and the viz read.
|
|
25255
|
-
resolvedSymbols: [symName],
|
|
25256
|
-
...symRelPath ? { resolvedRelPath: symRelPath } : {}
|
|
25257
|
-
})
|
|
25258
|
-
);
|
|
25259
|
-
mergeEdge(store2, p.id, solId, "SOLVED_BY", t);
|
|
25260
|
-
for (const ae of store2.outEdges(p.id, ["ANCHORED_AT"]))
|
|
25261
|
-
mergeEdge(store2, solId, ae.to, "ANCHORED_AT", t);
|
|
25262
|
-
}
|
|
25781
|
+
if (p.attrs["fixCandidateAt"] !== void 0) continue;
|
|
25782
|
+
if (store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25783
|
+
const s = store2.getNode(e.to);
|
|
25784
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25785
|
+
}))
|
|
25786
|
+
continue;
|
|
25263
25787
|
store2.updateNode(p.id, {
|
|
25264
|
-
attrs: {
|
|
25788
|
+
attrs: {
|
|
25789
|
+
...p.attrs,
|
|
25790
|
+
// The cue, kept as structured data so the render can name the file it is
|
|
25791
|
+
// asking about. Deliberately NOT `resolvedAt` — this is a question.
|
|
25792
|
+
fixCandidateAt: t,
|
|
25793
|
+
fixCandidateSymbol: symName,
|
|
25794
|
+
...symRelPath ? { fixCandidateFile: symRelPath } : {},
|
|
25795
|
+
// Snapshot the render-ledger counter so "shown since THIS ask" is
|
|
25796
|
+
// measurable. `shownCount` is the node's LIFETIME count across every
|
|
25797
|
+
// band, so comparing it raw would retire a fresh ask on a
|
|
25798
|
+
// frequently-surfaced problem before anyone had seen the question once.
|
|
25799
|
+
fixCandidateShownBase: Number(p.attrs["shownCount"] ?? 0)
|
|
25800
|
+
},
|
|
25265
25801
|
lastUpdatedAt: t
|
|
25266
25802
|
});
|
|
25267
25803
|
resolved++;
|
|
25268
25804
|
}
|
|
25269
25805
|
return resolved;
|
|
25270
25806
|
}
|
|
25807
|
+
var FIX_CANDIDATE_ASK_LIMIT = 5;
|
|
25808
|
+
function clearSettledFixCandidates(store2, t) {
|
|
25809
|
+
const report = { answered: 0, ignored: 0, unsubstantiated: 0, standing: 0 };
|
|
25810
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25811
|
+
if (p.attrs["fixCandidateAt"] === void 0) continue;
|
|
25812
|
+
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");
|
|
25813
|
+
const unsubstantiated = fileAnchors.length > 0 && fileAnchors.every((f) => f.attrs["contentChangedAt"] === void 0);
|
|
25814
|
+
const answered = p.attrs["resolvedAt"] != null || store2.outEdges(p.id, ["SOLVED_BY"]).some((e) => {
|
|
25815
|
+
const s = store2.getNode(e.to);
|
|
25816
|
+
return s !== null && !String(s.description ?? "").startsWith(AUTO_MINT_PREFIX);
|
|
25817
|
+
});
|
|
25818
|
+
const base = Number(
|
|
25819
|
+
p.attrs["fixCandidateShownBase"] ?? p.attrs["shownCount"] ?? 0
|
|
25820
|
+
);
|
|
25821
|
+
const shownSinceAsk = Number(p.attrs["shownCount"] ?? 0) - base;
|
|
25822
|
+
const ignored = shownSinceAsk >= FIX_CANDIDATE_ASK_LIMIT;
|
|
25823
|
+
if (!answered && !ignored && !unsubstantiated) {
|
|
25824
|
+
report.standing++;
|
|
25825
|
+
continue;
|
|
25826
|
+
}
|
|
25827
|
+
const attrs = { ...p.attrs };
|
|
25828
|
+
delete attrs["fixCandidateAt"];
|
|
25829
|
+
delete attrs["fixCandidateSymbol"];
|
|
25830
|
+
delete attrs["fixCandidateFile"];
|
|
25831
|
+
attrs["fixCandidateClearedAt"] = t;
|
|
25832
|
+
store2.updateNode(p.id, { attrs, lastUpdatedAt: t });
|
|
25833
|
+
if (answered) report.answered++;
|
|
25834
|
+
else if (ignored) report.ignored++;
|
|
25835
|
+
else report.unsubstantiated++;
|
|
25836
|
+
}
|
|
25837
|
+
return report;
|
|
25838
|
+
}
|
|
25839
|
+
|
|
25840
|
+
// ../../packages/local-graph/src/intent.ts
|
|
25841
|
+
init_src();
|
|
25271
25842
|
|
|
25272
25843
|
// ../../packages/local-graph/src/anchor-backfill.ts
|
|
25273
|
-
|
|
25844
|
+
init_src();
|
|
25845
|
+
init_src2();
|
|
25274
25846
|
function isLegacyAnchor(attrs) {
|
|
25275
25847
|
return attrs?.["captureTime"] === true && attrs["anchorProvenance"] === void 0;
|
|
25276
25848
|
}
|
|
25849
|
+
var STATEMENT_PATH_RE = new RegExp(
|
|
25850
|
+
String.raw`(?:[\w.+-]+\/)+[\w.+-]+\.(?:${anchorableExtensionAlternation()})`,
|
|
25851
|
+
"g"
|
|
25852
|
+
);
|
|
25853
|
+
function repairLegacyAnchorsFromStatement(store2, ts) {
|
|
25854
|
+
const report = {
|
|
25855
|
+
repaired: [],
|
|
25856
|
+
retiredGuesses: 0,
|
|
25857
|
+
noPathNamed: 0,
|
|
25858
|
+
pathUnknown: 0,
|
|
25859
|
+
alreadyCorrect: 0
|
|
25860
|
+
};
|
|
25861
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
25862
|
+
for (const f of store2.findNodesByLabel("File")) {
|
|
25863
|
+
const path = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
25864
|
+
if (path) byPath.set(path, { id: f.id, path });
|
|
25865
|
+
}
|
|
25866
|
+
const resolvePath = (named) => {
|
|
25867
|
+
const exact = byPath.get(named);
|
|
25868
|
+
if (exact) return exact;
|
|
25869
|
+
const hits = [...byPath.values()].filter(
|
|
25870
|
+
(f) => f.path.endsWith(`/${named}`) || named.endsWith(`/${f.path}`)
|
|
25871
|
+
);
|
|
25872
|
+
return hits.length === 1 ? hits[0] : null;
|
|
25873
|
+
};
|
|
25874
|
+
for (const p of store2.findNodesByLabel("Problem")) {
|
|
25875
|
+
try {
|
|
25876
|
+
const anchors = store2.outEdges(p.id, ["ANCHORED_AT"]);
|
|
25877
|
+
const legacy = anchors.filter((e) => e.attrs?.["anchorProvenance"] === "legacy");
|
|
25878
|
+
if (legacy.length === 0) continue;
|
|
25879
|
+
const better = anchors.filter((e) => {
|
|
25880
|
+
const prov = e.attrs?.["anchorProvenance"];
|
|
25881
|
+
return prov === "restated" || prov === "witnessed" || prov === "edited";
|
|
25882
|
+
});
|
|
25883
|
+
if (better.length > 0) {
|
|
25884
|
+
const betterTargets = new Set(better.map((e) => e.to));
|
|
25885
|
+
let retired = 0;
|
|
25886
|
+
for (const e of legacy) {
|
|
25887
|
+
if (!betterTargets.has(e.to)) {
|
|
25888
|
+
store2.closeEdge(e.id, ts);
|
|
25889
|
+
retired++;
|
|
25890
|
+
}
|
|
25891
|
+
}
|
|
25892
|
+
report.retiredGuesses += retired;
|
|
25893
|
+
continue;
|
|
25894
|
+
}
|
|
25895
|
+
const named = [...String(p.description ?? "").matchAll(STATEMENT_PATH_RE)].map((m) => m[0]);
|
|
25896
|
+
if (named.length === 0) {
|
|
25897
|
+
report.noPathNamed++;
|
|
25898
|
+
continue;
|
|
25899
|
+
}
|
|
25900
|
+
const guessedPaths = legacy.map((e) => {
|
|
25901
|
+
const f = store2.getNode(e.to);
|
|
25902
|
+
return String(f?.attrs["relPath"] ?? f?.description ?? "");
|
|
25903
|
+
});
|
|
25904
|
+
if (named.some((x) => guessedPaths.some((g) => g.endsWith(x) || x.endsWith(g)))) {
|
|
25905
|
+
report.alreadyCorrect++;
|
|
25906
|
+
continue;
|
|
25907
|
+
}
|
|
25908
|
+
const target = named.map(resolvePath).find((f) => f !== null);
|
|
25909
|
+
if (!target) {
|
|
25910
|
+
report.pathUnknown++;
|
|
25911
|
+
continue;
|
|
25912
|
+
}
|
|
25913
|
+
store2.mergeEdge({
|
|
25914
|
+
id: `edge_${digest({ from: p.id, type: "ANCHORED_AT", to: target.id })}`.slice(0, 24),
|
|
25915
|
+
from: p.id,
|
|
25916
|
+
to: target.id,
|
|
25917
|
+
type: "ANCHORED_AT",
|
|
25918
|
+
// Same confidence a capture-time anchor enters at: the source is the
|
|
25919
|
+
// agent's own statement either way, only the moment of reading differs.
|
|
25920
|
+
confidence: 0.4,
|
|
25921
|
+
extractionSource: "agent-observed",
|
|
25922
|
+
createdAt: ts,
|
|
25923
|
+
lastSeenAt: ts,
|
|
25924
|
+
navSuccesses: 0,
|
|
25925
|
+
navFailures: 0,
|
|
25926
|
+
attrs: {
|
|
25927
|
+
anchorProvenance: "restated",
|
|
25928
|
+
restatedFrom: guessedPaths[0] ?? "",
|
|
25929
|
+
restatedAt: ts
|
|
25930
|
+
}
|
|
25931
|
+
});
|
|
25932
|
+
for (const e of legacy) {
|
|
25933
|
+
if (e.to !== target.id) store2.closeEdge(e.id, ts);
|
|
25934
|
+
}
|
|
25935
|
+
report.retiredGuesses += legacy.filter((e) => e.to !== target.id).length;
|
|
25936
|
+
report.repaired.push({
|
|
25937
|
+
problemId: p.id,
|
|
25938
|
+
problem: p.description,
|
|
25939
|
+
guessed: guessedPaths[0] ?? "",
|
|
25940
|
+
restated: target.path
|
|
25941
|
+
});
|
|
25942
|
+
} catch {
|
|
25943
|
+
}
|
|
25944
|
+
}
|
|
25945
|
+
return report;
|
|
25946
|
+
}
|
|
25277
25947
|
function backfillLegacyAnchors(store2, ts) {
|
|
25278
25948
|
const report = {
|
|
25279
25949
|
demotedEdges: 0,
|
|
@@ -25313,7 +25983,11 @@ function backfillLegacyAnchors(store2, ts) {
|
|
|
25313
25983
|
store2,
|
|
25314
25984
|
sol,
|
|
25315
25985
|
`auto-closed off a pre-provenance anchor (guessed ${guessedAnchor}) \u2014 confirm the problem is really fixed, or reopen it`,
|
|
25316
|
-
ts
|
|
25986
|
+
ts,
|
|
25987
|
+
// "or reopen it" is half the ask, and the nightly reopen takes that
|
|
25988
|
+
// branch — so record what would answer this, or the flag can never be
|
|
25989
|
+
// lowered and the band fills with settled questions.
|
|
25990
|
+
"parentProblemOpen"
|
|
25317
25991
|
);
|
|
25318
25992
|
store2.updateNode(p.id, {
|
|
25319
25993
|
attrs: { ...p.attrs, resolutionSuspect: true },
|
|
@@ -25347,7 +26021,7 @@ function pagerank(input) {
|
|
|
25347
26021
|
const ids = input.nodeIds;
|
|
25348
26022
|
const n = ids.length;
|
|
25349
26023
|
if (n === 0) {
|
|
25350
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26024
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25351
26025
|
}
|
|
25352
26026
|
const index = /* @__PURE__ */ new Map();
|
|
25353
26027
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -25419,8 +26093,12 @@ function pagerank(input) {
|
|
|
25419
26093
|
}
|
|
25420
26094
|
}
|
|
25421
26095
|
const scores = /* @__PURE__ */ new Map();
|
|
25422
|
-
|
|
25423
|
-
|
|
26096
|
+
let danglingRank = 0;
|
|
26097
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26098
|
+
scores.set(ids[i2], score2[i2]);
|
|
26099
|
+
if (dangling[i2]) danglingRank += score2[i2];
|
|
26100
|
+
}
|
|
26101
|
+
return { scores, iterations: iter, converged, danglingRank };
|
|
25424
26102
|
}
|
|
25425
26103
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
25426
26104
|
const entries = [...scores.entries()];
|
|
@@ -25430,7 +26108,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
25430
26108
|
}
|
|
25431
26109
|
}
|
|
25432
26110
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
25433
|
-
entries.sort((a, b) => b[1] - a[1]);
|
|
26111
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
25434
26112
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
25435
26113
|
const out2 = /* @__PURE__ */ new Set();
|
|
25436
26114
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -25668,8 +26346,86 @@ function motifDecision(input) {
|
|
|
25668
26346
|
};
|
|
25669
26347
|
}
|
|
25670
26348
|
|
|
25671
|
-
// ../../packages/
|
|
25672
|
-
|
|
26349
|
+
// ../../packages/math/src/pagerank-local.ts
|
|
26350
|
+
function pagerankLocal(input) {
|
|
26351
|
+
const damping = input.damping ?? 0.85;
|
|
26352
|
+
const tol = input.tolerance ?? 1e-6;
|
|
26353
|
+
const maxIter = input.maxIterations ?? 100;
|
|
26354
|
+
const ids = input.regionIds;
|
|
26355
|
+
const n = ids.length;
|
|
26356
|
+
if (n === 0 || input.globalN === 0) {
|
|
26357
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26358
|
+
}
|
|
26359
|
+
const index = /* @__PURE__ */ new Map();
|
|
26360
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
26361
|
+
const base = (1 - damping) / input.globalN;
|
|
26362
|
+
const outSum = new Float64Array(n);
|
|
26363
|
+
const inflow = new Float64Array(n);
|
|
26364
|
+
let score2 = new Float64Array(n);
|
|
26365
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26366
|
+
const id = ids[i2];
|
|
26367
|
+
outSum[i2] = input.outSum.get(id) ?? 0;
|
|
26368
|
+
inflow[i2] = input.boundaryInflow.get(id) ?? 0;
|
|
26369
|
+
score2[i2] = input.rank0.get(id) ?? base;
|
|
26370
|
+
}
|
|
26371
|
+
const rowLen = new Int32Array(n);
|
|
26372
|
+
let edgeCount = 0;
|
|
26373
|
+
for (const [from, edges] of input.out) {
|
|
26374
|
+
const fi = index.get(from);
|
|
26375
|
+
if (fi === void 0) continue;
|
|
26376
|
+
let inSet = 0;
|
|
26377
|
+
for (const e of edges) if (index.has(e.to)) inSet++;
|
|
26378
|
+
rowLen[fi] = inSet;
|
|
26379
|
+
edgeCount += inSet;
|
|
26380
|
+
}
|
|
26381
|
+
const rowStart = new Int32Array(n + 1);
|
|
26382
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
26383
|
+
const colIdx = new Int32Array(edgeCount);
|
|
26384
|
+
const colW = new Float64Array(edgeCount);
|
|
26385
|
+
const cursor = rowStart.slice(0, n);
|
|
26386
|
+
for (const [from, edges] of input.out) {
|
|
26387
|
+
const fi = index.get(from);
|
|
26388
|
+
if (fi === void 0) continue;
|
|
26389
|
+
for (const e of edges) {
|
|
26390
|
+
const ti = index.get(e.to);
|
|
26391
|
+
if (ti === void 0) continue;
|
|
26392
|
+
const c = cursor[fi];
|
|
26393
|
+
cursor[fi] = c + 1;
|
|
26394
|
+
colIdx[c] = ti;
|
|
26395
|
+
colW[c] = e.weight;
|
|
26396
|
+
}
|
|
26397
|
+
}
|
|
26398
|
+
const externalDangling = input.externalDanglingRank ?? 0;
|
|
26399
|
+
let next = new Float64Array(n);
|
|
26400
|
+
let iter = 0;
|
|
26401
|
+
let converged = false;
|
|
26402
|
+
for (; iter < maxIter; iter++) {
|
|
26403
|
+
let internalDangling = 0;
|
|
26404
|
+
for (let i2 = 0; i2 < n; i2++) if (outSum[i2] <= 0) internalDangling += score2[i2];
|
|
26405
|
+
const danglingShare = damping * (internalDangling + externalDangling) / input.globalN;
|
|
26406
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] = base + danglingShare + damping * inflow[i2];
|
|
26407
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
26408
|
+
const os2 = outSum[i2];
|
|
26409
|
+
if (os2 <= 0) continue;
|
|
26410
|
+
const f = damping * score2[i2] / os2;
|
|
26411
|
+
const end = rowStart[i2 + 1];
|
|
26412
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
26413
|
+
}
|
|
26414
|
+
let diff = 0;
|
|
26415
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
26416
|
+
const tmp = score2;
|
|
26417
|
+
score2 = next;
|
|
26418
|
+
next = tmp;
|
|
26419
|
+
if (diff < tol) {
|
|
26420
|
+
iter++;
|
|
26421
|
+
converged = true;
|
|
26422
|
+
break;
|
|
26423
|
+
}
|
|
26424
|
+
}
|
|
26425
|
+
const scores = /* @__PURE__ */ new Map();
|
|
26426
|
+
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
26427
|
+
return { scores, iterations: iter, converged };
|
|
26428
|
+
}
|
|
25673
26429
|
|
|
25674
26430
|
// ../../packages/local-graph/src/triage.ts
|
|
25675
26431
|
init_src();
|
|
@@ -25678,95 +26434,8 @@ init_src();
|
|
|
25678
26434
|
init_src2();
|
|
25679
26435
|
var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
|
|
25680
26436
|
|
|
25681
|
-
// ../../packages/local-graph/src/
|
|
25682
|
-
|
|
25683
|
-
init_src();
|
|
25684
|
-
var REDIRECT_EDGES = [
|
|
25685
|
-
"CAUSED_BY",
|
|
25686
|
-
"SOLVED_BY",
|
|
25687
|
-
"FIXED_BY",
|
|
25688
|
-
"ANCHORED_AT",
|
|
25689
|
-
"MANIFESTED_IN",
|
|
25690
|
-
"EVIDENCED_BY"
|
|
25691
|
-
];
|
|
25692
|
-
function corroborations(n) {
|
|
25693
|
-
return Number(n.attrs["corroborations"] ?? 0);
|
|
25694
|
-
}
|
|
25695
|
-
function overlap(a, b) {
|
|
25696
|
-
if (a.size === 0 || b.size === 0) return 0;
|
|
25697
|
-
let inter = 0;
|
|
25698
|
-
for (const x of a) if (b.has(x)) inter++;
|
|
25699
|
-
return inter / Math.min(a.size, b.size);
|
|
25700
|
-
}
|
|
25701
|
-
function mergeDuplicateProblems(store2, opts) {
|
|
25702
|
-
const minOverlap = opts.minOverlap ?? 0.85;
|
|
25703
|
-
const minTokens = opts.minTokens ?? 4;
|
|
25704
|
-
const report = { clusters: 0, merged: 0 };
|
|
25705
|
-
const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
|
|
25706
|
-
const tokens = /* @__PURE__ */ new Map();
|
|
25707
|
-
for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
|
|
25708
|
-
const consumed = /* @__PURE__ */ new Set();
|
|
25709
|
-
store2.transaction(() => {
|
|
25710
|
-
for (let i2 = 0; i2 < open.length; i2++) {
|
|
25711
|
-
const a = open[i2];
|
|
25712
|
-
if (consumed.has(a.id)) continue;
|
|
25713
|
-
const cluster = [a];
|
|
25714
|
-
const ta = tokens.get(a.id);
|
|
25715
|
-
for (let j = i2 + 1; j < open.length; j++) {
|
|
25716
|
-
const b = open[j];
|
|
25717
|
-
if (consumed.has(b.id)) continue;
|
|
25718
|
-
const tb = tokens.get(b.id);
|
|
25719
|
-
if (Math.min(ta.size, tb.size) < minTokens) continue;
|
|
25720
|
-
if (overlap(ta, tb) >= minOverlap) {
|
|
25721
|
-
cluster.push(b);
|
|
25722
|
-
consumed.add(b.id);
|
|
25723
|
-
}
|
|
25724
|
-
}
|
|
25725
|
-
if (cluster.length < 2) continue;
|
|
25726
|
-
report.clusters++;
|
|
25727
|
-
cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
|
|
25728
|
-
const survivor = cluster[0];
|
|
25729
|
-
for (const dup of cluster.slice(1)) {
|
|
25730
|
-
foldDuplicateProblem(store2, dup, survivor, opts.ts);
|
|
25731
|
-
report.merged++;
|
|
25732
|
-
}
|
|
25733
|
-
}
|
|
25734
|
-
});
|
|
25735
|
-
return report;
|
|
25736
|
-
}
|
|
25737
|
-
function foldDuplicateProblem(store2, dup, survivor, ts) {
|
|
25738
|
-
const surv = store2.getNode(survivor.id);
|
|
25739
|
-
if (!surv) return;
|
|
25740
|
-
const sources = new Set(surv.attrs["sources"] ?? []);
|
|
25741
|
-
for (const s of dup.attrs["sources"] ?? []) sources.add(s);
|
|
25742
|
-
store2.updateNode(survivor.id, {
|
|
25743
|
-
attrs: {
|
|
25744
|
-
...surv.attrs,
|
|
25745
|
-
sources: [...sources],
|
|
25746
|
-
corroborations: corroborations(surv) + corroborations(dup) + 1
|
|
25747
|
-
},
|
|
25748
|
-
cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
|
|
25749
|
-
lastUpdatedAt: ts
|
|
25750
|
-
});
|
|
25751
|
-
for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
|
|
25752
|
-
if (e.to === survivor.id) continue;
|
|
25753
|
-
const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
|
|
25754
|
-
if (store2.getEdge(id)) continue;
|
|
25755
|
-
const redirected = {
|
|
25756
|
-
...e,
|
|
25757
|
-
id,
|
|
25758
|
-
from: survivor.id,
|
|
25759
|
-
createdAt: ts,
|
|
25760
|
-
lastSeenAt: ts
|
|
25761
|
-
};
|
|
25762
|
-
store2.mergeEdge(redirected);
|
|
25763
|
-
}
|
|
25764
|
-
store2.updateNode(dup.id, {
|
|
25765
|
-
attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
|
|
25766
|
-
lastUpdatedAt: ts
|
|
25767
|
-
});
|
|
25768
|
-
store2.closeNode(dup.id, ts);
|
|
25769
|
-
}
|
|
26437
|
+
// ../../packages/local-graph/src/community.ts
|
|
26438
|
+
init_src2();
|
|
25770
26439
|
|
|
25771
26440
|
// ../../packages/local-graph/src/tools.ts
|
|
25772
26441
|
init_src();
|
|
@@ -25783,6 +26452,9 @@ function rankToolsForHandles(store2, limit = 5) {
|
|
|
25783
26452
|
// ../../packages/local-graph/src/principle-sync.ts
|
|
25784
26453
|
init_src2();
|
|
25785
26454
|
|
|
26455
|
+
// ../../packages/local-graph/src/mechanism-liveness.ts
|
|
26456
|
+
var STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
26457
|
+
|
|
25786
26458
|
// ../../packages/generalizer/src/generalizer.ts
|
|
25787
26459
|
init_src2();
|
|
25788
26460
|
init_src();
|
|
@@ -25902,7 +26574,164 @@ function promoteMotifs(store2, t) {
|
|
|
25902
26574
|
}
|
|
25903
26575
|
|
|
25904
26576
|
// ../../packages/generalizer/src/nightly.ts
|
|
25905
|
-
|
|
26577
|
+
var FULL_RESCORE_EVERY = 12;
|
|
26578
|
+
var INCREMENTAL_REGION_MAX_FRACTION = 0.2;
|
|
26579
|
+
var INCREMENTAL_HOPS = 3;
|
|
26580
|
+
function runGraphRescore(store2, opts = {}) {
|
|
26581
|
+
const lastRescoreAt = Number(store2.getMeta?.("lastRescoreAt") ?? 0);
|
|
26582
|
+
const sinceFull = Number(store2.getMeta?.("rescoresSinceFull") ?? 0);
|
|
26583
|
+
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;
|
|
26584
|
+
if (incrementalCapable) {
|
|
26585
|
+
const started = Date.now();
|
|
26586
|
+
const inc = tryIncrementalRescore(store2, lastRescoreAt, started);
|
|
26587
|
+
if (inc) {
|
|
26588
|
+
store2.setMeta("lastRescoreAt", String(started));
|
|
26589
|
+
store2.setMeta("rescoresSinceFull", String(sinceFull + 1));
|
|
26590
|
+
return inc;
|
|
26591
|
+
}
|
|
26592
|
+
}
|
|
26593
|
+
const report = runFullRescore(store2);
|
|
26594
|
+
store2.setMeta?.("lastRescoreAt", String(report.startedAt));
|
|
26595
|
+
store2.setMeta?.("rescoresSinceFull", "0");
|
|
26596
|
+
store2.setMeta?.("danglingRankShare", String(report.danglingRank));
|
|
26597
|
+
const { startedAt: _drop, danglingRank: _drop2, ...rest } = report;
|
|
26598
|
+
return rest;
|
|
26599
|
+
}
|
|
26600
|
+
function tryIncrementalRescore(store2, lastRescoreAt, started) {
|
|
26601
|
+
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
26602
|
+
const liveN = store2.nodeCount();
|
|
26603
|
+
const cap = Math.max(1e3, Math.floor(liveN * INCREMENTAL_REGION_MAX_FRACTION));
|
|
26604
|
+
const region = new Set(store2.dirtyNodeIdsSince(lastRescoreAt));
|
|
26605
|
+
for (const e of store2.edgeEndpointsTouchedSince(lastRescoreAt)) {
|
|
26606
|
+
region.add(e.from);
|
|
26607
|
+
region.add(e.to);
|
|
26608
|
+
}
|
|
26609
|
+
if (region.size > cap) return null;
|
|
26610
|
+
let frontier = [...region];
|
|
26611
|
+
for (let hop = 0; hop < INCREMENTAL_HOPS && frontier.length > 0; hop++) {
|
|
26612
|
+
const next = [];
|
|
26613
|
+
for (const e of store2.outEdgesForMany(frontier)) {
|
|
26614
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26615
|
+
if (!region.has(e.to)) {
|
|
26616
|
+
region.add(e.to);
|
|
26617
|
+
next.push(e.to);
|
|
26618
|
+
}
|
|
26619
|
+
}
|
|
26620
|
+
if (region.size > cap) return null;
|
|
26621
|
+
frontier = next;
|
|
26622
|
+
}
|
|
26623
|
+
const rank0 = store2.ranksForMany([...region]);
|
|
26624
|
+
const regionIds = [...rank0.keys()];
|
|
26625
|
+
if (regionIds.length === 0) {
|
|
26626
|
+
return {
|
|
26627
|
+
scoredNodes: 0,
|
|
26628
|
+
iterations: 0,
|
|
26629
|
+
converged: true,
|
|
26630
|
+
landmarks: 0,
|
|
26631
|
+
communities: 0,
|
|
26632
|
+
motifsPromoted: 0,
|
|
26633
|
+
techniques: 0,
|
|
26634
|
+
antipatterns: 0,
|
|
26635
|
+
pageRankWritten: 0,
|
|
26636
|
+
landmarkFlips: 0,
|
|
26637
|
+
mode: "incremental",
|
|
26638
|
+
regionSize: 0,
|
|
26639
|
+
durationMs: Date.now() - started
|
|
26640
|
+
};
|
|
26641
|
+
}
|
|
26642
|
+
const inRegion = new Set(regionIds);
|
|
26643
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
26644
|
+
const outSum = /* @__PURE__ */ new Map();
|
|
26645
|
+
for (const e of store2.outEdgesForMany(regionIds)) {
|
|
26646
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26647
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26648
|
+
if (w <= 0) continue;
|
|
26649
|
+
outSum.set(e.from, (outSum.get(e.from) ?? 0) + w);
|
|
26650
|
+
if (!inRegion.has(e.to)) continue;
|
|
26651
|
+
const l = out2.get(e.from);
|
|
26652
|
+
if (l) l.push({ to: e.to, weight: w });
|
|
26653
|
+
else out2.set(e.from, [{ to: e.to, weight: w }]);
|
|
26654
|
+
}
|
|
26655
|
+
const boundaryEdges = store2.inEdgesForMany(regionIds).filter((e) => allowedTypes.has(e.type) && !inRegion.has(e.from) && (EDGE_WEIGHT[e.type] ?? 1) > 0);
|
|
26656
|
+
const boundarySources = [...new Set(boundaryEdges.map((e) => e.from))];
|
|
26657
|
+
if (boundarySources.length > cap) return null;
|
|
26658
|
+
const boundaryRanks = store2.ranksForMany(boundarySources);
|
|
26659
|
+
const boundaryOutSum = /* @__PURE__ */ new Map();
|
|
26660
|
+
for (const e of store2.outEdgesForMany(boundarySources)) {
|
|
26661
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
26662
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26663
|
+
if (w > 0) boundaryOutSum.set(e.from, (boundaryOutSum.get(e.from) ?? 0) + w);
|
|
26664
|
+
}
|
|
26665
|
+
const boundaryInflow = /* @__PURE__ */ new Map();
|
|
26666
|
+
for (const e of boundaryEdges) {
|
|
26667
|
+
const r = boundaryRanks.get(e.from);
|
|
26668
|
+
const os2 = boundaryOutSum.get(e.from);
|
|
26669
|
+
if (r === void 0 || !os2) continue;
|
|
26670
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
26671
|
+
boundaryInflow.set(e.to, (boundaryInflow.get(e.to) ?? 0) + r * w / os2);
|
|
26672
|
+
}
|
|
26673
|
+
const globalDangling = Number(store2.getMeta("danglingRankShare") ?? 0);
|
|
26674
|
+
let regionDangling = 0;
|
|
26675
|
+
for (const id of regionIds) {
|
|
26676
|
+
if ((outSum.get(id) ?? 0) <= 0) regionDangling += rank0.get(id) ?? 0;
|
|
26677
|
+
}
|
|
26678
|
+
const result = pagerankLocal({
|
|
26679
|
+
regionIds,
|
|
26680
|
+
rank0,
|
|
26681
|
+
out: out2,
|
|
26682
|
+
outSum,
|
|
26683
|
+
boundaryInflow,
|
|
26684
|
+
globalN: liveN,
|
|
26685
|
+
externalDanglingRank: Math.max(0, globalDangling - regionDangling),
|
|
26686
|
+
damping: 0.85,
|
|
26687
|
+
tolerance: 1e-6,
|
|
26688
|
+
maxIterations: 100
|
|
26689
|
+
});
|
|
26690
|
+
let pageRankWritten = 0;
|
|
26691
|
+
store2.transaction(() => {
|
|
26692
|
+
for (const [id, score2] of result.scores) {
|
|
26693
|
+
if (Math.abs(score2 - (rank0.get(id) ?? 0)) < 1e-9) continue;
|
|
26694
|
+
store2.setPageRank(id, score2);
|
|
26695
|
+
pageRankWritten++;
|
|
26696
|
+
}
|
|
26697
|
+
});
|
|
26698
|
+
const sweep = store2.landmarkSweepRows();
|
|
26699
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
26700
|
+
for (const row of sweep) {
|
|
26701
|
+
if (row.label === "Pattern" || row.label === "RootCause") {
|
|
26702
|
+
candidates.set(row.id, result.scores.get(row.id) ?? row.pageRank);
|
|
26703
|
+
}
|
|
26704
|
+
}
|
|
26705
|
+
const want = markLandmarks(candidates, 0.1);
|
|
26706
|
+
for (const row of sweep) if (row.memoryTier === "persistent") want.add(row.id);
|
|
26707
|
+
let landmarkFlips = 0;
|
|
26708
|
+
store2.transaction(() => {
|
|
26709
|
+
for (const row of sweep) {
|
|
26710
|
+
if (row.extractionSource === "bko-inferred") continue;
|
|
26711
|
+
const should = want.has(row.id);
|
|
26712
|
+
if (row.isLandmark === should) continue;
|
|
26713
|
+
store2.setLandmark(row.id, should);
|
|
26714
|
+
landmarkFlips++;
|
|
26715
|
+
}
|
|
26716
|
+
});
|
|
26717
|
+
return {
|
|
26718
|
+
scoredNodes: regionIds.length,
|
|
26719
|
+
iterations: result.iterations,
|
|
26720
|
+
converged: result.converged,
|
|
26721
|
+
landmarks: want.size,
|
|
26722
|
+
communities: 0,
|
|
26723
|
+
// deferred to the full backstop — see mode docs
|
|
26724
|
+
motifsPromoted: 0,
|
|
26725
|
+
techniques: 0,
|
|
26726
|
+
antipatterns: 0,
|
|
26727
|
+
pageRankWritten,
|
|
26728
|
+
landmarkFlips,
|
|
26729
|
+
mode: "incremental",
|
|
26730
|
+
regionSize: regionIds.length,
|
|
26731
|
+
durationMs: Date.now() - started
|
|
26732
|
+
};
|
|
26733
|
+
}
|
|
26734
|
+
function runFullRescore(store2) {
|
|
25906
26735
|
const started = Date.now();
|
|
25907
26736
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
25908
26737
|
const nodeIds = [];
|
|
@@ -25928,10 +26757,12 @@ function runNightlyPipeline(store2) {
|
|
|
25928
26757
|
maxIterations: 100
|
|
25929
26758
|
});
|
|
25930
26759
|
const scored = result.scores.size;
|
|
26760
|
+
let pageRankWritten = 0;
|
|
25931
26761
|
store2.transaction(() => {
|
|
25932
26762
|
for (const [id, score2] of result.scores) {
|
|
25933
26763
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
25934
26764
|
store2.setPageRank(id, score2);
|
|
26765
|
+
pageRankWritten++;
|
|
25935
26766
|
}
|
|
25936
26767
|
});
|
|
25937
26768
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -25946,11 +26777,13 @@ function runNightlyPipeline(store2) {
|
|
|
25946
26777
|
for (const id of nodeIds) {
|
|
25947
26778
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
25948
26779
|
}
|
|
26780
|
+
let landmarkFlips = 0;
|
|
25949
26781
|
store2.transaction(() => {
|
|
25950
26782
|
for (const id of nodeIds) {
|
|
25951
26783
|
const want = allLandmarks.has(id);
|
|
25952
26784
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
25953
26785
|
store2.setLandmark(id, want);
|
|
26786
|
+
landmarkFlips++;
|
|
25954
26787
|
}
|
|
25955
26788
|
});
|
|
25956
26789
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -25985,8 +26818,6 @@ function runNightlyPipeline(store2) {
|
|
|
25985
26818
|
for (const [id, c] of comm.community) store2.updateNode(id, { community: c });
|
|
25986
26819
|
});
|
|
25987
26820
|
const motifs = promoteMotifs(store2, started);
|
|
25988
|
-
const designResolved = resolveDesignProblems(store2, started);
|
|
25989
|
-
const cry = crystallize(store2, { ts: started });
|
|
25990
26821
|
return {
|
|
25991
26822
|
scoredNodes: scored,
|
|
25992
26823
|
iterations: result.iterations,
|
|
@@ -25996,15 +26827,61 @@ function runNightlyPipeline(store2) {
|
|
|
25996
26827
|
motifsPromoted: motifs.promoted,
|
|
25997
26828
|
techniques: motifs.techniques,
|
|
25998
26829
|
antipatterns: motifs.antipatterns,
|
|
25999
|
-
|
|
26000
|
-
|
|
26001
|
-
|
|
26830
|
+
pageRankWritten,
|
|
26831
|
+
landmarkFlips,
|
|
26832
|
+
mode: "full",
|
|
26833
|
+
regionSize: 0,
|
|
26834
|
+
startedAt: started,
|
|
26835
|
+
danglingRank: result.danglingRank,
|
|
26836
|
+
durationMs: Date.now() - started
|
|
26837
|
+
};
|
|
26838
|
+
}
|
|
26839
|
+
function runSemanticMaintenance(store2) {
|
|
26840
|
+
const started = Date.now();
|
|
26841
|
+
reopenAutoClosedProblems(store2, started);
|
|
26842
|
+
const revisits = clearAnsweredRevisits(store2, started);
|
|
26843
|
+
const fixCandidates = clearSettledFixCandidates(store2, started);
|
|
26844
|
+
const designResolved = markFixCandidates(store2, started);
|
|
26845
|
+
const cry = crystallize(store2, { ts: started });
|
|
26846
|
+
return {
|
|
26002
26847
|
designResolved,
|
|
26848
|
+
revisitsCleared: revisits.cleared,
|
|
26849
|
+
revisitsStanding: revisits.standing,
|
|
26850
|
+
fixCandidatesSettled: fixCandidates.answered + fixCandidates.ignored + fixCandidates.unsubstantiated,
|
|
26851
|
+
fixCandidates,
|
|
26003
26852
|
claimsEvaluated: cry.evaluated,
|
|
26004
26853
|
claimsDerived: cry.derived.length,
|
|
26005
26854
|
durationMs: Date.now() - started
|
|
26006
26855
|
};
|
|
26007
26856
|
}
|
|
26857
|
+
function runNightlyPipeline(store2) {
|
|
26858
|
+
const started = Date.now();
|
|
26859
|
+
const rescore = runGraphRescore(store2);
|
|
26860
|
+
const semantic = runSemanticMaintenance(store2);
|
|
26861
|
+
return {
|
|
26862
|
+
scoredNodes: rescore.scoredNodes,
|
|
26863
|
+
iterations: rescore.iterations,
|
|
26864
|
+
converged: rescore.converged,
|
|
26865
|
+
landmarks: rescore.landmarks,
|
|
26866
|
+
communities: rescore.communities,
|
|
26867
|
+
motifsPromoted: rescore.motifsPromoted,
|
|
26868
|
+
techniques: rescore.techniques,
|
|
26869
|
+
antipatterns: rescore.antipatterns,
|
|
26870
|
+
skillsInduced: 0,
|
|
26871
|
+
// skills are cloud-induced now (see above)
|
|
26872
|
+
skillsRefreshed: 0,
|
|
26873
|
+
pageRankWritten: rescore.pageRankWritten,
|
|
26874
|
+
landmarkFlips: rescore.landmarkFlips,
|
|
26875
|
+
mode: rescore.mode,
|
|
26876
|
+
regionSize: rescore.regionSize,
|
|
26877
|
+
designResolved: semantic.designResolved,
|
|
26878
|
+
revisitsCleared: semantic.revisitsCleared,
|
|
26879
|
+
fixCandidatesSettled: semantic.fixCandidatesSettled,
|
|
26880
|
+
claimsEvaluated: semantic.claimsEvaluated,
|
|
26881
|
+
claimsDerived: semantic.claimsDerived,
|
|
26882
|
+
durationMs: Date.now() - started
|
|
26883
|
+
};
|
|
26884
|
+
}
|
|
26008
26885
|
function codeAnchorProjection(store2, semanticIds, opts = {}) {
|
|
26009
26886
|
const anchorToNodes = /* @__PURE__ */ new Map();
|
|
26010
26887
|
for (const id of semanticIds) {
|
|
@@ -26049,12 +26926,14 @@ var AGENTS_POINTER_BODY = [
|
|
|
26049
26926
|
init_src2();
|
|
26050
26927
|
function buildSnapshot(opts) {
|
|
26051
26928
|
const problems = opts.store.findNodesByLabel("Problem").filter((p) => p.attrs["mergedInto"] === void 0);
|
|
26052
|
-
const
|
|
26929
|
+
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
26930
|
+
const allProblems = stillOpen.filter((p) => !isConstraintProblem(p));
|
|
26053
26931
|
allProblems.sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt);
|
|
26054
26932
|
const recent = allProblems.slice(0, 8).map((p) => ({
|
|
26055
26933
|
node: p,
|
|
26056
26934
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
26057
26935
|
}));
|
|
26936
|
+
const recentConstraints = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, 3);
|
|
26058
26937
|
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) => {
|
|
26059
26938
|
const solEdge = opts.store.outEdges(p.id, ["SOLVED_BY"])[0];
|
|
26060
26939
|
const solution = solEdge ? opts.store.getNode(solEdge.to) ?? void 0 : void 0;
|
|
@@ -26082,10 +26961,13 @@ function buildSnapshot(opts) {
|
|
|
26082
26961
|
episodeId,
|
|
26083
26962
|
problems: problems2
|
|
26084
26963
|
}));
|
|
26085
|
-
const
|
|
26086
|
-
|
|
26087
|
-
|
|
26088
|
-
|
|
26964
|
+
const revisitSeen = /* @__PURE__ */ new Set();
|
|
26965
|
+
const needsRevisit = listNeedsRevisit(opts.store, (opts.now ?? /* @__PURE__ */ new Date()).getTime()).filter((r) => {
|
|
26966
|
+
const key = `${r.label}:${r.description.trim().toLowerCase()}`;
|
|
26967
|
+
if (revisitSeen.has(key)) return false;
|
|
26968
|
+
revisitSeen.add(key);
|
|
26969
|
+
return true;
|
|
26970
|
+
}).slice(0, 5);
|
|
26089
26971
|
const norm = (x) => x.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
26090
26972
|
const pkgBase = (x) => {
|
|
26091
26973
|
const at = x.lastIndexOf("@");
|
|
@@ -26121,7 +27003,9 @@ function buildSnapshot(opts) {
|
|
|
26121
27003
|
profileContext,
|
|
26122
27004
|
recentProblems: recent,
|
|
26123
27005
|
recentResolved,
|
|
27006
|
+
recentConstraints,
|
|
26124
27007
|
...causalNudge ? { causalNudge } : {},
|
|
27008
|
+
...opts.selfCiteSkips && opts.selfCiteSkips > 0 ? { selfCiteSkips: opts.selfCiteSkips } : {},
|
|
26125
27009
|
...domainNudge ? { domainNudge } : {},
|
|
26126
27010
|
motifs,
|
|
26127
27011
|
reviewCount: opts.reviewCount,
|
|
@@ -26172,10 +27056,16 @@ function sliceForFile(store2, relPath) {
|
|
|
26172
27056
|
}
|
|
26173
27057
|
const fp = priorsForFile(store2, relPath);
|
|
26174
27058
|
const priors = fp ? {
|
|
26175
|
-
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27059
|
+
openProblems: fp.openProblems.slice(0, 3).map((p) => ({
|
|
27060
|
+
id: p.id,
|
|
27061
|
+
description: p.description,
|
|
27062
|
+
...typeof p.attrs["fixCandidateFile"] === "string" ? { fixCandidateFile: p.attrs["fixCandidateFile"] } : {}
|
|
27063
|
+
})),
|
|
27064
|
+
constraints: fp.constraints.slice(0, 2).map((c) => ({ id: c.id, description: c.description })),
|
|
27065
|
+
resolvedProblems: fp.resolvedProblems.slice(0, 3).map((p) => ({ id: p.id, description: p.description })),
|
|
26176
27066
|
related: fp.related.slice(0, 4).map((n) => ({ id: n.id, label: n.label, description: n.description })),
|
|
26177
27067
|
solutionsByProblem: Object.fromEntries(
|
|
26178
|
-
fp.openProblems.slice(0, 3).map((p) => [
|
|
27068
|
+
[...fp.openProblems.slice(0, 3), ...fp.resolvedProblems.slice(0, 3)].map((p) => [
|
|
26179
27069
|
p.id,
|
|
26180
27070
|
(fp.solutionsByProblem.get(p.id) ?? []).slice(0, 4).map((s) => ({ id: s.id, description: s.description }))
|
|
26181
27071
|
])
|
|
@@ -26283,9 +27173,15 @@ function* walkSource(dir) {
|
|
|
26283
27173
|
function listSourceFiles(root) {
|
|
26284
27174
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
26285
27175
|
}
|
|
26286
|
-
|
|
27176
|
+
var SCAN_YIELD_EVERY = 100;
|
|
27177
|
+
var LIVENESS_CHUNK = 4e3;
|
|
27178
|
+
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
26287
27179
|
const stale = [];
|
|
27180
|
+
const pending = [];
|
|
27181
|
+
const allTargets = [];
|
|
27182
|
+
let scanned = 0;
|
|
26288
27183
|
for (const abs of listSourceFiles(rootPath)) {
|
|
27184
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
26289
27185
|
let mtimeMs;
|
|
26290
27186
|
try {
|
|
26291
27187
|
mtimeMs = statSync2(abs).mtimeMs;
|
|
@@ -26296,32 +27192,52 @@ function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
26296
27192
|
const fnode = store2.getNode(fileNodeId(workspaceId, rel));
|
|
26297
27193
|
if (!fnode) {
|
|
26298
27194
|
stale.push(abs);
|
|
27195
|
+
} else if ((fnode.validTo ?? null) !== null) {
|
|
27196
|
+
stale.push(abs);
|
|
26299
27197
|
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
26300
27198
|
stale.push(abs);
|
|
26301
27199
|
} else {
|
|
26302
27200
|
const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
26303
27201
|
if (edges.length === 0) {
|
|
26304
27202
|
stale.push(abs);
|
|
26305
|
-
} else
|
|
26306
|
-
|
|
27203
|
+
} else {
|
|
27204
|
+
const targets = edges.map((e) => e.to);
|
|
27205
|
+
pending.push({ abs, targets });
|
|
27206
|
+
allTargets.push(...targets);
|
|
26307
27207
|
}
|
|
26308
27208
|
}
|
|
26309
27209
|
}
|
|
27210
|
+
const live = /* @__PURE__ */ new Set();
|
|
27211
|
+
for (let i2 = 0; i2 < allTargets.length; i2 += LIVENESS_CHUNK) {
|
|
27212
|
+
for (const id of store2.liveNodeIds(allTargets.slice(i2, i2 + LIVENESS_CHUNK))) live.add(id);
|
|
27213
|
+
if (i2 + LIVENESS_CHUNK < allTargets.length) await new Promise((r) => setImmediate(r));
|
|
27214
|
+
}
|
|
27215
|
+
for (const { abs, targets } of pending) {
|
|
27216
|
+
if (targets.some((id) => !live.has(id))) stale.push(abs);
|
|
27217
|
+
}
|
|
26310
27218
|
return stale;
|
|
26311
27219
|
}
|
|
26312
|
-
function isLive(n) {
|
|
26313
|
-
return n != null && (n.validTo ?? null) === null;
|
|
26314
|
-
}
|
|
26315
27220
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
26316
|
-
const stale = findStaleFiles(store2, rootPath, workspaceId);
|
|
27221
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId);
|
|
26317
27222
|
if (stale.length === 0) return 0;
|
|
26318
27223
|
let total = 0;
|
|
27224
|
+
let failedBatches = 0;
|
|
26319
27225
|
const BATCH = 20;
|
|
26320
27226
|
for (let i2 = 0; i2 < stale.length; i2 += BATCH) {
|
|
26321
|
-
|
|
26322
|
-
|
|
27227
|
+
try {
|
|
27228
|
+
const r = await incrementalReindex(store2, rootPath, workspaceId, stale.slice(i2, i2 + BATCH));
|
|
27229
|
+
total += r.filesReindexed;
|
|
27230
|
+
} catch (err2) {
|
|
27231
|
+
failedBatches++;
|
|
27232
|
+
console.warn(
|
|
27233
|
+
`[errata] reconcile: batch ${i2 / BATCH + 1} failed (${stale.slice(i2, i2 + BATCH).length} file(s) skipped): ${err2 instanceof Error ? err2.message : err2}`
|
|
27234
|
+
);
|
|
27235
|
+
}
|
|
26323
27236
|
if (i2 + BATCH < stale.length) await new Promise((res) => setImmediate(res));
|
|
26324
27237
|
}
|
|
27238
|
+
if (failedBatches > 0) {
|
|
27239
|
+
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
27240
|
+
}
|
|
26325
27241
|
return total;
|
|
26326
27242
|
}
|
|
26327
27243
|
|
|
@@ -26374,6 +27290,7 @@ function runNightly() {
|
|
|
26374
27290
|
const a = backfillLegacyAnchors(store, Date.now());
|
|
26375
27291
|
anchorsDemoted = a.demotedEdges;
|
|
26376
27292
|
resolutionsSuspect = a.suspects.length;
|
|
27293
|
+
repairLegacyAnchorsFromStatement(store, Date.now());
|
|
26377
27294
|
} catch {
|
|
26378
27295
|
}
|
|
26379
27296
|
const report = runNightlyPipeline(store);
|