feat(memcore): 三项优化——等价表述清单、即时引用快扫、并发写入冲突保护

- memory-lint: 新增 Phase 4-pre 等价表述加载(synonyms.md),矛盾检测先过等价组
  再判断,降低 AI 语义误判率;幽灵检测排除 synonyms.md;合并残留标记
  (<!-- merge-conflict --> / <!-- remote-diverge -->) 纳入 Phase 4 NEED-HUMAN 检测;
  Phase 9 独立输出增加 synonyms.md 创建提示

- memory-update: synthesis 双触发升级为三触发,新增 Phase 3C 即时引用计数快扫,
  每次执行末尾强制扫描,不依赖 lint 延迟触发,短会话也能捕获升级候选

- memory-sync: Phase 0 重构为"冲突优先检测 → 普通变更提交"两步结构,
  冲突语义合并规则补齐 user_profile/synthesis_* 保护边界;Phase 3 新增并发分歧
  检测(mtime 差 <7 天),对双边改动文件执行轻量节段合并;Phase 11 报告加入
  并发冲突处理摘要行;流程概述、执行约束同步更新

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SkyJourney
2026-05-10 18:33:49 +08:00
co-authored by Claude Sonnet 4.6
parent 9c2f19b579
commit f26e741b0e
3 changed files with 165 additions and 10 deletions
+60 -2
View File
@@ -49,7 +49,7 @@ ls "$PROJECT_DIR/.claude/memory/"*.md
| Phase | 定义 | 级别 | AUTO-FIX |
|-------|------|------|---------|
| 1 孤儿 | 索引有 → 磁盘无 | ERROR | 从 MEMORY.md 删该条目 |
| 2 幽灵 | 磁盘有 → 索引无(排除 MEMORY.md / lint_report.md | WARN | 补入索引(类型推断见下) |
| 2 幽灵 | 磁盘有 → 索引无(排除 MEMORY.md / lint_report.md / synonyms.md) | WARN | 补入索引(类型推断见下) |
幽灵类型推断:`user_*` → user / `project_*``decisions.md` → project / `feedback*` → feedback / `reference*` → reference / `synthesis_*` → synthesis / 其他默认 project。
@@ -84,6 +84,40 @@ ls "$PROJECT_DIR/.claude/memory/"*.md
三类全部为 NEED-HUMAN,写入 lint_report.md 时**必须附带 3 问 yes/no checklist + 决策矩阵**Phase 8 模板)。
### Phase 4-pre — 等价表述加载
矛盾检测前先加载 `$PROJECT_DIR/.claude/memory/synonyms.md`(可选文件)。
**加载方式**
```bash
[ -f "$PROJECT_DIR/.claude/memory/synonyms.md" ] && \
grep -v "^#\|^---\|^$\|^name:\|^description:\|^type:" \
"$PROJECT_DIR/.claude/memory/synonyms.md"
# 输出每行一个等价组(逗号分隔),大小写不敏感,存入 $SYNONYMS_GROUPS
```
**synonyms.md 格式**(用户自行在项目内创建和维护):
```markdown
---
name: 等价表述清单
description: 矛盾检测等价词表,同组词视为相同概念
type: reference
---
# 等价表述清单
> 每行一组,逗号分隔,大小写不敏感
PostgreSQL, PG, Postgres, postgresql
JWT, JSON Web Token
Vue3, Vue 3, Vue 3.x
```
**判定规则**:检查两处描述中出现的技术术语是否属于同一等价组。若属同组 → 跳过,不纳入矛盾候选。
**无 synonyms.md 时**:仅检测直接数值/版本冲突(如 PostgreSQL 14 vs PostgreSQL 16),对措辞差异不报告。
---
### Phase 4 内容矛盾
| 维度 | 检查 |
@@ -92,6 +126,25 @@ ls "$PROJECT_DIR/.claude/memory/"*.md
| decisions vs 架构文件 | 与 pom.xml / package.json / requirements.txt 实际依赖不一致 |
| 多 feedback 文件 | feedback.md 与 feedback_{topic}.md 重复或矛盾 |
| synthesis vs decisions | synthesis 结论与决策抵触 |
| 合并残留标记 | 文件含 `<!-- merge-conflict -->``<!-- remote-diverge -->` → WARNNEED-HUMAN |
```bash
grep -rn "<!-- merge-conflict\|<!-- remote-diverge" "$PROJECT_DIR/.claude/memory/" --include="*.md"
```
合并标记的 NEED-HUMAN 条目模板:
- **位置**`decisions.md → ## [合并待审] JWT 密钥安全``<!-- merge-conflict: 2026-05-10 -->`
- **Checklist**Q1 本地版本是否正确?Q2 远端版本是否有本地没有的有效信息?Q3 是否可合并为单一表述?
- **矩阵**Q2 否 → 删除 `## [合并待审]` 段落和标记;Q2 是 + Q3 是 → 合并后删标记;Q3 否 → 保留两段但清除 HTML 注释
矛盾判定门槛(过 synonyms.md 等价检查后):
| 情况 | 处理 |
|------|------|
| 同主题,等价组内术语不同 | 跳过,不报告 |
| 同主题,结论相反(推荐 A vs 推荐 B | WARN → NEED-HUMAN |
| 同主题,数值/版本直接冲突 | ERROR → NEED-HUMAN |
| 措辞不同,无直接逻辑冲突 | 跳过(宁漏报不误报) |
```bash
cat "$PROJECT_DIR/pom.xml" || cat "$PROJECT_DIR/package.json" || cat "$PROJECT_DIR/requirements.txt"
@@ -226,6 +279,11 @@ last_updated: YYYY-MM-DD
独立调用:扩展输出已修复 / 待处理清单 + 报告路径;全通过则 `✅ 记忆体系健康,已更新执行时间`
发现矛盾候选且 `synonyms.md` 不存在时,额外输出:
```
💡 创建 .claude/memory/synonyms.md 可将等价术语(如 "PostgreSQL, PG")预先排除出矛盾检测,降低误报率。
```
每次执行必须更新 lint_report.md(即使全通过也刷新执行时间)。
---
@@ -234,6 +292,6 @@ last_updated: YYYY-MM-DD
1. **AUTO-FIX 边界严格** — 只修结构性错误(孤儿、幽灵、断链、双链),不改业务内容
2. **NEED-HUMAN 完整记录** — 每项含 checklist + 决策矩阵
3. **矛盾检测宁缺勿滥**措辞不一致 ≠ 矛盾,宁漏报不误报
3. **矛盾检测先过等价表**先加载 `synonyms.md` 再判矛盾;措辞不一致 ≠ 矛盾,宁漏报不误报;项目可在 `.claude/memory/synonyms.md` 维护等价组降低误报率
4. **污染检测边界**(重申)— 架构层级保留 / 具体类名路径删除
5. **被调用静默返回**`/memory-sync` 内 Phase 9 不输出收尾
+72 -6
View File
@@ -8,7 +8,8 @@ description: 项目记忆体系完整同步。Git检查→远程补充本地→
记忆体系完整同步周期。**本地 `.claude/memory/` 是唯一权威**,远程为镜像。会话目录 = `$PROJECT_DIR`
```
Phase 0-4 前置(git + 锚点 + 远程→本地补充 + 多机检测 + diff
Phase 0 Git 准备(冲突解决优先 → 普通变更提交
Phase 1-4 前置(锚点 + 远程→本地补充 + 多机/并发检测 + diff)
Phase 5 /memory-update(本地写入)
Phase 6 /memory-lint(收敛)
Phase 7-9 CLAUDE.md 维护与记忆引导区块注入
@@ -18,15 +19,48 @@ Phase 11 完成报告
---
## Phase 0 — Git 检查
## Phase 0 — Git 准备(冲突解决 → 变更提交)
```bash
git -C "$PROJECT_DIR" status --short
```
- 有未提交变更 → 展示文件、询问 commit message(默认 `chore: sync before memory update`)→ `git add .claude/memory/ && git commit -m "<msg>"` → 记新 hash
- 干净 → `git rev-parse --short HEAD``$HEAD_HASH`
- 非 git 仓库 → 跳过 Phase 0commit 字段填 `N/A`
非 git 仓库 → 跳过 Phase 0commit 字段填 `N/A`
### Step 1 — 冲突优先检测(必须先于 commit)
```bash
git -C "$PROJECT_DIR" diff --name-only --diff-filter=U -- .claude/memory/
```
有冲突文件(输出非空)→ 执行**语义合并**(见规则表)→ `git add .claude/memory/ && git commit -m "chore: resolve memory merge conflicts"` → 更新 `$HEAD_HASH` → 跳至 Phase 1。
无冲突 → 继续 Step 2。
**语义合并规则**(冲突文件逐个处理):
| 冲突类型 | 处理方式 |
|---------|---------|
| frontmatter `last_updated` | 取两者较新日期 |
| frontmatter `commit` | 取 HEAD 侧(本地权威) |
| `## Section` 块 — 两边内容相同 | 保留一份 |
| `## Section` 块 — 仅本地有 | 保留 |
| `## Section` 块 — 仅远端有 | 追加到文件末尾 |
| `## Section` 块 — 两边均有且不同 | 本地原位保留,远端追加为 `## [合并待审] Section`,标注 `<!-- merge-conflict: YYYY-MM-DD -->` |
| `MEMORY.md` 索引 | 取本地版本,不尝试合并;Phase 6 lint 重建 |
| `user_profile.md` / `synthesis_*.md` | 整文件不自动合并,在文件头追加 `<!-- merge-conflict: YYYY-MM-DD, NEED-HUMAN -->` |
合并前备份:
```bash
cp "$conflicted_file" "${conflicted_file%.md}.conflict_backup_$(git rev-parse --short HEAD).md"
```
备份文件加入 `.gitignore``*.conflict_backup_*.md`),不提交。
### Step 2 — 普通变更提交
有未提交变更(非冲突)→ 展示文件、询问 commit message(默认 `chore: sync before memory update`)→ `git add .claude/memory/ && git commit -m "<msg>"` → 记新 hash。
干净工作区 → `git rev-parse --short HEAD``$HEAD_HASH`
---
@@ -81,6 +115,34 @@ done
- `n` → 终止整个 sync 流程
- `y` → 继续(Phase 10 仍按本地权威覆盖远程)
### 并发分歧检测(mtime 差 < 7 天的冲突预警)
对「两边都存在且 mtime 差 < 7 天」的文件,进一步做内容比对:
```bash
for f in ~/.claude/projects/{PROJECT_KEY}/memory/*.md; do
local="$PROJECT_DIR/.claude/memory/$(basename "$f")"
[ ! -f "$local" ] && continue
diff_days_abs=$(...) # 绝对值 < 7
if [ "$diff_days_abs" -lt 7 ] && ! diff -q "$f" "$local" > /dev/null 2>&1; then
echo "$(basename "$f") 双边近期均有改动,内容不同"
fi
done
```
发现 ≥1 个双边分歧文件 → 执行**轻量节段合并**(不等 git 冲突):
1. 提取远端文件中所有 `## Section`
2. 检查本地文件是否含同名 `## Section`
- **不含** → 将整个远端 Section 追加到本地文件末尾(自动合并)
- **含且内容相同** → 跳过
- **含且内容不同** → 在本地文件该 Section 末尾追加注释:
`<!-- remote-diverge: YYYY-MM-DD, 请手动核对 -->`,并将远端版本追加为 `## [远端版本] Section`
3. 不修改 `MEMORY.md`(由 Phase 6 lint 重建)
4. 完成后输出合并摘要,提示用户审查 `<!-- remote-diverge -->` 标记
**此步骤的保护边界**:仅处理 decisions.md / feedback*.md / project*.md`user_profile.md``synthesis_*.md` 不自动合并(内容主观性强,直接标 NEED-HUMAN)。
---
## Phase 4 — diff 范围
@@ -164,6 +226,9 @@ cp -pf "$PROJECT_DIR/.claude/memory/"*.md ~/.claude/projects/{PROJECT_KEY}/memor
```
✓ memory-sync 完成
⚡ 并发冲突处理:git冲突 N 个文件 / 双边分歧 M 个文件(均为 0 则省略本行)
含 <!-- merge-conflict --> / <!-- remote-diverge --> 标记的条目请通过 /memory-lint 审查
📋 CLAUDE.md [已更新 / 已新建 / 无变更]
📖 记忆引导区块 [已注入 / 已刷新 / 无变更]
@@ -185,8 +250,9 @@ cp -pf "$PROJECT_DIR/.claude/memory/"*.md ~/.claude/projects/{PROJECT_KEY}/memor
## 执行约束
1. **Phase 0 最先**有未提交代码不允许跳过
1. **Phase 0 最先**冲突检测(Step 1)必须先于普通变更提交(Step 2);有未解决冲突不允许跳过语义合并直接进入 Phase 1
2. **方向严格区分** — Phase 3 只补充 / Phase 10 才覆盖
3. **顺序固定** — Phase 5update)→ Phase 6lint);lint 在 update 写入完毕后检查
4. **统一推送** — update + lint 修改全部本地完成后由 Phase 10 一次推
5. **记忆引导区块强制存在** — 每次 sync 后 CLAUDE.md 必须含最新区块,文件清单与 MEMORY.md 严格一致
6. **并发冲突处理顺序** — git 冲突(Phase 0 Step 1)→ mtime 分歧合并(Phase 3)→ 两者均完成后才进 Phase 5;`<!-- merge-conflict -->` / `<!-- remote-diverge -->` 标记由 lint 扫描、用户事后审查
+33 -2
View File
@@ -109,7 +109,7 @@ commit: HASH
2. 主题相关 → 新条目末尾追加 `[[file.md#标题]]`
3. **反向也补**:被引用的旧条目末尾补充对新条目的引用
### synthesis 触发模式
### synthesis 触发模式(三路)
**A. 会话内主动**:会话出现技术选型对比 / Bug 根因 / 架构演进 / 性能安全分析时,主动提议归档为 `synthesis_{type}_{topic}.md`
@@ -126,6 +126,37 @@ commit: HASH
5. `n` → 原条目末尾加 `<!-- synthesis-decline: YYYY-MM-DD -->`30 天免打扰
6. `skip-all` → 本次不再提议
### 3C — 即时引用计数快扫(不依赖 lint)
每次执行 Phase 3 末尾**强制运行**。目的:在短会话或任务型对话中,不依赖 lint 的延迟触发,直接检测 synthesis 升级候选。
```bash
# 对 decisions.md / feedback*.md 的每个 ## 条目,统计被多少不同源文件引用
for entry_file in "$PROJECT_DIR/.claude/memory/decisions.md" \
"$PROJECT_DIR/.claude/memory/feedback"*.md; do
[ -f "$entry_file" ] || continue
fn=$(basename "$entry_file")
while IFS= read -r title; do
count=$(grep -rl "\[\[${fn}#${title}\]\]" \
"$PROJECT_DIR/.claude/memory/" --include="*.md" \
| grep -v "^${entry_file}$" | wc -l)
[ "$count" -ge 3 ] && echo "$count|$fn#$title"
done < <(grep "^## " "$entry_file" | sed 's/^## //')
done | sort -t'|' -k1 -rn
```
对每条输出候选(`count|file#title`):
1. 读原条目内是否含 `**Synthesized:**` → 已升级,跳过
2. 读原条目内是否含 `<!-- synthesis-decline: YYYY-MM-DD -->` → 30 天内,跳过
3. 以上均无 → 触发提议(同 B 模式 step 3-6)
**与 lint 的分工**
- Phase 3C(快扫):每次 memory-update 必跑,判据为「存在引用行数」,适合即时触发
- lint Phase 3A(精扫):按源文件去重的精确计数,月度健康检查时运行
- 两者以 `**Synthesized:**` 标记为唯一判重依据,不重复创建文件
---
### 写入要点
- 仅更新有变化维度,不重写无关文件
@@ -161,5 +192,5 @@ commit: HASH
2. **不记录可推断内容** — 实现细节、文件路径、git 历史不入库
3. **feedback 拆分** — 同主题 >5 条规则 → 拆出 `feedback_{topic}.md`
4. **See Also 双向同步** — 新增时扫关联并双向补
5. **synthesis 触发** — 会话主动 + lint Top 反向;`**Synthesized:**` 标记不可覆盖;`<!-- synthesis-decline -->` 标记 30 天免打扰
5. **synthesis 触发** — 会话主动 + Phase 3C 即时快扫 + lint Top 反向;`**Synthesized:**` 标记不可覆盖;`<!-- synthesis-decline -->` 标记 30 天免打扰
6. **被调用静默返回** — `/memory-sync` 内执行不输出收尾