@inerrata-corporation/errata 2.0.17-dev.3426 → 2.0.17-dev.3435
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 +46 -18
- package/errata.mjs +247 -123
- package/package.json +1 -1
- package/pass-worker.mjs +54 -39
package/consolidate-worker.mjs
CHANGED
|
@@ -15884,13 +15884,13 @@ function loadNodeSqlite() {
|
|
|
15884
15884
|
process.emitWarning = original;
|
|
15885
15885
|
}
|
|
15886
15886
|
}
|
|
15887
|
-
function openDatabase(path) {
|
|
15888
|
-
const db = new DatabaseSync(path);
|
|
15887
|
+
function openDatabase(path, opts) {
|
|
15888
|
+
const db = opts ? new DatabaseSync(path, opts) : new DatabaseSync(path);
|
|
15889
15889
|
try {
|
|
15890
15890
|
db.exec("PRAGMA foreign_keys = ON");
|
|
15891
15891
|
} catch {
|
|
15892
15892
|
}
|
|
15893
|
-
if (path !== ":memory:") {
|
|
15893
|
+
if (path !== ":memory:" && !opts?.readOnly) {
|
|
15894
15894
|
try {
|
|
15895
15895
|
db.exec("PRAGMA journal_mode = WAL");
|
|
15896
15896
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
@@ -16157,8 +16157,9 @@ init_src2();
|
|
|
16157
16157
|
init_src();
|
|
16158
16158
|
|
|
16159
16159
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
16160
|
+
var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
16160
16161
|
function installHistoryCacheFloor(db) {
|
|
16161
|
-
const key =
|
|
16162
|
+
const key = HISTORY_CACHE_FLOOR_KEY;
|
|
16162
16163
|
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key)) return;
|
|
16163
16164
|
db.transaction(() => {
|
|
16164
16165
|
db.exec("DROP TRIGGER IF EXISTS history_cache_nodes_insert; DROP TRIGGER IF EXISTS history_cache_nodes_update;");
|
|
@@ -16320,6 +16321,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
16320
16321
|
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
16321
16322
|
}
|
|
16322
16323
|
var SCHEMA_VERSION = 6;
|
|
16324
|
+
var SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
16323
16325
|
var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
16324
16326
|
var SCHEMA_SQL = `
|
|
16325
16327
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -16531,10 +16533,10 @@ var SqliteGraphStore = class {
|
|
|
16531
16533
|
}
|
|
16532
16534
|
this.db.exec(TEMPORAL_INDEX_SQL);
|
|
16533
16535
|
installHistoryCacheFloor(this.db);
|
|
16534
|
-
if (!this.db.prepare("SELECT value FROM store_meta WHERE key =
|
|
16536
|
+
if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
|
|
16535
16537
|
this.db.transaction(() => {
|
|
16536
16538
|
this.repairSessionAliases();
|
|
16537
|
-
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (
|
|
16539
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
16538
16540
|
});
|
|
16539
16541
|
}
|
|
16540
16542
|
this.stmts = {
|
|
@@ -16729,6 +16731,34 @@ var SqliteGraphStore = class {
|
|
|
16729
16731
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
16730
16732
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
16731
16733
|
}
|
|
16734
|
+
strandedSymbols() {
|
|
16735
|
+
return this.db.prepare(`
|
|
16736
|
+
SELECT f.id AS fileId, e.to_id AS symbolId
|
|
16737
|
+
-- Start from the small File population. An unconstrained JOIN lets
|
|
16738
|
+
-- SQLite start at edges_live and visit every live edge in the graph.
|
|
16739
|
+
FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
|
|
16740
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
16741
|
+
WHERE f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
|
|
16742
|
+
AND e.type IN ('DEFINES', 'CONTAINS')
|
|
16743
|
+
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
16744
|
+
`).all();
|
|
16745
|
+
}
|
|
16746
|
+
fileIndexStates(ids) {
|
|
16747
|
+
if (ids.length === 0) return [];
|
|
16748
|
+
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
16749
|
+
const rows = this.db.prepare(`
|
|
16750
|
+
SELECT f.id, f.last_updated_at AS lastUpdatedAt, f.valid_to AS validTo,
|
|
16751
|
+
CASE WHEN count(e.id) = 0 THEN
|
|
16752
|
+
(json_type(f.attrs_json, '$.symbolCount') IN ('integer','real') AND json_extract(f.attrs_json, '$.symbolCount') = 0)
|
|
16753
|
+
OR json_type(f.attrs_json, '$.indexSkipped') = 'text'
|
|
16754
|
+
ELSE max(CASE WHEN t.id IS NULL OR t.valid_to IS NOT NULL THEN 1 ELSE 0 END) = 0 END AS complete
|
|
16755
|
+
FROM nodes f
|
|
16756
|
+
LEFT JOIN edges e ON e.from_id = f.id AND e.valid_to IS NULL AND e.type IN ('DEFINES','CONTAINS')
|
|
16757
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
16758
|
+
WHERE f.id IN (${ids.map(() => "?").join(",")}) GROUP BY f.id
|
|
16759
|
+
`).all(...ids);
|
|
16760
|
+
return rows.map((row) => ({ ...row, complete: row.complete === 1 }));
|
|
16761
|
+
}
|
|
16732
16762
|
/** Forward-migrate the on-disk schema to the current SCHEMA_VERSION. */
|
|
16733
16763
|
migrate(from) {
|
|
16734
16764
|
this.db.transaction(() => {
|
|
@@ -18265,17 +18295,8 @@ var STALL_MIN_SPAN_MS = 60 * 60 * 1e3;
|
|
|
18265
18295
|
|
|
18266
18296
|
// src/reconcile.ts
|
|
18267
18297
|
init_src3();
|
|
18268
|
-
function isLive(n) {
|
|
18269
|
-
return n != null && (n.validTo ?? null) === null;
|
|
18270
|
-
}
|
|
18271
18298
|
function findStrandedSymbols(store) {
|
|
18272
|
-
|
|
18273
|
-
for (const file2 of store.findNodesByLabel("File")) {
|
|
18274
|
-
for (const e of store.outEdges(file2.id, ["DEFINES", "CONTAINS"])) {
|
|
18275
|
-
if (!isLive(store.getNode(e.to))) stranded.push({ fileId: file2.id, symbolId: e.to });
|
|
18276
|
-
}
|
|
18277
|
-
}
|
|
18278
|
-
return stranded;
|
|
18299
|
+
return store.strandedSymbols();
|
|
18279
18300
|
}
|
|
18280
18301
|
|
|
18281
18302
|
// src/package-index.ts
|
|
@@ -19592,7 +19613,8 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19592
19613
|
sidecarNoLexicon: 0,
|
|
19593
19614
|
sidecarNoSummary: 0,
|
|
19594
19615
|
fileRefsSubstituted: 0,
|
|
19595
|
-
fileRefLexiconMisses: 0
|
|
19616
|
+
fileRefLexiconMisses: 0,
|
|
19617
|
+
collapsedEdges: 0
|
|
19596
19618
|
};
|
|
19597
19619
|
const sharedCauses = /* @__PURE__ */ new Map();
|
|
19598
19620
|
const causalQueue = store.findNodesByLabel("Problem").filter(
|
|
@@ -19817,7 +19839,13 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
19817
19839
|
];
|
|
19818
19840
|
const dedup = /* @__PURE__ */ new Map();
|
|
19819
19841
|
for (const e of rawOuts) dedup.set(`${e.type}>${e.to}`, e);
|
|
19820
|
-
const outs = [...dedup.values()].map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) =>
|
|
19842
|
+
const outs = [...dedup.values()].map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) => {
|
|
19843
|
+
if (x.wireTo === wireFrom) {
|
|
19844
|
+
composition.collapsedEdges++;
|
|
19845
|
+
return false;
|
|
19846
|
+
}
|
|
19847
|
+
return x.wireTo != null;
|
|
19848
|
+
});
|
|
19821
19849
|
if (outs.length === 0) continue;
|
|
19822
19850
|
const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
|
|
19823
19851
|
const owner = nodeOf(sourceId);
|
package/errata.mjs
CHANGED
|
@@ -17838,13 +17838,13 @@ function loadNodeSqlite() {
|
|
|
17838
17838
|
process.emitWarning = original;
|
|
17839
17839
|
}
|
|
17840
17840
|
}
|
|
17841
|
-
function openDatabase(path2) {
|
|
17842
|
-
const db = new DatabaseSync(path2);
|
|
17841
|
+
function openDatabase(path2, opts) {
|
|
17842
|
+
const db = opts ? new DatabaseSync(path2, opts) : new DatabaseSync(path2);
|
|
17843
17843
|
try {
|
|
17844
17844
|
db.exec("PRAGMA foreign_keys = ON");
|
|
17845
17845
|
} catch {
|
|
17846
17846
|
}
|
|
17847
|
-
if (path2 !== ":memory:") {
|
|
17847
|
+
if (path2 !== ":memory:" && !opts?.readOnly) {
|
|
17848
17848
|
try {
|
|
17849
17849
|
db.exec("PRAGMA journal_mode = WAL");
|
|
17850
17850
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
@@ -17945,7 +17945,7 @@ var init_src2 = __esm({
|
|
|
17945
17945
|
|
|
17946
17946
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
17947
17947
|
function installHistoryCacheFloor(db) {
|
|
17948
|
-
const key =
|
|
17948
|
+
const key = HISTORY_CACHE_FLOOR_KEY;
|
|
17949
17949
|
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key)) return;
|
|
17950
17950
|
db.transaction(() => {
|
|
17951
17951
|
db.exec("DROP TRIGGER IF EXISTS history_cache_nodes_insert; DROP TRIGGER IF EXISTS history_cache_nodes_update;");
|
|
@@ -18001,9 +18001,11 @@ function installHistoryCacheFloor(db) {
|
|
|
18001
18001
|
db.prepare("INSERT INTO store_meta(key,value) VALUES (?,?)").run(key, "installed");
|
|
18002
18002
|
});
|
|
18003
18003
|
}
|
|
18004
|
+
var HISTORY_CACHE_FLOOR_KEY;
|
|
18004
18005
|
var init_history_cache_floor = __esm({
|
|
18005
18006
|
"../../packages/local-graph/src/history-cache-floor.ts"() {
|
|
18006
18007
|
"use strict";
|
|
18008
|
+
HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
18007
18009
|
}
|
|
18008
18010
|
});
|
|
18009
18011
|
|
|
@@ -18095,6 +18097,23 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
18095
18097
|
const verdict = isValidEdge(fromLabel, type, toLabel);
|
|
18096
18098
|
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
18097
18099
|
}
|
|
18100
|
+
function graphStoreNeedsPreparation(path2) {
|
|
18101
|
+
if (path2 === ":memory:") return false;
|
|
18102
|
+
let db;
|
|
18103
|
+
try {
|
|
18104
|
+
db = openDatabase(path2, { readOnly: true });
|
|
18105
|
+
if (db.prepare("SELECT version FROM schema_version LIMIT 1").get()?.["version"] !== SCHEMA_VERSION) return true;
|
|
18106
|
+
const indexes = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type = 'index'").all().map((r) => r["name"]));
|
|
18107
|
+
for (const match2 of (SCHEMA_SQL + TEMPORAL_INDEX_SQL).matchAll(/CREATE INDEX IF NOT EXISTS (\w+)/g)) {
|
|
18108
|
+
if (!indexes.has(match2[1])) return true;
|
|
18109
|
+
}
|
|
18110
|
+
return [SESSION_REPAIR_KEY, HISTORY_CACHE_FLOOR_KEY].some((key) => !db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key));
|
|
18111
|
+
} catch {
|
|
18112
|
+
return true;
|
|
18113
|
+
} finally {
|
|
18114
|
+
db?.close();
|
|
18115
|
+
}
|
|
18116
|
+
}
|
|
18098
18117
|
function encodeEmbedding(emb) {
|
|
18099
18118
|
if (!emb || emb.length === 0) return null;
|
|
18100
18119
|
const f = Float32Array.from(emb);
|
|
@@ -18155,7 +18174,7 @@ function rowToEdge(r) {
|
|
|
18155
18174
|
function openGraphStore(opts) {
|
|
18156
18175
|
return new SqliteGraphStore(opts);
|
|
18157
18176
|
}
|
|
18158
|
-
var LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SESSION_EVIDENCE_PREDICATE, SCHEMA_SQL, TEMPORAL_INDEX_SQL, FINGERPRINT_INDEXED_LABELS, SqliteGraphStore;
|
|
18177
|
+
var LOCAL_RULE_OVERRIDES, SCHEMA_VERSION, SESSION_REPAIR_KEY, SESSION_EVIDENCE_PREDICATE, SCHEMA_SQL, TEMPORAL_INDEX_SQL, FINGERPRINT_INDEXED_LABELS, SqliteGraphStore;
|
|
18159
18178
|
var init_store = __esm({
|
|
18160
18179
|
"../../packages/local-graph/src/store.ts"() {
|
|
18161
18180
|
"use strict";
|
|
@@ -18186,6 +18205,7 @@ var init_store = __esm({
|
|
|
18186
18205
|
REGRESSION_OF: { from: ["Problem"], to: ["Problem"] }
|
|
18187
18206
|
};
|
|
18188
18207
|
SCHEMA_VERSION = 6;
|
|
18208
|
+
SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
18189
18209
|
SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
18190
18210
|
SCHEMA_SQL = `
|
|
18191
18211
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -18340,10 +18360,10 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
|
|
|
18340
18360
|
}
|
|
18341
18361
|
this.db.exec(TEMPORAL_INDEX_SQL);
|
|
18342
18362
|
installHistoryCacheFloor(this.db);
|
|
18343
|
-
if (!this.db.prepare("SELECT value FROM store_meta WHERE key =
|
|
18363
|
+
if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
|
|
18344
18364
|
this.db.transaction(() => {
|
|
18345
18365
|
this.repairSessionAliases();
|
|
18346
|
-
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (
|
|
18366
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
18347
18367
|
});
|
|
18348
18368
|
}
|
|
18349
18369
|
this.stmts = {
|
|
@@ -18538,6 +18558,34 @@ CREATE INDEX IF NOT EXISTS nodes_anchor_hint_path ON nodes(json_extract(attrs_js
|
|
|
18538
18558
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
18539
18559
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
18540
18560
|
}
|
|
18561
|
+
strandedSymbols() {
|
|
18562
|
+
return this.db.prepare(`
|
|
18563
|
+
SELECT f.id AS fileId, e.to_id AS symbolId
|
|
18564
|
+
-- Start from the small File population. An unconstrained JOIN lets
|
|
18565
|
+
-- SQLite start at edges_live and visit every live edge in the graph.
|
|
18566
|
+
FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
|
|
18567
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
18568
|
+
WHERE f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
|
|
18569
|
+
AND e.type IN ('DEFINES', 'CONTAINS')
|
|
18570
|
+
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
18571
|
+
`).all();
|
|
18572
|
+
}
|
|
18573
|
+
fileIndexStates(ids) {
|
|
18574
|
+
if (ids.length === 0) return [];
|
|
18575
|
+
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
18576
|
+
const rows = this.db.prepare(`
|
|
18577
|
+
SELECT f.id, f.last_updated_at AS lastUpdatedAt, f.valid_to AS validTo,
|
|
18578
|
+
CASE WHEN count(e.id) = 0 THEN
|
|
18579
|
+
(json_type(f.attrs_json, '$.symbolCount') IN ('integer','real') AND json_extract(f.attrs_json, '$.symbolCount') = 0)
|
|
18580
|
+
OR json_type(f.attrs_json, '$.indexSkipped') = 'text'
|
|
18581
|
+
ELSE max(CASE WHEN t.id IS NULL OR t.valid_to IS NOT NULL THEN 1 ELSE 0 END) = 0 END AS complete
|
|
18582
|
+
FROM nodes f
|
|
18583
|
+
LEFT JOIN edges e ON e.from_id = f.id AND e.valid_to IS NULL AND e.type IN ('DEFINES','CONTAINS')
|
|
18584
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
18585
|
+
WHERE f.id IN (${ids.map(() => "?").join(",")}) GROUP BY f.id
|
|
18586
|
+
`).all(...ids);
|
|
18587
|
+
return rows.map((row) => ({ ...row, complete: row.complete === 1 }));
|
|
18588
|
+
}
|
|
18541
18589
|
/** Forward-migrate the on-disk schema to the current SCHEMA_VERSION. */
|
|
18542
18590
|
migrate(from) {
|
|
18543
18591
|
this.db.transaction(() => {
|
|
@@ -23353,7 +23401,7 @@ function triageOf(l2, q, mathOpts) {
|
|
|
23353
23401
|
if (!triage) return { triage: "", presenting: presentingId, routes: [] };
|
|
23354
23402
|
const seen = triage.attrs["perContextSeen"] ?? {};
|
|
23355
23403
|
const buckets = q.context ? triageContextBuckets(canonicalizeContext(q.context)) : [];
|
|
23356
|
-
const
|
|
23404
|
+
const active3 = new Set(q.activeProblemIds ?? []);
|
|
23357
23405
|
const routes = [];
|
|
23358
23406
|
for (const e of l2.outEdges(triageId, [...ROUTE_EDGE_TYPES])) {
|
|
23359
23407
|
const perContext = e.attrs["perContext"] ?? {};
|
|
@@ -23374,13 +23422,13 @@ function triageOf(l2, q, mathOpts) {
|
|
|
23374
23422
|
}
|
|
23375
23423
|
const post = triagePosterior(counts, buckets, mathOpts);
|
|
23376
23424
|
let probability = post.probability;
|
|
23377
|
-
if (
|
|
23425
|
+
if (active3.size > 0) {
|
|
23378
23426
|
const neighbors = /* @__PURE__ */ new Set([
|
|
23379
23427
|
...l2.outEdges(e.to).map((x) => x.to),
|
|
23380
23428
|
...l2.inEdges(e.to).map((x) => x.from)
|
|
23381
23429
|
]);
|
|
23382
23430
|
let hits = 0;
|
|
23383
|
-
for (const id of
|
|
23431
|
+
for (const id of active3) if (neighbors.has(id)) hits++;
|
|
23384
23432
|
if (hits > 0) probability = Math.min(1, probability * (1 + COOCCUR_BONUS * Math.tanh(hits)));
|
|
23385
23433
|
}
|
|
23386
23434
|
const cause = l2.getNode(e.to);
|
|
@@ -24990,6 +25038,7 @@ __export(src_exports2, {
|
|
|
24990
25038
|
foldSameAnchorProblem: () => foldSameAnchorProblem,
|
|
24991
25039
|
formatMechanismStatus: () => formatMechanismStatus,
|
|
24992
25040
|
getClaim: () => getClaim,
|
|
25041
|
+
graphStoreNeedsPreparation: () => graphStoreNeedsPreparation,
|
|
24993
25042
|
harvestAbstractionFences: () => harvestAbstractionFences,
|
|
24994
25043
|
harvestTriageFences: () => harvestTriageFences,
|
|
24995
25044
|
hasStarvedMechanism: () => hasStarvedMechanism,
|
|
@@ -25123,7 +25172,7 @@ function buildSnapshot(opts) {
|
|
|
25123
25172
|
node: p,
|
|
25124
25173
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
25125
25174
|
}));
|
|
25126
|
-
const
|
|
25175
|
+
const recent4 = openPool.slice(0, RECENT_OPEN);
|
|
25127
25176
|
const constraintPool = stillOpen.filter((p) => isConstraintProblem(p)).sort((a, b) => b.lastUpdatedAt - a.lastUpdatedAt).slice(0, RECENT_CONSTRAINTS * SCOPE_POOL_FACTOR);
|
|
25128
25177
|
const recentConstraints = constraintPool.slice(0, RECENT_CONSTRAINTS);
|
|
25129
25178
|
const regressions = problems.filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0).flatMap(
|
|
@@ -25195,10 +25244,10 @@ function buildSnapshot(opts) {
|
|
|
25195
25244
|
packages: opts.profile.stack.map((name2) => ({ name: name2, node: matchNode(pkgNodes, pkgBase(name2)) })),
|
|
25196
25245
|
tools: toolNodes.map((node2) => ({ name: String(node2.attrs["name"] ?? node2.description), node: node2 }))
|
|
25197
25246
|
};
|
|
25198
|
-
const causalNudge =
|
|
25247
|
+
const causalNudge = recent4.length >= 2 && recent4.every(
|
|
25199
25248
|
(r) => opts.store.outEdges(r.node.id, ["CAUSED_BY"]).length === 0 && opts.store.inEdges(r.node.id, ["CAUSED_BY"]).length === 0
|
|
25200
25249
|
);
|
|
25201
|
-
const abstractUndomained =
|
|
25250
|
+
const abstractUndomained = recent4.filter((r) => {
|
|
25202
25251
|
const grounded = opts.store.outEdges(r.node.id, ["ANCHORED_AT", "OCCURS_IN", "DEPENDS_ON"]).length > 0;
|
|
25203
25252
|
const hasDomain = opts.store.outEdges(r.node.id, ["PERTAIN_TO"]).length > 0;
|
|
25204
25253
|
return !grounded && !hasDomain;
|
|
@@ -25207,7 +25256,7 @@ function buildSnapshot(opts) {
|
|
|
25207
25256
|
return {
|
|
25208
25257
|
profile: opts.profile,
|
|
25209
25258
|
profileContext,
|
|
25210
|
-
recentProblems:
|
|
25259
|
+
recentProblems: recent4,
|
|
25211
25260
|
recentResolved,
|
|
25212
25261
|
recentConstraints,
|
|
25213
25262
|
regressions,
|
|
@@ -26597,11 +26646,27 @@ var init_client = __esm({
|
|
|
26597
26646
|
}
|
|
26598
26647
|
}
|
|
26599
26648
|
for (const edgeChunk of chunkArray(pendingEdges, MAX_EDGES_PER_PAYLOAD)) {
|
|
26600
|
-
const
|
|
26649
|
+
const projected = edgeChunk.map((e) => ({
|
|
26601
26650
|
...e,
|
|
26602
26651
|
from: echoedCloudId.get(e.from) ?? e.from,
|
|
26603
26652
|
to: echoedCloudId.get(e.to) ?? e.to
|
|
26604
26653
|
}));
|
|
26654
|
+
const rewritten = projected.filter((e, i2) => {
|
|
26655
|
+
if (e.from !== e.to) return true;
|
|
26656
|
+
const original = edgeChunk[i2];
|
|
26657
|
+
merged.edges.push({
|
|
26658
|
+
from: original.from,
|
|
26659
|
+
to: original.to,
|
|
26660
|
+
type: original.type,
|
|
26661
|
+
decision: "rejected",
|
|
26662
|
+
reason: "endpoints collapsed to the same cloud node"
|
|
26663
|
+
});
|
|
26664
|
+
return false;
|
|
26665
|
+
});
|
|
26666
|
+
if (rewritten.length === 0) {
|
|
26667
|
+
emit([], 0);
|
|
26668
|
+
continue;
|
|
26669
|
+
}
|
|
26605
26670
|
const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: rewritten }, randomUUID()));
|
|
26606
26671
|
emit([], summarizeIngestResult(r).accepted);
|
|
26607
26672
|
merged.edges.push(...r.edges);
|
|
@@ -43231,58 +43296,32 @@ function listSourceFiles(root) {
|
|
|
43231
43296
|
}
|
|
43232
43297
|
async function findStaleFiles(store, rootPath, workspaceId2) {
|
|
43233
43298
|
const stale = [];
|
|
43234
|
-
|
|
43235
|
-
const
|
|
43299
|
+
let pending = [];
|
|
43300
|
+
const check2 = () => {
|
|
43301
|
+
const states = new Map(store.fileIndexStates(pending.map((f) => f.id)).map((s) => [s.id, s]));
|
|
43302
|
+
for (const f of pending) {
|
|
43303
|
+
const state = states.get(f.id);
|
|
43304
|
+
if (!state || state.validTo !== null || state.lastUpdatedAt < f.mtimeMs || !state.complete) stale.push(f.abs);
|
|
43305
|
+
}
|
|
43306
|
+
pending = [];
|
|
43307
|
+
};
|
|
43236
43308
|
let scanned = 0;
|
|
43237
43309
|
for (const abs of listSourceFiles(rootPath)) {
|
|
43238
|
-
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
43239
|
-
let mtimeMs;
|
|
43240
43310
|
try {
|
|
43241
|
-
|
|
43311
|
+
const rel = relative3(rootPath, abs).split(sep2).join("/");
|
|
43312
|
+
pending.push({ abs, id: fileNodeId(workspaceId2, rel), mtimeMs: statSync4(abs).mtimeMs });
|
|
43242
43313
|
} catch {
|
|
43243
|
-
continue;
|
|
43244
43314
|
}
|
|
43245
|
-
|
|
43246
|
-
|
|
43247
|
-
|
|
43248
|
-
stale.push(abs);
|
|
43249
|
-
} else if ((fnode.validTo ?? null) !== null) {
|
|
43250
|
-
stale.push(abs);
|
|
43251
|
-
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
43252
|
-
stale.push(abs);
|
|
43253
|
-
} else {
|
|
43254
|
-
const edges = store.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
43255
|
-
if (edges.length === 0) {
|
|
43256
|
-
const converged = fnode.attrs["symbolCount"] === 0 || typeof fnode.attrs["indexSkipped"] === "string";
|
|
43257
|
-
if (!converged) stale.push(abs);
|
|
43258
|
-
} else {
|
|
43259
|
-
const targets = edges.map((e) => e.to);
|
|
43260
|
-
pending.push({ abs, targets });
|
|
43261
|
-
allTargets.push(...targets);
|
|
43262
|
-
}
|
|
43315
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) {
|
|
43316
|
+
check2();
|
|
43317
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
43263
43318
|
}
|
|
43264
43319
|
}
|
|
43265
|
-
|
|
43266
|
-
for (let i2 = 0; i2 < allTargets.length; i2 += LIVENESS_CHUNK) {
|
|
43267
|
-
for (const id of store.liveNodeIds(allTargets.slice(i2, i2 + LIVENESS_CHUNK))) live3.add(id);
|
|
43268
|
-
if (i2 + LIVENESS_CHUNK < allTargets.length) await new Promise((r) => setImmediate(r));
|
|
43269
|
-
}
|
|
43270
|
-
for (const { abs, targets } of pending) {
|
|
43271
|
-
if (targets.some((id) => !live3.has(id))) stale.push(abs);
|
|
43272
|
-
}
|
|
43320
|
+
check2();
|
|
43273
43321
|
return stale;
|
|
43274
43322
|
}
|
|
43275
|
-
function isLive(n) {
|
|
43276
|
-
return n != null && (n.validTo ?? null) === null;
|
|
43277
|
-
}
|
|
43278
43323
|
function findStrandedSymbols(store) {
|
|
43279
|
-
|
|
43280
|
-
for (const file2 of store.findNodesByLabel("File")) {
|
|
43281
|
-
for (const e of store.outEdges(file2.id, ["DEFINES", "CONTAINS"])) {
|
|
43282
|
-
if (!isLive(store.getNode(e.to))) stranded.push({ fileId: file2.id, symbolId: e.to });
|
|
43283
|
-
}
|
|
43284
|
-
}
|
|
43285
|
-
return stranded;
|
|
43324
|
+
return store.strandedSymbols();
|
|
43286
43325
|
}
|
|
43287
43326
|
function reapOrphanedFiles(store, rootPath, workspaceId2, ignoreDirs = []) {
|
|
43288
43327
|
const ignores = /* @__PURE__ */ new Set([...DEFAULT_IGNORES, ...ignoreDirs]);
|
|
@@ -43344,7 +43383,7 @@ async function reconcileStaleFiles(store, rootPath, workspaceId2) {
|
|
|
43344
43383
|
}
|
|
43345
43384
|
return total;
|
|
43346
43385
|
}
|
|
43347
|
-
var IGNORED, SOURCE_RE, SCAN_YIELD_EVERY,
|
|
43386
|
+
var IGNORED, SOURCE_RE, SCAN_YIELD_EVERY, ORPHAN_REAP_MAX_FRACTION, ORPHAN_REAP_MIN_ALLOWANCE;
|
|
43348
43387
|
var init_reconcile = __esm({
|
|
43349
43388
|
"src/reconcile.ts"() {
|
|
43350
43389
|
"use strict";
|
|
@@ -43352,7 +43391,6 @@ var init_reconcile = __esm({
|
|
|
43352
43391
|
IGNORED = /[\\/](?:\.git|node_modules|\.errata|dist|__pycache__)(?:[\\/]|$)/;
|
|
43353
43392
|
SOURCE_RE = /\.(ts|tsx|js|jsx|mjs|cjs|py)$/i;
|
|
43354
43393
|
SCAN_YIELD_EVERY = 100;
|
|
43355
|
-
LIVENESS_CHUNK = 4e3;
|
|
43356
43394
|
ORPHAN_REAP_MAX_FRACTION = 0.25;
|
|
43357
43395
|
ORPHAN_REAP_MIN_ALLOWANCE = 50;
|
|
43358
43396
|
}
|
|
@@ -72709,13 +72747,13 @@ function esc3(s) {
|
|
|
72709
72747
|
function slug(name2) {
|
|
72710
72748
|
return name2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
|
|
72711
72749
|
}
|
|
72712
|
-
function
|
|
72750
|
+
function isLive2(verb) {
|
|
72713
72751
|
return LIVE_VERBS.has(verb);
|
|
72714
72752
|
}
|
|
72715
72753
|
function nextHtml(n, opts) {
|
|
72716
72754
|
if (!n) return "";
|
|
72717
72755
|
const cmd2 = `errata ${n.verb}${n.arg ? " " + n.arg : ""}`;
|
|
72718
|
-
const live3 =
|
|
72756
|
+
const live3 = isLive2(n.verb);
|
|
72719
72757
|
if (!live3 && !opts.includeFutureVerbs) return "";
|
|
72720
72758
|
const hint = n.hint ? ` \u2014 ${esc3(n.hint)}` : "";
|
|
72721
72759
|
const cls = live3 ? "next" : "next soon";
|
|
@@ -78352,14 +78390,42 @@ import { Worker } from "node:worker_threads";
|
|
|
78352
78390
|
// src/pass-gate.ts
|
|
78353
78391
|
var QUEUE_WARN_MS = 12e4;
|
|
78354
78392
|
var chain = Promise.resolve();
|
|
78393
|
+
var nextId = 0;
|
|
78394
|
+
var waiting = /* @__PURE__ */ new Map();
|
|
78395
|
+
var active = null;
|
|
78396
|
+
var recent2 = [];
|
|
78397
|
+
function heavyPassState() {
|
|
78398
|
+
const now = Date.now();
|
|
78399
|
+
return {
|
|
78400
|
+
active: active ? { label: active.label, runningMs: now - active.startedAt, waitedMs: active.waitedMs } : null,
|
|
78401
|
+
waiting: waiting.size,
|
|
78402
|
+
pending: [...waiting.values()].slice(0, 32).map((p) => ({ label: p.label, waitedMs: now - p.queuedAt })),
|
|
78403
|
+
recent: [...recent2]
|
|
78404
|
+
};
|
|
78405
|
+
}
|
|
78355
78406
|
function withHeavyPassSlot(label, fn) {
|
|
78356
78407
|
const queuedAt = Date.now();
|
|
78357
|
-
const
|
|
78408
|
+
const id = nextId++;
|
|
78409
|
+
waiting.set(id, { label, queuedAt });
|
|
78410
|
+
const run3 = chain.then(async () => {
|
|
78358
78411
|
const waited = Date.now() - queuedAt;
|
|
78412
|
+
waiting.delete(id);
|
|
78413
|
+
const startedAt = Date.now();
|
|
78414
|
+
active = { label, startedAt, waitedMs: waited };
|
|
78359
78415
|
if (waited > QUEUE_WARN_MS) {
|
|
78360
78416
|
console.warn(`[errata] heavy-pass slot: ${label} waited ${Math.round(waited / 1e3)}s`);
|
|
78361
78417
|
}
|
|
78362
|
-
|
|
78418
|
+
let failed = false;
|
|
78419
|
+
try {
|
|
78420
|
+
return await fn();
|
|
78421
|
+
} catch (err2) {
|
|
78422
|
+
failed = true;
|
|
78423
|
+
throw err2;
|
|
78424
|
+
} finally {
|
|
78425
|
+
recent2.push({ label, waitedMs: waited, durationMs: Date.now() - startedAt, failed });
|
|
78426
|
+
if (recent2.length > 16) recent2.shift();
|
|
78427
|
+
active = null;
|
|
78428
|
+
}
|
|
78363
78429
|
});
|
|
78364
78430
|
chain = run3.then(
|
|
78365
78431
|
() => void 0,
|
|
@@ -78378,6 +78444,12 @@ var PassWorker = class {
|
|
|
78378
78444
|
pending = /* @__PURE__ */ new Map();
|
|
78379
78445
|
nextId = 1;
|
|
78380
78446
|
stopped = false;
|
|
78447
|
+
/** Open/migrate/index the database on the worker before the serving thread
|
|
78448
|
+
* opens its connection. Additive indexes and one-time repairs can take tens
|
|
78449
|
+
* of seconds on an existing workspace; a prepared reopen is cheap. */
|
|
78450
|
+
prepareStore() {
|
|
78451
|
+
return this.call("prepareStore");
|
|
78452
|
+
}
|
|
78381
78453
|
/** Run the nightly graph-math + induction pass on the worker. */
|
|
78382
78454
|
nightly() {
|
|
78383
78455
|
return this.call("nightly");
|
|
@@ -81326,7 +81398,7 @@ ${GITIGNORE_LINES.join("\n")}
|
|
|
81326
81398
|
var INTENT_TTL_MS = 15 * 60 * 1e3;
|
|
81327
81399
|
function createIntentState(ttlMs = INTENT_TTL_MS) {
|
|
81328
81400
|
const bySession = /* @__PURE__ */ new Map();
|
|
81329
|
-
let
|
|
81401
|
+
let recent4 = null;
|
|
81330
81402
|
const live3 = (s, nowMs) => s !== void 0 && nowMs - s.atMs < ttlMs;
|
|
81331
81403
|
return {
|
|
81332
81404
|
set(sessionId, text, nowMs) {
|
|
@@ -81334,8 +81406,8 @@ function createIntentState(ttlMs = INTENT_TTL_MS) {
|
|
|
81334
81406
|
if (!trimmed) return false;
|
|
81335
81407
|
const prev = bySession.get(sessionId);
|
|
81336
81408
|
bySession.set(sessionId, { text: trimmed, atMs: nowMs });
|
|
81337
|
-
const changed = !(
|
|
81338
|
-
|
|
81409
|
+
const changed = !(recent4 && recent4.text === trimmed);
|
|
81410
|
+
recent4 = { sessionId, text: trimmed, atMs: nowMs };
|
|
81339
81411
|
if (!changed && prev && prev.text === trimmed) return false;
|
|
81340
81412
|
return changed;
|
|
81341
81413
|
},
|
|
@@ -81344,12 +81416,12 @@ function createIntentState(ttlMs = INTENT_TTL_MS) {
|
|
|
81344
81416
|
return live3(s, nowMs) ? s.text : void 0;
|
|
81345
81417
|
},
|
|
81346
81418
|
mostRecent(nowMs) {
|
|
81347
|
-
if (!
|
|
81348
|
-
return nowMs -
|
|
81419
|
+
if (!recent4) return void 0;
|
|
81420
|
+
return nowMs - recent4.atMs < ttlMs ? recent4.text : void 0;
|
|
81349
81421
|
},
|
|
81350
81422
|
clear(sessionId) {
|
|
81351
81423
|
bySession.delete(sessionId);
|
|
81352
|
-
if (
|
|
81424
|
+
if (recent4?.sessionId === sessionId) recent4 = null;
|
|
81353
81425
|
}
|
|
81354
81426
|
};
|
|
81355
81427
|
}
|
|
@@ -81357,19 +81429,19 @@ function createIntentState(ttlMs = INTENT_TTL_MS) {
|
|
|
81357
81429
|
// src/working-file-state.ts
|
|
81358
81430
|
function createWorkingFileState() {
|
|
81359
81431
|
const bySession = /* @__PURE__ */ new Map();
|
|
81360
|
-
let
|
|
81432
|
+
let recent4 = null;
|
|
81361
81433
|
return {
|
|
81362
81434
|
set(sessionId, rel) {
|
|
81363
81435
|
bySession.set(sessionId, rel);
|
|
81364
|
-
if (rel ===
|
|
81365
|
-
|
|
81436
|
+
if (rel === recent4) return false;
|
|
81437
|
+
recent4 = rel;
|
|
81366
81438
|
return true;
|
|
81367
81439
|
},
|
|
81368
81440
|
get(sessionId) {
|
|
81369
81441
|
return bySession.get(sessionId);
|
|
81370
81442
|
},
|
|
81371
81443
|
mostRecent() {
|
|
81372
|
-
return
|
|
81444
|
+
return recent4;
|
|
81373
81445
|
}
|
|
81374
81446
|
};
|
|
81375
81447
|
}
|
|
@@ -81475,11 +81547,11 @@ function pendingFor(queue, channel) {
|
|
|
81475
81547
|
}
|
|
81476
81548
|
function partitionReplayable(queue, channel, isShipped) {
|
|
81477
81549
|
const replay2 = [];
|
|
81478
|
-
const
|
|
81550
|
+
const waiting2 = [];
|
|
81479
81551
|
for (const w of pendingFor(queue, channel)) {
|
|
81480
|
-
(isShipped(w.nodeId) ? replay2 :
|
|
81552
|
+
(isShipped(w.nodeId) ? replay2 : waiting2).push(w);
|
|
81481
81553
|
}
|
|
81482
|
-
return { replay: replay2, waiting };
|
|
81554
|
+
return { replay: replay2, waiting: waiting2 };
|
|
81483
81555
|
}
|
|
81484
81556
|
function retireWitnesses(queue, channel, settledKeys) {
|
|
81485
81557
|
return queue.filter((w) => !(w.channel === channel && settledKeys.has(w.witnessKey)));
|
|
@@ -81749,25 +81821,25 @@ import { join as join34 } from "node:path";
|
|
|
81749
81821
|
// src/loop-lag.ts
|
|
81750
81822
|
var HEARTBEAT_MS = 500;
|
|
81751
81823
|
var RECENT_PASS_LIMIT = 12;
|
|
81752
|
-
var
|
|
81824
|
+
var active2 = /* @__PURE__ */ new Map();
|
|
81753
81825
|
var nextPassId = 1;
|
|
81754
81826
|
var started = false;
|
|
81755
|
-
var
|
|
81827
|
+
var recent3 = [];
|
|
81756
81828
|
function markPass(name2) {
|
|
81757
81829
|
const id = nextPassId++;
|
|
81758
81830
|
const startedAt = Date.now();
|
|
81759
|
-
|
|
81831
|
+
active2.set(id, { name: name2, startedAt });
|
|
81760
81832
|
let ended = false;
|
|
81761
81833
|
return () => {
|
|
81762
81834
|
if (ended) return;
|
|
81763
81835
|
ended = true;
|
|
81764
|
-
|
|
81765
|
-
|
|
81766
|
-
if (
|
|
81836
|
+
active2.delete(id);
|
|
81837
|
+
recent3.unshift({ name: name2, ms: Date.now() - startedAt, endedAt: Date.now() });
|
|
81838
|
+
if (recent3.length > RECENT_PASS_LIMIT) recent3.pop();
|
|
81767
81839
|
};
|
|
81768
81840
|
}
|
|
81769
81841
|
function oldestActive() {
|
|
81770
|
-
for (const p of
|
|
81842
|
+
for (const p of active2.values()) return p;
|
|
81771
81843
|
return null;
|
|
81772
81844
|
}
|
|
81773
81845
|
function passState() {
|
|
@@ -81776,8 +81848,8 @@ function passState() {
|
|
|
81776
81848
|
return {
|
|
81777
81849
|
current: oldest?.name ?? "idle",
|
|
81778
81850
|
runningMs: oldest === null ? null : now - oldest.startedAt,
|
|
81779
|
-
active: [...
|
|
81780
|
-
recent: [...
|
|
81851
|
+
active: [...active2.values()].map((p) => ({ name: p.name, runningMs: now - p.startedAt })),
|
|
81852
|
+
recent: [...recent3]
|
|
81781
81853
|
};
|
|
81782
81854
|
}
|
|
81783
81855
|
function describeHolders(running, finished, windowStart, now) {
|
|
@@ -81796,7 +81868,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
|
|
|
81796
81868
|
const now = Date.now();
|
|
81797
81869
|
const lag = now - last - HEARTBEAT_MS;
|
|
81798
81870
|
if (lag > thresholdMs) {
|
|
81799
|
-
const who = describeHolders(
|
|
81871
|
+
const who = describeHolders(active2.values(), recent3, last, now);
|
|
81800
81872
|
console.warn(
|
|
81801
81873
|
`[loop-lag] ${(/* @__PURE__ */ new Date()).toISOString()} event loop held ~${(lag / 1e3).toFixed(1)}s during "${who}"`
|
|
81802
81874
|
);
|
|
@@ -82087,7 +82159,7 @@ function createWatchBreaker(deps) {
|
|
|
82087
82159
|
}
|
|
82088
82160
|
|
|
82089
82161
|
// src/engine.ts
|
|
82090
|
-
var DAEMON_VERSION = true ? "2.0.17-dev.
|
|
82162
|
+
var DAEMON_VERSION = true ? "2.0.17-dev.3435" : "2.0.0-alpha.0";
|
|
82091
82163
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
82092
82164
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
82093
82165
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -83821,15 +83893,15 @@ function createWorkspaceEngine(opts) {
|
|
|
83821
83893
|
}
|
|
83822
83894
|
witnessQueue = healed;
|
|
83823
83895
|
fresh = routedFresh;
|
|
83824
|
-
const { replay: queued, waiting } = partitionReplayable(witnessQueue, channel, isShipped);
|
|
83896
|
+
const { replay: queued, waiting: waiting2 } = partitionReplayable(witnessQueue, channel, isShipped);
|
|
83825
83897
|
if (fresh.length === 0 && queued.length === 0) {
|
|
83826
83898
|
if (undeliverable > 0) {
|
|
83827
83899
|
saveWitnessQueue(witnessQueuePath(paths.configDir), witnessQueue);
|
|
83828
83900
|
appendWitnessLedger(paths.configDir, { ts: Date.now(), channel, undeliverable, parked: pendingFor(witnessQueue, channel).length });
|
|
83829
83901
|
console.log(`[errata] ${channel}: ${undeliverable} witness(es) retired \u2014 cited node class never ships (counted, not parked)`);
|
|
83830
83902
|
}
|
|
83831
|
-
if (
|
|
83832
|
-
console.log(`[errata] ${channel}: ${
|
|
83903
|
+
if (waiting2.length > 0) {
|
|
83904
|
+
console.log(`[errata] ${channel}: ${waiting2.length} parked awaiting node drain`);
|
|
83833
83905
|
}
|
|
83834
83906
|
return;
|
|
83835
83907
|
}
|
|
@@ -83888,7 +83960,7 @@ function createWorkspaceEngine(opts) {
|
|
|
83888
83960
|
appendPassLedger(paths.configDir, "witness-queue", 0, {
|
|
83889
83961
|
recovered,
|
|
83890
83962
|
replayed: queued.length,
|
|
83891
|
-
awaitingDrain:
|
|
83963
|
+
awaitingDrain: waiting2.length,
|
|
83892
83964
|
parked: pendingFor(witnessQueue, channel).length,
|
|
83893
83965
|
expiredTtl: enq.dropped.expiredTtl,
|
|
83894
83966
|
exhaustedAttempts: enq.dropped.exhaustedAttempts,
|
|
@@ -83897,7 +83969,7 @@ function createWorkspaceEngine(opts) {
|
|
|
83897
83969
|
});
|
|
83898
83970
|
}
|
|
83899
83971
|
console.log(
|
|
83900
|
-
`[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "") + (
|
|
83972
|
+
`[errata] ${channel}: ${formatDisposition({ recorded, unmatched, duplicate, selfGated })}` + (queued.length > 0 ? ` (incl. ${queued.length} replayed)` : "") + (waiting2.length > 0 ? ` (${waiting2.length} awaiting drain)` : "")
|
|
83901
83973
|
);
|
|
83902
83974
|
appendWitnessLedger(paths.configDir, {
|
|
83903
83975
|
ts: Date.now(),
|
|
@@ -83907,7 +83979,7 @@ function createWorkspaceEngine(opts) {
|
|
|
83907
83979
|
duplicate,
|
|
83908
83980
|
selfGated,
|
|
83909
83981
|
parked: pendingFor(witnessQueue, channel).length,
|
|
83910
|
-
awaitingDrain:
|
|
83982
|
+
awaitingDrain: waiting2.length,
|
|
83911
83983
|
...undeliverable > 0 ? { undeliverable } : {}
|
|
83912
83984
|
});
|
|
83913
83985
|
};
|
|
@@ -85560,7 +85632,8 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
85560
85632
|
sidecarNoLexicon: 0,
|
|
85561
85633
|
sidecarNoSummary: 0,
|
|
85562
85634
|
fileRefsSubstituted: 0,
|
|
85563
|
-
fileRefLexiconMisses: 0
|
|
85635
|
+
fileRefLexiconMisses: 0,
|
|
85636
|
+
collapsedEdges: 0
|
|
85564
85637
|
};
|
|
85565
85638
|
const sharedCauses = /* @__PURE__ */ new Map();
|
|
85566
85639
|
const causalQueue = store.findNodesByLabel("Problem").filter(
|
|
@@ -85785,7 +85858,13 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
85785
85858
|
];
|
|
85786
85859
|
const dedup = /* @__PURE__ */ new Map();
|
|
85787
85860
|
for (const e of rawOuts) dedup.set(`${e.type}>${e.to}`, e);
|
|
85788
|
-
const outs = [...dedup.values()].map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) =>
|
|
85861
|
+
const outs = [...dedup.values()].map((e) => ({ e, wireTo: semanticEndpoints.get(e.to) })).filter((x) => {
|
|
85862
|
+
if (x.wireTo === wireFrom) {
|
|
85863
|
+
composition.collapsedEdges++;
|
|
85864
|
+
return false;
|
|
85865
|
+
}
|
|
85866
|
+
return x.wireTo != null;
|
|
85867
|
+
});
|
|
85789
85868
|
if (outs.length === 0) continue;
|
|
85790
85869
|
const dg = digest(outs.map(({ e, wireTo }) => `${e.type}>${wireTo}`).sort());
|
|
85791
85870
|
const owner = nodeOf(sourceId);
|
|
@@ -86115,6 +86194,19 @@ var ConsolidateWorker = class {
|
|
|
86115
86194
|
}
|
|
86116
86195
|
};
|
|
86117
86196
|
|
|
86197
|
+
// src/prepare-store.ts
|
|
86198
|
+
init_src5();
|
|
86199
|
+
async function prepareStore(path2, root, workspaceId2) {
|
|
86200
|
+
if (!import.meta.url.endsWith(".mjs") || path2 === ":memory:") return;
|
|
86201
|
+
if (!graphStoreNeedsPreparation(path2)) return;
|
|
86202
|
+
const worker = new PassWorker({ castaliaPath: path2, workspaceRoot: root, workspaceId: workspaceId2 });
|
|
86203
|
+
try {
|
|
86204
|
+
await worker.prepareStore();
|
|
86205
|
+
} finally {
|
|
86206
|
+
await worker.stop();
|
|
86207
|
+
}
|
|
86208
|
+
}
|
|
86209
|
+
|
|
86118
86210
|
// src/multi.ts
|
|
86119
86211
|
init_paths();
|
|
86120
86212
|
|
|
@@ -86551,11 +86643,11 @@ function cachedMap(cache2, store, key, build) {
|
|
|
86551
86643
|
}
|
|
86552
86644
|
async function lookupLive(store, m, id) {
|
|
86553
86645
|
const first = (await m.get()).get(id);
|
|
86554
|
-
if (first &&
|
|
86646
|
+
if (first && isLive(store, first)) return first;
|
|
86555
86647
|
const second = (await m.refresh()).get(id);
|
|
86556
|
-
return second &&
|
|
86648
|
+
return second && isLive(store, second) ? second : null;
|
|
86557
86649
|
}
|
|
86558
|
-
function
|
|
86650
|
+
function isLive(store, id) {
|
|
86559
86651
|
const n = store.getNode(id);
|
|
86560
86652
|
return n !== null && (n.validTo === null || n.validTo === void 0);
|
|
86561
86653
|
}
|
|
@@ -87589,6 +87681,10 @@ async function walk10(root, input, client, opts, readBlob) {
|
|
|
87589
87681
|
const launched = /* @__PURE__ */ new Map();
|
|
87590
87682
|
const cap = opts.maxUnits;
|
|
87591
87683
|
const edgeParked = /* @__PURE__ */ new Set();
|
|
87684
|
+
const parkedEarlier = new Set((profile.historyStage?.coverageGaps ?? []).filter((g) => g.reason === "edge-blocked").map((g) => `${g.sha}/${g.subIndex}`));
|
|
87685
|
+
units.forEach((u, j) => {
|
|
87686
|
+
if (parkedEarlier.has(`${u.sha}/${u.subIndex}`)) edgeParked.add(j);
|
|
87687
|
+
});
|
|
87592
87688
|
const predecessor = (k) => {
|
|
87593
87689
|
for (let j = k - 1; j >= 0; j--) if (!units[j].skip && !edgeParked.has(j)) return units[j];
|
|
87594
87690
|
return void 0;
|
|
@@ -87654,7 +87750,7 @@ async function walk10(root, input, client, opts, readBlob) {
|
|
|
87654
87750
|
let ahead = 0;
|
|
87655
87751
|
for (let j = k + 1; j < units.length && ahead < lookahead; j++) {
|
|
87656
87752
|
if (cap !== void 0 && processed + (j - k) >= cap) break;
|
|
87657
|
-
if (units[j].skip) continue;
|
|
87753
|
+
if (units[j].skip || edgeParked.has(j)) continue;
|
|
87658
87754
|
ahead += 1;
|
|
87659
87755
|
await launch(j);
|
|
87660
87756
|
}
|
|
@@ -87675,6 +87771,13 @@ async function walk10(root, input, client, opts, readBlob) {
|
|
|
87675
87771
|
log(`[errata] history ${label} skipped (${u.skip})`);
|
|
87676
87772
|
continue;
|
|
87677
87773
|
}
|
|
87774
|
+
if (edgeParked.has(k)) {
|
|
87775
|
+
profile = stage(profile, { state: dryRun ? "extracting" : "contributing", unitsDone: doneSoFar, counts, ...lastInPosition ? { watermarkSha: positionSha } : {} }, now);
|
|
87776
|
+
persist(profile);
|
|
87777
|
+
processed += 1;
|
|
87778
|
+
log(`[errata] history ${label}: edge-blocked on an earlier run; gap kept, not re-sent`);
|
|
87779
|
+
continue;
|
|
87780
|
+
}
|
|
87678
87781
|
const { body: body2, prepMs } = await prepare(k);
|
|
87679
87782
|
profile = stage(profile, { state: dryRun ? "extracting" : "contributing", unitsDone: doneSoFar - 1, lastError: void 0 }, now);
|
|
87680
87783
|
persist(profile);
|
|
@@ -88806,6 +88909,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
88806
88909
|
};
|
|
88807
88910
|
const sharedDbPath = sharedStorePath();
|
|
88808
88911
|
ensureParent(sharedDbPath);
|
|
88912
|
+
await prepareStore(sharedDbPath, process.cwd(), "shared");
|
|
88809
88913
|
const sharedStore = openGraphStore({ path: sharedDbPath });
|
|
88810
88914
|
const bundledRun = import.meta.url.endsWith(".mjs");
|
|
88811
88915
|
const consolidateWorker = bundledRun && sharedDbPath !== ":memory:" ? new ConsolidateWorker() : null;
|
|
@@ -88964,8 +89068,13 @@ async function startMultiDaemon(opts = {}) {
|
|
|
88964
89068
|
}
|
|
88965
89069
|
}
|
|
88966
89070
|
};
|
|
88967
|
-
|
|
89071
|
+
let stopping = false;
|
|
89072
|
+
const buildRecord = async (entry) => {
|
|
88968
89073
|
const id = entry.id;
|
|
89074
|
+
const castaliaPath = workspacePaths(entry.path).castalia;
|
|
89075
|
+
ensureParent(castaliaPath);
|
|
89076
|
+
await prepareStore(castaliaPath, entry.path, id);
|
|
89077
|
+
if (stopping) throw new Error("daemon stopping");
|
|
88969
89078
|
const wsIdentity = identityFor(entry);
|
|
88970
89079
|
let wsCloud = cloud;
|
|
88971
89080
|
if (wsIdentity.rule === "workspace-binding") {
|
|
@@ -89048,7 +89157,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89048
89157
|
if (opts.eagerWorkspaces ?? EAGER_WORKSPACES_AT_BOOT) {
|
|
89049
89158
|
for (const entry of entries) {
|
|
89050
89159
|
try {
|
|
89051
|
-
records.push(buildRecord(entry));
|
|
89160
|
+
records.push(await buildRecord(entry));
|
|
89052
89161
|
} catch (err2) {
|
|
89053
89162
|
console.warn(`[errata] skipping ${entry.name} (${entry.path}): ${err2 instanceof Error ? err2.message : err2}`);
|
|
89054
89163
|
}
|
|
@@ -89066,14 +89175,24 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89066
89175
|
}
|
|
89067
89176
|
const app = new Hono2();
|
|
89068
89177
|
const enqueueWakeIndex = serialQueue();
|
|
89178
|
+
const attaching = /* @__PURE__ */ new Map();
|
|
89069
89179
|
const attachWorkspace = (root) => {
|
|
89180
|
+
const norm = normPath(root);
|
|
89181
|
+
const pending = attaching.get(norm);
|
|
89182
|
+
if (pending) return pending;
|
|
89183
|
+
const run3 = attachWorkspaceOnce(root).finally(() => attaching.delete(norm));
|
|
89184
|
+
attaching.set(norm, run3);
|
|
89185
|
+
return run3;
|
|
89186
|
+
};
|
|
89187
|
+
const attachWorkspaceOnce = async (root) => {
|
|
89188
|
+
if (stopping) return { attached: false, id: null, reason: "daemon-stopping" };
|
|
89070
89189
|
const norm = normPath(root);
|
|
89071
89190
|
if (records.some((r) => normPath(r.root) === norm)) return { attached: false, id: null, reason: "already-watched" };
|
|
89072
89191
|
const entry = listWorkspaceEntries().find((e) => normPath(e.path) === norm);
|
|
89073
89192
|
if (!entry) return { attached: false, id: null, reason: "not-registered" };
|
|
89074
89193
|
let rec;
|
|
89075
89194
|
try {
|
|
89076
|
-
rec = buildRecord(entry);
|
|
89195
|
+
rec = await buildRecord(entry);
|
|
89077
89196
|
} catch (err2) {
|
|
89078
89197
|
return { attached: false, id: null, reason: err2 instanceof Error ? err2.message : "build-failed" };
|
|
89079
89198
|
}
|
|
@@ -89117,7 +89236,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89117
89236
|
}
|
|
89118
89237
|
const root = typeof body2["path"] === "string" ? body2["path"] : null;
|
|
89119
89238
|
if (!root) return c.json({ error: "path-required" }, 400);
|
|
89120
|
-
return c.json(attachWorkspace(root));
|
|
89239
|
+
return c.json(await attachWorkspace(root));
|
|
89121
89240
|
});
|
|
89122
89241
|
app.post("/api/notify-test", (c) => {
|
|
89123
89242
|
notify("Errata \xB7 test", "Fired from the daemon \u2014 if you see this, daemon toasts render.");
|
|
@@ -89202,6 +89321,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89202
89321
|
// pegged daemon is only diagnosable by attaching a CPU profiler to the
|
|
89203
89322
|
// live process — see watch-census.ts for the incident that proved it.
|
|
89204
89323
|
pass: passState(),
|
|
89324
|
+
heavyPass: heavyPassState(),
|
|
89205
89325
|
watch: watchCensus(),
|
|
89206
89326
|
// Registered but with no engine running (WS-cold-start). Listed so a cold
|
|
89207
89327
|
// daemon is legible as "nothing has needed waking yet" rather than as
|
|
@@ -89245,7 +89365,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89245
89365
|
if (!main2) continue;
|
|
89246
89366
|
owner = ownerOf(records, main2);
|
|
89247
89367
|
if (!owner && listWorkspaceEntries().some((e) => pickOwnerRoot([e.path], main2))) {
|
|
89248
|
-
if (attachWorkspace(main2).attached) owner = ownerOf(records, main2);
|
|
89368
|
+
if ((await attachWorkspace(main2)).attached) owner = ownerOf(records, main2);
|
|
89249
89369
|
}
|
|
89250
89370
|
if (owner) break;
|
|
89251
89371
|
}
|
|
@@ -89253,7 +89373,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89253
89373
|
if (!owner && others.length > 0) {
|
|
89254
89374
|
const registryRoots = listWorkspaceEntries().map((e) => e.path);
|
|
89255
89375
|
const registered = others.map((p) => pickOwnerRoot(registryRoots, p)).find((r) => r != null);
|
|
89256
|
-
if (registered && attachWorkspace(registered).attached) {
|
|
89376
|
+
if (registered && (await attachWorkspace(registered)).attached) {
|
|
89257
89377
|
owner = records.find((r) => r.root === registered) ?? null;
|
|
89258
89378
|
if (owner) console.log(`[errata] woke workspace ${owner.entry.name}`);
|
|
89259
89379
|
}
|
|
@@ -89262,7 +89382,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89262
89382
|
const missPath = pathInBody(body2);
|
|
89263
89383
|
const registeredRoot = missPath ? pickOwnerRoot(listWorkspaceEntries().map((e) => e.path), missPath) : null;
|
|
89264
89384
|
const wakeRoot = registeredRoot ?? (missPath ? findGitRoot(missPath) : null);
|
|
89265
|
-
if (wakeRoot && attachWorkspace(wakeRoot).attached) {
|
|
89385
|
+
if (wakeRoot && (await attachWorkspace(wakeRoot)).attached) {
|
|
89266
89386
|
owner = ownerOf(records, missPath) ?? null;
|
|
89267
89387
|
if (owner) console.log(`[errata] woke workspace ${owner.entry.name}`);
|
|
89268
89388
|
}
|
|
@@ -89271,7 +89391,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89271
89391
|
const root = sessionAffinity.rootFor(body2["session_id"]);
|
|
89272
89392
|
if (root) {
|
|
89273
89393
|
owner = records.find((r) => r.root === root) ?? null;
|
|
89274
|
-
if (!owner && attachWorkspace(root).attached) owner = records.find((r) => r.root === root) ?? null;
|
|
89394
|
+
if (!owner && (await attachWorkspace(root)).attached) owner = records.find((r) => r.root === root) ?? null;
|
|
89275
89395
|
}
|
|
89276
89396
|
}
|
|
89277
89397
|
if (!owner) {
|
|
@@ -89409,9 +89529,11 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89409
89529
|
});
|
|
89410
89530
|
candidates.forEach((c, i2) => {
|
|
89411
89531
|
const wake = setTimeout(() => {
|
|
89412
|
-
|
|
89413
|
-
|
|
89414
|
-
|
|
89532
|
+
void attachWorkspace(c.path).then((result) => {
|
|
89533
|
+
if (result.attached) {
|
|
89534
|
+
console.log(`[errata] woke workspace ${c.name} \u2014 recent Codex/Gemini/OpenCode transcript (headless capture)`);
|
|
89535
|
+
}
|
|
89536
|
+
});
|
|
89415
89537
|
}, wakeDelay(i2));
|
|
89416
89538
|
wake.unref?.();
|
|
89417
89539
|
});
|
|
@@ -89497,7 +89619,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
89497
89619
|
saveProfile(root, profile);
|
|
89498
89620
|
}
|
|
89499
89621
|
registerWorkspace(profile, root);
|
|
89500
|
-
attachWorkspace(root);
|
|
89622
|
+
await attachWorkspace(root);
|
|
89501
89623
|
},
|
|
89502
89624
|
// eslint-disable-next-line no-console
|
|
89503
89625
|
log: (line) => console.log(line)
|
|
@@ -90434,6 +90556,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
90434
90556
|
boundaryListeners.push(cb);
|
|
90435
90557
|
},
|
|
90436
90558
|
async stop() {
|
|
90559
|
+
stopping = true;
|
|
90437
90560
|
clearInterval(idleSweep);
|
|
90438
90561
|
clearInterval(codexWake);
|
|
90439
90562
|
clearInterval(anchorPull);
|
|
@@ -90448,6 +90571,7 @@ async function startMultiDaemon(opts = {}) {
|
|
|
90448
90571
|
);
|
|
90449
90572
|
updatePoller?.stop();
|
|
90450
90573
|
healthSentinel.stop();
|
|
90574
|
+
await Promise.allSettled(attaching.values());
|
|
90451
90575
|
for (const r of records) await r.engine.stop();
|
|
90452
90576
|
if (consolidateWorker) await consolidateWorker.stop();
|
|
90453
90577
|
try {
|
|
@@ -90528,18 +90652,18 @@ async function runUse(action, ctx) {
|
|
|
90528
90652
|
}
|
|
90529
90653
|
}
|
|
90530
90654
|
function runClear(ctx) {
|
|
90531
|
-
const
|
|
90655
|
+
const active3 = ctx.getActiveAgent();
|
|
90532
90656
|
ctx.clearActiveAgent();
|
|
90533
|
-
if (
|
|
90534
|
-
ctx.log(`no longer acting as ${
|
|
90657
|
+
if (active3) {
|
|
90658
|
+
ctx.log(`no longer acting as ${active3.handle} \u2014 the daemon acts as your default identity`);
|
|
90535
90659
|
} else {
|
|
90536
90660
|
ctx.log(`already on the default identity \u2014 nothing to clear`);
|
|
90537
90661
|
}
|
|
90538
90662
|
return 0;
|
|
90539
90663
|
}
|
|
90540
90664
|
async function runShow(ctx) {
|
|
90541
|
-
const
|
|
90542
|
-
ctx.log(
|
|
90665
|
+
const active3 = ctx.getActiveAgent();
|
|
90666
|
+
ctx.log(active3 ? `active agent: ${active3.handle} (id ${active3.id})` : `active agent: (default identity)`);
|
|
90543
90667
|
if (!ctx.loggedIn) {
|
|
90544
90668
|
ctx.log(` log in to list the agents you can act as: errata login`);
|
|
90545
90669
|
return 0;
|
|
@@ -90551,7 +90675,7 @@ async function runShow(ctx) {
|
|
|
90551
90675
|
ctx.log(` couldn't list actable agents: ${errText(err2)}`);
|
|
90552
90676
|
return 1;
|
|
90553
90677
|
}
|
|
90554
|
-
printActable(resp,
|
|
90678
|
+
printActable(resp, active3, ctx);
|
|
90555
90679
|
return 0;
|
|
90556
90680
|
}
|
|
90557
90681
|
async function runSet(handle2, ctx) {
|
|
@@ -90580,7 +90704,7 @@ async function runSet(handle2, ctx) {
|
|
|
90580
90704
|
ctx.log(`now acting as ${match2.handle} (id ${match2.id}) \u2014 saved; persists across daemon restarts`);
|
|
90581
90705
|
return 0;
|
|
90582
90706
|
}
|
|
90583
|
-
function printActable(resp,
|
|
90707
|
+
function printActable(resp, active3, ctx) {
|
|
90584
90708
|
if (resp.agents.length === 0) {
|
|
90585
90709
|
ctx.log(` (no agents you can act as)`);
|
|
90586
90710
|
return;
|
|
@@ -90588,7 +90712,7 @@ function printActable(resp, active2, ctx) {
|
|
|
90588
90712
|
ctx.log(` agents you can act as:`);
|
|
90589
90713
|
for (const a of resp.agents) {
|
|
90590
90714
|
const marks = [];
|
|
90591
|
-
if (
|
|
90715
|
+
if (active3 && a.id === active3.id) marks.push("active");
|
|
90592
90716
|
if (a.id === resp.defaultAgentId) marks.push("default");
|
|
90593
90717
|
const suffix = marks.length ? ` (${marks.join(", ")})` : "";
|
|
90594
90718
|
const scope = a.scope ? ` \xB7 ${a.scope}` : "";
|
|
@@ -92271,9 +92395,9 @@ async function cmdStatus() {
|
|
|
92271
92395
|
} else {
|
|
92272
92396
|
console.log(` logged in: yes (${cfg.email}) \u2014 key capability enforced by Console installation/scopes`);
|
|
92273
92397
|
}
|
|
92274
|
-
const
|
|
92398
|
+
const active3 = getActiveAgent(cfg);
|
|
92275
92399
|
console.log(
|
|
92276
|
-
` active agent: ${
|
|
92400
|
+
` active agent: ${active3 ? `${active3.handle} (acting-as; \`errata use --none\` to clear)` : "default identity"}`
|
|
92277
92401
|
);
|
|
92278
92402
|
const inspection = await inspectCloudEndpoint(cfg.cloudUrl);
|
|
92279
92403
|
const decision = evaluateCloudEndpoint(cfg.cloudUrl, inspection);
|
|
@@ -92393,8 +92517,8 @@ async function cmdWhoami(args2) {
|
|
|
92393
92517
|
return;
|
|
92394
92518
|
}
|
|
92395
92519
|
try {
|
|
92396
|
-
const
|
|
92397
|
-
const response = await resolveWhoami(authedCloudClient(cfg),
|
|
92520
|
+
const active3 = getActiveAgent(cfg);
|
|
92521
|
+
const response = await resolveWhoami(authedCloudClient(cfg), active3?.handle ?? null);
|
|
92398
92522
|
if (args2.includes("--json")) {
|
|
92399
92523
|
console.log(JSON.stringify(response, null, 2));
|
|
92400
92524
|
return;
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -16283,13 +16283,13 @@ function loadNodeSqlite() {
|
|
|
16283
16283
|
process.emitWarning = original;
|
|
16284
16284
|
}
|
|
16285
16285
|
}
|
|
16286
|
-
function openDatabase(path) {
|
|
16287
|
-
const db = new DatabaseSync(path);
|
|
16286
|
+
function openDatabase(path, opts) {
|
|
16287
|
+
const db = opts ? new DatabaseSync(path, opts) : new DatabaseSync(path);
|
|
16288
16288
|
try {
|
|
16289
16289
|
db.exec("PRAGMA foreign_keys = ON");
|
|
16290
16290
|
} catch {
|
|
16291
16291
|
}
|
|
16292
|
-
if (path !== ":memory:") {
|
|
16292
|
+
if (path !== ":memory:" && !opts?.readOnly) {
|
|
16293
16293
|
try {
|
|
16294
16294
|
db.exec("PRAGMA journal_mode = WAL");
|
|
16295
16295
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
@@ -25171,8 +25171,9 @@ init_src2();
|
|
|
25171
25171
|
init_src();
|
|
25172
25172
|
|
|
25173
25173
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
25174
|
+
var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
25174
25175
|
function installHistoryCacheFloor(db) {
|
|
25175
|
-
const key =
|
|
25176
|
+
const key = HISTORY_CACHE_FLOOR_KEY;
|
|
25176
25177
|
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(key)) return;
|
|
25177
25178
|
db.transaction(() => {
|
|
25178
25179
|
db.exec("DROP TRIGGER IF EXISTS history_cache_nodes_insert; DROP TRIGGER IF EXISTS history_cache_nodes_update;");
|
|
@@ -25334,6 +25335,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
25334
25335
|
return verdict.ok ? null : verdict.reason ?? "edge rule violation";
|
|
25335
25336
|
}
|
|
25336
25337
|
var SCHEMA_VERSION = 6;
|
|
25338
|
+
var SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
25337
25339
|
var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
25338
25340
|
var SCHEMA_SQL = `
|
|
25339
25341
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -25545,10 +25547,10 @@ var SqliteGraphStore = class {
|
|
|
25545
25547
|
}
|
|
25546
25548
|
this.db.exec(TEMPORAL_INDEX_SQL);
|
|
25547
25549
|
installHistoryCacheFloor(this.db);
|
|
25548
|
-
if (!this.db.prepare("SELECT value FROM store_meta WHERE key =
|
|
25550
|
+
if (!this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(SESSION_REPAIR_KEY)) {
|
|
25549
25551
|
this.db.transaction(() => {
|
|
25550
25552
|
this.repairSessionAliases();
|
|
25551
|
-
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (
|
|
25553
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
25552
25554
|
});
|
|
25553
25555
|
}
|
|
25554
25556
|
this.stmts = {
|
|
@@ -25743,6 +25745,34 @@ var SqliteGraphStore = class {
|
|
|
25743
25745
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
25744
25746
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
25745
25747
|
}
|
|
25748
|
+
strandedSymbols() {
|
|
25749
|
+
return this.db.prepare(`
|
|
25750
|
+
SELECT f.id AS fileId, e.to_id AS symbolId
|
|
25751
|
+
-- Start from the small File population. An unconstrained JOIN lets
|
|
25752
|
+
-- SQLite start at edges_live and visit every live edge in the graph.
|
|
25753
|
+
FROM nodes f CROSS JOIN edges e ON e.from_id = f.id
|
|
25754
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
25755
|
+
WHERE f.label = 'File' AND f.valid_to IS NULL AND e.valid_to IS NULL
|
|
25756
|
+
AND e.type IN ('DEFINES', 'CONTAINS')
|
|
25757
|
+
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
25758
|
+
`).all();
|
|
25759
|
+
}
|
|
25760
|
+
fileIndexStates(ids) {
|
|
25761
|
+
if (ids.length === 0) return [];
|
|
25762
|
+
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
25763
|
+
const rows = this.db.prepare(`
|
|
25764
|
+
SELECT f.id, f.last_updated_at AS lastUpdatedAt, f.valid_to AS validTo,
|
|
25765
|
+
CASE WHEN count(e.id) = 0 THEN
|
|
25766
|
+
(json_type(f.attrs_json, '$.symbolCount') IN ('integer','real') AND json_extract(f.attrs_json, '$.symbolCount') = 0)
|
|
25767
|
+
OR json_type(f.attrs_json, '$.indexSkipped') = 'text'
|
|
25768
|
+
ELSE max(CASE WHEN t.id IS NULL OR t.valid_to IS NOT NULL THEN 1 ELSE 0 END) = 0 END AS complete
|
|
25769
|
+
FROM nodes f
|
|
25770
|
+
LEFT JOIN edges e ON e.from_id = f.id AND e.valid_to IS NULL AND e.type IN ('DEFINES','CONTAINS')
|
|
25771
|
+
LEFT JOIN nodes t ON t.id = e.to_id
|
|
25772
|
+
WHERE f.id IN (${ids.map(() => "?").join(",")}) GROUP BY f.id
|
|
25773
|
+
`).all(...ids);
|
|
25774
|
+
return rows.map((row) => ({ ...row, complete: row.complete === 1 }));
|
|
25775
|
+
}
|
|
25746
25776
|
/** Forward-migrate the on-disk schema to the current SCHEMA_VERSION. */
|
|
25747
25777
|
migrate(from) {
|
|
25748
25778
|
this.db.transaction(() => {
|
|
@@ -29118,48 +29148,30 @@ function listSourceFiles(root) {
|
|
|
29118
29148
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
29119
29149
|
}
|
|
29120
29150
|
var SCAN_YIELD_EVERY = 100;
|
|
29121
|
-
var LIVENESS_CHUNK = 4e3;
|
|
29122
29151
|
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
29123
29152
|
const stale = [];
|
|
29124
|
-
|
|
29125
|
-
const
|
|
29153
|
+
let pending = [];
|
|
29154
|
+
const check2 = () => {
|
|
29155
|
+
const states = new Map(store2.fileIndexStates(pending.map((f) => f.id)).map((s) => [s.id, s]));
|
|
29156
|
+
for (const f of pending) {
|
|
29157
|
+
const state = states.get(f.id);
|
|
29158
|
+
if (!state || state.validTo !== null || state.lastUpdatedAt < f.mtimeMs || !state.complete) stale.push(f.abs);
|
|
29159
|
+
}
|
|
29160
|
+
pending = [];
|
|
29161
|
+
};
|
|
29126
29162
|
let scanned = 0;
|
|
29127
29163
|
for (const abs of listSourceFiles(rootPath)) {
|
|
29128
|
-
if (++scanned % SCAN_YIELD_EVERY === 0) await new Promise((r) => setImmediate(r));
|
|
29129
|
-
let mtimeMs;
|
|
29130
29164
|
try {
|
|
29131
|
-
|
|
29165
|
+
const rel = relative2(rootPath, abs).split(sep2).join("/");
|
|
29166
|
+
pending.push({ abs, id: fileNodeId(workspaceId, rel), mtimeMs: statSync3(abs).mtimeMs });
|
|
29132
29167
|
} catch {
|
|
29133
|
-
continue;
|
|
29134
29168
|
}
|
|
29135
|
-
|
|
29136
|
-
|
|
29137
|
-
|
|
29138
|
-
stale.push(abs);
|
|
29139
|
-
} else if ((fnode.validTo ?? null) !== null) {
|
|
29140
|
-
stale.push(abs);
|
|
29141
|
-
} else if (fnode.lastUpdatedAt < mtimeMs) {
|
|
29142
|
-
stale.push(abs);
|
|
29143
|
-
} else {
|
|
29144
|
-
const edges = store2.outEdges(fnode.id, ["DEFINES", "CONTAINS"]);
|
|
29145
|
-
if (edges.length === 0) {
|
|
29146
|
-
const converged = fnode.attrs["symbolCount"] === 0 || typeof fnode.attrs["indexSkipped"] === "string";
|
|
29147
|
-
if (!converged) stale.push(abs);
|
|
29148
|
-
} else {
|
|
29149
|
-
const targets = edges.map((e) => e.to);
|
|
29150
|
-
pending.push({ abs, targets });
|
|
29151
|
-
allTargets.push(...targets);
|
|
29152
|
-
}
|
|
29169
|
+
if (++scanned % SCAN_YIELD_EVERY === 0) {
|
|
29170
|
+
check2();
|
|
29171
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
29153
29172
|
}
|
|
29154
29173
|
}
|
|
29155
|
-
|
|
29156
|
-
for (let i2 = 0; i2 < allTargets.length; i2 += LIVENESS_CHUNK) {
|
|
29157
|
-
for (const id of store2.liveNodeIds(allTargets.slice(i2, i2 + LIVENESS_CHUNK))) live.add(id);
|
|
29158
|
-
if (i2 + LIVENESS_CHUNK < allTargets.length) await new Promise((r) => setImmediate(r));
|
|
29159
|
-
}
|
|
29160
|
-
for (const { abs, targets } of pending) {
|
|
29161
|
-
if (targets.some((id) => !live.has(id))) stale.push(abs);
|
|
29162
|
-
}
|
|
29174
|
+
check2();
|
|
29163
29175
|
return stale;
|
|
29164
29176
|
}
|
|
29165
29177
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
@@ -29574,6 +29586,9 @@ port.on("message", (msg) => {
|
|
|
29574
29586
|
});
|
|
29575
29587
|
function runPass(msg) {
|
|
29576
29588
|
switch (msg.kind) {
|
|
29589
|
+
case "prepareStore":
|
|
29590
|
+
return void 0;
|
|
29591
|
+
// opening the connection above completed all preparation
|
|
29577
29592
|
case "semanticMaintenance":
|
|
29578
29593
|
return runWorkspaceMaintenance(store, init2.workspaceId);
|
|
29579
29594
|
case "liveness":
|