@inerrata-corporation/errata 2.0.21-dev.3993 → 2.0.21-dev.4001

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.
@@ -16969,6 +16969,12 @@ var SqliteGraphStore = class {
16969
16969
  setPageRank: this.db.prepare("UPDATE nodes SET page_rank = :v WHERE id = :id"),
16970
16970
  setLandmark: this.db.prepare("UPDATE nodes SET is_landmark = :v WHERE id = :id"),
16971
16971
  setCommunity: this.db.prepare("UPDATE nodes SET community = :v WHERE id = :id"),
16972
+ setLocalCommunity: this.db.prepare(`
16973
+ UPDATE nodes SET
16974
+ attrs_json = CASE WHEN :v IS NULL THEN json_remove(attrs_json, '$.localCommunity')
16975
+ ELSE json_set(attrs_json, '$.localCommunity', :v) END,
16976
+ community = CASE WHEN :col = 1 THEN :v ELSE community END
16977
+ WHERE id = :id`),
16972
16978
  updateNode: this.db.prepare(`
16973
16979
  UPDATE nodes SET
16974
16980
  description = COALESCE(:description, description),
@@ -17289,6 +17295,35 @@ var SqliteGraphStore = class {
17289
17295
  setLandmark(id, isLandmark) {
17290
17296
  this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
17291
17297
  }
17298
+ setLocalCommunity(id, community, column) {
17299
+ this.stmts.setLocalCommunity.run({ id, v: community, col: column ? 1 : 0 });
17300
+ }
17301
+ fieldStmts = /* @__PURE__ */ new Map();
17302
+ findNodeFieldsByLabel(label, attrKeys) {
17303
+ for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
17304
+ const cacheKey = attrKeys.join(",");
17305
+ let stmt = this.fieldStmts.get(cacheKey);
17306
+ if (!stmt) {
17307
+ const cols = attrKeys.map((k, i) => `, json_extract(attrs_json, '$.${k}') AS a${i}`).join("");
17308
+ stmt = this.db.prepare(
17309
+ `SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
17310
+ );
17311
+ this.fieldStmts.set(cacheKey, stmt);
17312
+ }
17313
+ return stmt.all(label).map((r) => {
17314
+ const attrs = {};
17315
+ attrKeys.forEach((k, i) => {
17316
+ const v = r[`a${i}`];
17317
+ if (v !== null && v !== void 0) attrs[k] = v;
17318
+ });
17319
+ return {
17320
+ id: r["id"],
17321
+ community: r["community"] ?? null,
17322
+ extractionConfidence: r["extraction_confidence"],
17323
+ attrs
17324
+ };
17325
+ });
17326
+ }
17292
17327
  setCommunity(id, community) {
17293
17328
  this.stmts.setCommunity.run({ id, v: community });
17294
17329
  }
package/errata.mjs CHANGED
@@ -19699,6 +19699,12 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
19699
19699
  setPageRank: this.db.prepare("UPDATE nodes SET page_rank = :v WHERE id = :id"),
19700
19700
  setLandmark: this.db.prepare("UPDATE nodes SET is_landmark = :v WHERE id = :id"),
19701
19701
  setCommunity: this.db.prepare("UPDATE nodes SET community = :v WHERE id = :id"),
19702
+ setLocalCommunity: this.db.prepare(`
19703
+ UPDATE nodes SET
19704
+ attrs_json = CASE WHEN :v IS NULL THEN json_remove(attrs_json, '$.localCommunity')
19705
+ ELSE json_set(attrs_json, '$.localCommunity', :v) END,
19706
+ community = CASE WHEN :col = 1 THEN :v ELSE community END
19707
+ WHERE id = :id`),
19702
19708
  updateNode: this.db.prepare(`
19703
19709
  UPDATE nodes SET
19704
19710
  description = COALESCE(:description, description),
@@ -20019,6 +20025,35 @@ CREATE INDEX IF NOT EXISTS nodes_enrichment_pending ON nodes(json_extract(attrs_
20019
20025
  setLandmark(id, isLandmark) {
20020
20026
  this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
20021
20027
  }
20028
+ setLocalCommunity(id, community, column) {
20029
+ this.stmts.setLocalCommunity.run({ id, v: community, col: column ? 1 : 0 });
20030
+ }
20031
+ fieldStmts = /* @__PURE__ */ new Map();
20032
+ findNodeFieldsByLabel(label, attrKeys) {
20033
+ for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
20034
+ const cacheKey2 = attrKeys.join(",");
20035
+ let stmt = this.fieldStmts.get(cacheKey2);
20036
+ if (!stmt) {
20037
+ const cols = attrKeys.map((k, i2) => `, json_extract(attrs_json, '$.${k}') AS a${i2}`).join("");
20038
+ stmt = this.db.prepare(
20039
+ `SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
20040
+ );
20041
+ this.fieldStmts.set(cacheKey2, stmt);
20042
+ }
20043
+ return stmt.all(label).map((r) => {
20044
+ const attrs = {};
20045
+ attrKeys.forEach((k, i2) => {
20046
+ const v = r[`a${i2}`];
20047
+ if (v !== null && v !== void 0) attrs[k] = v;
20048
+ });
20049
+ return {
20050
+ id: r["id"],
20051
+ community: r["community"] ?? null,
20052
+ extractionConfidence: r["extraction_confidence"],
20053
+ attrs
20054
+ };
20055
+ });
20056
+ }
20022
20057
  setCommunity(id, community) {
20023
20058
  this.stmts.setCommunity.run({ id, v: community });
20024
20059
  }
