# 第三轮复核的整合与核实（2026-09-14 深夜）

> **用途**：GPT 对《合并总纲 v1》的复核答复，逐条核实 + 采纳/驳回 + 落成总纲 v2 的修订清单。
> **核实方式**：凡涉及代码现状的，一律**打开文件读当前字节**（不采信转述）；凡属设计判断的，标注为"采纳/部分采纳/保留意见"。
> **性质**：本文是**修订记录**，不是新方案。总纲 v2 由 `MASTER-PLAN-3.0.md` + 本文共同构成。

---

## 0. 总评

GPT 的复核**质量高于第一轮**：它没有顺着写，而是**指出了我方调和的具体错误**，且每条都给了落点与断言。它给出的总裁决是：

> 「分线实施、优先处理规则表达、精排改为可选异步，方向成立；**总纲还不能原样作为施工依据**。」

**这个否决是成立的**，理由见 §2（我方三处实质错误）。它给出的开工顺序已经可以执行（§5）。

---

## 1. 逐条核实：GPT 对代码现状的指控

| # | GPT 的主张 | 我方核实结果 | 判定 |
|---|---|---|---|
| V1 | `memory-writer-pre.js:235` 的 `_queue` 提供**实例内**串行化 | 读文件确认：`:235 _queue(filePath, job)`，按 `path.resolve(filePath)` 分键排队 ✓ | **真** |
| V2 | `replace` 在队列内部检查 `expectedDigest`，见同文件 `:344` | 读文件确认：`:343 /** 整篇替换(§9);同文件串行。 */`、`:344 replace(filePath, replacement, opts = {})`、`:345 return this._queue(filePath, async () => {` ✓ | **真** |
| V3 | `atomicReplace` 的文件替换**不是**"比较 digest 并替换"的原子 CAS | 读文件确认：`:199 export async function atomicReplace(target, data, fsApi = fsDefault)` —— **签名里没有 expectedDigest 参数**，只做原子写，不做比较 ✓ | **真** |
| V4 | `index.js:1444`：已保存配置**覆盖**默认值 | 读文件确认：`:1444 this.config = { ...DEFAULT_CONFIG, ...(parsed && ...) }` ✓ | **真** |
| V5 | `index.js:7455`：`Number(value) \|\| 5` 会把 **`0` 回退到 `5`** | 读文件确认：`:7455 const gap = Math.max(0, Number(engine.config.snapshotMinGapRounds) \|\| 5)` ✓ —— 且本机配置实测 `snapshotMinGapRounds = 5` | **真** |
| V6 | 明确写"**单对话进度快照**"的是 **WB 原方案 §1**，不是 GPT 自己 | 读文件确认：`WB-GRAPH-INTEGRATION-PLAN.md:42`「白板是**单对话进度快照**，dsh-graph 七阶段…」✓ | **真，且我方归因错误** |

**结论：V1–V6 全部成立，零误报。** 这六条里 **V6 是对我方的直接纠错**（见 §2.1）。

---

## 2. 我方三处实质错误（必须改）

### 2.1 错误一：把"单会话快照"这个前提**错误归因给了 GPT**

- **我方在矛盾扫描 §2 冲突 1 写**：「GPT 的 Phase 4 通篇假设白板是**当前会话**的进度快照，写入签名里没有会话身份概念」。
- **事实**：GPT 自己的方案里 `SnapshotRef` 带 **`workspaceKey`**，`writePlanSnapshot(projectDir, content, …)` 收的是 **projectDir** —— **它本来就是按工作区定位的**。写"单对话进度快照"的是 **`WB-GRAPH-INTEGRATION-PLAN.md:42`**（你自己的方案）。
- **影响**：共享图裁决**结论不变**（用户裁定仍然有效），但**理由要改**：不是"GPT 假设错了"，而是**两份方案对"持久数据 vs 会话执行态"的分工没讲清**。
- **处置**：矛盾扫描 §2 冲突 1 的归因作废；改为"应**调整持久数据与会话执行态的分工**，而不是推倒原快照模型"（GPT 原话）。

