@fenglimg/fabric-cli 2.0.0 → 2.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +6 -5
- package/dist/chunk-BATF4PEJ.js +361 -0
- package/dist/{chunk-OBQU6NHO.js → chunk-COI5VDFU.js} +0 -18
- package/dist/chunk-F46ORPOA.js +903 -0
- package/dist/chunk-HFQVXY6P.js +86 -0
- package/dist/chunk-L4Q55UC4.js +52 -0
- package/dist/chunk-LFIKMVY7.js +27 -0
- package/dist/chunk-MF3OTILQ.js +544 -0
- package/dist/chunk-PWLW3B57.js +18 -0
- package/dist/chunk-RYAFBNES.js +33 -0
- package/dist/chunk-T5RPGCCM.js +40 -0
- package/dist/chunk-WU6GAPKH.js +36 -0
- package/dist/config-XJIPZNUP.js +13 -0
- package/dist/doctor-QVNPHLJK.js +920 -0
- package/dist/index.js +23 -8
- package/dist/{init-BIRSIOXO.js → install-2HDO5FTQ.js} +807 -705
- package/dist/metrics-ACEQFPDU.js +122 -0
- package/dist/onboard-coverage-MFCAEBDO.js +220 -0
- package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-FC6P3WFE.js} +34 -28
- package/dist/scope-explain-2F2R5URO.js +33 -0
- package/dist/status-GLQWLWH6.js +23 -0
- package/dist/store-XTSE5TY6.js +105 -0
- package/dist/sync-BJCWDPNC.js +245 -0
- package/dist/uninstall-TAXSUSKH.js +1073 -0
- package/dist/whoami-B6AEMSEV.js +31 -0
- package/package.json +30 -5
- package/templates/hooks/cite-policy-evict.cjs +231 -0
- package/templates/hooks/configs/README.md +29 -6
- package/templates/hooks/configs/claude-code.json +14 -3
- package/templates/hooks/configs/codex-hooks.json +6 -3
- package/templates/hooks/configs/cursor-hooks.json +8 -10
- package/templates/hooks/fabric-hint.cjs +873 -105
- package/templates/hooks/knowledge-hint-broad.cjs +549 -135
- package/templates/hooks/knowledge-hint-narrow.cjs +830 -26
- package/templates/hooks/lib/banner-i18n.cjs +309 -0
- package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
- package/templates/hooks/lib/cite-contract-reminder.cjs +179 -0
- package/templates/hooks/lib/cite-line-parser.cjs +180 -0
- package/templates/hooks/lib/client-adapter.cjs +106 -0
- package/templates/hooks/lib/config-cache.cjs +107 -0
- package/templates/hooks/lib/state-store.cjs +84 -0
- package/templates/hooks/lib/summary-fallback.cjs +210 -0
- package/templates/skills/fabric-archive/SKILL.md +97 -419
- package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
- package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
- package/templates/skills/fabric-archive/ref/i18n-policy.md +86 -0
- package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
- package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +218 -0
- package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +62 -0
- package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +68 -0
- package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +108 -0
- package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
- package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
- package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
- package/templates/skills/fabric-archive/ref/rc-history.md +38 -0
- package/templates/skills/fabric-archive/ref/worked-examples.md +78 -0
- package/templates/skills/fabric-import/SKILL.md +77 -514
- package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
- package/templates/skills/fabric-import/ref/i18n-policy.md +79 -0
- package/templates/skills/fabric-import/ref/output-contract.md +61 -0
- package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
- package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
- package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
- package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
- package/templates/skills/fabric-review/SKILL.md +90 -284
- package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
- package/templates/skills/fabric-review/ref/i18n-policy.md +111 -0
- package/templates/skills/fabric-review/ref/modify-flow.md +103 -0
- package/templates/skills/fabric-review/ref/output-contract.md +58 -0
- package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
- package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
- package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
- package/templates/skills/fabric-sync/SKILL.md +46 -0
- package/templates/skills/lib/shared-policy.md +69 -0
- package/dist/chunk-6ICJICVU.js +0 -10
- package/dist/chunk-74SZWYPH.js +0 -658
- package/dist/chunk-EYIDD2YS.js +0 -1000
- package/dist/doctor-T7JWODKG.js +0 -282
- package/dist/hooks-Y74Y5LQS.js +0 -12
- package/dist/scan-LMK3UCWL.js +0 -22
- package/dist/serve-H554BHLG.js +0 -124
- package/templates/agents-md/AGENTS.md.template +0 -59
- package/templates/bootstrap/CLAUDE.md +0 -8
- package/templates/bootstrap/codex-AGENTS-header.md +0 -6
- package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
|
@@ -1,401 +1,147 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fabric-archive
|
|
3
|
-
description:
|
|
3
|
+
description: 归档对话洞察到 .fabric/knowledge/pending (NOT code review). Triggers 以后/always/never/下次/记一下;wrong-turn-revert;decision-confirm;dismissal-reason;/fabric-archive.
|
|
4
4
|
allowed-tools: Read, Glob, Grep, Bash, mcp__fabric__fab_extract_knowledge
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
> **Surface**:
|
|
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
|
-
|
|
11
|
+
Invoke this skill ONLY when ONE of the following holds:
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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
|
|
18
|
-
|
|
19
|
-
This skill is `Check-not-Ask`, not a preference interview:
|
|
20
|
-
|
|
21
|
-
- Phase 0 proactively gathers candidate evidence from the session
|
|
22
|
-
- Phase 0.5 viability gate aborts the skill if the session lacks any archive-signal (anti-archive guard)
|
|
23
|
-
- Phase 1 classifies / layers / slugs each candidate and presents one batch review for user correction
|
|
24
|
-
- Phase 1.5 assigns `scope=narrow|broad` and derives `relevance_paths` from edit history (rc.5 single-signal source)
|
|
25
|
-
- Phase 2 calls `fab_extract_knowledge` once per confirmed candidate
|
|
26
|
-
|
|
27
|
-
## 执行流程 (5 Phase / 1 User Review Round)
|
|
28
|
-
|
|
29
|
-
### Phase 0.0 — Collect Cross-Session Digests (v2.0.0-rc.7 T5)
|
|
30
|
-
|
|
31
|
-
Before any single-session collection or viability gating, stitch together
|
|
32
|
-
context from every session that has accumulated since the last
|
|
33
|
-
`knowledge_proposed` event. The rc.7 Stop hook writes a per-session digest to
|
|
34
|
-
`.fabric/.cache/session-digests/<session_id>.md` (≤5KB, contains top 10 user
|
|
35
|
-
messages + edit_paths + 1-line title), so this phase is a tail-scan + read.
|
|
17
|
+
If none hold, stop the skill and tell the user (UX i18n Policy class 2):
|
|
36
18
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
normal first-run state).
|
|
40
|
-
2. **Find the anchor.** Walk the tail backwards to locate the most recent
|
|
41
|
-
`knowledge_proposed` event (`event_type === "knowledge_proposed"`). The
|
|
42
|
-
anchor's `ts` becomes the lower bound for digest selection. If NO anchor
|
|
43
|
-
exists, treat all digests in the cache as in-scope.
|
|
44
|
-
3. **Collect session_ids since anchor.** Scan the tail forward from the
|
|
45
|
-
anchor and collect every distinct `session_id` field that appears on any
|
|
46
|
-
event newer than the anchor. Distinct ordering preserved.
|
|
47
|
-
4. **Load digests.** For each collected `session_id`, read
|
|
48
|
-
`.fabric/.cache/session-digests/<session_id>.md`. Missing digest files
|
|
49
|
-
degrade silently (the digest write was best-effort, so a Stop hook crash
|
|
50
|
-
can produce a session_id without a digest). Cap the loaded digest set at
|
|
51
|
-
10 most-recent sessions to bound LLM context (~50KB worst-case).
|
|
52
|
-
5. **Build cross-session context.** Concatenate the loaded digests into a
|
|
53
|
-
single `### Cross-session digest` block to carry into Phase 0.5 + Phase 1.
|
|
54
|
-
Use this block to:
|
|
55
|
-
- Detect session-spanning patterns (e.g. a discussion that started in
|
|
56
|
-
session A and continued in session B).
|
|
57
|
-
- Populate the `source_sessions` array on every fab_extract_knowledge
|
|
58
|
-
call — the array form (T5) replaces the legacy `source_session` string.
|
|
59
|
-
- Inform the `session_context` blob written to each pending entry's body
|
|
60
|
-
(3-5 lines summarizing goal + key turning point, per T6).
|
|
19
|
+
- zh-CN: `没有触发归档信号;如需手动归档请显式调用 fabric-archive`
|
|
20
|
+
- en: `No archive signal detected; to manually archive, explicitly invoke fabric-archive`
|
|
61
21
|
|
|
62
|
-
|
|
63
|
-
entirely, this phase reports an empty context and Phase 0 falls back to the
|
|
64
|
-
single-session behaviour. Tests that synthesize events.jsonl without
|
|
65
|
-
populating the digest cache continue to work.
|
|
22
|
+
Render per `fabric_language` resolved in Phase 0.5.
|
|
66
23
|
|
|
67
|
-
|
|
24
|
+
This skill runs automatically — it does not interview the user for preferences. It gathers evidence, aborts if no archive signal exists, then classifies + persists.
|
|
68
25
|
|
|
69
|
-
|
|
26
|
+
## 执行流程 (1 User Review Round)
|
|
70
27
|
|
|
71
|
-
|
|
72
|
-
- Use `Bash` with `tail -n 200 .fabric/events.jsonl` if the file is large.
|
|
73
|
-
- Tolerate ENOENT — empty ledger is a normal first-run state.
|
|
74
|
-
2. Enumerate `recent_paths`: workspace files touched by Read/Edit/Write in the current session. Cap at 20 most-recent paths.
|
|
75
|
-
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.
|
|
76
|
-
4. Build a candidate list: each candidate is one observation that MIGHT be worth archiving.
|
|
28
|
+
v2.0.0-rc.37 NEW-9 collapsed the flow to **3 macro-phases**; the legacy fine-grained phases survive as labelled sub-steps (back-compat for `ref/` navigation + existing traces):
|
|
77
29
|
|
|
78
|
-
|
|
30
|
+
- **GATHER** = Phase 0 (range) → 0.5 (config) → 1 (ledger scan via `fab_archive_scan`) → [1.5 onboard] → 2 (candidates). Resolve scope, load config, deterministically scan the ledger for in-scope sessions, collect candidate observations.
|
|
31
|
+
- **REVIEW** = Phase 2.5 (viability gate — abort guard) → 3 (classify / layer / slug + batch review) → 3.5 (scope + relevance_paths). The single user review round lives here.
|
|
32
|
+
- **PERSIST** = Phase 4 (`fab_extract_knowledge`, one call per candidate) → 4.5 (archive-attempt ledger).
|
|
79
33
|
|
|
80
|
-
|
|
34
|
+
Sub-step chain: `0 → 0.5 → 1 → [1.5] → 2 → 2.5 → 3 → 3.5 → 4 → 4.5`. Each below is a navigator stub — full procedure, decision tables, and worked examples live in `ref/`.
|
|
81
35
|
|
|
82
|
-
|
|
36
|
+
### Phase 0 — Range Resolution
|
|
83
37
|
|
|
84
|
-
|
|
38
|
+
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.
|
|
85
39
|
|
|
86
|
-
|
|
40
|
+
`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.
|
|
87
41
|
|
|
88
|
-
|
|
89
|
-
2. Wrong-turn-and-revert: a path was edited, then reverted (or partially undone) after diagnosis — indicates a pitfall worth recording.
|
|
90
|
-
3. Long diagnostic loop: an issue took > 15 minutes (or > ~10 tool turns) of debugging before resolution.
|
|
91
|
-
4. New dependency adoption: a new package / library / external tool was introduced (e.g. `package.json` / `pyproject.toml` / `Cargo.toml` diff adds a dep).
|
|
92
|
-
5. New pattern emergence: a reusable abstraction or naming convention was named ("the X phase", "the Y pattern", "let's call this Z").
|
|
93
|
-
6. Decision confirmation: ≥ 2 alternatives were weighed AND a rationale was given before settling.
|
|
94
|
-
7. Explicit dismissal-with-reason: user rejected an approach AND stated why (the why is the archivable knowledge, not the dismissal itself).
|
|
95
|
-
8. Process formalization: a multi-step procedure was executed in a specific order AND the order was identified as load-bearing.
|
|
42
|
+
### Phase 0.5 — Config Load
|
|
96
43
|
|
|
97
|
-
|
|
44
|
+
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.
|
|
98
45
|
|
|
99
|
-
|
|
100
|
-
2. Pure refactor: rename / move / extract with no behavior change AND no naming convention being established.
|
|
101
|
-
3. Narrow rename request: user asked to rename one symbol / file with no rationale.
|
|
102
|
-
4. Duplicate of existing canonical: the observation is already covered by an existing entry under `.fabric/knowledge/<type>/` (do a quick Glob before deciding).
|
|
46
|
+
### Phase 0.6 — Store routing (v2.1 multi-store)
|
|
103
47
|
|
|
104
|
-
|
|
48
|
+
Archives land in the **active write store** for the entry's scope — NEVER pick a store yourself. Run `fabric scope-explain team` (or the relevant scope) to get the resolved `writeTarget` (alias + UUID); that is where `fab_extract_knowledge` persists. Single-store / no global config → the lone store (back-compat). After persisting, **echo the target store alias** to the user (`归档到 store '<alias>'`). Personal-scope entries route to the personal store (never the shared team store, R5#3). Do NOT read `~/.fabric` store trees directly — go through `scope-explain` / the MCP write path.
|
|
105
49
|
|
|
106
|
-
|
|
107
|
-
archive_signals_hit = count of archive signals fired
|
|
108
|
-
anti_signals_hit = count of anti-archive signals fired
|
|
109
|
-
user_explicit_invoke = user typed "archive what we just did" / "fabric archive" / similar
|
|
50
|
+
### UX i18n Policy
|
|
110
51
|
|
|
111
|
-
|
|
112
|
-
gate = PASS # explicit invocation bypasses all gates
|
|
113
|
-
ELIF archive_signals_hit == 0:
|
|
114
|
-
gate = FAIL (reason="no_signal")
|
|
115
|
-
ELIF anti_signals_hit > 0 AND archive_signals_hit == 0:
|
|
116
|
-
gate = FAIL (reason="anti_signal_dominates")
|
|
117
|
-
ELSE:
|
|
118
|
-
gate = PASS
|
|
119
|
-
```
|
|
52
|
+
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).
|
|
120
53
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
Stop the skill with the exact user-facing message:
|
|
54
|
+
`Read ref/i18n-policy.md` for the full 5-class taxonomy + edge cases.
|
|
124
55
|
|
|
125
|
-
|
|
126
|
-
本次会话为常规执行,无新知识可归档(gate=<reason>)。如需强制归档,请显式调用 fabric-archive。
|
|
127
|
-
```
|
|
56
|
+
### Phase 1 — Collect Cross-Session Digests (server-side ledger scan, rc.37 NEW-9)
|
|
128
57
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
#### On gate PASS
|
|
132
|
-
|
|
133
|
-
Proceed to Phase 1 with the candidates carried over from Phase 0.
|
|
134
|
-
|
|
135
|
-
### Phase 1 — Classify, Layer, Slug, Review
|
|
58
|
+
The deterministic ledger scan now runs **server-side** — call `fab_archive_scan({ range, session_id })` (range = Phase 0's `session_id[]` or `"all"`/omitted). It returns:
|
|
136
59
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
- **summary** (1-2 sentences, will become the entry body's lead paragraph)
|
|
60
|
+
- `anchor_ts` — ts of the last `knowledge_proposed` (the lower bound).
|
|
61
|
+
- `session_ids[]` — distinct in-scope sessions since the anchor, ALREADY filtered through the outcome-ledger state machine (drops `user_dismissed`, sessions inside the 12h anti-loop cooldown, and watermarked sessions with no new high-value signal). First-seen order.
|
|
62
|
+
- `dropped[]` — `{session_id, reason}` for transparency.
|
|
63
|
+
- `covered_through_ts` — max ts examined (becomes the next watermark).
|
|
64
|
+
- `already_proposed_keys[]` — idempotency keys already proposed but not yet reviewed; drop matching candidates in Phase 3 (cross-session pending dedupe).
|
|
143
65
|
|
|
144
|
-
|
|
66
|
+
Then (LLM side, Boundary B): for each returned `session_id`, load `.fabric/.cache/session-digests/<session_id>.md`, concatenate into a `### Cross-session digest` block, and populate `source_sessions[]` + `session_context` for Phase 4. Cap at `archive_digest_max_sessions`. Missing digest files degrade silently.
|
|
145
67
|
|
|
146
|
-
-
|
|
147
|
-
- **decision** — A choice between alternatives with rationale. Worth-archive signal: ≥2 options were weighed AND a rationale was given. Skip-it signal: the choice was forced by external constraint with no real alternative. Positive: "Single .cjs hook script over three per-client scripts — rationale: identical stdout JSON shape across Claude/Codex". Negative: "Used the existing fab_extract_knowledge schema" (no alternative was considered).
|
|
148
|
-
- **guideline** — A normative rule for future similar situations. Worth-archive signal: the user said "always" / "never" / "from now on". Skip-it signal: a one-off preference that won't generalize. Positive: "Slug naming: kebab-case, 2-5 words, 20-40 chars, semantic core only". Negative: "Use 4-space indent in this one file" (too narrow).
|
|
149
|
-
- **pitfall** — A trap that wasted time and is non-obvious. Worth-archive signal: a bug took >15 min to diagnose AND is repeatable. Skip-it signal: a typo or one-time API quirk. Positive: "deepMerge replaces arrays — hooks.Stop[] needs special-case append-with-dedupe". Negative: "Forgot a comma in JSON" (too obvious).
|
|
150
|
-
- **process** — A multi-step procedure with a stable shape. Worth-archive signal: the steps were executed in a specific order AND the order matters. Skip-it signal: a one-shot script with no reusable structure. Positive: "fab_review approve = counter++ → frontmatter inject → git mv → meta rebuild → event append (5 atomic steps)". Negative: "Ran the tests, then committed" (trivial, no reusable shape).
|
|
68
|
+
`Read ref/phase-1-cross-session.md` for the (now server-side) filter state machine rules + the digest-stitch + graceful-degradation notes. The hand-rolled `tail -n 200` events.jsonl scan is retired — `fab_archive_scan` is the source of truth for which sessions to load.
|
|
151
69
|
|
|
152
|
-
|
|
70
|
+
Graceful degradation: missing digest cache → single-session fallback. Missing `session_archive_attempted` events (pre-rc.25) → legacy "scan everything since anchor" behaviour.
|
|
71
|
+
|
|
72
|
+
### Phase 1.5 — First-run Onboard (ref-only)
|
|
73
|
+
|
|
74
|
+
**SKIP this phase entirely unless** entry_point ∈ {E2_explicit_user_invoke, E4_user_range_rollback} AND `fabric onboard-coverage --json` reports `missing.length > 0`. For E1/E3/E5, silently fall through to Phase 0.
|
|
75
|
+
|
|
76
|
+
`Read ref/phase-1-5-onboard.md` for the Step 1-4 coverage check → user prompt → tour-and-propose procedure.
|
|
77
|
+
|
|
78
|
+
### Phase 2 — Collect Candidates
|
|
79
|
+
|
|
80
|
+
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.
|
|
81
|
+
|
|
82
|
+
### Phase 2.5 — Viability Gate (Anti-Archive Guard)
|
|
83
|
+
|
|
84
|
+
Coarse viability check. **PASS conditions**: user_explicit_invoke OR ≥1 archive signal hit. v2.0.0-rc.37 NEW-4 simplifies the legacy 8-signal list into **3 major categories** (each maps to multiple legacy signals for back-compat scoring):
|
|
85
|
+
|
|
86
|
+
1. **User-driven knowledge expression** — user message contains normative language (`always`/`never`/`from now on`/`下次注意`/`记一下`/`以后`/`永远不要`), OR user weighed ≥2 alternatives and gave rationale (decision confirmation), OR user explicitly dismissed an approach AND stated why (dismissal-with-reason). Legacy signals #1 + #6 + #7.
|
|
87
|
+
2. **Reflective discovery** — AI tried path X, reflected, then took path Y (wrong-turn-and-revert); OR a long diagnostic loop (>15 min / >10 turns) surfaced a non-obvious cause; OR a reusable pattern was named in the session ("the X phase", "the Y pattern"). Legacy signals #2 + #3 + #5.
|
|
88
|
+
3. **Concrete artifact change** — a new dependency was added (package.json/pyproject.toml/Cargo.toml diff), OR a load-bearing multi-step procedure was formalized in a specific order. Legacy signals #4 + #8.
|
|
89
|
+
|
|
90
|
+
Pre-PASS MUST step (rc.37 NEW-4): for each candidate, run a quick `Glob` over `.fabric/knowledge/**/*.md` keyed on slug-stem to check for duplicate canonical entry. If duplicate found → drop candidate (treat as anti-signal #4 'duplicate of existing canonical'). This is a HARD gate, not advisory — silently writing a near-duplicate is the highest-noise failure mode.
|
|
91
|
+
|
|
92
|
+
**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'`). Gate-FAIL message for E2/E4 MUST include the "to force-archive, explicitly invoke fabric-archive" remediation pointer so the user has an unambiguous escape hatch when the gate misclassifies (zh-CN: `如需强制归档,请显式调用 fabric-archive` / en: `To force-archive, explicitly invoke fabric-archive`).
|
|
93
|
+
|
|
94
|
+
`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.
|
|
95
|
+
|
|
96
|
+
### Phase 3 — Classify, Layer, Slug, Review
|
|
97
|
+
|
|
98
|
+
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).
|
|
99
|
+
|
|
100
|
+
#### Layer Classification Heuristic (verbatim, contract-locked)
|
|
153
101
|
|
|
154
102
|
> - **强 team**: 引用本项目代码、团队共识用语("we decided")、fabric-import 路径产物、业务领域、绑定本项目代码的 pitfall
|
|
155
103
|
> - **强 personal**: 第一人称偏好、跨项目通用、工具/编辑器偏好、个人工作流
|
|
156
104
|
> - **默认 team**: 安全偏置——错标 team 在 PR review 中会被发现,错标 personal 静默丢失
|
|
157
105
|
|
|
158
|
-
Resolution
|
|
106
|
+
Resolution: 强 team first; assign personal only if 强 personal dominates AND no 强 team applies; else default team.
|
|
107
|
+
|
|
108
|
+
`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.
|
|
159
109
|
|
|
160
|
-
|
|
110
|
+
### Phase 3.5 — Scope Decision + relevance_paths Derivation
|
|
161
111
|
|
|
162
|
-
|
|
163
|
-
2. 2-5 words separated by hyphens
|
|
164
|
-
3. 20-40 characters total length
|
|
165
|
-
4. semantic core only (drop articles "the/a", drop generic suffixes "stuff/thing")
|
|
166
|
-
5. unique within its (type, layer) bucket — if collision, the LLM must add a discriminating word, NOT a counter
|
|
112
|
+
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).
|
|
167
113
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
#### Decision Tree (是否值得归档)
|
|
171
|
-
|
|
172
|
-
```
|
|
173
|
-
Recent session contains an observation worth keeping?
|
|
174
|
-
├─ NO → skip (do nothing, no MCP call)
|
|
175
|
-
└─ YES → does it fit one of {model, decision, guideline, pitfall, process}?
|
|
176
|
-
├─ NO → skip (not classifiable = not yet ripe)
|
|
177
|
-
└─ YES → assign type
|
|
178
|
-
↓
|
|
179
|
-
Apply layer heuristic
|
|
180
|
-
↓
|
|
181
|
-
Propose slug per 5 rules
|
|
182
|
-
↓
|
|
183
|
-
Present in batch review
|
|
184
|
-
↓
|
|
185
|
-
User confirms / corrects / rejects
|
|
186
|
-
↓
|
|
187
|
-
Phase 2: call fab_extract_knowledge once per confirmed candidate
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
#### Batch Review Template
|
|
191
|
-
|
|
192
|
-
Present all candidates in a single screen using this exact structure:
|
|
193
|
-
|
|
194
|
-
```md
|
|
195
|
-
# Archive Review — N candidates
|
|
196
|
-
|
|
197
|
-
## C1 [type=decision] [layer=team] [scope=narrow] slug=wave-1-parallel-task-dag
|
|
198
|
-
Summary: <1-2 sentences capturing the observation>
|
|
199
|
-
Layer reasoning: <which 强 team / 强 personal signal applied, or default team>
|
|
200
|
-
Scope reasoning: <why narrow or broad — see Phase 1.5>
|
|
201
|
-
relevance_paths: ["packages/cli/src/commands/plan.ts", "packages/cli/templates/**/*.md"]
|
|
202
|
-
Confirm? (Y to accept, edit type/layer/slug/scope/relevance_paths inline, N to skip)
|
|
203
|
-
|
|
204
|
-
## C2 [type=pitfall] [layer=team] [scope=broad] slug=deepmerge-array-replace-trap
|
|
205
|
-
Summary: ...
|
|
206
|
-
Layer reasoning: ...
|
|
207
|
-
Scope reasoning: ...
|
|
208
|
-
relevance_paths: []
|
|
209
|
-
Confirm? ...
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
The user MAY edit type/layer/slug/scope/relevance_paths inline before confirming. The user MAY skip individual candidates without rejecting the whole batch. Inline-editing `[scope=...]` triggers a re-derivation of `relevance_paths` per the Phase 1.5 rules (narrow ⇒ recompute from edit_paths; broad ⇒ force `[]`).
|
|
213
|
-
|
|
214
|
-
### Phase 1.5 — Scope Decision + relevance_paths Derivation
|
|
114
|
+
`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.
|
|
215
115
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
#### Scope decision (narrow vs broad)
|
|
116
|
+
### Phase 4 — Persist via MCP
|
|
219
117
|
|
|
220
|
-
|
|
221
|
-
scope =
|
|
222
|
-
narrow IF the candidate is tied to a specific module / file / subsystem
|
|
223
|
-
AND there is explicit single-module evidence in edit_paths
|
|
224
|
-
(i.e. all worth-keeping edits in this session concentrated in one
|
|
225
|
-
module tree, OR the candidate explicitly references that module)
|
|
118
|
+
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**.
|
|
226
119
|
|
|
227
|
-
|
|
228
|
-
(applies regardless of which path the agent is working in)
|
|
120
|
+
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).
|
|
229
121
|
|
|
230
|
-
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Special case — Personal layer ALWAYS resolves to `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`.
|
|
234
|
-
|
|
235
|
-
##### Examples
|
|
236
|
-
|
|
237
|
-
- `decision: single-cjs-hook-script` → `narrow` (tied to `templates/claude-hooks/` + `packages/cli/src/commands/hooks.ts`)
|
|
238
|
-
- `pitfall: deepmerge-array-replace-trap` → `broad` (cross-cutting JSON merge gotcha, applies anywhere deepMerge is used)
|
|
239
|
-
- `guideline: slug-naming-rules` → `broad` (methodology, no specific module)
|
|
240
|
-
- `model: wave-1-parallel-task-dag` → `narrow` (tied to `packages/cli/src/commands/plan.ts`)
|
|
241
|
-
- `guideline: indent-style-by-language` (personal layer) → `broad + []` (personal forces broad)
|
|
242
|
-
|
|
243
|
-
#### relevance_paths derivation algorithm (rc.5 single-signal: edit_paths only)
|
|
244
|
-
|
|
245
|
-
rc.5 uses ONLY the `edit_paths` signal — list of paths modified by `Edit` / `Write` / `MultiEdit` tool calls in the current session. Multi-signal (read_paths + body regex + symbols) is explicitly deferred to rc.7 per design decision.
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
Step 1: COLLECT
|
|
249
|
-
edit_paths = []
|
|
250
|
-
Scan session transcript for tool_use entries where
|
|
251
|
-
tool_use.name ∈ {Edit, Write, MultiEdit}
|
|
252
|
-
Extract the file_path argument from each, push into edit_paths.
|
|
253
|
-
|
|
254
|
-
Step 2: DEDUPE
|
|
255
|
-
edit_paths = unique(edit_paths)
|
|
256
|
-
|
|
257
|
-
Step 3: BLACKLIST FILTER
|
|
258
|
-
Drop paths matching any of:
|
|
259
|
-
- **/*.<ext> where <ext> is a single trivial extension on a single file
|
|
260
|
-
(i.e. avoid emitting bare **/*.md as a relevance pattern)
|
|
261
|
-
- Repo-root single files: README.md, package.json, package-lock.json,
|
|
262
|
-
pnpm-lock.yaml, tsconfig.json, .gitignore, LICENSE, CHANGELOG.md
|
|
263
|
-
- Read-only paths (never modified) — those go to ## Evidence, not relevance_paths
|
|
264
|
-
|
|
265
|
-
Step 4: PUBLIC-PREFIX GENERALIZE (depth ≤ 2, minGroupSize = 2)
|
|
266
|
-
Group remaining paths by common prefix.
|
|
267
|
-
For each group of ≥ 2 sibling paths sharing a prefix:
|
|
268
|
-
- Compute longest common directory prefix
|
|
269
|
-
- Limit generalization depth: at most 2 levels below the common prefix
|
|
270
|
-
- Emit glob: <common-prefix>/**/*.<ext> (or <common-prefix>/**/<filename>)
|
|
271
|
-
Singleton paths (group size = 1) are kept as-is (literal path, no glob).
|
|
272
|
-
|
|
273
|
-
Step 5: SCOPE GATE
|
|
274
|
-
IF scope == broad → relevance_paths = [] (force empty regardless of edit_paths)
|
|
275
|
-
IF scope == narrow → relevance_paths = result of Step 4
|
|
276
|
-
|
|
277
|
-
Step 6: ATTACH READ-ONLY EVIDENCE
|
|
278
|
-
Read-only paths (filtered in Step 3) are emitted as a ## Evidence markdown
|
|
279
|
-
block in the pending entry body — NOT in relevance_paths. They document
|
|
280
|
-
what the agent consulted without making them part of the activation gate.
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
##### Worked generalization example
|
|
284
|
-
|
|
285
|
-
Edit history during session:
|
|
286
|
-
|
|
287
|
-
```
|
|
288
|
-
packages/server/src/services/extract.ts
|
|
289
|
-
packages/server/src/services/review.ts
|
|
290
|
-
packages/server/src/services/promote.ts
|
|
291
|
-
packages/cli/src/commands/plan.ts
|
|
292
|
-
README.md
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
Step 1-2 (collect + dedupe): all 5 unique.
|
|
296
|
-
Step 3 (blacklist): drop `README.md` (repo-root single file).
|
|
297
|
-
Step 4 (generalize, depth ≤ 2, minGroupSize = 2):
|
|
298
|
-
- `packages/server/src/services/{extract,review,promote}.ts` → group size 3 ≥ 2, common prefix `packages/server/src/services/`, glob: `packages/server/src/services/**/*.ts`
|
|
299
|
-
- `packages/cli/src/commands/plan.ts` → group size 1, kept literal.
|
|
300
|
-
|
|
301
|
-
Step 5 (assume `scope=narrow`):
|
|
302
|
-
|
|
303
|
-
```json
|
|
304
|
-
"relevance_paths": [
|
|
305
|
-
"packages/server/src/services/**/*.ts",
|
|
306
|
-
"packages/cli/src/commands/plan.ts"
|
|
307
|
-
]
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
If `scope=broad` had been chosen instead, `relevance_paths` would be `[]` regardless of the above.
|
|
122
|
+
`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.
|
|
311
123
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
-
|
|
317
|
-
|
|
318
|
-
-
|
|
319
|
-
|
|
320
|
-
### Phase 2 — Persist via MCP
|
|
321
|
-
|
|
322
|
-
For each user-confirmed candidate, call `fab_extract_knowledge` ONCE. Do NOT batch multiple candidates into one call.
|
|
323
|
-
|
|
324
|
-
#### Output Contract (MCP tool call shape)
|
|
325
|
-
|
|
326
|
-
```ts
|
|
327
|
-
mcp__fabric__fab_extract_knowledge({
|
|
328
|
-
source_sessions: ["<session id1>", "<session id2>", ...], // T5: array form (Phase 0.0)
|
|
329
|
-
recent_paths: ["<path1>", "<path2>", ...], // capped at 20
|
|
330
|
-
user_messages_summary: "<compact prose ≤500 chars>",
|
|
331
|
-
type: "decisions" | "pitfalls" | "guidelines" | "models" | "processes",
|
|
332
|
-
slug: "<kebab-case-2-to-5-words>",
|
|
333
|
-
layer: "team" | "personal",
|
|
334
|
-
scope: "narrow" | "broad", // from Phase 1.5
|
|
335
|
-
relevance_paths: ["<glob1>", "<literal2>", ...], // narrow ⇒ derived; broad ⇒ []
|
|
336
|
-
// v2.0.0-rc.7 T6: required fields for future-self reviewability.
|
|
337
|
-
proposed_reason:
|
|
338
|
-
"explicit-user-mark" // user said "always / never / 下次注意" etc.
|
|
339
|
-
| "diagnostic-then-fix" // long debug loop surfaced a new pattern/pitfall
|
|
340
|
-
| "decision-confirmation" // ≥2 options weighed AND rationale stated → decision/model
|
|
341
|
-
| "wrong-turn-revert" // tried path X, reverted → pitfall
|
|
342
|
-
| "new-dependency-or-pattern" // new dep/lib/abstraction introduced
|
|
343
|
-
| "dismissal-with-reason", // user rejected approach AND said why
|
|
344
|
-
session_context: "<3-5 line markdown: session goal + key turning point>",
|
|
345
|
-
// tags? — NOT in current schema; reserved for future
|
|
346
|
-
})
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
The Skill infers `proposed_reason` from the classification + viability-gate
|
|
350
|
-
signal that fired:
|
|
351
|
-
|
|
352
|
-
| Signal fired (Phase 0.5) | Classification | Default proposed_reason |
|
|
353
|
-
|--------------------------------|----------------|-----------------------------|
|
|
354
|
-
| Explicit normative language | guideline | `explicit-user-mark` |
|
|
355
|
-
| Wrong-turn-and-revert | pitfall | `wrong-turn-revert` |
|
|
356
|
-
| Long diagnostic loop | pitfall/model | `diagnostic-then-fix` |
|
|
357
|
-
| New dependency adoption | decision/model | `new-dependency-or-pattern` |
|
|
358
|
-
| New pattern emergence | model | `new-dependency-or-pattern` |
|
|
359
|
-
| Decision confirmation | decision | `decision-confirmation` |
|
|
360
|
-
| Explicit dismissal-with-reason | decision | `dismissal-with-reason` |
|
|
361
|
-
| Process formalization | process | `new-dependency-or-pattern` |
|
|
362
|
-
|
|
363
|
-
The `session_context` is a 3-5 line summary distilled from the Phase 0.0
|
|
364
|
-
cross-session digest (see Phase 0.0 below for digest source). Format:
|
|
365
|
-
|
|
366
|
-
```
|
|
367
|
-
Session goal: <one-line of what the user was trying to accomplish>
|
|
368
|
-
Turning point: <one-line of the key moment that produced the worth-archive observation>
|
|
369
|
-
[optional 1-3 more lines of supporting context]
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
Future-self reviewing the pending entry MUST be able to understand WHY this
|
|
373
|
-
entry was proposed without conversation transcript access — proposed_reason
|
|
374
|
-
is the structured why, session_context is the narrative why.
|
|
375
|
-
|
|
376
|
-
Note on type plurality: the MCP enum uses plural directory-form (decisions / pitfalls / guidelines / models / processes), while the conceptual classification above uses singular nouns (decision / pitfall / guideline / model / process) for natural English. They map 1:1.
|
|
377
|
-
|
|
378
|
-
The server returns `{ pending_path, idempotency_key }`. Display `pending_path` to the user so they can `Read` the persisted entry if they wish.
|
|
379
|
-
|
|
380
|
-
#### Idempotency Note
|
|
381
|
-
|
|
382
|
-
The MCP tool derives `idempotency_key = sha256({source_session, type, slug})`. Calling fab_extract_knowledge twice with the same `(source_session, type, slug)` triple is SAFE: the server appends new evidence to the existing pending file rather than overwriting or producing duplicates. This means the skill MAY be re-invoked on the same session without producing junk.
|
|
383
|
-
|
|
384
|
-
If the skill needs to record a genuinely separate observation in the same session+type, the slug MUST differ.
|
|
124
|
+
### Phase 4.5 — Persist Archive Attempt
|
|
125
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
**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.
|
|
129
|
+
|
|
130
|
+
`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.
|
|
385
131
|
|
|
386
132
|
## Hard Rules (DO NOT TRANSLATE) — DISPLAY / WRITE Split
|
|
387
133
|
|
|
388
134
|
### DISPLAY Rules
|
|
389
135
|
|
|
390
|
-
- MUST complete Phase
|
|
136
|
+
- MUST complete Phase 2 AND Phase 2.5 viability gate before any batch-review output.
|
|
391
137
|
- MUST abort with the gate-FAIL message (no MCP call) when the viability gate fails AND the user did not explicitly invoke fabric-archive.
|
|
392
|
-
- MUST present every candidate with explicit `[type=...]`, `[layer=...]`, `[
|
|
138
|
+
- MUST present every candidate with explicit `[type=...]`, `[layer=...]`, `[relevance_scope=...]`, and `slug=...` fields plus a `relevance_paths` line.
|
|
393
139
|
- MUST include a one-line `Layer reasoning:` for each candidate citing which 强 team / 强 personal signal applied (or default team).
|
|
394
140
|
- MUST include a one-line `Scope reasoning:` for each candidate citing why narrow or broad was chosen (or that personal forced broad).
|
|
395
141
|
- MUST classify against the canonical singular nouns: model / decision / guideline / pitfall / process. NEVER invent new types.
|
|
396
|
-
- MUST cap the batch at
|
|
142
|
+
- MUST cap the batch at `archive_max_candidates_per_batch` candidates (config-resolved, default 8); drop weaker ones over the cap.
|
|
397
143
|
- MUST display the resolved `pending_path` returned by `fab_extract_knowledge` so the user can verify.
|
|
398
|
-
- MUST treat user inline edits to type/layer/slug/
|
|
144
|
+
- MUST treat user inline edits to type/layer/slug/relevance_scope/relevance_paths as authoritative replacements before Phase 2.
|
|
399
145
|
- MUST skip rather than guess when an observation does not fit any of the 5 types.
|
|
400
146
|
|
|
401
147
|
### WRITE Rules
|
|
@@ -404,83 +150,15 @@ If the skill needs to record a genuinely separate observation in the same sessio
|
|
|
404
150
|
- NEVER write outside `.fabric/knowledge/pending/` — promotion to `.fabric/knowledge/<type>/` is rc.3 fab_review concern, NOT this skill.
|
|
405
151
|
- NEVER include an `id` field anywhere — pending entries have no id (late-bind on approve).
|
|
406
152
|
- NEVER classify a candidate as `personal` when a 强 team signal applies. Default to team on ambiguity.
|
|
407
|
-
- NEVER emit a non-empty `relevance_paths` when `
|
|
408
|
-
- NEVER emit a non-empty `relevance_paths` when `layer=personal` — personal forces `
|
|
409
|
-
-
|
|
153
|
+
- NEVER emit a non-empty `relevance_paths` when `relevance_scope=broad` — broad MUST always carry `relevance_paths=[]`.
|
|
154
|
+
- NEVER emit a non-empty `relevance_paths` when `layer=personal` — personal forces `relevance_scope=broad` + `relevance_paths=[]`.
|
|
155
|
+
- v2.0.0-rc.37 NEW-7 widened Phase 3.5: `edit_paths` ∪ `user_mentioned_paths` drives `relevance_paths`; `read_paths` flows separately to `evidence_paths` (structured frontmatter, not body markdown). NEVER lift body regex / symbol extraction into `relevance_paths` — those remain reserved for v2.1+.
|
|
410
156
|
- NEVER batch multiple candidates into a single fab_extract_knowledge call; one call per candidate.
|
|
411
157
|
- NEVER paraphrase the verbatim layer heuristic block above — the Chinese text is contract-locked.
|
|
412
|
-
- MUST preserve protected tokens exactly: `stable_id`, `knowledge_proposed`, `knowledge_archive_aborted`, `.fabric/knowledge/pending/`, `fab_extract_knowledge`, `relevance_paths`, `
|
|
413
|
-
|
|
414
|
-
## Worked Examples
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
Skill output:
|
|
421
|
-
|
|
422
|
-
```ts
|
|
423
|
-
mcp__fabric__fab_extract_knowledge({
|
|
424
|
-
source_sessions: ["WFS-2026-05-10-rc2"],
|
|
425
|
-
recent_paths: ["templates/claude-hooks/", "packages/cli/src/commands/hooks.ts"],
|
|
426
|
-
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.",
|
|
427
|
-
type: "decisions",
|
|
428
|
-
slug: "single-cjs-hook-script",
|
|
429
|
-
layer: "team",
|
|
430
|
-
scope: "narrow",
|
|
431
|
-
relevance_paths: [
|
|
432
|
-
"templates/claude-hooks/**/*.cjs",
|
|
433
|
-
"packages/cli/src/commands/hooks.ts"
|
|
434
|
-
],
|
|
435
|
-
proposed_reason: "decision-confirmation",
|
|
436
|
-
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."
|
|
437
|
-
})
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
Layer = team (引用本项目代码 + fabric-import 路径产物 signals). Scope = narrow (tied to hook templates + hooks command module; single-module evidence in edit_paths).
|
|
441
|
-
|
|
442
|
-
### Example 2 — pitfall (team)
|
|
443
|
-
|
|
444
|
-
Session: deepMerge silently replaced the existing `hooks.Stop[]` array in `.claude/settings.json` instead of appending. Cost ~30 min to diagnose.
|
|
445
|
-
|
|
446
|
-
Skill output:
|
|
447
|
-
|
|
448
|
-
```ts
|
|
449
|
-
mcp__fabric__fab_extract_knowledge({
|
|
450
|
-
source_sessions: ["WFS-2026-05-10-rc2"],
|
|
451
|
-
recent_paths: ["packages/cli/src/config/json.ts"],
|
|
452
|
-
user_messages_summary: "deepMerge default behavior REPLACES arrays. hooks.Stop[] needs an array-append-with-dedupe special case keyed on .command string match.",
|
|
453
|
-
type: "pitfalls",
|
|
454
|
-
slug: "deepmerge-array-replace-trap",
|
|
455
|
-
layer: "team",
|
|
456
|
-
scope: "broad",
|
|
457
|
-
relevance_paths: [],
|
|
458
|
-
proposed_reason: "diagnostic-then-fix",
|
|
459
|
-
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."
|
|
460
|
-
})
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
Layer = team (绑定本项目代码的 pitfall signal). Scope = broad (deepMerge gotcha is cross-cutting — applies anywhere JSON merge is used, not just `json.ts`).
|
|
464
|
-
|
|
465
|
-
### Example 3 — guideline (personal)
|
|
466
|
-
|
|
467
|
-
Session: User mentioned across three projects that they prefer 2-space indent in TypeScript and 4-space in Python.
|
|
468
|
-
|
|
469
|
-
Skill output:
|
|
470
|
-
|
|
471
|
-
```ts
|
|
472
|
-
mcp__fabric__fab_extract_knowledge({
|
|
473
|
-
source_sessions: ["WFS-2026-05-10-rc2"],
|
|
474
|
-
recent_paths: [".editorconfig"],
|
|
475
|
-
user_messages_summary: "Personal indent preference: 2-space TS / 4-space Py. Stable across multiple projects, not project-specific.",
|
|
476
|
-
type: "guidelines",
|
|
477
|
-
slug: "indent-style-by-language",
|
|
478
|
-
layer: "personal",
|
|
479
|
-
scope: "broad",
|
|
480
|
-
relevance_paths: [],
|
|
481
|
-
proposed_reason: "explicit-user-mark",
|
|
482
|
-
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."
|
|
483
|
-
})
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
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).
|
|
158
|
+
- 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`.
|
|
159
|
+
|
|
160
|
+
## Worked Examples / E5 Cron / Dry-run (ref-only)
|
|
161
|
+
|
|
162
|
+
- **Worked examples** (3 end-to-end fab_extract_knowledge calls: decision/team, pitfall/team, guideline/personal): `Read ref/worked-examples.md`
|
|
163
|
+
- **E5 Scheduled Daily Recap** (only when entry_point=E5_cron — OS cron, `/loop`, or scheduled trigger): `Read ref/e5-cron-recap.md`
|
|
164
|
+
- **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 — invocation MUST carry the verbatim token `--dry-run`; v2.0.0-rc.37 NEW-10 dropped the legacy substring fallback on bare `dry-run` / `dry_run` / `预览` to eliminate false positives on incidental mentions of those words mid-prompt) 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
|
+
| `fabric onboard-coverage` slot writes (Phase 1.5 fill-all / dismiss-all) | Each `Bash("fabric 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.
|