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

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 (30) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-SRX7WZUG.js → chunk-BATF4PEJ.js} +2 -2
  3. package/dist/{chunk-PNRWNUFX.js → chunk-XVS4F3P6.js} +105 -4
  4. package/dist/{config-5CH4EJQ2.js → config-XJIPZNUP.js} +1 -1
  5. package/dist/{doctor-E26YO67D.js → doctor-2FCRAWDZ.js} +23 -8
  6. package/dist/index.js +7 -7
  7. package/dist/{install-YSFVNY3T.js → install-HOTE5BPA.js} +61 -4
  8. package/dist/{onboard-coverage-6MN3CYHT.js → onboard-coverage-MFCAEBDO.js} +4 -4
  9. package/dist/{plan-context-hint-CXTLNVSV.js → plan-context-hint-UQLRKGBZ.js} +2 -2
  10. package/dist/{uninstall-VLLJG7JT.js → uninstall-BIJ5GLEU.js} +1 -1
  11. package/package.json +3 -4
  12. package/templates/hooks/cite-policy-evict.cjs +242 -0
  13. package/templates/hooks/configs/claude-code.json +11 -0
  14. package/templates/hooks/fabric-hint.cjs +11 -1
  15. package/templates/hooks/knowledge-hint-broad.cjs +34 -6
  16. package/templates/hooks/knowledge-hint-narrow.cjs +106 -1
  17. package/templates/hooks/lib/summary-fallback.cjs +210 -0
  18. package/templates/skills/fabric-archive/SKILL.md +38 -255
  19. package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
  20. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +1 -1
  21. package/templates/skills/fabric-archive/ref/i18n-policy.md +1 -1
  22. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +10 -10
  23. package/templates/skills/fabric-import/ref/i18n-policy.md +1 -1
  24. package/templates/skills/fabric-review/SKILL.md +55 -413
  25. package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
  26. package/templates/skills/fabric-review/ref/i18n-policy.md +1 -1
  27. package/templates/skills/fabric-review/ref/modify-flow.md +95 -0
  28. package/templates/skills/fabric-review/ref/output-contract.md +58 -0
  29. package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
  30. package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
@@ -4,58 +4,47 @@ description: 审 .fabric/knowledge pending+canonical (NOT PR review):approve/rej
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: pending count ≥ `review_hint_pending_count` (config-resolved, default 10) OR oldest pending age ≥ `review_hint_pending_age_days` (config-resolved, default 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 (UX i18n Policy class 2 — errors/preconditions):
17
+ If none hold, stop the skill and tell the user:
18
18
 
19
19
  - zh-CN: `没有触发 review 信号;如需手动 review 请显式调用 fabric-review`
20
20
  - en: `No review signal detected; to manually review, explicitly invoke fabric-review`
21
21
 
22
- (Render per `fabric_language` resolved in Config Load below.)
23
-
24
22
  This skill is `Infer-not-Ask` for mode and `Ask-when-genuine` for per-item actions:
25
23
 
26
24
  - Mode (pending / topic / health / revisit) is INFERRED from context — NEVER surfaced via AskUserQuestion
27
25
  - Per-item action (approve / reject / modify / defer) IS surfaced via AskUserQuestion — the user must judge
28
- - Layer-flip target (team vs personal) IS surfaced via AskUserQuestion when modify path includes layer change
29
-
30
- Required preconditions before any fab_review call:
26
+ - Layer-flip target (team vs personal) IS surfaced via AskUserQuestion when modify includes layer change
31
27
 
32
- - `.fabric/` directory exists in the project (or `~/.fabric/` for personal layer)
33
- - `mcp__fabric__fab_review` MCP tool is registered and reachable
34
- - `.fabric/agents.meta.json` is present (the id allocator reads it on approve)
35
- - `.fabric/events.jsonl` exists (tolerate ENOENT — empty ledger is normal first-run)
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).
36
29
 
37
30
  ### Config Load
38
31
 
39
- Before any mode inference work, the skill MUST read
40
- `.fabric/fabric-config.json` to resolve the following tunables (with documented
41
- defaults if absent):
32
+ Read `.fabric/fabric-config.json`; resolve:
42
33
 
43
34
  | Config field | Default | Used by |
44
35
  |---|---|---|
45
- | `review_topic_result_cap` | 8 | topic mode top-N rendering cap |
46
- | `review_stale_pending_days` | 14 | health mode stale-pending detection threshold (days) |
47
- | `review_hint_pending_count` | 10 | precondition overflow signal (pending-count branch) |
48
- | `review_hint_pending_age_days` | 7 | precondition overflow signal (oldest-pending-age branch) |
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) |
49
40
 
