@fenglimg/fabric-cli 2.0.0 → 2.0.1

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 (73) 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-D25XJ4BC.js +880 -0
  6. package/dist/chunk-MF3OTILQ.js +544 -0
  7. package/dist/chunk-PWLW3B57.js +18 -0
  8. package/dist/config-XJIPZNUP.js +13 -0
  9. package/dist/doctor-EJDSEJSS.js +810 -0
  10. package/dist/index.js +15 -8
  11. package/dist/{init-BIRSIOXO.js → install-EKWMFLUU.js} +622 -711
  12. package/dist/metrics-ACEQFPDU.js +122 -0
  13. package/dist/onboard-coverage-MFCAEBDO.js +220 -0
  14. package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-FC6P3WFE.js} +34 -28
  15. package/dist/uninstall-MH7ZIB6M.js +1064 -0
  16. package/package.json +30 -5
  17. package/templates/hooks/cite-policy-evict.cjs +231 -0
  18. package/templates/hooks/configs/README.md +29 -6
  19. package/templates/hooks/configs/claude-code.json +14 -3
  20. package/templates/hooks/configs/codex-hooks.json +6 -3
  21. package/templates/hooks/configs/cursor-hooks.json +8 -10
  22. package/templates/hooks/fabric-hint.cjs +833 -105
  23. package/templates/hooks/knowledge-hint-broad.cjs +509 -135
  24. package/templates/hooks/knowledge-hint-narrow.cjs +791 -26
  25. package/templates/hooks/lib/banner-i18n.cjs +309 -0
  26. package/templates/hooks/lib/cite-contract-reminder.cjs +173 -0
  27. package/templates/hooks/lib/cite-line-parser.cjs +158 -0
  28. package/templates/hooks/lib/client-adapter.cjs +106 -0
  29. package/templates/hooks/lib/config-cache.cjs +107 -0
  30. package/templates/hooks/lib/state-store.cjs +84 -0
  31. package/templates/hooks/lib/summary-fallback.cjs +210 -0
  32. package/templates/skills/fabric-archive/SKILL.md +93 -419
  33. package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
  34. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
  35. package/templates/skills/fabric-archive/ref/i18n-policy.md +86 -0
  36. package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
  37. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +218 -0
  38. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +62 -0
  39. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +68 -0
  40. package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +108 -0
  41. package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
  42. package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
  43. package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
  44. package/templates/skills/fabric-archive/ref/rc-history.md +38 -0
  45. package/templates/skills/fabric-archive/ref/worked-examples.md +78 -0
  46. package/templates/skills/fabric-import/SKILL.md +75 -516
  47. package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
  48. package/templates/skills/fabric-import/ref/i18n-policy.md +79 -0
  49. package/templates/skills/fabric-import/ref/output-contract.md +61 -0
  50. package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
  51. package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
  52. package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
  53. package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
  54. package/templates/skills/fabric-review/SKILL.md +86 -284
  55. package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
  56. package/templates/skills/fabric-review/ref/i18n-policy.md +111 -0
  57. package/templates/skills/fabric-review/ref/modify-flow.md +103 -0
  58. package/templates/skills/fabric-review/ref/output-contract.md +58 -0
  59. package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
  60. package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
  61. package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
  62. package/templates/skills/lib/shared-policy.md +69 -0
  63. package/dist/chunk-6ICJICVU.js +0 -10
  64. package/dist/chunk-74SZWYPH.js +0 -658
  65. package/dist/chunk-EYIDD2YS.js +0 -1000
  66. package/dist/doctor-T7JWODKG.js +0 -282
  67. package/dist/hooks-Y74Y5LQS.js +0 -12
  68. package/dist/scan-LMK3UCWL.js +0 -22
  69. package/dist/serve-H554BHLG.js +0 -124
  70. package/templates/agents-md/AGENTS.md.template +0 -59
  71. package/templates/bootstrap/CLAUDE.md +0 -8
  72. package/templates/bootstrap/codex-AGENTS-header.md +0 -6
  73. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
