@inerrata-corporation/errata 2.0.2-dev.650 → 2.0.2-dev.682
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 +514 -37
- 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,
|
|
@@ -53284,6 +53644,8 @@ function editSurprise(d) {
|
|
|
53284
53644
|
function episodeId(workspaceId2, t) {
|
|
53285
53645
|
return `episode_${digest({ kind: "episode", workspaceId: workspaceId2, t }).slice(0, 16)}`;
|
|
53286
53646
|
}
|
|
53647
|
+
var EPISODE_DELTA_CAP = 200;
|
|
53648
|
+
var EPISODE_PRODUCED_CAP = 500;
|
|
53287
53649
|
function producedEdge(from, to, t) {
|
|
53288
53650
|
return {
|
|
53289
53651
|
id: `edge_${digest({ from, type: "PRODUCED", to }).slice(0, 16)}`,
|
|
@@ -53305,6 +53667,10 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53305
53667
|
if (deltaIsEmpty(delta)) return null;
|
|
53306
53668
|
const id = episodeId(workspaceId2, t);
|
|
53307
53669
|
const surprise = editSurprise(delta);
|
|
53670
|
+
const overCap = delta.symbols.length > EPISODE_DELTA_CAP || delta.symbols.length > EPISODE_PRODUCED_CAP;
|
|
53671
|
+
const changeRank = { changed: 0, added: 1, removed: 2 };
|
|
53672
|
+
const prioritized = overCap ? [...delta.symbols].sort((a, b) => changeRank[a.change] - changeRank[b.change]) : delta.symbols;
|
|
53673
|
+
const storedDelta = prioritized.slice(0, EPISODE_DELTA_CAP);
|
|
53308
53674
|
const node2 = {
|
|
53309
53675
|
id,
|
|
53310
53676
|
label: "Episode",
|
|
@@ -53336,8 +53702,11 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53336
53702
|
edgesAdded: delta.edgesAdded,
|
|
53337
53703
|
edgesRemoved: delta.edgesRemoved,
|
|
53338
53704
|
editSurprise: surprise,
|
|
53339
|
-
// The per-symbol structural delta (signatures before→after, no raw text)
|
|
53340
|
-
|
|
53705
|
+
// The per-symbol structural delta (signatures before→after, no raw text),
|
|
53706
|
+
// capped changed-first; the aggregate counts above always cover the whole
|
|
53707
|
+
// edit, so nothing about the edit's MAGNITUDE is lost to the cap.
|
|
53708
|
+
delta: storedDelta,
|
|
53709
|
+
...delta.symbols.length > EPISODE_DELTA_CAP ? { deltaTotal: delta.symbols.length } : {},
|
|
53341
53710
|
// Causal context from the agent's tool stream, when correlated. Project
|
|
53342
53711
|
// ONLY the scrub-safe fields — opaque session id, enum tool, closed-vocab
|
|
53343
53712
|
// hints. declaredRenames carry identifier names (consumed upstream as
|
|
@@ -53352,11 +53721,14 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53352
53721
|
}
|
|
53353
53722
|
};
|
|
53354
53723
|
store.transaction(() => {
|
|
53724
|
+
let minted = 0;
|
|
53355
53725
|
store.mergeNode(node2);
|
|
53356
|
-
for (const s of
|
|
53726
|
+
for (const s of prioritized) {
|
|
53357
53727
|
if (s.change === "removed") continue;
|
|
53728
|
+
if (minted >= EPISODE_PRODUCED_CAP) break;
|
|
53358
53729
|
if (store.getNode(s.nodeId)) {
|
|
53359
53730
|
store.mergeEdge(producedEdge(id, s.nodeId, t));
|
|
53731
|
+
minted++;
|
|
53360
53732
|
}
|
|
53361
53733
|
if (s.change === "changed") {
|
|
53362
53734
|
const live = store.getNode(s.nodeId);
|
|
@@ -53364,6 +53736,7 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53364
53736
|
const frozenId = `${s.nodeId}@v${prev}`;
|
|
53365
53737
|
if (store.getNode(frozenId)) {
|
|
53366
53738
|
store.mergeEdge(producedEdge(id, frozenId, t));
|
|
53739
|
+
minted++;
|
|
53367
53740
|
}
|
|
53368
53741
|
}
|
|
53369
53742
|
}
|
|
@@ -53371,6 +53744,49 @@ function recordEpisode(store, workspaceId2, t, delta, causal) {
|
|
|
53371
53744
|
return id;
|
|
53372
53745
|
}
|
|
53373
53746
|
|
|
53747
|
+
// src/episode-retention.ts
|
|
53748
|
+
var EPISODE_RETAIN_DAYS_DEFAULT = 90;
|
|
53749
|
+
var EPISODE_RETIRE_BATCH_DEFAULT = 300;
|
|
53750
|
+
var DAY_MS = 864e5;
|
|
53751
|
+
function numEnv(key, fallback) {
|
|
53752
|
+
const raw2 = process.env[key];
|
|
53753
|
+
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
53754
|
+
const n = Number(raw2);
|
|
53755
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
53756
|
+
}
|
|
53757
|
+
function retireEpisodeProvenance(store, now, opts = {}) {
|
|
53758
|
+
const retainDays = opts.retainDays ?? numEnv("ERRATA_EPISODE_RETAIN_DAYS", EPISODE_RETAIN_DAYS_DEFAULT);
|
|
53759
|
+
const batch = opts.batch ?? EPISODE_RETIRE_BATCH_DEFAULT;
|
|
53760
|
+
const cutoff = now - retainDays * DAY_MS;
|
|
53761
|
+
const due = store.findNodesByLabel("Episode").filter((ep) => {
|
|
53762
|
+
if (ep.attrs["provenanceRetired"] != null) return false;
|
|
53763
|
+
const ts = ep.attrs["ts"] ?? ep.createdAt;
|
|
53764
|
+
return ts < cutoff;
|
|
53765
|
+
}).sort((a, b) => (a.attrs["ts"] ?? a.createdAt) - (b.attrs["ts"] ?? b.createdAt)).slice(0, batch);
|
|
53766
|
+
let edgesClosed = 0;
|
|
53767
|
+
let deltasDropped = 0;
|
|
53768
|
+
for (const ep of due) {
|
|
53769
|
+
store.transaction(() => {
|
|
53770
|
+
for (const e of store.outEdges(ep.id, ["PRODUCED"])) {
|
|
53771
|
+
store.closeEdge(e.id, now);
|
|
53772
|
+
edgesClosed++;
|
|
53773
|
+
}
|
|
53774
|
+
const { delta, ...rest2 } = ep.attrs;
|
|
53775
|
+
if (Array.isArray(delta)) deltasDropped++;
|
|
53776
|
+
store.updateNode(ep.id, {
|
|
53777
|
+
attrs: {
|
|
53778
|
+
...rest2,
|
|
53779
|
+
provenanceRetired: now,
|
|
53780
|
+
// How much detail the compaction elided — the aggregate counts
|
|
53781
|
+
// (symbolsAdded/Changed/Removed) still describe the edit itself.
|
|
53782
|
+
...Array.isArray(delta) ? { deltaDropped: delta.length } : {}
|
|
53783
|
+
}
|
|
53784
|
+
});
|
|
53785
|
+
});
|
|
53786
|
+
}
|
|
53787
|
+
return { retired: due.length, edgesClosed, deltasDropped };
|
|
53788
|
+
}
|
|
53789
|
+
|
|
53374
53790
|
// src/pass-worker-client.ts
|
|
53375
53791
|
import { Worker } from "node:worker_threads";
|
|
53376
53792
|
var PassWorker = class {
|
|
@@ -54991,7 +55407,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
54991
55407
|
}
|
|
54992
55408
|
|
|
54993
55409
|
// src/engine.ts
|
|
54994
|
-
var DAEMON_VERSION = true ? "2.0.2-dev.
|
|
55410
|
+
var DAEMON_VERSION = true ? "2.0.2-dev.682" : "2.0.0-alpha.0";
|
|
54995
55411
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
54996
55412
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
54997
55413
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -56593,7 +57009,14 @@ function createWorkspaceEngine(opts) {
|
|
|
56593
57009
|
scoredNodes: report.scoredNodes,
|
|
56594
57010
|
landmarks: report.landmarks,
|
|
56595
57011
|
communities: report.communities,
|
|
56596
|
-
motifsPromoted: report.motifsPromoted
|
|
57012
|
+
motifsPromoted: report.motifsPromoted,
|
|
57013
|
+
// Consumer-visible effect (momentum-gate calibration reads these):
|
|
57014
|
+
// a run of zero-flip rows is the gate learning to fire less.
|
|
57015
|
+
pageRankWritten: report.pageRankWritten,
|
|
57016
|
+
landmarkFlips: report.landmarkFlips,
|
|
57017
|
+
// HZ-incremental-pagerank: 1 = full backstop, 0 = dirty-region solve.
|
|
57018
|
+
fullPass: report.mode === "full" ? 1 : 0,
|
|
57019
|
+
regionSize: report.regionSize
|
|
56597
57020
|
});
|
|
56598
57021
|
appendPassLedger(paths.configDir, "semantic-maintenance", report.durationMs, {
|
|
56599
57022
|
revisitsCleared: report.revisitsCleared,
|
|
@@ -56601,6 +57024,26 @@ function createWorkspaceEngine(opts) {
|
|
|
56601
57024
|
designResolved: report.designResolved,
|
|
56602
57025
|
claimsEvaluated: report.claimsEvaluated
|
|
56603
57026
|
});
|
|
57027
|
+
try {
|
|
57028
|
+
const t0 = Date.now();
|
|
57029
|
+
const ret = retireEpisodeProvenance(store, t0);
|
|
57030
|
+
if (ret.retired > 0) {
|
|
57031
|
+
appendPassLedger(paths.configDir, "provenance-retire", Date.now() - t0, {
|
|
57032
|
+
episodes: ret.retired,
|
|
57033
|
+
edgesClosed: ret.edgesClosed,
|
|
57034
|
+
deltasDropped: ret.deltasDropped
|
|
57035
|
+
});
|
|
57036
|
+
}
|
|
57037
|
+
} catch (err2) {
|
|
57038
|
+
console.warn("[errata] provenance retention failed:", err2.message?.slice(0, 120));
|
|
57039
|
+
}
|
|
57040
|
+
try {
|
|
57041
|
+
const cp = store.checkpointWal?.();
|
|
57042
|
+
if (cp && cp.busy === 0 && cp.log > 25e3) {
|
|
57043
|
+
console.log(`[errata] wal checkpoint: truncated ${cp.log} frame(s)`);
|
|
57044
|
+
}
|
|
57045
|
+
} catch {
|
|
57046
|
+
}
|
|
56604
57047
|
return report;
|
|
56605
57048
|
},
|
|
56606
57049
|
async embedSettled() {
|
|
@@ -58623,9 +59066,11 @@ async function startMultiDaemon(opts = {}) {
|
|
|
58623
59066
|
}
|
|
58624
59067
|
return out2;
|
|
58625
59068
|
},
|
|
58626
|
-
async
|
|
59069
|
+
async nightlyFor(ids) {
|
|
59070
|
+
const want = new Set(ids);
|
|
58627
59071
|
const out2 = /* @__PURE__ */ new Map();
|
|
58628
59072
|
for (const r of records) {
|
|
59073
|
+
if (!want.has(r.id)) continue;
|
|
58629
59074
|
const done = markPass(`nightly:${r.entry.name}`);
|
|
58630
59075
|
try {
|
|
58631
59076
|
out2.set(r.id, await r.engine.nightly());
|
|
@@ -58637,6 +59082,9 @@ async function startMultiDaemon(opts = {}) {
|
|
|
58637
59082
|
}
|
|
58638
59083
|
return out2;
|
|
58639
59084
|
},
|
|
59085
|
+
async nightlyAll() {
|
|
59086
|
+
return daemon.nightlyFor(records.map((r) => r.id));
|
|
59087
|
+
},
|
|
58640
59088
|
async percolateAll() {
|
|
58641
59089
|
const ts = Date.now();
|
|
58642
59090
|
const out2 = /* @__PURE__ */ new Map();
|
|
@@ -59779,16 +60227,28 @@ function formatWhoami(response, localProfile) {
|
|
|
59779
60227
|
}
|
|
59780
60228
|
|
|
59781
60229
|
// src/consolidation-trigger.ts
|
|
60230
|
+
function effectiveMomentumThreshold(policy, liveNodes) {
|
|
60231
|
+
return Math.max(policy.momentumThreshold, Math.ceil(liveNodes * policy.momentumRatio));
|
|
60232
|
+
}
|
|
60233
|
+
function nextMomentumThreshold(args2) {
|
|
60234
|
+
const { current, consumed, landmarkFlips, floor, cap } = args2;
|
|
60235
|
+
const next = landmarkFlips > 0 ? Math.min(current, Math.round(consumed / 2)) : Math.max(current, Math.round(consumed * 1.5));
|
|
60236
|
+
return Math.max(floor, Math.min(cap, next));
|
|
60237
|
+
}
|
|
59782
60238
|
var DEFAULT_CONSOLIDATION_POLICY = {
|
|
59783
60239
|
baseFloorMs: 6e4,
|
|
59784
60240
|
dutyFactor: 20,
|
|
59785
60241
|
// ≤5% worst-case duty at any graph size (crater guard)
|
|
59786
60242
|
momentumThreshold: 500,
|
|
59787
|
-
//
|
|
60243
|
+
// POLICY freshness bound — the gate never demands less
|
|
60244
|
+
momentumRatio: 0.01,
|
|
60245
|
+
// cold-start PRIOR only; calibration replaces it after the first pass
|
|
60246
|
+
momentumRatioCap: 0.05,
|
|
60247
|
+
// POLICY staleness bound — refresh by 5% churn no matter what
|
|
59788
60248
|
quiescenceMs: 6e4
|
|
59789
60249
|
// sim preferred 60s over 90s (fresher, esp. heavy-code)
|
|
59790
60250
|
};
|
|
59791
|
-
function
|
|
60251
|
+
function numEnv2(env2, key, fallback) {
|
|
59792
60252
|
const raw2 = env2[key];
|
|
59793
60253
|
if (raw2 == null || raw2.trim() === "") return fallback;
|
|
59794
60254
|
const n = Number(raw2);
|
|
@@ -59796,21 +60256,17 @@ function numEnv(env2, key, fallback) {
|
|
|
59796
60256
|
}
|
|
59797
60257
|
function consolidationPolicyFromEnv(env2 = process.env) {
|
|
59798
60258
|
return {
|
|
59799
|
-
baseFloorMs:
|
|
59800
|
-
dutyFactor:
|
|
59801
|
-
momentumThreshold:
|
|
59802
|
-
|
|
60259
|
+
baseFloorMs: numEnv2(env2, "ERRATA_CONSOLIDATE_BASE_FLOOR_MS", DEFAULT_CONSOLIDATION_POLICY.baseFloorMs),
|
|
60260
|
+
dutyFactor: numEnv2(env2, "ERRATA_CONSOLIDATE_DUTY_FACTOR", DEFAULT_CONSOLIDATION_POLICY.dutyFactor),
|
|
60261
|
+
momentumThreshold: numEnv2(env2, "ERRATA_CONSOLIDATE_MOMENTUM", DEFAULT_CONSOLIDATION_POLICY.momentumThreshold),
|
|
60262
|
+
momentumRatio: numEnv2(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO", DEFAULT_CONSOLIDATION_POLICY.momentumRatio),
|
|
60263
|
+
momentumRatioCap: numEnv2(env2, "ERRATA_CONSOLIDATE_MOMENTUM_RATIO_CAP", DEFAULT_CONSOLIDATION_POLICY.momentumRatioCap),
|
|
60264
|
+
quiescenceMs: numEnv2(env2, "ERRATA_CONSOLIDATE_QUIESCENCE_MS", DEFAULT_CONSOLIDATION_POLICY.quiescenceMs)
|
|
59803
60265
|
};
|
|
59804
60266
|
}
|
|
59805
60267
|
function consolidationGapMs(lastPassMs, policy) {
|
|
59806
60268
|
return Math.max(policy.baseFloorMs, lastPassMs * policy.dutyFactor);
|
|
59807
60269
|
}
|
|
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
60270
|
|
|
59815
60271
|
// src/cli.ts
|
|
59816
60272
|
var exitCleanOnEpipe = (err2) => {
|
|
@@ -62170,10 +62626,10 @@ async function cmdDash(args2) {
|
|
|
62170
62626
|
let nightlyCount = 0;
|
|
62171
62627
|
const hotspotScanMark = /* @__PURE__ */ new Map();
|
|
62172
62628
|
const HOTSPOT_FANIN = 20;
|
|
62173
|
-
const runNightlyPass = async () => {
|
|
62629
|
+
const runNightlyPass = async (dueIds) => {
|
|
62174
62630
|
const yieldToLoop2 = () => new Promise((resolve6) => setImmediate(resolve6));
|
|
62175
62631
|
const fullReclusterPass = nightlyCount++ % FULL_RECLUSTER_EVERY === 0;
|
|
62176
|
-
const m = await handle2.nightlyAll();
|
|
62632
|
+
const m = dueIds ? await handle2.nightlyFor(dueIds) : await handle2.nightlyAll();
|
|
62177
62633
|
let skillsLearned = 0;
|
|
62178
62634
|
for (const [id, n] of m) {
|
|
62179
62635
|
skillsLearned += n.skillsInduced;
|
|
@@ -62317,36 +62773,57 @@ async function cmdDash(args2) {
|
|
|
62317
62773
|
}
|
|
62318
62774
|
}
|
|
62319
62775
|
maybeFlushDigests();
|
|
62776
|
+
return m;
|
|
62320
62777
|
};
|
|
62321
62778
|
const CONSOLIDATE_BOOT_GRACE_S = 45;
|
|
62322
62779
|
const consolidationPolicy = consolidationPolicyFromEnv();
|
|
62323
62780
|
const consolidationState = { momentum: 0, lastConsolidationEnd: 0, lastPassMs: 0 };
|
|
62324
|
-
const
|
|
62325
|
-
|
|
62781
|
+
const consolidatedAtByRecord = /* @__PURE__ */ new Map();
|
|
62782
|
+
const calibratedThreshold = /* @__PURE__ */ new Map();
|
|
62783
|
+
const thresholdFor = (r) => calibratedThreshold.get(r.id) ?? effectiveMomentumThreshold(consolidationPolicy, r.engine.store.nodeCount());
|
|
62784
|
+
const dueRecords = () => handle2.records.map((r) => ({
|
|
62785
|
+
id: r.id,
|
|
62786
|
+
momentum: r.engine.store.mutationCount() - (consolidatedAtByRecord.get(r.id) ?? 0),
|
|
62787
|
+
threshold: thresholdFor(r)
|
|
62788
|
+
})).filter((x) => x.momentum >= x.threshold).map(({ id, momentum }) => ({ id, momentum }));
|
|
62326
62789
|
let consolidating = false;
|
|
62327
62790
|
const maybeConsolidate = async (force) => {
|
|
62328
62791
|
if (consolidating) return;
|
|
62329
62792
|
if (process.uptime() < CONSOLIDATE_BOOT_GRACE_S) return;
|
|
62330
|
-
|
|
62331
|
-
|
|
62332
|
-
|
|
62333
|
-
|
|
62334
|
-
policy: consolidationPolicy,
|
|
62335
|
-
force
|
|
62336
|
-
})) {
|
|
62793
|
+
const due = dueRecords();
|
|
62794
|
+
consolidationState.momentum = due.length;
|
|
62795
|
+
if (due.length === 0) return;
|
|
62796
|
+
if (!force && Date.now() - consolidationState.lastConsolidationEnd < consolidationGapMs(consolidationState.lastPassMs, consolidationPolicy)) {
|
|
62337
62797
|
return;
|
|
62338
62798
|
}
|
|
62339
62799
|
consolidating = true;
|
|
62340
62800
|
const startedAt = Date.now();
|
|
62341
62801
|
try {
|
|
62342
|
-
await runNightlyPass();
|
|
62802
|
+
const reports = await runNightlyPass(due.map((d) => d.id));
|
|
62803
|
+
for (const { id, momentum } of due) {
|
|
62804
|
+
const r = handle2.records.find((x) => x.id === id);
|
|
62805
|
+
const report = reports?.get(id);
|
|
62806
|
+
if (!r || !report) continue;
|
|
62807
|
+
calibratedThreshold.set(
|
|
62808
|
+
id,
|
|
62809
|
+
nextMomentumThreshold({
|
|
62810
|
+
current: thresholdFor(r),
|
|
62811
|
+
consumed: momentum,
|
|
62812
|
+
landmarkFlips: report.landmarkFlips,
|
|
62813
|
+
floor: consolidationPolicy.momentumThreshold,
|
|
62814
|
+
cap: Math.ceil(r.engine.store.nodeCount() * consolidationPolicy.momentumRatioCap)
|
|
62815
|
+
})
|
|
62816
|
+
);
|
|
62817
|
+
}
|
|
62343
62818
|
} catch (err2) {
|
|
62344
62819
|
console.warn(`[consolidate] pass failed: ${err2 instanceof Error ? err2.message : err2}`);
|
|
62345
62820
|
} finally {
|
|
62346
62821
|
consolidationState.lastPassMs = Date.now() - startedAt;
|
|
62347
62822
|
consolidationState.lastConsolidationEnd = Date.now();
|
|
62348
|
-
|
|
62349
|
-
|
|
62823
|
+
for (const { id } of due) {
|
|
62824
|
+
const r = handle2.records.find((x) => x.id === id);
|
|
62825
|
+
if (r) consolidatedAtByRecord.set(id, r.engine.store.mutationCount());
|
|
62826
|
+
}
|
|
62350
62827
|
consolidating = false;
|
|
62351
62828
|
}
|
|
62352
62829
|
};
|
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,
|