50
- If `.fabric/fabric-config.json` is missing or unreadable, use defaults silently.
41
+ Missing or unreadable defaults silently.
51
42
 
52
43
  ### UX i18n Policy
53
44
 
54
- Read `.fabric/fabric-config.json` → `fabric_language` (`zh-CN` / `en` / `zh-CN-hybrid` / `match-existing`). Emit user-facing prose in the resolved variant. Protected tokens (`fab_review`, `fab_extract_knowledge`, `relevance_scope`, layer/scope enum values, `stable_id`, the verbatim `强 team` / `强 personal` / `默认 team` block) are NEVER translated.
55
-
56
- `AskUserQuestion` policy: `header` + `question` translate; `options[]` are routing keys — stay English regardless of locale.
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).
57
46
 
58
- **For the full 5-class taxonomy + edge cases:** `Read packages/cli/templates/skills/fabric-review/ref/i18n-policy.md` (or `.claude/skills/fabric-review/ref/i18n-policy.md` post-install).
47
+ `Read ref/i18n-policy.md` for the full 5-class taxonomy + edge cases.
59
48
 
60
49
  ## Mode Inference (System Infers — NEVER Ask)
61
50
 
@@ -63,11 +52,11 @@ Read `.fabric/fabric-config.json` → `fabric_language` (`zh-CN` / `en` / `zh-CN
63
52
  > "review 永远走 fabric-review skill,**模式从上下文推断**(4 种 mode:pending queue / by topic / health overview / revisit existing)"
64
53
  > "**AskUserQuestion 仅在真有选择时用**——'何种 mode' 不是真选择(系统能推断),'approve/reject/modify 单条' 是真选择"
65
54
 
66
- 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.
55
+ The skill MUST infer one of {`pending`, `topic`, `health`, `revisit`} BEFORE any user-facing output.
67
56
 
68
57
  ### 3-Step Inference Algorithm
69
58
 
70
- **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
+ **Step 1 — Recent user message keyword scan.** Match against priority order:
71
60
 
72
61
  | Keywords (zh-CN + en) | Inferred mode |
73
62
  |---|---|
@@ -76,351 +65,66 @@ The skill MUST infer one of {`pending`, `topic`, `health`, `revisit`} before any
76
65
  | "what's stale", "demote old", "health check", "过期的", "陈旧的", "整理一下" | `health` |
77
66
  | "look at <id>", "revisit KT-…", "show <slug>", "再看下 <id>", "回顾" | `revisit` |
78
67
 
79
- If exactly one row matches, lock that mode and skip to Step 3.
80
-
81
- **Step 2 — events.jsonl tail scan.** If Step 1 yielded zero or multiple matches, read the tail (last 200 lines) of `.fabric/events.jsonl`:
82
-
83
- - Count `knowledge_proposed` events since the last `knowledge_promoted` event. If `>5` recent proposals → infer `pending` (write side has piled up; review is overdue).
84
- - Count `knowledge_demoted` or `lint`-class events in the last 24h. If `≥1` → infer `health` (corpus quality signal already firing).
85
- - If a recent `knowledge_layer_changed` event exists for the entry the user just referenced → infer `revisit`.
68
+ Exactly one row matches lock mode, skip to Step 3.
86
69
 
87
- **Step 3Pending count default.** If Step 1 and Step 2 both produced no signal, glob `.fabric/knowledge/pending/**/*.md`:
70
+ **Step 2events.jsonl tail scan.** If Step 1 yielded 0 or >1 matches, tail (last 200 lines) `.fabric/events.jsonl`:
88
71
 
89
- - If pending count ≥ `review_hint_pending_count` (config-resolved, default 10) OR oldest pending file mtime is older than `review_hint_pending_age_days` (config-resolved, default 7) days infer `pending` (overflow signal same threshold the Stop-hook uses).
90
- - Otherwise default to `pending` (most common review entry point).
72
+ - `>5` recent `knowledge_proposed` since last `knowledge_promoted` → `pending` (write side piled up).
73
+ - `≥1` `knowledge_demoted` or `lint` events in 24h → `health` (corpus quality signal).
74
+ - Recent `knowledge_layer_changed` for the entry user referenced → `revisit`.
91
75
 
92
- ### Inference Examples (Sample User MessagesExpected Mode)
76
+ **Step 3 Pending count default.** Still ambiguous glob `.fabric/knowledge/pending/**/*.md`:
93
77
 
94
- - "review the pending knowledge" → `pending` (Step 1 keyword "review pending")
95
- - "find anything about deepMerge" → `topic` (Step 1 keyword "find … about")
96
- - "anything stale in our knowledge base?" → `health` (Step 1 keyword "stale")
97
- - "look at KT-D-7" → `revisit` (Step 1 keyword "look at <id>")
98
- - (Stop-hook fired with signal=review, no user typing) → `pending` (Step 3 default, overflow threshold tripped)
78
+ - Count `review_hint_pending_count` (default 10) OR oldest mtime > `review_hint_pending_age_days` (default 7) → `pending` (overflow, same threshold as Stop-hook).
79
+ - Otherwise default `pending` (most common review entry point).
99
80
 
100
- ### Anti-Pattern (Hard Rule)
101
-
102
- 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.
81
+ `Read ref/per-mode-flows.md` for 5 inference examples and anti-pattern restatement.
103
82
 
104
83
  ## Per-Mode Flow
105
84
 
106
85
  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.
107
86
 
108
- ### Mode: pending — Approve / Reject / Modify Backlog
109
-
110
- 1. Call `fab_review` with `action: "list"`, no filters (or `filters.layer="both"` if user explicitly mentioned both layers).
111
- 2. Server returns `items[]` (each = `{pending_path, type, layer, maturity, tags?, title?, summary?}`).
112
- 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.
113
- 4. For each pending item, render a per-item block. v2.0.0-rc.7 T6: render
114
- `proposed_reason` (frontmatter) + `## Why proposed` line (body, 1-line enum
115
- explanation) + first line of `## Session context` so future-self has full
116
- context without re-reading the transcript. UX i18n Policy class 1 — roll-up
117
- templates; protected tokens (`pending_path`, `layer`, `team`, `decisions`,
118
- `proposed_reason`, `Tags`, etc.) appear verbatim in BOTH variants:
119
-
120
- en variant (`fabric_language === "en"`):
121
-
122
- ```md
123
- ## [type=decisions] [layer=team] pending_path=knowledge/pending/decisions/single-cjs-hook.md
124
- Title: Single .cjs hook across clients
125
- Summary: stdout JSON shape is identical across the three clients; one script suffices.
126
- Maturity: draft Tags: [hook, cli]
127
- Proposed reason: decision-confirmation — ≥2 alternatives weighed; rationale stated.
128
- Session context: Session goal: ship Stop-hook for v2 release.
129
- ⚠ Possible duplicate of KT-D-0007 (LLM subjective dup/subsumption judgement; thresholds intentionally not quantified)
130
- ```
131
-
132
- zh-CN variant (`fabric_language === "zh-CN"`):
133
-
134
- ```md
135
- ## [type=decisions] [layer=team] pending_path=knowledge/pending/decisions/single-cjs-hook.md
136
- 标题: 单 .cjs hook 跨客户端
137
- 摘要: 三客户端 stdout JSON 格式一致,单脚本即可。
138
- 成熟度: draft Tags: [hook, cli]
139
- Proposed reason: decision-confirmation — ≥2 候选方案经权衡后确认选型。
140
- Session context: Session goal: ship Stop-hook for v2 release.
141
- ⚠ 可能重复 KT-D-0007 (LLM 主观判断 dup/subsumption;具体阈值不可量化)
142
- ```
143
-
144
- The Skill MUST read `proposed_reason` from the pending file's frontmatter
145
- (parse the YAML block, key `proposed_reason`) and the `## Why proposed`
146
- line / first non-blank line of `## Session context` from the body. If
147
- either is missing on a pre-rc.7 pending entry, render the legacy fallback
148
- (UX i18n Policy class 1):
149
-
150
- - en: `Proposed reason: <legacy entry, no reason recorded>` and `Session context: <not recorded>`
151
- - zh-CN: `Proposed reason: <历史条目,未记录 reason>` 与 `Session context: <未记录>`
152
-
153
- …so the reviewer can still proceed.
154
-
155
- 5. Surface a per-item AskUserQuestion. UX i18n Policy class 5 — `header` +
156
- `question` translated; `options[]` array remain English routing keys:
157
-
158
- ```ts
159
- // EN
160
- AskUserQuestion({
161
- header: "Review pending entry",
162
- question: "What action for 'Single .cjs hook across clients'?",
163
- options: ["approve", "reject", "modify", "defer", "skip"]
164
- })
165
-
166
- // zh-CN
167
- AskUserQuestion({
168
- header: "审核 pending 条目",
169
- question: "对 '单 .cjs hook 跨客户端' 执行什么操作?",
170
- options: ["approve", "reject", "modify", "defer", "skip"] // 不翻译
171
- })
172
- ```
173
-
174
- 6. Route the user's choice:
175
- - `approve` → accumulate pending_path into a batch; flush via single `fab_review action="approve"` with `pending_paths=[…]` after the loop ends.
176
- - `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`.
177
- - `modify` → see Modify Sub-Flow below.
178
- - `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).
179
- - `skip` → no MCP call; move to next item.
180
- 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.
181
-
182
- ### Mode: topic — Search & Surface Findings
183
-
184
- 1. Extract the topic keyword(s) from the user's message (e.g. "find about deepMerge" → query="deepMerge").
185
- 2. Call `fab_review action="search"` with `query` and any obvious filters (if user said "team-only" → `filters.layer="team"`).
186
- 3. Server returns `items[]` ranked by relevance — these are entries already in `.fabric/knowledge/{layer}/{type}/` (NOT pending), unless `filters` says otherwise.
187
- 4. Render top-N (cap at `review_topic_result_cap`, config-resolved, default 8) results with title / summary / pending_path.
188
- 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.
189
- 6. NEVER surface a per-item AskUserQuestion just for browsing — only when the user signals an action verb.
190
-
191
- ### Mode: health — Corpus Health & Stale Detection
192
-
193
- 1. Call `fab_review action="list"` with `filters.maturity="draft"` (or no filter for full corpus inspection).
194
- 2. Tail `.fabric/events.jsonl` for layer_changed / demoted / rejected counts in the trailing 30 days.
195
- 3. Compute stale candidates: pending entries with mtime older than `review_stale_pending_days` (config-resolved, default 14) OR maturity=draft entries with no recent evidence-append events.
196
- 4. Render a corpus dashboard. UX i18n Policy class 1 — roll-up templates; render per `fabric_language`:
197
-
198
- en variant:
199
-
200
- ```md
201
- ## Health Overview
202
- - Pending: 12 entries (oldest 18d) — recommend `defer` or `reject`
203
- - Drafts: 8 (3 are stale candidates: KP-G-3, KP-G-5, KT-P-9)
204
- - Layer flips (30d): 2
205
- - Rejections (30d): 1
206
- ```
207
-
208
- zh-CN variant:
209
-
210
- ```md
211
- ## 健康度总览
212
- - Pending: 12 条 (最旧 18 天) — 建议 `defer` 或 `reject`
213
- - Drafts: 8 条 (3 条为陈旧候选: KP-G-3, KP-G-5, KT-P-9)
214
- - Layer 切换 (30 天): 2
215
- - 已驳回 (30 天): 1
216
- ```
217
-
218
- 5. For each stale candidate, surface AskUserQuestion. UX i18n Policy class 5 — `header` + `question` translated; `options[]` remain English routing keys:
219
-
220
- ```ts
221
- // EN
222
- AskUserQuestion({
223
- header: "Stale entry triage",
224
- question: "Action for stale entry '{title}'?",
225
- options: ["defer", "demote", "skip"]
226
- })
227
-
228
- // zh-CN
229
- AskUserQuestion({
230
- header: "陈旧条目处理",
231
- question: "对陈旧条目 '{title}' 执行什么操作?",
232
- options: ["defer", "demote", "skip"] // 不翻译
233
- })
234
- ```
235
-
236
- 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).
237
-
238
- ### Mode: revisit — Specific Entry Deep Dive
239
-
240
- 1. The user referenced a specific entry (by id `KT-D-7` or by slug `single-cjs-hook`).
241
- 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`.
242
- 3. Display the full body (frontmatter + content). Tail the events.jsonl for any history events tagged with this stable_id.
243
- 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).
244
-
245
- ## Semantic Check Guidance (LLM-Assisted Duplicate / Contradiction Detection)
246
-
247
- > 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)"
248
-
249
- 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):
250
-
251
- For each pending entry to be presented:
252
-
253
- 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.
254
- 2. Compare semantically (LLM judgment, not string match). 三类判断均为 LLM 主观判断 dup/subsumption;具体阈值不可量化(不使用百分比 / 相似度数值伪精度):
255
- - **Duplicate** — same essential claim. 标题与摘要表达同一核心结论,pending 未提供新证据或新上下文。Flag: `⚠ Possible duplicate of <stable_id>`.
256
- - **Contradiction** — opposing claims about the same subject. 例:一个 entry 说 "use X",pending 说 "avoid X",且作用域一致。Flag: `⚠ Contradicts <stable_id>`.
257
- - **Subsumption** — pending fully covered by an existing entry plus extras. Flag: `⚠ Subsumed by <stable_id>; consider modify-to-merge`.
258
- 3. Surface the flag in the per-item display block (see pending mode step 4).
259
- 4. The user decides:
260
- - Still approve → flag is informational; pending becomes canonical alongside the existing entry.
261
- - Modify-to-harmonize → user supplies edits via `modify` action; consider merging language with the existing entry.
262
- - Reject as duplicate → reason field MUST cite the existing stable_id (e.g. `reason="duplicate of KT-D-7"`).
263
-
264
- 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.
265
-
266
- ## Narrowing Imported Entries
267
-
268
- The fabric-import skill creates pending entries with `relevance_scope=broad`
269
- + `relevance_paths=[]` as a deliberate contract — it cannot derive paths from
270
- git history. **Narrowing imported entries is fabric-review's responsibility.**
271
-
272
- ### Detection
273
-
274
- An entry is "import-origin" when `source_sessions[0]` starts with
275
- `fabric-import-` (e.g. `fabric-import-2026-05-10`).
276
-
277
- ### Pending mode rendering
278
-
279
- For each import-origin entry, prepend one warning line to the display block. UX i18n Policy class 1 — roll-up templates; the protected tokens `relevance_scope`, `relevance_paths`, `broad` appear verbatim in BOTH variants:
87
+ - **`pending`**list pending entries → run Semantic Check (see `ref/semantic-check.md`) per-item AskUserQuestion `{approve, reject, modify, defer, skip}` → route per choice (modify path = `ref/modify-flow.md`).
88
+ - **`topic`** — extract keywords → `fab_review action="search"` → render top-N (cap `review_topic_result_cap`) → only AskUserQuestion when user signals action verb.
89
+ - **`health`** `fab_review action="list"` + tail events compute stale render dashboard → per-stale AskUserQuestion `{defer, demote, skip}`.
90
+ - **`revisit`** user referenced specific id/slug → `Read` canonical file directly OR `fab_review action="list"` with narrow filters display body + history → AskUserQuestion only if pending.
280
91
 