@@ -0,0 +1,127 @@
1
+ # Worked Examples (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path points here for concrete end-to-end traces. These 4 examples illustrate Phase 2 mining (git + docs), Phase 3 dedup classification, and the out-of-band narrowing path that is NOT performed by this skill.
4
+
5
+ ## Example A — Phase 2 git mining: feat commit → pitfall entry
6
+
7
+ 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.
8
+
9
+ 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).
10
+
11
+ Skill output (note `relevance_scope: "broad"` + `relevance_paths: []` — mandatory for fabric-import):
12
+
13
+ ```ts
14
+ mcp__fabric__fab_extract_knowledge({
15
+ source_sessions: ["fabric-import-2026-05-10"],
16
+ recent_paths: ["packages/server/src/lib/retry.ts"], // provenance only
17
+ user_messages_summary: "重试无指数退避会在短暂上游故障下放大成雪崩。修正:jittered exponential backoff,30 秒上限。src=50367b5",
18
+ type: "pitfalls",
19
+ slug: "retry-without-backoff-thundering-herd",
20
+ relevance_scope: "broad", // MANDATORY
21
+ relevance_paths: [], // MANDATORY — do NOT infer ["packages/server/src/lib/retry.ts"]
22
+ 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.
23
+ 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."
24
+ })
25
+ ```
26
+
27
+ Counter-example — DO NOT do this:
28
+
29
+ ```ts
30
+ // WRONG — this skill must never produce narrow + paths from git metadata.
31
+ // The retry pitfall applies to every retry site, not just the file touched by 50367b5.
32
+ mcp__fabric__fab_extract_knowledge({
33
+ // ...
34
+ relevance_scope: "narrow", // VIOLATION
35
+ relevance_paths: ["packages/server/src/lib/retry.ts"] // VIOLATION
36
+ })
37
+ ```
38
+
39
+ 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.
40
+
41
+ State file delta:
42
+ ```json
43
+ { "p2_processed_commits": [
44
+ { "sha": "50367b5...", "skipped": false,
45
+ "pending_path": "knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md",
46
+ "type": "pitfalls", "slug": "retry-without-backoff-thundering-herd" }
47
+ ]
48
+ }
49
+ ```
50
+
51
+ ## Example B — Phase 2 doc mining: architecture.md → decision entry
52
+
53
+ 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.
54
+
55
+ 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).
56
+
57
+ Skill output (broad+[] mandatory; the doc's own path stays in `recent_paths` for provenance, NOT in `relevance_paths`):
58
+
59
+ ```ts
60
+ mcp__fabric__fab_extract_knowledge({
61
+ source_sessions: ["fabric-import-2026-05-10"],
62
+ recent_paths: ["docs/architecture.md"], // provenance only
63
+ user_messages_summary: "选择单体架构而非微服务:3 人团队无法承担多服务运维成本,且主要性能瓶颈在 DB 吞吐而非应用层水平扩展。src=docs/architecture.md",
64
+ type: "decisions",
65
+ slug: "monolith-over-microservices-small-team",
66
+ relevance_scope: "broad", // MANDATORY
67
+ relevance_paths: [] // MANDATORY — a monolith-vs-microservices decision applies repo-wide, not only to docs/
68
+ })
69
+ ```
70
+
71
+ ## Example C — Phase 3 dedup finds duplicate, rejects
72
+
73
+ After Example A's pending entry (`retry-without-backoff-thundering-herd`) is proposed, Phase 3 runs:
74
+
75
+ ```ts
76
+ mcp__fabric__fab_review({
77
+ action: "search",
78
+ query: "retry backoff thundering herd",
79
+ filters: { type: "pitfalls" }
80
+ })
81
+ ```
82
+
83
+ 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).
84
+
85
+ Skill output:
86
+
87
+ ```ts
88
+ mcp__fabric__fab_review({
89
+ action: "reject",
90
+ pending_paths: ["knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md"],
91
+ reason: "duplicate of KT-P-0007"
92
+ })
93
+ ```
94
+
95
+ State file delta:
96
+ ```json
97
+ { "p3_dedup_completed": [
98
+ { "pending_path": "knowledge/pending/pitfalls/retry-without-backoff-thundering-herd.md",
99
+ "action": "reject", "canonical_ref": "KT-P-0007" }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ Final roll-up to user reflects: 1 proposed, 0 kept, 1 rejected_dup, 0 merged, 0 contradictions.
105
+
106
+ ## Example D — Post-import narrowing (out-of-band, NOT this skill)
107
+
108
+ 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.
109
+
110
+ The user issues (via `fabric-review`, NOT via this skill):
111
+
112
+ ```ts
113
+ mcp__fabric__fab_review({
114
+ action: "modify",
115
+ pending_path: "knowledge/team/decisions/monolith-over-microservices-small-team.md",
116
+ changes: {
117
+ relevance_scope: "narrow",
118
+ relevance_paths: ["packages/server/**", "scripts/deploy/**"]
119
+ }
120
+ })
121
+ ```
122
+
123
+ Key invariants of this flow:
124
+
125
+ - The narrowing decision originates from the **user**, informed by the actual paths they propose — not from `fabric-import` inferring paths from git metadata.
126
+ - The modify call goes through `fab_review`, not `fab_extract_knowledge`, because the entry already exists (post-import or post-approval).
127
+ - 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.
@@ -1,256 +1,136 @@
1
1
  ---
2
2
  name: fabric-review
3
- description: Use this skill to review pending knowledge entries in `.fabric/knowledge/pending/` — list, approve (late-bind id allocation), reject, modify (incl. layer flip), search, defer. Mode is inferred from invocation context (recent user message + events.jsonl tail + pending count) — NEVER asked. Per-item actions (approve / reject / modify / defer) are surfaced via AskUserQuestion because they are genuine human-judgment choices.
3
+ description: .fabric/knowledge pending+canonical (NOT PR review):approve/reject/modify/revisit/defer。Triggers 审批/驳回/复审/重审/approve/reject/review pending.
4
4
  allowed-tools: Read, Glob, Grep, Bash, Edit, mcp__fabric__fab_review
5
5
  ---
6
6
 
7
- > **Surface**: This is a Skill (AI-driven, per-entry human-judgment routing). See [`docs/surfaces.md`](https://github.com/fenglimg/fabric/blob/main/docs/surfaces.md) for the CLI / Skill / MCP boundary.
7
+ > **Surface**: Skill (AI-driven, per-entry human-judgment routing). See [`docs/surfaces.md`](https://github.com/fenglimg/fabric/blob/main/docs/surfaces.md).
8
8
 
9
9
  ## Precondition
10
10
 
11
- This skill is invoked when one of the following holds:
11
+ Invoke this skill ONLY when ONE of the following holds:
12
12
 
13
- - The Stop-hook printed a stdout JSON pointer of shape `{"decision":"block","reason":"..."}` carrying a `signal=review` (pending overflow: ≥10 entries or oldest pending age ≥7 days)
14
- - The user typed an explicit review request (e.g. "review knowledge", "show pending", "approve what's queued", "what's stale", "look at KT-D-7")
15
- - A task end where the agent itself判定 review backlog has crossed the overflow threshold
13
+ - Stop-hook printed stdout JSON `{"decision":"block","reason":"..."}` carrying `signal=review` (pending overflow: count `review_hint_pending_count` (default 10) OR oldest age ≥ `review_hint_pending_age_days` (default 7) days)
14
+ - User typed an explicit review request (e.g. "review knowledge", "show pending", "approve what's queued", "what's stale", "look at KT-D-7")
15
+ - Agent判定 review backlog crossed the overflow threshold
16
16
 
17
- If none of the above hold, stop the skill immediately and tell the user `没有触发 review 信号;如需手动 review 请显式调用 fabric-review`.
17
+ If none hold, stop the skill and tell the user:
18
+
19
+ - zh-CN: `没有触发 review 信号;如需手动 review 请显式调用 fabric-review`
20
+ - en: `No review signal detected; to manually review, explicitly invoke fabric-review`
18
21
 
19
22
  This skill is `Infer-not-Ask` for mode and `Ask-when-genuine` for per-item actions:
20
23
 
21
24
  - Mode (pending / topic / health / revisit) is INFERRED from context — NEVER surfaced via AskUserQuestion
22
25
  - Per-item action (approve / reject / modify / defer) IS surfaced via AskUserQuestion — the user must judge
23
- - Layer-flip target (team vs personal) IS surfaced via AskUserQuestion when modify path includes layer change
26
+ - Layer-flip target (team vs personal) IS surfaced via AskUserQuestion when modify includes layer change
27
+
28
+ Required preconditions before any `fab_review` call: `.fabric/` exists (or `~/.fabric/` for personal layer); `mcp__fabric__fab_review` MCP tool registered; `.fabric/agents.meta.json` present (id allocator reads it on approve); `.fabric/events.jsonl` exists (tolerate ENOENT — empty ledger normal first-run).
29
+
30
+ ### Config Load
31
+
32
+ Read `.fabric/fabric-config.json`; resolve:
33
+
34
+ | Config field | Default | Used by |
35
+ |---|---|---|
36
+ | `review_topic_result_cap` | 8 | topic mode top-N cap |
37
+ | `review_stale_pending_days` | 14 | health mode stale threshold (days) |
38
+ | `review_hint_pending_count` | 10 | precondition overflow signal (count) |
39
+ | `review_hint_pending_age_days` | 7 | precondition overflow signal (age) |
24
40
 
25
- Required preconditions before any fab_review call:
41
+ Missing or unreadable defaults silently.
26
42
 
27
- - `.fabric/` directory exists in the project (or `~/.fabric/` for personal layer)
28
- - `mcp__fabric__fab_review` MCP tool is registered and reachable
29
- - `.fabric/agents.meta.json` is present (the id allocator reads it on approve)
30
- - `.fabric/events.jsonl` exists (tolerate ENOENT — empty ledger is normal first-run)
43
+ ### UX i18n Policy
44
+
45
+ Read `fabric_language` (`zh-CN` / `en` / `zh-CN-hybrid` / `match-existing`); emit user-facing prose in resolved variant. Protected tokens (`fab_review`, `fab_extract_knowledge`, `relevance_scope`, layer/scope enums, `stable_id`, the verbatim `强 team` / `强 personal` / `默认 team` block) NEVER translated. `AskUserQuestion` policy: `header` + `question` translate; `options[]` stay English (routing keys).
46
+
47
+ `Read ref/i18n-policy.md` for the full 5-class taxonomy + edge cases.
31
48
 
32
49
  ## Mode Inference (System Infers — NEVER Ask)
33
50
 
34
- > Verbatim from rc.3 locked decisions:
35
- > "review 永远走 fabric-review skill,**模式从上下文推断**(4 种 mode:pending queue / by topic / health overview / revisit existing)"
51
+ > Locked decision (KT-DEC-0006): "Review mode inferred from context, not solicited via AskUserQuestion."
36
52
  > "**AskUserQuestion 仅在真有选择时用**——'何种 mode' 不是真选择(系统能推断),'approve/reject/modify 单条' 是真选择"
37
53
 
38
- The skill MUST infer one of {`pending`, `topic`, `health`, `revisit`} before any user-facing output. NEVER call `AskUserQuestion` to ask the user which mode to use — the system MUST infer.
54
+ The skill MUST infer one of **2 modes** BEFORE any user-facing output (v2.0.0-rc.37 NEW-12 simplified 4 2):
39
55
 
40
- ### 3-Step Inference Algorithm
56
+ - **`pending`** — triage the write-side backlog (`.fabric/knowledge/pending/`): approve / reject / modify / defer per item. The dominant entry point.
57
+ - **`maintain`** — sustain the EXISTING canonical KB: browse by topic (search), survey staleness/health, or revisit a specific entry. Merges the legacy `topic` + `health` + `revisit` modes — they are all "operate on already-canonical knowledge", distinct from triaging new drafts.
41
58
 
42
- **Step 1 — Recent user message keyword scan.** Read the user's most recent invocation message (or the Stop-hook reason text). Match against keyword sets in this priority order:
59
+ ### 2-Step Inference Algorithm
60
+
61
+ **Step 1 — Recent user message keyword scan:**
43
62
 
44
63
  | Keywords (zh-CN + en) | Inferred mode |
45
64
  |---|---|
46
65
  | "approve", "review pending", "promote", "what's queued", "审核 pending", "通过" | `pending` |
47
- | "search for X about Y", "find entries about <topic>", "关于…的知识", "找一下 <topic>" | `topic` |
48
- | "what's stale", "demote old", "health check", "过期的", "陈旧的", "整理一下" | `health` |
49
- | "look at <id>", "revisit KT-…", "show <slug>", "再看下 <id>", "回顾" | `revisit` |
66
+ | "search/find about <topic>", "what's stale", "demote old", "health check", "look at <id>", "revisit KT-…", "关于…的知识", "过期的", "陈旧的", "整理一下", "再看下 <id>", "回顾" | `maintain` |
50
67
 
51
- If exactly one row matches, lock that mode and skip to Step 3.
68
+ A `maintain`-row match → lock `maintain`. A `pending`-row match (or 0/ambiguous) fall to Step 2.
52
69
 
53
- **Step 2 — events.jsonl tail scan.** If Step 1 yielded zero or multiple matches, read the tail (last 200 lines) of `.fabric/events.jsonl`:
70
+ **Step 2 — Backlog default.** Glob `.fabric/knowledge/pending/**/*.md`:
54
71
 
55
- - Count `knowledge_proposed` events since the last `knowledge_promoted` event. If `>5` recent proposalsinfer `pending` (write side has piled up; review is overdue).
56
- - Count `knowledge_demoted` or `lint`-class events in the last 24h. If `≥1` infer `health` (corpus quality signal already firing).
57
- - If a recent `knowledge_layer_changed` event exists for the entry the user just referenced → infer `revisit`.
72
+ - Count `review_hint_pending_count` (default 10) OR oldest mtime > `review_hint_pending_age_days` (default 7) → `pending` (overflow, same threshold as Stop-hook).
73
+ - Otherwisedefault `pending` (most common review entry point).
58
74
 
59
- **Step 3 Pending count default.** If Step 1 and Step 2 both produced no signal, glob `.fabric/knowledge/pending/**/*.md`:
75
+ > Back-compat: the legacy 4-mode names (`topic` / `health` / `revisit`) still resolve they all map to `maintain`. Old session traces / muscle memory keep working.
60
76
 
61
- - If pending count `≥10` OR oldest pending file mtime is `>7 days` ago infer `pending` (overflow signal — same threshold the Stop-hook uses).
62
- - Otherwise → default to `pending` (most common review entry point).
77
+ `Read ref/per-mode-flows.md` for inference examples and anti-pattern restatement.
63
78
 
64
- ### Inference Examples (Sample User Messages → Expected Mode)
79
+ ## Per-Mode Flow
65
80
 
66
- - "review the pending knowledge" `pending` (Step 1 keyword "review pending")
67
- - "find anything about deepMerge" → `topic` (Step 1 keyword "find … about")
68
- - "anything stale in our knowledge base?" → `health` (Step 1 keyword "stale")
69
- - "look at KT-D-7" → `revisit` (Step 1 keyword "look at <id>")
70
- - (Stop-hook fired with signal=review, no user typing) → `pending` (Step 3 default, overflow threshold tripped)
81
+ Each mode produces user-facing output, then routes per-item or per-batch decisions through `fab_review` actions. Display body = zh-CN summaries (M3 style); section headings = EN.
71
82
 
72
- ### Anti-Pattern (Hard Rule)
83
+ - **`pending`** — list pending entries → run Semantic Check (see `ref/semantic-check.md`) → per-item AskUserQuestion `{approve, reject, modify, defer, skip}` → route per choice. The modify branch chooses between two explicit actions (rc.37 NEW-12):
84
+ - `fab_review action="modify-content"` — edit scalars (title/summary/maturity/tags/relevance_*); NEVER flips layer.
85
+ - `fab_review action="modify-layer"` — the dedicated layer-flip path (`changes.layer` required); may reallocate the stable_id + emit an id-redirect.
86
+ - (Legacy `action="modify"` still works — it routes by whether `changes.layer` is present.) See `ref/modify-flow.md`.
87
+ - **`maintain`** — sub-flow inferred from the same keywords:
88
+ - *browse-by-topic*: extract keywords → `fab_review action="search"` → render top-N (cap `review_topic_result_cap`) → AskUserQuestion only on an action verb.
89
+ - *health/staleness*: `fab_review action="list"` + tail events → compute stale → render dashboard → per-stale AskUserQuestion `{defer, demote, skip}`.
90
+ - *revisit*: user referenced a specific id/slug → `Read` canonical file directly OR `fab_review action="list"` with narrow filters → display body + history → AskUserQuestion only if actionable.
73
91
 
74
- NEVER emit an `AskUserQuestion` whose options include {pending, topic, health, revisit}. The user does not pick the mode. If inference is genuinely ambiguous after all 3 steps, default to `pending` and proceed; the user can always cancel and redirect.
92
+ `Read ref/per-mode-flows.md` for full step-by-step procedures, bilingual rendering blocks (en + zh-CN per-item display, AskUserQuestion templates, health dashboard format), and the rc.7 T6 `proposed_reason` + `## Why proposed` + `## Session context` rendering contract.
75
93
 
