@fenglimg/fabric-cli 2.0.0-rc.35 → 2.0.0-rc.37
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/dist/{chunk-XVS4F3P6.js → chunk-D25XJ4BC.js} +49 -5
- package/dist/{chunk-G2CIOLD4.js → chunk-WWNXR34K.js} +1 -16
- package/dist/{doctor-2FCRAWDZ.js → doctor-764NFF3X.js} +112 -16
- package/dist/index.js +7 -6
- package/dist/{install-HOTE5BPA.js → install-U7MGIJ2L.js} +50 -22
- package/dist/metrics-ACEQFPDU.js +122 -0
- package/dist/{uninstall-BIJ5GLEU.js → uninstall-MH7ZIB6M.js} +6 -18
- package/package.json +30 -4
- package/templates/hooks/cite-policy-evict.cjs +80 -91
- package/templates/hooks/configs/README.md +19 -0
- package/templates/hooks/configs/codex-hooks.json +3 -0
- package/templates/hooks/configs/cursor-hooks.json +2 -1
- package/templates/hooks/fabric-hint.cjs +146 -8
- package/templates/hooks/knowledge-hint-broad.cjs +65 -104
- package/templates/hooks/knowledge-hint-narrow.cjs +122 -5
- package/templates/hooks/lib/cite-line-parser.cjs +7 -1
- 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/skills/fabric-archive/SKILL.md +29 -7
- package/templates/skills/fabric-archive/ref/dry-run-scope.md +1 -1
- package/templates/skills/fabric-archive/ref/i18n-policy.md +6 -0
- package/templates/skills/fabric-archive/ref/phase-1-5-onboard.md +1 -1
- package/templates/skills/fabric-archive/ref/phase-1-cross-session.md +2 -0
- package/templates/skills/fabric-archive/ref/phase-2-5-viability.md +25 -11
- package/templates/skills/fabric-archive/ref/phase-3-5-scope.md +43 -15
- package/templates/skills/fabric-import/SKILL.md +75 -163
- package/templates/skills/fabric-import/ref/i18n-policy.md +6 -0
- package/templates/skills/fabric-import/ref/phase-2-mining.md +2 -2
- package/templates/skills/fabric-review/SKILL.md +31 -25
- package/templates/skills/fabric-review/ref/i18n-policy.md +6 -0
- package/templates/skills/fabric-review/ref/modify-flow.md +9 -1
- package/templates/skills/fabric-review/ref/per-mode-flows.md +1 -1
- package/templates/skills/lib/shared-policy.md +69 -0
- package/dist/serve-43JTEM3U.js +0 -142
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.0.0-rc.37 NEW-19: shared `.fabric/.cache/` sidecar I/O for hook scripts.
|
|
3
|
+
*
|
|
4
|
+
* Hooks persist tiny per-session state (turn counters, last-emit timestamps,
|
|
5
|
+
* shown-hint sets) under `.fabric/.cache/`. Each hook had its own copy of the
|
|
6
|
+
* read-JSON-or-null / write-JSON-best-effort / read-text / write-text helpers
|
|
7
|
+
* (cite-policy-evict's readEvictState/writeEvictState, broad's
|
|
8
|
+
* readBroadLastEmit/writeBroadLastEmit, fabric-hint's shown-cache + edit-counter
|
|
9
|
+
* + maintenance-last-emit). This module is the single canonical implementation.
|
|
10
|
+
*
|
|
11
|
+
* Provides (all keyed on a bare `fileName` resolved under .fabric/.cache/):
|
|
12
|
+
* - cachePath(projectRoot, fileName) → absolute path
|
|
13
|
+
* - readJsonState(root, fileName, validate?) → parsed | null
|
|
14
|
+
* null on missing / parse error / validate() === false. Never throws.
|
|
15
|
+
* - writeJsonState(root, fileName, value) → boolean
|
|
16
|
+
* mkdir -p + write; false on failure. Never throws.
|
|
17
|
+
* - readTextState(root, fileName) → trimmed string | null
|
|
18
|
+
* - writeTextState(root, fileName, text) → boolean
|
|
19
|
+
*
|
|
20
|
+
* Never-throw contract: write failures return false (counter loss is
|
|
21
|
+
* acceptable — the hook never blocks user flow on sidecar I/O, KT-DEC-0007).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const { existsSync, mkdirSync, readFileSync, writeFileSync } = require("node:fs");
|
|
25
|
+
const { dirname, join } = require("node:path");
|
|
26
|
+
|
|
27
|
+
const CACHE_DIR_REL = join(".fabric", ".cache");
|
|
28
|
+
|
|
29
|
+
function cachePath(projectRoot, fileName) {
|
|
30
|
+
return join(projectRoot, CACHE_DIR_REL, fileName);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readJsonState(projectRoot, fileName, validate) {
|
|
34
|
+
const path = cachePath(projectRoot, fileName);
|
|
35
|
+
if (!existsSync(path)) return null;
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
38
|
+
if (typeof validate === "function" && !validate(parsed)) return null;
|
|
39
|
+
return parsed;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function writeJsonState(projectRoot, fileName, value) {
|
|
46
|
+
const path = cachePath(projectRoot, fileName);
|
|
47
|
+
try {
|
|
48
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
49
|
+
writeFileSync(path, JSON.stringify(value));
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readTextState(projectRoot, fileName) {
|
|
57
|
+
const path = cachePath(projectRoot, fileName);
|
|
58
|
+
if (!existsSync(path)) return null;
|
|
59
|
+
try {
|
|
60
|
+
return readFileSync(path, "utf8").trim();
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function writeTextState(projectRoot, fileName, text) {
|
|
67
|
+
const path = cachePath(projectRoot, fileName);
|
|
68
|
+
try {
|
|
69
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
70
|
+
writeFileSync(path, String(text));
|
|
71
|
+
return true;
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
cachePath,
|
|
79
|
+
readJsonState,
|
|
80
|
+
writeJsonState,
|
|
81
|
+
readTextState,
|
|
82
|
+
writeTextState,
|
|
83
|
+
CACHE_DIR_REL,
|
|
84
|
+
};
|
|
@@ -21,11 +21,17 @@ If none hold, stop the skill and tell the user (UX i18n Policy class 2):
|
|
|
21
21
|
|
|
22
22
|
Render per `fabric_language` resolved in Phase 0.5.
|
|
23
23
|
|
|
24
|
-
This skill
|
|
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.
|
|
25
25
|
|
|
26
26
|
## 执行流程 (1 User Review Round)
|
|
27
27
|
|
|
28
|
-
|
|
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):
|
|
29
|
+
|
|
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).
|
|
33
|
+
|
|
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/`.
|
|
29
35
|
|
|
30
36
|
### Phase 0 — Range Resolution
|
|
31
37
|
|
|
@@ -43,11 +49,19 @@ Read `fabric_language` (`zh-CN` / `en` / `zh-CN-hybrid` / `match-existing`); emi
|
|
|
43
49
|
|
|
44
50
|
`Read ref/i18n-policy.md` for the full 5-class taxonomy + edge cases.
|
|
45
51
|
|
|
46
|
-
### Phase 1 — Collect Cross-Session Digests
|
|
52
|
+
### Phase 1 — Collect Cross-Session Digests (server-side ledger scan, rc.37 NEW-9)
|
|
53
|
+
|
|
54
|
+
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:
|
|
55
|
+
|
|
56
|
+
- `anchor_ts` — ts of the last `knowledge_proposed` (the lower bound).
|
|
57
|
+
- `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.
|
|
58
|
+
- `dropped[]` — `{session_id, reason}` for transparency.
|
|
59
|
+
- `covered_through_ts` — max ts examined (becomes the next watermark).
|
|
60
|
+
- `already_proposed_keys[]` — idempotency keys already proposed but not yet reviewed; drop matching candidates in Phase 3 (cross-session pending dedupe).
|
|
47
61
|
|
|
48
|
-
|
|
62
|
+
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.
|
|
49
63
|
|
|
50
|
-
`Read ref/phase-1-cross-session.md` for the
|
|
64
|
+
`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.
|
|
51
65
|
|
|
52
66
|
Graceful degradation: missing digest cache → single-session fallback. Missing `session_archive_attempted` events (pre-rc.25) → legacy "scan everything since anchor" behaviour.
|
|
53
67
|
|
|
@@ -63,7 +77,15 @@ Gather raw evidence: tail `.fabric/events.jsonl` since last `knowledge_proposed`
|
|
|
63
77
|
|
|
64
78
|
### Phase 2.5 — Viability Gate (Anti-Archive Guard)
|
|
65
79
|
|
|
66
|
-
Coarse viability check. **PASS conditions**: user_explicit_invoke OR ≥1 archive signal hit
|
|
80
|
+
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):
|
|
81
|
+
|
|
82
|
+
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.
|
|
83
|
+
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.
|
|
84
|
+
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.
|
|
85
|
+
|
|
86
|
+
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.
|
|
87
|
+
|
|
88
|
+
**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`).
|
|
67
89
|
|
|
68
90
|
`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.
|
|
69
91
|
|
|
@@ -126,7 +148,7 @@ MANDATORY closing step on EVERY invocation (Phase 4 success path + every early-e
|
|
|
126
148
|
- NEVER classify a candidate as `personal` when a 强 team signal applies. Default to team on ambiguity.
|
|
127
149
|
- NEVER emit a non-empty `relevance_paths` when `relevance_scope=broad` — broad MUST always carry `relevance_paths=[]`.
|
|
128
150
|
- NEVER emit a non-empty `relevance_paths` when `layer=personal` — personal forces `relevance_scope=broad` + `relevance_paths=[]`.
|
|
129
|
-
-
|
|
151
|
+
- 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+.
|
|
130
152
|
- NEVER batch multiple candidates into a single fab_extract_knowledge call; one call per candidate.
|
|
131
153
|
- NEVER paraphrase the verbatim layer heuristic block above — the Chinese text is contract-locked.
|
|
132
154
|
- 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`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Dry-run Scope (unified)
|
|
2
2
|
|
|
3
|
-
`dry_run = true` (per Phase 4.5 detection rule —
|
|
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
4
|
|
|
5
5
|
| Write operation | Normal mode | Dry-run mode |
|
|
6
6
|
|---|---|---|
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# UX i18n Policy — full reference
|
|
2
2
|
|
|
3
|
+
> **Shared core (rc.37 NEW-13):** the cross-skill invariants — protected-token
|
|
4
|
+
> NEVER-translate list, AskUserQuestion routing-key rule, layer heuristic, and
|
|
5
|
+
> events-emit convention — live once in `../../lib/shared-policy.md`. This file
|
|
6
|
+
> keeps only the fabric-archive-specific 5-class examples. Read the shared lib
|
|
7
|
+
> for the common rules; do not fork them here.
|
|
8
|
+
|
|
3
9
|
> **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
10
|
|
|
5
11
|
## UX i18n Policy (5-class bilingualization)
|
|
@@ -23,7 +23,7 @@ is the canonical taxonomy for this gate.
|
|
|
23
23
|
|-------|--------|-------------------------------------------------------|
|
|
24
24
|
| **E1** | `hook_passive` | stdout JSON `{decision:'block', ...}` from `archive-hint.cjs` detected at skill entry (the Stop-hook reminder path). |
|
|
25
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
|
|
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` per AGENTS.md self-archive policy section; `<X>` is the signal name. v2.0.0-rc.37 NEW-2 simplified the AGENTS.md taxonomy to 2 categories: `User-driven normative` / `Wrong-turn-and-revert`. Back-compat: legacy 4-state names (`Normative` / `Decision confirmation` / `Explicit dismissal`) still route correctly because the substring gate only matches the verbatim prefix and treats any text after `signal:` as the signal label.) |
|
|
27
27
|
| **E4** | `user_range_rollback` | Prompt contains a **range hint** (parsed in Phase 0 — e.g. `今日` / `上周` / `rc.20`) AND the user is invoking. Sub-mode of E2. |
|
|
28
28
|
| **E5** | `cron` | Prompt contains literal `今日复盘` / `daily recap` / `daily-archive` AND no human is present (running under `/loop`, OS cron, or scheduled trigger). |
|
|
29
29
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> **Loaded on demand.** SKILL.md hot path retains Phase 1's purpose statement + 5-step summary + graceful-degradation note. This file holds Steps 1-5 detailed implementation (events.jsonl tail-scan, anchor-walk, digest load, rc.25 TASK-05 ledger filter algorithm + constants + worked examples, cross-session context build).
|
|
4
4
|
|
|
5
|
+
> **v2.0.0-rc.37 NEW-9 — Steps 1-4.5 moved server-side.** The deterministic part of this algorithm (events.jsonl tail-scan, anchor-find, session forward-collect, and the Step 4.5 outcome-ledger filter state machine) now runs in the server and is exposed as the `fab_archive_scan` MCP tool — call it instead of hand-running `tail`/grep. The tool returns the already-filtered `session_ids[]` + `anchor_ts` + `covered_through_ts` + `already_proposed_keys[]`. Steps 1-4.5 below remain as the AUTHORITATIVE SPEC of what the server computes (and the contract tests pin it); the Skill no longer executes them by hand. Step 5 (digest load + cross-session context stitch) stays LLM-side per Boundary B.
|
|
6
|
+
|
|
5
7
|
## Step 1 — Read events.jsonl tail
|
|
6
8
|
|
|
7
9
|
Use `Bash` with `tail -n 200 .fabric/events.jsonl` (tolerate ENOENT — empty ledger is a normal first-run state).
|
|
@@ -2,18 +2,32 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
## Archive signals — verbose explanation
|
|
5
|
+
## Archive signals — verbose explanation (rc.37 NEW-4 simplified 8 → 3)
|
|
6
6
|
|
|
7
|
-
Scan `user_messages_summary` + `recent_paths` + the events tail collected in Phase 2.
|
|
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
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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.
|
|
17
31
|
|
|
18
32
|
## Anti-archive signals — verbose explanation
|
|
19
33
|
|
|
@@ -22,7 +36,7 @@ These force the gate to FAIL **unless** an archive signal also fires (i.e. anti-
|
|
|
22
36
|
1. **Typo-only edits** — the entire session is whitespace / spelling / formatting changes. No semantic content to archive.
|
|
23
37
|
2. **Pure refactor** — rename / move / extract with no behavior change AND no naming convention being established.
|
|
24
38
|
3. **Narrow rename request** — user asked to rename one symbol / file with no rationale. Zero generalization potential.
|
|
25
|
-
4. **Duplicate of existing canonical** —
|
|
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.
|
|
26
40
|
|
|
27
41
|
## Gate-FAIL user messages (E2 / E4 only)
|
|
28
42
|
|
|
@@ -1,45 +1,73 @@
|
|
|
1
1
|
# Phase 3.5 — Scope Decision + relevance_paths Derivation (ref)
|
|
2
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.
|
|
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
4
|
|
|
5
|
-
## relevance_paths derivation algorithm (rc.
|
|
5
|
+
## relevance_paths derivation algorithm (rc.37 multi-signal — NEW-7)
|
|
6
6
|
|
|
7
|
-
rc.
|
|
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.
|
|
8
12
|
|
|
9
13
|
```
|
|
10
|
-
Step 1: COLLECT
|
|
14
|
+
Step 1: COLLECT (rc.37 NEW-7 — three sources)
|
|
11
15
|
edit_paths = []
|
|
16
|
+
read_paths = []
|
|
17
|
+
user_mentioned_paths = []
|
|
18
|
+
|
|
19
|
+
// 1a — edit signal (rc.5 primary)
|
|
12
20
|
Scan session transcript for tool_use entries where
|
|
13
21
|
tool_use.name ∈ {Edit, Write, MultiEdit}
|
|
14
22
|
Extract the file_path argument from each, push into edit_paths.
|
|
15
23
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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)
|
|
20
43
|
Drop paths matching any of:
|
|
21
44
|
- **/*.<ext> where <ext> is a single trivial extension on a single file
|
|
22
45
|
(i.e. avoid emitting bare **/*.md as a relevance pattern)
|
|
23
46
|
- Repo-root single files: README.md, package.json, package-lock.json,
|
|
24
47
|
pnpm-lock.yaml, tsconfig.json, .gitignore, LICENSE, CHANGELOG.md
|
|
25
|
-
- Read-only paths (never modified) — those go to ## Evidence, not relevance_paths
|
|
26
48
|
|
|
27
49
|
Step 4: PUBLIC-PREFIX GENERALIZE (depth ≤ 2, minGroupSize = 2)
|
|
28
|
-
Group remaining
|
|
50
|
+
Group remaining candidate_paths by common prefix.
|
|
29
51
|
For each group of ≥ 2 sibling paths sharing a prefix:
|
|
30
52
|
- Compute longest common directory prefix
|
|
31
53
|
- Limit generalization depth: at most 2 levels below the common prefix
|
|
32
54
|
- Emit glob: <common-prefix>/**/*.<ext> (or <common-prefix>/**/<filename>)
|
|
33
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.)
|
|
34
58
|
|
|
35
59
|
Step 5: SCOPE GATE
|
|
36
|
-
IF relevance_scope == broad → relevance_paths = [] (force empty regardless of
|
|
60
|
+
IF relevance_scope == broad → relevance_paths = [] (force empty regardless of candidate_paths)
|
|
37
61
|
IF relevance_scope == narrow → relevance_paths = result of Step 4
|
|
38
62
|
|
|
39
|
-
Step 6: ATTACH
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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.
|
|
43
71
|
```
|
|
44
72
|
|
|
45
73
|
## Worked generalization example
|