@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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +6 -5
  3. package/dist/chunk-BATF4PEJ.js +361 -0
  4. package/dist/{chunk-OBQU6NHO.js → chunk-COI5VDFU.js} +0 -18
  5. package/dist/chunk-F46ORPOA.js +903 -0
  6. package/dist/chunk-HFQVXY6P.js +86 -0
  7. package/dist/chunk-L4Q55UC4.js +52 -0
  8. package/dist/chunk-LFIKMVY7.js +27 -0
  9. package/dist/chunk-MF3OTILQ.js +544 -0
  10. package/dist/chunk-PWLW3B57.js +18 -0
  11. package/dist/chunk-RYAFBNES.js +33 -0
  12. package/dist/chunk-T5RPGCCM.js +40 -0
  13. package/dist/chunk-WU6GAPKH.js +36 -0
  14. package/dist/config-XJIPZNUP.js +13 -0
  15. package/dist/doctor-QVNPHLJK.js +920 -0
  16. package/dist/index.js +23 -8
  17. package/dist/{init-BIRSIOXO.js → install-2HDO5FTQ.js} +807 -705
  18. package/dist/metrics-ACEQFPDU.js +122 -0
  19. package/dist/onboard-coverage-MFCAEBDO.js +220 -0
  20. package/dist/{plan-context-hint-QMUPAXIB.js → plan-context-hint-FC6P3WFE.js} +34 -28
  21. package/dist/scope-explain-2F2R5URO.js +33 -0
  22. package/dist/status-GLQWLWH6.js +23 -0
  23. package/dist/store-XTSE5TY6.js +105 -0
  24. package/dist/sync-BJCWDPNC.js +245 -0
  25. package/dist/uninstall-TAXSUSKH.js +1073 -0
  26. package/dist/whoami-B6AEMSEV.js +31 -0
  27. package/package.json +30 -5
  28. package/templates/hooks/cite-policy-evict.cjs +231 -0
  29. package/templates/hooks/configs/README.md +29 -6
  30. package/templates/hooks/configs/claude-code.json +14 -3
  31. package/templates/hooks/configs/codex-hooks.json +6 -3
  32. package/templates/hooks/configs/cursor-hooks.json +8 -10
  33. package/templates/hooks/fabric-hint.cjs +873 -105
  34. package/templates/hooks/knowledge-hint-broad.cjs +549 -135
  35. package/templates/hooks/knowledge-hint-narrow.cjs +830 -26
  36. package/templates/hooks/lib/banner-i18n.cjs +309 -0
  37. package/templates/hooks/lib/bindings-snapshot-reader.cjs +81 -0
  38. package/templates/hooks/lib/cite-contract-reminder.cjs +179 -0
  39. package/templates/hooks/lib/cite-line-parser.cjs +180 -0
  40. package/templates/hooks/lib/client-adapter.cjs +106 -0
  41. package/templates/hooks/lib/config-cache.cjs +107 -0
  42. package/templates/hooks/lib/state-store.cjs +84 -0
  43. package/templates/hooks/lib/summary-fallback.cjs +210 -0
  44. package/templates/skills/fabric-archive/SKILL.md +97 -419
  45. package/templates/skills/fabric-archive/ref/dry-run-scope.md +16 -0
  46. package/templates/skills/fabric-archive/ref/e5-cron-recap.md +58 -0
  47. package/templates/skills/fabric-archive/ref/i18n-policy.md +86 -0
  48. package/templates/skills/fabric-archive/ref/phase-0-range-resolution.md +156 -0
  49. package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +218 -0
  50. package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +62 -0
  51. package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +68 -0
  52. package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +108 -0
  53. package/templates/skills/fabric-archive/ref/phase-3-classify.md +63 -0
  54. package/templates/skills/fabric-archive/ref/phase-4-5-emit.md +78 -0
  55. package/templates/skills/fabric-archive/ref/phase-4-mcp-persist.md +89 -0
  56. package/templates/skills/fabric-archive/ref/rc-history.md +38 -0
  57. package/templates/skills/fabric-archive/ref/worked-examples.md +78 -0
  58. package/templates/skills/fabric-import/SKILL.md +77 -514
  59. package/templates/skills/fabric-import/ref/checkpoint-state.md +85 -0
  60. package/templates/skills/fabric-import/ref/i18n-policy.md +79 -0
  61. package/templates/skills/fabric-import/ref/output-contract.md +61 -0
  62. package/templates/skills/fabric-import/ref/phase-2-mining.md +213 -0
  63. package/templates/skills/fabric-import/ref/phase-3-dedup.md +75 -0
  64. package/templates/skills/fabric-import/ref/state-recovery.md +57 -0
  65. package/templates/skills/fabric-import/ref/worked-examples.md +127 -0
  66. package/templates/skills/fabric-review/SKILL.md +90 -284
  67. package/templates/skills/fabric-review/ref/askuserquestion-policy.md +66 -0
  68. package/templates/skills/fabric-review/ref/i18n-policy.md +111 -0
  69. package/templates/skills/fabric-review/ref/modify-flow.md +103 -0
  70. package/templates/skills/fabric-review/ref/output-contract.md +58 -0
  71. package/templates/skills/fabric-review/ref/per-mode-flows.md +155 -0
  72. package/templates/skills/fabric-review/ref/semantic-check.md +26 -0
  73. package/templates/skills/fabric-review/ref/worked-examples.md +95 -0
  74. package/templates/skills/fabric-sync/SKILL.md +46 -0
  75. package/templates/skills/lib/shared-policy.md +69 -0
  76. package/dist/chunk-6ICJICVU.js +0 -10
  77. package/dist/chunk-74SZWYPH.js +0 -658
  78. package/dist/chunk-EYIDD2YS.js +0 -1000
  79. package/dist/doctor-T7JWODKG.js +0 -282
  80. package/dist/hooks-Y74Y5LQS.js +0 -12
  81. package/dist/scan-LMK3UCWL.js +0 -22
  82. package/dist/serve-H554BHLG.js +0 -124
  83. package/templates/agents-md/AGENTS.md.template +0 -59
  84. package/templates/bootstrap/CLAUDE.md +0 -8
  85. package/templates/bootstrap/codex-AGENTS-header.md +0 -6
  86. package/templates/bootstrap/cursor-fabric-bootstrap.mdc +0 -10