76
- ## Per-Mode Flow
94
+ ## Semantic Check (LLM-Assisted Duplicate / Contradiction Detection)
77
95
 
78
- Each mode produces user-facing output, then routes per-item or per-batch decisions through `fab_review` actions. Display body = zh-CN summaries (M3 style); section headings = EN.
96
+ > Boundary B (locked): "extraction / classification / layer / slug / mode / **semantic dedup** Skill (LLM); file write / frontmatter / idempotency / counter / layer-flip / atomic promote MCP (deterministic)"
79
97
 
80
- ### Mode: pending Approve / Reject / Modify Backlog
81
-
82
- 1. Call `fab_review` with `action: "list"`, no filters (or `filters.layer="both"` if user explicitly mentioned both layers).
83
- 2. Server returns `items[]` (each = `{pending_path, type, layer, maturity, tags?, title?, summary?}`).
84
- 3. Before presenting, perform **Semantic Check** (see below) by issuing one or more `action: "search"` calls scoped by `filters.type` to surface possible duplicates / contradictions among already-canonical entries.
85
- 4. For each pending item, render a per-item block. v2.0.0-rc.7 T6: render
86
- `proposed_reason` (frontmatter) + `## Why proposed` line (body, 1-line enum
87
- explanation) + first line of `## Session context` so future-self has full
88
- context without re-reading the transcript:
98
+ Semantic check is the LLM's job — the MCP tool does NOT compare meaning. Run during `pending` mode (and on demand during `topic`): for each pending entry, `fab_review action="search"` scoped by `filters.type` → LLM judges semantically against returned canonical entries → surface one of three flags as informational:
89
99
 
