# 白板格式约定（WB-FORMAT-CONVENTION）v1

> **效力**：白板/看板/handoff 账本的**格式与写入契约**。零代码，P0 级。任何改白板结构的实现（`lib/client.js` 渲染、sidecar schema、判据校验）都必须先符合本约定。
> **上位规范**：`SEMANTIC-ARCHITECTURE-SPEC.md` §8 与 **S10.1–S10.6**（wiki 层契约）；接口与预算见 `THREE-LAYER-CONTRACT.md`。
> **边界（沿用既有拍板，不可越）**：**白板不建状态机**（只在"写入"一个门设防）；不搬 dsh-graph 代码（只借范式）；不引入跨目标依赖图；不复制 MRAgent 代码。
> **建立**：2026-09-14（用户批准 S10 后落地）。

---

## 1. 三种页面，三种时态

| 页面 | 时态 | 谁写 | 用途 |
| --- | --- | --- | --- |
| **PLAN 白板** | **现在时**（唯一事实） | 模型维护区 + 用户备注区 | 当前全貌、在做的事、卡在哪 |
| **handoff 账本** | 过去时（append-only 历史） | 模型 | 交接；其"任务状态"段**只指路**，不复制白板状态（消解双状态源） |
| **笔记（MEMORY.md / 日志）** | 累积时（知识） | 模型（自动沉淀） | 长期事实、决策、规则 |

**索引（index）不单独手写**：由上述页面**派生**（S10.2），与检索侧 Tier-0 目录同源。

---

## 2. 锚点契约（S10.1 落地）——**最重要的一条**

**规则**：白板页面里的每一个**卡片/小节**，必须在其标题行下方紧跟一行锚点：

```markdown
### 卡片标题
<!-- memory:mem_<32hex> -->
```

- `id` 必须匹配 `^mem_[0-9a-f]{32}$`（与 L0 抽取的 `MEM_ANCHOR_RE` 完全一致）。
- **id 怎么算（内容寻址，可复算）**：`mem_` + `sha256(workspaceKey + '\u0000' + 页面相对路径 + '\u0000' + 卡片标题)` 的前 32 位十六进制。
  → 卡片**重排/移动不影响 id**；**标题改名 = 新 id**（旧 id 走 supersede 留痕，不得默默消失）。
- **收益**：白板内容凭锚点**自动进入检索语料**（L0 抽取按锚点切条），无需任何新机制；同时白板保持"现在时视图"的定位，状态仍归记忆条目。
- **禁止**：手写裸 `mem_` 前缀的其他形态、复用同一个 id 指两个卡片、把锚点写在卡片正文中间。

---

## 3. 索引派生格式（S10.2）

每条一行，供 Tier-0 目录与人读：

```markdown
- [卡片标题](<页面路径>#mem_<32hex>) — 一句话摘要 · layer=whiteboard · status=current · 2026-09-14
```

派生规则：**同日多卡按白板内顺序**；`status` 取卡片状态（`current` / `superseded` / `retracted`）；缺 `status` 视为 `current`（与 `isCurrentPre` 口径一致）。

---

## 4. 写入门（"只在写入一个门设防"）

只做**一件事**：**重写前后比对卡片集合**。

- 允许：移动、改状态、改正文、加卡。
- 不允许：卡片**凭空消失**。要"消失"必须显式移入 `archived` 集合并留痕（时间 + 原因）。
- 违反 → 拒绝写入并报出差异清单（不是静默接受）。

---

## 5. 人机分区（B4 的解法）

每张卡片分两区，**永不互相覆盖**：

```markdown
### 卡片标题
<!-- memory:mem_<32hex> -->
<!-- model -->
（模型维护：状态、进展、下一步）
<!-- /model -->
<!-- user -->
（你的备注；模型只读不改）
<!-- /user -->
```

模型整篇重写时**必须原样带回 `<!-- user -->` 段**。

---

## 6. lint 清单（S10.3）——试点期用这份逐条查

**零 token 可判的四类**（应做成自动检查，只读 + 留痕，不新增状态）：

1. **孤立条目**：无入站引用（没有任何其他卡片/笔记引用它的 id 或标题）。
2. **陈旧**：`status=superseded|retracted`，或日期超出阈值仍标 `current`。
3. **被提及却无独立卡**：正文反复提到的概念没有自己的卡片。
4. **缺交叉引用**：相关卡片之间没有互链。

**需要 LLM 的一类（必须手动触发，不进自动路径）**：

5. **矛盾检测**：两条卡片给出互相冲突的结论。

> 纪律：lint **只报告**，不自动改。任何自动修正都会把白板变成状态机（违反边界）。

---

## 7. 答案归档回流（S10.5）

一次分析/检索的结论，必须能**一键**沉淀为：① 白板新卡（带锚点），或 ② 记忆条目（`memory_note_pre`），或 ③ handoff 账本一条。

**判据**：任何"只活在对话里"的结论，都算流程不合格——知识复利就断在这里。

---

## 8. 验收清单（能失败）

- [ ] 白板每张卡都有合法锚点（正则校验通过），**且由页面派生出的 index 与 Tier-0 目录条目一致**。
- [ ] 故意删掉一张卡 → 写入被拒并报出差异（**丢卡可检出**）。
- [ ] 卡片重排后 id 不变；改标题后 id 变且旧 id 有 supersede 留痕。
- [ ] 模型整篇重写后，`<!-- user -->` 段逐字节保留。
- [ ] 造一个孤立条目 → lint 报出；造一对矛盾结论 → lint（手动触发时）报出。
- [ ] 上述每一条都在 `tests/smoke/` 有对应套件，且**故意改坏实现时会红**。
