@fenglimg/fabric-cli 2.0.0-rc.30 → 2.0.0-rc.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/{doctor-TTDTKOFJ.js → doctor-E26YO67D.js} +8 -2
  2. package/dist/index.js +3 -3
  3. package/dist/{install-OEBNSCS5.js → install-YSFVNY3T.js} +1 -1
  4. package/package.json +3 -3
  5. package/templates/hooks/knowledge-hint-broad.cjs +268 -21
  6. package/templates/hooks/knowledge-hint-narrow.cjs +466 -14
  7. package/templates/skills/fabric-archive/SKILL.md +144 -738
  8. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +5 -5
  9. package/templates/skills/fabric-archive/ref/i18n-policy.md +3 -3
  10. package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
  11. package/templates/skills/fabric-archive/ref/{phase-0-4-onboard.md → phase-1-5-onboard.md} +21 -21
  12. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +60 -0
  13. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +54 -0
  14. package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +80 -0
  15. package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
  16. package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
  17. package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
  18. package/templates/skills/fabric-archive/ref/rc-history.md +6 -6
  19. package/templates/skills/fabric-archive/ref/worked-examples.md +1 -1
  20. package/templates/skills/fabric-import/SKILL.md +29 -556
  21. package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
  22. package/templates/skills/fabric-import/ref/output-contract.md +61 -0
  23. package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
  24. package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
  25. package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
  26. package/templates/skills/fabric-review/SKILL.md +1 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: fabric-import
