@fenglimg/fabric-cli 2.0.0 → 2.1.0-rc.2

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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -5
  3. package/dist/chunk-BATF4PEJ.js +361 -0
  4. package/dist/{chunk-OBQU6NHO.js → chunk-COI5VDFU.js} +0 -18
  5. package/dist/chunk-F46ORPOA.js +903 -0
  6. package/dist/chunk-HFQVXY6P.js +86 -0
  7. package/dist/chunk-L4Q55UC4.js +52 -0
  8. package/dist/chunk-LFIKMVY7.js +27 -0
  9. package/dist/chunk-MF3OTILQ.js +544 -0
  10. package/dist/chunk-PWLW3B57.js +18 -0
  11. package/dist/chunk-RYAFBNES.js +33 -0
  12. package/dist/chunk-T5RPGCCM.js +40 -0
  13. package/dist/chunk-WU6GAPKH.js +36 -0
  14. package/dist/config-XJIPZNUP.js +13 -0
  15. package/dist/doctor-QVNPHLJK.js +920 -0
  16. package/dist/index.js +23 -8
  17. package/dist/{init-BIRSIOXO.js → install-2HDO5FTQ.js} +807 -705
  18. package/dist/metrics-ACEQFPDU.js +122 -0
  19. package/dist/onboard-coverage-MFCAEBDO.js +220 -0
  20. package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-FC6P3WFE.js} +34 -28
  21. package/dist/scope-explain-2F2R5URO.js +33 -0
  22. package/dist/status-GLQWLWH6.js +23 -0
  23. package/dist/store-XTSE5TY6.js +105 -0
  24. package/dist/sync-BJCWDPNC.js +245 -0
  25. package/dist/uninstall-TAXSUSKH.js +1073 -0
  26. package/dist/whoami-B6AEMSEV.js +31 -0
  27. package/package.json +30 -5
  28. package/templates/hooks/cite-policy-evict.cjs +231 -0
  29. package/templates/hooks/configs/README.md +29 -6
  30. package/templates/hooks/configs/claude-code.json +14 -3
  31. package/templates/hooks/configs/codex-hooks.json +6 -3
  32. package/templates/hooks/configs/cursor-hooks.json +8 -10
  33. package/templates/hooks/fabric-hint.cjs +873 -105
  34. package/templates/hooks/knowledge-hint-broad.cjs +549 -135
  35. package/templates/hooks/knowledge-hint-narrow.cjs +830 -26
  36. package/templates/hooks/lib/banner-i18n.cjs +309 -0
  37. package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
  38. package/templates/hooks/lib/cite-contract-reminder.cjs +179 -0
  39. package/templates/hooks/lib/cite-line-parser.cjs +180 -0
  40. package/templates/hooks/lib/client-adapter.cjs +106 -0
  41. package/templates/hooks/lib/config-cache.cjs +107 -0
  42. package/templates/hooks/lib/state-store.cjs +84 -0
  43. package/templates/hooks/lib/summary-fallback.cjs +210 -0
  44. package/templates/skills/fabric-archive/SKILL.md +97 -419
  45. package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
  46. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
  47. package/templates/skills/fabric-archive/ref/i18n-policy.md +86 -0
  48. package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
  49. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +218 -0
  50. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +62 -0
  51. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +68 -0
  52. package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +108 -0
  53. package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
  54. package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
  55. package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
  56. package/templates/skills/fabric-archive/ref/rc-history.md +38 -0
  57. package/templates/skills/fabric-archive/ref/worked-examples.md +78 -0
  58. package/templates/skills/fabric-import/SKILL.md +77 -514
  59. package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
  60. package/templates/skills/fabric-import/ref/i18n-policy.md +79 -0
  61. package/templates/skills/fabric-import/ref/output-contract.md +61 -0
  62. package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
  63. package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
  64. package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
  65. package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
  66. package/templates/skills/fabric-review/SKILL.md +90 -284
  67. package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
  68. package/templates/skills/fabric-review/ref/i18n-policy.md +111 -0
  69. package/templates/skills/fabric-review/ref/modify-flow.md +103 -0
  70. package/templates/skills/fabric-review/ref/output-contract.md +58 -0
  71. package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
  72. package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
  73. package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
  74. package/templates/skills/fabric-sync/SKILL.md +46 -0
  75. package/templates/skills/lib/shared-policy.md +69 -0
  76. package/dist/chunk-6ICJICVU.js +0 -10
  77. package/dist/chunk-74SZWYPH.js +0 -658
  78. package/dist/chunk-EYIDD2YS.js +0 -1000
  79. package/dist/doctor-T7JWODKG.js +0 -282
  80. package/dist/hooks-Y74Y5LQS.js +0 -12
  81. package/dist/scan-LMK3UCWL.js +0 -22
  82. package/dist/serve-H554BHLG.js +0 -124
  83. package/templates/agents-md/AGENTS.md.template +0 -59
  84. package/templates/bootstrap/CLAUDE.md +0 -8
  85. package/templates/bootstrap/codex-AGENTS-header.md +0 -6
  86. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