281
- - en: `⚠ Imported (relevance_scope=broad, relevance_paths=[]) pick 'modify' + say 'narrow to <paths>' to bind scope.`
282
- - zh-CN: `⚠ Imported (relevance_scope=broad, relevance_paths=[]) — 选择 'modify' 并指定 'narrow to <paths>' 以收紧作用域。`
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.
283
93
 
284
- This hint is informational. The user MAY ignore it; broad+[] is a valid
285
- final state for cross-cutting knowledge.
94
+ ## Semantic Check (LLM-Assisted Duplicate / Contradiction Detection)
286
95
 
287
- ### Modify follow-up narrow scope
96
+ > Boundary B (locked): "extraction / classification / layer / slug / mode / **semantic dedup** → Skill (LLM); file write / frontmatter / idempotency / counter / layer-flip / atomic promote → MCP (deterministic)"
288
97
 
289
- When the user picks `modify` on an import-origin entry, surface
290
- AskUserQuestion with an extended option list. UX i18n Policy class 5 — `header` + `question` translated; `options[]` remain English routing keys:
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:
291
99
 
292
- ```ts
293
- // EN
294
- AskUserQuestion({
295
- header: "Modify imported entry",
296
- question: "What aspect of '{title}' to modify?",
297
- options: ["narrow scope", "edit summary", "change layer", "change maturity", "skip"]
298
- })
100
+ - `⚠ Possible duplicate of <stable_id>` — same essential claim
101
+ - `⚠ Contradicts <stable_id>` — opposing claims, same scope
102
+ - `⚠ Subsumed by <stable_id>; consider modify-to-merge` — fully covered
299
103
 
300
- // zh-CN
301
- AskUserQuestion({
302
- header: "修改 imported 条目",
303
- question: "要修改 '{title}' 的哪一项?",
304
- options: ["narrow scope", "edit summary", "change layer", "change maturity", "skip"] // 不翻译
305
- })
306
- ```
307
-
308
- When user picks "narrow scope":
309
- 1. Free-text follow-up. UX i18n Policy class 3 — confirmation prompts:
310
- - en: `Type relevance_paths (comma-separated globs, e.g. packages/server/src/retry/**, packages/server/src/lib/retry.ts)`
311
- - zh-CN: `请输入 relevance_paths (逗号分隔的 glob,例如 packages/server/src/retry/**, packages/server/src/lib/retry.ts)`
312
- 2. Call fab_review action="modify" with:
313
- changes: { relevance_scope: "narrow", relevance_paths: [<parsed paths>] }
314
- 3. Display the resolved frontmatter to confirm.
315
-
316
- ### Special cases
317
-
318
- - Layer=personal entries: server auto-degrades narrow → broad+[]; surface
319
- the `knowledge_scope_degraded` event back to the user.
320
- - Non-import-origin entries: modify can still narrow (just doesn't show
321
- this UX nudge — user types it as a normal modify).
322
-
323
- ## Modify Sub-Flow & Layer-Flip Rules
104
+ Thresholds intentionally NOT quantified (no similarity %). User decides: still-approve (flag informational), modify-to-harmonize, or reject-as-duplicate (reason MUST cite existing stable_id).
324
105
 