### 2.2 错误二：把"账本判据"整体套给了白板

- **我方在总纲 §4 Phase 0 写**：「格式判据采纳 WB-GRAPH 的完整判据表（**H1–H4 硬 / S1–S4 软**）」。
- **事实**：`WB-GRAPH-INTEGRATION-PLAN.md` §2.2 里 **H1–H4/S1–S4 是"交接账本（kind=handoff）"的判据**；**白板 PLAN 另有 P-H1/P-H2/P-S1**（且刻意保持最弱，因 PLAN 是自由全貌文档，节名不固定）。
- **处置**：总纲 Phase 0 须写成"账本用 H1–H4/S1–S4，PLAN 用 P-H1/P-H2/P-S1 + 卡片完整性 + 用户区保护"。

### 2.3 错误三：规则真源**自相矛盾**（`MEMORY.md` vs 新增 `RULES.md`）

- **裁决记录 §8.3 写**：用户级规则＝**现有 `~/.dsh/memory/MEMORY.md`**。
- **总纲 Phase 6 写**：用户级 **`~/.dsh/memory/RULES.md`**（新增文件）。
- **事实**：同一件事在两份文档里给了**两个不同的真源**。这是调和时新引入的矛盾，GPT 抓到了。
- **处置（采纳 GPT 建议）**：**本版保留既有用户级记忆中"类型化规则"为唯一真源**；若将来生成 `RULES.md`，**只作单向派生视图**，不做双向维护。若将来真要改用独立真源，必须补**迁移、去重、旧模式读取、回滚**四类测试。

---

## 3. 采纳的设计修正（总纲 v2 的修订清单）

### 3.1 Q1（Phase 4 拆分）—— 部分采纳，边界重划

| GPT 的修正 | 处置 |
|---|---|
| **写入门不是注入边界本身**：写入门阻止**破坏源数据**；状态过滤阻止**不应使用的数据进入上下文**。可同阶段交付，但不能混成一个检查 | **采纳**。总纲 Phase 0 须把两者列为**两个独立检查** |
| **H1–H4/S1–S4 不能整体套给 PLAN** | **采纳**（见 §2.2） |
| **`archiveAnswerPre` 不以图遍历为必要前提**：「必须等 `memory_expand_pre` 才有意义」的因果不成立；保存、幂等和普通 recall 回流可先验收 | **采纳**。总纲 §5 该行作废 |
| **边界应为：3.0 拥有共同提交与保护入口，白板线拥有白板格式及其适配器** | **采纳**。这是比"移交/保留"更准的切法 |
| **§7"白板待定点不阻塞 3.0 主体"过于笼统** | **采纳**。改为：**不阻塞其他主体工作，但阻塞依赖这些定义的白板写入保护验收** |
| 新落点：`memory-mutation-pre.js:validateMutationBoundaryPre` 接收规范化投影（`beforeIds/afterIds/protectedRegions/changes`），**不自行解释图格式**；格式由 `wb-contract-pre.js:parseWhiteboardPre` 提供 | **采纳**。关键点：**格式只维护一份** |
| `criteriaGate=false` / 骨架 fail-soft **不得**跳过丢卡、用户区、版本、状态保护 | **采纳**。写进总纲 §6 交付纪律 |
| 新增断言 **T0-8B**（三条写入路径都不能绕过保护）、**T0-8C**（关质量门后仍拒绝丢卡）、**T4-4**（投影/快照/目录同一身份映射） | **采纳** |

### 3.2 Q2（并发）—— 采纳"需要原子提交边界"

**GPT 的核心纠正**：两个写者都先读 D、都通过比较、再分别写 A 和 B ⇒ **后写者仍会覆盖前写者**；「通常只有一个活跃窗口」**不能证明这个时序不存在**。

