@fenglimg/fabric-shared 2.0.0-rc.35 → 2.0.0-rc.37

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wangzhichao (fenglimg)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # @fenglimg/fabric-shared
2
+
3
+ Shared types, Zod schemas, i18n bundle, atomic-write helpers, MCP payload guards, and error classes consumed by [`@fenglimg/fabric-server`](https://www.npmjs.com/package/@fenglimg/fabric-server) and [`@fenglimg/fabric-cli`](https://www.npmjs.com/package/@fenglimg/fabric-cli).
4
+
5
+ End users should not depend on this package directly — it ships as a workspace dependency of the server + CLI. Pinned versions are not part of Fabric's public API.
6
+
7
+ ## Repo
8
+
9
+ Source + issues + roadmap: <https://github.com/fenglimg/fabric-v2>
10
+
11
+ ## License
12
+
13
+ MIT — see [LICENSE](./LICENSE).
@@ -134,7 +134,15 @@ var planContextOutputSchema = z2.object({
134
134
  // "knowledge meta auto-healed (was <prev>, now <curr>)" notice without
135
135
  // having to query the event ledger.
136
136
  auto_healed: z2.boolean().optional(),
137
- previous_revision_hash: z2.string().optional()
137
+ previous_revision_hash: z2.string().optional(),
138
+ // v2.0.0-rc.37 NEW-24: stale-id redirect map. Populated when one or more
139
+ // recent fab_review modify-layer flips reassigned a canonical stable_id
140
+ // and the NEW id is in this response's description_index. Callers that
141
+ // cached the OLD id from a prior session look it up here and substitute
142
+ // the new id before issuing fab_get_knowledge_sections / fab_recall. Empty
143
+ // (field omitted) when no actionable redirects exist for the surfaced
144
+ // candidate set. See packages/server/src/services/id-redirect.ts.
145
+ redirects: z2.record(z2.string()).optional()
138
146
  });
139
147
  var planContextAnnotations = {
140
148
  readOnlyHint: true,
@@ -208,11 +216,18 @@ var knowledgeSectionsOutputSchema = z2.object({
208
216
  message: z2.string()
209
217
  })
210
218
  ),
211
- // v2/rc.3 (Q6): present iff a layer-flip in fab_review/modify changed the
212
- // canonical stable_id since the caller's selection_token was minted.
213
- // Clients should retry against `redirect_to.stable_id`.
214
- redirect_to: z2.object({ stable_id: z2.string() }).optional().describe(
215
- "Post-layer-flip redirect. Populated when stable_id changed after token mint (rc.3 fab_review/modify)."
219
+ // v2/rc.3 (Q6) + v2.0.0-rc.37 NEW-24: present iff at least one stable_id in
220
+ // the caller-supplied ai_selected_stable_ids was rewritten by the layer-flip
221
+ // redirect resolver. Pre-rc.37: this was a single { stable_id } object set
222
+ // only on the rare token-mint-vs-flip race. rc.37+: also accepts a map of
223
+ // (old_id new_id) when multiple rewrites fire in one fetch. Both shapes
224
+ // are accepted for forward-compat; readers should branch on shape and
225
+ // refresh their cached ids accordingly.
226
+ redirect_to: z2.union([
227
+ z2.object({ stable_id: z2.string() }),
228
+ z2.record(z2.string())
229
+ ]).optional().describe(
230
+ "Post-layer-flip redirect. Pre-rc.37: { stable_id } shape from rc.3 fab_review/modify. rc.37+: also accepts a (old_id \u2192 new_id) map for fab_get_knowledge_sections / fab_recall transparent rewrite."
216
231
  ),
217
232
  warnings: z2.array(structuredWarningSchema).optional()
218
233
  });
@@ -223,6 +238,124 @@ var knowledgeSectionsAnnotations = {
223
238
  openWorldHint: false,
224
239
  title: "Filter rule sections"
225
240
  };
