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

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.
@@ -4,300 +4,104 @@ description: 归档对话洞察到 .fabric/knowledge/pending (NOT code review).
4
4
  allowed-tools: Read, Glob, Grep, Bash, mcp__fabric__fab_extract_knowledge
5
5
  ---
6
6
 
7
- > **Surface**: This is a Skill (AI-driven, LLM judgment over session digests). See [`docs/surfaces.md`](https://github.com/fenglimg/fabric/blob/main/docs/surfaces.md) for the CLI / Skill / MCP boundary.
7
+ > **Surface**: Skill (LLM judgment over session digests). 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":"..."}` mentioning fabric-archive
14
- - The user typed an explicit archive request (e.g. "archive what we just did", "fabric archive")
13
+ - Stop-hook printed stdout JSON `{"decision":"block","reason":"..."}` mentioning fabric-archive
14
+ - User typed an explicit archive request (e.g. "archive what we just did", "fabric archive")
15
15
  - A task wrap-up moment where the agent itself判定 a worth-keeping insight has surfaced
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 (UX i18n Policy class 2):
18
18
 
19
19
  - zh-CN: `没有触发归档信号;如需手动归档请显式调用 fabric-archive`
20
20
  - en: `No archive signal detected; to manually archive, explicitly invoke fabric-archive`
21
21
 
22
- (Render per `fabric_language` resolved in Phase 0.5 Config Load below.)
22
+ Render per `fabric_language` resolved in Phase 0.5.
23
23
 
24
- This skill is `Check-not-Ask`, not a preference interview:
25
-
26
- - **Phase 1.5 (rc.23 F8c) first-run onboard phase** — checks S5 onboard-slot coverage; if unclaimed slots remain, prompts user to fill / dismiss / skip before proceeding to normal archive flow
27
- - Phase 2 proactively gathers candidate evidence from the session
28
- - Phase 2.5 viability gate aborts the skill if the session lacks any archive-signal (anti-archive guard)
29
- - Phase 3 classifies / layers / slugs each candidate and presents one batch review for user correction
30
- - Phase 3.5 assigns `relevance_scope=narrow|broad` and derives `relevance_paths` from edit history (rc.5 single-signal source)
31
- - Phase 4 calls `fab_extract_knowledge` once per confirmed candidate
24
+ This skill is `Check-not-Ask`, not a preference interview. Phase 2 proactively gathers evidence; Phase 2.5 viability gate aborts if no archive signal exists; Phase 3 classifies / layers / slugs + batch-review; Phase 4 persists via `fab_extract_knowledge` (one call per candidate).
32
25
 
33
26
  ## 执行流程 (1 User Review Round)
34
27
 
35
- ### Phase 0 — Range Resolution
36
-
37
- When the skill is invoked, the user's prompt may carry an explicit range hint —
38
- a time window (`今日` / `last week`), a topic keyword (`rc.20`, `cite policy`),
39
- or a literal session_id reference. This phase parses those hints and resolves
40
- them to a concrete `session_id[]` set that constrains Phase 1 cross-session
41
- digest collection. **Falls through silently** when no hint is detected — Phase
42
- 1 then sees the legacy "all distinct sessions since last anchor" behaviour.
43
-
44
- This is the foundation of the **E4 (user-language range selection) entry
45
- point** per rc.25 Q3.3. AI (Claude/Codex) interprets the rules below at runtime
46
- — there is no parser code; the LLM IS the parser. Time-window patterns +
47
- keyword extraction are LLM-native tasks; an `AskUserQuestion` fallback covers
48
- the low-confidence case.
49
-
50
- #### Confidence decision rule (rc.33 — explicit LLM-as-parser contract)
51
-
52
- LLM-as-parser commits a result OR falls back to `AskUserQuestion` per these
53
- deterministic criteria. **Confidence is not a vibe** — it is a checklist.
54
-
55
- | Confidence | Conditions (ALL must hold) | Action |
56
- |---|---|---|
57
- | **HIGH (commit)** | (a) Time-window matches a row in the Step 2 bilingual pattern table verbatim (modulo case); AND (b) Topic-keywords are nouns from Step 3 retention rules (no ambiguous referents like `it`, `这个`); AND (c) No conflicting hints (e.g. `今日` + `上周` co-present) | Commit `time_window` + `topic_keywords[]` → Step 4 |
58
- | **MEDIUM (commit with audit log)** | Exactly ONE category present (only time-window OR only keywords) AND remaining categories empty (not contradictory) | Commit with single-category filter; emit `low_confidence_parse=false` field |
59
- | **LOW (AskUserQuestion)** | ANY of: (i) multiple competing time-windows; (ii) numeric N in `过去 N 天` parses outside 1..30; (iii) keyword set after Step 3 stop-word filter is empty BUT prompt clearly carried non-time intent (≥3 residual content words); (iv) literal `session_id=` substring present but malformed (not matching `[a-f0-9-]{36}` UUID); (v) entry = E2/E4 AND patterns yield BOTH time + keywords but they appear in unconnected clauses (LLM-judged separation) | Step 5 fallback (`AskUserQuestion` with structured options) |
60
- | **PARSE-MISS (silent skip)** | None of above match AND entry ∈ {E1, E3, E5} | Fall through with `range = "all"` sentinel; no user prompt |
61
-
62
- Implementation note: the LLM's evaluation MUST proceed top-to-bottom — HIGH
63
- checks first, then MEDIUM, then LOW. The first match wins. Do not skip
64
- categories or pick LOW preemptively to avoid commitment — that defeats the
65
- deterministic-parser contract and reintroduces the rc.32 audit P0 (T4) issue.
66
-
67
- #### Step 1 — Invocation context inspection
28
+ Phase chain: `0 → 0.5 → 1 → [1.5] → 2 → 2.5 → 3 → 3.5 → 4 → 4.5`. Each phase below is a navigator stub full procedure, decision tables, and worked examples live in `ref/`.
68
29
 
69
- Read three sources to determine whether a range hint is present:
70
-
71
- | Source | Inspection | Yields |
72
- |---|---|---|
73
- | User prompt text (the natural-language string that triggered the skill) | Free-form parse for time words + topic keywords + literal `session_id=...` | Candidate `time_window`, `topic_keywords[]`, `explicit_session_ids[]` |
74
- | Hook-context-marker (only when entry = E1 hook-triggered) | Already-parsed `{count, hours_since_last, sessions_since_last_proposed}` block emitted by archive-hint.cjs | Optional default scope = "since last archive" |
75
- | User invocation type | E1 / E2 / E3 / E4 / E5 (per rc.25 5-entry model) | Decides whether to fall back to `AskUserQuestion` (E2/E4 only) |
76
-
77
- If NONE of the three yields a usable hint AND `user_invocation_type ∉ {E2, E4}`,
78
- fall through directly to Phase 0.5 with `range = "all"` sentinel (legacy
79
- behaviour). E2 / E4 with no hint → proceed to Step 5 fallback.
80
-
81
- #### Steps 2-6 (ref-only)
30
+ ### Phase 0 Range Resolution
82
31
 
83
- For the full **Step 2** bilingual time-window pattern tables (zh-CN + en), **Step 3** topic-keyword extraction algorithm, **Step 4** session_id resolution pseudocode, **Step 5** AskUserQuestion fallback (E2/E4 only), **Step 6** carry-forward contract (`session_id[] | "all"`), and three worked examples (time-only / keyword-only / combined), `Read packages/cli/templates/skills/fabric-archive/ref/phase-0-range-resolution.md` (or `.claude/skills/fabric-archive/ref/phase-0-range-resolution.md` post-install).
32
+ Parse user's prompt for time-window (`今日` / `last week`), topic keyword (`rc.20`), or literal `session_id` reference; emit `session_id[]` OR `"all"` sentinel that constrains Phase 1 collection. LLM-as-parser contract no parser code.
84
33
 
85
- Brief output contract: Phase 0 emits ONE of `session_id[]` (non-empty distinct list, scope filter for Phase 1) OR `"all"` sentinel (legacy anchor-walk). Never empty array.
34
+ `Read ref/phase-0-range-resolution.md` for the HIGH/MEDIUM/LOW/PARSE-MISS confidence decision rule (rc.33), Step 1 inspection table, bilingual time-window patterns (zh-CN + en), topic-keyword extraction, session_id resolution pseudocode, AskUserQuestion fallback (E2/E4 only), and 3 worked examples.
86
35
 
87
36
  ### Phase 0.5 — Config Load
88
37
 
89
- Before any candidate-gathering work, the skill MUST read
90
- `.fabric/fabric-config.json` to resolve the following tunables (with documented
91
- defaults if absent):
92
-
93
- | Config field | Default | Used by |
94
- |---|---|---|
95
- | `archive_max_candidates_per_batch` | 8 | Phase 2 hard budget on candidates per Phase 3 batch |
96
- | `archive_max_recent_paths` | 20 | Phase 2 cap on `recent_paths` enumeration |
97
- | `archive_digest_max_sessions` | 10 | Phase 1 cap on cross-session digest load |
98
-
99
- If `.fabric/fabric-config.json` is missing or unreadable, use defaults silently.
38
+ Read `.fabric/fabric-config.json`; resolve `archive_max_candidates_per_batch` (default 8), `archive_max_recent_paths` (default 20), `archive_digest_max_sessions` (default 10). Missing file → defaults silently.
100
39
 
101
40
  ### UX i18n Policy
102
41
 
103
- Read `.fabric/fabric-config.json` → `fabric_language` (`zh-CN` / `en` / `zh-CN-hybrid` / `match-existing`). Emit all user-facing prose in the resolved variant. Protected tokens (MCP tool names like `fab_extract_knowledge`, schema fields like `relevance_scope`, the verbatim `强 team` / `强 personal` / `默认 team` heuristic block) are NEVER translated.
104
-
105
- `AskUserQuestion` policy: `header` + `question` translate; `options[]` are routing keys — stay English regardless of locale.
106
-
107
- **For the full 5-class taxonomy + edge cases:** `Read packages/cli/templates/skills/fabric-archive/ref/i18n-policy.md` (or `.claude/skills/fabric-archive/ref/i18n-policy.md` post-install).
42
+ Read `fabric_language` (`zh-CN` / `en` / `zh-CN-hybrid` / `match-existing`); emit user-facing prose in resolved variant. Protected tokens (MCP tool names, schema fields, the verbatim `强 team` / `强 personal` / `默认 team` heuristic) NEVER translated. `AskUserQuestion` policy: `header` + `question` translate; `options[]` stay English (routing keys).
108
43
 
44
+ `Read ref/i18n-policy.md` for the full 5-class taxonomy + edge cases.
109
45
 
110
46
  ### Phase 1 — Collect Cross-Session Digests
111
47
 
112
- Stitch together context from every session that has accumulated since the last `knowledge_proposed` event. The rc.7 Stop hook writes a per-session digest to `.fabric/.cache/session-digests/<session_id>.md` (≤5KB, contains top 10 user messages + edit_paths + 1-line title), so this phase is a tail-scan + read.
48
+ Stitch context from every session accumulated since the last `knowledge_proposed` event. Tail-scan `.fabric/events.jsonl` find anchor forward-collect distinct `session_id`s → load per-session digests from `.fabric/.cache/session-digests/<session_id>.md` concatenate into `### Cross-session digest` block, populating `source_sessions[]` + `session_context` for Phase 4. Cap at `archive_digest_max_sessions`.
113
49
 
114
- **5-step summary:**
50
+ `Read ref/phase-1-cross-session.md` for the Step 4.5 ledger filter state machine (rules a-f), `ANTI_LOOP_HOURS` / `HIGH_VALUE_EVENT_TYPES` / `NORMATIVE_KEYWORDS` constants, and 3 worked examples (user_dismissed / cooldown / re-scan-with-signal).
115
51
 
116
- 1. Read `.fabric/events.jsonl` tail (last 200 lines). Tolerate ENOENT.
117
- 2. Walk backward → find most recent `knowledge_proposed` event as anchor (ts lower bound).
118
- 3. Forward-scan from anchor → collect distinct `session_id`s.
119
- 4. Load each `<session_id>.md` digest. Cap at `archive_digest_max_sessions` (default 10).
120
- 4.5. **(rc.25 TASK-05) Apply ledger filter state machine** — drop sessions per outcome ledger: `user_dismissed` (permanent), within 12h cooldown, or no high-value signal past `covered_through_ts`. Cross-session pending dedupe also gates Phase 3 candidate emission.
121
- 5. Concatenate digests into `### Cross-session digest` block; populate `source_sessions[]` + `session_context` for Phase 4.
122
-
123
- For the full Step 4.5 ledger state machine (rules a-f), `ANTI_LOOP_HOURS` / `HIGH_VALUE_EVENT_TYPES` / `NORMATIVE_KEYWORDS` constants, and 3 worked examples (user_dismissed / cooldown / re-scan-with-signal), `Read packages/cli/templates/skills/fabric-archive/ref/phase-1-cross-session.md` (or `.claude/skills/fabric-archive/ref/phase-1-cross-session.md` post-install).
124
-
125
- Graceful degradation: missing digest cache → empty context, Phase 2 single-session fallback. Missing `session_archive_attempted` events (pre-rc.25) → Step 4.5 rule (e) applies uniformly (legacy "scan everything since anchor").
52
+ Graceful degradation: missing digest cache → single-session fallback. Missing `session_archive_attempted` events (pre-rc.25) legacy "scan everything since anchor" behaviour.
126
53
 
127
54
  ### Phase 1.5 — First-run Onboard (ref-only)
128
55
 
129
- **SKIP this phase entirely unless** entry_point ∈ {E2_explicit_user_invoke, E4_user_range_rollback} AND a fresh `fab onboard-coverage --json` reports `missing.length > 0`. For E1 (hook), E3 (AI self-trigger), and E5 (cron), onboard is non-applicable — silently fall through to Phase 0.
56
+ **SKIP this phase entirely unless** entry_point ∈ {E2_explicit_user_invoke, E4_user_range_rollback} AND `fab onboard-coverage --json` reports `missing.length > 0`. For E1/E3/E5, silently fall through to Phase 0.
130
57
 
131
- When the gate above does fire (live user + missing slots), `Read packages/cli/templates/skills/fabric-archive/ref/phase-1-5-onboard.md` (or `.claude/skills/fabric-archive/ref/phase-1-5-onboard.md` post-install) for the full Step 1-4 (coverage check → user prompt → tour-and-propose) procedure.
58
+ `Read ref/phase-1-5-onboard.md` for the Step 1-4 coverage check → user prompt → tour-and-propose procedure.
132
59
 
133
60
  ### Phase 2 — Collect Candidates
134
61
 
135
- Gather raw evidence from the recent session before any classification:
136
-
137
- 1. Read the tail of `.fabric/events.jsonl` since the last `knowledge_proposed` event.
138
- - Use `Bash` with `tail -n 200 .fabric/events.jsonl` if the file is large.
139
- - Tolerate ENOENT — empty ledger is a normal first-run state.
140
- 2. Enumerate `recent_paths`: workspace files touched by Read/Edit/Write in the current session. Cap at `archive_max_recent_paths` most-recent paths (config-resolved, default 20).
141
- 3. Distill `user_messages_summary`: a compact (≤500 char) prose summary of what the user asked for and what was decided. NOT a verbatim transcript.
142
- 4. Build a candidate list: each candidate is one observation that MIGHT be worth archiving.
143
-
144
- Hard budget: `archive_max_candidates_per_batch` candidates max per Phase 3 batch (config-resolved, default 8). If more surface, keep the configured-N with strongest worth-archiving signals (see Phase 3 type definitions) and drop the rest.
62
+ Gather raw evidence: tail `.fabric/events.jsonl` since last `knowledge_proposed`; enumerate `recent_paths` (workspace files touched by Read/Edit/Write); distill `user_messages_summary` (≤500 char prose, NOT verbatim transcript); build candidate list. Hard budget: `archive_max_candidates_per_batch` per batch (default 8); drop weaker overage.
145
63
 
146
64
  ### Phase 2.5 — Viability Gate (Anti-Archive Guard)
147
65
 
148
- Coarse viability check before Phase 3 batch review. Goal: short-circuit obvious no-archive sessions (routine execution, typo fixes, narrow renames).
149
-
150
- **Archive signals (≥ 1 hit ⇒ gate PASS):**
151
-
152
- 1. Explicit normative language (`always`/`never`/`以后`/`下次注意`/`记一下`/`from now on`/`永远不要`).
153
- 2. Wrong-turn-and-revert (edit-then-undo with diagnosis).
154
- 3. Long diagnostic loop (> 15 min or > ~10 tool turns).
155
- 4. New dependency adoption (package.json / pyproject.toml diff adds dep).
156
- 5. New pattern emergence (named abstraction/convention).
157
- 6. Decision confirmation (≥ 2 alternatives + rationale).
158
- 7. Explicit dismissal-with-reason.
159
- 8. Process formalization (load-bearing step order).
160
-
161
- **Anti-archive signals (force FAIL unless ≥ 1 archive signal also fires):** typo-only / pure refactor / narrow rename / duplicate-of-existing-canonical.
162
-
163
- **Gate decision:**
66
+ Coarse viability check. **PASS conditions**: user_explicit_invoke OR ≥1 archive signal hit (normative language, wrong-turn-and-revert, long diagnostic loop, new dependency, new pattern, decision confirmation, dismissal-with-reason, process formalization). **FAIL → branch by entry_point**: E1/E3/E5 silent-skip (emit Phase 4.5 event `outcome='skipped_no_signal'`); E2/E4 render gate-FAIL message (emit `outcome='viability_failed'`).
164
67
 
165
- ```
166
- IF user_explicit_invoke: gate = PASS
167
- ELIF archive_signals_hit == 0: gate = FAIL (reason="no_signal")
168
- ELSE: gate = PASS # any archive signal overrides anti-archive
169
- ```
170
-
171
- **On gate FAIL — branch by entry_point:**
172
-
173
- - `E1_hook` / `E3_ai_self_trigger` / `E5_cron` → SILENT-SKIP path. No message, no AskUserQuestion. Still emit Phase 4.5 `session_archive_attempted` event with `outcome='skipped_no_signal'`. Exit silently.
174
- - `E2_explicit` / `E4_user_range` → User-active path. Render gate-FAIL message (i18n class 2, see ref). Emit Phase 4.5 event with `outcome='viability_failed'`. Exit.
175
-
176
- **On gate PASS:** proceed to Phase 3 with carried-over candidates.
177
-
178
- For verbose signal explanations, zh-CN/en gate-FAIL message bodies, and the events.jsonl 4KB POSIX atomicity constraint note (single-line + self-truncate rules), `Read packages/cli/templates/skills/fabric-archive/ref/phase-2-5-viability.md` (or `.claude/skills/fabric-archive/ref/phase-2-5-viability.md` post-install).
68
+ `Read ref/phase-2-5-viability.md` for verbose signal definitions, zh-CN/en gate-FAIL message bodies, anti-archive signals (typo / refactor / rename / duplicate), and the events.jsonl 4KB POSIX atomicity constraint.
179
69
 
180
70
  ### Phase 3 — Classify, Layer, Slug, Review
181
71
 
182
- For each candidate, the skill proposes:
183
-
184
- - **type** ∈ {model, decision, guideline, pitfall, process}
185
- - **layer** ∈ {team, personal} via the verbatim heuristic below
186
- - **slug** per the 5-rule naming guideline below
187
- - **summary** (1-2 sentences, will become the entry body's lead paragraph)
188
-
189
- #### Five Knowledge Types
190
-
191
- `{model, decision, guideline, pitfall, process}` — singular noun = type concept. Pick one type per candidate; skip if none fits (not yet ripe is also a valid outcome).
192
-
193
- For verbose worth-archive vs skip-it signals per type with positive/negative examples, see `ref/phase-3-classify.md`.
72
+ For each candidate, propose **type** ∈ {model, decision, guideline, pitfall, process}, **layer** ∈ {team, personal} via the verbatim heuristic below, **slug** (kebab-case 2-5 words, 20-40 chars, unique within type+layer bucket), **summary** (1-2 sentences).
194
73
 
195
- #### Layer Classification Heuristic ( team 信号 / 强 personal 信号 / 默认 team)
74
+ #### Layer Classification Heuristic (verbatim, contract-locked)
196
75
 
197
76
  > - **强 team**: 引用本项目代码、团队共识用语("we decided")、fabric-import 路径产物、业务领域、绑定本项目代码的 pitfall
198
77
  > - **强 personal**: 第一人称偏好、跨项目通用、工具/编辑器偏好、个人工作流
199
78
  > - **默认 team**: 安全偏置——错标 team 在 PR review 中会被发现,错标 personal 静默丢失
200
79
 
201
- Resolution order: check 强 team signals first; only assign personal if 强 personal signals dominate AND no 强 team signal applies; otherwise default to team.
80
+ Resolution: 强 team first; assign personal only if 强 personal dominates AND no 强 team applies; else default team.
202
81
 
203
- #### Slug Naming (5 rules)
204
-
205
- 1. kebab-case (lowercase letters, digits, hyphens only).
206
- 2. 2-5 words.
207
- 3. 20-40 chars total.
208
- 4. Semantic core only (drop articles/generics).
209
- 5. Unique within (type, layer) bucket — collisions → add discriminator, NOT counter.
210
-
211
- Pass/fail examples → see `ref/phase-3-classify.md`.
212
-
213
- #### Decision Tree
214
-
215
- ```
216
- Observation worth keeping?
217
- NO → skip
218
- YES → fits {model, decision, guideline, pitfall, process}?
219
- NO → skip (not yet ripe)
220
- YES → assign type → apply layer heuristic → propose slug
221
- → batch review → user confirm → Phase 4: fab_extract_knowledge per candidate
222
- ```
223
-
224
- #### Batch Review
225
-
226
- Single-screen presentation of all candidates. UX i18n Policy classes 1 + 3 — structure + `Confirm?` prompt bilingualized; protected tokens verbatim; data values (slugs, paths, enum strings) NOT translated.
227
-
228
- For en + zh-CN batch review templates with example output, `Read packages/cli/templates/skills/fabric-archive/ref/phase-3-classify.md` (or `.claude/skills/fabric-archive/ref/phase-3-classify.md` post-install).
229
-
230
- User MAY inline-edit `type` / `layer` / `slug` / `relevance_scope` / `relevance_paths` before confirming. Editing `[relevance_scope=...]` triggers re-derivation per Phase 3.5 (narrow ⇒ recompute from edit_paths; broad ⇒ force `[]`).
82
+ `Read ref/phase-3-classify.md` for per-type worth-archive vs skip signals with positive/negative examples, slug pass/fail samples, decision tree, en + zh-CN batch review templates. User MAY inline-edit `type` / `layer` / `slug` / `relevance_scope` / `relevance_paths` before confirming; scope edits trigger Phase 3.5 re-derivation.
231
83
 
232
84
  ### Phase 3.5 — Scope Decision + relevance_paths Derivation
233
85
 
234
- After classify/layer/slug but BEFORE batch review output, assign a `relevance_scope` to each candidate and derive its `relevance_paths` array. These two fields drive rc.6 hint injection: narrow knowledge is gated by working in matching paths, broad knowledge is project-wide.
235
-
236
- #### Scope decision (narrow vs broad)
237
-
238
- ```
239
- relevance_scope =
240
- narrow IF the candidate is tied to a specific module / file / subsystem
241
- AND there is explicit single-module evidence in edit_paths
242
- (i.e. all worth-keeping edits in this session concentrated in one
243
- module tree, OR the candidate explicitly references that module)
244
-
245
- broad IF the candidate is cross-cutting / methodological / general
246
- (applies regardless of which path the agent is working in)
247
-
248
- broad (default, on uncertainty — safe偏置 per Q-1 in handoff)
249
- ```
250
-
251
- Special case — Personal layer ALWAYS resolves to `relevance_scope=broad` with `relevance_paths=[]`. Rationale: personal knowledge crosses projects; paths from one project do not generalize. If `layer=personal` and a narrow scope was tentatively chosen, auto-flip to `broad` and clear `relevance_paths`.
252
-
253
- ##### Examples
254
-
255
- - `decision: single-cjs-hook-script` → `narrow` (tied to `templates/claude-hooks/` + `packages/cli/src/commands/hooks.ts`)
256
- - `pitfall: deepmerge-array-replace-trap` → `broad` (cross-cutting JSON merge gotcha, applies anywhere deepMerge is used)
257
- - `guideline: slug-naming-rules` → `broad` (methodology, no specific module)
258
- - `model: wave-1-parallel-task-dag` → `narrow` (tied to `packages/cli/src/commands/plan.ts`)
259
- - `guideline: indent-style-by-language` (personal layer) → `broad + []` (personal forces broad)
260
-
261
- #### relevance_paths derivation (rc.5 single-signal: edit_paths)
86
+ Assign `relevance_scope` {narrow, broad} + derive `relevance_paths` BEFORE batch review. **narrow** = candidate tied to specific module/file with single-module evidence in edit_paths; **broad** = cross-cutting/methodological/general (default on uncertainty). **Personal layer ALWAYS forces broad + `relevance_paths=[]`** (cross-project, paths don't generalize).
262
87
 
263
- rc.5 derives `relevance_paths` exclusively from `edit_paths` (Edit/Write/MultiEdit tool calls). Multi-signal (read_paths + body regex + symbols) deferred to rc.7. The algorithm has 6 steps:
264
-
265
- 1. COLLECT edit_paths from session transcript.
266
- 2. DEDUPE.
267
- 3. BLACKLIST FILTER (drop repo-root single files like README.md, package.json; drop trivial `**/*.<ext>` globs; drop read-only paths).
268
- 4. PUBLIC-PREFIX GENERALIZE (group ≥ 2 siblings into glob, depth ≤ 2; singletons kept literal).
269
- 5. SCOPE GATE (broad → force `[]`; narrow → use Step 4 result).
270
- 6. ATTACH READ-ONLY EVIDENCE as `## Evidence` block (NOT in relevance_paths).
271
-
272
- For full pseudocode, a worked generalization example (5 sample paths → glob + literal output), and inline-edit re-derivation rules (narrow↔broad transitions), `Read packages/cli/templates/skills/fabric-archive/ref/phase-3-5-scope.md` (or `.claude/skills/fabric-archive/ref/phase-3-5-scope.md` post-install).
88
+ `Read ref/phase-3-5-scope.md` for the 6-step relevance_paths derivation pseudocode (COLLECT DEDUPE → BLACKLIST → PUBLIC-PREFIX GENERALIZE SCOPE GATE ATTACH READ-ONLY EVIDENCE), worked example (5 sample paths glob + literal output), and narrow↔broad inline-edit re-derivation rules.
273
89
 
274
90
  ### Phase 4 — Persist via MCP
275
91
 
276
- For each user-confirmed candidate, call `fab_extract_knowledge` ONCE. Do NOT batch multiple candidates into one call.
277
-
278
- #### Output Contract (essentials)
279
-
280
- `fab_extract_knowledge` call carries: `source_sessions[]` (T5 array), `recent_paths[]` (cap 20), `user_messages_summary` (≤500 chars), `type` ∈ {decisions, pitfalls, guidelines, models, processes} (plural directory-form), `slug` (kebab-case 2-5 words), `layer` ∈ {team, personal}, `relevance_scope` ∈ {narrow, broad}, `relevance_paths[]` (narrow ⇒ derived; broad ⇒ `[]`), `proposed_reason` (enum: `explicit-user-mark` | `diagnostic-then-fix` | `decision-confirmation` | `wrong-turn-revert` | `new-dependency-or-pattern` | `dismissal-with-reason`), `session_context` (3-5 line narrative).
92
+ For each user-confirmed candidate, call `fab_extract_knowledge` ONCE (NEVER batch). Required: `source_sessions[]`, `recent_paths[]` (cap 20), `user_messages_summary`, `type` (plural form: decisions/pitfalls/guidelines/models/processes), `slug`, `layer`, `relevance_scope`, `relevance_paths[]`, `proposed_reason` (enum), `session_context` (3-5 line narrative). Four OPTIONAL rc.23 triage fields (`intent_clues`, `tech_stack`, `impact`, `must_read_if`) — populate when clean, **omit rather than guess**.
281
93
 
282
- Four OPTIONAL rc.23 triage fields (`intent_clues`, `tech_stack`, `impact`, `must_read_if`) populate when the skill can infer cleanly; **omit rather than guess**.
94
+ Server returns `{ pending_path, idempotency_key }`. Display `pending_path` for the user. `idempotency_key = sha256({source_session, type, slug})` repeated calls SAFE (server merges evidence).
283
95
 
284
- For the full TypeScript call shape, the C1 triage-field inference table, the Phase-2.5-signal → `proposed_reason` mapping table, `session_context` format, and T5 array-form idempotency notes, `Read packages/cli/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md` (or `.claude/skills/fabric-archive/ref/phase-4-mcp-persist.md` post-install).
285
-
286
- Server returns `{ pending_path, idempotency_key }`. Display `pending_path` for the user. `idempotency_key = sha256({source_session, type, slug})` — calling twice with the same triple is SAFE (server merges evidence).
96
+ `Read ref/phase-4-mcp-persist.md` for full TypeScript call shape, C1 triage-field inference table, Phase 2.5 signal → `proposed_reason` mapping, `session_context` format, T5 array-form idempotency notes.
287
97
 
288
98
  ### Phase 4.5 — Persist Archive Attempt
289
99
 
290
- MANDATORY closing step on every skill invocation — runs AFTER Phase 4 (success path) AND on every early-exit path (Phase 1 dropped-all, Phase 2.5 gate-FAIL silent-skip or user-active, Phase 3 batch user-dismissed). Drives the Q3.4 outcome state machine + cross-session digest rescan filter.
291
-
292
- #### Dry-run override
293
-
294
- See the unified `## Dry-run Scope` section at the end of this file for the full catalogue of writes suspended in dry-run mode. Summary for Phase 4.5: dry-run SKIPS the `session_archive_attempted` emit entirely; the read-side machinery (Phase 1 digest, Phase 2.5 gate, Phase 3 preview) runs normally so the user sees what WOULD have been written.
295
-
296
- #### Event emission summary
100
+ MANDATORY closing step on EVERY invocation (Phase 4 success path + every early-exit). Append ONE `session_archive_attempted` line to `.fabric/events.jsonl` per `session_id` in run scope (single-line JSON ≤4KB POSIX atomicity). Outcome {`proposed` | `viability_failed` | `user_dismissed` | `skipped_no_signal`}. `covered_through_ts` = max ts of events examined. Best-effort write: failure → stderr log only, skill still exits successfully.
297
101
 
298
- Append ONE `session_archive_attempted` line to `.fabric/events.jsonl` PER `session_id` in the run scope. Single-line JSON 4KB (POSIX atomicity see Phase 2.5 ref). Best-effort write: append failure → log stderr only, skill still exits successfully. Outcome ∈ {`proposed` | `viability_failed` | `user_dismissed` | `skipped_no_signal`}. `covered_through_ts` = max ts of events the skill examined for that session.
102
+ **Dry-run override**: SKIPS the `session_archive_attempted` emit entirely; read-side runs normally so user previews what WOULD have been written. See unified `## Dry-run Scope` pointer below.
299
103
 
300
- For the full jsonc event shape, the outcome decision matrix (4 terminal states × outcome / candidates_proposed / knowledge_proposed_ids columns), `covered_through_ts` watermark spec, multi-session emission rule, the bash echo append pattern, and an E5-cron-silent-skip worked trace, `Read packages/cli/templates/skills/fabric-archive/ref/phase-4-5-emit.md` (or `.claude/skills/fabric-archive/ref/phase-4-5-emit.md` post-install).
104
+ `Read ref/phase-4-5-emit.md` for the full event jsonc shape, 4-state outcome decision matrix (outcome × candidates_proposed × knowledge_proposed_ids), `covered_through_ts` watermark spec, multi-session emission rule, bash echo append pattern, and E5-cron silent-skip worked trace.
301
105
 
302
106
  ## Hard Rules (DO NOT TRANSLATE) — DISPLAY / WRITE Split
303
107
 
@@ -327,29 +131,8 @@ For the full jsonc event shape, the outcome decision matrix (4 terminal states
327
131
  - NEVER paraphrase the verbatim layer heuristic block above — the Chinese text is contract-locked.
328
132
  - MUST preserve protected tokens exactly: `stable_id`, `knowledge_proposed`, `knowledge_archive_aborted`, `knowledge_scope_degraded`, `.fabric/knowledge/pending/`, `fab_extract_knowledge`, `relevance_paths`, `relevance_scope`, `narrow`, `broad`, `edit_paths`, `source_sessions`, `proposed_reason`, `session_context`, `intent_clues`, `tech_stack`, `impact`, `must_read_if`, `pending_path`, `layer`, `team`, `personal`, `MUST`, `NEVER`, `强 team`, `强 personal`, `默认 team`.
329
133
 
330
- ## Worked Examples (ref-only)
331
-
332
- Three end-to-end fab_extract_knowledge call examples (decision/team, pitfall/team, guideline/personal) live in `packages/cli/templates/skills/fabric-archive/ref/worked-examples.md` (or `.claude/skills/fabric-archive/ref/worked-examples.md` post-install). Load when you want to see all required + optional fields populated together in a realistic shape.
333
-
334
- ## E5 Scheduled Daily Recap (ref-only)
335
-
336
- Only relevant when entry_point=E5_cron (OS cron, `/loop`, or scheduled trigger). For interactive invocations, Phase 0 has already routed past this — nothing to load.
337
-
338
- When E5 fires: `Read packages/cli/templates/skills/fabric-archive/ref/e5-cron-recap.md` (or `.claude/skills/fabric-archive/ref/e5-cron-recap.md` post-install) for `/loop` vs OS cron tradeoffs + the `今日复盘` magic-phrase parse contract.
339
-
340
- ## Dry-run Scope (unified)
341
-
342
- `dry_run = true` (per Phase 4.5 detection rule — substring match on `--dry-run` | `dry-run` | `dry_run` | `预览` token) suspends ALL side-effecting writes below; read-side machinery (Phase 1 digest collection, Phase 2.5 viability gate evaluation, Phase 3 candidate render) executes normally so the user can preview what WOULD happen.
343
-
344
- | Write operation | Normal mode | Dry-run mode |
345
- |---|---|---|
346
- | `fab_extract_knowledge` MCP call (Phase 4) | One call per confirmed candidate, writes to `.fabric/knowledge/pending/<slug>.md` | SKIPPED. Phase 4 renders "would write N pending entries" preview table instead. |
347
- | `session_archive_attempted` event (Phase 4.5) | Appended to `.fabric/events.jsonl` for every session in scope | SKIPPED entirely. No ledger entry. |
348
- | `fab_review reject` (Phase 3 user-dismissed branch) | Invoked when user types `撤销` / `reject` after self-archive proposal | SKIPPED. The dismissal is rendered to console but no MCP write occurs. |
349
- | `fab onboard-coverage` slot writes (Phase 1.5 fill-all / dismiss-all) | Each `Bash("fab config dismiss-slot <slot>")` invocation runs | SKIPPED. Slot decisions are shown as "would dismiss/propose" preview. |
350
- | `.fabric/.cache/session-digests/<session_id>.md` reads | Read freely (read-side, safe) | Read freely — same as normal. |
351
- | Stop-hook / archive-hint stdin/stdout | Read-only inspection of `.fabric/events.jsonl` | Same — no change. |
352
-
353
- All user-facing output in dry-run mode MUST prefix `[DRY-RUN]` at the start of each Phase header (e.g. `[DRY-RUN] Phase 3 — Batch Review`). Exit message: `[DRY-RUN complete] would have written N entry/entries; no .fabric/ files were modified. Re-invoke without --dry-run to commit.`
134
+ ## Worked Examples / E5 Cron / Dry-run (ref-only)
354
135
 
355
- Cross-reference: Phase 4.5 §Dry-run override holds the rationale; this section is the authoritative catalogue of skipped writes. When adding a new write side-effect to any phase, update BOTH the phase section AND this table.
136
+ - **Worked examples** (3 end-to-end fab_extract_knowledge calls: decision/team, pitfall/team, guideline/personal): `Read ref/worked-examples.md`
137
+ - **E5 Scheduled Daily Recap** (only when entry_point=E5_cron — OS cron, `/loop`, or scheduled trigger): `Read ref/e5-cron-recap.md`
138
+ - **Dry-run Scope** (authoritative catalogue of all writes suspended by `--dry-run`): `Read ref/dry-run-scope.md`
@@ -0,0 +1,16 @@
1
+ # Dry-run Scope (unified)
2
+
3
+ `dry_run = true` (per Phase 4.5 detection rule — substring match on `--dry-run` | `dry-run` | `dry_run` | `预览` token) suspends ALL side-effecting writes below; read-side machinery (Phase 1 digest collection, Phase 2.5 viability gate evaluation, Phase 3 candidate render) executes normally so the user can preview what WOULD happen.
4
+
5
+ | Write operation | Normal mode | Dry-run mode |
6
+ |---|---|---|
7
+ | `fab_extract_knowledge` MCP call (Phase 4) | One call per confirmed candidate, writes to `.fabric/knowledge/pending/<slug>.md` | SKIPPED. Phase 4 renders "would write N pending entries" preview table instead. |
8
+ | `session_archive_attempted` event (Phase 4.5) | Appended to `.fabric/events.jsonl` for every session in scope | SKIPPED entirely. No ledger entry. |
9
+ | `fab_review reject` (Phase 3 user-dismissed branch) | Invoked when user types `撤销` / `reject` after self-archive proposal | SKIPPED. The dismissal is rendered to console but no MCP write occurs. |
10
+ | `fab onboard-coverage` slot writes (Phase 1.5 fill-all / dismiss-all) | Each `Bash("fab config dismiss-slot <slot>")` invocation runs | SKIPPED. Slot decisions are shown as "would dismiss/propose" preview. |
11
+ | `.fabric/.cache/session-digests/<session_id>.md` reads | Read freely (read-side, safe) | Read freely — same as normal. |
12
+ | Stop-hook / archive-hint stdin/stdout | Read-only inspection of `.fabric/events.jsonl` | Same — no change. |
13
+
14
+ All user-facing output in dry-run mode MUST prefix `[DRY-RUN]` at the start of each Phase header (e.g. `[DRY-RUN] Phase 3 — Batch Review`). Exit message: `[DRY-RUN complete] would have written N entry/entries; no .fabric/ files were modified. Re-invoke without --dry-run to commit.`
15
+
16
+ Cross-reference: Phase 4.5 `Dry-run override` section in SKILL.md holds the rationale. When adding a new write side-effect to any phase, update BOTH the phase section AND this table.