@fenglimg/fabric-cli 1.8.0-rc.3 → 2.0.0-rc.10

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 (49) hide show
  1. package/README.md +6 -6
  2. package/dist/chunk-6ICJICVU.js +10 -0
  3. package/dist/chunk-AW3G7ZH5.js +576 -0
  4. package/dist/chunk-HQLEHH4O.js +321 -0
  5. package/dist/chunk-MT3R57VG.js +1000 -0
  6. package/dist/{chunk-QPCRBQ5Y.js → chunk-OBQU6NHO.js} +1 -52
  7. package/dist/chunk-WPTA74BY.js +184 -0
  8. package/dist/chunk-WWNXR34K.js +49 -0
  9. package/dist/doctor-RILCO5OG.js +282 -0
  10. package/dist/hooks-NX32PPEN.js +13 -0
  11. package/dist/index.js +8 -5
  12. package/dist/{init-7EYGUJNJ.js → init-SAVH4SKE.js} +281 -1235
  13. package/dist/plan-context-hint-QMUPAXIB.js +98 -0
  14. package/dist/scan-ELSNCSKS.js +22 -0
  15. package/dist/{serve-466QXQ5Q.js → serve-NGLXHDYC.js} +8 -4
  16. package/dist/uninstall-DBAR2JBS.js +1082 -0
  17. package/package.json +3 -3
  18. package/templates/agents-md/AGENTS.md.template +55 -17
  19. package/templates/bootstrap/CLAUDE.md +1 -1
  20. package/templates/bootstrap/codex-AGENTS-header.md +1 -1
  21. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +1 -1
  22. package/templates/hooks/configs/README.md +73 -0
  23. package/templates/hooks/configs/claude-code.json +37 -0
  24. package/templates/hooks/configs/codex-hooks.json +20 -0
  25. package/templates/hooks/configs/cursor-hooks.json +20 -0
  26. package/templates/hooks/fabric-hint.cjs +1337 -0
  27. package/templates/hooks/knowledge-hint-broad.cjs +612 -0
  28. package/templates/hooks/knowledge-hint-narrow.cjs +826 -0
  29. package/templates/hooks/lib/session-digest-writer.cjs +172 -0
  30. package/templates/skills/fabric-archive/SKILL.md +486 -0
  31. package/templates/skills/fabric-import/SKILL.md +560 -0
  32. package/templates/skills/fabric-review/SKILL.md +382 -0
  33. package/dist/chunk-NMMUETVK.js +0 -216
  34. package/dist/doctor-F52XWWZC.js +0 -98
  35. package/dist/scan-NNBNGIZG.js +0 -12
  36. package/templates/agents-md/variants/cocos.md +0 -20
  37. package/templates/agents-md/variants/next.md +0 -20
  38. package/templates/agents-md/variants/vite.md +0 -20
  39. package/templates/bootstrap/GEMINI.md +0 -8
  40. package/templates/bootstrap/roo-fabric.md +0 -5
  41. package/templates/bootstrap/windsurf-fabric.md +0 -5
  42. package/templates/claude-hooks/fabric-init-reminder.cjs +0 -18
  43. package/templates/claude-skills/fabric-init/SKILL.md +0 -163
  44. package/templates/codex-hooks/fabric-session-start.cjs +0 -19
  45. package/templates/codex-hooks/fabric-stop-reminder.cjs +0 -18
  46. package/templates/codex-skills/fabric-init/SKILL.md +0 -162
  47. package/templates/husky/pre-commit +0 -9
  48. package/templates/skill-source/fabric-init/SOURCE.md +0 -157
  49. package/templates/skill-source/fabric-init/clients.json +0 -17
