@inerrata-corporation/errata 2.0.0-dev.91 → 2.0.0-dev.93

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
@@ -17918,11 +17918,90 @@ function linkProblemToLanguages(store, problemId, text, ts, index) {
17918
17918
  }
17919
17919
  return linked;
17920
17920
  }
17921
- function deriveContextFromStatement(store, problemId, text, ts, pkgIndex) {
17922
- return {
17923
- packages: linkProblemToPackages(store, problemId, text, ts, pkgIndex).linked,
17924
- languages: linkProblemToLanguages(store, problemId, text, ts)
17921
+ function isShapedSymbol(name2) {
17922
+ if (name2.length < SYMBOL_MIN_LEN) return false;
17923
+ return name2 !== name2.toLowerCase() || name2.includes("_") || name2.includes(".");
17924
+ }
17925
+ function buildSymbolIndex(store) {
17926
+ const idx = /* @__PURE__ */ new Map();
17927
+ const ambiguous = /* @__PURE__ */ new Set();
17928
+ for (const label of SYMBOL_LABELS) {
17929
+ for (const n of store.findNodesByLabel(label)) {
17930
+ const name2 = n.description?.trim();
17931
+ if (!name2 || ambiguous.has(name2) || !isShapedSymbol(name2)) continue;
17932
+ if (idx.has(name2)) {
17933
+ idx.delete(name2);
17934
+ ambiguous.add(name2);
17935
+ } else {
17936
+ idx.set(name2, n);
17937
+ }
17938
+ }
17939
+ }
17940
+ return idx;
17941
+ }
17942
+ function matchSymbolsInText(text, index) {
17943
+ const out2 = [];
17944
+ for (const [name2, node2] of index) {
17945
+ if (!text.includes(name2)) continue;
17946
+ const bounded = new RegExp(`(?<![\\w$])${escapeRe3(name2)}(?![\\w$])`);
17947
+ if (bounded.test(text)) out2.push(node2);
17948
+ }
17949
+ return out2;
17950
+ }
17951
+ function linkProblemToSymbols(store, problemId, text, ts, index) {
17952
+ const p = store.getNode(problemId);
17953
+ if (!p || p.label !== "Problem") return [];
17954
+ const idx = index ?? buildSymbolIndex(store);
17955
+ if (idx.size === 0) return [];
17956
+ let fileByPath = null;
17957
+ const fileFor2 = (relPath) => {
17958
+ if (!fileByPath) {
17959
+ fileByPath = /* @__PURE__ */ new Map();
17960
+ for (const f of store.findNodesByLabel("File")) {
17961
+ const rp = String(f.attrs["relPath"] ?? f.description ?? "");
17962
+ if (rp) fileByPath.set(rp, f);
17963
+ }
17964
+ }
17965
+ return fileByPath.get(relPath) ?? null;
17925
17966
  };
17967
+ const already = new Set(store.outEdges(problemId, ["ANCHORED_AT"]).map((e) => e.to));
17968
+ const linked = [];
17969
+ for (const sym of matchSymbolsInText(text, idx)) {
17970
+ const relPath = String(sym.attrs["relPath"] ?? "");
17971
+ if (!relPath) continue;
17972
+ const file2 = fileFor2(relPath);
17973
+ if (!file2 || already.has(file2.id)) continue;
17974
+ store.mergeEdge({
17975
+ id: `edge_${digest({ from: problemId, type: "ANCHORED_AT", to: file2.id })}`.slice(0, 24),
17976
+ from: problemId,
17977
+ to: file2.id,
17978
+ type: "ANCHORED_AT",
17979
+ confidence: 0.3,
17980
+ // soft — a lexical mention, below a witnessed anchor's 0.4
17981
+ extractionSource: "daemon-extracted",
17982
+ createdAt: ts,
17983
+ lastSeenAt: ts,
17984
+ navSuccesses: 0,
17985
+ navFailures: 0,
17986
+ // `mention` gates it out of resolveDesignProblems (fix-detection); the
17987
+ // symbol name is kept for provenance + eventual symbol-level anchoring.
17988
+ attrs: { provisional: true, mention: true, mentionSymbol: sym.description }
17989
+ });
17990
+ already.add(file2.id);
17991
+ linked.push(file2.id);
17992
+ }
17993
+ return linked;
17994
+ }
17995
+ function deriveContextFromStatement(store, problemId, text, ts, pkgIndex) {
17996
+ const packages = linkProblemToPackages(store, problemId, text, ts, pkgIndex).linked;
17997
+ const languages = linkProblemToLanguages(store, problemId, text, ts);
17998
+ const anchoredFiles = linkProblemToSymbols(store, problemId, text, ts);
17999
+ if (anchoredFiles.length > 0) {
18000
+ const ctx = deriveContextFromAnchors(store, problemId, ts, pkgIndex);
18001
+ for (const l of ctx.languages) if (!languages.includes(l)) languages.push(l);
18002
+ for (const pk of ctx.packages) if (!packages.includes(pk)) packages.push(pk);
18003
+ }
18004
+ return { packages, languages };
17926
18005
  }
17927
18006
  function problemText(store, p) {
17928
18007
  const parts2 = [p.description];
@@ -18045,7 +18124,7 @@ function backfillProblemContext(store, ts) {
18045
18124
  }
18046
18125
  return report;
18047
18126
  }
18048
- var NAME_SEPARATOR, MIN_NAME_LEN, LANGUAGE_MIN_LEN;
18127
+ var NAME_SEPARATOR, MIN_NAME_LEN, LANGUAGE_MIN_LEN, SYMBOL_MIN_LEN, SYMBOL_LABELS;
18049
18128
  var init_problem_package_link = __esm({
18050
18129
  "../../packages/local-graph/src/problem-package-link.ts"() {
18051
18130
  "use strict";
@@ -18054,6 +18133,8 @@ var init_problem_package_link = __esm({
18054
18133
  NAME_SEPARATOR = /[@/\-._]/;
18055
18134
  MIN_NAME_LEN = 3;
18056
18135
  LANGUAGE_MIN_LEN = 4;
18136
+ SYMBOL_MIN_LEN = 5;
18137
+ SYMBOL_LABELS = ["Function", "Method", "Class", "Const"];
18057
18138
  }
18058
18139
  });
18059
18140
 
@@ -18218,6 +18299,23 @@ function mintPatternNode(store, name2, ts) {
18218
18299
  }
18219
18300
  return id;
18220
18301
  }
18302
+ function titleCaseDomain(name2) {
18303
+ return name2.replace(/\s+/g, " ").trim().split(" ").map((w) => w.length > 0 ? w[0].toUpperCase() + w.slice(1) : w).join(" ");
18304
+ }
18305
+ function mintDomainNode(store, name2, ts) {
18306
+ const display = titleCaseDomain(name2);
18307
+ const id = `dom_${digest({ domain: display.toLowerCase() })}`.slice(0, 56);
18308
+ if (!store.getNode(id)) {
18309
+ store.mergeNode(
18310
+ buildNode(id, "Domain", display, ts, {
18311
+ source: "convo",
18312
+ provisional: true,
18313
+ canonicalId: genericCanonicalId("dom", { type: "Domain", description: display })
18314
+ })
18315
+ );
18316
+ }
18317
+ return id;
18318
+ }
18221
18319
  function resolveFileNode(store, path2, workspaceId2) {
18222
18320
  const want = path2.trim().replace(/^\.?\//, "");
18223
18321
  for (const n of store.findNodesByLabel("File")) {
@@ -18437,6 +18535,7 @@ function resolveDesignProblems(store, t) {
18437
18535
  let symRelPath;
18438
18536
  let edited = false;
18439
18537
  for (const e of store.outEdges(p.id, ["ANCHORED_AT"])) {
18538
+ if (e.attrs?.["mention"] === true) continue;
18440
18539
  const sym = store.getNode(e.to);
18441
18540
  if (sym && sym.lastUpdatedAt > p.createdAt) {
18442
18541
  edited = true;
@@ -20630,6 +20729,7 @@ __export(src_exports2, {
20630
20729
  buildLanguageIndex: () => buildLanguageIndex,
20631
20730
  buildPackageIndex: () => buildPackageIndex,
20632
20731
  buildPrincipleSync: () => buildPrincipleSync,
20732
+ buildSymbolIndex: () => buildSymbolIndex,
20633
20733
  causalChain: () => causalChain,
20634
20734
  claimId: () => claimId,
20635
20735
  clearRevisit: () => clearRevisit,
@@ -20658,12 +20758,15 @@ __export(src_exports2, {
20658
20758
  isPlaceholderStatement: () => isPlaceholderStatement,
20659
20759
  linkProblemToLanguages: () => linkProblemToLanguages,
20660
20760
  linkProblemToPackages: () => linkProblemToPackages,
20761
+ linkProblemToSymbols: () => linkProblemToSymbols,
20661
20762
  listNeedsRevisit: () => listNeedsRevisit,
20662
20763
  markRevisit: () => markRevisit,
20663
20764
  matchLanguagesInText: () => matchLanguagesInText,
20664
20765
  matchPackagesInText: () => matchPackagesInText,
20766
+ matchSymbolsInText: () => matchSymbolsInText,
20665
20767
  mergeCloudCounts: () => mergeCloudCounts,
20666
20768
  mergeDuplicateProblems: () => mergeDuplicateProblems,
20769
+ mintDomainNode: () => mintDomainNode,
20667
20770
  mintPatternNode: () => mintPatternNode,
20668
20771
  openGraphStore: () => openGraphStore,
20669
20772
  osNodeId: () => osNodeId,
@@ -20796,12 +20899,19 @@ function buildSnapshot(opts) {
20796
20899
  const causalNudge = recent.length >= 2 && recent.every(
20797
20900
  (r) => opts.store.outEdges(r.node.id, ["CAUSED_BY"]).length === 0 && opts.store.inEdges(r.node.id, ["CAUSED_BY"]).length === 0
20798
20901
  );
20902
+ const abstractUndomained = recent.filter((r) => {
20903
+ const grounded = opts.store.outEdges(r.node.id, ["ANCHORED_AT", "OCCURS_IN", "DEPENDS_ON"]).length > 0;
20904
+ const hasDomain = opts.store.outEdges(r.node.id, ["PERTAIN_TO"]).length > 0;
20905
+ return !grounded && !hasDomain;
20906
+ });
20907
+ const domainNudge = abstractUndomained.length >= 2;
20799
20908
  return {
20800
20909
  profile: opts.profile,
20801
20910
  profileContext,
20802
20911
  recentProblems: recent,
20803
20912
  recentResolved,
20804
20913
  ...causalNudge ? { causalNudge } : {},
20914
+ ...domainNudge ? { domainNudge } : {},
20805
20915
  motifs,
20806
20916
  reviewCount: opts.reviewCount,
20807
20917
  reviewUiUrl: opts.reviewUiUrl,
@@ -20962,6 +21072,11 @@ function renderSnapshot(s) {
20962
21072
  "_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._"
20963
21073
  );
20964
21074
  }
21075
+ if (s.domainNudge) {
21076
+ lines.push(
21077
+ "_Some of these are abstract (no code anchor) and belong to no topic \u2014 name the area each is about with `(domain: The Area)` (e.g. `(domain: Observability)`). Without it an abstract problem is an invisible island; the Domain is where problems in the same area cluster and stay findable._"
21078
+ );
21079
+ }
20965
21080
  }
20966
21081
  if (s.recentResolved.length > 0) {
20967
21082
  lines.push("_Recently resolved \u2014 solved here; jumping-off points, not live defects:_");
@@ -24843,6 +24958,9 @@ function linkBullet(ref) {
24843
24958
  ` \xB7 ${TAG_EXAMPLE.pattern()} \u2014 name the general shape it instantiates (e.g. (pattern: unbounded`,
24844
24959
  " queue growth under backpressure)) \u2014 this works with NO code anchor, and two agents naming",
24845
24960
  " the same pattern converge on one node. Cite a shown Pattern by handle: (pattern:[handle]).",
24961
+ ` \xB7 ${TAG_EXAMPLE.domain()} \u2014 name the ABSTRACT AREA it's about (e.g. (domain: Observability),`,
24962
+ " (domain: Community Detection)). An abstract problem with no code anchor NEEDS this or it's",
24963
+ " an invisible island: the Domain is the topic other problems in the area cluster on. Title Case.",
24846
24964
  ` \xB7 (aids:[${ref}],\u2026) \u2014 your FIX could also help these other, even unrelated, problems.`,
24847
24965
  " A hypothesis, not a claim \u2014 it's recorded as may-resolve and checked by whoever tries it.",
24848
24966
  ` \xB7 when your fix relates to a PRIOR solution you were primed with (a problem's`,
@@ -24911,6 +25029,9 @@ var init_agent_signals = __esm({
24911
25029
  // DIFFERENT relevant priors is the target shape (see linkBullet).
24912
25030
  instance: (ref) => `(instance:[${ref}],[another],[a-third])`,
24913
25031
  pattern: () => `(pattern: name the general shape)`,
25032
+ // DOMAIN — the abstract area a problem is about; the concept layer an
25033
+ // anchor-less problem clusters on. Mints/resolves a Domain by name.
25034
+ domain: () => `(domain: The Area)`,
24914
25035
  aids: (ref) => `(aids:[${ref}])`
24915
25036
  };
24916
25037
  GLOSS = {
@@ -25287,7 +25408,7 @@ function isDistinctiveIdentifier(name2) {
25287
25408
  function buildSymbolLexicon(store, summariesByBodyHash2) {
25288
25409
  const lex = /* @__PURE__ */ new Map();
25289
25410
  const candidates = [];
25290
- for (const [label, kind] of SYMBOL_LABELS) {
25411
+ for (const [label, kind] of SYMBOL_LABELS2) {
25291
25412
  for (const n of store.findNodesByLabel(label)) {
25292
25413
  const bodyHash = typeof n.attrs["bodyHash"] === "string" ? n.attrs["bodyHash"] : void 0;
25293
25414
  const summary = bodyHash ? summariesByBodyHash2?.get(bodyHash) : void 0;
@@ -25326,13 +25447,13 @@ function generalizeSymbols(text, lexicon, level = 1) {
25326
25447
  }
25327
25448
  return generalize(out2, { level }).text;
25328
25449
  }
25329
- var SYMBOL_LABELS, KIND_PHRASE, RE_RESERVED2, escapeRe4, TOKEN_RE3;
25450
+ var SYMBOL_LABELS2, KIND_PHRASE, RE_RESERVED2, escapeRe4, TOKEN_RE3;
25330
25451
  var init_generalize_graph = __esm({
25331
25452
  "src/generalize-graph.ts"() {
25332
25453
  "use strict";
25333
25454
  init_src8();
25334
25455
  init_src();
25335
- SYMBOL_LABELS = [
25456
+ SYMBOL_LABELS2 = [
25336
25457
  ["Function", "function"],
25337
25458
  ["Method", "method"],
25338
25459
  ["Class", "class"],
@@ -49246,6 +49367,7 @@ var SUPERSEDES_PREFIX = /^\s*supersedes:(?:#([a-z][\w-]{0,63}))?\s*/i;
49246
49367
  var ALTERNATIVE_PREFIX = /^\s*alternative:(?:#([a-z][\w-]{0,63}))?\s*/i;
49247
49368
  var INSTANCE_PREFIX = /^\s*instance:\s*/i;
49248
49369
  var PATTERN_RE = /\(\s*pattern:\s*([^()\n]{3,}?)\s*\)/gi;
49370
+ var DOMAIN_RE = /\(\s*domain:\s*([^()\n]{3,}?)\s*\)/gi;
49249
49371
  var CAUSE_TEXT_MIN = 8;
49250
49372
  var FLAG_RE = /\[([!?])(?:#([a-z][\w-]{0,63})\s+)?\s*([^\]\n]{8,}?)\s*(?:@\s*([^\s\]]+?)(?::(\d+))?\s*)?\]/g;
49251
49373
  var CONSTRAINT_MIN = 8;
@@ -49268,7 +49390,7 @@ function parseInlineTags(text) {
49268
49390
  let seqNo = -1;
49269
49391
  for (const raw2 of deFenced.split(/(?<=[.!?])\s+|\n+/)) {
49270
49392
  seqNo++;
49271
- if (raw2.length > 600) continue;
49393
+ if (raw2.length > 2e4) continue;
49272
49394
  const seqStart = out2.length;
49273
49395
  const sentence = raw2.replace(
49274
49396
  /`[^`]*`/g,
@@ -49435,6 +49557,12 @@ function parseInlineTags(text) {
49435
49557
  }
49436
49558
  }
49437
49559
  }
49560
+ DOMAIN_RE.lastIndex = 0;
49561
+ let dm;
49562
+ while ((dm = DOMAIN_RE.exec(sentence)) !== null) {
49563
+ const raw3 = dm[1].replace(/\s+/g, " ").trim();
49564
+ if (raw3.length >= 3) out2.push({ kind: "domain", domainText: raw3, sentence: sfield });
49565
+ }
49438
49566
  CONSTRAINT_RE.lastIndex = 0;
49439
49567
  let c;
49440
49568
  while ((c = CONSTRAINT_RE.exec(sentence)) !== null) {
@@ -49568,7 +49696,7 @@ function harvestInlineTags(store, text, opts) {
49568
49696
  const source = opts.sourceId ? store.getNode(opts.sourceId) : null;
49569
49697
  const mintPriors = opts.mintPriors ?? true;
49570
49698
  const touched = opts.sessionTouchedIds ?? /* @__PURE__ */ new Set();
49571
- const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], refutes: [], corroborations: [] };
49699
+ const plan = { priorEdges: 0, problems: [], fixes: [], triages: [], attempts: [], unresolvedFixes: [], transfers: [], solutionLinks: [], instances: [], patterns: [], domains: [], refutes: [], corroborations: [] };
49572
49700
  const tags = parseInlineTags(text);
49573
49701
  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);
49574
49702
  const bindSymptom = (seq, threadId) => {
@@ -49699,6 +49827,15 @@ function harvestInlineTags(store, text, opts) {
49699
49827
  } else if (tag.patternText) {
49700
49828
  plan.patterns.push({ patternText: tag.patternText, ...bound });
49701
49829
  }
49830
+ } else if (tag.kind === "domain") {
49831
+ const b = bindSymptom(tag.seq, tag.threadId);
49832
+ plan.domains.push({
49833
+ domainText: tag.domainText,
49834
+ ...b.statement ? { boundStatement: b.statement } : {},
49835
+ ...b.problemId ? { problemId: b.problemId } : {},
49836
+ ...tag.threadId ? { threadId: tag.threadId } : {},
49837
+ evidence: b.evidence
49838
+ });
49702
49839
  } else if (tag.kind === "attempt" || tag.kind === "failure") {
49703
49840
  for (const h of tag.refuteHandles ?? []) {
49704
49841
  const nodeId = resolveHandle(store, h, opts.handleMap);
@@ -51301,7 +51438,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
51301
51438
  }
51302
51439
 
51303
51440
  // src/engine.ts
51304
- var DAEMON_VERSION = true ? "2.0.0-dev.91" : "2.0.0-alpha.0";
51441
+ var DAEMON_VERSION = true ? "2.0.0-dev.93" : "2.0.0-alpha.0";
51305
51442
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
51306
51443
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
51307
51444
  var GIT_OP_MUTE_MS = 4e3;
@@ -52128,6 +52265,13 @@ function createWorkspaceEngine(opts) {
52128
52265
  if (!patternId || patternId === pid) continue;
52129
52266
  mintCiteEdge(pid, patternId, "INSTANCE_OF", pt.evidence === "witnessed" ? 0.4 : 0.3, { patternCite: true, evidence: pt.evidence });
52130
52267
  }
52268
+ for (const d of plan.domains) {
52269
+ const pid = bindPid(d);
52270
+ if (!pid) continue;
52271
+ const domainId = mintDomainNode(store, d.domainText, t);
52272
+ if (domainId === pid) continue;
52273
+ mintCiteEdge(pid, domainId, "PERTAIN_TO", d.evidence === "witnessed" ? 0.4 : 0.3, { domainCite: true, evidence: d.evidence });
52274
+ }
52131
52275
  for (const inst of plan.instances) {
52132
52276
  const pid = bindPid(inst);
52133
52277
  if (!pid) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.0-dev.91",
3
+ "version": "2.0.0-dev.93",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -25215,6 +25215,7 @@ function resolveDesignProblems(store2, t) {
25215
25215
  let symRelPath;
25216
25216
  let edited = false;
25217
25217
  for (const e of store2.outEdges(p.id, ["ANCHORED_AT"])) {
25218
+ if (e.attrs?.["mention"] === true) continue;
25218
25219
  const sym = store2.getNode(e.to);
25219
25220
  if (sym && sym.lastUpdatedAt > p.createdAt) {
25220
25221
  edited = true;
@@ -26090,12 +26091,19 @@ function buildSnapshot(opts) {
26090
26091
  const causalNudge = recent.length >= 2 && recent.every(
26091
26092
  (r) => opts.store.outEdges(r.node.id, ["CAUSED_BY"]).length === 0 && opts.store.inEdges(r.node.id, ["CAUSED_BY"]).length === 0
26092
26093
  );
26094
+ const abstractUndomained = recent.filter((r) => {
26095
+ const grounded = opts.store.outEdges(r.node.id, ["ANCHORED_AT", "OCCURS_IN", "DEPENDS_ON"]).length > 0;
26096
+ const hasDomain = opts.store.outEdges(r.node.id, ["PERTAIN_TO"]).length > 0;
26097
+ return !grounded && !hasDomain;
26098
+ });
26099
+ const domainNudge = abstractUndomained.length >= 2;
26093
26100
  return {
26094
26101
  profile: opts.profile,
26095
26102
  profileContext,
26096
26103
  recentProblems: recent,
26097
26104
  recentResolved,
26098
26105
  ...causalNudge ? { causalNudge } : {},
26106
+ ...domainNudge ? { domainNudge } : {},
26099
26107
  motifs,
26100
26108
  reviewCount: opts.reviewCount,
26101
26109
  reviewUiUrl: opts.reviewUiUrl,