241
+ var recallInputSchema = z2.object({
242
+ paths: z2.array(z2.string()).min(1).describe(
243
+ "Candidate file paths to recall Fabric rules for. Same semantics as fab_plan_context.paths."
244
+ ),
245
+ intent: z2.string().optional().describe("User-stated requirement or implementation intent; used to build a neutral requirement profile."),
246
+ known_tech: z2.array(z2.string()).optional().describe("Known technologies involved."),
247
+ detected_entities: z2.record(z2.array(z2.string())).optional().describe("Optional path-keyed detected entities."),
248
+ client_hash: z2.string().optional().describe("Revision hash from a prior call; enables stale detection."),
249
+ correlation_id: z2.string().optional().describe("Optional caller-provided correlation id for Event Ledger records."),
250
+ session_id: z2.string().optional().describe(
251
+ "Current client session id (Claude Code: $session_id; Codex: corresponding identifier). Enables cross-session debt tracking. Falls back gracefully if omitted."
252
+ ),
253
+ layer_filter: z2.enum(["team", "personal", "both"]).optional().describe(
254
+ "Restrict recall to the named layer. Default: fabric-config.default_layer_filter."
255
+ ),
256
+ target_paths: z2.array(z2.string()).optional().describe(
257
+ "Path context for narrow-scope relevance filtering. Defaults to `paths`; empty = no filter."
258
+ ),
259
+ ids: z2.array(z2.string()).optional().describe(
260
+ "Optional explicit stable_ids to fetch. When omitted, fab_recall returns ALL entries plan-context surfaces (the common case after rc.37 selectable-filter removal). When provided, filters fetched bodies to this set."
261
+ )
262
+ });
263
+ var recallOutputSchema = z2.object({
264
+ revision_hash: z2.string(),
265
+ stale: z2.boolean(),
266
+ // Selection token surfaced for callers who want to continue the conversation
267
+ // with fab_get_knowledge_sections (e.g. fetch additional ids later) — every
268
+ // recall response is still token-backed internally.
269
+ selection_token: z2.string(),
270
+ entries: z2.array(
271
+ z2.object({
272
+ path: z2.string(),
273
+ requirement_profile: _requirementProfileSchema,
274
+ description_index: z2.array(_descriptionIndexItemSchema)
275
+ })
276
+ ),
277
+ shared: z2.object({
278
+ description_index: z2.array(_descriptionIndexItemSchema),
279
+ preflight_diagnostics: z2.array(
280
+ z2.object({
281
+ code: z2.literal("missing_description"),
282
+ severity: z2.literal("warn"),
283
+ message: z2.string(),
284
+ stable_ids: z2.array(z2.string()).optional(),
285
+ path: z2.string().optional()
286
+ })
287
+ )
288
+ }),
289
+ // Same shape as knowledgeSectionsOutputSchema.rules — full body keyed by stable_id.
290
+ rules: z2.array(
291
+ z2.object({
292
+ stable_id: z2.string(),
293
+ level: z2.enum(["L0", "L1", "L2"]),
294
+ path: z2.string(),
295
+ body: z2.string()
296
+ })
297
+ ),
298
+ selected_stable_ids: z2.array(z2.string()),
299
+ diagnostics: z2.array(
300
+ z2.object({
301
+ code: z2.literal("missing_knowledge_metadata"),
302
+ severity: z2.literal("warn"),
303
+ stable_id: z2.string(),
304
+ message: z2.string()
305
+ })
306
+ ),
307
+ warnings: z2.array(structuredWarningSchema).optional(),
308
+ auto_healed: z2.boolean().optional(),
309
+ previous_revision_hash: z2.string().optional(),
310
+ // v2.0.0-rc.37 NEW-24: parallel to planContextOutputSchema.redirects — see
311
+ // that field for semantics. fab_recall transparently rewrites any old ids
312
+ // passed via `ids` before fetching bodies; the surfaced map still exposes
313
+ // the substitution to callers that want to refresh their cached state.
314
+ redirects: z2.record(z2.string()).optional()
315
+ });
316
+ var recallAnnotations = {
317
+ readOnlyHint: true,
318
+ idempotentHint: true,
319
+ destructiveHint: false,
320
+ openWorldHint: false,
321
+ title: "Recall Fabric knowledge (one-call)"
322
+ };
323
+ var archiveScanInputSchema = z2.object({
324
+ range: z2.union([z2.array(z2.string()).min(1), z2.literal("all")]).optional().describe(
325
+ "Phase 0 scope: explicit session_id[] to constrain the scan, or the 'all' sentinel. Omitted = scan everything since the last knowledge_proposed anchor."
326
+ ),
327
+ now_ms: z2.number().int().nonnegative().optional().describe("Override for the anti-loop cooldown clock (testing). Defaults to Date.now()."),
328
+ correlation_id: z2.string().optional().describe("Optional caller-provided correlation id for Event Ledger records."),
329
+ session_id: z2.string().optional().describe("Current client session id; recorded for cross-session debt tracking.")
330
+ });
331
+ var archiveScanOutputSchema = z2.object({
332
+ // ts of the most recent knowledge_proposed event (the lower bound), or null
333
+ // when the workspace has never archived (scan everything).
334
+ anchor_ts: z2.number().nullable(),
335
+ // Distinct session_ids since the anchor that survived the outcome filter,
336
+ // in first-seen order — ready for the Skill to load digests + stitch.
337
+ session_ids: z2.array(z2.string()),
338
+ // Sessions dropped by the filter, with the rule that fired (audit/debug).
339
+ dropped: z2.array(
340
+ z2.object({
341
+ session_id: z2.string(),
342
+ reason: z2.enum(["user_dismissed", "cooldown", "no_new_signal"])
343
+ })
344
+ ),
345
+ // max ts examined across the scan — becomes the next covered_through_ts.
346
+ covered_through_ts: z2.number().nullable(),
347
+ // Idempotency keys already proposed by prior archive runs but not yet
348
+ // reviewed (Phase 4.5 cross-session pending dedupe). Drop matching candidates.
349
+ already_proposed_keys: z2.array(z2.string()),
350
+ warnings: z2.array(structuredWarningSchema).optional()
351
+ });
352
+ var archiveScanAnnotations = {
353
+ readOnlyHint: true,
354
+ idempotentHint: true,
355
+ destructiveHint: false,
356
+ openWorldHint: false,
357
+ title: "Scan event ledger for archive candidates (deterministic)"
358
+ };
226
359
  var ProposedReasonSchema = z2.enum([
227
360
  "explicit-user-mark",
228
361
  "diagnostic-then-fix",
@@ -311,6 +444,17 @@ var _FabExtractKnowledgeInputBaseSchema = z2.object({
311
444
  must_read_if: z2.string().optional().describe(
312
445
  "One-line strong trigger; when this condition holds the entry is considered required reading. Single line \u2264160 chars (e.g. 'touching anything under packages/cli/src/commands/hooks.ts'). Optional \u2014 omit when no single strong trigger fits."
313
446
  ),
447
+ // v2.0.0-rc.37 NEW-37 (werewolf dogfood remediation): optional tags array.
448
+ // Werewolf实测发现 100% canonical entries 的 `tags: []` 为空,主题聚类与
449
+ // 跨条目检索退化。Skills (fabric-archive / fabric-import) 应每个 entry 产
450
+ // 2-4 个 kebab-case 主题词。Server 写入时直接落 frontmatter `tags: [...]`;
451
+ // empty array 仍然合法(skill 无法 confident 推断时显式空)。
452
+ // IDEMPOTENCY: tags MUST NOT 参与 idempotency_key hash(同 relevance_*
453
+ // / intent_clues 等可变字段一致),re-extract 时 tags 调整不应产生重复
454
+ // pending file。
455
+ tags: z2.array(z2.string()).optional().describe(
456
+ "Optional topic tags (2-4 kebab-case strings recommended). Drives cross-entry retrieval + topic clustering. Skill-inferred from session content; omit when not confidently inferable. Empty array allowed but discouraged (degrades narrow hint topic signal)."
457
+ ),
314
458
  // v2.0.0-rc.23 TASK-014 (F8c): optional onboard-slot tag. The S5 slot
315
459
  // mechanism reintroduces a Skill-orchestrated "project tone" capture
316
460
  // surface after F8a deleted the auto-`fabric scan` baseline pipeline.
@@ -331,6 +475,20 @@ var _FabExtractKnowledgeInputBaseSchema = z2.object({
331
475
  // duplicate entries.
332
476
  onboard_slot: onboardSlotSchema.optional().describe(
333
477
  "Optional slot tag from the S5 onboarding set (tech-stack-decision / architecture-pattern / code-style-tone / build-system-idiom / domain-vocabulary); lets fabric-archive's first-run phase claim a project-tone slot. Skill propose-time only; never required."
478
+ ),
479
+ // v2.0.0-rc.37 NEW-7: read-only evidence paths lifted from the legacy
480
+ // body `## Evidence` markdown block into structured frontmatter. These are
481
+ // paths the agent CONSULTED while building this knowledge but never
482
+ // modified — they document context without participating in the
483
+ // activation gate (relevance_paths does that). Splitting evidence into a
484
+ // first-class frontmatter array lets future plan-context retrieval read
485
+ // it as data (intersect with current paths to surface high-recall hits)
486
+ // instead of re-parsing markdown. Optional; omit when no read-only
487
+ // signal was captured. Like relevance_paths it MUST NOT participate in
488
+ // the idempotency_key hash (an idempotent re-extract may surface a
489
+ // slightly different read set without spawning a duplicate pending).
490
+ evidence_paths: z2.array(z2.string()).optional().describe(
491
+ "Workspace-relative paths the agent CONSULTED (read but never modified) while building this knowledge. Documents context without affecting activation. Lifted from the legacy body ## Evidence markdown block into structured frontmatter so plan-context retrieval can read it as data."
334
492
  )
335
493
  });
336
494
  var FabExtractKnowledgeInputSchema = _FabExtractKnowledgeInputBaseSchema.superRefine(
@@ -421,6 +579,24 @@ var FabReviewInputSchema = z2.discriminatedUnion("action", [
421
579
  pending_path: z2.string().min(1),
422
580
  changes: _fabReviewModifyChangesSchema
423
581
  }),
582
+ // v2.0.0-rc.37 NEW-12: explicit modify split. `modify-content` edits scalar
583
+ // frontmatter/body fields (title/summary/maturity/tags/relevance_*) and MUST
584
+ // NOT carry a layer change. `modify-layer` is the dedicated layer-flip path
585
+ // (changes.layer REQUIRED) which may reallocate the stable_id + emit an
586
+ // id-redirect (rc.37 NEW-24). Legacy `modify` stays for back-compat and
587
+ // routes by whether changes.layer is present.
588
+ z2.object({
589
+ action: z2.literal("modify-content"),
590
+ pending_path: z2.string().min(1),
591
+ changes: _fabReviewModifyChangesSchema
592
+ }),
593
+ z2.object({
594
+ action: z2.literal("modify-layer"),
595
+ pending_path: z2.string().min(1),
596
+ changes: _fabReviewModifyChangesSchema.extend({
597
+ layer: z2.enum(["team", "personal"])
598
+ })
599
+ }),
424
600
  z2.object({
425
601
  action: z2.literal("search"),
426
602
  query: z2.string().min(1),
@@ -434,8 +610,8 @@ var FabReviewInputSchema = z2.discriminatedUnion("action", [
434
610
  })
435
611
  ]);
436
612
  var FabReviewInputShape = {
437
- action: z2.enum(["list", "approve", "reject", "modify", "search", "defer"]).describe(
438
- "Action selector. Discriminates the per-action fields below; required."
613
+ action: z2.enum(["list", "approve", "reject", "modify", "modify-content", "modify-layer", "search", "defer"]).describe(
614
+ "Action selector. Discriminates the per-action fields below; required. modify-content edits scalars (no layer); modify-layer is the layer-flip path (changes.layer required); modify is the legacy combined alias."
439
615
  ),
440
616
  filters: _fabReviewFiltersSchema.describe(
441
617
  "Optional filters (type/layer/maturity/tags/created_after). Used by action=list and action=search."
@@ -756,6 +932,12 @@ export {
756
932
  knowledgeSectionsInputSchema,
757
933
  knowledgeSectionsOutputSchema,
758
934
  knowledgeSectionsAnnotations,
935
+ recallInputSchema,
936
+ recallOutputSchema,
937
+ recallAnnotations,
938
+ archiveScanInputSchema,
939
+ archiveScanOutputSchema,
940
+ archiveScanAnnotations,
759
941
  ProposedReasonSchema,
760
942
  PROPOSED_REASON_DESCRIPTIONS,
761
943
  FabExtractKnowledgeInputSchema,
@@ -0,0 +1,100 @@
1
+ // src/templates/bootstrap-canonical.ts
2
+ var BOOTSTRAP_MARKER_BEGIN = "<!-- fabric:bootstrap:begin -->";
3
+ var BOOTSTRAP_MARKER_END = "<!-- fabric:bootstrap:end -->";
4
+ var LEGACY_KB_MARKER_BEGIN = "<!-- fabric:knowledge-base:begin -->";
5
+ var LEGACY_KB_MARKER_END = "<!-- fabric:knowledge-base:end -->";
6
+ var BOOTSTRAP_REGEX = /(?:\r?\n){0,2}<!-- fabric:bootstrap:begin -->[\s\S]*?<!-- fabric:bootstrap:end -->/;
7
+ var LEGACY_KB_REGEX = /(?:\r?\n){0,2}<!-- fabric:knowledge-base:begin -->[\s\S]*?<!-- fabric:knowledge-base:end -->/;
8
+ var BOOTSTRAP_CANONICAL = `# Fabric Bootstrap
9
+
10
+ \u672C\u9879\u76EE\u4F7F\u7528 Fabric \u7BA1\u7406\u8DE8\u5BA2\u6237\u7AEF AI \u77E5\u8BC6\u4E0E\u884C\u4E3A\u89C4\u5219\u3002\u672C\u6587\u4EF6\u7531 \`fabric install\` \u540C\u6B65\u5230\u4E09\u7AEF managed block,**\u4E0D\u8981\u624B\u52A8\u7F16\u8F91\u4E09\u7AEF\u7684 block**,\u53EA\u6539\u8FD9\u91CC + \u91CD\u8DD1 \`fabric install\`\u3002
11
+
12
+ ## For Developers
13
+
14
+ \u8FD9\u4E2A\u6587\u4EF6\u662F **AI \u5BA2\u6237\u7AEF\u7684\u7B56\u7565\u4E0E\u89C4\u7EA6\u914D\u7F6E**,\u4E0D\u662F dev onboarding\u3002\u4F60\u4E0D\u9700\u8981\u8BFB Self-archive / Cite / Phase 0.4 \u7B49\u7EC6\u8282\u3002
15
+ \u4F5C\u4E3A dev \u4F60\u53EA\u9700\u8981:\u5728\u6BCF\u4E2A repo \u8DD1\u4E00\u6B21 \`fabric install\`,\u51FA\u95EE\u9898\u8DD1 \`fabric doctor\`,\u5728 \`.fabric/knowledge/<type>/\` \u4E0B\u5199 markdown\u3002
16
+ **\u4E25\u7981\u624B\u52A8\u7F16\u8F91 \`.fabric/agents.meta.json\`** \u2014 \u6D3E\u751F\u72B6\u6001\u7531 engine \u91CD\u5EFA\u3002
17
+
18
+ ## 5 \u5206\u949F\u4E0A\u624B (Dev Quickstart)
19
+
20
+ **Fabric \u662F\u4EC0\u4E48**:\u8DE8\u5BA2\u6237\u7AEF(Claude Code / Codex CLI / Cursor)\u7684 AI \u77E5\u8BC6\u5C42\u3002\u628A\u56E2\u961F/\u9879\u76EE\u7684 **decisions / pitfalls / guidelines / models / processes** \u5B58\u4E3A markdown,hook \u81EA\u52A8 surface \u7ED9 AI,\u8BA9 AI \u4E0D\u7528\u6BCF\u6B21\u91CD\u5B66\u3002
21
+
22
+ **\u4F60\u8981\u505A\u7684 (DO)** vs **engine \u81EA\u52A8\u7684 (DON'T \u624B\u52A8)**:
23
+
24
+ | \u4F60 DO | \u4F60 DON'T |
25
+ | --- | --- |
26
+ | \u6BCF\u4E2A repo \u8DD1\u4E00\u6B21 \`fabric install\` | \u624B\u7F16 \`.fabric/agents.meta.json\` |
27
+ | \u5F02\u5E38\u65F6\u8DD1 \`fabric doctor\` (--fix \u81EA\u6108) | \u624B\u7F16 \`.claude/hooks/\` \u4E0B \`.cjs\` |
28
+ | \u5728 \`.fabric/knowledge/<type>/\` \u4E0B\u5199 markdown | \u64CD\u5FC3 Phase 0.4 / E3 / cite policy |
29
+ | \`npm install -g @fenglimg/fabric-cli@latest\` \u5347\u7EA7 | \u80CC 35 \u6761 doctor lint \u4EE3\u7801 |
30
+
31
+ **4 \u6B65\u5FAA\u73AF**: \`fabric install\` (\u4E00\u6B21) \u2192 AI \u6B63\u5E38\u5DE5\u4F5C (hook on session start + edit) \u2192 AI \u63D0\u8BAE\u6761\u76EE\u5165 \`.fabric/knowledge/pending/\` \u2192 \u7528 \`fabric-review\` skill \u6216 \`fabric doctor --fix\` \u5BA1\u6838\u5F52\u6863\u3002
32
+
33
+ **\u771F\u4F8B**:\u67D0 sprite \u9ED1\u8FB9 root cause \u662F \`atlas.premultiplyAlpha\` flag \u53CD\u5411 \u2014 \u5199\u8FDB \`.fabric/knowledge/pitfalls/\` \u540E,\u4E0B\u6B21\u540C\u7C7B\u95EE\u9898 AI \u81EA\u52A8 reference\u3002
34
+
35
+ \u5B8C\u6574 maintainer \u7248\u89C1 \`docs/USER-QUICKSTART.md\`\u3002
36
+
37
+ ## \u884C\u4E3A\u89C4\u5219
38
+ - **\u4FEE\u6539\u4EFB\u4F55\u6587\u4EF6\u524D**:\u4E24\u6B65\u8C03\u7528\u2014\u2014\u5148 \`fab_plan_context(paths=[<\u88AB\u6539\u6587\u4EF6>])\` \u62FF\u5230 \`selection_token\` \u4E0E\u5019\u9009 \`entries\`(\u6311 \`selectable===true\` \u7684 \`stable_id\`),\u518D \`fab_get_knowledge_sections({ selection_token, ai_selected_stable_ids: [<id>...] })\` \u53D6\u89C4\u5219\u6B63\u6587\u3002
39
+ - **\`.fabric/agents.meta.json\` \u4E25\u7981\u624B\u52A8\u7F16\u8F91**;engine \u4F1A\u81EA\u52A8\u540C\u6B65\u6D3E\u751F\u72B6\u6001,\u663E\u5F0F reconcile \u8DD1 \`fabric doctor --fix\`\u3002
40
+
41
+ ## \u77E5\u8BC6\u5E93(KB)
42
+ - **Discovery**:SessionStart hook \u5217 broad-scoped \u6761\u76EE(\u542B personal layer \`KP-*\` \u6761\u76EE,\u5F15\u7528\u65B9\u5F0F\u76F8\u540C);edit \u6587\u4EF6\u65F6 PreToolUse hook \u53EF\u80FD\u89E6\u53D1 narrow hint\u3002
43
+ - **Usage**:\u4E24\u6B65\u5F0F\u2014\u2014\`fab_plan_context(paths=[...])\` \u8FD4\u56DE \`selection_token\` + \u5019\u9009 entries,\u518D \`fab_get_knowledge_sections({ selection_token, ai_selected_stable_ids: [<id>...] })\` \u62C9\u5168\u6587;\`selection_token\` \u5FC5\u987B\u6765\u81EA\u6700\u8FD1\u4E00\u6B21 \`fab_plan_context\`,\u4E0D\u53EF\u51ED\u7A7A\u7F16\u9020\u3002
44
+ - **session_id**: \u8C03\u7528 \`fab_plan_context\` \u65F6, \u52A1\u5FC5\u628A\u5F53\u524D client session id \u4F5C\u4E3A \`session_id\` \u53C2\u6570\u4F20\u5165(Claude Code \u7684 session id \u5728 stdin payload \u4E2D, Codex \u7684\u5BF9\u5E94 identifier \u540C\u7406)\u3002\u8FD9\u80FD\u8BA9 \`fabric doctor --archive-history\` \u4E0E archive-hint hook \u51C6\u786E\u8BC6\u522B\u8DE8\u4F1A\u8BDD debt \u72B6\u6001\u3002
45
+ - **Write flows**:\`fabric-archive\` / \`fabric-review\` / \`fabric-import\` \u4E09\u4E2A Skills\u3002
46
+ - **Language**:\u6E32\u67D3\u6309 \`.fabric/fabric-config.json\` \u7684 \`fabric_language\` \u5B57\u6BB5\u3002
47
+ - **Archive cadence nudge** (rc.36): \u6BCF\u5B8C\u6210 5+ \u6B21 Edit / \u663E\u8457 decision \u540E,\u5728\u5408\u9002\u56DE\u5408\u4E3B\u52A8 propose \u8C03 \`fabric-archive\` skill \u2014 archive \u6CA1\u5EFA\u7ACB\u9891\u7387\u4F1A\u8BA9 KB \u6162\u901F\u6B7B\u6389\u3002
48
+ - **Review backlog nudge** (rc.36): \`.fabric/knowledge/pending/\` \u7D2F\u79EF >10 \u6761\u65F6,\u5728\u5408\u9002\u56DE\u5408\u4E3B\u52A8 propose \u8C03 \`fabric-review\` skill \u6279\u91CF\u5BA1,\u907F\u514D draft \u5361\u6B7B\u3002
49
+
50
+ ## Self-archive policy (v2.0.0-rc.37 NEW-2: \u7B80\u5316 4 \u4FE1\u53F7 \u2192 2 \u5927\u7C7B)
51
+
52
+ - **\u89E6\u53D1** (\u672C\u4F1A\u8BDD\u51FA\u73B0\u4EE5\u4E0B\u4EFB\u4E00\u4FE1\u53F7, \u5728\u8BE5 turn \u56DE\u590D\u672B\u5C3E\u81EA\u4E3B\u8C03\u7528 \`fabric-archive\` skill, \u8D70 AI \u81EA\u89E6\u53D1\u5165\u53E3 E3_ai_self_trigger):
53
+ 1. **User-driven normative** \u2014 \u7528\u6237\u6D88\u606F\u4E2D\u663E\u5F0F\u8868\u8FBE normative \u610F\u56FE: \`\u4EE5\u540E\` / \`always\` / \`never\` / \`from now on\` / \`\u4E0B\u6B21\u6CE8\u610F\` / \`\u8BB0\u4E00\u4E0B\` / \`\u6C38\u8FDC\u4E0D\u8981\`, OR \u7528\u6237\u5728 \u22652 \u5019\u9009\u95F4\u6743\u8861\u540E\u7ED9\u51FA rationale \u9501\u5B9A\u65B9\u5411, OR \u7528\u6237\u62D2\u4E86\u67D0\u5EFA\u8BAE**\u5E76**\u8BF4\u4E86\u539F\u56E0 (\u62D2\u7EDD\u7406\u7531\u672C\u8EAB\u662F\u77E5\u8BC6)
54
+ 2. **Wrong-turn-and-revert** \u2014 AI \u5C1D\u8BD5 path X, \u53CD\u601D\u540E\u6539\u8D70 path Y (\u5C24\u5176\u5F53 path X \u662F\u975E\u663E\u7136\u8E29\u5751); \u6DB5\u76D6\u6280\u672F\u51B3\u7B56\u53CD\u8F6C + \u5DE5\u5177/\u8303\u5F0F\u5207\u6362 + \u5931\u8D25\u91CD\u8BD5\u3002Anchor: \u4E00\u5B9A\u6709"\u5426\u5B9A+\u66FF\u4EE3"\u7684\u4E24\u6B65\u7ED3\u6784, \u4E0D\u662F\u5355\u7EAF\u63A2\u7D22\u5931\u8D25
55
+
56
+ \u8001 4-state (Normative / Decision-confirmation / Explicit-dismissal / Wrong-turn) \u73B0\u5408\u5E76: \u524D 3 \u4E2A\u5168\u662F"\u7528\u6237\u6D88\u606F\u4E2D\u663E\u5F0F\u8868\u8FBE"\u6027\u8D28, \u6298\u6210 1 \u7C7B; \u7B2C 4 \u662F"AI \u81EA\u5DF1\u7684\u53CD\u601D\u8DEF\u5F84", \u72EC\u7ACB 1 \u7C7B\u3002\u4E24\u7C7B\u5404\u81EA\u7684\u672C\u8D28\u5224\u522B\u4E0D\u53D8, \u89E6\u53D1\u9762\u6CA1\u53D8\u7A84\u3002
57
+
58
+ - **Anti-trigger** (\u660E\u786E\u4E0D\u89E6\u53D1):
59
+ - \u7528\u6237\u7EAF\u8BE2\u95EE (\u65E0 normative \u8868\u8FBE)
60
+ - \u7B80\u5355 refactor / typo fix
61
+ - AI \u81EA\u5DF1\u4EA7\u751F\u7684'\u6D1E\u5BDF' (\u5FC5\u987B\u7531\u7528\u6237\u6D88\u606F\u4E2D\u4FE1\u53F7\u6216 AI \u81EA\u5DF1\u7684 wrong-turn \u89E6\u53D1, \u4E0D\u662F\u51ED\u7A7A"\u6211\u5B66\u5230\u4E86"\u6027\u8D28)
62
+
63
+ - **Anti-loop \u4E09\u6761\u9632\u62A4**:
64
+ - \u540C turn \u6700\u591A\u81EA\u8C03 1 \u6B21
65
+ - \u540C session \u540C outcome \u4E0D\u91CD\u590D (\u82E5 user_dismissed, \u672C\u4F1A\u8BDD\u4E0D\u518D\u81EA\u8C03\u76F8\u540C\u4E3B\u9898)
66
+ - Phase 2.5 viability gate \u515C\u5E95 (skill \u5185\u90E8\u4ECD\u8DD1 gate, AI \u5224\u9519\u4E0D\u4F1A\u4E71\u5199 pending)
67
+
68
+ - **\u5448\u73B0\u6A21\u677F** (turn \u672B\u5C3E\u63D2\u5165, \u4E24\u884C: \u5148 marker \u884C\u4F9B Phase 1.5 \u68C0\u6D4B, \u518D user-facing \u63D0\u793A):
69
+ \`\`\`
70
+ self-archive policy triggered by signal: <User-driven normative|Wrong-turn-and-revert>
71
+ \u987A\u624B\u5F52\u6863: \u6CE8\u610F\u5230\u4F60\u8BF4 \`<\u89E6\u53D1\u77ED\u8BED>\`, \u5DF2\u8C03\u7528 fabric-archive \u6293 N \u6761\u5019\u9009 \u2192 .fabric/knowledge/pending/...
72
+ \u82E5\u4E0D\u8BE5\u8BB0, \u7B54 '\u64A4\u9500' \u6211\u4F1A\u8C03 fab_review reject\u3002
73
+ \`\`\`
74
+ \u7B2C\u4E00\u884C\u662F Phase 1.5 Trigger Gate \u8BC6\u522B E3 \u5165\u53E3\u7684 structured marker (verbatim \u5B57\u7B26\u4E32 \`self-archive policy triggered by signal\`, \u540E\u63A5\u5192\u53F7 + \u89E6\u53D1\u4FE1\u53F7\u540D)\u3002\u7B2C\u4E8C\u884C\u8D77\u662F\u7ED9\u7528\u6237\u770B\u7684\u4E2D\u6587\u63D0\u793A\u3002\u4E24\u884C\u90FD\u5FC5\u987B\u51FA\u73B0; \u7F3A marker \u884C Phase 1.5 \u65E0\u6CD5\u8DEF\u7531\u5230 E3_ai_self_trigger\u3002
75
+
76
+ Backward compat: Phase 1.5 entry-point regex \u540C\u65F6\u8BC6\u522B\u8001 4 \u4E2A\u4FE1\u53F7\u540D (Normative / Wrong-turn-and-revert / Decision confirmation / Explicit dismissal) \u4E0E\u65B0 2 \u5927\u7C7B\u540D, \u65E7 session marker \u4ECD\u80FD\u6B63\u786E\u8DEF\u7531\u3002
77
+
78
+ ## Cite policy (v2.0.0-rc.37 NEW-1: \u7B80\u5316 4-state \u2192 2-state)
79
+
80
+ - **\u89E6\u53D1**: \u505A edit / decide / propose plan \u4E4B\u524D,**\u56DE\u590D\u9996\u884C**\u5FC5\u987B\u5199 \`KB: <id> (<\u22648\u5B57 \u7528\u6CD5>) [applied|dismissed:<reason>]\` \u6216 \`KB: none [<reason>]\`\u3002
81
+ - **\`[applied]\` \u9A8C\u8BC1\u4E49\u52A1**: \u5F15\u7528\u4EFB\u4F55 id \u524D\u5FC5\u987B\u5148\u7528 fab_recall (\u6216\u4E24\u6B65 fab_plan_context \u2192 fab_get_knowledge_sections) \u5B9E\u9645\u6293 KB body, \u9632\u6B62\u7F16\u9020 id\u3002\u9A8C\u8BC1\u4E0D\u901A\u8FC7 = \u4E0D\u80FD cite\u3002
82
+ - **contract \u8BED\u6CD5**: decisions/pitfalls \u7C7B \`[applied]\` cite \u5C3E\u6BB5\u52A0 contract: \`\u2192 <operator> [<operator> ...]\`,operator \u2208 {\`edit:<glob>\` \`!edit:<glob>\` \`require:<symbol>\` \`forbid:<symbol>\` \`skip:<reason>\`}\u3002\u4F8B:\`KB: K-001 (auth) [applied] \u2192 edit:src/auth/**/*.ts !edit:src/legacy/**\`\u3002
83
+ - **skip reason \u8BCD\u5178**: \`sequencing | conditional | semantic | aesthetic | architectural | other:<text>\`\u3002
84
+ - **type \u8DEF\u7531**: models \u7C7B\u5F15\u7528\u4E3A reference cite,\u4E0D\u9700\u8981 contract;guidelines/processes \u7C7B\u6682\u4E0D\u5F3A\u5236,\u63A8\u540E LLM-judge\u3002
85
+ - **\u7528\u6237\u53E3\u5934\u63D0\u89C4\u5219\u6CA1\u7ED9 id**: \u5148\u8C03 \`fab_recall(paths)\` \u6216 \`fab_extract_knowledge\` \u53CD\u67E5\u3002
86
+ - **dismissed reason**: \u679A\u4E3E \`scope-mismatch | outdated | not-applicable | other:<text>\`\u3002
87
+ - **\`KB: none\` sentinel**: \u679A\u4E3E\u4E24\u79CD\u5408\u89C4\u7406\u7531\u2014\u2014\`[no-relevant]\` \u5DF2\u8C03 \`fab_recall\` / \`fab_plan_context\`(\u6216 hook \u8F93\u51FA\u53EF\u89C1)\u4F46\u65E0\u53EF\u7528\u6761\u76EE;\`[not-applicable]\` \u5F53\u524D\u52A8\u4F5C\u4E0D\u5728 cite \u8303\u56F4(\u7EAF\u63A2\u7D22 / Bash \u53EA\u8BFB / \u7528\u6237\u95EE\u7B54)\u3002\u88F8 \`KB: none\`(\u65E0\u540E\u7F00)\u4ECD\u7136 valid,\u5F52\u7C7B\u4E3A \`[unspecified]\`(legacy \u517C\u5BB9,\u9F13\u52B1\u540E\u7EED\u8865\u6CE8)\u3002
88
+ - **\u7A3D\u6838**: \`fabric doctor --cite-coverage [--since=7d] [--client=cc|codex|all]\` \u8F93\u51FA cite \u8986\u76D6\u7387,\u542B \`KB: none\` sentinel \u62C6\u5206\u3002\u672C\u89C4\u5219\u4E0D\u963B\u65AD\u4F60\u5DE5\u4F5C,\u53EA\u8BB0\u5F55\u3002
89
+ - **Backward compat**: \u89E3\u6790\u5668\u540C\u65F6\u63A5\u53D7\u8001 4-state tags (\`planned\` / \`recalled\` / \`chained-from <id>\`) \u2014 \u90FD\u6620\u5C04\u5230 \`[applied]\` \u8BED\u4E49,gradually \u8FC1\u5230\u65B0\u7B80\u5316\u5F62\u6001\u5373\u53EF,\u65E7 session \u7559\u4E0B\u7684 cite \u4ECD\u7136\u8BA1\u5165 cite-coverage\u3002
90
+ `;
91
+
92
+ export {
93
+ BOOTSTRAP_MARKER_BEGIN,
94
+ BOOTSTRAP_MARKER_END,
95
+ LEGACY_KB_MARKER_BEGIN,
96
+ LEGACY_KB_MARKER_END,
97
+ BOOTSTRAP_REGEX,
98
+ LEGACY_KB_REGEX,
99
+ BOOTSTRAP_CANONICAL
100
+ };