- 我方原写法"乐观并发 + 冲突可见，**不加锁**"**不完整**。
- **修正**：**保留现有短提交队列**（`_queue` 即实例内串行化），所有窗口通过**同一工作区的写入 owner** 提交；`expectedDigest` **必须在该提交边界内检查**（代码里 `replace` 已在队列内部检查 ✓）。**不引入长期编辑锁、不按会话分片。**
- **并发范围**：3.0 支持**多窗口经同一宿主提交**；**多宿主同目录**通过 U2 单独认证。若作者要求多进程直接写同一文件，则**必须**增加跨进程原子提交或单写者路由 —— 「不加锁」方案给不了这个保证。
- 提交接口补来源身份：`{workspaceKey, boardId, txId, expectedDigest, expectedStateVersion, actor:{sessionId,contSeq,kind}, writes, stateChanges}`，其中 **`boardId` 在工作区内稳定、不含当前会话号**。
- **会话隔离与图共享要分开**：共享数据按工作区缓存；**激活、冷却、精排任务、已交付标记仍按会话隔离**。
- **`Cue(session)` 是节点来源，不能自动成为"只允许检索当前会话内容"的硬过滤**。
- **`done/passed/archived` 不能塞进 `current/superseded/retracted`** —— 任务进度、判据确认、记忆有效状态**分别表示**。
- 新增断言：**T1-7B**（A 的激活包不能出现在 B）、**T1-7C**（旧接续窗口的迟到写入不能覆盖新图）。

### 3.3 Q3（预算）—— 采纳分项账本 + 选 (ii)

**Q3a**：与"唯一组装器、统一记账"**不冲突**；与原来的"**全部**动态文本不超过 `injectBudgetChars`"**冲突** ⇒ **T0-3 必须改**。
**采纳**：`FinalEnvelope` 改为**分项账本**（仍由同一个 `composeMemoryEnvelopePre` 生成）：

```
chars: { rules, memoryReferences, otherDynamic, total }
limits: { memoryReferences, otherDynamic }
```

- 标题、分隔符、降级说明**必须归入某个分项**，不允许未计费尾巴。
- **规则"不参与裁剪"不等于"永久注入"** —— 规则被撤回后应消失。
- 修订 **T0-3 / T7-2 / T6-5**：规则完整性逐字比较；两个参考分项分别不超限；总计等于最终序列化长度。

**Q3b**：用户把裁决交给 GPT，**GPT 选 (ii)：规则与参考分开预算，但只保留一个组装器、一本账**。
**采纳**，理由（GPT 给）：(iii) 允许裁规则违背已定要求；(i) 固定总额遇到规则本身超总额时仍须拒绝或裁剪，还引入预留不足/借用/回收；(ii) 只需完整规则段加两个有界参考段再求和，**不需要新增调度或分配框架**（直接回应了用户"怕复杂出 bug"的顾虑）。

**关键设计**（正面回答了用户"预留数字合不合适"）：

```
R  = 本轮完整规则段实际字符数（含其标题与边界）   ← 不设魔数，由实际内容决定
Bm = injectBudgetChars（本机 4800）
Bo = otherDynamicBudgetChars
memoryReferencesChars <= Bm
otherDynamicChars     <= Bo
totalChars = R + memoryReferencesChars + otherDynamicChars
```

- **`Bo` 新增为独立配置；未配置时以当前 `Bm` 作为派生初值**，不另设魔数，不覆盖已有配置。
- **明确代价**：**不再承诺"插件全部动态内容有一个与规则规模无关的固定字符上限"**。规则越长，用户输入成本越高 —— **不能用换记账写法掩盖它**。
- **物理容量边界**：若完整规则 + 请求必需内容超过宿主上下文容量 ⇒ **拒绝发送并显示明确容量错误，保留原规则**，让用户整理；**不能静默裁规则**。
- 真实 token 判断依赖 U6/U7；**尚未具备时不得宣称已保证模型 token 上限**。
- **成本措辞收紧**：不改"成本极小"——**规则数量没有实测前不填写**。

