# 5-15 本地 Markdown 路径身份统一 Checklist v1

> 状态：done
>
> 归档说明（2026-05-21）：本地 Markdown 路径身份统一 checklist 已完成并从 `process/` 归档到 `done/`。

## 背景

当前本地 Markdown 主链正在对齐 VS Code 文件模型：普通 `.md` 文件的运行时身份应来自文件资源地址，而不是 frontmatter 或 MNote 内部稳定 ID。

VS Code 参考：

- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/files/common/fileService.ts`
  - `createFile(resource)` 以 `URI` 创建文件。
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/fileCommands.ts`
  - 新建文件后 `openEditor({ resource: saveUri })`。
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts`
  - `matches()` 使用 `isEqual(otherInput.resource, this.resource)`。
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/editors/fileEditorHandler.ts`
  - working copy 与 editor 是否同一打开项，比较 `workingCopy.resource` 与 `editor.resource`。

本阶段目标不是引入新的稳定语义 ID，而是先消除普通本地 Markdown runtime 中的多 ID 竞争。

## 身份原则

- 普通本地 Markdown 的真实身份是 `sourceKind + rootUri + relativePath`。
- 对外 `documentId` 继续使用路径型短句柄：`local-md:<encoded relative_path>`。
- `mnote_id` 只作为已有 frontmatter 元数据保留，不参与普通本地 Markdown 的 `documentId` 决策。
- `.mnote/page-ids.json` 只允许作为历史兼容/迁移辅助，不参与普通本地 Markdown 的主身份生成。
- 不主动修改、删除或重写用户 Markdown frontmatter。

## 允许修改范围

- `rust/crates/mnote-web/src/routes/local_search_index.rs`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- 必要时更新直接相关测试：
  - `rust/crates/mnote-web/src/routes/search.rs`
  - `rust/crates/mnote-web/src/routes/tree.rs`
  - `rust/crates/mnote-web/src/routes/documents.rs`

不要修改用户数据目录，不要清理仓库中无关脏文件，不要做 git commit。

## P0 Checklist

### 1. 搜索索引 documentId 与树形列表统一

- [ ] `local_search_index.rs` 中 `index_markdown_file()` 永远按 `relative_path` 生成 `local-md:<encoded relative_path>`。
- [ ] frontmatter `mnote_id` 不再生成 `local-mdid:*`。
- [ ] 搜索结果、最近修改、反链接口返回的 Markdown `documentId` 与文件树一致。
- [ ] 含 `mnote_id` 的 Markdown 搜索测试必须断言仍返回路径型 ID，例如 `local-md:docs~2Fchild.md`。

验收：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web search_local_index -- --nocapture
```

### 2. 清理本地 Markdown 身份生成的误导参数

- [ ] `local_markdown_page_id(relative_path, _mnote_id, _metadata)` 不再保留无用参数签名。
- [ ] 普通调用点直接使用 `local_markdown_path_page_id(relative_path)` 或新的清晰命名函数。
- [ ] 如果必须保留 legacy resolver，函数名必须包含 `legacy` / `compat`，并且不能参与普通本地 Markdown 主链。

验收：

```bash
rg -n "local_markdown_page_id\\(|local-mdid|mnote_id|page-ids" rust/crates/mnote-web/src/routes/local_folder_source.rs rust/crates/mnote-web/src/routes/local_search_index.rs
```

期望：

- `local_markdown_page_id(` 不应再出现在普通主链调用中。
- `local_search_index.rs` 不应再出现 `local-mdid` 生成逻辑。
- `mnote_id` 可以继续出现在解析、兼容测试或“保留 frontmatter”测试中，但不能参与普通 `documentId` 生成。

### 3. 不污染用户文件

- [ ] 新建本地 Markdown 不写 `mnote_id` frontmatter。
- [ ] 复制本地 Markdown 不改写 `mnote_id`。
- [ ] 保存已有 Markdown 时保留原 frontmatter，但不新增身份字段。

验收：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_tree_command_local_folder_lifecycle -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_save -- --nocapture
```

## P1 Checklist

### 4. 路径型身份覆盖打开/保存/搜索一致性

- [ ] 同一个含 `mnote_id` 的 `.md` 文件，在文件树、页面 aggregate、搜索索引中返回同一个 `local-md:<encoded relative_path>`。
- [ ] `documents.content.get` / 页面打开不再需要 `local-mdid:*` 才能命中文件。
- [ ] rename/move 后新路径返回新的路径型 `documentId`，旧路径型 ID 不作为稳定页面 ID 保留。

验收：

```bash
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_identity_uses_path_even_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_lifecycle -- --nocapture
```

## 最终验收

```bash
cargo fmt --check --all --manifest-path rust/Cargo.toml
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web search_local_index -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_identity_uses_path_even_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_lifecycle -- --nocapture
```

若涉及浏览器可见行为，Codex 复核阶段必须使用真实浏览器验证并将截图放在 `tmp/` 下。

## 执行状态（2026-05-19）

- [x] P0-1 搜索索引 `documentId` 已统一为 `local-md:<encoded relative_path>`，不再由 frontmatter `mnote_id` 生成 `local-mdid:*`。
- [x] P0-2 普通本地 Markdown 主链已移除 `local_markdown_page_id(relative_path, _mnote_id, _metadata)` 误导签名，文件树、page aggregate、find-by-id 改用路径型 ID。
- [x] P0-3 新建、复制、rename/move/restore/purge 普通本地 Markdown 不再写 `.mnote/page-ids.json`，也不再写入或改写 `mnote_id`。
- [x] P1-4 rename/move 后返回新路径型 `documentId`，旧 ID 不再作为稳定页面 ID 保留。
- [x] 追加清理：删除 `ParsedLocalMarkdownPage.mnote_id` 字段，避免后续代码重新把 frontmatter `mnote_id` 当身份入口。

已通过验收：

```bash
cargo fmt --check --all --manifest-path rust/Cargo.toml
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web search_local_index -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_identity_uses_path_even_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_aggregate_accepts_path_id_when_frontmatter_has_mnote_id -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_id_initialization_returns_path_id_without_writing_page_ids -- --nocapture
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder -- --nocapture
```
