@inerrata-corporation/errata 2.0.1-dev.99 → 2.0.2-dev.125

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`) ──
@@ -15307,7 +15310,12 @@ var init_wire = __esm({
15307
15310
  * knows which packages are private. The door does NOT trust a `public` claim
15308
15311
  * blindly (it re-confirms on the spine); absent ⇒ unknown ⇒ fail-closed to
15309
15312
  * org-private. Accepted-but-ignored until ORG_MEMBRANE_ENABLED flips. */
15310
- anchorVisibility: external_exports.enum(["public", "private"]).optional()
15313
+ anchorVisibility: external_exports.enum(["public", "private"]).optional(),
15314
+ /** The spine-resolvable anchor id backing a `public` tag (OM-anchor-tag): a
15315
+ * Package purl or `languageCanonicalId`. The door validates THIS on the
15316
+ * public spine (falling back to `canonicalId` when absent — context stubs
15317
+ * are self-anchored). Never trusted without spine confirmation. */
15318
+ anchor: external_exports.string().min(1).max(300).optional()
15311
15319
  });
15312
15320
  RouteContextCountWireSchema = external_exports.object({
15313
15321
  confirmed: external_exports.number().int().min(0),
@@ -16023,6 +16031,13 @@ var SqliteGraphStore = class {
16023
16031
  findByLabel: this.db.prepare(
16024
16032
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
16025
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
+ ),
16026
16041
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
16027
16042
  // clean-reindex purge so a true wipe removes history too, not just live rows.
16028
16043
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -16276,6 +16291,13 @@ var SqliteGraphStore = class {
16276
16291
  const rows = this.stmts.findByLabel.all(label);
16277
16292
  return rows.map(rowToNode);
16278
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
+ }
16279
16301
  findAllVersionsByLabel(label) {
16280
16302
  const rows = this.stmts.findByLabelAll.all(label);
16281
16303
  return rows.map(rowToNode);
package/errata.mjs CHANGED
@@ -938,6 +938,12 @@ function packageCanonicalId(p) {
938
938
  const name2 = eco === "npm" ? p.name.toLowerCase() : p.name;
939
939
  return `pkg:${eco}/${name2}${p.version ? `@${p.version}` : ""}`;
940
940
  }
941
+ function versionlessPurl(purl) {
942
+ if (!purl.startsWith("pkg:")) return null;
943
+ const lastSlash = purl.lastIndexOf("/");
944
+ const lastAt = purl.lastIndexOf("@");
945
+ return lastAt > lastSlash ? purl.slice(0, lastAt) : purl;
946
+ }
941
947
  function parsePackageRef(ref) {
942
948
  let rest2 = ref.trim();
943
949
  if (rest2.startsWith("pkg:")) {
@@ -15363,7 +15369,10 @@ var init_edge_rules = __esm({
15363
15369
  // NOT ruled here — the type pre-exists with broader extractor senses, and a
15364
15370
  // new rule on an old type would reject legitimate live flows (reject-never-flip
15365
15371
  // cuts both ways: only rule types you introduce or senses that are documented).
15366
- 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"] },
15367
15376
  OPERATES_ON: { from: ["Algorithm"], to: ["DataStructure"] },
15368
15377
  INVOLVES: { from: ["Problem", "Solution"], to: ["DataStructure"] },
15369
15378
  // ── Taxonomy (Track 4 Stream C: MITRE CWE `ChildOf` → `Weakness —IS_A→ Weakness`) ──
@@ -15602,7 +15611,12 @@ var init_wire = __esm({
15602
15611
  * knows which packages are private. The door does NOT trust a `public` claim
15603
15612
  * blindly (it re-confirms on the spine); absent ⇒ unknown ⇒ fail-closed to
15604
15613
  * org-private. Accepted-but-ignored until ORG_MEMBRANE_ENABLED flips. */
15605
- anchorVisibility: external_exports.enum(["public", "private"]).optional()
15614
+ anchorVisibility: external_exports.enum(["public", "private"]).optional(),
15615
+ /** The spine-resolvable anchor id backing a `public` tag (OM-anchor-tag): a
15616
+ * Package purl or `languageCanonicalId`. The door validates THIS on the
15617
+ * public spine (falling back to `canonicalId` when absent — context stubs
15618
+ * are self-anchored). Never trusted without spine confirmation. */
15619
+ anchor: external_exports.string().min(1).max(300).optional()
15606
15620
  });
15607
15621
  RouteContextCountWireSchema = external_exports.object({
15608
15622
  confirmed: external_exports.number().int().min(0),
@@ -15993,6 +16007,7 @@ __export(src_exports, {
15993
16007
  toCloudAttrs: () => toCloudAttrs,
15994
16008
  toolCanonicalId: () => toolCanonicalId,
15995
16009
  validateCastaliaPayload: () => validateCastaliaPayload,
16010
+ versionlessPurl: () => versionlessPurl,
15996
16011
  vetSidecarSummaries: () => vetSidecarSummaries
15997
16012
  });
15998
16013
  var init_src = __esm({
@@ -16662,6 +16677,13 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16662
16677
  findByLabel: this.db.prepare(
16663
16678
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
16664
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
+ ),
16665
16687
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
16666
16688
  // clean-reindex purge so a true wipe removes history too, not just live rows.
16667
16689
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -16915,6 +16937,13 @@ CREATE INDEX IF NOT EXISTS nodes_relpath ON nodes(json_extract(attrs_json, '$.re
16915
16937
  const rows = this.stmts.findByLabel.all(label);
16916
16938
  return rows.map(rowToNode);
16917
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
+ }
16918
16947
  findAllVersionsByLabel(label) {
16919
16948
  const rows = this.stmts.findByLabelAll.all(label);
16920
16949
  return rows.map(rowToNode);
@@ -18316,6 +18345,55 @@ function mintDomainNode(store, name2, ts) {
18316
18345
  }
18317
18346
  return id;
18318
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
+ }
18319
18397
  function resolveFileNode(store, path2, workspaceId2) {
18320
18398
  const want = path2.trim().replace(/^\.?\//, "");
18321
18399
  for (const n of store.findNodesByLabel("File")) {
@@ -20766,6 +20844,8 @@ __export(src_exports2, {
20766
20844
  matchSymbolsInText: () => matchSymbolsInText,
20767
20845
  mergeCloudCounts: () => mergeCloudCounts,
20768
20846
  mergeDuplicateProblems: () => mergeDuplicateProblems,
20847
+ mintCitedPackageNode: () => mintCitedPackageNode,
20848
+ mintComponentNode: () => mintComponentNode,
20769
20849
  mintDomainNode: () => mintDomainNode,
20770
20850
  mintPatternNode: () => mintPatternNode,
20771
20851
  openGraphStore: () => openGraphStore,
@@ -21530,7 +21610,12 @@ function toWirePayload(batch, runId) {
21530
21610
  // attrs travel; node-level bookkeeping (embedding, momentum counters,
21531
21611
  // bi-temporal fields) is local-stratum and never crosses.
21532
21612
  attrs: n.attrs ?? {},
21533
- extractionSource: n.extractionSource
21613
+ extractionSource: n.extractionSource,
21614
+ // Org-membrane anchor tag (OM-anchor-tag): the batch builders set these
21615
+ // on wire-bound projections only; the door re-validates the claim on the
21616
+ // public spine, so lifting them is routing input, not a grant.
21617
+ ...n.anchorVisibility ? { anchorVisibility: n.anchorVisibility } : {},
21618
+ ...n.anchor ? { anchor: n.anchor } : {}
21534
21619
  }));
21535
21620
  const edges = batch.edges.filter((e) => !droppedNodeIds.has(e.from) && !droppedNodeIds.has(e.to)).map((e) => {
21536
21621
  const perContext = wirePerContext(e.attrs?.["perContext"]);
@@ -21851,8 +21936,14 @@ var init_client = __esm({
21851
21936
  * the per-decision response into flush accounting.
21852
21937
  *
21853
21938
  * The v1 door caps a payload at `MAX_NODES_PER_PAYLOAD` / `MAX_EDGES_PER_PAYLOAD`
21854
- * (413 over). A batch above the cap is split and drained across calls under ONE
21855
- * runId: every NODE chunk first (edges empty), then EDGE chunks (nodes empty).
21939
+ * (413 over). A batch above the cap is split and drained across calls, every
21940
+ * NODE chunk first (edges empty), then EDGE chunks (nodes empty). Each chunk
21941
+ * gets its OWN runId: the door's durable idempotency claim is keyed on
21942
+ * (agent, org, runId) with the payload digest, so reusing one runId across
21943
+ * different chunk payloads 409s "runId was already used with a different
21944
+ * payload" on the second chunk — exactly how the first real >25-node drain
21945
+ * died (2026-07-22). The runId never grouped anything server-side; it exists
21946
+ * for duplicate-POST protection, which is per-request by nature.
21856
21947
  * Recognition resolves an edge's endpoints against nodes already ingested this
21857
21948
  * drain (endpoint-label validation is deferred to the service when an endpoint
21858
21949
  * isn't in-payload), so the split never orphans an edge. Sub-results concat into
@@ -21865,6 +21956,7 @@ var init_client = __esm({
21865
21956
  }
21866
21957
  const merged = { runId, nodes: [], edges: [] };
21867
21958
  let pendingEdges = [...batch.edges];
21959
+ const echoedCloudId = /* @__PURE__ */ new Map();
21868
21960
  for (const nodeChunk of chunkArray(batch.nodes, INGEST_NODE_CHUNK)) {
21869
21961
  const ids = new Set(nodeChunk.map((n) => n.id));
21870
21962
  const inChunk = pendingEdges.filter((e) => ids.has(e.from) && ids.has(e.to)).slice(0, MAX_EDGES_PER_PAYLOAD);
@@ -21872,15 +21964,23 @@ var init_client = __esm({
21872
21964
  const shipped = new Set(inChunk);
21873
21965
  pendingEdges = pendingEdges.filter((e) => !shipped.has(e));
21874
21966
  }
21875
- const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk }, runId));
21967
+ const r = await this.ingestWire(toWirePayload({ ...batch, nodes: nodeChunk, edges: inChunk }, randomUUID()));
21876
21968
  merged.nodes.push(...r.nodes);
21877
21969
  merged.edges.push(...r.edges);
21970
+ for (const rn of r.nodes) {
21971
+ if (rn.nodeId && rn.nodeId !== rn.canonicalId) echoedCloudId.set(rn.canonicalId, rn.nodeId);
21972
+ }
21878
21973
  if (r.patternReconciliation) {
21879
21974
  merged.patternReconciliation = { ...merged.patternReconciliation, ...r.patternReconciliation };
21880
21975
  }
21881
21976
  }
21882
21977
  for (const edgeChunk of chunkArray(pendingEdges, MAX_EDGES_PER_PAYLOAD)) {
21883
- const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: edgeChunk }, runId));
21978
+ const rewritten = edgeChunk.map((e) => ({
21979
+ ...e,
21980
+ from: echoedCloudId.get(e.from) ?? e.from,
21981
+ to: echoedCloudId.get(e.to) ?? e.to
21982
+ }));
21983
+ const r = await this.ingestWire(toWirePayload({ ...batch, nodes: [], edges: rewritten }, randomUUID()));
21884
21984
  merged.edges.push(...r.edges);
21885
21985
  }
21886
21986
  return { ...summarizeIngestResult(merged), result: merged };
@@ -25110,6 +25210,11 @@ function linkBullet(ref) {
25110
25210
  ` \xB7 ${TAG_EXAMPLE.domain()} \u2014 name the ABSTRACT AREA it's about (e.g. (domain: Observability),`,
