@inerrata-corporation/errata 2.0.21-dev.4009 → 2.0.21-dev.4014
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 +33 -8
- package/errata.mjs +36 -11
- package/generalize-worker.mjs +18 -4
- package/package.json +1 -1
- package/pass-worker.mjs +17 -3
- package/resources/claude-mods/errata-minimap/.claude-plugin/plugin.json +1 -1
- package/resources/claude-mods/errata-minimap/hooks/register.tsx +41 -25
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +1 -1
package/consolidate-worker.mjs
CHANGED
|
@@ -17371,7 +17371,7 @@ var SqliteGraphStore = class {
|
|
|
17371
17371
|
if (!stmt) {
|
|
17372
17372
|
const cols = attrKeys.map((k, i) => `, json_extract(attrs_json, '$.${k}') AS a${i}`).join("");
|
|
17373
17373
|
stmt = this.db.prepare(
|
|
17374
|
-
`SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
17374
|
+
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
17375
17375
|
);
|
|
17376
17376
|
this.fieldStmts.set(cacheKey, stmt);
|
|
17377
17377
|
}
|
|
@@ -17383,6 +17383,7 @@ var SqliteGraphStore = class {
|
|
|
17383
17383
|
});
|
|
17384
17384
|
return {
|
|
17385
17385
|
id: r["id"],
|
|
17386
|
+
description: r["description"] ?? "",
|
|
17386
17387
|
community: r["community"] ?? null,
|
|
17387
17388
|
extractionConfidence: r["extraction_confidence"],
|
|
17388
17389
|
attrs
|
|
@@ -17515,6 +17516,19 @@ var SqliteGraphStore = class {
|
|
|
17515
17516
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
17516
17517
|
* blob; the liveness watch's coverage pass spent 3.4s of a 4.3s main-thread
|
|
17517
17518
|
* hold doing exactly that on a 227k-node store (2026-09-24). */
|
|
17519
|
+
findNodeStampsByLabel(label, attrKey) {
|
|
17520
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
17521
|
+
const rows = this.db.prepare(
|
|
17522
|
+
`SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has
|
|
17523
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
17524
|
+
).all(label);
|
|
17525
|
+
return rows.map((r) => ({
|
|
17526
|
+
id: r.id,
|
|
17527
|
+
createdAt: r.created_at,
|
|
17528
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
17529
|
+
has: r.has === 1
|
|
17530
|
+
}));
|
|
17531
|
+
}
|
|
17518
17532
|
findNodeAttrsByLabel(label) {
|
|
17519
17533
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
17520
17534
|
return rows.map((r) => ({
|
|
@@ -19599,7 +19613,7 @@ var DOC_COMMENT_KINDS = /* @__PURE__ */ new Set(["docstring", "explanation"]);
|
|
|
19599
19613
|
var MAX_DOC_CHARS = 600;
|
|
19600
19614
|
function buildDocIndex(store) {
|
|
19601
19615
|
const byName = /* @__PURE__ */ new Map();
|
|
19602
|
-
for (const c of store.
|
|
19616
|
+
for (const c of store.findNodeFieldsByLabel("Comment", ["attachedTo", "kind", "text"])) {
|
|
19603
19617
|
const name = c.attrs["attachedTo"];
|
|
19604
19618
|
const kind = c.attrs["kind"];
|
|
19605
19619
|
if (typeof name !== "string" || !name) continue;
|
|
@@ -19615,7 +19629,7 @@ function buildSymbolLexicon(store) {
|
|
|
19615
19629
|
const docs = buildDocIndex(store);
|
|
19616
19630
|
const candidates = [];
|
|
19617
19631
|
for (const [label, kind] of SYMBOL_LABELS) {
|
|
19618
|
-
for (const n of store.
|
|
19632
|
+
for (const n of store.findNodeFieldsByLabel(label, ["qname"])) {
|
|
19619
19633
|
const qname = String(n.attrs["qname"] ?? "");
|
|
19620
19634
|
const doc = (n.description ? docs.get(n.description) : void 0) ?? (qname ? docs.get(qname) : void 0);
|
|
19621
19635
|
for (const name of [n.description, qname]) {
|
|
@@ -19721,7 +19735,7 @@ function buildFileLexicon(store) {
|
|
|
19721
19735
|
const basenameCount = /* @__PURE__ */ new Map();
|
|
19722
19736
|
const stemRelPaths = /* @__PURE__ */ new Map();
|
|
19723
19737
|
const entries = [];
|
|
19724
|
-
for (const n of store.
|
|
19738
|
+
for (const n of store.findNodeFieldsByLabel("File", ["relPath"])) {
|
|
19725
19739
|
const relPath = typeof n.attrs["relPath"] === "string" && n.attrs["relPath"].length > 0 ? n.attrs["relPath"] : n.description;
|
|
19726
19740
|
if (!relPath) continue;
|
|
19727
19741
|
const basename = relPath.split("/").pop();
|
|
@@ -20043,9 +20057,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
20043
20057
|
collapsedEdges: 0
|
|
20044
20058
|
};
|
|
20045
20059
|
const sharedCauses = /* @__PURE__ */ new Map();
|
|
20046
|
-
const causalQueue = store.
|
|
20047
|
-
(n) => n.attrs["source"] !== "cloud" && n.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE && !ignored(n.description)
|
|
20048
|
-
).map((n) => n.id);
|
|
20060
|
+
const causalQueue = store.findNodeFieldsByLabel("Problem", ["source", "resolvedAs"]).filter((r) => r.attrs["source"] !== "cloud" && r.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE).map((r) => r.id).filter((id) => ignorePatterns.length === 0 || !ignored(store.getNode(id)?.description ?? ""));
|
|
20049
20061
|
const causalSeen = new Set(causalQueue);
|
|
20050
20062
|
for (let i = 0; i < causalQueue.length; i++) {
|
|
20051
20063
|
const id = causalQueue[i];
|
|
@@ -20061,7 +20073,20 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
20061
20073
|
for (const label of INSTANCE_LABELS) {
|
|
20062
20074
|
const ref = [];
|
|
20063
20075
|
includedByLabel.set(label, ref);
|
|
20064
|
-
const
|
|
20076
|
+
const local = [];
|
|
20077
|
+
for (const row of store.findNodeFieldsByLabel(label, ["source", "privateFinding"])) {
|
|
20078
|
+
if (row.attrs["source"] !== "cloud") {
|
|
20079
|
+
const n = store.getNode(row.id);
|
|
20080
|
+
if (n) local.push(n);
|
|
20081
|
+
continue;
|
|
20082
|
+
}
|
|
20083
|
+
if (seen.has(row.id) || walked.has(row.id)) continue;
|
|
20084
|
+
walked.add(row.id);
|
|
20085
|
+
composition.candidates++;
|
|
20086
|
+
if ((row.attrs["privateFinding"] || label === "Vulnerability") && !opts.project) composition.privateTargetMissing++;
|
|
20087
|
+
else composition.cloudSourced++;
|
|
20088
|
+
}
|
|
20089
|
+
const population = label === "RootCause" && opts.sharedStore ? [...local, ...sharedCauses.values()] : local;
|
|
20065
20090
|
for (const n of population) {
|
|
20066
20091
|
if (seen.has(n.id)) continue;
|
|
20067
20092
|
if (walked.has(n.id)) continue;
|
package/errata.mjs
CHANGED
|
@@ -20104,7 +20104,7 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
|
|
|
20104
20104
|
if (!stmt) {
|
|
20105
20105
|
const cols = attrKeys.map((k, i2) => `, json_extract(attrs_json, '$.${k}') AS a${i2}`).join("");
|
|
20106
20106
|
stmt = this.db.prepare(
|
|
20107
|
-
`SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
20107
|
+
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
20108
20108
|
);
|
|
20109
20109
|
this.fieldStmts.set(cacheKey2, stmt);
|
|
20110
20110
|
}
|
|
@@ -20116,6 +20116,7 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
|
|
|
20116
20116
|
});
|
|
20117
20117
|
return {
|
|
20118
20118
|
id: r["id"],
|
|
20119
|
+
description: r["description"] ?? "",
|
|
20119
20120
|
community: r["community"] ?? null,
|
|
20120
20121
|
extractionConfidence: r["extraction_confidence"],
|
|
20121
20122
|
attrs
|
|
@@ -20248,6 +20249,19 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
|
|
|
20248
20249
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
20249
20250
|
* blob; the liveness watch's coverage pass spent 3.4s of a 4.3s main-thread
|
|
20250
20251
|
* hold doing exactly that on a 227k-node store (2026-09-24). */
|
|
20252
|
+
findNodeStampsByLabel(label, attrKey) {
|
|
20253
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
20254
|
+
const rows = this.db.prepare(
|
|
20255
|
+
`SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has
|
|
20256
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
20257
|
+
).all(label);
|
|
20258
|
+
return rows.map((r) => ({
|
|
20259
|
+
id: r.id,
|
|
20260
|
+
createdAt: r.created_at,
|
|
20261
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
20262
|
+
has: r.has === 1
|
|
20263
|
+
}));
|
|
20264
|
+
}
|
|
20251
20265
|
findNodeAttrsByLabel(label) {
|
|
20252
20266
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
20253
20267
|
return rows.map((r) => ({
|
|
@@ -25290,8 +25304,8 @@ function evaluateFed(store, d) {
|
|
|
25290
25304
|
if (!d.fed) return { count: 0, total: 0, fraction: 0 };
|
|
25291
25305
|
const column = COLUMN_INPUTS[d.fed.attr];
|
|
25292
25306
|
const fed = d.fed;
|
|
25293
|
-
const
|
|
25294
|
-
const
|
|
25307
|
+
const nodes = fed.labels.flatMap((label) => store.findNodeStampsByLabel ? store.findNodeStampsByLabel(label, fed.attr) : store.findNodeAttrsByLabel(label));
|
|
25308
|
+
const has = (n) => column ? column(n) : n.has ?? n.attrs?.[fed.attr] !== void 0;
|
|
25295
25309
|
let window2 = nodes;
|
|
25296
25310
|
if (fed.recent !== void 0) {
|
|
25297
25311
|
window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, fed.recent);
|
|
@@ -35151,7 +35165,7 @@ function isDistinctiveIdentifier(name2) {
|
|
|
35151
35165
|
}
|
|
35152
35166
|
function buildDocIndex(store) {
|
|
35153
35167
|
const byName = /* @__PURE__ */ new Map();
|
|
35154
|
-
for (const c of store.
|
|
35168
|
+
for (const c of store.findNodeFieldsByLabel("Comment", ["attachedTo", "kind", "text"])) {
|
|
35155
35169
|
const name2 = c.attrs["attachedTo"];
|
|
35156
35170
|
const kind = c.attrs["kind"];
|
|
35157
35171
|
if (typeof name2 !== "string" || !name2) continue;
|
|
@@ -35167,7 +35181,7 @@ function buildSymbolLexicon(store) {
|
|
|
35167
35181
|
const docs = buildDocIndex(store);
|
|
35168
35182
|
const candidates = [];
|
|
35169
35183
|
for (const [label, kind] of SYMBOL_LABELS2) {
|
|
35170
|
-
for (const n of store.
|
|
35184
|
+
for (const n of store.findNodeFieldsByLabel(label, ["qname"])) {
|
|
35171
35185
|
const qname = String(n.attrs["qname"] ?? "");
|
|
35172
35186
|
const doc = (n.description ? docs.get(n.description) : void 0) ?? (qname ? docs.get(qname) : void 0);
|
|
35173
35187
|
for (const name2 of [n.description, qname]) {
|
|
@@ -35222,7 +35236,7 @@ function buildFileLexicon(store) {
|
|
|
35222
35236
|
const basenameCount = /* @__PURE__ */ new Map();
|
|
35223
35237
|
const stemRelPaths = /* @__PURE__ */ new Map();
|
|
35224
35238
|
const entries = [];
|
|
35225
|
-
for (const n of store.
|
|
35239
|
+
for (const n of store.findNodeFieldsByLabel("File", ["relPath"])) {
|
|
35226
35240
|
const relPath = typeof n.attrs["relPath"] === "string" && n.attrs["relPath"].length > 0 ? n.attrs["relPath"] : n.description;
|
|
35227
35241
|
if (!relPath) continue;
|
|
35228
35242
|
const basename9 = relPath.split("/").pop();
|
|
@@ -84052,7 +84066,7 @@ function createWatchBreaker(deps) {
|
|
|
84052
84066
|
}
|
|
84053
84067
|
|
|
84054
84068
|
// src/engine.ts
|
|
84055
|
-
var DAEMON_VERSION = true ? "2.0.21-dev.
|
|
84069
|
+
var DAEMON_VERSION = true ? "2.0.21-dev.4014" : "2.0.0-alpha.0";
|
|
84056
84070
|
var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
|
|
84057
84071
|
var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
|
|
84058
84072
|
var GIT_OP_MUTE_MS = 4e3;
|
|
@@ -87670,9 +87684,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
87670
87684
|
collapsedEdges: 0
|
|
87671
87685
|
};
|
|
87672
87686
|
const sharedCauses = /* @__PURE__ */ new Map();
|
|
87673
|
-
const causalQueue = store.
|
|
87674
|
-
(n) => n.attrs["source"] !== "cloud" && n.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE && !ignored(n.description)
|
|
87675
|
-
).map((n) => n.id);
|
|
87687
|
+
const causalQueue = store.findNodeFieldsByLabel("Problem", ["source", "resolvedAs"]).filter((r) => r.attrs["source"] !== "cloud" && r.attrs["resolvedAs"] !== PROBLEM_RESOLUTION.FALSE_POSITIVE).map((r) => r.id).filter((id) => ignorePatterns.length === 0 || !ignored(store.getNode(id)?.description ?? ""));
|
|
87676
87688
|
const causalSeen = new Set(causalQueue);
|
|
87677
87689
|
for (let i2 = 0; i2 < causalQueue.length; i2++) {
|
|
87678
87690
|
const id = causalQueue[i2];
|
|
@@ -87688,7 +87700,20 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
|
|
|
87688
87700
|
for (const label of INSTANCE_LABELS) {
|
|
87689
87701
|
const ref = [];
|
|
87690
87702
|
includedByLabel.set(label, ref);
|
|
87691
|
-
const
|
|
87703
|
+
const local = [];
|
|
87704
|
+
for (const row of store.findNodeFieldsByLabel(label, ["source", "privateFinding"])) {
|
|
87705
|
+
if (row.attrs["source"] !== "cloud") {
|
|
87706
|
+
const n = store.getNode(row.id);
|
|
87707
|
+
if (n) local.push(n);
|
|
87708
|
+
continue;
|
|
87709
|
+
}
|
|
87710
|
+
if (seen.has(row.id) || walked.has(row.id)) continue;
|
|
87711
|
+
walked.add(row.id);
|
|
87712
|
+
composition.candidates++;
|
|
87713
|
+
if ((row.attrs["privateFinding"] || label === "Vulnerability") && !opts.project) composition.privateTargetMissing++;
|
|
87714
|
+
else composition.cloudSourced++;
|
|
87715
|
+
}
|
|
87716
|
+
const population = label === "RootCause" && opts.sharedStore ? [...local, ...sharedCauses.values()] : local;
|
|
87692
87717
|
for (const n of population) {
|
|
87693
87718
|
if (seen.has(n.id)) continue;
|
|
87694
87719
|
if (walked.has(n.id)) continue;
|
package/generalize-worker.mjs
CHANGED
|
@@ -15779,7 +15779,7 @@ var SqliteGraphStore = class {
|
|
|
15779
15779
|
if (!stmt) {
|
|
15780
15780
|
const cols = attrKeys.map((k, i) => `, json_extract(attrs_json, '$.${k}') AS a${i}`).join("");
|
|
15781
15781
|
stmt = this.db.prepare(
|
|
15782
|
-
`SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
15782
|
+
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
15783
15783
|
);
|
|
15784
15784
|
this.fieldStmts.set(cacheKey, stmt);
|
|
15785
15785
|
}
|
|
@@ -15791,6 +15791,7 @@ var SqliteGraphStore = class {
|
|
|
15791
15791
|
});
|
|
15792
15792
|
return {
|
|
15793
15793
|
id: r["id"],
|
|
15794
|
+
description: r["description"] ?? "",
|
|
15794
15795
|
community: r["community"] ?? null,
|
|
15795
15796
|
extractionConfidence: r["extraction_confidence"],
|
|
15796
15797
|
attrs
|
|
@@ -15923,6 +15924,19 @@ var SqliteGraphStore = class {
|
|
|
15923
15924
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
15924
15925
|
* blob; the liveness watch's coverage pass spent 3.4s of a 4.3s main-thread
|
|
15925
15926
|
* hold doing exactly that on a 227k-node store (2026-09-24). */
|
|
15927
|
+
findNodeStampsByLabel(label, attrKey) {
|
|
15928
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
15929
|
+
const rows = this.db.prepare(
|
|
15930
|
+
`SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has
|
|
15931
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
15932
|
+
).all(label);
|
|
15933
|
+
return rows.map((r) => ({
|
|
15934
|
+
id: r.id,
|
|
15935
|
+
createdAt: r.created_at,
|
|
15936
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
15937
|
+
has: r.has === 1
|
|
15938
|
+
}));
|
|
15939
|
+
}
|
|
15926
15940
|
findNodeAttrsByLabel(label) {
|
|
15927
15941
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
15928
15942
|
return rows.map((r) => ({
|
|
@@ -17100,7 +17114,7 @@ var DOC_COMMENT_KINDS = /* @__PURE__ */ new Set(["docstring", "explanation"]);
|
|
|
17100
17114
|
var MAX_DOC_CHARS = 600;
|
|
17101
17115
|
function buildDocIndex(store) {
|
|
17102
17116
|
const byName = /* @__PURE__ */ new Map();
|
|
17103
|
-
for (const c of store.
|
|
17117
|
+
for (const c of store.findNodeFieldsByLabel("Comment", ["attachedTo", "kind", "text"])) {
|
|
17104
17118
|
const name = c.attrs["attachedTo"];
|
|
17105
17119
|
const kind = c.attrs["kind"];
|
|
17106
17120
|
if (typeof name !== "string" || !name) continue;
|
|
@@ -17116,7 +17130,7 @@ function buildSymbolLexicon(store) {
|
|
|
17116
17130
|
const docs = buildDocIndex(store);
|
|
17117
17131
|
const candidates = [];
|
|
17118
17132
|
for (const [label, kind] of SYMBOL_LABELS) {
|
|
17119
|
-
for (const n of store.
|
|
17133
|
+
for (const n of store.findNodeFieldsByLabel(label, ["qname"])) {
|
|
17120
17134
|
const qname = String(n.attrs["qname"] ?? "");
|
|
17121
17135
|
const doc = (n.description ? docs.get(n.description) : void 0) ?? (qname ? docs.get(qname) : void 0);
|
|
17122
17136
|
for (const name of [n.description, qname]) {
|
|
@@ -17222,7 +17236,7 @@ function buildFileLexicon(store) {
|
|
|
17222
17236
|
const basenameCount = /* @__PURE__ */ new Map();
|
|
17223
17237
|
const stemRelPaths = /* @__PURE__ */ new Map();
|
|
17224
17238
|
const entries = [];
|
|
17225
|
-
for (const n of store.
|
|
17239
|
+
for (const n of store.findNodeFieldsByLabel("File", ["relPath"])) {
|
|
17226
17240
|
const relPath = typeof n.attrs["relPath"] === "string" && n.attrs["relPath"].length > 0 ? n.attrs["relPath"] : n.description;
|
|
17227
17241
|
if (!relPath) continue;
|
|
17228
17242
|
const basename = relPath.split("/").pop();
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -26637,7 +26637,7 @@ var SqliteGraphStore = class {
|
|
|
26637
26637
|
if (!stmt) {
|
|
26638
26638
|
const cols = attrKeys.map((k, i2) => `, json_extract(attrs_json, '$.${k}') AS a${i2}`).join("");
|
|
26639
26639
|
stmt = this.db.prepare(
|
|
26640
|
-
`SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
26640
|
+
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
26641
26641
|
);
|
|
26642
26642
|
this.fieldStmts.set(cacheKey, stmt);
|
|
26643
26643
|
}
|
|
@@ -26649,6 +26649,7 @@ var SqliteGraphStore = class {
|
|
|
26649
26649
|
});
|
|
26650
26650
|
return {
|
|
26651
26651
|
id: r["id"],
|
|
26652
|
+
description: r["description"] ?? "",
|
|
26652
26653
|
community: r["community"] ?? null,
|
|
26653
26654
|
extractionConfidence: r["extraction_confidence"],
|
|
26654
26655
|
attrs
|
|
@@ -26781,6 +26782,19 @@ var SqliteGraphStore = class {
|
|
|
26781
26782
|
* read attrs and never the vector. `findNodesByLabel` decodes every embedding
|
|
26782
26783
|
* blob; the liveness watch's coverage pass spent 3.4s of a 4.3s main-thread
|
|
26783
26784
|
* hold doing exactly that on a 227k-node store (2026-09-24). */
|
|
26785
|
+
findNodeStampsByLabel(label, attrKey) {
|
|
26786
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
26787
|
+
const rows = this.db.prepare(
|
|
26788
|
+
`SELECT id, created_at, created_at_seq, json_type(attrs_json, '$.${attrKey}') IS NOT NULL AS has
|
|
26789
|
+
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
26790
|
+
).all(label);
|
|
26791
|
+
return rows.map((r) => ({
|
|
26792
|
+
id: r.id,
|
|
26793
|
+
createdAt: r.created_at,
|
|
26794
|
+
...r.created_at_seq != null ? { createdAtSeq: r.created_at_seq } : {},
|
|
26795
|
+
has: r.has === 1
|
|
26796
|
+
}));
|
|
26797
|
+
}
|
|
26784
26798
|
findNodeAttrsByLabel(label) {
|
|
26785
26799
|
const rows = this.stmts.attrsByLabel.all(label);
|
|
26786
26800
|
return rows.map((r) => ({
|
|
@@ -28834,8 +28848,8 @@ function evaluateFed(store2, d) {
|
|
|
28834
28848
|
if (!d.fed) return { count: 0, total: 0, fraction: 0 };
|
|
28835
28849
|
const column = COLUMN_INPUTS[d.fed.attr];
|
|
28836
28850
|
const fed = d.fed;
|
|
28837
|
-
const
|
|
28838
|
-
const
|
|
28851
|
+
const nodes = fed.labels.flatMap((label) => store2.findNodeStampsByLabel ? store2.findNodeStampsByLabel(label, fed.attr) : store2.findNodeAttrsByLabel(label));
|
|
28852
|
+
const has = (n) => column ? column(n) : n.has ?? n.attrs?.[fed.attr] !== void 0;
|
|
28839
28853
|
let window2 = nodes;
|
|
28840
28854
|
if (fed.recent !== void 0) {
|
|
28841
28855
|
window2 = [...nodes].sort((a, b) => b.createdAt - a.createdAt).slice(0, fed.recent);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "errata-minimap",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"author": { "name": "inErrata", "url": "https://inerrata.ai" },
|
|
5
5
|
"description": "The errata library: the causal chains of the latest burst that entered the agent's context, in bookcases by technology with a catalog card — and errata's inline tags, tidied in the chat.",
|
|
6
6
|
"types": "./types/index.d.ts"
|
|
@@ -386,6 +386,11 @@ function isTagMode(value: unknown): value is TagMode {
|
|
|
386
386
|
return TAG_MODES.includes(value as TagMode)
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
+
/** A book's hover group: its spine rows and its card light together. */
|
|
390
|
+
function scopeOf(book: { key: string }): string {
|
|
391
|
+
return `book:${book.key}`.slice(0, 64)
|
|
392
|
+
}
|
|
393
|
+
|
|
389
394
|
function isPaneView(value: unknown): value is PaneView {
|
|
390
395
|
return PANE_VIEWS.includes(value as PaneView)
|
|
391
396
|
}
|
|
@@ -657,28 +662,35 @@ export const register: Register = on => {
|
|
|
657
662
|
return (
|
|
658
663
|
<Box key="library" position="relative" width={width} height={libraryRows}>
|
|
659
664
|
<Raster key="map" columns={width} rows={libraryRows} cells={cells} />
|
|
665
|
+
{/* Every book's hit-area first… */}
|
|
666
|
+
{spots.flatMap(s => {
|
|
667
|
+
const scope = scopeOf(s.book)
|
|
668
|
+
const pin = () => {
|
|
669
|
+
motion.focus = s.book.problem?.id ?? s.book.memberIds[0]
|
|
670
|
+
$.ui.invalidate('ui.render')
|
|
671
|
+
}
|
|
672
|
+
return [...Array(s.height).keys()].map(r => (
|
|
673
|
+
<Box key={`${scope}:${r}`} position="absolute" top={s.top + r} left={s.x} hover={{ scope }}>
|
|
674
|
+
<Button key={`${scope}:press:${r}`} plain onPress={pin}>
|
|
675
|
+
{[...Array(s.width).keys()].map(dx => {
|
|
676
|
+
const c = cell(s.x + dx, s.top + r)
|
|
677
|
+
return <Text color={colourOf(c.fg)} backgroundColor={colourOf(c.bg)}>{c.ch}</Text>
|
|
678
|
+
})}
|
|
679
|
+
</Button>
|
|
680
|
+
</Box>
|
|
681
|
+
))
|
|
682
|
+
})}
|
|
683
|
+
{/* …then every card. Positioned boxes paint in tree order, so a card emitted beside its own
|
|
684
|
+
book was painted over by every book after it. Last in the tree, a card is over all of them;
|
|
685
|
+
the background makes it opaque, or the books behind would show through its blank cells. */}
|
|
660
686
|
{spots.map(s => {
|
|
661
|
-
const scope =
|
|
687
|
+
const scope = scopeOf(s.book)
|
|
662
688
|
const parts = (['cause', 'problem', 'fix'] as const).flatMap(r => (s.book[r] ? [s.book[r]!] : []))
|
|
663
689
|
const cardHeight = parts.length + 3
|
|
664
690
|
const cardLeft = s.x + s.width + 1 + cardWidth <= width ? s.x + s.width + 1 : Math.max(0, s.x - cardWidth - 1)
|
|
665
691
|
const cardTop = Math.max(0, Math.min(s.top, libraryRows - cardHeight))
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
$.ui.invalidate('ui.render')
|
|
669
|
-
}
|
|
670
|
-
return [
|
|
671
|
-
...[...Array(s.height).keys()].map(r => (
|
|
672
|
-
<Box key={`${scope}:${r}`} position="absolute" top={s.top + r} left={s.x} hover={{ scope }}>
|
|
673
|
-
<Button key={`${scope}:press:${r}`} plain onPress={pin}>
|
|
674
|
-
{[...Array(s.width).keys()].map(dx => {
|
|
675
|
-
const c = cell(s.x + dx, s.top + r)
|
|
676
|
-
return <Text color={colourOf(c.fg)} backgroundColor={colourOf(c.bg)}>{c.ch}</Text>
|
|
677
|
-
})}
|
|
678
|
-
</Button>
|
|
679
|
-
</Box>
|
|
680
|
-
)),
|
|
681
|
-
// The hover card: what the book holds, revealed while the pointer is anywhere on its spine.
|
|
692
|
+
const paper = hex(palette.card)
|
|
693
|
+
return (
|
|
682
694
|
<Box
|
|
683
695
|
key={`${scope}:card`}
|
|
684
696
|
position="absolute"
|
|
@@ -686,23 +698,27 @@ export const register: Register = on => {
|
|
|
686
698
|
left={cardLeft}
|
|
687
699
|
width={cardWidth}
|
|
688
700
|
display="none"
|
|
689
|
-
hover={{ scope, display: 'flex' }}
|
|
701
|
+
hover={{ scope, display: 'flex', backgroundColor: paper }}
|
|
690
702
|
flexDirection="column"
|
|
691
703
|
borderStyle="round"
|
|
692
704
|
borderColor={hex(palette.gilt)}
|
|
693
705
|
>
|
|
694
|
-
<Text bold wrap="truncate-end" color={hex(palette.binding.problem)}>
|
|
695
|
-
{callNumber(catalog, s.book)}
|
|
706
|
+
<Text bold wrap="truncate-end" color={hex(palette.binding.problem)} backgroundColor={paper}>
|
|
707
|
+
{fitTitle(`${callNumber(catalog, s.book)} ${s.book.tech ?? 'general'}`, cardWidth - 2).padEnd(cardWidth - 2)}
|
|
696
708
|
</Text>
|
|
697
709
|
{parts.map(part => (
|
|
698
|
-
<Text wrap="truncate-end">
|
|
710
|
+
<Text wrap="truncate-end" backgroundColor={paper}>
|
|
699
711
|
<Text color={hex(palette.binding[bindingOf(part.label)])}>{BINDING_GLYPH[bindingOf(part.label)]} </Text>
|
|
700
|
-
<Text
|
|
712
|
+
<Text color={hex(part.isDelivered ? palette.cardInk : palette.cardFaint)}>
|
|
713
|
+
{fitTitle(part.name, cardWidth - 4).padEnd(cardWidth - 4)}
|
|
714
|
+
</Text>
|
|
701
715
|
</Text>
|
|
702
716
|
))}
|
|
703
|
-
<Text
|
|
704
|
-
|
|
705
|
-
|
|
717
|
+
<Text color={hex(palette.cardFaint)} backgroundColor={paper} wrap="truncate-end">
|
|
718
|
+
{'click to read it on the card'.padEnd(cardWidth - 2)}
|
|
719
|
+
</Text>
|
|
720
|
+
</Box>
|
|
721
|
+
)
|
|
706
722
|
})}
|
|
707
723
|
</Box>
|
|
708
724
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
3a8ec47210590c6000ed069594b1e6bd6a0c1bf3836374d51933cb7ba6c7b0a7 .codex-plugin/plugin.json
|
|
2
2
|
a7bebae9fb01c838d6c75fc784129e28be571233fa66ff5ee8039b574f35ccf7 assets/icon.png
|
|
3
3
|
fc38b64e4df1e0b72c35764bef6c4c05430d97c9009ad0ecc8a9e75b4deab232 configure-local.mjs
|
|
4
4
|
73dc6028b3b070257c7c31f252d11e55a9e096f4aed0f187f10cb21716e1e6d7 dist/client.js
|