@inerrata-corporation/errata 2.0.2-dev.633 → 2.0.2-dev.646

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.
Files changed (2) hide show
  1. package/errata.mjs +119 -35
  2. package/package.json +1 -1
package/errata.mjs CHANGED
@@ -18962,14 +18962,19 @@ function ingestDesignProblem(store, flag, opts) {
18962
18962
  }
18963
18963
  let solutionId = null;
18964
18964
  if (flag.fix?.trim() && !isFragmentFixNote(flag.fix)) {
18965
- solutionId = `dfix_${digest({ problemId, fix: flag.fix })}`.slice(0, 56);
18966
- store.mergeNode(
18967
- buildNode(store, solutionId, "Solution", flag.fix.trim(), opts.ts, {
18968
- source: "convo",
18969
- provisional: true,
18970
- rationale: flag.fix.trim()
18971
- })
18972
- );
18965
+ const reuse = findReusableSolution(store, flag.fix);
18966
+ if (reuse) {
18967
+ solutionId = reuse.id;
18968
+ } else {
18969
+ solutionId = `dfix_${digest({ problemId, fix: flag.fix })}`.slice(0, 56);
18970
+ store.mergeNode(
18971
+ buildNode(store, solutionId, "Solution", flag.fix.trim(), opts.ts, {
18972
+ source: "convo",
18973
+ provisional: true,
18974
+ rationale: flag.fix.trim()
18975
+ })
18976
+ );
18977
+ }
18973
18978
  mergeEdge(store, problemId, solutionId, "SOLVED_BY", opts.ts);
18974
18979
  }
18975
18980
  let anchored = false;
