@inerrata-corporation/errata 2.0.6-dev.2627 → 2.0.6-dev.2635
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 +18 -0
- package/errata.mjs +106 -43
- package/package.json +1 -1
- package/pass-worker.mjs +18 -0
package/consolidate-worker.mjs
CHANGED
|
@@ -16143,6 +16143,9 @@ CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
|
16143
16143
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
16144
16144
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
16145
16145
|
CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.relPath')) WHERE valid_to IS NULL;
|
|
16146
|
+
-- Live rows by name, for looking up the few symbols a statement mentions
|
|
16147
|
+
-- instead of hydrating every code symbol per minted problem.
|
|
16148
|
+
CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
|
|
16146
16149
|
`;
|
|
16147
16150
|
function encodeEmbedding(emb) {
|
|
16148
16151
|
if (!emb || emb.length === 0) return null;
|
|
@@ -16957,6 +16960,21 @@ var SqliteGraphStore = class {
|
|
|
16957
16960
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
16958
16961
|
* expression index so the incremental reindex fetches only the changed files'
|
|
16959
16962
|
* symbols instead of scanning every versioned node. */
|
|
16963
|
+
/** Live nodes of `labels` whose description is exactly one of `names`, via
|
|
16964
|
+
* the nodes_live_description index. Chunked under the bound-parameter cap. */
|
|
16965
|
+
liveNodesByDescription(names, labels) {
|
|
16966
|
+
if (names.length === 0 || labels.length === 0) return [];
|
|
16967
|
+
const out = [];
|
|
16968
|
+
const labelPh = labels.map(() => "?").join(",");
|
|
16969
|
+
for (let i = 0; i < names.length; i += 400) {
|
|
16970
|
+
const chunk = names.slice(i, i + 400);
|
|
16971
|
+
const rows = this.db.prepare(
|
|
16972
|
+
`SELECT * FROM nodes WHERE valid_to IS NULL AND description IN (${chunk.map(() => "?").join(",")}) AND label IN (${labelPh})`
|
|
16973
|
+
).all(...chunk, ...labels);
|
|
16974
|
+
for (const r of rows) out.push(rowToNode(r));
|
|
16975
|
+
}
|
|
16976
|
+
return out;
|
|
16977
|
+
}
|
|
16960
16978
|
nodesByRelPath(relPaths) {
|
|
16961
16979
|
if (relPaths.length === 0) return [];
|
|
16962
16980
|
const placeholders = relPaths.map(() => "?").join(",");
|
package/errata.mjs
CHANGED
|
@@ -17560,6 +17560,9 @@ CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
|
17560
17560
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
17561
17561
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
17562
17562
|
CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.relPath')) WHERE valid_to IS NULL;
|
|
17563
|
+
-- Live rows by name, for looking up the few symbols a statement mentions
|
|
17564
|
+
-- instead of hydrating every code symbol per minted problem.
|
|
17565
|
+
CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
|
|
17563
17566
|
`;
|
|
17564
17567
|
SqliteGraphStore = class {
|
|
17565
17568
|
db;
|
|
@@ -18317,6 +18320,21 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
|
|
|
18317
18320
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
18318
18321
|
* expression index so the incremental reindex fetches only the changed files'
|
|
18319
18322
|
* symbols instead of scanning every versioned node. */
|
|
18323
|
+
/** Live nodes of `labels` whose description is exactly one of `names`, via
|
|
18324
|
+
* the nodes_live_description index. Chunked under the bound-parameter cap. */
|
|
18325
|
+
liveNodesByDescription(names, labels) {
|
|
18326
|
+
if (names.length === 0 || labels.length === 0) return [];
|
|
18327
|
+
const out2 = [];
|
|
18328
|
+
const labelPh = labels.map(() => "?").join(",");
|
|
18329
|
+
for (let i2 = 0; i2 < names.length; i2 += 400) {
|
|
18330
|
+
const chunk = names.slice(i2, i2 + 400);
|
|
18331
|
+
const rows = this.db.prepare(
|
|
18332
|
+
`SELECT * FROM nodes WHERE valid_to IS NULL AND description IN (${chunk.map(() => "?").join(",")}) AND label IN (${labelPh})`
|
|
18333
|
+
).all(...chunk, ...labels);
|
|
18334
|
+
for (const r of rows) out2.push(rowToNode(r));
|
|
18335
|
+
}
|
|
18336
|
+
return out2;
|
|
18337
|
+
}
|
|
18320
18338
|
nodesByRelPath(relPaths) {
|
|
18321
18339
|
if (relPaths.length === 0) return [];
|
|
18322
18340
|
const placeholders = relPaths.map(() => "?").join(",");
|
|
@@ -19303,6 +19321,33 @@ function buildSymbolIndex(store) {
|
|
|
19303
19321
|
}
|
|
19304
19322
|
return idx;
|
|
19305
19323
|
}
|
|
19324
|
+
function symbolCandidatesInText(text) {
|
|
19325
|
+
const out2 = /* @__PURE__ */ new Set();
|
|
19326
|
+
for (const run3 of text.match(/[\w$.]+/g) ?? []) {
|
|
19327
|
+
const segs = run3.split(".");
|
|
19328
|
+
for (let i2 = 0; i2 < segs.length; i2++) {
|
|
19329
|
+
if (!segs[i2]) continue;
|
|
19330
|
+
let name2 = "";
|
|
19331
|
+
for (let j = i2; j < segs.length && segs[j]; j++) {
|
|
19332
|
+
name2 = j === i2 ? segs[j] : `${name2}.${segs[j]}`;
|
|
19333
|
+
if (isShapedSymbol(name2)) out2.add(name2);
|
|
19334
|
+
}
|
|
19335
|
+
}
|
|
19336
|
+
}
|
|
19337
|
+
return [...out2];
|
|
19338
|
+
}
|
|
19339
|
+
function symbolIndexForText(store, text) {
|
|
19340
|
+
const counts = /* @__PURE__ */ new Map();
|
|
19341
|
+
for (const n of store.liveNodesByDescription(symbolCandidatesInText(text), SYMBOL_LABELS)) {
|
|
19342
|
+
const name2 = n.description.trim();
|
|
19343
|
+
const list = counts.get(name2);
|
|
19344
|
+
if (list) list.push(n);
|
|
19345
|
+
else counts.set(name2, [n]);
|
|
19346
|
+
}
|
|
19347
|
+
const idx = /* @__PURE__ */ new Map();
|
|
19348
|
+
for (const [name2, nodes] of counts) if (nodes.length === 1) idx.set(name2, nodes[0]);
|
|
19349
|
+
return idx;
|
|
19350
|
+
}
|
|
19306
19351
|
function matchSymbolsInText(text, index) {
|
|
19307
19352
|
const out2 = [];
|
|
19308
19353
|
for (const [name2, node2] of index) {
|
|
@@ -19315,19 +19360,9 @@ function matchSymbolsInText(text, index) {
|
|
|
19315
19360
|
function linkProblemToSymbols(store, problemId, text, ts, index) {
|
|
19316
19361
|
const p = store.getNode(problemId);
|
|
19317
19362
|
if (!p || p.label !== "Problem") return [];
|
|
19318
|
-
const idx = index ??
|
|
19363
|
+
const idx = index ?? symbolIndexForText(store, text);
|
|
19319
19364
|
if (idx.size === 0) return [];
|
|
19320
|
-
|
|
19321
|
-
const fileFor2 = (relPath) => {
|
|
19322
|
-
if (!fileByPath) {
|
|
19323
|
-
fileByPath = /* @__PURE__ */ new Map();
|
|
19324
|
-
for (const f of store.findNodesByLabel("File")) {
|
|
19325
|
-
const rp = String(f.attrs["relPath"] ?? f.description ?? "");
|
|
19326
|
-
if (rp) fileByPath.set(rp, f);
|
|
19327
|
-
}
|
|
19328
|
-
}
|
|
19329
|
-
return fileByPath.get(relPath) ?? null;
|
|
19330
|
-
};
|
|
19365
|
+
const fileFor2 = (relPath) => store.nodesByRelPath([relPath]).find((f) => f.label === "File") ?? null;
|
|
19331
19366
|
const already = new Set(store.outEdges(problemId, ["ANCHORED_AT"]).map((e) => e.to));
|
|
19332
19367
|
const linked = [];
|
|
19333
19368
|
for (const sym of matchSymbolsInText(text, idx)) {
|
|
@@ -24052,6 +24087,8 @@ __export(src_exports2, {
|
|
|
24052
24087
|
stampObservedOs: () => stampObservedOs,
|
|
24053
24088
|
statementIdentityKey: () => statementIdentityKey,
|
|
24054
24089
|
staticConductance: () => staticConductance,
|
|
24090
|
+
symbolCandidatesInText: () => symbolCandidatesInText,
|
|
24091
|
+
symbolIndexForText: () => symbolIndexForText,
|
|
24055
24092
|
toolNodeId: () => toolNodeId,
|
|
24056
24093
|
toolPriorsFor: () => toolPriorsFor,
|
|
24057
24094
|
triageOf: () => triageOf,
|
|
@@ -78478,7 +78515,7 @@ function createWatchBreaker(deps) {
|
|
|
78478
78515
|
}
|
|
78479
78516
|
|
|
78480
78517
|
// src/engine.ts
|
|
78481
|
-
var DAEMON_VERSION = true ? "2.0.6-dev.
|
|
78518
|
+
var DAEMON_VERSION = true ? "2.0.6-dev.2635" : "2.0.0-alpha.0";
|
|
78482
78519
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
78483
78520
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
78484
78521
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -82211,26 +82248,40 @@ function classifyAnchorVersion(cloudAttrs, localSymbol) {
|
|
|
82211
82248
|
return seen === have ? "current" : "pre-change";
|
|
82212
82249
|
}
|
|
82213
82250
|
function buildReverseSymbolMap(opts) {
|
|
82251
|
+
return collectFirstWins(reverseSymbolEntries(opts));
|
|
82252
|
+
}
|
|
82253
|
+
function* reverseSymbolEntries(opts) {
|
|
82214
82254
|
const { store, salt, projectId } = opts;
|
|
82215
|
-
const reverse = /* @__PURE__ */ new Map();
|
|
82216
82255
|
for (const kind of ANCHOR_SYMBOL_KINDS2) {
|
|
82217
82256
|
for (const n of store.findNodeIdentitiesByLabel(kind)) {
|
|
82218
82257
|
const { relPath, qname } = n;
|
|
82219
82258
|
if (relPath === null || qname === null) continue;
|
|
82220
82259
|
const sym = projectSymbolId(salt, projectId, relPath, qname, kind);
|
|
82221
|
-
|
|
82222
|
-
|
|
82223
|
-
if (!reverse.has(twin)) reverse.set(twin, n.id);
|
|
82260
|
+
yield [sym, n.id];
|
|
82261
|
+
yield [projectSaltedNodeId(sym, salt), n.id];
|
|
82224
82262
|
}
|
|
82225
82263
|
}
|
|
82226
82264
|
for (const n of store.findNodeIdentitiesByLabel("File")) {
|
|
82227
82265
|
if (n.relPath === null) continue;
|
|
82228
82266
|
const sym = projectSymbolId(salt, projectId, n.relPath, FILE_ANCHOR_QNAME, "File");
|
|
82229
|
-
|
|
82230
|
-
|
|
82231
|
-
|
|
82267
|
+
yield [sym, n.id];
|
|
82268
|
+
yield [projectSaltedNodeId(sym, salt), n.id];
|
|
82269
|
+
}
|
|
82270
|
+
}
|
|
82271
|
+
function collectFirstWins(entries) {
|
|
82272
|
+
const m = /* @__PURE__ */ new Map();
|
|
82273
|
+
for (const [k, v] of entries) if (!m.has(k)) m.set(k, v);
|
|
82274
|
+
return m;
|
|
82275
|
+
}
|
|
82276
|
+
var MAP_BUILD_BATCH = 2e3;
|
|
82277
|
+
async function collectFirstWinsYielding(entries) {
|
|
82278
|
+
const m = /* @__PURE__ */ new Map();
|
|
82279
|
+
let i2 = 0;
|
|
82280
|
+
for (const [k, v] of entries) {
|
|
82281
|
+
if (++i2 % MAP_BUILD_BATCH === 0) await new Promise((r) => setImmediate(r));
|
|
82282
|
+
if (!m.has(k)) m.set(k, v);
|
|
82232
82283
|
}
|
|
82233
|
-
return
|
|
82284
|
+
return m;
|
|
82234
82285
|
}
|
|
82235
82286
|
function anchorConfidence(attrs) {
|
|
82236
82287
|
const v = typeof attrs["extractionConfidence"] === "number" ? attrs["extractionConfidence"] : attrs["confidence"];
|
|
@@ -82245,15 +82296,25 @@ var ANCHOR_SOURCE_KINDS = [
|
|
|
82245
82296
|
"AntiPattern"
|
|
82246
82297
|
];
|
|
82247
82298
|
function buildOwnKnowledgeMap(opts) {
|
|
82299
|
+
return new Map(ownKnowledgeEntries(opts));
|
|
82300
|
+
}
|
|
82301
|
+
function* ownKnowledgeEntries(opts) {
|
|
82248
82302
|
const { store, salt } = opts;
|
|
82249
|
-
const own = /* @__PURE__ */ new Map();
|
|
82250
82303
|
for (const kind of ANCHOR_SOURCE_KINDS) {
|
|
82251
82304
|
for (const n of store.findNodeIdentitiesByLabel(kind)) {
|
|
82252
82305
|
if (n.id.startsWith("projn_")) continue;
|
|
82253
|
-
|
|
82306
|
+
yield [projectSaltedNodeId(n.id, salt), n.id];
|
|
82254
82307
|
}
|
|
82255
82308
|
}
|
|
82256
|
-
|
|
82309
|
+
}
|
|
82310
|
+
async function collectLastWinsYielding(entries) {
|
|
82311
|
+
const m = /* @__PURE__ */ new Map();
|
|
82312
|
+
let i2 = 0;
|
|
82313
|
+
for (const [k, v] of entries) {
|
|
82314
|
+
if (++i2 % MAP_BUILD_BATCH === 0) await new Promise((r) => setImmediate(r));
|
|
82315
|
+
m.set(k, v);
|
|
82316
|
+
}
|
|
82317
|
+
return m;
|
|
82257
82318
|
}
|
|
82258
82319
|
function resolveAnchorSource(store, from, own) {
|
|
82259
82320
|
if (store.getNode(from)) return from;
|
|
@@ -82266,30 +82327,27 @@ function existingAnchor(store, from, to) {
|
|
|
82266
82327
|
var reverseCache = /* @__PURE__ */ new WeakMap();
|
|
82267
82328
|
var ownCache = /* @__PURE__ */ new WeakMap();
|
|
82268
82329
|
function cachedMap(cache2, store, key, build) {
|
|
82269
|
-
let
|
|
82330
|
+
let rebuild = null;
|
|
82270
82331
|
const put = () => {
|
|
82271
|
-
|
|
82272
|
-
|
|
82273
|
-
|
|
82332
|
+
rebuild ??= build().then((map2) => {
|
|
82333
|
+
cache2.set(store, { key, map: map2 });
|
|
82334
|
+
return map2;
|
|
82335
|
+
});
|
|
82336
|
+
return rebuild;
|
|
82274
82337
|
};
|
|
82275
82338
|
return {
|
|
82276
|
-
get: () => {
|
|
82339
|
+
get: async () => {
|
|
82277
82340
|
const hit = cache2.get(store);
|
|
82278
82341
|
if (hit && hit.key === key) return hit.map;
|
|
82279
|
-
refreshed = true;
|
|
82280
82342
|
return put();
|
|
82281
82343
|
},
|
|
82282
|
-
refresh: () =>
|
|
82283
|
-
if (refreshed) return cache2.get(store)?.map ?? put();
|
|
82284
|
-
refreshed = true;
|
|
82285
|
-
return put();
|
|
82286
|
-
}
|
|
82344
|
+
refresh: () => put()
|
|
82287
82345
|
};
|
|
82288
82346
|
}
|
|
82289
|
-
function lookupLive(store, m, id) {
|
|
82290
|
-
const first = m.get().get(id);
|
|
82347
|
+
async function lookupLive(store, m, id) {
|
|
82348
|
+
const first = (await m.get()).get(id);
|
|
82291
82349
|
if (first && isLive2(store, first)) return first;
|
|
82292
|
-
const second = m.refresh().get(id);
|
|
82350
|
+
const second = (await m.refresh()).get(id);
|
|
82293
82351
|
return second && isLive2(store, second) ? second : null;
|
|
82294
82352
|
}
|
|
82295
82353
|
function isLive2(store, id) {
|
|
@@ -82370,8 +82428,13 @@ async function syncProjectAnchors(opts) {
|
|
|
82370
82428
|
if (!profile.projectId) return { synced: false, reason: "no-project" };
|
|
82371
82429
|
const projectId = profile.projectId;
|
|
82372
82430
|
const key = `${projectId}|${salt}`;
|
|
82373
|
-
const reverse = cachedMap(
|
|
82374
|
-
|
|
82431
|
+
const reverse = cachedMap(
|
|
82432
|
+
reverseCache,
|
|
82433
|
+
store,
|
|
82434
|
+
key,
|
|
82435
|
+
() => collectFirstWinsYielding(reverseSymbolEntries({ store, salt, projectId }))
|
|
82436
|
+
);
|
|
82437
|
+
const own = cachedMap(ownCache, store, salt, () => collectLastWinsYielding(ownKnowledgeEntries({ store, salt })));
|
|
82375
82438
|
const limit = opts.limit ?? SYNC_PAGE;
|
|
82376
82439
|
const maxPages = opts.maxPages ?? SYNC_MAX_PAGES;
|
|
82377
82440
|
let anchors = 0;
|
|
@@ -82383,19 +82446,19 @@ async function syncProjectAnchors(opts) {
|
|
|
82383
82446
|
for (let page = 0; page < maxPages; page++) {
|
|
82384
82447
|
const res = await cloud.getProjectAnchors(projectId, cursor, limit);
|
|
82385
82448
|
if (res.anchors.length === 0) break;
|
|
82386
|
-
if (reverse.get().size === 0 && reverse.refresh().size === 0) {
|
|
82449
|
+
if ((await reverse.get()).size === 0 && (await reverse.refresh()).size === 0) {
|
|
82387
82450
|
return { synced: false, reason: "no-local-symbols" };
|
|
82388
82451
|
}
|
|
82389
82452
|
const byId = new Map(res.nodes.map((n) => [n.id, n]));
|
|
82390
82453
|
const now = Date.now();
|
|
82391
82454
|
for (const a of res.anchors) {
|
|
82392
82455
|
if (a.type !== "ANCHORED_AT") continue;
|
|
82393
|
-
const localTo = lookupLive(store, reverse, a.toSymbolId);
|
|
82456
|
+
const localTo = await lookupLive(store, reverse, a.toSymbolId);
|
|
82394
82457
|
if (!localTo) {
|
|
82395
82458
|
dropped++;
|
|
82396
82459
|
continue;
|
|
82397
82460
|
}
|
|
82398
|
-
let from = store.getNode(a.from) ? a.from : lookupLive(store, own, a.from);
|
|
82461
|
+
let from = store.getNode(a.from) ? a.from : await lookupLive(store, own, a.from);
|
|
82399
82462
|
if (!from) {
|
|
82400
82463
|
const node2 = byId.get(a.from);
|
|
82401
82464
|
if (!node2) {
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -24525,6 +24525,9 @@ CREATE INDEX IF NOT EXISTS edges_live ON edges(valid_to);
|
|
|
24525
24525
|
-- incremental reindex can fetch just the changed files' symbols instead of
|
|
24526
24526
|
-- scanning every versioned node. Partial: only live rows are ever queried.
|
|
24527
24527
|
CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.relPath')) WHERE valid_to IS NULL;
|
|
24528
|
+
-- Live rows by name, for looking up the few symbols a statement mentions
|
|
24529
|
+
-- instead of hydrating every code symbol per minted problem.
|
|
24530
|
+
CREATE INDEX IF NOT EXISTS nodes_live_description ON nodes(description, label) WHERE valid_to IS NULL;
|
|
24528
24531
|
`;
|
|
24529
24532
|
function encodeEmbedding(emb) {
|
|
24530
24533
|
if (!emb || emb.length === 0) return null;
|
|
@@ -25339,6 +25342,21 @@ var SqliteGraphStore = class {
|
|
|
25339
25342
|
/** Live nodes whose attrs.relPath is one of `relPaths`. Uses the nodes_relpath
|
|
25340
25343
|
* expression index so the incremental reindex fetches only the changed files'
|
|
25341
25344
|
* symbols instead of scanning every versioned node. */
|
|
25345
|
+
/** Live nodes of `labels` whose description is exactly one of `names`, via
|
|
25346
|
+
* the nodes_live_description index. Chunked under the bound-parameter cap. */
|
|
25347
|
+
liveNodesByDescription(names, labels) {
|
|
25348
|
+
if (names.length === 0 || labels.length === 0) return [];
|
|
25349
|
+
const out2 = [];
|
|
25350
|
+
const labelPh = labels.map(() => "?").join(",");
|
|
25351
|
+
for (let i2 = 0; i2 < names.length; i2 += 400) {
|
|
25352
|
+
const chunk = names.slice(i2, i2 + 400);
|
|
25353
|
+
const rows = this.db.prepare(
|
|
25354
|
+
`SELECT * FROM nodes WHERE valid_to IS NULL AND description IN (${chunk.map(() => "?").join(",")}) AND label IN (${labelPh})`
|
|
25355
|
+
).all(...chunk, ...labels);
|
|
25356
|
+
for (const r of rows) out2.push(rowToNode(r));
|
|
25357
|
+
}
|
|
25358
|
+
return out2;
|
|
25359
|
+
}
|
|
25342
25360
|
nodesByRelPath(relPaths) {
|
|
25343
25361
|
if (relPaths.length === 0) return [];
|
|
25344
25362
|
const placeholders = relPaths.map(() => "?").join(",");
|