@fenglimg/fabric-server 1.8.0-rc.3 → 2.0.0-rc.8

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.
@@ -1,21 +1,20 @@
1
1
  import {
2
- AGENTS_MD_RESOURCE_URI,
3
2
  EVENT_LEDGER_PATH,
4
3
  LEDGER_PATH,
5
4
  LEGACY_LEDGER_PATH,
6
5
  appendEventLedgerEvent,
7
6
  contextCache,
8
7
  getEventLedgerPath,
8
+ getKnowledge,
9
9
  getLedgerPath,
10
10
  getLegacyLedgerPath,
11
- getRules,
12
- invalidateRuleSyncCooldown,
11
+ invalidateKnowledgeSyncCooldown,
13
12
  isNodeError,
14
13
  readAgentsMeta,
15
14
  readEventLedger,
16
15
  runDoctorReport,
17
16
  sha256
18
- } from "./chunk-ZZGARZL5.js";
17
+ } from "./chunk-AR2HV5JT.js";
19
18
 
20
19
  // src/http.ts
21
20
  import { randomUUID as randomUUID2 } from "crypto";
@@ -87,7 +86,6 @@ import { join } from "path";
87
86
  import {
88
87
  agentsMetaSchema,
89
88
  fabricEventSchema,
90
- forensicReportSchema,
91
89
  ledgerEntrySchema as ledgerEntrySchema2
92
90
  } from "@fenglimg/fabric-shared";
93
91
  import { eventLedgerEventSchema } from "@fenglimg/fabric-shared";
@@ -132,34 +130,6 @@ async function readLegacyLedger(projectRoot) {
132
130
  }
133
131
  return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0).map((line, index) => parseLedgerLine(line, index)).filter((entry) => entry !== null);
134
132
  }
