@polycode-projects/the-mechanical-code-talker 2.5.2 → 2.5.4

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 (91) hide show
  1. package/README.md +11 -7
  2. package/bin/tmct.mjs +7 -4
  3. package/corpus/LICENSES.json +7 -0
  4. package/corpus/reference/LICENSE-NOTICE +36 -0
  5. package/corpus/reference/README.md +19 -0
  6. package/corpus/reference/index.json.gz +0 -0
  7. package/corpus/reference/manifest.json +367 -0
  8. package/corpus/reference/shards/ref-00.jsonl.gz +0 -0
  9. package/corpus/reference/shards/ref-01.jsonl.gz +0 -0
  10. package/corpus/reference/shards/ref-02.jsonl.gz +0 -0
  11. package/corpus/reference/shards/ref-03.jsonl.gz +0 -0
  12. package/corpus/reference/shards/ref-04.jsonl.gz +0 -0
  13. package/corpus/reference/shards/ref-05.jsonl.gz +0 -0
  14. package/corpus/reference/shards/ref-06.jsonl.gz +0 -0
  15. package/corpus/reference/shards/ref-07.jsonl.gz +0 -0
  16. package/corpus/reference/shards/ref-08.jsonl.gz +0 -0
  17. package/corpus/reference/shards/ref-09.jsonl.gz +0 -0
  18. package/corpus/reference/shards/ref-0a.jsonl.gz +0 -0
  19. package/corpus/reference/shards/ref-0b.jsonl.gz +0 -0
  20. package/corpus/reference/shards/ref-0c.jsonl.gz +0 -0
  21. package/corpus/reference/shards/ref-0d.jsonl.gz +0 -0
  22. package/corpus/reference/shards/ref-0e.jsonl.gz +0 -0
  23. package/corpus/reference/shards/ref-0f.jsonl.gz +0 -0
  24. package/corpus/reference/shards/ref-10.jsonl.gz +0 -0
  25. package/corpus/reference/shards/ref-11.jsonl.gz +0 -0
  26. package/corpus/reference/shards/ref-12.jsonl.gz +0 -0
  27. package/corpus/reference/shards/ref-13.jsonl.gz +0 -0
  28. package/corpus/reference/shards/ref-14.jsonl.gz +0 -0
  29. package/corpus/reference/shards/ref-15.jsonl.gz +0 -0
  30. package/corpus/reference/shards/ref-16.jsonl.gz +0 -0
  31. package/corpus/reference/shards/ref-17.jsonl.gz +0 -0
  32. package/corpus/reference/shards/ref-18.jsonl.gz +0 -0
  33. package/corpus/reference/shards/ref-19.jsonl.gz +0 -0
  34. package/corpus/reference/shards/ref-1a.jsonl.gz +0 -0
  35. package/corpus/reference/shards/ref-1b.jsonl.gz +0 -0
  36. package/corpus/reference/shards/ref-1c.jsonl.gz +0 -0
  37. package/corpus/reference/shards/ref-1d.jsonl.gz +0 -0
  38. package/corpus/reference/shards/ref-1e.jsonl.gz +0 -0
  39. package/corpus/reference/shards/ref-1f.jsonl.gz +0 -0
  40. package/corpus/reference/shards/ref-20.jsonl.gz +0 -0
  41. package/corpus/reference/shards/ref-21.jsonl.gz +0 -0
  42. package/corpus/reference/shards/ref-22.jsonl.gz +0 -0
  43. package/corpus/reference/shards/ref-23.jsonl.gz +0 -0
  44. package/corpus/reference/shards/ref-24.jsonl.gz +0 -0
  45. package/corpus/reference/shards/ref-25.jsonl.gz +0 -0
  46. package/corpus/reference/shards/ref-26.jsonl.gz +0 -0
  47. package/corpus/reference/shards/ref-27.jsonl.gz +0 -0
  48. package/corpus/reference/shards/ref-28.jsonl.gz +0 -0
  49. package/corpus/reference/shards/ref-29.jsonl.gz +0 -0
  50. package/corpus/reference/shards/ref-2a.jsonl.gz +0 -0
  51. package/corpus/reference/shards/ref-2b.jsonl.gz +0 -0
  52. package/corpus/reference/shards/ref-2c.jsonl.gz +0 -0
  53. package/corpus/reference/shards/ref-2d.jsonl.gz +0 -0
  54. package/corpus/reference/shards/ref-2e.jsonl.gz +0 -0
  55. package/corpus/reference/shards/ref-2f.jsonl.gz +0 -0
  56. package/corpus/reference/shards/ref-30.jsonl.gz +0 -0
  57. package/corpus/reference/shards/ref-31.jsonl.gz +0 -0
  58. package/corpus/reference/shards/ref-32.jsonl.gz +0 -0
  59. package/corpus/reference/shards/ref-33.jsonl.gz +0 -0
  60. package/corpus/reference/shards/ref-34.jsonl.gz +0 -0
  61. package/corpus/reference/shards/ref-35.jsonl.gz +0 -0
  62. package/corpus/reference/shards/ref-36.jsonl.gz +0 -0
  63. package/corpus/reference/shards/ref-37.jsonl.gz +0 -0
  64. package/corpus/reference/shards/ref-38.jsonl.gz +0 -0
  65. package/corpus/reference/shards/ref-39.jsonl.gz +0 -0
  66. package/corpus/reference/shards/ref-3a.jsonl.gz +0 -0
  67. package/corpus/reference/shards/ref-3b.jsonl.gz +0 -0
  68. package/corpus/reference/shards/ref-3c.jsonl.gz +0 -0
  69. package/corpus/reference/shards/ref-3d.jsonl.gz +0 -0
  70. package/corpus/reference/shards/ref-3e.jsonl.gz +0 -0
  71. package/corpus/reference/shards/ref-3f.jsonl.gz +0 -0
  72. package/package.json +5 -1
  73. package/src/adapters/corpus/reference-pack.mjs +113 -0
  74. package/src/adapters/memory/core.mjs +104 -10
  75. package/src/domain/ask.mjs +66 -9
  76. package/src/domain/cli-verbs.mjs +3 -3
  77. package/src/domain/codegraph.mjs +6 -0
  78. package/src/domain/dialogue-acts.mjs +160 -0
  79. package/src/domain/interpret/normalize.mjs +14 -2
  80. package/src/domain/interpret/strategies/keywords.mjs +10 -1
  81. package/src/domain/memory/trust.mjs +12 -0
  82. package/src/domain/reference-pack.mjs +72 -0
  83. package/src/domain/skos-view.mjs +111 -0
  84. package/src/domain/syllogise.mjs +647 -56
  85. package/src/services/chat.mjs +937 -142
  86. package/src/services/index.mjs +4 -1
  87. package/src/surfaces/web/chat-browser-entry.mjs +87 -0
  88. package/src/surfaces/web/memory-ask-browser.bundle.js +1187 -85
  89. package/src/tools/definitions.mjs +13 -0
  90. package/src/tools/handlers/index.mjs +2 -0
  91. package/src/tools/handlers/tmct-related.mjs +30 -0
@@ -975,6 +975,12 @@
975
975
  function provenanceTagToSource(tag) {
976
976
  const t = String(tag || "").trim();
977
977
  if (!t) return null;
978
+ if (t.startsWith("reference:")) {
979
+ const rest = t.slice("reference:".length);
980
+ const colon = rest.indexOf(":");
981
+ if (colon < 0) return { kind: "reference", pack: rest || "unknown", article: "" };
982
+ return { kind: "reference", pack: rest.slice(0, colon) || "unknown", article: rest.slice(colon + 1) };
983
+ }
978
984
  const head = t.split(/\s+/)[0];
979
985
  if (head.startsWith("corpus-weak:")) return { kind: "corpusWeak", name: head.slice("corpus-weak:".length) || "unknown" };
980
986
  if (head.startsWith("corpus:")) return { kind: "corpus", name: head.slice("corpus:".length) || "unknown" };
@@ -1056,6 +1062,7 @@
1056
1062
  teach: 0.95,
1057
1063
  provider: 0.9,
1058
1064
  corpus: 0.7,
1065
+ reference: 0.6,
1059
1066
  corpusWeak: 0.55,
1060
1067
  web: 0.4,
1061
1068
  extracted: 0.45,
@@ -1346,6 +1353,7 @@
1346
1353
  if (!subjModId || !objModId) continue;
1347
1354
  const subjLabel = graph.byId.get(subjModId)?.label || subjModId;
1348
1355
  addDependent(objModId, subjModId, subjLabel, g.predicate);
1356
+ if (subjModId !== objModId) addDependent(e.object, subjModId, subjLabel, g.predicate);
1349
1357
  }
