@fenglimg/fabric-cli 2.0.0-rc.36 → 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-XSUIX6AD.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 +3 -3
- 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
|
|
@@ -65,7 +65,7 @@ Classify each candidate into 5 types (decisions/pitfalls/guidelines/models/proce
|
|
|
65
65
|
|
|
66
66
|
#### Mandatory Scope Rule — broad + empty paths (NON-NEGOTIABLE)
|
|
67
67
|
|
|
68
|
-
Every call MUST `relevance_scope="broad"` AND `relevance_paths=[]`. No exceptions. Why: import is LLM-driven (not session-driven); LLM-inferred narrow
|
|
68
|
+
Every call MUST `relevance_scope="broad"` AND `relevance_paths=[]`. No exceptions. Why: import is LLM-driven (not session-driven); LLM-inferred narrow lies about applicability. Post-rc.37 A1 the server returns every selectable entry regardless of scope, so false-narrow no longer hides knowledge — but it still poisons doctor lint accounting + downstream consumers that read `relevance_paths` literally. Narrowing deferred to `fab_review.modify` post-import when the user has the real applicability surface. Full rationale + prohibitions + doctor lint #23 → `Read .../ref/phase-2-mining.md`.
|
|
69
69
|
|
|
70
70
|
#### Step 2.1 — Git Mining
|
|
71
71
|
|
|
@@ -85,7 +85,7 @@ Skip if: cosmetic-only / metadata-only / in baseline / not classifiable / slug n
|
|
|
85
85
|
|
|
86
86
|
#### Dry-Run
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
Explicit token `--dry-run` in invocation → skip MCP, render bilingual preview table (every Scope row `broad+[]`). State NOT written. P3 skipped. v2.0.0-rc.37 NEW-10 dropped legacy substring fallback on bare `dry-run` / `预览` (false-positive on incidental mentions).
|
|
89
89
|
|
|
90
90
|
Full MCP call shape, Step 2.1.5 table, dry-run templates, T5 idempotency → `Read .../ref/phase-2-mining.md`.
|
|
91
91
|
|
|
@@ -144,4 +144,4 @@ Roll-up sections (per `fabric_language`): `Phase 2 — Mining` | `Phase 3 — De
|
|
|
144
144
|
- State corruption: P0 detects → rename `.json.corrupt-<ISO>` → restart P1.
|
|
145
145
|
- MCP unreachable: halt + `MCP 工具未注册;请检查 fabric server 是否运行` / `MCP tool not registered; please check that the fabric server is running` → exit without state write.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
Resume policy: inspect existing state and continue from the last completed phase — do not prompt the user mid-flow.
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# UX i18n Policy — fabric-import 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-import-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 you need to disambiguate which of the 5 classes a given string belongs to. SKILL.md gives the operative rule.
|
|
4
10
|
|
|
5
11
|
## UX i18n Policy (5-class bilingualization)
|
|
@@ -15,7 +15,7 @@ This is non-negotiable and applies to BOTH Step 2.1 (git mining) AND Step 2.2 (d
|
|
|
15
15
|
|
|
16
16
|
1. `fabric-import` is LLM-driven (mines git log + docs), not session-driven (no live `edit_paths` signal).
|
|
17
17
|
2. `git diff --stat` lists files touched by a commit, but those files are the commit's **effect surface**, not the **applicability surface** of the underlying observation. A pitfall surfaced by a fix in `packages/server/src/retry.ts` may apply to every retry call-site in the repo, not just that one file.
|
|
18
|
-
3. LLM-inferred `relevance_paths` from historical commit metadata produces false-narrow bindings —
|
|
18
|
+
3. LLM-inferred `relevance_paths` from historical commit metadata produces false-narrow bindings — `relevance_paths` becomes a lie about applicability. Post-rc.37 A1 the server no longer filters by `relevance_scope`, so false-narrow does NOT hide knowledge from AI recall (every selectable entry is surfaced regardless of scope). The damage is now downstream: doctor lint accounting, future-AI judgment, and any consumer that reads `relevance_paths` literally treats the wrong globs as ground truth. Broad+[] keeps the metadata honest until the user has the real applicability surface in hand to declare narrow.
|
|
19
19
|
4. Doc-mined observations are usually architectural / cross-cutting (a `docs/architecture.md` "Why a monolith?" decision applies to the whole codebase, not just to `docs/`).
|
|
20
20
|
|
|
21
21
|
**Strict prohibitions — DO NOT attempt any of the following:**
|
|
@@ -174,7 +174,7 @@ After Step 2.2 completes (or hits the cap), update `.fabric/.import-state.json`:
|
|
|
174
174
|
|
|
175
175
|
## Dry-Run Mode
|
|
176
176
|
|
|
177
|
-
When the user invocation
|
|
177
|
+
When the user invocation carries the verbatim token `--dry-run`, Phase 2 runs WITHOUT calling `fab_extract_knowledge`. Instead it prints a table. v2.0.0-rc.37 NEW-10 dropped the legacy substring fallback on bare `dry-run` / `预览` because those caused false positives on incidental mentions ("preview the table" / "do a dry run later"). UX i18n Policy class 4 — header + column titles bilingualized; row content (slug / commit sha / doc path) NOT translated. Protected tokens `broad`, `relevance_scope`, `relevance_paths` appear verbatim:
|
|
178
178
|
|
|
179
179
|
### zh-CN variant (`fabric_language === "zh-CN"`)
|
|
180
180
|
|
|
@@ -48,46 +48,46 @@ Read `fabric_language` (`zh-CN` / `en` / `zh-CN-hybrid` / `match-existing`); emi
|
|
|
48
48
|
|
|
49
49
|
## Mode Inference (System Infers — NEVER Ask)
|
|
50
50
|
|
|
51
|
-
>
|
|
52
|
-
> "review 永远走 fabric-review skill,**模式从上下文推断**(4 种 mode:pending queue / by topic / health overview / revisit existing)"
|
|
51
|
+
> Locked decision (KT-DEC-0006): "Review mode inferred from context, not solicited via AskUserQuestion."
|
|
53
52
|
> "**AskUserQuestion 仅在真有选择时用**——'何种 mode' 不是真选择(系统能推断),'approve/reject/modify 单条' 是真选择"
|
|
54
53
|
|
|
55
|
-
The skill MUST infer one of
|
|
54
|
+
The skill MUST infer one of **2 modes** BEFORE any user-facing output (v2.0.0-rc.37 NEW-12 simplified 4 → 2):
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
- **`pending`** — triage the write-side backlog (`.fabric/knowledge/pending/`): approve / reject / modify / defer per item. The dominant entry point.
|
|
57
|
+
- **`maintain`** — sustain the EXISTING canonical KB: browse by topic (search), survey staleness/health, or revisit a specific entry. Merges the legacy `topic` + `health` + `revisit` modes — they are all "operate on already-canonical knowledge", distinct from triaging new drafts.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
### 2-Step Inference Algorithm
|
|
60
|
+
|
|
61
|
+
**Step 1 — Recent user message keyword scan:**
|
|
60
62
|
|
|
61
63
|
| Keywords (zh-CN + en) | Inferred mode |
|
|
62
64
|
|---|---|
|
|
63
65
|
| "approve", "review pending", "promote", "what's queued", "审核 pending", "通过" | `pending` |
|
|
64
|
-
| "search
|
|
65
|
-
| "what's stale", "demote old", "health check", "过期的", "陈旧的", "整理一下" | `health` |
|
|
66
|
-
| "look at <id>", "revisit KT-…", "show <slug>", "再看下 <id>", "回顾" | `revisit` |
|
|
67
|
-
|
|
68
|
-
Exactly one row matches → lock mode, skip to Step 3.
|
|
69
|
-
|
|
70
|
-
**Step 2 — events.jsonl tail scan.** If Step 1 yielded 0 or >1 matches, tail (last 200 lines) `.fabric/events.jsonl`:
|
|
66
|
+
| "search/find about <topic>", "what's stale", "demote old", "health check", "look at <id>", "revisit KT-…", "关于…的知识", "过期的", "陈旧的", "整理一下", "再看下 <id>", "回顾" | `maintain` |
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
- `≥1` `knowledge_demoted` or `lint` events in 24h → `health` (corpus quality signal).
|
|
74
|
-
- Recent `knowledge_layer_changed` for the entry user referenced → `revisit`.
|
|
68
|
+
A `maintain`-row match → lock `maintain`. A `pending`-row match (or 0/ambiguous) → fall to Step 2.
|
|
75
69
|
|
|
76
|
-
**Step
|
|
70
|
+
**Step 2 — Backlog default.** Glob `.fabric/knowledge/pending/**/*.md`:
|
|
77
71
|
|
|
78
72
|
- Count ≥ `review_hint_pending_count` (default 10) OR oldest mtime > `review_hint_pending_age_days` (default 7) → `pending` (overflow, same threshold as Stop-hook).
|
|
79
73
|
- Otherwise → default `pending` (most common review entry point).
|
|
80
74
|
|
|
81
|
-
|
|
75
|
+
> Back-compat: the legacy 4-mode names (`topic` / `health` / `revisit`) still resolve — they all map to `maintain`. Old session traces / muscle memory keep working.
|
|
76
|
+
|
|
77
|
+
`Read ref/per-mode-flows.md` for inference examples and anti-pattern restatement.
|
|
82
78
|
|
|
83
79
|
## Per-Mode Flow
|
|
84
80
|
|
|
85
81
|
Each mode produces user-facing output, then routes per-item or per-batch decisions through `fab_review` actions. Display body = zh-CN summaries (M3 style); section headings = EN.
|
|
86
82
|
|
|
87
|
-
- **`pending`** — list pending entries → run Semantic Check (see `ref/semantic-check.md`) → per-item AskUserQuestion `{approve, reject, modify, defer, skip}` → route per choice
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
83
|
+
- **`pending`** — list pending entries → run Semantic Check (see `ref/semantic-check.md`) → per-item AskUserQuestion `{approve, reject, modify, defer, skip}` → route per choice. The modify branch chooses between two explicit actions (rc.37 NEW-12):
|
|
84
|
+
- `fab_review action="modify-content"` — edit scalars (title/summary/maturity/tags/relevance_*); NEVER flips layer.
|
|
85
|
+
- `fab_review action="modify-layer"` — the dedicated layer-flip path (`changes.layer` required); may reallocate the stable_id + emit an id-redirect.
|
|
86
|
+
- (Legacy `action="modify"` still works — it routes by whether `changes.layer` is present.) See `ref/modify-flow.md`.
|
|
87
|
+
- **`maintain`** — sub-flow inferred from the same keywords:
|
|
88
|
+
- *browse-by-topic*: extract keywords → `fab_review action="search"` → render top-N (cap `review_topic_result_cap`) → AskUserQuestion only on an action verb.
|
|
89
|
+
- *health/staleness*: `fab_review action="list"` + tail events → compute stale → render dashboard → per-stale AskUserQuestion `{defer, demote, skip}`.
|
|
90
|
+
- *revisit*: user referenced a specific id/slug → `Read` canonical file directly OR `fab_review action="list"` with narrow filters → display body + history → AskUserQuestion only if actionable.
|
|
91
91
|
|
|
92
92
|
`Read ref/per-mode-flows.md` for full step-by-step procedures, bilingual rendering blocks (en + zh-CN per-item display, AskUserQuestion templates, health dashboard format), and the rc.7 T6 `proposed_reason` + `## Why proposed` + `## Session context` rendering contract.
|
|
93
93
|
|
|
@@ -97,11 +97,17 @@ Each mode produces user-facing output, then routes per-item or per-batch decisio
|
|
|
97
97
|
|
|
98
98
|
Semantic check is the LLM's job — the MCP tool does NOT compare meaning. Run during `pending` mode (and on demand during `topic`): for each pending entry, `fab_review action="search"` scoped by `filters.type` → LLM judges semantically against returned canonical entries → surface one of three flags as informational:
|
|
99
99
|
|
|
100
|
-
- `⚠ Possible duplicate of <stable_id
|
|
101
|
-
- `⚠ Contradicts <stable_id
|
|
102
|
-
- `⚠ Subsumed by <stable_id
|
|
100
|
+
- `⚠ Possible duplicate of <stable_id> (overlap: high)` — same essential claim
|
|
101
|
+
- `⚠ Contradicts <stable_id> (overlap: high)` — opposing claims, same scope
|
|
102
|
+
- `⚠ Subsumed by <stable_id> (overlap: medium+); consider modify-to-merge` — fully covered
|
|
103
|
+
|
|
104
|
+
**Quantified overlap band (rc.37 NEW-12).** The LLM still judges meaning (no embedding %), but MUST tag each flag with a 3-level band so the signal is comparable across entries and the user can triage at a glance:
|
|
105
|
+
|
|
106
|
+
- `high` — ≥ ~80% of the candidate's essential claim is restated; near-certain dup/contradiction. Recommend reject-as-duplicate or modify-to-merge.
|
|
107
|
+
- `medium` — substantial conceptual overlap but the new entry adds a distinct facet (different path scope, added caveat). Recommend modify-to-harmonize.
|
|
108
|
+
- `low` — adjacent topic, not a real overlap. Do NOT raise a flag at `low` — it is below the surfacing threshold (suppresses noise).
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
Only `medium`+ flags are surfaced. User decides: still-approve (flag informational), modify-to-harmonize, or reject-as-duplicate (reason MUST cite existing stable_id).
|
|
105
111
|
|
|
106
112
|
DO NOT AskUserQuestion "is this a duplicate?" — LLM already judged. User only chooses approve/reject/modify.
|
|
107
113
|
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# UX i18n Policy — fabric-review 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-review-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 you need to disambiguate which of the 5 classes a given string belongs to. SKILL.md gives the operative rule.
|
|
4
10
|
|
|
5
11
|
## UX i18n Policy (5-class bilingualization)
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Modify Sub-Flow
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The modify family is the only action group that mutates frontmatter or stable_id. It accepts `changes` of shape `{title?, summary?, layer?, maturity?, tags?, relevance_scope?, relevance_paths?}`.
|
|
6
|
+
|
|
7
|
+
**v2.0.0-rc.37 NEW-12 — explicit split.** Prefer the dedicated action over the legacy combined `modify`:
|
|
8
|
+
|
|
9
|
+
- `action="modify-content"` — scalar edits (title/summary/tags/maturity/relevance_*); stable_id PRESERVED; the server STRIPS any `changes.layer` so this path can never flip layer. Emits `knowledge_slug_renamed` only when slug derives from title.
|
|
10
|
+
- `action="modify-layer"` — the dedicated layer-flip path; `changes.layer` is REQUIRED. The ONLY legal stable_id mutation in the system (see Layer-Flip Rules below).
|
|
11
|
+
- `action="modify"` (legacy alias) — still accepted; routes to content-edit OR layer-flip by whether `changes.layer` is present. New call sites should use the explicit pair.
|
|
12
|
+
|
|
13
|
+
Server semantics (identical regardless of which action selected the path):
|
|
6
14
|
|
|
7
15
|
- **title / summary / tags / maturity changes** → in-place rewrite; stable_id PRESERVED; emits `knowledge_slug_renamed` only when slug derives from title.
|
|
8
16
|
- **layer change** → the ONLY legal stable_id mutation in the system.
|
|
@@ -152,4 +152,4 @@ Full bilingual rendering blocks + step-by-step procedures for the four modes ref
|
|
|
152
152
|
|
|
153
153
|
### Anti-Pattern (Hard Rule restatement)
|
|
154
154
|
|
|
155
|
-
NEVER emit an `AskUserQuestion` whose options include {pending, topic, health, revisit}. The user does not pick the mode. If inference is genuinely ambiguous after
|
|
155
|
+
NEVER emit an `AskUserQuestion` whose options include {pending, maintain} (or the legacy {topic, health, revisit} aliases). The user does not pick the mode. If inference is genuinely ambiguous after both steps, default to `pending` and proceed; the user can always cancel and redirect. (rc.37 NEW-12 collapsed the 4 legacy modes to 2: `pending` + `maintain`.)
|