Files
mnote/design/old/10-review/done/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
T
Agent Board b798f628ee chore: land tree view-state, vault, Pi module split, and repo hygiene
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.
2026-07-21 05:13:05 +08:00

47 KiB
Raw Blame History

[recycle] 17 Sidex / MNote Workbench Gap Execution Checklist v1

状态:done

创建时间:2026-05-27

Owner10-review / 05-editor-mainline / 03-rust-web / 04-tree-domain / 07-ai

目标:把本轮 Sidex / VSCode 对照审查转成可执行 checklist,优先补齐 MNote 当前最缺的统一工作台服务层,而不是继续扩散 UI 分支或照搬 VSCode 全量复杂度。

1. 背景结论

MNote 当前已经具备知识库型 VSCode-like workspace 骨架:

  • Rust Web 3000 主壳、SSR 文档页和独立 browser runtime asset。
  • Resource Tree / File Tree / Page Tree 三层模型。
  • local-first Markdown、Page Aggregate、leptos-tiptap island。
  • resource tab、secondary pane、DocumentBuffer、冲突面板。
  • tree live WS/SSE、本地文件夹 watcher、本地搜索索引。
  • Hermes / Reasonix local-first 文件编辑控制面。

相比 Sidex / VSCode,当前最大差距不是某个控件,而是统一服务层仍分散:

  • ObjectWorkspacePath / KernelObjectIdentity 没有成为 browser runtime 的统一身份消费入口。
  • BufferStore 已存在,但 tiptap、watcher、AI、resource tab 仍有各自 session / dirty / conflict 状态。
  • resource tab / open editors 仍是页面内 Map,不是可恢复、可被 Sidebar / 快捷键 / AI target resolver 共同消费的轻量 workbench editor model。
  • command context / when / keybinding / menu enablement 在 Rust core-protocol 与 browser JS 中各有简化版本。
  • tree live 仍有 local-folder SSE、watch batch、projection refresh、polling fallback 多链并存。
  • tree.* 已是 preferred command,但 compat 路径仍保留 documents.* 映射。

2. Sidex 对照边界

2.1 值得借鉴

Sidex / VSCode 模型 MNote 对应改进
ExplorerModel -> ExplorerService -> ExplorerView 三层分工 FileTree / PageTree 继续拆成 projection data source、runtime service、DOM view state,不让 DOM 自己拼真相
IEditorService.openEditor + IEditorGroup 建立轻量 MNoteEditorInput / MNoteEditorGroupState / OpenEditorsSnapshot,统一 page、resource tab、secondary pane
EditorGroupModel 管 active / preview / sticky / transient / MRU / selection resource tab 补 preview / pinned / dirty close veto / MRU / keyboard action 的模型层
ContextKeyService + CommandsRegistry + KeybindingsRegistry MNote 建立统一 command context,用 Rust command contract 驱动 browser menu / shortcut enablement
Working Copy dirty / save / backup / conflict BufferStore 成为 tiptap、watcher、AI、resource tab 的唯一打开态和版本仲裁面
Explorer watcher 节流、编辑中抑制、局部 parent refresh local-folder watcher / filetree projection 只刷新受影响 parent,避免整树替换与编辑冲突

2.2 不照搬

  • 不引入完整 VSCode DI / service container。
  • 不把文件系统 Explorer 当成 MNote 树真相;MNote 真相仍是 Rust kernel projection。
  • 不引入完整 Extension Host、Editor Override、Auxiliary Editor、多窗口 grid。
  • 不把 Monaco TextModel 当成 Markdown 事实源。
  • 不把 Sidex 的 ResourceFileEdit 直接替代 tree.* / tree.resource.* 命令面。
  • terminal / git / debug / tasks / extensions 是否进入 MNote,单独做产品边界决策,不混入本 checklist 的 P0。

3. 执行原则

  • 优先做服务层统一,不做大规模视觉重写。
  • 每一批必须有 smoke 或 Rust/JS 定点测试;浏览器可见能力必须有真实用户路径验证。
  • 不新增第二套树真相、页面真相或资源生命周期真相。
  • 不碰与本 checklist 无关的历史清理、回收区删除或用户已有改动。
  • 对旧 compat 只做可证明的瘦身;不能为了“干净”破坏 cloud / remote / legacy 边界。

4. Checklist

Batch 0:基线取证与任务拆分

状态:done

  • 对照 Sidex Explorer / EditorGroup / command / working copy 模型。
  • 对照 MNote Rust Web / browser runtime 当前能力。
  • 对照现有 design / smoke / process backlog。
  • 确认本轮不做代码清理、不改用户已有脏改动。
  • 输出本执行 checklist,并把后续工作拆成 P0 / P1 / P2。

验收证据:

  • reference-code/sidex-main/src/vs/workbench/contrib/files/common/explorerModel.ts
  • reference-code/sidex-main/src/vs/workbench/contrib/files/browser/explorerService.ts
  • reference-code/sidex-main/src/vs/workbench/common/editor/editorGroupModel.ts
  • rust/crates/core-protocol/src/kernel.rs
  • rust/crates/mnote-web/src/document_buffer_store.rs
  • rust/crates/mnote-web/browser/document-resource-tab-runtime.js
  • rust/crates/mnote-web/browser/tree-live-controller.js

Batch 1:轻量 Workbench Editor Model

状态:done

目标:

  • 定义 MNote 轻量 EditorInput / EditorGroupState / OpenEditorsSnapshot 合同。
  • 统一 page、resource tab、secondary pane 的打开态。
  • 让 Sidebar、快捷键、AI target resolver 和 smoke 都能消费同一个 open editors snapshot。

Sidex 对照:

  • Sidex EditorGroupModel 持有 active editor、preview editor、sticky count、MRU、selection。
  • Sidex Explorer 打开资源统一走 IEditorService.openEditor({ resource, options })
  • MNote 不照搬多 group grid,只保留 primary / secondary pane 和 resource tab 的轻量模型。

待办:

  • 盘点当前 open editor 数据源:page tab、resource tab registry、secondary pane、URL query、window.__mnoteOpenEditorsSnapshot
  • 写一个最小合同文档或 Rust/JS 类型注释,明确字段:
    • objectIdentity
    • workspacePath
    • paneRole
    • editorKind
    • active
    • dirtyState
    • preview
    • pinned
    • lastActiveAt
  • document-resource-tab-runtime.js 的 open editors snapshot 增加 primary/secondary 分组语义。
  • 补 smoke:打开页面、打开 mindmap/office/code/text 资源、切换 tab、刷新后至少能恢复 active page/resource 的可见状态。
  • 补 AI target resolver 输入:last focused editor / resource tab 应来自 open editors snapshot,不再从 DOM 临时猜。

本轮最小实现切片:

  • OpenEditorsSnapshot 保持 mnote.open_editors_snapshot.v1,新增 groups.primary / groups.secondary
  • page editor entry 与 resource editor entry 均输出 paneRoleeditorKindpreviewpinnedlastActiveAt
  • resource tab entry 补 documentId / workspaceId,为后续 ObjectWorkspacePath 消费统一预留字段。
  • page / resource editor entry 补 workspacePath,合同以 mnote.workspace_path.v1 表达 workspace/source/root/relativePath/documentId/objectIdentity。
  • passive resourceOffice/PDF/image 等无 session tab)激活后也写入 lastActiveAt,避免 MRU / AI target resolver 后续只能看到 0。
  • task457-main-editor-resource-tab-smoke 覆盖 primary markdown resource active、primary pinned page、secondary page editor。
  • task457-main-editor-resource-tab-smoke 覆盖 Office passive resource active snapshot 的 documentId / workspaceId / editorKind / lastActiveAt
  • task457-main-editor-resource-tab-smoke 覆盖 canonical resourceTab URL 刷新后恢复 active markdown resource tab,并验证 workspacePath.relativePath
  • sidebar-page-ai-runtime.jsOpenEditorsSnapshot 生成 mnote.ai_editor_target.v1,写入 pageContext.aiContext.activeEditorTargetopenEditorsSnapshot/api/hermes/client/runseditorTarget
  • hermes_client.rs 在 local source instructions 中保留 editorTarget,避免 agent 只靠 URL / DOM 猜当前编辑目标。
  • 同步 task457 中已过期的 Office 默认 iframe 断言:默认主编辑区是 /office-preview 轻量预览,显式 new-window 仍走 /onlyoffice?mode=edit

本轮验证证据:

node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
node --check scripts/task457-main-editor-resource-tab-smoke.js
git diff --check -- design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md rust/crates/mnote-web/browser/document-resource-tab-runtime.js scripts/task457-main-editor-resource-tab-smoke.js
node scripts/task457-main-editor-resource-tab-smoke.js
node --check scripts/task501-office-preview-light-viewer-smoke.js
node scripts/task501-office-preview-light-viewer-smoke.js
node scripts/task490-runtime-surfaces-smoke.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web office_preview_page_serves_lightweight_viewer_shell -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_body_local_source_uses_file_scope_not_full_page_context -- --test-threads=1
node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
node --check scripts/task453-local-folder-page-ai-changed-files-smoke.js
node scripts/task453-local-folder-page-ai-changed-files-smoke.js

建议验证:

node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
node scripts/task496-editor-open-parallel-runtime-aggregate-smoke.js
node scripts/task490-runtime-surfaces-smoke.js

Batch 2ObjectWorkspacePath / Resource Identity runtime 消费统一

状态:done

目标:

  • browser runtime 不再主要依赖 documentId + rootUri + relativePath + assetId 临时拼接身份。
  • FileTree、resource open、copy-id、AI scope、tree command 参数统一消费 ObjectWorkspacePath / KernelObjectIdentity

Sidex 对照:

  • Sidex ExplorerItem 的 identity 基于 root resource + item resource。
  • MNote identity 必须多带 workspaceId/sourceKind/rootUri/relativePath/objectIdentity,不能照搬纯文件 URI。

待办:

  • 盘点 browser runtime 中手拼 identity 的位置:
    • filetree-runtime.js
    • sidebar-filetree-command-runtime.js
    • document-resource-tab-runtime.js
    • resource-open-runtime.js
    • sidebar-filetree-open-runtime.js
  • 为 FileTree row 输出 / 读取建立统一 helper:readWorkspacePathFromRow(row)
  • copy-id 和 resource tab key 优先使用 workspacePath / objectIdentity。
  • local_folder 下绝对路径还原继续从 rootUri + relativePath 得出,但只作为展示/复制结果,不作为内部 identity。
  • 补 smokelocal_folder Markdown、folder、asset、mindmap、office row 的 identity 一致且复制路径不暴露 local:* projection id。