@@ -24640,25 +24675,23 @@ function detectLocalCommunities(store, opts = {}) {
24640
24675
  const nodes = /* @__PURE__ */ new Map();
24641
24676
  const unowned = [];
24642
24677
  for (const label of JOINT_COMMUNITY_LABELS) {
24643
- for (const n of store.findNodesByLabel(label)) {
24678
+ for (const n of store.findNodeFieldsByLabel(label, PASS_ATTRS)) {
24644
24679
  if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) unowned.push(n);
24645
- else nodes.set(n.id, n);
24680
+ else nodes.set(n.id, { ...n, label });
24646
24681
  }
24647
24682
  }
24648
24683
  const ids = new Set(nodes.keys());
24649
24684
  report.candidates = ids.size;
24650
24685
  for (const label of SEMANTIC_NODE_LABELS) {
24651
- if (!JOINT_COMMUNITY_LABELS.includes(label)) unowned.push(...store.findNodesByLabel(label));
24686
+ if (!JOINT_COMMUNITY_LABELS.includes(label)) unowned.push(...store.findNodeFieldsByLabel(label, PASS_ATTRS));
24652
24687
  }
24653
24688
  store.transaction(() => {
24654
24689
  for (const n of unowned) {
24655
- if (n.attrs["localCommunity"] !== void 0) {
24656
- const { localCommunity: _gone, ...rest2 } = n.attrs;
24657
- store.updateNode(n.id, { attrs: rest2 });
24658
- }
24659
- if (!n.community || n.attrs["source"] === "cloud") continue;
24660
- store.setCommunity(n.id, null);
24661
- report.cleared++;
24690
+ const pulled = n.attrs["source"] === "cloud";
24691
+ const clearColumn = !!n.community && !pulled;
24692
+ if (n.attrs["localCommunity"] === void 0 && !clearColumn) continue;
24693
+ store.setLocalCommunity(n.id, null, clearColumn);
24694
+ if (clearColumn) report.cleared++;
24662
24695
  }
24663
24696
  });
24664
24697
  if (ids.size === 0) return report;
@@ -24706,14 +24739,10 @@ function detectLocalCommunities(store, opts = {}) {
24706
24739
  const pulled = n.attrs["source"] === "cloud";
24707
24740
  if (pulled) hasCloud = true;
24708
24741
  else hasLocal = true;
24709
- const want = qualifies ? cid : void 0;
24710
- if (n.attrs["localCommunity"] !== want) {
24711
- const nextAttrs = { ...n.attrs };
24712
- if (want) nextAttrs["localCommunity"] = want;
24713
- else delete nextAttrs["localCommunity"];
24714
- store.updateNode(id, { attrs: nextAttrs });
24715
- }
24716
- if (!pulled && (n.community ?? null) !== (want ?? null)) store.setCommunity(id, want ?? null);
24742
+ const want = qualifies ? cid : null;
24743
+ const attrChanged = (n.attrs["localCommunity"] ?? null) !== want;
24744
+ const columnChanged = !pulled && (n.community ?? null) !== want;
24745
+ if (attrChanged || columnChanged) store.setLocalCommunity(id, want, !pulled);
24717
24746
  }
24718
24747
  if (qualifies) {
24719
24748
  report.communities++;
@@ -24797,12 +24826,13 @@ function communityInductionRequests(store, opts = {}) {
24797
24826
  };
24798
24827
  }).filter((r) => r.members.length >= MIN_INDUCTION_MEMBERS).slice(0, maxCommunities);
