@inerrata-corporation/errata 2.0.2-dev.437 → 2.0.2-dev.455

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.
@@ -16684,6 +16684,10 @@ init_src2();
16684
16684
  // ../../packages/local-graph/src/problem-package-link.ts
16685
16685
  init_src();
16686
16686
 
16687
+ // ../../packages/local-graph/src/problem-dedup.ts
16688
+ init_src();
16689
+ init_src();
16690
+
16687
16691
  // ../../packages/local-graph/src/anchor-backfill.ts
16688
16692
  init_src();
16689
16693
  init_src2();
@@ -17421,10 +17425,6 @@ function revisitContradictedPrinciples(l2, ts) {
17421
17425
  // ../../packages/local-graph/src/community.ts
17422
17426
  init_src2();
17423
17427
 
17424
- // ../../packages/local-graph/src/problem-dedup.ts
17425
- init_src();
17426
- init_src();
17427
-
17428
17428
  // ../../packages/local-graph/src/tools.ts
17429
17429
  init_src();
17430
17430
 
package/errata.mjs CHANGED
@@ -18562,6 +18562,132 @@ var init_problem_package_link = __esm({
18562
18562
  }
18563
18563
  });
18564
18564
 
18565
+ // ../../packages/local-graph/src/problem-dedup.ts
18566
+ function corroborations(n) {
18567
+ return Number(n.attrs["corroborations"] ?? 0);
18568
+ }
18569
+ function overlap(a, b) {
18570
+ if (a.size === 0 || b.size === 0) return 0;
18571
+ let inter = 0;
18572
+ for (const x of a) if (b.has(x)) inter++;
18573
+ return inter / Math.min(a.size, b.size);
18574
+ }
18575
+ function mergeDuplicateProblems(store, opts) {
18576
+ const minOverlap = opts.minOverlap ?? 0.85;
18577
+ const minTokens = opts.minTokens ?? 4;
18578
+ const report = { clusters: 0, merged: 0 };
18579
+ const open2 = store.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
18580
+ const tokens = /* @__PURE__ */ new Map();
18581
+ for (const p of open2) tokens.set(p.id, new Set(conceptTokens(p.description)));
18582
+ const consumed = /* @__PURE__ */ new Set();
18583
+ store.transaction(() => {
18584
+ for (let i2 = 0; i2 < open2.length; i2++) {
18585
+ const a = open2[i2];
18586
+ if (consumed.has(a.id)) continue;
18587
+ const cluster = [a];
18588
+ const ta = tokens.get(a.id);
18589
+ for (let j = i2 + 1; j < open2.length; j++) {
18590
+ const b = open2[j];
18591
+ if (consumed.has(b.id)) continue;
18592
+ const tb = tokens.get(b.id);
18593
+ if (Math.min(ta.size, tb.size) < minTokens) continue;
18594
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
18595
+ if (overlap(ta, tb) >= minOverlap) {
18596
+ cluster.push(b);
18597
+ consumed.add(b.id);
18598
+ }
18599
+ }
18600
+ if (cluster.length < 2) continue;
18601
+ report.clusters++;
18602
+ cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
18603
+ const survivor = cluster[0];
18604
+ for (const dup of cluster.slice(1)) {
18605
+ foldDuplicateProblem(store, dup, survivor, opts.ts);
18606
+ report.merged++;
18607
+ }
18608
+ }
18609
+ });
18610
+ return report;
18611
+ }
18612
+ function foldDuplicateProblem(store, dup, survivor, ts) {
18613
+ const surv = store.getNode(survivor.id);
18614
+ if (!surv) return;
18615
+ const sources = new Set(surv.attrs["sources"] ?? []);
18616
+ for (const s of dup.attrs["sources"] ?? []) sources.add(s);
18617
+ store.updateNode(survivor.id, {
18618
+ attrs: {
18619
+ ...surv.attrs,
18620
+ sources: [...sources],
18621
+ corroborations: corroborations(surv) + corroborations(dup) + 1
18622
+ },
18623
+ cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
18624
+ lastUpdatedAt: ts
18625
+ });
18626
+ for (const e of store.outEdges(dup.id, REDIRECT_EDGES)) {
18627
+ if (e.to === survivor.id) continue;
18628
+ const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
18629
+ if (store.getEdge(id)) continue;
18630
+ const redirected = {
18631
+ ...e,
18632
+ id,
18633
+ from: survivor.id,
18634
+ createdAt: ts,
18635
+ lastSeenAt: ts
18636
+ };
18637
+ store.mergeEdge(redirected);
18638
+ }
18639
+ store.updateNode(dup.id, {
18640
+ attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
18641
+ lastUpdatedAt: ts
18642
+ });
18643
+ store.closeNode(dup.id, ts);
18644
+ }
18645
+ function statementIdentityKey(statement) {
18646
+ return statement.toLowerCase().replace(/[`"'“”‘’*_]/g, "").replace(/[—–]/g, "-").replace(/\s+/g, " ").trim().replace(/[.,;:!?]+$/, "");
18647
+ }
18648
+ function findMintTimeDuplicate(store, statement, kind, opts) {
18649
+ const minOverlap = opts?.minOverlap ?? 0.85;
18650
+ const minTokens = opts?.minTokens ?? 4;
18651
+ const incomingIsConstraint = kind === "constraint";
18652
+ const open2 = store.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
18653
+ if (open2.length === 0) return null;
18654
+ const key = statementIdentityKey(statement);
18655
+ for (const p of open2) {
18656
+ if (isConstraintProblem(p) !== incomingIsConstraint) continue;
18657
+ if (statementIdentityKey(p.description) === key) return p.id;
18658
+ }
18659
+ const ta = new Set(conceptTokens(statement));
18660
+ if (ta.size < minTokens) return null;
18661
+ let best = null;
18662
+ for (const p of open2) {
18663
+ if (isConstraintProblem(p) !== incomingIsConstraint) continue;
18664
+ const tb = new Set(conceptTokens(p.description));
18665
+ if (Math.min(ta.size, tb.size) < minTokens) continue;
18666
+ const score2 = overlap(ta, tb);
18667
+ if (score2 >= minOverlap && (best === null || score2 > best.score)) {
18668
+ best = { id: p.id, score: score2 };
18669
+ }
18670
+ }
18671
+ return best?.id ?? null;
18672
+ }
18673
+ var REDIRECT_EDGES;
18674
+ var init_problem_dedup = __esm({
18675
+ "../../packages/local-graph/src/problem-dedup.ts"() {
18676
+ "use strict";
18677
+ init_src();
18678
+ init_src();
18679
+ init_design_problem();
18680
+ REDIRECT_EDGES = [
18681
+ "CAUSED_BY",
18682
+ "SOLVED_BY",
18683
+ "FIXED_BY",
18684
+ "ANCHORED_AT",
18685
+ "MANIFESTED_IN",
18686
+ "EVIDENCED_BY"
18687
+ ];
18688
+ }
18689
+ });
18690
+
18565
18691
  // ../../packages/local-graph/src/design-problem.ts
18566
18692
  function isConstraintProblem(node2) {
18567
18693
  return node2.attrs["kind"] === "constraint";
@@ -18644,7 +18770,9 @@ function ingestDesignProblem(store, flag, opts) {
18644
18770
  rejected: true
18645
18771
  };
18646
18772
  }
18647
- const problemId = identityId({ kind: "DesignProblem", statement });
18773
+ const rawId = identityId({ kind: "DesignProblem", statement });
18774
+ const dupId = store.getNode(rawId) ? null : findMintTimeDuplicate(store, statement, flag.kind);
18775
+ const problemId = dupId ?? rawId;
18648
18776
  const existing = store.getNode(problemId);
18649
18777
  let created = false;
18650
18778
  let corroborated = false;
@@ -18659,7 +18787,18 @@ function ingestDesignProblem(store, flag, opts) {
18659
18787
  ...existing.attrs,
18660
18788
  sources: [...sources, opts.source],
18661
18789
  corroborations: corroborations2,
18662
- ...promoted ? { provisional: false } : {}
18790
+ ...promoted ? { provisional: false } : {},
18791
+ // Keep the variant wording when a restatement resolved here rather
18792
+ // than minting — audit trail, and input for a future judge tier.
18793
+ ...dupId && statement !== existing.description ? {
18794
+ statementAliases: [
18795
+ ...(existing.attrs["statementAliases"] ?? []).slice(
18796
+ 0,
18797
+ STATEMENT_ALIAS_CAP - 1
18798
+ ),
18799
+ statement
18800
+ ]
18801
+ } : {}
18663
18802
  },
18664
18803
  cumulativeHits: (existing.cumulativeHits ?? 0) + 1,
18665
18804
  ...promoted ? { extractionConfidence: 0.65 } : {},
@@ -19133,7 +19272,7 @@ function markFixCandidates(store, t) {
19133
19272
  }
19134
19273
  return resolved;
19135
19274
  }
19136
- var DESIGN_PROMOTE_AT, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
19275
+ var DESIGN_PROMOTE_AT, STATEMENT_ALIAS_CAP, PATTERN_DEDUP_COSINE, PATTERN_ALIAS_CAP, DEDUP_STOPWORDS, SAME_ANCHOR_DEDUP_JACCARD, CITABLE_PRIOR_LABELS, MAX_ANCHOR_FILES, AUTO_MINT_PREFIX;
19137
19276
  var init_design_problem = __esm({
19138
19277
  "../../packages/local-graph/src/design-problem.ts"() {
19139
19278
  "use strict";
@@ -19142,7 +19281,9 @@ var init_design_problem = __esm({
19142
19281
  init_src3();
19143
19282
  init_src2();
19144
19283
  init_problem_package_link();
19284
+ init_problem_dedup();
19145
19285
  DESIGN_PROMOTE_AT = 1;
19286
+ STATEMENT_ALIAS_CAP = 5;
19146
19287
  PATTERN_DEDUP_COSINE = 0.85;
19147
19288
  PATTERN_ALIAS_CAP = 5;
19148
19289
  DEDUP_STOPWORDS = /* @__PURE__ */ new Set([
@@ -21222,104 +21363,6 @@ var init_community2 = __esm({
21222
21363
  }
21223
21364
  });
21224
21365
 
21225
- // ../../packages/local-graph/src/problem-dedup.ts
21226
- function corroborations(n) {
21227
- return Number(n.attrs["corroborations"] ?? 0);
21228
- }
21229
- function overlap(a, b) {
21230
- if (a.size === 0 || b.size === 0) return 0;
21231
- let inter = 0;
21232
- for (const x of a) if (b.has(x)) inter++;
21233
- return inter / Math.min(a.size, b.size);
21234
- }
21235
- function mergeDuplicateProblems(store, opts) {
21236
- const minOverlap = opts.minOverlap ?? 0.85;
21237
- const minTokens = opts.minTokens ?? 4;
21238
- const report = { clusters: 0, merged: 0 };
21239
- const open2 = store.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
21240
- const tokens = /* @__PURE__ */ new Map();
21241
- for (const p of open2) tokens.set(p.id, new Set(conceptTokens(p.description)));
21242
- const consumed = /* @__PURE__ */ new Set();
21243
- store.transaction(() => {
21244
- for (let i2 = 0; i2 < open2.length; i2++) {
21245
- const a = open2[i2];
21246
- if (consumed.has(a.id)) continue;
21247
- const cluster = [a];
21248
- const ta = tokens.get(a.id);
21249
- for (let j = i2 + 1; j < open2.length; j++) {
21250
- const b = open2[j];
21251
- if (consumed.has(b.id)) continue;
21252
- const tb = tokens.get(b.id);
21253
- if (Math.min(ta.size, tb.size) < minTokens) continue;
21254
- if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
21255
- if (overlap(ta, tb) >= minOverlap) {
21256
- cluster.push(b);
21257
- consumed.add(b.id);
21258
- }
21259
- }
21260
- if (cluster.length < 2) continue;
21261
- report.clusters++;
21262
- cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
21263
- const survivor = cluster[0];
21264
- for (const dup of cluster.slice(1)) {
21265
- foldDuplicateProblem(store, dup, survivor, opts.ts);
21266
- report.merged++;
21267
- }
21268
- }
21269
- });
21270
- return report;
21271
- }
21272
- function foldDuplicateProblem(store, dup, survivor, ts) {
21273
- const surv = store.getNode(survivor.id);
21274
- if (!surv) return;
21275
- const sources = new Set(surv.attrs["sources"] ?? []);
21276
- for (const s of dup.attrs["sources"] ?? []) sources.add(s);
21277
- store.updateNode(survivor.id, {
21278
- attrs: {
21279
- ...surv.attrs,
21280
- sources: [...sources],
21281
- corroborations: corroborations(surv) + corroborations(dup) + 1
21282
- },
21283
- cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
21284
- lastUpdatedAt: ts
21285
- });
21286
- for (const e of store.outEdges(dup.id, REDIRECT_EDGES)) {
21287
- if (e.to === survivor.id) continue;
21288
- const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
21289
- if (store.getEdge(id)) continue;
21290
- const redirected = {
21291
- ...e,
21292
- id,
21293
- from: survivor.id,
21294
- createdAt: ts,
21295
- lastSeenAt: ts
21296
- };
21297
- store.mergeEdge(redirected);
21298
- }
21299
- store.updateNode(dup.id, {
21300
- attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
21301
- lastUpdatedAt: ts
21302
- });
21303
- store.closeNode(dup.id, ts);
21304
- }
21305
- var REDIRECT_EDGES;
21306
- var init_problem_dedup = __esm({
21307
- "../../packages/local-graph/src/problem-dedup.ts"() {
21308
- "use strict";
21309
- init_src();
21310
- init_src();
21311
- init_design_problem();
21312
- REDIRECT_EDGES = [
21313
- "CAUSED_BY",
21314
- "SOLVED_BY",
21315
- "FIXED_BY",
21316
- "ANCHORED_AT",
21317
- "MANIFESTED_IN",
21318
- "EVIDENCED_BY"
21319
- ];
21320
- }
21321
- });
21322
-
21323
21366
  // ../../packages/local-graph/src/tools.ts
21324
21367
  function toolNodeId(name2) {
21325
21368
  return `tool:${name2}`;
@@ -21559,6 +21602,7 @@ __export(src_exports2, {
21559
21602
  detectLocalCommunities: () => detectLocalCommunities,
21560
21603
  edgeConductance: () => edgeConductance,
21561
21604
  evaluateDependency: () => evaluateDependency,
21605
+ findMintTimeDuplicate: () => findMintTimeDuplicate,
21562
21606
  findOrCreatePackage: () => findOrCreatePackage,
21563
21607
  findPath: () => findPath,
21564
21608
  flattenCloudCounts: () => flattenCloudCounts,
@@ -21623,6 +21667,7 @@ __export(src_exports2, {
21623
21667
  solutionForProblem: () => solutionForProblem,
21624
21668
  splittingAxis: () => splittingAxis,
21625
21669
  stampObservedOs: () => stampObservedOs,
21670
+ statementIdentityKey: () => statementIdentityKey,
21626
21671
  staticConductance: () => staticConductance,
21627
21672
  toolNodeId: () => toolNodeId,
21628
21673
  toolPriorsFor: () => toolPriorsFor,
@@ -26177,7 +26222,7 @@ function buildAgentInstruction(opts = {}) {
26177
26222
  const ref = opts.referent ?? "its-handle";
26178
26223
  const token = (s) => s === "problem" ? TAG_EXAMPLE.problem() : s === "constraint" ? TAG_EXAMPLE.constraint() : s === "attempt" ? TAG_EXAMPLE.tried() : s === "domain" ? TAG_EXAMPLE.domain() : s === "link" ? TAG_EXAMPLE.pattern() : TAG_EXAMPLE[s](ref);
26179
26224
  const head2 = `We tag the priors we show you with a short handle like [${opts.handleExample ?? "chokidar-glob"}]. Tags are the ONLY signal recorded \u2014 anything you state but don't tag is silently lost. Weave each tag inline in your prose as you state it (no fences or backticks, no extra calls); tag everything, we filter downstream:`;
26180
- const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 RESTATE every tag in your turn-final message; the final message is what reliably survives.";
26225
+ const tail = "Mid-turn text (between tool calls) can be dropped by the harness \u2014 RESTATE every tag in your turn-final message; the final message is what reliably survives \u2014 EXCEPT one already shown above with an id: that is captured, so cite `[its-handle]` rather than retyping it (drift mints a duplicate).";
26181
26226
  return [
26182
26227
  head2,
26183
26228
  ...signals.map(
@@ -53453,7 +53498,7 @@ function startLoopLagMonitor(thresholdMs = 1e3) {
53453
53498
  }
53454
53499
 
53455
53500
  // src/engine.ts
53456
- var DAEMON_VERSION = true ? "2.0.2-dev.437" : "2.0.0-alpha.0";
53501
+ var DAEMON_VERSION = true ? "2.0.2-dev.455" : "2.0.0-alpha.0";
53457
53502
  var IGNORED_PATH = /[\\/](?:\.git|node_modules|\.errata|\.claude|\.codex|\.cursor|\.turbo|\.next|dist|coverage|test-results|playwright-report|__pycache__)(?:[\\/]|$)/;
53458
53503
  var IGNORED_NOISE = /castalia\.db|eventlog\.sqlite|turn-cursors/;
53459
53504
  var GIT_OP_MUTE_MS = 4e3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inerrata-corporation/errata",
3
- "version": "2.0.2-dev.437",
3
+ "version": "2.0.2-dev.455",
4
4
  "description": "errata - local-first observation engine for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
package/pass-worker.mjs CHANGED
@@ -25316,6 +25316,97 @@ function backfillProblemContext(store2, ts) {
25316
25316
  return report;
25317
25317
  }
25318
25318
 
25319
+ // ../../packages/local-graph/src/problem-dedup.ts
25320
+ init_src();
25321
+ init_src();
25322
+ var REDIRECT_EDGES = [
25323
+ "CAUSED_BY",
25324
+ "SOLVED_BY",
25325
+ "FIXED_BY",
25326
+ "ANCHORED_AT",
25327
+ "MANIFESTED_IN",
25328
+ "EVIDENCED_BY"
25329
+ ];
25330
+ function corroborations(n) {
25331
+ return Number(n.attrs["corroborations"] ?? 0);
25332
+ }
25333
+ function overlap(a, b) {
25334
+ if (a.size === 0 || b.size === 0) return 0;
25335
+ let inter = 0;
25336
+ for (const x of a) if (b.has(x)) inter++;
25337
+ return inter / Math.min(a.size, b.size);
25338
+ }
25339
+ function mergeDuplicateProblems(store2, opts) {
25340
+ const minOverlap = opts.minOverlap ?? 0.85;
25341
+ const minTokens = opts.minTokens ?? 4;
25342
+ const report = { clusters: 0, merged: 0 };
25343
+ const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
25344
+ const tokens = /* @__PURE__ */ new Map();
25345
+ for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
25346
+ const consumed = /* @__PURE__ */ new Set();
25347
+ store2.transaction(() => {
25348
+ for (let i2 = 0; i2 < open.length; i2++) {
25349
+ const a = open[i2];
25350
+ if (consumed.has(a.id)) continue;
25351
+ const cluster = [a];
25352
+ const ta = tokens.get(a.id);
25353
+ for (let j = i2 + 1; j < open.length; j++) {
25354
+ const b = open[j];
25355
+ if (consumed.has(b.id)) continue;
25356
+ const tb = tokens.get(b.id);
25357
+ if (Math.min(ta.size, tb.size) < minTokens) continue;
25358
+ if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
25359
+ if (overlap(ta, tb) >= minOverlap) {
25360
+ cluster.push(b);
25361
+ consumed.add(b.id);
25362
+ }
25363
+ }
25364
+ if (cluster.length < 2) continue;
25365
+ report.clusters++;
25366
+ cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
25367
+ const survivor = cluster[0];
25368
+ for (const dup of cluster.slice(1)) {
25369
+ foldDuplicateProblem(store2, dup, survivor, opts.ts);
25370
+ report.merged++;
25371
+ }
25372
+ }
25373
+ });
25374
+ return report;
25375
+ }
25376
+ function foldDuplicateProblem(store2, dup, survivor, ts) {
25377
+ const surv = store2.getNode(survivor.id);
25378
+ if (!surv) return;
25379
+ const sources = new Set(surv.attrs["sources"] ?? []);
25380
+ for (const s of dup.attrs["sources"] ?? []) sources.add(s);
25381
+ store2.updateNode(survivor.id, {
25382
+ attrs: {
25383
+ ...surv.attrs,
25384
+ sources: [...sources],
25385
+ corroborations: corroborations(surv) + corroborations(dup) + 1
25386
+ },
25387
+ cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
25388
+ lastUpdatedAt: ts
25389
+ });
25390
+ for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
25391
+ if (e.to === survivor.id) continue;
25392
+ const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
25393
+ if (store2.getEdge(id)) continue;
25394
+ const redirected = {
25395
+ ...e,
25396
+ id,
25397
+ from: survivor.id,
25398
+ createdAt: ts,
25399
+ lastSeenAt: ts
25400
+ };
25401
+ store2.mergeEdge(redirected);
25402
+ }
25403
+ store2.updateNode(dup.id, {
25404
+ attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
25405
+ lastUpdatedAt: ts
25406
+ });
25407
+ store2.closeNode(dup.id, ts);
25408
+ }
25409
+
25319
25410
  // ../../packages/local-graph/src/design-problem.ts
25320
25411
  function isConstraintProblem(node) {
25321
25412
  return node.attrs["kind"] === "constraint";
@@ -25952,97 +26043,6 @@ var DRIFT_VALUES = new Set(Object.values(DRIFT_KIND));
25952
26043
  // ../../packages/local-graph/src/community.ts
25953
26044
  init_src2();
25954
26045
 
25955
- // ../../packages/local-graph/src/problem-dedup.ts
25956
- init_src();
25957
- init_src();
25958
- var REDIRECT_EDGES = [
25959
- "CAUSED_BY",
25960
- "SOLVED_BY",
25961
- "FIXED_BY",
25962
- "ANCHORED_AT",
25963
- "MANIFESTED_IN",
25964
- "EVIDENCED_BY"
25965
- ];
25966
- function corroborations(n) {
25967
- return Number(n.attrs["corroborations"] ?? 0);
25968
- }
25969
- function overlap(a, b) {
25970
- if (a.size === 0 || b.size === 0) return 0;
25971
- let inter = 0;
25972
- for (const x of a) if (b.has(x)) inter++;
25973
- return inter / Math.min(a.size, b.size);
25974
- }
25975
- function mergeDuplicateProblems(store2, opts) {
25976
- const minOverlap = opts.minOverlap ?? 0.85;
25977
- const minTokens = opts.minTokens ?? 4;
25978
- const report = { clusters: 0, merged: 0 };
25979
- const open = store2.findNodesByLabel("Problem").filter((p) => p.attrs["resolvedAt"] == null && p.attrs["mergedInto"] === void 0);
25980
- const tokens = /* @__PURE__ */ new Map();
25981
- for (const p of open) tokens.set(p.id, new Set(conceptTokens(p.description)));
25982
- const consumed = /* @__PURE__ */ new Set();
25983
- store2.transaction(() => {
25984
- for (let i2 = 0; i2 < open.length; i2++) {
25985
- const a = open[i2];
25986
- if (consumed.has(a.id)) continue;
25987
- const cluster = [a];
25988
- const ta = tokens.get(a.id);
25989
- for (let j = i2 + 1; j < open.length; j++) {
25990
- const b = open[j];
25991
- if (consumed.has(b.id)) continue;
25992
- const tb = tokens.get(b.id);
25993
- if (Math.min(ta.size, tb.size) < minTokens) continue;
25994
- if (isConstraintProblem(a) !== isConstraintProblem(b)) continue;
25995
- if (overlap(ta, tb) >= minOverlap) {
25996
- cluster.push(b);
25997
- consumed.add(b.id);
25998
- }
25999
- }
26000
- if (cluster.length < 2) continue;
26001
- report.clusters++;
26002
- cluster.sort((x, y) => corroborations(y) - corroborations(x) || x.createdAt - y.createdAt);
26003
- const survivor = cluster[0];
26004
- for (const dup of cluster.slice(1)) {
26005
- foldDuplicateProblem(store2, dup, survivor, opts.ts);
26006
- report.merged++;
26007
- }
26008
- }
26009
- });
26010
- return report;
26011
- }
26012
- function foldDuplicateProblem(store2, dup, survivor, ts) {
26013
- const surv = store2.getNode(survivor.id);
26014
- if (!surv) return;
26015
- const sources = new Set(surv.attrs["sources"] ?? []);
26016
- for (const s of dup.attrs["sources"] ?? []) sources.add(s);
26017
- store2.updateNode(survivor.id, {
26018
- attrs: {
26019
- ...surv.attrs,
26020
- sources: [...sources],
26021
- corroborations: corroborations(surv) + corroborations(dup) + 1
26022
- },
26023
- cumulativeHits: (surv.cumulativeHits ?? 0) + (dup.cumulativeHits ?? 0),
26024
- lastUpdatedAt: ts
26025
- });
26026
- for (const e of store2.outEdges(dup.id, REDIRECT_EDGES)) {
26027
- if (e.to === survivor.id) continue;
26028
- const id = `edge_${digest({ from: survivor.id, type: e.type, to: e.to })}`.slice(0, 24);
26029
- if (store2.getEdge(id)) continue;
26030
- const redirected = {
26031
- ...e,
26032
- id,
26033
- from: survivor.id,
26034
- createdAt: ts,
26035
- lastSeenAt: ts
26036
- };
26037
- store2.mergeEdge(redirected);
26038
- }
26039
- store2.updateNode(dup.id, {
26040
- attrs: { ...dup.attrs, mergedInto: survivor.id, resolvedAs: "duplicate", resolvedAt: ts },
26041
- lastUpdatedAt: ts
26042
- });
26043
- store2.closeNode(dup.id, ts);
26044
- }
26045
-
26046
26046
  // ../../packages/local-graph/src/tools.ts
26047
26047
  init_src();
26048
26048
  function rankToolsForHandles(store2, limit = 5) {