1350
1358
  } else if (kind === "tests") {
1351
1359
  for (const e of g.edges) {
@@ -2875,8 +2883,8 @@ ${shown.join("\n")}${tail}`;
2875
2883
  EXPLAIN_WRAPPER_RE = /^explain\s+(?:to\s+me\s+|please\s+)*(.+?)\??$/i;
2876
2884
  TELL_ME_WRAPPER_RE = /^tell\s+me\s+(.+?)\??$/i;
2877
2885
  KNOW_WRAPPER_RE = /^do\s+you\s+know\s+(.+?)\??$/i;
2878
- WANT_KNOW_WRAPPER_RE = /^i(?:'d|\s+would)?\s+(?:like|want|need)\s+to\s+know\s+(.+?)\??$/i;
2879
- WONDERING_WRAPPER_RE = /^i(?:\s+was|\s+am|'m)?\s+(?:just\s+)?(?:wonder(?:ing|ed)|curious)(?:\s+(?:if|whether|about))?\s+(.+?)\??$/i;
2886
+ WANT_KNOW_WRAPPER_RE = /^i(?:'d|\s+would)?\s+(?:(?:like|want|need)\s+to|wanna)\s+know\s+(.+?)\??$/i;
2887
+ WONDERING_WRAPPER_RE = /^i(?:\s+was|\s+am|'m)?\s+(?:just\s+)?(?:wonder(?:ing|ed)?|curious)(?:\s+(?:if|whether|about))?\s+(.+?)\??$/i;
2880
2888
  EMBEDDED_WHATIS_RE = /^what\s+((?:an?\s+|the\s+)?[\w'-]+(?:\s+[\w'-]+){0,2})\s+(is|are)\??$/i;
2881
2889
  EMBEDDED_MEANS_RE = /^what\s+((?:an?\s+|the\s+)?[\w'-]+(?:\s+[\w'-]+){0,2})\s+means\??$/i;
2882
2890
  SHOW_GIVE_ME_RE = /^(?:show|give)\s+me\s+(?:the\s+)?(.+?)\??$/i;
@@ -2992,6 +3000,17 @@ ${shown.join("\n")}${tail}`;
2992
3000
  { re: /^(?:is|are)\s+(?!.*\bnot\b)(.+?)\s+tested\??$/i, to: (m) => `what tests ${m[1]}` },
2993
3001
  // NEEDS-TESTS → the untested-module survey.
2994
3002
  { re: /^what\s+needs\s+(?:to\s+be\s+)?(?:a\s+)?(?:tested|tests?|testing|coverage|covering)\??$/i, to: () => "untested modules" },
3003
+ // NO-TESTS → the same untested survey, attributive form. "no tests" would
3004
+ // otherwise read as a literal object term ("…defines no tests") and the
3005
+ // resulting empty answers as "it has none" to a coverage question.
3006
+ {
3007
+ re: /^(?:which|what|list|show(?:\s+me)?|find)?\s*(?:the\s+|all\s+)?([a-z][a-z-]*?)\s+(?:(?:that|which)\s+)?(?:have|has|having|with)\s+no\s+tests?\??$/i,
3008
+ to: (m) => `untested ${m[1].toLowerCase()}`
3009
+ },
3010
+ {
3011
+ re: /^(?:which|what|list|show(?:\s+me)?|find)?\s*(?:the\s+|all\s+)?([a-z][a-z-]*?)\s+without\s+(?:any\s+)?tests?\??$/i,
3012
+ to: (m) => `untested ${m[1].toLowerCase()}`
3013
+ },
2995
3014
  // DOES-X-VERB-ANYTHING-ELSE → "what does X <verb>" (drops the placeholder
2996
3015
  // "anything/something else" object, which otherwise made the two parse
2997
3016
  // strategies disagree on the span). Anchored to VERB_TO_KIND so it can't
@@ -3410,7 +3429,10 @@ ${shown.join("\n")}${tail}`;
3410
3429
  if (INHERITS_REVERSE_VERBS.includes(verbPhrase)) [subject, object] = [object, subject];
3411
3430
  return stamp({ shape: "ask", entityType: null, modifier: "direct", kind, subject, object });
3412
3431
  }
3413
- if (afterText) return stamp({ shape: "reverse", entityType, modifier, kind, object: afterText });
3432
+ if (afterText) {
3433
+ const resultType = entityHit && entityHit.start >= verbHit.end ? null : entityType;
3434
+ return stamp({ shape: "reverse", entityType: resultType, modifier, kind, object: afterText });
3435
+ }
3414
3436
  if (kind === "inherits" && !beforeText && entityHit && entityHit.start === verbHit.end) {
3415
3437
  const entityText = canonWords.slice(entityHit.start, entityHit.end).join(" ");
3416
3438
  if (entityText) return stamp({ shape: "reverse", entityType: null, modifier, kind, object: entityText });
@@ -3855,7 +3877,9 @@ ${shown.join("\n")}${tail}`;
3855
3877
  return classes;
3856
3878
  }
3857
3879
  function kindObjectClass(graph, kind) {
3858
- const classes = classesForKinds(graph, kindsFor(kind));
3880
+ const kinds = kindsFor(kind);
3881
+ const coarse = kinds.filter((k) => !SYMBOL_GRAIN_KINDS.has(k));
3882
+ const classes = classesForKinds(graph, coarse.length ? coarse : kinds);
3859
3883
  return classes.size === 1 ? [...classes][0] : null;
3860
3884
  }
3861
3885
  function parseSetPhrase(text, nlp, depth) {
@@ -4233,7 +4257,9 @@ ${shown.join("\n")}${tail}`;
4233
4257
  if (!entityType) return { node: "miss", reason: "a superlative needs an entity kind (module, class, function, \u2026)" };
4234
4258
  }
4235
4259
  if (!metric) return { node: "miss", reason: "name what to rank by (imports, callers, methods, tests, or connections)" };
4236
- return { node: "superlative", entityType, metric, metricNoun, extreme: ext };
4260
+ const NEED_LACK_WORDS = ["needs", "need", "needing", "lacks", "lack", "lacking", "misses", "missing"];
4261
+ const extreme = lc.some((x) => NEED_LACK_WORDS.includes(x)) ? ext === "most" ? "fewest" : "most" : ext;
4262
+ return { node: "superlative", entityType, metric, metricNoun, extreme };
4237
4263
  }
4238
4264
  function parseFind(w, lc, nlp, depth) {
4239
4265
  if (lc[0] !== "find") return null;
@@ -5426,7 +5452,20 @@ ${shown.join("\n")}${tail}`;
5426
5452
  return out;
5427
5453
  }
5428
5454
  function declineOnUnplacedWords(result, term) {
5429
- if (!result?.match || result.ambiguous || result.tier !== 3 || result.matchedVia) return result;
5455
+ if (!result?.match || result.tier !== 3 || result.matchedVia) return result;
5456
+ if (result.ambiguous) {
5457
+ const pool = [result.match, ...result.candidates || []];
5458
+ const shared = pool.map((m) => new Set(unplacedTermWords(term, m.label))).reduce((acc, s) => acc.filter((w) => s.has(w)), unplacedTermWords(term, pool[0].label));
5459
+ if (!shared.length) return result;
5460
+ return {
5461
+ match: null,
5462
+ candidates: [],
5463
+ tier: null,
5464
+ ambiguous: false,
5465
+ unplacedWords: shared,
5466
+ nearestLabel: listJoin(pool.slice(0, 4).map((m) => m.label))
5467
+ };
5468
+ }
5430
5469
  const unplaced = unplacedTermWords(term, result.match.label);
5431
5470
  if (!unplaced.length) return result;
5432
5471
  return {
@@ -5774,7 +5813,7 @@ ${shown.join("\n")}${tail}`;
5774
5813
  traversal: `reverse dependency closure over imports+calls edges from ${objMatch.label} (impactClosure, maxDepth=${TRANSITIVE_MAX_DEPTH})`
5775
5814
  };
5776
5815
  }
5777
- const symbolKind = SYMBOL_GRAIN_SIBLING[kind];
5816
+ const symbolKind = SYMBOL_GRAIN_SIBLING[kind] || kindsFor(kind).find((k) => SYMBOL_GRAIN_KINDS.has(k)) || null;
5778
5817
  const objIsFineSymbol = !!(objMatch.class && FINE_ENTITY_TYPES.has(objMatch.class));
5779
5818
  if (symbolKind && (FINE_ENTITY_TYPES.has(entityType) || objIsFineSymbol)) {
5780
5819
  const edges2 = edgesOfKind2(graph, symbolKind).filter((e) => e.object === objMatch.id);
@@ -5789,7 +5828,7 @@ ${shown.join("\n")}${tail}`;
5789
5828
  widenNote = `, widened to ${siblingClass} subjects (no ${entityType} recorded)`;
5790
5829
  }
5791
5830
  }
5792
- const upRefineEligible = (kind === "touches" || kind === "calls") && objMatch.class === "Class" && !edgesOfKind2(graph, "contains").some((e) => e.subject === objMatch.id);
5831
+ const upRefineEligible = (kind === "touches" || kind === "calls" || kind === "uses") && objMatch.class === "Class" && !edgesOfKind2(graph, "contains").some((e) => e.subject === objMatch.id);
5793
5832
  const upRefineModule = upRefineEligible ? graph.byId.get(moduleIdOf2(graph, objMatch) || "") : null;
5794
5833
  if (matches2.length || !(upRefineEligible && upRefineModule)) {
5795
5834
  return { matches: matches2, objMatch, candidates, traversal: `${symbolKind} edges where object = ${objMatch.label}${widenNote}`, ambiguous, matchedVia };
@@ -6224,6 +6263,14 @@ ${result.branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.co
6224
6263
  }
6225
6264
  if (!result.matches.length) {
6226
6265
  if (parsed.shape === "forward") {
6266
+ if (MEMBERSHIP_KINDS.includes(parsed.kind) && graph && result.objMatch) {
6267
+ for (const alt of MEMBERSHIP_KINDS) {
6268
+ if (alt === parsed.kind) continue;
6269
+ const altParsed = { ...parsed, kind: alt };
6270
+ const altResult = traverse(graph, altParsed, { pinnedObjMatch: result.objMatch });
6271
+ if (altResult?.matches?.length && !altResult.ambiguous) return renderCore(altParsed, altResult, graph);
6272
+ }
6273
+ }
6227
6274
  return {
6228
6275
  content: `${result.objMatch.label} has no ${verbFor(parsed.kind)} edges in the index.`,
6229
6276
  miss: true,
@@ -6265,7 +6312,7 @@ ${result.branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.co
6265
6312
  return { content: clauses.join(" and ") + extra + ".", miss: false, ambiguous: false, matches: result.matches };
6266
6313
  }
6267
6314
  function fuzzyCascadeWord(w) {
6268
- if (w.length < 4) return null;
6315
+ if (w.length < 4 || CASCADE_FUZZY_REAL_WORDS.has(w)) return null;
6269
6316
  const bound = fuzzyBound(w);
6270
6317
  let best = bound + 1;
6271
6318
  let hit2 = null;
@@ -6582,7 +6629,7 @@ ${lines.join("\n")}`;
6582
6629
  }
6583
6630
  };
6584
6631
  }
6585
- var askEdgesOfKindCache, SYMBOL_GRAIN_SIBLING, FINE_ENTITY_TYPES, FINE_CLASS_SIBLING, KIND_UNIONS, kindsFor, OVERFLOW_CAP, PLURAL_FORMS, REVERSE_MISS_VERB, PLURAL_SUBJECT_VERB, pluralVerbFor, LEADING_RELATION_VERB_RE, FROZEN_META_AMBIGUOUS_TERMS, MAX_COMPOSE_DEPTH, NEST_SENTINEL, PRED_LEAD_SKIP, FRAME_WORDS, COPULA_WORDS, entityNoun, isGerundVerb, UNIVERSAL_DET, FWD_NEG_FRAME, PLURAL_ANAPHORA_OBJECT, TEMPORAL_AUX, TEMPORAL_TAIL, TEMPORAL_TRAIL_FILLER, TEMPORAL_DET, COMMIT_FILTER_OPS, ANAPHORA_NAME_TOKEN_RE, AGG_TAIL_FILLER, LIST_SKIP, LIST_TRIGGERS_SORTED, LISTABLE_KINDS, SCOPE_PREPOSITIONS, FIND_LINKERS, RECENT_COMMIT_LEAD, qualCache, META_FALLBACK_CLASSES, inheritsApplicableCache, FIND_TIER, oppositeQualifierSpec, DEGREE_KINDS, COMMIT_FILTER_DATE_RE, compositeList, LEADING_ARTICLE_RE, TRAILING_GRAIN_WORD_RE, ENTRY_POINT_QUERY_RE, ENTRY_POINT_BASENAMES, TEST_FIXTURE_PATH_SEGMENTS, moduleStemOf, isTestFixturePath, TRANSITIVE_MAX_DEPTH, CONTENT_VOCAB, STRUCTURAL_WORDS, CASCADE_NOISE_SET, NOISE_OR_SCAFFOLD, TRIGGER_FUZZY_WORDS, CASCADE_FUZZY_TARGETS, LAST_COMMIT_PHRASE_RE, BARE_WHEN_COMMIT_RE, DYNAMIC_LIST_TRIGGER_RE, DYNAMIC_COUNT_TRIGGER_RE, DYNAMIC_TAIL_OK_RE, BARE_META_WHATIS_RE, WHATIS_FOR_FALLBACK_RE;
6632
+ var askEdgesOfKindCache, SYMBOL_GRAIN_SIBLING, FINE_ENTITY_TYPES, FINE_CLASS_SIBLING, KIND_UNIONS, kindsFor, SYMBOL_GRAIN_KINDS, OVERFLOW_CAP, PLURAL_FORMS, REVERSE_MISS_VERB, PLURAL_SUBJECT_VERB, pluralVerbFor, LEADING_RELATION_VERB_RE, FROZEN_META_AMBIGUOUS_TERMS, MAX_COMPOSE_DEPTH, NEST_SENTINEL, PRED_LEAD_SKIP, FRAME_WORDS, COPULA_WORDS, entityNoun, isGerundVerb, UNIVERSAL_DET, FWD_NEG_FRAME, PLURAL_ANAPHORA_OBJECT, TEMPORAL_AUX, TEMPORAL_TAIL, TEMPORAL_TRAIL_FILLER, TEMPORAL_DET, COMMIT_FILTER_OPS, ANAPHORA_NAME_TOKEN_RE, AGG_TAIL_FILLER, LIST_SKIP, LIST_TRIGGERS_SORTED, LISTABLE_KINDS, SCOPE_PREPOSITIONS, FIND_LINKERS, RECENT_COMMIT_LEAD, qualCache, META_FALLBACK_CLASSES, inheritsApplicableCache, FIND_TIER, oppositeQualifierSpec, DEGREE_KINDS, COMMIT_FILTER_DATE_RE, compositeList, LEADING_ARTICLE_RE, TRAILING_GRAIN_WORD_RE, ENTRY_POINT_QUERY_RE, ENTRY_POINT_BASENAMES, TEST_FIXTURE_PATH_SEGMENTS, moduleStemOf, isTestFixturePath, TRANSITIVE_MAX_DEPTH, CONTENT_VOCAB, STRUCTURAL_WORDS, CASCADE_NOISE_SET, NOISE_OR_SCAFFOLD, TRIGGER_FUZZY_WORDS, CASCADE_FUZZY_TARGETS, CASCADE_FUZZY_REAL_WORDS, LAST_COMMIT_PHRASE_RE, BARE_WHEN_COMMIT_RE, DYNAMIC_LIST_TRIGGER_RE, DYNAMIC_COUNT_TRIGGER_RE, DYNAMIC_TAIL_OK_RE, BARE_META_WHATIS_RE, WHATIS_FOR_FALLBACK_RE;
6586
6633
  var init_ask = __esm({
6587
6634
  "src/domain/ask.mjs"() {
6588
6635
  init_codegraph();
@@ -6603,6 +6650,7 @@ ${lines.join("\n")}`;
6603
6650
  FINE_CLASS_SIBLING = { Function: "Method", Method: "Function" };
6604
6651
  KIND_UNIONS = { uses: ["imports", "calls", "callsSymbol"] };
6605
6652
  kindsFor = (kind) => KIND_UNIONS[kind] || [kind];
6653
+ SYMBOL_GRAIN_KINDS = new Set(Object.values(SYMBOL_GRAIN_SIBLING));
6606
6654
  OVERFLOW_CAP = 12;
6607
6655
  PLURAL_FORMS = {
6608
6656
  Function: ["function", "functions"],
@@ -6752,6 +6800,7 @@ ${lines.join("\n")}`;
6752
6800
  ...Object.keys(ENTITY_TO_TYPE),
6753
6801
  ...TRIGGER_FUZZY_WORDS
6754
6802
  ])].filter((wd) => /^[a-z]+$/.test(wd) && wd.length >= 4 && !STOPWORDS2.has(wd));
6803
+ CASCADE_FUZZY_REAL_WORDS = /* @__PURE__ */ new Set(["impact", "impacts", "impacted"]);
6755
6804
  LAST_COMMIT_PHRASE_RE = /\b(?:the\s+)?(?:last|latest|most\s+recent)\s+commit\b/i;
6756
6805
  BARE_WHEN_COMMIT_RE = /^when\s+(?:was|were|is|did|does|do)\s+commit\s+[0-9a-fA-F:]+$/i;
6757
6806
  DYNAMIC_LIST_TRIGGER_RE = /^(?:list|show(?:\s+me)?)\s+(?:all\s+|the\s+)?([a-z][a-z'-]*)\s*(.*)$/i;
@@ -7599,6 +7648,7 @@ ${bodyText}` : graphText, tier });
7599
7648
  CAX_MAXC0_RULE_CONFIDENCE: () => CAX_MAXC0_RULE_CONFIDENCE,
7600
7649
  CLS_SVF1_RULE: () => CLS_SVF1_RULE,
7601
7650
  CLS_SVF1_RULE_CONFIDENCE: () => CLS_SVF1_RULE_CONFIDENCE,
7651
+ DEFAULT_MAX_ENVIRONMENTS: () => DEFAULT_MAX_ENVIRONMENTS,
7602
7652
  DISJOINT_PREDICATE: () => DISJOINT_PREDICATE,
7603
7653
  ENTAILED_DISJOINT_PROVENANCE: () => ENTAILED_DISJOINT_PROVENANCE,
7604
7654
  ENTAILED_PROVENANCE: () => ENTAILED_PROVENANCE,
@@ -7612,10 +7662,12 @@ ${bodyText}` : graphText, tier });
7612
7662
  SUBCLASS_PREDICATE: () => SUBCLASS_PREDICATE,
7613
7663
  TYPE_PREDICATE: () => TYPE_PREDICATE,
7614
7664
  buildCardinalityRestrictions: () => buildCardinalityRestrictions,
7665
+ buildRelevanceFrontier: () => buildRelevanceFrontier,
7615
7666
  deriveDisjointViolations: () => deriveDisjointViolations,
7616
7667
  deriveSomeValuesFromApplication: () => deriveSomeValuesFromApplication,
7617
7668
  deriveSomeValuesFromSubsumption: () => deriveSomeValuesFromSubsumption,
7618
7669
  deriveSubClassClosure: () => deriveSubClassClosure,
7670
+ deriveSubClassClosureDelta: () => deriveSubClassClosureDelta,
7619
7671
  deriveTypePropagation: () => deriveTypePropagation,
7620
7672
  entailedTrustFrom: () => entailedTrustFrom,
7621
7673
  findConsistencyViolations: () => findConsistencyViolations,
@@ -7693,6 +7745,65 @@ ${bodyText}` : graphText, tier });
7693
7745
  }
7694
7746
  return derived;
7695
7747
  }
7748
+ function deriveSubClassClosureDelta(allEdges, deltaEdges, { depth = 32, budget = 50, focus = null } = {}) {
7749
+ const present = /* @__PURE__ */ new Set();
7750
+ const succ = /* @__PURE__ */ new Map();
7751
+ const pred = /* @__PURE__ */ new Map();
7752
+ for (const [a, b] of allEdges || []) {
7753
+ if (!a || !b || a === b) continue;
7754
+ present.add(`${a}${SEP}${b}`);
7755
+ if (!succ.has(a)) succ.set(a, /* @__PURE__ */ new Set());
7756
+ succ.get(a).add(b);
7757
+ if (!pred.has(b)) pred.set(b, /* @__PURE__ */ new Set());
7758
+ pred.get(b).add(a);
7759
+ }
7760
+ let delta = [];
7761
+ const seenDelta = /* @__PURE__ */ new Set();
7762
+ for (const [a, b] of deltaEdges || []) {
7763
+ if (!a || !b || a === b) continue;
7764
+ const key = `${a}${SEP}${b}`;
7765
+ if (seenDelta.has(key)) continue;
7766
+ seenDelta.add(key);
7767
+ delta.push([a, b]);
7768
+ }
7769
+ const focusSet = focus instanceof Set ? focus.size ? focus : null : normalizeFocus(focus);
7770
+ const inFocus = (a, b, c) => !focusSet || focusSet.has(a) || focusSet.has(b) || focusSet.has(c);
7771
+ const derived = [];
7772
+ const derivedKeys = /* @__PURE__ */ new Set();
7773
+ for (let round2 = 0; round2 < depth && delta.length; round2 += 1) {
7774
+ const additions = [];
7775
+ const consider = (a, b, c) => {
7776
+ if (a === c) return;
7777
+ const key = `${a}${SEP}${c}`;
7778
+ if (present.has(key) || derivedKeys.has(key)) return;
7779
+ if (!inFocus(a, b, c)) return;
7780
+ additions.push([a, b, c, key]);
7781
+ };
7782
+ for (const [a, b] of delta) {
7783
+ for (const c of succ.get(b) || []) consider(a, b, c);
7784
+ for (const z of pred.get(a) || []) consider(z, a, b);
7785
+ }
7786
+ if (!additions.length) break;
7787
+ additions.sort((x, y) => x[0].localeCompare(y[0]) || x[2].localeCompare(y[2]) || x[1].localeCompare(y[1]));
7788
+ let progressed = false;
7789
+ const nextDelta = [];
7790
+ for (const [a, b, c, key] of additions) {
7791
+ if (derivedKeys.has(key)) continue;
7792
+ if (derived.length >= budget) break;
7793
+ derivedKeys.add(key);
7794
+ derived.push({ subject: a, object: c, via: b });
7795
+ if (!succ.has(a)) succ.set(a, /* @__PURE__ */ new Set());
7796
+ succ.get(a).add(c);
7797
+ if (!pred.has(c)) pred.set(c, /* @__PURE__ */ new Set());
7798
+ pred.get(c).add(a);
7799
+ nextDelta.push([a, c]);
7800
+ progressed = true;
7801
+ }
7802
+ if (derived.length >= budget || !progressed) break;
7803
+ delta = nextDelta;
7804
+ }
7805
+ return derived;
7806
+ }
7696
7807
  function buildAncestorCloser(subClassEdges) {
7697
7808
  const succ = /* @__PURE__ */ new Map();
7698
7809
  for (const [a, b] of subClassEdges || []) {
@@ -7715,11 +7826,41 @@ ${bodyText}` : graphText, tier });
7715
7826
  return seen;
7716
7827
  };
7717
7828
  }
7718
- function deriveTypePropagation(typeEdges, subClassEdges, { budget = 50, focus = null } = {}) {
7829
+ function buildDescendantCloser(subClassEdges) {
7830
+ return buildAncestorCloser((subClassEdges || []).map(([a, b]) => [b, a]));
7831
+ }
7832
+ function buildRelevanceFrontier(rows, seedTerms) {
7833
+ const subClassEdges = [];
7834
+ const typeEdges = [];
7835
+ const onPropertyOf = /* @__PURE__ */ new Map();
7836
+ const someValuesFromOf = /* @__PURE__ */ new Map();
7837
+ for (const r of rows || []) {
7838
+ if (!r || !r.subject || !r.predicate || !r.object) continue;
7839
+ if (isSubClassOf(r.predicate)) subClassEdges.push([r.subject, r.object]);
7840
+ else if (isType(r.predicate)) typeEdges.push([r.subject, r.object]);
7841
+ else if (isOnProperty(r.predicate)) onPropertyOf.set(r.subject, r.object);
7842
+ else if (isSomeValuesFrom(r.predicate)) someValuesFromOf.set(r.subject, r.object);
7843
+ }
7844
+ const descendantsOf2 = buildDescendantCloser(subClassEdges);
7845
+ const affectedClasses = /* @__PURE__ */ new Set();
7846
+ for (const t of seedTerms || []) {
7847
+ const n = normFactTerm(t);
7848
+ if (!n) continue;
7849
+ affectedClasses.add(n);
7850
+ for (const d of descendantsOf2(n)) affectedClasses.add(d);
7851
+ }
7852
+ const frontier = new Set(affectedClasses);
7853
+ for (const [x, c] of typeEdges) if (affectedClasses.has(c)) frontier.add(x);
7854
+ for (const [restriction, target] of someValuesFromOf) {
7855
+ if (onPropertyOf.has(restriction) && affectedClasses.has(target)) frontier.add(restriction);
7856
+ }
7857
+ return frontier;
7858
+ }
7859
+ function deriveTypePropagation(typeEdges, subClassEdges, { budget = 50, focus = null, presentTypeEdges = typeEdges } = {}) {
7719
7860
  const ancestorsOf2 = buildAncestorCloser(subClassEdges);
7720
7861
  const present = /* @__PURE__ */ new Set();
7721
7862
  const seenTypeEdge = /* @__PURE__ */ new Set();
7722
- for (const [x, c] of typeEdges || []) if (x && c) present.add(`${x}${SEP}${c}`);
7863
+ for (const [x, c] of presentTypeEdges || []) if (x && c) present.add(`${x}${SEP}${c}`);
7723
7864
  const focusSet = focus instanceof Set ? focus.size ? focus : null : normalizeFocus(focus);
7724
7865
  const inFocus = (x, c, d) => !focusSet || focusSet.has(x) || focusSet.has(c) || focusSet.has(d);
7725
7866
  const candidates = [];
@@ -8015,8 +8156,17 @@ ${bodyText}` : graphText, tier });
8015
8156
  }
8016
8157
  return derived;
8017
8158
  }