24799
24828
  }
24800
- var JOINT_COMMUNITY_LABELS, JOINT_COMMUNITY_EDGES, DOMAIN_EDGES, DOMAIN_INHERIT_EDGES, MIN_INDUCTION_MEMBERS;
24829
+ var PASS_ATTRS, JOINT_COMMUNITY_LABELS, JOINT_COMMUNITY_EDGES, DOMAIN_EDGES, DOMAIN_INHERIT_EDGES, MIN_INDUCTION_MEMBERS;
24801
24830
  var init_community2 = __esm({
24802
24831
  "../../packages/local-graph/src/community.ts"() {
24803
24832
  "use strict";
24804
24833
  init_src2();
24805
24834
  init_src3();
24835
+ PASS_ATTRS = ["resolvedAs", "source", "localCommunity"];
24806
24836
  JOINT_COMMUNITY_LABELS = [
24807
24837
  "Problem",
24808
24838
  "Vulnerability",
@@ -32153,12 +32183,12 @@ function promoteMotifs(store, t) {
32153
32183
  const report = { communities: 0, candidates: 0, incoherent: 0, promoted: 0, techniques: 0, antipatterns: 0, retracted: 0, retired: 0 };
32154
32184
  const byCommunity = /* @__PURE__ */ new Map();
32155
32185
  for (const label of MEMBER_LABELS) {
32156
- for (const n of store.findNodesByLabel(label)) {
32157
- if (n.label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
32186
+ for (const n of store.findNodeFieldsByLabel(label, ["localCommunity", "resolvedAs"])) {
32187
+ if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
32158
32188
  const c = n.attrs["localCommunity"];
32159
32189
  if (typeof c !== "string") continue;
32160
32190
  const g = byCommunity.get(c) ?? [];
32161
- g.push(n);
32191
+ g.push({ id: n.id, label, extractionConfidence: n.extractionConfidence });
32162
32192
  byCommunity.set(c, g);
32163
32193
  }
32164
32194
  }
@@ -32211,7 +32241,8 @@ function promoteMotifs(store, t) {
32211
32241
  report.candidates++;
32212
32242
  kept.add(motifId);
32213
32243
  const kind = classifyMotif(store, instances, d.promoted);
32214
- const exemplar = exemplarOf(members);
32244
+ const exemplarRef = exemplarOf(members);
32245
+ const exemplar = exemplarRef ? store.getNode(exemplarRef.id) ?? void 0 : void 0;
32215
32246
  const motif = {
32216
32247
  id: motifId,
32217
32248
  label: kind,
@@ -45013,6 +45044,10 @@ function edgeTier(store, farId) {
45013
45044
  function byTier(store, items, farId) {
45014
45045
  return items.map((item2, i2) => ({ item: item2, i: i2, tier: edgeTier(store, farId(item2)) })).sort((a, b) => a.tier - b.tier || a.i - b.i).map((x) => x.item);
45015
45046
  }
45047
+ function localCommunityOf(node2) {
45048
+ const cid = node2.attrs["localCommunity"];
45049
+ return typeof cid === "string" ? cid : null;
45050
+ }
45016
45051
  function searchGraph(store, query, limit) {
45017
45052
  const q = query.trim();
45018
45053
  if (!q || !Number.isFinite(limit) || limit < 1) return [];
@@ -45550,7 +45585,7 @@ var init_mcp = __esm({
45550
45585
  },
45551
45586
  {
45552
45587
  name: "errata.neighbors",
45553
- description: "Return direct in/out edges of a node. Pass either a nodeId or a qname (e.g. ClassName.method) \u2014 resolved like the other nav verbs. Use after errata.search or errata.locate to walk the graph. A cloud id from search's collective blend (e.g. dprob_\u2026) that isn't in the local graph falls back to its collective neighborhood, tagged provenance:collective.",
45588
+ description: "Return direct in/out edges of a node. Pass either a nodeId or a qname (e.g. ClassName.method) \u2014 resolved like the other nav verbs. Use after errata.search or errata.locate to walk the graph. `node.community` (and each placed far end's `community`) is the local partition's community id, null when unplaced. A cloud id from search's collective blend (e.g. dprob_\u2026) that isn't in the local graph falls back to its collective neighborhood, tagged provenance:collective.",
45554
45589
  inputSchema: {
45555
45590
  type: "object",
45556
45591
  properties: {
@@ -45588,12 +45623,16 @@ var init_mcp = __esm({
45588
45623
  const p = e.attrs?.provenance;
45589
45624
  return p ? { provenance: p } : {};
45590
45625
  };
45626
+ const far = (n) => {
45627
+ const community = localCommunityOf(n);
45628
+ return { id: n.id, label: n.label, name: n.description, ...community ? { community } : {} };
45629
+ };
45591
45630
  const out2 = byTier(store, store.outEdges(id), (e) => e.to).slice(0, limit).map((e) => {
45592
45631
  const t = store.getNode(e.to);
45593
45632
  return {
45594
45633
  type: e.type,
45595
45634
  ...prov(e),
45596
- to: t ? { id: t.id, label: t.label, name: t.description } : { id: e.to }
45635
+ to: t ? far(t) : { id: e.to }
45597
45636
  };
45598
45637
  });
45599
45638
  const incoming = byTier(store, store.inEdges(id), (e) => e.from).slice(0, limit).map((e) => {
@@ -45601,12 +45640,12 @@ var init_mcp = __esm({
45601
45640
  return {
45602
45641
  type: e.type,
45603
45642
  ...prov(e),
45604
- from: f ? { id: f.id, label: f.label, name: f.description } : { id: e.from }
45643
+ from: f ? far(f) : { id: e.from }
45605
45644
  };
45606
45645
  });
45607
45646
  return {
45608
45647
  found: true,
45609
- node: { id: node2.id, label: node2.label, name: node2.description },
45648
+ node: { id: node2.id, label: node2.label, name: node2.description, community: localCommunityOf(node2) },
45610
45649
  out: out2,
45611
45650
  in: incoming
45612
45651
  };
@@ -83934,7 +83973,7 @@ function createWatchBreaker(deps) {
83934
83973
  }
83935
83974
 
83936
83975
  // src/engine.ts
83937
- var DAEMON_VERSION = true ? "2.0.21-dev.3993" : "2.0.0-alpha.0";
83976
+ var DAEMON_VERSION = true ? "2.0.21-dev.4001" : "2.0.0-alpha.0";
83938
83977
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
83939
83978
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
83940
83979
  var GIT_OP_MUTE_MS = 4e3;
@@ -15363,6 +15363,12 @@ var SqliteGraphStore = class {
15363
15363
  setPageRank: this.db.prepare("UPDATE nodes SET page_rank = :v WHERE id = :id"),
15364
15364
  setLandmark: this.db.prepare("UPDATE nodes SET is_landmark = :v WHERE id = :id"),
15365
15365
  setCommunity: this.db.prepare("UPDATE nodes SET community = :v WHERE id = :id"),
15366
+ setLocalCommunity: this.db.prepare(`
15367
+ UPDATE nodes SET
15368
+ attrs_json = CASE WHEN :v IS NULL THEN json_remove(attrs_json, '$.localCommunity')
15369
+ ELSE json_set(attrs_json, '$.localCommunity', :v) END,
15370
+ community = CASE WHEN :col = 1 THEN :v ELSE community END
15371
+ WHERE id = :id`),
15366
15372
  updateNode: this.db.prepare(`
15367
15373
  UPDATE nodes SET
15368
15374
  description = COALESCE(:description, description),
@@ -15683,6 +15689,35 @@ var SqliteGraphStore = class {
15683
15689
  setLandmark(id, isLandmark) {
15684
15690
  this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
15685
15691
  }
15692
+ setLocalCommunity(id, community, column) {
15693
+ this.stmts.setLocalCommunity.run({ id, v: community, col: column ? 1 : 0 });
15694
+ }
15695
+ fieldStmts = /* @__PURE__ */ new Map();
15696
+ findNodeFieldsByLabel(label, attrKeys) {
15697
+ for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
15698
+ const cacheKey = attrKeys.join(",");
15699
+ let stmt = this.fieldStmts.get(cacheKey);
15700
+ if (!stmt) {
15701
+ const cols = attrKeys.map((k, i) => `, json_extract(attrs_json, '$.${k}') AS a${i}`).join("");
15702
+ stmt = this.db.prepare(
15703
+ `SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
15704
+ );
15705
+ this.fieldStmts.set(cacheKey, stmt);
15706
+ }
15707
+ return stmt.all(label).map((r) => {
15708
+ const attrs = {};
15709
+ attrKeys.forEach((k, i) => {
15710
+ const v = r[`a${i}`];
15711
+ if (v !== null && v !== void 0) attrs[k] = v;
15712
+ });
15713
+ return {
15714
+ id: r["id"],
15715
+ community: r["community"] ?? null,
15716
+ extractionConfidence: r["extraction_confidence"],
15717
+ attrs
15718
+ };
15719
+ });
15720
+ }
15686
15721
  setCommunity(id, community) {
15687
15722
  this.stmts.setCommunity.run({ id, v: community });
15688
15723
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.21-dev.3993",
3
+ "version": "2.0.21-dev.4001",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -26244,6 +26244,12 @@ var SqliteGraphStore = class {
26244
26244
  setPageRank: this.db.prepare("UPDATE nodes SET page_rank = :v WHERE id = :id"),
26245
26245
  setLandmark: this.db.prepare("UPDATE nodes SET is_landmark = :v WHERE id = :id"),
26246
26246
  setCommunity: this.db.prepare("UPDATE nodes SET community = :v WHERE id = :id"),
26247
+ setLocalCommunity: this.db.prepare(`
26248
+ UPDATE nodes SET
26249
+ attrs_json = CASE WHEN :v IS NULL THEN json_remove(attrs_json, '$.localCommunity')
26250
+ ELSE json_set(attrs_json, '$.localCommunity', :v) END,
26251
+ community = CASE WHEN :col = 1 THEN :v ELSE community END
26252
+ WHERE id = :id`),
26247
26253
  updateNode: this.db.prepare(`
26248
26254
  UPDATE nodes SET
26249
26255
  description = COALESCE(:description, description),
@@ -26564,6 +26570,35 @@ var SqliteGraphStore = class {
26564
26570
  setLandmark(id, isLandmark) {
26565
26571
  this.stmts.setLandmark.run({ id, v: isLandmark ? 1 : 0 });
26566
26572
  }
26573
+ setLocalCommunity(id, community, column) {
26574
+ this.stmts.setLocalCommunity.run({ id, v: community, col: column ? 1 : 0 });
26575
+ }
26576
+ fieldStmts = /* @__PURE__ */ new Map();
26577
+ findNodeFieldsByLabel(label, attrKeys) {
26578
+ for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
26579
+ const cacheKey = attrKeys.join(",");
26580
+ let stmt = this.fieldStmts.get(cacheKey);
26581
+ if (!stmt) {
26582
+ const cols = attrKeys.map((k, i2) => `, json_extract(attrs_json, '$.${k}') AS a${i2}`).join("");
26583
+ stmt = this.db.prepare(
26584
+ `SELECT id, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
26585
+ );
26586
+ this.fieldStmts.set(cacheKey, stmt);
26587
+ }
26588
+ return stmt.all(label).map((r) => {
26589
+ const attrs = {};
26590
+ attrKeys.forEach((k, i2) => {
26591
+ const v = r[`a${i2}`];
26592
+ if (v !== null && v !== void 0) attrs[k] = v;
26593
+ });
26594
+ return {
26595
+ id: r["id"],
26596
+ community: r["community"] ?? null,
26597
+ extractionConfidence: r["extraction_confidence"],
26598
+ attrs
26599
+ };
26600
+ });
26601
+ }
26567
26602
  setCommunity(id, community) {
26568
26603
  this.stmts.setCommunity.run({ id, v: community });
26569
26604
  }
@@ -28322,6 +28357,7 @@ var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
28322
28357
 
28323
28358
  // ../../packages/local-graph/src/community.ts
28324
28359
  init_src2();
28360
+ var PASS_ATTRS = ["resolvedAs", "source", "localCommunity"];
28325
28361
  var JOINT_COMMUNITY_LABELS = [
28326
28362
  "Problem",
28327
28363
  "Vulnerability",
@@ -28369,25 +28405,23 @@ function detectLocalCommunities(store2, opts = {}) {
28369
28405
  const nodes = /* @__PURE__ */ new Map();
28370
28406
  const unowned = [];
28371
28407
  for (const label of JOINT_COMMUNITY_LABELS) {
28372
- for (const n of store2.findNodesByLabel(label)) {
28408
+ for (const n of store2.findNodeFieldsByLabel(label, PASS_ATTRS)) {
28373
28409
  if (n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) unowned.push(n);
28374
- else nodes.set(n.id, n);
28410
+ else nodes.set(n.id, { ...n, label });
28375
28411
  }
28376
28412
  }
28377
28413
  const ids = new Set(nodes.keys());
28378
28414
  report.candidates = ids.size;
28379
28415
  for (const label of SEMANTIC_NODE_LABELS) {
28380
- if (!JOINT_COMMUNITY_LABELS.includes(label)) unowned.push(...store2.findNodesByLabel(label));
28416
+ if (!JOINT_COMMUNITY_LABELS.includes(label)) unowned.push(...store2.findNodeFieldsByLabel(label, PASS_ATTRS));
28381
28417
  }
28382
28418
  store2.transaction(() => {
28383
28419
  for (const n of unowned) {
28384
- if (n.attrs["localCommunity"] !== void 0) {
28385
- const { localCommunity: _gone, ...rest } = n.attrs;
28386
- store2.updateNode(n.id, { attrs: rest });
28387
- }
28388
- if (!n.community || n.attrs["source"] === "cloud") continue;
28389
- store2.setCommunity(n.id, null);
28390
- report.cleared++;
28420
+ const pulled = n.attrs["source"] === "cloud";
28421
+ const clearColumn = !!n.community && !pulled;
28422
+ if (n.attrs["localCommunity"] === void 0 && !clearColumn) continue;
28423
+ store2.setLocalCommunity(n.id, null, clearColumn);
28424
+ if (clearColumn) report.cleared++;
28391
28425
  }
28392
28426
  });
28393
28427
  if (ids.size === 0) return report;
@@ -28435,14 +28469,10 @@ function detectLocalCommunities(store2, opts = {}) {
28435
28469
  const pulled = n.attrs["source"] === "cloud";
28436
28470
  if (pulled) hasCloud = true;
28437
28471
  else hasLocal = true;
28438
- const want = qualifies ? cid : void 0;
28439
- if (n.attrs["localCommunity"] !== want) {
28440
- const nextAttrs = { ...n.attrs };
28441
- if (want) nextAttrs["localCommunity"] = want;
28442
- else delete nextAttrs["localCommunity"];
28443
- store2.updateNode(id, { attrs: nextAttrs });
28444
- }
28445
- if (!pulled && (n.community ?? null) !== (want ?? null)) store2.setCommunity(id, want ?? null);
28472
+ const want = qualifies ? cid : null;
28473
+ const attrChanged = (n.attrs["localCommunity"] ?? null) !== want;
28474
+ const columnChanged = !pulled && (n.community ?? null) !== want;
28475
+ if (attrChanged || columnChanged) store2.setLocalCommunity(id, want, !pulled);
28446
28476
  }
28447
28477
  if (qualifies) {
28448
28478
  report.communities++;
@@ -28916,12 +28946,12 @@ function promoteMotifs(store2, t) {
28916
28946
  const report = { communities: 0, candidates: 0, incoherent: 0, promoted: 0, techniques: 0, antipatterns: 0, retracted: 0, retired: 0 };
28917
28947
  const byCommunity = /* @__PURE__ */ new Map();
28918
28948
  for (const label of MEMBER_LABELS) {
28919
- for (const n of store2.findNodesByLabel(label)) {
28920
- if (n.label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
28949
+ for (const n of store2.findNodeFieldsByLabel(label, ["localCommunity", "resolvedAs"])) {
28950
+ if (label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) continue;
28921
28951
  const c = n.attrs["localCommunity"];
28922
28952
  if (typeof c !== "string") continue;
28923
28953
  const g = byCommunity.get(c) ?? [];
28924
- g.push(n);
28954
+ g.push({ id: n.id, label, extractionConfidence: n.extractionConfidence });
28925
28955
  byCommunity.set(c, g);
28926
28956
  }
28927
28957
  }
@@ -28974,7 +29004,8 @@ function promoteMotifs(store2, t) {
28974
29004
  report.candidates++;
28975
29005
  kept.add(motifId);
28976
29006
  const kind = classifyMotif(store2, instances, d.promoted);
28977
- const exemplar = exemplarOf(members);
29007
+ const exemplarRef = exemplarOf(members);
29008
+ const exemplar = exemplarRef ? store2.getNode(exemplarRef.id) ?? void 0 : void 0;
28978
29009
  const motif = {
28979
29010
  id: motifId,
28980
29011
  label: kind,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "errata-minimap",
3
- "version": "0.1.0",
3
+ "version": "0.3.1",
4
4
  "author": { "name": "inErrata", "url": "https://inerrata.ai" },
5
- "description": "Side pane: an errata graph minimap of the latest burst neighborhood that entered the agent's context.",
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"
7
7
  }