**Q3c**：**只能部分确认**"只需改默认值"。

| 我方原判断 | GPT 的修正 | 核实 |
|---|---|---|
| 节奏部分只需把默认值 5 改为 1 | **新装且未保存配置时**可以；**已有用户的已保存配置会覆盖默认值**（`index.js:1444`） | **真** |
| — | **`0` 无法表达**：`Number(value) \|\| 5` 把 0 回退到 5（`:7455`）⇒ 需**修正零值解析**，区分合法零值与缺失/非法值 | **真** |
| — | 旧节流分支**提前返回时可能跳过本轮动态快照**（`:7483`）⇒ 须"**先提供规则段，再对参考内容应用 gap**" | 需采纳 |
| 我方总纲 T7-1 断言"每轮注入文本中规则都在" | **改测"最终请求 messages"** —— 局部注入块的存在不等于模型真收到了 | **采纳**（这正是"源码接线守卫 ≠ 行为断言"的又一变体） |
| T7-6 断言前缀字节稳定 ⇒ 缓存友好 | **只能证明字节稳定**；不能证明完整请求的缓存命中，**更不能证明按 1/10 计费** | **采纳，措辞收紧** |

**同时 GPT 也收敛了我的一个过强结论**：不能把"部分轮次不追加新快照"直接等同于"本轮 messages 完全没有旧规则" —— 后者需要 **U6 的实际消息证据**。目标应表述为：**每个实际请求具备当前有效规则，且更新、撤回后不继续使用旧版本**。

### 3.4 Q4（精排）—— 采纳，且修正我方两处夸大

**我方两处必须改的**：

| 我方原写法 | 事实 | 修正 |
|---|---|---|
| 把 bge（50 对/题）与 qwen（10 题 × 10 对）的 P95 **并列展示** | **候选规模不同，不可直接比较速度或全量质量** | 拆表标注候选规模；qwen 是**小样本探针** |
| "cross-encoder 模型已下载 ⇒ U5 阻塞解除" | **已下载模型 ≠ int8 快档产物与运行性能已就绪** | U5 拆成：模型资产 ✓ / GPU torch ✗ / 快档量化产物 ✗ |
| "T5-5 费用门按新预算重算" | **T5-5 是生成式改写（H1）的费用门**，不应因 H2 变慢而重算 | **保留 H1 费用门**；本地精排**另设资源门 T5-5R** |
| — | RSS 峰值实测 **3.84GB（bge）/ 4.95GB（qwen）** | 旧 2048MiB 建议上限**不能沿用** |

**采纳的核心机制修正**：**一分钟必须定义为"有界的后台结果窗口"，不能解释成"下一轮无条件使用上一轮结果"**。新增：

```
enqueueRerankPre({originRequestKey, inputKey, queuedAt, expiresAt, candidates})
takeReusableRerankPre({requestKey, inputKey, now})
invalidateReranksPre({sessionId, workspaceKey, reason})
```

`inputKey` 至少含：`workspace + scope + session + 查询输入摘要 + miv + 候选ID及正文摘要 + embeddingIdentity + rerankerIdentity + 处理版本`。

**规则**：一分钟从**入队时**开始（含排队与计算），**到期不续命**；本轮立即用现有排序；后台完成**只进有界结果缓存**，不改本轮 envelope、不再次 emit；下一请求**重新生成 requestKey**，仅 `inputKey` 完全匹配才复用，且**重跑当轮状态/fv2/冷却/预算检查**；会话关闭、接续换会话、查询或 `miv` 改变即不复用；**没有下一轮就过期，不主动制造模型请求**。

