@fenglimg/fabric-cli 2.0.0-rc.27 → 2.0.0-rc.28

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.
@@ -0,0 +1,58 @@
1
+ # E5 Scheduled Daily Recap — full reference
2
+
3
+ > **Loaded on demand.** Only relevant when invocation context = `cron` / `/loop` (E5 entry). SKILL.md's Phase -0.5 already gates this — if the user just typed `/fabric-archive`, none of the below applies.
4
+
5
+ ## E5 周期触发 (Scheduled Daily Recap)
6
+
7
+ ## Overview
8
+
9
+ `今日复盘` = E5 entry point. Default scope = today. Falls back to historical scan if today yields no candidates (silent-skip per Phase 2.5).
10
+
11
+ E5 是 5 入口模型中唯一由 OS 调度器或 Claude Code `/loop` 周期触发的入口形态。fab 端**零代码**——不提供 `fab schedule` 子命令,亦不内嵌 daemon。用户基于自己的执行环境二选一接入: `/loop`(Claude Code 原生,推荐) 或 OS cron(跨平台 fallback)。
12
+
13
+ ### /loop sample (primary path for Claude Code)
14
+
15
+ ```
16
+ /loop /fabric-archive 今日复盘 --cron "0 23 * * *"
17
+ ```
18
+
19
+ 每晚 23:00 在当前 Claude Code session 中触发 fabric-archive skill,scope = today。`/loop` 复用现有 Claude session 鉴权,无需独立 token。
20
+
21
+ ### OS cron sample (cross-platform alternative)
22
+
23
+ ```
24
+ # crontab -e
25
+ 0 23 * * * cd /path/to/project && claude code -p "/fabric-archive 今日复盘" 2>&1 >> /var/log/fabric-daily-recap.log
26
+ ```
27
+
28
+ 适用于:
29
+ - 非 Claude Code 环境(纯 server / CI 节点)
30
+ - 希望脱离 /loop session 生命周期独立运行的场景
31
+ - 已有 cron / launchd 调度基础设施的团队
32
+
33
+ macOS 用户可改用 `launchd` plist;Linux 用户直接 `crontab -e`。命令需自行确保 `claude code` CLI 已安装且鉴权可用。
34
+
35
+ ### E5 prompt parse contract
36
+
37
+ 当用户或 cron 以 `今日复盘` / `daily recap` 字面短语触发 fabric-archive 时,skill 应按以下契约处理:
38
+
39
+ - **Phase -0.5 Range Resolution**: 识别 `今日复盘` / `daily recap` 为 magic phrase, 直接设置 `time_window = today` (00:00 local timezone → current ts), 无需 AskUserQuestion 兜底。
40
+ - **Phase 0.4 Onboard Coverage**: 跳过 (entry_point = E5_cron, 非 E2_explicit, 不弹 onboard 弹问)。
41
+ - **Phase 2.5 Persist Archive Attempt**: 始终写入 `session_archive_attempted` event。当今日无 archive 信号触发 viability gate FAIL 时,走 silent-skip 路径(outcome = `skipped_no_signal`),skill 静默退出,cron 日志为空。
42
+
43
+ ### Trade-off table (/loop vs OS cron)
44
+
45
+ | 维度 | /loop | OS cron |
46
+ |---|---|---|
47
+ | 鉴权 | 复用 Claude session | 独立 token / login |
48
+ | 跨平台 | Claude Code 全平台一致 | macOS launchd / Linux cron 不同 |
49
+ | Token 成本 | 累积 (长 session) | 每 tick fresh, 无累积 |
50
+ | 调试 | Claude UI 可见 | 日志文件 |
51
+
52
+ ### Failure modes
53
+
54
+ - **/loop session crash**: 归档暂停,用户需重启 `/loop`。无自动恢复机制——`/loop` 与 Claude Code session 生命周期绑定。
55
+ - **OS cron**: 自带恢复(下一个 tick 重新启动),但需独立 `claude code` CLI 安装与鉴权;鉴权 token 过期时 cron job 会静默失败,需人工 `claude login` 重置。
56
+
57
+ ### NOT in scope
58
+
@@ -0,0 +1,80 @@
1
+ # UX i18n Policy — full reference
2
+
3
+ > **Loaded on demand.** Only consult when rendering bilingual output AND you're unsure which class a string belongs to. SKILL.md gives the operative rule: read `.fabric/fabric-config.json` → `fabric_language`, emit prose in resolved variant, never translate protected tokens. The 5-class taxonomy below disambiguates edge cases.
4
+
5
+ ## UX i18n Policy (5-class bilingualization)
6
+
7
+ The skill consults `fabric_language` from `.fabric/fabric-config.json`
8
+ (固化于 init 时,via `lib/detect-language.ts:detectExistingLanguage`; default `"en"` when no
9
+ CJK signal is detected in README + docs/; may resolve to `"match-existing"`,
10
+ `"zh-CN"`, `"en"`, or `"zh-CN-hybrid"`). All user-facing text in the
11
+ following 5 categories MUST be rendered in the resolved language:
12
+
13
+ 1. **Roll-up templates** — the `# Archive Review — N candidates` batch
14
+ review block (one per candidate) AND any final session summary the
15
+ skill emits after Phase 2 completes. zh-CN ↔ en mirror.
16
+ 2. **Errors / Preconditions warnings** — abort + gate-fail messages (e.g.
17
+ the "没有触发归档信号…" trigger-miss and the "本次会话为常规执行…"
18
+ viability-gate-FAIL message). zh-CN ↔ en mirror.
19
+ 3. **Confirmation prompts** — the per-candidate `Confirm? (Y to accept,
20
+ edit … inline, N to skip)` line in the batch review template. zh-CN
21
+ ↔ en mirror.
22
+ 4. **Dry-run table headers** — v2.0.0-rc.27 TASK-007 added a dry-run
23
+ override path (see Phase 2.5 "dry-run") so users can preview the
24
+ archive proposal without writing pending entries. The dry-run summary
25
+ header and per-candidate preview labels MUST be bilingualized per
26
+ this policy. zh-CN ↔ en mirror.
27
+ 5. **AskUserQuestion** — `header` + `question` fields (NOT `options[]`).
28
+ zh-CN ↔ en mirror. fabric-archive itself does not surface
29
+ AskUserQuestion in the current contract (Phase 1 batch review is a
30
+ single markdown screen, not a structured question), but if a future
31
+ version adds one — e.g. to confirm layer flip — this rule applies.
32
+
33
+ Rendering rule:
34
+
35
+ - `fabric_language === "zh-CN"` → emit the zh-CN variant; pure monolingual, no language mixing inside a single user-facing block.
36
+ - `fabric_language === "en"` → emit the en variant; pure monolingual, no language mixing inside a single user-facing block.
37
+ - `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`).
38
+ - `fabric_language === "match-existing"` or any other value → emit the en variant; pure monolingual.
39
+
40
+ Protected tokens (`fab_extract_knowledge`, `relevance_scope`,
41
+ `relevance_paths`, `narrow`, `broad`, `source_sessions`, `proposed_reason`,
42
+ `session_context`, `intent_clues`, `tech_stack`, `impact`, `must_read_if`,
43
+ `pending_path`, `layer`, `team`, `personal`,
44
+ `knowledge_scope_degraded`, `MUST`, `NEVER`, `.fabric/knowledge/`, the verbatim
45
+ `强 team` / `强 personal` / `默认 team` heuristic block, etc.) are NEVER
46
+ translated — they appear verbatim in both language variants. The
47
+ bilingualization scope is prose ONLY.
48
+
49
+ ### AskUserQuestion i18n Policy (value vs label)
50
+
51
+ When a skill (this one or any sibling skill the user is composing with)
52
+ issues an `AskUserQuestion`, the `header` and `question` strings are
53
+ user-facing prose → translated per `fabric_language`. The `options[]`
54
+ array entries (e.g. `["approve", "reject", "modify", "defer", "skip"]` in
55
+ fabric-review, or `["team", "personal"]` for a layer-flip target) are
56
+ **routing keys** consumed by the skill state machine — they MUST remain
57
+ English regardless of `fabric_language`.
58
+
59
+ ```ts
60
+ // EN (fabric_language === "en")
61
+ AskUserQuestion({
62
+ header: "Layer-flip target",
63
+ question: "Move '{title}' to which layer? (current: {current_layer})",
64
+ options: ["team", "personal"]
65
+ })
66
+
67
+ // zh-CN (fabric_language === "zh-CN")
68
+ AskUserQuestion({
69
+ header: "Layer 切换目标",
70
+ question: "将 '{title}' 切换到哪一层?(当前: {current_layer})",
71
+ options: ["team", "personal"] // 不翻译 — routing key
72
+ })
73
+ ```
74
+
75
+ Rationale: localizing routing keys would force every routing branch to
76
+ dual-string match (e.g. `if (choice === "team" || choice === "团队")`),
77
+ which doubles the surface area for protected-token regressions and breaks
78
+ the option-list invariants that downstream tooling depends on. Keeping
79
+ `options[]` English-only is contract-locked across all three skills.
80
+
@@ -0,0 +1,218 @@
1
+ # Phase 0.4 — First-run Onboard Phase (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path only runs this when entry_point ∈ {E2_explicit_user_invoke, E4_user_range_rollback} AND `fab onboard-coverage --json` reports `missing.length > 0`. For E1/E3/E5 entries OR fully-covered workspaces, this entire phase is skipped — no reason to load.
4
+
5
+ ## Phase 0.4 — First-run Onboard Phase
6
+
7
+ #### Phase 0.4 Trigger Gate (rc.25 — entry-context aware)
8
+
9
+ Before running ANY of the onboard coverage steps below, evaluate the
10
+ **entry-context gate**. Onboard slot collection is an interactive,
11
+ one-time project-tone capture flow that REQUIRES live user dialogue.
12
+ Non-user-active entries (hook / AI self-trigger / cron) either interrupt
13
+ the user mid-work or run unattended where dialogue is impossible, so
14
+ they MUST skip Phase 0.4 entirely and fall through to Phase 0.
15
+
16
+ Read `context.entry_point` — already determined in **Phase -0.5 Range
17
+ Resolution** (see TASK-04 / Phase -0.5 section above). The 5-entry model
18
+ is the canonical taxonomy for this gate.
19
+
20
+ ##### Entry-context detection rules
21
+
22
+ | Entry | Symbol | Detection rule (LLM-native, evaluated at skill entry) |
23
+ |-------|--------|-------------------------------------------------------|
24
+ | **E1** | `hook_passive` | stdout JSON `{decision:'block', ...}` from `archive-hint.cjs` detected at skill entry (the Stop-hook reminder path). |
25
+ | **E2** | `explicit_user_invoke` | User prompt is a direct invocation: `fabric archive` / `/fabric-archive` / `archive what we just did` / `归档一下` / similar imperative. |
26
+ | **E3** | `ai_self_trigger` | AI internal marker `self-archive policy triggered by signal: <X>` present (substring match on the verbatim prefix `self-archive policy triggered by signal`; `<X>` is one of the 4 self-trigger signals from AGENTS.md E3 section: `Normative` / `Wrong-turn-and-revert` / `Decision confirmation` / `Explicit dismissal`). |
27
+ | **E4** | `user_range_rollback` | Prompt contains a **range hint** (parsed in Phase -0.5 — e.g. `今日` / `上周` / `rc.20`) AND the user is invoking. Sub-mode of E2. |
28
+ | **E5** | `cron` | Prompt contains literal `今日复盘` / `daily recap` / `daily-archive` AND no human is present (running under `/loop`, OS cron, or scheduled trigger). |
29
+
30
+ ##### Gate decision
31
+
32
+ ```
33
+ IF context.entry_point ∈ {E2_explicit_user_invoke, E4_user_range_rollback}:
34
+ → gate = PROCEED # user is live, dialogue is possible
35
+ → continue to Step 1 (Check coverage) below
36
+ ELSE (E1_hook_passive | E3_ai_self_trigger | E5_cron):
37
+ → gate = SKIP # no live user, onboard prompting would misfire
38
+ → emit one-line log: "Phase 0.4 skipped (entry=<E1|E3|E5>, no live user)"
39
+ → proceed directly to Phase 0
40
+ ```
41
+
42
+ ##### Rationale
43
+
44
+ Onboard slot collection is a one-time project-tone capture flow that
45
+ requires user dialogue. Non-user-active entries (hook / AI / cron)
46
+ interrupt the user mid-work or run unattended where dialogue is
47
+ impossible, so they MUST skip Phase 0.4. The S5 slot semantics
48
+ (`tech-stack-decision`, `architecture-pattern`, ...) are user-validated
49
+ baselines — populating them from a hook fire-and-forget or a cron daily
50
+ recap would defeat the purpose of capturing _user-confirmed_ project
51
+ tone.
52
+
53
+ ##### Tradeoff (documented in CHANGELOG)
54
+
55
+ A first-time user whose ONLY invocations ever come via hook (never an
56
+ explicit `/fabric-archive`) will not see the onboard prompt; the 5
57
+ onboard slots remain empty. Mitigation: documentation tells users to
58
+ run an explicit `fab archive` at least once to populate the onboard
59
+ baseline.
60
+
61
+ ##### Worked example
62
+
63
+ ```
64
+ $ /loop 24h /fabric-archive 今日复盘
65
+ → cron context, no live user
66
+ → Phase -0.5 detects literal "今日复盘" + no-human marker
67
+ → context.entry_point = E5_cron
68
+ → Phase 0.4 Trigger Gate evaluates: E5 ∉ {E2, E4} → SKIP
69
+ → emit log "Phase 0.4 skipped (entry=E5, no live user)"
70
+ → proceed directly to Phase 0 (collect candidates for daily window)
71
+ ```
72
+
73
+ Contrast with E2:
74
+
75
+ ```
76
+ $ /fabric-archive
77
+ → user typed explicit invocation
78
+ → Phase -0.5: context.entry_point = E2_explicit_user_invoke
79
+ → Phase 0.4 Trigger Gate evaluates: E2 ∈ {E2, E4} → PROCEED
80
+ → run Step 1 (Check coverage) below
81
+ ```
82
+
83
+ ---
84
+
85
+ After F8a removed the auto-`fab scan` baseline pipeline, a freshly installed
86
+ Fabric workspace ships with an EMPTY `.fabric/knowledge/` tree. Five fixed
87
+ **S5 onboard slots** capture the "project tone" baseline that the AI needs
88
+ for high-quality plan_context retrieval from day one:
89
+
90
+ - `tech-stack-decision` — primary languages / frameworks / runtime stack
91
+ - `architecture-pattern` — module layout, service boundaries, layering rules
92
+ - `code-style-tone` — naming / formatting / idiom conventions the project enforces
93
+ - `build-system-idiom` — build tool quirks, scripts, deploy pipeline shape
94
+ - `domain-vocabulary` — business / product terminology that names code entities
95
+
96
+ This phase runs ONCE per archive-skill invocation, BEFORE Phase 0 evidence
97
+ gathering, so coverage state is fresh for the session.
98
+
99
+ #### Step 1 — Check coverage
100
+
101
+ Invoke `fab onboard-coverage --json` and parse the JSON payload:
102
+
103
+ ```bash
104
+ fab onboard-coverage --json
105
+ ```
106
+
107
+ Expected shape:
108
+
109
+ ```json
110
+ {
111
+ "filled": { "tech-stack-decision": ["KT-DEC-0012"], ... },
112
+ "missing": ["architecture-pattern", "code-style-tone"],
113
+ "opted_out": ["domain-vocabulary"],
114
+ "total": 5
115
+ }
116
+ ```
117
+
118
+ #### Step 2 — Decide
119
+
120
+ ```
121
+ IF missing.length === 0:
122
+ → skip Phase 0.4 entirely; proceed to Phase 0.
123
+ ELSE:
124
+ → ask the user how to handle the missing slots (Step 3).
125
+ ```
126
+
127
+ #### Step 3 — Prompt user
128
+
129
+ Present a single roll-up listing each missing slot. UX i18n Policy class 5
130
+ applies: the `header` + `question` strings are translated per
131
+ `fabric_language`; the `options[]` routing keys stay English.
132
+
133
+ ```ts
134
+ AskUserQuestion({
135
+ header: "Onboard coverage", // zh-CN: "首装基调覆盖"
136
+ question:
137
+ "KB is missing the following project-tone slots: " +
138
+ missing.join(", ") +
139
+ ". Tour the project and propose pending entries for each?",
140
+ options: ["fill-all", "fill-each", "dismiss-all", "skip"]
141
+ })
142
+ ```
143
+
144
+ `fab_extract_knowledge` is called with `onboard_slot: <slot>` set so each
145
+ proposed entry counts toward coverage once approved via fab_review.
146
+
147
+ | User choice | Action |
148
+ |----------------|--------|
149
+ | `fill-all` | For EACH slot in `missing`, run Step 4 (Tour-and-propose). All proposals share session_id; one batch review at the end (Phase 1). |
150
+ | `fill-each` | Loop slot-by-slot through `missing`. Per slot: ask user `confirm | dismiss | skip` (per-slot AskUserQuestion); `confirm` → run Step 4; `dismiss` → `fab config dismiss-slot <slot>`; `skip` → leave for next archive run. |
151
+ | `dismiss-all` | For EACH slot in `missing`, invoke `Bash("fab config dismiss-slot <slot>")`. Print a one-line confirmation each. Skip to Phase 0. |
152
+ | `skip` | No-op. Slots remain in `missing` for the next archive run. Skip to Phase 0. |
153
+
154
+ #### Step 4 — Tour-and-propose (per-slot)
155
+
156
+ For each slot to fill, the LLM independently sources slot-specific evidence
157
+ from the project (no user prompt — this is a Read-only tour):
158
+
159
+ | Slot | Source files (LLM should Read these) |
160
+ |--------------------------|---------------------------------------|
161
+ | `tech-stack-decision` | `package.json` (+ lockfile), `pyproject.toml` / `Cargo.toml` / `go.mod`, `tsconfig.json`, root README |
162
+ | `architecture-pattern` | Top-level dir tree (`ls -F`), 1-2 entry-point files (`src/index.ts`, `main.go`, etc.), framework-config files (`next.config`, `vite.config`, `astro.config`) |
163
+ | `code-style-tone` | `.editorconfig`, `prettier.config.*`, `eslint.config.*`, `biome.*`, `.prettierrc*`, framework lint config, 2-3 representative source files for naming-pattern inference |
164
+ | `build-system-idiom` | `package.json` `scripts` block, `Makefile`, `taskfile.yaml`, CI yml (`.github/workflows/*.yml`), Dockerfile if present |
165
+ | `domain-vocabulary` | README, `docs/*.md`, top-level `src/` directory names (often domain-aligned), public API entry types |
166
+
167
+ After Read-ing the slot-specific sources, classify the observation:
168
+
169
+ - `tech-stack-decision` → type=`decisions`, `proposed_reason=decision-confirmation`
170
+ - `architecture-pattern` → type=`models`, `proposed_reason=new-dependency-or-pattern`
171
+ - `code-style-tone` → type=`guidelines`, `proposed_reason=explicit-user-mark` (the project ITSELF is the mark)
172
+ - `build-system-idiom` → type=`processes`, `proposed_reason=new-dependency-or-pattern`
173
+ - `domain-vocabulary` → type=`models`, `proposed_reason=new-dependency-or-pattern`
174
+
175
+ Call `fab_extract_knowledge` with the inferred fields PLUS `onboard_slot:
176
+ <slot>`. The pending file's frontmatter will carry the slot label, and the
177
+ next `fab onboard-coverage` run will see the slot as filled (once approved
178
+ via fab_review).
179
+
180
+ Example:
181
+
182
+ ```ts
183
+ mcp__fabric__fab_extract_knowledge({
184
+ source_sessions: ["<current-session-id>"],
185
+ recent_paths: ["package.json", "tsconfig.json"],
186
+ user_messages_summary: "Project uses TypeScript + pnpm workspace + Vitest. Node 20 LTS target. ESM-only.",
187
+ type: "decisions",
188
+ slug: "primary-tech-stack",
189
+ layer: "team",
190
+ relevance_scope: "broad", // tech stack applies everywhere
191
+ relevance_paths: [],
192
+ proposed_reason: "decision-confirmation",
193
+ session_context:
194
+ "Session goal: capture onboard tech-stack baseline.\nTurning point: read package.json + tsconfig.json + pnpm-workspace.yaml; stack confirmed.",
195
+ onboard_slot: "tech-stack-decision", // ← claims the slot
196
+ tech_stack: ["typescript", "nodejs", "pnpm", "vitest"]
197
+ })
198
+ ```
199
+
200
+ #### Onboard phase constraints (DO NOT TRANSLATE)
201
+
202
+ - MUST run BEFORE Phase 0 evidence gathering — onboard is a separate flow,
203
+ not interleaved with session-archive candidates.
204
+ - MUST call `fab onboard-coverage --json` before deciding; never assume
205
+ coverage state.
206
+ - NEVER fill a slot that is in `opted_out` — `fab onboard-coverage` already
207
+ excludes those from `missing`, but the Skill MUST NOT re-propose them
208
+ even if the user asks "fill all of them" — the dismiss is intentional.
209
+ - NEVER prompt the user when `missing.length === 0` — silent skip.
210
+ - NEVER set `onboard_slot` on a regular session-archive candidate in
211
+ Phase 2 — that field is RESERVED for the onboard phase. Mixing the
212
+ two would let session-archive proposals masquerade as onboard
213
+ coverage and let any random pending file claim a slot.
214
+ - MUST emit `onboard_slot: <slot>` verbatim — the slot name is one of
215
+ the locked S5 strings (tech-stack-decision / architecture-pattern /
216
+ code-style-tone / build-system-idiom / domain-vocabulary). The
217
+ fab_extract_knowledge schema enum will reject anything else.
218
+
@@ -0,0 +1,38 @@
1
+ # rc-history — version landmarks for fabric-archive
2
+
3
+ > **Loaded on demand.** SKILL.md hot path does NOT need this file. It exists so when you encounter an inline comment like `(rc.25 TASK-01)` and wonder "what changed then?", you can `Read` here without polluting routine archive invocations.
4
+
5
+ ## v2.0.0-rc.7 (T5)
6
+
7
+ Cross-session digest mechanism introduced. Stop hook writes per-session digest to `.fabric/.cache/session-digests/<session_id>.md`. fabric-archive Phase 0.0 reads these to stitch context across sessions since the last `knowledge_proposed` event.
8
+
9
+ ## v2.0.0-rc.20 (TASK-02 / TASK-03)
10
+
11
+ `assistant_turn_observed` event + cite-policy observability landed. KB-line parsing surfaces `cite_ids` / `cite_tags`. fabric-archive doesn't directly consume — see fabric-hint for the parser.
12
+
13
+ ## v2.0.0-rc.23 (F8c / a-C2)
14
+
15
+ - **F8c**: Phase 0.4 first-run onboard phase added. S5 slots (`tech-stack-decision`, `architecture-pattern`, `code-style-tone`, `build-system-idiom`, `domain-vocabulary`) baseline the workspace tone for plan_context retrieval.
16
+ - **a-C2**: knowledge_enriched event for description-grade frontmatter back-fill.
17
+
18
+ ## v2.0.0-rc.24 (TASK-01 / TASK-04)
19
+
20
+ `cite_contract_policy_activated` marker + per-cite `cite_commitments` parallel array. CJS twin of shared cite-line-parser shipped to hooks.
21
+
22
+ ## v2.0.0-rc.25 (TASK-01 / E4 / Q3.3 / Q3.4)
23
+
24
+ Major fabric-archive overhaul:
25
+
26
+ - **TASK-01**: `session_archive_attempted` event — drives Phase 0.0 cross-session digest rescan filter (outcome state machine: proposed / viability_failed / user_dismissed / skipped_no_signal).
27
+ - **TASK-05**: 5-entry model (E1 hook / E2 explicit / E3 AI-self / E4 user-range / E5 cron). Phase -0.5 Range Resolution parses time-window + topic-keyword hints into a `session_id[]` scope filter. Anti-loop constants (12h cooldown, normative-keyword scan) landed here.
28
+ - **Q3.4**: outcome-based rescan suppression — `user_dismissed` permanently skips a session.
29
+
30
+ ## v2.0.0-rc.27 (TASK-007 / TASK-011 / TASK-012)
31
+
32
+ - **TASK-007**: Phase 2.5 dry-run override path (Codex audit §2.25).
33
+ - **TASK-011** (Codex review fix): cite_commitments index-aligned with cite_ids on multi-id parses (audit §2.18 follow-up).
34
+ - **TASK-012** (Codex review fix): dropped stale "no dry-run mode" prose that conflicted with TASK-007.
35
+
36
+ ## v2.0.0-rc.28 (this release)
37
+
38
+ SKILL.md split into entry (hot path) + `ref/` (this file + i18n-policy + phase-0-4-onboard + worked-examples + e5-cron-recap). 1343 → ~950 lines for the hot path (~29% reduction). Active flow logic unchanged; reference-only content (almost-never-loaded i18n class taxonomy, Phase 0.4 onboard, end-to-end examples, E5 cron setup) moved out.
@@ -0,0 +1,78 @@
1
+ # Worked Examples — full reference
2
+
3
+ > **Loaded on demand.** SKILL.md gives the operative contract (the 5 types + layer heuristic + slug rules + MCP call shape). These end-to-end examples illustrate the integration — load when you want to see all fields filled out together.
4
+
5
+ ## Worked Examples
6
+
7
+ ## Example 1 — decision (team)
8
+
9
+ Session: User and agent debated whether the Stop-hook should be one .cjs script or three per-client scripts. Settled on one because stdout JSON shape `{"decision":"block","reason"}` is identical across Claude / Codex.
10
+
11
+ Skill output:
12
+
13
+ ```ts
14
+ mcp__fabric__fab_extract_knowledge({
15
+ source_sessions: ["WFS-2026-05-10-rc2"],
16
+ recent_paths: ["templates/claude-hooks/", "packages/cli/src/commands/hooks.ts"],
17
+ user_messages_summary: "User pushed back on three-script proposal; agreed single .cjs because stdout JSON shape is universal across Claude Code and Codex CLI.",
18
+ type: "decisions",
19
+ slug: "single-cjs-hook-script",
20
+ layer: "team",
21
+ relevance_scope: "narrow",
22
+ relevance_paths: [
23
+ "templates/claude-hooks/**/*.cjs",
24
+ "packages/cli/src/commands/hooks.ts"
25
+ ],
26
+ proposed_reason: "decision-confirmation",
27
+ session_context: "Session goal: ship Stop-hook for v2 release.\nTurning point: user rejected 3-script proposal after seeing identical stdout JSON across Claude / Codex.\nResult: single .cjs path locked in."
28
+ })
29
+ ```
30
+
31
+ Layer = team (引用本项目代码 + fabric-import 路径产物 signals). Scope = narrow (tied to hook templates + hooks command module; single-module evidence in edit_paths).
32
+
33
+ ### Example 2 — pitfall (team)
34
+
35
+ Session: deepMerge silently replaced the existing `hooks.Stop[]` array in `.claude/settings.json` instead of appending. Cost ~30 min to diagnose.
36
+
37
+ Skill output:
38
+
39
+ ```ts
40
+ mcp__fabric__fab_extract_knowledge({
41
+ source_sessions: ["WFS-2026-05-10-rc2"],
42
+ recent_paths: ["packages/cli/src/config/json.ts"],
43
+ user_messages_summary: "deepMerge default behavior REPLACES arrays. hooks.Stop[] needs an array-append-with-dedupe special case keyed on .command string match.",
44
+ type: "pitfalls",
45
+ slug: "deepmerge-array-replace-trap",
46
+ layer: "team",
47
+ relevance_scope: "broad",
48
+ relevance_paths: [],
49
+ proposed_reason: "diagnostic-then-fix",
50
+ session_context: "Session goal: wire hook installer for v2.\nTurning point: spent ~30 min chasing why prior Stop[] entries vanished — root cause was deepMerge replacing arrays silently.\nResult: array-append-with-dedupe special case added."
51
+ })
52
+ ```
53
+
54
+ Layer = team (绑定本项目代码的 pitfall signal). Scope = broad (deepMerge gotcha is cross-cutting — applies anywhere JSON merge is used, not just `json.ts`).
55
+
56
+ ### Example 3 — guideline (personal)
57
+
58
+ Session: User mentioned across three projects that they prefer 2-space indent in TypeScript and 4-space in Python.
59
+
60
+ Skill output:
61
+
62
+ ```ts
63
+ mcp__fabric__fab_extract_knowledge({
64
+ source_sessions: ["WFS-2026-05-10-rc2"],
65
+ recent_paths: [".editorconfig"],
66
+ user_messages_summary: "Personal indent preference: 2-space TS / 4-space Py. Stable across multiple projects, not project-specific.",
67
+ type: "guidelines",
68
+ slug: "indent-style-by-language",
69
+ layer: "personal",
70
+ relevance_scope: "broad",
71
+ relevance_paths: [],
72
+ proposed_reason: "explicit-user-mark",
73
+ session_context: "Session goal: align editor config.\nTurning point: user said '一直 prefer 2-space TS / 4-space Py,across projects'.\nResult: personal-layer guideline; not bound to this project."
74
+ })
75
+ ```
76
+
77
+ Layer = personal (跨项目通用 + 工具/编辑器偏好 signals dominate; no 强 team signal applies). Scope = broad with `relevance_paths=[]` (personal layer ALWAYS forces broad — paths don't generalize across projects per Phase 1.5 special case).
78
+
@@ -41,58 +41,9 @@ Required preconditions before any MCP call:
41
41
  - `mcp__fabric__fab_extract_knowledge` AND `mcp__fabric__fab_review` MCP tools are registered and reachable
42
42
  - Working tree is reasonably clean (large uncommitted churn pollutes git-log mining; warn but allow)
43
43
 
44
- ### Phase 0 — Init & .tmp Residue Scan
45
-
46
- Before reading `.fabric/.import-state.json`, scan for residue left by a
47
- prior crashed run. Skill state writes use a 2-step atomic pattern (Write
48
- `.tmp` then `Bash mv`); a crash between Step A and Step B leaves a
49
- `.fabric/.import-state.json.tmp` sidecar that the next invocation MUST
50
- triage.
51
-
52
- 1. Does `.fabric/.import-state.json.tmp` exist? (`Bash: ls .fabric/.import-state.json.tmp 2>/dev/null`)
53
- - **Does not exist** → proceed normally to Phase 0.1 (no residue work).
54
- - **Exists** → triage:
55
- 1. `Read` the `.tmp` file; try `JSON.parse` on the content.
56
- 2. Compare `mtime` of `.tmp` vs `.fabric/.import-state.json` via `Bash: stat`.
57
- - **Parse OK + .tmp mtime newer than main file** → rescue:
58
- `Bash: mv .fabric/.import-state.json.tmp .fabric/.import-state.json`
59
- (commits the last incomplete write atomically).
60
- - **Parse OK + .tmp mtime older than main file** → stale residue
61
- from an earlier run that subsequently completed; delete it:
62
- `Bash: rm .fabric/.import-state.json.tmp`.
63
- - **Parse fails** (syntax error / unterminated structure / truncated
64
- mid-write) → half-written, unrecoverable; delete it:
65
- `Bash: rm .fabric/.import-state.json.tmp`.
66
- 3. After triage, proceed to Phase 0.1.
67
-
68
- The 5-minute mtime heuristic (treat any `.tmp` older than 5 minutes as
69
- stale regardless of parse result) is an acceptable conservative simplification:
70
- no legitimate atomic write window stays open that long; anything older
71
- than 5 minutes is definitely crash residue. Implementations MAY use either
72
- the mtime-comparison rule above OR the 5-minute staleness rule.
73
-
74
- ### Phase 0.1 — State Corruption Recovery
75
-
76
- After residue triage, `Read` `.fabric/.import-state.json`. Detect
77
- corruption if ANY of the following hold:
78
-
79
- - `JSON.parse` throws (syntax error / unterminated structure / truncated)
80
- - Missing required field: `phase` OR `started_at` OR `last_checkpoint_at`
81
- - `phase` value not in the enum `{P1-done, P2-done, complete}`
82
-
83
- On corruption (any condition above):
84
-
85
- 1. `Bash: mv .fabric/.import-state.json .fabric/.import-state.json.corrupt-<ISO8601>`
86
- (preserve the corrupt file for postmortem; do NOT silently overwrite).
87
- 2. Phase 1 restarts from scratch (Phase 1 produces no MCP calls, so re-run
88
- is safe — re-globbing `.fabric/knowledge/team/**/*.md` is cheap and
89
- idempotent; the `p1_baseline_titles` array is regenerated).
90
- 3. DO NOT attempt automatic partial recovery; corrupt state is a signal
91
- that something serious happened (disk-full, kill -9 mid-write, fs
92
- error). Discard-and-restart is the only safe path.
93
-
94
- ENOENT (state file absent) is NOT corruption — it is the normal
95
- first-run state. Proceed to Phase 0.5.
44
+ ### Phase 0 — Init (state-recovery ref-only)
45
+
46
+ On invocation: read/initialize `.fabric/.import-state.json` (resumable phase tracker — see Checkpoint Logic section below for schema). Scan workspace for stale `.tmp-import-*` residues (`.fabric/.import-state.json.tmp-*`). For details on the .tmp scan + state corruption recovery (rare — only triggers when a prior import crashed mid-phase), `Read packages/cli/templates/skills/fabric-import/ref/state-recovery.md` (or `.claude/skills/fabric-import/ref/state-recovery.md` post-install).
96
47
 
97
48
  ### Phase 0.5 — Config Load
98
49
 
@@ -112,74 +63,13 @@ Whether the run is "first-run" vs "re-run" is decided by inspecting
112
63
  `.fabric/.import-state.json`: ENOENT (or any state with `phase != "complete"`
113
64
  and `final_summary.proposed == 0`) → first-run window; otherwise re-run window.
114
65
 
115
- ### UX i18n Policy (5-class bilingualization)
116
-
117
- The skill consults `fabric_language` from `.fabric/fabric-config.json`
118
- (固化于 install 时,via `scan.ts:detectExistingLanguage`; default `"en"` when no
119
- CJK signal is detected in README + docs/; may resolve to `"match-existing"`,
120
- `"zh-CN"`, `"en"`, or `"zh-CN-hybrid"`). All user-facing text in the
121
- following 5 categories MUST be rendered in the resolved language:
122
-
123
- 1. **Roll-up templates** — final summary blocks (`# Import Summary — phase=...`,
124
- `## Phase 2 — Mining`, `## Phase 3 — Dedup`, etc.). zh-CN ↔ en mirror.
125
- 2. **Errors / Preconditions warnings** — abort + gate-fail messages (e.g.
126
- "请先运行 fabric install 完成基线扫描…" / "Please run fabric install first…").
127
- zh-CN ↔ en mirror.
128
- 3. **Confirmation prompts** — re-run-within-24h prompt, reset prompts, etc.
129
- zh-CN ↔ en mirror.
130
- 4. **Dry-run table headers** — `# Import Dry Run — would propose N pending
131
- entries…` + the `| # | Source | Type | Slug | Scope | Summary |` header row.
132
- zh-CN ↔ en mirror.
133
- 5. **AskUserQuestion** — `header` + `question` fields (NOT `options[]`).
134
- zh-CN ↔ en mirror. fabric-import itself does not surface AskUserQuestion
135
- in the current contract (the rare re-run prompt is free-text), but if a
136
- future version adds one, this rule applies.
137
-
138
- Rendering rule:
139
-
140
- - `fabric_language === "zh-CN"` → emit the zh-CN variant; pure monolingual, no language mixing inside a single user-facing block.
141
- - `fabric_language === "en"` → emit the en variant; pure monolingual, no language mixing inside a single user-facing block.
142
- - `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`).
143
- - `fabric_language === "match-existing"` or any other value → emit the en variant; pure monolingual.
144
-
145
- Protected tokens (`fab_extract_knowledge`, `fab_review`, `relevance_scope`,
146
- `relevance_paths`, `broad`, `narrow`, `source_sessions`, `proposed_reason`,
147
- `session_context`, `intent_clues`, `tech_stack`, `impact`, `must_read_if`,
148
- `pending_path`, `layer`, `team`, `personal`,
149
- `knowledge_scope_degraded`, `MUST`, `NEVER`, `.fabric/knowledge/`, etc.)
150
- are NEVER translated — they appear verbatim in both language variants.
151
- The bilingualization scope is prose ONLY.
152
-
153
- ### AskUserQuestion i18n Policy (value vs label)
154
-
155
- When a skill (this one or any sibling skill the user is composing with)
156
- issues an `AskUserQuestion`, the `header` and `question` strings are
157
- user-facing prose → translated per `fabric_language`. The `options[]`
158
- array entries (e.g. `["approve", "reject", "modify", "defer", "skip"]` in
159
- fabric-review) are **routing keys** consumed by the skill state machine —
160
- they MUST remain English regardless of `fabric_language`.
66
+ ### UX i18n Policy
161
67
 
