@inerrata-corporation/errata 2.0.22-dev.4430 → 2.0.22-dev.4530
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 +155 -4
- package/errata.mjs +1244 -535
- package/generalize-worker.mjs +149 -2
- package/package.json +1 -1
- package/pass-worker.mjs +176 -9
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +1 -1
package/generalize-worker.mjs
CHANGED
|
@@ -14712,6 +14712,17 @@ var HistoryRevisionRequestSchema = external_exports.strictObject({
|
|
|
14712
14712
|
dependencyContext: HistoryDependencyContextSchema.optional()
|
|
14713
14713
|
})).max(50)
|
|
14714
14714
|
});
|
|
14715
|
+
var HistoryCarryRequestSchema = external_exports.strictObject({
|
|
14716
|
+
fromCommit: Sha,
|
|
14717
|
+
atCommit: Sha,
|
|
14718
|
+
parents: external_exports.array(external_exports.strictObject({ commit: Sha, parents: external_exports.array(Sha).max(32) })).max(8192),
|
|
14719
|
+
/** Every symbol in a file the range changed, both sides; `null` = too many to name, treat all as changed. */
|
|
14720
|
+
changedSymbols: external_exports.array(external_exports.string().regex(/^sym_[a-f0-9]{8,64}$/)).max(5e3).nullable(),
|
|
14721
|
+
/** Manifests the range changed; `null` = a lockfile (or too many manifests) changed, treat all as changed. */
|
|
14722
|
+
changedManifests: external_exports.array(external_exports.string().regex(/^hmanifest_[a-f0-9]{32}$/)).max(512).nullable()
|
|
14723
|
+
});
|
|
14724
|
+
var HISTORY_REVIEW_BUDGET = 48e3;
|
|
14725
|
+
var HISTORY_REVIEW_POLICY = `reviewer-view-${HISTORY_REVIEW_BUDGET}`;
|
|
14715
14726
|
|
|
14716
14727
|
// ../../packages/shared/src/edit-placeholder.ts
|
|
14717
14728
|
var AUTO_MINT_PREFIX = "addressed by an edit to ";
|
|
@@ -15667,6 +15678,114 @@ function textIndexReady(db) {
|
|
|
15667
15678
|
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
15668
15679
|
}
|
|
15669
15680
|
|
|
15681
|
+
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
15682
|
+
var EXPOSURE_ATTRS = [
|
|
15683
|
+
"shownCount",
|
|
15684
|
+
"evictedCount",
|
|
15685
|
+
"firstShownAt",
|
|
15686
|
+
"firstEvictedAt",
|
|
15687
|
+
"firstShownAtSeq",
|
|
15688
|
+
"lastShownAtSeq",
|
|
15689
|
+
"lastEvictedAtSeq",
|
|
15690
|
+
"shownSinceTouch",
|
|
15691
|
+
"shownSinceTouchAt",
|
|
15692
|
+
"exposureLog",
|
|
15693
|
+
"exposureLogStart",
|
|
15694
|
+
"exposureLogSince",
|
|
15695
|
+
"exposureFirst",
|
|
15696
|
+
"exposureTrimmed"
|
|
15697
|
+
];
|
|
15698
|
+
var EXPOSURE_SCOPE = "exposure";
|
|
15699
|
+
|
|
15700
|
+
// ../../packages/local-graph/src/change-feed.ts
|
|
15701
|
+
var CHANGE_FEED_KEY = "change-feed";
|
|
15702
|
+
var ANY_CHANGE = "*";
|
|
15703
|
+
var CHANGE_LOG_KEEP = 25e4;
|
|
15704
|
+
var PRUNED_TO_KEY = "change-log-pruned-to";
|
|
15705
|
+
function installChangeFeed(db) {
|
|
15706
|
+
if (changeFeedInstalled(db)) return;
|
|
15707
|
+
db.transaction(() => {
|
|
15708
|
+
db.exec("CREATE TABLE IF NOT EXISTS change_seq (scope TEXT PRIMARY KEY, seq INTEGER NOT NULL) WITHOUT ROWID");
|
|
15709
|
+
db.exec(`CREATE TABLE IF NOT EXISTS change_log (change_seq INTEGER PRIMARY KEY, kind TEXT NOT NULL, id TEXT NOT NULL,
|
|
15710
|
+
label TEXT, from_id TEXT, to_id TEXT)`);
|
|
15711
|
+
for (const name of ["nodes", "edges"].flatMap((t) => ["insert", "update", "delete"].map((op) => `change_feed_${t}_${op}`))) {
|
|
15712
|
+
db.exec(`DROP TRIGGER IF EXISTS ${name}`);
|
|
15713
|
+
}
|
|
15714
|
+
const changed = (table) => columns(db, table).map((c) => `OLD."${c}" IS NOT NEW."${c}"`).join(" OR ");
|
|
15715
|
+
const strip = (row) => `json_remove(${row}.attrs_json, ${EXPOSURE_ATTRS.map((a) => `'$.${a}'`).join(", ")})`;
|
|
15716
|
+
const exposureOnly = `(${columns(db, "nodes").filter((c) => c !== "attrs_json").map((c) => `OLD."${c}" IS NEW."${c}"`).join(" AND ")}
|
|
15717
|
+
AND CASE WHEN json_valid(OLD.attrs_json) AND json_valid(NEW.attrs_json) THEN ${strip("OLD")} IS ${strip("NEW")} ELSE 0 END)`;
|
|
15718
|
+
const bump = (...scopes) => `INSERT INTO change_seq(scope, seq) VALUES ${scopes.map((s) => `(${s}, 1)`).join(", ")}
|
|
15719
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;`;
|
|
15720
|
+
const seq = `(SELECT seq FROM change_seq WHERE scope = '*')`;
|
|
15721
|
+
const logNode = (row) => `INSERT INTO change_log(change_seq, kind, id, label) VALUES (${seq}, 'node', ${row}.id, ${row}.label);`;
|
|
15722
|
+
const logEdge = (row) => `INSERT INTO change_log(change_seq, kind, id, label, from_id, to_id) VALUES (${seq}, 'edge', ${row}.id, ${row}.type, ${row}.from_id, ${row}.to_id);`;
|
|
15723
|
+
const node = (row) => `'node:' || ${row}.label`;
|
|
15724
|
+
const edge = (row) => `'edge:' || ${row}.type`;
|
|
15725
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_insert AFTER INSERT ON nodes BEGIN ${bump(`'*'`, node("NEW"))} ${logNode("NEW")} END;`);
|
|
15726
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_update AFTER UPDATE ON nodes WHEN ${changed("nodes")} BEGIN
|
|
15727
|
+
${bump(`'*'`)}
|
|
15728
|
+
INSERT INTO change_seq(scope, seq) SELECT CASE WHEN ${exposureOnly} THEN '${EXPOSURE_SCOPE}' ELSE ${node("NEW")} END, 1 WHERE 1
|
|
15729
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
15730
|
+
INSERT INTO change_seq(scope, seq) SELECT ${node("OLD")}, 1 WHERE OLD.label IS NOT NEW.label
|
|
15731
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
15732
|
+
${logNode("NEW")}
|
|
15733
|
+
END;`);
|
|
15734
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_delete AFTER DELETE ON nodes BEGIN ${bump(`'*'`, node("OLD"))} ${logNode("OLD")} END;`);
|
|
15735
|
+
db.exec(`CREATE TRIGGER change_feed_edges_insert AFTER INSERT ON edges BEGIN ${bump(`'*'`, edge("NEW"))} ${logEdge("NEW")} END;`);
|
|
15736
|
+
db.exec(`CREATE TRIGGER change_feed_edges_update AFTER UPDATE ON edges WHEN ${changed("edges")} BEGIN
|
|
15737
|
+
${bump(`'*'`, edge("NEW"))}
|
|
15738
|
+
INSERT INTO change_seq(scope, seq) SELECT ${edge("OLD")}, 1 WHERE OLD.type IS NOT NEW.type
|
|
15739
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
15740
|
+
${logEdge("NEW")}
|
|
15741
|
+
END;`);
|
|
15742
|
+
db.exec(`CREATE TRIGGER change_feed_edges_delete AFTER DELETE ON edges BEGIN ${bump(`'*'`, edge("OLD"))} ${logEdge("OLD")} END;`);
|
|
15743
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(readChangeSeq(db)));
|
|
15744
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(CHANGE_FEED_KEY, columnSignature(db));
|
|
15745
|
+
});
|
|
15746
|
+
}
|
|
15747
|
+
function readChangeSeq(db, scopes = [ANY_CHANGE]) {
|
|
15748
|
+
if (!scopes.length) return 0;
|
|
15749
|
+
const row = db.prepare(`SELECT coalesce(sum(seq), 0) AS n FROM change_seq WHERE scope IN (${scopes.map(() => "?").join(",")})`).get(...scopes);
|
|
15750
|
+
return Number(row?.n ?? 0);
|
|
15751
|
+
}
|
|
15752
|
+
function changeLogCovers(db, sinceSeq) {
|
|
15753
|
+
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
15754
|
+
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
15755
|
+
}
|
|
15756
|
+
function readLineageChangedFiles(db, sinceSeq) {
|
|
15757
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
15758
|
+
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
15759
|
+
return rows.map((r) => r.id);
|
|
15760
|
+
}
|
|
15761
|
+
var LINEAGE_CHANGED_FILES_SQL = `
|
|
15762
|
+
SELECT id FROM change_log WHERE change_seq > :s AND kind = 'node' AND label = 'File'
|
|
15763
|
+
UNION SELECT from_id FROM change_log WHERE change_seq > :s AND kind = 'edge' AND label IN ('DEFINES','CONTAINS')
|
|
15764
|
+
UNION SELECT e.from_id FROM (SELECT DISTINCT id FROM change_log WHERE change_seq > :s AND kind = 'node') c
|
|
15765
|
+
CROSS JOIN edges e ON e.to_id = c.id AND e.type IN ('DEFINES','CONTAINS')
|
|
15766
|
+
`;
|
|
15767
|
+
function pruneChangeLog(db) {
|
|
15768
|
+
const max = Number(db.prepare("SELECT coalesce(max(change_seq), 0) AS n FROM change_log").get()?.["n"] ?? 0);
|
|
15769
|
+
const cut = max - CHANGE_LOG_KEEP;
|
|
15770
|
+
if (cut <= 0) return 0;
|
|
15771
|
+
let removed = 0;
|
|
15772
|
+
db.transaction(() => {
|
|
15773
|
+
removed = Number(db.prepare("DELETE FROM change_log WHERE change_seq <= ?").run(cut).changes);
|
|
15774
|
+
const prev = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? 0);
|
|
15775
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(Math.max(prev, cut)));
|
|
15776
|
+
});
|
|
15777
|
+
return removed;
|
|
15778
|
+
}
|
|
15779
|
+
function changeFeedInstalled(db) {
|
|
15780
|
+
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CHANGE_FEED_KEY)?.["value"] === columnSignature(db);
|
|
15781
|
+
}
|
|
15782
|
+
function columns(db, table) {
|
|
15783
|
+
return db.prepare(`SELECT name FROM pragma_table_info('${table}') ORDER BY cid`).all().map((r) => r.name);
|
|
15784
|
+
}
|
|
15785
|
+
function columnSignature(db) {
|
|
15786
|
+
return `5|${columns(db, "nodes").join(",")}|${columns(db, "edges").join(",")}|${EXPOSURE_ATTRS.join(",")}`;
|
|
15787
|
+
}
|
|
15788
|
+
|
|
15670
15789
|
// ../../packages/local-graph/src/lineage-integrity.ts
|
|
15671
15790
|
var statements = /* @__PURE__ */ new WeakMap();
|
|
15672
15791
|
function stmt(db, sql) {
|
|
@@ -16277,6 +16396,7 @@ var SqliteGraphStore = class {
|
|
|
16277
16396
|
});
|
|
16278
16397
|
}
|
|
16279
16398
|
installLineageIntegrityRepair(this.db);
|
|
16399
|
+
installChangeFeed(this.db);
|
|
16280
16400
|
this.stmts = {
|
|
16281
16401
|
insertNode: this.db.prepare(`
|
|
16282
16402
|
INSERT INTO nodes (
|
|
@@ -17064,12 +17184,39 @@ var SqliteGraphStore = class {
|
|
|
17064
17184
|
mutationCount() {
|
|
17065
17185
|
return this.mutations;
|
|
17066
17186
|
}
|
|
17187
|
+
changeStamp() {
|
|
17188
|
+
const { data_version } = this.db.pragma("data_version");
|
|
17189
|
+
const { n } = this.db.prepare("SELECT total_changes() AS n").get();
|
|
17190
|
+
return `${data_version}:${n}`;
|
|
17191
|
+
}
|
|
17192
|
+
changeSeq(scopes) {
|
|
17193
|
+
return readChangeSeq(this.db, scopes);
|
|
17194
|
+
}
|
|
17195
|
+
lineageChangedFiles(sinceSeq) {
|
|
17196
|
+
return readLineageChangedFiles(this.db, sinceSeq);
|
|
17197
|
+
}
|
|
17198
|
+
pruneChangeLog() {
|
|
17199
|
+
return pruneChangeLog(this.db);
|
|
17200
|
+
}
|
|
17201
|
+
fileRowStates(ids) {
|
|
17202
|
+
if (ids.length === 0) return [];
|
|
17203
|
+
if (ids.length > 500) throw new RangeError("fileRowStates accepts at most 500 ids per read");
|
|
17204
|
+
return this.db.prepare(`SELECT id, last_updated_at AS lastUpdatedAt, valid_to AS validTo FROM nodes WHERE id IN (${ids.map(() => "?").join(",")})`).all(...ids);
|
|
17205
|
+
}
|
|
17206
|
+
// Read and advanced in SQL, never from a per-connection copy: pass workers and
|
|
17207
|
+
// MCP processes open their own connections and advance the same row, so a
|
|
17208
|
+
// copy loaded at open went stale, and `copy + 1` wrote a duplicate or a
|
|
17209
|
+
// backwards seq from whichever connection had fallen behind.
|
|
17067
17210
|
currentIngestSeq() {
|
|
17211
|
+
const row = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
17212
|
+
this.ingestSeq = row ? Number(row.value) : 0;
|
|
17068
17213
|
return this.ingestSeq;
|
|
17069
17214
|
}
|
|
17070
17215
|
nextIngestSeq() {
|
|
17071
|
-
this.
|
|
17072
|
-
|
|
17216
|
+
const row = this.db.prepare(
|
|
17217
|
+
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(value AS INTEGER) + 1 RETURNING value"
|
|
17218
|
+
).get();
|
|
17219
|
+
this.ingestSeq = Number(row.value);
|
|
17073
17220
|
return this.ingestSeq;
|
|
17074
17221
|
}
|
|
17075
17222
|
edgeCount() {
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -16184,7 +16184,7 @@ var init_history_temporal = __esm({
|
|
|
16184
16184
|
});
|
|
16185
16185
|
|
|
16186
16186
|
// ../../packages/shared/src/history-revision.ts
|
|
16187
|
-
var Sha, HistoryRevisionRequestSchema;
|
|
16187
|
+
var Sha, HistoryRevisionRequestSchema, HistoryCarryRequestSchema, HISTORY_REVIEW_BUDGET, HISTORY_REVIEW_POLICY;
|
|
16188
16188
|
var init_history_revision = __esm({
|
|
16189
16189
|
"../../packages/shared/src/history-revision.ts"() {
|
|
16190
16190
|
"use strict";
|
|
@@ -16204,6 +16204,17 @@ var init_history_revision = __esm({
|
|
|
16204
16204
|
dependencyContext: HistoryDependencyContextSchema.optional()
|
|
16205
16205
|
})).max(50)
|
|
16206
16206
|
});
|
|
16207
|
+
HistoryCarryRequestSchema = external_exports.strictObject({
|
|
16208
|
+
fromCommit: Sha,
|
|
16209
|
+
atCommit: Sha,
|
|
16210
|
+
parents: external_exports.array(external_exports.strictObject({ commit: Sha, parents: external_exports.array(Sha).max(32) })).max(8192),
|
|
16211
|
+
/** Every symbol in a file the range changed, both sides; `null` = too many to name, treat all as changed. */
|
|
16212
|
+
changedSymbols: external_exports.array(external_exports.string().regex(/^sym_[a-f0-9]{8,64}$/)).max(5e3).nullable(),
|
|
16213
|
+
/** Manifests the range changed; `null` = a lockfile (or too many manifests) changed, treat all as changed. */
|
|
16214
|
+
changedManifests: external_exports.array(external_exports.string().regex(/^hmanifest_[a-f0-9]{32}$/)).max(512).nullable()
|
|
16215
|
+
});
|
|
16216
|
+
HISTORY_REVIEW_BUDGET = 48e3;
|
|
16217
|
+
HISTORY_REVIEW_POLICY = `reviewer-view-${HISTORY_REVIEW_BUDGET}`;
|
|
16207
16218
|
}
|
|
16208
16219
|
});
|
|
16209
16220
|
|
|
@@ -26099,6 +26110,114 @@ function textIndexReady(db) {
|
|
|
26099
26110
|
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
26100
26111
|
}
|
|
26101
26112
|
|
|
26113
|
+
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
26114
|
+
var EXPOSURE_ATTRS = [
|
|
26115
|
+
"shownCount",
|
|
26116
|
+
"evictedCount",
|
|
26117
|
+
"firstShownAt",
|
|
26118
|
+
"firstEvictedAt",
|
|
26119
|
+
"firstShownAtSeq",
|
|
26120
|
+
"lastShownAtSeq",
|
|
26121
|
+
"lastEvictedAtSeq",
|
|
26122
|
+
"shownSinceTouch",
|
|
26123
|
+
"shownSinceTouchAt",
|
|
26124
|
+
"exposureLog",
|
|
26125
|
+
"exposureLogStart",
|
|
26126
|
+
"exposureLogSince",
|
|
26127
|
+
"exposureFirst",
|
|
26128
|
+
"exposureTrimmed"
|
|
26129
|
+
];
|
|
26130
|
+
var EXPOSURE_SCOPE = "exposure";
|
|
26131
|
+
|
|
26132
|
+
// ../../packages/local-graph/src/change-feed.ts
|
|
26133
|
+
var CHANGE_FEED_KEY = "change-feed";
|
|
26134
|
+
var ANY_CHANGE = "*";
|
|
26135
|
+
var CHANGE_LOG_KEEP = 25e4;
|
|
26136
|
+
var PRUNED_TO_KEY = "change-log-pruned-to";
|
|
26137
|
+
function installChangeFeed(db) {
|
|
26138
|
+
if (changeFeedInstalled(db)) return;
|
|
26139
|
+
db.transaction(() => {
|
|
26140
|
+
db.exec("CREATE TABLE IF NOT EXISTS change_seq (scope TEXT PRIMARY KEY, seq INTEGER NOT NULL) WITHOUT ROWID");
|
|
26141
|
+
db.exec(`CREATE TABLE IF NOT EXISTS change_log (change_seq INTEGER PRIMARY KEY, kind TEXT NOT NULL, id TEXT NOT NULL,
|
|
26142
|
+
label TEXT, from_id TEXT, to_id TEXT)`);
|
|
26143
|
+
for (const name2 of ["nodes", "edges"].flatMap((t) => ["insert", "update", "delete"].map((op) => `change_feed_${t}_${op}`))) {
|
|
26144
|
+
db.exec(`DROP TRIGGER IF EXISTS ${name2}`);
|
|
26145
|
+
}
|
|
26146
|
+
const changed = (table) => columns(db, table).map((c) => `OLD."${c}" IS NOT NEW."${c}"`).join(" OR ");
|
|
26147
|
+
const strip = (row) => `json_remove(${row}.attrs_json, ${EXPOSURE_ATTRS.map((a) => `'$.${a}'`).join(", ")})`;
|
|
26148
|
+
const exposureOnly = `(${columns(db, "nodes").filter((c) => c !== "attrs_json").map((c) => `OLD."${c}" IS NEW."${c}"`).join(" AND ")}
|
|
26149
|
+
AND CASE WHEN json_valid(OLD.attrs_json) AND json_valid(NEW.attrs_json) THEN ${strip("OLD")} IS ${strip("NEW")} ELSE 0 END)`;
|
|
26150
|
+
const bump = (...scopes) => `INSERT INTO change_seq(scope, seq) VALUES ${scopes.map((s) => `(${s}, 1)`).join(", ")}
|
|
26151
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;`;
|
|
26152
|
+
const seq = `(SELECT seq FROM change_seq WHERE scope = '*')`;
|
|
26153
|
+
const logNode = (row) => `INSERT INTO change_log(change_seq, kind, id, label) VALUES (${seq}, 'node', ${row}.id, ${row}.label);`;
|
|
26154
|
+
const logEdge = (row) => `INSERT INTO change_log(change_seq, kind, id, label, from_id, to_id) VALUES (${seq}, 'edge', ${row}.id, ${row}.type, ${row}.from_id, ${row}.to_id);`;
|
|
26155
|
+
const node = (row) => `'node:' || ${row}.label`;
|
|
26156
|
+
const edge = (row) => `'edge:' || ${row}.type`;
|
|
26157
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_insert AFTER INSERT ON nodes BEGIN ${bump(`'*'`, node("NEW"))} ${logNode("NEW")} END;`);
|
|
26158
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_update AFTER UPDATE ON nodes WHEN ${changed("nodes")} BEGIN
|
|
26159
|
+
${bump(`'*'`)}
|
|
26160
|
+
INSERT INTO change_seq(scope, seq) SELECT CASE WHEN ${exposureOnly} THEN '${EXPOSURE_SCOPE}' ELSE ${node("NEW")} END, 1 WHERE 1
|
|
26161
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
26162
|
+
INSERT INTO change_seq(scope, seq) SELECT ${node("OLD")}, 1 WHERE OLD.label IS NOT NEW.label
|
|
26163
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
26164
|
+
${logNode("NEW")}
|
|
26165
|
+
END;`);
|
|
26166
|
+
db.exec(`CREATE TRIGGER change_feed_nodes_delete AFTER DELETE ON nodes BEGIN ${bump(`'*'`, node("OLD"))} ${logNode("OLD")} END;`);
|
|
26167
|
+
db.exec(`CREATE TRIGGER change_feed_edges_insert AFTER INSERT ON edges BEGIN ${bump(`'*'`, edge("NEW"))} ${logEdge("NEW")} END;`);
|
|
26168
|
+
db.exec(`CREATE TRIGGER change_feed_edges_update AFTER UPDATE ON edges WHEN ${changed("edges")} BEGIN
|
|
26169
|
+
${bump(`'*'`, edge("NEW"))}
|
|
26170
|
+
INSERT INTO change_seq(scope, seq) SELECT ${edge("OLD")}, 1 WHERE OLD.type IS NOT NEW.type
|
|
26171
|
+
ON CONFLICT(scope) DO UPDATE SET seq = seq + 1;
|
|
26172
|
+
${logEdge("NEW")}
|
|
26173
|
+
END;`);
|
|
26174
|
+
db.exec(`CREATE TRIGGER change_feed_edges_delete AFTER DELETE ON edges BEGIN ${bump(`'*'`, edge("OLD"))} ${logEdge("OLD")} END;`);
|
|
26175
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(readChangeSeq(db)));
|
|
26176
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(CHANGE_FEED_KEY, columnSignature(db));
|
|
26177
|
+
});
|
|
26178
|
+
}
|
|
26179
|
+
function readChangeSeq(db, scopes = [ANY_CHANGE]) {
|
|
26180
|
+
if (!scopes.length) return 0;
|
|
26181
|
+
const row = db.prepare(`SELECT coalesce(sum(seq), 0) AS n FROM change_seq WHERE scope IN (${scopes.map(() => "?").join(",")})`).get(...scopes);
|
|
26182
|
+
return Number(row?.n ?? 0);
|
|
26183
|
+
}
|
|
26184
|
+
function changeLogCovers(db, sinceSeq) {
|
|
26185
|
+
const prunedTo = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? NaN);
|
|
26186
|
+
return Number.isFinite(prunedTo) && sinceSeq >= prunedTo;
|
|
26187
|
+
}
|
|
26188
|
+
function readLineageChangedFiles(db, sinceSeq) {
|
|
26189
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
26190
|
+
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
26191
|
+
return rows.map((r) => r.id);
|
|
26192
|
+
}
|
|
26193
|
+
var LINEAGE_CHANGED_FILES_SQL = `
|
|
26194
|
+
SELECT id FROM change_log WHERE change_seq > :s AND kind = 'node' AND label = 'File'
|
|
26195
|
+
UNION SELECT from_id FROM change_log WHERE change_seq > :s AND kind = 'edge' AND label IN ('DEFINES','CONTAINS')
|
|
26196
|
+
UNION SELECT e.from_id FROM (SELECT DISTINCT id FROM change_log WHERE change_seq > :s AND kind = 'node') c
|
|
26197
|
+
CROSS JOIN edges e ON e.to_id = c.id AND e.type IN ('DEFINES','CONTAINS')
|
|
26198
|
+
`;
|
|
26199
|
+
function pruneChangeLog(db) {
|
|
26200
|
+
const max = Number(db.prepare("SELECT coalesce(max(change_seq), 0) AS n FROM change_log").get()?.["n"] ?? 0);
|
|
26201
|
+
const cut = max - CHANGE_LOG_KEEP;
|
|
26202
|
+
if (cut <= 0) return 0;
|
|
26203
|
+
let removed = 0;
|
|
26204
|
+
db.transaction(() => {
|
|
26205
|
+
removed = Number(db.prepare("DELETE FROM change_log WHERE change_seq <= ?").run(cut).changes);
|
|
26206
|
+
const prev = Number(db.prepare("SELECT value FROM store_meta WHERE key = ?").get(PRUNED_TO_KEY)?.["value"] ?? 0);
|
|
26207
|
+
db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, ?)").run(PRUNED_TO_KEY, String(Math.max(prev, cut)));
|
|
26208
|
+
});
|
|
26209
|
+
return removed;
|
|
26210
|
+
}
|
|
26211
|
+
function changeFeedInstalled(db) {
|
|
26212
|
+
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(CHANGE_FEED_KEY)?.["value"] === columnSignature(db);
|
|
26213
|
+
}
|
|
26214
|
+
function columns(db, table) {
|
|
26215
|
+
return db.prepare(`SELECT name FROM pragma_table_info('${table}') ORDER BY cid`).all().map((r) => r.name);
|
|
26216
|
+
}
|
|
26217
|
+
function columnSignature(db) {
|
|
26218
|
+
return `5|${columns(db, "nodes").join(",")}|${columns(db, "edges").join(",")}|${EXPOSURE_ATTRS.join(",")}`;
|
|
26219
|
+
}
|
|
26220
|
+
|
|
26102
26221
|
// ../../packages/local-graph/src/lineage-integrity.ts
|
|
26103
26222
|
var statements = /* @__PURE__ */ new WeakMap();
|
|
26104
26223
|
function stmt(db, sql) {
|
|
@@ -27145,6 +27264,7 @@ var SqliteGraphStore = class {
|
|
|
27145
27264
|
});
|
|
27146
27265
|
}
|
|
27147
27266
|
installLineageIntegrityRepair(this.db);
|
|
27267
|
+
installChangeFeed(this.db);
|
|
27148
27268
|
this.stmts = {
|
|
27149
27269
|
insertNode: this.db.prepare(`
|
|
27150
27270
|
INSERT INTO nodes (
|
|
@@ -27932,12 +28052,39 @@ var SqliteGraphStore = class {
|
|
|
27932
28052
|
mutationCount() {
|
|
27933
28053
|
return this.mutations;
|
|
27934
28054
|
}
|
|
28055
|
+
changeStamp() {
|
|
28056
|
+
const { data_version } = this.db.pragma("data_version");
|
|
28057
|
+
const { n } = this.db.prepare("SELECT total_changes() AS n").get();
|
|
28058
|
+
return `${data_version}:${n}`;
|
|
28059
|
+
}
|
|
28060
|
+
changeSeq(scopes) {
|
|
28061
|
+
return readChangeSeq(this.db, scopes);
|
|
28062
|
+
}
|
|
28063
|
+
lineageChangedFiles(sinceSeq) {
|
|
28064
|
+
return readLineageChangedFiles(this.db, sinceSeq);
|
|
28065
|
+
}
|
|
28066
|
+
pruneChangeLog() {
|
|
28067
|
+
return pruneChangeLog(this.db);
|
|
28068
|
+
}
|
|
28069
|
+
fileRowStates(ids) {
|
|
28070
|
+
if (ids.length === 0) return [];
|
|
28071
|
+
if (ids.length > 500) throw new RangeError("fileRowStates accepts at most 500 ids per read");
|
|
28072
|
+
return this.db.prepare(`SELECT id, last_updated_at AS lastUpdatedAt, valid_to AS validTo FROM nodes WHERE id IN (${ids.map(() => "?").join(",")})`).all(...ids);
|
|
28073
|
+
}
|
|
28074
|
+
// Read and advanced in SQL, never from a per-connection copy: pass workers and
|
|
28075
|
+
// MCP processes open their own connections and advance the same row, so a
|
|
28076
|
+
// copy loaded at open went stale, and `copy + 1` wrote a duplicate or a
|
|
28077
|
+
// backwards seq from whichever connection had fallen behind.
|
|
27935
28078
|
currentIngestSeq() {
|
|
28079
|
+
const row = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
28080
|
+
this.ingestSeq = row ? Number(row.value) : 0;
|
|
27936
28081
|
return this.ingestSeq;
|
|
27937
28082
|
}
|
|
27938
28083
|
nextIngestSeq() {
|
|
27939
|
-
this.
|
|
27940
|
-
|
|
28084
|
+
const row = this.db.prepare(
|
|
28085
|
+
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(value AS INTEGER) + 1 RETURNING value"
|
|
28086
|
+
).get();
|
|
28087
|
+
this.ingestSeq = Number(row.value);
|
|
27941
28088
|
return this.ingestSeq;
|
|
27942
28089
|
}
|
|
27943
28090
|
edgeCount() {
|
|
@@ -30669,12 +30816,12 @@ function buildSnapshot(opts) {
|
|
|
30669
30816
|
const problems = opts.store.selectNodes({ labels: ["Problem", "Vulnerability"], attrs: PROBLEM_BAND_ATTRS }).filter((p) => p.attrs["mergedInto"] === void 0 && passiveAllowed(p, opts.atCommit));
|
|
30670
30817
|
const stillOpen = problems.filter((p) => p.attrs["resolvedAt"] == null);
|
|
30671
30818
|
const allProblems = rankRecentBand(stillOpen.filter((p) => !isConstraintProblem(p)));
|
|
30672
|
-
const openPool = allProblems.slice(0, RECENT_OPEN * SCOPE_POOL_FACTOR).map((p) => ({
|
|
30819
|
+
const openPool = allProblems.slice(0, Math.max(RECENT_OPEN * SCOPE_POOL_FACTOR, RECENT_WINDOW_BANDS * RECENT_OPEN)).map((p) => ({
|
|
30673
30820
|
node: hydrate(p.id),
|
|
30674
30821
|
anchors: opts.store.outEdges(p.id, ["MANIFESTED_IN", "EVIDENCED_BY"])
|
|
30675
30822
|
}));
|
|
30676
30823
|
const recent = openPool.slice(0, RECENT_OPEN);
|
|
30677
|
-
const constraintPool = rankRecentBand(stillOpen.filter((p) => isConstraintProblem(p)), { window: RECENT_WINDOW_BANDS * RECENT_CONSTRAINTS }).slice(0, RECENT_CONSTRAINTS * SCOPE_POOL_FACTOR).map((p) => hydrate(p.id));
|
|
30824
|
+
const constraintPool = rankRecentBand(stillOpen.filter((p) => isConstraintProblem(p)), { window: RECENT_WINDOW_BANDS * RECENT_CONSTRAINTS }).slice(0, Math.max(RECENT_CONSTRAINTS * SCOPE_POOL_FACTOR, RECENT_WINDOW_BANDS * RECENT_CONSTRAINTS)).map((p) => hydrate(p.id));
|
|
30678
30825
|
const recentConstraints = constraintPool.slice(0, RECENT_CONSTRAINTS);
|
|
30679
30826
|
const regressionSources = /* @__PURE__ */ new Set();
|
|
30680
30827
|
for (const target of opts.store.inDegreeByType(["REGRESSION_OF"]).keys()) {
|
|
@@ -30970,11 +31117,20 @@ function listSourceFiles(root) {
|
|
|
30970
31117
|
return gitSourceFiles(root) ?? walkSource(root);
|
|
30971
31118
|
}
|
|
30972
31119
|
var SCAN_YIELD_EVERY = 100;
|
|
30973
|
-
async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
31120
|
+
async function findStaleFiles(store2, rootPath, workspaceId, lineageVerifiedAt) {
|
|
30974
31121
|
const stale = [];
|
|
31122
|
+
const changed = lineageVerifiedAt !== void 0 && store2.fileRowStates ? store2.lineageChangedFiles?.(lineageVerifiedAt) : null;
|
|
31123
|
+
const relineage = changed ? new Set(changed) : null;
|
|
30975
31124
|
let pending = [];
|
|
30976
31125
|
const check2 = () => {
|
|
30977
|
-
const
|
|
31126
|
+
const full = relineage ? pending.filter((f) => relineage.has(f.id)) : pending;
|
|
31127
|
+
const states = new Map(
|
|
31128
|
+
store2.fileIndexStates(full.map((f) => f.id)).map((s) => [s.id, s])
|
|
31129
|
+
);
|
|
31130
|
+
if (relineage) {
|
|
31131
|
+
const rowOnly = pending.filter((f) => !relineage.has(f.id)).map((f) => f.id);
|
|
31132
|
+
for (const s of store2.fileRowStates(rowOnly)) states.set(s.id, { ...s, complete: true });
|
|
31133
|
+
}
|
|
30978
31134
|
for (const f of pending) {
|
|
30979
31135
|
const state = states.get(f.id);
|
|
30980
31136
|
if (!state || state.validTo !== null || state.lastUpdatedAt < f.mtimeMs || !state.complete) stale.push(f.abs);
|
|
@@ -30996,9 +31152,18 @@ async function findStaleFiles(store2, rootPath, workspaceId) {
|
|
|
30996
31152
|
check2();
|
|
30997
31153
|
return stale;
|
|
30998
31154
|
}
|
|
31155
|
+
var LINEAGE_VERIFIED_KEY = "pass-inputs:reconcile-lineage";
|
|
30999
31156
|
async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
31000
|
-
const
|
|
31001
|
-
|
|
31157
|
+
const startedAt = store2.changeSeq?.();
|
|
31158
|
+
const verified = Number(store2.getMeta?.(LINEAGE_VERIFIED_KEY) ?? NaN);
|
|
31159
|
+
const stale = await findStaleFiles(store2, rootPath, workspaceId, Number.isFinite(verified) ? verified : void 0);
|
|
31160
|
+
const verify = () => {
|
|
31161
|
+
if (startedAt !== void 0) store2.setMeta?.(LINEAGE_VERIFIED_KEY, String(startedAt));
|
|
31162
|
+
};
|
|
31163
|
+
if (stale.length === 0) {
|
|
31164
|
+
verify();
|
|
31165
|
+
return 0;
|
|
31166
|
+
}
|
|
31002
31167
|
let total = 0;
|
|
31003
31168
|
let failedBatches = 0;
|
|
31004
31169
|
const BATCH = 20;
|
|
@@ -31016,6 +31181,8 @@ async function reconcileStaleFiles(store2, rootPath, workspaceId) {
|
|
|
31016
31181
|
}
|
|
31017
31182
|
if (failedBatches > 0) {
|
|
31018
31183
|
console.warn(`[errata] reconcile: ${failedBatches} batch(es) failed; ${total} file(s) reindexed`);
|
|
31184
|
+
} else {
|
|
31185
|
+
verify();
|
|
31019
31186
|
}
|
|
31020
31187
|
return total;
|
|
31021
31188
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
070354c29f752597560a79b2f85cda3b3988bc46099b17b49f95e8ce37fdad47 .codex-plugin/plugin.json
|
|
2
2
|
a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
|
|
3
3
|
fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
|
|
4
4
|
73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js
|