**运行隔离**：精排用**独立、懒加载的 worker 角色**，复用既有 sidecar 传输；**不能把几十秒的同步计算塞进同时承担稠密查询与索引服务的 worker**。一期**全机最多一个精排任务**，忙时继续粗排。

**断言修订**：T3-2（按选定 `ordering` 验证，异步时不再断言 RRF 顺序）、T3-6（后台完成不能创建第二个主激活）、T5-1/T5-3/T5-4/T5-5/T5-6、T6-2/T6-3（前台返回与后台完成分开测）、T6-6（关闭增强后旧任务完成不得重启用）；新增 **T5-3R / T5-4R / T5-5R / T5-6R**（含"零次实际复用时不得把离线收益写成异步注入收益"）。

### 3.5 Q5（引擎隔离）—— 采纳，并收回我方"成本为零"

| GPT 的修正 | 处置 |
|---|---|
| 单个 `PROVIDER_ID_INT8` **不足以**标识模型内容/tokenizer/预处理；两层缓存都只凭 `chunkId` 或裸输入哈希读取，**仍会串用向量** | **采纳**。引擎身份须含：模型/权重摘要、tokenizer 版本、精度格式、维度、池化、归一化、输入处理版本 |
| **模型下载完成 ≠ 当前引擎索引完成**；向导须区分 `assetsReady` 与 `indexReady` | **采纳** |
| **T2-9 拆四个子断言**：T2-9a（e5→BGE→e5 每次都完整重建，旧缓存存在也不能跳过）、T2-9b（A 未完成又发 B，A 迟到不得发布 B 的 ready）、T2-9c（编码中暂停/失败/重启，进度来自实际完成量、manifest 发布前不得显示完成）、T2-9d（任一失败词法仍可用且原因可见） | **采纳** |
| 我方写"**成本为零**" | **改**为"**身份比较开销小；全量重建成本由本机承担**" |
| `ensureDeps({gpu:true})` 装的是 `onnxruntime-gpu`，**不等于 GPU torch 已可供精排使用**；向导须分别探测"嵌入／精排CPU／精排CUDA"，不能用一个 `depsOk` 冒充全部就绪 | **采纳** |
| 新增 `beginEngineSwitchPre / getEngineSwitchStatusPre / cancelEngineSwitchPre`，状态由 `createIndexSyncHostPre` 持有 | **采纳** |
| 保存后才启动切换（不能下拉框未保存就重建）；进度**只能有一个真实所有者** | **采纳**（正面回应了用户"进度条不要和引导耦合"的顾虑） |

### 3.6 Q6（工具数）—— 采纳，且修正我方分类

- **我方矛盾扫描把"工具数"列为"真冲突"，同一节又写"不真冲突"** —— 自相矛盾，GPT 指出。
- **修正分类**：属**集成与版本边界**，不是真冲突。
- **采纳处置**：**同一套测试中明确两种能力集合**（白板工具关闭 14 / 启用 16）；三个套件验证**精确名称集合、无重复、schema 及可调用性**，**数量作为派生检查**。
- **关键**：测试预期须来自**已批准的公共工具清单**，**不能从被测注册结果自动生成**（否则自证正确）。
- **Phase 6 的 `kind` 参数即使不增工具数也改 schema** ⇒ 必须保留旧调用兼容测试（`memory_log_pre({note,date})` 仍有效）。
- **独立立项 + 回归窗口是对的，但不能代替最终集成回归**。

### 3.7 Q7（遗漏）—— 采纳，四处"不能靠沿用解决"

GPT 指出调和的主要损失不是"整章被删"，而是**"沿用"掩盖了编号归属变化、接口前提变化和回滚矛盾**。四处必须处理：

