# RESUME · 压缩后从这里开始（2026-09-18 22:40 固化）

> **这份文件是压缩上下文后的唯一入口。** 读完它 + 它指定的 2 份文档，即可无缝继续。
> 用户操作：压缩 → 说「开始」→ agent 读本文件 → 直接开工第 1 批。

---

## 0. 30 秒状态

| 层 | 状态 |
|---|---|
| **L 层**（底层：界限+接线） | ✅ **全部完工并冻结**（L1–L7 + G4/M3） |
| **M 层** | ✅ M1 完工 · ✅ M2-lint 完工 · ⏸ **M2 的 G3 部分待做** |
| **M2.5**（检索质量） | ✅ a/b 完工，c 已撤销 |
| **R 系列**（降级留痕） | ✅ R1→R2→R2-E1→R3→R3-② 全绿 |
| **R4**（检索区分度+配额） | 📋 **已立项，未开工** ← 用户已批准第 1 批可立即做 |
| **S 层** | ⬜ 未开工（大项目） |

**全量回归**：`PASS 119 / FAIL 0 / TIMEOUT 0`（160.3s）
**宿主重启**：`lib/` 有改动（`wb-contract-pre.js` 等），**需用户自行重启**才生效。

---

## 1. ★ 压缩后必须读的两份文档（按顺序）

```
① docs/internal/BATTLE-PLAN-20260917.md      ← 执行顺序的唯一权威（755 行）
② docs/internal/R4-RECALL-QUOTA-PLAN-20260918.md  ← 下一批工作的完整设计（含行号证据）
```

**只在需要时读**：
- `docs/internal/G3-DESIGN-20260918.md` —— G3 状态写入设计（待用户拍板 2 个边界）
- `docs/internal/MEMORY-GOVERNANCE-20260917.md` §8 —— G3 的权威纲领
- `docs/internal/WB-FORMAT-CONVENTION.md` —— 白板格式契约

---

## 2. ★ 下一步做什么（用户已批准，可直接开工）

### 第 1 批（低风险，用户原话「分层显示可以先做」）

| 项 | 内容 | 关键位置 |
|---|---|---|
| **R4-B 分层展示** | `memory_recall_pre` 输出按层分组（结论层/流水层），**不改排序只改呈现** | `index.js:6093`（语义臂输出行）、`index.js:5977`（词法臂输出行） |
| **R4-留痕补齐** | `py→C2→词法` 三级降级**每一跳都 record**（现只有 diag） | `index.js:5867`（择优逻辑）、`:5882`（现为 `diag(...)`，应改 `_degradeSink.record(...)`）、`:5883`（只有外层失败才记录） |

**用户痛点的原话**：「区分度有点问题，更加有含金量的结论和每一次都有的日志会混在一起」
「如果它会有回退或者不可用的状态，记得及时在日志里面报告」

### 第 2 批

- **R4-配额锁死**（★ 用户实测踩到的真 bug）：先写套件**复现** → 判定设计vs缺陷 → 修
  ```
  :227  COMPACT_THROTTLE_MS = 10 分钟
  :4655 ensureBudget 超限 → 整理循环（3 轮）
  :4744 allowFold = !(last && now - last < THROTTLE)
  :4832 this._lastCompactAt[layer] = Date.now()   ← 整理成功后无条件上锁
  :4689 「整理过但没腾够」路径同样上锁
  ```
  **倾向判定为缺陷**：节流应只防"反复整理"，不该阻止**合法写入**
- **R4-配额测量闭环**：复用 R3 degrade 台账采集 `state.tier0Meta.dropped/perLayer` 趋势
  （数据**已在采集**：`index.js:5099`，只是没用来调参）

### 第 3 批（随 G3）· 第 4 批（单独评估）

- R4-A 作废条目「返回但标记」+ `supersededBy` 指针
- R4-C 层次保底配额（**会改排序，风险最高**）

---

## 3. ⏸ 待用户拍板的 3 件事（BATTLE-PLAN §7）

| # | 事项 | agent 倾向 |
|---|---|---|
| 5 | `superseded` 的可见后果：**剔除**（现状）/ **降权** / **返回但标记** | **返回但标记**（用户原话「明确告诉 AI 这是过时的结论」） |
| 6 | `supersededBy` 算不算违反 S10.4「不新建状态源」 | **不算** —— 是 `status` 的**伴生字段**，非平行账本 |
| 2 | S1.3 literature 理念：(a) 多处可挂载 / (b) 设置内切换归属 | 待定（S 层才需） |

> **问题 5 与 G3 是同一决策的两面**，定完即可实施 G3。

---

## 4. 铁律（压缩后仍需遵守）

### 用户硬性规则（不可违）
1. **严禁停止/重启 dsh web 宿主**（3080）—— 会截断思维链。改完 host 代码**只告知用户自行重启**，agent 绝不 `Stop-Process`/`Start-Process`
2. **严禁无差别杀进程**（`Get-Process node | Stop-Process`）—— DSH 自身跑在 node 上
3. **代码改动一律留在 pre 线**，不提交/不推送/不发布
4. **写文件分块**（一次工具调用不要写过大内容）
5. **编辑前备份**（`*.bak-YYYYMMDD-<tag>`）；`edit` 的 `replace_all` 后必须核对命中数 + `node --check`
6. **无 BOM**