90
- ```md
91
- ## [type=decisions] [layer=team] pending_path=knowledge/pending/decisions/single-cjs-hook.md
92
- Title: .cjs hook 跨客户端
93
- Summary: 三客户端 stdout JSON 格式一致,单脚本即可。
94
- Maturity: draft Tags: [hook, cli]
95
- Proposed reason: decision-confirmation — ≥2 候选方案经权衡后确认选型。
96
- Session context: Session goal: ship Stop-hook for v2 release.
97
- ⚠ Possible duplicate of KT-D-0007 (similarity 0.78 on title + summary)
98
- ```
100
+ - `⚠ Possible duplicate of <stable_id> (overlap: high)` — same essential claim
101
+ - `⚠ Contradicts <stable_id> (overlap: high)` — opposing claims, same scope
102
+ - `⚠ Subsumed by <stable_id> (overlap: medium+); consider modify-to-merge` — fully covered
99
103
 
100
- The Skill MUST read `proposed_reason` from the pending file's frontmatter
101
- (parse the YAML block, key `proposed_reason`) and the `## Why proposed`
102
- line / first non-blank line of `## Session context` from the body. If
103
- either is missing on a pre-rc.7 pending entry, render `Proposed reason:
104
- <legacy entry, no reason recorded>` and `Session context: <not recorded>`
105
- so the reviewer can still proceed.
106
-
107
- 5. Surface a per-item AskUserQuestion:
108
-
109
- ```ts
110
- AskUserQuestion({
111
- header: "Review pending entry",
112
- question: "What action for '单 .cjs hook 跨客户端'?",
113
- options: ["approve", "reject", "modify", "defer", "skip"]
114
- })
115
- ```
116
-
117
- 6. Route the user's choice:
118
- - `approve` → accumulate pending_path into a batch; flush via single `fab_review action="approve"` with `pending_paths=[…]` after the loop ends.
119
- - `reject` → ask the user for a one-line reason via free-text follow-up; call `fab_review action="reject"` with `pending_paths=[path]` and `reason`.
120
- - `modify` → see Modify Sub-Flow below.
121
- - `defer` → call `fab_review action="defer"` with `pending_paths=[path]`; optional `until` ISO datetime if the user supplies one ("defer 2 weeks" → compute and set).
122
- - `skip` → no MCP call; move to next item.
123
- 7. After the loop, display a roll-up: counts by action, list of newly-allocated `stable_id`s (from approve output), and tail of `.fabric/events.jsonl` showing the appended events.
124
-
125
- ### Mode: topic — Search & Surface Findings
126
-
127
- 1. Extract the topic keyword(s) from the user's message (e.g. "find about deepMerge" → query="deepMerge").
128
- 2. Call `fab_review action="search"` with `query` and any obvious filters (if user said "team-only" → `filters.layer="team"`).
129
- 3. Server returns `items[]` ranked by relevance — these are entries already in `.fabric/knowledge/{layer}/{type}/` (NOT pending), unless `filters` says otherwise.
130
- 4. Render top-N (cap at 8) results with title / summary / pending_path.
131
- 5. If the user follow-up indicates intent to act ("approve all", "modify the second one"), pivot into the corresponding pending mode action — the search result already gives the `pending_path` needed for the action.
132
- 6. NEVER surface a per-item AskUserQuestion just for browsing — only when the user signals an action verb.
133
-
134
- ### Mode: health — Corpus Health & Stale Detection
135
-
136
- 1. Call `fab_review action="list"` with `filters.maturity="draft"` (or no filter for full corpus inspection).
137
- 2. Tail `.fabric/events.jsonl` for layer_changed / demoted / rejected counts in the trailing 30 days.
138
- 3. Compute stale candidates: pending entries with mtime `>14 days` OR maturity=draft entries with no recent evidence-append events.
139
- 4. Render a corpus dashboard:
140
-
141
- ```md
142
- ## Health Overview
143
- - Pending: 12 entries (oldest 18d) — recommend `defer` or `reject`
144
- - Drafts: 8 (3 are stale candidates: KP-G-3, KP-G-5, KT-P-9)
145
- - Layer flips (30d): 2
146
- - Rejections (30d): 1
147
- ```
148
-
149
- 5. For each stale candidate, surface AskUserQuestion `{options: ["defer", "demote", "skip"]}`; route `defer` → `fab_review action="defer"`, `demote` → `fab_review action="modify"` with `changes.maturity` lowered (or `reject` if the user wants outright removal of a pending entry).
150
-
151
- ### Mode: revisit — Specific Entry Deep Dive
152
-
153
- 1. The user referenced a specific entry (by id `KT-D-7` or by slug `single-cjs-hook`).
154
- 2. Call `fab_review action="list"` with `filters` narrowed by best-guess fields; if the entry is canonical (has stable_id), `Read` the file directly at `.fabric/knowledge/{layer}/{type}/<id>--<slug>.md`.
155
- 3. Display the full body (frontmatter + content). Tail the events.jsonl for any history events tagged with this stable_id.
156
- 4. Surface AskUserQuestion `{options: ["approve", "modify", "reject", "skip"]}` only if the entry is still pending; for canonical entries the only mutation path is `modify` (incl. layer flip).
157
-
158
- ## Semantic Check Guidance (LLM-Assisted Duplicate / Contradiction Detection)
159
-
160
- > Boundary B (locked): "extraction classification / layer inference / slug naming / mode inference / **semantic dedup** → Skill (LLM); pending file write / frontmatter assembly / idempotency check / counter mgmt / layer-flip transaction / atomic promote → MCP (deterministic)"
161
-
162
- Semantic check is the LLM's job — the MCP tool does NOT compare meaning. Run this check during `pending` mode (and on demand during `topic` mode):
163
-
164
- For each pending entry to be presented:
165
-
166
- 1. Call `fab_review action="search"` with `query=<title or summary keywords>` and `filters.type=<same type>` to fetch already-canonical entries of the same type.
167
- 2. Compare semantically (LLM judgment, not string match):
168
- - **Duplicate** — same essential claim. Heuristics: title keyword overlap >60%, summary asserts the same outcome with no novel context. Flag: `⚠ Possible duplicate of <stable_id>`.
169
- - **Contradiction** — opposing claims about the same subject. Heuristics: one entry says "use X" while pending says "avoid X" on identical scope. Flag: `⚠ Contradicts <stable_id>`.
170
- - **Subsumption** — pending fully covered by an existing entry plus extras. Flag: `⚠ Subsumed by <stable_id>; consider modify-to-merge`.
171
- 3. Surface the flag in the per-item display block (see pending mode step 4).
172
- 4. The user decides:
173
- - Still approve → flag is informational; pending becomes canonical alongside the existing entry.
174
- - Modify-to-harmonize → user supplies edits via `modify` action; consider merging language with the existing entry.
175
- - Reject as duplicate → reason field MUST cite the existing stable_id (e.g. `reason="duplicate of KT-D-7"`).
176
-
177
- DO NOT call `AskUserQuestion` to ask "is this a duplicate?" — the LLM has already judged. The user only chooses among approve / reject / modify, which is a genuine choice.
178
-
179
- ## Modify Sub-Flow & Layer-Flip Rules
180
-
181
- `modify` is the only action that mutates frontmatter or stable_id. It accepts `changes` of shape `{title?, summary?, layer?, maturity?, tags?}`. Server semantics:
182
-
183
- - **title / summary / tags / maturity changes** → in-place rewrite; stable_id PRESERVED; emits `knowledge_slug_renamed` only when slug derives from title.
184
- - **layer change** → the ONLY legal stable_id mutation in the system.
185
-
186
- ### Layer-Flip Rules (the only legal stable_id mutation)
187
-
188
- Triggered when `changes.layer` differs from current entry layer. Server-side transaction:
189
-
190
- 1. Allocate new id under target layer via `KnowledgeIdAllocator.allocate(new_layer, type)` (e.g. KT-D-7 in `team/decisions/` flips to KP-D-3 in `personal/decisions/`).
191
- 2. `git mv <old-layer>/<type>/<old-id>--<slug>.md <new-layer>/<type>/<new-id>--<slug>.md`.
192
- 3. Append `knowledge_layer_changed` event with `{from_layer, to_layer, prior_stable_id, new_stable_id}`.
193
- 4. Server response includes `prior_stable_id` and `new_stable_id` — surface BOTH to the user in the roll-up.
194
-
195
- Skill responsibilities for layer flip:
196
-
197
- - BEFORE calling fab_review, surface `AskUserQuestion {options: ["team", "personal"]}` to confirm target layer. The default in the question header should reflect the verbatim layer heuristic (default team unless 强 personal signals dominate). This IS a genuine choice — the user must pick.
198
- - AFTER server returns, render: `Layer flipped: <prior_stable_id> → <new_stable_id>`. Do NOT silently swallow the id change — downstream agents may have cached the prior id.
104
+ **Quantified overlap band (rc.37 NEW-12).** The LLM still judges meaning (no embedding %), but MUST tag each flag with a 3-level band so the signal is comparable across entries and the user can triage at a glance:
199
105
 
