feat: advance local-first conflict and search indexing

- 补齐本地 markdown 冲突处理与合并写回路径\n- 增加本地搜索索引路由、刷新与 browser smoke\n- 同步更新 current-priority checklist 的阶段进度
This commit is contained in:
lix-2026
2026-05-19 09:38:57 +08:00
parent 8ed594f1c2
commit 1b5d6a2a2d
12 changed files with 1622 additions and 49 deletions
@@ -88,7 +88,8 @@
- 结论:本地 markdown aggregate 已暴露 `fileVersion` alias`documents/save` compat 会把 `expectedFileVersion` / `conflictDetectionKey` 收敛到 `PageBodyWriteRequest.expected_file_version``/api/page-body/write` 只接受 `expectedFileVersion`,避免 alias 重复。
- [x] 确认所有本地 markdown 写入口都携带 expected file versiontiptap 保存、`mnote.doc.markdown_edit``mnote.page.save`、documents compat save。
- 实现:tiptap local-folder 保存改走 `/api/page-body/write` 并只传 `expectedFileVersion``mnote.doc.markdown_edit` 从当前 aggregate 的 `fileVersion` / `conflictDetectionKey` 取 expected version`mnote.page.save` 读取 tool 入参 `expectedFileVersion`documents compat save 继续兼容旧 `conflictDetectionKey`
- [ ] 统一冲突错误 envelope:错误码、当前磁盘版本、编辑器基线版本、documentId、rootUri、建议动作。
- [x] 统一冲突错误 envelope:错误码、当前磁盘版本、编辑器基线版本、documentId、rootUri、建议动作。
- 实现:本地 markdown stale 写入返回 `details.conflict`,包含 `code``currentDiskVersion``editorBaseVersion``documentId``rootUri``suggestedActions`
- [x] 让冲突错误不丢失当前编辑器内容,前端可以继续保留未保存 buffer。
- 实现:冲突态优先从当前挂载的 ProseMirror DOM 捕获文本;“保留当前编辑器版本”会用最新 `fileVersion` 重新提交当前 buffer。
@@ -99,9 +100,12 @@
- [x] 实现“接受磁盘版本”:重新读取文件,替换编辑器 buffer。
- [x] 实现“保留编辑器版本”:用最新 fileVersion 重新提交当前编辑器内容。
- [x] 实现“打开 diff”:支持 markdown 文本并排查看;复杂块结构先降级为 markdown 文本。
- [ ] 实现“合并”:支持从 diff 面板选择合并结果并写回。
- [ ] 合并完成后写回本地 markdown,并刷新 page aggregate / file tree snapshot
- [ ] AI 写入导致冲突时,提示来源为 agent run,而不是普通外部修改
- [x] 实现“合并”:支持从 diff 面板选择合并结果并写回。
- 实现:冲突 diff 面板新增合并 textarea,可选择当前版本 / 磁盘版本作为合并基础,并写回合并结果
- [x] 合并完成后写回本地 markdown,并刷新 page aggregate / file tree snapshot
- 验证:`node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js` 覆盖合并写回,文件内容包含当前编辑器内容、磁盘内容和新增合并内容。
- [x] AI 写入导致冲突时,提示来源为 agent run,而不是普通外部修改。
- 实现:页面收到 `mnote:page-ai-tool-write-completed` / `agent.changed_files` 后记录 agent run 来源;冲突面板来源显示为 `agent run <runId>`
建议验证:
@@ -109,7 +113,8 @@
- 验证:`cargo test -p mnote-web local_folder_documents_save_rejects_stale_expected_file_version -- --nocapture`
- [x] 新增 browser smoke:浏览器打开页面后外部修改同一 `.md` 文件,保存时出现冲突 UI。
- 验证:`node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`
- [ ] 新增 browser smokeagent 修改同一 `.md` 文件后,tiptap 保存触发冲突 UI。
- [x] 新增 browser smokeagent 修改同一 `.md` 文件后,tiptap 保存触发冲突 UI。
- 验证:`node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js` 覆盖 agent 修改同一 `.md` 后的冲突 UI,并断言面板出现 `agent run <runId>`
补充验证:
@@ -173,31 +178,53 @@
### 4.1 索引边界
- [ ] 设计本地索引目录:建议放在 workspace `.mnote/index/``/mnt/Data1T/Mnote_data/control-plane/index/`,二者职责需明确。
- [ ] 确认索引只扫描授权 root 内文件,不扫描用户未授权目录
- [x] 设计本地索引目录:建议放在 workspace `.mnote/index/``/mnt/Data1T/Mnote_data/control-plane/index/`,二者职责需明确。
- 实现:单 workspace 本地索引落在授权 root 的 `.mnote/index/search-index.json`control-plane index 只保留给后续跨 workspace / shared cache 总索引
- [x] 确认索引只扫描授权 root 内文件,不扫描用户未授权目录。
- 实现:`/api/search/documents``local_folder` 分支先走 `ensure_local_workspace_read_access`,再对 canonical root 内 `.md` 文件重建索引。
- [ ] 定义索引输入:markdown 正文、frontmatter、附件引用、mindmap / office resource metadata。
- [ ] 定义索引输出:全文 search、backlinks、resource refs、tags、recent changes
- 已完成最小输入:markdown 正文、frontmatter `title/tags/mnote_id`、markdown 链接、双链、附件 / mindmap / office 文件引用;待补 mindmap / office 原生 metadata 索引
- [x] 定义索引输出:全文 search、backlinks、resource refs、tags、recent changes。
- 实现:本地索引输出 `results``recentChanges`,结果包含全文命中、`backlinks``resourceRefs``tags``path``resourceType`
### 4.2 索引更新
- [ ] 本地文件 watcher 事件触发增量索引。
- [ ] 手动 refresh / resync 触发 root 全量索引。
- [ ] 文件移动、重命名、删除、恢复后同步更新索引
- [ ] AI 写入和 tiptap 保存后更新索引。
- [ ] 索引损坏时可重建,不影响正文文件
- [x] 手动 refresh / resync 触发 root 全量索引。
- 实现:`POST /api/search/local-index/refresh` 会在授权 root 内全量重建 `.mnote/index/search-index.json`
- 验证:`cargo test -p mnote-web search_local_index_refresh_rebuilds_authorized_root -- --nocapture`
- [x] 文件移动、重命名、删除、恢复后同步更新索引
- 实现:本地 `tree command` 成功后统一 best-effort 刷新 `.mnote/index/search-index.json`;验证覆盖 rename 后索引路径更新。
- 验证:`cargo test -p mnote-web local_tree_command_refreshes_search_index_after_rename -- --nocapture`
- [x] AI 写入和 tiptap 保存后更新索引。
- 实现:`save_local_markdown_page` 成功写回后统一 best-effort 刷新本地搜索索引,覆盖 tiptap 保存与 MNote tool 写入兼容链。
- 验证:`cargo test -p mnote-web local_markdown_save_refreshes_search_index_after_write -- --nocapture`
- [x] 索引损坏时可重建,不影响正文文件。
- 实现:本地搜索每次从授权 root 重新生成 `.mnote/index/search-index.json`,不会读取索引作为正文真相。
### 4.3 搜索体验
- [ ] 全局搜索优先搜索当前 workspace 本地索引。
- [ ] 搜索结果显示文件路径、标题、命中片段和资源类型
- [x] 全局搜索优先搜索当前 workspace 本地索引。
- 实现:页面搜索请求携带 `sourceKind/rootUri``local_folder` 请求直接查本地索引,不先走 Convex runtime
- [x] 搜索结果显示文件路径、标题、命中片段和资源类型。
- 实现:搜索结果行显示标题、命中片段、路径和 `resourceType` 标识;API 同步返回 `path/title/snippet/resourceType`
- [ ] 反链面板读取本地索引,不再依赖云端搜索。
- 已完成数据源:`GET /api/search/local-index/backlinks` 从授权 root 本地索引读取反链;待挂接真实反链面板 UI。
- [ ] 标签列表读取本地索引。
- 已完成数据源:`GET /api/search/local-index/tags` 从授权 root 本地索引读取标签聚合;待挂接真实标签列表 UI。
建议验证:
- [ ] 单测:markdown 链接和双链可生成 backlinks。
- [ ] 单测:附件 / mindmap / office 引用可生成 resource refs。
- [ ] browser smoke:新建页面后立即可搜索,重命名后搜索结果路径更新
- [x] 单测:markdown 链接和双链可生成 backlinks。
- 验证:`cargo test -p mnote-web local_search_index -- --nocapture`
- [x] 单测:附件 / mindmap / office 引用可生成 resource refs
- 验证:`cargo test -p mnote-web local_search_index -- --nocapture`
- [x] browser smoke:新建页面后立即可搜索,重命名后搜索结果路径更新。
- 验证:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task452-local-search-index-browser-smoke.js`
补充验证:
- [x] `cargo test -p mnote-web search_local_index_backlinks_and_tags_read_authorized_root -- --nocapture`
---
@@ -358,8 +385,10 @@
本文件迁入 `done/` 前必须满足:
- [ ] P0 管理员目录授权 UI / API 完成并有 browser smoke。
- [ ] P1 冲突处理 UI 完成,并覆盖 tiptap 保存与 agent 写回冲突
- [x] P0 管理员目录授权 UI / API 完成并有 browser smoke。
- 证据:`node scripts/task450-admin-access-policy-ui-smoke.js``cargo test -p mnote-web admin_access_policy -- --nocapture`
- [x] P1 冲突处理 UI 完成,并覆盖 tiptap 保存与 agent 写回冲突。
- 证据:`node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js``cargo test -p mnote-web local_folder_documents_save_rejects_stale_expected_file_version -- --nocapture`
- [ ] P2 agent changed files / diff 审计完成,并能在 AI 会话 UI 查看。
- [ ] P3 本地搜索 / 反链 / 资源引用索引完成最小闭环。
- [ ] P4 分享与同步闭环完成 read / write / ai capability 最小闭环。