本轮最小实现切片:

  • filetree-runtime.js 新增 readWorkspacePathFromRow(row, deps),统一读取 workspaceId/sourceKind/rootUri/relativePath/objectIdentity/rowId/documentId/assetId/title
  • fileTreeAssetDownloadDetail 与 filetree context menu detail 开始携带 workspacePath,为后续 copy/open/command context 统一消费铺底。
  • task495-filetree-copy-id-absolute-path-smoke 增加 readWorkspacePathFromRow 定点断言,同时保持 local_folder 复制 ID 返回绝对路径。
  • sidebar-tree-runtime.js 的 filetree open / asset open event detail 携带 workspacePathresource open 不再只能从 DOM 临时拼身份。
  • sidebar-filetree-open-runtime.jsworkspacePath.objectIdentity/rootUri/relativePath/assetId 生成 resource tab object identity,覆盖 local file、mindmap、office、pdf 和侧栏打开。
  • document-resource-tab-runtime.js 在 resource tab entry 中保留 workspacePathsnapshot 输出优先沿同一 workspacePath 合同。
  • local_folder_source.rs 的真实 local_folder projection 为 mindmap/office/asset row 写入 workspacePath.objectIdentity.assetId,避免资源行 identity 碰撞。
  • filetree-runtime.js 收窄 isLocalFolder 判定:只信 sourceKind=local_folder,或 sourceKind 缺失时的明确 local row/doc 前缀,避免误伤 cloud/compat id。
  • 移除已退役表格引擎的 legacy 分类分支;表格资源只按当前 table object/icon kind 识别。

本轮验证证据:

node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
node --check rust/crates/mnote-web/browser/filetree-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-filetree-open-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js
node --check scripts/task495-filetree-copy-id-absolute-path-smoke.js
node scripts/task495-filetree-copy-id-absolute-path-smoke.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_file_tree_classifies_mindmap_and_office_assets -- --test-threads=1
node scripts/task457-main-editor-resource-tab-smoke.js
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
node scripts/task490-runtime-surfaces-smoke.js
node scripts/task501-office-preview-light-viewer-smoke.js
git diff --check -- rust/crates/mnote-web/browser/document-resource-tab-runtime.js rust/crates/mnote-web/browser/filetree-runtime.js rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js rust/crates/mnote-web/browser/sidebar-filetree-open-runtime.js rust/crates/mnote-web/browser/sidebar-tree-runtime.js rust/crates/mnote-web/src/routes/local_folder_source.rs scripts/task495-filetree-copy-id-absolute-path-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md

边界记录:

  • node scripts/task456-resource-object-shell-sync-smoke.js 当前走已退役 Convex compat,失败为 503 convex_retired,不作为 local-first Batch 2 验收入口。
  • node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js 暴露上传后 dirty conflict 与删除附件后等待超时,归入 Batch 3/资源生命周期后续处理,不在本 identity 切片扩大修复面。

建议验证:

node --check rust/crates/mnote-web/browser/filetree-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js
node --check scripts/task495-filetree-copy-id-absolute-path-smoke.js
node scripts/task495-filetree-copy-id-absolute-path-smoke.js
node scripts/task456-resource-object-shell-sync-smoke.js
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js

Batch 3BufferStore / Working Copy 唯一打开态

状态:done

目标:

  • BufferStore 成为 tiptap、watcher、AI、resource tab 的统一 dirty / stale / external modified / deleted 仲裁面。
  • browser session 的 dirty/conflict 状态不再独立形成第二套事实。

Sidex 对照:

  • VSCode WorkingCopyService 统一 dirty、save、backup、conflict。
  • MNote 不引入完整 backup service;先把 local-first Markdown 的 dirty/stale/deleted 状态统一到 BufferStore。

待办:

  • 盘点 documentSessionRegistry、localStorage draft、conflictDetectionKeyexpectedFileVersion 当前写读链。
  • 让 watcher 标记外部修改后,前端 session 从 BufferStore / aggregate 读取状态,而不是只靠本地 session flag。
  • 资源 tab close guard 读取统一 dirty state。
  • AI 写入前检查目标 buffer dirty/staledirty 时必须显式确认或阻断。
  • 补 smokeclean buffer 外部写入可同步;dirty buffer 外部写入弹冲突;删除当前文件后 tab 不静默保留旧正文。

本轮最小实现切片:

  • Sidex 对照:借鉴 WorkingCopyService 的统一 dirty/event 仲裁;不引入 VSCode 完整 backup/hot-exit/URI provider。
  • documents.rs 增加 POST /api/documents/buffer-state/dirty,浏览器编辑变 dirty 时写入 Rust BufferStore::mark_dirty,保存成功仍由 Rust 写链 mark_saved 清理。
  • document-session-runtime.js 增加 fetchSessionBufferState() / applyBufferStateToSession(),外部刷新前先读取 /api/documents/buffer-state,对 Dirty/Stale/Deleted 进入冲突/删除保护,不再只靠本地 session flag。
  • queueSessionSave() 在 autosave 排队时同步标记 BufferStore dirty,形成 watcher / AI / save 共享的打开态。
  • clean 外部写入、dirty 外部写入、保存失败保留编辑器内容、AI dirty 写入冲突路径均已通过 smoke。
  • sidebar-page-ai-runtime.js 在发起 Hermes run 前读取 OpenEditorsSnapshot 与 /api/documents/buffer-state,当目标 buffer 为 Dirty / Stale / Deleted / ExternalModified 时阻断 AI 写入,不再让 dirty buffer 进入 agent 写盘后冲突。
  • document-resource-tab-runtime.js 的 page entry 从当前 documentSessionRegistry 输出 dirty state,避免页面刚输入但 /buffer-state/dirty 仍未完成时 Page AI 被放行。
  • document-resource-tab-runtime.js 的 resource tab close 在关闭前 await 读取 BufferStore dirty state,并把 Dirty / Stale / Deleted / ExternalModified 纳入 close veto;本地 resource session 的小写 dirty 同时被 Page AI 阻断逻辑规范化识别。
  • local-upload-runtime.js 上传保存成功后广播 mnote:local-upload-editor-save-completeddocument-session-runtime.js 同步 session fileVersion / conflict key,避免编辑器自己写盘被 watcher 误判为外部冲突。
  • sidebar-attachment-open-runtime.js 为 local_folder 附件链接增加存在性刷新、missing 状态缓存、stat 请求序号和幂等 DOM 写入;删除真实附件后 Markdown 链接保留,点击进入 resource tab missing/error statestat 非 OK 不再伪装成 missing,而是写入诊断属性。