@@ -0,0 +1,85 @@
1
+ # Checkpoint Logic — `.fabric/.import-state.json` (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains a 2-line mention of the 2-step atomic write pattern + a pointer to this file. This file holds the full Atomic State Write rationale, events.jsonl 4KB constraint, complete schema, and resume-logic state machine.
4
+
5
+ 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.
6
+
7
+ ## Atomic State Write (2-step pattern)
8
+
9
+ **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):
10
+
11
+ - **Step A**: `Write` tool → `.fabric/.import-state.json.tmp` (full JSON content; never partial / never appended).
12
+ - **Step B**: `Bash` → `mv .fabric/.import-state.json.tmp .fabric/.import-state.json`.
13
+
14
+ 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.
15
+
16
+ Crash safety expectations:
17
+
18
+ - Crash between Step A and Step B → leaves `.fabric/.import-state.json.tmp`. Phase 0 residue scan (see `ref/state-recovery.md`) triages it on next invocation.
19
+ - 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.
20
+ - Crash before Step A → no state mutation occurred; prior state file is unchanged.
21
+
22
+ 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.
23
+
24
+ ## events.jsonl Constraint Note
25
+
26
+ Event lines appended to `.fabric/events.jsonl` are subject to POSIX single-write atomicity: only writes ≤ 4KB (`PIPE_BUF`) are guaranteed atomic via `Bash: echo "..." >> file`. Lines exceeding 4KB risk interleaved corruption under concurrent skill + server writes to the same ledger.
27
+
28
+ Skills MUST ensure:
29
+
30
+ - Each event JSON line is a **single line** (no embedded newlines; escape `\n` in any string value).
31
+ - `session_context` and other free-form text fields **self-truncate** to keep the entire serialized line under 4KB. Suggested per-field caps: `session_context` first 500 chars; `source_sessions` cap at 5 entries; `recent_paths` cap at 20 entries; `user_messages_summary` first 500 chars.
32
+ - If approaching the 4KB ceiling after the per-field caps, drop optional fields (e.g. tags / extra metadata) **before** truncating semantic content.
33
+ - This constraint applies to any event the skill itself appends; MCP-server-side appends (via `appendEventLedgerEvent`) are already line-length-bounded server-side.
34
+
35
+ ## Schema (all fields)
36
+
37
+ ```json
38
+ {
39
+ "phase": "P1-done | P2-done | complete",
40
+ "started_at": "<ISO8601 first invocation>",
41
+ "last_checkpoint_at": "<ISO8601 most recent successful sub-step>",
42
+ "p1_baseline_titles": ["<title1>", "<title2>"],
43
+ "p2_processed_commits": [
44
+ { "sha": "<full sha>", "skipped": true,
45
+ "skip_reason": "cosmetic | metadata-only | already-in-baseline | unclassifiable | overlong-slug" },
46
+ { "sha": "<full sha>", "skipped": false,
47
+ "pending_path": "knowledge/pending/<type>/<slug>.md",
48
+ "type": "<one of 5>", "slug": "<kebab-case-slug>" }
49
+ ],
50
+ "p2_processed_docs": [
51
+ { "path": "docs/<file>.md", "observations_proposed": 2,
52
+ "pending_paths": ["<path1>", "<path2>"] }
53
+ ],
54
+ "p2_cap_reached": false,
55
+ "p3_dedup_completed": [
56
+ { "pending_path": "<new pending path>",
57
+ "action": "reject | modify-then-reject | kept",
58
+ "canonical_ref": "<stable_id or null>" }
59
+ ],
60
+ "errors": [
61
+ { "step": "P2.git", "ref": "<commit sha or doc path>", "error": "<message>" }
62
+ ],
63
+ "final_summary": {
64
+ "proposed": 0, "kept": 0, "rejected_dup": 0, "merged": 0, "contradictions_flagged": 0
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## Resume Logic (Idempotent Re-Invocation)
70
+
71
+ On every skill invocation, BEFORE Phase 1 starts:
72
+
73
+ 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).
74
+ 2. If `phase === "complete"` AND `last_checkpoint_at < 24h ago` → SKIP this invocation (precondition warning) unless user explicitly typed `re-run import` or `reset import`.
75
+ 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:
76
+
77
+ - zh-CN: `上次 import 已完成 (<N> 天前)。重新运行将基于当前 canonical 重做 P2/P3。继续?(y/n)`
78
+ - en: `Last import completed (<N> days ago). Re-running will redo P2/P3 against the current canonical set. Continue? (y/n)`
79
+
80
+ If `n`, exit.
81
+ 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[]`.
82
+ 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[]`.
83
+ 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. Failures append to `errors[]` and proceed (or halt with prompt if cumulative errors `>5`).
84
+
85
+ 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.
@@ -0,0 +1,79 @@
1
+ # UX i18n Policy — fabric-import full reference
2
+
3
+ > **Shared core (rc.37 NEW-13):** the cross-skill invariants — protected-token
4
+ > NEVER-translate list, AskUserQuestion routing-key rule, layer heuristic, and
5
+ > events-emit convention — live once in `../../lib/shared-policy.md`. This file
6
+ > keeps only the fabric-import-specific 5-class examples. Read the shared lib
7
+ > for the common rules; do not fork them here.
8
+
9
+ > **Loaded on demand.** Only consult when you need to disambiguate which of the 5 classes a given string belongs to. SKILL.md gives the operative rule.
10
+
11
+ ## UX i18n Policy (5-class bilingualization)
12
+
13
+ The skill consults `fabric_language` from `.fabric/fabric-config.json`
14
+ (固化于 install 时,via `scan.ts:detectExistingLanguage`; default `"en"` when no
15
+ CJK signal is detected in README + docs/; may resolve to `"match-existing"`,
16
+ `"zh-CN"`, `"en"`, or `"zh-CN-hybrid"`). All user-facing text in the
17
+ following 5 categories MUST be rendered in the resolved language:
18
+
19
+ 1. **Roll-up templates** — final summary blocks (`# Import Summary — phase=...`,
20
+ `## Phase 2 — Mining`, `## Phase 3 — Dedup`, etc.). zh-CN ↔ en mirror.
21
+ 2. **Errors / Preconditions warnings** — abort + gate-fail messages (e.g.
22
+ "请先运行 fabric install 完成基线扫描…" / "Please run fabric install first…").
23
+ zh-CN ↔ en mirror.
24
+ 3. **Confirmation prompts** — re-run-within-24h prompt, reset prompts, etc.
25
+ zh-CN ↔ en mirror.
26
+ 4. **Dry-run table headers** — `# Import Dry Run — would propose N pending
27
+ entries…` + the `| # | Source | Type | Slug | Scope | Summary |` header row.
28
+ zh-CN ↔ en mirror.
29
+ 5. **AskUserQuestion** — `header` + `question` fields (NOT `options[]`).
30
+ zh-CN ↔ en mirror. fabric-import itself does not surface AskUserQuestion
31
+ in the current contract (the rare re-run prompt is free-text), but if a
32
+ future version adds one, this rule applies.
33
+
34
+ Rendering rule:
35
+
36
+ - `fabric_language === "zh-CN"` → emit the zh-CN variant; pure monolingual, no language mixing inside a single user-facing block.
37
+ - `fabric_language === "en"` → emit the en variant; pure monolingual, no language mixing inside a single user-facing block.
38
+ - `fabric_language === "zh-CN-hybrid"` → emit Chinese narrative prose with English technical terms preserved. Protected tokens (always EN): MCP tool names (e.g. `fab_get_knowledge_sections`), CLI command names (e.g. `fabric install`), file paths, technical concepts (`Skill`, `SessionStart`, `hook`, `MCP`, `revision_hash`, `pending`, `proven`, `verified`, `draft`).
39
+ - `fabric_language === "match-existing"` or any other value → emit the en variant; pure monolingual.
40
+
41
+ Protected tokens (`fab_extract_knowledge`, `fab_review`, `relevance_scope`,
42
+ `relevance_paths`, `broad`, `narrow`, `source_sessions`, `proposed_reason`,
43
+ `session_context`, `intent_clues`, `tech_stack`, `impact`, `must_read_if`,
44
+ `pending_path`, `layer`, `team`, `personal`,
45
+ `knowledge_scope_degraded`, `MUST`, `NEVER`, `.fabric/knowledge/`, etc.)
46
+ are NEVER translated — they appear verbatim in both language variants.
47
+ The bilingualization scope is prose ONLY.
48
+
49
+ ### AskUserQuestion i18n Policy (value vs label)
50
+
51
+ When a skill (this one or any sibling skill the user is composing with)
52
+ issues an `AskUserQuestion`, the `header` and `question` strings are
53
+ user-facing prose → translated per `fabric_language`. The `options[]`
54
+ array entries (e.g. `["approve", "reject", "modify", "defer", "skip"]` in
55
+ fabric-review) are **routing keys** consumed by the skill state machine —
56
+ they MUST remain English regardless of `fabric_language`.
57
+
58
+ ```ts
59
+ // EN (fabric_language === "en")
60
+ AskUserQuestion({
61
+ header: "Review pending entry",
62
+ question: "What action for '{title}'?",
63
+ options: ["approve", "reject", "modify", "defer", "skip"]
64
+ })
65
+
66
+ // zh-CN (fabric_language === "zh-CN")
67
+ AskUserQuestion({
68
+ header: "审核 pending 条目",
69
+ question: "对 '{title}' 执行什么操作?",
70
+ options: ["approve", "reject", "modify", "defer", "skip"] // 不翻译 — routing key
71
+ })
72
+ ```
73
+
74
+ Rationale: localizing routing keys would force every routing branch to
75
+ dual-string match (e.g. `if (choice === "approve" || choice === "通过")`),
76
+ which doubles the surface area for protected-token regressions and breaks
77
+ the option-list invariants that downstream tooling depends on. Keeping
78
+ `options[]` English-only is contract-locked across all three skills.
79
+
@@ -0,0 +1,61 @@
1
+ # Output Contract (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path mentions the roll-up requirement + section names. This file holds the full bilingual roll-up templates rendered after Phase 3 completes (or on any phase exit due to cap / error / interrupt).
4
+
5
+ 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.
6
+
7
+ ## en variant (`fabric_language === "en"`)
8
+
9
+ ```md
10
+ # Import Summary — phase=<P1-done | P2-done | complete>
11
+
12
+ ## Phase 2 — Mining
13
+ - Commits scanned: <N> (skipped: <S> — cosmetic/metadata/baseline-overlap)
14
+ - Docs scanned: <D> (skipped: <DS> — README/CHANGELOG/boilerplate)
15
+ - Pending proposed: <P> (cap_reached: <true|false>)
16
+ - Scope: all <P> proposed entries use relevance_scope=broad, relevance_paths=[] (fabric-import contract).
17
+
18
+ ## Phase 3 — Dedup
19
+ - Kept (genuinely new): <K>
20
+ - Rejected (duplicate): <RD>
21
+ - Modified-then-rejected: <MR> (canonical entries enriched: <list of stable_ids>)
22
+ - Contradictions flagged: <C> (require manual fabric-review)
23
+
24
+ ## State
25
+ - .fabric/.import-state.json phase: <phase>
26
+ - last_checkpoint_at: <ISO8601>
27
+ - Re-invoke to continue if phase != complete.
28
+
29
+ ## Next Steps
30
+ - Run `fabric-review` to approve the <K> kept pending entries.
31
+ - Resolve <C> contradictions manually if any.
32
+ - 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).
33
+ ```
34
+
35
+ ## zh-CN variant (`fabric_language === "zh-CN"`)
36
+
37
+ ```md
38
+ # Import 汇总 — phase=<P1-done | P2-done | complete>
39
+
40
+ ## Phase 2 — 挖掘
41
+ - 扫描 commit 数: <N> (跳过: <S> — cosmetic/metadata/与 baseline 重叠)
42
+ - 扫描文档数: <D> (跳过: <DS> — README/CHANGELOG/样板文件)
43
+ - 提议 pending: <P> (cap_reached: <true|false>)
44
+ - 作用域: 全部 <P> 条提议使用 relevance_scope=broad, relevance_paths=[] (fabric-import 契约)。
45
+
46
+ ## Phase 3 — 去重
47
+ - 保留 (新知识): <K>
48
+ - 已驳回 (重复): <RD>
49
+ - 修改后驳回: <MR> (被合入 evidence 的 canonical 条目: <stable_ids 列表>)
50
+ - 已标记冲突: <C> (需手动通过 fabric-review 解决)
51
+
52
+ ## 状态
53
+ - .fabric/.import-state.json phase: <phase>
54
+ - last_checkpoint_at: <ISO8601>
55
+ - 如 phase != complete, 请重新调用 fabric-import 续作。
56
+
57
+ ## 下一步
58
+ - 运行 `fabric-review` 审批 <K> 条新 pending。
59
+ - 手动解决 <C> 条 contradictions 标记 (如有)。
60
+ - 若某条 kept 条目实际是 narrow-scoped, 通过 `fab_review action="modify"` 配 `changes.relevance_scope="narrow"` + `changes.relevance_paths=[...]` 收窄 (本 skill 无法收窄 — 见 Phase 2 Mandatory Scope Rule)。
61
+ ```
@@ -0,0 +1,213 @@
1
+ # Phase 2 — LLM-Driven Git + Doc Mining (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the broad+[] contract one-liner, brief Step 2.1/2.2 summaries, and a pointer to this file. This file holds: full Mandatory Scope Rule rationale + strict prohibitions, Step 2.1 git mining call shape + 6 conventional-commit signals, Step 2.1.5 Proposed Reason Inference table, Step 2.2 docs mining filter list + call shape, Skip Decision Tree, bilingual dry-run preview templates, and T5 array-form idempotency notes.
4
+
5
+ ## Mandatory Scope Rule — Always Broad + Empty Paths (Q-1 Resolution)
6
+
7
+ **EVERY `fab_extract_knowledge` call issued from this skill MUST set:**
8
+
9
+ - `relevance_scope = "broad"`
10
+ - `relevance_paths = []`
11
+
12
+ 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.
13
+
14
+ **Rationale — why fabric-import cannot bind paths from git history:**
15
+
16
+ 1. `fabric-import` is LLM-driven (mines git log + docs), not session-driven (no live `edit_paths` signal).
17
+ 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.
18
+ 3. LLM-inferred `relevance_paths` from historical commit metadata produces false-narrow bindings — `relevance_paths` becomes a lie about applicability. Post-rc.37 A1 the server no longer filters by `relevance_scope`, so false-narrow does NOT hide knowledge from AI recall (every selectable entry is surfaced regardless of scope). The damage is now downstream: doctor lint accounting, future-AI judgment, and any consumer that reads `relevance_paths` literally treats the wrong globs as ground truth. Broad+[] keeps the metadata honest until the user has the real applicability surface in hand to declare narrow.
19
+ 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/`).
20
+
21
+ **Strict prohibitions — DO NOT attempt any of the following:**
22
+
23
+ - DO NOT derive `relevance_paths` from `git log --name-only` / `git show --stat` / `git diff` file lists.
24
+ - 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/**"]`).
25
+ - DO NOT extract path-shaped tokens from commit subjects / bodies / doc text and lift them into `relevance_paths`.
26
+ - DO NOT classify a candidate as `relevance_scope = "narrow"` under ANY heuristic.
27
+ - DO NOT copy the public-prefix-generalization logic from fabric-archive Phase 3.5 — that logic is valid only when bound to a real-time `edit_paths` signal from an active session, which fabric-import lacks.
28
+
29
+ **Cross-reference — fabric-import vs fabric-archive scope handling:**
30
+
31
+ | Skill | Scope decision | Why |
32
+ |------------------|--------------------|-----------------------------------------------------------------------|
33
+ | `fabric-archive` | narrow OR broad, case-by-case per Phase 3.5 rules | Has live `edit_paths` from the active session — the actual applicability surface. |
34
+ | `fabric-import` | ALWAYS broad + `[]` (this skill) | LLM-only, no live session signal; git-history paths are effect-surface, not applicability-surface. |
35
+
36
+ `fabric-archive`'s Phase 3.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.
37
+
38
+ **Post-import narrowing path — deferred to user, via `fab_review.modify`:**
39
+
40
+ 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:
41
+
42
+ ```ts
43
+ mcp__fabric__fab_review({
44
+ action: "modify",
45
+ pending_path: "<the imported pending or its post-approval canonical path>",
46
+ changes: {
47
+ relevance_scope: "narrow",
48
+ relevance_paths: ["packages/server/src/retry/**", "packages/server/src/lib/retry.ts"]
49
+ }
50
+ })
51
+ ```
52
+
53
+ 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.
54
+
55
+ **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.
56
+
57
+ ## Step 2.1 — Git Log Mining
58
+
59
+ Bash command (executed via the `Bash` tool — substitute `<window>` and `<commits-cap>` with values resolved from Phase 0.5 config load):
60
+
61
+ ```bash
62
+ git log --since="<window> months ago" --pretty=format:"%H%n%s%n%b%n---ENDCOMMIT---" -n <commits-cap>
63
+ ```
64
+
65
+ - `<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.
66
+ - `<commits-cap>` resolves to `import_max_commits_scan` (default 500).
67
+
68
+ 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.
69
+
70
+ For each commit:
71
+
72
+ 1. Inspect the conventional-commit prefix in the subject line. Strong signals:
73
+ - `feat(...)` with a non-empty body → likely **decision** or **model** (a new capability landed; the body usually explains why)
74
+ - `fix(...)` with body length >100 chars → likely **pitfall** (a bug worth diagnosing was non-trivial)
75
+ - `refactor(...)` with body → likely **decision** (architectural choice was made)
76
+ - `docs(...)` → usually a **guideline** if the body announces a convention; skip if it's just typo/reformat
77
+ - `chore(...)`, `test(...)`, `ci(...)` → almost always skip (mechanical; no reusable insight)
78
+ 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).
79
+ 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.
80
+ 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):
81
+
82
+ ```ts
83
+ mcp__fabric__fab_extract_knowledge({
84
+ source_sessions: ["fabric-import-<ISO8601-date>"], // T5: array form; stable per import run
85
+ recent_paths: ["<files touched by this commit, capped at 20>"], // provenance only, NOT a path-binding signal
86
+ user_messages_summary: "<zh-CN 1-2 sentence summary of the commit's core observation; cite the commit sha as 'src=<sha7>'>",
87
+ type: "decisions" | "pitfalls" | "guidelines" | "models" | "processes",
88
+ slug: "<kebab-case 2-5 words derived from commit subject + body>",
89
+ relevance_scope: "broad", // MANDATORY — never "narrow" from fabric-import
90
+ relevance_paths: [], // MANDATORY — never derived from git history
91
+ proposed_reason: "<inferred per Step 2.1.5 — varies>",
92
+ session_context: "Imported from git log analysis. Origin: commit <sha7> (<subject 30 chars>). No live session — see commit body for full context.",
93
+ // rc.23 TASK-006 four OPTIONAL triage fields — populate from commit body when clean, omit otherwise.
94
+ intent_clues: ["<inferred trigger if commit body suggests one>"],
95
+ tech_stack: ["<lang/framework from recent_paths extensions>"],
96
+ impact: ["<consequence stated in commit body / doc>"],
97
+ must_read_if: "<one-line strongest trigger from commit's touched-path family>"
98
+ })
99
+ ```
100
+
101
+ Note: `recent_paths` carries the touched-file list for **provenance display** only. It is NOT lifted into `relevance_paths` — those two fields serve different purposes and the prohibition on path inference from git history applies.
102
+
103
+ 5. On success the server returns `{pending_path, idempotency_key}`. Append to `.fabric/.import-state.json`:
104
+ - `p2_processed_commits[].push({sha: <full sha>, skipped: false, pending_path, type, slug})`
105
+ - `last_checkpoint_at = <ISO8601 now>`
106
+ Update is atomic via the 2-step `.tmp` + `mv` pattern documented in the **Atomic State Write** section under "Checkpoint Logic" below.
107
+ 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.
108
+
109
+ ## Step 2.1.5 — Proposed Reason Inference (rc.7 T6)
110
+
111
+ For each non-skipped commit OR doc section, infer `proposed_reason` from prefix + body signal jointly. The 6 reasons below are the full enum accepted by `fab_extract_knowledge` (schema-locked):
112
+
113
+ | Source signal | Body cue | Inferred reason |
114
+ |---|---|---|
115
+ | `feat(...)` commit | "vs" / "instead of" / "chose" / "rejected X for Y" | `decision-confirmation` |
116
+ | `feat(...)` commit | Announces new dep/lib/abstraction, no alternative cited | `new-dependency-or-pattern` |
117
+ | `fix(...)` commit | Cites wrong direction tried + reverted | `wrong-turn-revert` |
118
+ | `fix(...)` commit | Cites long diagnostic chain → root cause | `diagnostic-then-fix` |
119
+ | `refactor(...)` commit | Cites structural rationale (without "vs" alternatives) | `decision-confirmation` |
120
+ | `docs(...)` commit | Announces convention ("always X" / "never Y") | `explicit-user-mark` |
121
+ | Any commit | Body explicitly rejects an approach + states why | `dismissal-with-reason` |
122
+ | Doc section | "Why we chose X over Y" heading | `decision-confirmation` |
123
+ | Doc section | "Don't do Y because..." section | `dismissal-with-reason` |
124
+ | Doc section | "Always" / "Never" guidelines | `explicit-user-mark` |
125
+ | Doc section | Architecture/design narrative (descriptive, no choice rationale) | `new-dependency-or-pattern` |
126
+
127
+ **Edge cases:**
128
+
129
+ - `chore(` / `test(` / `ci(` should already be skipped per the Skip Decision Tree below; if they slip through, default to `new-dependency-or-pattern`.
130
+ - Ambiguous signals: prefer the reason matching **body content** over **prefix** (a `feat(` with strong revert-language is `wrong-turn-revert`, not `new-dependency-or-pattern`).
131
+
132
+ **Fallback**: when no row clearly applies, use `new-dependency-or-pattern` (the broadest "noticed something new" semantic).
133
+
134
+ ## Step 2.2 — Docs Mining
135
+
136
+ Bash command:
137
+
138
+ ```bash
139
+ find docs/ -maxdepth 3 -name '*.md' -type f 2>/dev/null
140
+ ls -1 *.md 2>/dev/null # root-level architectural docs
141
+ ```
142
+
143
+ For each Markdown file:
144
+
145
+ 1. **Skip filter**:
146
+ - `README.md` → skip (its first paragraph already lives in init-scan; body too generic for fine-grained classification)
147
+ - `CHANGELOG.md` → skip (rendered from commit log; mining commits already covers it)
148
+ - `LICENSE.md`, `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md` → skip (boilerplate)
149
+ - Files <300 bytes → skip (too thin to extract meaningful observations)
150
+ 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.
151
+ 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.
152
+ 4. Append to `.fabric/.import-state.json`:
153
+ - `p2_processed_docs[].push({path: <doc path>, observations_proposed: <count>, pending_paths: [...]})`
154
+ 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.
155
+
156
+ ## Skip Decision Tree
157
+
158
+ ```
159
+ A candidate signal surfaces (commit body or doc section).
160
+ ├─ Is it cosmetic only? ("fix typo", whitespace, formatting)
161
+ │ └─ YES → skip
162
+ ├─ Is the body just metadata? (Co-Authored-By, Signed-off-by, no prose)
163
+ │ └─ YES → skip
164
+ ├─ Is the same observation already covered by an init-scan baseline title (Phase 1 list)?
165
+ │ └─ YES → skip (don't re-propose what init already captured)
166
+ ├─ Does the observation fit one of {decisions, pitfalls, guidelines, models, processes}?
167
+ │ └─ NO → skip (not classifiable = not yet ripe)
168
+ ├─ Is the slug derivable as 2-5 kebab-case words?
169
+ │ └─ NO → skip (signal too vague for stable identifier)
170
+ └─ Else → propose via fab_extract_knowledge
171
+ ```
172
+
173
+ After Step 2.2 completes (or hits the cap), update `.fabric/.import-state.json`: `phase = "P2-done"`, `last_checkpoint_at = <ISO8601 now>`.
174
+
175
+ ## Dry-Run Mode
176
+
177
+ When the user invocation carries the verbatim token `--dry-run`, Phase 2 runs WITHOUT calling `fab_extract_knowledge`. Instead it prints a table. v2.0.0-rc.37 NEW-10 dropped the legacy substring fallback on bare `dry-run` / `预览` because those caused false positives on incidental mentions ("preview the table" / "do a dry run later"). UX i18n Policy class 4 — header + column titles bilingualized; row content (slug / commit sha / doc path) NOT translated. Protected tokens `broad`, `relevance_scope`, `relevance_paths` appear verbatim:
178
+
179
+ ### zh-CN variant (`fabric_language === "zh-CN"`)
180
+
181
+ ```md
182
+ # Import 预览 — 将提议 N 条 pending 条目(全部 relevance_scope=broad, relevance_paths=[])
183
+
184
+ | # | 来源 | 类型 | Slug | 作用域 | 摘要 (zh-CN) |
185
+ |---|-----------------------|-----------|-------------------------------|--------|-------------------------------------------------------------|
186
+ | 1 | git c0a351d | decisions | layer-flip-id-mutation | broad+[] | layer 切换是唯一合法的 stable_id 变更途径,绑定原子事务。 |
187
+ | 2 | docs/architecture.md | decisions | monolith-over-microservices | broad+[] | 决定保留单体架构,三人团队不值微服务运维成本。 |
188
+ | 3 | git 50367b5 | pitfalls | thundering-herd-no-backoff | broad+[] | 重试无指数回退导致雪崩;必须 jittered exponential backoff。|
189
+ ```
190
+
191
+ ### en variant (`fabric_language === "en"`)
192
+
193
+ ```md
194
+ # Import Dry Run — would propose N pending entries (all relevance_scope=broad, relevance_paths=[])
195
+
196
+ | # | Source | Type | Slug | Scope | Summary |
197
+ |---|-----------------------|-----------|-------------------------------|----------|---------------------------------------------------------------|
198
+ | 1 | git c0a351d | decisions | layer-flip-id-mutation | broad+[] | Layer change is the only legal stable_id mutation path; atomic txn. |
199
+ | 2 | docs/architecture.md | decisions | monolith-over-microservices | broad+[] | Keep the monolith — 3-engineer team can't justify microservice ops. |
200
+ | 3 | git 50367b5 | pitfalls | thundering-herd-no-backoff | broad+[] | Retries without exponential backoff caused a thundering herd outage. |
201
+ ```
202
+
203
+ Every dry-run row MUST show `broad+[]` in the Scope column (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.
204
+
205
+ ## Idempotency Note — T5 array form
206
+
207
+ 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.
208
+
209
+ **T5 array-form note (rc.7+)**: when `source_sessions` is passed as an array (rc.7 T5 contract), only `source_sessions[0]` participates in the server-side idempotency hash. 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:
210
+
211
+ - Every Phase 2 call uses `source_sessions: ["fabric-import-<ISO8601-date>"]` (single-element array, stable per import run). First-element-only rule means re-runs on the same date produce the same idempotency key per `(type, slug)` → resume-safe by construction.
212
+ - If a future enhancement adds a trailing element (e.g. `["fabric-import-<date>", "<commit-sha>"]`), only the first element participates in the hash — the commit-sha tail would NOT change the idempotency key for the same `(type, slug)`. Plan accordingly.
213
+ - 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.
@@ -0,0 +1,75 @@
1
+ # Phase 3 — LLM-Driven Dedup vs Canonical (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the Phase 3 purpose statement + 4-step outline + completion sentinel. This file holds the full Step 3.1/3.2/3.3/3.4 MCP call shapes, semantic compare 5-way classification, and the underseed sentinel rationale.
4
+
5
+ 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.**
6
+
7
+ ## Step 3.1 — Search Canonical of Same Type
8
+
9
+ For each just-proposed pending entry (read its frontmatter via the `Read` tool to get type + slug + title):
10
+
11
+ ```ts
12
+ mcp__fabric__fab_review({
13
+ action: "search",
14
+ query: "<title or summary keywords from the pending entry>",
15
+ filters: { type: "<same type as pending>" }
16
+ })
17
+ ```
18
+
19
+ The server returns ranked `items[]` of CANONICAL entries (not pending) of the same type. Cap the comparison set at the top 5 results.
20
+
21
+ ## Step 3.2 — Semantic Compare
22
+
23
+ For each `(pending, canonical)` pair the LLM judges:
24
+
25
+ - **Duplicate** — same essential claim. LLM 主观判断:标题与摘要表达同一核心结论,新 pending 未提供新证据。具体阈值不可量化。Action: **reject** the new pending.
26
+ - **Subsumption** (pending narrower) — canonical fully covers the pending plus more. Action: **reject** the new pending (canonical already serves).
27
+ - **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.
28
+ - **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.
29
+ - **Genuinely new** — no canonical match. Action: leave pending in place (will surface in next `fabric-review` run for normal approval flow).
30
+
31
+ ## Step 3.3 — Issue Dedup MCP Calls
32
+
33
+ For each `reject`-classified pending:
34
+
35
+ ```ts
36
+ mcp__fabric__fab_review({
37
+ action: "reject",
38
+ pending_paths: ["<the new pending path>"],
39
+ reason: "duplicate of <stable_id of canonical>" // OR "subsumed by <stable_id>"
40
+ })
41
+ ```
42
+
43
+ For each `subsumption-with-novelty` case (modify canonical, then reject pending):
44
+
45
+ ```ts
46
+ // Step A: merge new evidence into canonical
47
+ mcp__fabric__fab_review({
48
+ action: "modify",
49
+ pending_path: "<canonical's pending_path-style relative path>",
50
+ changes: { summary: "<merged summary; original + new evidence cite>" }
51
+ })
52
+
53
+ // Step B: reject the now-superseded pending
54
+ mcp__fabric__fab_review({
55
+ action: "reject",
56
+ pending_paths: ["<the new pending path>"],
57
+ reason: "merged into <stable_id of modified canonical>"
58
+ })
59
+ ```
60
+
61
+ Append to `.fabric/.import-state.json` after EACH successful MCP call:
62
+
63
+ - `p3_dedup_completed[].push({pending_path: <new pending>, action: "reject" | "modify-then-reject" | "kept", canonical_ref: "<stable_id>" | null})`
64
+ - `last_checkpoint_at = <ISO8601 now>`
65
+
66
+ ## Step 3.4 — Phase 3 Completion
67
+
68
+ After all Phase 2 outputs are dedup-reviewed:
69
+
70
+ - 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}`.
71
+ - Render the final roll-up to the user (see Output Contract — see `ref/output-contract.md`).
72
+
73
+ > 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 下线。
74
+
75
+ 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.
@@ -0,0 +1,57 @@
1
+ # Phase 0 + 0.1 — State Recovery (fabric-import ref-only)
2
+
3
+ > **Loaded on demand.** Only relevant when a prior import crashed mid-phase, leaving `.tmp-*` residue or a torn `.fabric/.import-state.json`. SKILL.md's hot path inline says "scan for .tmp residue + load state" — this file is the full recovery procedure (atomic-write pattern, sweep rules, corruption detection, fallback to fresh import).
4
+
5
+ ## Phase 0 — Init & .tmp Residue Scan
6
+
7
+ Before reading `.fabric/.import-state.json`, scan for residue left by a
8
+ prior crashed run. Skill state writes use a 2-step atomic pattern (Write
9
+ `.tmp` then `Bash mv`); a crash between Step A and Step B leaves a
10
+ `.fabric/.import-state.json.tmp` sidecar that the next invocation MUST
11
+ triage.
12
+
13
+ 1. Does `.fabric/.import-state.json.tmp` exist? (`Bash: ls .fabric/.import-state.json.tmp 2>/dev/null`)
14
+ - **Does not exist** → proceed normally to Phase 0.1 (no residue work).
15
+ - **Exists** → triage:
16
+ 1. `Read` the `.tmp` file; try `JSON.parse` on the content.
17
+ 2. Compare `mtime` of `.tmp` vs `.fabric/.import-state.json` via `Bash: stat`.
18
+ - **Parse OK + .tmp mtime newer than main file** → rescue:
19
+ `Bash: mv .fabric/.import-state.json.tmp .fabric/.import-state.json`
20
+ (commits the last incomplete write atomically).
21
+ - **Parse OK + .tmp mtime older than main file** → stale residue
22
+ from an earlier run that subsequently completed; delete it:
23
+ `Bash: rm .fabric/.import-state.json.tmp`.
24
+ - **Parse fails** (syntax error / unterminated structure / truncated
25
+ mid-write) → half-written, unrecoverable; delete it:
26
+ `Bash: rm .fabric/.import-state.json.tmp`.
27
+ 3. After triage, proceed to Phase 0.1.
28
+
29
+ The 5-minute mtime heuristic (treat any `.tmp` older than 5 minutes as
30
+ stale regardless of parse result) is an acceptable conservative simplification:
31
+ no legitimate atomic write window stays open that long; anything older
32
+ than 5 minutes is definitely crash residue. Implementations MAY use either
33
+ the mtime-comparison rule above OR the 5-minute staleness rule.
34
+
35
+ ### Phase 0.1 — State Corruption Recovery
36
+
37
+ After residue triage, `Read` `.fabric/.import-state.json`. Detect
38
+ corruption if ANY of the following hold:
39
+
40
+ - `JSON.parse` throws (syntax error / unterminated structure / truncated)
41
+ - Missing required field: `phase` OR `started_at` OR `last_checkpoint_at`
42
+ - `phase` value not in the enum `{P1-done, P2-done, complete}`
43
+
44
+ On corruption (any condition above):
45
+
46
+ 1. `Bash: mv .fabric/.import-state.json .fabric/.import-state.json.corrupt-<ISO8601>`
47
+ (preserve the corrupt file for postmortem; do NOT silently overwrite).
48
+ 2. Phase 1 restarts from scratch (Phase 1 produces no MCP calls, so re-run
49
+ is safe — re-globbing `.fabric/knowledge/team/**/*.md` is cheap and
50
+ idempotent; the `p1_baseline_titles` array is regenerated).
51
+ 3. DO NOT attempt automatic partial recovery; corrupt state is a signal
52
+ that something serious happened (disk-full, kill -9 mid-write, fs
53
+ error). Discard-and-restart is the only safe path.
54
+
55
+ ENOENT (state file absent) is NOT corruption — it is the normal
56
+ first-run state. Proceed to Phase 0.5.
57
+