3
- description: Use this skill for cold-start enrichment of `.fabric/knowledge/` from existing project artifacts — mines `git log` and `docs/*.md` for candidate observations, proposes pending entries via `fab_extract_knowledge`, then deduplicates against canonical entries via `fab_review action=search` (rejecting obvious duplicates, modifying-to-merge marginal duplicates). Triggered by user prompts like "import knowledge from git history" / "bootstrap fabric for this repo" or by an explicit fabric-import skill mention. Default layer is `team` (project artifacts are team-shared). The 3-phase pipeline is resumable via `.fabric/.import-state.json`.
3
+ description: 冷启动从 git log + docs/*.md 回灌 .fabric/knowledge/pending (NOT code/data import). Triggers 导入历史/bootstrap fabric/mine changelog/挖掘 commit.
4
4
  allowed-tools: Read, Glob, Grep, Bash, mcp__fabric__fab_extract_knowledge, mcp__fabric__fab_review
5
5
  ---
6
6
 
@@ -113,405 +113,54 @@ For each candidate signal mined from git or docs, the skill classifies into one
113
113
  - `relevance_scope = "broad"`
114
114
  - `relevance_paths = []`
115
115
 
116
- This is non-negotiable and applies to BOTH Step 2.1 (git mining) AND Step 2.2 (docs mining). No exceptions, no per-candidate override, no Agent judgment.
117
-
118
- **Rationale — why fabric-import cannot bind paths from git history:**
119
-
120
- 1. `fabric-import` is LLM-driven (mines git log + docs), not session-driven (no live `edit_paths` signal).
121
- 2. `git diff --stat` lists files touched by a commit, but those files are the commit's **effect surface**, not the **applicability surface** of the underlying observation. A pitfall surfaced by a fix in `packages/server/src/retry.ts` may apply to every retry call-site in the repo, not just that one file.
122
- 3. LLM-inferred `relevance_paths` from historical commit metadata produces false-narrow bindings — entries get filtered out for paths they actually govern. False-narrow is worse than broad because it silently hides knowledge during plan-context filtering.
123
- 4. Doc-mined observations are usually architectural / cross-cutting (a `docs/architecture.md` "Why a monolith?" decision applies to the whole codebase, not just to `docs/`).
124
-
125
- **Strict prohibitions — DO NOT attempt any of the following:**
126
-
127
- - DO NOT derive `relevance_paths` from `git log --name-only` / `git show --stat` / `git diff` file lists.
128
- - DO NOT derive `relevance_paths` from the path of a mined Markdown file (e.g. do NOT bind a `docs/architecture.md` observation to `["docs/**"]`).
129
- - DO NOT extract path-shaped tokens from commit subjects / bodies / doc text and lift them into `relevance_paths`.
130
- - DO NOT classify a candidate as `relevance_scope = "narrow"` under ANY heuristic.
131
- - DO NOT copy the public-prefix-generalization logic from fabric-archive Phase 1.5 — that logic is valid only when bound to a real-time `edit_paths` signal from an active session, which fabric-import lacks.
132
-
133
- **Cross-reference — fabric-import vs fabric-archive scope handling:**
134
-
135
- | Skill | Scope decision | Why |
136
- |------------------|--------------------|-----------------------------------------------------------------------|
137
- | `fabric-archive` | narrow OR broad, case-by-case per Phase 1.5 rules | Has live `edit_paths` from the active session — the actual applicability surface. |
138
- | `fabric-import` | ALWAYS broad + `[]` (this skill) | LLM-only, no live session signal; git-history paths are effect-surface, not applicability-surface. |
139
-
140
- `fabric-archive`'s Phase 1.5 scope decision (narrow-vs-broad rules + public-prefix generalization + glob blacklist) is INTENTIONALLY MORE PERMISSIVE than fabric-import because archive has the data to bind safely. fabric-import is the STRICTER case.
141
-
142
- **Post-import narrowing path — deferred to user, via `fab_review.modify`:**
143
-
144
- After import completes, the user reviews each kept pending entry via `fabric-review`. When the user judges that an imported entry is actually narrow-scoped, they (or the reviewing Agent on their explicit instruction) issue:
145
-
146
- ```ts
147
- mcp__fabric__fab_review({
148
- action: "modify",
149
- pending_path: "<the imported pending or its post-approval canonical path>",
150
- changes: {
151
- relevance_scope: "narrow",
152
- relevance_paths: ["packages/server/src/retry/**", "packages/server/src/lib/retry.ts"]
153
- }
154
- })
155
- ```
156
-
157
- This is the ONLY legal path for an imported entry to acquire `relevance_paths`. The narrowing decision is the user's, informed by the actual `relevance_paths` candidates they propose — not the skill's, inferred from git metadata.
158
-
159
- **Lint backstop:** doctor lint #23 (`narrow_no_paths`) warns on any `relevance_scope=narrow` entry with empty `relevance_paths`. If this skill ever deviates from the broad+[] rule and writes narrow without paths, lint #23 catches the mistake post-hoc.
160
-
161
- #### Step 2.1 — Git Log Mining
162
-
163
- Bash command (executed via the `Bash` tool — substitute `<window>` and `<commits-cap>` with values resolved from Phase 0.5 config load):
164
-
165
- ```bash
166
- git log --since="<window> months ago" --pretty=format:"%H%n%s%n%b%n---ENDCOMMIT---" -n <commits-cap>
167
- ```
168
-
169
- - `<window>` resolves to `import_window_first_run_months` on a first-run (default 60) or `import_window_rerun_months` on subsequent runs (default 2); first-run-vs-rerun is decided per the Phase 0.5 rule.
170
- - `<commits-cap>` resolves to `import_max_commits_scan` (default 500).
171
-
172
- Tolerate empty output (shallow clone or new repo). Cap the working set at the **`import_max_commits_scan`-most-recent commits (config-resolved)** regardless of date range to bound LLM context.
173
-
174
- For each commit:
175
-
176
- 1. Inspect the conventional-commit prefix in the subject line. Strong signals:
177
- - `feat(...)` with a non-empty body → likely **decision** or **model** (a new capability landed; the body usually explains why)
178
- - `fix(...)` with body length >100 chars → likely **pitfall** (a bug worth diagnosing was non-trivial)
179
- - `refactor(...)` with body → likely **decision** (architectural choice was made)
180
- - `docs(...)` → usually a **guideline** if the body announces a convention; skip if it's just typo/reformat
181
- - `chore(...)`, `test(...)`, `ci(...)` → almost always skip (mechanical; no reusable insight)
182
- 2. Read the commit body. Extract the LLM-judged "core observation" — what would a future engineer want to know about this commit beyond the diff? Aim for 1–2 sentences in zh-CN (project fabric_language; mirror fabric-archive M3 style).
183
- 3. Apply the **Skip Decision Tree** below. If the commit is skip-worthy, record it in `p2_processed_commits[]` with `skipped: true` and move on.
184
- 4. For non-skipped commits, classify type / propose slug / draft summary. Then call `fab_extract_knowledge` with the **mandatory broad + [] scope** (see "Mandatory Scope Rule" above):
185
-
186
- ```ts
187
- mcp__fabric__fab_extract_knowledge({
188
- source_sessions: ["fabric-import-<ISO8601-date>"], // T5: array form; stable per import run
189
- recent_paths: ["<files touched by this commit, capped at 20>"], // provenance only, NOT a path-binding signal
190
- user_messages_summary: "<zh-CN 1-2 sentence summary of the commit's core observation; cite the commit sha as 'src=<sha7>'>",
191
- type: "decisions" | "pitfalls" | "guidelines" | "models" | "processes",
192
- slug: "<kebab-case 2-5 words derived from commit subject + body>",
193
- relevance_scope: "broad", // MANDATORY — never "narrow" from fabric-import
194
- relevance_paths: [], // MANDATORY — never derived from git history
195
- // v2.0.0-rc.9 T6: proposed_reason inferred per Step 2.1.5 (see above).
196
- // session_context cites the commit / doc origin so future-self reviewers
197
- // know this is an LLM-mined entry rather than a live-session capture.
198
- proposed_reason: "<inferred per Step 2.1.5 — varies>",
199
- session_context: "Imported from git log analysis. Origin: commit <sha7> (<subject 30 chars>). No live session — see commit body for full context.",
200
- // v2.0.0-rc.23 TASK-006 (a-C1): four OPTIONAL structured triage fields.
201
- // Inference for the import path (no live session, only commit/doc evidence):
202
- // intent_clues: pull from commit subject/body — when is this rule worth
203
- // consulting? (e.g. ["editing retry/backoff logic"]). Omit if unclear.
204
- // tech_stack: derived from extensions in `recent_paths` (.ts→typescript;
205
- // package.json→nodejs; pyproject.toml→python; etc.). Omit if mixed.
206
- // impact: quote the commit body's "fixes …" / "prevents …" clause
207
- // when present (e.g. ["thundering-herd outage on retry"]). Omit if
208
- // the body has no impact statement.
209
- // must_read_if: ONE strong trigger, ≤160 chars, from the commit's
210
- // primary touched-path family (e.g. "touching retry / backoff logic
211
- // in packages/server/"). Omit if no single path family dominates.
212
- // ALL FOUR ARE OPTIONAL — omit any field that cannot be inferred cleanly
213
- // from commit/doc text alone. None participate in the idempotency_key hash
214
- // (server formula: sha256({source_session, type, slug})), so subsequent
215
- // imports with refined inference do NOT split a single pending entry into
216
- // duplicates.
217
- intent_clues: ["<inferred trigger if commit body suggests one>"],
218
- tech_stack: ["<lang/framework from recent_paths extensions>"],
219
- impact: ["<consequence stated in commit body / doc>"],
220
- must_read_if: "<one-line strongest trigger from commit's touched-path family>"
221
- })
222
- ```
223
-
224
- Note: `recent_paths` continues to carry the touched-file list for **provenance display** (so the user can audit which commit produced which entry). It is NOT lifted into `relevance_paths` — those two fields serve different purposes and the prohibition on path inference from git history applies.
225
-
226
- 5. On success the server returns `{pending_path, idempotency_key}`. Append to `.fabric/.import-state.json`:
227
- - `p2_processed_commits[].push({sha: <full sha>, skipped: false, pending_path, type, slug})`
228
- - `last_checkpoint_at = <ISO8601 now>`
229
- Update is atomic via the 2-step `.tmp` + `mv` pattern documented in the **Atomic State Write** section under "Checkpoint Logic" below, so a crash between commits never corrupts the state file.
230
- 6. **Hard cap**: at most **`import_max_pending_per_run` new pending entries (config-resolved, default 10)** per Phase 2 run. When the cap is reached, mark `p2_cap_reached = true` and stop git-log iteration (the user can re-invoke for more — idempotent resume picks up from the next unprocessed commit).
231
-
232
- #### Step 2.1.5 — Proposed Reason Inference (rc.7 T6)
233
-
234
- For each non-skipped commit OR doc section, infer `proposed_reason` from
235
- prefix + body signal jointly. The 6 reasons below are the full enum accepted
236
- by `fab_extract_knowledge` (schema-locked):
237
-
238
- | Source signal | Body cue | Inferred reason |
239
- |---|---|---|
240
- | `feat(...)` commit | "vs" / "instead of" / "chose" / "rejected X for Y" | `decision-confirmation` |
241
- | `feat(...)` commit | Announces new dep/lib/abstraction, no alternative cited | `new-dependency-or-pattern` |
242
- | `fix(...)` commit | Cites wrong direction tried + reverted | `wrong-turn-revert` |
243
- | `fix(...)` commit | Cites long diagnostic chain → root cause | `diagnostic-then-fix` |
244
- | `refactor(...)` commit | Cites structural rationale (without "vs" alternatives) | `decision-confirmation` |
245
- | `docs(...)` commit | Announces convention ("always X" / "never Y") | `explicit-user-mark` |
246
- | Any commit | Body explicitly rejects an approach + states why | `dismissal-with-reason` |
247
- | Doc section | "Why we chose X over Y" heading | `decision-confirmation` |
248
- | Doc section | "Don't do Y because..." section | `dismissal-with-reason` |
249
- | Doc section | "Always" / "Never" guidelines | `explicit-user-mark` |
250
- | Doc section | Architecture/design narrative (descriptive, no choice rationale) | `new-dependency-or-pattern` |
251
-
252
- **Edge cases**:
253
-
254
- - `chore(` / `test(` / `ci(` should already be skipped per the Skip Decision
255
- Tree below; if they slip through, default to `new-dependency-or-pattern`.
256
- - Ambiguous signals: prefer the reason matching **body content** over
257
- **prefix** (a `feat(` with strong revert-language is `wrong-turn-revert`,
258
- not `new-dependency-or-pattern`).
259
-
260
- **Fallback**: when no row clearly applies, use `new-dependency-or-pattern`
261
- (the broadest "noticed something new" semantic — preserves prior rc.7
262
- behavior and matches the schema's default-import slot).
263
-
264
- #### Step 2.2 — Docs Mining
265
-
266
- Bash command (executed via the `Bash` tool):
267
-
268
- ```bash
269
- find docs/ -maxdepth 3 -name '*.md' -type f 2>/dev/null
270
- ls -1 *.md 2>/dev/null # root-level architectural docs
271
- ```
272
-
273
- For each Markdown file:
274
-
275
- 1. **Skip filter**:
276
- - `README.md` → skip (its first paragraph already lives in init-scan; its body is too generic for fine-grained classification)
277
- - `CHANGELOG.md` → skip (rendered from commit log; mining commits already covers it)
278
- - `LICENSE.md`, `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md` → skip (boilerplate)
279
- - Files <300 bytes → skip (too thin to extract meaningful observations)
280
- 2. Read the file. Identify candidate observations: section headings that read like decisions ("we chose X over Y"), guidelines ("always do X"), pitfalls ("don't do Y because..."), or process steps ("the deploy procedure is..."). Architecture diagrams in fenced code blocks are strong **model** signals.
281
- 3. For each observation, classify type / propose slug / draft summary. Call `fab_extract_knowledge` with the same shape as Step 2.1 (including the **mandatory `relevance_scope: "broad"` + `relevance_paths: []`**), replacing `recent_paths` with `[<this doc path>]` and citing `src=<doc-relative-path>` in the summary. The mined doc's own path goes into `recent_paths` for provenance display ONLY — it is NOT lifted into `relevance_paths`.
282
- 4. Append to `.fabric/.import-state.json`:
283
- - `p2_processed_docs[].push({path: <doc path>, observations_proposed: <count>, pending_paths: [...]})`
284
- 5. **Hard cap shared with Step 2.1**: total new pending entries across git + docs is capped at `import_max_pending_per_run` (config-resolved, default 10) per Phase 2 run.
285
-
286
- #### Skip Decision Tree (when to NOT propose)
287
-
288
- ```
289
- A candidate signal surfaces (commit body or doc section).
290
- ├─ Is it cosmetic only? ("fix typo", whitespace, formatting)
291
- │ └─ YES → skip
292
- ├─ Is the body just metadata? (Co-Authored-By, Signed-off-by, no prose)
293
- │ └─ YES → skip
294
- ├─ Is the same observation already covered by an init-scan baseline title (Phase 1 list)?
295
- │ └─ YES → skip (don't re-propose what init already captured)
296
- ├─ Does the observation fit one of {decisions, pitfalls, guidelines, models, processes}?
297
- │ └─ NO → skip (not classifiable = not yet ripe)
298
- ├─ Is the slug derivable as 2-5 kebab-case words?
299
- │ └─ NO → skip (signal too vague for stable identifier)
300
- └─ Else → propose via fab_extract_knowledge
301
- ```
302
-
303
- After Step 2.2 completes (or hits the cap), update `.fabric/.import-state.json`: `phase = "P2-done"`, `last_checkpoint_at = <ISO8601 now>`.
116
+ This is non-negotiable. Applies to BOTH Step 2.1 (git mining) AND Step 2.2 (docs mining). No exceptions, no per-candidate override, no Agent judgment.
304
117
 
305
- #### Dry-Run Mode
118
+ **Why broad-only:** fabric-import is LLM-driven (mines git/docs), not session-driven. Git's touched-files list is the commit's effect-surface, not the observation's applicability-surface; LLM-inferred narrow bindings produce false-narrow that silently hides knowledge. Doc observations are usually cross-cutting. Narrowing of imported entries is deferred to `fab_review.modify` (user decision, post-import).
306
119
 
307
- When the user invocation includes `dry-run` / `预览` / `--dry-run` keywords, Phase 2 runs WITHOUT calling `fab_extract_knowledge`. Instead it prints a table. UX i18n Policy class 4 — dry-run table headers; the header + column titles are bilingualized; row content (slug / commit sha / doc path) is data and is NOT translated. The protected tokens `broad`, `relevance_scope`, `relevance_paths` appear verbatim in both variants:
120
+ For the full rationale + strict prohibitions list + fabric-archive cross-reference + post-import narrowing path + doctor lint #23 backstop, `Read packages/cli/templates/skills/fabric-import/ref/phase-2-mining.md` (or `.claude/skills/fabric-import/ref/phase-2-mining.md` post-install) §"Mandatory Scope Rule".
308
121
 
309
- zh-CN variant (`fabric_language === "zh-CN"`):
122
+ #### Step 2.1 Git Log Mining (summary)
310
123
 
311
- ```md
312
- # Import 预览 — 将提议 N 条 pending 条目(全部 relevance_scope=broad, relevance_paths=[])
124
+ Run `git log --since="<window> months ago" --pretty=format:"%H%n%s%n%b%n---ENDCOMMIT---" -n <commits-cap>` (window/cap from Phase 0.5 config). For each commit: inspect conventional-commit prefix → infer type signal (feat→decision/model, fix→pitfall, refactor→decision, docs→guideline; chore/test/ci usually skip) → read body → extract core observation → apply Skip Decision Tree → classify type/slug/summary → call `fab_extract_knowledge` with broad+[] scope. Hard cap: `import_max_pending_per_run` (default 10) per run.
313
125
 
314
- | # | 来源 | 类型 | Slug | 作用域 | 摘要 (zh-CN) |
315
- |---|-----------------------|-----------|-------------------------------|--------|-------------------------------------------------------------|
316
- | 1 | git c0a351d | decisions | layer-flip-id-mutation | broad+[] | layer 切换是唯一合法的 stable_id 变更途径,绑定原子事务。 |
317
- | 2 | docs/architecture.md | decisions | monolith-over-microservices | broad+[] | 决定保留单体架构,三人团队不值微服务运维成本。 |
318
- | 3 | git 50367b5 | pitfalls | thundering-herd-no-backoff | broad+[] | 重试无指数回退导致雪崩;必须 jittered exponential backoff。|
319
- ```
126
+ #### Step 2.1.5 Proposed Reason Inference
320
127
 
321
- en variant (`fabric_language === "en"`):
128
+ Infer one of 6 enum values: `decision-confirmation` | `new-dependency-or-pattern` | `wrong-turn-revert` | `diagnostic-then-fix` | `explicit-user-mark` | `dismissal-with-reason`. Fallback: `new-dependency-or-pattern`. Full source-signal × body-cue mapping table in ref.
322
129
 
323
- ```md
324
- # Import Dry Run — would propose N pending entries (all relevance_scope=broad, relevance_paths=[])
130
+ #### Step 2.2 — Docs Mining (summary)
325
131
 
326
- | # | Source | Type | Slug | Scope | Summary |
327
- |---|-----------------------|-----------|-------------------------------|----------|---------------------------------------------------------------|
328
- | 1 | git c0a351d | decisions | layer-flip-id-mutation | broad+[] | Layer change is the only legal stable_id mutation path; atomic txn. |
329
- | 2 | docs/architecture.md | decisions | monolith-over-microservices | broad+[] | Keep the monolith — 3-engineer team can't justify microservice ops. |
330
- | 3 | git 50367b5 | pitfalls | thundering-herd-no-backoff | broad+[] | Retries without exponential backoff caused a thundering herd outage. |
331
- ```
132
+ `find docs/ -maxdepth 3 -name '*.md'` + root `*.md`. Skip README.md / CHANGELOG.md / LICENSE.md / CODE_OF_CONDUCT.md / CONTRIBUTING.md / files <300 bytes. Identify decision/guideline/pitfall/process/model heading patterns. Same `fab_extract_knowledge` call shape as Step 2.1. Cap shared with Step 2.1.
332
133
 
333
- Every dry-run row MUST show `broad+[]` in the Scope column (it is a constant for fabric-import). A row showing anything else is a skill bug — refuse to proceed and surface the violation. Dry-run output is informational only. The state file is NOT written to in dry-run mode (so a real run later starts clean). Phase 3 is also skipped in dry-run.
134
+ #### Skip Decision Tree
334
135
 
335
- #### Idempotency Note T5 array form
136
+ Skip if: cosmetic-only, metadata-only body, already in init-scan baseline, not classifiable to 5 types, or slug not derivable to 2-5 kebab words. Otherwise propose.
336
137
 
337
- The server derives `idempotency_key = sha256({source_session, type, slug})` for every `fab_extract_knowledge` call. Re-invoking with the same `(source_session, type, slug)` triple is SAFE: the server appends new evidence to the existing pending file rather than overwriting or producing duplicates — this is why `fabric-import` resume after Ctrl-C / crash never produces duplicate pending entries for already-processed commits.
138
+ #### Dry-Run Mode
338
139
 
339
- **T5 array-form note (rc.7+)**: when `source_sessions` is passed as an array (the rc.7 T5 contract), only `source_sessions[0]` participates in the server-side idempotency hash. The actual server formula at `packages/server/src/services/extract-knowledge.ts:78` is `sha256(JSON.stringify({source_session: sourceSessions[0], type, slug}))`. Implications for fabric-import:
140
+ `dry-run` / `预览` / `--dry-run` keyword skip MCP calls, render bilingual preview table instead. Every row Scope column shows `broad+[]` (constant for fabric-import). State file NOT written. Phase 3 also skipped.
340
141
 
341
- - Every Phase 2 call uses `source_sessions: ["fabric-import-<ISO8601-date>"]` (single-element array, stable per import run). The first-element-only rule means re-runs on the same date produce the same idempotency key per `(type, slug)` → resume-safe by construction.
342
- - If a future enhancement adds a trailing element (e.g. `["fabric-import-<date>", "<commit-sha>"]`), only the first element will participate in the hash — the commit-sha tail would NOT change the idempotency key for the same `(type, slug)`. Plan accordingly.
343
- - The formula is intentionally stable across the rc.5 → rc.7 migration; adding or removing tail entries does NOT change the idempotency key, preserving rc.5 single-session compat.
142
+ For full Step 2.1 MCP call shape (all rc.7/rc.23 fields), the Step 2.1.5 inference table (11 rows), bilingual dry-run templates (zh-CN + en), and T5 array-form idempotency notes, `Read packages/cli/templates/skills/fabric-import/ref/phase-2-mining.md` (or `.claude/skills/fabric-import/ref/phase-2-mining.md` post-install).
344
143
 
345
144
  ### Phase 3 — LLM-Driven Dedup vs Canonical
346
145
 
347
- For each pending entry created in Phase 2 (read from `p2_processed_commits[].pending_path` and `p2_processed_docs[].pending_paths`), check if it duplicates / contradicts / is subsumed by an existing canonical entry. **Semantic comparison is the LLM's job — `fab_review` does not compare meaning.**
348
-
349
- #### Step 3.1 — Search Canonical of Same Type
350
-
351
- For each just-proposed pending entry (read its frontmatter via the `Read` tool to get type + slug + title):
352
-
353
- ```ts
354
- mcp__fabric__fab_review({
355
- action: "search",
356
- query: "<title or summary keywords from the pending entry>",
357
- filters: { type: "<same type as pending>" }
358
- })
359
- ```
360
-
361
- The server returns ranked `items[]` of CANONICAL entries (not pending) of the same type. Cap the comparison set at the top 5 results.
362
-
363
- #### Step 3.2 — Semantic Compare
364
-
365
- For each `(pending, canonical)` pair the LLM judges:
366
-
367
- - **Duplicate** — same essential claim. LLM 主观判断:标题与摘要表达同一核心结论,新 pending 未提供新证据。具体阈值不可量化。Action: **reject** the new pending.
368
- - **Subsumption** (pending narrower) — canonical fully covers the pending plus more. Action: **reject** the new pending (canonical already serves).
369
- - **Subsumption-with-novelty** (pending adds evidence) — canonical covers the claim but the new pending brings new evidence (commit sha, file paths). Action: **modify** the canonical to merge in the new evidence; **reject** the new pending citing the modified canonical.
370
- - **Contradiction** — opposing claims about the same scope. Action: leave pending; flag for user via roll-up. The user must decide via `fabric-review` later — `fabric-import` does NOT auto-resolve contradictions.
371
- - **Genuinely new** — no canonical match. Action: leave pending in place (will surface in next `fabric-review` run for normal approval flow).
372
-
373
- #### Step 3.3 — Issue Dedup MCP Calls
374
-
375
- For each `reject`-classified pending:
376
-
377
- ```ts
378
- mcp__fabric__fab_review({
379
- action: "reject",
380
- pending_paths: ["<the new pending path>"],
381
- reason: "duplicate of <stable_id of canonical>" // OR "subsumed by <stable_id>"
382
- })
383
- ```
384
-
385
- For each `subsumption-with-novelty` case (modify canonical, then reject pending):
386
-
387
- ```ts
388
- // Step A: merge new evidence into canonical
389
- mcp__fabric__fab_review({
390
- action: "modify",
391
- pending_path: "<canonical's pending_path-style relative path>",
392
- changes: { summary: "<merged summary; original + new evidence cite>" }
393
- })
394
-
395
- // Step B: reject the now-superseded pending
396
- mcp__fabric__fab_review({
397
- action: "reject",
398
- pending_paths: ["<the new pending path>"],
399
- reason: "merged into <stable_id of modified canonical>"
400
- })
401
- ```
146
+ For each pending entry created in Phase 2, check if it duplicates / contradicts / is subsumed by an existing canonical entry. **Semantic comparison is the LLM's job — `fab_review` does not compare meaning.**
402
147
 
403
- Append to `.fabric/.import-state.json` after EACH successful MCP call:
148
+ **4-step summary:**
404
149
 
405
- - `p3_dedup_completed[].push({pending_path: <new pending>, action: "reject" | "modify-then-reject" | "kept", canonical_ref: "<stable_id>" | null})`
406
- - `last_checkpoint_at = <ISO8601 now>`
150
+ 1. **Step 3.1** `fab_review action="search"` filtered by `type`, cap top 5 canonical results.
151
+ 2. **Step 3.2** — LLM classifies each (pending, canonical) pair: `duplicate` (reject pending) | `subsumption` (reject pending) | `subsumption-with-novelty` (modify canonical + reject pending) | `contradiction` (leave + flag) | `genuinely-new` (keep).
152
+ 3. **Step 3.3** — Issue `fab_review` reject / modify MCP calls per classification.
153
+ 4. **Step 3.4** — Update state to `phase="complete"` + write `final_summary`. Render roll-up.
407
154
 
408
- #### Step 3.4 Phase 3 Completion
155
+ For full Step 3.1/3.3 MCP call shapes, the 5-way semantic compare classification with action-per-case, and the rc.8 sentinel-removal note, `Read packages/cli/templates/skills/fabric-import/ref/phase-3-dedup.md` (or `.claude/skills/fabric-import/ref/phase-3-dedup.md` post-install).
409
156
 
410
- After all Phase 2 outputs are dedup-reviewed:
411
-
412
- - Update `.fabric/.import-state.json`: `phase = "complete"`, `last_checkpoint_at = <ISO8601 now>`, `final_summary = {proposed: N, kept: K, rejected_dup: R, merged: M, contradictions_flagged: C}`.
413
- - Render the final roll-up to the user (see Output Contract below).
414
-
415
- > Setting `phase = "complete"` in `.fabric/.import-state.json` is enough to silence the SessionStart underseed self-check banner (`shouldRecommendImport()` returns false for any non-`absent` state). 无需额外清理 sentinel 文件 — 该机制已在 rc.8 下线。
157
+ ## Checkpoint Logic `.fabric/.import-state.json`
416
158
 
417
- The user MAY manually delete `.fabric/.import-state.json` to reset, or the skill MAY offer a one-line "reset state and re-run from scratch?" prompt the next time it is invoked with `phase="complete"` already present.
159
+ State file `.fabric/.import-state.json` is the single source of resumability. Every write uses the 2-step atomic pattern: **Step A** `Write` to `.fabric/.import-state.json.tmp` **Step B** `Bash: mv .tmp` to commit. POSIX `rename(2)` guarantees atomicity. `Write` alone is NOT atomic.
418
160
 
419
- ## Checkpoint Logic — `.fabric/.import-state.json`
161
+ Resume contract: re-invoking fabric-import after ANY interruption (Ctrl-C, crash, MCP network blip) MUST NOT propose duplicates and MUST NOT redo completed dedup. Resume per `phase` field: `P1-done` → resume Phase 2.1; `P2-done` → resume Phase 3.1; `complete` AND <24h → skip; ≥24h → confirm with user.
420
162
 
421
- The state file lives at `.fabric/.import-state.json` and is the single source of resumability for fabric-import. It is written via the explicit 2-step atomic pattern documented below so a crash between phases / between sub-steps never corrupts it.
422
-
423
- ### Atomic State Write (2-step pattern)
424
-
425
- **Every** update to `.fabric/.import-state.json` MUST use the following two-step pattern, executed by the skill itself (not delegated to an external helper):
426
-
427
- - **Step A**: `Write` tool → `.fabric/.import-state.json.tmp` (full JSON content; never partial / never appended).
428
- - **Step B**: `Bash` → `mv .fabric/.import-state.json.tmp .fabric/.import-state.json`.
429
-
430
- This 2-step pattern is mandatory for every state file update. `mv` is atomic on POSIX (`rename(2)` on the same filesystem guarantees the target either points to the old or new inode, never to a half-written file). `Write` alone is NOT atomic — the open + truncate + write sequence opens a window in which a crash leaves a zero-length or partially-written file on disk, which Phase 0.1 then has to discard. The `.tmp` + `mv` pattern eliminates that window.
431
-
432
- Crash safety expectations:
433
-
434
- - Crash between Step A and Step B → leaves `.fabric/.import-state.json.tmp`. Phase 0 residue scan (above) triages it on next invocation.
435
- - Crash during Step B (between the `rename` syscall start and return) → POSIX `rename` is atomic; either the prior `.import-state.json` is intact, or the new one is in place. No torn state.
436
- - Crash before Step A → no state mutation occurred; prior state file is unchanged.
437
-
438
- The legacy phrasing `atomicWriteJson` / `write-temp-then-rename` used in earlier drafts of this skill refers to this exact 2-step pattern; the explicit Step A / Step B description above is the canonical form.
439
-
440
- ### events.jsonl Constraint Note
441
-
442
- Event lines appended to `.fabric/events.jsonl` are subject to POSIX
443
- single-write atomicity: only writes ≤ 4KB (`PIPE_BUF`) are guaranteed
444
- atomic via `Bash: echo "..." >> file`. Lines exceeding 4KB risk
445
- interleaved corruption under concurrent skill + server writes to the
446
- same ledger.
447
-
448
- Skills MUST ensure:
449
-
450
- - Each event JSON line is a **single line** (no embedded newlines;
451
- escape `\n` in any string value).
452
- - `session_context` and other free-form text fields **self-truncate** to
453
- keep the entire serialized line under 4KB. Suggested per-field caps:
454
- `session_context` first 500 chars; `source_sessions` cap at 5
455
- entries; `recent_paths` cap at 20 entries; `user_messages_summary`
456
- first 500 chars.
457
- - If approaching the 4KB ceiling after the per-field caps, drop optional
458
- fields (e.g. tags / extra metadata) **before** truncating semantic
459
- content (the summary / context that carries the actual observation).
460
- - This constraint applies to any event the skill itself appends (e.g.
461
- abort signals); MCP-server-side appends (via `appendEventLedgerEvent`)
462
- are already line-length-bounded server-side.
463
-
464
- ### Schema (all fields)
465
-
466
- ```json
467
- {
468
- "phase": "P1-done | P2-done | complete",
469
- "started_at": "<ISO8601 first invocation>",
470
- "last_checkpoint_at": "<ISO8601 most recent successful sub-step>",
471
- "p1_baseline_titles": ["<title1>", "<title2>"],
472
- "p2_processed_commits": [
473
- { "sha": "<full sha>", "skipped": true,
474
- "skip_reason": "cosmetic | metadata-only | already-in-baseline | unclassifiable | overlong-slug" },
475
- { "sha": "<full sha>", "skipped": false,
476
- "pending_path": "knowledge/pending/<type>/<slug>.md",
477
- "type": "<one of 5>", "slug": "<kebab-case-slug>" }
478
- ],
479
- "p2_processed_docs": [
480
- { "path": "docs/<file>.md", "observations_proposed": 2,
481
- "pending_paths": ["<path1>", "<path2>"] }
482
- ],
483
- "p2_cap_reached": false,
484
- "p3_dedup_completed": [
485
- { "pending_path": "<new pending path>",
486
- "action": "reject | modify-then-reject | kept",
487
- "canonical_ref": "<stable_id or null>" }
488
- ],
489
- "errors": [
490
- { "step": "P2.git", "ref": "<commit sha or doc path>", "error": "<message>" }
491
- ],
492
- "final_summary": {
493
- "proposed": 0, "kept": 0, "rejected_dup": 0, "merged": 0, "contradictions_flagged": 0
494
- }
495
- }
496
- ```
497
-
498
- ### Resume Logic (Idempotent Re-Invocation)
499
-
500
- On every skill invocation, BEFORE Phase 1 starts:
501
-
502
- 1. Read `.fabric/.import-state.json`. ENOENT → fresh run, initialize state with `phase: "P1-done"` after Phase 1 completes (state file is created at end of Phase 1, not at start).
503
- 2. If `phase === "complete"` AND `last_checkpoint_at < 24h ago` → SKIP this invocation (precondition warning above) unless user explicitly typed `re-run import` or `reset import`.
504
- 3. If `phase === "complete"` AND `last_checkpoint_at ≥ 24h ago` → ask the user (free-text prompt, NOT AskUserQuestion since this is rare). UX i18n Policy class 3 — confirmation prompts:
505
-
506
- - zh-CN: `上次 import 已完成 (<N> 天前)。重新运行将基于当前 canonical 重做 P2/P3。继续?(y/n)`
507
- - en: `Last import completed (<N> days ago). Re-running will redo P2/P3 against the current canonical set. Continue? (y/n)`
508
-
509
- If `n`, exit.
510
- 4. If `phase === "P1-done"` → skip Phase 1; resume from Phase 2 Step 2.1; iterate git log skipping any sha already in `p2_processed_commits[]`.
511
- 5. If `phase === "P2-done"` → skip Phase 1 + Phase 2; resume from Phase 3 Step 3.1; iterate Phase 2 outputs skipping any pending_path already in `p3_dedup_completed[]`.
512
- 6. After every successful sub-step (one commit processed, one doc processed, one dedup pair resolved), write the updated state file via the 2-step `.tmp` + `mv` pattern (see "Atomic State Write" above). Failures append to `errors[]` and proceed (or halt with prompt if cumulative errors `>5`).
513
-
514
- The contract: re-invoking fabric-import after ANY interruption (Ctrl-C, crash, network blip on MCP) MUST NOT propose duplicates of already-proposed entries and MUST NOT redo already-completed dedup decisions.
163
+ For the full Atomic State Write rationale + crash-safety reasoning, the events.jsonl 4KB POSIX atomicity constraint (single-line + self-truncate rules), the complete `.import-state.json` JSON schema, and the 6-step Resume Logic state machine, `Read packages/cli/templates/skills/fabric-import/ref/checkpoint-state.md` (or `.claude/skills/fabric-import/ref/checkpoint-state.md` post-install).
515
164
 
516
165
  ## Default Behavior & Knobs
517
166
 
@@ -560,191 +209,15 @@ The contract: re-invoking fabric-import after ANY interruption (Ctrl-C, crash, n
560
209
 
561
210
  ## Output Contract
562
211
 
563
- After Phase 3 completes (or on any phase exit due to cap / error / interrupt), the skill MUST produce a roll-up. UX i18n Policy class 1 — render either the en variant or the zh-CN variant per `fabric_language`; the protected tokens (`relevance_scope`, `relevance_paths`, `broad`, `pending_path`, `layer`, `team`, `personal`, `fab_review`, `.fabric/.import-state.json`, etc.) appear verbatim in BOTH variants.
564
-
565
- en variant (`fabric_language === "en"`):
566
-
567
- ```md
568
- # Import Summary — phase=<P1-done | P2-done | complete>
569
-
570
- ## Phase 2 — Mining
571
- - Commits scanned: <N> (skipped: <S> — cosmetic/metadata/baseline-overlap)
572
- - Docs scanned: <D> (skipped: <DS> — README/CHANGELOG/boilerplate)
573
- - Pending proposed: <P> (cap_reached: <true|false>)
574
- - Scope: all <P> proposed entries use relevance_scope=broad, relevance_paths=[] (fabric-import contract).
575
-
576
- ## Phase 3 — Dedup
577
- - Kept (genuinely new): <K>
578
- - Rejected (duplicate): <RD>
579
- - Modified-then-rejected: <MR> (canonical entries enriched: <list of stable_ids>)
580
- - Contradictions flagged: <C> (require manual fabric-review)
581
-
582
- ## State
583
- - .fabric/.import-state.json phase: <phase>
584
- - last_checkpoint_at: <ISO8601>
585
- - Re-invoke to continue if phase != complete.
586
-
587
- ## Next Steps
588
- - Run `fabric-review` to approve the <K> kept pending entries.
589
- - Resolve <C> contradictions manually if any.
590
- - If any kept entry is actually narrow-scoped, narrow it via `fab_review action="modify"` with `changes.relevance_scope="narrow"` + `changes.relevance_paths=[...]` (this skill cannot narrow — see Mandatory Scope Rule in Phase 2).
591
- ```
592
-
593
- zh-CN variant (`fabric_language === "zh-CN"`):
594
-
595
- ```md
596
- # Import 汇总 — phase=<P1-done | P2-done | complete>
597
-
598
- ## Phase 2 — 挖掘
599
- - 扫描 commit 数: <N> (跳过: <S> — cosmetic/metadata/与 baseline 重叠)
600
- - 扫描文档数: <D> (跳过: <DS> — README/CHANGELOG/样板文件)
601
- - 提议 pending: <P> (cap_reached: <true|false>)
602
- - 作用域: 全部 <P> 条提议使用 relevance_scope=broad, relevance_paths=[] (fabric-import 契约)。
603
-
604
- ## Phase 3 — 去重
605
- - 保留 (新知识): <K>
606
- - 已驳回 (重复): <RD>
607
- - 修改后驳回: <MR> (被合入 evidence 的 canonical 条目: <stable_ids 列表>)
608
- - 已标记冲突: <C> (需手动通过 fabric-review 解决)
609
-
610
- ## 状态
611
- - .fabric/.import-state.json phase: <phase>
612
- - last_checkpoint_at: <ISO8601>
613
- - 若 phase != complete, 重新调用以继续。
212
+ After Phase 3 completes (or on any phase exit due to cap / error / interrupt), the skill MUST produce a roll-up. UX i18n Policy class 1 — render either en or zh-CN variant per `fabric_language`. Section names: `Phase 2 — Mining` (commits scanned / docs scanned / pending proposed / scope confirmation), `Phase 3 — Dedup` (kept / rejected_dup / modified-then-rejected / contradictions_flagged), `State` (phase + last_checkpoint_at), `Next Steps` (fabric-review for kept entries; manual resolve for contradictions; narrowing via `fab_review action="modify"`).
614
213
 
615
- ## 后续操作
616
- - 运行 `fabric-review` 审批 <K> 条保留 pending。
617
- - 若有冲突 (<C>), 请手动处理。
618
- - 若某条 pending 实际是 narrow 作用域,通过 `fab_review action="modify"` 配合 `changes.relevance_scope="narrow"` + `changes.relevance_paths=[...]` 收紧 (本 skill 不会自动收紧 — 见 Phase 2 的 Mandatory Scope Rule)。
619
- ```
214
+ For the full bilingual roll-up templates (zh-CN + en) with all section/field placeholders, `Read packages/cli/templates/skills/fabric-import/ref/output-contract.md` (or `.claude/skills/fabric-import/ref/output-contract.md` post-install).
620
215
 
621
216
  Also surface a one-line `git status` of `.fabric/knowledge/` so the user sees the new pending files appear (and any canonical files modified by dedup-merge).
622
217
 
623
218
  ## Worked Examples
624
219
 
625
- ### Example APhase 2 git mining: feat commit → pitfall entry
626
-
627
- Source signal: `git log` surfaces commit `50367b5` with subject `feat(server): add custom retry logic` and body explaining that initial implementation retried without exponential backoff, causing a thundering-herd outage during a brief upstream hiccup; the fix was jittered exponential backoff with a 30s ceiling.
628
-
629
- LLM analysis: this is a **pitfall** (a non-obvious trap that wasted time and is repeatable across services). The body itself documents the trap. Slug candidates: `retry-without-backoff-thundering-herd` (5 words, 38 chars — passes 5 rules).
630
-
631
- Skill output (note `relevance_scope: "broad"` + `relevance_paths: []` — mandatory for fabric-import):
632
-
633
- ```ts
634
- mcp__fabric__fab_extract_knowledge({
635
- source_sessions: ["fabric-import-2026-05-10"],
636
- recent_paths: ["packages/server/src/lib/retry.ts"], // provenance only
637
- user_messages_summary: "重试无指数退避会在短暂上游故障下放大成雪崩。修正:jittered exponential backoff,30 秒上限。src=50367b5",
638
- type: "pitfalls",
639
- slug: "retry-without-backoff-thundering-herd",
640
- relevance_scope: "broad", // MANDATORY
641
- relevance_paths: [], // MANDATORY — do NOT infer ["packages/server/src/lib/retry.ts"]
642
- proposed_reason: "diagnostic-then-fix", // Step 2.1.5: body describes long diagnostic chain (no-backoff → thundering-herd outage → root cause) → root-cause fix; this overrides the `feat(` prefix per the "body content wins over prefix" ambiguity rule.
643
- session_context: "Imported from git log analysis. Origin: commit 50367b5 (feat(server): add custom retry logic). No live session — see commit body for full context."
644
- })
645
- ```
646
-
647
- Counter-example — DO NOT do this:
648
-
649
- ```ts
650
- // WRONG — this skill must never produce narrow + paths from git metadata.
651
- // The retry pitfall applies to every retry site, not just the file touched by 50367b5.
652
- mcp__fabric__fab_extract_knowledge({
653
- // ...
654
- relevance_scope: "narrow", // VIOLATION
655
- relevance_paths: ["packages/server/src/lib/retry.ts"] // VIOLATION
656
- })
657
- ```
658
-
659
- If the user later judges this pitfall to be narrow-scoped, they (via `fabric-review`) issue `fab_review action="modify"` with `changes.relevance_scope` + `changes.relevance_paths` — that is the legal narrowing path.
660
-
661
- State file delta:
662
- ```json
663
- { "p2_processed_commits": [
664
- { "sha": "50367b5...", "skipped": false,
665
- "pending_path": "knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md",
666
- "type": "pitfalls", "slug": "retry-without-backoff-thundering-herd" }
667
- ]
668
- }
669
- ```
670
-
671
- ### Example B — Phase 2 doc mining: architecture.md → decision entry
672
-
673
- Source signal: `docs/architecture.md` contains a section heading "## Why a monolith?" with body explaining the team chose monolith over microservices because the 3-engineer team couldn't justify the operational cost of multi-service deploys, and the dominant performance constraint (DB throughput) doesn't benefit from horizontal split.
674
-
675
- LLM analysis: this is a **decision** (≥2 alternatives weighed — monolith vs microservices — with explicit rationale). Slug candidates: `monolith-over-microservices-small-team` (5 words, 38 chars — passes 5 rules).
676
-
677
- Skill output (broad+[] mandatory; the doc's own path stays in `recent_paths` for provenance, NOT in `relevance_paths`):
678
-
679
- ```ts
680
- mcp__fabric__fab_extract_knowledge({
681
- source_sessions: ["fabric-import-2026-05-10"],
682
- recent_paths: ["docs/architecture.md"], // provenance only
683
- user_messages_summary: "选择单体架构而非微服务:3 人团队无法承担多服务运维成本,且主要性能瓶颈在 DB 吞吐而非应用层水平扩展。src=docs/architecture.md",
684
- type: "decisions",
685
- slug: "monolith-over-microservices-small-team",
686
- relevance_scope: "broad", // MANDATORY
687
- relevance_paths: [] // MANDATORY — a monolith-vs-microservices decision applies repo-wide, not only to docs/
688
- })
689
- ```
690
-
691
- ### Example C — Phase 3 dedup finds duplicate, rejects
692
-
693
- After Example A's pending entry (`retry-without-backoff-thundering-herd`) is proposed, Phase 3 runs:
694
-
695
- ```ts
696
- mcp__fabric__fab_review({
697
- action: "search",
698
- query: "retry backoff thundering herd",
699
- filters: { type: "pitfalls" }
700
- })
701
- ```
702
-
703
- Server returns 1 canonical match: `KT-P-0007--retry-no-jitter-amplification.md` with summary "重试缺少 jitter 在并发场景放大原始故障峰值". LLM judgment: the existing canonical asserts the same essential claim (retry without jitter amplifies failures) — this is a **duplicate**, not subsumption-with-novelty (the new pending offers no new evidence beyond restating the trap).
704
-
705
- Skill output:
706
-
707
- ```ts
708
- mcp__fabric__fab_review({
709
- action: "reject",
710
- pending_paths: ["knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md"],
711
- reason: "duplicate of KT-P-0007"
712
- })
713
- ```
714
-
715
- State file delta:
716
- ```json
717
- { "p3_dedup_completed": [
718
- { "pending_path": "knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md",
719
- "action": "reject", "canonical_ref": "KT-P-0007" }
720
- ]
721
- }
722
- ```
723
-
724
- Final roll-up to user reflects: 1 proposed, 0 kept, 1 rejected_dup, 0 merged, 0 contradictions.
725
-
726
- ### Example D — Post-import narrowing (out-of-band, NOT this skill)
727
-
728
- This example documents the legal narrowing path; it is NOT performed by `fabric-import` itself. After Example B's `monolith-over-microservices-small-team` decision is imported (with `relevance_scope=broad`, `relevance_paths=[]`) and later approved into canonical via `fabric-review`, the user decides the decision is actually narrow to the server package's deploy tooling.
729
-
730
- The user issues (via `fabric-review`, NOT via this skill):
731
-
732
- ```ts
733
- mcp__fabric__fab_review({
734
- action: "modify",
735
- pending_path: "knowledge/team/decisions/monolith-over-microservices-small-team.md",
736
- changes: {
737
- relevance_scope: "narrow",
738
- relevance_paths: ["packages/server/**", "scripts/deploy/**"]
739
- }
740
- })
741
- ```
742
-
743
- Key invariants of this flow:
744
-
745
- - The narrowing decision originates from the **user**, informed by the actual paths they propose — not from `fabric-import` inferring paths from git metadata.
746
- - The modify call goes through `fab_review`, not `fab_extract_knowledge`, because the entry already exists (post-import or post-approval).
747
- - If the user later flips the entry's layer from `team` to `personal`, server-side auto-degrades scope back to `broad` and clears `relevance_paths` (see rc.5 C3 acceptance criterion; personal knowledge crosses projects so paths don't generalize). This is the only legal way for `relevance_paths` to be re-cleared.
220
+ Four end-to-end examples(A) feat commit → pitfall entry showing broad+[] discipline and the "WRONG" counter-example; (B) docs/architecture.md → decision entry; (C) Phase 3 dedup finds duplicate → reject MCP call; (D) post-import narrowing via `fab_review.modify` (out-of-band, NOT this skill) — live in `packages/cli/templates/skills/fabric-import/ref/worked-examples.md` (or `.claude/skills/fabric-import/ref/worked-examples.md` post-install). Load when you want to see complete MCP call shapes + state file deltas in realistic scenarios.
748
221
 
749
222
  ## Failure Recovery
750
223