本轮验证证据:

node --check rust/crates/mnote-web/browser/document-session-runtime.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_buffer_state_falls_back_to_document_id_without_relative_path -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web document_buffer_mark_dirty_and_saved_round_trip -- --test-threads=1
node scripts/task436-local-markdown-open-document-external-change-smoke.js
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js
node scripts/task486-local-markdown-save-error-editor-preserves-content-smoke.js
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
node scripts/task457-main-editor-resource-tab-smoke.js
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js

补充验证证据(2026-05-28):

node --check rust/crates/mnote-web/browser/document-session-runtime.js
node --check rust/crates/mnote-web/browser/local-upload-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-attachment-open-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
node --check scripts/task460-resource-tab-close-dirty-smoke.js
node --check scripts/task453-local-folder-page-ai-changed-files-smoke.js
node --check scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
git diff --check -- rust/crates/mnote-web/browser/document-resource-tab-runtime.js rust/crates/mnote-web/browser/document-session-runtime.js rust/crates/mnote-web/browser/local-upload-runtime.js rust/crates/mnote-web/browser/sidebar-attachment-open-runtime.js rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js scripts/task453-local-folder-page-ai-changed-files-smoke.js scripts/task460-resource-tab-close-dirty-smoke.js scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_buffer_state_falls_back_to_document_id_without_relative_path -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web document_buffer_mark_dirty_and_saved_round_trip -- --test-threads=1
node scripts/task436-local-markdown-open-document-external-change-smoke.js
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js
node scripts/task486-local-markdown-save-error-editor-preserves-content-smoke.js
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
node scripts/task460-resource-tab-close-dirty-smoke.js
node scripts/task457-main-editor-resource-tab-smoke.js
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js

边界记录:

  • Sidex / VSCode WorkingCopyService 的统一 dirty / conflict / backup 模型只借鉴为 BufferStore 仲裁面;MNote 暂不引入完整 hot-exit / backup service。
  • Page AI dirty buffer 策略本批选择阻断,不做确认弹窗;后续若需要“显式确认后继续”,应进入 07-ai 单独交互设计。
  • task479 的 broken link 检查在 headless 中显式调用现有 attachment refresh hook,避免浏览器定时器调度导致误判;真实 runtime 仍保留 tree event 与定时刷新。该项只证明 refresh/runtime 行为正确,不单独证明定时器或 tree event 调度可靠,后续 Batch 5 需要补真实 event 链路覆盖。

Batch 4Page Aggregate legacy fallback 退役

状态:done

目标:

  • 继续减少 documents.content / legacy body payload / compat projection 在 runtime 主链中的占比。
  • Page Aggregate 成为页面读取和编辑刷新事实入口。

Sidex 对照:

  • Sidex editor model 有明确输入和持久化模型。
  • MNote 的主模型不是 Monaco TextModel,而是 local Markdown -> Page Aggregate -> tiptap projection。

待办:

  • 盘点 document-tiptap-conversion-runtime.js 中 legacy conversion 的 runtime 调用面。
  • 区分 local-first .md projection、cloud compat projection、fixture/test helper。
  • 为 Page Aggregate 输出增加或验证 source 标记:避免 UI 偏好 / 页面事实 / compat fallback 混淆。
  • 逐步把可控 runtime fallback 改成显式 degraded / compat owner。
  • 补 smoke:刷新后 body/blockDocument/options/source 一致,不能静默读旧 legacy 内容。

本轮最小实现切片:

  • Sidex 对照:借鉴 EditorInput 显式 input/resource/capabilities 边界;MNote 不照搬 EditorResolver,只把页面正文 source 分类写入 runtime session。
  • document-tiptap-conversion-runtime.js 增加 pageBodyTiptapDocumentSource(),把 local_markdown.contentpage_aggregate.block_documentcompat.legacy_contentdegraded.fallback_textempty 显式分类,避免无标记地从 legacy body.content 猜正文来源。
  • document-session-runtime.js 保存 pageBodySource/projectionSource/blockProjectionVersiondocument-editor-adapter-runtime.js 在编辑器 root 上写入 data-mnote-page-body-source / data-mnote-projection-source / data-mnote-block-projection-version
  • task167-local-markdown-title-body-options-no-convex-smoke.js 断言 local-first Page Aggregate 的 projectionSource=local_markdown.contentblockDocument 存在,保存后不退回 legacy documents.content

本轮验证证据:

node --check rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js
node --check rust/crates/mnote-web/browser/document-session-runtime.js
node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js
node --check scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
git diff --check -- rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js rust/crates/mnote-web/browser/document-session-runtime.js rust/crates/mnote-web/browser/document-editor-adapter-runtime.js scripts/task167-local-markdown-title-body-options-no-convex-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
node scripts/task484-local-folder-page-body-refresh-readback-smoke.js
node scripts/task493-page-settings-sqlite-preferences-smoke.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate_endpoint_returns_local_markdown_readonly_snapshot -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_options_metadata_flows_into_page_aggregate -- --test-threads=1

边界记录:

  • node scripts/task-page-aggregate-body-sync-smoke.jsnode scripts/task-page-aggregate-refresh-persistence-smoke.js 当前通过旧 /api/tree/commands 入口创建文档,失败为 503 convex_retired;它们属于 legacy/cloud helper,不作为 local-first Batch 4 验收入口。
  • 本批只把 fallback 分类显式化并锁住 local-first source,不删除 legacy conversion 函数;cloud compat / fixture 仍可显式走 compat.legacy_content

建议验证:

node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
node scripts/task-page-aggregate-body-sync-smoke.js
node scripts/task-page-aggregate-refresh-persistence-smoke.js

Batch 5Tree live cache / FileTree data source 收敛

状态:done

目标:

  • 减少 watcher/SSE/projection refresh/polling 多链并存。
  • FileTree refresh 按受影响 parent 局部刷新,保留展开、选择和焦点。

Sidex 对照:

  • Sidex AsyncDataTree 按 node resolve children,有 in-flight 去重、slow state、view state。
  • MNote 已有 lazy/cache/view state 设计,继续把它固化为 data source 而不是整树 DOM 替换。

待办:

  • 盘点 /api/local-folder/events?treeLive=truetree:deltawatch_batch、projection refresh、polling fallback 的调用关系。
  • 定义统一 local-folder tree event payload 到 filetree parent refresh 的映射。
  • 整树替换只保留在 root scope 切换或 resync 场景。
  • view state 保存只存 expanded/selected/focused 等用户状态,不存 rows/cache 派生数据。
  • 补大目录真实用户路径 smoke:从首页进入本地 workspace,点击星标/Explorer 逐级打开目标 Markdown,记录 longtask 和 editor ready。

本轮最小实现切片:

  • Sidex 对照:借鉴 ExplorerModel / ExplorerService / AsyncDataTree 的节点身份、局部 children resolve、in-flight 去重和 view state 思路;MNote 不照搬 VSCode 完整树控件,仍以 Rust projection / watcher revision 为事实源。
  • sidebar-tree-live-apply-runtime.jswatch_batchfallbackResync/requiresResync 下显式走 refreshLocalFolderSidebarSnapshot(),非 resync 情况只刷新受影响 parent。
  • watch_batch 缺少 affectedParents 但包含 changedPaths 时,从 changed path 派生 parent scope,并写入 data-mnote-local-folder-watch-batch-derived-parents 作为诊断标记。
  • 修复 scoped navigation shell 缺 primary editor host 导致 openPrimaryDocument()pane_root_missing_primary 并回退整页 reload 的问题:HomePage navigation 分支保留导航 placeholder,同时渲染隐藏 primary DocumentPane 供 pane runtime 原地替换。
  • document-editor-adapter-runtime.js 在 primary 文档成功激活后隐藏 navigation placeholder,保持点击 scoped FileTree Markdown 后不重建侧栏、不丢 JS marker。

本轮验证证据:

node --check rust/crates/mnote-web/browser/sidebar-tree-live-apply-runtime.js
node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js
rustfmt --edition 2021 --check rust/crates/mnote-web/src/ssr/pages/home.rs
git diff --check -- rust/crates/mnote-web/browser/sidebar-tree-live-apply-runtime.js rust/crates/mnote-web/browser/document-editor-adapter-runtime.js rust/crates/mnote-web/src/ssr/pages/home.rs scripts/task494-filetree-lazy-loading-dedup-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
node scripts/task494-filetree-lazy-loading-dedup-smoke.js
node scripts/task492-sidebar-starred-shortcuts-smoke.js
node scripts/task497-local-page-tree-filetree-open-performance-smoke.js
node scripts/task499-sidebar-tree-view-state-smoke.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_local_folder_without_page_renders_navigation_page_without_editor_bootstrap -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_local_folder_scope_renders_navigation_without_root_sibling_document -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_active_page_includes_document_panes_bootstrap -- --test-threads=1

边界记录:

  • watch_batch 到 FileTree parent refresh 只消费 Rust local-folder event payload;前端不新增 rows/cache 事实源。
  • view-state smoke 证明持久化内容仍是 expanded / selected / focused / active / scroll 等用户状态,不把 rows 或 projection cache 写入 view state。
  • scoped navigation shell 的 primary DocumentPane 是 editor host 能力补齐,不改变 local_folder navigation page 的 Page Aggregate / editor bootstrap 初始加载边界;导航页仍不输出 __MNOTE_PAGE_AGGREGATE____MNOTE_EDITOR_BOOTSTRAP__

建议验证:

node scripts/task492-sidebar-starred-shortcuts-smoke.js
node scripts/task494-filetree-lazy-loading-dedup-smoke.js
node scripts/task497-local-page-tree-filetree-open-performance-smoke.js
node scripts/task499-sidebar-tree-view-state-smoke.js

Batch 6Command Context / Keybinding / Menu Enablement 统一

状态:done

目标:

  • 统一 readonly、selection、resource kind、dirty、AI write capability、source kind 等上下文。
  • browser menu、快捷键、tree command enablement 消费同一 context,而不是多处硬编码。

Sidex 对照:

  • Sidex 由 ContextKeyService + CommandsRegistry + KeybindingsRegistry 统一驱动。
  • MNote 只做轻量实现,最终执行仍落到 Rust kernel / tree.* command。

待办:

  • 对齐 Rust CommandContext 和 browser when parser 的字段名。
  • 建立 buildFileTreeCommandContext(row, openEditorsSnapshot, bufferState)
  • 右键菜单、键盘动作、toolbar action 统一从 command context 判断 enable/disabled/reason。
  • 为危险动作补 destructive / requiresApproval 元数据。
  • 补 smokereadonly、dirty、folder、asset、markdown、多选下的菜单与快捷键状态一致。