8018
- async function syllogise(repoDir, { depth = 32, budget = 50, focus = null, store } = {}) {
8159
+ async function syllogise(repoDir, {
8160
+ depth = 32,
8161
+ budget = 50,
8162
+ focus = null,
8163
+ expandFocus = false,
8164
+ maxEnvironments = DEFAULT_MAX_ENVIRONMENTS,
8165
+ full = false,
8166
+ store
8167
+ } = {}) {
8019
8168
  const { loadMemory: loadMemory2, readFactRows: readFactRows2, appendFacts: appendFacts2 } = requireStore(store, ["loadMemory", "readFactRows", "appendFacts"], "syllogise");
8169
+ const stateFnsPresent = typeof store?.loadSyllogiseState === "function" && typeof store?.saveSyllogiseState === "function";
8020
8170
  const memory = await loadMemory2(repoDir);
8021
8171
  const rows = readFactRows2(memory);
8022
8172
  const subClassEdges = rows.filter((r) => isSubClassOf(r.predicate)).map((r) => [r.subject, r.object]);
@@ -8036,22 +8186,64 @@ ${bodyText}` : graphText, tier });
8036
8186
  const target = someValuesFromOf.get(restriction);
8037
8187
  if (target) restrictionEdges.push({ restriction, property, target });
8038
8188
  }
8039
- const normalizedFocus = normalizeFocus(focus);
8189
+ const callerFocus = normalizeFocus(focus);
8190
+ const normalizedFocus = expandFocus && callerFocus ? buildRelevanceFrontier(rows, [...callerFocus]) : callerFocus;
8191
+ const state = normalizedFocus === null && stateFnsPresent ? await store.loadSyllogiseState(repoDir) : null;
8192
+ const currentIdSet = new Set(rows.map((r) => r.id));
8193
+ const removedSinceLast = Array.isArray(state?.factIds) ? state.factIds.filter((id) => !currentIdSet.has(id)) : [];
8194
+ const mode = state && Array.isArray(state.factIds) && !removedSinceLast.length && !full ? "delta" : "full";
8195
+ const watermark = mode === "delta" ? new Set(state.factIds) : null;
8196
+ const deltaRows = mode === "delta" ? rows.filter((r) => !watermark.has(r.id)) : rows;
8197
+ const deltaSize = deltaRows.length;
8040
8198
  const trustByTriple = /* @__PURE__ */ new Map();
8041
8199
  for (const r of rows) trustByTriple.set(`${r.subject}${SEP}${r.predicate}${SEP}${r.object}`, r.trust);
8042
8200
  const premiseTrust = (s, p, o) => trustByTriple.get(`${s}${SEP}${p}${SEP}${o}`);
8043
8201
  const hasTriple = (s, p, o) => trustByTriple.has(`${s}${SEP}${p}${SEP}${o}`);
8044
8202
  const numericOnly = (arr) => arr.filter((t) => typeof t === "number");
8045
- const scmDerived = deriveSubClassClosure(subClassEdges, { depth, budget, focus: normalizedFocus });
8203
+ const deltaEmpty = mode === "delta" && !deltaRows.length;
8204
+ const deltaSubEdges = mode === "delta" ? deltaRows.filter((r) => isSubClassOf(r.predicate)).map((r) => [r.subject, r.object]) : [];
8205
+ const scmDerived = mode === "delta" ? deltaSubEdges.length ? deriveSubClassClosureDelta(subClassEdges, deltaSubEdges, { depth, budget, focus: normalizedFocus }) : [] : deriveSubClassClosure(subClassEdges, { depth, budget, focus: normalizedFocus });
8046
8206
  const enlargedSubClassEdges = subClassEdges.concat(scmDerived.map((d) => [d.subject, d.object]));
8207
+ let kernelFocus = normalizedFocus;
8208
+ let frontier = null;
8209
+ if (mode === "delta" && !deltaEmpty) {
8210
+ const frontierRows = rows.concat(scmDerived.map((d) => ({ subject: d.subject, predicate: SUBCLASS_PREDICATE, object: d.object })));
8211
+ const seeds = [];
8212
+ for (const r of deltaRows) seeds.push(r.subject, r.object);
8213
+ frontier = buildRelevanceFrontier(frontierRows, seeds);
8214
+ kernelFocus = frontier;
8215
+ }
8216
+ const inFrontier = (t) => !frontier || frontier.has(t);
8217
+ const caxTypeEdges = frontier ? typeEdges.filter(([x, c]) => inFrontier(x) || inFrontier(c)) : typeEdges;
8218
+ const deltaDwEndpoints = /* @__PURE__ */ new Set();
8219
+ if (frontier) {
8220
+ for (const r of deltaRows) {
8221
+ if (isDisjoint(r.predicate)) {
8222
+ deltaDwEndpoints.add(r.subject);
8223
+ deltaDwEndpoints.add(r.object);
8224
+ }
8225
+ }
8226
+ }
8227
+ const dwAncestorsOf = frontier && deltaDwEndpoints.size ? buildAncestorCloser(enlargedSubClassEdges) : null;
8228
+ const dwTypeEdges = frontier ? typeEdges.filter(([x, c]) => inFrontier(x) || inFrontier(c) || dwAncestorsOf && [...dwAncestorsOf(c)].some((a) => deltaDwEndpoints.has(a))) : typeEdges;
8229
+ const deltaRestrictionProperties = /* @__PURE__ */ new Set();
8230
+ if (frontier) {
8231
+ for (const r of deltaRows) {
8232
+ if (isOnProperty(r.predicate) || isSomeValuesFrom(r.predicate)) {
8233
+ const property = onPropertyOf.get(r.subject);
8234
+ if (property) deltaRestrictionProperties.add(normFactTerm(property));
8235
+ }
8236
+ }
8237
+ }
8238
+ const svf1PropertyEdges = frontier ? propertyEdges.filter(([x, p, y]) => inFrontier(x) || inFrontier(y) || deltaRestrictionProperties.has(normFactTerm(p))) : propertyEdges;
8047
8239
  const remainingBudget = Math.max(0, budget - scmDerived.length);
8048
- const caxDerived = remainingBudget > 0 ? deriveTypePropagation(typeEdges, enlargedSubClassEdges, { budget: remainingBudget, focus: normalizedFocus }) : [];
8240
+ const caxDerived = remainingBudget > 0 && !deltaEmpty ? deriveTypePropagation(caxTypeEdges, enlargedSubClassEdges, { budget: remainingBudget, focus: kernelFocus, presentTypeEdges: typeEdges }) : [];
8049
8241
  const remainingBudgetDw = Math.max(0, budget - scmDerived.length - caxDerived.length);
8050
- const dwDerived = remainingBudgetDw > 0 ? deriveDisjointViolations(typeEdges, enlargedSubClassEdges, disjointEdges, { budget: remainingBudgetDw, focus: normalizedFocus }) : [];
8242
+ const dwDerived = remainingBudgetDw > 0 && !deltaEmpty ? deriveDisjointViolations(dwTypeEdges, enlargedSubClassEdges, disjointEdges, { budget: remainingBudgetDw, focus: kernelFocus }) : [];
8051
8243
  const remainingBudgetSvf1 = Math.max(0, budget - scmDerived.length - caxDerived.length - dwDerived.length);
8052
- const svf1Derived = remainingBudgetSvf1 > 0 && restrictionEdges.length ? deriveSomeValuesFromApplication(propertyEdges, typeEdges, enlargedSubClassEdges, restrictionEdges, { budget: remainingBudgetSvf1, focus: normalizedFocus }) : [];
8244
+ const svf1Derived = remainingBudgetSvf1 > 0 && restrictionEdges.length && !deltaEmpty ? deriveSomeValuesFromApplication(svf1PropertyEdges, typeEdges, enlargedSubClassEdges, restrictionEdges, { budget: remainingBudgetSvf1, focus: kernelFocus }) : [];
8053
8245
  const remainingBudgetScmSvf = Math.max(0, budget - scmDerived.length - caxDerived.length - dwDerived.length - svf1Derived.length);
8054
- const scmSvfDerived = remainingBudgetScmSvf > 0 && restrictionEdges.length > 1 ? deriveSomeValuesFromSubsumption(restrictionEdges, enlargedSubClassEdges, { budget: remainingBudgetScmSvf, focus: normalizedFocus }) : [];
8246
+ const scmSvfDerived = remainingBudgetScmSvf > 0 && restrictionEdges.length > 1 && !deltaEmpty ? deriveSomeValuesFromSubsumption(restrictionEdges, enlargedSubClassEdges, { budget: remainingBudgetScmSvf, focus: kernelFocus }) : [];
8055
8247
  const restrictionByRid = new Map(restrictionEdges.map((r) => [r.restriction, r]));
8056
8248
  const toWrite = [
8057
8249
  ...scmDerived.map((d) => ({
@@ -8059,16 +8251,18 @@ ${bodyText}` : graphText, tier });
8059
8251
  predicate: SUBCLASS_PREDICATE,
8060
8252
  object: d.object,
8061
8253
  provenance: ENTAILED_PROVENANCE,
8062
- // Persisted justification: the premise fact ids this conclusion rode
8063
- // (a⊑b, b⊑c) content-addressed ids work even when a premise is
8064
- // itself an entailment this same pass just derived. Read back by
8254
+ // Persisted justification: one environment per independent derivation,
8255
+ // each an ordered premise fact-id list this first one is the premise
8256
+ // set the conclusion rode (a⊑b, b⊑c); the alternate-discovery step
8257
+ // below may append more. Content-addressed ids work even when a premise
8258
+ // is itself an entailment this same pass just derived. Read back by
8065
8259
  // retractSubClassOf (below) to find every entailment a retracted
8066
8260
  // premise could have supported. All five rules persist one, each
8067
8261
  // citing its own premise shape.
8068
- justification: [
8262
+ justification: [[
8069
8263
  factIdForTriple(d.subject, SUBCLASS_PREDICATE, d.via),
8070
8264
  factIdForTriple(d.via, SUBCLASS_PREDICATE, d.object)
8071
- ]
8265
+ ]]
8072
8266
  })),
8073
8267
  ...caxDerived.map((d) => ({
8074
8268
  subject: d.subject,
@@ -8079,10 +8273,10 @@ ${bodyText}` : graphText, tier });
8079
8273
  // taught chain is multi-hop: scm-sco materialises that edge (this same
8080
8274
  // pass or an earlier one), and retraction re-VERIFIES every candidate
8081
8275
  // anyway, so a citation left dangling by budget truncation is inert.
8082
- justification: [
8276
+ justification: [[
8083
8277
  factIdForTriple(d.subject, TYPE_PREDICATE, d.via),
8084
8278
  factIdForTriple(d.via, SUBCLASS_PREDICATE, d.object)
8085
- ]
8279
+ ]]
8086
8280
  })),
8087
8281
  ...dwDerived.map((d) => {
8088
8282
  const dwStoredForward = hasTriple(d.viaClass, DISJOINT_PREDICATE, d.object);
@@ -8099,11 +8293,11 @@ ${bodyText}` : graphText, tier });
8099
8293
  predicate: DISJOINT_PREDICATE,
8100
8294
  object: d.object,
8101
8295
  provenance: ENTAILED_DISJOINT_PROVENANCE,
8102
- justification: [
8296
+ justification: [[
8103
8297
  factIdForTriple(d.subject, TYPE_PREDICATE, d.viaType),
8104
8298
  factIdForTriple(dwS, DISJOINT_PREDICATE, dwO),
8105
8299
  ...d.viaClass !== d.viaType ? [factIdForTriple(d.viaType, SUBCLASS_PREDICATE, d.viaClass)] : []
8106
- ],
8300
+ ]],
8107
8301
  ...premiseTrusts.length ? { premiseTrusts, ruleConfidence: CAX_DW_RULE_CONFIDENCE } : {}
8108
8302
  };
8109
8303
  }),
@@ -8122,13 +8316,13 @@ ${bodyText}` : graphText, tier });
8122
8316
  predicate: TYPE_PREDICATE,
8123
8317
  object: d.object,
8124
8318
  provenance: ENTAILED_SVF1_PROVENANCE,
8125
- justification: [
8319
+ justification: [[
8126
8320
  factIdForTriple(d.subject, d.viaProperty, d.viaValue),
8127
8321
  factIdForTriple(d.viaValue, TYPE_PREDICATE, d.viaType),
8128
8322
  factIdForTriple(d.object, ON_PROPERTY_PREDICATE, d.viaPropertyKey),
8129
8323
  factIdForTriple(d.object, SOME_VALUES_FROM_PREDICATE, d.viaTarget),
8130
8324
  ...d.viaType !== d.viaTarget ? [factIdForTriple(d.viaType, SUBCLASS_PREDICATE, d.viaTarget)] : []
8131
- ],
8325
+ ]],
8132
8326
  // same sub-1 discount as cax-dw, same reason (see CAX_DW_RULE_CONFIDENCE).
8133
8327
  ...premiseTrusts.length ? { premiseTrusts, ruleConfidence: CLS_SVF1_RULE_CONFIDENCE } : {}
8134
8328
  };
@@ -8148,18 +8342,70 @@ ${bodyText}` : graphText, tier });
8148
8342
  predicate: SUBCLASS_PREDICATE,
8149
8343
  object: d.object,
8150
8344
  provenance: ENTAILED_SCM_SVF_PROVENANCE,
8151
- justification: [
8345
+ justification: [[
8152
8346
  ...r1 ? [factIdForTriple(d.subject, ON_PROPERTY_PREDICATE, r1.property)] : [],
8153
8347
  factIdForTriple(d.subject, SOME_VALUES_FROM_PREDICATE, d.viaY1),
8154
8348
  ...r2 ? [factIdForTriple(d.object, ON_PROPERTY_PREDICATE, r2.property)] : [],
8155
8349
  factIdForTriple(d.object, SOME_VALUES_FROM_PREDICATE, d.viaY2),
8156
8350
  factIdForTriple(d.viaY1, SUBCLASS_PREDICATE, d.viaY2)
8157
- ],
8351
+ ]],
8158
8352
  // same sub-1 discount as cax-dw/cls-svf1, same reason (see CAX_DW_RULE_CONFIDENCE).
8159
8353
  ...premiseTrusts.length ? { premiseTrusts, ruleConfidence: SCM_SVF_RULE_CONFIDENCE } : {}
8160
8354
  };
8161
8355
  })
8162
8356
  ];
8357
+ const conclusionCandidates = toWrite.map((w) => ({
8358
+ id: factIdForTriple(w.subject, w.predicate, w.object),
8359
+ subject: w.subject,
8360
+ predicate: w.predicate,
8361
+ object: w.object,
8362
+ environments: w.justification,
8363
+ write: w
8364
+ }));
8365
+ const enumerateSupport = buildSupportEnumerator(rows.concat(conclusionCandidates.map((c) => ({
8366
+ id: c.id,
8367
+ subject: c.subject,
8368
+ predicate: c.predicate,
8369
+ object: c.object
8370
+ }))));
8371
+ const rowById = new Map(rows.map((r) => [r.id, r]));
8372
+ const ownedPredicate = (p) => isSubClassOf(p) || isType(p) || isDisjoint(p);
8373
+ const storedCandidateInScope = (r) => mode !== "delta" || frontier !== null && (frontier.has(r.subject) || frontier.has(r.object));
8374
+ const storedCandidates = rows.filter((r) => ownedPredicate(r.predicate) && isPurelyEntailed(r.provenance) && environmentsOf(r).length < maxEnvironments && storedCandidateInScope(r)).map((r) => ({
8375
+ id: r.id,
8376
+ subject: r.subject,
8377
+ predicate: r.predicate,
8378
+ object: r.object,
8379
+ environments: environmentsOf(r),
8380
+ provenance: r.provenance
8381
+ }));
8382
+ const alternateCandidates = [...conclusionCandidates, ...storedCandidates].sort((a, b) => a.subject.localeCompare(b.subject) || a.predicate.localeCompare(b.predicate) || a.object.localeCompare(b.object));
8383
+ let environmentsAdded = 0;
8384
+ let alternatesTruncated = false;
8385
+ let examined = 0;
8386
+ for (const cand of alternateCandidates) {
8387
+ if (examined >= budget) {
8388
+ alternatesTruncated = true;
8389
+ break;
8390
+ }
8391
+ examined += 1;
8392
+ const discovered = enumerateSupport(cand, { maxEnvironments: maxEnvironments + 1 });
8393
+ const { kept, truncated: mergeTruncated } = capMergeEnvironments(cand.environments, discovered, maxEnvironments);
8394
+ if (mergeTruncated) alternatesTruncated = true;
8395
+ if (kept.length <= cand.environments.length) continue;
8396
+ environmentsAdded += kept.length - cand.environments.length;
8397
+ if (cand.write) {
8398
+ cand.write.justification = kept;
8399
+ continue;
8400
+ }
8401
+ toWrite.push({
8402
+ subject: cand.subject,
8403
+ predicate: cand.predicate,
8404
+ object: cand.object,
8405
+ justification: kept,
8406
+ ...bestEnvironmentTrustOpts(cand.provenance, kept, (pid) => rowById.get(pid)?.trust) || {}
8407
+ });
8408
+ }
8163
8409
  const { ids } = await appendFacts2(repoDir, toWrite);
8164
8410
  const written = [];
8165
8411
  let i = 0;
@@ -8183,7 +8429,27 @@ ${bodyText}` : graphText, tier });
8183
8429
  written.push({ id: ids[i], subject: d.subject, object: d.object, via: d.viaY1, rule: SCM_SVF_RULE });
8184
8430
  i += 1;
8185
8431
  }
8186
- return { derived: written, count: written.length, budget, depth, truncated: written.length >= budget };
8432
+ const truncated = written.length >= budget;
8433
+ if (normalizedFocus === null && stateFnsPresent && !truncated) {
8434
+ const factIds = new Set(currentIdSet);
8435
+ for (const id of ids) factIds.add(id);
8436
+ await store.saveSyllogiseState(repoDir, {
8437
+ version: 1,
8438
+ factIds: [...factIds].sort(),
8439
+ completedAt: (/* @__PURE__ */ new Date()).toISOString()
8440
+ });
8441
+ }
8442
+ return {
8443
+ derived: written,
8444
+ count: written.length,
8445
+ budget,
8446
+ depth,
8447
+ truncated,
8448
+ mode,
8449
+ deltaSize,
8450
+ environmentsAdded,
8451
+ alternatesTruncated
8452
+ };
8187
8453
  }
8188
8454
  function isPurelyEntailed(provenance) {
8189
8455
  const tags = String(provenance || "").split(" | ").filter(Boolean);
@@ -8254,8 +8520,185 @@ ${bodyText}` : graphText, tier });
8254
8520
  return true;
8255
8521
  };
8256
8522
  }
8257
- async function retractSubClassOf(repoDir, subject, object, { budget = 50, depth = 32, store } = {}) {
8523
+ function buildSupportEnumerator(rows) {
8524
+ const storedIds = /* @__PURE__ */ new Set();
8525
+ const subClassEdges = [];
8526
+ const succ = /* @__PURE__ */ new Map();
8527
+ const typesOf = /* @__PURE__ */ new Map();
8528
+ const disjointForward = /* @__PURE__ */ new Set();
8529
+ const disjointOf = /* @__PURE__ */ new Map();
8530
+ const onPropertyOf = /* @__PURE__ */ new Map();
8531
+ const someValuesFromOf = /* @__PURE__ */ new Map();
8532
+ const propertyEdgesOf = /* @__PURE__ */ new Map();
8533
+ for (const r of rows) {
8534
+ if (!r || !r.subject || !r.predicate || !r.object) continue;
8535
+ if (r.id) storedIds.add(r.id);
8536
+ const pLower = String(r.predicate || "").trim().toLowerCase();
8537
+ if (isSubClassOf(r.predicate)) {
8538
+ subClassEdges.push([r.subject, r.object]);
8539
+ if (!succ.has(r.subject)) succ.set(r.subject, /* @__PURE__ */ new Set());
8540
+ succ.get(r.subject).add(r.object);
8541
+ } else if (isType(r.predicate)) {
8542
+ if (!typesOf.has(r.subject)) typesOf.set(r.subject, /* @__PURE__ */ new Set());
8543
+ typesOf.get(r.subject).add(r.object);
8544
+ } else if (isDisjoint(r.predicate)) {
8545
+ disjointForward.add(`${r.subject}${SEP}${r.object}`);
8546
+ if (!disjointOf.has(r.subject)) disjointOf.set(r.subject, /* @__PURE__ */ new Set());
8547
+ disjointOf.get(r.subject).add(r.object);
8548
+ if (!disjointOf.has(r.object)) disjointOf.set(r.object, /* @__PURE__ */ new Set());
8549
+ disjointOf.get(r.object).add(r.subject);
8550
+ } else if (isOnProperty(r.predicate)) onPropertyOf.set(r.subject, r.object);
8551
+ else if (isSomeValuesFrom(r.predicate)) someValuesFromOf.set(r.subject, r.object);
8552
+ else if (!RESERVED_PREDICATES.has(pLower)) {
8553
+ if (!propertyEdgesOf.has(r.subject)) propertyEdgesOf.set(r.subject, []);
8554
+ propertyEdgesOf.get(r.subject).push([r.predicate, r.object]);
8555
+ }
8556
+ }
8557
+ const ancestorsOf2 = buildAncestorCloser(subClassEdges);
8558
+ const succOf = (a) => succ.get(a) || /* @__PURE__ */ new Set();
8559
+ const restrictionOf = (node) => {
8560
+ const property = onPropertyOf.get(node);
8561
+ const target = someValuesFromOf.get(node);
8562
+ return property && target ? { property, propertyKey: normFactTerm(property), target } : null;
8563
+ };
8564
+ return (row, { maxEnvironments = DEFAULT_MAX_ENVIRONMENTS } = {}) => {
8565
+ const out = [];
8566
+ const seen = /* @__PURE__ */ new Set();
8567
+ const admit = (env) => {
8568
+ if (out.length >= maxEnvironments) return;
8569
+ if (row.id && env.includes(row.id)) return;
8570
+ if (!env.every((id) => storedIds.has(id))) return;
8571
+ const key = [...env].sort().join(" ");
8572
+ if (seen.has(key)) return;
8573
+ seen.add(key);
8574
+ out.push(env);
8575
+ };
8576
+ if (isSubClassOf(row.predicate)) {
8577
+ for (const m of [...succOf(row.subject)].sort()) {
8578
+ if (out.length >= maxEnvironments) break;
8579
+ if (m === row.subject || m === row.object) continue;
8580
+ if (!succOf(m).has(row.object)) continue;
8581
+ admit([
8582
+ factIdForTriple(row.subject, SUBCLASS_PREDICATE, m),
8583
+ factIdForTriple(m, SUBCLASS_PREDICATE, row.object)
8584
+ ]);
8585
+ }
8586
+ if (out.length < maxEnvironments) {
8587
+ const r1 = restrictionOf(row.subject);
8588
+ const r2 = restrictionOf(row.object);
8589
+ if (r1 && r2 && r1.propertyKey === r2.propertyKey && r1.target !== r2.target && succOf(r1.target).has(r2.target)) {
8590
+ admit([
8591
+ factIdForTriple(row.subject, ON_PROPERTY_PREDICATE, r1.property),
8592
+ factIdForTriple(row.subject, SOME_VALUES_FROM_PREDICATE, r1.target),
8593
+ factIdForTriple(row.object, ON_PROPERTY_PREDICATE, r2.property),
8594
+ factIdForTriple(row.object, SOME_VALUES_FROM_PREDICATE, r2.target),
8595
+ factIdForTriple(r1.target, SUBCLASS_PREDICATE, r2.target)
8596
+ ]);
8597
+ }
8598
+ }
8599
+ return out;
8600
+ }
8601
+ if (isType(row.predicate)) {
8602
+ for (const c of [...typesOf.get(row.subject) || []].sort()) {
8603
+ if (out.length >= maxEnvironments) break;
8604
+ if (c === row.object) continue;
8605
+ if (!succOf(c).has(row.object)) continue;
8606
+ admit([
8607
+ factIdForTriple(row.subject, TYPE_PREDICATE, c),
8608
+ factIdForTriple(c, SUBCLASS_PREDICATE, row.object)
8609
+ ]);
8610
+ }
8611
+ const rec = restrictionOf(row.object);
8612
+ if (rec) {
8613
+ const edges = [...propertyEdgesOf.get(row.subject) || []].filter(([p]) => normFactTerm(p) === rec.propertyKey).sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1]));
8614
+ for (const [p, y] of edges) {
8615
+ if (out.length >= maxEnvironments) break;
8616
+ for (const c of [...typesOf.get(y) || []].sort()) {
8617
+ if (out.length >= maxEnvironments) break;
8618
+ if (c !== rec.target && !succOf(c).has(rec.target)) continue;
8619
+ admit([
8620
+ factIdForTriple(row.subject, p, y),
8621
+ factIdForTriple(y, TYPE_PREDICATE, c),
8622
+ factIdForTriple(row.object, ON_PROPERTY_PREDICATE, rec.propertyKey),
8623
+ factIdForTriple(row.object, SOME_VALUES_FROM_PREDICATE, rec.target),
8624
+ ...c !== rec.target ? [factIdForTriple(c, SUBCLASS_PREDICATE, rec.target)] : []
8625
+ ]);
8626
+ }
8627
+ }
8628
+ }
8629
+ return out;
8630
+ }
8631
+ if (isDisjoint(row.predicate)) {
8632
+ const pairs = [];
8633
+ for (const c of typesOf.get(row.subject) || []) {
8634
+ for (const d of [c, ...ancestorsOf2(c)]) {
8635
+ if ((disjointOf.get(d) || /* @__PURE__ */ new Set()).has(row.object)) pairs.push([c, d]);
8636
+ }
8637
+ }
8638
+ pairs.sort((a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1]));
8639
+ for (const [c, d] of pairs) {
8640
+ if (out.length >= maxEnvironments) break;
8641
+ const dwStoredForward = disjointForward.has(`${d}${SEP}${row.object}`);
8642
+ const [dwS, dwO] = dwStoredForward ? [d, row.object] : [row.object, d];
8643
+ admit([
8644
+ factIdForTriple(row.subject, TYPE_PREDICATE, c),
8645
+ factIdForTriple(dwS, DISJOINT_PREDICATE, dwO),
8646
+ ...d !== c ? [factIdForTriple(c, SUBCLASS_PREDICATE, d)] : []
8647
+ ]);
8648
+ }
8649
+ return out;
8650
+ }
8651
+ return out;
8652
+ };
8653
+ }
8654
+ function capMergeEnvironments(storedEnvs, discoveredEnvs, cap) {
8655
+ const kept = [];
8656
+ const seen = /* @__PURE__ */ new Set();
8657
+ let truncated = false;
8658
+ for (const env of [...storedEnvs || [], ...discoveredEnvs || []]) {
8659
+ if (!Array.isArray(env) || !env.length) continue;
8660
+ const key = [...env].sort().join(" ");
8661
+ if (seen.has(key)) continue;
8662
+ if (kept.length >= cap) {
8663
+ truncated = true;
8664
+ continue;
8665
+ }
8666
+ seen.add(key);
8667
+ kept.push(env);
8668
+ }
8669
+ return { kept, truncated };
8670
+ }
8671
+ function bestEnvironmentTrustOpts(provenance, environments, trustOfId) {
8672
+ let ruleConfidence;
8673
+ for (const tag of String(provenance || "").split(" | ")) {
8674
+ const rc = ENTAILED_RULE_CONFIDENCE_BY_TAG.get(tag);
8675
+ if (rc !== void 0) {
8676
+ ruleConfidence = rc;
8677
+ break;
8678
+ }
8679
+ }
8680
+ if (ruleConfidence === void 0) return null;
8681
+ let best = null;
8682
+ let bestMin = -1;
8683
+ for (const env of environments || []) {
8684
+ const trusts = env.map((id) => trustOfId(id)).filter((t) => typeof t === "number");
8685
+ if (trusts.length !== env.length) continue;
8686
+ const weakest = Math.min(...trusts);
8687
+ if (weakest > bestMin) {
8688
+ bestMin = weakest;
8689
+ best = trusts;
8690
+ }
8691
+ }
8692
+ return best ? { premiseTrusts: best, ruleConfidence } : null;
8693
+ }
8694
+ async function retractSubClassOf(repoDir, subject, object, {
8695
+ budget = 50,
8696
+ depth = 32,
8697
+ maxEnvironments = DEFAULT_MAX_ENVIRONMENTS,
8698
+ store
8699
+ } = {}) {
8258
8700
  const { loadMemory: loadMemory2, readFactRows: readFactRows2, removeFacts: removeFacts2 } = requireStore(store, ["loadMemory", "readFactRows", "removeFacts"], "retractSubClassOf");
8701
+ const appendFactsFn = typeof store?.appendFacts === "function" ? store.appendFacts : null;
8259
8702
  const s = normFactTerm(subject);
8260
8703
  const o = normFactTerm(object);
8261
8704
  const targetId = factIdForTriple(s, SUBCLASS_PREDICATE, o);
@@ -8263,28 +8706,58 @@ ${bodyText}` : graphText, tier });
8263
8706
  const rows = readFactRows2(memory);
8264
8707
  const byId = new Map(rows.map((r) => [r.id, r]));
8265
8708
  if (!byId.has(targetId)) return { retracted: [], count: 0, budget, depth, truncated: false, found: false };
8266
- const entailedRows = rows.filter((r) => r.justification.length && isPurelyEntailed(r.provenance));
8709
+ const entailedRows = rows.filter((r) => environmentsOf(r).length && isPurelyEntailed(r.provenance));
8710
+ const citedBy = /* @__PURE__ */ new Map();
8711
+ for (const r of entailedRows) {
8712
+ for (const env of environmentsOf(r)) {
8713
+ for (const premiseId of env) {
8714
+ if (!citedBy.has(premiseId)) citedBy.set(premiseId, /* @__PURE__ */ new Set());
8715
+ citedBy.get(premiseId).add(r.id);
8716
+ }
8717
+ }
8718
+ }
8267
8719
  const removed = /* @__PURE__ */ new Set([targetId]);
8268
8720
  const order = [targetId];
8721
+ const reground = /* @__PURE__ */ new Map();
8269
8722
  let truncated = false;
8270
8723
  let round2 = 0;
8724
+ let newlyRemoved = [targetId];
8271
8725
  for (; round2 < depth; round2 += 1) {
8272
- const candidates = entailedRows.filter((r) => !removed.has(r.id) && r.justification.some((j) => removed.has(j))).sort((a, b) => a.subject.localeCompare(b.subject) || a.predicate.localeCompare(b.predicate) || a.object.localeCompare(b.object));
8726
+ const candidateIds = /* @__PURE__ */ new Set();
8727
+ for (const id of newlyRemoved) {
8728
+ for (const cited of citedBy.get(id) || []) {
8729
+ if (!removed.has(cited)) candidateIds.add(cited);
8730
+ }
8731
+ }
8732
+ const candidates = [...candidateIds].map((id) => byId.get(id)).sort((a, b) => a.subject.localeCompare(b.subject) || a.predicate.localeCompare(b.predicate) || a.object.localeCompare(b.object));
8273
8733
  if (!candidates.length) break;
8274
- const candidateIds = new Set(candidates.map((c) => c.id));
8275
- const stillDerivable = buildSurvivorDerivabilityCheck(
8276
- rows.filter((r) => !removed.has(r.id) && !candidateIds.has(r.id))
8277
- );
8734
+ const survivors = rows.filter((r) => !removed.has(r.id) && !candidateIds.has(r.id));
8735
+ const survivorIds = new Set(survivors.map((r) => r.id));
8736
+ const enumerateSupport = buildSupportEnumerator(survivors);
8737
+ const stillDerivable = buildSurvivorDerivabilityCheck(survivors);
8278
8738
  let progressed = false;
8279
8739
  let hitBudget = false;
8740
+ newlyRemoved = [];
8280
8741
  for (const c of candidates) {
8281
8742
  if (removed.size >= budget) {
8282
8743
  hitBudget = true;
8283
8744
  break;
8284
8745
  }
8746
+ const environments = environmentsOf(c);
8747
+ const intact = environments.filter((env) => env.every((id) => survivorIds.has(id)));
8748
+ if (intact.length) {
8749
+ if (intact.length !== environments.length) reground.set(c.id, intact);
8750
+ continue;
8751
+ }
8752
+ const fresh = enumerateSupport(c, { maxEnvironments });
8753
+ if (fresh.length) {
8754
+ reground.set(c.id, fresh);
8755
+ continue;
8756
+ }
8285
8757
  if (stillDerivable(c)) continue;
8286
8758
  removed.add(c.id);
8287
8759
  order.push(c.id);
8760
+ newlyRemoved.push(c.id);
8288
8761
  progressed = true;
8289
8762
  }
8290
8763
  if (hitBudget) {
@@ -8297,6 +8770,20 @@ ${bodyText}` : graphText, tier });
8297
8770
  truncated = entailedRows.some((r) => !removed.has(r.id) && r.justification.some((j) => removed.has(j)));
8298
8771
  }
8299
8772
  const { removed: actuallyRemoved } = await removeFacts2(repoDir, order);
8773
+ if (appendFactsFn) {
8774
+ const regroundWrites = [...reground.entries()].filter(([id]) => !removed.has(id)).sort((a, b) => a[0].localeCompare(b[0])).map(([id, environments]) => {
8775
+ const row = byId.get(id);
8776
+ return {
8777
+ subject: row.subject,
8778
+ predicate: row.predicate,
8779
+ object: row.object,
8780
+ // provenance omitted — appendFacts' first-write-wins keeps the union
8781
+ justification: environments,
8782
+ ...bestEnvironmentTrustOpts(row.provenance, environments, (pid) => byId.get(pid)?.trust) || {}
8783
+ };
8784
+ });
8785
+ if (regroundWrites.length) await appendFactsFn(repoDir, regroundWrites);
8786
+ }
8300
8787
  return { retracted: actuallyRemoved, count: actuallyRemoved.length, budget, depth, truncated, found: true };
8301
8788
  }
