@inerrata-corporation/errata 2.0.2-dev.79 → 2.0.2-dev.85

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.
@@ -15194,7 +15194,10 @@ var init_edge_rules = __esm({
15194
15194
  // NOT ruled here — the type pre-exists with broader extractor senses, and a
15195
15195
  // new rule on an old type would reject legitimate live flows (reject-never-flip
15196
15196
  // cuts both ways: only rule types you introduce or senses that are documented).
15197
- CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15197
+ // `Component` joined the target set with OM-agent-anchors: an agent-named
15198
+ // component ("React Router") is the same knowledge→named-unit anchor shape as
15199
+ // a Tool — the knowledge is ABOUT it, not dependent on it.
15200
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
15198
15201
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15199
15202
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15200
15203
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -16028,6 +16031,13 @@ var SqliteGraphStore = class {
16028
16031
  findByLabel: this.db.prepare(
16029
16032
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
16030
16033
  ),
16034
+ // Scalar count twin of findByLabel — same live-row semantics, no row
16035
+ // hydration. Exists because status surfaces (daemon `/` + `/health`) used
16036
+ // findNodesByLabel(...).length, materializing every row's attrs JSON and
16037
+ // embedding blob per request — seconds of synchronous loop-hold per poll.
16038
+ countByLabel: this.db.prepare(
16039
+ "SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
16040
+ ),
16031
16041
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
16032
16042
  // clean-reindex purge so a true wipe removes history too, not just live rows.
16033
16043
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -16281,6 +16291,13 @@ var SqliteGraphStore = class {
16281
16291
  const rows = this.stmts.findByLabel.all(label);
16282
16292
  return rows.map(rowToNode);
16283
16293
  }
16294
+ /** Live-row count for a label — `findNodesByLabel(label).length` without the
16295
+ * per-row hydration (attrs JSON + embedding blob). Status surfaces poll this
16296
+ * per project per request; the materializing form held the daemon's event
16297
+ * loop for seconds at scale. */
16298
+ countNodesByLabel(label) {
16299
+ return Number(this.stmts.countByLabel.get(label).n);
16300
+ }
16284
16301
  findAllVersionsByLabel(label) {
16285
16302
  const rows = this.stmts.findByLabelAll.all(label);
16286
16303
  return rows.map(rowToNode);
package/errata.mjs CHANGED
@@ -15369,7 +15369,10 @@ var init_edge_rules = __esm({
15369
15369
  // NOT ruled here — the type pre-exists with broader extractor senses, and a
15370
15370
  // new rule on an old type would reject legitimate live flows (reject-never-flip
15371
15371
  // cuts both ways: only rule types you introduce or senses that are documented).
15372
- CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15372
+ // `Component` joined the target set with OM-agent-anchors: an agent-named
15373
+ // component ("React Router") is the same knowledge→named-unit anchor shape as
15374
+ // a Tool — the knowledge is ABOUT it, not dependent on it.
15375
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
15373
15376
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15374
15377
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15375
15378
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -16674,6 +16677,13 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16674
16677
  findByLabel: this.db.prepare(
16675
16678
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
16676
16679
  ),
16680
+ // Scalar count twin of findByLabel — same live-row semantics, no row
16681
+ // hydration. Exists because status surfaces (daemon `/` + `/health`) used
16682
+ // findNodesByLabel(...).length, materializing every row's attrs JSON and
16683
+ // embedding blob per request — seconds of synchronous loop-hold per poll.
16684
+ countByLabel: this.db.prepare(
16685
+ "SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
16686
+ ),
16677
16687
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
16678
16688
  // clean-reindex purge so a true wipe removes history too, not just live rows.
16679
16689
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -16927,6 +16937,13 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16927
16937
  const rows = this.stmts.findByLabel.all(label);
16928
16938
  return rows.map(rowToNode);
16929
16939
  }
16940
+ /** Live-row count for a label — `findNodesByLabel(label).length` without the
16941
+ * per-row hydration (attrs JSON + embedding blob). Status surfaces poll this
16942
+ * per project per request; the materializing form held the daemon's event
16943
+ * loop for seconds at scale. */
16944
+ countNodesByLabel(label) {
16945
+ return Number(this.stmts.countByLabel.get(label).n);
16946
+ }
16930
16947
  findAllVersionsByLabel(label) {
16931
16948
  const rows = this.stmts.findByLabelAll.all(label);
16932
16949
  return rows.map(rowToNode);
@@ -18328,6 +18345,55 @@ function mintDomainNode(store, name2, ts) {
18328
18345
  }
18329
18346
  return id;
18330
18347
  }
18348
+ function mintCitedPackageNode(store, ref, ts) {
18349
+ const cleaned = ref.replace(/\s+/g, " ").trim();
18350
+ const slash = /^([a-z0-9-]+)\/(.+)$/i.exec(cleaned);
18351
+ const hasEco = slash != null && !cleaned.startsWith("@");
18352
+ const name2 = (hasEco ? slash[2] : cleaned).trim();
18353
+ const nameLc = name2.toLowerCase();
18354
+ const existing = store.findNodesByLabel("Package").find((n) => String(n.attrs["name"] ?? "").toLowerCase() === nameLc);
18355
+ if (existing) return existing.id;
18356
+ let eco = hasEco ? slash[1].toLowerCase() : "";
18357
+ if (!eco && cleaned.startsWith("@")) eco = "npm";
18358
+ if (!eco) {
18359
+ const counts = /* @__PURE__ */ new Map();
18360
+ for (const p of store.findNodesByLabel("Package")) {
18361
+ const e = String(p.attrs["ecosystem"] ?? "").toLowerCase();
18362
+ if (e) counts.set(e, (counts.get(e) ?? 0) + 1);
18363
+ }
18364
+ eco = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? "npm";
18365
+ }
18366
+ const purlName = eco === "npm" ? nameLc : name2;
18367
+ const purl = `pkg:${eco}/${purlName}`;
18368
+ if (!store.getNode(purl)) {
18369
+ store.mergeNode(
18370
+ buildNode(purl, "Package", name2, ts, {
18371
+ purl,
18372
+ name: name2,
18373
+ ecosystem: eco,
18374
+ resolved: false,
18375
+ // agent-cited, no lockfile resolution
18376
+ source: "convo",
18377
+ provisional: true
18378
+ })
18379
+ );
18380
+ }
18381
+ return purl;
18382
+ }
18383
+ function mintComponentNode(store, name2, ts) {
18384
+ const display = name2.replace(/\s+/g, " ").trim();
18385
+ const slug2 = display.toLowerCase();
18386
+ if (!store.getNode(slug2)) {
18387
+ store.mergeNode(
18388
+ buildNode(slug2, "Component", display, ts, {
18389
+ name: display,
18390
+ source: "convo",
18391
+ provisional: true
18392
+ })
18393
+ );
18394
+ }
18395
+ return slug2;
18396
+ }
18331
18397
  function resolveFileNode(store, path2, workspaceId2) {
18332
18398
  const want = path2.trim().replace(/^\.?\//, "");
18333
18399
  for (const n of store.findNodesByLabel("File")) {
@@ -20778,6 +20844,8 @@ __export(src_exports2, {
20778
20844
  matchSymbolsInText: () => matchSymbolsInText,
20779
20845
  mergeCloudCounts: () => mergeCloudCounts,
20780
20846
  mergeDuplicateProblems: () => mergeDuplicateProblems,
20847
+ mintCitedPackageNode: () => mintCitedPackageNode,
20848
+ mintComponentNode: () => mintComponentNode,
20781
20849
  mintDomainNode: () => mintDomainNode,
20782
20850
  mintPatternNode: () => mintPatternNode,
20783
20851
  openGraphStore: () => openGraphStore,
@@ -25142,6 +25210,11 @@ function linkBullet(ref) {
25142
25210
  ` \xB7 ${TAG_EXAMPLE.domain()} \u2014 name the ABSTRACT AREA it's about (e.g. (domain: Observability),`,
25143
25211
  " (domain: Community Detection)). An abstract problem with no code anchor NEEDS this or it's",
25144
25212
  " an invisible island: the Domain is the topic other problems in the area cluster on. Title Case.",
25213
+ ` \xB7 ${TAG_EXAMPLE.package()} \u2014 the PUBLIC PACKAGE the problem is about, even when this`,
25214
+ " workspace doesn't depend on it (e.g. (package: chokidar), (package: pypi/requests)) \u2014 a",
25215
+ " public-registry anchor lets the knowledge cross to the collective; internal names stay private.",
25216
+ ` \xB7 ${TAG_EXAMPLE.component()} \u2014 the framework/product-level UNIT it concerns when it's not a`,
25217
+ " package or a language (e.g. (component: React Router), (component: V8 Isolate)).",
25145
25218
  ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
25146
25219
  " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it.",
25147
25220
  ` \xB7 when your fix relates to a PRIOR solution you were primed with (a problem's`,
@@ -25213,6 +25286,12 @@ var init_agent_signals = __esm({
25213
25286
  // DOMAIN — the abstract area a problem is about; the concept layer an
25214
25287
  // anchor-less problem clusters on. Mints/resolves a Domain by name.
25215
25288
  domain: () => `(domain: The Area)`,
25289
+ // PACKAGE/COMPONENT — agent-named public anchors (OM-agent-anchors): the
25290
+ // package a problem is ABOUT (even when not a dependency) and the
25291
+ // framework/product-level unit it concerns. Public spine anchors → the
25292
+ // knowledge can cross to the collective.
25293
+ package: () => `(package: the-package-name)`,
25294
+ component: () => `(component: The Component)`,
25216
25295
  aids: (ref) => `(aids:[${ref}])`
25217
25296
  };
25218
25297
  GLOSS = {
@@ -49611,6 +49690,8 @@ var ALTERNATIVE_PREFIX = /^\s*alternative:(?:#([a-z][\w-]{0,63}))?\s*/i;
49611
49690
  var INSTANCE_PREFIX = /^\s*instance:\s*/i;
49612
49691
  var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
49613
49692
  var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
49693
+ var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
49694
+ var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
49614
49695
  var CAUSE_TEXT_MIN = 8;
49615
49696
  var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*([^\]\n]{8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
49616
49697
  var CONSTRAINT_MIN = 8;
@@ -49806,6 +49887,18 @@ function parseInlineTags(text) {
49806
49887
  const raw3 = dm[1].replace(/\s+/g, " ").trim();
49807
49888
  if (raw3.length >= 3) out2.push({ kind: "domain", domainText: raw3, sentence: sfield });
49808
49889
  }
49890
+ PACKAGE_RE.lastIndex = 0;
49891
+ let pk;
49892
+ while ((pk = PACKAGE_RE.exec(sentence)) !== null) {
49893
+ const raw3 = pk[1].replace(/\s+/g, " ").trim();
49894
+ if (raw3.length >= 2) out2.push({ kind: "package", packageText: raw3, sentence: sfield });
49895
+ }
49896
+ COMPONENT_RE.lastIndex = 0;
49897
+ let cm;
49898
+ while ((cm = COMPONENT_RE.exec(sentence)) !== null) {
49899
+ const raw3 = cm[1].replace(/\s+/g, " ").trim();
49900
+ if (raw3.length >= 2) out2.push({ kind: "component", componentText: raw3, sentence: sfield });
49901
+ }
49809
49902
  CONSTRAINT_RE.lastIndex = 0;
49810
49903
  let c;
49811
49904
  while ((c = CONSTRAINT_RE.exec(sentence)) !== null) {
@@ -49840,7 +49933,12 @@ var LABEL_PAIR = {
49840
49933
  // EDGE_RULES-clean by construction: CONCERNS allows exactly these sources.
49841
49934
  "Problem>Tool": "CONCERNS",
49842
49935
  "Solution>Tool": "CONCERNS",
49843
- "RootCause>Tool": "CONCERNS"
49936
+ "RootCause>Tool": "CONCERNS",
49937
+ // Agent-named component anchors (OM-agent-anchors): a Component is a
49938
+ // framework/product-level unit the knowledge is ABOUT — CONCERNS, like Tool.
49939
+ "Problem>Component": "CONCERNS",
49940
+ "Solution>Component": "CONCERNS",
49941
+ "RootCause>Component": "CONCERNS"
49844
49942
  };
49845
49943
  var STACK_GROUNDING_EDGES = ["WRITTEN_IN", "DEPENDS_ON", "OCCURS_IN"];
49846
49944
  var TIEBREAK = [
@@ -49939,7 +50037,7 @@ function harvestInlineTags(store, text, opts) {
49939
50037
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
49940
50038
  const mintPriors = opts.mintPriors ?? true;
49941
50039
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
49942
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], refutes: [], corroborations: [] };
50040
+ const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
49943
50041
  const tags = parseInlineTags(text);
49944
50042
  const symptomSeqs = tags.filter((t) => (t.kind === "problem" || t.kind === "todo" || t.kind === "constraint") && t.statement).map((t) => ({ seq: t.seq ?? -1, statement: t.statement, threadId: t.threadId })).sort((a, b) => a.seq - b.seq);
49945
50043
  const bindSymptom = (seq, threadId) => {
@@ -50079,6 +50177,16 @@ function harvestInlineTags(store, text, opts) {
50079
50177
  ...tag.threadId ? { threadId: tag.threadId } : {},
50080
50178
  evidence: b.evidence
50081
50179
  });
50180
+ } else if (tag.kind === "package" || tag.kind === "component") {
50181
+ const b = bindSymptom(tag.seq, tag.threadId);
50182
+ const bound = {
50183
+ ...b.statement ? { boundStatement: b.statement } : {},
50184
+ ...b.problemId ? { problemId: b.problemId } : {},
50185
+ ...tag.threadId ? { threadId: tag.threadId } : {},
50186
+ evidence: b.evidence
50187
+ };
50188
+ if (tag.kind === "package") plan.packages.push({ packageText: tag.packageText, ...bound });
50189
+ else plan.components.push({ componentText: tag.componentText, ...bound });
50082
50190
  } else if (tag.kind === "attempt" || tag.kind === "failure") {
50083
50191
  for (const h of tag.refuteHandles ?? []) {
50084
50192
  const nodeId = resolveHandle(store, h, opts.handleMap);
@@ -51681,7 +51789,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
51681
51789
  }
51682
51790
 
51683
51791
  // src/engine.ts
51684
- var DAEMON_VERSION = true ? "2.0.2-dev.79" : "2.0.0-alpha.0";
51792
+ var DAEMON_VERSION = true ? "2.0.2-dev.85" : "2.0.0-alpha.0";
51685
51793
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
51686
51794
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
51687
51795
  var GIT_OP_MUTE_MS = 4e3;
@@ -52515,6 +52623,20 @@ function createWorkspaceEngine(opts) {
52515
52623
  if (domainId === pid) continue;
52516
52624
  mintCiteEdge(pid, domainId, "PERTAIN_TO", d.evidence === "witnessed" ? 0.4 : 0.3, { domainCite: true, evidence: d.evidence });
52517
52625
  }
52626
+ for (const p of plan.packages) {
52627
+ const pid = bindPid(p);
52628
+ if (!pid) continue;
52629
+ const pkgId = mintCitedPackageNode(store, p.packageText, t);
52630
+ if (pkgId === pid) continue;
52631
+ mintCiteEdge(pid, pkgId, "DEPENDS_ON", p.evidence === "witnessed" ? 0.4 : 0.3, { packageCite: true, evidence: p.evidence });
52632
+ }
52633
+ for (const cpt of plan.components) {
52634
+ const pid = bindPid(cpt);
52635
+ if (!pid) continue;
52636
+ const componentId = mintComponentNode(store, cpt.componentText, t);
52637
+ if (componentId === pid) continue;
52638
+ mintCiteEdge(pid, componentId, "CONCERNS", cpt.evidence === "witnessed" ? 0.4 : 0.3, { componentCite: true, evidence: cpt.evidence });
52639
+ }
52518
52640
  for (const inst of plan.instances) {
52519
52641
  const pid = bindPid(inst);
52520
52642
  if (!pid) continue;
@@ -53377,8 +53499,8 @@ function noveltyAgainst(node2, neighbors, opts = {}) {
53377
53499
  // src/instance-ingest.ts
53378
53500
  var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
53379
53501
  var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
53380
- var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO"];
53381
- var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain"];
53502
+ var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
53503
+ var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
53382
53504
  function stripCodebaseScope(scope) {
53383
53505
  const s = { ...scope ?? {} };
53384
53506
  delete s["codebase"];
@@ -53413,7 +53535,11 @@ function shareableContext(n, wireId) {
53413
53535
  embedding: [],
53414
53536
  attrs,
53415
53537
  ...n.label === "Package" ? { anchorVisibility: "public" } : {},
53416
- ...n.label === "Language" && langAnchor(n) ? { anchorVisibility: "public", anchor: langAnchor(n) } : {}
53538
+ ...n.label === "Language" && langAnchor(n) ? { anchorVisibility: "public", anchor: langAnchor(n) } : {},
53539
+ // A Component stub claims itself by slug (OM-agent-anchors) — the door
53540
+ // confirms only against an EXISTING public Component of that slug, so an
53541
+ // org-internal component name never crosses (fails closed to org).
53542
+ ...n.label === "Component" ? { anchorVisibility: "public", anchor: `component:${wireId}` } : {}
53417
53543
  };
53418
53544
  }
53419
53545
  function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [], opts = {}) {
@@ -53474,15 +53600,18 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53474
53600
  if (e.type === "OCCURS_IN" && t.label !== "Language") continue;
53475
53601
  if (e.type === "DEPENDS_ON" && t.label !== "Package") continue;
53476
53602
  if (e.type === "PERTAIN_TO" && t.label !== "Domain") continue;
53477
- if (t.label === "Package" && opts.includePackages !== true) continue;
53603
+ if (e.type === "CONCERNS" && t.label !== "Component") continue;
53604
+ if ((t.label === "Package" || t.label === "Component") && opts.includePackages !== true) continue;
53478
53605
  if (ignored(t.description) || ignored(String(t.attrs["name"] ?? ""))) continue;
53479
53606
  targets.push({ e, t, wireId: wireContextId(t) });
53480
53607
  }
53481
53608
  if (targets.length === 0) continue;
53482
53609
  const shipped = shippedById.get(sourceId);
53483
53610
  if (shipped) {
53484
- const anchors = (label) => targets.filter((x) => x.t.label === label).map((x) => label === "Language" ? langAnchor(x.t) : x.wireId).filter((a) => a != null).sort();
53485
- const anchor = anchors("Package")[0] ?? anchors("Language")[0];
53611
+ const anchors = (label) => targets.filter((x) => x.t.label === label).map(
53612
+ (x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
53613
+ ).filter((a) => a != null).sort();
53614
+ const anchor = anchors("Package")[0] ?? anchors("Component")[0] ?? anchors("Language")[0];
53486
53615
  if (anchor) {
53487
53616
  shipped.anchorVisibility = "public";
53488
53617
  shipped.anchor = anchor;
@@ -54213,8 +54342,10 @@ async function startMultiDaemon(opts = {}) {
54213
54342
  path: r.root,
54214
54343
  stack: r.entry.stack,
54215
54344
  nodes: r.engine.store.nodeCount(),
54216
- problems: r.engine.store.findNodesByLabel("Problem").length,
54217
- solutions: r.engine.store.findNodesByLabel("Solution").length,
54345
+ // COUNT(*) — the materializing findNodesByLabel(...).length form held
54346
+ // the event loop for seconds per poll once stores grew (HZ-index-hydrate).
54347
+ problems: r.engine.store.countNodesByLabel("Problem"),
54348
+ solutions: r.engine.store.countNodesByLabel("Solution"),
54218
54349
  endpoints: `/ws/${r.id}/`
54219
54350
  })),
54220
54351
  humanView: "run `errata report` \u2014 the dashboard was retired (GRAFT 4e)"
@@ -54270,8 +54401,9 @@ async function startMultiDaemon(opts = {}) {
54270
54401
  name: r.entry.name,
54271
54402
  path: r.root,
54272
54403
  nodes: r.engine.store.nodeCount(),
54273
- problems: r.engine.store.findNodesByLabel("Problem").length,
54274
- solutions: r.engine.store.findNodesByLabel("Solution").length,
54404
+ // COUNT(*) — same hydration hazard as `/` (HZ-index-hydrate).
54405
+ problems: r.engine.store.countNodesByLabel("Problem"),
54406
+ solutions: r.engine.store.countNodesByLabel("Solution"),
54275
54407
  stranded: strandedCount(r)
54276
54408
  }))
54277
54409
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.79",
3
+ "version": "2.0.2-dev.85",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -15049,7 +15049,10 @@ var init_edge_rules = __esm({
15049
15049
  // NOT ruled here — the type pre-exists with broader extractor senses, and a
15050
15050
  // new rule on an old type would reject legitimate live flows (reject-never-flip
15051
15051
  // cuts both ways: only rule types you introduce or senses that are documented).
15052
- CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool"] },
15052
+ // `Component` joined the target set with OM-agent-anchors: an agent-named
15053
+ // component ("React Router") is the same knowledge→named-unit anchor shape as
15054
+ // a Tool — the knowledge is ABOUT it, not dependent on it.
15055
+ CONCERNS: { from: ["Problem", "Solution", "RootCause", "Claim", "Pattern"], to: ["Tool", "Component"] },
15053
15056
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15054
15057
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15055
15058
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -24335,6 +24338,13 @@ var SqliteGraphStore = class {
24335
24338
  findByLabel: this.db.prepare(
24336
24339
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
24337
24340
  ),
24341
+ // Scalar count twin of findByLabel — same live-row semantics, no row
24342
+ // hydration. Exists because status surfaces (daemon `/` + `/health`) used
24343
+ // findNodesByLabel(...).length, materializing every row's attrs JSON and
24344
+ // embedding blob per request — seconds of synchronous loop-hold per poll.
24345
+ countByLabel: this.db.prepare(
24346
+ "SELECT COUNT(*) AS n FROM nodes WHERE label = ? AND valid_to IS NULL"
24347
+ ),
24338
24348
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
24339
24349
  // clean-reindex purge so a true wipe removes history too, not just live rows.
24340
24350
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -24588,6 +24598,13 @@ var SqliteGraphStore = class {
24588
24598
  const rows = this.stmts.findByLabel.all(label);
24589
24599
  return rows.map(rowToNode);
24590
24600
  }
24601
+ /** Live-row count for a label — `findNodesByLabel(label).length` without the
24602
+ * per-row hydration (attrs JSON + embedding blob). Status surfaces poll this
24603
+ * per project per request; the materializing form held the daemon's event
24604
+ * loop for seconds at scale. */
24605
+ countNodesByLabel(label) {
24606
+ return Number(this.stmts.countByLabel.get(label).n);
24607
+ }
24591
24608
  findAllVersionsByLabel(label) {
24592
24609
  const rows = this.stmts.findByLabelAll.all(label);
24593
24610
  return rows.map(rowToNode);