本轮最小实现切片:

  • Sidex 对照:借鉴 ContextKeyService + CommandsRegistry + KeybindingsRegistry 的“同一 command id / 同一 context / 同一 when”模型;MNote 不照搬 VSCode DI、Extension Host、完整 menu contribution 或复杂 when 语法。
  • filetree-context-menu-runtime.js 增加 buildFileTreeCommandContext(row, openEditorsSnapshot, bufferState, deps),统一输出 workspace.sourceKindworkspace.readonlytree.focusKindtree.selectionCounttree.selectionResourceKindtree.targetResourceKindtree.targetIsFoldertree.targetIsAsseteditor.dirtyeditor.dirtyStateeditor.hasSelectionai.canWrite
  • core-protocol::CommandContext 对齐新增 target / dirtyState keys,并补单测锁住 Rust when evaluator 语义。
  • sidebar-filetree-command-runtime.js 菜单打开时从 open editors snapshot / bufferState / row selection 生成统一 command context,并把 context、whendata-disabled-reasondata-destructivedata-requires-approval 暴露到菜单 DOM。
  • filetree-keyboard-runtime.jssidebar-tree-runtime.js inline fallback 的 F2 / Delete / Ctrl+V 使用同一 buildSidebarFileTreeContext + whendirty 目标和 readonly workspace 下不执行写动作。
  • task495-filetree-copy-id-absolute-path-smoke.js 增加 command context 定点断言:markdown dirty target 禁用 rename/delete 类写操作,readonly target 禁用写操作,同时保持 local_folder copy-id 返回绝对路径。

本轮验证证据:

node --check rust/crates/mnote-web/browser/filetree-context-menu-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js
node --check rust/crates/mnote-web/browser/filetree-keyboard-runtime.js
node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js
node --check scripts/task495-filetree-copy-id-absolute-path-smoke.js
rustfmt --edition 2021 --check rust/crates/core-protocol/src/command.rs rust/crates/mnote-web/src/ssr/pages/layout.rs
git diff --check -- rust/crates/mnote-web/browser/filetree-context-menu-runtime.js rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js rust/crates/mnote-web/browser/filetree-keyboard-runtime.js rust/crates/mnote-web/browser/sidebar-tree-runtime.js rust/crates/core-protocol/src/command.rs rust/crates/mnote-web/src/ssr/pages/layout.rs scripts/task495-filetree-copy-id-absolute-path-smoke.js
cargo test --manifest-path rust/Cargo.toml -p core-protocol command_context -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_command_context_helper_functions_exist -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_context_menu_runtime_contains_command_context_helpers -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_context_menu_items_have_when_for_readonly -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_context_evaluator_uses_readonly_and_selection_count_for_delete_key -- --test-threads=1
node scripts/task490-runtime-surfaces-smoke.js
node scripts/task495-filetree-copy-id-absolute-path-smoke.js

