@inerrata-corporation/errata 2.0.0-dev.40 → 2.0.0-dev.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/errata.mjs CHANGED
@@ -18445,7 +18445,7 @@ var init_triage = __esm({
18445
18445
  // ../../packages/math/src/relevance.ts
18446
18446
  function scoreRelevance(input, weights) {
18447
18447
  const w = { ...DEFAULT_RELEVANCE_WEIGHTS, ...weights };
18448
- const semantic = input.semantic == null ? 1 : clamp01((input.semantic - w.semanticFloor) / (1 - w.semanticFloor));
18448
+ const semantic = input.semantic != null ? clamp01((input.semantic - w.semanticFloor) / (1 - w.semanticFloor)) : input.semanticUnknown ? clamp01(w.semanticNeutral) : 1;
18449
18449
  const specificity = 1 + w.specificityWeight * clamp01(input.specificity ?? 0);
18450
18450
  const centrality = 1 + w.centralityWeight * Math.tanh((input.centrality ?? 0) / w.centralityScale);
18451
18451
  const quality = 1 + w.qualityWeight * Math.tanh((input.quality ?? 0) / w.qualityScale);
@@ -18462,6 +18462,7 @@ var init_relevance = __esm({
18462
18462
  "use strict";
18463
18463
  DEFAULT_RELEVANCE_WEIGHTS = {
18464
18464
  semanticFloor: 0.5,
18465
+ semanticNeutral: 0.4,
18465
18466
  centralityWeight: 0.5,
18466
18467
  centralityScale: 1,
18467
18468
  qualityWeight: 0.4,
@@ -20054,11 +20055,15 @@ function buildSnapshot(opts) {
20054
20055
  languages: opts.profile.languages.map((name2) => ({ name: name2, node: matchNode(langNodes, name2) })),
20055
20056
  packages: opts.profile.stack.map((name2) => ({ name: name2, node: matchNode(pkgNodes, pkgBase(name2)) }))
20056
20057
  };
20058
+ const causalNudge = recent.length >= 2 && recent.every(
20059
+ (r) => opts.store.outEdges(r.node.id, ["CAUSED_BY"]).length === 0 && opts.store.inEdges(r.node.id, ["CAUSED_BY"]).length === 0
20060
+ );
20057
20061
  return {
20058
20062
  profile: opts.profile,
20059
20063
  profileContext,
20060
20064
  recentProblems: recent,
20061
20065
  recentResolved,
20066
+ ...causalNudge ? { causalNudge } : {},
20062
20067
  motifs,
20063
20068
  reviewCount: opts.reviewCount,
20064
20069
  reviewUiUrl: opts.reviewUiUrl,
@@ -20205,6 +20210,11 @@ function renderSnapshot(s) {
20205
20210
  const surprise = (r.node.cumulativeSurprise ?? 0).toFixed(2);
20206
20211
  lines.push(` - observed ${hits}\xD7 \xB7 cumulativeSurprise ${surprise}`);
20207
20212
  }
20213
+ if (s.causalNudge) {
20214
+ lines.push(
20215
+ "_None of these problems are causally linked yet \u2014 if one caused another (or your report says so in prose), bind them with `(cause:#slug \u2026)` / `(cause:[handle])`. Trace can only follow edges you declare._"
20216
+ );
20217
+ }
20208
20218
  }
20209
20219
  if (s.recentResolved.length > 0) {
20210
20220
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
@@ -21361,7 +21371,7 @@ function loadConfig() {
21361
21371
  saveConfig(cfg);
21362
21372
  return cfg;
21363
21373
  }
21364
- const raw2 = readFileSync3(p, "utf8");
21374
+ const raw2 = readFileSync3(p, "utf8").replace(/^\uFEFF/, "");
21365
21375
  const parsed = JSON.parse(raw2);
21366
21376
  const base = defaultConfig();
21367
21377
  return {
@@ -24583,6 +24593,18 @@ import { mkdirSync as mkdirSync4, existsSync as existsSync6 } from "node:fs";
24583
24593
  import { homedir as homedir2 } from "node:os";
24584
24594
  import { dirname as dirname3, join as join4 } from "node:path";
24585
24595
  import { createRequire } from "node:module";
24596
+ function semanticFloorFor(version2) {
24597
+ if (version2 === EMBEDDING_VERSION) return 0.25;
24598
+ return 0.5;
24599
+ }
24600
+ function noteHashFallback() {
24601
+ if (warnedHashFallback) return;
24602
+ if (process.env["ERRATA_EMBED_HASH_ONLY"] === "1") return;
24603
+ warnedHashFallback = true;
24604
+ console.warn(
24605
+ `[errata] embedding model unavailable (${lastError?.message ?? "unknown"}) \u2014 falling back to ${EMBEDDING_VERSION} hash vectors; semantic ranking runs at reduced fidelity`
24606
+ );
24607
+ }
24586
24608
  function getCacheDir() {
24587
24609
  return process.env["ERRATA_MODEL_CACHE"] ?? join4(homedir2(), ".errata", "models");
24588
24610
  }
@@ -24673,6 +24695,7 @@ async function embedBatchWithModelOrHash(texts) {
24673
24695
  }
24674
24696
  } catch {
24675
24697
  }
24698
+ noteHashFallback();
24676
24699
  return texts.map((t) => {
24677
24700
  const v = embed(t);
24678
24701
  return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
@@ -24687,11 +24710,12 @@ async function embedTextWithModelOrHash(text) {
24687
24710
  const v = await embedTextWithModel(text);
24688
24711
  return { vector: v, version: MODEL_EMBEDDING_VERSION, dim: v.length };
24689
24712
  } catch {
24713
+ noteHashFallback();
24690
24714
  const v = embed(text);
24691
24715
  return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
24692
24716
  }
24693
24717
  }
24694
- var MODEL_NAME, MODEL_EMBEDDING_DIM, MODEL_EMBEDDING_VERSION, pipelinePromise, lastError;
24718
+ var MODEL_NAME, MODEL_EMBEDDING_DIM, MODEL_EMBEDDING_VERSION, pipelinePromise, lastError, warnedHashFallback;
24695
24719
  var init_model = __esm({
24696
24720
  "../../packages/embedding/src/model.ts"() {
24697
24721
  "use strict";
@@ -24701,6 +24725,7 @@ var init_model = __esm({
24701
24725
  MODEL_EMBEDDING_VERSION = "minilm-l6-v2";
24702
24726
  pipelinePromise = null;
24703
24727
  lastError = null;
24728
+ warnedHashFallback = false;
24704
24729
  }
24705
24730
  });
24706
24731
 
@@ -25860,11 +25885,11 @@ var init_generalizer = __esm({
25860
25885
  function layerOf(kind) {
25861
25886
  return kind === "Technique" ? "technique" : kind === "AntiPattern" ? "antipattern" : "pattern";
25862
25887
  }
25863
- function motifTitle(members, layer) {
25888
+ function motifTitle(members, layer, instanceCount) {
25864
25889
  const top = [...members].sort(
25865
25890
  (a, b) => b.extractionConfidence - a.extractionConfidence
25866
25891
  )[0];
25867
- return `${layer}: ${top?.description ?? "recurring problem cluster"}`;
25892
+ return `${layer} \xD7${instanceCount} (exemplar): ${top?.description ?? "recurring problem cluster"}`;
25868
25893
  }
25869
25894
  function classifyMotif(store, instances, promoted) {
25870
25895
  if (!promoted || instances.length === 0) return "Pattern";
@@ -25910,7 +25935,9 @@ function promoteMotifs(store, t) {
25910
25935
  const motif = {
25911
25936
  id: motifId,
25912
25937
  label: kind,
25913
- description: existing?.description ?? motifTitle(members, layerOf(kind)),
25938
+ // Refresh the deterministic title every run (exemplar/count/kind drift),
25939
+ // but never clobber a distilled one — a distillation pass owns it then.
25940
+ description: existing?.attrs["titleDistilled"] === true ? existing.description : motifTitle(members, layerOf(kind), instanceCount),
25914
25941
  extractionConfidence: d.motifConfidence,
25915
25942
  extractionSource: "bko-inferred",
25916
25943
  embedding: existing?.embedding ?? [],
@@ -26121,8 +26148,8 @@ async function embedNodesByLabels(store, labels, opts = {}) {
26121
26148
  durationMs: 0,
26122
26149
  versionsSeen: {}
26123
26150
  };
26124
- const probe = await embedBatchWithModelOrHash(["probe"]);
26125
- const currentVersion = probe[0]?.version ?? "unknown";
26151
+ const hashOnly = opts.mode === "hash";
26152
+ const currentVersion = hashOnly ? EMBEDDING_VERSION : (await embedBatchWithModelOrHash(["probe"]))[0]?.version ?? "unknown";
26126
26153
  const todo = [];
26127
26154
  for (const label of labels) {
26128
26155
  for (const node2 of store.findNodesByLabel(label)) {
@@ -26131,6 +26158,10 @@ async function embedNodesByLabels(store, labels, opts = {}) {
26131
26158
  if (existingVersion) {
26132
26159
  report.versionsSeen[existingVersion] = (report.versionsSeen[existingVersion] ?? 0) + 1;
26133
26160
  }
26161
+ if (hashOnly && node2.embedding.length > 0) {
26162
+ report.skippedFresh++;
26163
+ continue;
26164
+ }
26134
26165
  if (!opts.force && existingVersion === currentVersion && node2.embedding.length > 0) {
26135
26166
  report.skippedFresh++;
26136
26167
  continue;
@@ -26144,7 +26175,10 @@ async function embedNodesByLabels(store, labels, opts = {}) {
26144
26175
  const batch = todo.slice(i2, i2 + BATCH_SIZE);
26145
26176
  let results;
26146
26177
  try {
26147
- results = await embedBatchWithModelOrHash(batch.map((b) => b.text));
26178
+ results = hashOnly ? batch.map((b) => {
26179
+ const v = embed(b.text);
26180
+ return { vector: v, version: EMBEDDING_VERSION, dim: v.length };
26181
+ }) : await embedBatchWithModelOrHash(batch.map((b) => b.text));
26148
26182
  } catch {
26149
26183
  report.failed += batch.length;
26150
26184
  continue;
@@ -26210,12 +26244,15 @@ function nodeFreshness(node2, currentIngestSeq) {
26210
26244
  }
26211
26245
  function scoreCandidate(candidate, ctx = {}, weights) {
26212
26246
  const sameVersion = !ctx.seedEmbeddingVersion || !candidate.attrs["embeddingVersion"] || candidate.attrs["embeddingVersion"] === ctx.seedEmbeddingVersion;
26213
- const semantic = ctx.seedEmbedding && candidate.embedding.length === ctx.seedEmbedding.length && sameVersion ? cosine(ctx.seedEmbedding, candidate.embedding) : void 0;
26247
+ const comparable = ctx.seedEmbedding != null && candidate.embedding.length === ctx.seedEmbedding.length && sameVersion;
26248
+ const semantic = comparable ? cosine(ctx.seedEmbedding, candidate.embedding) : void 0;
26249
+ const semanticUnknown = ctx.seedEmbedding != null && !comparable;
26214
26250
  const quality = typeof candidate.attrs["effectivenessScore"] === "number" ? candidate.attrs["effectivenessScore"] : void 0;
26215
26251
  const trustTier = candidate.attrs["trustTier"];
26216
26252
  return scoreRelevance(
26217
26253
  {
26218
26254
  ...semantic !== void 0 ? { semantic } : {},
26255
+ ...semanticUnknown ? { semanticUnknown } : {},
26219
26256
  centrality: candidate.pageRank,
26220
26257
  ...quality !== void 0 ? { quality } : {},
26221
26258
  ...ctx.specificity !== void 0 ? { specificity: ctx.specificity } : {},
@@ -26235,7 +26272,11 @@ function rankByRelevance(store, seedId, candidateIds, opts = {}) {
26235
26272
  };
26236
26273
  const nodes = candidateIds.map((id) => store.getNode(id)).filter((n) => n != null);
26237
26274
  const maxPr = Math.max(1e-9, ...nodes.map((n) => n.pageRank));
26238
- const weights = { centralityScale: maxPr, ...opts.weights };
26275
+ const weights = {
26276
+ centralityScale: maxPr,
26277
+ ...ctx.seedEmbedding ? { semanticFloor: semanticFloorFor(ctx.seedEmbeddingVersion) } : {},
26278
+ ...opts.weights
26279
+ };
26239
26280
  const now = store.currentIngestSeq();
26240
26281
  return nodes.map((node2) => {
26241
26282
  const r = scoreCandidate(node2, { ...ctx, decay: nodeFreshness(node2, now) }, weights);
@@ -26270,7 +26311,11 @@ function localBurst(store, seedId, opts = {}) {
26270
26311
  const gated = seed.embedding.length > 0;
26271
26312
  const nodes = [...hopOf.entries()].map(([id, hops]) => ({ node: store.getNode(id), hops })).filter((x) => x.node != null);
26272
26313
  const maxPr = Math.max(1e-9, ...nodes.map((x) => x.node.pageRank));
26273
- const weights = { centralityScale: maxPr, ...opts.weights };
26314
+ const weights = {
26315
+ centralityScale: maxPr,
26316
+ ...gated ? { semanticFloor: semanticFloorFor(ctxSeed.seedEmbeddingVersion) } : {},
26317
+ ...opts.weights
26318
+ };
26274
26319
  const now = store.currentIngestSeq();
26275
26320
  return nodes.map(({ node: node2, hops }) => {
26276
26321
  const r = scoreCandidate(node2, { ...ctxSeed, hops, decay: nodeFreshness(node2, now) }, weights);
@@ -42504,7 +42549,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
42504
42549
  }
42505
42550
 
42506
42551
  // src/engine.ts
42507
- var DAEMON_VERSION = true ? "2.0.0-dev.40" : "2.0.0-alpha.0";
42552
+ var DAEMON_VERSION = true ? "2.0.0-dev.41" : "2.0.0-alpha.0";
42508
42553
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
42509
42554
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
42510
42555
  var GIT_OP_MUTE_MS = 4e3;
@@ -43532,6 +43577,16 @@ function createWorkspaceEngine(opts) {
43532
43577
  telemetry.count("problems_resolved", gen.problemsResolved);
43533
43578
  telemetry.count("reviews_triggered", gen.reviewsTriggered);
43534
43579
  telemetry.count("signals_credited", report.signalsCredited);
43580
+ if (gen.problemsWritten > 0) {
43581
+ try {
43582
+ const inline = await embedSemanticNodes(store, { mode: "hash" });
43583
+ if (inline.embedded > 0) {
43584
+ console.log(`[errata] inline-embedded ${inline.embedded} fresh semantic node(s) (hash)`);
43585
+ }
43586
+ } catch (err2) {
43587
+ console.warn("[errata] inline semantic embed failed:", err2 instanceof Error ? err2.message : err2);
43588
+ }
43589
+ }
43535
43590
  const graphChanged = gen.problemsWritten + gen.problemsResolved + gen.problemsReinforced + gen.reviewsTriggered + report.signalsCredited > 0;
43536
43591
  if (!opts.skipContextWriter && (graphChanged || contextDirty)) {
43537
43592
  refreshContextNow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.40",
3
+ "version": "2.0.0-dev.41",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -25393,11 +25393,11 @@ var MEMBER_LABELS = ["Problem", "RootCause", "Solution"];
25393
25393
  function layerOf(kind) {
25394
25394
  return kind === "Technique" ? "technique" : kind === "AntiPattern" ? "antipattern" : "pattern";
25395
25395
  }
25396
- function motifTitle(members, layer) {
25396
+ function motifTitle(members, layer, instanceCount) {
25397
25397
  const top = [...members].sort(
25398
25398
  (a, b) => b.extractionConfidence - a.extractionConfidence
25399
25399
  )[0];
25400
- return `${layer}: ${top?.description ?? "recurring problem cluster"}`;
25400
+ return `${layer} \xD7${instanceCount} (exemplar): ${top?.description ?? "recurring problem cluster"}`;
25401
25401
  }
25402
25402
  function classifyMotif(store2, instances, promoted) {
25403
25403
  if (!promoted || instances.length === 0) return "Pattern";
@@ -25443,7 +25443,9 @@ function promoteMotifs(store2, t) {
25443
25443
  const motif = {
25444
25444
  id: motifId,
25445
25445
  label: kind,
25446
- description: existing?.description ?? motifTitle(members, layerOf(kind)),
25446
+ // Refresh the deterministic title every run (exemplar/count/kind drift),
25447
+ // but never clobber a distilled one — a distillation pass owns it then.
25448
+ description: existing?.attrs["titleDistilled"] === true ? existing.description : motifTitle(members, layerOf(kind), instanceCount),
25447
25449
  extractionConfidence: d.motifConfidence,
25448
25450
  extractionSource: "bko-inferred",
25449
25451
  embedding: existing?.embedding ?? [],
@@ -25681,11 +25683,15 @@ function buildSnapshot(opts) {
25681
25683
  languages: opts.profile.languages.map((name2) => ({ name: name2, node: matchNode(langNodes, name2) })),
25682
25684
  packages: opts.profile.stack.map((name2) => ({ name: name2, node: matchNode(pkgNodes, pkgBase(name2)) }))
25683
25685
  };
25686
+ const causalNudge = recent.length >= 2 && recent.every(
25687
+ (r) => opts.store.outEdges(r.node.id, ["CAUSED_BY"]).length === 0 && opts.store.inEdges(r.node.id, ["CAUSED_BY"]).length === 0
25688
+ );
25684
25689
  return {
25685
25690
  profile: opts.profile,
25686
25691
  profileContext,
25687
25692
  recentProblems: recent,
25688
25693
  recentResolved,
25694
+ ...causalNudge ? { causalNudge } : {},
25689
25695
  motifs,
25690
25696
  reviewCount: opts.reviewCount,
25691
25697
  reviewUiUrl: opts.reviewUiUrl,