Persist PageTree expand state via control-plane view-state and align chevron/DOM with restored expansion; keep Sidex-style shallow page-tree scan and drop the unused recursive scanner that only added cargo noise. Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi into a module package, and retire Hermes/ACP/OpenHub recycle + root harness evidence from the index while gitignoring recycle and local diag dumps. Archive superseded design/bugs docs under old/, point architecture at ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor regressions so the working tree can stay clean.
314 lines
25 KiB
Markdown
314 lines
25 KiB
Markdown
# [recycle] 1-6 [process] 下一阶段缺口补齐 checklist v1
|
||
|
||
> 创建时间:2026-05-19
|
||
>
|
||
> 当前状态:`DONE`(A0/A2/A7/B2/C1 已完成;`task431` 只读 DnD smoke 仅作为可选回归项,不再阻塞本清单归档)
|
||
>
|
||
> 2026-05-22 归档治理补充:上位路线图 `1-4` 与顺序清单 `1-5` 已移动到 `reference/`;本文只保留真实仍需收口的缺口与证据尾项,不再承接完整下一阶段路线图职责。
|
||
>
|
||
> 上位依据:
|
||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/reference/1-5-next-phase-sequential-execution-checklist-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/reference/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"}`);`task451` / `task452` / `task453` 已复跑,旧 `task431` 仅保留为可选回归项。
|
||
- [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 个必补测试)。
|
||
- [x] **Gap A7(UI/命令接入)**: File Tree 右键菜单、Delete/Backspace 快捷键、AI 写入能力已消费 `CommandContext` / `workspace.readonly` / `ai.canWrite`。
|
||
- [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 本地索引 panel(backlinks/tags)30s 超时未渲染。归因:**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`
|
||
- **Codex 复核(2026-05-22)**:✅ **PASS** — 已扩展为 local-first AI 前台同步 smoke;clean path 验证 Hermes `run.completed.agentAudit.changedFiles` 后工具卡、磁盘 `.md`、Page Aggregate、ProseMirror 均出现 marker 且不进冲突态;dirty path 验证同一路径触发 `external-change-conflict`、冲突面板显示 agent run、冲突信封包含 `externalActor / dirtyState / bufferFileVersion`,diff 同时显示本地未保存 token 与 AI 写盘 token。
|
||
|
||
### 1.2 收尾
|
||
|
||
- [x] guard 输出已记录(见上)
|
||
- [x] browser smokes 已完成主路径复跑;`task443` / `task445` 已迁移到 local-first fixture 并作为归档证据。
|
||
- [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
|
||
- [x] conflict UI 数据源改为 buffer state:冲突信封已包含 `externalActor / dirtyState / bufferFileVersion`,`task451` 已覆盖
|
||
- [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`
|
||
- [x] **已完成**:conflict UI 数据源已改为 buffer state 查询;`save_local_markdown_page_with_buffer_store` 在冲突发生时从 BufferStore 查询 `externalActor`、`dirtyState`、`bufferFileVersion` 并加入冲突信封;`/api/documents/buffer-state` 端点提供显式 buffer 状态查询;`document_page_shell` 在打开 local_folder 文档时调用 `init_buffer` 初始化 file_version。task451 smoke 已扩展为在每次冲突后验证信封包含这些 BufferStore 字段。
|
||
- [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 数据源已从纯前端猜测改为 BufferStore 信封 + `/api/documents/buffer-state` 端点。后续若需要实时更新(SSE 推送 buffer 变更事件)可在此基础上扩展。
|
||
|
||
---
|
||
|
||
## 3. Gap A7:落地 command context / context key — ✅ 主路径完成
|
||
|
||
### 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)
|
||
- [x] **File Tree 右键菜单接入 context**(Batch C Worker C):在 SSR `layout.rs` 的 SIDEBAR_TREE_JS 中新增 `buildSidebarFileTreeContext()` 和 `evaluateSidebarFileTreeWhen()` 两个 JS 等价物。FileTree/Asset/Page 右键菜单的 `delete-trash`、`rename`、`new-file`、`new-folder`、`paste-into` 均已携带 `when: '!workspace.readonly'`。菜单构建时评估 when 表达式,命中时设置 `disabled: true` 并调整 title。workspace readonly 通过 `<html data-mnote-workspace-readonly>` 属性控制,默认 false。
|
||
- [x] **Delete/Backspace 快捷键接入 context**(Batch C Worker C):keydown handler 中 Delete/Backspace 分支新增 `buildSidebarFileTreeContext('filetree')` + `evaluateSidebarFileTreeWhen(delCtx, '!workspace.readonly && tree.selectionCount')` 守卫。只读或无选中时阻止动作。
|
||
- [x] 把 AI 写入能力接入 context:`ai.canWrite` key 已定义,并已在 hermes_tools 写入守卫中消费 `args.commandContext` / `args.command_context` 的 `ai.canWrite` 与 `workspace.readonly`。
|
||
|
||
### 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 的禁用态可测试
|
||
- [x] **FileTree 右键菜单已接入**(Batch C Worker C):delete-trash、rename、new-file、new-folder、paste-into 携带 `when: '!workspace.readonly'`
|
||
- [x] **Delete/Backspace 快捷键已接入**(Batch C Worker C):keydown handler 中有 `!workspace.readonly && tree.selectionCount` 守卫
|
||
- [x] 下载、多选删除、粘贴、只读阻断的主路径 smoke 已由 `task471` / `task476` 在 Batch H 复跑覆盖;`task431` 作为更旧 DnD readonly smoke 仍保留为可选回归项。
|
||
- [x] **已完成**:AI 写入能力 (`ai.canWrite`) 已接入 hermes_tools 写入守卫;`mnote.block.*`、`mnote.doc.markdown_edit`、`mnote.page.*`、`mnote.artifact.*`、`mnote.mindmap.apply_ops` 写入链共享 `ensure_write_authorized`,只读 scope、`workspace.readonly=true` 或 `ai.canWrite=false` 均会拒绝写入。
|
||
|
||
### 3.5 下一步
|
||
|
||
- `layout.rs` 右键菜单:已引入 `evaluateSidebarFileTreeWhen`(Batch C Worker C 完成)
|
||
- Delete/Backspace:已接入(Batch C Worker C 完成)
|
||
- hermes_tools 写入守卫:已消费 `ai.canWrite` / `workspace.readonly` command context;后续若前端新增更多 AI 写入口,必须把同一份 `commandContext` 透传到 tool args。
|
||
- 归档前可选补跑 `task431-vscode-explorer-dnd-readonly-conflict-smoke.js`,但它不再代表 A7 主路径缺失。
|
||
|
||
### 3.6 Batch F 复核记录
|
||
|
||
- 2026-05-21:Codex 将 Hermes 写入守卫收口为 `ensure_write_authorized`,`ToolCallInput` 从 `args.commandContext` / `args.command_context` 读取 `ai.canWrite` 与 `workspace.readonly`。
|
||
- 2026-05-21:`mnote.block.*` / `mnote.doc.markdown_edit` 继续经 `block::ensure_write_contract`,该函数已委托共享守卫;`mnote.page.*`、`mnote.artifact.*`、`mnote.mindmap.apply_ops` 也委托共享守卫。
|
||
- 2026-05-21:新增测试覆盖 `ai.canWrite=false` 与 `workspace.readonly=true` 的拒绝路径。
|
||
|
||
---
|
||
|
||
## 4. Gap B2:Resource 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`)
|
||
- [x] `mindmap_resource_trash_restore_keeps_markdown_body` — 已覆盖:
|
||
- `mindmap_delete_restore_keeps_markdown_reference_out_of_lifecycle_command` ✅(验证 mindmap 生命周期不携带 documentId 从而不影响 markdown 页面)
|
||
- `local_folder_mindmap_delete_moves_resource_file_to_trash` ✅(2026-05-22 补断言:archive / restore 后 `Page.md` 正文引用保持原文)
|
||
- [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 路由改为 resourceTab,smoke 未同步)。结果文件:`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 为空,单独保留为后续噪声/边界排查。
|
||
- **Codex 复核(2026-05-22)**:✅ **代码级修复** — `task445` 的 400 不是纯噪声,根因是跨文档后 asset row 打开时沿用了当前 URL 的 document,而不是资源 owner document。已补 `sidebar_filetree_asset_open_uses_owner_document_id` 回归测试,并让 filetree asset row 输出/消费 `data-owner-document-id`;`task445` 同步增加 owner URL 断言。浏览器复跑当前被旧 Convex fixture 阻塞:`workspaces:ensureDefaultWorkspace` function 已退役,返回 502。
|
||
- **Codex 复核(2026-05-22 续)**:✅ **PASS** — `task443` / `task445` 已迁移为 local-first fixture(临时 root + `.mnote/workspace.json` + 本地 `.md` / `.mindmap.json`),不再依赖退役 Convex function。复跑命令:
|
||
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task443-filetree-mindmap-click-active-row-smoke.js`
|
||
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task445-filetree-mindmap-switch-no-flicker-smoke.js`
|
||
- 两者结果均为 PASS,结果文件分别为 `tmp/task443-filetree-mindmap-click-active-row-smoke/result.json`、`tmp/task445-filetree-mindmap-switch-no-flicker-smoke/result.json`。
|
||
|
||
### 4.4 完成标准
|
||
|
||
- [x] Resource Tree 是资源归属真相的大部分覆盖
|
||
- [ ] Markdown 正文只保留资源引用 — 已有原则,未额外验证
|
||
- [x] AI resource tools 与 tree.resource 生命周期一致 — 验证通过
|
||
|
||
---
|
||
|
||
## 5. Gap C1:Convex 迁移产品化补证据 — ✅ 完成
|
||
|
||
### 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] 新增 smoke:dry run 不写文件 — `task455` 验证 `--dry-run` 后 pages 目录不存在 ✅
|
||
- [x] 新增 smoke:同名冲突不覆盖 — `task455` 验证冲突后 exit(2) + 页面前内容保持 ✅
|
||
- [x] 新增 smoke:rollback 恢复导出前状态 — `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** ✅ — clean AI 写入后 Page Aggregate / ProseMirror 前台同步,dirty AI 写入触发 conflict UI 并显示 BufferStore 信封
|
||
- 命令:同上
|
||
- 结果:`tmp/task453-local-folder-page-ai-changed-files-smoke/result.json`
|
||
- `task443-filetree-mindmap-click-active-row-smoke.js`
|
||
- **PASS** ✅ — local-first fixture 下,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`
|
||
- 归因:**脚本过时后已更新并去 Convex fixture 化** — mindmap 已改为 resourceTab 路由,fixture 改为 local_folder
|
||
- `task445-filetree-mindmap-switch-no-flicker-smoke.js`
|
||
- **PASS** ✅ — local-first fixture 下,页面和 mindmap 来回切换时 file tree root 稳定,选中态正确;跨文档资源重新打开时 URL pathname 使用资源 owner document
|
||
- 命令:`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`
|
||
- 归因:**脚本过时后已更新并去 Convex fixture 化** — 400 根因已收口到 ResourceTab owner document URL 同步
|
||
|
||
### 6.2.2 本次执行汇总
|
||
|
||
| Smoke | 结果 | 归因 |
|
||
|-------|------|------|
|
||
| task451 | ✅ PASS | — |
|
||
| task452 | ✅ PASS | 脚本断言过时,已按当前路径标题和链接片段语义更新 |
|
||
| task453 | ✅ PASS | 已扩展为 AI clean/dirty 前台同步与 conflict UI 证据 |
|
||
| task443 | ✅ PASS | 已改为 resourceTab 路由断言,并迁移到 local-first fixture |
|
||
| task445 | ✅ PASS | 已改为 resourceTab 路由断言,并迁移到 local-first fixture;ResourceTab owner document URL 已修复 |
|
||
|
||
---
|
||
|
||
### 6.3 总完成率
|
||
|
||
| Gap | 状态 | 关键产出 | 剩余 |
|
||
|-----|------|----------|------|
|
||
| A0 | ✅ 主路径完成 | guard、task451、task452、task453 通过 | task431 可选复跑 |
|
||
| A2 | ✅ 完成 | `BufferStore` 实现 + 11 tests;task451 覆盖 BufferStore 冲突信封;task484 验证保存后磁盘、PageAggregate、刷新后 ProseMirror 全部读回 | — |
|
||
| A7 | ✅ 主路径完成 | `CommandContext` + when evaluator + 7 tests;FileTree 菜单、Delete/Backspace、AI 写入守卫接入 | task431 可选复跑 |
|
||
| B2 | ✅ 主路径完成 | 资源生命周期 + agent audit 测试通过;mindmap 正文引用保持、ResourceTab owner 修复与 task443/task445 local-first browser 证据已补齐 | Resource Tree projection 产品形态深化另列后续项 |
|
||
| C1 | ✅ 完成 | export/rollback/dry-run 脚本 + task444/task455 smoke 全部通过 | — |
|
||
|
||
归档结论:本清单原本承担的 gap closure 已完成。后续活跃执行转入 `1-8` 的顺序表与 `7-14` 的 AI Markdown 编辑路径收敛,不再把本文件作为 active process。
|