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.
37 KiB
[recycle] 1-3 [process] 当前主线持续推进 checklist v1
创建时间:2026-05-19
当前状态:
DONE上位依据:
/mnt/Data1T/mnote/design/01-05-current-priority-overview.md/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-2-local-first-workspace-convex-control-plane-v1.md/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-3-local-workspace-access-control-productization-v1.md/mnt/Data1T/mnote/ARCHITECTURE.md/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md目标:把
01-05 当前主线与优先级总览转成可持续推进、可验证、可迁移到done/的执行清单。
0. 总原则
- 产品形态固定为
VSCode 简化版工作区内核 + tiptap markdown 编辑器 + Hermes / Reasonix agent + simplemindmap / office 插件 + Wolai 风格 web 壳 + 鉴权控制面。 - 本地文件夹是默认数据真相;Rust kernel 是唯一语义真相。
- Rust SQLite control-plane 承接账号、分享、同步、协作和 AI 隔离默认控制面;Convex 仅保留历史迁移源、显式 cloud source / compat / sync replica 边界。
- AI 默认尽量使用 agent 原生文件读写、diff、patch 能力;MNote 只提供授权 root、页面定位、必要元数据和特殊资源工具。
- 每完成一个阶段后,把对应过程稿移动到该分类
done/,并在本 checklist 写入验证证据。- 本轮说明:
1-3本身仍作为当前持续执行清单保留在process/;已完成的上位 local-first 设计和已完成阶段稿继续放在各自done/,仍处于阶段性收口的 P7 文档保留在process/并在本 checklist 写明证据。
- 本轮说明:
1. P0 管理员目录授权控制面
对应 01-05 执行顺序:1. 管理员目录授权 UI / API。
1.1 后端 API 与权限底座
- 管理员身份支持
MNOTE_ADMIN_USER_IDS。 - access policy 默认路径固定为
/mnt/Data1T/Mnote_data/control-plane/access-policy.json。 - 管理员可读写任意本地目录,普通用户只能访问 owner / grant 授权目录。
- read grant 只能读,write grant 可写。
- 新增
GET /api/admin/access-policy。 - 新增
POST /api/admin/access-policy/validate-root。 - 新增
POST /api/admin/access-policy/grants。 - 新增
DELETE /api/admin/access-policy/grants/{grantId}。 - 全入口权限审计完成:local folder、page body、tree command、Hermes / Reasonix、shared AI session。
验证证据:
cargo test -p mnote-web local_access_policy -- --nocapturecargo test -p mnote-web local_workspace_access -- --nocapturecargo test -p mnote-web local_folder -- --nocapturecargo test -p mnote-web hermes_client_local_acp -- --nocapturecargo test -p mnote-web hermes_tools_markdown_edit_shared_read_is_forbidden -- --nocapture
1.2 管理员 UI
目标:让管理员不用手写 JSON 就能管理用户目录授权。
- 定位当前 Rust SSR 设置 / 管理入口,确认管理员页面应挂在
rust/crates/mnote-web/src/ssr/pages/的哪个壳内。 - 增加管理员访问入口:非管理员不可见,管理员可进入本地目录授权管理页。
- 实现:
/admin/access-policy+PageLayout管理员 quick action。
- 实现:
- 增加 policy 列表:显示
policyPath、env admins、policy admins、grant 列表。- 实现:
rust/crates/mnote-web/src/ssr/pages/admin.rs调用GET /api/admin/access-policy并展示 JSON。
- 实现:
- 增加 validate root 表单:输入
rootPath或rootUri后显示 canonical path / rootUri。 - 增加 create grant 表单:
userId、permission、recursive、capabilities。 - 增加 delete grant 操作:删除前显示授权目录和用户,删除后刷新列表。
- 增加 UI 错误态:未登录、非管理员、目录不存在、重复授权、无效 capability。
- 实现:页面展示 API 错误;SSR route 对非管理员返回 403;API 保持后端错误码。
- 补管理员 UI smoke:真实登录管理员可新增 read grant,普通用户可读不可写。
- 验证:
node scripts/task450-admin-access-policy-ui-smoke.js。
- 验证:
- 补普通用户 smoke:普通用户无法打开管理员授权页,直接调用 API 返回 403。
- 验证:
cargo test -p mnote-web admin_access_policy -- --nocapture。
- 验证:
建议验证:
cargo test -p mnote-web local_access_policy -- --nocapturecargo test -p mnote-web local_workspace_access -- --nocapturenode scripts/task450-admin-access-policy-ui-smoke.js
2. P1 VSCode-like 冲突处理 UI
对应 01-05 执行顺序:2. VSCode-like 冲突处理 UI。
目标:tiptap 前端保存与 agent 后台写文件同时发生时,用户可以像 VSCode 一样看见冲突、选择磁盘版本、保留编辑器版本或打开 diff 合并。
2.1 冲突模型收口
- 盘点现有
fileVersion/conflictDetectionKey的生成、传递和校验路径。- 结论:本地 markdown aggregate 已暴露
fileVersionalias;documents/savecompat 会把expectedFileVersion/conflictDetectionKey收敛到PageBodyWriteRequest.expected_file_version;/api/page-body/write只接受expectedFileVersion,避免 alias 重复。
- 结论:本地 markdown aggregate 已暴露
- 确认所有本地 markdown 写入口都携带 expected file version:tiptap 保存、
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。
- 实现:tiptap local-folder 保存改走
- 统一冲突错误 envelope:错误码、当前磁盘版本、编辑器基线版本、documentId、rootUri、建议动作。
- 实现:本地 markdown stale 写入返回
details.conflict,包含code、currentDiskVersion、editorBaseVersion、documentId、rootUri、suggestedActions。
- 实现:本地 markdown stale 写入返回
- 让冲突错误不丢失当前编辑器内容,前端可以继续保留未保存 buffer。
- 实现:冲突态优先从当前挂载的 ProseMirror DOM 捕获文本;“保留当前编辑器版本”会用最新
fileVersion重新提交当前 buffer。
- 实现:冲突态优先从当前挂载的 ProseMirror DOM 捕获文本;“保留当前编辑器版本”会用最新
2.2 冲突交互
- 设计冲突 modal / side panel:显示当前编辑器版本、磁盘版本、文件路径、最后修改来源。
- 实现:文档页内嵌
mnote-editor-conflict-panel,展示文件标识、来源为本地文件变更,并提供磁盘 / 当前 / diff 三个动作。
- 实现:文档页内嵌
- 实现“接受磁盘版本”:重新读取文件,替换编辑器 buffer。
- 实现“保留编辑器版本”:用最新 fileVersion 重新提交当前编辑器内容。
- 实现“打开 diff”:支持 markdown 文本并排查看;复杂块结构先降级为 markdown 文本。
- 实现“合并”:支持从 diff 面板选择合并结果并写回。
- 实现:冲突 diff 面板新增合并 textarea,可选择当前版本 / 磁盘版本作为合并基础,并写回合并结果。
- 合并完成后写回本地 markdown,并刷新 page aggregate / file tree snapshot。
- 验证:
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js覆盖合并写回,文件内容包含当前编辑器内容、磁盘内容和新增合并内容。
- 验证:
- AI 写入导致冲突时,提示来源为 agent run,而不是普通外部修改。
- 实现:页面收到
mnote:page-ai-tool-write-completed/agent.changed_files后记录 agent run 来源;冲突面板来源显示为agent run <runId>。
- 实现:页面收到
建议验证:
- 新增单测:stale
expectedFileVersion被拒绝。- 验证:
cargo test -p mnote-web local_folder_documents_save_rejects_stale_expected_file_version -- --nocapture
- 验证:
- 新增 browser smoke:浏览器打开页面后外部修改同一
.md文件,保存时出现冲突 UI。- 验证:
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js
- 验证:
- 新增 browser smoke:agent 修改同一
.md文件后,tiptap 保存触发冲突 UI。- 验证:
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js覆盖 agent 修改同一.md后的冲突 UI,并断言面板出现agent run <runId>。
- 验证:
补充验证:
cargo fmt --check --all --manifest-path rust/Cargo.tomlcargo test -p mnote-web document_shell_renders_local_markdown_with_same_sidebar_surfaces -- --nocapturecargo test -p mnote-web local_folder -- --nocapturenpm run check:local-first-convex-guard
3. P2 Agent changed files / diff 审计
对应 01-05 执行顺序:3. agent changed files / diff 审计。
目标:agent 可以像在 VSCode 里一样直接改授权目录文件,但 MNote 要记录“谁、在哪个 root、通过哪个 run、改了哪些文件、diff 摘要是什么”。
3.1 审计事件模型
- 设计本地审计目录:建议放在
/mnt/Data1T/Mnote_data/control-plane/agent-audit/。 - 定义审计事件 JSONL 字段:
eventId、actorId、agentKind、runId、rootUri、permission、changedFiles、diffSummary、createdAt。 - 区分 agent 原生文件修改与 MNote tool 写入:二者都要能归入同一个 run audit。
- 实现:agent 原生文件修改继续由 run 前后 root snapshot 生成
changedFiles;MNote tool 本地成功写入额外追加origin=mnote_tool的同runIdaudit event,只读拒绝追加writeAttemptRejected=true的同runIdaudit event。
- 实现:agent 原生文件修改继续由 run 前后 root snapshot 生成
- 对只读 grant 的 agent run 写入尝试记录拒绝事件。
- 实现:
mnote.doc.markdown_edit/mnote.page.save/mnote.block.*在read_onlyAI scope 下直接拒绝写入;本地 mnote tool 写入拒绝会按同一runId追加 control-planeagent-audit.jsonl事件,标记writeAttemptRejected=true。
- 实现:
3.2 写入采集
- 在 Hermes / Reasonix run 启动前记录 root snapshot:文件 mtime、size、hash。
- run 结束后对比 root snapshot,生成 changed files。
- 对 markdown 文件生成简短 diff summary;大文件只记录 hash / size / path。
- 把审计事件落盘到 control-plane,不写进用户正文目录。
- 在 AI 会话 UI 显示 changed files 列表,并可展开查看 diff 摘要。
- 实现:
run.completed.agentAudit.changedFiles会进入agent.changed_files工具卡并显示 changed files 与 diff summary。 - 验证:
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task453-local-folder-page-ai-changed-files-smoke.js
- 实现:
建议验证:
- 单测:run 前后文件变化可生成 changed files。
- 验证:
cargo test -p mnote-web local_agent_audit_snapshot_detects_changed_files -- --nocapture
- 验证:
- 单测:只读授权下写入被拒绝并产生拒绝审计事件。
- 验证:
cargo test -p mnote-web hermes_tools_markdown_edit_shared_read -- --nocapture
- 验证:
- 单测:MNote tool 本地写入归入同一 run audit,并标记
origin=mnote_tool。- 验证:
cargo test -p mnote-web hermes_tools_page_save_local_folder_writes_markdown_file -- --nocapture
- 验证:
- browser smoke:AI 修改一篇本地 markdown 后,会话面板显示 changed files。
- 验证:
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task453-local-folder-page-ai-changed-files-smoke.js覆盖本地README.md写入标记后,会话面板显示agent.changed_files、README.md与 diff 摘要。
- 验证:
补充验证:
cargo fmt --check --allcargo test -p mnote-web hermes_client_acp_run_registers_scoped_runtime_record_in_convex -- --nocapture(历史测试名;当前默认运行态记录已由 SQLite control-plane 承接,旧 Convex store 只保留显式 compat 覆盖)cargo test -p mnote-web hermes_client_local_acp -- --nocapturecargo test -p mnote-web document_shell_renders_local_markdown_with_same_sidebar_surfaces -- --nocapture
4. P3 本地搜索、反链和资源引用索引
对应 01-05 执行顺序:4. 本地搜索、反链和资源引用索引。
目标:本地 workspace 不依赖 Convex search 也能搜索正文、查反链、查资源引用和标签。
4.1 索引边界
- 设计本地索引目录:建议放在 workspace
.mnote/index/或/mnt/Data1T/Mnote_data/control-plane/index/,二者职责需明确。- 实现:单 workspace 本地索引落在授权 root 的
.mnote/index/search-index.json;control-plane index 只保留给后续跨 workspace / shared cache 总索引。
- 实现:单 workspace 本地索引落在授权 root 的
- 确认索引只扫描授权 root 内文件,不扫描用户未授权目录。
- 实现:
/api/search/documents的local_folder分支先走ensure_local_workspace_read_access,再对 canonical root 内.md文件重建索引。
- 实现:
- 定义索引输入:markdown 正文、frontmatter、附件引用、mindmap / office resource metadata。
- 实现:输入覆盖 markdown 正文、frontmatter
title/tags/mnote_id、markdown 链接、双链、附件引用、.mindmap.json与 office 文件元数据。 - 验证:
cargo test -p mnote-web local_search_index -- --nocapture
- 实现:输入覆盖 markdown 正文、frontmatter
- 定义索引输出:全文 search、backlinks、resource refs、tags、recent changes。
- 实现:本地索引输出
results与recentChanges,结果包含全文命中、backlinks、resourceRefs、tags、path、resourceType,并可返回 mindmap / office 资源元数据搜索结果。
- 实现:本地索引输出
4.2 索引更新
- 本地文件 watcher 事件触发增量索引。
- 实现:本地 markdown / mindmap / office watcher 事件会调用单文件索引更新
refresh_local_search_index_for_path,避免等到搜索时才重建。 - 验证:
cargo test -p mnote-web watcher_event_refreshes_local_search_index_path -- --nocapture
- 实现:本地 markdown / mindmap / office watcher 事件会调用单文件索引更新
- 手动 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
- 实现:
- 文件移动、重命名、删除、恢复后同步更新索引。
- 实现:本地
tree command成功后统一 best-effort 刷新.mnote/index/search-index.json;验证覆盖 rename 后索引路径更新。 - 验证:
cargo test -p mnote-web local_tree_command_refreshes_search_index_after_rename -- --nocapture
- 实现:本地
- 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
- 实现:
- 索引损坏时可重建,不影响正文文件。
- 实现:本地搜索每次从授权 root 重新生成
.mnote/index/search-index.json,不会读取索引作为正文真相。
- 实现:本地搜索每次从授权 root 重新生成
4.3 搜索体验
- 全局搜索优先搜索当前 workspace 本地索引。
- 实现:页面搜索请求携带
sourceKind/rootUri;local_folder请求直接查本地索引,不先走 Convex runtime。
- 实现:页面搜索请求携带
- 搜索结果显示文件路径、标题、命中片段和资源类型。
- 实现:搜索结果行显示标题、命中片段、路径和
resourceType标识;API 同步返回path/title/snippet/resourceType。
- 实现:搜索结果行显示标题、命中片段、路径和
- 反链面板读取本地索引,不再依赖云端搜索。
- 实现:页面设置 drawer 新增“索引”页签,按当前授权 root 读取
GET /api/search/local-index/backlinks。
- 实现:页面设置 drawer 新增“索引”页签,按当前授权 root 读取
- 标签列表读取本地索引。
- 实现:页面设置 drawer 新增“索引”页签,按当前授权 root 读取
GET /api/search/local-index/tags。
- 实现:页面设置 drawer 新增“索引”页签,按当前授权 root 读取
建议验证:
- 单测:markdown 链接和双链可生成 backlinks。
- 验证:
cargo test -p mnote-web local_search_index -- --nocapture
- 验证:
- 单测:附件 / mindmap / office 引用可生成 resource refs。
- 验证:
cargo test -p mnote-web local_search_index -- --nocapture
- 验证:
- 单测:mindmap / office 原生资源元数据可进入本地索引结果。
- 验证:
cargo test -p mnote-web local_search_index -- --nocapture
- 验证:
- browser smoke:新建页面后立即可搜索,重命名后搜索结果路径更新。
- 验证:
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task452-local-search-index-browser-smoke.js
- 验证:
- browser smoke:页面设置里的本地索引页签可显示反链和标签。
- 验证:
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task452-local-search-index-browser-smoke.js
- 验证:
补充验证:
cargo test -p mnote-web search_local_index_backlinks_and_tags_read_authorized_root -- --nocapture
5. P4 分享与同步闭环
对应 01-05 执行顺序:5. 分享与同步闭环。
目标:个人本地空间默认隔离,显式分享后才产生共享 workspace / shared AI session;权限、同步和协作默认由 Rust SQLite control-plane 承接,Convex 只保留历史迁移源、显式 cloud source / compat / sync replica 边界。
5.1 分享权限
- 定义 share grant 与 local access grant 的关系:share grant 是产品层共享授权,local access grant 是本机文件系统目录授权;分享不自动扩大本机 root 读写权限。
- 实现:
share_grant_admin_api_creates_reads_and_revokes_without_local_access验证 share grant 不授予 target 用户本地 filesystem read/write access。
- 实现:
- 增加 canonical share grant 控制面:默认落在
/mnt/Data1T/Mnote_data/control-plane/share-grants.json,记录shareId、owner、target、root、resource scope、permission、capabilities、生命周期和撤销状态。- 实现:新增
GET/POST/DELETE /api/admin/share-grants;支持MNOTE_SHARE_GRANTS_FILE覆盖测试路径。
- 实现:新增
- share grant 支持
read/write/aicapability,并可映射到AiAccessScope.permissionLevel=shared_read/shared_write。- 实现:Hermes local ACP run 从 canonical share grant 生成
shared_read/shared_write。
- 实现:Hermes local ACP run 从 canonical share grant 生成
- shared AI session 只能访问 share grant 允许的资源;
allowedResourceIds/shareContext必须来自 canonical share grant,不接受浏览器自报扩大范围。- 实现:
hermes_client_run_body_shared_scope_uses_share_grant_resources与acp_runtime_env_limits_shared_scope_to_granted_files覆盖shareContext、allowedResourceIds和文件级 allowed roots。
- 实现:
- 管理员可查看、创建和撤销 share grant;owner 后续可管理自己创建的 share grant。
- 实现:管理员页面新增独立“分享授权”面板,与目录授权面板分离。
5.2 同步缓存
- 设计 shared workspace cache 目录:默认位于
/mnt/Data1T/Mnote_data/shared/workspaces/<share-id>/,workspace 内缓存.mnote/share-cache.json和.mnote/sync-state.json只保存授权、版本、来源和同步状态,不成为第二份正文真相。- 实现:新增
/api/local-folder/shared-cache/record与/api/local-folder/sync/pending-change,均只写.mnotesidecar。
- 实现:新增
- 云端同步到本地 cache 时保留来源、版本、权限和冲突信息:至少记录
shareId、remoteVersion、baseVersion、permission、sourceActor、syncedAt。- 实现:
.mnote/share-cache.json写入mnote.share_cache.v1。
- 实现:
- 本地修改同步回云端前进行权限和版本校验:
shared_read永远不能上传 pending write;shared_write必须校验 share grant 未撤销且 base version 未过期。- 验证:
shared_cache_and_sync_state_record_permissions_and_conflict_report覆盖 shared_read 拒绝 pending write、shared_write 写入 pending change、remote/base version 冲突返回 409。
- 验证:
- 离线期间记录 pending changes,恢复在线后生成同步报告;pending changes 只能来自已有授权 cache,离线不能新增或扩大授权。
- 实现:pending changes 写入
.mnote/sync-state.json,写入前强制校验 canonical share grant。
- 实现:pending changes 写入
5.3 同步冲突报告
- 同步冲突复用 P1 的冲突 UI,并把冲突来源标记为 shared sync 而不是普通外部文件变更。
- 实现:
/api/local-folder/sync/conflict-report生成source=shared_sync、suggestedActions=["accept_local","accept_remote","open_diff_merge"]的报告 envelope,供 P1 diff / merge UI 消费。
- 实现:
- 冲突报告包含本地版本、远端版本、base version、修改 actor、shareId 和 resource id。
- 验证:
shared_cache_and_sync_state_record_permissions_and_conflict_report。
- 验证:
- 管理员或 owner 可导出冲突报告;报告写入 control-plane 或 workspace
.mnote/sync-reports/,不写入正文目录。- 实现:报告写入授权 workspace 的
.mnote/sync-reports/。
- 实现:报告写入授权 workspace 的
建议验证:
- 单测:share read 不允许写入。
- 验证:
cargo test -p mnote-web shared_cache -- --nocapture;cargo test -p mnote-web hermes_client_local_shared -- --nocapture。
- 验证:
- 单测:shared AI session 不扩大 workspace root。
- 验证:
cargo test -p mnote-web acp_runtime_env_limits_shared_scope_to_granted_files -- --nocapture。
- 验证:
- browser smoke:共享页面只读用户无法通过 AI 写入正文。
- 验证:
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task454-shared-read-page-ai-browser-smoke.js。
- 验证:
6. P5 插件资源模型产品化
对应 01-05 执行顺序:6. 插件资源模型产品化。
目标:simplemindmap / office 是 Resource Tree 对象;Markdown 正文只保留链接或嵌入引用,不把资源内容塞回 markdown 正文真相。
6.1 Resource Tree 对象统一
- 盘点 simplemindmap 当前 object identity、保存路径、打开路径。
- 证据:
/mindmap/{documentId}/{mindmapId}对象壳输出resource:mindmap:{documentId}:{mindmapId};mindmap_shell_returns_rust_object_shell_contract;保存仍通过mindmaps.put/mindmap.command.apply兼容链。
- 证据:
- 盘点 office 当前 object identity、保存路径、打开路径。
- 证据:新增
/office/{documentId}/{assetId}对象壳输出resource:onlyoffice:{documentId}:{assetId},内部复用/onlyofficebootstrap;cargo test -p mnote-web onlyoffice_object_shell_exposes_resource_identity -- --nocapture;cargo test -p mnote-web sidebar_tree_runtime_opens_office_assets_through_resource_shell -- --nocapture。
- 证据:新增
- 统一资源创建、重命名、移动、删除、恢复命令到
tree.resource.*。- 实现:上传/创建走
tree.resource.upload或资源对象专用创建事件;附件 rename / move / archive / restore / purge 已输出tree.resource.rename/move/archive/restore/purge;mindmap/table 生命周期输出tree.resource.*。 - 验证:
cargo test -p mnote-web resource_ -- --nocapture;新增resource_move_uses_resource_command_not_document_command。
- 实现:上传/创建走
- filetree 显示资源行,pagetree 只显示页面导航投影。
- 验证:
cargo test -p mnote-web file_tree_projection_ -- --nocapture;cargo test -p bridge-runtime file_tree_projection -- --nocapture。
- 验证:
- Markdown 中插入资源引用时,只写相对链接或嵌入引用。
- 证据:
task169-mindmap-realtime-smoke.js的assertDocumentMindmapBlockUsesReferenceSource验证mindmapId/rootNodeId且不写入data;附件/office 仍通过链接或 asset 引用打开对象壳。
- 证据:
6.2 AI 资源工具
- 设计
mnote.mindmap.*工具:读取结构、增删改节点、移动节点、导出 markdown summary。- 设计:
design/07-ai/reference/7-28-resource-ai-tool-contract-v1.md。
- 设计:
- 设计
mnote.office.*工具:读取文本摘要、写入建议、导出变更摘要;真实编辑优先复用 officecli / OnlyOffice 保存链。- 设计:
design/07-ai/reference/7-28-resource-ai-tool-contract-v1.md。
- 设计:
- AI 资源工具必须走授权 root 和 resource capability。
- 实现:Hermes manifest 已暴露
mnote.mindmap.fetch/mnote.mindmap.apply_ops/mnote.office.fetch_summary/mnote.office.propose_changes;执行侧统一校验rootUri本地授权与allowedResourceIds/ object identity。 - 验证:
cargo test -p mnote-web hermes_tools_resource -- --nocapture;cargo test -p mnote-web hermes_tools_mindmap -- --nocapture;cargo test -p mnote-web hermes_tools_office -- --nocapture;cargo test -p mnote-web hermes_tools_manifest_returns_first_batch_tools -- --nocapture。
- 实现:Hermes manifest 已暴露
- agent changed files 审计包含 mindmap / office 资源文件。
- 验证:
cargo test -p mnote-web local_agent_audit_snapshot_detects -- --nocapture;覆盖.mindmap.json和同大小二进制.docx内容变更。
- 验证:
建议验证:
- 单测:resource rename 不走 document command。
- 验证:
cargo test -p mnote-web resource_rename_uses_resource_command_not_document_command -- --nocapture。
- 验证:
- 单测:mindmap resource 删除 / 恢复不破坏 markdown 页面。
- 验证:
cargo test -p mnote-web mindmap_delete_restore_keeps_markdown_reference_out_of_lifecycle_command -- --nocapture;生命周期命令只返回tree.resource.archive/restore和resourceKind=mindmap,不携带正文documentId写入语义。
- 验证:
- browser smoke:从 markdown 打开 mindmap / office,修改保存后 filetree 和引用状态同步。
- 证据:
node scripts/task456-resource-object-shell-sync-smoke.js。
- 证据:
7. P6 旧 Convex 数据迁移产品化
对应 01-05 执行顺序:7. 旧 Convex 数据迁移产品化。
目标:把旧 Convex workspace 迁移成本变成可视、可回滚、可验证的产品流程。
7.1 导出与备份
- 冻结旧 Web 导出入口设想,不再作为当前落码入口。
- 历史设计:
design/old/03-rust-web/process/3-17-convex-export-web-entry-v1.md;当前 CLI fixture 入口已支持 dry-run / apply / manifest / conflict report / rollback。由于 Convex 默认运行时与根 functions 已退役,Web route 与 admin 面板不再按该旧稿继续落码,未来如需 cloud/import UI 应按 SQLite control-plane 与 local-first 口径新建 checklist。
- 历史设计:
- 导出前创建 manifest,记录迁移计划、目标 root、workspace、操作列表、冲突、created files、backup dir 与索引刷新结果。
- 实现:
scripts/export-convex-workspace-to-local.js --manifest <file>;apply 成功后同时写入<root>/.mnote/migration-manifest.json。
- 实现:
- 导出页面为
.md,资源写为本地文件,附件保持相对路径。- 验证:
node scripts/task444-convex-workspace-export-local-fixture-smoke.js。
- 验证:
- 导出过程记录进度:总页面数、资源数、AI session 数、操作列表、冲突数。
- 实现:manifest 写入
documentCount、resourceCount、aiSessionCount、operations、conflicts。
- 实现:manifest 写入
7.2 冲突与回滚
- 目标 root 已有同名文件时生成冲突报告,不直接覆盖。
- 验证:
node scripts/task455-convex-export-plan-rollback-smoke.js。
- 验证:
- 支持 dry run:只生成迁移计划,不写文件。
- 验证:
node scripts/task455-convex-export-plan-rollback-smoke.js。
- 验证:
- 支持回滚:根据 manifest 删除本次新增文件或恢复备份文件。
- 验证:
node scripts/task455-convex-export-plan-rollback-smoke.js;当前最小闭环覆盖新增文件删除,备份文件恢复按 manifest 字段实现,覆盖写入仍由冲突策略禁止。
- 验证:
- 导出完成后自动跑本地索引重建。
- 实现:CLI apply 写入
.mnote/index/search-index.json,包含导出页面与资源。
- 实现:CLI apply 写入
建议验证:
- 脚本 smoke:Convex fixture 导出到本地 root。
- 验证:
node scripts/task444-convex-workspace-export-local-fixture-smoke.js。
- 验证:
- 脚本 smoke:同名文件冲突时不覆盖。
- 验证:
node scripts/task455-convex-export-plan-rollback-smoke.js。
- 验证:
- 脚本 smoke:回滚后 root 回到导出前状态。
- 验证:
node scripts/task455-convex-export-plan-rollback-smoke.js。
- 验证:
8. P7 Page Aggregate / tree command / realtime 兼容链瘦身
对应 01-05 执行顺序:8. Page Aggregate / tree command / realtime 兼容链继续瘦身。
目标:不是继续扩新功能,而是减少双真相、双命令面、补偿链和旧 Convex runtime fallback。
8.1 Page Aggregate 单一真源
- 盘点
documents.content仍作为正文兼容源的入口。- 证据:
routes/web_shell.rs非 local 分支仍以load_document_meta_result + load_document_content_result拼meta + content给page.aggregate.get;routes/documents.rs仍保留documents.content.getcompat;bridge-runtime仍将CompatMetaContentJoin/ legacydocuments.content投影为blockDocument。
- 证据:
- 让本地
.md与 EditorBlockDocument projection 的读写边界写入 5-5 / 5-6。- 证据:
5-5与5-6已明确 local-first 下.md是正文真相,EditorBlockDocument是 runtime-native projection / cache;写侧长期退出/api/documents/save主路径,收口到带expectedFileVersion的page.body.write/ LocalFS executor。
- 证据:
- 标题、正文、页面设置写入后只通过 page aggregate 刷新 UI。
- 阶段进展:文档壳初始化、外部刷新和冲突比较已优先消费
body.blockDocument/editorDocument,再 fallback 到 legacybody.content。 - 验证:
cargo test -p mnote-web document_shell_returns_page_aggregate_snapshot -- --nocapture;cargo test -p mnote-web page_aggregate -- --nocapture;scripts/task-page-aggregate-refresh-persistence-smoke.js。
- 阶段进展:文档壳初始化、外部刷新和冲突比较已优先消费
- 删除或降级前端手工拼
meta + content的 runtime fallback。- 证据:文档壳初始化与外部刷新优先消费
/api/page-aggregate,前端只在 Page Aggregatebody.blockDocument/editorDocument缺失时把 legacybody.content作为兼容读取;非 local 的后端CompatMetaContentJoin仍作为 cloud / legacy substrate 边界保留,不再描述为前端 runtime 真相。
- 证据:文档壳初始化与外部刷新优先消费
- AI 页面设置写入走统一 page aggregate / page command 入口。
- 证据:Hermes
mnote.page.update_options转page.layout.updateOptions,local-first 分支走update_local_page_options。
- 证据:Hermes
建议验证:
cargo test -p mnote-web page_aggregate -- --nocapture- 验证:7 个测试通过。
- browser smoke:标题、正文、页面设置保存后刷新仍一致。
- 证据:
scripts/task-page-aggregate-refresh-persistence-smoke.js,结果tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.json。
- 证据:
8.2 Tree command cutover
- 盘点仍在 runtime route / adapter / bridge / CLI 中使用的
documents.*命令。- 证据:
routes/mod.rs仍注册/api/documents/*compat;transport/convex.rs仍做documents.*到 legacy Convex validator 的适配;bridge-runtime仍有documents.stats.update、documents.duplicate、documents.template、documents.emptyTrashByWorkspace等历史命名。
- 证据:
- 新增命令统一命名为
tree.*或tree.resource.*。- 证据:
cargo test -p mnote-web tree_command -- --nocapture覆盖tree_commands_prefer_tree_protocol_names_in_command_wire。
- 证据:
- 对历史
documents.*命令只保留 compat adapter,不再扩展新语义。- 证据:
4-6已冻结documents.* -> tree.*兼容映射;cargo test -p mnote-web tree_command -- --nocapture覆盖 alias mapping。
- 证据:
- 资源 rename、移动、删除、恢复补齐
tree.resource.*。- 证据:资源生命周期已输出
tree.resource.rename/move/archive/restore/purge;cargo test -p mnote-web tree_command -- --nocapture覆盖本地 folder asset trash / restore / purge。
- 证据:资源生命周期已输出
建议验证:
cargo test -p mnote-web tree_command -- --nocapture- 验证:15 个测试通过。
- browser smoke:filetree 新建、重命名、移动、删除、恢复不触发页面 reload。
- 证据:
task432-filetree-trash-page-dual-browser-no-refresh-smoke.js、task446-tree-rename-dual-browser-live-smoke.js、task447-tree-move-order-dual-browser-live-smoke.js,证据路径见design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md。
- 证据:
8.3 Tree realtime live cache
- 盘点 Sidebar、page subtree、filetree、preferred snapshot 的数据来源。
- 证据:Sidebar/filetree 已消费 Rust projection 与 live tree events;local folder 仍有
/api/tree/local-folder-watch+ HTML refetch 补偿链;WS 主链已启用/api/realtime/ws,SSE 为 close 后 fallback。
- 证据:Sidebar/filetree 已消费 Rust projection 与 live tree events;local folder 仍有
- 统一 snapshot / delta / resync consumer 到同一 live cache。
- 阶段进展:WS
delta不再无条件resync,仅resync_required/requiresResync时请求服务端 resync,普通 delta 先交给本地 live consumer。 - 验证:
cargo test -p mnote-web tree_live_controller_marks_transport_and_closes_source_on_pagehide -- --nocapture;本地 folder sidebar watch 不再抓整页 HTML,而是复用/api/tree/projections/sidebar与/api/tree/projections/file后交给同一renderSidebarSnapshot/renderFileProjection。
- 阶段进展:WS
- SSE fallback 只作为 WS 不可用时的降级,不作为并行主链。
- 证据:
TREE_LIVE_CONTROLLER_JS在transport=convex-command-log-ws时优先startWithWebSocket,onclose后才startWithSseFallback;cargo test -p mnote-web tree_live_controller_marks_transport_and_closes_source_on_pagehide -- --nocapture。
- 证据:
- 双浏览器文件树操作无刷新同步。
- 证据:
task432页面生命周期、task433file asset、task434mindmap/table、task446rename、task447move、task448resync 已在 3000 主入口通过,详见3-3与4-34。
- 证据:
- 删除不必要的 polling / refetch 补偿链。
- 证据:文档壳 local folder watch 已从整页
text/htmlrefetch 改为 projection API 刷新;cargo test -p mnote-web sidebar_tree_runtime_polls_local_folder_without_browser_reload -- --nocapture。
- 证据:文档壳 local folder watch 已从整页
建议验证:
cargo test -p mnote-web local_folder -- --nocapture- 验证:59 个测试通过。
- 双浏览器 smoke:页面、文件、垃圾箱操作互相同步且不刷新页面。
- 证据:
task432/task433/task434/task446/task447/task448。
- 证据:
9. 每轮推进固定验证包
后续每次持续推进本 checklist,至少执行与改动相关的子集;跨域改动需要执行完整包。
cargo fmt --check --allcargo test -p mnote-web local_folder -- --nocapturecargo test -p mnote-web local_access_policy -- --nocapturecargo test -p mnote-web hermes_client_local_acp -- --nocapturecargo test -p mnote-web tree_command -- --nocapturecargo test -p mnote-web page_aggregate -- --nocapturenpm run check:local-first-convex-guardgit diff --check -- <changed-files>- 影响 UI / 交互时补 browser smoke,并把脚本名写回对应阶段。
10. done 迁移标准
本文件迁入 done/ 前必须满足:
- P0 管理员目录授权 UI / API 完成并有 browser smoke。
- 证据:
node scripts/task450-admin-access-policy-ui-smoke.js;cargo test -p mnote-web admin_access_policy -- --nocapture。
- 证据:
- 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 最小闭环。
- 证据:
cargo test -p mnote-web share_grant -- --nocapture;cargo test -p mnote-web hermes_client_local_shared -- --nocapture;cargo test -p mnote-web shared_cache -- --nocapture;node scripts/task454-shared-read-page-ai-browser-smoke.js。
- 证据:
- P5 simplemindmap / office 资源模型完成 Resource Tree 产品化。
- 证据:资源命令面、资源对象壳、AI resource tools 与 changed-files 审计已闭环;
node scripts/task456-resource-object-shell-sync-smoke.js已通过。
- 证据:资源命令面、资源对象壳、AI resource tools 与 changed-files 审计已闭环;
- P6 Convex 导出到本地 workspace 有 dry run、备份、冲突报告和回滚。
- 证据:
node scripts/task444-convex-workspace-export-local-fixture-smoke.js;node scripts/task455-convex-export-plan-rollback-smoke.js;旧 Web 入口设想已退到design/old/03-rust-web/process/3-17-convex-export-web-entry-v1.md,不作为当前 reference 或实施入口。
- 证据:
- P7 Page Aggregate / tree command / realtime 兼容链完成阶段性瘦身,并把被替代 process 稿移入
old/或done/。- 证据:Page Aggregate refresh、tree command、local folder projection refresh 与 realtime 双浏览器 smoke 均有验证;
5-5、5-6、3-3、4-34仍作为未完全 kernel-native / local-folder no-refresh 深水区过程稿保留在process/,本 checklist 记录阶段性瘦身结果。
- 证据:Page Aggregate refresh、tree command、local folder projection refresh 与 realtime 双浏览器 smoke 均有验证;
01-05-current-priority-overview.md同步更新状态,不再把已完成项描述为当前第一优先级。- 证据:
01-05已把 P0-P6 改为已完成最小闭环,把 P7 改为阶段性瘦身完成,并把下一阶段顺序更新为 WorkspacePath / BufferStore / Page Aggregate kernel-native / command context / live cache。
- 证据: