@fenglimg/fabric-server 2.3.0-rc.1 → 2.3.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/index.ts
2
2
  import { existsSync as existsSync9 } from "fs";
3
- import { readFile as readFile19 } from "fs/promises";
4
- import { join as join23, resolve as resolve4 } from "path";
3
+ import { readFile as readFile20 } from "fs/promises";
4
+ import { join as join25, resolve as resolve4 } from "path";
5
5
  import { fileURLToPath } from "url";
6
6
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -742,7 +742,7 @@ var SUPPORTED_EMBED_MODELS = /* @__PURE__ */ new Set([
742
742
  function readEmbedConfig(projectRoot) {
743
743
  try {
744
744
  const config = readFabricConfig(projectRoot);
745
- const enabled = config.embed_enabled === true;
745
+ const enabled = config.embed_enabled !== false;
746
746
  const rawWeight = config.embed_weight;
747
747
  const weight = typeof rawWeight === "number" && Number.isInteger(rawWeight) && rawWeight >= 0 && rawWeight <= 49 ? rawWeight : 30;
748
748
  const rawModel = config.embed_model;
@@ -817,6 +817,15 @@ function readOrphanDemoteThresholdDays(projectRoot) {
817
817
  return {};
818
818
  }
819
819
  }
820
+ function readFusion(projectRoot) {
821
+ try {
822
+ const raw = readFabricConfig(projectRoot).fusion;
823
+ if (raw === "rrf" || raw === "additive") return raw;
824
+ return "auto";
825
+ } catch {
826
+ return "auto";
827
+ }
828
+ }
820
829
  function readConflictLintThreshold(projectRoot) {
821
830
  try {
822
831
  const cfgPath = join4(projectRoot, ".fabric", "fabric-config.json");
@@ -1534,11 +1543,44 @@ function buildBm25Model(docs) {
1534
1543
  for (const field of BM25_FIELDS) {
1535
1544
  avgFieldLength[field] = totalDocs > 0 ? totalFieldLength[field] / totalDocs : 0;
1536
1545
  }
1546
+ const serialized = {
1547
+ version: 1,
1548
+ totalDocs,
1549
+ documentFrequency: [...documentFrequency],
1550
+ avgFieldLength,
1551
+ perDoc: [...perDoc].map(([id, stats]) => ({
1552
+ id,
1553
+ fieldTermFreq: emptyFieldRecord(() => []),
1554
+ fieldLength: { ...stats.fieldLength }
1555
+ }))
1556
+ };
1557
+ for (const entry of serialized.perDoc) {
1558
+ const stats = perDoc.get(entry.id);
1559
+ if (stats === void 0) continue;
1560
+ for (const field of BM25_FIELDS) {
1561
+ entry.fieldTermFreq[field] = [...stats.fieldTermFreq[field]];
1562
+ }
1563
+ }
1564
+ return modelFromStats(serialized);
1565
+ }
1566
+ function modelFromStats(serialized) {
1567
+ const totalDocs = serialized.totalDocs;
1568
+ const documentFrequency = new Map(serialized.documentFrequency);
1569
+ const avgFieldLength = serialized.avgFieldLength;
1570
+ const perDoc = /* @__PURE__ */ new Map();
1571
+ for (const entry of serialized.perDoc) {
1572
+ const fieldTermFreq = emptyFieldRecord(() => /* @__PURE__ */ new Map());
1573
+ for (const field of BM25_FIELDS) {
1574
+ fieldTermFreq[field] = new Map(entry.fieldTermFreq[field]);
1575
+ }
1576
+ perDoc.set(entry.id, { fieldTermFreq, fieldLength: entry.fieldLength });
1577
+ }
1537
1578
  const idf = (term) => {
1538
1579
  const n = documentFrequency.get(term) ?? 0;
1539
1580
  return Math.log(1 + (totalDocs - n + 0.5) / (n + 0.5));
1540
1581
  };
1541
1582
  return {
1583
+ __serialized: serialized,
1542
1584
  scoreDoc(id, queryTerms) {
1543
1585
  const data = perDoc.get(id);
1544
1586
  if (data === void 0 || queryTerms.length === 0) {
@@ -1574,9 +1616,48 @@ function buildBm25Model(docs) {
1574
1616
  }
1575
1617
  };
1576
1618
  }
1619
+ function serializeBm25Model(model) {
1620
+ return model.__serialized;
1621
+ }
1622
+ function rehydrateBm25Model(serialized) {
1623
+ return modelFromStats(serialized);
1624
+ }
1577
1625
  function buildQueryTerms(text) {
1578
1626
  return tokenize(text);
1579
1627
  }
1628
+ function rankDocuments(model, ids, queryTerms) {
1629
+ if (queryTerms.length === 0) {
1630
+ return /* @__PURE__ */ new Map();
1631
+ }
1632
+ const scored = [];
1633
+ ids.forEach((id, order) => {
1634
+ const score = model.scoreDoc(id, queryTerms);
1635
+ if (score > 0) {
1636
+ scored.push({ id, score, order });
1637
+ }
1638
+ });
1639
+ scored.sort((a, b) => a.score !== b.score ? b.score - a.score : a.order - b.order);
1640
+ const ranks = /* @__PURE__ */ new Map();
1641
+ scored.forEach((entry, index) => {
1642
+ ranks.set(entry.id, index + 1);
1643
+ });
1644
+ return ranks;
1645
+ }
1646
+ function rankByScore(ids, scores) {
1647
+ const scored = [];
1648
+ ids.forEach((id, order) => {
1649
+ const score = scores.get(id) ?? 0;
1650
+ if (score > 0) {
1651
+ scored.push({ id, score, order });
1652
+ }
1653
+ });
1654
+ scored.sort((a, b) => a.score !== b.score ? b.score - a.score : a.order - b.order);
1655
+ const ranks = /* @__PURE__ */ new Map();
1656
+ scored.forEach((entry, index) => {
1657
+ ranks.set(entry.id, index + 1);
1658
+ });
1659
+ return ranks;
1660
+ }
1580
1661
 
1581
1662
  // src/services/conflict-lint.ts
1582
1663
  function buildSimilarityModel(docs) {
@@ -2670,13 +2751,37 @@ var LEDGER_DUAL_WRITE_METRIC_NAMES = {
2670
2751
  edit_intent_checked: METRIC_COUNTER_NAMES.edit_intent_checked
2671
2752
  };
2672
2753
 
2754
+ // src/services/plan-context.ts
2755
+ import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile2 } from "fs/promises";
2756
+ import { join as join10 } from "path";
2757
+
2673
2758
  // src/services/vector-retrieval.ts
2759
+ import { mkdirSync } from "fs";
2760
+ import { join as join9 } from "path";
2761
+ import { resolveGlobalRoot as resolveGlobalRoot3 } from "@fenglimg/fabric-shared";
2674
2762
  var embedderLoad;
2675
2763
  var OPTIONAL_EMBED_PACKAGE = "fastembed";
2764
+ var embedderModuleLoader = (name) => import(name);
2765
+ var MISSING_EMBEDDER_HINT = "[fabric] vector semantic recall is enabled but the optional 'fastembed' package is unavailable \u2014 falling back to text-only ranking. Install it where the server resolves modules (e.g. `npm i -g fastembed`) to enable embeddings, or set embed_enabled:false to silence this.\n";
2766
+ var defaultMissingEmbedderHint = () => {
2767
+ process.stderr.write(MISSING_EMBEDDER_HINT);
2768
+ };
2769
+ var missingEmbedderHinted = false;
2770
+ var emitMissingEmbedderHint = defaultMissingEmbedderHint;
2771
+ function hintMissingEmbedderOnce() {
2772
+ if (missingEmbedderHinted) {
2773
+ return;
2774
+ }
2775
+ missingEmbedderHinted = true;
2776
+ emitMissingEmbedderHint();
2777
+ }
2778
+ function defaultEmbedCacheDir() {
2779
+ return join9(resolveGlobalRoot3(), "cache", "embed");
2780
+ }
2676
2781
  function buildEmbedInitOptions(modelName) {
2677
2782
  return {
2678
2783
  maxLength: 512,
2679
- cacheDir: process.env.FABRIC_EMBED_CACHE_DIR,
2784
+ cacheDir: process.env.FABRIC_EMBED_CACHE_DIR ?? defaultEmbedCacheDir(),
2680
2785
  ...typeof modelName === "string" && modelName.length > 0 ? { model: modelName } : {}
2681
2786
  };
2682
2787
  }
@@ -2685,11 +2790,14 @@ async function loadEmbedder(modelName) {
2685
2790
  embedderLoad = (async () => {
2686
2791
  try {
2687
2792
  const moduleName = OPTIONAL_EMBED_PACKAGE;
2688
- const mod = await import(moduleName);
2793
+ const mod = await embedderModuleLoader(moduleName);
2689
2794
  if (mod?.FlagEmbedding?.init === void 0) {
2795
+ hintMissingEmbedderOnce();
2690
2796
  return null;
2691
2797
  }
2692
- const model = await mod.FlagEmbedding.init(buildEmbedInitOptions(modelName));
2798
+ const initOpts = buildEmbedInitOptions(modelName);
2799
+ mkdirSync(initOpts.cacheDir, { recursive: true });
2800
+ const model = await mod.FlagEmbedding.init(initOpts);
2693
2801
  return {
2694
2802
  async embed(texts) {
2695
2803
  const out = [];
@@ -2702,6 +2810,7 @@ async function loadEmbedder(modelName) {
2702
2810
  }
2703
2811
  };
2704
2812
  } catch {
2813
+ hintMissingEmbedderOnce();
2705
2814
  return null;
2706
2815
  }
2707
2816
  })();
@@ -2732,7 +2841,7 @@ function cosineSimilarity(a, b) {
2732
2841
  if (!Number.isFinite(sim)) {
2733
2842
  return 0;
2734
2843
  }
2735
- return Math.max(-1, Math.min(1, sim));
2844
+ return Math.max(0, Math.min(1, sim));
2736
2845
  }
2737
2846
  var docVectorCache = /* @__PURE__ */ new Map();
2738
2847
  var DOC_VECTOR_CACHE_MAX = 1e4;
@@ -2837,56 +2946,28 @@ async function planContext(projectRoot, input) {
2837
2946
  ...input.known_tech ?? [],
2838
2947
  ...Object.values(input.detected_entities ?? {}).flat()
2839
2948
  ].join(" ");
2840
- const scoringContext = {
2841
- nowMs: Date.now(),
2842
- targetPaths: input.target_paths ?? dedupePaths(input.paths),
2843
- queryTerms: buildQueryTerms(queryText)
2844
- };
2845
2949
  const storeRawItems = await buildCrossStoreRawItems(projectRoot).catch(() => []);
2846
2950
  const { rawItems: allRawItems, suppressedStableIds } = partitionEmptyShells(storeRawItems);
2847
2951
  const effectiveLayerFilter = input.layer_filter ?? readDefaultLayerFilter(projectRoot);
2848
2952
  const rawItems = effectiveLayerFilter === "both" ? allRawItems : allRawItems.filter((item) => item.description.knowledge_layer === effectiveLayerFilter);
2849
- const docTexts = /* @__PURE__ */ new Map();
2850
- for (const item of rawItems) {
2851
- docTexts.set(item.stable_id, documentTextForItem(item.description));
2852
- }
2853
- scoringContext.docTexts = docTexts;
2854
- if (scoringContext.queryTerms.length > 0 && rawItems.length > 0) {
2855
- scoringContext.bm25 = getOrBuildBm25Model(revision, rawItems, docTexts);
2856
- }
2857
- scoringContext.scopeRank = buildScopeRankMap(rawItems, projectRoot);
2858
- const embedConfig = readEmbedConfig(projectRoot);
2859
- if (embedConfig.enabled && queryText.trim().length > 0 && rawItems.length > 0) {
2860
- const embedder = await loadEmbedder(embedConfig.model);
2861
- const vectorScores = await buildVectorScores(
2862
- embedder,
2863
- queryText,
2864
- rawItems.map((item) => ({
2865
- stable_id: item.stable_id,
2866
- text: docTexts.get(item.stable_id) ?? documentTextForItem(item.description)
2867
- }))
2868
- );
2869
- if (vectorScores !== null) {
2870
- scoringContext.vectorScores = vectorScores;
2871
- scoringContext.vectorWeight = embedConfig.weight;
2872
- }
2873
- }
2874
- const scoredSorted = sortDescriptionItems(rawItems, scoringContext);
2875
- const seenStableIds = /* @__PURE__ */ new Set();
2876
- const rankedScored = scoredSorted.filter(({ item }) => {
2877
- if (seenStableIds.has(item.stable_id)) return false;
2878
- seenStableIds.add(item.stable_id);
2879
- return true;
2953
+ const scoringContext = await buildScoringContext(projectRoot, revision, rawItems, {
2954
+ queryText,
2955
+ targetPaths: input.target_paths ?? dedupePaths(input.paths)
2880
2956
  });
2957
+ const rankedScored = rankDescriptionItems(rawItems, scoringContext, "triage");
2881
2958
  const rankedCandidates = rankedScored.map((entry) => entry.item);
2882
- const topK = readPlanContextTopK(projectRoot);
2883
- const cappedScored = rankedScored.slice(0, topK);
2884
- const relevanceRatio = readRecallRelevanceRatio(projectRoot);
2885
- const hasQuery = scoringContext.queryTerms.length > 0;
2886
- const maxScore = rankedScored.length > 0 ? rankedScored[0].score : 0;
2887
- const relevanceFloor = maxScore * relevanceRatio;
2888
- const survivingScored = hasQuery && maxScore > 0 && relevanceRatio > 0 ? cappedScored.filter((entry) => entry.score >= relevanceFloor) : cappedScored;
2959
+ const survivingScored = rankDescriptionItems(rawItems, scoringContext, "recall", {
2960
+ topK: readPlanContextTopK(projectRoot),
2961
+ relevanceRatio: readRecallRelevanceRatio(projectRoot)
2962
+ });
2889
2963
  const topKCandidates = survivingScored.map((entry) => entry.item);
2964
+ const candidateScores = /* @__PURE__ */ new Map();
2965
+ for (const entry of survivingScored) {
2966
+ candidateScores.set(entry.item.stable_id, {
2967
+ score: entry.score,
2968
+ score_breakdown: scoreBreakdownForItem(entry.item, scoringContext)
2969
+ });
2970
+ }
2890
2971
  const topKIds = new Set(topKCandidates.map((item) => item.stable_id));
2891
2972
  const retrievalDropped = rankedCandidates.filter((item) => !topKIds.has(item.stable_id)).map((item) => ({ id: item.stable_id, reason: "retrieval_budget" }));
2892
2973
  const omittedCandidateCount = retrievalDropped.length;
@@ -2992,6 +3073,8 @@ async function planContext(projectRoot, input) {
2992
3073
  // ONLY when at least one neighbour was actually appended. Empty (graph-empty
2993
3074
  // no-op) → field omitted, steady-state wire shape unchanged.
2994
3075
  ...Object.keys(relatedAppended).length > 0 ? { related_appended: relatedAppended } : {},
3076
+ // P1 recall-observability: runtime-only score channel (Map → {} on the wire).
3077
+ ...candidateScores.size > 0 ? { candidate_scores: candidateScores } : {},
2995
3078
  ...payloadTrimDropped > 0 ? { payload_trimmed: true } : {},
2996
3079
  ...payloadOverBudget ? { payload_over_budget: true } : {}
2997
3080
  };
@@ -3080,10 +3163,38 @@ function partitionEmptyShells(items) {
3080
3163
  }
3081
3164
  var bm25ModelCache = null;
3082
3165
  var bm25BuildCount = 0;
3083
- function getOrBuildBm25Model(revision, rawItems, docTexts) {
3166
+ var BM25_CACHE_DIR = ".fabric/cache/bm25";
3167
+ function bm25CachePath(projectRoot, revision) {
3168
+ const safe = revision.replace(/[^A-Za-z0-9_-]/g, "_");
3169
+ return join10(projectRoot, BM25_CACHE_DIR, `${safe}.json`);
3170
+ }
3171
+ async function loadBm25ModelFromDisk(projectRoot, revision) {
3172
+ try {
3173
+ const raw = await readFile5(bm25CachePath(projectRoot, revision), "utf8");
3174
+ const parsed = JSON.parse(raw);
3175
+ if (parsed.version !== 1) return null;
3176
+ return rehydrateBm25Model(parsed);
3177
+ } catch {
3178
+ return null;
3179
+ }
3180
+ }
3181
+ async function saveBm25ModelToDisk(projectRoot, revision, model) {
3182
+ try {
3183
+ const path = bm25CachePath(projectRoot, revision);
3184
+ await mkdir4(join10(projectRoot, BM25_CACHE_DIR), { recursive: true });
3185
+ await writeFile2(path, JSON.stringify(serializeBm25Model(model)), "utf8");
3186
+ } catch {
3187
+ }
3188
+ }
3189
+ async function getOrBuildBm25Model(projectRoot, revision, rawItems, docTexts) {
3084
3190
  if (bm25ModelCache !== null && bm25ModelCache.revision === revision) {
3085
3191
  return bm25ModelCache.model;
3086
3192
  }
3193
+ const fromDisk = await loadBm25ModelFromDisk(projectRoot, revision);
3194
+ if (fromDisk !== null) {
3195
+ bm25ModelCache = { revision, model: fromDisk };
3196
+ return fromDisk;
3197
+ }
3087
3198
  bm25BuildCount += 1;
3088
3199
  const model = buildBm25Model(
3089
3200
  rawItems.map((item) => ({
@@ -3092,6 +3203,7 @@ function getOrBuildBm25Model(revision, rawItems, docTexts) {
3092
3203
  }))
3093
3204
  );
3094
3205
  bm25ModelCache = { revision, model };
3206
+ await saveBm25ModelToDisk(projectRoot, revision, model);
3095
3207
  return model;
3096
3208
  }
3097
3209
  function compareStableIds(a, b) {
@@ -3137,6 +3249,51 @@ function compareScopeThenId(left, right, scopeRank) {
3137
3249
  }
3138
3250
  return compareStableIds(left.stable_id, right.stable_id);
3139
3251
  }
3252
+ async function buildScoringContext(projectRoot, revision, rawItems, opts) {
3253
+ const scoringContext = {
3254
+ nowMs: Date.now(),
3255
+ targetPaths: opts.targetPaths,
3256
+ queryTerms: buildQueryTerms(opts.queryText)
3257
+ };
3258
+ const docTexts = /* @__PURE__ */ new Map();
3259
+ for (const item of rawItems) {
3260
+ docTexts.set(item.stable_id, documentTextForItem(item.description));
3261
+ }
3262
+ scoringContext.docTexts = docTexts;
3263
+ if (scoringContext.queryTerms.length > 0 && rawItems.length > 0) {
3264
+ scoringContext.bm25 = await getOrBuildBm25Model(projectRoot, revision, rawItems, docTexts);
3265
+ }
3266
+ scoringContext.scopeRank = buildScopeRankMap(rawItems, projectRoot);
3267
+ const embedConfig = readEmbedConfig(projectRoot);
3268
+ if (embedConfig.enabled && opts.queryText.trim().length > 0 && rawItems.length > 0) {
3269
+ const embedder = await loadEmbedder(embedConfig.model);
3270
+ const vectorScores = await buildVectorScores(
3271
+ embedder,
3272
+ opts.queryText,
3273
+ rawItems.map((item) => ({
3274
+ stable_id: item.stable_id,
3275
+ text: docTexts.get(item.stable_id) ?? documentTextForItem(item.description)
3276
+ }))
3277
+ );
3278
+ if (vectorScores !== null) {
3279
+ scoringContext.vectorScores = vectorScores;
3280
+ scoringContext.vectorWeight = embedConfig.weight;
3281
+ }
3282
+ }
3283
+ const configuredFusion = readFusion(projectRoot);
3284
+ const vectorActive = scoringContext.vectorScores !== void 0 && scoringContext.vectorScores.size > 0;
3285
+ scoringContext.fusion = configuredFusion === "auto" ? vectorActive ? "rrf" : "additive" : configuredFusion;
3286
+ if (scoringContext.fusion === "rrf" && scoringContext.queryTerms.length > 0 && rawItems.length > 0) {
3287
+ const rankIds = rawItems.map((item) => item.stable_id).sort((a, b) => compareStableIds(a, b));
3288
+ if (scoringContext.bm25 !== void 0) {
3289
+ scoringContext.bm25Ranks = rankDocuments(scoringContext.bm25, rankIds, scoringContext.queryTerms);
3290
+ }
3291
+ if (scoringContext.vectorScores !== void 0) {
3292
+ scoringContext.vectorRanks = rankByScore(rankIds, scoringContext.vectorScores);
3293
+ }
3294
+ }
3295
+ return scoringContext;
3296
+ }
3140
3297
  function sortDescriptionItems(rawItems, scoringContext) {
3141
3298
  if (scoringContext === void 0) {
3142
3299
  return [...rawItems].sort((left, right) => compareStableIds(left.stable_id, right.stable_id)).map((item) => ({ item, score: 0 }));
@@ -3151,6 +3308,25 @@ function sortDescriptionItems(rawItems, scoringContext) {
3151
3308
  );
3152
3309
  return scored;
3153
3310
  }
3311
+ function rankDescriptionItems(items, scoringContext, mode, options = {}) {
3312
+ const sorted = sortDescriptionItems(items, scoringContext);
3313
+ const seen = /* @__PURE__ */ new Set();
3314
+ const rankedScored = sorted.filter(({ item }) => {
3315
+ if (seen.has(item.stable_id)) return false;
3316
+ seen.add(item.stable_id);
3317
+ return true;
3318
+ });
3319
+ if (mode === "triage") {
3320
+ return rankedScored;
3321
+ }
3322
+ const topK = options.topK ?? rankedScored.length;
3323
+ const cappedScored = rankedScored.slice(0, topK);
3324
+ const relevanceRatio = options.relevanceRatio ?? 0;
3325
+ const hasQuery = scoringContext.queryTerms.length > 0;
3326
+ const maxScore = rankedScored.length > 0 ? rankedScored[0].score : 0;
3327
+ const relevanceFloor = maxScore * relevanceRatio;
3328
+ return hasQuery && maxScore > 0 && relevanceRatio > 0 ? cappedScored.filter((entry) => entry.score >= relevanceFloor) : cappedScored;
3329
+ }
3154
3330
  function documentTextForItem(description) {
3155
3331
  return [
3156
3332
  description.summary,
@@ -3199,6 +3375,9 @@ var BM25_WEIGHT = 50;
3199
3375
  var SALIENCE_PROVEN = 15;
3200
3376
  var SALIENCE_VERIFIED = 8;
3201
3377
  var SALIENCE_DRAFT = 0;
3378
+ var RRF_K = 10;
3379
+ var RRF_NORMALIZATION = 2e3;
3380
+ var RRF_STRUCTURAL_SCALE = 0.2;
3202
3381
  function salienceScore(item) {
3203
3382
  switch (item.description?.maturity) {
3204
3383
  case "proven":
@@ -3209,35 +3388,87 @@ function salienceScore(item) {
3209
3388
  return SALIENCE_DRAFT;
3210
3389
  }
3211
3390
  }
3212
- function scoreDescriptionItem(item, context) {
3213
- let score = 0;
3214
- if (context.bm25 !== void 0 && context.queryTerms.length > 0) {
3215
- score += BM25_WEIGHT * context.bm25.scoreDoc(item.stable_id, context.queryTerms);
3391
+ function contentScore(item, context) {
3392
+ const hasQuery = context.queryTerms.length > 0;
3393
+ if (context.fusion === "rrf" && hasQuery) {
3394
+ let rrf = 0;
3395
+ const bm25Rank = context.bm25Ranks?.get(item.stable_id);
3396
+ if (bm25Rank !== void 0) rrf += 1 / (RRF_K + bm25Rank);
3397
+ const vectorRank = context.vectorRanks?.get(item.stable_id);
3398
+ if (vectorRank !== void 0) rrf += 1 / (RRF_K + vectorRank);
3399
+ return RRF_NORMALIZATION * rrf;
3400
+ }
3401
+ let content = 0;
3402
+ if (context.bm25 !== void 0 && hasQuery) {
3403
+ content += BM25_WEIGHT * context.bm25.scoreDoc(item.stable_id, context.queryTerms);
3216
3404
  }
3217
3405
  if (context.vectorScores !== void 0) {
3218
- score += (context.vectorWeight ?? 0) * (context.vectorScores.get(item.stable_id) ?? 0);
3406
+ content += (context.vectorWeight ?? 0) * (context.vectorScores.get(item.stable_id) ?? 0);
3219
3407
  }
3220
- score += salienceScore(item);
3408
+ return content;
3409
+ }
3410
+ function recencyBoost(item, context) {
3221
3411
  const createdAtRaw = item.description?.created_at;
3222
3412
  if (typeof createdAtRaw === "string" && createdAtRaw.length > 0) {
3223
3413
  const createdMs = Date.parse(createdAtRaw);
3224
3414
  if (Number.isFinite(createdMs) && context.nowMs - createdMs < RECENCY_WINDOW_MS) {
3225
- score += RECENCY_BOOST;
3415
+ return RECENCY_BOOST;
3226
3416
  }
3227
3417
  }
3228
- if (context.targetPaths.length > 0) {
3229
- const relevancePaths = item.description?.relevance_paths ?? [];
3230
- let best = 0;
3231
- outer: for (const rp of relevancePaths) {
3232
- for (const tp of context.targetPaths) {
3233
- const tier = localityTier(rp, tp);
3234
- if (tier > best) best = tier;
3235
- if (best === LOCALITY_SAME_FILE) break outer;
3236
- }
3418
+ return 0;
3419
+ }
3420
+ function localityBoost(item, context) {
3421
+ if (context.targetPaths.length === 0) return 0;
3422
+ const relevancePaths = item.description?.relevance_paths ?? [];
3423
+ let best = 0;
3424
+ outer: for (const rp of relevancePaths) {
3425
+ for (const tp of context.targetPaths) {
3426
+ const tier = localityTier(rp, tp);
3427
+ if (tier > best) best = tier;
3428
+ if (best === LOCALITY_SAME_FILE) break outer;
3237
3429
  }
3238
- score += best;
3239
3430
  }
3240
- return score;
3431
+ return best;
3432
+ }
3433
+ function structuralScaleFor(context) {
3434
+ return context.fusion === "rrf" && context.queryTerms.length > 0 ? RRF_STRUCTURAL_SCALE : 1;
3435
+ }
3436
+ function scoreDescriptionItem(item, context) {
3437
+ const content = contentScore(item, context);
3438
+ const structural = salienceScore(item) + recencyBoost(item, context) + localityBoost(item, context);
3439
+ return content + structuralScaleFor(context) * structural;
3440
+ }
3441
+ function scoreBreakdownForItem(item, context) {
3442
+ const hasQuery = context.queryTerms.length > 0;
3443
+ const rrfMode = context.fusion === "rrf" && hasQuery;
3444
+ let bm25 = 0;
3445
+ let vector = 0;
3446
+ let bm25Rank;
3447
+ let vectorRank;
3448
+ if (rrfMode) {
3449
+ bm25Rank = context.bm25Ranks?.get(item.stable_id);
3450
+ if (bm25Rank !== void 0) bm25 = RRF_NORMALIZATION * (1 / (RRF_K + bm25Rank));
3451
+ vectorRank = context.vectorRanks?.get(item.stable_id);
3452
+ if (vectorRank !== void 0) vector = RRF_NORMALIZATION * (1 / (RRF_K + vectorRank));
3453
+ } else {
3454
+ bm25 = context.bm25 !== void 0 && hasQuery ? BM25_WEIGHT * context.bm25.scoreDoc(item.stable_id, context.queryTerms) : 0;
3455
+ vector = context.vectorScores !== void 0 ? (context.vectorWeight ?? 0) * (context.vectorScores.get(item.stable_id) ?? 0) : 0;
3456
+ }
3457
+ const scale = structuralScaleFor(context);
3458
+ const salience = salienceScore(item) * scale;
3459
+ const recency = recencyBoost(item, context) * scale;
3460
+ const locality = localityBoost(item, context) * scale;
3461
+ const final = bm25 + vector + salience + recency + locality;
3462
+ return {
3463
+ final,
3464
+ ...bm25 !== 0 ? { bm25 } : {},
3465
+ ...bm25Rank !== void 0 ? { bm25_rank: bm25Rank } : {},
3466
+ ...vector !== 0 ? { vector } : {},
3467
+ ...vectorRank !== void 0 ? { vector_rank: vectorRank } : {},
3468
+ salience,
3469
+ recency,
3470
+ locality
3471
+ };
3241
3472
  }
3242
3473
  function localityTier(relevancePath, targetPath) {
3243
3474
  if (relevancePath === targetPath) return LOCALITY_SAME_FILE;
@@ -3274,7 +3505,13 @@ function relatedLookupKeys(stableId) {
3274
3505
  var RECALL_DIRECTIVE = "These entries are auto-accounted as citations for edits whose paths overlap this recall \u2014 no first-line cite needed. Speak up only to dismiss one you judge inapplicable: `dismissed: <id> (<reason>)`.";
3275
3506
  async function recall(projectRoot, input) {
3276
3507
  const planResult = await planContext(projectRoot, input);
3277
- const { selection_token: _token, payload_trimmed: _pt, payload_over_budget: _pob, ...planRest } = planResult;
3508
+ const {
3509
+ selection_token: _token,
3510
+ payload_trimmed: _pt,
3511
+ payload_over_budget: _pob,
3512
+ candidate_scores: candidateScores,
3513
+ ...planRest
3514
+ } = planResult;
3278
3515
  let bodyIndex;
3279
3516
  try {
3280
3517
  bodyIndex = await buildCrossStoreBodyIndex(projectRoot);
@@ -3323,13 +3560,15 @@ async function recall(projectRoot, input) {
3323
3560
  const pathByStableId = new Map(paths.map((p) => [p.stable_id, p]));
3324
3561
  const entries = planRest.candidates.map((c, index) => {
3325
3562
  const readPath = pathByStableId.get(c.stable_id);
3563
+ const scored = candidateScores?.get(c.stable_id);
3326
3564
  return {
3327
3565
  stable_id: c.stable_id,
3328
3566
  rank: index + 1,
3329
3567
  description: c.description,
3330
3568
  ...readPath ? { read_path: readPath.path } : {},
3331
3569
  ...readPath?.store ? { store: readPath.store } : {},
3332
- ...isAlwaysActive(c) ? { body_in_context: true } : {}
3570
+ ...isAlwaysActive(c) ? { body_in_context: true } : {},
3571
+ ...scored ? { score: scored.score, score_breakdown: scored.score_breakdown } : {}
3333
3572
  };
3334
3573
  });
3335
3574
  const { entries: _reqProfiles, candidates: _candidates, ...planRestNoLists } = planRest;
@@ -3679,9 +3918,9 @@ import { enforcePayloadLimit as enforcePayloadLimit4 } from "@fenglimg/fabric-sh
3679
3918
  // src/services/review.ts
3680
3919
  import { execFileSync } from "child_process";
3681
3920
  import { existsSync as existsSync5 } from "fs";
3682
- import { readFile as readFile6, readdir as readdir2, stat as stat2, unlink as unlink2 } from "fs/promises";
3921
+ import { readFile as readFile7, readdir as readdir2, stat as stat2, unlink as unlink2 } from "fs/promises";
3683
3922
  import { homedir } from "os";
3684
- import { basename, isAbsolute, join as join10, relative, resolve as resolve2, sep as sep2 } from "path";
3923
+ import { basename, isAbsolute, join as join12, relative, resolve as resolve2, sep as sep2 } from "path";
3685
3924
 
3686
3925
  // src/services/promotion-gate.ts
3687
3926
  function toLocalId(id) {
@@ -3721,8 +3960,8 @@ import { allocateStoreKnowledgeId, isPersonalScope as isPersonalScope2 } from "@
3721
3960
 
3722
3961
  // src/services/pending-dedupe.ts
3723
3962
  import { existsSync as existsSync4 } from "fs";
3724
- import { readdir, readFile as readFile5, unlink } from "fs/promises";
3725
- import { join as join9 } from "path";
3963
+ import { readdir, readFile as readFile6, unlink } from "fs/promises";
3964
+ import { join as join11 } from "path";
3726
3965
  var PENDING_TYPES = ["decisions", "pitfalls", "guidelines", "models", "processes"];
3727
3966
  var DISAMBIGUATION_SUFFIX = /^(.+)-([2-9])\.md$/u;
3728
3967
  var SOURCE_SESSIONS_LINE = /^source_sessions:\s*\[(.*)\]\s*$/mu;
@@ -3795,7 +4034,7 @@ async function mergePendingTwins(projectRoot) {
3795
4034
  continue;
3796
4035
  }
3797
4036
  for (const type of PENDING_TYPES) {
3798
- const dir = join9(pendingBase2, type);
4037
+ const dir = join11(pendingBase2, type);
3799
4038
  if (!existsSync4(dir)) continue;
3800
4039
  let names;
3801
4040
  try {
@@ -3816,10 +4055,10 @@ async function mergePendingTwins(projectRoot) {
3816
4055
  if (groupNames.length < 2) continue;
3817
4056
  const parsed = [];
3818
4057
  for (const name of groupNames) {
3819
- const abs = join9(dir, name);
4058
+ const abs = join11(dir, name);
3820
4059
  let content;
3821
4060
  try {
3822
- content = await readFile5(abs, "utf8");
4061
+ content = await readFile6(abs, "utf8");
3823
4062
  } catch {
3824
4063
  continue;
3825
4064
  }
@@ -3919,7 +4158,7 @@ async function reviewPending(projectRoot, input) {
3919
4158
  case "search":
3920
4159
  return {
3921
4160
  action: "search",
3922
- items: await searchEntries(projectRoot, input.query, input.filters)
4161
+ items: await triageSearch(projectRoot, input.query, input.filters)
3923
4162
  };
3924
4163
  default: {
3925
4164
  const exhaustive = input;
@@ -3999,7 +4238,7 @@ async function listPending(projectRoot, filters) {
3999
4238
  }
4000
4239
  for (const source of sources) {
4001
4240
  for (const type of typesToScan) {
4002
- const dir = join10(source.root, type);
4241
+ const dir = join12(source.root, type);
4003
4242
  if (!existsSync5(dir)) {
4004
4243
  continue;
4005
4244
  }
@@ -4011,10 +4250,10 @@ async function listPending(projectRoot, filters) {
4011
4250
  }
4012
4251
  for (const name of entries) {
4013
4252
  if (!name.endsWith(".md")) continue;
4014
- const absolutePath = join10(dir, name);
4253
+ const absolutePath = join12(dir, name);
4015
4254
  let content;
4016
4255
  try {
4017
- content = await readFile6(absolutePath, "utf8");
4256
+ content = await readFile7(absolutePath, "utf8");
4018
4257
  } catch {
4019
4258
  continue;
4020
4259
  }
@@ -4124,7 +4363,7 @@ async function approveOne(projectRoot, pendingPath) {
4124
4363
  let targetAbs;
4125
4364
  let writtenTarget = false;
4126
4365
  try {
4127
- const content = await readFile6(sourceAbs, "utf8");
4366
+ const content = await readFile7(sourceAbs, "utf8");
4128
4367
  const fm = parseFrontmatter(content);
4129
4368
  const pluralType = fm.type;
4130
4369
  if (pluralType === void 0 || !PLURAL_TYPES.includes(pluralType)) {
@@ -4138,7 +4377,7 @@ async function approveOne(projectRoot, pendingPath) {
4138
4377
  );
4139
4378
  allocatedId = stableId;
4140
4379
  const newFilename = `${stableId}--${slug}.md`;
4141
- targetAbs = join10(resolveStoreCanonicalBase(layer, projectRoot), pluralType, newFilename);
4380
+ targetAbs = join12(resolveStoreCanonicalBase(layer, projectRoot), pluralType, newFilename);
4142
4381
  await ensureParentDirectory(targetAbs);
4143
4382
  const rewritten = rewriteFrontmatterMerge(
4144
4383
  rewriteFrontmatterForPromote(content, stableId),
@@ -4196,7 +4435,7 @@ async function rejectAll(projectRoot, pendingPaths, reason) {
4196
4435
  try {
4197
4436
  const sandboxed = resolveSandboxedPath(projectRoot, pendingPath, { allowPersonal: true });
4198
4437
  if (existsSync5(sandboxed.abs)) {
4199
- const content = await readFile6(sandboxed.abs, "utf8");
4438
+ const content = await readFile7(sandboxed.abs, "utf8");
4200
4439
  const merged = rewriteFrontmatterMerge(content, { status: "rejected" });
4201
4440
  const rejectedAbs = sandboxed.abs.includes(`${sep2}pending${sep2}`) ? sandboxed.abs.replace(`${sep2}pending${sep2}`, `${sep2}rejected${sep2}`) : null;
4202
4441
  if (rejectedAbs !== null) {
@@ -4223,7 +4462,7 @@ async function modifyEntry(projectRoot, pendingPath, changes) {
4223
4462
  if (target === null) {
4224
4463
  throw new Error(`modify target not found: ${pendingPath}`);
4225
4464
  }
4226
- const content = await readFile6(target.absPath, "utf8");
4465
+ const content = await readFile7(target.absPath, "utf8");
4227
4466
  const fm = parseFrontmatter(content);
4228
4467
  const currentLayer = fm.layer ?? "team";
4229
4468
  if (fm.maturity === "verified" && changes.maturity === "proven" && fm.id !== void 0) {
@@ -4325,7 +4564,7 @@ async function modifyLayerFlip(projectRoot, target, content, fm, changes) {
4325
4564
  pluralType,
4326
4565
  resolveWriteTargetStoreDir(toLayer, projectRoot)
4327
4566
  );
4328
- const toAbs = join10(
4567
+ const toAbs = join12(
4329
4568
  resolveStoreCanonicalBase(toLayer, projectRoot),
4330
4569
  pluralType,
4331
4570
  `${newStableId}--${slug}.md`
@@ -4425,7 +4664,7 @@ function getSearchDirectoryCache(cacheKey) {
4425
4664
  return created;
4426
4665
  }
4427
4666
  async function listIndexedSearchEntries(source, type) {
4428
- const dir = join10(source.root, type);
4667
+ const dir = join12(source.root, type);
4429
4668
  let entries;
4430
4669
  try {
4431
4670
  entries = await readdir2(dir);
@@ -4438,7 +4677,7 @@ async function listIndexedSearchEntries(source, type) {
4438
4677
  const indexed = [];
4439
4678
  for (const name of entries) {
4440
4679
  if (!name.endsWith(".md")) continue;
4441
- const absolutePath = join10(dir, name);
4680
+ const absolutePath = join12(dir, name);
4442
4681
  let fingerprint;
4443
4682
  try {
4444
4683
  const st = await stat2(absolutePath);
@@ -4455,7 +4694,7 @@ async function listIndexedSearchEntries(source, type) {
4455
4694
  }
4456
4695
  let content;
4457
4696
  try {
4458
- content = await readFile6(absolutePath, "utf8");
4697
+ content = await readFile7(absolutePath, "utf8");
4459
4698
  searchEntryIndexContentReads += 1;
4460
4699
  } catch {
4461
4700
  directoryCache.files.delete(absolutePath);
@@ -4482,9 +4721,41 @@ async function listIndexedSearchEntries(source, type) {
4482
4721
  }
4483
4722
  return indexed;
4484
4723
  }
4485
- async function searchEntries(projectRoot, query, filters) {
4724
+ function matchesTriageQuery(indexed, lowerQuery, includeBody) {
4725
+ const haystacks = [
4726
+ indexed.fm.title ?? "",
4727
+ indexed.fm.summary ?? "",
4728
+ ...indexed.fm.tags ?? [],
4729
+ indexed.name,
4730
+ includeBody ? indexed.body : ""
4731
+ ].map((s) => s.toLowerCase());
4732
+ return haystacks.some((h) => h.includes(lowerQuery));
4733
+ }
4734
+ function pendingEntryToRankerItem(indexed) {
4735
+ const { fm, name } = indexed;
4736
+ const slug = name.replace(/\.md$/u, "");
4737
+ const summary = fm.summary ?? fm.title ?? slug;
4738
+ const description = {
4739
+ summary,
4740
+ intent_clues: [],
4741
+ tech_stack: [],
4742
+ impact: [],
4743
+ must_read_if: fm.title ?? summary,
4744
+ ...fm.id !== void 0 ? { id: fm.id } : {},
4745
+ ...fm.type !== void 0 ? { knowledge_type: fm.type } : {},
4746
+ maturity: fm.maturity ?? "draft",
4747
+ knowledge_layer: indexed.layer,
4748
+ ...fm.semantic_scope !== void 0 ? { semantic_scope: fm.semantic_scope } : {},
4749
+ ...fm.created_at !== void 0 ? { created_at: fm.created_at } : {},
4750
+ tags: fm.tags ?? [],
4751
+ relevance_scope: fm.relevance_scope ?? "broad",
4752
+ relevance_paths: fm.relevance_paths ?? []
4753
+ };
4754
+ return { stable_id: indexed.absolutePath, description };
4755
+ }
4756
+ async function triageSearch(projectRoot, query, filters) {
4486
4757
  const lowerQuery = query.toLowerCase();
4487
- const items = [];
4758
+ const includeBody = filters?.include_body === true;
4488
4759
  const sources = [];
4489
4760
  for (const layer of ["team", "personal"]) {
4490
4761
  const isPersonal = layer === "personal";
@@ -4507,10 +4778,12 @@ async function searchEntries(projectRoot, query, filters) {
4507
4778
  }
4508
4779
  }
4509
4780
  const typesToScan = filters?.type !== void 0 ? [filters.type] : PLURAL_TYPES;
4781
+ const matchedByKey = /* @__PURE__ */ new Map();
4782
+ const rankerItems = [];
4510
4783
  for (const source of sources) {
4511
4784
  for (const type of typesToScan) {
4512
4785
  for (const indexed of await listIndexedSearchEntries(source, type)) {
4513
- const { absolutePath, fm, layer, maturity, name } = indexed;
4786
+ const { fm, layer, maturity } = indexed;
4514
4787
  if (filters?.layer !== void 0 && filters.layer !== "both" && filters.layer !== layer) {
4515
4788
  continue;
4516
4789
  }
@@ -4531,42 +4804,56 @@ async function searchEntries(projectRoot, query, filters) {
4531
4804
  if (!isVisibleByLifecycle(fm, filters)) {
4532
4805
  continue;
4533
4806
  }
4534
- const bodyForSearch = filters?.include_body === true ? indexed.body : "";
4535
- const haystacks = [
4536
- fm.title ?? "",
4537
- fm.summary ?? "",
4538
- ...fm.tags ?? [],
4539
- name,
4540
- bodyForSearch
4541
- ].map((s) => s.toLowerCase());
4542
- const matches = haystacks.some((h) => h.includes(lowerQuery));
4543
- if (!matches) continue;
4544
- const reportedPath = source.isStore ? absolutePath : source.isPersonal ? `~/${relative(resolvePersonalRoot(), absolutePath)}` : relative(projectRoot, absolutePath);
4545
- items.push({
4546
- area: source.isPending ? "pending" : "canonical",
4547
- path: reportedPath,
4548
- ...source.isPersonal ? { path_absolute: absolutePath } : {},
4549
- type,
4550
- layer,
4551
- maturity,
4552
- // Only pending entries carry an origin tag (canonical hits live
4553
- // outside the dual-pending-root convention).
4554
- ...source.isPending ? { origin: source.isPersonal ? "personal" : "team" } : {},
4555
- ...fm.tags !== void 0 && fm.tags.length > 0 ? { tags: fm.tags } : {},
4556
- ...fm.title !== void 0 ? { title: fm.title } : {},
4557
- ...fm.summary !== void 0 ? { summary: fm.summary } : {},
4558
- ...fm.status !== void 0 ? { status: fm.status } : {},
4559
- ...fm.deferred_until !== void 0 ? { deferred_until: fm.deferred_until } : {},
4560
- // v2.0.0-rc.27 TASK-006 (audit §2.23): body emission when opted in.
4561
- ...filters?.include_body === true ? { body: bodyForSearch } : {},
4562
- // Canonical hits always have an id; pending hits typically don't
4563
- // yet — surface the frontmatter id when present so consumers can
4564
- // dedupe across runs.
4565
- ...fm.id !== void 0 ? { stable_id: fm.id } : {}
4566
- });
4807
+ if (!matchesTriageQuery(indexed, lowerQuery, includeBody)) continue;
4808
+ const item = pendingEntryToRankerItem(indexed);
4809
+ matchedByKey.set(item.stable_id, { indexed, source, type });
4810
+ rankerItems.push(item);
4567
4811
  }
4568
4812
  }
4569
4813
  }
4814
+ if (rankerItems.length === 0) {
4815
+ return [];
4816
+ }
4817
+ let revision;
4818
+ try {
4819
+ revision = await computeReadSetRevision(projectRoot);
4820
+ } catch {
4821
+ revision = "triage-search";
4822
+ }
4823
+ const scoringContext = await buildScoringContext(projectRoot, revision, rankerItems, {
4824
+ queryText: query,
4825
+ targetPaths: []
4826
+ });
4827
+ const ranked = rankDescriptionItems(rankerItems, scoringContext, "triage");
4828
+ const items = [];
4829
+ for (const { item } of ranked) {
4830
+ const match = matchedByKey.get(item.stable_id);
4831
+ if (match === void 0) continue;
4832
+ const { indexed, source, type } = match;
4833
+ const { absolutePath, fm, layer, maturity } = indexed;
4834
+ const reportedPath = source.isStore ? absolutePath : source.isPersonal ? `~/${relative(resolvePersonalRoot(), absolutePath)}` : relative(projectRoot, absolutePath);
4835
+ items.push({
4836
+ area: source.isPending ? "pending" : "canonical",
4837
+ path: reportedPath,
4838
+ ...source.isPersonal ? { path_absolute: absolutePath } : {},
4839
+ type,
4840
+ layer,
4841
+ maturity,
4842
+ // Only pending entries carry an origin tag (canonical hits live
4843
+ // outside the dual-pending-root convention).
4844
+ ...source.isPending ? { origin: source.isPersonal ? "personal" : "team" } : {},
4845
+ ...fm.tags !== void 0 && fm.tags.length > 0 ? { tags: fm.tags } : {},
4846
+ ...fm.title !== void 0 ? { title: fm.title } : {},
4847
+ ...fm.summary !== void 0 ? { summary: fm.summary } : {},
4848
+ ...fm.status !== void 0 ? { status: fm.status } : {},
4849
+ ...fm.deferred_until !== void 0 ? { deferred_until: fm.deferred_until } : {},
4850
+ // v2.0.0-rc.27 TASK-006 (audit §2.23): body emission when opted in.
4851
+ ...includeBody ? { body: indexed.body } : {},
4852
+ // Canonical hits always have an id; pending hits typically don't yet —
4853
+ // surface the frontmatter id when present so consumers can dedupe.
4854
+ ...fm.id !== void 0 ? { stable_id: fm.id } : {}
4855
+ });
4856
+ }
4570
4857
  return items;
4571
4858
  }
4572
4859
  async function deferAll(projectRoot, pendingPaths, until, reason) {
@@ -4576,7 +4863,7 @@ async function deferAll(projectRoot, pendingPaths, until, reason) {
4576
4863
  try {
4577
4864
  const sandboxed = resolveSandboxedPath(projectRoot, pendingPath, { allowPersonal: true });
4578
4865
  if (existsSync5(sandboxed.abs)) {
4579
- const content = await readFile6(sandboxed.abs, "utf8");
4866
+ const content = await readFile7(sandboxed.abs, "utf8");
4580
4867
  stableId = parseFrontmatter(content).id;
4581
4868
  const patch = {
4582
4869
  status: "deferred",
@@ -4917,9 +5204,9 @@ function registerPending(server, tracker) {
4917
5204
  }
4918
5205
 
4919
5206
  // src/services/doctor.ts
4920
- import { access as access4, readFile as readFile16, readdir as readdirAsync, stat as statAsync, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
5207
+ import { access as access4, readFile as readFile17, readdir as readdirAsync, stat as statAsync, unlink as unlink4, writeFile as writeFile4 } from "fs/promises";
4921
5208
  import { constants as constants2 } from "fs";
4922
- import { isAbsolute as isAbsolute2, join as join21, posix as posix5, resolve as resolve3 } from "path";
5209
+ import { isAbsolute as isAbsolute2, join as join23, posix as posix5, resolve as resolve3 } from "path";
4923
5210
  import {
4924
5211
  createTranslator,
4925
5212
  forensicReportSchema,
@@ -5133,13 +5420,13 @@ function createKnowledgeSummaryOpaqueCheck(t, inspection) {
5133
5420
  }
5134
5421
 
5135
5422
  // src/services/doctor-stable-id-collision.ts
5136
- import { readFile as readFile7 } from "fs/promises";
5137
- import { basename as basename2, join as join11 } from "path";
5423
+ import { readFile as readFile8 } from "fs/promises";
5424
+ import { basename as basename2, join as join13 } from "path";
5138
5425
  import {
5139
5426
  buildStoreResolveInput as buildStoreResolveInput4,
5140
5427
  createStoreResolver as createStoreResolver4,
5141
5428
  readKnowledgeAcrossStores as readKnowledgeAcrossStores2,
5142
- resolveGlobalRoot as resolveGlobalRoot3,
5429
+ resolveGlobalRoot as resolveGlobalRoot4,
5143
5430
  storeRelativePathForMount as storeRelativePathForMount3
5144
5431
  } from "@fenglimg/fabric-shared";
5145
5432
  var ID_LINE = /^id:\s*"?([^"\n]+?)"?\s*$/mu;
@@ -5155,13 +5442,13 @@ function resolveIntegrityStores(projectRoot) {
5155
5442
  const personalUuids = new Set(
5156
5443
  input.mountedStores.filter((s) => s.personal).map((s) => s.store_uuid)
5157
5444
  );
5158
- const globalRoot = resolveGlobalRoot3();
5445
+ const globalRoot = resolveGlobalRoot4();
5159
5446
  const dirs = readSet.stores.map((entry) => {
5160
5447
  const mounted = input.mountedStores.find((s) => s.store_uuid === entry.store_uuid);
5161
5448
  return {
5162
5449
  store_uuid: entry.store_uuid,
5163
5450
  alias: entry.alias,
5164
- dir: join11(globalRoot, storeRelativePathForMount3(mounted ?? { store_uuid: entry.store_uuid }))
5451
+ dir: join13(globalRoot, storeRelativePathForMount3(mounted ?? { store_uuid: entry.store_uuid }))
5165
5452
  };
5166
5453
  });
5167
5454
  return { dirs, personalUuids };
@@ -5180,7 +5467,7 @@ async function inspectStoreStableIdIntegrity(projectRoot) {
5180
5467
  for (const ref of await readKnowledgeAcrossStores2(resolved.dirs)) {
5181
5468
  let source;
5182
5469
  try {
5183
- source = await readFile7(ref.file, "utf8");
5470
+ source = await readFile8(ref.file, "utf8");
5184
5471
  } catch {
5185
5472
  continue;
5186
5473
  }
@@ -5265,7 +5552,7 @@ function createLayerMismatchCheck(t, inspection) {
5265
5552
  // src/services/doctor-relevance-paths.ts
5266
5553
  import { execFileSync as execFileSync2 } from "child_process";
5267
5554
  import { existsSync as existsSync6, readdirSync, statSync as statSync3 } from "fs";
5268
- import { join as join12, sep as sep3 } from "path";
5555
+ import { join as join14, sep as sep3 } from "path";
5269
5556
  import { minimatch } from "minimatch";
5270
5557
  var MS_PER_DAY = 24 * 60 * 60 * 1e3;
5271
5558
  var RELEVANCE_PATHS_DRIFT_WINDOW_DAYS = 90;
@@ -5308,7 +5595,7 @@ function collectWorkspacePaths(projectRoot) {
5308
5595
  continue;
5309
5596
  }
5310
5597
  for (const entry of entries) {
5311
- const abs = join12(current, entry.name);
5598
+ const abs = join14(current, entry.name);
5312
5599
  const rel = toPosix(abs.slice(projectRoot.length + 1));
5313
5600
  if (rel.length === 0) continue;
5314
5601
  if (entry.isDirectory()) {
@@ -5501,16 +5788,16 @@ function createNarrowNoPathsCheck(t, inspection) {
5501
5788
  }
5502
5789
 
5503
5790
  // src/services/doctor-broad-index.ts
5504
- import { readFile as readFile8 } from "fs/promises";
5505
- import { join as join13 } from "path";
5791
+ import { readFile as readFile9 } from "fs/promises";
5792
+ import { join as join15 } from "path";
5506
5793
  var DEFAULT_BROAD_INDEX_BACKSTOP = 50;
5507
5794
  var BROAD_INDEX_BACKSTOP_MIN = 20;
5508
5795
  var BROAD_INDEX_BACKSTOP_MAX = 500;
5509
5796
  var BROAD_INDEX_DRIFT_RATIO = 0.8;
5510
5797
  async function readBroadIndexBackstop(projectRoot) {
5511
- const configPath = join13(projectRoot, ".fabric", "fabric-config.json");
5798
+ const configPath = join15(projectRoot, ".fabric", "fabric-config.json");
5512
5799
  try {
5513
- const raw = await readFile8(configPath, "utf8");
5800
+ const raw = await readFile9(configPath, "utf8");
5514
5801
  const parsed = JSON.parse(raw);
5515
5802
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
5516
5803
  const v = parsed.broad_index_backstop;
@@ -5827,15 +6114,15 @@ function createBroadReviewRecheckCheck(t, inspection) {
5827
6114
  }
5828
6115
 
5829
6116
  // src/services/doctor-scope-lint.ts
5830
- import { readFile as readFile9 } from "fs/promises";
5831
- import { join as join14 } from "path";
6117
+ import { readFile as readFile10 } from "fs/promises";
6118
+ import { join as join16 } from "path";
5832
6119
  import {
5833
6120
  buildStoreResolveInput as buildStoreResolveInput5,
5834
6121
  createStoreResolver as createStoreResolver5,
5835
6122
  isPersonalScope as isPersonalScope3,
5836
6123
  readKnowledgeAcrossStores as readKnowledgeAcrossStores3,
5837
6124
  readStoreProjects,
5838
- resolveGlobalRoot as resolveGlobalRoot4,
6125
+ resolveGlobalRoot as resolveGlobalRoot5,
5839
6126
  scopeRoot as scopeRoot2,
5840
6127
  storeRelativePathForMount as storeRelativePathForMount4
5841
6128
  } from "@fenglimg/fabric-shared";
@@ -5860,10 +6147,10 @@ async function resolveLintStores(projectRoot) {
5860
6147
  const personalUuids = new Set(
5861
6148
  input.mountedStores.filter((s) => s.personal).map((s) => s.store_uuid)
5862
6149
  );
5863
- const globalRoot = resolveGlobalRoot4();
6150
+ const globalRoot = resolveGlobalRoot5();
5864
6151
  return Promise.all(readSet.stores.map(async (entry) => {
5865
6152
  const mounted = input.mountedStores.find((s) => s.store_uuid === entry.store_uuid);
5866
- const dir = join14(
6153
+ const dir = join16(
5867
6154
  globalRoot,
5868
6155
  storeRelativePathForMount4(mounted ?? { store_uuid: entry.store_uuid })
5869
6156
  );
@@ -5895,7 +6182,7 @@ async function lintStoreScopes(projectRoot) {
5895
6182
  }
5896
6183
  let source;
5897
6184
  try {
5898
- source = await readFile9(ref.file, "utf8");
6185
+ source = await readFile10(ref.file, "utf8");
5899
6186
  } catch {
5900
6187
  continue;
5901
6188
  }
@@ -6018,7 +6305,7 @@ function createUnboundProjectCheck(t, violation) {
6018
6305
 
6019
6306
  // src/services/doctor-store-counters.ts
6020
6307
  import { existsSync as existsSync7, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
6021
- import { join as join15 } from "path";
6308
+ import { join as join17 } from "path";
6022
6309
  import {
6023
6310
  buildStoreResolveInput as buildStoreResolveInput6,
6024
6311
  createStoreResolver as createStoreResolver6,
@@ -6027,7 +6314,7 @@ import {
6027
6314
  parseKnowledgeId as parseKnowledgeId2,
6028
6315
  readStoreCounters,
6029
6316
  reconcileStoreCounters,
6030
- resolveGlobalRoot as resolveGlobalRoot5,
6317
+ resolveGlobalRoot as resolveGlobalRoot6,
6031
6318
  STORE_KNOWLEDGE_TYPE_DIRS,
6032
6319
  STORE_LAYOUT as STORE_LAYOUT2,
6033
6320
  storeRelativePathForMount as storeRelativePathForMount5
@@ -6041,11 +6328,11 @@ function resolveCounterStores(projectRoot) {
6041
6328
  if (readSet.stores.length === 0) {
6042
6329
  return [];
6043
6330
  }
6044
- const globalRoot = resolveGlobalRoot5();
6331
+ const globalRoot = resolveGlobalRoot6();
6045
6332
  return readSet.stores.map((entry) => ({
6046
6333
  uuid: entry.store_uuid,
6047
6334
  alias: entry.alias,
6048
- dir: join15(
6335
+ dir: join17(
6049
6336
  globalRoot,
6050
6337
  storeRelativePathForMount5(
6051
6338
  input.mountedStores.find((s) => s.store_uuid === entry.store_uuid) ?? {
@@ -6073,7 +6360,7 @@ function readEntryId(file) {
6073
6360
  function computeStoreDiskMax(storeDir) {
6074
6361
  const max = defaultAgentsMetaCounters();
6075
6362
  for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
6076
- const dir = join15(storeDir, STORE_LAYOUT2.knowledgeDir, type);
6363
+ const dir = join17(storeDir, STORE_LAYOUT2.knowledgeDir, type);
6077
6364
  if (!existsSync7(dir)) {
6078
6365
  continue;
6079
6366
  }
@@ -6087,7 +6374,7 @@ function computeStoreDiskMax(storeDir) {
6087
6374
  if (!name.endsWith(".md")) {
6088
6375
  continue;
6089
6376
  }
6090
- const parsed = parseKnowledgeId2(readEntryId(join15(dir, name)) ?? "");
6377
+ const parsed = parseKnowledgeId2(readEntryId(join17(dir, name)) ?? "");
6091
6378
  if (parsed === null) {
6092
6379
  continue;
6093
6380
  }
@@ -6170,14 +6457,14 @@ function createStoreCounterCheck(t, drifts) {
6170
6457
 
6171
6458
  // src/services/doctor-store-orphan.ts
6172
6459
  import { readdirSync as readdirSync3 } from "fs";
6173
- import { join as join16 } from "path";
6460
+ import { join as join18 } from "path";
6174
6461
  import {
6175
6462
  STORES_ROOT_DIR,
6176
6463
  addMountedStore,
6177
6464
  disambiguateAlias,
6178
6465
  loadGlobalConfig,
6179
6466
  readStoreIdentity,
6180
- resolveGlobalRoot as resolveGlobalRoot6,
6467
+ resolveGlobalRoot as resolveGlobalRoot7,
6181
6468
  saveGlobalConfig
6182
6469
  } from "@fenglimg/fabric-shared";
6183
6470
  var STORE_BY_ALIAS_DIR = "by-alias";
@@ -6188,21 +6475,21 @@ function listDir(dir) {
6188
6475
  return [];
6189
6476
  }
6190
6477
  }
6191
- function inspectStoreOrphans(globalRoot = resolveGlobalRoot6()) {
6478
+ function inspectStoreOrphans(globalRoot = resolveGlobalRoot7()) {
6192
6479
  const config = loadGlobalConfig(globalRoot);
6193
6480
  if (config === null) {
6194
6481
  return [];
6195
6482
  }
6196
6483
  const registered = new Set(config.stores.map((s) => s.store_uuid));
6197
- const storesRoot = join16(globalRoot, STORES_ROOT_DIR);
6484
+ const storesRoot = join18(globalRoot, STORES_ROOT_DIR);
6198
6485
  const orphans = [];
6199
6486
  for (const group of listDir(storesRoot)) {
6200
6487
  if (group === STORE_BY_ALIAS_DIR) {
6201
6488
  continue;
6202
6489
  }
6203
- const groupDir = join16(storesRoot, group);
6490
+ const groupDir = join18(storesRoot, group);
6204
6491
  for (const mount of listDir(groupDir)) {
6205
- const dir = join16(groupDir, mount);
6492
+ const dir = join18(groupDir, mount);
6206
6493
  const identity = readStoreIdentity(dir);
6207
6494
  if (identity === null || registered.has(identity.store_uuid)) {
6208
6495
  continue;
@@ -6212,7 +6499,7 @@ function inspectStoreOrphans(globalRoot = resolveGlobalRoot6()) {
6212
6499
  }
6213
6500
  return orphans;
6214
6501
  }
6215
- function fixStoreOrphans(globalRoot = resolveGlobalRoot6()) {
6502
+ function fixStoreOrphans(globalRoot = resolveGlobalRoot7()) {
6216
6503
  let config = loadGlobalConfig(globalRoot);
6217
6504
  if (config === null) {
6218
6505
  return [];
@@ -6339,8 +6626,8 @@ async function inspectEventsJsonlGates(projectRoot, options = {}) {
6339
6626
  }
6340
6627
 
6341
6628
  // src/services/doctor-skill-lints.ts
6342
- import { readdir as readdir3, readFile as readFile10 } from "fs/promises";
6343
- import { join as join17, posix as posix2 } from "path";
6629
+ import { readdir as readdir3, readFile as readFile11 } from "fs/promises";
6630
+ import { join as join19, posix as posix2 } from "path";
6344
6631
  var FABRIC_SKILL_SLUGS = ["fabric-archive", "fabric-review"];
6345
6632
  var SKILL_MD_FRONTMATTER_ROOTS = [".claude/skills", ".codex/skills"];
6346
6633
  var SKILL_FRONTMATTER_KEY_PATTERN = /^([A-Za-z_][A-Za-z0-9_-]*):[ \t]+(.+?)[ \t]*$/u;
@@ -6370,8 +6657,8 @@ async function listMarkdownFiles(dir) {
6370
6657
  async function inspectSkillRefMirror(projectRoot) {
6371
6658
  const driftedPaths = [];
6372
6659
  for (const slug of FABRIC_SKILL_SLUGS) {
6373
- const claudeRef = join17(projectRoot, ".claude", "skills", slug, "ref");
6374
- const codexRef = join17(projectRoot, ".codex", "skills", slug, "ref");
6660
+ const claudeRef = join19(projectRoot, ".claude", "skills", slug, "ref");
6661
+ const codexRef = join19(projectRoot, ".codex", "skills", slug, "ref");
6375
6662
  const [claudeFiles, codexFiles] = await Promise.all([listMarkdownFiles(claudeRef), listMarkdownFiles(codexRef)]);
6376
6663
  if (claudeFiles === null || codexFiles === null) continue;
6377
6664
  const claudeSet = new Set(claudeFiles);
@@ -6388,8 +6675,8 @@ async function inspectSkillRefMirror(projectRoot) {
6388
6675
  let codexBody;
6389
6676
  try {
6390
6677
  [claudeBody, codexBody] = await Promise.all([
6391
- readFile10(join17(claudeRef, fname), "utf8"),
6392
- readFile10(join17(codexRef, fname), "utf8")
6678
+ readFile11(join19(claudeRef, fname), "utf8"),
6679
+ readFile11(join19(codexRef, fname), "utf8")
6393
6680
  ]);
6394
6681
  } catch {
6395
6682
  continue;
@@ -6408,10 +6695,10 @@ async function inspectSkillTokenBudget(projectRoot) {
6408
6695
  const overSize = [];
6409
6696
  let highestSeverity = "ok";
6410
6697
  for (const slug of FABRIC_SKILL_SLUGS) {
6411
- const skillMdPath = join17(projectRoot, ".claude", "skills", slug, "SKILL.md");
6698
+ const skillMdPath = join19(projectRoot, ".claude", "skills", slug, "SKILL.md");
6412
6699
  let body;
6413
6700
  try {
6414
- body = await readFile10(skillMdPath, "utf8");
6701
+ body = await readFile11(skillMdPath, "utf8");
6415
6702
  } catch {
6416
6703
  continue;
6417
6704
  }
@@ -6432,10 +6719,10 @@ async function inspectSkillDescription(projectRoot) {
6432
6719
  const CJK_PATTERN = /[\u3400-\u4dbf\u4e00-\u9fff]/u;
6433
6720
  const ASCII_PATTERN = /[a-zA-Z]{2,}/u;
6434
6721
  for (const slug of FABRIC_SKILL_SLUGS) {
6435
- const skillMdPath = join17(projectRoot, ".claude", "skills", slug, "SKILL.md");
6722
+ const skillMdPath = join19(projectRoot, ".claude", "skills", slug, "SKILL.md");
6436
6723
  let body;
6437
6724
  try {
6438
- body = await readFile10(skillMdPath, "utf8");
6725
+ body = await readFile11(skillMdPath, "utf8");
6439
6726
  } catch {
6440
6727
  continue;
6441
6728
  }
@@ -6466,7 +6753,7 @@ async function inspectSkillDescription(projectRoot) {
6466
6753
  async function inspectSkillMdYamlInvalid(projectRoot) {
6467
6754
  const candidates = [];
6468
6755
  for (const rootRel of SKILL_MD_FRONTMATTER_ROOTS) {
6469
- const rootAbs = join17(projectRoot, rootRel);
6756
+ const rootAbs = join19(projectRoot, rootRel);
6470
6757
  let dirEntries;
6471
6758
  try {
6472
6759
  dirEntries = await readdir3(rootAbs, { withFileTypes: true });
@@ -6475,10 +6762,10 @@ async function inspectSkillMdYamlInvalid(projectRoot) {
6475
6762
  }
6476
6763
  for (const dirEntry of dirEntries) {
6477
6764
  if (!dirEntry.isDirectory()) continue;
6478
- const skillFile = join17(rootAbs, dirEntry.name, "SKILL.md");
6765
+ const skillFile = join19(rootAbs, dirEntry.name, "SKILL.md");
6479
6766
  let raw;
6480
6767
  try {
6481
- raw = await readFile10(skillFile, "utf8");
6768
+ raw = await readFile11(skillFile, "utf8");
6482
6769
  } catch {
6483
6770
  continue;
6484
6771
  }
@@ -6601,8 +6888,8 @@ function createSkillMdYamlInvalidCheck(t, inspection) {
6601
6888
  }
6602
6889
 
6603
6890
  // src/services/doctor-retired-references-lint.ts
6604
- import { readdir as readdir4, readFile as readFile11, stat as stat3 } from "fs/promises";
6605
- import { join as join18, posix as posix3, relative as relative2 } from "path";
6891
+ import { readdir as readdir4, readFile as readFile12, stat as stat3 } from "fs/promises";
6892
+ import { join as join20, posix as posix3, relative as relative2 } from "path";
6606
6893
  var RETIRED_TOKENS = [
6607
6894
  { token: "fab_plan_context", replacement: "fab_recall", reason: "retrieval collapsed to one lean fab_recall (KT-DEC-0026)" },
6608
6895
  { token: "fab_get_knowledge_sections", replacement: "fab_recall", reason: "two-step fetch retired (KT-DEC-0026)" },
@@ -6625,7 +6912,7 @@ var RETIRED_TOKENS = [
6625
6912
  ];
6626
6913
  var HOOK_DIRS = [".claude/hooks", ".codex/hooks"];
6627
6914
  var SKILL_DIRS = [".claude/skills", ".codex/skills"];
6628
- var BOOTSTRAP_FILES = ["AGENTS.md", "CLAUDE.md", join18(".fabric", "AGENTS.md")];
6915
+ var BOOTSTRAP_FILES = ["AGENTS.md", "CLAUDE.md", join20(".fabric", "AGENTS.md")];
6629
6916
  function isCommentLine(line) {
6630
6917
  const t = line.trim();
6631
6918
  return t.startsWith("//") || t.startsWith("*") || t.startsWith("/*");
@@ -6639,7 +6926,7 @@ async function walkFiles(dir, exts) {
6639
6926
  }
6640
6927
  const out = [];
6641
6928
  for (const e of entries) {
6642
- const full = join18(dir, e.name);
6929
+ const full = join20(dir, e.name);
6643
6930
  if (e.isDirectory()) {
6644
6931
  out.push(...await walkFiles(full, exts));
6645
6932
  } else if (exts.some((ext) => e.name.endsWith(ext))) {
@@ -6665,29 +6952,29 @@ async function inspectRetiredReferences(projectRoot) {
6665
6952
  let scannedFiles = 0;
6666
6953
  const toRel = (p) => posix3.normalize(relative2(projectRoot, p).split("\\").join("/"));
6667
6954
  for (const rel of BOOTSTRAP_FILES) {
6668
- const abs = join18(projectRoot, rel);
6955
+ const abs = join20(projectRoot, rel);
6669
6956
  try {
6670
6957
  if ((await stat3(abs)).isFile()) {
6671
6958
  scannedFiles += 1;
6672
- scanText(toRel(abs), await readFile11(abs, "utf8"), false, hits);
6959
+ scanText(toRel(abs), await readFile12(abs, "utf8"), false, hits);
6673
6960
  }
6674
6961
  } catch {
6675
6962
  }
6676
6963
  }
6677
6964
  for (const dir of SKILL_DIRS) {
6678
- for (const file of await walkFiles(join18(projectRoot, dir), [".md"])) {
6965
+ for (const file of await walkFiles(join20(projectRoot, dir), [".md"])) {
6679
6966
  scannedFiles += 1;
6680
6967
  try {
6681
- scanText(toRel(file), await readFile11(file, "utf8"), false, hits);
6968
+ scanText(toRel(file), await readFile12(file, "utf8"), false, hits);
6682
6969
  } catch {
6683
6970
  }
6684
6971
  }
6685
6972
  }
6686
6973
  for (const dir of HOOK_DIRS) {
6687
- for (const file of await walkFiles(join18(projectRoot, dir), [".cjs"])) {
6974
+ for (const file of await walkFiles(join20(projectRoot, dir), [".cjs"])) {
6688
6975
  scannedFiles += 1;
6689
6976
  try {
6690
- scanText(toRel(file), await readFile11(file, "utf8"), true, hits);
6977
+ scanText(toRel(file), await readFile12(file, "utf8"), true, hits);
6691
6978
  } catch {
6692
6979
  }
6693
6980
  }
@@ -6722,8 +7009,8 @@ function createRetiredReferenceCheck(t, inspection) {
6722
7009
 
6723
7010
  // src/services/doctor-hooks-lints.ts
6724
7011
  import { constants } from "fs";
6725
- import { access, readdir as readdir5, readFile as readFile12, stat as stat4 } from "fs/promises";
6726
- import { join as join19, posix as posix4 } from "path";
7012
+ import { access, readdir as readdir5, readFile as readFile13, stat as stat4 } from "fs/promises";
7013
+ import { join as join21, posix as posix4 } from "path";
6727
7014
  import { Script } from "vm";
6728
7015
  var HOOKS_RUNTIME_CLIENT_DIRS = [
6729
7016
  { client: "claude", dir: ".claude/hooks" },
@@ -6785,14 +7072,14 @@ async function isFile(absPath) {
6785
7072
  }
6786
7073
  }
6787
7074
  async function inspectHooksWired(projectRoot) {
6788
- const claudeEntries = await readDirectoryFileNames(join19(projectRoot, ".claude"));
7075
+ const claudeEntries = await readDirectoryFileNames(join21(projectRoot, ".claude"));
6789
7076
  if (claudeEntries === null) {
6790
7077
  return { status: "skipped", missingHooks: [] };
6791
7078
  }
6792
- const settingsPath = join19(projectRoot, ".claude", "settings.json");
7079
+ const settingsPath = join21(projectRoot, ".claude", "settings.json");
6793
7080
  let parsed;
6794
7081
  try {
6795
- parsed = JSON.parse(await readFile12(settingsPath, "utf8"));
7082
+ parsed = JSON.parse(await readFile13(settingsPath, "utf8"));
6796
7083
  } catch {
6797
7084
  return { status: "missing-settings", missingHooks: [] };
6798
7085
  }
@@ -6815,8 +7102,8 @@ async function inspectHooksWired(projectRoot) {
6815
7102
  }
6816
7103
  async function inspectHookCacheWritability(projectRoot) {
6817
7104
  const relPath = posix4.join(".fabric", ".cache");
6818
- const fabricDir = join19(projectRoot, ".fabric");
6819
- const cacheDir = join19(projectRoot, ".fabric", ".cache");
7105
+ const fabricDir = join21(projectRoot, ".fabric");
7106
+ const cacheDir = join21(projectRoot, ".fabric", ".cache");
6820
7107
  try {
6821
7108
  try {
6822
7109
  const cacheStats = await stat4(cacheDir);
@@ -6865,12 +7152,12 @@ async function inspectHookCacheWritability(projectRoot) {
6865
7152
  async function inspectHooksContentDrift(projectRoot) {
6866
7153
  const hookFilesByBasename = /* @__PURE__ */ new Map();
6867
7154
  for (const { client, dir } of HOOKS_RUNTIME_CLIENT_DIRS) {
6868
- const absDir = join19(projectRoot, dir);
7155
+ const absDir = join21(projectRoot, dir);
6869
7156
  const entries = await readDirectoryFileNames(absDir);
6870
7157
  if (entries === null) continue;
6871
7158
  for (const name of entries) {
6872
7159
  if (!name.endsWith(".cjs")) continue;
6873
- const abs = join19(absDir, name);
7160
+ const abs = join21(absDir, name);
6874
7161
  if (!await isFile(abs)) continue;
6875
7162
  const arr = hookFilesByBasename.get(name) ?? [];
6876
7163
  arr.push({ client, abs });
@@ -6885,7 +7172,7 @@ async function inspectHooksContentDrift(projectRoot) {
6885
7172
  const hashes = [];
6886
7173
  for (const { client, abs } of copies) {
6887
7174
  try {
6888
- const body = await readFile12(abs, "utf8");
7175
+ const body = await readFile13(abs, "utf8");
6889
7176
  hashes.push({ client, sha: sha256(body) });
6890
7177
  } catch {
6891
7178
  }
@@ -6907,18 +7194,18 @@ async function inspectHooksRuntime(projectRoot) {
6907
7194
  const issues = [];
6908
7195
  let scanned = 0;
6909
7196
  for (const { client, dir } of HOOKS_RUNTIME_CLIENT_DIRS) {
6910
- const absDir = join19(projectRoot, dir);
7197
+ const absDir = join21(projectRoot, dir);
6911
7198
  const entries = await readDirectoryFileNames(absDir);
6912
7199
  if (entries === null) continue;
6913
7200
  for (const name of entries) {
6914
7201
  if (!name.endsWith(".cjs")) continue;
6915
- const abs = join19(absDir, name);
7202
+ const abs = join21(absDir, name);
6916
7203
  const displayPath = `${dir}/${name}`;
6917
7204
  if (!await isFile(abs)) continue;
6918
7205
  scanned += 1;
6919
7206
  let body;
6920
7207
  try {
6921
- body = await readFile12(abs, "utf8");
7208
+ body = await readFile13(abs, "utf8");
6922
7209
  } catch (err) {
6923
7210
  issues.push({
6924
7211
  path: displayPath,
@@ -7055,8 +7342,8 @@ function createHookCacheWritabilityCheck(t, inspection) {
7055
7342
  }
7056
7343
 
7057
7344
  // src/services/doctor-bootstrap-lints.ts
7058
- import { access as access2, readFile as readFile13 } from "fs/promises";
7059
- import { join as join20 } from "path";
7345
+ import { access as access2, readFile as readFile14 } from "fs/promises";
7346
+ import { join as join22 } from "path";
7060
7347
  import {
7061
7348
  BOOTSTRAP_MARKER_BEGIN,
7062
7349
  BOOTSTRAP_MARKER_END,
@@ -7088,17 +7375,17 @@ async function fileExists(path) {
7088
7375
  }
7089
7376
  async function inspectBootstrapAnchor(projectRoot) {
7090
7377
  const [hasAgentsMd, hasClaudeMd] = await Promise.all([
7091
- fileExists(join20(projectRoot, "AGENTS.md")),
7092
- fileExists(join20(projectRoot, "CLAUDE.md"))
7378
+ fileExists(join22(projectRoot, "AGENTS.md")),
7379
+ fileExists(join22(projectRoot, "CLAUDE.md"))
7093
7380
  ]);
7094
7381
  return { hasAgentsMd, hasClaudeMd };
7095
7382
  }
7096
7383
  async function inspectL1BootstrapSnapshotDrift(target) {
7097
- const abs = join20(target, ".fabric", "AGENTS.md");
7384
+ const abs = join22(target, ".fabric", "AGENTS.md");
7098
7385
  const canonical = resolveBootstrapCanonical();
7099
7386
  let onDisk;
7100
7387
  try {
7101
- onDisk = await readFile13(abs, "utf8");
7388
+ onDisk = await readFile14(abs, "utf8");
7102
7389
  } catch {
7103
7390
  return { status: "missing", canonical, onDisk: null };
7104
7391
  }
@@ -7124,17 +7411,17 @@ function createL1BootstrapSnapshotDriftCheck(t, inspection) {
7124
7411
  );
7125
7412
  }
7126
7413
  async function inspectL2ManagedBlockDrift(target) {
7127
- const snapshotPath = join20(target, ".fabric", "AGENTS.md");
7414
+ const snapshotPath = join22(target, ".fabric", "AGENTS.md");
7128
7415
  let snapshot;
7129
7416
  try {
7130
- snapshot = await readFile13(snapshotPath, "utf8");
7417
+ snapshot = await readFile14(snapshotPath, "utf8");
7131
7418
  } catch {
7132
7419
  return { status: "ok", drifted: [] };
7133
7420
  }
7134
- const projectRulesPath = join20(target, ".fabric", "project-rules.md");
7421
+ const projectRulesPath = join22(target, ".fabric", "project-rules.md");
7135
7422
  let expectedBody = snapshot;
7136
7423
  try {
7137
- const projectRules = await readFile13(projectRulesPath, "utf8");
7424
+ const projectRules = await readFile14(projectRulesPath, "utf8");
7138
7425
  expectedBody = `${snapshot}
7139
7426
  ---
7140
7427
  ${projectRules}`;
@@ -7143,12 +7430,12 @@ ${projectRules}`;
7143
7430
  const drifted = [];
7144
7431
  let anyManagedBlockFound = false;
7145
7432
  const blockTargets = [
7146
- join20(target, "AGENTS.md")
7433
+ join22(target, "AGENTS.md")
7147
7434
  ];
7148
7435
  for (const abs of blockTargets) {
7149
7436
  let content;
7150
7437
  try {
7151
- content = await readFile13(abs, "utf8");
7438
+ content = await readFile14(abs, "utf8");
7152
7439
  } catch {
7153
7440
  continue;
7154
7441
  }
@@ -7171,9 +7458,9 @@ ${projectRules}`;
7171
7458
  drifted.push({ path: abs, expected: expectedBody, actual: body });
7172
7459
  }
7173
7460
  }
7174
- const claudeMdPath = join20(target, "CLAUDE.md");
7461
+ const claudeMdPath = join22(target, "CLAUDE.md");
7175
7462
  try {
7176
- const claudeContent = await readFile13(claudeMdPath, "utf8");
7463
+ const claudeContent = await readFile14(claudeMdPath, "utf8");
7177
7464
  anyManagedBlockFound = true;
7178
7465
  const lines = claudeContent.split(/\r?\n/u);
7179
7466
  const hasAtImport = lines.some((line) => line.trim() === "@.fabric/AGENTS.md");
@@ -7241,7 +7528,7 @@ import { appendFile as appendFile3 } from "fs/promises";
7241
7528
  import { minimatch as minimatch2 } from "minimatch";
7242
7529
 
7243
7530
  // src/services/cite-rollup.ts
7244
- import { readFile as readFile14 } from "fs/promises";
7531
+ import { readFile as readFile15 } from "fs/promises";
7245
7532
  import { createLedgerWriteQueue as createLedgerWriteQueue3 } from "@fenglimg/fabric-shared/node/atomic-write";
7246
7533
  var citeRollupQueue = createLedgerWriteQueue3();
7247
7534
  async function appendCiteRollupRow(projectRoot, row) {
@@ -7253,7 +7540,7 @@ async function readCiteRollup(projectRoot) {
7253
7540
  const path = getCiteRollupPath(projectRoot);
7254
7541
  let raw;
7255
7542
  try {
7256
- raw = await readFile14(path, "utf8");
7543
+ raw = await readFile15(path, "utf8");
7257
7544
  } catch (error) {
7258
7545
  if (isNodeError(error) && error.code === "ENOENT") return [];
7259
7546
  throw error;
@@ -8383,7 +8670,7 @@ async function runDoctorReport(target) {
8383
8670
  const globalCliVersion = process.env.VITEST === "true" ? { status: "ok", version: "test-skipped" } : inspectGlobalCliVersion();
8384
8671
  const targetFiles = Object.fromEntries(
8385
8672
  await Promise.all(
8386
- TARGET_FILE_PATHS.map(async (path) => [path, await pathExists(join21(projectRoot, path))])
8673
+ TARGET_FILE_PATHS.map(async (path) => [path, await pathExists(join23(projectRoot, path))])
8387
8674
  )
8388
8675
  );
8389
8676
  const checks = [
@@ -8587,7 +8874,7 @@ async function runDoctorFix(target) {
8587
8874
  const fixed = [];
8588
8875
  const ledgerWarnings = [];
8589
8876
  if (before.fixable_errors.some((issue) => issue.code === "bootstrap_snapshot_drift")) {
8590
- const snapshotPath = join21(projectRoot, ".fabric", "AGENTS.md");
8877
+ const snapshotPath = join23(projectRoot, ".fabric", "AGENTS.md");
8591
8878
  await ensureParentDirectory(snapshotPath);
8592
8879
  await atomicWriteText4(snapshotPath, resolveBootstrapCanonical2());
8593
8880
  fixed.push(findIssue(before.fixable_errors, "bootstrap_snapshot_drift"));
@@ -8660,7 +8947,7 @@ async function runDoctorFix(target) {
8660
8947
  if (before.infos.some((issue) => issue.code === "stale_serve_lock")) {
8661
8948
  const lockInspection = inspectStaleServeLock(projectRoot, Date.now());
8662
8949
  if (lockInspection.present && !lockInspection.pidAlive) {
8663
- const lockFilePath = join21(projectRoot, ".fabric", ".serve.lock");
8950
+ const lockFilePath = join23(projectRoot, ".fabric", ".serve.lock");
8664
8951
  try {
8665
8952
  await unlink4(lockFilePath);
8666
8953
  } catch (err) {
@@ -8777,7 +9064,7 @@ function createApplyLintMessage(succeeded, failed, manualErrorCount) {
8777
9064
  }
8778
9065
  async function applySessionHintsStaleCleanup(projectRoot, candidate) {
8779
9066
  const detail = `deleted (${candidate.age_days}d old)`;
8780
- const absPath = join21(projectRoot, candidate.path);
9067
+ const absPath = join23(projectRoot, candidate.path);
8781
9068
  try {
8782
9069
  const { unlink: unlink5 } = await import("fs/promises");
8783
9070
  await unlink5(absPath);
@@ -8802,9 +9089,9 @@ function truncateErrorMessage(error) {
8802
9089
  return raw.length > 240 ? `${raw.slice(0, 237)}...` : raw;
8803
9090
  }
8804
9091
  async function inspectForensic(projectRoot) {
8805
- const path = join21(projectRoot, ".fabric", "forensic.json");
9092
+ const path = join23(projectRoot, ".fabric", "forensic.json");
8806
9093
  try {
8807
- const parsed = forensicReportSchema.parse(JSON.parse(await readFile16(path, "utf8")));
9094
+ const parsed = forensicReportSchema.parse(JSON.parse(await readFile17(path, "utf8")));
8808
9095
  return { present: true, valid: true, report: parsed };
8809
9096
  } catch (error) {
8810
9097
  if (isMissingFileError(error)) {
@@ -8834,7 +9121,7 @@ async function inspectEventLedger(projectRoot) {
8834
9121
  try {
8835
9122
  await access4(path, constants2.W_OK);
8836
9123
  const { warnings } = await readEventLedger(projectRoot);
8837
- const raw = await readFile16(path, "utf8");
9124
+ const raw = await readFile17(path, "utf8");
8838
9125
  const invalidLine = raw.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean).find((line) => !isValidJsonLine(line));
8839
9126
  const partialWarning = warnings.find((w) => w.kind === "partial_write_at_tail");
8840
9127
  const schemaVersionSamples = [];
@@ -9379,7 +9666,7 @@ async function inspectPreexistingRootFiles(projectRoot) {
9379
9666
  const candidates = ["CLAUDE.md", "AGENTS.md"];
9380
9667
  const detected = [];
9381
9668
  for (const name of candidates) {
9382
- if (await pathExists(join21(projectRoot, name))) {
9669
+ if (await pathExists(join23(projectRoot, name))) {
9383
9670
  detected.push(name);
9384
9671
  }
9385
9672
  }
@@ -9464,7 +9751,7 @@ async function buildLastActiveIndex(projectRoot) {
9464
9751
  return map;
9465
9752
  }
9466
9753
  async function inspectSessionHintsStale(projectRoot, now) {
9467
- const cacheDir = join21(projectRoot, ".fabric", ".cache");
9754
+ const cacheDir = join23(projectRoot, ".fabric", ".cache");
9468
9755
  let entries;
9469
9756
  try {
9470
9757
  entries = await readdirAsync(cacheDir, { withFileTypes: true });
@@ -9476,7 +9763,7 @@ async function inspectSessionHintsStale(projectRoot, now) {
9476
9763
  if (!entry.isFile()) continue;
9477
9764
  if (!entry.name.startsWith(SESSION_HINTS_FILE_PREFIX)) continue;
9478
9765
  if (!entry.name.endsWith(SESSION_HINTS_FILE_SUFFIX)) continue;
9479
- const absPath = join21(cacheDir, entry.name);
9766
+ const absPath = join23(cacheDir, entry.name);
9480
9767
  let mtimeMs = 0;
9481
9768
  try {
9482
9769
  mtimeMs = (await statAsync(absPath)).mtimeMs;
@@ -9508,9 +9795,9 @@ function inspectStaleServeLock(projectRoot, now) {
9508
9795
  };
9509
9796
  }
9510
9797
  async function readUnderseedThresholdFromConfig(projectRoot) {
9511
- const configPath = join21(projectRoot, ".fabric", "fabric-config.json");
9798
+ const configPath = join23(projectRoot, ".fabric", "fabric-config.json");
9512
9799
  try {
9513
- const raw = await readFile16(configPath, "utf8");
9800
+ const raw = await readFile17(configPath, "utf8");
9514
9801
  const parsed = JSON.parse(raw);
9515
9802
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
9516
9803
  const v = parsed.underseed_node_threshold;
@@ -9626,10 +9913,10 @@ async function inspectOnboardCoverage(projectRoot) {
9626
9913
  return { filled, missing, opted_out: optedOut };
9627
9914
  }
9628
9915
  async function readOnboardOptedOut(projectRoot) {
9629
- const path = join21(projectRoot, ".fabric", "fabric-config.json");
9916
+ const path = join23(projectRoot, ".fabric", "fabric-config.json");
9630
9917
  let raw;
9631
9918
  try {
9632
- raw = await readFile16(path, "utf8");
9919
+ raw = await readFile17(path, "utf8");
9633
9920
  } catch {
9634
9921
  return [];
9635
9922
  }
@@ -9731,22 +10018,22 @@ async function* iterateCanonicalFilenames(projectRoot) {
9731
10018
  }
9732
10019
  }
9733
10020
  async function rewriteThreeEndManagedBlocks(projectRoot) {
9734
- const snapshotPath = join21(projectRoot, ".fabric", "AGENTS.md");
10021
+ const snapshotPath = join23(projectRoot, ".fabric", "AGENTS.md");
9735
10022
  if (!await pathExists(snapshotPath)) {
9736
10023
  return;
9737
10024
  }
9738
10025
  let snapshot;
9739
10026
  try {
9740
- snapshot = await readFile16(snapshotPath, "utf8");
10027
+ snapshot = await readFile17(snapshotPath, "utf8");
9741
10028
  } catch {
9742
10029
  return;
9743
10030
  }
9744
- const projectRulesPath = join21(projectRoot, ".fabric", "project-rules.md");
10031
+ const projectRulesPath = join23(projectRoot, ".fabric", "project-rules.md");
9745
10032
  const hasProjectRules = await pathExists(projectRulesPath);
9746
10033
  let expectedBody = snapshot;
9747
10034
  if (hasProjectRules) {
9748
10035
  try {
9749
- const projectRules = await readFile16(projectRulesPath, "utf8");
10036
+ const projectRules = await readFile17(projectRulesPath, "utf8");
9750
10037
  expectedBody = `${snapshot}
9751
10038
  ---
9752
10039
  ${projectRules}`;
@@ -9757,7 +10044,7 @@ ${projectRules}`;
9757
10044
  ${expectedBody}
9758
10045
  ${BOOTSTRAP_MARKER_END2}`;
9759
10046
  const blockTargets = [
9760
- join21(projectRoot, "AGENTS.md")
10047
+ join23(projectRoot, "AGENTS.md")
9761
10048
  ];
9762
10049
  for (const abs of blockTargets) {
9763
10050
  if (!await pathExists(abs)) {
@@ -9765,7 +10052,7 @@ ${BOOTSTRAP_MARKER_END2}`;
9765
10052
  }
9766
10053
  let existing;
9767
10054
  try {
9768
- existing = await readFile16(abs, "utf8");
10055
+ existing = await readFile17(abs, "utf8");
9769
10056
  } catch {
9770
10057
  continue;
9771
10058
  }
@@ -9790,11 +10077,11 @@ ${managedBlock}
9790
10077
  }
9791
10078
  await atomicWriteText4(abs, next);
9792
10079
  }
9793
- const claudeMdPath = join21(projectRoot, "CLAUDE.md");
10080
+ const claudeMdPath = join23(projectRoot, "CLAUDE.md");
9794
10081
  if (await pathExists(claudeMdPath)) {
9795
10082
  let claudeContent;
9796
10083
  try {
9797
- claudeContent = await readFile16(claudeMdPath, "utf8");
10084
+ claudeContent = await readFile17(claudeMdPath, "utf8");
9798
10085
  } catch {
9799
10086
  return;
9800
10087
  }
@@ -9821,7 +10108,7 @@ ${managedBlock}
9821
10108
  async function ensureEventLedger(projectRoot) {
9822
10109
  const path = getEventLedgerPath(projectRoot);
9823
10110
  await ensureParentDirectory(path);
9824
- await writeFile3(path, "", { encoding: "utf8", flag: "a" });
10111
+ await writeFile4(path, "", { encoding: "utf8", flag: "a" });
9825
10112
  }
9826
10113
  function createFixMessage(fixed, report) {
9827
10114
  const fixedText = fixed.length === 0 ? "No deterministic doctor fixes were needed." : `Applied ${fixed.length} deterministic doctor fix${fixed.length === 1 ? "" : "es"}.`;
@@ -9860,7 +10147,7 @@ async function collectEntryPoints(root) {
9860
10147
  continue;
9861
10148
  }
9862
10149
  for (const entry of await readdirAsync(current, { withFileTypes: true })) {
9863
- const absolutePath = join21(current, entry.name);
10150
+ const absolutePath = join23(current, entry.name);
9864
10151
  const relativePath = normalizePath2(absolutePath.slice(root.length + 1));
9865
10152
  if (relativePath.length === 0) {
9866
10153
  continue;
@@ -9936,7 +10223,7 @@ async function enrichDescriptions(projectRoot, opts = {}) {
9936
10223
  scanned += 1;
9937
10224
  let source;
9938
10225
  try {
9939
- source = await readFile16(absPath, "utf8");
10226
+ source = await readFile17(absPath, "utf8");
9940
10227
  } catch {
9941
10228
  continue;
9942
10229
  }
@@ -10074,14 +10361,14 @@ async function runDoctorConflictLint(projectRoot, opts = {}) {
10074
10361
  }
10075
10362
 
10076
10363
  // src/services/why-not-surfaced.ts
10077
- import { readFile as readFile17 } from "fs/promises";
10078
- import { basename as basename3, join as join22 } from "path";
10364
+ import { readFile as readFile18 } from "fs/promises";
10365
+ import { basename as basename3, join as join24 } from "path";
10079
10366
  import {
10080
10367
  buildStoreResolveInput as buildStoreResolveInput7,
10081
10368
  createStoreResolver as createStoreResolver7,
10082
10369
  loadProjectConfig as loadProjectConfig4,
10083
10370
  readKnowledgeAcrossStores as readKnowledgeAcrossStores4,
10084
- resolveGlobalRoot as resolveGlobalRoot7,
10371
+ resolveGlobalRoot as resolveGlobalRoot8,
10085
10372
  scopeRoot as scopeRoot3,
10086
10373
  storeRelativePathForMount as storeRelativePathForMount6
10087
10374
  } from "@fenglimg/fabric-shared";
@@ -10111,11 +10398,11 @@ async function explainWhyNotSurfaced(projectRoot, query) {
10111
10398
  if (input === null) {
10112
10399
  return base;
10113
10400
  }
10114
- const globalRoot = resolveGlobalRoot7();
10401
+ const globalRoot = resolveGlobalRoot8();
10115
10402
  const allStores = input.mountedStores.map((s) => ({
10116
10403
  store_uuid: s.store_uuid,
10117
10404
  alias: s.alias,
10118
- dir: join22(globalRoot, storeRelativePathForMount6(s))
10405
+ dir: join24(globalRoot, storeRelativePathForMount6(s))
10119
10406
  }));
10120
10407
  const refs = await readKnowledgeAcrossStores4(allStores);
10121
10408
  const candidate = refs.find((ref) => fileMatchesId(ref.file, localId));
@@ -10124,7 +10411,7 @@ async function explainWhyNotSurfaced(projectRoot, query) {
10124
10411
  }
10125
10412
  let source;
10126
10413
  try {
10127
- source = await readFile17(candidate.file, "utf8");
10414
+ source = await readFile18(candidate.file, "utf8");
10128
10415
  } catch {
10129
10416
  return base;
10130
10417
  }
@@ -10212,7 +10499,7 @@ import { IOFabricError as IOFabricError2, RuleError } from "@fenglimg/fabric-sha
10212
10499
 
10213
10500
  // src/services/read-ledger.ts
10214
10501
  import { randomUUID as randomUUID7 } from "crypto";
10215
- import { access as access5, copyFile, readFile as readFile18, rm } from "fs/promises";
10502
+ import { access as access5, copyFile, readFile as readFile19, rm } from "fs/promises";
10216
10503
  import { ledgerEntrySchema } from "@fenglimg/fabric-shared";
10217
10504
  async function resolveLedgerPaths(projectRoot) {
10218
10505
  const primaryPath = getLedgerPath(projectRoot);
@@ -10240,7 +10527,7 @@ async function readLegacyLedger(projectRoot) {
10240
10527
  const { readPath } = await resolveLedgerPaths(projectRoot);
10241
10528
  let raw;
10242
10529
  try {
10243
- raw = await readFile18(readPath, "utf8");
10530
+ raw = await readFile19(readPath, "utf8");
10244
10531
  } catch (error) {
10245
10532
  if (isNodeError(error) && error.code === "ENOENT") {
10246
10533
  return [];
@@ -10495,8 +10782,8 @@ function formatError(error) {
10495
10782
  }
10496
10783
  function formatPreexistingRootMessage(projectRoot) {
10497
10784
  const preexisting = [];
10498
- if (existsSync9(join23(projectRoot, "CLAUDE.md"))) preexisting.push("CLAUDE.md");
10499
- if (existsSync9(join23(projectRoot, "AGENTS.md"))) preexisting.push("AGENTS.md");
10785
+ if (existsSync9(join25(projectRoot, "CLAUDE.md"))) preexisting.push("CLAUDE.md");
10786
+ if (existsSync9(join25(projectRoot, "AGENTS.md"))) preexisting.push("AGENTS.md");
10500
10787
  if (preexisting.length === 0) return null;
10501
10788
  return `[startup] info: detected ${preexisting.join(", ")} at project root. Note: Fabric serves knowledge from mounted stores via MCP \u2014 root markdown files are not auto-loaded into the AI context.`;
10502
10789
  }
@@ -10523,7 +10810,7 @@ function createFabricServer(tracker) {
10523
10810
  const server = new McpServer(
10524
10811
  {
10525
10812
  name: "fabric-knowledge-server",
10526
- version: "2.3.0-rc.1"
10813
+ version: "2.3.0-rc.3"
10527
10814
  },
10528
10815
  {
10529
10816
  instructions: FABRIC_SERVER_INSTRUCTIONS
@@ -10543,10 +10830,10 @@ function createFabricServer(tracker) {
10543
10830
  },
10544
10831
  async (_uri) => {
10545
10832
  const projectRoot = process.env.FABRIC_PROJECT_ROOT ?? process.cwd();
10546
- const path = join23(projectRoot, ".fabric", "bootstrap", "README.md");
10833
+ const path = join25(projectRoot, ".fabric", "bootstrap", "README.md");
10547
10834
  let text = "";
10548
10835
  if (existsSync9(path)) {
10549
- text = await readFile19(path, "utf8");
10836
+ text = await readFile20(path, "utf8");
10550
10837
  }
10551
10838
  return {
10552
10839
  contents: [
@@ -10644,6 +10931,7 @@ export {
10644
10931
  LEGACY_LEDGER_PATH,
10645
10932
  METRICS_LEDGER_PATH,
10646
10933
  METRIC_COUNTER_NAMES,
10934
+ OPTIONAL_EMBED_PACKAGE,
10647
10935
  RETIRED_TOKENS,
10648
10936
  appendEventLedgerEvent,
10649
10937
  buildAlwaysActiveBodies,
@@ -10654,6 +10942,7 @@ export {
10654
10942
  createFabricServer,
10655
10943
  createInFlightTracker,
10656
10944
  createShutdownHandler,
10945
+ defaultEmbedCacheDir,
10657
10946
  detectUnboundProject,
10658
10947
  drainCounters,
10659
10948
  enrichDescriptions,
@@ -10670,9 +10959,12 @@ export {
10670
10959
  inspectRetiredReferences,
10671
10960
  lintConflicts,
10672
10961
  loadConflictEntries,
10962
+ loadEmbedder,
10673
10963
  pairSimilarity,
10674
10964
  planContext,
10965
+ readEmbedConfig,
10675
10966
  readEventLedger,
10967
+ readFusion,
10676
10968
  readLedger,
10677
10969
  readMetrics,
10678
10970
  readSelectionToken,