| # | 问题 | 处置 |
|---|---|---|
| **1** | **Phase 6 并非整体无依赖** | **采纳拆分**：**6A 表达 + 既有节奏接线**（紧随 P0）；**6B 分类持久化、规则修改撤回、跨窗口失效**（接在 P1 上）。"不必等检索算法，但不能绕过状态提交" |
| **2** | **T7-4 关键词强制升级不可原样实现** | **采纳，且这是对我方提案的纠正**。反例：`"文档写着必须重启"`、`"曾经要求必须 X 但已取消"` 也会命中。改为：**只有明确规则来源才进规则层；普通资料命中关键词只产生「待确认候选」，不自动取得不可裁地位**。若选无条件升级，必须接受规则膨胀、旧规则复活、引用内容被当指令的误报 |
| **3** | **规则真源漂移** | 见 §2.3 |
| **4** | **新旧开关不能撤掉共同保护** | **采纳**：「共同状态、版本、用户区和提交保护放在分支外；旧算法仍可选」；`criteriaGate=false` 与"硬失败仍照写"**不得成为共同保护的旁路**；写入保护故障回**只读**，不回到覆盖丢卡的旧写法 |

**新增断言**：T7-4 修订（引文/历史描述/已撤回规则即使含"必须"也不得自动升级）、**T7-7 行为验收**（除检查规则在最终请求内，还要检查**可机械判断的执行结果**是否满足规则 —— 仅引导语不同不能判"遵守问题已解决"）、T6-6 扩展（新旧模式分别提交丢卡/撤回/旧版本/用户区改动，共同保护均须生效）、**规则迁移断言**、**原文能力补钉**（`readVerifiedMemoryChunkPre` 须核对 UTF-8 区间、行号、digest —— 原 50 条里缺少直接的块保真断言）。

---

## 4. 原 50 条断言与 U1–U7 的完整去向

GPT 给出了**逐条去向表**（50 条 + 7 项 U），我方**全部采纳**，不在此重复（见其原文）。三条元规则必须记牢：

1. **"保留"表示仍须施工和验收，不表示已通过。**
2. **T0-8 与 T1-7 是迁移或扩展，不应把同一个保障重复计数。**
3. **新 Phase 编号与旧 T 编号分开记录** —— 避免把 `phase6` 测试文件误认成规则层测试。

---

## 5. 采纳后的开工顺序（GPT 给出，我方认可）

```
P0 与白板最小适配边界  →  P6A  →  P1 与 P6B  →  P2  →  P3  →  P4  →  P5
        （完整白板线独立推进，在"共同接口"与"最终集成回归"两处汇合）
```

**与总纲 v1 的两处差别**：
- v1 把 Phase 6 整体排在 Phase 0 之后；**v2 拆成 6A（紧随 P0）/ 6B（接在 P1 上）** —— 因为 6B 的分类持久化**不能绕过状态提交**。
- v1 把白板线整体排在外面；**v2 要求 P0 与"白板最小适配边界"同时做** —— 因为写入门要保护卡片集合与用户区，**没有适配器就是"接口接上了但保护失效"**。

---

## 6. 未采纳 / 保留意见

**暂无驳回项。** GPT 本轮的全部修正均在核实后成立或属设计判断上的更优选择。

**唯一需要用户裁决的一处**：§3.3 的 Q3b，GPT 选 (ii) 并给出 `Bo` 派生初值方案。用户此前说"偏向 GPT 决定、我方审" —— 我方审核结论：**通过**。理由：它同时满足用户两条约束（预留数字由实际内容决定，不设魔数；不新增调度/分配框架），且明确说出了代价（不再承诺固定总量上限）。

---

## 7. 本次核实的方法纪律（留痕）

- 凡 GPT 断言"代码现状"，**一律打开文件读当前字节**，不采信转述（V1–V6 六条全部实读验证）。
- 凡我方判定与它冲突，**先假定我方错**，再去找反证 —— 本轮正是这样发现 §2.1 的归因错误。
- 凡"看起来合理"的设计修正，**不因它合理就跳过核实**（如 §3.3 的 `0` 回退问题，实读 `:7455` 确认）。