325
- `modify` is the only action that mutates frontmatter or stable_id. It accepts `changes` of shape `{title?, summary?, layer?, maturity?, tags?}`. Server semantics:
106
+ DO NOT AskUserQuestion "is this a duplicate?" LLM already judged. User only chooses approve/reject/modify.
326
107
 
327
- - **title / summary / tags / maturity changes** → in-place rewrite; stable_id PRESERVED; emits `knowledge_slug_renamed` only when slug derives from title.
328
- - **layer change** → the ONLY legal stable_id mutation in the system.
108
+ `Read ref/semantic-check.md` for full procedure + 三类判断的细化定义.
329
109
 
330
- ### Layer-Flip Rules (the only legal stable_id mutation)
110
+ ## Narrowing Imported Entries & Modify Sub-Flow
331
111
 
332
- Triggered when `changes.layer` differs from current entry layer. Server-side transaction:
112
+ `modify` is the only action that mutates frontmatter or stable_id. Two paths:
333
113
 
334
- 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/`).
335
- 2. `git mv <old-layer>/<type>/<old-id>--<slug>.md <new-layer>/<type>/<new-id>--<slug>.md`.
336
- 3. Append `knowledge_layer_changed` event with `{from_layer, to_layer, prior_stable_id, new_stable_id}`.
337
- 4. Server response includes `prior_stable_id` and `new_stable_id` — surface BOTH to the user in the roll-up.
114
+ - **Title/summary/tags/maturity** in-place rewrite; stable_id PRESERVED.
115
+ - **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.
338
116
 
339
- Skill responsibilities for layer flip:
117
+ **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`.
340
118
 
