# G3 · 磁盘表示缺口与形态设计（2026-09-19）

> **本文档的地位**：`G3-DESIGN-20260918.md` / `MEMORY-GOVERNANCE-20260917.md` §8 的**实施前补丁**。
> 它不推翻任何已拍板项，只补一处**设计稿断言与实机不符**的地方，并提出形态方案。
> **触发**：R4-A 落地后按 PRE-FRONTEND-CHECKLIST 执行序推进 ⑥ G3 时的实施前核查。

---

## 1. 一句话

设计稿 §8.1 断言「**地基已经存在，只差写入方**」。核查结论：
**该断言只对「内存索引列」成立；「`status` 在磁盘上怎么表示」这一层完全不存在，且没有现成载体。**

⇒ G3 不是「接一根线」，而是**必须先定一个磁盘形态**。这是本文档要解决的问题。

---

## 2. 证据链（可复现）

| # | 断言 | 证据 | 结论 |
|---|---|---|---|
| 1 | 锚点语法**不容纳属性** | `lib/memory-anchor-pre.js:28` `MARKER_RE = /^<!-- memory:(mem_[0-9a-f]{32}) -->$/` | 严格匹配，加 `status=…` 即不匹配 |
| 2 | 非法锚点行 ⇒ **冲突**（非静默忽略） | `memory-anchor-pre.js:214-217`：行首 `startsWith(MARKER_OPEN)` 但 `!MARKER_RE.test` ⇒ `conflicts.push({type:'malformed-anchor'})` | 不是"忽略"，是**记冲突** |
| 3 | 冲突 ⇒ 该文件**写入被拒** | `storage-manage-pre.js:181-182`：`parsed.status !== 'clean'` ⇒ `{ok:false, reason:'conflict:'+types}` | 改锚点语法 = 破坏写入通道 |
| 4 | **零处**从磁盘读 `status` | `grep statusOf(` 全仓仅命中 `l0-extract-pre.js` 自身（定义 + 调用）；无任何调用方传该参数 | 读取侧没有生产者 |
| 5 | 现存两处 `status: 'current'` 是**硬编码** | `tier0-catalog-pre.js:296`（注入目录条目）、`wb-contract-pre.js:186`（白板卡片） | 均为**运行期对象**，不落盘 |
| 6 | sidecar **不能**承载 | `memory-writer-pre.js:434` 注释：「sidecar 是**可重建的派生数据**」；`storage-manage-pre.js:26` 把 `stale-source` 列为**可自愈 rebuild** 分类 | 正文档 digest 一变 ⇒ sidecar 重建 ⇒ status 丢失 |
| 7 | 真机 MEMORY.md 无状态字段 | 实测 `~/.dsh/memory/workspaces/--D--dsh-auto-memory--/MEMORY.md`：25 个锚点、23549 字符；`status` 字样出现 6 次**全在正文散文里** | 现状确为「零写入」 |

### 2.1 与设计稿的差异（必须纠正的表述）

`MEMORY-GOVERNANCE-20260917.md` §8.1 的六行表格中：

- 第 1/2/3/4/5 行（`L0_STATUSES` / `L0_LAYERS` / 索引带列 / 状态进指纹 / 缺失即默认）—— **成立**，且是内存索引层的事实。
- 第 6 行「但没有任何地方在写这两个字段」—— **成立**，但**低估了缺口**：不只是「没有写入方」，
  而是**「没有可写入的位置」**。

⇒ 修正表述：**缺口 = 「内存索引有列，磁盘无位」**，不是「有列，差个写入方」。

---

## 3. 三个候选形态（含排除理由）

### 方案 A：锚点行附加属性 —— ❌ **排除**
`<!-- memory:mem_xxx status=superseded -->`
- 撞证据 1/2/3：`MARKER_RE` 不匹配 ⇒ `malformed-anchor` 冲突 ⇒ 文件写入被永久拒绝。
- 即便同步改 `MARKER_RE`，`checkReservedSyntaxInContent`（`memory-anchor-pre.js:57`）用**同一个** `MARKER_OPEN` 判据，
  且 `parseAnchors` 已被 40 个套件依赖 —— 属**契约级改动**，风险与收益不成比例。
- **结论：不动锚点语法。**（锚点是 issue #54 的核心防线：一旦漂移，整份文件永久拒写。）

### 方案 B：独立状态文件（如 `.dsh-memory/status.json`）—— ❌ **排除**
- 直接违反 S10.4「不新建状态源」，设计稿 §3.3 / §8.5-4 明确禁止。

### 方案 C：条目**正文内**保留行 —— ✅ **推荐**
```
<!-- memory:mem_xxx -->
## 2026-09-17
### 某结论
正文……

status: superseded（已被 mem_yyyy 取代）
```
- **合规**：状态仍归**记忆条目自身**，不新建状态源（S10.4 ✅）。
- **零语法风险**：不碰锚点，不触发任何 conflict。
- **用户可读**：MEMORY.md 是明文 Markdown，人打开就能看见。
- **对旧数据零影响**：无状态行的条目解析结果 = `current`（与「缺失即默认」口径一致）。

**★ 关键实现约束（否则会重蹈 M2.5a 的坑）**：状态行**不得放在条目正文开头**。
`extractL0Pre` 规则①取 `## 标题`、规则②取首个 `- ` 条目首句；若状态行在首行且不含 `##`，
会被规则②/③当作正文 ⇒ **L0 变成「⚠已作废…」，污染检索质量**（这正是 M2.5a 修的那类问题）。
⇒ **状态行只允许追加在条目正文末尾**，并在 L0 抽取时视为不可见。

---

## 4. 待用户拍板的两个形态项

设计稿 §7「待确认的两个判据」在 BATTLE-PLAN §7 已拍板（门槛 = **(a) 只认显式声明**）。
本文档新提出的是**另一组**形态问题 —— 它们决定「模型怎么写、用户看到什么」：

| # | 问题 | 候选 | agent 倾向 |
|---|---|---|---|
| **F1** | **状态行的具体语法** | (a) `status: superseded`（YAML 风格单行）<br>(b) 复用 R4-A 标记原文：`⚠已作废（已被 mem_xxx 取代）`<br>(c) 其他 | **(a)** —— 机器可解析且稳定；(b) 是**给人/AI 看的呈现**，把它当磁盘格式会让解析依赖文案，R4-A 改文案就会破坏读取 |
| **F2** | **「显式声明取代」的模型侧写法** | (a) `memory_note_pre` 增 `supersedes: [mem_id]` 参数（结构化）<br>(b) 从 content 正文里正则识别「取代/替换了 mem_xxx」<br>(c) 两者都支持 | **(a) 优先**，理由见下 |

**F2 倾向 (a) 的理由**（与「宁可漏判不可误判」同源）：
- 结构化参数是**明确意图**，误判率≈0；正则识别自然语言**必然**有误判。
- 符合用户裁定的门槛「(a) 只认显式声明」—— 参数即最强的显式。
- (b) 可作**后续增量**（先跑一段看数据），不必首个版本就上。

> ⚠️ **纪律**：按「误判代价不对称」（漏判=维持现状；误判=有效结论被当废纸），
> **F1/F2 未定前不得上线任何自动写入**。判定函数与套件可以先写（纯函数、零副作用），
> 但**写盘接线必须等形态定稿**。