边界记录:

  • 本批只统一 enablement / reason / metadata / keyboard gating,不改变最终执行面;写动作仍落到现有 Rust tree.* / tree.resource.* command 路径。
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree -- --test-threads=1 宽过滤额外暴露两个历史/相邻断言失败:sidebar_filetree_blocks_readonly_paste_and_drop_with_action_status 仍期待旧 ensureFileTreeWritableTarget('paste' 字符串,sidebar_filetree_runtime_does_not_keep_retired_table_engine_branches 命中 legacy table 分支;两者未进入本批定点验收,后续如要清理应单独归入 tree/filetree runtime 测试债。
  • node scripts/task476-filetree-editor-context-menu-download-smoke.js 在菜单验证前等待 report-a.pdf asset row 超时;node scripts/task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.js 在 restore 后等待 docs/report.txt row 超时。两者失败点早于 Batch 6 command context enablement,记录为 FileTree projection / restore 可见性后续风险,不作为本批 command context 验收入口。

建议验证:

node scripts/task490-runtime-surfaces-smoke.js
node scripts/task476-filetree-editor-context-menu-download-smoke.js
node scripts/task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.js

Batch 7tree.* / tree.resource.* compat 瘦身

状态:done

目标:

  • tree.* / tree.resource.* 成为正式命令面。
  • documents.* 只保留为显式 compat alias,不继续承载长期语义。

Sidex 对照:

  • Sidex Explorer file actions 统一进入 file operation / bulk edit。
  • MNote 的统一入口应是 Rust kernel tree/resource command,不是直接文件 bulk edit。

待办:

  • 盘点 routes/tree.rsdocuments.* legacy mapping。
  • 标注每个 compat mapping 的 source kind、保留原因和退役条件。
  • local-first 路径禁止静默走 legacy cloud command。
  • resource trash / restore / purge 统一走 tree.resource.*
  • 补 smokecreate/rename/move/delete/restore/purge 在 local_folder 下不触发 legacy owner。

本轮最小实现切片:

  • Sidex 对照:借鉴 file action 明确区分 moveFileToTrash / deleteFile 的语义分叉和回收站 fallbackMNote 保持 archive/restore/purge 三段语义,不照搬 ResourceFileEdit 或 file system-only undo 模型。
  • routes/tree.rs/api/tree/commands 中对缺失 sourceKindrootUrifile:// 的请求推断为 local_folder,避免 local-first 请求静默走 legacy cloud executor。
  • routes/tree.rs 增加 TREE_DOCUMENT_COMPAT_ALIAS_CATALOG,把 documents.* / tree.* 兼容边界、保留原因与退役条件显式化。
  • local_folder 资源 archive / restore / purge smoke 断言 canonicalCommand 分别为 tree.resource.archive / tree.resource.restore / tree.resource.purge
  • 本轮不改 bridge-runtime 旧 compat executor 的退役状态;documents.* 相关 legacy 兼容桥测试属于已退役 cloud 验证面,失败原因是旧 Convex compat bridge 已退役,不作为 local-first Batch 7 主验收入口。

本轮验证证据:

cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_create_rename_copy_trash_restore_and_purge_use_same_endpoint -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_asset_trash_restore_and_purge_use_trash_index -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_documents_compat_alias_catalog_marks_cloud_retirement_boundary -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_local_folder -- --test-threads=1

边界记录:

  • cargo test --manifest-path rust/Cargo.toml -p bridge-runtime documents_lifecycle_aliases_are_marked_as_deprecated_tree_protocol_aliases -- --test-threads=1tree_lifecycle_command_aliases_keep_tree_command_names 当前失败,错误为“旧 Convex 兼容桥已退役;请改用 local-first Rust/SQLite control-plane 路径”。这说明旧 bridge-runtime 兼容桥已不再是 Batch 7 的主线验收目标,不应回拉为当前实现依赖。
  • local-first 请求的 sourceKind 推断只在 rootUrifile:// 时触发,不会把非本地请求静默改成本地。

建议验证:

cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib routes::tree -- --test-threads=1
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
node scripts/task471-local-folder-bulk-resource-trash-smoke.js

Batch 8AI target / changed_files / diff 审计

状态:done

目标:

  • local-first AI 默认走授权文件引用 + agent 原生 patch/diff。
  • MNote 负责 target resolver、allowed roots/files、dirty conflict、changed_files/diff 审计和前台同步。

Sidex 对照:

  • VSCode agent/workspace 模式的价值是目标、权限、working copy、diff 和审计统一。
  • MNote 不继续扩普通 Markdown 的专用 mnote.doc.markdown_edit 主路径。

待办:

  • target chip / picker 使用 open editors snapshot。
  • run 开始后冻结 target 摘要,避免执行中 DOM 焦点变化改目标。
  • changed_files 回收并展示:路径、版本、diff 摘要、actor。
  • dirty buffer 下 agent 写入必须阻断或进入冲突合并。
  • 补 smokeclean 写入同步、dirty 写入阻断、跨 workspace target 不串、changed_files 可见。

本轮最小实现切片:

  • Sidex 对照:借鉴 VSCode/Sidex 的 working copy / SCM / diff 审计分层,把 target、权限、dirty、diff 和 changed files 放在统一 run/audit 边界;MNote 不照搬 SCM provider、QuickDiff provider 或完整 VSCode agent workspace 模型。
  • sidebar-page-ai-runtime.js 的 Page AI target resolver 继续从 OpenEditorsSnapshot 生成 mnote.ai_editor_target.v1;发 run 前新增 workspace/root/source guard,阻断跨 workspace 或陈旧 target。
  • sidebar-page-ai-runtime.js 新增 mnote.page_ai_run_target_snapshot.v1,在 run 发起时冻结 editorTarget/openEditorsSnapshot/contextScope/rootUri/workspaceId/documentId,并写入 /api/hermes/client/runs payload 与 pageContext.aiContext.runTargetSnapshot
  • hermes_client.rs 对 local source 的 runTargetSnapshot 做白名单 sanitization,只保留 target/workspace/open editor 摘要,不把 pageText/pageXml/contextBlocks 等正文塞进本地 agent instructions。
  • agent.changed_files 工具卡显示路径、版本/hash/mtime 摘要、diff 摘要和 actor 信息;前台仍只消费 local agent before/after snapshot,不新增第二套 diff 真相。
  • task453-local-folder-page-ai-changed-files-smoke.js 覆盖 clean 写入同步、dirty buffer 阻断、跨 workspace target 不发 run、changed_files 卡片可见、runTargetSnapshot payload 可见。

本轮验证证据:

node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
node --check scripts/task453-local-folder-page-ai-changed-files-smoke.js
rustfmt --edition 2021 --check rust/crates/mnote-web/src/routes/hermes_client.rs
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_body_local_source_uses_file_scope_not_full_page_context -- --test-threads=1
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_agent_audit_snapshot_detects_changed_files -- --test-threads=1
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
git diff --check
codegraph sync .

边界记录:

  • node scripts/task-hermes-page-ai-audit-smoke.jsnode scripts/task488-acp-multi-session-stability-smoke.js 当前通过旧 helper 调 /api/tree/commands 创建 Convex 文档,失败为 503 convex_retired;两者属于 legacy/cloud 验证入口,需要后续改造为 local-first fixture 后再纳入默认 Batch 8 回归。
  • Batch 8 不把普通 Markdown 编辑主路径扩回 mnote.doc.markdown_editlocal-first 默认仍是授权文件引用 + agent 原生 patch/diff + watcher / Page Aggregate 同步。
  • codegraph sync .codegraph index . --force 均已成功执行;codegraph status . 仍持续显示 Pending Changes: Added 3 files,属于本轮前后均存在的 CodeGraph pending 残留,已记录但不清理用户工作区。

建议验证:

node scripts/task453-local-folder-page-ai-changed-files-smoke.js
node scripts/task-hermes-page-ai-audit-smoke.js
node scripts/task488-acp-multi-session-stability-smoke.js

Batch 9:是否扩展 IDE 面的产品决策

状态:done

目标:

  • 明确 MNote 是否需要 Sidex 式 terminal / git / debug / tasks / extension host。
  • 若需要,单独写 design/10-review/reference 或对应 owner 的 process checklist;不混进当前 P0 服务层收口。

待办:

  • 列出 MNote 用户工作流里真实需要 terminal/git/tasks 的场景。
  • 区分“知识库 workspace 内嵌工具”与“完整 IDE”。
  • 给出引入、不引入、延后引入三种方案。
  • 由用户确认产品边界后再进入实现。

本轮产品边界结论:

  • Sidex 对照:可借鉴 Sidex terminal 的 PTY/session/event-stream 模型、git porcelain status/diff/log 解析、tasks 的 detect/parse/run/output-stream 模型,以及 extension host 的贡献点/隔离 runtime 概念。
  • 不引入完整 VSCode/Sidex workbench、DAP debug、VSCode extension host 或通用 Git 写操作;MNote 继续以 Rust kernel / tree-first projection 作为 workspace 真相。
  • Terminal 若后续引入,只作为 workspace-root-scoped、allowed-roots 约束下的受控 terminal session,不默认开放全局 shell。
  • Git 若后续引入,优先只做 changed_files / diff / log / status 审计;commit / push / pull / restore / clean / checkout 等 destructive 操作不进 MVP。
  • Tasks 若后续引入,只支持白名单知识库任务:导出、索引、链接检查、附件整理、smoke;不做完整 .vscode/tasks.json 兼容。
  • Debug 能力收口为 agent runtime / projection / session log inspector;不引入 DAP、breakpoints 或 debug adapter manager。
  • Extension host 延后,仅保留 importer / exporter / renderer / action provider 的 MNote-native 插件设想,不接 VSCode marketplace 生态。

三方案:

  • 引入:只引入 MNote-native Workspace Command Center,包含受控 terminal session、只读 git status/diff、白名单 tasks、流式输出、changed_files/diff 审计;不引入 VSCode extension host,不暴露 destructive Git。
  • 不引入:继续让 Hermes/Reasonix 用自身文件与 shell 能力,MNote 只做 target / allowed roots / audit / watcher;这是当前 local-first AI 主线成本最低的方案。
  • 延后:先把 terminal/git/tasks 作为 Phase B/C 设计冻结项,只落 checklist,不实现;等 changed_files/diff 审计、BufferStore 冲突、workspace readonly 策略稳定后,再开最小 MVP。

当前建议:

  • 选择“延后 + 小范围引入只读 Git/diff 审计”的产品边界;Terminal/tasks 等 agent runtime 审计闭环稳定后再单独立项,Debug/DAP/VSCode extension host 不进入 MVP。

5. 当前运行状态

历史运行记录:Batch 1 轻量 Workbench Editor Model。

第一步只做只读核验和现有状态盘点,不改代码:

  • 读取 document-resource-tab-runtime.js 的 open editors snapshot 构建逻辑。
  • 读取 document-editor-adapter-runtime.js 的 page / secondary pane 打开入口。
  • 读取 resource-open-runtime.js 与 FileTree open runtime。
  • 输出 Batch 1 的具体文件边界与最小 smoke 切片。

只读核验结论(2026-05-27):

  • document-resource-tab-runtime.js 已有 resourceTabRegistryresourceTabMrubuildOpenEditorsSnapshot()window.__mnoteOpenEditorsSnapshot
  • 当前 snapshot 已包含 schema/generatedAt/activeObjectIdentity/editors/resourceEditors,但没有显式 groups,调用方需要自己按 paneRole 过滤。
  • Page entry 已按 primary/secondary 生成;secondary entry 仅在有 documentId 或 pane visible 时进入 snapshot。
  • Resource entry 已有 objectIdentity/title/kind/badgeKind/active/dirtyGuard/assetId/path,但缺少 paneRole/documentId/workspaceId/lastActiveAt/preview/pinned 等轻量 editor group 字段。
  • document-editor-adapter-runtime.js 的公开入口集中在 window.__mnoteDocumentPaneRuntime,包括 openPrimaryDocumentopenSecondaryDocumentopenResourceInActiveTabopenResourceAsSideTarget
  • sidebar-filetree-open-runtime.js 仍在多处手拼 resource:file:<rootUri>:<relativePath>resource:mindmap:<documentId>:<assetId>resource:onlyoffice:<documentId>:<assetId> 等 identity;这应进入 Batch 2,而不是阻塞 Batch 1。

Batch 1 最小文件边界:

  • 修改:rust/crates/mnote-web/browser/document-resource-tab-runtime.js
  • 可选测试:复用或扩展 scripts/task496-editor-open-parallel-runtime-aggregate-smoke.js
  • 暂不修改:document-editor-adapter-runtime.jssidebar-filetree-open-runtime.jsresource-open-runtime.js

Batch 1 最小实现切片:

  • buildOpenEditorsSnapshot() 增加 groups 字段,按 primary/secondary 输出每个 pane 的 active object 与 editors。
  • openEditorsSnapshotEntry()paneRoledocumentIdworkspaceIdlastActiveAtpreview:falsepinned:false
  • Page entry 补 preview:falsepinned:truelastActiveAt 占位,保持页面 tab 是固定 tab。
  • 更新 smoke 断言:window.__mnoteOpenEditorsSnapshot.groups.primary 存在,secondary 打开后 groups.secondary 存在。

6. 最小成功标准

本 checklist 完成到 P0 时,MNote 至少应满足:

  • 打开页面 / 资源 / secondary pane 的状态有统一 snapshot,可恢复、可被 AI target resolver 消费。
  • local-first Markdown dirty/stale/conflict 只由 BufferStore / Page Aggregate 主链表达。
  • FileTree row identity、resource tab identity、AI target identity 不再多处临时拼接。
  • tree live / local-folder watcher 不再常规整树替换,view state 不丢。
  • 菜单、快捷键、dangerous action 的 enabled/disabled/reason 有统一 context。
  • local-first tree/resource command 不静默退回 legacy documents.* 主链。

7. 验证总入口

按改动范围选择运行:

node scripts/task114-rust-web-gateway-entry-smoke.js
node scripts/task159-auth-entry-smoke.js
node scripts/task164-desktop-hot-local-folder-main-entry-smoke.js
node scripts/task166-local-first-managed-workspace-no-convex-smoke.js
node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
node scripts/task490-runtime-surfaces-smoke.js
cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib -- --test-threads=1
git diff --check
codegraph sync .
codegraph status .