25111
25211
  " (domain: Community Detection)). An abstract problem with no code anchor NEEDS this or it's",
25112
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)).",
25113
25218
  ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
25114
25219
  " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it.",
25115
25220
  ` \xB7 when your fix relates to a PRIOR solution you were primed with (a problem's`,
@@ -25181,6 +25286,12 @@ var init_agent_signals = __esm({
25181
25286
  // DOMAIN — the abstract area a problem is about; the concept layer an
25182
25287
  // anchor-less problem clusters on. Mints/resolves a Domain by name.
25183
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)`,
25184
25295
  aids: (ref) => `(aids:[${ref}])`
25185
25296
  };
25186
25297
  GLOSS = {
@@ -37324,7 +37435,7 @@ var init_mcp = __esm({
37324
37435
  },
37325
37436
  {
37326
37437
  name: "errata.why",
37327
- description: "Causal/resolution neighborhood along the causal edge families (CAUSED_BY, MANIFESTS_AS, FIXED_BY, SOLVED_BY, \u2026), walked in both directions. For a Problem this surfaces its root causes AND solutions; for a Solution, the problems it resolves. Ranked by accumulated conductance. Pass nodeId or qname. Returns {seedId, nodes:[{id,label,description,hops,edgeType,relevance}]}.",
37438
+ description: "Causal/resolution neighborhood along the causal edge families (CAUSED_BY, MANIFESTS_AS, FIXED_BY, SOLVED_BY, \u2026), walked in both directions. For a Problem this surfaces its root causes AND solutions; for a Solution, the problems it resolves. Merges the machine-wide shared store's triage layer (cause chains from inline `<-` diagnoses land there, keyed by the same content-derived problem id). Ranked by accumulated conductance. Pass nodeId or qname. Returns {seedId, nodes:[{id,label,description,hops,edgeType,relevance}]}.",
37328
37439
  inputSchema: {
37329
37440
  type: "object",
37330
37441
  properties: {
@@ -37339,7 +37450,26 @@ var init_mcp = __esm({
37339
37450
  if (!id) return unresolved(args2);
37340
37451
  const maxHops = Math.max(1, Math.min(8, Number(args2["maxHops"] ?? 4)));
37341
37452
  const limit = Math.max(1, Math.min(100, Number(args2["limit"] ?? 30)));
37342
- return { found: true, ...causalChain(store, { seedId: id, direction: "both", maxHops, limit }) };
37453
+ const local = causalChain(store, { seedId: id, direction: "both", maxHops, limit });
37454
+ try {
37455
+ const path2 = sharedStorePath();
37456
+ if (!existsSync10(path2)) return { found: true, ...local };
37457
+ const shared = openGraphStore({ path: path2 });
37458
+ try {
37459
+ if (!shared.getNode(id)) return { found: true, ...local };
37460
+ const l2 = causalChain(shared, { seedId: id, direction: "both", maxHops, limit });
37461
+ const seen = new Set(local.nodes.map((n) => n.id));
37462
+ const merged = [
37463
+ ...local.nodes,
37464
+ ...l2.nodes.filter((n) => !seen.has(n.id)).map((n) => ({ ...n, store: "shared" }))
37465
+ ].sort((a, b) => (b.relevance ?? 0) - (a.relevance ?? 0)).slice(0, limit);
37466
+ return { found: true, seedId: local.seedId, nodes: merged };
37467
+ } finally {
37468
+ shared.close();
37469
+ }
37470
+ } catch {
37471
+ return { found: true, ...local };
37472
+ }
37343
37473
  }
37344
37474
  },
37345
37475
  {
@@ -49579,6 +49709,8 @@ var ALTERNATIVE_PREFIX = /^\s*alternative:(?:#([a-z][\w-]{0,63}))?\s*/i;
49579
49709
  var INSTANCE_PREFIX = /^\s*instance:\s*/i;
49580
49710
  var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
49581
49711
  var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
49712
+ var PACKAGE_RE = /\(\s*package:\s*([^()\n]{2,}?)\s*\)/gi;
49713
+ var COMPONENT_RE = /\(\s*component:\s*([^()\n]{2,}?)\s*\)/gi;
49582
49714
  var CAUSE_TEXT_MIN = 8;
49583
49715
  var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*([^\]\n]{8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
49584
49716
  var CONSTRAINT_MIN = 8;
@@ -49774,6 +49906,18 @@ function parseInlineTags(text) {
49774
49906
  const raw3 = dm[1].replace(/\s+/g, " ").trim();
49775
49907
  if (raw3.length >= 3) out2.push({ kind: "domain", domainText: raw3, sentence: sfield });
49776
49908
  }
49909
+ PACKAGE_RE.lastIndex = 0;
49910
+ let pk;
49911
+ while ((pk = PACKAGE_RE.exec(sentence)) !== null) {
49912
+ const raw3 = pk[1].replace(/\s+/g, " ").trim();
49913
+ if (raw3.length >= 2) out2.push({ kind: "package", packageText: raw3, sentence: sfield });
49914
+ }
49915
+ COMPONENT_RE.lastIndex = 0;
49916
+ let cm;
49917
+ while ((cm = COMPONENT_RE.exec(sentence)) !== null) {
49918
+ const raw3 = cm[1].replace(/\s+/g, " ").trim();
49919
+ if (raw3.length >= 2) out2.push({ kind: "component", componentText: raw3, sentence: sfield });
49920
+ }
49777
49921
  CONSTRAINT_RE.lastIndex = 0;
49778
49922
  let c;
49779
49923
  while ((c = CONSTRAINT_RE.exec(sentence)) !== null) {
@@ -49808,7 +49952,12 @@ var LABEL_PAIR = {
49808
49952
  // EDGE_RULES-clean by construction: CONCERNS allows exactly these sources.
49809
49953
  "Problem>Tool": "CONCERNS",
49810
49954
  "Solution>Tool": "CONCERNS",
49811
- "RootCause>Tool": "CONCERNS"
49955
+ "RootCause>Tool": "CONCERNS",
49956
+ // Agent-named component anchors (OM-agent-anchors): a Component is a
49957
+ // framework/product-level unit the knowledge is ABOUT — CONCERNS, like Tool.
49958
+ "Problem>Component": "CONCERNS",
49959
+ "Solution>Component": "CONCERNS",
49960
+ "RootCause>Component": "CONCERNS"
49812
49961
  };
49813
49962
  var STACK_GROUNDING_EDGES = ["WRITTEN_IN", "DEPENDS_ON", "OCCURS_IN"];
49814
49963
  var TIEBREAK = [
@@ -49907,7 +50056,7 @@ function harvestInlineTags(store, text, opts) {
49907
50056
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
49908
50057
  const mintPriors = opts.mintPriors ?? true;
49909
50058
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
49910
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], refutes: [], corroborations: [] };
50059
+ const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], packages: [], components: [], refutes: [], corroborations: [] };
49911
50060
  const tags = parseInlineTags(text);
49912
50061
  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);
49913
50062
  const bindSymptom = (seq, threadId) => {
@@ -50047,6 +50196,16 @@ function harvestInlineTags(store, text, opts) {
50047
50196
  ...tag.threadId ? { threadId: tag.threadId } : {},
50048
50197
  evidence: b.evidence
50049
50198
  });
50199
+ } else if (tag.kind === "package" || tag.kind === "component") {
50200
+ const b = bindSymptom(tag.seq, tag.threadId);
50201
+ const bound = {
50202
+ ...b.statement ? { boundStatement: b.statement } : {},
50203
+ ...b.problemId ? { problemId: b.problemId } : {},
50204
+ ...tag.threadId ? { threadId: tag.threadId } : {},
50205
+ evidence: b.evidence
50206
+ };
50207
+ if (tag.kind === "package") plan.packages.push({ packageText: tag.packageText, ...bound });
50208
+ else plan.components.push({ componentText: tag.componentText, ...bound });
50050
50209
  } else if (tag.kind === "attempt" || tag.kind === "failure") {
50051
50210
  for (const h of tag.refuteHandles ?? []) {
50052
50211
  const nodeId = resolveHandle(store, h, opts.handleMap);
@@ -50071,6 +50230,17 @@ function harvestInlineTags(store, text, opts) {
50071
50230
  boundStatement: b.statement
50072
50231
  });
50073
50232
  }
50233
+ } else if (tag.kind === "prior") {
50234
+ const targetId = resolveHandle(store, tag.handle, opts.handleMap);
50235
+ const target = targetId ? store.getNode(targetId) : null;
50236
+ const citedLabel = target?.label ?? opts.handleMap[tag.handle]?.label;
50237
+ if (targetId && citedLabel && CORROBORATABLE_LABELS.has(citedLabel)) {
50238
+ const witnessKey = `corrob:${targetId}:lean:${digest({ h: tag.handle })}`.slice(0, 72);
50239
+ if (!plan.corroborations.some((c) => c.witnessKey === witnessKey)) {
50240
+ plan.corroborations.push({ nodeId: targetId, witnessKey });
50241
+ }
50242
+ }
50243
+ if (mintPriors && source && target && mintPriorEdge(store, source, target, tag.sentence, touched, opts.ts)) plan.priorEdges++;
50074
50244
  } else if (mintPriors && source) {
50075
50245
  const targetId = resolveHandle(store, tag.handle, opts.handleMap);
50076
50246
  const target = targetId ? store.getNode(targetId) : null;
@@ -50079,6 +50249,12 @@ function harvestInlineTags(store, text, opts) {
50079
50249
  }
50080
50250
  return plan;
50081
50251
  }
50252
+ var CORROBORATABLE_LABELS = /* @__PURE__ */ new Set(["Problem", "Solution", "RootCause"]);
50253
+ function stampWitnessOrigin(items, origin) {
50254
+ if (!origin) return [...items];
50255
+ const o = digest({ p: origin }).slice(0, 12);
50256
+ return items.map((i2) => ({ ...i2, witnessKey: `${i2.witnessKey}:o:${o}` }));
50257
+ }
50082
50258
 
50083
50259
  // src/rollup.ts
50084
50260
  function readConversation(transcriptPath, includeThinking = true, maxChars = 6e4) {
@@ -50581,7 +50757,9 @@ function generalizeRouteForSync(edge2, level) {
50581
50757
  navFailures: 0
50582
50758
  };
50583
50759
  }
50584
- function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2) {
50760
+ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2, opts = {}) {
50761
+ const lang = opts.primaryLanguage?.trim().toLowerCase();
50762
+ const claim = lang ? { anchorVisibility: "public", anchor: `lang:${lang}` } : {};
50585
50763
  const nodes = [];
50586
50764
  const edges = [];
50587
50765
  const seenIds = /* @__PURE__ */ new Set();
@@ -50594,7 +50772,8 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
50594
50772
  ...n,
50595
50773
  description: generalize(n.description, { level }).text,
50596
50774
  embedding: [],
50597
- attrs: { scope: {} }
50775
+ attrs: { scope: {} },
50776
+ ...claim
50598
50777
  });
50599
50778
  };
50600
50779
  for (const tri of shared.findNodesByLabel("Triage")) {
@@ -50610,7 +50789,8 @@ function buildTriageIngest(shared, daemonVersion, ignorePatterns = [], level = 2
50610
50789
  attrs: {
50611
50790
  ...statement ? { statement: generalize(statement, { level }).text } : {},
50612
50791
  ...tri.attrs["perContextSeen"] ? { perContextSeen: tri.attrs["perContextSeen"] } : {}
50613
- }
50792
+ },
50793
+ ...claim
50614
50794
  });
50615
50795
  }
50616
50796
  for (const tb of shared.inEdges(tri.id, ["TRIAGED_BY"])) {
@@ -51649,7 +51829,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
51649
51829
  }
51650
51830
 
51651
51831
  // src/engine.ts
51652
- var DAEMON_VERSION = true ? "2.0.1-dev.99" : "2.0.0-alpha.0";
51832
+ var DAEMON_VERSION = true ? "2.0.2-dev.125" : "2.0.0-alpha.0";
51653
51833
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
51654
51834
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
51655
51835
  var GIT_OP_MUTE_MS = 4e3;
@@ -52483,6 +52663,20 @@ function createWorkspaceEngine(opts) {
52483
52663
  if (domainId === pid) continue;
52484
52664
  mintCiteEdge(pid, domainId, "PERTAIN_TO", d.evidence === "witnessed" ? 0.4 : 0.3, { domainCite: true, evidence: d.evidence });
52485
52665
  }
52666
+ for (const p of plan.packages) {
52667
+ const pid = bindPid(p);
52668
+ if (!pid) continue;
52669
+ const pkgId = mintCitedPackageNode(store, p.packageText, t);
52670
+ if (pkgId === pid) continue;
52671
+ mintCiteEdge(pid, pkgId, "DEPENDS_ON", p.evidence === "witnessed" ? 0.4 : 0.3, { packageCite: true, evidence: p.evidence });
52672
+ }
52673
+ for (const cpt of plan.components) {
52674
+ const pid = bindPid(cpt);
52675
+ if (!pid) continue;
52676
+ const componentId = mintComponentNode(store, cpt.componentText, t);
52677
+ if (componentId === pid) continue;
52678
+ mintCiteEdge(pid, componentId, "CONCERNS", cpt.evidence === "witnessed" ? 0.4 : 0.3, { componentCite: true, evidence: cpt.evidence });
52679
+ }
52486
52680
  for (const inst of plan.instances) {
52487
52681
  const pid = bindPid(inst);
52488
52682
  if (!pid) continue;
@@ -52524,14 +52718,14 @@ function createWorkspaceEngine(opts) {
52524
52718
  }
52525
52719
  }
52526
52720
  if (plan.refutes.length > 0 && typeof cloud.reportContradictions === "function") {
52527
- void cloud.reportContradictions({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: plan.refutes }).then((r) => {
52721
+ void cloud.reportContradictions({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: stampWitnessOrigin(plan.refutes, profile.id) }).then((r) => {
52528
52722
  if (r?.recorded) console.log(`[errata] refute: ${r.recorded} contradiction(s) recorded`);
52529
52723
  }).catch(
52530
52724
  (err2) => console.warn("[errata] refute transport failed (continuing):", err2 instanceof Error ? err2.message : err2)
52531
52725
  );
52532
52726
  }
52533
52727
  if (plan.corroborations.length > 0 && typeof cloud.reportCorroborations === "function") {
52534
- void cloud.reportCorroborations({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: plan.corroborations }).then((r) => {
52728
+ void cloud.reportCorroborations({ daemonVersion: DAEMON_VERSION, projectId: profile.id, items: stampWitnessOrigin(plan.corroborations, profile.id) }).then((r) => {
52535
52729
  if (r?.recorded) console.log(`[errata] corroborate: ${r.recorded} corroboration(s) recorded`);
52536
52730
  }).catch(
52537
52731
  (err2) => console.warn("[errata] corroboration transport failed (continuing):", err2 instanceof Error ? err2.message : err2)
@@ -53238,13 +53432,21 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53238
53432
  // Package ids are already the purl (= the cross-stratum canonicalId).
53239
53433
  id: label === "Language" ? languageCanonicalId(String(n.attrs["name"] ?? "").trim() || n.description) : n.id,
53240
53434
  embedding: [],
53435
+ // No `version` attr on the wire: the purl already encodes the resolved
53436
+ // version, and the door's temporal guard rejects ANY `attrs.version` as
53437
+ // bi-temporal bookkeeping (ingest-temporal-guard.ts) — shipping it 422s
53438
+ // the whole batch. `resolved` still travels (range-vs-lockfile signal).
53241
53439
  attrs: label === "Package" ? {
53242
53440
  purl: n.attrs["purl"],
53243
53441
  name: n.attrs["name"],
53244
- version: n.attrs["version"],
53245
53442
  ecosystem: n.attrs["ecosystem"],
53246
53443
  resolved: n.attrs["resolved"]
53247
- } : { name: n.attrs["name"] }
53444
+ } : { name: n.attrs["name"] },
53445
+ // OM-anchor-tag: context stubs are self-anchored — the wire id IS the
53446
+ // spine key (purl / languageCanonicalId). The door re-confirms on the
53447
+ // public spine; a private-registry or workspace package never confirms
53448
+ // and fails closed to org, exactly as an untagged one would.
53449
+ anchorVisibility: "public"
53248
53450
  });
53249
53451
  seen.add(n.id);
53250
53452
  }
@@ -53258,6 +53460,10 @@ function buildContextIngest(store, profile, daemonVersion, ignorePatterns = [],
53258
53460
  }
53259
53461
  const base = {
53260
53462
  daemonVersion,
53463
+ // Origin key (= project, the salted `wp_…`) — the door stamps it onto created
53464
+ // nodes as `authoringProject`, arming the evidence channels' cross-origin gate
53465
+ // (EE-corrob-live: without it every node is fail-open to self-corroboration).
53466
+ ...profile.id ? { originProject: profile.id } : {},
53261
53467
  profile: { stack: profile.stack, languages: profile.languages, domains: profile.domains },
53262
53468
  nodes,
53263
53469
  edges
@@ -53337,8 +53543,8 @@ function noveltyAgainst(node2, neighbors, opts = {}) {
53337
53543
  // src/instance-ingest.ts
53338
53544
  var INSTANCE_LABELS = ["Problem", "Solution", "RootCause"];
53339
53545
  var INSTANCE_EDGES = ["CAUSED_BY", "SOLVED_BY"];
53340
- var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO"];
53341
- var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain"];
53546
+ var ANCHOR_EDGES = ["OCCURS_IN", "DEPENDS_ON", "PERTAIN_TO", "CONCERNS"];
53547
+ var ANCHOR_TARGET_LABELS = ["Language", "Package", "Domain", "Component"];
53342
53548
  function stripCodebaseScope(scope) {
53343
53549
  const s = { ...scope ?? {} };
53344
53550
  delete s["codebase"];
@@ -53354,15 +53560,31 @@ function wireContextId(n) {
53354
53560
  }
53355
53561
  return n.id;
53356
53562
  }
53563
+ function langAnchor(t) {
53564
+ const name2 = String(t.attrs["name"] ?? "").trim() || t.description.trim() || t.id.replace(/^lang:/, "").trim();
53565
+ return name2 ? `lang:${name2.toLowerCase()}` : void 0;
53566
+ }
53357
53567
  function shareableContext(n, wireId) {
53358
53568
  const attrs = n.label === "Package" ? {
53569
+ // No `version` attr: the purl encodes it, and the door's temporal
53570
+ // guard 422s any `attrs.version` (mirrors buildContextIngest).
53359
53571
  purl: n.attrs["purl"],
53360
53572
  name: n.attrs["name"],
53361
- version: n.attrs["version"],
53362
53573
  ecosystem: n.attrs["ecosystem"],
53363
53574
  resolved: n.attrs["resolved"]
53364
53575
  } : n.label === "Domain" ? { name: n.description, canonicalId: n.attrs["canonicalId"] } : { name: n.attrs["name"] };
53365
- return { ...n, id: wireId, embedding: [], attrs };
53576
+ return {
53577
+ ...n,
53578
+ id: wireId,
53579
+ embedding: [],
53580
+ attrs,
53581
+ ...n.label === "Package" ? { anchorVisibility: "public" } : {},
53582
+ ...n.label === "Language" && langAnchor(n) ? { anchorVisibility: "public", anchor: langAnchor(n) } : {},
53583
+ // A Component stub claims itself by slug (OM-agent-anchors) — the door
53584
+ // confirms only against an EXISTING public Component of that slug, so an
53585
+ // org-internal component name never crosses (fails closed to org).
53586
+ ...n.label === "Component" ? { anchorVisibility: "public", anchor: `component:${wireId}` } : {}
53587
+ };
53366
53588
  }
53367
53589
  function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [], opts = {}) {
53368
53590
  const level = opts.level ?? 1;
@@ -53380,6 +53602,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53380
53602
  });
53381
53603
  const nodes = [];
53382
53604
  const seen = /* @__PURE__ */ new Set();
53605
+ const shippedById = /* @__PURE__ */ new Map();
53383
53606
  const anchorSources = /* @__PURE__ */ new Set();
53384
53607
  const includedByLabel = /* @__PURE__ */ new Map();
53385
53608
  for (const label of INSTANCE_LABELS) {
@@ -53397,7 +53620,9 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53397
53620
  }
53398
53621
  if (!noveltyAgainst(n, ref, noveltyOpts).ready) continue;
53399
53622
  ref.push(n);
53400
- nodes.push(shareable(n));
53623
+ const wireNode = shareable(n);
53624
+ nodes.push(wireNode);
53625
+ shippedById.set(n.id, wireNode);
53401
53626
  seen.add(n.id);
53402
53627
  anchorSources.add(n.id);
53403
53628
  }
@@ -53419,27 +53644,53 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53419
53644
  if (e.type === "OCCURS_IN" && t.label !== "Language") continue;
53420
53645
  if (e.type === "DEPENDS_ON" && t.label !== "Package") continue;
53421
53646
  if (e.type === "PERTAIN_TO" && t.label !== "Domain") continue;
53422
- if (t.label === "Package" && opts.includePackages !== true) continue;
53647
+ if (e.type === "CONCERNS" && t.label !== "Component") continue;
53648
+ if ((t.label === "Package" || t.label === "Component") && opts.includePackages !== true) continue;
53423
53649
  if (ignored(t.description) || ignored(String(t.attrs["name"] ?? ""))) continue;
53424
53650
  targets.push({ e, t, wireId: wireContextId(t) });
53425
53651
  }
53426
53652
  if (targets.length === 0) continue;
53653
+ const shipped = shippedById.get(sourceId);
53654
+ if (shipped) {
53655
+ const anchors = (label) => targets.filter((x) => x.t.label === label).map(
53656
+ (x) => label === "Language" ? langAnchor(x.t) : label === "Component" ? `component:${x.wireId}` : x.wireId
53657
+ ).filter((a) => a != null).sort();
53658
+ const anchor = anchors("Package")[0] ?? anchors("Component")[0] ?? anchors("Language")[0];
53659
+ if (anchor) {
53660
+ shipped.anchorVisibility = "public";
53661
+ shipped.anchor = anchor;
53662
+ }
53663
+ }
53427
53664
  const dg = digest(targets.map(({ e, wireId }) => `${e.type}>${wireId}`).sort());
53428
- if (store.getNode(sourceId)?.attrs["anchorsContributedDigest"] === dg) continue;
53665
+ const sourceNode = store.getNode(sourceId);
53666
+ if (sourceNode?.attrs["anchorsContributedDigest"] === dg) continue;
53429
53667
  anchorDigests[sourceId] = dg;
53668
+ const skippedCloudId = !seen.has(sourceId) ? sourceNode?.attrs["cloudNodeId"] : void 0;
53669
+ const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
53430
53670
  for (const { e, t, wireId } of targets) {
53431
- anchorEdges.push({ ...e, to: wireId, attrs: {} });
53671
+ anchorEdges.push({ ...e, from: wireFrom, to: wireId, attrs: {} });
53432
53672
  if (t.attrs["source"] === "cloud" || contextSeen.has(wireId)) continue;
53433
53673
  contextSeen.add(wireId);
53434
53674
  contextNodes.push(shareableContext(t, wireId));
53435
53675
  }
53436
53676
  }
53677
+ const primaryLang = (profile.languages ?? []).map((l) => String(l).trim().toLowerCase()).filter(Boolean)[0];
53678
+ if (primaryLang) {
53679
+ for (const shipped of shippedById.values()) {
53680
+ if (!shipped.anchorVisibility) {
53681
+ shipped.anchorVisibility = "public";
53682
+ shipped.anchor = `lang:${primaryLang}`;
53683
+ }
53684
+ }
53685
+ }
53437
53686
  const project = opts.project;
53438
53687
  if (project) {
53439
53688
  const now = Date.now();
53440
53689
  for (const sourceId of anchorSources) {
53441
53690
  const source = store.getNode(sourceId);
53442
53691
  if (!source) continue;
53692
+ const skippedCloudId = !seen.has(sourceId) ? source.attrs["cloudNodeId"] : void 0;
53693
+ const wireFrom = typeof skippedCloudId === "string" && skippedCloudId.length > 0 ? skippedCloudId : sourceId;
53443
53694
  for (const e of store.outEdges(sourceId, ["ANCHORED_AT"])) {
53444
53695
  const target = store.getNode(e.to);
53445
53696
  if (!target) continue;
@@ -53474,7 +53725,7 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53474
53725
  stability: "unstable"
53475
53726
  });
53476
53727
  }
53477
- anchorEdges.push({ ...e, to: symId, attrs: {} });
53728
+ anchorEdges.push({ ...e, from: wireFrom, to: symId, attrs: {} });
53478
53729
  }
53479
53730
  }
53480
53731
  }
@@ -53509,6 +53760,11 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53509
53760
  edges.push(...anchorEdges);
53510
53761
  const base = {
53511
53762
  daemonVersion,
53763
+ // Origin key (= project, the salted `wp_…`) — the door stamps it onto created
53764
+ // nodes as `authoringProject`, arming the evidence channels' cross-origin gate
53765
+ // (EE-corrob-live: this is the MAIN semantic drain; without the stamp every
53766
+ // Problem/Solution lands fail-open to self-corroboration).
53767
+ ...profile.id ? { originProject: profile.id } : {},
53512
53768
  profile: { stack: profile.stack, languages: profile.languages, domains: profile.domains },
53513
53769
  // Context nodes FIRST: a chunked drain (cloud-client, 25-node chunks) then
53514
53770
  // co-locates the few Language/Package stubs with the first instance chunk,
@@ -53522,6 +53778,46 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
53522
53778
  return { ...base, payloadDigest: digest(base), anchorDigests };
53523
53779
  }
53524
53780
 
53781
+ // src/backfill-edges.ts
53782
+ init_src();
53783
+ init_src2();
53784
+ var CAUSAL_LABELS = ["Problem", "Solution", "RootCause"];
53785
+ var CAUSAL_EDGES2 = ["SOLVED_BY", "CAUSED_BY", "FIXED_BY"];
53786
+ function cloudWireId(store, id) {
53787
+ const n = store.getNode(id);
53788
+ if (!n || !CAUSAL_LABELS.includes(n.label)) return null;
53789
+ if (n.label === "Problem" && n.attrs["resolvedAs"] === PROBLEM_RESOLUTION.FALSE_POSITIVE) return null;
53790
+ const cloudNodeId = n.attrs["cloudNodeId"];
53791
+ if (typeof cloudNodeId === "string" && cloudNodeId.length > 0) return cloudNodeId;
53792
+ if (n.attrs["source"] === "cloud") return n.id;
53793
+ return typeof n.attrs["contributedAtSeq"] === "number" ? n.id : null;
53794
+ }
53795
+ function buildCausalEdgeBackfill(store, profile, daemonVersion) {
53796
+ const edges = [];
53797
+ const seenEdge = /* @__PURE__ */ new Set();
53798
+ for (const label of CAUSAL_LABELS) {
53799
+ for (const n of store.findNodesByLabel(label)) {
53800
+ for (const e of store.outEdges(n.id, [...CAUSAL_EDGES2])) {
53801
+ if (seenEdge.has(e.id)) continue;
53802
+ seenEdge.add(e.id);
53803
+ const from = cloudWireId(store, e.from);
53804
+ const to = cloudWireId(store, e.to);
53805
+ if (!from || !to) continue;
53806
+ edges.push({ ...e, from, to, attrs: {} });
53807
+ }
53808
+ }
53809
+ }
53810
+ if (edges.length === 0) return null;
53811
+ const base = {
53812
+ daemonVersion,
53813
+ profile: { stack: profile.stack, languages: profile.languages, domains: profile.domains },
53814
+ originProject: profile.id,
53815
+ nodes: [],
53816
+ edges
53817
+ };
53818
+ return { ...base, payloadDigest: digest(base) };
53819
+ }
53820
+
53525
53821
  // src/multi.ts
53526
53822
  init_generalize_graph();
53527
53823
  init_symbol_summaries();
@@ -54095,8 +54391,10 @@ async function startMultiDaemon(opts = {}) {
54095
54391
  path: r.root,
54096
54392
  stack: r.entry.stack,
54097
54393
  nodes: r.engine.store.nodeCount(),
54098
- problems: r.engine.store.findNodesByLabel("Problem").length,
54099
- solutions: r.engine.store.findNodesByLabel("Solution").length,
54394
+ // COUNT(*) — the materializing findNodesByLabel(...).length form held
54395
+ // the event loop for seconds per poll once stores grew (HZ-index-hydrate).
54396
+ problems: r.engine.store.countNodesByLabel("Problem"),
54397
+ solutions: r.engine.store.countNodesByLabel("Solution"),
54100
54398
  endpoints: `/ws/${r.id}/`
54101
54399
  })),
54102
54400
  humanView: "run `errata report` \u2014 the dashboard was retired (GRAFT 4e)"
@@ -54152,8 +54450,9 @@ async function startMultiDaemon(opts = {}) {
54152
54450
  name: r.entry.name,
54153
54451
  path: r.root,
54154
54452
  nodes: r.engine.store.nodeCount(),
54155
- problems: r.engine.store.findNodesByLabel("Problem").length,
54156
- solutions: r.engine.store.findNodesByLabel("Solution").length,
54453
+ // COUNT(*) — same hydration hazard as `/` (HZ-index-hydrate).
54454
+ problems: r.engine.store.countNodesByLabel("Problem"),
54455
+ solutions: r.engine.store.countNodesByLabel("Solution"),
54157
54456
  stranded: strandedCount(r)
54158
54457
  }))
54159
54458
  })
@@ -54522,7 +54821,19 @@ async function startMultiDaemon(opts = {}) {
54522
54821
  async syncTriagePublic() {
54523
54822
  if (!loadConfig().consent.sync) return { uploaded: 0, skipped: "consent-off" };
54524
54823
  const ignore = loadClaimIgnorePatterns(globalDir());
54525
- const payload = buildTriageIngest(sharedStore, DAEMON_VERSION, ignore);
54824
+ const langCounts = /* @__PURE__ */ new Map();
54825
+ for (const r of records) {
54826
+ for (const l of r.engine.profile.languages ?? []) {
54827
+ const k = String(l).trim().toLowerCase();
54828
+ if (k) langCounts.set(k, (langCounts.get(k) ?? 0) + 1);
54829
+ }
54830
+ }
54831
+ const primaryLanguage = [...langCounts.entries()].sort(
54832
+ (a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1)
54833
+ )[0]?.[0];
54834
+ const payload = buildTriageIngest(sharedStore, DAEMON_VERSION, ignore, 2, {
54835
+ ...primaryLanguage ? { primaryLanguage } : {}
54836
+ });
54526
54837
  if (!payload) return { uploaded: 0 };
54527
54838
  const res = await cloudNow().ingest(payload);
54528
54839
  return { uploaded: res.accepted };
@@ -54533,14 +54844,27 @@ async function startMultiDaemon(opts = {}) {
54533
54844
  const ignore = loadClaimIgnorePatterns(globalDir());
54534
54845
  const client = cloudNow();
54535
54846
  let uploaded = 0;
54847
+ const errors = [];
54848
+ const lane = async (name2, projectName, run3) => {
54849
+ try {
54850
+ await run3();
54851
+ } catch (err2) {
54852
+ const msg = `[${projectName}] ${name2}: ${err2 instanceof Error ? err2.message : String(err2)}`;
54853
+ errors.push(msg);
54854
+ console.error(`[sync\u2192cloud] ${msg}`);
54855
+ }
54856
+ };
54536
54857
  for (const r of records) {
54537
54858
  const store = r.engine.store;
54859
+ const projectName = r.engine.profile.name ?? r.engine.profile.id;
54538
54860
  const context = buildContextIngest(store, r.engine.profile, DAEMON_VERSION, ignore, {
54539
54861
  includePackages: cfg2.consent.contributePackages
54540
54862
  });
54541
54863
  if (context) {
54542
- const res = await client.ingest(context);
54543
- uploaded += res.accepted;
54864
+ await lane("context", projectName, async () => {
54865
+ const res = await client.ingest(context);
54866
+ uploaded += res.accepted;
54867
+ });
54544
54868
  }
54545
54869
  let lexicon;
54546
54870
  try {
@@ -54563,34 +54887,58 @@ async function startMultiDaemon(opts = {}) {
54563
54887
  });
54564
54888
  if (instances) {
54565
54889
  if (project) instances.projectId = project.projectId;
54566
- const res = await client.ingest(instances);
54567
- uploaded += res.accepted;
54568
- const seq = store.currentIngestSeq();
54569
- const cloudIdByLocal = new Map(
54570
- res.result.nodes.filter((rn) => rn.nodeId).map((rn) => [rn.canonicalId, rn.nodeId])
54890
+ await lane("instances", projectName, async () => {
54891
+ const res = await client.ingest(instances);
54892
+ uploaded += res.accepted;
54893
+ const seq = store.currentIngestSeq();
54894
+ const cloudIdByLocal = new Map(
54895
+ res.result.nodes.filter((rn) => rn.nodeId).map((rn) => [rn.canonicalId, rn.nodeId])
54896
+ );
54897
+ for (const n of instances.nodes) {
54898
+ const local = store.getNode(n.id);
54899
+ if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
54900
+ const cloudNodeId = cloudIdByLocal.get(n.id);
54901
+ store.updateNode(n.id, {
54902
+ attrs: {
54903
+ ...local.attrs,
54904
+ contributedAtSeq: seq,
54905
+ ...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
54906
+ }
54907
+ });
54908
+ }
54909
+ for (const [localId, dg] of Object.entries(instances.anchorDigests)) {
54910
+ const local = store.getNode(localId);
54911
+ if (!local) continue;
54912
+ store.updateNode(localId, {
54913
+ attrs: { ...local.attrs, anchorsContributedDigest: dg }
54914
+ });
54915
+ }
54916
+ });
54917
+ }
54918
+ }
54919
+ return { uploaded, ...errors.length > 0 ? { errors } : {} };
54920
+ },
54921
+ async backfillCausalEdges() {
54922
+ if (!loadConfig().consent.sync) return { accepted: 0, rejected: 0, skipped: "consent-off" };
54923
+ const client = cloudNow();
54924
+ let accepted = 0;
54925
+ let rejected = 0;
54926
+ const errors = [];
54927
+ for (const r of records) {
54928
+ const payload = buildCausalEdgeBackfill(r.engine.store, r.engine.profile, DAEMON_VERSION);
54929
+ if (!payload) continue;
54930
+ try {
54931
+ const res = await client.ingest(payload);
54932
+ accepted += res.accepted;
54933
+ rejected += res.rejected;
54934
+ if (res.rejected > 0) errors.push(...res.violations.slice(0, 5));
54935
+ } catch (err2) {
54936
+ errors.push(
54937
+ `[${r.engine.profile.name ?? r.engine.profile.id}] backfill: ${err2 instanceof Error ? err2.message : String(err2)}`
54571
54938
  );
54572
- for (const n of instances.nodes) {
54573
- const local = store.getNode(n.id);
54574
- if (!local || !["Problem", "Solution", "RootCause"].includes(local.label)) continue;
54575
- const cloudNodeId = cloudIdByLocal.get(n.id);
54576
- store.updateNode(n.id, {
54577
- attrs: {
54578
- ...local.attrs,
54579
- contributedAtSeq: seq,
54580
- ...cloudNodeId && cloudNodeId !== n.id ? { cloudNodeId } : {}
54581
- }
54582
- });
54583
- }
54584
- for (const [localId, dg] of Object.entries(instances.anchorDigests)) {
54585
- const local = store.getNode(localId);
54586
- if (!local) continue;
54587
- store.updateNode(localId, {
54588
- attrs: { ...local.attrs, anchorsContributedDigest: dg }
54589
- });
54590
- }
54591
54939
  }
54592
54940
  }
54593
- return { uploaded };
54941
+ return { accepted, rejected, ...errors.length > 0 ? { errors } : {} };
54594
54942
  },
54595
54943
  async pullTriagePublic() {
54596
54944
  if (!loadConfig().consent.sync) return { merged: 0, skipped: "consent-off" };
@@ -54640,7 +54988,12 @@ async function startMultiDaemon(opts = {}) {
54640
54988
  }
54641
54989
  const inst = await daemon.syncInstancesPublic();
54642
54990
  const skills = await daemon.syncSkillsAll();
54643
- return { uploaded: inst.uploaded, written: skills.written, pruned: skills.pruned };
54991
+ return {
54992
+ uploaded: inst.uploaded,
54993
+ written: skills.written,
54994
+ pruned: skills.pruned,
54995
+ ...inst.errors ? { errors: inst.errors } : {}
54996
+ };
54644
54997
  } finally {
54645
54998
  boundaryFlushing = false;
54646
54999
  }
@@ -54667,13 +55020,18 @@ async function startMultiDaemon(opts = {}) {
54667
55020
  }
54668
55021
  };
54669
55022
  app.post("/api/sync", async (c) => {
55023
+ if (boundaryFlushing) return c.json({ skipped: "in-flight" }, 409);
55024
+ boundaryFlushing = true;
54670
55025
  try {
54671
55026
  const principles = await daemon.syncPrinciplesPublic();
54672
55027
  const triage = await daemon.syncTriagePublic();
54673
55028
  const instances = await daemon.syncInstancesPublic();
54674
- return c.json({ principles, triage, instances });
55029
+ const edgeBackfill = await daemon.backfillCausalEdges();
55030
+ return c.json({ principles, triage, instances, edgeBackfill });
54675
55031
  } catch (err2) {
54676
55032
  return c.json({ error: err2 instanceof Error ? err2.message : String(err2) }, 500);
55033
+ } finally {
55034
+ boundaryFlushing = false;
54677
55035
  }
54678
55036
  });
54679
55037
  app.post("/api/tick", async (c) => {
@@ -57497,8 +57855,12 @@ async function cmdSync(arg) {
57497
57855
  try {
57498
57856
  const res = await fetch(`${lock.webUiUrl}/api/sync`, {
57499
57857
  method: "POST",
57500
- signal: AbortSignal.timeout(3e4)
57858
+ signal: AbortSignal.timeout(15 * 6e4)
57501
57859
  });
57860
+ if (res.status === 409) {
57861
+ console.log("sync already running: the daemon is mid-drain \u2014 it will finish on its own. Nothing new was started.");
57862
+ return;
57863
+ }
57502
57864
  if (!res.ok) {
57503
57865
  const detail = await res.text().catch(() => "");
57504
57866
  console.error(`sync failed: daemon at ${lock.webUiUrl} returned ${res.status}${detail ? ` \u2014 ${detail}` : ""}`);
@@ -57509,7 +57871,7 @@ async function cmdSync(arg) {
57509
57871
  } catch (err2) {
57510
57872
  const timedOut = err2 instanceof Error && err2.name === "TimeoutError";
57511
57873
  console.error(
57512
- timedOut ? `sync failed: the running daemon at ${lock.webUiUrl} didn't respond within 30s \u2014 it may be mid-reindex. Retry shortly.` : `sync failed: could not reach the running daemon at ${lock.webUiUrl} (${err2 instanceof Error ? err2.message : err2})`
57874
+ timedOut ? `sync gave up waiting after 15 min \u2014 the daemon's pass may STILL be running (aborting this request does not cancel it). Check daemon.log before retrying; a retry while it runs answers 409.` : `sync failed: could not reach the running daemon at ${lock.webUiUrl} (${err2 instanceof Error ? err2.message : err2})`
57513
57875
  );