8302
8789
  function findIsaChain(subj, targets, typeEdges, subClassEdges, { maxHops = 6 } = {}) {
@@ -8331,7 +8818,7 @@ ${bodyText}` : graphText, tier });
8331
8818
  }
8332
8819
  return null;
8333
8820
  }
8334
- var SUBCLASS_PREDICATE, SYLLOGISE_RULE, ENTAILED_PROVENANCE, TYPE_PREDICATE, CAX_SCO_RULE, ENTAILED_TYPE_PROVENANCE, DISJOINT_PREDICATE, CAX_DW_RULE, ENTAILED_DISJOINT_PROVENANCE, CAX_DW_RULE_CONFIDENCE, ON_PROPERTY_PREDICATE, SOME_VALUES_FROM_PREDICATE, CLS_SVF1_RULE, ENTAILED_SVF1_PROVENANCE, CLS_SVF1_RULE_CONFIDENCE, SEP, isSubClassOf, isType, isDisjoint, isOnProperty, isSomeValuesFrom, isOnClass, RESERVED_PREDICATES, HAS_PROPERTY_KEY, CARDINALITY_KIND_OF, SCM_SVF_RULE, ENTAILED_SCM_SVF_PROVENANCE, SCM_SVF_RULE_CONFIDENCE, CARDINALITY_RULE_CONFIDENCE, CAX_MAXC0_RULE_CONFIDENCE;
8821
+ var SUBCLASS_PREDICATE, SYLLOGISE_RULE, ENTAILED_PROVENANCE, TYPE_PREDICATE, CAX_SCO_RULE, ENTAILED_TYPE_PROVENANCE, DISJOINT_PREDICATE, CAX_DW_RULE, ENTAILED_DISJOINT_PROVENANCE, CAX_DW_RULE_CONFIDENCE, ON_PROPERTY_PREDICATE, SOME_VALUES_FROM_PREDICATE, CLS_SVF1_RULE, ENTAILED_SVF1_PROVENANCE, CLS_SVF1_RULE_CONFIDENCE, DEFAULT_MAX_ENVIRONMENTS, SEP, isSubClassOf, isType, isDisjoint, isOnProperty, isSomeValuesFrom, isOnClass, RESERVED_PREDICATES, HAS_PROPERTY_KEY, CARDINALITY_KIND_OF, SCM_SVF_RULE, ENTAILED_SCM_SVF_PROVENANCE, SCM_SVF_RULE_CONFIDENCE, CARDINALITY_RULE_CONFIDENCE, CAX_MAXC0_RULE_CONFIDENCE, environmentsOf, ENTAILED_RULE_CONFIDENCE_BY_TAG;
8335
8822
  var init_syllogise = __esm({
8336
8823
  "src/domain/syllogise.mjs"() {
8337
8824
  init_hash();
@@ -8350,6 +8837,7 @@ ${bodyText}` : graphText, tier });
8350
8837
  CLS_SVF1_RULE = "someValuesFrom";
8351
8838
  ENTAILED_SVF1_PROVENANCE = `entailed:${CLS_SVF1_RULE}`;
8352
8839
  CLS_SVF1_RULE_CONFIDENCE = 0.95;
8840
+ DEFAULT_MAX_ENVIRONMENTS = 4;
8353
8841
  SEP = "\u241F";
8354
8842
  isSubClassOf = (p) => String(p || "").trim().toLowerCase() === "rdfs:subclassof";
8355
8843
  isType = (p) => String(p || "").trim().toLowerCase() === "rdf:type";
@@ -8372,6 +8860,12 @@ ${bodyText}` : graphText, tier });
8372
8860
  SCM_SVF_RULE_CONFIDENCE = 0.95;
8373
8861
  CARDINALITY_RULE_CONFIDENCE = 0.95;
8374
8862
  CAX_MAXC0_RULE_CONFIDENCE = 0.95;
8863
+ environmentsOf = (row) => row.environments || (Array.isArray(row.justification) && row.justification.length ? [row.justification] : []);
8864
+ ENTAILED_RULE_CONFIDENCE_BY_TAG = /* @__PURE__ */ new Map([
8865
+ [ENTAILED_DISJOINT_PROVENANCE, CAX_DW_RULE_CONFIDENCE],
8866
+ [ENTAILED_SVF1_PROVENANCE, CLS_SVF1_RULE_CONFIDENCE],
8867
+ [ENTAILED_SCM_SVF_PROVENANCE, SCM_SVF_RULE_CONFIDENCE]
8868
+ ]);
8375
8869
  }
8376
8870
  });
8377
8871
 
@@ -8698,6 +9192,7 @@ ${bodyText}` : graphText, tier });
8698
9192
  SOURCE_CLASS: () => SOURCE_CLASS,
8699
9193
  SOURCE_RELIABILITY_PROP: () => SOURCE_RELIABILITY_PROP,
8700
9194
  STATED_BY_PROP: () => STATED_BY_PROP,
9195
+ SYLLOGISE_STATE_REL: () => SYLLOGISE_STATE_REL,
8701
9196
  UPDATED_AT_PROP: () => UPDATED_AT_PROP,
8702
9197
  UTTERANCE_CLASS: () => UTTERANCE_CLASS,
8703
9198
  appendFact: () => appendFact,
@@ -8714,6 +9209,7 @@ ${bodyText}` : graphText, tier });
8714
9209
  findRuleByName: () => findRuleByName,
8715
9210
  findRulesByName: () => findRulesByName,
8716
9211
  loadMemory: () => loadMemory,
9212
+ loadSyllogiseState: () => loadSyllogiseState,
8717
9213
  normFactPredicate: () => normFactPredicate,
8718
9214
  normFactTerm: () => normFactTerm,
8719
9215
  openMemoryBackend: () => openMemoryBackend,
@@ -8725,6 +9221,7 @@ ${bodyText}` : graphText, tier });
8725
9221
  resolveMemoryGraphFile: () => resolveMemoryGraphFile,
8726
9222
  resolveRelationChase: () => resolveRelationChase,
8727
9223
  resolveRelationChaseReverse: () => resolveRelationChaseReverse,
9224
+ saveSyllogiseState: () => saveSyllogiseState,
8728
9225
  snapshotMemory: () => snapshotMemory
8729
9226
  });
8730
9227
  function emptyMemory() {
@@ -9035,7 +9532,9 @@ ${bodyText}` : graphText, tier });
9035
9532
  for (const ind of payload.individuals) {
9036
9533
  if (Array.isArray(ind?.derived_from) && ind.derived_from.length) ind.derived_from = ind.derived_from.map(remapId);
9037
9534
  const just = (ind?.attributes || []).find((a) => a?.prop === "mgx:factJustification");
9038
- if (just?.value) just.value = just.value.split(" ").filter(Boolean).map(remapId).join(" ");
9535
+ if (just?.value) {
9536
+ just.value = just.value.split(" | ").map((env) => env.split(" ").filter(Boolean).map(remapId).join(" ")).filter(Boolean).join(" | ");
9537
+ }
9039
9538
  }
9040
9539
  return payload;
9041
9540
  }
@@ -9051,6 +9550,32 @@ ${bodyText}` : graphText, tier });
9051
9550
  await mkdir2(dirname(memoryGraphFile(dir)), { recursive: true });
9052
9551
  await atomicWriteJson(memoryGraphFile(dir), payload);
9053
9552
  }
9553
+ async function loadSyllogiseState(dir) {
9554
+ if (isMemoryHandle(dir)) return dir.syllogiseState ? structuredClone(dir.syllogiseState) : null;
9555
+ if (isSqliteHandle(dir)) {
9556
+ const row = dir.db.prepare("SELECT v FROM meta WHERE k = ?").get(SQLITE_SYLLOGISE_STATE_KEY);
9557
+ return row?.v ? JSON.parse(row.v) : null;
9558
+ }
9559
+ try {
9560
+ return JSON.parse(await readFile2(join(dir, SYLLOGISE_STATE_REL), "utf8"));
9561
+ } catch (e) {
9562
+ if (e?.code === "ENOENT") return null;
9563
+ throw e;
9564
+ }
9565
+ }
9566
+ async function saveSyllogiseState(dir, state) {
9567
+ if (isMemoryHandle(dir)) {
9568
+ dir.syllogiseState = structuredClone(state);
9569
+ return;
9570
+ }
9571
+ if (isSqliteHandle(dir)) {
9572
+ dir.db.prepare("INSERT OR REPLACE INTO meta(k, v) VALUES (?, ?)").run(SQLITE_SYLLOGISE_STATE_KEY, JSON.stringify(state));
9573
+ return;
9574
+ }
9575
+ const file = join(dir, SYLLOGISE_STATE_REL);
9576
+ await mkdir2(dirname(file), { recursive: true });
9577
+ await atomicWriteJson(file, state);
9578
+ }
9054
9579
  function buildMemoryIndex(payload) {
9055
9580
  const individualsById = /* @__PURE__ */ new Map();
9056
9581
  const sourcesById = /* @__PURE__ */ new Map();
@@ -9097,6 +9622,10 @@ ${bodyText}` : graphText, tier });
9097
9622
  return { id: `src:provider:${desc.name}`, type: "provider" };
9098
9623
  case "corpus":
9099
9624
  return { id: `src:corpus:${desc.name}`, type: "corpus" };
9625
+ // One Source per pack article (the @revid stays in the article segment),
9626
+ // so two facts from the same article corroborate nothing extra.
9627
+ case "reference":
9628
+ return { id: `src:reference:${desc.pack}:${desc.article}`, type: "reference" };
9100
9629
  // One Source per source-file basename, not per extraction run.
9101
9630
  case "extracted":
9102
9631
  return { id: `src:extracted:${desc.name}`, type: "extracted" };
@@ -9407,6 +9936,21 @@ ${bodyText}` : graphText, tier });
9407
9936
  });
9408
9937
  return { id };
9409
9938
  }
9939
+ function normalizeJustificationEnvironments(justification) {
9940
+ if (!Array.isArray(justification)) return void 0;
9941
+ const rawEnvs = justification.some(Array.isArray) ? justification.filter(Array.isArray) : [justification];
9942
+ const envs = [];
9943
+ const seen = /* @__PURE__ */ new Set();
9944
+ for (const raw of rawEnvs) {
9945
+ const env = raw.filter((id) => typeof id === "string" && id);
9946
+ if (!env.length) continue;
9947
+ const key = [...env].sort().join(" ");
9948
+ if (seen.has(key)) continue;
9949
+ seen.add(key);
9950
+ envs.push(env);
9951
+ }
9952
+ return envs.length ? envs : void 0;
9953
+ }
9410
9954
  async function appendFacts(dir, facts) {
9411
9955
  const prepared = [];
9412
9956
  let skipped = 0;
@@ -9432,7 +9976,7 @@ ${bodyText}` : graphText, tier });
9432
9976
  quantifier: normText(f?.quantifier),
9433
9977
  premiseTrusts: Array.isArray(f?.premiseTrusts) ? f.premiseTrusts : void 0,
9434
9978
  ruleConfidence: typeof f?.ruleConfidence === "number" ? f.ruleConfidence : void 0,
9435
- justification: Array.isArray(f?.justification) ? f.justification.filter(Boolean) : void 0
9979
+ environments: normalizeJustificationEnvironments(f?.justification)
9436
9980
  });
9437
9981
  }
9438
9982
  const ids = [];
@@ -9465,7 +10009,7 @@ ${bodyText}` : graphText, tier });
9465
10009
  ...provs.length ? [{ prop: "mgx:factProvenance", key: "provenance", value: provs.join(" | ") }] : [],
9466
10010
  ...f.tokens.length ? [{ prop: "mgx:hasProseTokens", key: "prose_tokens", value: f.tokens.join(" ") }] : [],
9467
10011
  ...qVal ? [{ prop: "mgx:factQuantifier", key: "quantifier", value: qVal }] : [],
9468
- ...f.justification && f.justification.length ? [{ prop: "mgx:factJustification", key: "justification", value: f.justification.join(" ") }] : []
10012
+ ...f.environments ? [{ prop: "mgx:factJustification", key: "justification", value: f.environments.map((e) => e.join(" ")).join(" | ") }] : []
9469
10013
  ]
9470
10014
  };
9471
10015
  const stored = upsertIndividual(payload, ind);
@@ -9700,6 +10244,22 @@ ${bodyText}` : graphText, tier });
9700
10244
  const sourceIds = byFact.get(ind.id) || [];
9701
10245
  const sourceTypes = sourceIds.map((id) => (sourcesById.get(id)?.attributes || []).find((a) => a?.prop === "mgx:sourceType")?.value).filter(Boolean);
9702
10246
  const justificationRaw = get("justification");
10247
+ const environments = [];
10248
+ if (justificationRaw) {
10249
+ for (const chunk of justificationRaw.split(" | ")) {
10250
+ const env = chunk.split(" ").filter(Boolean);
10251
+ if (env.length) environments.push(env);
10252
+ }
10253
+ }
10254
+ const justification = [];
10255
+ const seenPremise = /* @__PURE__ */ new Set();
10256
+ for (const env of environments) {
10257
+ for (const id of env) {
10258
+ if (seenPremise.has(id)) continue;
10259
+ seenPremise.add(id);
10260
+ justification.push(id);
10261
+ }
10262
+ }
9703
10263
  rows.push({
9704
10264
  id: ind.id,
9705
10265
  subject: get("subject"),
@@ -9712,9 +10272,11 @@ ${bodyText}` : graphText, tier });
9712
10272
  sourceIds,
9713
10273
  sourceTypes,
9714
10274
  trust: Number((ind.attributes || []).find((a) => a?.prop === TRUST_SCORE_PROP)?.value) || 0,
9715
- // [] unless a rule persisted its premise fact ids (justification-tracking,
9716
- // scm-sco only today; see syllogise.mjs).
9717
- justification: justificationRaw ? justificationRaw.split(" ").filter(Boolean) : []
10275
+ // `environments`: every persisted premise set (empty unless entailed);
10276
+ // `justification`: their deduped union in first-occurrence order, for
10277
+ // readers that only need "which premises does this fact cite at all".
10278
+ environments,
10279
+ justification
9718
10280
  });
9719
10281
  }
9720
10282
  return rows;
@@ -9759,7 +10321,7 @@ ${bodyText}` : graphText, tier });
9759
10321
  }
9760
10322
  return out.sort((a, b) => `${a[0].subject} ${a[0].predicate}`.localeCompare(`${b[0].subject} ${b[0].predicate}`));
9761
10323
  }
9762
- var MEMORY_DIR_REL, MEMORY_GRAPH_REL, UTTERANCE_CLASS, FACT_CLASS, MEMORY_SESSION_CLASS, SOURCE_CLASS, RULE_CLASS, SAID_IN_SESSION_PROP, IN_REPLY_TO_PROP, DERIVED_FROM_PROP, STATED_BY_PROP, CANONICALISED_FROM_PROP, SOURCE_RELIABILITY_PROP, OPERATOR_SOURCE_ID, TEACH_SOURCE_ID, ROLES, LABEL_CAP, MEMORY_VOCABULARY, memoryGraphFile, BACKEND_MEMORY, BACKEND_SQLITE, SQLITE_DDL, STD_EDGE_KEYS, cloneJson, MEMORY_MANIFEST_REL, DEFAULT_RETENTION, resolveManifestFile, LEGACY_FACT_ID_RE, MEMORY_INDEX, memoryIndexOf, labelOf, nowIso, sourceLabel, PROV_CLASS_BY_SOURCE_TYPE, isSessionScopedSourceId, RULE_KIND_COMPOSE2, RULE_KIND_FILTER, RULE_KIND_RECURSIVE, RULE_KIND_ACTION_SIGNATURE, RULE_KIND_ACTION_PRECOND, RULE_KIND_ACTION_EFFECT, RULE_KIND_ACTION_CONSTRAINT, RULE_KINDS2, RULE_NAME_PROP, RULE_KIND_PROP, RULE_SLOT_SPEC, ruleIdFor, CONTRADICTION_TRUST_FLOOR, HAS_A_PREDICATE, CAPABLE_OF_PREDICATE2, MULTI_VALUED_PREDICATES;
10324
+ var MEMORY_DIR_REL, MEMORY_GRAPH_REL, UTTERANCE_CLASS, FACT_CLASS, MEMORY_SESSION_CLASS, SOURCE_CLASS, RULE_CLASS, SAID_IN_SESSION_PROP, IN_REPLY_TO_PROP, DERIVED_FROM_PROP, STATED_BY_PROP, CANONICALISED_FROM_PROP, SOURCE_RELIABILITY_PROP, OPERATOR_SOURCE_ID, TEACH_SOURCE_ID, ROLES, LABEL_CAP, MEMORY_VOCABULARY, memoryGraphFile, BACKEND_MEMORY, BACKEND_SQLITE, SQLITE_DDL, STD_EDGE_KEYS, cloneJson, MEMORY_MANIFEST_REL, DEFAULT_RETENTION, resolveManifestFile, LEGACY_FACT_ID_RE, SYLLOGISE_STATE_REL, SQLITE_SYLLOGISE_STATE_KEY, MEMORY_INDEX, memoryIndexOf, labelOf, nowIso, sourceLabel, PROV_CLASS_BY_SOURCE_TYPE, isSessionScopedSourceId, RULE_KIND_COMPOSE2, RULE_KIND_FILTER, RULE_KIND_RECURSIVE, RULE_KIND_ACTION_SIGNATURE, RULE_KIND_ACTION_PRECOND, RULE_KIND_ACTION_EFFECT, RULE_KIND_ACTION_CONSTRAINT, RULE_KINDS2, RULE_NAME_PROP, RULE_KIND_PROP, RULE_SLOT_SPEC, ruleIdFor, CONTRADICTION_TRUST_FLOOR, HAS_A_PREDICATE, CAPABLE_OF_PREDICATE2, MULTI_VALUED_PREDICATES;
9763
10325
  var init_core = __esm({
9764
10326
  "src/adapters/memory/core.mjs"() {
9765
10327
  init_promises();
@@ -9801,6 +10363,7 @@ ${bodyText}` : graphText, tier });
9801
10363
  { prop: "rdf:object", note: "reified fact: the triple's object term" },
9802
10364
  { prop: "mgx:factProvenance", note: "LEGACY COMPAT SHIM: the ' | '-joined provenance tag string a fact came from; the source-of-truth is now the mgx:statedBy edges derived from it" },
9803
10365
  { prop: "mgx:factQuantifier", note: "OPTIONAL: the quantifier word a plural class-membership teach used ('every'/'some'/'a few'), for literal recall by 'how many Xs are Ys' \u2014 never real cardinality counting" },