@@ -0,0 +1,68 @@
1
+ # Phase 2.5 — Viability Gate (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the signal lists, gate decision pseudocode, and entry-point branching summary. This file holds the verbose explanations for each signal, gate-FAIL message variants (zh-CN/en), the events.jsonl 4KB atomicity constraint note, and rationale.
4
+
5
+ ## Archive signals — verbose explanation (rc.37 NEW-4 simplified 8 → 3)
6
+
7
+ Scan `user_messages_summary` + `recent_paths` + the events tail collected in Phase 2. The legacy 8-signal list was simplified in v2.0.0-rc.37 NEW-4 to **3 major categories**, mirroring the AGENTS.md Self-archive policy rc.37 NEW-2 simplification. Legacy signal names remain valid scoring inputs for back-compat (any path that fires a legacy signal also fires the corresponding category):
8
+
9
+ ### Category 1 — User-driven knowledge expression
10
+
11
+ Covers legacy signals #1 (normative language) + #6 (decision confirmation) + #7 (dismissal-with-reason). All three are "user message contains structured knowledge worth keeping":
12
+
13
+ - **Normative language** — user said `always` / `never` / `from now on` / `下次注意` / `记一下` / `以后` / `永远不要`. Strongest single cue.
14
+ - **Decision confirmation** — ≥ 2 alternatives were weighed AND a rationale was given before settling. The rationale is the archivable knowledge.
15
+ - **Dismissal-with-reason** — user rejected an approach AND stated why. The why is archivable, not the dismissal itself.
16
+
17
+ ### Category 2 — Reflective discovery
18
+
19
+ Covers legacy signals #2 (wrong-turn-and-revert) + #3 (long diagnostic loop) + #5 (new pattern emergence). All three are "AI execution surfaced a non-obvious insight":
20
+
21
+ - **Wrong-turn-and-revert** — a path was edited, then reverted (or partially undone) after diagnosis. The why-not lives in the revert.
22
+ - **Long diagnostic loop** — an issue took > 15 minutes (or > ~10 tool turns) of debugging before resolution. Non-obvious cause worth capturing.
23
+ - **New pattern emergence** — a reusable abstraction or naming convention was named in-session ("the X phase", "the Y pattern", "let's call this Z").
24
+
25
+ ### Category 3 — Concrete artifact change
26
+
27
+ Covers legacy signals #4 (new dependency) + #8 (process formalization). Both surface in tangible workspace artifacts:
28
+
29
+ - **New dependency adoption** — a new package / library / external tool was introduced (`package.json` / `pyproject.toml` / `Cargo.toml` diff adds a dep).
30
+ - **Process formalization** — a multi-step procedure was executed in a specific order AND the order was identified as load-bearing.
31
+
32
+ ## Anti-archive signals — verbose explanation
33
+
34
+ These force the gate to FAIL **unless** an archive signal also fires (i.e. anti-signals are dominated by any archive signal per the gate decision rules):
35
+
36
+ 1. **Typo-only edits** — the entire session is whitespace / spelling / formatting changes. No semantic content to archive.
37
+ 2. **Pure refactor** — rename / move / extract with no behavior change AND no naming convention being established.
38
+ 3. **Narrow rename request** — user asked to rename one symbol / file with no rationale. Zero generalization potential.
39
+ 4. **Duplicate of existing canonical** — v2.0.0-rc.37 NEW-4: this check is now **mandatory** (was "do a quick Glob before deciding"). Pre-PASS MUST step: for each candidate, `Glob('.fabric/knowledge/**/*.md')` keyed on slug-stem. If duplicate found → drop candidate. Silently writing a near-duplicate is the highest-noise failure mode and dwarfs the cost of one extra glob per candidate.
40
+
41
+ ## Gate-FAIL user messages (E2 / E4 only)
42
+
43
+ For the user-active branch, the gate-FAIL message variants are:
44
+
45
+ ### zh-CN variant
46
+
47
+ ```
48
+ 本次会话为常规执行,无新知识可归档(gate=<reason>)。如需强制归档,请显式调用 fabric-archive。
49
+ ```
50
+
51
+ ### en variant
52
+
53
+ ```
54
+ Current session is routine execution; no new knowledge to archive (gate=<reason>). To force-archive, explicitly invoke fabric-archive.
55
+ ```
56
+
57
+ In BOTH branches: do NOT proceed to Phase 3, do NOT call any MCP tool. The legacy `knowledge_archive_aborted` event line (`{"ts":"...","kind":"knowledge_archive_aborted","reason":"<reason>","session":"<id>"}`) MAY be appended in addition to the mandatory Phase 4.5 `session_archive_attempted` event — they serve different audit purposes (legacy abort reason vs new outcome state machine) and the two coexist during the rc.25 transition window.
58
+
59
+ ## events.jsonl Constraint Note (POSIX 4KB atomicity)
60
+
61
+ Event lines appended to `.fabric/events.jsonl` are subject to POSIX single-write atomicity: only writes ≤ 4KB (`PIPE_BUF`) are guaranteed atomic via `Bash: echo "..." >> file`. Lines exceeding 4KB risk interleaved corruption under concurrent skill + server writes to the same ledger.
62
+
63
+ Skills MUST ensure:
64
+
65
+ - Each event JSON line is a **single line** (no embedded newlines; escape `\n` in any string value).
66
+ - `session_context` and other free-form text fields **self-truncate** to keep the entire serialized line under 4KB. Suggested per-field caps: `session_context` first 500 chars; `source_sessions` cap at 5 entries; `recent_paths` cap at 20 entries; `user_messages_summary` first 500 chars.
67
+ - If approaching the 4KB ceiling after the per-field caps, drop optional fields (e.g. tags / extra metadata) **before** truncating semantic content (the summary / context that carries the actual observation).
68
+ - This constraint applies to any event the skill itself appends (e.g. the abort signal above); MCP-server-side appends (via `appendEventLedgerEvent`) are already line-length-bounded server-side.
@@ -0,0 +1,108 @@
1
+ # Phase 3.5 — Scope Decision + relevance_paths Derivation (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the scope decision pseudocode, personal-layer-forces-broad rule, and brief examples. This file holds the rc.37 multi-signal derivation algorithm (edit_paths + read_paths + user_mentioned_paths), Steps 1-6 + worked generalization example + inline-edit re-derivation rules + the frontmatter `evidence_paths` upgrade.
4
+
5
+ ## relevance_paths derivation algorithm (rc.37 multi-signal — NEW-7)
6
+
7
+ rc.37 NEW-7 widens Step 1 from the rc.5 single-signal (`edit_paths` only) to three sources:
8
+
9
+ 1. **`edit_paths`** — files modified by `Edit` / `Write` / `MultiEdit` tool calls. The primary activation signal: if the agent CHANGED a file, the knowledge derived in this session most likely applies there.
10
+ 2. **`read_paths`** — files inspected via `Read` / `Grep` / `Glob` without modification. Secondary signal: read-only inspection often anchors the applicability surface even when no write happened (e.g. discovering that a pitfall surfaces in a getter that the agent only READ).
11
+ 3. **`user_mentioned_paths`** — paths the user typed verbatim in messages (`packages/server/src/foo.ts`, `\`packages/cli/**/*.ts\`` etc.). Strongest signal of all: an explicit user-named path is ground-truth applicability surface, independent of what the agent did.
12
+
13
+ ```
14
+ Step 1: COLLECT (rc.37 NEW-7 — three sources)
15
+ edit_paths = []
16
+ read_paths = []
17
+ user_mentioned_paths = []
18
+
19
+ // 1a — edit signal (rc.5 primary)
20
+ Scan session transcript for tool_use entries where
21
+ tool_use.name ∈ {Edit, Write, MultiEdit}
22
+ Extract the file_path argument from each, push into edit_paths.
23
+
24
+ // 1b — read signal (rc.37 NEW-7 secondary)
25
+ Scan session transcript for tool_use entries where
26
+ tool_use.name ∈ {Read, Grep, Glob}
27
+ Extract the file_path / path / glob argument from each, push into read_paths.
28
+
29
+ // 1c — user-mentioned signal (rc.37 NEW-7 ground truth)
30
+ Scan user messages for token sequences matching workspace-relative
31
+ path patterns: `<segment>/<segment>/...<ext>` or `<segment>/**` or
32
+ ``<path>`` (backtick-quoted). De-dupe and push into user_mentioned_paths.
33
+
34
+ Step 2: DEDUPE + CLASSIFY
35
+ // Union all three sources for the relevance_paths candidate set.
36
+ candidate_paths = unique(edit_paths ∪ user_mentioned_paths)
37
+ // read_paths stay separate — they become evidence_paths (Step 6) rather
38
+ // than activation triggers. A path that appears in BOTH edit_paths and
39
+ // read_paths goes to candidate_paths (writes dominate reads).
40
+ evidence_candidate_paths = unique(read_paths \ edit_paths)
41
+
42
+ Step 3: BLACKLIST FILTER (applies to BOTH candidate sets)
43
+ Drop paths matching any of:
44
+ - **/*.<ext> where <ext> is a single trivial extension on a single file
45
+ (i.e. avoid emitting bare **/*.md as a relevance pattern)
46
+ - Repo-root single files: README.md, package.json, package-lock.json,
47
+ pnpm-lock.yaml, tsconfig.json, .gitignore, LICENSE, CHANGELOG.md
48
+
49
+ Step 4: PUBLIC-PREFIX GENERALIZE (depth ≤ 2, minGroupSize = 2)
50
+ Group remaining candidate_paths by common prefix.
51
+ For each group of ≥ 2 sibling paths sharing a prefix:
52
+ - Compute longest common directory prefix
53
+ - Limit generalization depth: at most 2 levels below the common prefix
54
+ - Emit glob: <common-prefix>/**/*.<ext> (or <common-prefix>/**/<filename>)
55
+ Singleton paths (group size = 1) are kept as-is (literal path, no glob).
56
+ (Evidence paths are NOT generalized — they stay literal so plan-context
57
+ retrieval can do exact-match recall lookups.)
58
+
59
+ Step 5: SCOPE GATE
60
+ IF relevance_scope == broad → relevance_paths = [] (force empty regardless of candidate_paths)
61
+ IF relevance_scope == narrow → relevance_paths = result of Step 4
62
+
63
+ Step 6: ATTACH evidence_paths to FRONTMATTER (rc.37 NEW-7 upgrade)
64
+ Pass evidence_candidate_paths (from Step 2, post-blacklist Step 3) to
65
+ fab_extract_knowledge as the `evidence_paths` input field. Server writes
66
+ them to frontmatter `evidence_paths: [...]` (NOT to body `## Evidence`).
67
+ This makes evidence consumable by plan-context retrieval as structured
68
+ data instead of forcing markdown re-parsing every recall. The legacy
69
+ body `## Evidence` block stays for back-compat readers but is no longer
70
+ the source of truth.
71
+ ```
72
+
73
+ ## Worked generalization example
74
+
75
+ Edit history during session:
76
+
77
+ ```
78
+ packages/server/src/services/extract.ts
79
+ packages/server/src/services/review.ts
80
+ packages/server/src/services/promote.ts
81
+ packages/cli/src/commands/plan.ts
82
+ README.md
83
+ ```
84
+
85
+ Step 1-2 (collect + dedupe): all 5 unique.
86
+ Step 3 (blacklist): drop `README.md` (repo-root single file).
87
+ Step 4 (generalize, depth ≤ 2, minGroupSize = 2):
88
+ - `packages/server/src/services/{extract,review,promote}.ts` → group size 3 ≥ 2, common prefix `packages/server/src/services/`, glob: `packages/server/src/services/**/*.ts`
89
+ - `packages/cli/src/commands/plan.ts` → group size 1, kept literal.
90
+
91
+ Step 5 (assume `relevance_scope=narrow`):
92
+
93
+ ```json
94
+ "relevance_paths": [
95
+ "packages/server/src/services/**/*.ts",
96
+ "packages/cli/src/commands/plan.ts"
97
+ ]
98
+ ```
99
+
100
+ If `relevance_scope=broad` had been chosen instead, `relevance_paths` would be `[]` regardless of the above.
101
+
102
+ ## Inline-edit support during batch review
103
+
104
+ The user MAY inline-edit `[relevance_scope=...]` in the batch review. When this happens:
105
+
106
+ - Edit changes `narrow → broad`: clear `relevance_paths` to `[]`.
107
+ - Edit changes `broad → narrow`: re-run Steps 1-4 of the derivation algorithm to recompute.
108
+ - The user MAY also directly inline-edit `relevance_paths` to a custom array; treat this as authoritative and skip auto-derivation.
@@ -0,0 +1,63 @@
1
+ # Phase 3 — Classify, Layer, Slug, Review (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the contract (type/layer/slug/summary fields), the verbatim layer heuristic block (protected tokens — NEVER paraphrased), and brief slug rules + decision tree. This file holds verbose 5-type explanations, slug examples, and the bilingual batch review templates.
4
+
5
+ ## Five Knowledge Types (verbose)
6
+
7
+ - **model** — A reusable mental abstraction or domain object schema. Worth-archive signal: the user names something ("the X pattern", "the Y phase"). Skip-it signal: ad-hoc terminology used once. Positive: "Wave-1/Wave-2 task DAG decomposition for parallel-safe planning". Negative: "the thing we did just now" (too thin, no reusable abstraction).
8
+ - **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).
9
+ - **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).
10
+ - **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).
11
+ - **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).
12
+
13
+ ## Slug Naming — examples
14
+
15
+ Passing examples: `wave-1-parallel-task-dag` (4 words, 24 chars), `deepmerge-array-replace-trap` (4 words, 28 chars).
16
+
17
+ Failing examples: `the_solution` (underscore + article), `fix` (1 word, too short), `how-we-decided-to-handle-the-merge-conflict-in-stop-hook-config` (overlong).
18
+
19
+ ## Batch Review Template (bilingual)
20
+
21
+ Present all candidates in a single screen. UX i18n Policy classes 1 + 3 — the roll-up structure AND the per-candidate `Confirm?` prompt are bilingualized; protected tokens (`relevance_scope`, `relevance_paths`, `narrow`, `broad`, `layer`, `team`, `personal`, `pending_path`, etc.) appear verbatim in BOTH variants. Field VALUES (slugs, file paths, type/layer enum strings like `decision` / `team`) are data and are NOT translated.
22
+
23
+ ### en variant (`fabric_language === "en"`)
24
+
25
+ ```md
26
+ # Archive Review — N candidates
27
+
28
+ ## C1 [type=decision] [layer=team] [relevance_scope=narrow] slug=wave-1-parallel-task-dag
29
+ Summary: <1-2 sentences capturing the observation>
30
+ Layer reasoning: <which 强 team / 强 personal signal applied, or default team>
31
+ Scope reasoning: <why narrow or broad — see Phase 3.5>
32
+ relevance_paths: ["packages/cli/src/commands/plan.ts", "packages/cli/templates/**/*.md"]
33
+ Confirm? (Y to accept, edit type/layer/slug/relevance_scope/relevance_paths inline, N to skip)
34
+
35
+ ## C2 [type=pitfall] [layer=team] [relevance_scope=broad] slug=deepmerge-array-replace-trap
36
+ Summary: ...
37
+ Layer reasoning: ...
38
+ Scope reasoning: ...
39
+ relevance_paths: []
40
+ Confirm? ...
41
+ ```
42
+
43
+ ### zh-CN variant (`fabric_language === "zh-CN"`)
44
+
45
+ ```md
46
+ # 归档 Review — N 条候选
47
+
48
+ ## C1 [type=decision] [layer=team] [relevance_scope=narrow] slug=wave-1-parallel-task-dag
49
+ 摘要: <1-2 句捕捉该观察>
50
+ Layer 判定: <命中哪条 强 team / 强 personal 信号,或默认 team>
51
+ Scope 判定: <为什么 narrow 或 broad — 见 Phase 3.5>
52
+ relevance_paths: ["packages/cli/src/commands/plan.ts", "packages/cli/templates/**/*.md"]
53
+ 确认?(Y 接受 / 内联编辑 type/layer/slug/relevance_scope/relevance_paths / N 跳过)
54
+
55
+ ## C2 [type=pitfall] [layer=team] [relevance_scope=broad] slug=deepmerge-array-replace-trap
56
+ 摘要: ...
57
+ Layer 判定: ...
58
+ Scope 判定: ...
59
+ relevance_paths: []
60
+ 确认?...
61
+ ```
62
+
63
+ The user MAY edit type/layer/slug/relevance_scope/relevance_paths inline before confirming. The user MAY skip individual candidates without rejecting the whole batch. Inline-editing `[relevance_scope=...]` triggers a re-derivation of `relevance_paths` per the Phase 3.5 rules (narrow ⇒ recompute from edit_paths; broad ⇒ force `[]`).
@@ -0,0 +1,78 @@
1
+ # Phase 4.5 — Persist Archive Attempt (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the MANDATORY-on-every-invocation rule, the dry-run cross-reference, and the outcome enum. This file holds the full event emission jsonc schema, outcome decision matrix, covered_through_ts watermark spec, multi-session emission rule, append-pattern reference, and the E5-cron-silent-skip worked example.
4
+
5
+ ## What to emit
6
+
7
+ For EACH `session_id` in the run's scope (multi-session E4 runs emit MULTIPLE events — one per session_id; single-session E1/E2/E3/E5 runs emit ONE event), append ONE `session_archive_attempted` line to `.fabric/events.jsonl`:
8
+
9
+ ```jsonc
10
+ {
11
+ "kind": "fabric-event",
12
+ "id": "<uuid or ts-derived>",
13
+ "ts": <epoch ms>,
14
+ "schema_version": 1,
15
+ "session_id": "<the session this event pertains to>",
16
+ "event_type": "session_archive_attempted",
17
+ "outcome": "proposed" | "viability_failed" | "user_dismissed" | "skipped_no_signal",
18
+ "covered_through_ts": <max event ts scanned for this session>,
19
+ "candidates_proposed": <integer, default 0>,
20
+ "knowledge_proposed_ids": ["<idempotency_key_1>", "..."] // default []
21
+ }
22
+ ```
23
+
24
+ ## Outcome decision matrix
25
+
26
+ | Skill terminal state | outcome | candidates_proposed | knowledge_proposed_ids |
27
+ |----------------------------------------------------------------------|----------------------|---------------------|-------------------------------------------------|
28
+ | Phase 4 wrote ≥ 1 pending entry | `proposed` | N (count written) | `[idempotency_key_1, idempotency_key_2, ...]` (from each fab_extract_knowledge response) |
29
+ | Phase 2.5 viability_failed AND entry_point ∈ {E2_explicit, E4_user_range} AND user saw + accepted the gate-FAIL message | `viability_failed` | 0 | `[]` |
30
+ | Phase 3 batch review — user dismissed ALL presented candidates | `user_dismissed` | 0 | `[]` |
31
+ | Phase 1 filter dropped every session in scope OR Phase 2.5 silent-skip path (E1_hook / E3_ai_self_trigger / E5_cron) | `skipped_no_signal` | 0 | `[]` |
32
+
33
+ Rationale highlights:
34
+ - `user_dismissed` is the ONLY outcome that suppresses future auto-rescan (respects user decision per Q3.4).
35
+ - `proposed` populates `knowledge_proposed_ids` so the cross-session digest in Phase 1 can dedupe future runs against already-proposed entries.
36
+ - `viability_failed` vs `skipped_no_signal` distinguishes "user was prompted but the gate stopped us" from "we never bothered the user" — both allow rescan but the doctor history report differentiates them.
37
+
38
+ ## covered_through_ts watermark
39
+
40
+ ```
41
+ covered_through_ts = max(events_in_scope[*].ts)
42
+ ```
43
+
44
+ where `events_in_scope` is the set of events the skill actually examined for THAT session_id (Phase 2 + Phase 1 digest input). On rescan, Phase 1 compares the current `max(ts)` against this stored watermark — only sessions with new events past the watermark are eligible candidates.
45
+
46
+ ## Multi-session emission rule
47
+
48
+ When the run scope spans multiple session_ids (E4 user-range with `--since` / topic-keyword matching multiple sessions), emit ONE `session_archive_attempted` event PER session_id. Each event's `covered_through_ts` is computed against that session's own event subset. The `knowledge_proposed_ids` for a multi-session `proposed` run lists ALL idempotency_keys produced by the run; ledger consumers that want per-session breakdown should join against `source_sessions` on each pending entry.
49
+
50
+ ## Append pattern (Bash echo, 4KB-safe, fail-tolerant)
51
+
52
+ Reuse the Phase 2.5 `events.jsonl Constraint Note` pattern: single-line JSON ≤ 4KB, no embedded newlines. Best-effort write — if the append fails (disk full, permission denied, race), the skill MUST still exit successfully. Log the failure to stderr only; do NOT surface it to the user. Rationale: a missing `session_archive_attempted` event degrades gracefully — the next Phase 1 digest treats the session as "never archived" and re-evaluates it, which is the safe-default behavior.
53
+
54
+ ```bash
55
+ # Pseudo — actual implementation uses the same pattern as the legacy
56
+ # knowledge_archive_aborted emit at the end of Phase 2.5.
57
+ echo '{"kind":"fabric-event","id":"...","ts":..., "schema_version":1, "session_id":"...", "event_type":"session_archive_attempted","outcome":"...","covered_through_ts":...,"candidates_proposed":0,"knowledge_proposed_ids":[]}' >> .fabric/events.jsonl
58
+ ```
59
+
60
+ The per-field caps from Phase 2.5's constraint note carry over: `knowledge_proposed_ids` capped at 20 entries (drop tail with `...` marker in `id` field if truncated); other fields are bounded by schema.
61
+
62
+ ## Worked example: E5 cron silent-skip
63
+
64
+ Setup: An OS cron job runs `fabric-archive` at 03:00 daily for the "today" range (E5 entry_point). Today's session was routine config edits — no archive signals fire.
65
+
66
+ Trace:
67
+ 1. Phase 0 resolves `entry_point=E5_cron`, range = "today" → 1 session_id in scope.
68
+ 2. Phase 1 digest collects events for that session_id; nothing dropped.
69
+ 3. Phase 1.5 onboard is skipped (E5 is not E2).
70
+ 4. Phase 2.5 viability gate runs — `archive_signals_hit=0` → `gate=FAIL (reason=no_signal)`.
71
+ 5. `entry_point=E5_cron` ∈ {E1, E3, E5} → SILENT-SKIP branch. No message rendered.
72
+ 6. Phase 4.5 (mandatory) appends ONE event:
73
+ ```
74
+ {"kind":"fabric-event","id":"...","ts":<now>,"schema_version":1,"session_id":"<today-session-id>","event_type":"session_archive_attempted","outcome":"skipped_no_signal","covered_through_ts":<max ts of today's events>,"candidates_proposed":0,"knowledge_proposed_ids":[]}
75
+ ```
76
+ 7. Skill exits silently. Cron output is empty.
77
+
78
+ Next day's cron rescan: Phase 1 sees `covered_through_ts < max(ts of session's new events)` → session is rescan-eligible → loop continues without `user_dismissed` block.
@@ -0,0 +1,89 @@
1
+ # Phase 4 — Persist via MCP (ref)
2
+
3
+ > **Loaded on demand.** SKILL.md hot path retains the per-candidate one-call rule + brief mention of `proposed_reason` enum + session_context format. This file holds the full MCP call shape (with all rc.7/rc.23 optional fields), the C1 triage-field inference table, the proposed_reason mapping table, and the T5 array-form idempotency notes.
4
+
5
+ ## Full MCP tool call shape
6
+
7
+ ```ts
8
+ mcp__fabric__fab_extract_knowledge({
9
+ source_sessions: ["<session id1>", "<session id2>", ...], // T5: array form (Phase 1)
10
+ recent_paths: ["<path1>", "<path2>", ...], // capped at archive_max_recent_paths (config-resolved, default 20)
11
+ user_messages_summary: "<compact prose ≤500 chars>",
12
+ type: "decisions" | "pitfalls" | "guidelines" | "models" | "processes",
13
+ slug: "<kebab-case-2-to-5-words>",
14
+ layer: "team" | "personal",
15
+ relevance_scope: "narrow" | "broad", // from Phase 3.5
16
+ relevance_paths: ["<glob1>", "<literal2>", ...], // narrow ⇒ derived; broad ⇒ []
17
+ // v2.0.0-rc.7 T6: required fields for future-self reviewability.
18
+ proposed_reason:
19
+ "explicit-user-mark" // user said "always / never / 下次注意" etc.
20
+ | "diagnostic-then-fix" // long debug loop surfaced a new pattern/pitfall
21
+ | "decision-confirmation" // ≥2 options weighed AND rationale stated → decision/model
22
+ | "wrong-turn-revert" // tried path X, reverted → pitfall
23
+ | "new-dependency-or-pattern" // new dep/lib/abstraction introduced
24
+ | "dismissal-with-reason", // user rejected approach AND said why
25
+ session_context: "<3-5 line markdown: session goal + key turning point>",
26
+ // v2.0.0-rc.23 TASK-006 (a-C1): four OPTIONAL structured triage fields.
27
+ // Lift implicit signals out of `## Session context` prose so future-self
28
+ // reviewers / plan-context retrievers can triage relevance from
29
+ // frontmatter alone, without re-reading the body. Omit any field the
30
+ // skill cannot infer cleanly — guessing is worse than omitting.
31
+ intent_clues: ["<short trigger>", "<negative trigger e.g. 'NOT for X'>"], // when this rule applies / when NOT
32
+ tech_stack: ["<lang/framework>", "..."], // inferred from recent_paths (see table below)
33
+ impact: ["<consequence of ignoring>"], // why future-self should care
34
+ must_read_if: "<one-line strong trigger>" // single condition; if it holds, the entry is required reading
35
+ // tags? — NOT in current schema; reserved for future
36
+ })
37
+ ```
38
+
39
+ ## C1 triage-field inference table
40
+
41
+ | Field | Inference source | Skip when |
42
+ |----------------|----------------------------------------------------------------------------------|------------------------------------|
43
+ | `intent_clues` | Pull from `session_context` turning point + negative phrasing in the transcript ("not for", "don't do X when") | No clear trigger phrasing surfaced |
44
+ | `tech_stack` | Map `recent_paths` extensions: `.ts`→`typescript`, `.tsx`→`typescript`+`react`, `.go`→`go`, `package.json`→`nodejs`, `pyproject.toml`→`python`, `Cargo.toml`→`rust`. Add framework markers from path heuristics (`cocos`→`cocos-creator`, `next.config`→`nextjs`) | Rule is stack-agnostic |
45
+ | `impact` | Pull from the diagnostic-loop body — "wasted 30 min", "production outage", "silent data loss" | No observable consequence stated |
46
+ | `must_read_if` | Strongest single trigger from the worth-archive signal: a file path, a routine, a recurring condition; ≤160 chars | No single dominant trigger fits |
47
+
48
+ All four fields are STRICTLY OPTIONAL. The schema accepts the call without any of them — omit rather than guess. None of the four participate in the idempotency_key hash (server formula at `extract-knowledge.ts:100-106` is frozen to `{source_session, type, slug}`), so partial-vs-full fill of these fields on the same triple is safe.
49
+
50
+ ## proposed_reason → classification mapping
51
+
52
+ The skill infers `proposed_reason` from the classification + viability-gate signal that fired:
53
+
54
+ | Signal fired (Phase 2.5) | Classification | Default proposed_reason |
55
+ |--------------------------------|----------------|-----------------------------|
56
+ | Explicit normative language | guideline | `explicit-user-mark` |
57
+ | Wrong-turn-and-revert | pitfall | `wrong-turn-revert` |
58
+ | Long diagnostic loop | pitfall/model | `diagnostic-then-fix` |
59
+ | New dependency adoption | decision/model | `new-dependency-or-pattern` |
60
+ | New pattern emergence | model | `new-dependency-or-pattern` |
61
+ | Decision confirmation | decision | `decision-confirmation` |
62
+ | Explicit dismissal-with-reason | decision | `dismissal-with-reason` |
63
+ | Process formalization | process | `new-dependency-or-pattern` |
64
+
65
+ ## session_context format
66
+
67
+ ```
68
+ Session goal: <one-line of what the user was trying to accomplish>
69
+ Turning point: <one-line of the key moment that produced the worth-archive observation>
70
+ [optional 1-3 more lines of supporting context]
71
+ ```
72
+
73
+ Future-self reviewing the pending entry MUST be able to understand WHY this entry was proposed without conversation transcript access — `proposed_reason` is the structured why; `session_context` is the narrative why.
74
+
75
+ Note on type plurality: the MCP enum uses plural directory-form (decisions / pitfalls / guidelines / models / processes), while the conceptual classification uses singular nouns (decision / pitfall / guideline / model / process). They map 1:1.
76
+
77
+ The server returns `{ pending_path, idempotency_key }`. Display `pending_path` to the user so they can `Read` the persisted entry if they wish.
78
+
79
+ ## Idempotency Notes (T5 array-form, rc.7+)
80
+
81
+ 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. The skill MAY be re-invoked on the same session without producing junk.
82
+
83
+ If the skill needs to record a genuinely separate observation in the same session+type, the slug MUST differ.
84
+
85
+ **T5 array-form**: when `source_sessions` is passed as an array (rc.7 T5 contract), only `source_sessions[0]` participates in the server-side idempotency hash. Server formula at `packages/server/src/services/extract-knowledge.ts:78` is `sha256(JSON.stringify({source_session: sourceSessions[0], type, slug}))`. Implications:
86
+
87
+ - Same `(type, slug)` but a different **first** session → distinct idempotency key → produces two pending files.
88
+ - Same first session but different tail sessions → evidence-merge into the SAME pending file; tail `session_id`s are NOT recorded as independent evidence keys.
89
+ - The formula is intentionally stable across the rc.5 → rc.7 migration; adding or removing tail entries does NOT change the idempotency key, preserving rc.5 single-session compat.
@@ -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 1 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 1.5 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 1 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 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 4.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-1-5-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 1.5 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 3.5 special case).
78
+