# Batch A3 Worker B — Local folder Markdown trash lifecycle smoke

## 修改文件

| 文件 | 操作 | 说明 |
| --- | --- | --- |
| `scripts/task475-local-folder-markdown-trash-lifecycle-smoke.js` | 新增 | loose Markdown 页面的 delete/restore/delete+purge 完整生命周期 smoke |
| `design/04-tree-domain/process/4-34-filetree-trash-dual-browser-no-refresh-v1.md` | 修改 | 更新验收矩阵、审计结论、执行记录、边界和完成标准 |

## Smoke 覆盖内容

- **测试对象**：loose Markdown 页面（非 bundle 目录形式），`docs/test-page.md`
- **documentId 格式**：`local-md:docs~2Ftest-page.md`
- **API 路径**：`POST /api/tree/commands` with `sourceKind: "local_folder"`
- **5 个步骤**：
  1. `delete` → 源 `.md` 消失，`.mnote/trash` 出现对应文件，`trash-index.json` 记录 `local-md:*` entry
  2. `restore` → `.md` 回原路径（内容不变），trash 文件消失，index 清理
  3. 再次 `delete` → 同步骤 1，验证可重复
  4. `purge` → 源路径与 trash 路径均不存在，index 清理
  5. 重建后 delete+purge 周期 → `trash-index.json` 仍为合法 JSON

## 当前实现观察

- `trash_local_markdown_page` 响应**不包含** `canonicalCommand` 字段（与 `trash_local_raw_file` / `trash_local_directory` 不同，后两者返回 `"canonicalCommand": "tree.resource.archive"`）
- `restore_local_markdown_page` 响应包含 `previousDocumentId`、`relativePath`、`documentId`
- `purge_local_markdown_page` 响应包含 `ok: true`

## 未覆盖：bundled Markdown

bundled Markdown（页面目录形式，含 `Page.md` + 资源文件）未在 smoke 中覆盖，原因：

- `trash_local_markdown_page` 会将 bundle 目录整体移入 trash（`resourceKind: "markdown_bundle"`），涉及目录遍历和子文件处理
- `restore_local_markdown_bundle` 路径还原有额外逻辑（同名冲突处理等）
- Rust 单测已覆盖：
  - `local_tree_command_manages_loose_markdown_like_regular_file`（loose rename/copy/delete/restore）
  - `local_tree_command_delete_page_bundle_directory_uses_page_trash`（bundle delete/restore）
  - `local_tree_command_delete_local_file_id_uses_trash_index`（local file trash/restore/purge）
  - `local_tree_command_delete_chinese_named_asset_in_subdir_uses_trash_index`（中文资源名 trash 周期）

bundled Markdown case 可在后续独立编写——需构造含 `Page.md` 和附件的页面目录，验证 bundle 整体移入/移出 trash。

## 验证命令和结果

```bash
# 语法检查
node --check scripts/task475-local-folder-markdown-trash-lifecycle-smoke.js   # exit 0

# 运行 smoke（需要 mnote-web 在 3000 运行）
node scripts/task475-local-folder-markdown-trash-lifecycle-smoke.js
# 输出: task475 local folder Markdown trash lifecycle smoke passed
# result: tmp/task475-local-folder-markdown-trash-lifecycle-smoke/result.json
```

## 风险

| 风险 | 说明 |
| --- | --- |
| canonicalCommand 缺失 | loose Markdown 的 `delete`/`restore`/`purge` 响应不含 `canonicalCommand`，与非 md 资源 lifecycle 响应不一致。如果未来统一添加，smoke 需同步更新。 |
| trash 文件名冲突 | `next_available_path` 使用 file_stem_title 生成文件名，同名文件会附加序号（如 `test-page 2.md`）。smoke 中的路径 `trash/test-page.md` 在当前实现下恰好是单文件场景，若后续并行场景新增同名文件会冲突——但 smoke 每次使用独立 temp dir，不受影响。 |
| API 响应结构变动 | smoke 依赖 `result.result?.execution` 的 JSON 路径，若 `json_response` 包装层变动需同步更新。 |