200
- ### Modify Examples
106
+ - `high` — ≥ ~80% of the candidate's essential claim is restated; near-certain dup/contradiction. Recommend reject-as-duplicate or modify-to-merge.
107
+ - `medium` — substantial conceptual overlap but the new entry adds a distinct facet (different path scope, added caveat). Recommend modify-to-harmonize.
108
+ - `low` — adjacent topic, not a real overlap. Do NOT raise a flag at `low` — it is below the surfacing threshold (suppresses noise).
201
109
 
202
- ```ts
203
- // Maturity bump only (no id change)
204
- mcp__fabric__fab_review({
205
- action: "modify",
206
- pending_path: "knowledge/team/decisions/KT-D-0007--single-cjs-hook.md",
207
- changes: { maturity: "verified" }
208
- })
110
+ Only `medium`+ flags are surfaced. User decides: still-approve (flag informational), modify-to-harmonize, or reject-as-duplicate (reason MUST cite existing stable_id).
209
111
 
210
- // Layer flip team personal (id WILL change)
211
- mcp__fabric__fab_review({
212
- action: "modify",
213
- pending_path: "knowledge/team/guidelines/KT-G-0003--indent-style.md",
214
- changes: { layer: "personal" }
215
- })
216
- ```
112
+ DO NOT AskUserQuestion "is this a duplicate?" LLM already judged. User only chooses approve/reject/modify.
217
113
 