10366
+ { prop: "mgx:factJustification", note: "an entailed Fact's supporting premise fact ids: ' | '-separated environments, one space-separated premise-id list per independent derivation, capped by syllogise's maxEnvironments knob; a value with no ' | ' is a single environment" },
9804
10367
  { prop: "mgx:ruleName", note: "a taught Rule's own name (e.g. 'grandparent') \u2014 the query-dispatcher's lookup key, PLAN_TAUGHT_RELATIONS.md \xA72/\xA73" },
9805
10368
  { prop: "mgx:ruleKind", note: "a taught Rule's SHAPE tag \u2014 the closed vocabulary compose2 | filter | recursive (structural, like 'Fact'/'Rule' themselves, never a domain word)" },
9806
10369
  { prop: "mgx:ruleBase1", note: "compose2: the first hop's base relation name; filter: the base rule/relation being filtered (same 'base relation' role in both kinds, so the name is shared)" },
@@ -9813,7 +10376,7 @@ ${bodyText}` : graphText, tier });
9813
10376
  { prop: DERIVED_FROM_PROP, predicate: "derivedFrom", note: "umbrella: a Fact derived from a Source (or another Fact). ext ref prov:wasDerivedFrom (UNVERIFIED-pending-web-check)" },
9814
10377
  { prop: STATED_BY_PROP, predicate: "statedBy", note: "subPropertyOf derivedFrom: a Source directly asserts this Fact (one edge per independent source \u2014 replaces the factProvenance union)" },
9815
10378
  { prop: CANONICALISED_FROM_PROP, predicate: "canonicalisedFrom", note: "subPropertyOf derivedFrom: a canonical Fact cleaned from a raw Block/Source, never replacing it" },
9816
- { prop: "mgx:sourceType", note: "a Source's kind: operator | teach | provider | corpus | corpusWeak | extracted | web | entailed (the trust-prior key)" },
10379
+ { prop: "mgx:sourceType", note: "a Source's kind: operator | teach | provider | corpus | corpusWeak | reference | extracted | web | entailed (the trust-prior key)" },
9817
10380
  { prop: "mgx:sourceUrl", note: "a web Source's URL" },
9818
10381
  { prop: "mgx:sourceRule", note: "an entailed Source's rule id" },
9819
10382
  { prop: "mgx:sourceReliability", note: "actor-level (session-scoped) trust nudge in [0.5,1.5], neutral 1.0 when absent \u2014 materialised by recomputeSourceReliability from a session's asserted-vs-contradicted track record (memory/trust.mjs's sessionReliabilityFrom); folds into computeTrust's per-source prior" },
@@ -9839,6 +10402,8 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
9839
10402
  DEFAULT_RETENTION = 5;
9840
10403
  resolveManifestFile = (dir) => join(dir, MEMORY_MANIFEST_REL);
9841
10404
  LEGACY_FACT_ID_RE = /^fact:[0-9a-f]{8}$/;
10405
+ SYLLOGISE_STATE_REL = join(MEMORY_DIR_REL, "syllogise-state.json");
10406
+ SQLITE_SYLLOGISE_STATE_KEY = "syllogiseState";
9842
10407
  MEMORY_INDEX = /* @__PURE__ */ Symbol("mutateMemory lookup index");
9843
10408
  memoryIndexOf = (payload) => payload?.[MEMORY_INDEX] || null;
9844
10409
  labelOf = (text) => text.length > LABEL_CAP ? text.slice(0, LABEL_CAP - 1) + "\u2026" : text;
@@ -9850,6 +10415,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
9850
10415
  provider: { subClass: "tmct:AgentSource", prov: "prov:Agent" },
9851
10416
  corpus: { subClass: "tmct:DocumentSource", prov: "prov:Entity" },
9852
10417
  corpusWeak: { subClass: "tmct:DocumentSource", prov: "prov:Entity" },
10418
+ reference: { subClass: "tmct:DocumentSource", prov: "prov:Entity" },
9853
10419
  web: { subClass: "tmct:DocumentSource", prov: "prov:Entity" },
9854
10420
  extracted: { subClass: "tmct:DocumentSource", prov: "prov:Entity" },
9855
10421
  entailed: { subClass: "tmct:ActivitySource", prov: "prov:Activity" }
@@ -20810,6 +21376,189 @@ ${codeblock}`, options);
20810
21376
  }
20811
21377
  });
20812
21378
 
21379
+ // src/domain/concept.mjs
21380
+ var CONCEPT_CLASS, CLASS_NOUN, FOLLOWUP_SHAPES, RELATION_TERM, RELATION_KINDS, RELATION_RENDER, RELATION_FOLLOWUP_SHAPES;
21381
+ var init_concept = __esm({
21382
+ "src/domain/concept.mjs"() {
21383
+ init_ask();
21384
+ init_codegraph();
21385
+ CONCEPT_CLASS = Object.freeze({
21386
+ class: "Class",
21387
+ module: "Module",
21388
+ function: "Function",
21389
+ method: "Method",
21390
+ attribute: "Attribute",
21391
+ variable: "GlobalVariable",
21392
+ constant: "GlobalVariable",
21393
+ commit: "Commit"
21394
+ });
21395
+ CLASS_NOUN = Object.freeze({
21396
+ Class: ["class", "classes"],
21397
+ Module: ["module", "modules"],
21398
+ Function: ["function", "functions"],
21399
+ Method: ["method", "methods"],
21400
+ Attribute: ["attribute", "attributes"],
21401
+ GlobalVariable: ["variable", "variables"],
21402
+ Commit: ["commit", "commits"]
21403
+ });
21404
+ FOLLOWUP_SHAPES = Object.freeze({
21405
+ Class: [
21406
+ (x) => `which classes inherit from ${x}`,
21407
+ (x) => `what does ${x} contain`,
21408
+ (x) => `where is ${x} defined`
21409
+ ],
21410
+ Module: [
21411
+ (x) => `what does ${x} import`,
21412
+ (x) => `which modules import ${x}`,
21413
+ (x) => `where is ${x} defined`
21414
+ ],
21415
+ Function: [
21416
+ (x) => `what calls ${x}`,
21417
+ (x) => `what does ${x} call`,
21418
+ (x) => `where is ${x} defined`
21419
+ ],
21420
+ Method: [
21421
+ (x) => `which class contains ${x}`,
21422
+ (x) => `what calls ${x}`,
21423
+ (x) => `where is ${x} defined`
21424
+ ],
21425
+ Attribute: [
21426
+ (x) => `which class contains ${x}`,
21427
+ (x) => `where is ${x} defined`
21428
+ ],
21429
+ GlobalVariable: [
21430
+ (x) => `where is ${x} defined`,
21431
+ (x) => `where is ${x} mentioned`
21432
+ ],
21433
+ Commit: [
21434
+ (x) => `what did commit ${x} touch`,
21435
+ (x) => `when did ${x} change`
21436
+ ]
21437
+ });
21438
+ RELATION_TERM = Object.freeze({
21439
+ import: "imports",
21440
+ imports: "imports",
21441
+ importing: "imports",
21442
+ imported: "imports",
21443
+ call: "calls",
21444
+ calls: "calls",
21445
+ calling: "calls",
21446
+ called: "calls",
21447
+ invoke: "calls",
21448
+ invokes: "calls",
21449
+ invoking: "calls",
21450
+ contain: "contains",
21451
+ contains: "contains",
21452
+ containing: "contains",
21453
+ containment: "contains",
21454
+ member: "contains",
21455
+ members: "contains",
21456
+ inherit: "inherits",
21457
+ inherits: "inherits",
21458
+ inheriting: "inherits",
21459
+ inheritance: "inherits",
21460
+ extend: "inherits",
21461
+ extends: "inherits",
21462
+ extending: "inherits",
21463
+ subclass: "inherits",
21464
+ subclasses: "inherits",
21465
+ subclassing: "inherits",
21466
+ test: "tests",
21467
+ tests: "tests",
21468
+ testing: "tests",
21469
+ tested: "tests",
21470
+ coverage: "tests",
21471
+ define: "defines",
21472
+ defines: "defines",
21473
+ defining: "defines",
21474
+ defined: "defines",
21475
+ definition: "defines",
21476
+ definitions: "defines",
21477
+ declaration: "defines",
21478
+ touch: "touches",
21479
+ touches: "touches",
21480
+ touching: "touches",
21481
+ touched: "touches",
21482
+ cochange: "cochange",
21483
+ "co-change": "cochange",
21484
+ "change-coupling": "cochange",
21485
+ coupled: "cochange",
21486
+ // "export"/"exports" is also a curated seon lexicon noun, but that meta reading
21487
+ // only owns "what does export mean" — no conflict with this vague-touch table.
21488
+ export: "reexports",
21489
+ exports: "reexports",
21490
+ exporting: "reexports",
21491
+ exported: "reexports",
21492
+ reexport: "reexports",
21493
+ reexports: "reexports",
21494
+ reexporting: "reexports",
21495
+ "re-export": "reexports",
21496
+ "re-exports": "reexports",
21497
+ "re-exporting": "reexports"
21498
+ });
21499
+ RELATION_KINDS = Object.freeze({
21500
+ imports: ["imports"],
21501
+ calls: ["calls", "callsSymbol"],
21502
+ contains: ["contains"],
21503
+ inherits: ["inherits"],
21504
+ tests: ["tests"],
21505
+ defines: ["defines"],
21506
+ touches: ["touches", "touchesSymbol"],
21507
+ cochange: ["cochange"],
21508
+ reexports: ["reexports"]
21509
+ });
21510
+ RELATION_RENDER = Object.freeze({
21511
+ imports: { verb: "imports", edgeNoun: "import" },
21512
+ calls: { verb: "calls", edgeNoun: "call" },
21513
+ contains: { verb: "contains", edgeNoun: "containment" },
21514
+ inherits: { verb: "inherits from", edgeNoun: "inheritance" },
21515
+ tests: { verb: "tests", edgeNoun: "test" },
21516
+ defines: { verb: "defines", edgeNoun: "definition" },
21517
+ touches: { verb: "touches", edgeNoun: "touch" },
21518
+ cochange: { verb: "changes together with", edgeNoun: "change-coupling" },
21519
+ reexports: { verb: "re-exports", edgeNoun: "re-export" }
21520
+ });
21521
+ RELATION_FOLLOWUP_SHAPES = Object.freeze({
21522
+ imports: [
21523
+ { side: "obj", make: (x) => `which modules import ${x}` },
21524
+ { side: "subj", make: (x) => `what does ${x} import` }
21525
+ ],
21526
+ calls: [
21527
+ { side: "obj", make: (x) => `what calls ${x}` },
21528
+ { side: "subj", make: (x) => `what does ${x} call` }
21529
+ ],
21530
+ contains: [
21531
+ { side: "subj", make: (x) => `what does ${x} contain` },
21532
+ { side: "obj", make: (x) => `which class contains ${x}` }
21533
+ ],
21534
+ inherits: [
21535
+ { side: "obj", make: (x) => `which classes inherit from ${x}` },
21536
+ { side: "subj", make: (x) => `where is ${x} defined` }
21537
+ ],
21538
+ tests: [
21539
+ { side: "obj", make: (x) => `what tests ${x}` },
21540
+ { side: "obj", make: (x) => `where is ${x} defined` }
21541
+ ],
21542
+ defines: [
21543
+ { side: "obj", make: (x) => `where is ${x} defined` },
21544
+ { side: "subj", make: (x) => `what does ${x} contain` }
21545
+ ],
21546
+ touches: [
21547
+ { side: "obj", make: (x) => `when did ${x} change` },
21548
+ { side: "subj", make: (x) => `what did commit ${x} touch` }
21549
+ ],
21550
+ cochange: [
21551
+ { side: "obj", make: (x) => `where is ${x} defined` },
21552
+ { side: "subj", make: (x) => `which modules import ${x}` }
21553
+ ],
21554
+ reexports: [
21555
+ { side: "subj", make: (x) => `what does ${x} export` },
21556
+ { side: "obj", make: (x) => `where is ${x} defined` }
21557
+ ]
21558
+ });
21559
+ }
21560
+ });
21561
+
20813
21562
  // src/adapters/toml-config.mjs
20814
21563
  var init_toml_config = __esm({
20815
21564
  "src/adapters/toml-config.mjs"() {
@@ -20866,7 +21615,7 @@ ${codeblock}`, options);
20866
21615
  });
20867
21616
 
20868
21617
  // src/adapters/corpus/conceptnet.mjs
20869
- var import_meta4, PKG_ROOT2, SLICE_FILE, MAP_FILE, SEON_CONCEPTS_FILE, SEON_DEFINITIONS_FILE, TIER2_DIR, TIER2_MANIFEST_FILE, WORDNET_DIR, WORDNET_MANIFEST_FILE;
21618
+ var import_meta5, PKG_ROOT3, SLICE_FILE, MAP_FILE, SEON_CONCEPTS_FILE, SEON_DEFINITIONS_FILE, TIER2_DIR, TIER2_MANIFEST_FILE, WORDNET_DIR, WORDNET_MANIFEST_FILE;
20870
21619
  var init_conceptnet = __esm({
20871
21620
  "src/adapters/corpus/conceptnet.mjs"() {
20872
21621
  init_node_fs();
@@ -20876,15 +21625,15 @@ ${codeblock}`, options);
20876
21625
  init_node_path();
20877
21626
  init_dist();
20878
21627
  init_core();
20879
- import_meta4 = {};
20880
- PKG_ROOT2 = join(dirname(fileURLToPath2(import_meta4.url)), "..", "..", "..");
20881
- SLICE_FILE = join(PKG_ROOT2, "corpus", "conceptnet", "slice.jsonl");
20882
- MAP_FILE = join(PKG_ROOT2, "src", "adapters", "corpus", "conceptnet-map.toml");
20883
- SEON_CONCEPTS_FILE = join(PKG_ROOT2, "corpus", "seon", "concepts.jsonl");
20884
- SEON_DEFINITIONS_FILE = join(PKG_ROOT2, "corpus", "seon", "definitions.jsonl");
20885
- TIER2_DIR = join(PKG_ROOT2, "corpus", "tier2");
21628
+ import_meta5 = {};
21629
+ PKG_ROOT3 = join(dirname(fileURLToPath2(import_meta5.url)), "..", "..", "..");
21630
+ SLICE_FILE = join(PKG_ROOT3, "corpus", "conceptnet", "slice.jsonl");
21631
+ MAP_FILE = join(PKG_ROOT3, "src", "adapters", "corpus", "conceptnet-map.toml");
21632
+ SEON_CONCEPTS_FILE = join(PKG_ROOT3, "corpus", "seon", "concepts.jsonl");
21633
+ SEON_DEFINITIONS_FILE = join(PKG_ROOT3, "corpus", "seon", "definitions.jsonl");
21634
+ TIER2_DIR = join(PKG_ROOT3, "corpus", "tier2");
20886
21635
  TIER2_MANIFEST_FILE = join(TIER2_DIR, "manifest.json");
20887
- WORDNET_DIR = join(PKG_ROOT2, "corpus", "wordnet");
21636
+ WORDNET_DIR = join(PKG_ROOT3, "corpus", "wordnet");
20888
21637
  WORDNET_MANIFEST_FILE = join(WORDNET_DIR, "manifest.json");
20889
21638
  }
20890
21639
  });
@@ -20978,7 +21727,7 @@ ${codeblock}`, options);
20978
21727
  }
20979
21728
  };
20980
21729
  }
20981
- var import_meta5, NAMENET_DIR, EXTENSION_KINDS, CONCEPTNET_PREFER, BUILTIN_EXTENSIONS;
21730
+ var import_meta6, NAMENET_DIR, EXTENSION_KINDS, CONCEPTNET_PREFER, BUILTIN_EXTENSIONS;
20982
21731
  var init_extensions = __esm({
20983
21732
  "src/services/extensions.mjs"() {
20984
21733
  init_node_path();
@@ -20986,8 +21735,8 @@ ${codeblock}`, options);
20986
21735
  init_node_url();
20987
21736
  init_toml_config();
20988
21737
  init_conceptnet();
20989
- import_meta5 = {};
20990
- NAMENET_DIR = join(dirname(fileURLToPath2(import_meta5.url)), "..", "..", "corpus", "namenet");
21738
+ import_meta6 = {};
21739
+ NAMENET_DIR = join(dirname(fileURLToPath2(import_meta6.url)), "..", "..", "corpus", "namenet");
20991
21740
  EXTENSION_KINDS = Object.freeze(["corpus", "lexicon", "templates", "pack", "ontology"]);
20992
21741
  CONCEPTNET_PREFER = ["rdfs:subClassOf", "rdf:type", "mgx:usedFor", "mgx:partOf", "mgx:capableOf"];
20993
21742
  BUILTIN_EXTENSIONS = Object.freeze(builtinExtensions());
