@fenglimg/fabric-server 2.0.0-rc.28 → 2.0.0-rc.29

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.
@@ -84,7 +84,7 @@ import { readFile } from "fs/promises";
84
84
  import { join } from "path";
85
85
  import { agentsMetaSchema } from "@fenglimg/fabric-shared";
86
86
  import { IOFabricError } from "@fenglimg/fabric-shared/errors";
87
- import { agentsMetaNodeSchema, agentsMetaSchema as agentsMetaSchema2 } from "@fenglimg/fabric-shared";
87
+ import { agentsMetaSchema as agentsMetaSchema2 } from "@fenglimg/fabric-shared";
88
88
  var AgentsMetaFileMissingError = class extends IOFabricError {
89
89
  constructor(metaPath, opts) {
90
90
  super(`Fabric agents metadata file is missing: ${metaPath}`, {
@@ -1078,9 +1078,17 @@ function extractKnowledgeFieldsFromFrontmatter(frontmatter) {
1078
1078
  `);
1079
1079
  }
1080
1080
  }
1081
+ const SINGULAR_TO_PLURAL = {
1082
+ model: "models",
1083
+ decision: "decisions",
1084
+ guideline: "guidelines",
1085
+ pitfall: "pitfalls",
1086
+ process: "processes"
1087
+ };
1081
1088
  let knowledge_type;
1082
1089
  if (rawType !== void 0) {
1083
- const parsed = KnowledgeTypeSchema.safeParse(rawType);
1090
+ const normalized = SINGULAR_TO_PLURAL[rawType] ?? rawType;
1091
+ const parsed = KnowledgeTypeSchema.safeParse(normalized);
1084
1092
  if (parsed.success) {
1085
1093
  knowledge_type = parsed.data;
1086
1094
  } else {
@@ -1429,6 +1437,12 @@ async function ensureKnowledgeFresh(projectRoot, opts) {
1429
1437
  contextCache.invalidate("file_watch", projectRoot);
1430
1438
  }
1431
1439
  freshSyncCooldown.delete(projectRoot);
1440
+ if (opts?.autoHealOnDrift === true && events.length > 0) {
1441
+ try {
1442
+ await reconcileKnowledge(projectRoot, { trigger: "auto-heal-after-drift" });
1443
+ } catch {
1444
+ }
1445
+ }
1432
1446
  const status = warnings.length > 0 ? "errors" : "reconciled";
1433
1447
  return {
1434
1448
  status,
@@ -1620,11 +1634,11 @@ function checkLockOrThrow(projectRoot, opts) {
1620
1634
 
1621
1635
  // src/services/doctor.ts
1622
1636
  import { execFileSync } from "child_process";
1623
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2, statSync as statSync4 } from "fs";
1637
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
1624
1638
  import { access, mkdir as mkdir4, readFile as readFile5, rename, unlink, writeFile as writeFile2 } from "fs/promises";
1625
1639
  import { constants } from "fs";
1626
1640
  import { homedir as homedir3 } from "os";
1627
- import { isAbsolute as isAbsolute2, join as join6, posix, relative as nodeRelative, resolve as resolve3, sep as sep3 } from "path";
1641
+ import { isAbsolute as isAbsolute2, join as join7, posix, relative as nodeRelative, resolve as resolve3, sep as sep3 } from "path";
1628
1642
  import { minimatch } from "minimatch";
1629
1643
  import {
1630
1644
  agentsMetaSchema as agentsMetaSchema4,
@@ -1643,6 +1657,41 @@ import {
1643
1657
  resolveFabricLocale as resolveFabricLocale2
1644
1658
  } from "@fenglimg/fabric-shared";
1645
1659
  import { detectFramework } from "@fenglimg/fabric-shared/node";
1660
+ import {
1661
+ PAYLOAD_LIMIT_DEFAULT_HARD_BYTES,
1662
+ PAYLOAD_LIMIT_DEFAULT_WARN_BYTES
1663
+ } from "@fenglimg/fabric-shared/node/mcp-payload-guard";
1664
+
1665
+ // src/config-loader.ts
1666
+ import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
1667
+ import { join as join6 } from "path";
1668
+ import { selectionTokenTtlMsSchema } from "@fenglimg/fabric-shared";
1669
+ function readFabricConfig(projectRoot) {
1670
+ const configPath = join6(projectRoot, "fabric.config.json");
1671
+ if (!existsSync4(configPath)) {
1672
+ return {};
1673
+ }
1674
+ const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
1675
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1676
+ throw new Error(`Expected object in ${configPath}`);
1677
+ }
1678
+ return parsed;
1679
+ }
1680
+ function readPayloadLimits(projectRoot) {
1681
+ return readFabricConfig(projectRoot).mcpPayloadLimits;
1682
+ }
1683
+ function readSelectionTokenTtlMs(projectRoot) {
1684
+ try {
1685
+ const raw = readFabricConfig(projectRoot).selection_token_ttl_ms;
1686
+ if (raw === void 0) return void 0;
1687
+ const parsed = selectionTokenTtlMsSchema.safeParse(raw);
1688
+ return parsed.success ? parsed.data : void 0;
1689
+ } catch {
1690
+ return void 0;
1691
+ }
1692
+ }
1693
+
1694
+ // src/services/doctor.ts
1646
1695
  import { atomicWriteJson as atomicWriteJson2, atomicWriteText as atomicWriteText4 } from "@fenglimg/fabric-shared/node/atomic-write";
1647
1696
  var ORPHAN_DEMOTE_THRESHOLD_DAYS = {
1648
1697
  stable: 90,
@@ -1914,11 +1963,27 @@ async function runDoctorReport(target) {
1914
1963
  warningCount: warnings.length,
1915
1964
  infoCount: infos.length,
1916
1965
  targetFiles: Object.fromEntries(
1917
- TARGET_FILE_PATHS.map((path2) => [path2, existsSync4(join6(projectRoot, path2))])
1918
- )
1966
+ TARGET_FILE_PATHS.map((path2) => [path2, existsSync5(join7(projectRoot, path2))])
1967
+ ),
1968
+ // v2.0.0-rc.29 TASK-008 (BUG-F2): resolve and surface payload thresholds.
1969
+ // Best-effort: a corrupt fabric.config.json should not fail doctor; on
1970
+ // any read/parse error fall back to library defaults with source="default".
1971
+ payload_limits: resolvePayloadLimits(projectRoot)
1919
1972
  }
1920
1973
  };
1921
1974
  }
1975
+ function resolvePayloadLimits(projectRoot) {
1976
+ let override;
1977
+ try {
1978
+ override = readPayloadLimits(projectRoot);
1979
+ } catch {
1980
+ override = void 0;
1981
+ }
1982
+ const warn = override?.warnBytes ?? PAYLOAD_LIMIT_DEFAULT_WARN_BYTES;
1983
+ const hard = override?.hardBytes ?? PAYLOAD_LIMIT_DEFAULT_HARD_BYTES;
1984
+ const source = override?.warnBytes !== void 0 || override?.hardBytes !== void 0 ? "config" : "default";
1985
+ return { warn_bytes: warn, hard_bytes: hard, source };
1986
+ }
1922
1987
  async function runDoctorFix(target) {
1923
1988
  const projectRoot = normalizeTarget(target);
1924
1989
  const before = await runDoctorReport(projectRoot);
@@ -1941,7 +2006,7 @@ async function runDoctorFix(target) {
1941
2006
  }
1942
2007
  }
1943
2008
  if (before.fixable_errors.some((issue) => issue.code === "bootstrap_snapshot_drift")) {
1944
- const snapshotPath = join6(projectRoot, ".fabric", "AGENTS.md");
2009
+ const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
1945
2010
  await ensureParentDirectory(snapshotPath);
1946
2011
  await atomicWriteText4(snapshotPath, BOOTSTRAP_CANONICAL);
1947
2012
  fixed.push(findIssue(before.fixable_errors, "bootstrap_snapshot_drift"));
@@ -2015,7 +2080,7 @@ async function runDoctorFix(target) {
2015
2080
  if (before.infos.some((issue) => issue.code === "stale_serve_lock")) {
2016
2081
  const lockInspection = inspectStaleServeLock(projectRoot, Date.now());
2017
2082
  if (lockInspection.present && !lockInspection.pidAlive) {
2018
- const lockFilePath = join6(projectRoot, ".fabric", ".serve.lock");
2083
+ const lockFilePath = join7(projectRoot, ".fabric", ".serve.lock");
2019
2084
  try {
2020
2085
  await unlink(lockFilePath);
2021
2086
  } catch (err) {
@@ -2172,7 +2237,7 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
2172
2237
  };
2173
2238
  }
2174
2239
  const detail = `${candidate.maturity} -> ${next}`;
2175
- const absPath = join6(projectRoot, candidate.path);
2240
+ const absPath = join7(projectRoot, candidate.path);
2176
2241
  try {
2177
2242
  const source = await readFile5(absPath, "utf8");
2178
2243
  const rewritten = rewriteFrontmatterMaturity(source, next);
@@ -2239,11 +2304,11 @@ async function applyOrphanDemote(projectRoot, candidate, now) {
2239
2304
  }
2240
2305
  }
2241
2306
  async function applyStaleArchive(projectRoot, candidate, now) {
2242
- const sourceAbs = join6(projectRoot, candidate.path);
2243
- const destAbs = join6(projectRoot, candidate.archive_path);
2307
+ const sourceAbs = join7(projectRoot, candidate.path);
2308
+ const destAbs = join7(projectRoot, candidate.archive_path);
2244
2309
  const detail = `${candidate.path} -> ${candidate.archive_path}`;
2245
2310
  try {
2246
- await mkdir4(join6(destAbs, ".."), { recursive: true });
2311
+ await mkdir4(join7(destAbs, ".."), { recursive: true });
2247
2312
  try {
2248
2313
  await rename(sourceAbs, destAbs);
2249
2314
  } catch (renameError) {
@@ -2302,7 +2367,7 @@ async function applyStaleArchive(projectRoot, candidate, now) {
2302
2367
  async function applyPendingAutoArchive(projectRoot, candidate, now) {
2303
2368
  const detail = `${candidate.pending_path} -> ${candidate.archived_to}`;
2304
2369
  try {
2305
- await mkdir4(join6(candidate.archived_to_abs, ".."), { recursive: true });
2370
+ await mkdir4(join7(candidate.archived_to_abs, ".."), { recursive: true });
2306
2371
  let moved = false;
2307
2372
  if (candidate.layer === "team") {
2308
2373
  try {
@@ -2379,7 +2444,7 @@ function relativePosix(projectRoot, absolutePath) {
2379
2444
  }
2380
2445
  async function applySessionHintsStaleCleanup(projectRoot, candidate) {
2381
2446
  const detail = `deleted (${candidate.age_days}d old)`;
2382
- const absPath = join6(projectRoot, candidate.path);
2447
+ const absPath = join7(projectRoot, candidate.path);
2383
2448
  try {
2384
2449
  const { unlink: unlink2 } = await import("fs/promises");
2385
2450
  await unlink2(absPath);
@@ -2400,7 +2465,7 @@ async function applySessionHintsStaleCleanup(projectRoot, candidate) {
2400
2465
  }
2401
2466
  }
2402
2467
  async function applyIndexDriftFix(projectRoot, inspection) {
2403
- const metaPath = join6(projectRoot, ".fabric", "agents.meta.json");
2468
+ const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
2404
2469
  const detailParts = [];
2405
2470
  try {
2406
2471
  const meta = agentsMetaSchema4.parse(JSON.parse(await readFile5(metaPath, "utf8")));
@@ -2436,7 +2501,7 @@ function truncateErrorMessage(error) {
2436
2501
  return raw.length > 240 ? `${raw.slice(0, 237)}...` : raw;
2437
2502
  }
2438
2503
  async function inspectForensic(projectRoot) {
2439
- const path2 = join6(projectRoot, ".fabric", "forensic.json");
2504
+ const path2 = join7(projectRoot, ".fabric", "forensic.json");
2440
2505
  try {
2441
2506
  const parsed = forensicReportSchema.parse(JSON.parse(await readFile5(path2, "utf8")));
2442
2507
  return { present: true, valid: true, report: parsed };
@@ -2448,12 +2513,12 @@ async function inspectForensic(projectRoot) {
2448
2513
  }
2449
2514
  }
2450
2515
  function inspectMcpConfigInWrongFile(projectRoot) {
2451
- const settingsPath = join6(projectRoot, ".claude", "settings.json");
2452
- if (!existsSync4(settingsPath)) {
2516
+ const settingsPath = join7(projectRoot, ".claude", "settings.json");
2517
+ if (!existsSync5(settingsPath)) {
2453
2518
  return { hasWrongEntry: false, settingsPath };
2454
2519
  }
2455
2520
  try {
2456
- const parsed = JSON.parse(readFileSync2(settingsPath, "utf8"));
2521
+ const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
2457
2522
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2458
2523
  return { hasWrongEntry: false, settingsPath };
2459
2524
  }
@@ -2469,7 +2534,7 @@ function inspectMcpConfigInWrongFile(projectRoot) {
2469
2534
  }
2470
2535
  }
2471
2536
  async function inspectMeta(projectRoot) {
2472
- const metaPath = join6(projectRoot, ".fabric", "agents.meta.json");
2537
+ const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
2473
2538
  const built = await tryBuildRuleMeta(projectRoot);
2474
2539
  try {
2475
2540
  const raw = await readFile5(metaPath, "utf8");
@@ -2542,7 +2607,7 @@ function inspectContentRefs(projectRoot, meta) {
2542
2607
  if (isPersonalKnowledge) {
2543
2608
  continue;
2544
2609
  }
2545
- if (!existsSync4(join6(projectRoot, contentRef))) {
2610
+ if (!existsSync5(join7(projectRoot, contentRef))) {
2546
2611
  missing.push(contentRef);
2547
2612
  }
2548
2613
  }
@@ -2550,7 +2615,7 @@ function inspectContentRefs(projectRoot, meta) {
2550
2615
  }
2551
2616
  async function inspectEventLedger(projectRoot) {
2552
2617
  const path2 = getEventLedgerPath(projectRoot);
2553
- const exists = existsSync4(path2);
2618
+ const exists = existsSync5(path2);
2554
2619
  if (!exists) {
2555
2620
  return {
2556
2621
  exists: false,
@@ -2626,8 +2691,8 @@ function inspectSkillRefMirror(projectRoot) {
2626
2691
  const skillSlugs = ["fabric-archive", "fabric-review", "fabric-import"];
2627
2692
  const driftedPaths = [];
2628
2693
  for (const slug of skillSlugs) {
2629
- const claudeRef = join6(projectRoot, ".claude", "skills", slug, "ref");
2630
- const codexRef = join6(projectRoot, ".codex", "skills", slug, "ref");
2694
+ const claudeRef = join7(projectRoot, ".claude", "skills", slug, "ref");
2695
+ const codexRef = join7(projectRoot, ".codex", "skills", slug, "ref");
2631
2696
  let claudeFiles = null;
2632
2697
  let codexFiles = null;
2633
2698
  try {
@@ -2652,12 +2717,12 @@ function inspectSkillRefMirror(projectRoot) {
2652
2717
  let claudeBody;
2653
2718
  let codexBody;
2654
2719
  try {
2655
- claudeBody = readFileSync2(join6(claudeRef, fname), "utf8");
2720
+ claudeBody = readFileSync3(join7(claudeRef, fname), "utf8");
2656
2721
  } catch {
2657
2722
  continue;
2658
2723
  }
2659
2724
  try {
2660
- codexBody = readFileSync2(join6(codexRef, fname), "utf8");
2725
+ codexBody = readFileSync3(join7(codexRef, fname), "utf8");
2661
2726
  } catch {
2662
2727
  continue;
2663
2728
  }
@@ -2670,7 +2735,7 @@ function inspectSkillRefMirror(projectRoot) {
2670
2735
  return { status: "drift", driftedPaths };
2671
2736
  }
2672
2737
  async function inspectKnowledgeTestIndex(projectRoot) {
2673
- const path2 = join6(projectRoot, ".fabric", ".cache", "knowledge-test.index.json");
2738
+ const path2 = join7(projectRoot, ".fabric", ".cache", "knowledge-test.index.json");
2674
2739
  const built = await tryBuildRuleMeta(projectRoot);
2675
2740
  try {
2676
2741
  const index = knowledgeTestIndexSchema2.parse(JSON.parse(await readFile5(path2, "utf8")));
@@ -2694,8 +2759,8 @@ async function inspectKnowledgeTestIndex(projectRoot) {
2694
2759
  }
2695
2760
  function inspectBootstrapAnchor(projectRoot) {
2696
2761
  return {
2697
- hasAgentsMd: existsSync4(join6(projectRoot, "AGENTS.md")),
2698
- hasClaudeMd: existsSync4(join6(projectRoot, "CLAUDE.md"))
2762
+ hasAgentsMd: existsSync5(join7(projectRoot, "AGENTS.md")),
2763
+ hasClaudeMd: existsSync5(join7(projectRoot, "CLAUDE.md"))
2699
2764
  };
2700
2765
  }
2701
2766
  var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
@@ -2707,8 +2772,8 @@ var BOOTSTRAP_MARKER_MIGRATION_TARGETS = [
2707
2772
  async function inspectBootstrapMarkerMigration(target) {
2708
2773
  const filesNeedingMigration = [];
2709
2774
  for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
2710
- const abs = join6(target, rel);
2711
- if (!existsSync4(abs)) {
2775
+ const abs = join7(target, rel);
2776
+ if (!existsSync5(abs)) {
2712
2777
  continue;
2713
2778
  }
2714
2779
  let content;
@@ -2745,8 +2810,8 @@ function createBootstrapMarkerMigrationCheck(t, inspection) {
2745
2810
  );
2746
2811
  }
2747
2812
  async function inspectL1BootstrapSnapshotDrift(target) {
2748
- const abs = join6(target, ".fabric", "AGENTS.md");
2749
- if (!existsSync4(abs)) {
2813
+ const abs = join7(target, ".fabric", "AGENTS.md");
2814
+ if (!existsSync5(abs)) {
2750
2815
  return { status: "missing", canonical: BOOTSTRAP_CANONICAL, onDisk: null };
2751
2816
  }
2752
2817
  let onDisk;
@@ -2777,8 +2842,8 @@ function createL1BootstrapSnapshotDriftCheck(t, inspection) {
2777
2842
  );
2778
2843
  }
2779
2844
  async function inspectL2ManagedBlockDrift(target) {
2780
- const snapshotPath = join6(target, ".fabric", "AGENTS.md");
2781
- if (!existsSync4(snapshotPath)) {
2845
+ const snapshotPath = join7(target, ".fabric", "AGENTS.md");
2846
+ if (!existsSync5(snapshotPath)) {
2782
2847
  return { status: "ok", drifted: [] };
2783
2848
  }
2784
2849
  let snapshot;
@@ -2787,9 +2852,9 @@ async function inspectL2ManagedBlockDrift(target) {
2787
2852
  } catch {
2788
2853
  return { status: "ok", drifted: [] };
2789
2854
  }
2790
- const projectRulesPath = join6(target, ".fabric", "project-rules.md");
2855
+ const projectRulesPath = join7(target, ".fabric", "project-rules.md");
2791
2856
  let expectedBody = snapshot;
2792
- if (existsSync4(projectRulesPath)) {
2857
+ if (existsSync5(projectRulesPath)) {
2793
2858
  try {
2794
2859
  const projectRules = await readFile5(projectRulesPath, "utf8");
2795
2860
  expectedBody = `${snapshot}
@@ -2801,11 +2866,11 @@ ${projectRules}`;
2801
2866
  const drifted = [];
2802
2867
  let anyManagedBlockFound = false;
2803
2868
  const blockTargets = [
2804
- join6(target, "AGENTS.md"),
2805
- join6(target, ".cursor", "rules", "fabric-bootstrap.mdc")
2869
+ join7(target, "AGENTS.md"),
2870
+ join7(target, ".cursor", "rules", "fabric-bootstrap.mdc")
2806
2871
  ];
2807
2872
  for (const abs of blockTargets) {
2808
- if (!existsSync4(abs)) {
2873
+ if (!existsSync5(abs)) {
2809
2874
  continue;
2810
2875
  }
2811
2876
  let content;
@@ -2836,8 +2901,8 @@ ${projectRules}`;
2836
2901
  drifted.push({ path: abs, expected: expectedBody, actual: body });
2837
2902
  }
2838
2903
  }
2839
- const claudeMdPath = join6(target, "CLAUDE.md");
2840
- if (existsSync4(claudeMdPath)) {
2904
+ const claudeMdPath = join7(target, "CLAUDE.md");
2905
+ if (existsSync5(claudeMdPath)) {
2841
2906
  let claudeContent;
2842
2907
  try {
2843
2908
  claudeContent = await readFile5(claudeMdPath, "utf8");
@@ -2907,11 +2972,11 @@ function createBootstrapAnchorCheck(t, inspection) {
2907
2972
  );
2908
2973
  }
2909
2974
  function inspectKnowledgeDirMissing(projectRoot) {
2910
- const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
2975
+ const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
2911
2976
  const missingSubdirs = [];
2912
2977
  for (const sub of KNOWLEDGE_SUBDIRS3) {
2913
- const path2 = join6(knowledgeRoot, sub);
2914
- if (!existsSync4(path2)) {
2978
+ const path2 = join7(knowledgeRoot, sub);
2979
+ if (!existsSync5(path2)) {
2915
2980
  missingSubdirs.push(`.fabric/knowledge/${sub}`);
2916
2981
  }
2917
2982
  }
@@ -2919,13 +2984,13 @@ function inspectKnowledgeDirMissing(projectRoot) {
2919
2984
  }
2920
2985
  function inspectBaselineFilenameFormat(projectRoot) {
2921
2986
  const offenders = [];
2922
- const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
2923
- if (!existsSync4(knowledgeRoot)) {
2987
+ const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
2988
+ if (!existsSync5(knowledgeRoot)) {
2924
2989
  return { offenders };
2925
2990
  }
2926
2991
  for (const sub of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
2927
- const dir = join6(knowledgeRoot, sub);
2928
- if (!existsSync4(dir)) {
2992
+ const dir = join7(knowledgeRoot, sub);
2993
+ if (!existsSync5(dir)) {
2929
2994
  continue;
2930
2995
  }
2931
2996
  let entries;
@@ -2942,10 +3007,10 @@ function inspectBaselineFilenameFormat(projectRoot) {
2942
3007
  if (BASELINE_ID_PREFIXED_FILENAME_PATTERN.test(entryName)) {
2943
3008
  continue;
2944
3009
  }
2945
- const abs = join6(dir, entryName);
3010
+ const abs = join7(dir, entryName);
2946
3011
  let source;
2947
3012
  try {
2948
- source = readFileSync2(abs, "utf8");
3013
+ source = readFileSync3(abs, "utf8");
2949
3014
  } catch {
2950
3015
  continue;
2951
3016
  }
@@ -3319,8 +3384,8 @@ function findIssue(issues, code) {
3319
3384
  };
3320
3385
  }
3321
3386
  async function inspectMetaManuallyDiverged(projectRoot) {
3322
- const metaPath = join6(projectRoot, ".fabric", "agents.meta.json");
3323
- if (!existsSync4(metaPath)) {
3387
+ const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
3388
+ if (!existsSync5(metaPath)) {
3324
3389
  return { extraMetaEntries: [], hashMismatchEntries: [], readable: false };
3325
3390
  }
3326
3391
  let meta;
@@ -3339,13 +3404,13 @@ async function inspectMetaManuallyDiverged(projectRoot) {
3339
3404
  const hashMismatchEntries = [];
3340
3405
  for (const node of Object.values(meta.nodes)) {
3341
3406
  const contentRef = node.content_ref ?? node.file;
3342
- const absPath = join6(projectRoot, contentRef);
3343
- if (!existsSync4(absPath)) {
3407
+ const absPath = join7(projectRoot, contentRef);
3408
+ if (!existsSync5(absPath)) {
3344
3409
  extraMetaEntries.push(contentRef);
3345
3410
  continue;
3346
3411
  }
3347
3412
  try {
3348
- const content = readFileSync2(absPath, "utf8");
3413
+ const content = readFileSync3(absPath, "utf8");
3349
3414
  const diskHash = sha256(content);
3350
3415
  if (node.hash !== "" && node.hash !== diskHash) {
3351
3416
  hashMismatchEntries.push(contentRef);
@@ -3358,7 +3423,7 @@ async function inspectMetaManuallyDiverged(projectRoot) {
3358
3423
  }
3359
3424
  function inspectKnowledgeDirUnindexed(projectRoot, meta) {
3360
3425
  const physicalMdFiles = /* @__PURE__ */ new Set();
3361
- collectMdFilesUnder(physicalMdFiles, projectRoot, join6(projectRoot, ".fabric", "knowledge"), ".fabric/knowledge");
3426
+ collectMdFilesUnder(physicalMdFiles, projectRoot, join7(projectRoot, ".fabric", "knowledge"), ".fabric/knowledge");
3362
3427
  if (physicalMdFiles.size === 0) {
3363
3428
  return { unindexedFiles: [] };
3364
3429
  }
@@ -3373,7 +3438,7 @@ function inspectKnowledgeDirUnindexed(projectRoot, meta) {
3373
3438
  return { unindexedFiles };
3374
3439
  }
3375
3440
  function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
3376
- if (!existsSync4(rootDir)) {
3441
+ if (!existsSync5(rootDir)) {
3377
3442
  return;
3378
3443
  }
3379
3444
  const stack = [rootDir];
@@ -3383,7 +3448,7 @@ function collectMdFilesUnder(out, projectRoot, rootDir, relPrefix) {
3383
3448
  continue;
3384
3449
  }
3385
3450
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3386
- const abs = join6(dir, entry.name);
3451
+ const abs = join7(dir, entry.name);
3387
3452
  if (entry.isDirectory()) {
3388
3453
  if (entry.name !== "pending" && entry.name !== "archive") {
3389
3454
  stack.push(abs);
@@ -3416,8 +3481,8 @@ function createKnowledgeDirUnindexedCheck(t, inspection) {
3416
3481
  }
3417
3482
  async function inspectStableIdCollisions(projectRoot) {
3418
3483
  const found = [];
3419
- const knowledgeDir = join6(projectRoot, ".fabric", "knowledge");
3420
- if (existsSync4(knowledgeDir)) {
3484
+ const knowledgeDir = join7(projectRoot, ".fabric", "knowledge");
3485
+ if (existsSync5(knowledgeDir)) {
3421
3486
  const stack = [knowledgeDir];
3422
3487
  while (stack.length > 0) {
3423
3488
  const dir = stack.pop();
@@ -3425,7 +3490,7 @@ async function inspectStableIdCollisions(projectRoot) {
3425
3490
  continue;
3426
3491
  }
3427
3492
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
3428
- const abs = join6(dir, entry.name);
3493
+ const abs = join7(dir, entry.name);
3429
3494
  if (entry.isDirectory()) {
3430
3495
  stack.push(abs);
3431
3496
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -3490,11 +3555,11 @@ function inspectCounterDesync(meta) {
3490
3555
  }
3491
3556
  const layer = parsed.layer === "personal" ? "KP" : "KT";
3492
3557
  const typeCode = [
3493
- ["model", "MOD"],
3494
- ["decision", "DEC"],
3495
- ["guideline", "GLD"],
3496
- ["pitfall", "PIT"],
3497
- ["process", "PRO"]
3558
+ ["models", "MOD"],
3559
+ ["decisions", "DEC"],
3560
+ ["guidelines", "GLD"],
3561
+ ["pitfalls", "PIT"],
3562
+ ["processes", "PRO"]
3498
3563
  ].find(([t]) => t === parsed.type)?.[1];
3499
3564
  if (typeCode === void 0) {
3500
3565
  continue;
@@ -3604,18 +3669,18 @@ function createMetaManuallyDivergedCheck(t, inspection) {
3604
3669
  }
3605
3670
  function inspectPreexistingRootFiles(projectRoot) {
3606
3671
  const candidates = ["CLAUDE.md", "AGENTS.md"];
3607
- const detected = candidates.filter((name) => existsSync4(join6(projectRoot, name)));
3672
+ const detected = candidates.filter((name) => existsSync5(join7(projectRoot, name)));
3608
3673
  return { detected };
3609
3674
  }
3610
3675
  async function inspectFilesystemEditFallback(projectRoot) {
3611
- const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
3612
- if (!existsSync4(knowledgeRoot)) {
3676
+ const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
3677
+ if (!existsSync5(knowledgeRoot)) {
3613
3678
  return { synthesized: 0, synthesizedStableIds: [] };
3614
3679
  }
3615
3680
  const canonicalIds = /* @__PURE__ */ new Set();
3616
3681
  for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
3617
- const dir = join6(knowledgeRoot, typeDir);
3618
- if (!existsSync4(dir)) {
3682
+ const dir = join7(knowledgeRoot, typeDir);
3683
+ if (!existsSync5(dir)) {
3619
3684
  continue;
3620
3685
  }
3621
3686
  let entries;
@@ -3655,17 +3720,40 @@ async function inspectFilesystemEditFallback(projectRoot) {
3655
3720
  }
3656
3721
  orphanIds.sort();
3657
3722
  for (const stable_id of orphanIds) {
3658
- await appendEventLedgerEvent(projectRoot, {
3659
- event_type: "knowledge_promoted",
3660
- stable_id,
3661
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3662
- reason: SYNTHESIZED_PROMOTED_REASON,
3663
- correlation_id: "doctor-synthesized",
3664
- session_id: "doctor-synthesized"
3665
- });
3723
+ await emitSynthesizedPromotionTriplet(projectRoot, stable_id);
3666
3724
  }
3667
3725
  return { synthesized: orphanIds.length, synthesizedStableIds: orphanIds };
3668
3726
  }
3727
+ async function emitSynthesizedPromotionTriplet(projectRoot, stable_id) {
3728
+ const baseTs = Date.now();
3729
+ await appendEventLedgerEvent(projectRoot, {
3730
+ event_type: "knowledge_proposed",
3731
+ stable_id,
3732
+ timestamp: new Date(baseTs).toISOString(),
3733
+ reason: SYNTHESIZED_PROMOTED_REASON,
3734
+ correlation_id: "doctor-synthesized",
3735
+ session_id: "doctor-synthesized",
3736
+ ts: baseTs
3737
+ });
3738
+ await appendEventLedgerEvent(projectRoot, {
3739
+ event_type: "knowledge_promote_started",
3740
+ stable_id,
3741
+ timestamp: new Date(baseTs + 1).toISOString(),
3742
+ reason: SYNTHESIZED_PROMOTED_REASON,
3743
+ correlation_id: "doctor-synthesized",
3744
+ session_id: "doctor-synthesized",
3745
+ ts: baseTs + 1
3746
+ });
3747
+ await appendEventLedgerEvent(projectRoot, {
3748
+ event_type: "knowledge_promoted",
3749
+ stable_id,
3750
+ timestamp: new Date(baseTs + 2).toISOString(),
3751
+ reason: SYNTHESIZED_PROMOTED_REASON,
3752
+ correlation_id: "doctor-synthesized",
3753
+ session_id: "doctor-synthesized",
3754
+ ts: baseTs + 2
3755
+ });
3756
+ }
3669
3757
  function createFilesystemEditFallbackCheck(t, inspection) {
3670
3758
  if (inspection.synthesized === 0) {
3671
3759
  return okCheck(
@@ -3824,13 +3912,13 @@ function extractKnowledgeFrontmatterCreatedAt(source) {
3824
3912
  return Number.isFinite(parsed) ? parsed : null;
3825
3913
  }
3826
3914
  function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
3827
- const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
3828
- if (!existsSync4(knowledgeRoot)) {
3915
+ const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
3916
+ if (!existsSync5(knowledgeRoot)) {
3829
3917
  return;
3830
3918
  }
3831
3919
  for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
3832
- const dir = join6(knowledgeRoot, typeDir);
3833
- if (!existsSync4(dir)) {
3920
+ const dir = join7(knowledgeRoot, typeDir);
3921
+ if (!existsSync5(dir)) {
3834
3922
  continue;
3835
3923
  }
3836
3924
  let entries;
@@ -3848,10 +3936,10 @@ function* iterateCanonicalEntries(projectRoot, lastActiveIndex) {
3848
3936
  continue;
3849
3937
  }
3850
3938
  const stableId = match[1];
3851
- const absPath = join6(dir, entry.name);
3939
+ const absPath = join7(dir, entry.name);
3852
3940
  let source;
3853
3941
  try {
3854
- source = readFileSync2(absPath, "utf8");
3942
+ source = readFileSync3(absPath, "utf8");
3855
3943
  } catch {
3856
3944
  continue;
3857
3945
  }
@@ -3924,13 +4012,13 @@ async function inspectStaleArchive(projectRoot, now) {
3924
4012
  return { candidates };
3925
4013
  }
3926
4014
  function* iteratePendingFiles(projectRoot, now) {
3927
- const teamRoot = join6(projectRoot, ".fabric", "knowledge", "pending");
3928
- const personalRoot = join6(resolvePersonalRootForPending(), ".fabric", "knowledge", "pending");
4015
+ const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
4016
+ const personalRoot = join7(resolvePersonalRootForPending(), ".fabric", "knowledge", "pending");
3929
4017
  for (const [layer, root, displayPrefix] of [
3930
4018
  ["team", teamRoot, ".fabric/knowledge/pending"],
3931
4019
  ["personal", personalRoot, "~/.fabric/knowledge/pending"]
3932
4020
  ]) {
3933
- if (!existsSync4(root)) {
4021
+ if (!existsSync5(root)) {
3934
4022
  continue;
3935
4023
  }
3936
4024
  let typeDirs = [];
@@ -3940,7 +4028,7 @@ function* iteratePendingFiles(projectRoot, now) {
3940
4028
  continue;
3941
4029
  }
3942
4030
  for (const typeDir of typeDirs) {
3943
- const dir = join6(root, typeDir);
4031
+ const dir = join7(root, typeDir);
3944
4032
  let entries;
3945
4033
  try {
3946
4034
  entries = readdirSync(dir, { withFileTypes: true });
@@ -3951,10 +4039,10 @@ function* iteratePendingFiles(projectRoot, now) {
3951
4039
  if (!entry.isFile() || !entry.name.endsWith(".md")) {
3952
4040
  continue;
3953
4041
  }
3954
- const absPath = join6(dir, entry.name);
4042
+ const absPath = join7(dir, entry.name);
3955
4043
  let source = "";
3956
4044
  try {
3957
- source = readFileSync2(absPath, "utf8");
4045
+ source = readFileSync3(absPath, "utf8");
3958
4046
  } catch {
3959
4047
  continue;
3960
4048
  }
@@ -4026,7 +4114,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
4026
4114
  pending_path: visit.pending_path,
4027
4115
  pending_path_abs: visit.pending_path_abs,
4028
4116
  archived_to: archivedToRel,
4029
- archived_to_abs: join6(projectRoot, archivedToRel),
4117
+ archived_to_abs: join7(projectRoot, archivedToRel),
4030
4118
  age_days: visit.age_days
4031
4119
  });
4032
4120
  } else {
@@ -4035,7 +4123,7 @@ function inspectPendingAutoArchive(projectRoot, now) {
4035
4123
  visit.type,
4036
4124
  visit.filename
4037
4125
  );
4038
- const archivedToAbs = join6(
4126
+ const archivedToAbs = join7(
4039
4127
  resolvePersonalRootForPending(),
4040
4128
  ".fabric",
4041
4129
  ".archive",
@@ -4059,12 +4147,12 @@ function inspectPendingAutoArchive(projectRoot, now) {
4059
4147
  }
4060
4148
  function inspectUnderseeded(projectRoot) {
4061
4149
  const threshold = readUnderseedThresholdFromConfig(projectRoot);
4062
- const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
4150
+ const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
4063
4151
  let nodeCount = 0;
4064
- if (existsSync4(knowledgeRoot)) {
4152
+ if (existsSync5(knowledgeRoot)) {
4065
4153
  for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
4066
- const dir = join6(knowledgeRoot, typeDir);
4067
- if (!existsSync4(dir)) continue;
4154
+ const dir = join7(knowledgeRoot, typeDir);
4155
+ if (!existsSync5(dir)) continue;
4068
4156
  let entries;
4069
4157
  try {
4070
4158
  entries = readdirSync(dir, { withFileTypes: true });
@@ -4085,8 +4173,8 @@ function inspectUnderseeded(projectRoot) {
4085
4173
  };
4086
4174
  }
4087
4175
  function inspectSessionHintsStale(projectRoot, now) {
4088
- const cacheDir = join6(projectRoot, ".fabric", ".cache");
4089
- if (!existsSync4(cacheDir)) {
4176
+ const cacheDir = join7(projectRoot, ".fabric", ".cache");
4177
+ if (!existsSync5(cacheDir)) {
4090
4178
  return { candidates: [] };
4091
4179
  }
4092
4180
  let entries;
@@ -4100,7 +4188,7 @@ function inspectSessionHintsStale(projectRoot, now) {
4100
4188
  if (!entry.isFile()) continue;
4101
4189
  if (!entry.name.startsWith(SESSION_HINTS_FILE_PREFIX)) continue;
4102
4190
  if (!entry.name.endsWith(SESSION_HINTS_FILE_SUFFIX)) continue;
4103
- const absPath = join6(cacheDir, entry.name);
4191
+ const absPath = join7(cacheDir, entry.name);
4104
4192
  let mtimeMs = 0;
4105
4193
  try {
4106
4194
  mtimeMs = statSync4(absPath).mtimeMs;
@@ -4144,11 +4232,11 @@ function inspectNarrowTooFew(projectRoot, now) {
4144
4232
  const structuralFlagged = total >= NARROW_MIN_TOTAL && narrowRatio < NARROW_RATIO_THRESHOLD;
4145
4233
  const windowStartMs = now - SILENCE_WINDOW_DAYS * MS_PER_DAY;
4146
4234
  const editFires = readCounterTimestamps(
4147
- join6(projectRoot, EDIT_COUNTER_FILE_REL),
4235
+ join7(projectRoot, EDIT_COUNTER_FILE_REL),
4148
4236
  windowStartMs
4149
4237
  );
4150
4238
  const silenceFires = readCounterTimestamps(
4151
- join6(projectRoot, HINT_SILENCE_COUNTER_FILE_REL),
4239
+ join7(projectRoot, HINT_SILENCE_COUNTER_FILE_REL),
4152
4240
  windowStartMs
4153
4241
  );
4154
4242
  const telemetrySkipped = editFires === 0;
@@ -4167,10 +4255,10 @@ function inspectNarrowTooFew(projectRoot, now) {
4167
4255
  };
4168
4256
  }
4169
4257
  function readCounterTimestamps(absPath, windowStartMs) {
4170
- if (!existsSync4(absPath)) return 0;
4258
+ if (!existsSync5(absPath)) return 0;
4171
4259
  let raw;
4172
4260
  try {
4173
- raw = readFileSync2(absPath, "utf8");
4261
+ raw = readFileSync3(absPath, "utf8");
4174
4262
  } catch {
4175
4263
  return 0;
4176
4264
  }
@@ -4186,10 +4274,10 @@ function readCounterTimestamps(absPath, windowStartMs) {
4186
4274
  return count;
4187
4275
  }
4188
4276
  function readUnderseedThresholdFromConfig(projectRoot) {
4189
- const configPath = join6(projectRoot, ".fabric", "fabric-config.json");
4190
- if (!existsSync4(configPath)) return DEFAULT_UNDERSEED_NODE_THRESHOLD;
4277
+ const configPath = join7(projectRoot, ".fabric", "fabric-config.json");
4278
+ if (!existsSync5(configPath)) return DEFAULT_UNDERSEED_NODE_THRESHOLD;
4191
4279
  try {
4192
- const raw = readFileSync2(configPath, "utf8");
4280
+ const raw = readFileSync3(configPath, "utf8");
4193
4281
  const parsed = JSON.parse(raw);
4194
4282
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4195
4283
  const v = parsed.underseed_node_threshold;
@@ -4383,11 +4471,11 @@ function extractKnowledgeFrontmatterRelevancePaths(source) {
4383
4471
  }
4384
4472
  function* iterateRelevanceFrontmatter(projectRoot) {
4385
4473
  for (const visit of iterateCanonicalFilenames(projectRoot)) {
4386
- const layerRoot = visit.layer === "team" ? join6(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
4387
- const absPath = join6(layerRoot, visit.type, visit.filename);
4474
+ const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
4475
+ const absPath = join7(layerRoot, visit.type, visit.filename);
4388
4476
  let source;
4389
4477
  try {
4390
- source = readFileSync2(absPath, "utf8");
4478
+ source = readFileSync3(absPath, "utf8");
4391
4479
  } catch {
4392
4480
  continue;
4393
4481
  }
@@ -4449,7 +4537,7 @@ function inspectRelevancePathsDangling(projectRoot) {
4449
4537
  return { entries };
4450
4538
  }
4451
4539
  function collectWorkspacePathsForGlobMatch(projectRoot) {
4452
- if (!existsSync4(projectRoot)) {
4540
+ if (!existsSync5(projectRoot)) {
4453
4541
  return [];
4454
4542
  }
4455
4543
  let rootStat;
@@ -4473,7 +4561,7 @@ function collectWorkspacePathsForGlobMatch(projectRoot) {
4473
4561
  continue;
4474
4562
  }
4475
4563
  for (const entry of entries) {
4476
- const abs = join6(current, entry.name);
4564
+ const abs = join7(current, entry.name);
4477
4565
  const rel = normalizePath(abs.slice(projectRoot.length + 1));
4478
4566
  if (rel.length === 0) continue;
4479
4567
  if (entry.isDirectory()) {
@@ -4630,8 +4718,8 @@ function inspectRelevanceFieldsMissing(projectRoot) {
4630
4718
  const candidates = [];
4631
4719
  let scannedCount = 0;
4632
4720
  const FM_PATTERN = /^(?:\uFEFF)?---\r?\n([\s\S]*?)\r?\n---/u;
4633
- const teamRoot = join6(projectRoot, ".fabric", "knowledge", "pending");
4634
- const personalRoot = join6(
4721
+ const teamRoot = join7(projectRoot, ".fabric", "knowledge", "pending");
4722
+ const personalRoot = join7(
4635
4723
  resolvePersonalRootForPending(),
4636
4724
  ".fabric",
4637
4725
  "knowledge",
@@ -4641,7 +4729,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
4641
4729
  [teamRoot, ".fabric/knowledge/pending"],
4642
4730
  [personalRoot, "~/.fabric/knowledge/pending"]
4643
4731
  ]) {
4644
- if (!existsSync4(root)) {
4732
+ if (!existsSync5(root)) {
4645
4733
  continue;
4646
4734
  }
4647
4735
  let typeDirs = [];
@@ -4651,7 +4739,7 @@ function inspectRelevanceFieldsMissing(projectRoot) {
4651
4739
  continue;
4652
4740
  }
4653
4741
  for (const typeDir of typeDirs) {
4654
- const dir = join6(root, typeDir);
4742
+ const dir = join7(root, typeDir);
4655
4743
  let entries;
4656
4744
  try {
4657
4745
  entries = readdirSync(dir, { withFileTypes: true });
@@ -4662,10 +4750,10 @@ function inspectRelevanceFieldsMissing(projectRoot) {
4662
4750
  if (!entry.isFile() || !entry.name.endsWith(".md")) {
4663
4751
  continue;
4664
4752
  }
4665
- const absPath = join6(dir, entry.name);
4753
+ const absPath = join7(dir, entry.name);
4666
4754
  let source;
4667
4755
  try {
4668
- source = readFileSync2(absPath, "utf8");
4756
+ source = readFileSync3(absPath, "utf8");
4669
4757
  } catch {
4670
4758
  continue;
4671
4759
  }
@@ -4797,8 +4885,8 @@ var SKILL_QUOTED_VALUE_LEADS = /* @__PURE__ */ new Set(['"', "'", "[", "{", ">",
4797
4885
  function inspectSkillMdYamlInvalid(projectRoot) {
4798
4886
  const candidates = [];
4799
4887
  for (const rootRel of SKILL_MD_FRONTMATTER_ROOTS) {
4800
- const rootAbs = join6(projectRoot, rootRel);
4801
- if (!existsSync4(rootAbs)) continue;
4888
+ const rootAbs = join7(projectRoot, rootRel);
4889
+ if (!existsSync5(rootAbs)) continue;
4802
4890
  let dirEntries;
4803
4891
  try {
4804
4892
  dirEntries = readdirSync(rootAbs, { withFileTypes: true });
@@ -4807,11 +4895,11 @@ function inspectSkillMdYamlInvalid(projectRoot) {
4807
4895
  }
4808
4896
  for (const dirEntry of dirEntries) {
4809
4897
  if (!dirEntry.isDirectory()) continue;
4810
- const skillFile = join6(rootAbs, dirEntry.name, "SKILL.md");
4811
- if (!existsSync4(skillFile)) continue;
4898
+ const skillFile = join7(rootAbs, dirEntry.name, "SKILL.md");
4899
+ if (!existsSync5(skillFile)) continue;
4812
4900
  let raw;
4813
4901
  try {
4814
- raw = readFileSync2(skillFile, "utf8");
4902
+ raw = readFileSync3(skillFile, "utf8");
4815
4903
  } catch {
4816
4904
  continue;
4817
4905
  }
@@ -4893,11 +4981,11 @@ function inspectOnboardCoverage(projectRoot) {
4893
4981
  for (const slot of ONBOARD_SLOT_NAMES) {
4894
4982
  filled[slot] = [];
4895
4983
  }
4896
- const knowledgeRoot = join6(projectRoot, ".fabric", "knowledge");
4897
- if (existsSync4(knowledgeRoot)) {
4984
+ const knowledgeRoot = join7(projectRoot, ".fabric", "knowledge");
4985
+ if (existsSync5(knowledgeRoot)) {
4898
4986
  for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS_FOR_ONBOARD) {
4899
- const dir = join6(knowledgeRoot, typeDir);
4900
- if (!existsSync4(dir)) continue;
4987
+ const dir = join7(knowledgeRoot, typeDir);
4988
+ if (!existsSync5(dir)) continue;
4901
4989
  let entries;
4902
4990
  try {
4903
4991
  entries = readdirSync(dir, { withFileTypes: true });
@@ -4907,10 +4995,10 @@ function inspectOnboardCoverage(projectRoot) {
4907
4995
  for (const entry of entries) {
4908
4996
  if (!entry.isFile()) continue;
4909
4997
  if (!entry.name.endsWith(".md")) continue;
4910
- const filePath = join6(dir, entry.name);
4998
+ const filePath = join7(dir, entry.name);
4911
4999
  let content;
4912
5000
  try {
4913
- content = readFileSync2(filePath, "utf8");
5001
+ content = readFileSync3(filePath, "utf8");
4914
5002
  } catch {
4915
5003
  continue;
4916
5004
  }
@@ -4934,11 +5022,11 @@ function inspectOnboardCoverage(projectRoot) {
4934
5022
  return { filled, missing, opted_out: optedOut };
4935
5023
  }
4936
5024
  function readOnboardOptedOut(projectRoot) {
4937
- const path2 = join6(projectRoot, ".fabric", "fabric-config.json");
4938
- if (!existsSync4(path2)) return [];
5025
+ const path2 = join7(projectRoot, ".fabric", "fabric-config.json");
5026
+ if (!existsSync5(path2)) return [];
4939
5027
  let raw;
4940
5028
  try {
4941
- raw = readFileSync2(path2, "utf8");
5029
+ raw = readFileSync3(path2, "utf8");
4942
5030
  } catch {
4943
5031
  return [];
4944
5032
  }
@@ -5054,7 +5142,7 @@ function createNarrowTooFewCheck(t, inspection) {
5054
5142
  }
5055
5143
  function resolvePersonalKnowledgeRoot() {
5056
5144
  const home = process.env.FABRIC_HOME ?? homedir3();
5057
- return join6(home, ".fabric", "knowledge");
5145
+ return join7(home, ".fabric", "knowledge");
5058
5146
  }
5059
5147
  function parseStableIdFromCanonicalFilename(filename) {
5060
5148
  const match = CANONICAL_KNOWLEDGE_FILENAME_PATTERN.exec(filename);
@@ -5074,18 +5162,18 @@ function parseStableIdFromCanonicalFilename(filename) {
5074
5162
  };
5075
5163
  }
5076
5164
  function* iterateCanonicalFilenames(projectRoot) {
5077
- const teamRoot = join6(projectRoot, ".fabric", "knowledge");
5165
+ const teamRoot = join7(projectRoot, ".fabric", "knowledge");
5078
5166
  const personalRoot = resolvePersonalKnowledgeRoot();
5079
5167
  for (const [layer, root, displayPrefix] of [
5080
5168
  ["team", teamRoot, ".fabric/knowledge"],
5081
5169
  ["personal", personalRoot, "~/.fabric/knowledge"]
5082
5170
  ]) {
5083
- if (!existsSync4(root)) {
5171
+ if (!existsSync5(root)) {
5084
5172
  continue;
5085
5173
  }
5086
5174
  for (const typeDir of KNOWLEDGE_CANONICAL_TYPE_DIRS) {
5087
- const dir = join6(root, typeDir);
5088
- if (!existsSync4(dir)) {
5175
+ const dir = join7(root, typeDir);
5176
+ if (!existsSync5(dir)) {
5089
5177
  continue;
5090
5178
  }
5091
5179
  let entries;
@@ -5256,8 +5344,8 @@ async function migrateBootstrapMarkers(projectRoot) {
5256
5344
  const paths = [];
5257
5345
  const countPerPath = {};
5258
5346
  for (const rel of BOOTSTRAP_MARKER_MIGRATION_TARGETS) {
5259
- const abs = join6(projectRoot, rel);
5260
- if (!existsSync4(abs)) {
5347
+ const abs = join7(projectRoot, rel);
5348
+ if (!existsSync5(abs)) {
5261
5349
  continue;
5262
5350
  }
5263
5351
  let original;
@@ -5283,8 +5371,8 @@ async function migrateBootstrapMarkers(projectRoot) {
5283
5371
  return { paths, countPerPath };
5284
5372
  }
5285
5373
  async function rewriteThreeEndManagedBlocks(projectRoot) {
5286
- const snapshotPath = join6(projectRoot, ".fabric", "AGENTS.md");
5287
- if (!existsSync4(snapshotPath)) {
5374
+ const snapshotPath = join7(projectRoot, ".fabric", "AGENTS.md");
5375
+ if (!existsSync5(snapshotPath)) {
5288
5376
  return;
5289
5377
  }
5290
5378
  let snapshot;
@@ -5293,8 +5381,8 @@ async function rewriteThreeEndManagedBlocks(projectRoot) {
5293
5381
  } catch {
5294
5382
  return;
5295
5383
  }
5296
- const projectRulesPath = join6(projectRoot, ".fabric", "project-rules.md");
5297
- const hasProjectRules = existsSync4(projectRulesPath);
5384
+ const projectRulesPath = join7(projectRoot, ".fabric", "project-rules.md");
5385
+ const hasProjectRules = existsSync5(projectRulesPath);
5298
5386
  let expectedBody = snapshot;
5299
5387
  if (hasProjectRules) {
5300
5388
  try {
@@ -5309,11 +5397,11 @@ ${projectRules}`;
5309
5397
  ${expectedBody}
5310
5398
  ${BOOTSTRAP_MARKER_END}`;
5311
5399
  const blockTargets = [
5312
- join6(projectRoot, "AGENTS.md"),
5313
- join6(projectRoot, ".cursor", "rules", "fabric-bootstrap.mdc")
5400
+ join7(projectRoot, "AGENTS.md"),
5401
+ join7(projectRoot, ".cursor", "rules", "fabric-bootstrap.mdc")
5314
5402
  ];
5315
5403
  for (const abs of blockTargets) {
5316
- if (!existsSync4(abs)) {
5404
+ if (!existsSync5(abs)) {
5317
5405
  continue;
5318
5406
  }
5319
5407
  let existing;
@@ -5343,8 +5431,8 @@ ${managedBlock}
5343
5431
  }
5344
5432
  await atomicWriteText4(abs, next);
5345
5433
  }
5346
- const claudeMdPath = join6(projectRoot, "CLAUDE.md");
5347
- if (existsSync4(claudeMdPath)) {
5434
+ const claudeMdPath = join7(projectRoot, "CLAUDE.md");
5435
+ if (existsSync5(claudeMdPath)) {
5348
5436
  let claudeContent;
5349
5437
  try {
5350
5438
  claudeContent = await readFile5(claudeMdPath, "utf8");
@@ -5372,13 +5460,13 @@ ${managedBlock}
5372
5460
  }
5373
5461
  }
5374
5462
  async function fixMcpConfigInWrongFile(projectRoot) {
5375
- const settingsPath = join6(projectRoot, ".claude", "settings.json");
5376
- if (!existsSync4(settingsPath)) {
5463
+ const settingsPath = join7(projectRoot, ".claude", "settings.json");
5464
+ if (!existsSync5(settingsPath)) {
5377
5465
  return;
5378
5466
  }
5379
5467
  let settings;
5380
5468
  try {
5381
- const parsed = JSON.parse(readFileSync2(settingsPath, "utf8"));
5469
+ const parsed = JSON.parse(readFileSync3(settingsPath, "utf8"));
5382
5470
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
5383
5471
  return;
5384
5472
  }
@@ -5406,12 +5494,12 @@ async function fixMcpConfigInWrongFile(projectRoot) {
5406
5494
  }
5407
5495
  async function ensureKnowledgeSubdirs(projectRoot) {
5408
5496
  for (const sub of KNOWLEDGE_SUBDIRS3) {
5409
- await mkdir4(join6(projectRoot, ".fabric", "knowledge", sub), { recursive: true });
5497
+ await mkdir4(join7(projectRoot, ".fabric", "knowledge", sub), { recursive: true });
5410
5498
  }
5411
5499
  }
5412
5500
  async function fixCounterDesync(projectRoot) {
5413
- const metaPath = join6(projectRoot, ".fabric", "agents.meta.json");
5414
- if (!existsSync4(metaPath)) {
5501
+ const metaPath = join7(projectRoot, ".fabric", "agents.meta.json");
5502
+ if (!existsSync5(metaPath)) {
5415
5503
  return;
5416
5504
  }
5417
5505
  let meta;
@@ -5829,8 +5917,8 @@ async function runDoctorCiteCoverage(projectRoot, options) {
5829
5917
  continue;
5830
5918
  }
5831
5919
  bumpLayerType(citeId, kbType);
5832
- if (kbType === "decision" || kbType === "pitfall") {
5833
- if (kbType === "decision") decisionsCited += 1;
5920
+ if (kbType === "decisions" || kbType === "pitfalls") {
5921
+ if (kbType === "decisions") decisionsCited += 1;
5834
5922
  else pitfallsCited += 1;
5835
5923
  const commitment = commitments[i];
5836
5924
  const operators = commitment?.operators ?? [];
@@ -5994,7 +6082,7 @@ function normalizePath(path2) {
5994
6082
  return posix.normalize(path2.split("\\").join("/"));
5995
6083
  }
5996
6084
  function collectEntryPoints(root) {
5997
- if (!existsSync4(root) || !statSync4(root).isDirectory()) {
6085
+ if (!existsSync5(root) || !statSync4(root).isDirectory()) {
5998
6086
  return [];
5999
6087
  }
6000
6088
  const entries = [];
@@ -6005,7 +6093,7 @@ function collectEntryPoints(root) {
6005
6093
  continue;
6006
6094
  }
6007
6095
  for (const entry of readdirSync(current, { withFileTypes: true })) {
6008
- const absolutePath = join6(current, entry.name);
6096
+ const absolutePath = join7(current, entry.name);
6009
6097
  const relativePath = normalizePath(absolutePath.slice(root.length + 1));
6010
6098
  if (relativePath.length === 0) {
6011
6099
  continue;
@@ -6071,14 +6159,14 @@ var ENRICH_DESC_FIELD_PATTERNS = {
6071
6159
  async function enrichDescriptions(projectRoot, opts = {}) {
6072
6160
  const auto = opts.auto === true;
6073
6161
  const dryRun = opts.dryRun === true;
6074
- const mode = auto ? "auto" : "interactive";
6162
+ const mode = auto ? dryRun ? "preview" : "auto" : "readonly";
6075
6163
  const candidates = [];
6076
6164
  let scanned = 0;
6077
6165
  let modified = 0;
6078
6166
  let skipped = 0;
6079
6167
  for (const visit of iterateCanonicalFilenames(projectRoot)) {
6080
- const layerRoot = visit.layer === "team" ? join6(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
6081
- const absPath = join6(layerRoot, visit.type, visit.filename);
6168
+ const layerRoot = visit.layer === "team" ? join7(projectRoot, ".fabric", "knowledge") : resolvePersonalKnowledgeRoot();
6169
+ const absPath = join7(layerRoot, visit.type, visit.filename);
6082
6170
  scanned += 1;
6083
6171
  let source;
6084
6172
  try {
@@ -6281,7 +6369,7 @@ async function emitAutoHealEventBestEffort(projectRoot, payload) {
6281
6369
 
6282
6370
  // src/services/get-knowledge.ts
6283
6371
  import { readFile as readFile6 } from "fs/promises";
6284
- import { join as join7 } from "path";
6372
+ import { join as join8 } from "path";
6285
6373
  import { minimatch as minimatch2 } from "minimatch";
6286
6374
  var PRIORITY_ORDER = {
6287
6375
  high: 0,
@@ -6325,7 +6413,7 @@ async function loadGetKnowledgeContext(projectRoot) {
6325
6413
  return cached;
6326
6414
  }
6327
6415
  const meta = await readAgentsMeta(projectRoot);
6328
- const l0Content = await readFile6(join7(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
6416
+ const l0Content = await readFile6(join8(projectRoot, ".fabric", "bootstrap", "README.md"), "utf8");
6329
6417
  const context = {
6330
6418
  meta,
6331
6419
  l0Content,
@@ -6464,7 +6552,7 @@ async function readRuleContent(projectRoot, file, fileContentCache) {
6464
6552
  if (cached !== void 0) {
6465
6553
  return await cached;
6466
6554
  }
6467
- const pending = readFile6(join7(projectRoot, file), "utf8");
6555
+ const pending = readFile6(join8(projectRoot, file), "utf8");
6468
6556
  fileContentCache.set(file, pending);
6469
6557
  return await pending;
6470
6558
  }
@@ -6499,6 +6587,8 @@ export {
6499
6587
  invalidateKnowledgeSyncCooldown,
6500
6588
  ensureKnowledgeFresh,
6501
6589
  reconcileKnowledge,
6590
+ readPayloadLimits,
6591
+ readSelectionTokenTtlMs,
6502
6592
  loadActiveMeta,
6503
6593
  loadActiveMetaOrStale,
6504
6594
  getKnowledge,