218
- ## AskUserQuestion Policy
114
+ `Read ref/semantic-check.md` for full procedure + 三类判断的细化定义.
219
115
 
220
- ### DO ask (genuine choices that require human judgment)
116
+ ## Narrowing Imported Entries & Modify Sub-Flow
221
117
 
222
- - Per-pending-item action: `["approve", "reject", "modify", "defer", "skip"]`
223
- - Per-stale-item action (health mode): `["defer", "demote", "skip"]`
224
- - Layer-flip target when modify path includes layer change: `["team", "personal"]`
225
- - Reject reason follow-up (free-text, may use AskUserQuestion's free-form variant if available, otherwise plain prompt)
118
+ `modify` is the only action that mutates frontmatter or stable_id. Two paths:
226
119
 
227
- ### DO NOT ask (system must infer or operate deterministically)
120
+ - **Title/summary/tags/maturity** in-place rewrite; stable_id PRESERVED.
121
+ - **Layer change** → ONLY legal stable_id mutation. BEFORE call, AskUserQuestion `{team, personal}` to confirm target (this IS genuine choice). AFTER server returns, surface BOTH `prior_stable_id` and `new_stable_id` in roll-up — downstream agents may cache the prior id.
228
122
 
229
- - Mode picking (pending / topic / health / revisit) — INFERRED per the 3-step algorithm
230
- - Whether to invoke this skill at all — Stop-hook signal or explicit user request decides
231
- - Whether an entry is a duplicate — LLM semantic check answers
232
- - Frontmatter parsing — deterministic, never asked
233
- - Allocate next id — deterministic via KnowledgeIdAllocator, never asked
123
+ **Import-origin entries** (`source_sessions[0]` starts with `fabric-import-`) ship with `relevance_scope=broad + relevance_paths=[]` by design; narrowing is fabric-review's responsibility. Render `⚠ Imported (relevance_scope=broad, relevance_paths=[]) — pick 'modify' + say 'narrow to <paths>'` as informational hint. On `modify` of import-origin: extended option list `{narrow scope, edit summary, change layer, change maturity, skip}`; "narrow scope" → free-text follow-up → `changes: { relevance_scope: "narrow", relevance_paths: [<parsed>] }`. Personal layer auto-degrades narrow → broad+[] (server-side), emitting `knowledge_scope_degraded`.
234
124
 
235
- ### Per-Item Question Phrasing Template
125
+ `Read ref/modify-flow.md` for layer-flip server transaction (4-step), modify call shape examples (maturity bump / layer flip), and full narrowing flow with bilingual AskUserQuestion templates.
236
126
 
237
- ```ts
238
- AskUserQuestion({
239
- header: "Review pending entry",
240
- question: "What action for '{title}'? ({pending_path})",
241
- options: ["approve", "reject", "modify", "defer", "skip"]
242
- })
243
- ```
127
+ ## AskUserQuestion Policy
244
128
 
245
- For layer-flip target:
129
+ **DO ask** (genuine choices): per-pending action `{approve, reject, modify, defer, skip}` · per-stale action `{defer, demote, skip}` · layer-flip target `{team, personal}` · reject reason follow-up (free-text).
246
130
 
247
- ```ts
248
- AskUserQuestion({
249
- header: "Layer-flip target",
250
- question: "Move '{title}' to which layer? (current: {current_layer})",
251
- options: ["team", "personal"]
252
- })
253
- ```
131
+ **DO NOT ask**: mode picking (inferred) · whether to invoke skill (Stop-hook/explicit decides) · whether duplicate (LLM judges) · frontmatter parsing (deterministic) · next id allocation (deterministic via KnowledgeIdAllocator).
132
+
133
+ `Read ref/askuserquestion-policy.md` for full DO/DO NOT lists + bilingual per-item question phrasing templates (pending action / layer-flip target).
254
134
 
255
135
  ## Decision Tree — Is This Entry Approvable?
256
136
 
@@ -289,94 +169,16 @@ Pending entry presented for review
289
169
  - NEVER batch heterogeneous decisions into a single MCP call. Approve and reject MAY be batched within their own action; modify MUST be one call per entry.
290
170
  - NEVER invoke `fab_review action="approve"` without at least one `pending_paths` entry.
291
171
  - NEVER infer a layer-flip target — the user MUST choose via AskUserQuestion.
292
- - MUST preserve protected tokens exactly: `stable_id`, `pending_path`, `layer`, `team`, `personal`, `knowledge_promoted`, `knowledge_layer_changed`, `knowledge_proposed`, `fab_review`, `MUST`, `NEVER`.
293
-
294
- ## Output Contract
295
-
296
- After each invocation, the skill MUST produce a brief roll-up to the user:
297
-
298
- ```md
299
- # Review Summary — mode={pending|topic|health|revisit}
300
- - Listed: N entries
301
- - Approved: M (new stable_ids: KT-D-12, KT-G-4, KP-P-2)
302
- - Rejected: R
303
- - Modified: U (incl. K layer flips)
304
- - Deferred: D
305
- - Skipped: S
306
-
307
- ## Events appended (.fabric/events.jsonl tail)
308
- - knowledge_promote_started ×M
309
- - knowledge_promoted ×M
310
- - knowledge_layer_changed ×K
311
- - knowledge_rejected ×R
312
- - knowledge_deferred ×D
313
- ```
314
-
315
- Also surface a one-line `git status` of `.fabric/knowledge/` so the user sees the file moves caused by approve / layer-flip.
316
-
317
- ## Worked Examples
318
-
319
- ### Example A — pending mode with semantic check flagging a duplicate (user chooses reject)
320
-
321
- User: "review the pending knowledge".
322
-
323
- Inferred mode: `pending` (Step 1 keyword "review … pending").
324
-
325
- Skill flow:
326
-
327
- 1. `fab_review action="list"` → returns 3 pending items.
328
- 2. Semantic check on item 2 (`pending/decisions/single-cjs-hook.md`) — `fab_review action="search"` with `query="single cjs hook"` filter `type=decisions` returns canonical `KT-D-0007--single-cjs-hook-across-clients.md` (similarity high).
329
- 3. Display block:
330
-
331
- ```md
332
- ## [type=decisions] [layer=team] pending_path=knowledge/pending/decisions/single-cjs-hook.md
333
- Title: 单 .cjs hook 跨客户端
334
- Summary: 三客户端 stdout JSON 格式一致,单脚本即可。
335
- ⚠ Possible duplicate of KT-D-0007 (similarity 0.84 on title + summary)
336
- ```
337
-
338
- 4. AskUserQuestion fires; user picks `reject`.
339
- 5. Free-text follow-up: user types `duplicate of KT-D-7`.
340
- 6. `fab_review action="reject"` with `pending_paths=["knowledge/pending/decisions/single-cjs-hook.md"]` and `reason="duplicate of KT-D-7"`.
341
- 7. Roll-up reports: 1 rejected, 0 approved, events appended.
342
-
343
- ### Example B — revisit mode with layer flip (KT → KP)
344
-
345
- User: "look at KT-G-3, that's actually personal not team".
346
-
347
- Inferred mode: `revisit` (Step 1 keyword "look at <id>").
348
-
349
- Skill flow:
350
-
351
- 1. Read `.fabric/knowledge/team/guidelines/KT-G-0003--indent-style.md`. Display body to user.
352
- 2. AskUserQuestion `{options: ["approve", "modify", "reject", "skip"]}` — user picks `modify`.
353
- 3. Skill detects user-stated intent "actually personal not team" — surface AskUserQuestion `{options: ["team", "personal"]}` with current layer=team noted; user confirms `personal`.
354
- 4. Call:
355
-
356
- ```ts
357
- mcp__fabric__fab_review({
358
- action: "modify",
359
- pending_path: "knowledge/team/guidelines/KT-G-0003--indent-style.md",
360
- changes: { layer: "personal" }
361
- })
362
- ```
172
+ - MUST preserve protected tokens exactly: `stable_id`, `pending_path`, `layer`, `team`, `personal`, `knowledge_promoted`, `knowledge_layer_changed`, `knowledge_proposed`, `knowledge_scope_degraded`, `fab_review`, `MUST`, `NEVER`, `relevance_scope`, `relevance_paths`, `narrow`, `broad`, `proposed_reason`, `session_context`.
363
173
 
364
- 5. Server returns `{prior_stable_id: "KT-G-0003", new_stable_id: "KP-G-0001"}`.
365
- 6. Roll-up: `Layer flipped: KT-G-0003 → KP-G-0001`. `git status` shows the rename across layer roots.
174
+ ## Output Contract & events.jsonl Constraint (ref-only)
366
175
 
367
- ### Example C health mode finding stale entries (defer 2, demote 1)
176
+ After each invocation, produce a bilingual `# Review Summary` (en) / `# Review 汇总` (zh-CN) roll-up: listed/approved/rejected/modified/deferred/skipped counts + new stable_ids + tail of `.fabric/events.jsonl` events (`knowledge_promote_started`, `knowledge_promoted`, `knowledge_layer_changed`, `knowledge_rejected`, `knowledge_deferred`). Also surface `git status` of `.fabric/knowledge/` so file moves are visible.
368
177
 
369
- User: "anything stale in our knowledge base?"
178
+ events.jsonl appends MUST stay single-line + ≤4KB (POSIX `PIPE_BUF` atomicity).
370
179
 
371
- Inferred mode: `health` (Step 1 keyword "stale").
180
+ `Read ref/output-contract.md` for full bilingual rollup templates + per-field self-truncate caps (`session_context` 500 chars; `source_sessions` 5 entries; `recent_paths` 20 entries; `user_messages_summary` 500 chars).
372
181
 
373
- Skill flow:
182
+ ## Worked Examples (ref-only)
374
183
 
375
- 1. `fab_review action="list"` (no filter) + tail events.jsonl for trailing-30d demoted/layer_changed counts.
376
- 2. Compute stale candidates: 3 pending entries with mtime >14d (KP-G-5 candidate-pending, KT-P-9 candidate-pending, KP-G-3 canonical draft with no evidence-append in 21d).
377
- 3. Render dashboard then loop per stale item.
378
- 4. Per-item AskUserQuestion fires:
379
- - KP-G-5 → user picks `defer` (until="2026-06-01") → `fab_review action="defer"` with `until` set.
380
- - KT-P-9 → user picks `defer` (no until) → `fab_review action="defer"` with no `until`.
381
- - KP-G-3 → user picks `demote` → `fab_review action="modify"` with `changes.maturity="draft"` (already draft; equivalently demote means reject if pending — skill chooses correct action by inspecting current state).
382
- 5. Roll-up: 2 deferred, 1 modified, events appended (`knowledge_deferred ×2`, `knowledge_promote_started/promoted` not relevant; `knowledge_layer_changed` not relevant).
184
+ Four worked examples (pending-mode dedupe / revisit layer-flip / health mode / narrowing imported entries) live in `ref/worked-examples.md`. Load when you want to see how Mode + AskUserQuestion + MCP-call shape composes on real candidate sets.