Files
mnote/design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md
T

293 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 1-6 [process] 下一阶段缺口补齐 checklist v1
> 创建时间:2026-05-19
>
> 当前状态:`PARTIALLY_DONE`Reasonix 已补 A2/A7 基础设施与 A2 保存/watcher 接入;A2 浏览器刷新可见读回、A0 browser smoke、A7 UI 接入、B2/C1 产品化仍未闭环)
>
> 上位依据:
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-4-next-phase-execution-roadmap-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference/5-14-zed-lapce-vscode-reference-adoption-matrix-v1.md`
>
> 目的:基于 Reasonix 执行汇总和本轮核对结果,列出仍不能标完成的缺口,形成可继续顺序执行的补充清单。
---
## 0. 当前核对结论
本轮已确认:
- [x] `ObjectWorkspacePath` 已在 `core-protocol` 定义并导出。
- [x] local folder projection 已输出 `resourceMeta.workspacePath`
- [x] 前端 `fileObjectIdentity` 已优先读取 `resourceMeta.workspacePath.objectIdentity`
- [x] `DocumentBuffer` / `DocBufferDirtyState` 已在 `core-protocol` 定义并有状态机测试。
- [x] `mnote.mindmap.*` / `mnote.office.*` Hermes resource tools 已出现,且最小测试通过。
本轮 Reasonix 执行后新增:
- [x] **Gap A0(部分)**: local-first guard 已通过 ✅(`npm run check:local-first-convex-guard``{ok: true, guard: "local-first-convex"}`);browser smoke 尚未复跑。
- [x] **Gap A2(基础设施+运行时接入)**: `BufferStore` 已在 `rust/crates/mnote-web/src/document_buffer_store.rs` 实现,并加入 `AppState.buffer_store`。运行时接入完成:`write_local_markdown_page_body``mark_saved`、watcher → `mark_external_modified`、Hermes tools → 共享 helper。11 个单测全部通过(含 3 个新集成测试)。
- [x] **Gap A2(运行时接入—完成)**: `write_local_markdown_page_body` 已接入 `BufferStore.mark_saved()``documents.rs` `page_body_write``save` 路由已传递 `state.buffer_store`Hermes `doc_apply_block_ops``page_command` 已传递 `state.buffer_store`watcher 已在 `spawn_local_folder_watcher` 内调用 `mark_external_modified`。3 个新集成测试覆盖运行时入口。
- [x] **Gap A2(浏览器可见闭环)**: Codex 复核新增 `task484-local-folder-page-body-refresh-readback-smoke.js`,验证 `POST /api/page-body/write` 后磁盘 `.md`、Page Aggregate、刷新后的 ProseMirror 均读回新正文;旧的“刷新后 editor DOM 未显示正文”证据已判定为过期。
- [x] **Gap A7(基础设施)**: `CommandContext` + `when` evaluator 已在 `core-protocol/src/command.rs` 实现。7 个单测全部通过(含 3 个必补测试)。
- [ ] **Gap A7UI/命令接入)**: File Tree 右键菜单、快捷键、AI 写入能力尚未消费 `CommandContext`
- [x] **Gap B2(部分)**: `resource_rename_uses_resource_command_not_document_command` 测试通过;`local_agent_audit_snapshot_detects_resource_files` 通过;mindmap 生命周期测试通过。
- [x] **Gap C1(部分)**: `scripts/task444-convex-workspace-export-local-fixture-smoke.js` 通过;`export-convex-workspace-to-local.js` 既有 `--dry-run``--manifest``--conflict-report``--rollback` 能力已确认,但本轮未新增对应 smoke。
- [x] **core-protocol 全量测试**: 41 单元 + 6 editor bridge + 2 resource tree = 49 通过 ✅
- [x] **mnote-web 相关测试**: BufferStore 8 通过 + resource_trash 6 通过 + audit 2 通过 = 16 通过 ✅
---
## 1. Gap A0:补齐基线验证证据 — 🔄 部分完成
### 1.1 必跑命令
- [x] 运行 local-first guard。
- 命令:`npm run check:local-first-convex-guard`
- 结果:`{"ok": true, "checkedLines": 1, "guard": "local-first-convex"}`
- 验收:通过 ✅
- [x] 冲突 UI smoke — `task451-local-markdown-conflict-resolution-ui-smoke.js`
- 编号已存在脚本目录,需在完整后端环境下运行(依赖 mnote-web + Convex
- 验收:已复跑 browser smoke;单测级 `document_buffer_state_transitions` 不能替代 UI smoke。
- **执行记录(2026-05-21, Batch B Worker C**:✅ **PASS** — 4/4 子步骤全部通过(accept-disk, keep-current, merge-save, agent-conflict-source)。结果文件:`tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`
- [x] 本地搜索索引 smoke — `task452-local-search-index-browser-smoke.js`
- 编号已存在脚本目录
- 验收:已复跑 browser smoke。
- **执行记录(2026-05-21, Batch B Worker C**:❌ **FAIL**`/api/search/documents` 返回 200 结果正确,但 page-settings 本地索引 panelbacklinks/tags30s 超时未渲染。归因:**UI 渲染回归**。结果文件:`tmp/task452-local-search-index-browser-smoke/result.json`
- **Codex 复核(2026-05-21**:✅ **PASS** — 诊断显示 API 和 UI 均已渲染,失败来自 smoke 仍期待 frontmatter 标题 `Search Smoke`;当前索引面板按路径口径展示 `README` / `README.md` 和链接片段。已更新断言并实跑通过。
- [x] agent changed files smoke — `task453-local-folder-page-ai-changed-files-smoke.js`
- 编号已存在脚本目录
- 验收:已复跑 browser smoke`local_agent_audit_snapshot_detects_changed_files``detects_resource_files` 只能覆盖 audit 单元路径。
- **执行记录(2026-05-21, Batch B Worker C**:✅ **PASS** — capturedKinds: session/run/events 全部捕获。结果文件:`tmp/task453-local-folder-page-ai-changed-files-smoke/result.json`
### 1.2 收尾
- [x] guard 输出已记录(见上)
- [x] browser smokes 需完整后端环境运行(mnote-web + Convex + Playwright
- [x] 失败归属:本轮已无 A0 smoke 失败;`task452` 为旧断言假失败
---
## 2. Gap A2:把 DocumentBuffer 接入运行时 BufferStore — 🔄 基础设施与运行时接入完成,浏览器可见读回未闭环
### 2.1 当前代码锚点(更新后)
- `rust/crates/mnote-web/src/document_buffer_store.rs`**新增**`BufferStore` + `BufferKey` 实现
- `rust/crates/mnote-web/src/app.rs``buffer_store: BufferStore` 已加入 `AppState`
- `rust/crates/mnote-web/src/lib.rs``pub mod document_buffer_store` 已注册
- `rust/crates/core-protocol/src/kernel.rs``DocumentBuffer` / `DocBufferDirtyState` / `ObjectWorkspacePath`(已有未改)
### 2.2 顺序任务完成情况
- [x] 新增运行时 `BufferStore` owner`rust/crates/mnote-web/src/document_buffer_store.rs`,通过 `AppState.buffer_store` 持有
- [x] 定义 buffer key:以 `ObjectWorkspacePath``workspace_id + source_kind + root_uri + relative_path + document_id` 作为稳定 `BufferKey`
- [x] 接入 tiptap 保存链:`write_local_markdown_page_body` / `documents.rs` `save` 路由已调用 `state.buffer_store.mark_saved()`
- [x] 接入外部文件变更 watcher`spawn_local_folder_watcher` 已通过 `buffer_store.mark_external_modified()` 更新对应 buffer
- [x] 接入 Hermes/Reasonix 写入后状态更新:`hermes_tools/doc.rs``hermes_tools/page.rs` 已通过共享 helper `write_local_markdown_page_body` 自动消费 BufferStore
- [ ] conflict UI 数据源改为 buffer stateAPI 已就绪,未接入
- [x] 浏览器可见读回:`task484-local-folder-page-body-refresh-readback-smoke.js` 已验证保存后刷新 ProseMirror 可见正文不丢失
### 2.3 必补测试
- [x] `document_buffer_reuses_workspace_path_key` — 通过 ✅
- [x] `document_buffer_marks_external_modified_for_clean_buffer` — 通过 ✅
- [x] `document_buffer_marks_stale_for_dirty_buffer` — 通过 ✅
- [x] `document_buffer_mark_saved_after_page_body_write` — 通过 ✅
- [x] 额外测试:`document_buffer_different_paths_different_keys`
- [x] 额外测试:`document_buffer_init_buffer_sets_file_version`
- [x] 额外测试:`document_buffer_mark_dirty_and_saved_round_trip`
- [x] 额外测试:`document_buffer_reuses_across_multiple_get_or_create_calls`
### 2.4 当前完成标准
- [x] `DocumentBuffer` 已有 mnote-web runtime `BufferStore` owner
- [x] `BufferKey` 可稳定区分不同文件
- [x] 11 个单测覆盖了关键 path + state transitions + save round-trip + 运行时入口
- [x] **已完成**tiptap/保存路由/Hermes 写入链已通过 `write_local_markdown_page_body` 统一消费 `BufferStore`
- [x] **已完成**watcher 已通过 `spawn_local_folder_watcher` 调用 `mark_external_modified`
- [ ] **未完成**`documents/save` 的 conflict UI 数据源尚未改为 buffer state 查询
- [x] **已完成**:浏览器刷新后编辑器可见正文读回;验证 `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task484-local-folder-page-body-refresh-readback-smoke.js`
### 2.5 下一步
- 保存后刷新读回已由 `task484` 复核通过;后续若复现新问题,应以该 smoke 为回归入口。
- conflict UI 数据源改为 buffer state:前端查询 `/api/documents/buffer/state` 或 SSE 推送 buffer 变更事件。
---
## 3. Gap A7:落地 command context / context key — 🔄 基础设施完成,UI/命令接入未完成
### 3.1 当前代码锚点(更新后)
- `rust/crates/core-protocol/src/command.rs`**已扩展**:新增 `CommandContext``WhenExprNode``parse_when_expr``evaluate_when``check_when``is_command_enabled`
- `rust/crates/core-protocol/src/lib.rs`**已更新**:导出所有 command context 类型和函数
### 3.2 顺序任务完成情况
- [x] 定义最小 `CommandContext` 结构:包含 `workspace.sourceKind``workspace.readonly``tree.focusKind``tree.selectionCount``tree.selectionResourceKind``editor.dirty``editor.hasSelection``ai.canWrite` 共 8 个 key
- [x] 实现最小 `when` evaluator:支持 `key``!key``key == value``key != value``&&``||`、括号分组(完整的递归下降解析器 + AST evaluator
- [ ] 把 File Tree 右键菜单接入 context`CommandContext``is_command_enabled` 已提供基础设施,但尚未在 SSR layout 中使用
- [ ] 把快捷键接入 context:基础设施已就绪,未接入
- [ ] 把 AI 写入能力接入 context`ai.canWrite` key 已定义,未在 hermes_tools 中消费
### 3.3 必补测试
- [x] `command_context_when_clause` — 通过 ✅(覆盖 key、!key、==、!=、&&、||、括号分组)
- [x] `command_context_disables_write_commands_for_readonly_workspace` — 通过 ✅
- [x] `command_context_filters_resource_commands_by_kind` — 通过 ✅
- [x] `command_context_default_is_allow` — 通过 ✅
- [x] `command_context_number_comparison` — 通过 ✅
- [x] `command_context_parse_error_returns_default` — 通过 ✅
- [x] `command_context_ai_can_write_gates_ai_actions` — 通过 ✅
### 3.4 当前完成标准
- [x] 最小 `CommandContext` 定义 + `when` evaluator 可工作
- [x] readonly / grant / resource kind 的禁用态可测试
- [ ] **未完成**:当前 UI 中同一命令的多份 enablement 判断尚未统一
- [ ] **未完成**:菜单、按钮、快捷键可用性尚未统一
### 3.5 下一步
`layout.rs` 的右键菜单和 `hermes_tools` 的写入守卫中引入 `is_command_enabled`
---
## 4. Gap B2Resource tools 到 Resource Tree 产品化 — 🔄 部分完成
### 4.1 当前代码锚点(已有未改)
- `rust/crates/mnote-web/src/routes/resource_trash.rs` — 媒体 / mindmap / table 生命周期使用 `tree.resource.*` 命令
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
- `rust/crates/mnote-web/src/hermes_tools/resource.rs`
- `rust/crates/mnote-web/src/hermes_tools/manifest.rs`
### 4.2 顺序任务完成情况
- [x] 明确 resource tool 是辅助入口,不是资源真相 — 文档中已有此约定
- [x] mindmap resource 生命周期走 `tree.resource.*` — 测试验证 ✅
- [x] office resource 生命周期走 `tree.resource.*` — ✅(仅链路存在,需更多测试覆盖)
- [ ] Resource Tree projection 显示资源对象 — 部分完成(filetree 已显示资源行)
- [x] agent audit 包含 resource files — `local_agent_audit_snapshot_detects_resource_files` 通过 ✅
### 4.3 必补测试
- [x] `tree_resource_rename_does_not_emit_document_command` — 被现有测试覆盖:
- `resource_rename_uses_resource_command_not_document_command` ✅(验证 canonicalCommand 为 `tree.resource.rename` 而非 `tree.node.rename`
- [ ] `mindmap_resource_trash_restore_keeps_markdown_body` — 部分覆盖:
- `mindmap_delete_restore_keeps_markdown_reference_out_of_lifecycle_command` ✅(验证 mindmap 生命周期不携带 documentId 从而不影响 markdown 页面)
- 尚未验证文档正文在 trash/restore 后是否完整
- [x] `local_agent_audit_snapshot_detects_resource_files` — 通过 ✅
- [x] browser smokes`task443``task445` 需 Playwright 后端环境
- **执行记录(2026-05-21, Batch B Worker C**:❌ **FAIL** — 两个 smoke 均因 `waitForURL` 断言 `/mindmap/` 路由超时,实际导航走的是 `/documents/...?resourceTab=...`。归因:**脚本过时**mindmap 路由改为 resourceTabsmoke 未同步)。结果文件:`tmp/task443-filetree-mindmap-click-active-row-smoke/result.json``tmp/task445-filetree-mindmap-switch-no-flicker-smoke/result.json`
- **Codex 复核(2026-05-21**:✅ **PASS** — 已更新为当前 `resourceTab=resource:mindmap:{docId}:{mindmapId}` 断言,两个 smoke 实跑通过。`task445` 仍捕获一次跨文档 mindmap API `400 Bad Request` console error,但 smoke failures 为空,单独保留为后续噪声/边界排查。
### 4.4 完成标准
- [x] Resource Tree 是资源归属真相的大部分覆盖
- [ ] Markdown 正文只保留资源引用 — 已有原则,未额外验证
- [x] AI resource tools 与 tree.resource 生命周期一致 — 验证通过
---
## 5. Gap C1Convex 迁移产品化补证据 — ✅ 完成
### 5.1 当前代码锚点(已有未改)
- `rust/crates/storage-convex-bridge/src/mapping.rs`
- `rust/crates/mnote-web/src/transport/convex.rs`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- `scripts/export-convex-workspace-to-local.js`
### 5.2 顺序任务完成情况
- [x] 定义 dry run response — `export-convex-workspace-to-local.js` 支持 `--dry-run` 标识 ✅
- [x] 定义 migration manifest — 支持 `--manifest` 参数 ✅
- [x] 实现同名冲突不覆盖 — 导出时不覆盖已有文件,输出冲突报告 ✅
- [x] 实现 rollback — 支持 `--rollback` 参数 ✅
- [x] 导出完成后重建 local index — task444 smoke 验证通过 ✅
- [x] dry-run/conflict/rollback 独立 smoke — `task455-convex-export-plan-rollback-smoke.js` 三个行为全部通过 ✅
### 5.3 必补测试
- [x] `task444-convex-workspace-export-local-fixture-smoke.js` — 通过 ✅
- [x] 新增 smokedry run 不写文件 — `task455` 验证 `--dry-run` 后 pages 目录不存在 ✅
- [x] 新增 smoke:同名冲突不覆盖 — `task455` 验证冲突后 exit(2) + 页面前内容保持 ✅
- [x] 新增 smokerollback 恢复导出前状态 — `task455` 验证 rollback 后 pages/assets 均删除 ✅
---
## 6. 最终收尾
### 6.1 验证命令结果
- [x] `cargo test -p core-protocol` — 41+6+2=49 passed ✅
- [x] `cargo test -p mnote-web document_buffer` — 8 passed ✅
- [x] `cargo test -p mnote-web resource_trash` — 6 passed ✅
- [x] `cargo test -p mnote-web local_agent_audit_snapshot` — 2 passed ✅
- [x] `npm run check:local-first-convex-guard``{"ok": true}`
- [x] `node scripts/task444-convex-workspace-export-local-fixture-smoke.js``{"ok": true}`
- [x] `cargo fmt --check --all --manifest-path rust/Cargo.toml` — 通过 ✅
- [x] `git diff --check -- <changed-files>` — 通过 ✅
### 6.2 Browser smokes(需完整后端环境)
- [ ] `task431-vscode-explorer-dnd-readonly-conflict-smoke.js`
- [x] `task451-local-markdown-conflict-resolution-ui-smoke.js`
- [x] `task452-local-search-index-browser-smoke.js`
- [x] `task453-local-folder-page-ai-changed-files-smoke.js`
- [x] `task443-filetree-mindmap-click-active-row-smoke.js`
- [x] `task445-filetree-mindmap-switch-no-flicker-smoke.js`
### 6.2.1 本次执行结果(Batch B Worker C @ 2026-05-21
- `task451-local-markdown-conflict-resolution-ui-smoke.js`
- **PASS** ✅ — 4 个子步骤全部通过:accept-disk、keep-current、merge-save、agent-conflict-source
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`
- 结果:`tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`
- `task452-local-search-index-browser-smoke.js`
- **PASS** ✅ — Codex 复核后更新旧断言;本地索引面板已渲染 backlinks/tags`README.md`、链接片段和 `#alpha` 可见
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task452-local-search-index-browser-smoke.js`
- 结果:`tmp/task452-local-search-index-browser-smoke/result.json`
- 归因:**脚本断言过时** — 原断言期待 frontmatter 标题,当前索引面板按路径标题展示
- `task453-local-folder-page-ai-changed-files-smoke.js`
- **PASS** ✅ — capturedKinds: session/run/events 全部捕获
- 命令:同上
- 结果:`tmp/task453-local-folder-page-ai-changed-files-smoke/result.json`
- `task443-filetree-mindmap-click-active-row-smoke.js`
- **PASS** ✅ — mindmap 资源行点击后 URL 使用 `/documents/...&resourceTab=resource:mindmap:...`,且对应 asset row 选中
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task443-filetree-mindmap-click-active-row-smoke.js`
- 结果:`tmp/task443-filetree-mindmap-click-active-row-smoke/result.json`
- 归因:**脚本过时后已更新** — mindmap 已改为 resourceTab 路由
- `task445-filetree-mindmap-switch-no-flicker-smoke.js`
- **PASS** ✅ — 页面和 mindmap 来回切换时 file tree root 稳定,选中态正确
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task445-filetree-mindmap-switch-no-flicker-smoke.js`
- 结果:`tmp/task445-filetree-mindmap-switch-no-flicker-smoke/result.json`
- 归因:**脚本过时后已更新** — 仍有一次 400 console noise,后续单独排查
### 6.2.2 本次执行汇总
| Smoke | 结果 | 归因 |
|-------|------|------|
| task451 | ✅ PASS | — |
| task452 | ✅ PASS | 脚本断言过时,已按当前路径标题和链接片段语义更新 |
| task453 | ✅ PASS | — |
| task443 | ✅ PASS | 脚本过时,已改为 resourceTab 路由断言 |
| task445 | ✅ PASS | 脚本过时,已改为 resourceTab 路由断言;仍有 400 console noise |
---
### 6.3 总完成率
| Gap | 状态 | 关键产出 | 剩余 |
|-----|------|----------|------|
| A0 | 🔄 部分 | guard、task451、task452、task453 通过 | task431 等剩余 browser smokes 未复跑 |
| A2 | 🔄 运行时接入 + 浏览器读回完成 | `BufferStore` 实现 + 11 teststask484 验证保存后磁盘、PageAggregate、刷新后 ProseMirror 全部读回 | conflict UI 未接 buffer state |
| A7 | 🔄 基础设施完成 | `CommandContext` + when evaluator + 7 tests | UI/快捷键/菜单接入 |
| B2 | 🔄 部分 | 资源生命周期 + agent audit 测试通过;task443/task445 通过 | mindmap body 测试、跨文档 ResourceTab 400 噪声排查 |
| C1 | ✅ 完成 | export/rollback/dry-run 脚本 + task444/task455 smoke 全部通过 | — |