162
- ```ts
163
- // EN (fabric_language === "en")
164
- AskUserQuestion({
165
- header: "Review pending entry",
166
- question: "What action for '{title}'?",
167
- options: ["approve", "reject", "modify", "defer", "skip"]
168
- })
68
+ 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_extract_knowledge`, `fab_review`, `.fabric/.import-state.json`, schema/scope/layer enum values) are NEVER translated.
169
69
 
170
- // zh-CN (fabric_language === "zh-CN")
171
- AskUserQuestion({
172
- header: "审核 pending 条目",
173
- question: "对 '{title}' 执行什么操作?",
174
- options: ["approve", "reject", "modify", "defer", "skip"] // 不翻译 — routing key
175
- })
176
- ```
70
+ `AskUserQuestion` policy: `header` + `question` translate; `options[]` are routing keys — stay English regardless of locale.
177
71
 
178
- Rationale: localizing routing keys would force every routing branch to
179
- dual-string match (e.g. `if (choice === "approve" || choice === "通过")`),
180
- which doubles the surface area for protected-token regressions and breaks
181
- the option-list invariants that downstream tooling depends on. Keeping
182
- `options[]` English-only is contract-locked across all three skills.
72
+ **For the full 5-class taxonomy + edge cases:** `Read packages/cli/templates/skills/fabric-import/ref/i18n-policy.md` (or `.claude/skills/fabric-import/ref/i18n-policy.md` post-install).
183
73
 
184
74
  ## 3-Phase Pipeline (P1 reference / P2 mine / P3 dedup)
185
75