57514
57876
  process.exit(1);
57515
57877
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.1-dev.99",
3
+ "version": "2.0.2-dev.125",
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`) ──
@@ -15162,7 +15165,12 @@ var init_wire = __esm({
15162
15165
  * knows which packages are private. The door does NOT trust a `public` claim
15163
15166
  * blindly (it re-confirms on the spine); absent ⇒ unknown ⇒ fail-closed to
15164
15167
  * org-private. Accepted-but-ignored until ORG_MEMBRANE_ENABLED flips. */
15165
- anchorVisibility: external_exports.enum(["public", "private"]).optional()
15168
+ anchorVisibility: external_exports.enum(["public", "private"]).optional(),
15169
+ /** The spine-resolvable anchor id backing a `public` tag (OM-anchor-tag): a
15170
+ * Package purl or `languageCanonicalId`. The door validates THIS on the
15171
+ * public spine (falling back to `canonicalId` when absent — context stubs
15172
+ * are self-anchored). Never trusted without spine confirmation. */
15173
+ anchor: external_exports.string().min(1).max(300).optional()
15166
15174
  });
15167
15175
  RouteContextCountWireSchema = external_exports.object({
15168
15176
  confirmed: external_exports.number().int().min(0),
@@ -24330,6 +24338,13 @@ var SqliteGraphStore = class {
24330
24338
  findByLabel: this.db.prepare(
24331
24339
  "SELECT * FROM nodes WHERE label = ? AND valid_to IS NULL"
24332
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
+ ),
24333
24348
  // ALL versions of a label — incl frozen/closed (valid_to set). Used by the
24334
24349
  // clean-reindex purge so a true wipe removes history too, not just live rows.
24335
24350
  findByLabelAll: this.db.prepare("SELECT * FROM nodes WHERE label = ?"),
@@ -24583,6 +24598,13 @@ var SqliteGraphStore = class {
24583
24598
  const rows = this.stmts.findByLabel.all(label);
24584
24599
  return rows.map(rowToNode);
24585
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
+ }
24586
24608
  findAllVersionsByLabel(label) {
24587
24609
  const rows = this.stmts.findByLabelAll.all(label);
24588
24610
  return rows.map(rowToNode);