135
- async function appendLedgerEntry(projectRoot, entry) {
136
- const nextEntry = createStoredLedgerEntry(entry);
137
- for (const affectedPath of nextEntry.affected_paths) {
138
- await appendEventLedgerEvent(projectRoot, {
139
- event_type: "edit_intent_checked",
140
- ts: nextEntry.ts,
141
- path: affectedPath,
142
- compliant: true,
143
- intent: nextEntry.intent,
144
- ledger_entry_id: nextEntry.id,
145
- ledger_source: nextEntry.source,
146
- commit_sha: nextEntry.source === "ai" ? nextEntry.commit_sha : void 0,
147
- parent_sha: nextEntry.source === "human" ? nextEntry.parent_sha : void 0,
148
- parent_ledger_entry_id: nextEntry.source === "human" ? nextEntry.parent_ledger_entry_id : void 0,
149
- diff_stat: nextEntry.source === "human" ? nextEntry.diff_stat : void 0,
150
- annotation: nextEntry.source === "human" ? nextEntry.annotation : void 0,
151
- matched_rule_context_ts: null,
152
- window_ms: 0
153
- });
154
- }
155
- return nextEntry;
156
- }
157
- function createStoredLedgerEntry(entry) {
158
- return ledgerEntrySchema.parse({
159
- ...entry,
160
- id: entry.id ?? `ledger:${randomUUID()}`
161
- });
162
- }
163
133
  function parseLedgerLine(line, index) {
164
134
  try {
165
135
  const parsed = JSON.parse(line);
@@ -254,10 +224,8 @@ async function pathExists(path) {
254
224
 
255
225
  // src/api/events.ts
256
226
  var AGENTS_META_PATH = ".fabric/agents.meta.json";
257
- var FORENSIC_PATH = ".fabric/forensic.json";
258
227
  var WATCHED_PATHS = [
259
228
  AGENTS_META_PATH,
260
- FORENSIC_PATH,
261
229
  EVENT_LEDGER_PATH,
262
230
  LEDGER_PATH,
263
231
  LEGACY_LEDGER_PATH
@@ -438,10 +406,6 @@ async function readEventsForFile(state, projectRoot, relativePath) {
438
406
  const event = await readMetaUpdatedEvent(projectRoot);
439
407
  return event === null ? [] : [event];
440
408
  }
441
- if (relativePath === FORENSIC_PATH) {
442
- const event = await readDriftDetectedEvent(projectRoot);
443
- return event === null ? [] : [event];
444
- }
445
409
  if (relativePath === EVENT_LEDGER_PATH) {
446
410
  return await readEventLedgerAppendedEvents(state, projectRoot);
447
411
  }
@@ -462,18 +426,6 @@ async function readMetaUpdatedEvent(projectRoot) {
462
426
  payload: parsed
463
427
  };
464
428
  }
465
- async function readDriftDetectedEvent(projectRoot) {
466
- const filePath = join(projectRoot, FORENSIC_PATH);
467
- const raw = await readUtf8File(filePath);
468
- if (raw === null) {
469
- return null;
470
- }
471
- const parsed = forensicReportSchema.parse(JSON.parse(raw));
472
- return {
473
- type: "drift:detected",
474
- payload: parsed
475
- };
476
- }
477
429
  async function readLedgerAppendedEvents(state, projectRoot) {
478
430
  const ledgerState = await resolveLedgerWatchState(projectRoot);
479
431
  const ledgerPath = ledgerState.path;
@@ -830,60 +782,6 @@ function registerHistoryApi(app, projectRoot) {
830
782
  });
831
783
  }
832
784
 
833
- // src/api/intent.ts
834
- import { annotateIntentRequestSchema } from "@fenglimg/fabric-shared";
835
-
836
- // src/services/annotate-intent.ts
837
- async function annotateIntent(projectRoot, input) {
838
- const entries = await readLedger(projectRoot);
839
- const parentEntry = entries.find((entry2) => entry2.id === input.ledger_entry_id);
840
- if (parentEntry === void 0) {
841
- throw new LedgerEntryNotFoundError(`Cannot find ledger entry: ${input.ledger_entry_id}`);
842
- }
843
- const lastEntry = entries[entries.length - 1];
844
- if (lastEntry?.source === "human" && lastEntry.parent_ledger_entry_id === input.ledger_entry_id && lastEntry.annotation === input.annotation) {
845
- return {
846
- created: false,
847
- entry: lastEntry
848
- };
849
- }
850
- const entry = await appendLedgerEntry(projectRoot, createAnnotationEntry(parentEntry, input));
851
- return {
852
- created: true,
853
- entry
854
- };
855
- }
856
- function createAnnotationEntry(parentEntry, input) {
857
- return {
858
- ts: Date.now(),
859
- source: "human",
860
- parent_sha: input.ledger_entry_id,
861
- parent_ledger_entry_id: input.ledger_entry_id,
862
- intent: input.annotation,
863
- annotation: input.annotation,
864
- affected_paths: parentEntry.affected_paths,
865
- diff_stat: "annotation"
866
- };
867
- }
868
-
869
- // src/api/intent.ts
870
- function registerIntentApi(app, projectRoot) {
871
- app.post("/api/intent/annotate", async (req, res) => {
872
- const validation = annotateIntentRequestSchema.safeParse(req.body);
873
- if (!validation.success) {
874
- sendValidationError(res, "Invalid intent annotation payload", validation.error.flatten());
875
- return;
876
- }
877
- try {
878
- await readAgentsMeta(projectRoot);
879
- const result = await annotateIntent(projectRoot, validation.data);
880
- res.status(result.created ? 201 : 200).json(result);
881
- } catch (error) {
882
- sendUnknownError(res, error);
883
- }
884
- });
885
- }
886
-
887
785
  // src/api/ledger.ts
888
786
  import { ledgerQuerySchema } from "@fenglimg/fabric-shared";
889
787
  function registerLedgerApi(app, projectRoot) {
@@ -905,8 +803,8 @@ function registerLedgerApi(app, projectRoot) {
905
803
  });
906
804
  }
907
805
 
908
- // src/api/rules.ts
909
- function registerRulesApi(app, projectRoot) {
806
+ // src/api/knowledge.ts
807
+ function registerKnowledgeApi(app, projectRoot) {
910
808
  app.get("/api/rules", async (_req, res) => {
911
809
  try {
912
810
  res.json(await readAgentsMeta(projectRoot));
@@ -916,8 +814,8 @@ function registerRulesApi(app, projectRoot) {
916
814
  });
917
815
  }
918
816
 
919
- // src/api/rules-context.ts
920
- function registerRulesContextApi(app, projectRoot) {
817
+ // src/api/knowledge-context.ts
818
+ function registerKnowledgeContextApi(app, projectRoot) {
921
819
  app.get("/api/rules/context", async (req, res) => {
922
820
  const path = typeof req.query.path === "string" ? req.query.path.trim() : "";
923
821
  if (path.length === 0) {
@@ -929,7 +827,7 @@ function registerRulesContextApi(app, projectRoot) {
929
827
  return;
930
828
  }
931
829
  try {
932
- const result = await getRules(projectRoot, { path });
830
+ const result = await getKnowledge(projectRoot, { path });
933
831
  res.json(result.rules);
934
832
  } catch (error) {
935
833
  sendUnknownError(res, error);
@@ -967,7 +865,7 @@ async function createScanReport(targetInput = process.cwd()) {
967
865
  const framework = detectFramework(target);
968
866
  const readmeQuality = getReadmeQuality(target);
969
867
  const hasContributing = existsSync(join2(target, "CONTRIBUTING.md"));
970
- const hasExistingFabric = existsSync(join2(target, ".fabric", "bootstrap", "README.md")) || existsSync(join2(target, ".fabric"));
868
+ const hasExistingFabric = existsSync(join2(target, ".fabric"));
971
869
  const walkResult = walkFiles(target, DEFAULT_IGNORES);
972
870
  return {
973
871
  target,
@@ -1044,13 +942,13 @@ function toPosixPath(path) {
1044
942
  function buildRecommendations(input) {
1045
943
  const recommendations = [];
1046
944
  if (!input.hasExistingFabric) {
1047
- recommendations.push("L0: Run fab init to scaffold .fabric/bootstrap/README.md with TODO markers.");
945
+ recommendations.push("L0: Run `fab init` to scaffold the .fabric/ knowledge layout (decisions, pitfalls, guidelines, models, processes).");
1048
946
  }
1049
947
  if (input.readmeQuality === "stub") {
1050
- recommendations.push("L0: Expand README.md before promoting project facts into Fabric references.");
948
+ recommendations.push("L0: Expand README.md before promoting project facts into Fabric knowledge entries.");
1051
949
  }
1052
950
  if (!input.hasContributing) {
1053
- recommendations.push("L0: Add CONTRIBUTING.md or leave a bootstrap TODO reference for contribution flow.");
951
+ recommendations.push("L0: Add CONTRIBUTING.md or capture contribution-flow guidance under .fabric/knowledge/processes/.");
1054
952
  }
1055
953
  if (input.framework.kind === "unknown") {
1056
954
  recommendations.push("L1: Add tech-stack TODOs manually because no framework marker was detected.");
@@ -1060,42 +958,6 @@ function buildRecommendations(input) {
1060
958
  return recommendations;
1061
959
  }
1062
960
 
1063
- // src/api/static.ts
1064
- import { existsSync as existsSync2 } from "fs";
1065
- import { dirname, resolve as resolve2 } from "path";
1066
- import { fileURLToPath } from "url";
1067
- import express from "express";
1068
- var DEFAULT_STATIC_DIR = resolve2(dirname(fileURLToPath(import.meta.url)), "static");
1069
- function registerDashboardStatic(app, options = {}) {
1070
- if (options.dev ?? process.env.NODE_ENV === "development") {
1071
- return;
1072
- }
1073
- const staticDir = resolve2(options.dashboardDistPath ?? DEFAULT_STATIC_DIR);
1074
- const indexPath = resolve2(staticDir, "index.html");
1075
- if (!existsSync2(indexPath)) {
1076
- warnMissingDashboard(staticDir);
1077
- app.get("/", (_req, res) => {
1078
- res.status(404).json({
1079
- error: {
1080
- code: "DASHBOARD_DIST_MISSING",
1081
- message: `Fabric dashboard dist was not found at ${staticDir}. Run pnpm --filter @fenglimg/fabric-dashboard build.`
1082
- }
1083
- });
1084
- });
1085
- return;
1086
- }
1087
- app.use("/", express.static(staticDir, { index: "index.html", fallthrough: true }));
1088
- app.get(/^\/(?!api(?:\/|$)|mcp(?:\/|$)|events(?:\/|$)).*/, (_req, res) => {
1089
- res.sendFile(indexPath);
1090
- });
1091
- }
1092
- function warnMissingDashboard(staticDir) {
1093
- process.stderr.write(
1094
- `[fabric-server] dashboard dist missing at ${staticDir}; '/' will return 404 until dashboard assets are built.
1095
- `
1096
- );
1097
- }
1098
-
1099
961
  // src/middleware/bearer-auth.ts
1100
962
  import { createHash, timingSafeEqual } from "crypto";
1101
963
  function createBearerAuthMiddleware(token) {
@@ -1221,23 +1083,13 @@ function handleCacheWatcherEvent(relativePath, projectRoot, sessions, timers) {
1221
1083
  );
1222
1084
  return;
1223
1085
  }
1224
- if (normalized === ".fabric/bootstrap/README.md") {
1225
- contextCache.invalidate("file_watch", projectRoot);
1226
- clearTimeout(timers.getAgentsMdTimer());
1227
- timers.setAgentsMdTimer(
1228
- setTimeout(() => {
1229
- notifyAllSessions(sessions, "resource_updated", AGENTS_MD_RESOURCE_URI);
1230
- }, NOTIFY_DEBOUNCE_MS)
1231
- );
1232
- return;
1233
- }
1234
- if (normalized.startsWith(".fabric/rules/") && normalized.endsWith(".md")) {
1086
+ if (normalized.startsWith(".fabric/knowledge/") && normalized.endsWith(".md")) {
1235
1087
  contextCache.invalidate("file_watch", projectRoot);
1236
- invalidateRuleSyncCooldown(projectRoot);
1088
+ invalidateKnowledgeSyncCooldown(projectRoot);
1237
1089
  }
1238
1090
  }
1239
1091
  function createFabricHttpApp(options) {
1240
- const { projectRoot, host = DEFAULT_HOST, authToken, dashboardDistPath, dev } = options;
1092
+ const { projectRoot, host = DEFAULT_HOST, authToken } = options;
1241
1093
  const app = createMcpExpressApp({ host });
1242
1094
  const eventStore = new JsonlEventStore(projectRoot);
1243
1095
  const sessions = /* @__PURE__ */ new Map();
@@ -1245,8 +1097,8 @@ function createFabricHttpApp(options) {
1245
1097
  const cacheWatcher = chokidar2.watch(
1246
1098
  [
1247
1099
  ".fabric/agents.meta.json",
1248
- ".fabric/bootstrap/README.md",
1249
- ".fabric/rules/**/*.md"
1100
+ ".fabric/knowledge/**/*.md",
1101
+ ".fabric/knowledge/pending/**/*.md"
1250
1102
  ],
1251
1103
  {
1252
1104
  cwd: projectRoot,
@@ -1291,13 +1143,12 @@ function createFabricHttpApp(options) {
1291
1143
  app.use("/events", bearerAuth);
1292
1144
  app.use("/mcp", bearerAuth);
1293
1145
  }
1294
- registerRulesApi(app, projectRoot);
1295
- registerRulesContextApi(app, projectRoot);
1146
+ registerKnowledgeApi(app, projectRoot);
1147
+ registerKnowledgeContextApi(app, projectRoot);
1296
1148
  registerLedgerApi(app, projectRoot);
1297
1149
  registerHistoryApi(app, projectRoot);
1298
1150
  registerScanApi(app, projectRoot);
1299
1151
  registerDoctorApi(app, projectRoot);
1300
- registerIntentApi(app, projectRoot);
1301
1152
  app.get("/events", createEventsHandler({ projectRoot }));
1302
1153
  app.all("/mcp", async (req, res) => {
1303
1154
  const sessionId = readHeader(req.headers["mcp-session-id"]);
@@ -1317,7 +1168,6 @@ function createFabricHttpApp(options) {
1317
1168
  const session = await createSession(eventStore, sessions);
1318
1169
  await session.transport.handleRequest(req, res, req.body);
1319
1170
  });
1320
- registerDashboardStatic(app, { dashboardDistPath, dev });
1321
1171
  return app;
1322
1172
  }
1323
1173
  function notifyAllSessions(sessions, kind, uri) {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Server } from 'node:http';
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { AgentsMeta, RuleTestIndex, AgentsLayer, AgentsTopologyType } from '@fenglimg/fabric-shared';
3
+ import { AgentsMeta, KnowledgeTestIndex, AgentsLayer, AgentsTopologyType, Layer, KnowledgeType, StableId, AgentsMetaCounters, EventLedgerEventInput, EventLedgerEvent, RuleDescriptionIndexItem } from '@fenglimg/fabric-shared';
4
+ import { FabExtractKnowledgeInput, FabExtractKnowledgeOutput, FabReviewInput, FabReviewOutput } from '@fenglimg/fabric-shared/schemas/api-contracts';
4
5
  import { IOFabricError } from '@fenglimg/fabric-shared/errors';
5
6
 
6
7
  interface InFlightTracker {
@@ -76,33 +77,116 @@ type DoctorFixReport = {
76
77
  message: string;
77
78
  report: DoctorReport;
78
79
  };
80
+ type DoctorApplyLintMutationKind = "knowledge_orphan_demote_required" | "knowledge_stale_archive_required" | "knowledge_index_drift" | "knowledge_pending_auto_archive" | "knowledge_session_hints_stale_cleanup";
81
+ type DoctorApplyLintMutation = {
82
+ kind: DoctorApplyLintMutationKind;
83
+ path: string;
84
+ detail: string;
85
+ applied: boolean;
86
+ error?: string;
87
+ };
88
+ type DoctorApplyLintReport = {
89
+ changed: boolean;
90
+ mutations: DoctorApplyLintMutation[];
91
+ manual_errors: DoctorIssue[];
92
+ aborted: boolean;
93
+ abort_reason?: string;
94
+ message: string;
95
+ report: DoctorReport;
96
+ };
79
97
  declare function runDoctorReport(target: string): Promise<DoctorReport>;
80
98
  declare function runDoctorFix(target: string): Promise<DoctorFixReport>;
99
+ declare function runDoctorApplyLint(target: string): Promise<DoctorApplyLintReport>;
81
100
 
82
- type RuleMetaBuildSource = "doctor_fix" | "sync_meta";
83
- type RuleMetaBuildResult = {
101
+ type KnowledgeMetaBuildSource = "doctor_fix" | "sync_meta";
102
+ type KnowledgeMetaBuildResult = {
84
103
  meta: AgentsMeta;
85
- ruleTestIndex: RuleTestIndex;
104
+ knowledgeTestIndex: KnowledgeTestIndex;
86
105
  changed: boolean;
87
106
  };
88
- type WriteRuleMetaOptions = {
89
- source: RuleMetaBuildSource;
107
+ type WriteKnowledgeMetaOptions = {
108
+ source: KnowledgeMetaBuildSource;
90
109
  };
91
- declare function buildRuleMeta(projectRootInput: string): Promise<RuleMetaBuildResult>;
92
- declare function writeRuleMeta(projectRootInput: string, options: WriteRuleMetaOptions): Promise<RuleMetaBuildResult>;
93
- declare function computeRulesBasedAgentsMeta(projectRootInput: string, existingMeta?: AgentsMeta): Promise<AgentsMeta>;
94
- declare function computeRuleTestIndex(projectRootInput: string, computedMeta: AgentsMeta, previousIndex?: RuleTestIndex): Promise<RuleTestIndex>;
95
- declare function deriveRuleMetaLayer(relativePath: string): AgentsLayer;
96
- declare function deriveRuleMetaTopologyType(relativePath: string): AgentsTopologyType;
97
- declare function isSameRuleTestIndex(left: RuleTestIndex, right: RuleTestIndex): boolean;
110
+ declare function buildKnowledgeMeta(projectRootInput: string): Promise<KnowledgeMetaBuildResult>;
111
+ declare function writeKnowledgeMeta(projectRootInput: string, options: WriteKnowledgeMetaOptions): Promise<KnowledgeMetaBuildResult>;
112
+ declare function computeKnowledgeBasedAgentsMeta(projectRootInput: string, existingMeta?: AgentsMeta): Promise<AgentsMeta>;
113
+ declare function computeKnowledgeTestIndex(projectRootInput: string, computedMeta: AgentsMeta, previousIndex?: KnowledgeTestIndex): Promise<KnowledgeTestIndex>;
114
+ declare function deriveKnowledgeMetaLayer(relativePath: string): AgentsLayer;
115
+ declare function deriveKnowledgeMetaTopologyType(relativePath: string): AgentsTopologyType;
116
+ declare function isSameKnowledgeTestIndex(left: KnowledgeTestIndex, right: KnowledgeTestIndex): boolean;
98
117
  declare function stableStringify(value: unknown): string;
99
118
 
100
119
  /**
101
- * Shared constants used across the server package.
120
+ * v2.0 KnowledgeIdAllocator
121
+ *
122
+ * Wraps the pure `allocateKnowledgeId` allocator with persistence: it reads
123
+ * `agents.meta.json`, advances the counter for the requested (layer, type)
124
+ * pair, and writes the updated meta atomically (write-to-tmp + rename) so
125
+ * concurrent readers always see a consistent file.
126
+ *
127
+ * Counters are MONOTONIC across the lifetime of the meta file: deleting a
128
+ * knowledge entry does NOT free its counter slot, so previously-allocated
129
+ * stable_ids remain unique even after their files are removed.
130
+ *
131
+ * Key invariants:
132
+ * - Counters envelope is initialized to zeros if absent (v1.x meta compat).
133
+ * - Each allocate() call performs read → mutate → atomic-write in sequence.
134
+ * - The returned id is guaranteed to differ from every previously-returned
135
+ * id for the same meta path.
102
136
  */
103
- /** MCP resource URI for the project's bootstrap README (L0 rules) file. */
104
- declare const AGENTS_MD_RESOURCE_URI = "fabric://bootstrap-readme";
137
+ declare class KnowledgeIdAllocator {
138
+ private readonly metaPath;
139
+ constructor(metaPath: string);
140
+ /**
141
+ * Allocate the next stable_id for the given (layer, type) pair and persist
142
+ * the advanced counter to `agents.meta.json`.
143
+ */
144
+ allocate(layer: Layer, type: KnowledgeType): Promise<StableId>;
145
+ /**
146
+ * Returns the current counters envelope, defaulting to all-zero slots when
147
+ * the meta file is absent or pre-v2.0 (counters key missing).
148
+ */
149
+ getCounters(): Promise<AgentsMetaCounters>;
150
+ private readMeta;
151
+ private normalizeCounters;
152
+ private writeMetaAtomic;
153
+ }
154
+
155
+ /**
156
+ * Append-evidence-on-collision service for fab_extract_knowledge.
157
+ *
158
+ * Idempotency_key = sha256({source_session, type, slug}). When the same
159
+ * triple hits an existing pending file (verified by frontmatter
160
+ * `x-fabric-idempotency-key`), the body is preserved and a fresh
161
+ * `## Evidence (call N)` section is appended — LLM-regenerated summaries
162
+ * stay observable without overwriting prior context.
163
+ *
164
+ * NO `id` frontmatter is written — Q2 late-bind delegates id allocation
165
+ * to rc.3 fab_review approve. See planning-context.md "NO id frontmatter".
166
+ */
167
+ declare function extractKnowledge(projectRoot: string, input: FabExtractKnowledgeInput): Promise<FabExtractKnowledgeOutput>;
105
168
 
169
+ /**
170
+ * v2.0 rc.3 fab_review service.
171
+ *
172
+ * Pure async dispatcher over a discriminated union of 6 actions (list, approve,
173
+ * reject, modify, search, defer). All branches are implemented as of TASK-002.
174
+ *
175
+ * Approve performs late-bind id allocation (KP-/KT- + type-code + monotonic
176
+ * counter via KnowledgeIdAllocator), emits 2-phase events (knowledge_promote_started
177
+ * → knowledge_promoted | knowledge_promote_failed), and uses git mv to preserve
178
+ * file history when the target lives in the team layer (same repo). Personal
179
+ * layer files use plain fs.rename because they live under ~/.fabric/ outside
180
+ * the project's git tree.
181
+ *
182
+ * Modify implements the only legal stable_id mutation: layer-flip across
183
+ * KP/KT counter spaces. Allocates a new id under the target layer, renames
184
+ * the file across layer roots, emits knowledge_layer_changed.
185
+ */
186
+ declare function reviewKnowledge(projectRoot: string, input: FabReviewInput): Promise<FabReviewOutput>;
187
+
188
+ type StoredEventLedgerEvent = EventLedgerEvent;
189
+ declare function appendEventLedgerEvent(projectRoot: string, event: EventLedgerEventInput): Promise<StoredEventLedgerEvent>;
106
190
  /**
107
191
  * Synchronously fsync the event ledger file to ensure OS page-cache buffers are
108
192
  * flushed to durable storage. Must be called AFTER in-flight drain but BEFORE
@@ -113,22 +197,79 @@ declare const AGENTS_MD_RESOURCE_URI = "fabric://bootstrap-readme";
113
197
  */
114
198
  declare function flushAndSyncEventLedger(projectRoot: string): void;
115
199
 
200
+ type PlanContextInput = {
201
+ paths: string[];
202
+ intent?: string;
203
+ known_tech?: string[];
204
+ detected_entities?: Record<string, string[]>;
205
+ client_hash?: string;
206
+ correlation_id?: string;
207
+ session_id?: string;
208
+ target_paths?: string[];
209
+ };
210
+ type RequirementProfile = {
211
+ target_path: string;
212
+ path_segments: string[];
213
+ extension: string;
214
+ known_tech: string[];
215
+ user_intent: string;
216
+ detected_entities: string[];
217
+ };
218
+ type PlanContextEntry = {
219
+ path: string;
220
+ requirement_profile: RequirementProfile;
221
+ description_index: RuleDescriptionIndexItem[];
222
+ };
223
+ type PlanContextResult = {
224
+ revision_hash: string;
225
+ stale: boolean;
226
+ selection_token: string;
227
+ entries: PlanContextEntry[];
228
+ shared: {
229
+ description_index: RuleDescriptionIndexItem[];
230
+ preflight_diagnostics: Array<{
231
+ code: "missing_description";
232
+ severity: "warn";
233
+ message: string;
234
+ stable_ids?: string[];
235
+ path?: string;
236
+ }>;
237
+ };
238
+ };
239
+ type SelectionTokenState = {
240
+ token: string;
241
+ revision_hash: string;
242
+ target_paths: string[];
243
+ required_stable_ids: string[];
244
+ ai_selectable_stable_ids: string[];
245
+ created_at: number;
246
+ expires_at: number;
247
+ };
248
+ declare function planContext(projectRoot: string, input: PlanContextInput): Promise<PlanContextResult>;
249
+ declare function readSelectionToken(token: string, now?: number): SelectionTokenState | undefined;
250
+
251
+ /**
252
+ * Shared constants used across the server package.
253
+ */
254
+ /** MCP resource URI for the project's bootstrap README (L0 rules) file. */
255
+ declare const AGENTS_MD_RESOURCE_URI = "fabric://bootstrap-readme";
256
+
116
257
  /**
117
- * rule-sync.ts — Rule-sync orchestrator framework (R28, TASK-011)
258
+ * knowledge-sync.ts — Rule-sync orchestrator framework (R28, TASK-011)
118
259
  *
119
- * Public surface: ensureRulesFresh, reconcileRules + exported types.
260
+ * Public surface: ensureKnowledgeFresh, reconcileKnowledge + exported types.
120
261
  * Internal helpers are co-located in this file.
121
262
  * Does NOT wire any consumers (MCP tools, doctor, watchers).
122
263
  *
123
264
  * Distinction between the two public entry points:
124
265
  *
125
- * - `ensureRulesFresh`: detects drift, emits ledger events, invalidates cache.
266
+ * - `ensureKnowledgeFresh`: detects drift, emits ledger events, invalidates cache.
126
267
  * Does NOT rewrite agents.meta.json. Optimised for hot-path consumers (MCP tools).
127
268
  *
128
- * - `reconcileRules`: full scan + rewrites agents.meta.json (via rule-meta-builder)
269
+ * - `reconcileKnowledge`: full scan + rewrites agents.meta.json (via knowledge-meta-builder)
129
270
  * + emits ledger events. Used by startup (TASK-022) and doctor repair (TASK-023).
130
271
  */
131
- interface RuleSyncOptions {
272
+ interface KnowledgeSyncOptions {
132
273
  mode?: "incremental" | "full";
133
274
  /** When true, invalid frontmatter throws RuleValidationError (default: false — collect as warning). */
134
275
  throwOnInvalidFrontmatter?: boolean;
@@ -140,23 +281,23 @@ interface StructuredWarning {
140
281
  action_hint: string;
141
282
  }
142
283
  /**
143
- * Granular ledger event shape for rule-sync operations.
144
- * These are returned in RuleSyncReport and also appended to the event ledger
145
- * using the nearest available ledger event type (rule_drift_detected /
146
- * baseline_synced). The shape below is what callers receive in `.events`.
284
+ * Granular ledger event shape for knowledge-sync operations.
285
+ * These are returned in KnowledgeSyncReport and also appended to the event ledger
286
+ * using the nearest available ledger event type (knowledge_drift_detected).
287
+ * The shape below is what callers receive in `.events`.
147
288
  */
148
- interface RuleSyncLedgerEvent {
289
+ interface KnowledgeSyncLedgerEvent {
149
290
  type: "rule_content_changed" | "rule_added" | "rule_removed";
150
291
  stable_id: string;
151
292
  path: string;
152
293
  prev_hash: string | null;
153
294
  new_hash: string | null;
154
295
  changed_fields: string[];
155
- source: "ensureRulesFresh" | "reconcileRules";
296
+ source: "ensureKnowledgeFresh" | "reconcileKnowledge";
156
297
  }
157
298
  /** Alias so the public API says LedgerEvent (as documented). */
158
- type LedgerEvent = RuleSyncLedgerEvent;
159
- interface RuleSyncReport {
299
+ type LedgerEvent = KnowledgeSyncLedgerEvent;
300
+ interface KnowledgeSyncReport {
160
301
  status: "fresh" | "reconciled" | "errors";
161
302
  events: LedgerEvent[];
162
303
  warnings: StructuredWarning[];
@@ -167,8 +308,8 @@ interface RuleSyncReport {
167
308
  * invalidates the cache. Does NOT rewrite agents.meta.json. Optimised for
168
309
  * hot-path consumers (MCP tools).
169
310
  */
170
- declare function ensureRulesFresh(projectRoot: string, opts?: RuleSyncOptions): Promise<RuleSyncReport>;
171
- interface ReconcileRulesOptions {
311
+ declare function ensureKnowledgeFresh(projectRoot: string, opts?: KnowledgeSyncOptions): Promise<KnowledgeSyncReport>;
312
+ interface ReconcileKnowledgeOptions {
172
313
  /** Identifies who triggered the reconcile; controls which summary ledger event is written. */
173
314
  trigger?: "startup" | "doctor" | "manual";
174
315
  }
@@ -181,7 +322,7 @@ interface ReconcileRulesOptions {
181
322
  * ledger event is appended after per-file drift events. Other trigger values
182
323
  * append a `meta_reconciled` event. Omitting the trigger skips the summary.
183
324
  */
184
- declare function reconcileRules(projectRoot: string, opts?: ReconcileRulesOptions): Promise<RuleSyncReport>;
325
+ declare function reconcileKnowledge(projectRoot: string, opts?: ReconcileKnowledgeOptions): Promise<KnowledgeSyncReport>;
185
326
 
186
327
  declare class ServeLockHeldError extends IOFabricError {
187
328
  readonly code = "SERVE_LOCK_HELD";
@@ -239,8 +380,6 @@ declare function startHttpServer(options: {
239
380
  projectRoot: string;
240
381
  host?: string;
241
382
  authToken?: string;
242
- dashboardDistPath?: string;
243
- dev?: boolean;
244
383
  }): Promise<Server>;
245
384
 
246
- export { AGENTS_MD_RESOURCE_URI, type AcquireOptions, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type InFlightTracker, LEDGER_PATH, LEGACY_LEDGER_PATH, type LedgerEvent, type LockState, type ReconcileRulesOptions, type RuleMetaBuildResult, type RuleMetaBuildSource, type RuleSyncLedgerEvent, type RuleSyncOptions, type RuleSyncReport, ServeLockHeldError, type ShutdownHandlerDeps, type StructuredWarning, type WriteRuleMetaOptions, acquireLock, buildRuleMeta, checkLockOrThrow, computeRuleTestIndex, computeRulesBasedAgentsMeta, createFabricServer, createInFlightTracker, createShutdownHandler, deriveRuleMetaLayer, deriveRuleMetaTopologyType, ensureRulesFresh, flushAndSyncEventLedger, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, isSameRuleTestIndex, readLockState, reconcileRules, releaseLock, runDoctorFix, runDoctorReport, stableStringify, startHttpServer, startStdioServer, writeRuleMeta };
385
+ export { AGENTS_MD_RESOURCE_URI, type AcquireOptions, type DoctorApplyLintMutation, type DoctorApplyLintMutationKind, type DoctorApplyLintReport, type DoctorFixReport, type DoctorIssue, type DoctorReport, EVENT_LEDGER_PATH, type InFlightTracker, KnowledgeIdAllocator, type KnowledgeMetaBuildResult, type KnowledgeMetaBuildSource, type KnowledgeSyncLedgerEvent, type KnowledgeSyncOptions, type KnowledgeSyncReport, LEDGER_PATH, LEGACY_LEDGER_PATH, type LedgerEvent, type LockState, type PlanContextInput, type PlanContextResult, type ReconcileKnowledgeOptions, type RequirementProfile, type SelectionTokenState, ServeLockHeldError, type ShutdownHandlerDeps, type StructuredWarning, type WriteKnowledgeMetaOptions, acquireLock, appendEventLedgerEvent, buildKnowledgeMeta, checkLockOrThrow, computeKnowledgeBasedAgentsMeta, computeKnowledgeTestIndex, createFabricServer, createInFlightTracker, createShutdownHandler, deriveKnowledgeMetaLayer, deriveKnowledgeMetaTopologyType, ensureKnowledgeFresh, extractKnowledge, flushAndSyncEventLedger, formatPreexistingRootMessage, getEventLedgerPath, getLedgerPath, getLegacyLedgerPath, isSameKnowledgeTestIndex, planContext, readLockState, readSelectionToken, reconcileKnowledge, releaseLock, reviewKnowledge, runDoctorApplyLint, runDoctorFix, runDoctorReport, stableStringify, startHttpServer, startStdioServer, writeKnowledgeMeta };