### 项目铁律（血泪换来的）
1. **fail-soft 路径必须返回可观察的成功/失败信号**（如 `persisted` 字段）—— 静默降级 = 结构性缺陷
2. **「源码接线正确 + 诊断数据齐全 + 回归全绿」不能证明功能存在** —— 必须**实机验证真实副作用**（如落盘文件真的出现）
3. **守卫判据键若同时存在于 `DEFAULT_CONFIG`**，判据必须读**磁盘原文**而非合并后 `config`（否则结构性恒真/恒假；已犯两次：M8、M9）
4. **判据纪律：宁可漏判，不可误伤**（漏判=维持现状；误伤=合法内容被改变行为路径）
5. **语义引擎铁律**：JS 引擎=默认形态，Python=发烧者进阶项，**两套可互换、严禁互相依赖**
6. **观测面三层边界**：`diag()`=过程日志 / `debugView()`=各域局部状态 / `degrade`=跨臂统一台账

### 每步固定收尾序列
```
备份 → 改码 → node --check → 写新套件 → 跑该套件(看红) 
→ 变异演示(确认真红) → 还原(SHA256 校验) → 跑全量回归(0 失败) → 留痕
```

---

## 5. 当前代码状态（关键行号，均本次实测）

### 已改动的文件（pre 线未提交）
```
lib/tier0-catalog-pre.js      ← M1：TIER0_ANCHOR_ID_RE + renderCatalogLinePre 两种渲染
lib/wb-sidecar-pre.js         ← M1：buildSidecarEntryPre 增 layer/status + sidecarEntryToCatalogItemPre
lib/wb-contract-pre.js        ← M2：lintWhiteboardPre（43/43）+ WB_LINT_* 常量
lib/recall-fusion-pre.js      ← M2.5b：layer 臂
lib/l0-extract-pre.js         ← M2.5a：L0 质量门
lib/index.js                  ← R 系列：degrade 台账接线
lib/client.js                 ← L 层
```

### 本次新增的套件
```
tests/smoke/smoke-test-m1-contract-line-pre.mjs   (55/55)
tests/smoke/smoke-test-m2-lint-pre.mjs            (43/43)
tests/smoke/smoke-test-m25a-l0-qualitygate-pre.mjs(39)
tests/smoke/smoke-test-m25b-layer-arm-pre.mjs     (37)
tests/smoke/smoke-test-r2e1-evidence-guard-pre.mjs(20)
tests/smoke/smoke-test-r3-degrade-pre.mjs         (77)
```

### 本次新增的备份
```
lib/tier0-catalog-pre.js.bak-20260918-M1
lib/wb-sidecar-pre.js.bak-20260918-M1
lib/wb-contract-pre.js.bak-20260918-M2
lib/index.js.bak-20260918-M25b / -R2ev / -M9cap
lib/l0-extract-pre.js.bak-20260918-M25a
lib/recall-fusion-pre.js.bak-20260918-M25b
```

---

## 6. 本次会话（2026-09-18）完成的事

| 项 | 结果 |
|---|---|
| **M1 契约行渲染** | ✅ 三处改动，55/55，变异 5/5 真红，回归 117→118 |
| **M2-lint**（缺口 4） | ✅ 43/43，变异 6/6 真红，只读纪律有硬断言，回归 118→119 |
| **R4 立项** | ✅ 用户四个痛点全部定位代码级，产出 13KB 设计文档 |
| **BATTLE-PLAN** | ✅ 新增 §3.7（R4）· §8（远期清单）· §7 扩到 6 件待拍板 |

### M2-lint 的判据口径（重要，勿推翻）
| 契约 §6 | 实现口径 | 为什么收窄 |
|---|---|---|
| ① 孤立条目 | 全语料找不到其 id/标题 | 标题 <2 字符**不参与匹配**（防单字误命中） |
| ② 陈旧 | 状态直读 + 日期比较（严格 `<`） | **缺日期不报**（不猜） |
| ③ 被提及无卡 | **只判客观可证的悬空 `mem_` 引用** | 「什么算概念」无法零 token 判定 |
| ④ 缺交叉引用 | 共享 ≥2 个 tag 却互不引用 | 由 **tag 交集代言**，不猜语义 |
| ⑤ 矛盾检测 | **不判**，但 `counts.notChecked=['contradiction']` 显式声明 | 需 LLM，**不许静默省略** |

---

## 7. 踩过的两个坑（可复用）

1. **运算符优先级**：`'a' + x + 'b'.match(re)` 中 `.match` 只绑到 `'b'`（`.` 优先于 `+`）⇒ 得到字符串 `'...null'`。**必须先拼成变量再 `.match()`**
2. **源码级断言必须先剥离注释**：JSDoc 里提到函数名/文件名会造成假阳性（与 R3 裸 `join` 断言同源）。已加 `stripComments` + **反向自检**断言证明剥离有效

---

*固化时间：2026-09-18 22:40 · 全量回归 PASS 119 / FAIL 0 / TIMEOUT 0*