@@ -21187,6 +21936,19 @@ ${codeblock}`, options);
21187
21936
  inputSchema: classArg("The class to walk the inheritance edges of."),
21188
21937
  example: { class: "Field" }
21189
21938
  },
21939
+ {
21940
+ name: "tmct_related",
21941
+ tier: "cold",
21942
+ summary: "A term's synonyms (skos:altLabel) and related concepts (skos:related), from the memory graph's relation facts.",
21943
+ inputSchema: {
21944
+ type: "object",
21945
+ required: ["term"],
21946
+ properties: {
21947
+ term: { type: "string", description: "The term whose synonyms / related concepts you want." }
21948
+ }
21949
+ },
21950
+ example: { term: "sofa" }
21951
+ },
21190
21952
  {
21191
21953
  name: "tmct_architecture",
21192
21954
  tier: "cold",
@@ -21770,6 +22532,110 @@ ${hint}` : ""}${cand}`;
21770
22532
  resolveOrThrow(svc, symbol, "class");
21771
22533
  }
21772
22534
 
22535
+ // src/tools/handlers/tmct-related.mjs
22536
+ init_config();
22537
+
22538
+ // src/domain/skos-view.mjs
22539
+ init_hash();
22540
+ var SKOS_NS = "http://www.w3.org/2004/02/skos/core#";
22541
+ var DEFAULT_RELATION_MAP = {
22542
+ synonym: ["mgx:synonym"],
22543
+ related: ["mgx:relatedTo", "mgx:similarTo"]
22544
+ };
22545
+ function buildSkosConceptView(rows, { conceptBase = "concept:", relationMap = DEFAULT_RELATION_MAP } = {}) {
22546
+ const synonymPreds = new Set(relationMap.synonym || []);
22547
+ const relatedPreds = new Set(relationMap.related || []);
22548
+ const parent = /* @__PURE__ */ new Map();
22549
+ const ensure = (t) => {
22550
+ if (!parent.has(t)) parent.set(t, t);
22551
+ };
22552
+ const find = (x) => {
22553
+ let r = x;
22554
+ while (parent.get(r) !== r) r = parent.get(r);
22555
+ while (parent.get(x) !== r) {
22556
+ const next = parent.get(x);
22557
+ parent.set(x, r);
22558
+ x = next;
22559
+ }
22560
+ return r;
22561
+ };
22562
+ const union = (a, b) => {
22563
+ const ra = find(a), rb = find(b);
22564
+ if (ra === rb) return;
22565
+ if (ra < rb) parent.set(rb, ra);
22566
+ else parent.set(ra, rb);
22567
+ };
22568
+ const relatedRaw = [];
22569
+ for (const r of rows) {
22570
+ const p = r.predicate;
22571
+ if (!synonymPreds.has(p) && !relatedPreds.has(p)) continue;
22572
+ const s = normFactTerm(r.subject), o = normFactTerm(r.object);
22573
+ if (!s || !o) continue;
22574
+ ensure(s);
22575
+ ensure(o);
22576
+ if (synonymPreds.has(p)) union(s, o);
22577
+ else relatedRaw.push({ s, o });
22578
+ }
22579
+ const iriFor = (rep) => conceptBase + rep.replace(/ /g, "_");
22580
+ const componentTerms = /* @__PURE__ */ new Map();
22581
+ for (const t of parent.keys()) {
22582
+ const rep = find(t);
22583
+ if (!componentTerms.has(rep)) componentTerms.set(rep, /* @__PURE__ */ new Set());
22584
+ componentTerms.get(rep).add(t);
22585
+ }
22586
+ const concepts = [];
22587
+ for (const [rep, terms] of componentTerms) {
22588
+ const sorted = [...terms].sort();
22589
+ concepts.push({ id: iriFor(rep), prefLabel: rep, altLabels: sorted.filter((t) => t !== rep) });
22590
+ }
22591
+ concepts.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
22592
+ const seen = /* @__PURE__ */ new Set();
22593
+ const related = [];
22594
+ for (const { s, o } of relatedRaw) {
22595
+ const cs = iriFor(find(s)), co = iriFor(find(o));
22596
+ if (cs === co) continue;
22597
+ const key = cs < co ? `${cs}\0${co}` : `${co}\0${cs}`;
22598
+ if (seen.has(key)) continue;
22599
+ seen.add(key);
22600
+ related.push({ subject: cs, object: co });
22601
+ }
22602
+ related.sort((a, b) => `${a.subject}${a.object}` < `${b.subject}${b.object}` ? -1 : 1);
22603
+ const conceptIdForTerm = (term) => {
22604
+ const t = normFactTerm(term);
22605
+ return parent.has(t) ? iriFor(find(t)) : null;
22606
+ };
22607
+ return { concepts, related, conceptIdForTerm, namespace: SKOS_NS };
22608
+ }
22609
+ function relatedForTerm(rows, term, options = {}) {
22610
+ const view = buildSkosConceptView(rows, options);
22611
+ const conceptId = view.conceptIdForTerm(term);
22612
+ if (!conceptId) return null;
22613
+ const byId = new Map(view.concepts.map((c) => [c.id, c]));
22614
+ const concept = byId.get(conceptId);
22615
+ const queried = normFactTerm(term);
22616
+ const synonyms = [concept.prefLabel, ...concept.altLabels].filter((label) => label !== queried);
22617
+ const related = view.related.filter((r) => r.subject === conceptId || r.object === conceptId).map((r) => byId.get(r.subject === conceptId ? r.object : r.subject)).filter(Boolean);
22618
+ return { conceptId, prefLabel: concept.prefLabel, altLabels: concept.altLabels, synonyms, related };
22619
+ }
22620
+
22621
+ // src/tools/handlers/tmct-related.mjs
22622
+ init_memory_fallthrough();
22623
+ async function tmct_related(args, { config }) {
22624
+ const term = requiredArg(args, "term");
22625
+ const hit2 = relatedForTerm(await memoryFactRows(config), term);
22626
+ if (!hit2) {
22627
+ throw new ToolError(
22628
+ `no synonym or related facts for "${term}" in the memory graph. This view answers where the store holds mgx:synonym / mgx:relatedTo facts (a corpus import seeds them).`
22629
+ );
22630
+ }
22631
+ const lines = [`${hit2.prefLabel} [${hit2.conceptId}]`];
22632
+ if (hit2.synonyms.length) lines.push(`synonyms (skos:altLabel): ${hit2.synonyms.join(", ")}`);
22633
+ if (hit2.related.length) lines.push(`related (skos:related): ${hit2.related.map((c) => c.prefLabel).join(", ")}`);
22634
+ lines.push("(from the memory graph's mgx:synonym / mgx:relatedTo / mgx:similarTo facts)");
22635
+ return lines.join("\n");
22636
+ }
22637
+ tmct_related.ownsGraphLoad = true;
22638
+
21773
22639
  // src/tools/handlers/tmct-architecture.mjs
21774
22640
  init_codegraph();
21775
22641
  function tmct_architecture(args, { graph }) {
@@ -21847,6 +22713,7 @@ ${JSON.stringify(envelope, null, 2)}`;
21847
22713
  tmct_search,
21848
22714
  tmct_members,
21849
22715
  tmct_subclasses,
22716
+ tmct_related,
21850
22717
  tmct_architecture,
21851
22718
  tmct_exports,
21852
22719
  tmct_untested,
@@ -21949,6 +22816,134 @@ ${JSON.stringify(envelope, null, 2)}`;
21949
22816
  init_fuzzy();
21950
22817
  init_lexicon();
21951
22818
 
22819
+ // src/domain/reference-pack.mjs
22820
+ init_hash();
22821
+ init_lexicon();
22822
+ init_concept();
22823
+
22824
+ // src/adapters/corpus/reference-pack.mjs
22825
+ init_node_fs();
22826
+ init_node_url();
22827
+ init_node_path();
22828
+ var import_meta4 = {};
22829
+ var PKG_ROOT2 = join(dirname(fileURLToPath2(import_meta4.url)), "..", "..", "..");
22830
+
22831
+ // src/domain/dialogue-acts.mjs
22832
+ var DIALOGUE_ACT_DIMENSIONS = Object.freeze([
22833
+ "task",
22834
+ "autoFeedback",
22835
+ "alloFeedback",
22836
+ "turnManagement",
22837
+ "timeManagement",
22838
+ "discourseStructuring",
22839
+ "ownCommunicationManagement",
22840
+ "partnerCommunicationManagement",
22841
+ "socialObligationsManagement",
22842
+ "contactManagement"
22843
+ ]);
22844
+ var DIALOGUE_ACTS = Object.freeze({
22845
+ propositionalQuestion: Object.freeze({
22846
+ dimension: "task",
22847
+ gloss: "a yes/no question about whether a proposition holds ('does X import Y?')"
22848
+ }),
22849
+ checkQuestion: Object.freeze({
22850
+ dimension: "task",
22851
+ gloss: "a yes/no question whose asker already expects the answer yes ('..., right?')"
22852
+ }),
22853
+ setQuestion: Object.freeze({
22854
+ dimension: "task",
22855
+ gloss: "a wh-question asking for the members of a set ('what does X import?')"
22856
+ }),
22857
+ choiceQuestion: Object.freeze({
22858
+ dimension: "task",
22859
+ gloss: "a question asking which of the listed alternatives holds ('is X a module or a class?')"
22860
+ }),
22861
+ inform: Object.freeze({
22862
+ dimension: "task",
22863
+ gloss: "a declarative telling the addressee something (a teach turn; also tmct explaining its own function)"
22864
+ }),
22865
+ answer: Object.freeze({
22866
+ dimension: "task",
22867
+ gloss: "an inform that discharges a question just asked (an answer grounded in the graph)"
22868
+ }),
22869
+ confirm: Object.freeze({
22870
+ dimension: "task",
22871
+ gloss: "an answer 'yes' to a check question"
22872
+ }),
22873
+ disconfirm: Object.freeze({
22874
+ dimension: "task",
22875
+ gloss: "an answer 'no' to a check question"
22876
+ }),
22877
+ agreement: Object.freeze({
22878
+ dimension: "task",
22879
+ gloss: "an inform stating that the speaker holds what the addressee just stated to be true"
22880
+ }),
22881
+ disagreement: Object.freeze({
22882
+ dimension: "task",
22883
+ gloss: "an inform stating that the speaker holds what the addressee just stated to be false"
22884
+ }),
22885
+ correction: Object.freeze({
22886
+ dimension: "task",
22887
+ gloss: "a disagreement that also supplies the replacement ('no, I meant Y')"
22888
+ }),
22889
+ request: Object.freeze({
22890
+ dimension: "task",
22891
+ gloss: "asks the addressee to perform an action ('solve it' \u2014 a goal turn)"
22892
+ }),
22893
+ instruct: Object.freeze({
22894
+ dimension: "task",
22895
+ gloss: "a request the addressee is expected to carry out without negotiation (a bare imperative)"
22896
+ }),
22897
+ suggestion: Object.freeze({
22898
+ dimension: "task",
22899
+ gloss: "puts an action forward as advisable without claiming authority over the addressee"
22900
+ }),
22901
+ offer: Object.freeze({
22902
+ dimension: "task",
22903
+ gloss: "commits the speaker to an action, conditional on the addressee wanting it"
22904
+ }),
22905
+ autoPositive: Object.freeze({
22906
+ dimension: "autoFeedback",
22907
+ gloss: "the sender reports its own processing of the previous turn succeeded (an acknowledgement)"
22908
+ }),
22909
+ autoNegative: Object.freeze({
22910
+ dimension: "autoFeedback",
22911
+ gloss: "the sender reports its own processing of the previous turn failed \u2014 tmct's honest miss"
22912
+ }),
22913
+ initialGreeting: Object.freeze({
22914
+ dimension: "socialObligationsManagement",
22915
+ gloss: "opens an exchange of greetings ('hi')"
22916
+ }),
22917
+ returnGreeting: Object.freeze({
22918
+ dimension: "socialObligationsManagement",
22919
+ gloss: "answers a greeting with a greeting"
22920
+ }),
22921
+ thanking: Object.freeze({
22922
+ dimension: "socialObligationsManagement",
22923
+ gloss: "expresses gratitude for something the addressee did"
22924
+ }),
22925
+ apology: Object.freeze({
22926
+ dimension: "socialObligationsManagement",
22927
+ gloss: "expresses regret for something the speaker did"
22928
+ })
22929
+ });
22930
+ var LANE_DIALOGUE_ACTS = Object.freeze({
22931
+ teach: "inform",
22932
+ "ask-set": "setQuestion",
22933
+ "ask-propositional": "propositionalQuestion",
22934
+ goal: "request",
22935
+ imperative: "instruct",
22936
+ "honest-miss": "autoNegative",
22937
+ greeting: "initialGreeting",
22938
+ thanks: "thanking",
22939
+ help: "inform",
22940
+ // The guessing game's turns are task-dimension: a reply that discharges
22941
+ // the other side's move (a hint, a win, a rebuttal) is an answer; tmct
22942
+ // stating its own move (an opening, its next guess) is an inform.
22943
+ "game-answer": "answer",
22944
+ "game-inform": "inform"
22945
+ });
22946
+
21952
22947
  // src/services/chat-session.mjs
21953
22948
  init_node_path();
21954
22949
  init_node_fs();
@@ -22326,7 +23321,15 @@ ${JSON.stringify(envelope, null, 2)}`;
22326
23321
  "i"
22327
23322
  );
22328
23323
  var GOAL_TEACH_IMPERATIVE_RE = new RegExp(
22329
- `^(?:get|put|place)\\s+(?:(every|each|all|both)\\s+)?(?:the\\s+)?([\\w-]+?)\\s+(${PREP_SRC})\\s+([\\w-]+)[?.!]*$`,
23324
+ `^(?:get|put|place|stack)\\s+(?:(every|each|all|both)\\s+)?(?:the\\s+)?([\\w-]+?)\\s+(${PREP_SRC})\\s+([\\w-]+)[?.!]*$`,
23325
+ "i"
23326
+ );
23327
+ var GOAL_TEACH_NP_RE = new RegExp(
23328
+ `^the\\s+goal\\s+is\\s+(?:(every|each|all|both)\\s+)?(?:the\\s+)?([\\w-]+?)\\s+(${PREP_SRC})\\s+([\\w-]+)[?.!]*$`,
23329
+ "i"
23330
+ );
23331
+ var GOAL_CONJUNCT_RE = new RegExp(
23332
+ `^(?:(every|each|all)\\s+)?([\\w-]+)\\s+([a-z]+s)\\s+(${PREP_SRC})\\s+([\\w-]+)$`,
22330
23333
  "i"
22331
23334
  );
22332
23335
  var BOARD_REVERSE_LOC_RE = new RegExp(
@@ -22533,6 +23536,13 @@ ${JSON.stringify(envelope, null, 2)}`;
22533
23536
  if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
22534
23537
  return null;
22535
23538
  }
23539
+ function isaInconsistencyRefusal(posFact, disjointFact) {
23540
+ const cite = (f) => `${factPhrase(f)}${f.provenance ? ` (source: ${f.provenance})` : ""}`;
23541
+ return {
23542
+ text: `you've told me both ${cite(posFact)} and ${cite(disjointFact)} \u2014 together those contradict, and I won't derive an answer from an inconsistency. To settle it, say "forget that ${posFact.subject} is ${indefiniteArticleFor(posFact.object)} ${posFact.object}".`,
23543
+ replace: true
23544
+ };
23545
+ }
22536
23546
  function renderIsaChain(premises) {
22537
23547
  const step = (f) => `${factPhrase(f)}${f.provenance ? ` (source: ${f.provenance})` : ""}`;
22538
23548
  const first = premises[0];
@@ -22588,6 +23598,12 @@ ${JSON.stringify(envelope, null, 2)}`;
22588
23598
  return raw;
22589
23599
  }
22590
23600
  }
23601
+ function suggestibleSubjectPhrase(subject) {
23602
+ const m = String(subject || "").trim().match(/^(?:all|every|each|both|most|some)\s+([\w-]+)$/i);
23603
+ if (!m) return teachableSubjectOf(subject);
23604
+ const singular = teachableSubjectOf(singularizeSurface(m[1]));
23605
+ return `${indefiniteArticleFor(singular)} ${singular}`;
23606
+ }
22591
23607
  var QUANTIFIER_LEAD_RE = /^(?:every|each|all|any)\s+/i;
22592
23608
  function factTermVariants(normFactTerm2, term) {
22593
23609
  const t = normFactTerm2(term);
@@ -23067,8 +24083,13 @@ ${shown.map(renderFactLine).join("\n")}`,
23067
24083
  if (isa) {
23068
24084
  const subj = factTermVariants(normFactTerm2, isa[1]);
23069
24085
  const obj = factTermVariants(normFactTerm2, isa[2]);
23070
- const isaRows = await memoryFacts(memoryDir);
24086
+ const isaRows = await factRows(memoryDir, cache2);
23071
24087
  const onTerms = (f) => subj.has(f.subject) && obj.has(f.object);
24088
+ const { DISJOINT_PREDICATE: DISJOINT_PREDICATE2 } = await Promise.resolve().then(() => (init_syllogise(), syllogise_exports));
24089
+ const touchesAskedTerm = (f) => subj.has(f.subject) || subj.has(f.object) || obj.has(f.subject) || obj.has(f.object);
24090
+ if (isaRows.some((f) => f.predicate === DISJOINT_PREDICATE2 && isOperatorTaught(f) && touchesAskedTerm(f))) {
24091
+ return null;
24092
+ }
23072
24093
  const reply = isaPolarityReply(
23073
24094
  isaRows.find((f) => ISA_PREDICATES2.has(f.predicate) && onTerms(f)),
23074
24095
  isaRows.find((f) => f.predicate === NEG_SUBCLASS_PREDICATE && onTerms(f))
@@ -23096,10 +24117,20 @@ ${shown.map(renderFactLine).join("\n")}`,
23096
24117
  if (doesHave) {
23097
24118
  const subj = factTermVariants(normFactTerm2, doesHave[1]);
23098
24119
  const obj = factTermVariants(normFactTerm2, doesHave[2]);
23099
- const hit2 = (await memoryFacts(memoryDir)).find(
23100
- (f) => f.predicate === "mgx:hasA" && subj.has(f.subject) && obj.has(f.object)
24120
+ const HAS_PREDICATES = /* @__PURE__ */ new Set(["mgx:hasA", "tmct:has"]);
24121
+ const facts = await memoryFacts(memoryDir);
24122
+ const hasHit = (subjectSet) => facts.find(
24123
+ (f) => HAS_PREDICATES.has(f.predicate) && subjectSet.has(f.subject) && obj.has(f.object)
23101
24124
  );
24125
+ const hit2 = hasHit(subj);
23102
24126
  if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
24127
+ const isaStep = facts.find((f) => ISA_PREDICATES2.has(f.predicate) && subj.has(f.subject));
24128
+ if (isaStep) {
24129
+ const lifted = hasHit(factTermVariants(normFactTerm2, isaStep.object));
24130
+ if (lifted) {
24131
+ return { text: `yes \u2014 ${renderFactLine(isaStep)}; ${renderFactLine(lifted)}`, replace: true };
24132
+ }
24133
+ }
23103
24134
  return null;
23104
24135
  }
23105
24136
  const doAsk = positiveQuestionSurface(q).match(DO_VERB_ASK_RE);
@@ -23128,6 +24159,14 @@ ${shown.map(renderFactLine).join("\n")}`,
23128
24159
  }
23129
24160
  const base = capabilityBaseRateReply(doAsk[2], doAsk[3], facts);
23130
24161
  if (base) return base;
24162
+ if (!/\s/.test(doAsk[2].trim()) && !facts.some((f) => subj.has(f.subject) || subj.has(f.object))) {
24163
+ const noun = singularizeSurface(teachableSubjectOf(doAsk[2]));
24164
+ return {
24165
+ text: `I can't confirm that \u2014 I don't know anything about "${doAsk[2]}" yet. Teach me "a ${noun} can ${doAsk[3]}" (or "a ${noun} cannot ${doAsk[3]}") and I'll remember it.`,
24166
+ replace: true,
24167
+ miss: true
24168
+ };
24169
+ }
23131
24170
  }