@@ -19177,6 +19182,19 @@ function isFragmentFixNote(note) {
19177
19182
  function isAutoMinted(n) {
19178
19183
  return n.label === "Solution" && String(n.description ?? "").startsWith(AUTO_MINT_PREFIX);
19179
19184
  }
19185
+ function solutionTextKey(text) {
19186
+ return text.trim().replace(/\s+/g, " ").toLowerCase();
19187
+ }
19188
+ function findReusableSolution(store, fixText) {
19189
+ const key = solutionTextKey(fixText);
19190
+ if (!key) return null;
19191
+ for (const s of store.findNodesByLabel("Solution")) {
19192
+ if (s.validTo != null) continue;
19193
+ if (isAutoMinted(s)) continue;
19194
+ if (solutionTextKey(s.description ?? "") === key) return s;
19195
+ }
19196
+ return null;
19197
+ }
19180
19198
  function priorsForFile(store, relPath) {
19181
19199
  const want = relPath.trim().replace(/^\.?\//, "");
19182
19200
  let file2 = null;
@@ -19360,11 +19378,16 @@ function closeDesignProblem(store, problemId, fixNote, t) {
19360
19378
  if (fixNote?.trim() && !isFragmentFixNote(fixNote)) {
19361
19379
  const sols = store.outEdges(problemId, ["SOLVED_BY"]).map((e) => store.getNode(e.to)).filter((n) => n !== null);
19362
19380
  if (sols.every((s) => isAutoMinted(s))) {
19363
- const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
19364
- store.mergeNode(
19365
- buildNode(store, solId, "Solution", fixNote.trim(), t, { source: "convo", provisional: false })
19366
- );
19367
- mergeEdge(store, problemId, solId, "SOLVED_BY", t);
19381
+ const reuse = findReusableSolution(store, fixNote);
19382
+ if (reuse) {
19383
+ mergeEdge(store, problemId, reuse.id, "SOLVED_BY", t);
19384
+ } else {
19385
+ const solId = `dfix_${digest({ problemId, fix: fixNote })}`.slice(0, 56);
19386
+ store.mergeNode(
19387
+ buildNode(store, solId, "Solution", fixNote.trim(), t, { source: "convo", provisional: false })
19388
+ );
19389
+ mergeEdge(store, problemId, solId, "SOLVED_BY", t);
19390
+ }
19368
19391
  }
19369
19392
  }
19370
19393
  if (p.attrs["resolvedAt"]) return false;
@@ -21246,8 +21269,9 @@ function harvestTriageFences(l2, text, opts) {
21246
21269
  );
21247
21270
  if (f.fix) {
21248
21271
  const fix = f.fix.trim();
21249
- const fixId = `dfix_${digest({ cause: causeId, fix })}`.slice(0, 56);
21250
- if (!l2.getNode(fixId)) {
21272
+ const reuse = findReusableSolution(l2, fix);
21273
+ const fixId = reuse ? reuse.id : `dfix_${digest({ cause: causeId, fix })}`.slice(0, 56);
21274
+ if (!reuse && !l2.getNode(fixId)) {
21251
21275
  l2.mergeNode(semNode(l2, fixId, "Solution", fix, opts.ts, { scope: {} }));
21252
21276
  }
21253
21277
  const solEdgeId = `edge_sol_${causeId}_${fixId}`;
@@ -21264,6 +21288,7 @@ var init_triage2 = __esm({
21264
21288
  "../../packages/local-graph/src/triage.ts"() {
21265
21289
  "use strict";
21266
21290
  init_src();
21291
+ init_design_problem();
21267
21292
  init_src();
21268
21293
  init_src();
21269
21294
  init_src4();
@@ -22294,6 +22319,7 @@ __export(src_exports2, {
22294
22319
  findMintTimeDuplicate: () => findMintTimeDuplicate,
22295
22320
  findOrCreatePackage: () => findOrCreatePackage,
22296
22321
  findPath: () => findPath,
22322
+ findReusableSolution: () => findReusableSolution,
22297
22323
  flattenCloudCounts: () => flattenCloudCounts,
22298
22324
  foldDuplicateProblem: () => foldDuplicateProblem,
22299
22325
  formatMechanismStatus: () => formatMechanismStatus,
@@ -27529,10 +27555,15 @@ function buildSymbolLexicon(store, summariesByBodyHash2) {
27529
27555
  }
27530
27556
  return lex;
27531
27557
  }
27558
+ function isBareMatchableStem(stem) {
27559
+ return stem.length >= 5 && /^[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)+$/.test(stem);
27560
+ }
27532
27561
  function buildFileLexicon(store) {
27533
27562
  const byKey = /* @__PURE__ */ new Map();
27534
27563
  const byBasename = /* @__PURE__ */ new Map();
27564
+ const byStem = /* @__PURE__ */ new Map();
27535
27565
  const basenameCount = /* @__PURE__ */ new Map();
27566
+ const stemRelPaths = /* @__PURE__ */ new Map();
27536
27567
  const entries = [];
27537
27568
  for (const n of store.findNodesByLabel("File")) {
27538
27569
  const relPath = typeof n.attrs["relPath"] === "string" && n.attrs["relPath"].length > 0 ? n.attrs["relPath"] : n.description;
@@ -27541,6 +27572,12 @@ function buildFileLexicon(store) {
27541
27572
  if (!basename5.includes(".")) continue;
27542
27573
  entries.push({ relPath, basename: basename5 });
27543
27574
  basenameCount.set(basename5, (basenameCount.get(basename5) ?? 0) + 1);
27575
+ const stem = basename5.slice(0, basename5.indexOf("."));
27576
+ if (isBareMatchableStem(stem)) {
27577
+ let s = stemRelPaths.get(stem);
27578
+ if (!s) stemRelPaths.set(stem, s = /* @__PURE__ */ new Set());
27579
+ s.add(relPath);
27580
+ }
27544
27581
  }
27545
27582
  for (const { relPath, basename: basename5 } of entries) {
27546
27583
  const entry = { relPath };
@@ -27550,8 +27587,18 @@ function buildFileLexicon(store) {
27550
27587
  const list = byBasename.get(basename5);
27551
27588
  if (list) list.push(entry);
27552
27589
  else byBasename.set(basename5, [entry]);
27590
+ const stem = basename5.slice(0, basename5.indexOf("."));
27591
+ if (isBareMatchableStem(stem) && !CONVENTION_STEMS.has(stem) && !byStem.has(stem)) {
27592
+ const dirStems = new Set(
27593
+ [...stemRelPaths.get(stem) ?? []].map((p) => {
27594
+ const bn = p.split("/").pop();
27595
+ return p.slice(0, p.length - bn.length) + bn.slice(0, bn.indexOf("."));
27596
+ })
27597
+ );
27598
+ if (dirStems.size === 1) byStem.set(stem, entry);
27599
+ }
27553
27600
  }
27554
- return { byKey, byBasename };
27601
+ return { byKey, byBasename, byStem };
27555
27602
  }
27556
27603
  function resolveFileToken(token, lexicon) {
27557
27604
  const exact = lexicon.byKey.get(token);
@@ -27582,24 +27629,40 @@ function fileRolePhrase(relPath, level = 1) {
27582
27629
  if (/\.(json|ya?ml|toml|env)$/i.test(relPath)) return "a config file";
27583
27630
  return "a source file";
27584
27631
  }
27585
- function generalizeSymbols(text, lexicon, level = 1, fileLexicon) {
27632
+ function generalizeFileRefs(text, fileLexicon, level = 1) {
27586
27633
  let out2 = text;
27587
- if (fileLexicon) {
27588
- const files = [];
27589
- const seenFiles = /* @__PURE__ */ new Set();
27590
- for (const m of out2.matchAll(FILE_TOKEN_RE)) {
27591
- const tok = m[0];
27592
- if (seenFiles.has(tok)) continue;
27593
- seenFiles.add(tok);
27594
- const entry = resolveFileToken(tok, fileLexicon);
27595
- if (entry) files.push([tok, entry]);
27596
- }
27597
- files.sort((a, b) => b[0].length - a[0].length);
27598
- for (const [tok, entry] of files) {
27599
- const re = new RegExp(`(?<![\\w$/\\\\.-])${escapeRe4(tok)}(?![\\w$-])`, "g");
27600
- out2 = out2.replace(re, fileRolePhrase(entry.relPath, level));
27601
- }
27634
+ const files = [];
27635
+ const seenFiles = /* @__PURE__ */ new Set();
27636
+ for (const m of out2.matchAll(FILE_TOKEN_RE)) {
27637
+ const tok = m[0];
27638
+ if (seenFiles.has(tok)) continue;
27639
+ seenFiles.add(tok);
27640
+ const entry = resolveFileToken(tok, fileLexicon);
27641
+ if (entry) files.push([tok, entry]);
27642
+ }
27643
+ files.sort((a, b) => b[0].length - a[0].length);
27644
+ for (const [tok, entry] of files) {
27645
+ const re = new RegExp(`(?<![\\w$/\\\\.-])${escapeRe4(tok)}(?![\\w$-])`, "g");
27646
+ out2 = out2.replace(re, fileRolePhrase(entry.relPath, level));
27647
+ }
27648
+ const stems = [];
27649
+ const seenStems = /* @__PURE__ */ new Set();
27650
+ for (const m of out2.matchAll(STEM_TOKEN_RE)) {
27651
+ const tok = m[0];
27652
+ if (seenStems.has(tok)) continue;
27653
+ seenStems.add(tok);
27654
+ const entry = fileLexicon.byStem.get(tok);
27655
+ if (entry) stems.push([tok, entry]);
27656
+ }
27657
+ stems.sort((a, b) => b[0].length - a[0].length);
27658
+ for (const [tok, entry] of stems) {
27659
+ const re = new RegExp(`(?<![\\w$/\\\\.-])${escapeRe4(tok)}(?![\\w$/\\\\.-])`, "g");
27660
+ out2 = out2.replace(re, fileRolePhrase(entry.relPath, level));
27602
27661
  }
27662
+ return out2;
27663
+ }
27664
+ function generalizeSymbols(text, lexicon, level = 1, fileLexicon) {
27665
+ let out2 = fileLexicon ? generalizeFileRefs(text, fileLexicon, level) : text;
27603
27666
  const present = [];
27604
27667
  const seen = /* @__PURE__ */ new Set();
27605
27668
  for (const m of out2.matchAll(TOKEN_RE3)) {
@@ -27618,7 +27681,7 @@ function generalizeSymbols(text, lexicon, level = 1, fileLexicon) {
27618
27681
  }
27619
27682
  return generalize(out2, { level }).text;
27620
27683
  }
27621
- var SYMBOL_LABELS2, KIND_PHRASE, RE_RESERVED2, escapeRe4, CONVENTION_FILENAMES, CONVENTION_BASENAME_MIN_FILES, FILE_TOKEN_RE, TOKEN_RE3;
27684
+ var SYMBOL_LABELS2, KIND_PHRASE, RE_RESERVED2, escapeRe4, CONVENTION_FILENAMES, CONVENTION_BASENAME_MIN_FILES, CONVENTION_STEMS, FILE_TOKEN_RE, STEM_TOKEN_RE, TOKEN_RE3;
27622
27685
  var init_generalize_graph = __esm({
27623
27686
  "src/generalize-graph.ts"() {
27624
27687
  "use strict";
@@ -27667,7 +27730,27 @@ var init_generalize_graph = __esm({
27667
27730
  "index.mjs"
27668
27731
  ]);
27669
27732
  CONVENTION_BASENAME_MIN_FILES = 3;
27733
+ CONVENTION_STEMS = /* @__PURE__ */ new Set([
27734
+ "well-known",
27735
+ "read-only",
27736
+ "type-safe",
27737
+ "end-to-end",
27738
+ "fail-open",
27739
+ "fail-closed",
27740
+ "up-to-date",
27741
+ "server-side",
27742
+ "client-side",
27743
+ "cross-origin",
27744
+ "same-origin",
27745
+ "well-formed",
27746
+ "first-class",
27747
+ "in-memory",
27748
+ "single-page",
27749
+ "use-case",
27750
+ "out-of-band"
27751
+ ]);
27670
27752
  FILE_TOKEN_RE = /(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_-][A-Za-z0-9_.-]*\.[A-Za-z0-9]+/g;
27753
+ STEM_TOKEN_RE = /[A-Za-z0-9]+(?:[-_][A-Za-z0-9]+)+/g;
27671
27754
  TOKEN_RE3 = /[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
27672
27755
  }
27673
27756
  });
@@ -54908,7 +54991,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
54908
54991
  }
54909
54992
 
54910
54993
  // src/engine.ts
54911
- var DAEMON_VERSION = true ? "2.0.2-dev.633" : "2.0.0-alpha.0";
54994
+ var DAEMON_VERSION = true ? "2.0.2-dev.646" : "2.0.0-alpha.0";
54912
54995
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
54913
54996
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
54914
54997
  var GIT_OP_MUTE_MS = 4e3;
@@ -57274,7 +57357,8 @@ function buildInstanceIngest(store, profile, daemonVersion, ignorePatterns = [],
57274
57357
  const fileLexicon = buildFileLexicon(store);
57275
57358
  const shareable = (n) => {
57276
57359
  const preserved = symbolIndex ? preservedSymbolsFor(n.description, symbolIndex) : [];
57277
- const shippedText = generalize(n.description, { level, project: projectIdentity }).text;
57360
+ const fileScrubbed = generalizeFileRefs(n.description, fileLexicon, level);
57361
+ const shippedText = generalize(fileScrubbed, { level, project: projectIdentity }).text;
57278
57362
  const internalSyms = internalSymbolsFor(shippedText, (t) => ownLexicon.has(t), symbolIndex);
57279
57363
  const internalFiles = internalFilesFor(shippedText, fileLexicon);
57280
57364
  const internal = [.../* @__PURE__ */ new Set([...internalFiles, ...internalSyms])].slice(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.633",
3
+ "version": "2.0.2-dev.646",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {