@inerrata-corporation/errata 2.0.2-dev.650 → 2.0.2-dev.685
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 +91 -0
- package/errata.mjs +520 -38
- package/package.json +1 -1
- package/pass-worker.mjs +356 -5
package/consolidate-worker.mjs
CHANGED
|
@@ -15579,6 +15579,7 @@ function openDatabase(path) {
|
|
|
15579
15579
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15580
15580
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15581
15581
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15582
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15582
15583
|
} catch {
|
|
15583
15584
|
}
|
|
15584
15585
|
}
|
|
@@ -16562,6 +16563,96 @@ var SqliteGraphStore = class {
|
|
|
16562
16563
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
16563
16564
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
16564
16565
|
}
|
|
16566
|
+
getMeta(key) {
|
|
16567
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
16568
|
+
return r?.value ?? null;
|
|
16569
|
+
}
|
|
16570
|
+
setMeta(key, value) {
|
|
16571
|
+
this.db.prepare(
|
|
16572
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
16573
|
+
).run({ key, value });
|
|
16574
|
+
}
|
|
16575
|
+
dirtyNodeIdsSince(ts) {
|
|
16576
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
16577
|
+
return rows.map((r) => r.id);
|
|
16578
|
+
}
|
|
16579
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
16580
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
16581
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
16582
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
16583
|
+
edgeEndpointsTouchedSince(ts) {
|
|
16584
|
+
const rows = this.db.prepare(
|
|
16585
|
+
`SELECT from_id, to_id FROM edges
|
|
16586
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
16587
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
16588
|
+
).all({ ts });
|
|
16589
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
16590
|
+
}
|
|
16591
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
16592
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
16593
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
16594
|
+
outEdgesForMany(ids) {
|
|
16595
|
+
return this.edgesForMany(ids, "from_id");
|
|
16596
|
+
}
|
|
16597
|
+
inEdgesForMany(ids) {
|
|
16598
|
+
return this.edgesForMany(ids, "to_id");
|
|
16599
|
+
}
|
|
16600
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
16601
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
16602
|
+
ranksForMany(ids) {
|
|
16603
|
+
const out = /* @__PURE__ */ new Map();
|
|
16604
|
+
const CHUNK = 400;
|
|
16605
|
+
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
16606
|
+
const chunk = ids.slice(i, i + CHUNK);
|
|
16607
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
16608
|
+
const rows = this.db.prepare(
|
|
16609
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
16610
|
+
).all(...chunk);
|
|
16611
|
+
for (const r of rows) out.set(r.id, r.page_rank);
|
|
16612
|
+
}
|
|
16613
|
+
return out;
|
|
16614
|
+
}
|
|
16615
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
16616
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
16617
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
16618
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
16619
|
+
* scanLiveNodes materialization. */
|
|
16620
|
+
landmarkSweepRows() {
|
|
16621
|
+
const rows = this.db.prepare(
|
|
16622
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
16623
|
+
WHERE valid_to IS NULL
|
|
16624
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
16625
|
+
).all();
|
|
16626
|
+
return rows.map((r) => ({
|
|
16627
|
+
id: r.id,
|
|
16628
|
+
label: r.label,
|
|
16629
|
+
pageRank: r.page_rank,
|
|
16630
|
+
isLandmark: r.is_landmark === 1,
|
|
16631
|
+
memoryTier: r.memory_tier,
|
|
16632
|
+
extractionSource: r.extraction_source
|
|
16633
|
+
}));
|
|
16634
|
+
}
|
|
16635
|
+
edgesForMany(ids, col) {
|
|
16636
|
+
const out = [];
|
|
16637
|
+
const CHUNK = 400;
|
|
16638
|
+
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
16639
|
+
const chunk = ids.slice(i, i + CHUNK);
|
|
16640
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
16641
|
+
const rows = this.db.prepare(
|
|
16642
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
16643
|
+
).all(...chunk);
|
|
16644
|
+
for (const r of rows) out.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
16645
|
+
}
|
|
16646
|
+
return out;
|
|
16647
|
+
}
|
|
16648
|
+
checkpointWal() {
|
|
16649
|
+
try {
|
|
16650
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
16651
|
+
return r ?? null;
|
|
16652
|
+
} catch {
|
|
16653
|
+
return null;
|
|
16654
|
+
}
|
|
16655
|
+
}
|
|
16565
16656
|
reviveReobserved(relPaths, workspaceId, since) {
|
|
16566
16657
|
let revived = 0;
|
|
16567
16658
|
const CHUNK = 400;
|
package/errata.mjs
CHANGED
|
@@ -305,6 +305,10 @@ var init_castalia = __esm({
|
|
|
305
305
|
// git topology, not signal-flow
|
|
306
306
|
"AUTHORED_BY",
|
|
307
307
|
// git authorship, not signal-flow
|
|
308
|
+
"PRODUCED",
|
|
309
|
+
// edit-episode provenance (Episode→symbol), not signal-flow — the family
|
|
310
|
+
// above was excluded together but this member slipped the net (LC-produced-pagerank);
|
|
311
|
+
// at 43% of all live edges it was the largest single edge population flowing rank
|
|
308
312
|
"CONTRIBUTED",
|
|
309
313
|
// agent attribution (Agent → knowledge), not signal-flow
|
|
310
314
|
"SUPERSEDES",
|
|
@@ -16399,6 +16403,7 @@ function openDatabase(path2) {
|
|
|
16399
16403
|
db.exec("PRAGMA journal_mode = WAL");
|
|
16400
16404
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
16401
16405
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
16406
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
16402
16407
|
} catch {
|
|
16403
16408
|
}
|
|
16404
16409
|
}
|
|
@@ -17237,6 +17242,96 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
17237
17242
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
17238
17243
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
17239
17244
|
}
|
|
17245
|
+
getMeta(key) {
|
|
17246
|
+
const r = this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key);
|
|
17247
|
+
return r?.value ?? null;
|
|
17248
|
+
}
|
|
17249
|
+
setMeta(key, value) {
|
|
17250
|
+
this.db.prepare(
|
|
17251
|
+
"INSERT INTO store_meta (key, value) VALUES (:key, :value) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
17252
|
+
).run({ key, value });
|
|
17253
|
+
}
|
|
17254
|
+
dirtyNodeIdsSince(ts) {
|
|
17255
|
+
const rows = this.db.prepare("SELECT id FROM nodes WHERE valid_to IS NULL AND last_updated_at > ?").all(ts);
|
|
17256
|
+
return rows.map((r) => r.id);
|
|
17257
|
+
}
|
|
17258
|
+
/** Endpoints of edges TOUCHED since `ts` — created, re-seen, or CLOSED.
|
|
17259
|
+
* Closures matter as much as additions: a node that lost inflow is rank-dirty
|
|
17260
|
+
* while its own row never updated, so removal endpoints must seed the
|
|
17261
|
+
* incremental region or the stale inflow persists until the full backstop. */
|
|
17262
|
+
edgeEndpointsTouchedSince(ts) {
|
|
17263
|
+
const rows = this.db.prepare(
|
|
17264
|
+
`SELECT from_id, to_id FROM edges
|
|
17265
|
+
WHERE (valid_to IS NULL AND (created_at > :ts OR last_seen_at > :ts))
|
|
17266
|
+
OR (valid_to IS NOT NULL AND valid_to > :ts)`
|
|
17267
|
+
).all({ ts });
|
|
17268
|
+
return rows.map((r) => ({ from: r.from_id, to: r.to_id }));
|
|
17269
|
+
}
|
|
17270
|
+
/** Lightweight out-edges for a SET of sources, chunked IN-lists — the region
|
|
17271
|
+
* assembly path for incremental PageRank (per-node outEdges() at region
|
|
17272
|
+
* scale re-creates the 106k-prepared-calls problem the batch scan solved). */
|
|
17273
|
+
outEdgesForMany(ids) {
|
|
17274
|
+
return this.edgesForMany(ids, "from_id");
|
|
17275
|
+
}
|
|
17276
|
+
inEdgesForMany(ids) {
|
|
17277
|
+
return this.edgesForMany(ids, "to_id");
|
|
17278
|
+
}
|
|
17279
|
+
/** Stored pageRank for a SET of ids (live rows only — a closed id is simply
|
|
17280
|
+
* absent, which is how incremental region assembly drops dead endpoints). */
|
|
17281
|
+
ranksForMany(ids) {
|
|
17282
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
17283
|
+
const CHUNK = 400;
|
|
17284
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
17285
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
17286
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
17287
|
+
const rows = this.db.prepare(
|
|
17288
|
+
`SELECT id, page_rank FROM nodes WHERE id IN (${placeholders}) AND valid_to IS NULL`
|
|
17289
|
+
).all(...chunk);
|
|
17290
|
+
for (const r of rows) out2.set(r.id, r.page_rank);
|
|
17291
|
+
}
|
|
17292
|
+
return out2;
|
|
17293
|
+
}
|
|
17294
|
+
/** The minimal row set the landmark sweep needs — landmark CANDIDATES
|
|
17295
|
+
* (Pattern/RootCause), everything currently flagged, and every
|
|
17296
|
+
* persistent-tier node (force-landmarks). A few thousand rows, so the
|
|
17297
|
+
* incremental path can refresh the GLOBAL landmark set without the 179k-row
|
|
17298
|
+
* scanLiveNodes materialization. */
|
|
17299
|
+
landmarkSweepRows() {
|
|
17300
|
+
const rows = this.db.prepare(
|
|
17301
|
+
`SELECT id, label, page_rank, is_landmark, memory_tier, extraction_source FROM nodes
|
|
17302
|
+
WHERE valid_to IS NULL
|
|
17303
|
+
AND (memory_tier = 'persistent' OR is_landmark = 1 OR label IN ('Pattern', 'RootCause'))`
|
|
17304
|
+
).all();
|
|
17305
|
+
return rows.map((r) => ({
|
|
17306
|
+
id: r.id,
|
|
17307
|
+
label: r.label,
|
|
17308
|
+
pageRank: r.page_rank,
|
|
17309
|
+
isLandmark: r.is_landmark === 1,
|
|
17310
|
+
memoryTier: r.memory_tier,
|
|
17311
|
+
extractionSource: r.extraction_source
|
|
17312
|
+
}));
|
|
17313
|
+
}
|
|
17314
|
+
edgesForMany(ids, col) {
|
|
17315
|
+
const out2 = [];
|
|
17316
|
+
const CHUNK = 400;
|
|
17317
|
+
for (let i2 = 0; i2 < ids.length; i2 += CHUNK) {
|
|
17318
|
+
const chunk = ids.slice(i2, i2 + CHUNK);
|
|
17319
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
17320
|
+
const rows = this.db.prepare(
|
|
17321
|
+
`SELECT from_id, to_id, type FROM edges WHERE ${col} IN (${placeholders}) AND valid_to IS NULL`
|
|
17322
|
+
).all(...chunk);
|
|
17323
|
+
for (const r of rows) out2.push({ from: r.from_id, to: r.to_id, type: r.type });
|
|
17324
|
+
}
|
|
17325
|
+
return out2;
|
|
17326
|
+
}
|
|
17327
|
+
checkpointWal() {
|
|
17328
|
+
try {
|
|
17329
|
+
const r = this.db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
17330
|
+
return r ?? null;
|
|
17331
|
+
} catch {
|
|
17332
|
+
return null;
|
|
17333
|
+
}
|
|
17334
|
+
}
|
|
17240
17335
|
reviveReobserved(relPaths, workspaceId2, since) {
|
|
17241
17336
|
let revived = 0;
|
|
17242
17337
|
const CHUNK = 400;
|
|
@@ -20199,7 +20294,7 @@ function pagerank(input) {
|
|
|
20199
20294
|
const ids = input.nodeIds;
|
|
20200
20295
|
const n = ids.length;
|
|
20201
20296
|
if (n === 0) {
|
|
20202
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
20297
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
20203
20298
|
}
|
|
20204
20299
|
const index = /* @__PURE__ */ new Map();
|
|
20205
20300
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -20271,8 +20366,12 @@ function pagerank(input) {
|
|
|
20271
20366
|
}
|
|
20272
20367
|
}
|
|
20273
20368
|
const scores = /* @__PURE__ */ new Map();
|
|
20274
|
-
|
|
20275
|
-
|
|
20369
|
+
let danglingRank = 0;
|
|
20370
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
20371
|
+
scores.set(ids[i2], score2[i2]);
|
|
20372
|
+
if (dangling[i2]) danglingRank += score2[i2];
|
|
20373
|
+
}
|
|
20374
|
+
return { scores, iterations: iter, converged, danglingRank };
|
|
20276
20375
|
}
|
|
20277
20376
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
20278
20377
|
const entries = [...scores.entries()];
|
|
@@ -20282,7 +20381,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
20282
20381
|
}
|
|
20283
20382
|
}
|
|
20284
20383
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
20285
|
-
entries.sort((a, b) => b[1] - a[1]);
|
|
20384
|
+
entries.sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));
|
|
20286
20385
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
20287
20386
|
const out2 = /* @__PURE__ */ new Set();
|
|
20288
20387
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -20739,6 +20838,92 @@ var init_bko = __esm({
|
|
|
20739
20838
|
}
|
|
20740
20839
|
});
|
|
20741
20840
|
|
|
20841
|
+
// ../../packages/math/src/pagerank-local.ts
|
|
20842
|
+
function pagerankLocal(input) {
|
|
20843
|
+
const damping = input.damping ?? 0.85;
|
|
20844
|
+
const tol = input.tolerance ?? 1e-6;
|
|
20845
|
+
const maxIter = input.maxIterations ?? 100;
|
|
20846
|
+
const ids = input.regionIds;
|
|
20847
|
+
const n = ids.length;
|
|
20848
|
+
if (n === 0 || input.globalN === 0) {
|
|
20849
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
20850
|
+
}
|
|
20851
|
+
const index = /* @__PURE__ */ new Map();
|
|
20852
|
+
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
20853
|
+
const base = (1 - damping) / input.globalN;
|
|
20854
|
+
const outSum = new Float64Array(n);
|
|
20855
|
+
const inflow = new Float64Array(n);
|
|
20856
|
+
let score2 = new Float64Array(n);
|
|
20857
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
20858
|
+
const id = ids[i2];
|
|
20859
|
+
outSum[i2] = input.outSum.get(id) ?? 0;
|
|
20860
|
+
inflow[i2] = input.boundaryInflow.get(id) ?? 0;
|
|
20861
|
+
score2[i2] = input.rank0.get(id) ?? base;
|
|
20862
|
+
}
|
|
20863
|
+
const rowLen = new Int32Array(n);
|
|
20864
|
+
let edgeCount = 0;
|
|
20865
|
+
for (const [from, edges] of input.out) {
|
|
20866
|
+
const fi = index.get(from);
|
|
20867
|
+
if (fi === void 0) continue;
|
|
20868
|
+
let inSet = 0;
|
|
20869
|
+
for (const e of edges) if (index.has(e.to)) inSet++;
|
|
20870
|
+
rowLen[fi] = inSet;
|
|
20871
|
+
edgeCount += inSet;
|
|
20872
|
+
}
|
|
20873
|
+
const rowStart = new Int32Array(n + 1);
|
|
20874
|
+
for (let i2 = 0; i2 < n; i2++) rowStart[i2 + 1] = rowStart[i2] + rowLen[i2];
|
|
20875
|
+
const colIdx = new Int32Array(edgeCount);
|
|
20876
|
+
const colW = new Float64Array(edgeCount);
|
|
20877
|
+
const cursor = rowStart.slice(0, n);
|
|
20878
|
+
for (const [from, edges] of input.out) {
|
|
20879
|
+
const fi = index.get(from);
|
|
20880
|
+
if (fi === void 0) continue;
|
|
20881
|
+
for (const e of edges) {
|
|
20882
|
+
const ti = index.get(e.to);
|
|
20883
|
+
if (ti === void 0) continue;
|
|
20884
|
+
const c = cursor[fi];
|
|
20885
|
+
cursor[fi] = c + 1;
|
|
20886
|
+
colIdx[c] = ti;
|
|
20887
|
+
colW[c] = e.weight;
|
|
20888
|
+
}
|
|
20889
|
+
}
|
|
20890
|
+
const externalDangling = input.externalDanglingRank ?? 0;
|
|
20891
|
+
let next = new Float64Array(n);
|
|
20892
|
+
let iter = 0;
|
|
20893
|
+
let converged = false;
|
|
20894
|
+
for (; iter < maxIter; iter++) {
|
|
20895
|
+
let internalDangling = 0;
|
|
20896
|
+
for (let i2 = 0; i2 < n; i2++) if (outSum[i2] <= 0) internalDangling += score2[i2];
|
|
20897
|
+
const danglingShare = damping * (internalDangling + externalDangling) / input.globalN;
|
|
20898
|
+
for (let i2 = 0; i2 < n; i2++) next[i2] = base + danglingShare + damping * inflow[i2];
|
|
20899
|
+
for (let i2 = 0; i2 < n; i2++) {
|
|
20900
|
+
const os2 = outSum[i2];
|
|
20901
|
+
if (os2 <= 0) continue;
|
|
20902
|
+
const f = damping * score2[i2] / os2;
|
|
20903
|
+
const end = rowStart[i2 + 1];
|
|
20904
|
+
for (let c = rowStart[i2]; c < end; c++) next[colIdx[c]] += f * colW[c];
|
|
20905
|
+
}
|
|
20906
|
+
let diff = 0;
|
|
20907
|
+
for (let i2 = 0; i2 < n; i2++) diff += Math.abs(next[i2] - score2[i2]);
|
|
20908
|
+
const tmp = score2;
|
|
20909
|
+
score2 = next;
|
|
20910
|
+
next = tmp;
|
|
20911
|
+
if (diff < tol) {
|
|
20912
|
+
iter++;
|
|
20913
|
+
converged = true;
|
|
20914
|
+
break;
|
|
20915
|
+
}
|
|
20916
|
+
}
|
|
20917
|
+
const scores = /* @__PURE__ */ new Map();
|
|
20918
|
+
for (let i2 = 0; i2 < n; i2++) scores.set(ids[i2], score2[i2]);
|
|
20919
|
+
return { scores, iterations: iter, converged };
|
|
20920
|
+
}
|
|
20921
|
+
var init_pagerank_local = __esm({
|
|
20922
|
+
"../../packages/math/src/pagerank-local.ts"() {
|
|
20923
|
+
"use strict";
|
|
20924
|
+
}
|
|
20925
|
+
});
|
|
20926
|
+
|
|
20742
20927
|
// ../../packages/math/src/index.ts
|
|
20743
20928
|
var init_src4 = __esm({
|
|
20744
20929
|
"../../packages/math/src/index.ts"() {
|
|
@@ -20762,6 +20947,7 @@ var init_src4 = __esm({
|
|
|
20762
20947
|
init_spectral();
|
|
20763
20948
|
init_motif_gate();
|
|
20764
20949
|
init_bko();
|
|
20950
|
+
init_pagerank_local();
|
|
20765
20951
|
}
|
|
20766
20952
|
});
|
|
20767
20953
|
|
|
@@ -29194,7 +29380,161 @@ var init_motifs = __esm({
|
|
|
29194
29380
|
});
|
|
29195
29381
|
|
|
29196
29382
|
// ../../packages/generalizer/src/nightly.ts
|
|
29197
|
-
function runGraphRescore(store) {
|
|
29383
|
+
function runGraphRescore(store, opts = {}) {
|
|
29384
|
+
const lastRescoreAt = Number(store.getMeta?.("lastRescoreAt") ?? 0);
|
|
29385
|
+
const sinceFull = Number(store.getMeta?.("rescoresSinceFull") ?? 0);
|
|
29386
|
+
const incrementalCapable = !opts.forceFull && lastRescoreAt > 0 && sinceFull < FULL_RESCORE_EVERY - 1 && !!store.getMeta && !!store.setMeta && !!store.dirtyNodeIdsSince && !!store.edgeEndpointsTouchedSince && !!store.outEdgesForMany && !!store.inEdgesForMany && !!store.ranksForMany && !!store.landmarkSweepRows;
|
|
29387
|
+
if (incrementalCapable) {
|
|
29388
|
+
const started2 = Date.now();
|
|
29389
|
+
const inc = tryIncrementalRescore(store, lastRescoreAt, started2);
|
|
29390
|
+
if (inc) {
|
|
29391
|
+
store.setMeta("lastRescoreAt", String(started2));
|
|
29392
|
+
store.setMeta("rescoresSinceFull", String(sinceFull + 1));
|
|
29393
|
+
return inc;
|
|
29394
|
+
}
|
|
29395
|
+
}
|
|
29396
|
+
const report = runFullRescore(store);
|
|
29397
|
+
store.setMeta?.("lastRescoreAt", String(report.startedAt));
|
|
29398
|
+
store.setMeta?.("rescoresSinceFull", "0");
|
|
29399
|
+
store.setMeta?.("danglingRankShare", String(report.danglingRank));
|
|
29400
|
+
const { startedAt: _drop, danglingRank: _drop2, ...rest2 } = report;
|
|
29401
|
+
return rest2;
|
|
29402
|
+
}
|
|
29403
|
+
function tryIncrementalRescore(store, lastRescoreAt, started2) {
|
|
29404
|
+
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
29405
|
+
const liveN = store.nodeCount();
|
|
29406
|
+
const cap = Math.max(1e3, Math.floor(liveN * INCREMENTAL_REGION_MAX_FRACTION));
|
|
29407
|
+
const region = new Set(store.dirtyNodeIdsSince(lastRescoreAt));
|
|
29408
|
+
for (const e of store.edgeEndpointsTouchedSince(lastRescoreAt)) {
|
|
29409
|
+
region.add(e.from);
|
|
29410
|
+
region.add(e.to);
|
|
29411
|
+
}
|
|
29412
|
+
if (region.size > cap) return null;
|
|
29413
|
+
let frontier = [...region];
|
|
29414
|
+
for (let hop = 0; hop < INCREMENTAL_HOPS && frontier.length > 0; hop++) {
|
|
29415
|
+
const next = [];
|
|
29416
|
+
for (const e of store.outEdgesForMany(frontier)) {
|
|
29417
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
29418
|
+
if (!region.has(e.to)) {
|
|
29419
|
+
region.add(e.to);
|
|
29420
|
+
next.push(e.to);
|
|
29421
|
+
}
|
|
29422
|
+
}
|
|
29423
|
+
if (region.size > cap) return null;
|
|
29424
|
+
frontier = next;
|
|
29425
|
+
}
|
|
29426
|
+
const rank0 = store.ranksForMany([...region]);
|
|
29427
|
+
const regionIds = [...rank0.keys()];
|
|
29428
|
+
if (regionIds.length === 0) {
|
|
29429
|
+
return {
|
|
29430
|
+
scoredNodes: 0,
|
|
29431
|
+
iterations: 0,
|
|
29432
|
+
converged: true,
|
|
29433
|
+
landmarks: 0,
|
|
29434
|
+
communities: 0,
|
|
29435
|
+
motifsPromoted: 0,
|
|
29436
|
+
techniques: 0,
|
|
29437
|
+
antipatterns: 0,
|
|
29438
|
+
pageRankWritten: 0,
|
|
29439
|
+
landmarkFlips: 0,
|
|
29440
|
+
mode: "incremental",
|
|
29441
|
+
regionSize: 0,
|
|
29442
|
+
durationMs: Date.now() - started2
|
|
29443
|
+
};
|
|
29444
|
+
}
|
|
29445
|
+
const inRegion = new Set(regionIds);
|
|
29446
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
29447
|
+
const outSum = /* @__PURE__ */ new Map();
|
|
29448
|
+
for (const e of store.outEdgesForMany(regionIds)) {
|
|
29449
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
29450
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
29451
|
+
if (w <= 0) continue;
|
|
29452
|
+
outSum.set(e.from, (outSum.get(e.from) ?? 0) + w);
|
|
29453
|
+
if (!inRegion.has(e.to)) continue;
|
|
29454
|
+
const l = out2.get(e.from);
|
|
29455
|
+
if (l) l.push({ to: e.to, weight: w });
|
|
29456
|
+
else out2.set(e.from, [{ to: e.to, weight: w }]);
|
|
29457
|
+
}
|
|
29458
|
+
const boundaryEdges = store.inEdgesForMany(regionIds).filter((e) => allowedTypes.has(e.type) && !inRegion.has(e.from) && (EDGE_WEIGHT[e.type] ?? 1) > 0);
|
|
29459
|
+
const boundarySources = [...new Set(boundaryEdges.map((e) => e.from))];
|
|
29460
|
+
if (boundarySources.length > cap) return null;
|
|
29461
|
+
const boundaryRanks = store.ranksForMany(boundarySources);
|
|
29462
|
+
const boundaryOutSum = /* @__PURE__ */ new Map();
|
|
29463
|
+
for (const e of store.outEdgesForMany(boundarySources)) {
|
|
29464
|
+
if (!allowedTypes.has(e.type)) continue;
|
|
29465
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
29466
|
+
if (w > 0) boundaryOutSum.set(e.from, (boundaryOutSum.get(e.from) ?? 0) + w);
|
|
29467
|
+
}
|
|
29468
|
+
const boundaryInflow = /* @__PURE__ */ new Map();
|
|
29469
|
+
for (const e of boundaryEdges) {
|
|
29470
|
+
const r = boundaryRanks.get(e.from);
|
|
29471
|
+
const os2 = boundaryOutSum.get(e.from);
|
|
29472
|
+
if (r === void 0 || !os2) continue;
|
|
29473
|
+
const w = EDGE_WEIGHT[e.type] ?? 1;
|
|
29474
|
+
boundaryInflow.set(e.to, (boundaryInflow.get(e.to) ?? 0) + r * w / os2);
|
|
29475
|
+
}
|
|
29476
|
+
const globalDangling = Number(store.getMeta("danglingRankShare") ?? 0);
|
|
29477
|
+
let regionDangling = 0;
|
|
29478
|
+
for (const id of regionIds) {
|
|
29479
|
+
if ((outSum.get(id) ?? 0) <= 0) regionDangling += rank0.get(id) ?? 0;
|
|
29480
|
+
}
|
|
29481
|
+
const result = pagerankLocal({
|
|
29482
|
+
regionIds,
|
|
29483
|
+
rank0,
|
|
29484
|
+
out: out2,
|
|
29485
|
+
outSum,
|
|
29486
|
+
boundaryInflow,
|
|
29487
|
+
globalN: liveN,
|
|
29488
|
+
externalDanglingRank: Math.max(0, globalDangling - regionDangling),
|
|
29489
|
+
damping: 0.85,
|
|
29490
|
+
tolerance: 1e-6,
|
|
29491
|
+
maxIterations: 100
|
|
29492
|
+
});
|
|
29493
|
+
let pageRankWritten = 0;
|
|
29494
|
+
store.transaction(() => {
|
|
29495
|
+
for (const [id, score2] of result.scores) {
|
|
29496
|
+
if (Math.abs(score2 - (rank0.get(id) ?? 0)) < 1e-9) continue;
|
|
29497
|
+
store.setPageRank(id, score2);
|
|
29498
|
+
pageRankWritten++;
|
|
29499
|
+
}
|
|
29500
|
+
});
|
|
29501
|
+
const sweep = store.landmarkSweepRows();
|
|
29502
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
29503
|
+
for (const row of sweep) {
|
|
29504
|
+
if (row.label === "Pattern" || row.label === "RootCause") {
|
|
29505
|
+
candidates.set(row.id, result.scores.get(row.id) ?? row.pageRank);
|
|
29506
|
+
}
|
|
29507
|
+
}
|
|
29508
|
+
const want = markLandmarks(candidates, 0.1);
|
|
29509
|
+
for (const row of sweep) if (row.memoryTier === "persistent") want.add(row.id);
|
|
29510
|
+
let landmarkFlips = 0;
|
|
29511
|
+
store.transaction(() => {
|
|
29512
|
+
for (const row of sweep) {
|
|
29513
|
+
if (row.extractionSource === "bko-inferred") continue;
|
|
29514
|
+
const should = want.has(row.id);
|
|
29515
|
+
if (row.isLandmark === should) continue;
|
|
29516
|
+
store.setLandmark(row.id, should);
|
|
29517
|
+
landmarkFlips++;
|
|
29518
|
+
}
|
|
29519
|
+
});
|
|
29520
|
+
return {
|
|
29521
|
+
scoredNodes: regionIds.length,
|
|
29522
|
+
iterations: result.iterations,
|
|
29523
|
+
converged: result.converged,
|
|
29524
|
+
landmarks: want.size,
|
|
29525
|
+
communities: 0,
|
|
29526
|
+
// deferred to the full backstop — see mode docs
|
|
29527
|
+
motifsPromoted: 0,
|
|
29528
|
+
techniques: 0,
|
|
29529
|
+
antipatterns: 0,
|
|
29530
|
+
pageRankWritten,
|
|
29531
|
+
landmarkFlips,
|
|
29532
|
+
mode: "incremental",
|
|
29533
|
+
regionSize: regionIds.length,
|
|
29534
|
+
durationMs: Date.now() - started2
|
|
29535
|
+
};
|
|
29536
|
+
}
|
|
29537
|
+
function runFullRescore(store) {
|
|
29198
29538
|
const started2 = Date.now();
|
|
29199
29539
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
29200
29540
|
const nodeIds = [];
|
|
@@ -29220,10 +29560,12 @@ function runGraphRescore(store) {
|
|
|
29220
29560
|
maxIterations: 100
|
|
29221
29561
|
});
|
|
29222
29562
|
const scored = result.scores.size;
|
|
29563
|
+
let pageRankWritten = 0;
|
|
29223
29564
|
store.transaction(() => {
|
|
29224
29565
|
for (const [id, score2] of result.scores) {
|
|
29225
29566
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
29226
29567
|
store.setPageRank(id, score2);
|
|
29568
|
+
pageRankWritten++;
|
|
29227
29569
|
}
|
|
29228
29570
|
});
|
|
29229
29571
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -29238,11 +29580,13 @@ function runGraphRescore(store) {
|
|
|
29238
29580
|
for (const id of nodeIds) {
|
|
29239
29581
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
29240
29582
|
}
|
|
29583
|
+
let landmarkFlips = 0;
|
|
29241
29584
|
store.transaction(() => {
|
|
29242
29585
|
for (const id of nodeIds) {
|
|
29243
29586
|
const want = allLandmarks.has(id);
|
|
29244
29587
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
29245
29588
|
store.setLandmark(id, want);
|
|
29589
|
+
landmarkFlips++;
|
|
29246
29590
|
}
|
|
29247
29591
|
});
|
|
29248
29592
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -29286,6 +29630,12 @@ function runGraphRescore(store) {
|
|
|
29286
29630
|
motifsPromoted: motifs.promoted,
|
|
29287
29631
|
techniques: motifs.techniques,
|
|
29288
29632
|
antipatterns: motifs.antipatterns,
|
|
29633
|
+
pageRankWritten,
|
|
29634
|
+
landmarkFlips,
|
|
29635
|
+
mode: "full",
|
|
29636
|
+
regionSize: 0,
|
|
29637
|
+
startedAt: started2,
|
|
29638
|
+
danglingRank: result.danglingRank,
|
|
29289
29639
|
durationMs: Date.now() - started2
|
|
29290
29640
|
};
|
|
29291
29641
|
}
|
|
@@ -29323,6 +29673,10 @@ function runNightlyPipeline(store) {
|
|
|
29323
29673
|
skillsInduced: 0,
|
|
29324
29674
|
// skills are cloud-induced now (see above)
|
|
29325
29675
|
skillsRefreshed: 0,
|
|
29676
|
+
pageRankWritten: rescore.pageRankWritten,
|
|
29677
|
+
landmarkFlips: rescore.landmarkFlips,
|
|
29678
|
+
mode: rescore.mode,
|
|
29679
|
+
regionSize: rescore.regionSize,
|
|
29326
29680
|
designResolved: semantic.designResolved,
|
|
29327
29681
|
revisitsCleared: semantic.revisitsCleared,
|
|
29328
29682
|
fixCandidatesSettled: semantic.fixCandidatesSettled,
|
|
@@ -29353,6 +29707,7 @@ function codeAnchorProjection(store, semanticIds, opts = {}) {
|
|
|
29353
29707
|
}
|
|
29354
29708
|
return out2;
|
|
29355
29709
|
}
|
|
29710
|
+
var FULL_RESCORE_EVERY, INCREMENTAL_REGION_MAX_FRACTION, INCREMENTAL_HOPS;
|
|
29356
29711
|
var init_nightly = __esm({
|
|
29357
29712
|
"../../packages/generalizer/src/nightly.ts"() {
|
|
29358
29713
|
"use strict";
|
|
@@ -29360,6 +29715,9 @@ var init_nightly = __esm({
|
|
|
29360
29715
|
init_src4();
|
|
29361
29716
|
init_src5();
|
|
29362
29717
|
init_motifs();
|
|
29718
|
+
FULL_RESCORE_EVERY = 12;
|
|
29719
|
+
INCREMENTAL_REGION_MAX_FRACTION = 0.2;
|
|
29720
|
+
INCREMENTAL_HOPS = 3;
|
|
29363
29721
|
}
|
|
29364
29722
|
});
|
|
29365
29723
|
|
|
@@ -29595,6 +29953,8 @@ var init_relevance_rank = __esm({
|
|
|
29595
29953
|
// ../../packages/generalizer/src/index.ts
|
|
29596
29954
|
var src_exports4 = {};
|
|
29597
29955
|
__export(src_exports4, {
|
|
29956
|
+
FULL_RESCORE_EVERY: () => FULL_RESCORE_EVERY,
|
|
29957
|
+
INCREMENTAL_REGION_MAX_FRACTION: () => INCREMENTAL_REGION_MAX_FRACTION,
|
|
29598
29958
|
anchorByLine: () => anchorByLine,
|
|
29599
29959
|
anchorCandidates: () => anchorCandidates,
|
|
29600
29960
|
anchorProblemToCode: () => anchorProblemToCode,
|
|
@@ -53198,7 +53558,12 @@ function passLedgerPath(configDir) {
|
|
|
53198
53558
|
function appendPassLedger(configDir, kind, durationMs, counts) {
|
|
53199
53559
|
const path2 = passLedgerPath(configDir);
|
|
53200
53560
|
try {
|
|
53201
|
-
const entry = {
|
|
53561
|
+
const entry = {
|
|
53562
|
+
ts: Date.now(),
|
|
53563
|
+
kind,
|
|
53564
|
+
durationMs,
|
|
53565
|
+
counts: { ...counts, rssMB: Math.round(process.memoryUsage().rss / 1048576) }
|
|
53566
|
+
};
|
|
53202
53567
|
appendFileSync2(path2, JSON.stringify(entry) + "\n");
|
|
53203
53568
|
const n = (appendsSinceTrim.get(path2) ?? 0) + 1;
|
|
53204
53569
|
if (n < TRIM_EVERY) {
|
|
@@ -53284,6 +53649,8 @@ function editSurprise(d) {
|
|
|
53284
53649
|
function episodeId(workspaceId2, t) {
|
|
53285
53650
|
return `episode_${digest({ kind: "episode", workspaceId: workspaceId2, t }).slice(0, 16)}`;
|
|
53286
53651
|
}
|
|
53652
|
+
var EPISODE_DELTA_CAP = 200;
|
|
53653
|
+
var EPISODE_PRODUCED_CAP = 500;
|
|
53287
53654
|
function producedEdge(from, to, t) {
|
|
53288
53655
|
return {
|
|
53289
53656
|
id: `edge_${digest({ from, type: "PRODUCED", to }).slice(0, 16)}`,
|
|
@@ -53305,6 +53672,10 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53305
53672
|
if (deltaIsEmpty(delta)) return null;
|
|
53306
53673
|
const id = episodeId(workspaceId2, t);
|
|
53307
53674
|
const surprise = editSurprise(delta);
|
|
53675
|
+
const overCap = delta.symbols.length > EPISODE_DELTA_CAP || delta.symbols.length > EPISODE_PRODUCED_CAP;
|
|
53676
|
+
const changeRank = { changed: 0, added: 1, removed: 2 };
|
|
53677
|
+
const prioritized = overCap ? [...delta.symbols].sort((a, b) => changeRank[a.change] - changeRank[b.change]) : delta.symbols;
|
|
53678
|
+
const storedDelta = prioritized.slice(0, EPISODE_DELTA_CAP);
|
|
53308
53679
|
const node2 = {
|
|
53309
53680
|
id,
|
|
53310
53681
|
label: "Episode",
|
|
@@ -53336,8 +53707,11 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53336
53707
|
edgesAdded: delta.edgesAdded,
|
|
53337
53708
|
edgesRemoved: delta.edgesRemoved,
|
|
53338
53709
|
editSurprise: surprise,
|
|
53339
|
-
// The per-symbol structural delta (signatures before→after, no raw text)
|
|
53340
|
-
|
|
53710
|
+
// The per-symbol structural delta (signatures before→after, no raw text),
|
|
53711
|
+
// capped changed-first; the aggregate counts above always cover the whole
|
|
53712
|
+
// edit, so nothing about the edit's MAGNITUDE is lost to the cap.
|
|
53713
|
+
delta: storedDelta,
|
|
53714
|
+
...delta.symbols.length > EPISODE_DELTA_CAP ? { deltaTotal: delta.symbols.length } : {},
|
|
53341
53715
|
// Causal context from the agent's tool stream, when correlated. Project
|
|
53342
53716
|
// ONLY the scrub-safe fields — opaque session id, enum tool, closed-vocab
|
|
53343
53717
|
// hints. declaredRenames carry identifier names (consumed upstream as
|
|
@@ -53352,11 +53726,14 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53352
53726
|
}
|
|
53353
53727
|
};
|
|
53354
53728
|
store.transaction(() => {
|
|
53729
|
+
let minted = 0;
|
|
53355
53730
|
store.mergeNode(node2);
|
|
53356
|
-
for (const s of
|
|
53731
|
+
for (const s of prioritized) {
|
|
53357
53732
|
if (s.change === "removed") continue;
|
|
53733
|
+
if (minted >= EPISODE_PRODUCED_CAP) break;
|
|
53358
53734
|
if (store.getNode(s.nodeId)) {
|
|
53359
53735
|
store.mergeEdge(producedEdge(id, s.nodeId, t));
|
|
53736
|
+
minted++;
|
|
53360
53737
|
}
|
|
53361
53738
|
if (s.change === "changed") {
|
|
53362
53739
|
const live = store.getNode(s.nodeId);
|
|
@@ -53364,6 +53741,7 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53364
53741
|
const frozenId = `${s.nodeId}@v${prev}`;
|
|
53365
53742
|
if (store.getNode(frozenId)) {
|
|
53366
53743
|
store.mergeEdge(producedEdge(id, frozenId, t));
|
|
53744
|
+
minted++;
|
|
53367
53745
|
}
|
|
53368
53746
|
}
|
|
53369
53747
|
}
|
|
@@ -53371,6 +53749,49 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53371
53749
|
return id;
|
|
53372
53750
|
}
|
|
53373
53751
|
|
|
53752
|
+
// src/episode-retention.ts
|
|
53753
|
+
var EPISODE_RETAIN_DAYS_DEFAULT = 90;
|
|
53754
|
+
var EPISODE_RETIRE_BATCH_DEFAULT = 300;
|
|
53755
|
+
var DAY_MS = 864e5;
|
|
53756
|
+
function numEnv(key, fallback) {
|
|
53757
|
+
const raw2 = process.env[key];
|
|
53758
|
+
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
53759
|
+
const n = Number(raw2);
|
|
53760
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
53761
|
+
}
|
|
53762
|
+
function retireEpisodeProvenance(store, now, opts = {}) {
|
|
53763
|
+
const retainDays = opts.retainDays ?? numEnv("ERRATA_EPISODE_RETAIN_DAYS", EPISODE_RETAIN_DAYS_DEFAULT);
|
|
53764
|
+
const batch = opts.batch ?? EPISODE_RETIRE_BATCH_DEFAULT;
|
|
53765
|
+
const cutoff = now - retainDays * DAY_MS;
|
|
53766
|
+
const due = store.findNodesByLabel("Episode").filter((ep) => {
|
|
53767
|
+
if (ep.attrs["provenanceRetired"] != null) return false;
|
|
53768
|
+
const ts = ep.attrs["ts"] ?? ep.createdAt;
|
|
53769
|
+
return ts < cutoff;
|
|
53770
|
+
}).sort((a, b) => (a.attrs["ts"] ?? a.createdAt) - (b.attrs["ts"] ?? b.createdAt)).slice(0, batch);
|
|
53771
|
+
let edgesClosed = 0;
|
|
53772
|
+
let deltasDropped = 0;
|
|
53773
|
+
for (const ep of due) {
|
|
53774
|
+
store.transaction(() => {
|
|
53775
|
+
for (const e of store.outEdges(ep.id, ["PRODUCED"])) {
|
|
53776
|
+
store.closeEdge(e.id, now);
|
|
53777
|
+
edgesClosed++;
|
|
53778
|
+
}
|
|
53779
|
+
const { delta, ...rest2 } = ep.attrs;
|
|
53780
|
+
if (Array.isArray(delta)) deltasDropped++;
|
|
53781
|
+
store.updateNode(ep.id, {
|
|
53782
|
+
attrs: {
|
|
53783
|
+
...rest2,
|
|
53784
|
+
provenanceRetired: now,
|
|
53785
|
+
// How much detail the compaction elided — the aggregate counts
|
|
53786
|
+
// (symbolsAdded/Changed/Removed) still describe the edit itself.
|
|
53787
|
+
...Array.isArray(delta) ? { deltaDropped: delta.length } : {}
|
|
53788
|
+
}
|
|
53789
|
+
});
|
|
53790
|
+
});
|
|
53791
|
+
}
|
|
53792
|
+
return { retired: due.length, edgesClosed, deltasDropped };
|
|
53793
|
+
}
|
|
53794
|
+
|
|
53374
53795
|
// src/pass-worker-client.ts
|
|
53375
53796
|
import { Worker } from "node:worker_threads";
|
|
53376
53797
|
var PassWorker = class {
|
|
@@ -54991,7 +55412,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
54991
55412
|
}
|
|
54992
55413
|
|
|
54993
55414
|
// src/engine.ts
|
|
54994
|
-
var DAEMON_VERSION = true ? "2.0.2-dev.
|
|
55415
|
+
var DAEMON_VERSION = true ? "2.0.2-dev.685" : "2.0.0-alpha.0";
|
|
54995
55416
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
54996
55417
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
54997
55418
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -56593,7 +57014,14 @@ function createWorkspaceEngine(opts) {
|
|
|
56593
57014
|
scoredNodes: report.scoredNodes,
|
|
56594
57015
|
landmarks: report.landmarks,
|
|
56595
57016
|
communities: report.communities,
|
|
56596
|
-
motifsPromoted: report.motifsPromoted
|
|
57017
|
+
motifsPromoted: report.motifsPromoted,
|
|
57018
|
+
// Consumer-visible effect (momentum-gate calibration reads these):
|
|
57019
|
+
// a run of zero-flip rows is the gate learning to fire less.
|
|
57020
|
+
pageRankWritten: report.pageRankWritten,
|
|
57021
|
+
landmarkFlips: report.landmarkFlips,
|
|
57022
|
+
// HZ-incremental-pagerank: 1 = full backstop, 0 = dirty-region solve.
|
|
57023
|
+
fullPass: report.mode === "full" ? 1 : 0,
|
|
57024
|
+
regionSize: report.regionSize
|
|
56597
57025
|
});
|
|
56598
57026
|
appendPassLedger(paths.configDir, "semantic-maintenance", report.durationMs, {
|
|
56599
57027
|
revisitsCleared: report.revisitsCleared,
|
|
@@ -56601,6 +57029,26 @@ function createWorkspaceEngine(opts) {
|
|
|
56601
57029
|
designResolved: report.designResolved,
|
|
56602
57030
|
claimsEvaluated: report.claimsEvaluated
|
|
56603
57031
|
});
|
|
57032
|
+
try {
|
|
57033
|
+
const t0 = Date.now();
|
|
57034
|
+
const ret = retireEpisodeProvenance(store, t0);
|
|
57035
|
+
if (ret.retired > 0) {
|
|
57036
|
+
appendPassLedger(paths.configDir, "provenance-retire", Date.now() - t0, {
|
|
57037
|
+
episodes: ret.retired,
|
|
57038
|
+
edgesClosed: ret.edgesClosed,
|
|
57039
|
+
deltasDropped: ret.deltasDropped
|
|
57040
|
+
});
|
|
57041
|
+
}
|
|
57042
|
+
} catch (err2) {
|
|
57043
|
+
console.warn("[errata] provenance retention failed:", err2.message?.slice(0, 120));
|
|
57044
|
+
}
|
|
57045
|
+
try {
|
|
57046
|
+
const cp = store.checkpointWal?.();
|
|
57047
|
+
if (cp && cp.busy === 0 && cp.log > 25e3) {
|
|
57048
|
+
console.log(`[errata] wal checkpoint: truncated ${cp.log} frame(s)`);
|
|
57049
|
+
}
|
|
57050
|
+
} catch {
|
|
57051
|
+
}
|
|
56604
57052
|
return report;
|
|
56605
57053
|
},
|
|
56606
57054
|
async embedSettled() {
|
|
@@ -58623,9 +59071,11 @@ async function startMultiDaemon(opts = {}) {
|
|
|
58623
59071
|
}
|
|
58624
59072
|
return out2;
|
|
58625
59073
|
},
|
|
58626
|
-
async
|
|
59074
|
+
async nightlyFor(ids) {
|
|
59075
|
+
const want = new Set(ids);
|
|
58627
59076
|
const out2 = /* @__PURE__ */ new Map();
|
|
58628
59077
|
for (const r of records) {
|
|
59078
|
+
if (!want.has(r.id)) continue;
|
|
58629
59079
|
const done = markPass(`nightly:${r.entry.name}`);
|
|
58630
59080
|
try {
|
|
58631
59081
|
out2.set(r.id, await r.engine.nightly());
|
|
@@ -58637,6 +59087,9 @@ async function startMultiDaemon(opts = {}) {
|
|
|
58637
59087
|
}
|
|
58638
59088
|
return out2;
|
|
58639
59089
|
},
|
|
59090
|
+
async nightlyAll() {
|
|
59091
|
+
return daemon.nightlyFor(records.map((r) => r.id));
|
|
59092
|
+
},
|
|
58640
59093
|
async percolateAll() {
|
|
58641
59094
|
const ts = Date.now();
|
|
58642
59095
|
const out2 = /* @__PURE__ */ new Map();
|
|
@@ -59779,16 +60232,28 @@ function formatWhoami(response, localProfile) {
|
|
|
59779
60232
|
}
|
|
59780
60233
|
|
|
59781
60234
|
// src/consolidation-trigger.ts
|
|
60235
|
+
function effectiveMomentumThreshold(policy, liveNodes) {
|
|
60236
|
+
return Math.max(policy.momentumThreshold, Math.ceil(liveNodes * policy.momentumRatio));
|
|
60237
|
+
}
|
|
60238
|
+
function nextMomentumThreshold(args2) {
|
|
60239
|
+
const { current, consumed, landmarkFlips, floor, cap } = args2;
|
|
60240
|
+
const next = landmarkFlips > 0 ? Math.min(current, Math.round(consumed / 2)) : Math.max(current, Math.round(consumed * 1.5));
|
|
60241
|
+
return Math.max(floor, Math.min(cap, next));
|
|
60242
|
+
}
|
|
59782
60243
|
var DEFAULT_CONSOLIDATION_POLICY = {
|
|
59783
60244
|
baseFloorMs: 6e4,
|
|
59784
60245
|
dutyFactor: 20,
|
|
59785
60246
|
// ≤5% worst-case duty at any graph size (crater guard)
|
|
59786
60247
|
momentumThreshold: 500,
|
|
59787
|
-
//
|
|
60248
|
+
// POLICY freshness bound — the gate never demands less
|
|
60249
|
+
momentumRatio: 0.01,
|
|
60250
|
+
// cold-start PRIOR only; calibration replaces it after the first pass
|
|
60251
|
+
momentumRatioCap: 0.05,
|
|
60252
|
+
// POLICY staleness bound — refresh by 5% churn no matter what
|
|
59788
60253
|
quiescenceMs: 6e4
|
|
59789
60254
|
// sim preferred 60s over 90s (fresher, esp. heavy-code)
|
|
59790
60255
|
};
|
|
59791
|
-
function
|
|
60256
|
+
function numEnv2(env2, key, fallback) {
|
|
59792
60257
|
const raw2 = env2[key];
|
|
59793
60258
|
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
59794
60259
|
const n = Number(raw2);
|
|
@@ -59796,21 +60261,17 @@ function numEnv(env2, key, fallback) {
|
|
|
59796
60261
|
}
|
|
59797
60262
|
function consolidationPolicyFromEnv(env2 = process.env) {
|
|
59798
60263
|
return {
|
|
59799
|
-
baseFloorMs:
|
|
59800
|
-
dutyFactor:
|
|
59801
|
-
momentumThreshold:
|
|
59802
|
-
|
|
60264
|
+
baseFloorMs: numEnv2(env2, "ERRATA_CONSOLIDATE_BASE_FLOOR_MS", DEFAULT_CONSOLIDATION_POLICY.baseFloorMs),
|
|
60265
|
+
dutyFactor: numEnv2(env2, "ERRATA_CONSOLIDATE_DUTY_FACTOR", DEFAULT_CONSOLIDATION_POLICY.dutyFactor),
|
|
60266
|
+
momentumThreshold: numEnv2(env2, "ERRATA_CONSOLIDATE_MOMENTUM", DEFAULT_CONSOLIDATION_POLICY.momentumThreshold),
|
|
60267
|
+
momentumRatio: numEnv2(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO", DEFAULT_CONSOLIDATION_POLICY.momentumRatio),
|
|
60268
|
+
momentumRatioCap: numEnv2(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO_CAP", DEFAULT_CONSOLIDATION_POLICY.momentumRatioCap),
|
|
60269
|
+
quiescenceMs: numEnv2(env2, "ERRATA_CONSOLIDATE_QUIESCENCE_MS", DEFAULT_CONSOLIDATION_POLICY.quiescenceMs)
|
|
59803
60270
|
};
|
|
59804
60271
|
}
|
|
59805
60272
|
function consolidationGapMs(lastPassMs, policy) {
|
|
59806
60273
|
return Math.max(policy.baseFloorMs, lastPassMs * policy.dutyFactor);
|
|
59807
60274
|
}
|
|
59808
|
-
function shouldConsolidate(args2) {
|
|
59809
|
-
const { now, state, policy, force = false } = args2;
|
|
59810
|
-
if (state.momentum < policy.momentumThreshold) return false;
|
|
59811
|
-
if (force) return true;
|
|
59812
|
-
return now - state.lastConsolidationEnd >= consolidationGapMs(state.lastPassMs, policy);
|
|
59813
|
-
}
|
|
59814
60275
|
|
|
59815
60276
|
// src/cli.ts
|
|
59816
60277
|
var exitCleanOnEpipe = (err2) => {
|
|
@@ -62170,10 +62631,10 @@ async function cmdDash(args2) {
|
|
|
62170
62631
|
let nightlyCount = 0;
|
|
62171
62632
|
const hotspotScanMark = /* @__PURE__ */ new Map();
|
|
62172
62633
|
const HOTSPOT_FANIN = 20;
|
|
62173
|
-
const runNightlyPass = async () => {
|
|
62634
|
+
const runNightlyPass = async (dueIds) => {
|
|
62174
62635
|
const yieldToLoop2 = () => new Promise((resolve6) => setImmediate(resolve6));
|
|
62175
62636
|
const fullReclusterPass = nightlyCount++ % FULL_RECLUSTER_EVERY === 0;
|
|
62176
|
-
const m = await handle2.nightlyAll();
|
|
62637
|
+
const m = dueIds ? await handle2.nightlyFor(dueIds) : await handle2.nightlyAll();
|
|
62177
62638
|
let skillsLearned = 0;
|
|
62178
62639
|
for (const [id, n] of m) {
|
|
62179
62640
|
skillsLearned += n.skillsInduced;
|
|
@@ -62317,36 +62778,57 @@ async function cmdDash(args2) {
|
|
|
62317
62778
|
}
|
|
62318
62779
|
}
|
|
62319
62780
|
maybeFlushDigests();
|
|
62781
|
+
return m;
|
|
62320
62782
|
};
|
|
62321
62783
|
const CONSOLIDATE_BOOT_GRACE_S = 45;
|
|
62322
62784
|
const consolidationPolicy = consolidationPolicyFromEnv();
|
|
62323
62785
|
const consolidationState = { momentum: 0, lastConsolidationEnd: 0, lastPassMs: 0 };
|
|
62324
|
-
const
|
|
62325
|
-
|
|
62786
|
+
const consolidatedAtByRecord = /* @__PURE__ */ new Map();
|
|
62787
|
+
const calibratedThreshold = /* @__PURE__ */ new Map();
|
|
62788
|
+
const thresholdFor = (r) => calibratedThreshold.get(r.id) ?? effectiveMomentumThreshold(consolidationPolicy, r.engine.store.nodeCount());
|
|
62789
|
+
const dueRecords = () => handle2.records.map((r) => ({
|
|
62790
|
+
id: r.id,
|
|
62791
|
+
momentum: r.engine.store.mutationCount() - (consolidatedAtByRecord.get(r.id) ?? 0),
|
|
62792
|
+
threshold: thresholdFor(r)
|
|
62793
|
+
})).filter((x) => x.momentum >= x.threshold).map(({ id, momentum }) => ({ id, momentum }));
|
|
62326
62794
|
let consolidating = false;
|
|
62327
62795
|
const maybeConsolidate = async (force) => {
|
|
62328
62796
|
if (consolidating) return;
|
|
62329
62797
|
if (process.uptime() < CONSOLIDATE_BOOT_GRACE_S) return;
|
|
62330
|
-
|
|
62331
|
-
|
|
62332
|
-
|
|
62333
|
-
|
|
62334
|
-
policy: consolidationPolicy,
|
|
62335
|
-
force
|
|
62336
|
-
})) {
|
|
62798
|
+
const due = dueRecords();
|
|
62799
|
+
consolidationState.momentum = due.length;
|
|
62800
|
+
if (due.length === 0) return;
|
|
62801
|
+
if (!force && Date.now() - consolidationState.lastConsolidationEnd < consolidationGapMs(consolidationState.lastPassMs, consolidationPolicy)) {
|
|
62337
62802
|
return;
|
|
62338
62803
|
}
|
|
62339
62804
|
consolidating = true;
|
|
62340
62805
|
const startedAt = Date.now();
|
|
62341
62806
|
try {
|
|
62342
|
-
await runNightlyPass();
|
|
62807
|
+
const reports = await runNightlyPass(due.map((d) => d.id));
|
|
62808
|
+
for (const { id, momentum } of due) {
|
|
62809
|
+
const r = handle2.records.find((x) => x.id === id);
|
|
62810
|
+
const report = reports?.get(id);
|
|
62811
|
+
if (!r || !report) continue;
|
|
62812
|
+
calibratedThreshold.set(
|
|
62813
|
+
id,
|
|
62814
|
+
nextMomentumThreshold({
|
|
62815
|
+
current: thresholdFor(r),
|
|
62816
|
+
consumed: momentum,
|
|
62817
|
+
landmarkFlips: report.landmarkFlips,
|
|
62818
|
+
floor: consolidationPolicy.momentumThreshold,
|
|
62819
|
+
cap: Math.ceil(r.engine.store.nodeCount() * consolidationPolicy.momentumRatioCap)
|
|
62820
|
+
})
|
|
62821
|
+
);
|
|
62822
|
+
}
|
|
62343
62823
|
} catch (err2) {
|
|
62344
62824
|
console.warn(`[consolidate] pass failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
62345
62825
|
} finally {
|
|
62346
62826
|
consolidationState.lastPassMs = Date.now() - startedAt;
|
|
62347
62827
|
consolidationState.lastConsolidationEnd = Date.now();
|
|
62348
|
-
|
|
62349
|
-
|
|
62828
|
+
for (const { id } of due) {
|
|
62829
|
+
const r = handle2.records.find((x) => x.id === id);
|
|
62830
|
+
if (r) consolidatedAtByRecord.set(id, r.engine.store.mutationCount());
|
|
62831
|
+
}
|
|
62350
62832
|
consolidating = false;
|
|
62351
62833
|
}
|
|
62352
62834
|
};
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -266,6 +266,10 @@ var init_castalia = __esm({
|
|
|
266
266
|
// git topology, not signal-flow
|
|
267
267
|
"AUTHORED_BY",
|
|
268
268
|
// git authorship, not signal-flow
|
|
269
|
+
"PRODUCED",
|
|
270
|
+
// edit-episode provenance (Episode→symbol), not signal-flow — the family
|
|
271
|
+
// above was excluded together but this member slipped the net (LC-produced-pagerank);
|
|
272
|
+
// at 43% of all live edges it was the largest single edge population flowing rank
|
|
269
273
|
"CONTRIBUTED",
|
|
270
274
|
// agent attribution (Agent → knowledge), not signal-flow
|
|
271
275
|
"SUPERSEDES",
|
|
@@ -15436,6 +15440,7 @@ function openDatabase(path) {
|
|
|
15436
15440
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15437
15441
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
15438
15442
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
15443
|
+
db.exec("PRAGMA journal_size_limit = 67108864");
|
|
15439
15444
|
} catch {
|
|
15440
15445
|
}
|
|
15441
15446
|
}
|
|
@@ -24903,6 +24908,96 @@ var SqliteGraphStore = class {
|
|
|
24903
24908
|
const rows = this.db.prepare("SELECT from_id, to_id, type FROM edges WHERE valid_to IS NULL").all();
|
|
24904
24909
|
return rows.map((r) => ({ from: r.from_id, to: r.to_id, type: r.type }));
|
|
24905
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
|
+
}
|
|
24906
25001
|
reviveReobserved(relPaths, workspaceId, since) {
|
|
24907
25002
|
let revived = 0;
|
|
24908
25003
|
const CHUNK = 400;
|
|
@@ -25926,7 +26021,7 @@ function pagerank(input) {
|
|
|
25926
26021
|
const ids = input.nodeIds;
|
|
25927
26022
|
const n = ids.length;
|
|
25928
26023
|
if (n === 0) {
|
|
25929
|
-
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true };
|
|
26024
|
+
return { scores: /* @__PURE__ */ new Map(), iterations: 0, converged: true, danglingRank: 0 };
|
|
25930
26025
|
}
|
|
25931
26026
|
const index = /* @__PURE__ */ new Map();
|
|
25932
26027
|
for (let i2 = 0; i2 < n; i2++) index.set(ids[i2], i2);
|
|
@@ -25998,8 +26093,12 @@ function pagerank(input) {
|
|
|
25998
26093
|
}
|
|
25999
26094
|
}
|
|
26000
26095
|
const scores = /* @__PURE__ */ new Map();
|
|
26001
|
-
|
|
26002
|
-
|
|
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 };
|
|
26003
26102
|
}
|
|
26004
26103
|
function markLandmarks(scores, percentile = 0.1, filter) {
|
|
26005
26104
|
const entries = [...scores.entries()];
|
|
@@ -26009,7 +26108,7 @@ function markLandmarks(scores, percentile = 0.1, filter) {
|
|
|
26009
26108
|
}
|
|
26010
26109
|
}
|
|
26011
26110
|
if (entries.length === 0) return /* @__PURE__ */ new Set();
|
|
26012
|
-
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));
|
|
26013
26112
|
const cutoff = Math.max(1, Math.floor(entries.length * percentile));
|
|
26014
26113
|
const out2 = /* @__PURE__ */ new Set();
|
|
26015
26114
|
for (let i2 = 0; i2 < cutoff; i2++) {
|
|
@@ -26247,6 +26346,87 @@ function motifDecision(input) {
|
|
|
26247
26346
|
};
|
|
26248
26347
|
}
|
|
26249
26348
|
|
|
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
|
+
}
|
|
26429
|
+
|
|
26250
26430
|
// ../../packages/local-graph/src/triage.ts
|
|
26251
26431
|
init_src();
|
|
26252
26432
|
init_src();
|
|
@@ -26394,7 +26574,164 @@ function promoteMotifs(store2, t) {
|
|
|
26394
26574
|
}
|
|
26395
26575
|
|
|
26396
26576
|
// ../../packages/generalizer/src/nightly.ts
|
|
26397
|
-
|
|
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) {
|
|
26398
26735
|
const started = Date.now();
|
|
26399
26736
|
const allowedTypes = new Set(pagerankEdgeTypes());
|
|
26400
26737
|
const nodeIds = [];
|
|
@@ -26420,10 +26757,12 @@ function runGraphRescore(store2) {
|
|
|
26420
26757
|
maxIterations: 100
|
|
26421
26758
|
});
|
|
26422
26759
|
const scored = result.scores.size;
|
|
26760
|
+
let pageRankWritten = 0;
|
|
26423
26761
|
store2.transaction(() => {
|
|
26424
26762
|
for (const [id, score2] of result.scores) {
|
|
26425
26763
|
if (Math.abs(score2 - (meta3.get(id)?.pageRank ?? 0)) < 1e-9) continue;
|
|
26426
26764
|
store2.setPageRank(id, score2);
|
|
26765
|
+
pageRankWritten++;
|
|
26427
26766
|
}
|
|
26428
26767
|
});
|
|
26429
26768
|
const landmarkCandidates = /* @__PURE__ */ new Map();
|
|
@@ -26438,11 +26777,13 @@ function runGraphRescore(store2) {
|
|
|
26438
26777
|
for (const id of nodeIds) {
|
|
26439
26778
|
if (meta3.get(id)?.memoryTier === "persistent") allLandmarks.add(id);
|
|
26440
26779
|
}
|
|
26780
|
+
let landmarkFlips = 0;
|
|
26441
26781
|
store2.transaction(() => {
|
|
26442
26782
|
for (const id of nodeIds) {
|
|
26443
26783
|
const want = allLandmarks.has(id);
|
|
26444
26784
|
if ((meta3.get(id)?.isLandmark ?? false) === want) continue;
|
|
26445
26785
|
store2.setLandmark(id, want);
|
|
26786
|
+
landmarkFlips++;
|
|
26446
26787
|
}
|
|
26447
26788
|
});
|
|
26448
26789
|
const MOTIF_LABELS = /* @__PURE__ */ new Set(["Pattern", "Technique", "AntiPattern"]);
|
|
@@ -26486,6 +26827,12 @@ function runGraphRescore(store2) {
|
|
|
26486
26827
|
motifsPromoted: motifs.promoted,
|
|
26487
26828
|
techniques: motifs.techniques,
|
|
26488
26829
|
antipatterns: motifs.antipatterns,
|
|
26830
|
+
pageRankWritten,
|
|
26831
|
+
landmarkFlips,
|
|
26832
|
+
mode: "full",
|
|
26833
|
+
regionSize: 0,
|
|
26834
|
+
startedAt: started,
|
|
26835
|
+
danglingRank: result.danglingRank,
|
|
26489
26836
|
durationMs: Date.now() - started
|
|
26490
26837
|
};
|
|
26491
26838
|
}
|
|
@@ -26523,6 +26870,10 @@ function runNightlyPipeline(store2) {
|
|
|
26523
26870
|
skillsInduced: 0,
|
|
26524
26871
|
// skills are cloud-induced now (see above)
|
|
26525
26872
|
skillsRefreshed: 0,
|
|
26873
|
+
pageRankWritten: rescore.pageRankWritten,
|
|
26874
|
+
landmarkFlips: rescore.landmarkFlips,
|
|
26875
|
+
mode: rescore.mode,
|
|
26876
|
+
regionSize: rescore.regionSize,
|
|
26526
26877
|
designResolved: semantic.designResolved,
|
|
26527
26878
|
revisitsCleared: semantic.revisitsCleared,
|
|
26528
26879
|
fixCandidatesSettled: semantic.fixCandidatesSettled,
|