341
- - 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.
342
- - 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.
343
-
344
- ### Modify Examples
345
-
346
- ```ts
347
- // Maturity bump only (no id change)
348
- mcp__fabric__fab_review({
349
- action: "modify",
350
- pending_path: "knowledge/team/decisions/KT-D-0007--single-cjs-hook.md",
351
- changes: { maturity: "verified" }
352
- })
353
-
354
- // Layer flip team → personal (id WILL change)
355
- mcp__fabric__fab_review({
356
- action: "modify",
357
- pending_path: "knowledge/team/guidelines/KT-G-0003--indent-style.md",
358
- changes: { layer: "personal" }
359
- })
360
- ```
119
+ `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.
361
120
 
362
121
  ## AskUserQuestion Policy
363
122
 
364
- ### DO ask (genuine choices that require human judgment)
365
-
366
- - Per-pending-item action: `["approve", "reject", "modify", "defer", "skip"]`
367
- - Per-stale-item action (health mode): `["defer", "demote", "skip"]`
368
- - Layer-flip target when modify path includes layer change: `["team", "personal"]`
369
- - Reject reason follow-up (free-text, may use AskUserQuestion's free-form variant if available, otherwise plain prompt)
123
+ **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).
370
124
 
371
- ### DO NOT ask (system must infer or operate deterministically)
125
+ **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).
372
126
 
373
- - Mode picking (pending / topic / health / revisit) INFERRED per the 3-step algorithm
374
- - Whether to invoke this skill at all — Stop-hook signal or explicit user request decides
375
- - Whether an entry is a duplicate — LLM semantic check answers
376
- - Frontmatter parsing — deterministic, never asked
377
- - Allocate next id — deterministic via KnowledgeIdAllocator, never asked
378
-
379
- ### Per-Item Question Phrasing Template
380
-
381
- UX i18n Policy class 5 — `header` + `question` translated per `fabric_language`; `options[]` arrays remain English routing keys in BOTH variants. Choose the variant matching the resolved language; the structure (field names, options) is identical.
382
-
383
- en variant:
384
-
385
- ```ts
386
- AskUserQuestion({
387
- header: "Review pending entry",
388
- question: "What action for '{title}'? ({pending_path})",
389
- options: ["approve", "reject", "modify", "defer", "skip"]
390
- })
391
- ```
392
-
393
- zh-CN variant:
394
-
395
- ```ts
396
- AskUserQuestion({
397
- header: "审核 pending 条目",
398
- question: "对 '{title}' 执行什么操作?({pending_path})",
399
- options: ["approve", "reject", "modify", "defer", "skip"] // 不翻译 — routing key
400
- })
401
- ```
402
-
403
- For layer-flip target.
404
-
405
- en variant:
406
-
407
- ```ts
408
- AskUserQuestion({
409
- header: "Layer-flip target",
410
- question: "Move '{title}' to which layer? (current: {current_layer})",
411
- options: ["team", "personal"]
412
- })
413
- ```
414
-
415
- zh-CN variant:
416
-
417
- ```ts
418
- AskUserQuestion({
419
- header: "Layer 切换目标",
420
- question: "将 '{title}' 切换到哪一层?(当前: {current_layer})",
421
- options: ["team", "personal"] // 不翻译 — routing key
422
- })
423
- ```
127
+ `Read ref/askuserquestion-policy.md` for full DO/DO NOT lists + bilingual per-item question phrasing templates (pending action / layer-flip target).
424
128
 
425
129
  ## Decision Tree — Is This Entry Approvable?
426
130
 
@@ -461,76 +165,14 @@ Pending entry presented for review
461
165
  - NEVER infer a layer-flip target — the user MUST choose via AskUserQuestion.
462
166
  - 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`.