23132
24171
  }
23133
24172
  const canDo = q.match(WHAT_CAN_DO_RE);
@@ -23550,11 +24589,55 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
23550
24589
  const objVariants = factTermVariants(normFactTerm2, stripTrailingDiscourseTag(isaAsk[2]));
23551
24590
  const isaSubject = focusLabel && IS_ADJECTIVE_PRONOUN_RE.test(isaAsk[1].trim()) ? focusLabel : isaAsk[1];
23552
24591
  const subjCandidates = new Set(factTermVariants(normFactTerm2, isaSubject));
24592
+ if ([...subjCandidates].some((s) => objVariants.has(s))) {
24593
+ const kindEcho = stripTrailingDiscourseTag(isaAsk[2]).trim();
24594
+ return {
24595
+ text: `yes \u2014 ${indefiniteArticleFor(kindEcho)} ${kindEcho} is ${indefiniteArticleFor(kindEcho)} ${kindEcho}, trivially: every kind is a kind of itself.`,
24596
+ replace: true
24597
+ };
24598
+ }
23553
24599
  const noun = await entityClassNoun(graph, isaSubject);
23554
24600
  if (noun) for (const v of factTermVariants(normFactTerm2, noun)) subjCandidates.add(v);
24601
+ const {
24602
+ findIsaChain: findIsaChain2,
24603
+ deriveDisjointViolations: deriveDisjointViolations2,
24604
+ SUBCLASS_PREDICATE: SC_PREDICATE,
24605
+ TYPE_PREDICATE: RDF_TYPE_PREDICATE,
24606
+ DISJOINT_PREDICATE: DISJOINT_PREDICATE2
24607
+ } = await Promise.resolve().then(() => (init_syllogise(), syllogise_exports));
24608
+ const isTaught = isOperatorTaught;
24609
+ const chainSubClassRows = isa.filter((f) => f.predicate === SC_PREDICATE && isTaught(f));
24610
+ const chainTypeRows = isa.filter((f) => f.predicate === RDF_TYPE_PREDICATE && isTaught(f));
24611
+ const chainSubClassEdges = chainSubClassRows.map((f) => [f.subject, f.object]);
24612
+ const chainTypeEdges = chainTypeRows.map((f) => [f.subject, f.object]);
24613
+ const mixedSubClassRows = isa.filter((f) => f.predicate === SC_PREDICATE);
24614
+ const mixedTypeRows = isa.filter((f) => f.predicate === RDF_TYPE_PREDICATE);
24615
+ const mixedTypeEdges = mixedTypeRows.map((f) => [f.subject, f.object]);
24616
+ const mixedSubClassEdges = mixedSubClassRows.map((f) => [f.subject, f.object]);
24617
+ const disjointRows = rows.filter((f) => f.predicate === DISJOINT_PREDICATE2 && isTaught(f));
24618
+ const disjointEdges = disjointRows.map((f) => [f.subject, f.object]);
24619
+ const disjointGateViolations = disjointRows.length ? deriveDisjointViolations2(
24620
+ mixedTypeEdges.concat(mixedSubClassEdges),
24621
+ mixedSubClassEdges,
24622
+ disjointEdges,
24623
+ { budget: 20, focus: /* @__PURE__ */ new Set([...subjCandidates, ...objVariants]) }
24624
+ ) : [];
24625
+ const disjointRefusalFor = (subj) => {
24626
+ const v = disjointGateViolations.find((vv) => vv.subject === subj && objVariants.has(vv.object));
24627
+ if (!v) return null;
24628
+ const posFact = isa.filter((f) => objVariants.has(f.object) && (f.subject === v.viaClass || f.subject === v.subject)).sort(byTrust)[0];
24629
+ const disjointFact = disjointRows.find((f) => f.subject === v.viaClass && f.object === v.object || f.subject === v.object && f.object === v.viaClass);
24630
+ if (!posFact || !disjointFact) return null;
24631
+ return isaInconsistencyRefusal(posFact, disjointFact);
24632
+ };
23555
24633
  const hit2 = isa.filter((f) => subjCandidates.has(f.subject) && objVariants.has(f.object)).sort(byTrust)[0];
23556
24634
  const negHit = rows.filter((f) => f.predicate === NEG_SUBCLASS_PREDICATE && subjCandidates.has(f.subject) && objVariants.has(f.object)).sort(byTrust)[0];
23557
- const polarityReply = isaPolarityReply(hit2, negHit);
24635
+ const directDisjoint = disjointRows.find((f) => subjCandidates.has(f.subject) && objVariants.has(f.object) || subjCandidates.has(f.object) && objVariants.has(f.subject));
24636
+ if (hit2 && !negHit) {
24637
+ const chainRefusal = disjointRefusalFor(hit2.subject);
24638
+ if (chainRefusal) return chainRefusal;
24639
+ }
24640
+ const polarityReply = isaPolarityReply(hit2, negHit || directDisjoint);
23558
24641
  if (polarityReply) return polarityReply;
23559
24642
  const ent = await resolveEntity(graph, isaSubject);
23560
24643
  if (ent) {
@@ -23572,34 +24655,26 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
23572
24655
  };
23573
24656
  }
23574
24657
  }
23575
- const { findIsaChain: findIsaChain2, SUBCLASS_PREDICATE: SC_PREDICATE, TYPE_PREDICATE: RDF_TYPE_PREDICATE } = await Promise.resolve().then(() => (init_syllogise(), syllogise_exports));
23576
- const isTaught = isOperatorTaught;
23577
- const chainSubClassRows = isa.filter((f) => f.predicate === SC_PREDICATE && isTaught(f));
23578
- const chainTypeRows = isa.filter((f) => f.predicate === RDF_TYPE_PREDICATE && isTaught(f));
23579
- const chainSubClassEdges = chainSubClassRows.map((f) => [f.subject, f.object]);
23580
- const chainTypeEdges = chainTypeRows.map((f) => [f.subject, f.object]);
23581
24658
  const factForStep = (step) => (step.predicate === SC_PREDICATE ? chainSubClassRows : chainTypeRows).find((f) => f.subject === step.subject && f.object === step.object);
23582
24659
  for (const subj of subjCandidates) {
23583
24660
  const chain = findIsaChain2(subj, objVariants, chainTypeEdges, chainSubClassEdges, { maxHops: 2 });
23584
24661
  if (!chain) continue;
24662
+ const chainRefusal = disjointRefusalFor(subj);
24663
+ if (chainRefusal) return chainRefusal;
23585
24664
  const premises = chain.map(factForStep);
23586
24665
  if (premises.every(Boolean)) return { text: `yes \u2014 ${renderIsaChain(premises)}`, replace: true };
23587
24666
  }
23588
- const mixedSubClassRows = isa.filter((f) => f.predicate === SC_PREDICATE);
23589
- const mixedTypeRows = isa.filter((f) => f.predicate === RDF_TYPE_PREDICATE);
23590
24667
  const mixedFactForStep = (step) => (step.predicate === SC_PREDICATE ? mixedSubClassRows : mixedTypeRows).find((f) => f.subject === step.subject && f.object === step.object);
23591
- const mixedTypeEdges = mixedTypeRows.map((f) => [f.subject, f.object]);
23592
- const mixedSubClassEdges = mixedSubClassRows.map((f) => [f.subject, f.object]);
23593
24668
  for (const subj of subjCandidates) {
23594
24669
  const chain = findIsaChain2(subj, objVariants, mixedTypeEdges, mixedSubClassEdges, { maxHops: 2 });
23595
24670
  if (!chain) continue;
24671
+ const chainRefusal = disjointRefusalFor(subj);
24672
+ if (chainRefusal) return chainRefusal;
23596
24673
  const premises = chain.map(mixedFactForStep);
23597
24674
  if (premises.every(Boolean) && premises.some(isTaught)) {
23598
24675
  return { text: `yes \u2014 ${renderIsaChain(premises)}`, replace: true };
23599
24676
  }
23600
24677
  }
23601
- const { deriveDisjointViolations: deriveDisjointViolations2, DISJOINT_PREDICATE: DISJOINT_PREDICATE2 } = await Promise.resolve().then(() => (init_syllogise(), syllogise_exports));
23602
- const disjointRows = rows.filter((f) => f.predicate === DISJOINT_PREDICATE2 && isTaught(f));
23603
24678
  const negSubjectMatch = isaSubject.match(/^(.*\S)\s+not$/i);
23604
24679
  const negSubject = negSubjectMatch && [
23605
24680
  negSubjectMatch[0],
@@ -23621,14 +24696,16 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
23621
24696
  };
23622
24697
  }
23623
24698
  if (disjointRows.length) {
23624
- const directDisjoint = disjointRows.find((f) => subjCandidates.has(f.subject) && objVariants.has(f.object) || subjCandidates.has(f.object) && objVariants.has(f.subject));
23625
- if (directDisjoint) return { text: `no \u2014 ${renderFactLine(directDisjoint)}`, replace: true };
23626
- const disjointEdges = disjointRows.map((f) => [f.subject, f.object]);
23627
- const violations = deriveDisjointViolations2(chainTypeEdges, chainSubClassEdges, disjointEdges, { budget: 10 });
24699
+ const violations = deriveDisjointViolations2(
24700
+ chainTypeEdges.concat(chainSubClassEdges),
24701
+ chainSubClassEdges,
24702
+ disjointEdges,
24703
+ { budget: 10 }
24704
+ );
23628
24705
  for (const subj of subjCandidates) {
23629
24706
  const v = violations.find((vv) => vv.subject === subj && objVariants.has(vv.object));
23630
24707
  if (!v) continue;
23631
- const typeFact = chainTypeRows.find((f) => f.subject === v.subject && f.object === v.viaType);
24708
+ const typeFact = chainTypeRows.concat(chainSubClassRows).find((f) => f.subject === v.subject && f.object === v.viaType);
23632
24709
  const disjointFact = disjointRows.find((f) => f.subject === v.viaClass && f.object === v.object || f.subject === v.object && f.object === v.viaClass);
23633
24710
  const parts = [typeFact, disjointFact].filter(Boolean).map(renderFactLine);
23634
24711
  return { text: `no \u2014 ${parts.length ? parts.join("; ") : `${v.viaClass} and ${v.object} are disjoint.`}`, replace: true };
@@ -23725,6 +24802,14 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
23725
24802
  // still a MISS in the turn record — honest wording, not an answer
23726
24803
  };
23727
24804
  }
24805
+ const converseHit = isa.filter((f) => subjCandidates.has(f.object) && objVariants.has(f.subject)).sort(byTrust)[0];
24806
+ if (converseHit) {
24807
+ return {
24808
+ text: `I can't confirm that \u2014 what I know runs the other way: ${renderFactLine(converseHit)}. A kind doesn't reverse. If it's true, teach me: "every ${subjectWord} is a ${kindWord}".`,
24809
+ replace: true,
24810
+ miss: true
24811
+ };
24812
+ }
23728
24813
  if (!ent && !noun && !isPronoun(subjectWord) && !rows.some((f) => subjCandidates.has(f.subject) || subjCandidates.has(f.object))) {
23729
24814
  return {
23730
24815
  text: `I can't confirm that \u2014 I don't know "${subjectWord}" at all yet. If it's true, teach me: "${subjectWord} is a kind of ${kindWord}".`,
@@ -23902,7 +24987,7 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
23902
24987
  }
23903
24988
  }
23904
24989
  if (rows.some(subjectMatch) && !envelope?.parsed) {
23905
- return { text: `I don't have a fact saying ${teachableSubjectOf(subject)} is ${adjective}.`, replace: true };
24990
+ return { text: `I don't have a fact saying ${suggestibleSubjectPhrase(subject)} is ${adjective}.`, replace: true };
23906
24991
  }
23907
24992
  if (!envelope?.parsed) return unknownAdjectiveOffer(subject, adjective);
23908
24993
  }
@@ -24024,8 +25109,25 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
24024
25109
  capabilities: "see what /plan can plan over \u2014 built-in query tools and taught actions",
24025
25110
  syllogise: "materialize the entailed facts that follow from what's remembered about one term"
24026
25111
  };
25112
+ var IMPACT_CHANGE_VERBS = "(?:changed?|modif(?:y|ied)|edits?|edited|touch(?:es|ed)?|updates?|updated|alters?|altered|deletes?|deleted|removes?|removed|drops?|dropped)";
24027
25113
  var IMPACT_PARAPHRASE_RE = new RegExp(
24028
- "^what\\s+(?:would|will|might|could|does|do)?\\s*(?:breaks?|fails?|is\\s+affected|are\\s+affected|gets?\\s+affected|be\\s+affected|is\\s+impacted|be\\s+impacted)\\s+if\\s+(?:i|we|you|one|someone)\\s+(?:changed?|modif(?:y|ied)|edits?|edited|touch(?:es|ed)?|updates?|updated|alters?|altered)\\s+(?:the\\s+)?(.+?)[?.!\\s]*$",
25114
+ `^what\\s+(?:would|will|might|could|does|do)?\\s*(?:breaks?|fails?|happens?|stops?\\s+working|is\\s+affected|are\\s+affected|gets?\\s+affected|be\\s+affected|is\\s+impacted|be\\s+impacted)\\s+if\\s+(?:i|we|you|one|someone)\\s+${IMPACT_CHANGE_VERBS}\\s+(?:the\\s+)?(.+?)[?.!\\s]*$`,
25115
+ "i"
25116
+ );
25117
+ var IMPACT_REVERSED_RE = new RegExp(
25118
+ `^if\\s+(?:i|we|you|one|someone)\\s+${IMPACT_CHANGE_VERBS}\\s+(?:the\\s+)?(.+?),?\\s+what\\s+(?:would\\s+|will\\s+|might\\s+|could\\s+|does\\s+|do\\s+)?(?:breaks?|fails?|happens?|stops?\\s+working|is\\s+affected|are\\s+affected|gets?\\s+affected|be\\s+affected|would\\s+break|will\\s+break)[?.!\\s]*$`,
25119
+ "i"
25120
+ );
25121
+ var IMPACT_AFFECTED_BY_RE = new RegExp(
25122
+ "^what\\s+(?:is|are|gets?|would\\s+be|will\\s+be)\\s+(?:affected|impacted|broken)\\s+(?:by|when|if)\\s+(?:i\\s+|we\\s+|you\\s+)?(?:chang(?:e|es|ing)|edit(?:s|ing)?|modif(?:y|ies|ying)|touch(?:es|ing)?|updat(?:e|es|ing)|delet(?:e|es|ing)|remov(?:e|es|ing)|a\\s+change\\s+to)\\s+(?:the\\s+)?(.+?)[?.!\\s]*$",
25123
+ "i"
25124
+ );
25125
+ var IMPACT_SAFE_CHANGE_RE = new RegExp(
25126
+ "^(?:(?:can|could)\\s+(?:i|we|you|one|someone)\\s+safely|is\\s+it\\s+safe\\s+to)\\s+(?:change|edit|modify|touch|update|alter|delete|remove|drop)\\s+(?:the\\s+)?(.+?)[?.!\\s]*$",
25127
+ "i"
25128
+ );
25129
+ var IMPACT_NOUN_RE = new RegExp(
25130
+ "^(?:what(?:'s|\\s+is)\\s+the\\s+)?(?:blast\\s+radius|impact)\\s+(?:of|for)\\s+(?:chang(?:ing|es)\\s+|editing\\s+|modifying\\s+|touching\\s+|updating\\s+|deleting\\s+|removing\\s+)?(?:the\\s+)?(.+?)[?.!\\s]*$",
24029
25131
  "i"
24030
25132
  );
24031
25133
  var BASE_QUALIFIER_SRC = "as\\s+(?:its|the|an?)?\\s*(?:base\\s+class|parent\\s+class|base|parent)";