@@ -0,0 +1,560 @@
1
+ ---
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: team (project artifacts are team-shared). The 3-phase pipeline is resumable via `.fabric/.import-state.json`.
4
+ allowed-tools: Read, Glob, Grep, Bash, mcp__fabric__fab_extract_knowledge, mcp__fabric__fab_review
5
+ ---
6
+
7
+ > **Surface**: This is a Skill (AI-driven, LLM judgment over git log + docs for cold-start enrichment). See [`docs/surfaces.md`](https://github.com/fenglimg/fabric/blob/main/docs/surfaces.md) for the CLI / Skill / MCP boundary.
8
+
9
+ ## Purpose
10
+
11
+ `fabric-import` is a one-time (per project) cold-start skill that lifts existing project artifacts — git commit history and Markdown documentation — into the knowledge layer as pending entries. It is the bridge between a brand-new Fabric installation (which only has the 4–7 baseline entries produced by `fabric init`'s deterministic scan) and a useful corpus that reflects accumulated team thinking. Run it once when adopting Fabric on an existing repo, or after a major refactor that invalidates large chunks of canonical knowledge. Default layer is `team`: project artifacts in git/docs are team-shared by definition; the user can later layer-flip individual entries to `personal` via `fabric-review` modify.
12
+
13
+ ## Precondition
14
+
15
+ This skill is invoked when one of the following holds:
16
+
17
+ - The user typed an explicit import request (e.g. "import knowledge from git history", "bootstrap fabric for this repo", "mine the changelog into pending", "fabric import")
18
+ - The user explicitly mentioned this skill by name (`fabric-import`)
19
+ - A `fabric import` CLI command was run (rc.4 wires this if shipped; otherwise treat as user prompt)
20
+
21
+ If none of the above hold, stop the skill immediately and tell the user `没有触发 import 信号;如需手动 import 请显式调用 fabric-import`.
22
+
23
+ > **Recommendation source (rc.8+)**: 过去版本的 `.fabric/.import-requested` sentinel 机制已下线;推荐由 SessionStart hook 的 underseed 自检触发(`templates/hooks/knowledge-hint-broad.cjs` 的 `shouldRecommendImport()`:`agents.meta.json` 存在 + canonical 节点数 < `underseed_node_threshold` + `.import-state.json` 缺失三条件齐备时一次性提示)。本 skill 不再读写 sentinel 文件,也不需要在 Phase 3 完成时手动清理它。
24
+
25
+ This skill SHOULD be skipped (warn the user, do not proceed) when:
26
+
27
+ - `.fabric/` does not exist — direct the user to run `fabric init` first; `fabric-import` is NOT a substitute for the deterministic init-scan
28
+ - `.fabric/knowledge/` already holds **>50 canonical entries** across all types — the project is mature; use `fabric-archive` (per-session capture) and `fabric-review` (lifecycle review) instead; bulk import would just create dup churn
29
+ - `.fabric/.import-state.json` exists with `phase: "complete"` and `last_checkpoint_at` is **<24h ago** — the user just ran import; surface the prior result rather than re-running
30
+
31
+ Required preconditions before any MCP call:
32
+
33
+ - `.fabric/` directory exists in the project root
34
+ - `.fabric/agents.meta.json` is present (init produced it; the id allocator reads it on later approve)
35
+ - `.fabric/events.jsonl` exists (tolerate ENOENT — empty ledger is normal first-run)
36
+ - `mcp__fabric__fab_extract_knowledge` AND `mcp__fabric__fab_review` MCP tools are registered and reachable
37
+ - Working tree is reasonably clean (large uncommitted churn pollutes git-log mining; warn but allow)
38
+
39
+ ## 3-Phase Pipeline (P1 reference / P2 mine / P3 dedup)
40
+
41
+ The pipeline runs strictly in order. Each phase reads the prior phase's outputs and updates `.fabric/.import-state.json` after every successful sub-step (not just at phase end). The skill is `Infer-not-Ask` for which phase to run (always all three when starting fresh, or resumes from the checkpoint phase).
42
+
43
+ ### Phase 1 — Init-Scan Reference (NO RE-IMPLEMENTATION)
44
+
45
+ > Verbatim boundary: `fabric init` (rc.1, deterministic CLI) already produces the baseline scan. Phase 1 of this skill **REFERENCES** that output. It does NOT redo the scan.
46
+
47
+ The deterministic init-scan has already populated `.fabric/knowledge/team/` with 4–7 baseline entries derived from:
48
+
49
+ - `package.json` (tech stack, scripts, key dependencies)
50
+ - `README.md` first paragraph (project elevator pitch)
51
+ - Build configuration (`tsconfig.json`, `pyproject.toml`, `Cargo.toml`, etc.)
52
+ - Code style (`.editorconfig`, lint config)
53
+ - CI configuration (`.github/workflows/`, `.gitlab-ci.yml`, etc.) when present
54
+ - The first sentence of any top-level `LICENSE` (rare baseline signal)
55
+
56
+ Phase 1 actions performed by THIS skill:
57
+
58
+ 1. Read `.fabric/agents.meta.json` to confirm baseline counters exist (each type's `next_id` should be `>1` if init-scan landed entries; `=1` means init produced zero entries of that type — informational, not an error).
59
+ 2. Glob `.fabric/knowledge/team/**/*.md` to enumerate baseline entry titles. Capture the list — Phase 2 uses these titles as a **negative filter** (signals already covered by init-scan should be skipped, not re-proposed).
60
+ 3. If `.fabric/agents.meta.json` is missing OR `.fabric/knowledge/team/` is empty: STOP. Tell the user `请先运行 fabric init 完成基线扫描,再调用 fabric-import` and exit.
61
+ 4. Update `.fabric/.import-state.json`: `phase = "P1-done"`, `p1_baseline_titles = [<list>]`, `last_checkpoint_at = <ISO8601 now>`.
62
+
63
+ **Phase 1 produces no MCP calls.** It only reads the on-disk init-scan output.
64
+
65
+ ### Phase 2 — LLM-Driven Git + Doc Mining
66
+
67
+ For each candidate signal mined from git or docs, the skill classifies into one of the 5 types (`decisions / pitfalls / guidelines / models / processes`), drafts a slug per the 5-rule naming guideline (see fabric-archive for the canonical rules), and proposes a pending entry via `fab_extract_knowledge`. Default layer for every Phase 2 proposal: `team`.
68
+
69
+ #### Mandatory Scope Rule — Always Broad + Empty Paths (Q-1 Resolution)
70
+
71
+ **EVERY `fab_extract_knowledge` call issued from this skill MUST set:**
72
+
73
+ - `relevance_scope = "broad"`
74
+ - `relevance_paths = []`
75
+
76
+ 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.
77
+
78
+ **Rationale — why fabric-import cannot bind paths from git history:**
79
+
80
+ 1. `fabric-import` is LLM-driven (mines git log + docs), not session-driven (no live `edit_paths` signal).
81
+ 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.
82
+ 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.
83
+ 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/`).
84
+
85
+ **Strict prohibitions — DO NOT attempt any of the following:**
86
+
87
+ - DO NOT derive `relevance_paths` from `git log --name-only` / `git show --stat` / `git diff` file lists.
88
+ - 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/**"]`).
89
+ - DO NOT extract path-shaped tokens from commit subjects / bodies / doc text and lift them into `relevance_paths`.
90
+ - DO NOT classify a candidate as `relevance_scope = "narrow"` under ANY heuristic.
91
+ - 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.
92
+
93
+ **Cross-reference — fabric-import vs fabric-archive scope handling:**
94
+
95
+ | Skill | Scope decision | Why |
96
+ |------------------|--------------------|-----------------------------------------------------------------------|
97
+ | `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. |
98
+ | `fabric-import` | ALWAYS broad + `[]` (this skill) | LLM-only, no live session signal; git-history paths are effect-surface, not applicability-surface. |
99
+
100
+ `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.
101
+
102
+ **Post-import narrowing path — deferred to user, via `fab_review.modify`:**
103
+
104
+ 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:
105
+
106
+ ```ts
107
+ mcp__fabric__fab_review({
108
+ action: "modify",
109
+ pending_path: "<the imported pending or its post-approval canonical path>",
110
+ changes: {
111
+ relevance_scope: "narrow",
112
+ relevance_paths: ["packages/server/src/retry/**", "packages/server/src/lib/retry.ts"]
113
+ }
114
+ })
115
+ ```
116
+
117
+ 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.
118
+
119
+ **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.
120
+
121
+ #### Step 2.1 — Git Log Mining
122
+
123
+ Bash command (executed via the `Bash` tool):
124
+
125
+ ```bash
126
+ git log --since="2 months ago" --pretty=format:"%H%n%s%n%b%n---ENDCOMMIT---" -n 200
127
+ ```
128
+
129
+ Tolerate empty output (shallow clone or new repo). Cap the working set at the **most recent 200 commits** regardless of date range to bound LLM context.
130
+
131
+ For each commit:
132
+
133
+ 1. Inspect the conventional-commit prefix in the subject line. Strong signals:
134
+ - `feat(...)` with a non-empty body → likely **decision** or **model** (a new capability landed; the body usually explains why)
135
+ - `fix(...)` with body length >100 chars → likely **pitfall** (a bug worth diagnosing was non-trivial)
136
+ - `refactor(...)` with body → likely **decision** (architectural choice was made)
137
+ - `docs(...)` → usually a **guideline** if the body announces a convention; skip if it's just typo/reformat
138
+ - `chore(...)`, `test(...)`, `ci(...)` → almost always skip (mechanical; no reusable insight)
139
+ 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 knowledge_language; mirror fabric-archive M3 style).
140
+ 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.
141
+ 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):
142
+
143
+ ```ts
144
+ mcp__fabric__fab_extract_knowledge({
145
+ source_sessions: ["fabric-import-<ISO8601-date>"], // T5: array form; stable per import run
146
+ recent_paths: ["<files touched by this commit, capped at 20>"], // provenance only, NOT a path-binding signal
147
+ user_messages_summary: "<zh-CN 1-2 sentence summary of the commit's core observation; cite the commit sha as 'src=<sha7>'>",
148
+ type: "decisions" | "pitfalls" | "guidelines" | "models" | "processes",
149
+ slug: "<kebab-case 2-5 words derived from commit subject + body>",
150
+ relevance_scope: "broad", // MANDATORY — never "narrow" from fabric-import
151
+ relevance_paths: [], // MANDATORY — never derived from git history
152
+ // v2.0.0-rc.7 T6: fabric-import defaults to `new-dependency-or-pattern`
153
+ // because git-log mining surfaces newly-introduced abstractions/conventions.
154
+ // session_context cites the commit / doc origin so future-self reviewers
155
+ // know this is an LLM-mined entry rather than a live-session capture.
156
+ proposed_reason: "new-dependency-or-pattern",
157
+ session_context: "Imported from git log analysis. Origin: commit <sha7> (<subject 30 chars>). No live session — see commit body for full context."
158
+ })
159
+ ```
160
+
161
+ 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.
162
+
163
+ 5. On success the server returns `{pending_path, idempotency_key}`. Append to `.fabric/.import-state.json`:
164
+ - `p2_processed_commits[].push({sha: <full sha>, skipped: false, pending_path, type, slug})`
165
+ - `last_checkpoint_at = <ISO8601 now>`
166
+ Update is atomic (write to temp file then rename) so a crash between commits never corrupts the state file.
167
+ 6. **Hard cap**: at most **10 new pending entries** 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).
168
+
169
+ #### Step 2.2 — Docs Mining
170
+
171
+ Bash command (executed via the `Bash` tool):
172
+
173
+ ```bash
174
+ find docs/ -maxdepth 3 -name '*.md' -type f 2>/dev/null
175
+ ls -1 *.md 2>/dev/null # root-level architectural docs
176
+ ```
177
+
178
+ For each Markdown file:
179
+
180
+ 1. **Skip filter**:
181
+ - `README.md` → skip (its first paragraph already lives in init-scan; its body is too generic for fine-grained classification)
182
+ - `CHANGELOG.md` → skip (rendered from commit log; mining commits already covers it)
183
+ - `LICENSE.md`, `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md` → skip (boilerplate)
184
+ - Files <300 bytes → skip (too thin to extract meaningful observations)
185
+ 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.
186
+ 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`.
187
+ 4. Append to `.fabric/.import-state.json`:
188
+ - `p2_processed_docs[].push({path: <doc path>, observations_proposed: <count>, pending_paths: [...]})`
189
+ 5. **Hard cap shared with Step 2.1**: total new pending entries across git + docs is capped at 10 per Phase 2 run.
190
+
191
+ #### Skip Decision Tree (when to NOT propose)
192
+
193
+ ```
194
+ A candidate signal surfaces (commit body or doc section).
195
+ ├─ Is it cosmetic only? ("fix typo", whitespace, formatting)
196
+ │ └─ YES → skip
197
+ ├─ Is the body just metadata? (Co-Authored-By, Signed-off-by, no prose)
198
+ │ └─ YES → skip
199
+ ├─ Is the same observation already covered by an init-scan baseline title (Phase 1 list)?
200
+ │ └─ YES → skip (don't re-propose what init already captured)
201
+ ├─ Does the observation fit one of {decisions, pitfalls, guidelines, models, processes}?
202
+ │ └─ NO → skip (not classifiable = not yet ripe)
203
+ ├─ Is the slug derivable as 2-5 kebab-case words?
204
+ │ └─ NO → skip (signal too vague for stable identifier)
205
+ └─ Else → propose via fab_extract_knowledge
206
+ ```
207
+
208
+ After Step 2.2 completes (or hits the cap), update `.fabric/.import-state.json`: `phase = "P2-done"`, `last_checkpoint_at = <ISO8601 now>`.
209
+
210
+ #### Dry-Run Mode
211
+
212
+ When the user invocation includes `dry-run` / `预览` / `--dry-run` keywords, Phase 2 runs WITHOUT calling `fab_extract_knowledge`. Instead it prints a table:
213
+
214
+ ```md
215
+ # Import Dry Run — would propose N pending entries (all relevance_scope=broad, relevance_paths=[])
216
+
217
+ | # | Source | Type | Slug | Scope | Summary (zh-CN) |
218
+ |---|-----------------------|-----------|-------------------------------|-------|------------------------------------------------------------|
219
+ | 1 | git c0a351d | decisions | layer-flip-id-mutation | broad+[] | layer 切换是唯一合法的 stable_id 变更途径,绑定原子事务。 |
220
+ | 2 | docs/architecture.md | decisions | monolith-over-microservices | broad+[] | 决定保留单体架构,三人团队不值微服务运维成本。 |
221
+ | 3 | git 50367b5 | pitfalls | thundering-herd-no-backoff | broad+[] | 重试无指数回退导致雪崩;必须 jittered exponential backoff。|
222
+ ```
223
+
224
+ 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.
225
+
226
+ ### Phase 3 — LLM-Driven Dedup vs Canonical
227
+
228
+ 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.**
229
+
230
+ #### Step 3.1 — Search Canonical of Same Type
231
+
232
+ For each just-proposed pending entry (read its frontmatter via the `Read` tool to get type + slug + title):
233
+
234
+ ```ts
235
+ mcp__fabric__fab_review({
236
+ action: "search",
237
+ query: "<title or summary keywords from the pending entry>",
238
+ filters: { type: "<same type as pending>" }
239
+ })
240
+ ```
241
+
242
+ The server returns ranked `items[]` of CANONICAL entries (not pending) of the same type. Cap the comparison set at the top 5 results.
243
+
244
+ #### Step 3.2 — Semantic Compare
245
+
246
+ For each `(pending, canonical)` pair the LLM judges:
247
+
248
+ - **Duplicate** — same essential claim. Heuristics: title keyword overlap >60%, summary asserts the same outcome with no novel evidence. Action: **reject** the new pending.
249
+ - **Subsumption** (pending narrower) — canonical fully covers the pending plus more. Action: **reject** the new pending (canonical already serves).
250
+ - **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.
251
+ - **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.
252
+ - **Genuinely new** — no canonical match. Action: leave pending in place (will surface in next `fabric-review` run for normal approval flow).
253
+
254
+ #### Step 3.3 — Issue Dedup MCP Calls
255
+
256
+ For each `reject`-classified pending:
257
+
258
+ ```ts
259
+ mcp__fabric__fab_review({
260
+ action: "reject",
261
+ pending_paths: ["<the new pending path>"],
262
+ reason: "duplicate of <stable_id of canonical>" // OR "subsumed by <stable_id>"
263
+ })
264
+ ```
265
+
266
+ For each `subsumption-with-novelty` case (modify canonical, then reject pending):
267
+
268
+ ```ts
269
+ // Step A: merge new evidence into canonical
270
+ mcp__fabric__fab_review({
271
+ action: "modify",
272
+ pending_path: "<canonical's pending_path-style relative path>",
273
+ changes: { summary: "<merged summary; original + new evidence cite>" }
274
+ })
275
+
276
+ // Step B: reject the now-superseded pending
277
+ mcp__fabric__fab_review({
278
+ action: "reject",
279
+ pending_paths: ["<the new pending path>"],
280
+ reason: "merged into <stable_id of modified canonical>"
281
+ })
282
+ ```
283
+
284
+ Append to `.fabric/.import-state.json` after EACH successful MCP call:
285
+
286
+ - `p3_dedup_completed[].push({pending_path: <new pending>, action: "reject" | "modify-then-reject" | "kept", canonical_ref: "<stable_id>" | null})`
287
+ - `last_checkpoint_at = <ISO8601 now>`
288
+
289
+ #### Step 3.4 — Phase 3 Completion
290
+
291
+ After all Phase 2 outputs are dedup-reviewed:
292
+
293
+ - 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}`.
294
+ - Render the final roll-up to the user (see Output Contract below).
295
+
296
+ > 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 下线。
297
+
298
+ 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.
299
+
300
+ ## Checkpoint Logic — `.fabric/.import-state.json`
301
+
302
+ The state file lives at `.fabric/.import-state.json` and is the single source of resumability for fabric-import. It is written via `atomicWriteJson` (write-temp-then-rename) so a crash between phases / between sub-steps never corrupts it.
303
+
304
+ ### Schema (all fields)
305
+
306
+ ```json
307
+ {
308
+ "phase": "P1-done | P2-done | complete",
309
+ "started_at": "<ISO8601 first invocation>",
310
+ "last_checkpoint_at": "<ISO8601 most recent successful sub-step>",
311
+ "p1_baseline_titles": ["<title1>", "<title2>"],
312
+ "p2_processed_commits": [
313
+ { "sha": "<full sha>", "skipped": true,
314
+ "skip_reason": "cosmetic | metadata-only | already-in-baseline | unclassifiable | overlong-slug" },
315
+ { "sha": "<full sha>", "skipped": false,
316
+ "pending_path": "knowledge/pending/<type>/<slug>.md",
317
+ "type": "<one of 5>", "slug": "<kebab-case-slug>" }
318
+ ],
319
+ "p2_processed_docs": [
320
+ { "path": "docs/<file>.md", "observations_proposed": 2,
321
+ "pending_paths": ["<path1>", "<path2>"] }
322
+ ],
323
+ "p2_cap_reached": false,
324
+ "p3_dedup_completed": [
325
+ { "pending_path": "<new pending path>",
326
+ "action": "reject | modify-then-reject | kept",
327
+ "canonical_ref": "<stable_id or null>" }
328
+ ],
329
+ "errors": [
330
+ { "step": "P2.git", "ref": "<commit sha or doc path>", "error": "<message>" }
331
+ ],
332
+ "final_summary": {
333
+ "proposed": 0, "kept": 0, "rejected_dup": 0, "merged": 0, "contradictions_flagged": 0
334
+ }
335
+ }
336
+ ```
337
+
338
+ ### Resume Logic (Idempotent Re-Invocation)
339
+
340
+ On every skill invocation, BEFORE Phase 1 starts:
341
+
342
+ 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).
343
+ 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`.
344
+ 3. If `phase === "complete"` AND `last_checkpoint_at ≥ 24h ago` → ask the user (free-text prompt, NOT AskUserQuestion since this is rare): "上次 import 已完成 (<N> 天前)。重新运行将基于当前 canonical 重做 P2/P3。继续?(y/n)"; if `n`, exit.
345
+ 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[]`.
346
+ 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[]`.
347
+ 6. After every successful sub-step (one commit processed, one doc processed, one dedup pair resolved), atomically write the updated state file. Failures append to `errors[]` and proceed (or halt with prompt if cumulative errors `>5`).
348
+
349
+ 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.
350
+
351
+ ## Default Behavior & Knobs
352
+
353
+ | Knob | Default | Override |
354
+ |-------------------------------------|-------------|----------------------------------------------------------------|
355
+ | Layer for new entries | `team` | User explicit instruction ("import these as personal") |
356
+ | `relevance_scope` for new entries | `broad` | NONE — contract-locked; narrowing deferred to `fab_review.modify` post-import |
357
+ | `relevance_paths` for new entries | `[]` | NONE — contract-locked; populating deferred to `fab_review.modify` post-import |
358
+ | Max new pending entries per P2 run | `10` | User explicit ("import up to 25"); skill caps at 50 hard |
359
+ | Git log window | `2 months` | User explicit ("import the full year") |
360
+ | Docs scan depth | `3` | User explicit ("scan docs/ recursively") |
361
+ | Dry-run mode | OFF | User keyword `dry-run` / `预览` / `--dry-run` |
362
+ | Re-run within 24h of complete | BLOCKED | User keyword `re-run import` / `reset import` |
363
+
364
+ ## Hard Rules — DISPLAY / WRITE Split
365
+
366
+ ### DISPLAY Rules
367
+
368
+ - MUST present every proposed pending entry with explicit `[type=...]`, `[layer=team]`, `[scope=broad]`, `slug=...`, AND `src=<commit-sha7 or doc-path>` so the user can audit the provenance and the (constant) scope.
369
+ - MUST display zh-CN body for proposed summaries (M3 style consistent with fabric-archive / fabric-review).
370
+ - MUST display EN section headings.
371
+ - MUST surface the resolved `pending_path` returned by `fab_extract_knowledge` in the per-entry display block.
372
+ - MUST display the final roll-up with proposed / kept / rejected_dup / merged / contradictions_flagged counts.
373
+ - MUST display the `.fabric/.import-state.json` `phase` value when the skill exits (so the user knows whether re-invocation is required).
374
+ - NEVER hide the source signal; provenance is the only audit trail for bulk-imported entries.
375
+ - NEVER classify a Phase 2 candidate as `personal` automatically — default `team` is contract-locked; only flip layer at the user's explicit instruction (and even then, do it post-import via fabric-review).
376
+ - NEVER show raw `idempotency_key` to the user (internal server-side concern).
377
+
378
+ ### WRITE Rules
379
+
380
+ - NEVER write a knowledge entry directly via `Edit` / `Write` / `Bash`; the only legal write paths are `mcp__fabric__fab_extract_knowledge` (Phase 2) and `mcp__fabric__fab_review` (Phase 3).
381
+ - NEVER batch multiple Phase 2 candidates into a single `fab_extract_knowledge` call; one call per candidate.
382
+ - NEVER skip the Phase 1 reference step — even if init-scan landed zero entries, the skill MUST verify `.fabric/agents.meta.json` is present.
383
+ - NEVER call `fab_review action="approve"` from this skill — promotion of pending → canonical is `fabric-review`'s concern, not import's. Imported entries land in `pending/` and wait for normal review flow.
384
+ - NEVER call `git mv` directly — layer flips during Phase 3 dedup go through `fab_review action="modify"` with `changes.layer`, which is a server-side transaction.
385
+ - NEVER infer a layer-flip target without explicit user instruction — fabric-import defaults `team`; if the user later wants `personal` for an entry, that's a `fabric-review` modify call, not an import-time decision.
386
+ - NEVER overwrite `.fabric/.import-state.json` non-atomically — use `atomicWriteJson` (write-temp-then-rename).
387
+ - NEVER exceed the 10-entry-per-run hard cap without explicit user override.
388
+ - NEVER pass `relevance_scope = "narrow"` to `fab_extract_knowledge` — every call from this skill MUST use `relevance_scope: "broad"`. No heuristic, no Agent judgment, no per-candidate override (see "Mandatory Scope Rule" in Phase 2).
389
+ - NEVER populate `relevance_paths` with a non-empty array on import — every call from this skill MUST pass `relevance_paths: []`. Do not derive paths from `git log --name-only`, `git show --stat`, commit subjects/bodies, or the path of a mined Markdown file.
390
+ - NEVER copy fabric-archive's Phase 1.5 scope-decision logic (narrow-vs-broad rules, public-prefix generalization, glob blacklist) into this skill — that logic requires a live `edit_paths` signal from an active session, which fabric-import does not have.
391
+ - Narrowing of imported entries happens out-of-band through `fab_review action="modify"` (issued by user via `fabric-review`), NOT inside this skill.
392
+ - MUST preserve protected tokens exactly: `stable_id`, `pending_path`, `layer`, `team`, `personal`, `knowledge_proposed`, `fab_extract_knowledge`, `fab_review`, `MUST`, `NEVER`, `phase`, `.import-state.json`, `relevance_scope`, `relevance_paths`, `broad`, `narrow`.
393
+
394
+ ## Output Contract
395
+
396
+ After Phase 3 completes (or on any phase exit due to cap / error / interrupt), the skill MUST produce a roll-up:
397
+
398
+ ```md
399
+ # Import Summary — phase=<P1-done | P2-done | complete>
400
+
401
+ ## Phase 2 — Mining
402
+ - Commits scanned: <N> (skipped: <S> — cosmetic/metadata/baseline-overlap)
403
+ - Docs scanned: <D> (skipped: <DS> — README/CHANGELOG/boilerplate)
404
+ - Pending proposed: <P> (cap_reached: <true|false>)
405
+ - Scope: all <P> proposed entries use relevance_scope=broad, relevance_paths=[] (fabric-import contract).
406
+
407
+ ## Phase 3 — Dedup
408
+ - Kept (genuinely new): <K>
409
+ - Rejected (duplicate): <RD>
410
+ - Modified-then-rejected: <MR> (canonical entries enriched: <list of stable_ids>)
411
+ - Contradictions flagged: <C> (require manual fabric-review)
412
+
413
+ ## State
414
+ - .fabric/.import-state.json phase: <phase>
415
+ - last_checkpoint_at: <ISO8601>
416
+ - Re-invoke to continue if phase != complete.
417
+
418
+ ## Next Steps
419
+ - Run `fabric-review` to approve the <K> kept pending entries.
420
+ - Resolve <C> contradictions manually if any.
421
+ - 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).
422
+ ```
423
+
424
+ 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).
425
+
426
+ ## Worked Examples
427
+
428
+ ### Example A — Phase 2 git mining: feat commit → pitfall entry
429
+
430
+ 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.
431
+
432
+ 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).
433
+
434
+ Skill output (note `relevance_scope: "broad"` + `relevance_paths: []` — mandatory for fabric-import):
435
+
436
+ ```ts
437
+ mcp__fabric__fab_extract_knowledge({
438
+ source_sessions: ["fabric-import-2026-05-10"],
439
+ recent_paths: ["packages/server/src/lib/retry.ts"], // provenance only
440
+ user_messages_summary: "重试无指数退避会在短暂上游故障下放大成雪崩。修正:jittered exponential backoff,30 秒上限。src=50367b5",
441
+ type: "pitfalls",
442
+ slug: "retry-without-backoff-thundering-herd",
443
+ relevance_scope: "broad", // MANDATORY
444
+ relevance_paths: [], // MANDATORY — do NOT infer ["packages/server/src/lib/retry.ts"]
445
+ proposed_reason: "new-dependency-or-pattern", // T6: import default
446
+ 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."
447
+ })
448
+ ```
449
+
450
+ Counter-example — DO NOT do this:
451
+
452
+ ```ts
453
+ // WRONG — this skill must never produce narrow + paths from git metadata.
454
+ // The retry pitfall applies to every retry site, not just the file touched by 50367b5.
455
+ mcp__fabric__fab_extract_knowledge({
456
+ // ...
457
+ relevance_scope: "narrow", // VIOLATION
458
+ relevance_paths: ["packages/server/src/lib/retry.ts"] // VIOLATION
459
+ })
460
+ ```
461
+
462
+ 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.
463
+
464
+ State file delta:
465
+ ```json
466
+ { "p2_processed_commits": [
467
+ { "sha": "50367b5...", "skipped": false,
468
+ "pending_path": "knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md",
469
+ "type": "pitfalls", "slug": "retry-without-backoff-thundering-herd" }
470
+ ]
471
+ }
472
+ ```
473
+
474
+ ### Example B — Phase 2 doc mining: architecture.md → decision entry
475
+
476
+ 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.
477
+
478
+ 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).
479
+
480
+ Skill output (broad+[] mandatory; the doc's own path stays in `recent_paths` for provenance, NOT in `relevance_paths`):
481
+
482
+ ```ts
483
+ mcp__fabric__fab_extract_knowledge({
484
+ source_session: "fabric-import-2026-05-10",
485
+ recent_paths: ["docs/architecture.md"], // provenance only
486
+ user_messages_summary: "选择单体架构而非微服务:3 人团队无法承担多服务运维成本,且主要性能瓶颈在 DB 吞吐而非应用层水平扩展。src=docs/architecture.md",
487
+ type: "decisions",
488
+ slug: "monolith-over-microservices-small-team",
489
+ relevance_scope: "broad", // MANDATORY
490
+ relevance_paths: [] // MANDATORY — a monolith-vs-microservices decision applies repo-wide, not only to docs/
491
+ })
492
+ ```
493
+
494
+ ### Example C — Phase 3 dedup finds duplicate, rejects
495
+
496
+ After Example A's pending entry (`retry-without-backoff-thundering-herd`) is proposed, Phase 3 runs:
497
+
498
+ ```ts
499
+ mcp__fabric__fab_review({
500
+ action: "search",
501
+ query: "retry backoff thundering herd",
502
+ filters: { type: "pitfalls" }
503
+ })
504
+ ```
505
+
506
+ 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).
507
+
508
+ Skill output:
509
+
510
+ ```ts
511
+ mcp__fabric__fab_review({
512
+ action: "reject",
513
+ pending_paths: ["knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md"],
514
+ reason: "duplicate of KT-P-0007"
515
+ })
516
+ ```
517
+
518
+ State file delta:
519
+ ```json
520
+ { "p3_dedup_completed": [
521
+ { "pending_path": "knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md",
522
+ "action": "reject", "canonical_ref": "KT-P-0007" }
523
+ ]
524
+ }
525
+ ```
526
+
527
+ Final roll-up to user reflects: 1 proposed, 0 kept, 1 rejected_dup, 0 merged, 0 contradictions.
528
+
529
+ ### Example D — Post-import narrowing (out-of-band, NOT this skill)
530
+
531
+ 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.
532
+
533
+ The user issues (via `fabric-review`, NOT via this skill):
534
+
535
+ ```ts
536
+ mcp__fabric__fab_review({
537
+ action: "modify",
538
+ pending_path: "knowledge/team/decisions/monolith-over-microservices-small-team.md",
539
+ changes: {
540
+ relevance_scope: "narrow",
541
+ relevance_paths: ["packages/server/**", "scripts/deploy/**"]
542
+ }
543
+ })
544
+ ```
545
+
546
+ Key invariants of this flow:
547
+
548
+ - The narrowing decision originates from the **user**, informed by the actual paths they propose — not from `fabric-import` inferring paths from git metadata.
549
+ - The modify call goes through `fab_review`, not `fab_extract_knowledge`, because the entry already exists (post-import or post-approval).
550
+ - 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.
551
+
552
+ ## Failure Recovery
553
+
554
+ - **Phase 2 mid-run failure** (e.g. `fab_extract_knowledge` errors on commit 5 of 10): state already records commits 1–4; rerun resumes at commit 5 by skipping any sha in `p2_processed_commits[]`. Error appended to `errors[]`.
555
+ - **Phase 3 mid-run failure** (e.g. `fab_review action="search"` MCP timeout on dedup pair 3 of 7): state records pairs 1–2 in `p3_dedup_completed[]`; rerun resumes at pair 3.
556
+ - **Cumulative `errors[].length > 5`**: skill halts; asks free-text "继续 (y) / 中止并保留 state (n)".
557
+ - **State file corruption**: skill renames it to `.fabric/.import-state.json.corrupt-<ISO8601>` and starts fresh from Phase 1.
558
+ - **MCP tool unreachable**: skill halts before any work; surfaces "MCP 工具未注册;请检查 fabric server 是否运行" and exits without writing state.
559
+
560
+ The skill is `Check-not-Ask` for recovery: it inspects state and resumes deterministically; it does NOT ask the user where to resume from.