463
167
 
464
- ## Output Contract
168
+ ## Output Contract & events.jsonl Constraint (ref-only)
465
169
 
466
- After each invocation, the skill MUST produce a brief roll-up to the user. UX i18n Policy class 1 roll-up templates; render per `fabric_language`. Protected tokens (event-type strings such as `knowledge_promoted` / `knowledge_layer_changed` / `knowledge_rejected` / `knowledge_deferred`, plus `.fabric/events.jsonl`) appear verbatim in BOTH variants:
170
+ 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.
467
171
 
468
- en variant (`fabric_language === "en"`):
469
-
470
- ```md
471
- # Review Summary — mode={pending|topic|health|revisit}
472
- - Listed: N entries
473
- - Approved: M (new stable_ids: KT-D-12, KT-G-4, KP-P-2)
474
- - Rejected: R
475
- - Modified: U (incl. K layer flips)
476
- - Deferred: D
477
- - Skipped: S
478
-
479
- ## Events appended (.fabric/events.jsonl tail)
480
- - knowledge_promote_started ×M
481
- - knowledge_promoted ×M
482
- - knowledge_layer_changed ×K
483
- - knowledge_rejected ×R
484
- - knowledge_deferred ×D
485
- ```
486
-
487
- zh-CN variant (`fabric_language === "zh-CN"`):
488
-
489
- ```md
490
- # Review 汇总 — mode={pending|topic|health|revisit}
491
- - 列出: N 条
492
- - 已批准: M (新分配 stable_ids: KT-D-12, KT-G-4, KP-P-2)
493
- - 已驳回: R
494
- - 已修改: U (含 K 次 layer 切换)
495
- - 已延后: D
496
- - 已跳过: S
497
-
498
- ## 追加事件 (.fabric/events.jsonl 末尾)
499
- - knowledge_promote_started ×M
500
- - knowledge_promoted ×M
501
- - knowledge_layer_changed ×K
502
- - knowledge_rejected ×R
503
- - knowledge_deferred ×D
504
- ```
172
+ events.jsonl appends MUST stay single-line + ≤4KB (POSIX `PIPE_BUF` atomicity).
505
173
 
506
- ### events.jsonl Constraint Note
507
-
508
- Event lines appended to `.fabric/events.jsonl` are subject to POSIX
509
- single-write atomicity: only writes ≤ 4KB (`PIPE_BUF`) are guaranteed
510
- atomic via `Bash: echo "..." >> file`. Lines exceeding 4KB risk
511
- interleaved corruption under concurrent skill + server writes to the
512
- same ledger.
513
-
514
- Skills MUST ensure:
515
-
516
- - Each event JSON line is a **single line** (no embedded newlines;
517
- escape `\n` in any string value).
518
- - `session_context` and other free-form text fields **self-truncate** to
519
- keep the entire serialized line under 4KB. Suggested per-field caps:
520
- `session_context` first 500 chars; `source_sessions` cap at 5
521
- entries; `recent_paths` cap at 20 entries; `user_messages_summary`
522
- first 500 chars.
523
- - If approaching the 4KB ceiling after the per-field caps, drop optional
524
- fields (e.g. tags / extra metadata) **before** truncating semantic
525
- content (the summary / context that carries the actual observation).
526
- - The promote / reject / modify / defer events listed above are emitted
527
- by the MCP server via `appendEventLedgerEvent` and are already
528
- length-bounded server-side; this constraint applies to any event the
529
- skill itself appends directly to the ledger (rare, but possible for
530
- diagnostic markers).
531
-
532
- Also surface a one-line `git status` of `.fabric/knowledge/` so the user sees the file moves caused by approve / layer-flip.
174
+ `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).
533
175
 
534
176
  ## Worked Examples (ref-only)
535
177
 
536
- Four worked examples (pending-mode dedupe / revisit layer-flip / health mode / narrowing imported entries) live in `packages/cli/templates/skills/fabric-review/ref/worked-examples.md` (or `.claude/skills/fabric-review/ref/worked-examples.md` post-install). Load when you want to see how the full Mode + AskUserQuestion + MCP-call shape composes on real candidate sets.
178
+ 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.
@@ -0,0 +1,66 @@
1
+ # AskUserQuestion Policy — fabric-review
2
+
3
+ Full DO / DO NOT split + per-item question phrasing templates referenced from SKILL.md.
4
+
5
+ ## DO ask (genuine choices that require human judgment)
6
+
7
+ - Per-pending-item action: `["approve", "reject", "modify", "defer", "skip"]`
8
+ - Per-stale-item action (health mode): `["defer", "demote", "skip"]`
9
+ - Layer-flip target when modify path includes layer change: `["team", "personal"]`
10
+ - Reject reason follow-up (free-text, may use AskUserQuestion's free-form variant if available, otherwise plain prompt)
11
+
12
+ ## DO NOT ask (system must infer or operate deterministically)
13
+
14
+ - Mode picking (pending / topic / health / revisit) — INFERRED per the 3-step algorithm
15
+ - Whether to invoke this skill at all — Stop-hook signal or explicit user request decides
16
+ - Whether an entry is a duplicate — LLM semantic check answers
17
+ - Frontmatter parsing — deterministic, never asked
18
+ - Allocate next id — deterministic via KnowledgeIdAllocator, never asked
19
+
20
+ ## Per-Item Question Phrasing Template
21
+
22
+ UX i18n Policy class 5 — `header` + `question` translated per `fabric_language`; `options[]` arrays remain English routing keys in BOTH variants. Choose the variant matching the resolved language; the structure (field names, options) is identical.
23
+
24
+ ### Pending entry action
25
+
26
+ **en variant**:
27
+
28
+ ```ts
29
+ AskUserQuestion({
30
+ header: "Review pending entry",
31
+ question: "What action for '{title}'? ({pending_path})",
32
+ options: ["approve", "reject", "modify", "defer", "skip"]
33
+ })
34
+ ```
35
+
36
+ **zh-CN variant**:
37
+
38
+ ```ts
39
+ AskUserQuestion({
40
+ header: "审核 pending 条目",
41
+ question: "对 '{title}' 执行什么操作?({pending_path})",
42
+ options: ["approve", "reject", "modify", "defer", "skip"] // 不翻译 — routing key
43
+ })
44
+ ```
45
+
46
+ ### Layer-flip target
47
+
48
+ **en variant**:
49
+
50
+ ```ts
51
+ AskUserQuestion({
52
+ header: "Layer-flip target",
53
+ question: "Move '{title}' to which layer? (current: {current_layer})",
54
+ options: ["team", "personal"]
55
+ })
56
+ ```
57
+
58
+ **zh-CN variant**:
59
+
60
+ ```ts
61
+ AskUserQuestion({
62
+ header: "Layer 切换目标",
63
+ question: "将 '{title}' 切换到哪一层?(当前: {current_layer})",
64
+ options: ["team", "personal"] // 不翻译 — routing key
65
+ })
66
+ ```
@@ -33,7 +33,7 @@ Rendering rule:
33
33
 
34
34
  - `fabric_language === "zh-CN"` → emit the zh-CN variant; pure monolingual, no language mixing inside a single user-facing block.
35
35
  - `fabric_language === "en"` → emit the en variant; pure monolingual, no language mixing inside a single user-facing block.
36
- - `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. `fab install`), file paths, technical concepts (`Skill`, `SessionStart`, `hook`, `MCP`, `revision_hash`, `pending`, `proven`, `verified`, `draft`).
36
+ - `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`).
37
37
  - `fabric_language === "match-existing"` or any other value → emit the en variant; pure monolingual.
38
38
 
39
39
  Protected tokens (`fab_review`, `relevance_scope`, `relevance_paths`,