Files
mnote/design/01-tree-first-graph-kernel/done/1-7-local-workspace-root-page-bundle-checklist-v1.md
T
lix-2026 1956a8a21a chore: align mvp design governance
- 统一 local-first MVP 后阶段架构口径,补充 process 执行总序和 Reasonix 协作记录

- 归档已完成的 design checklist,标注参考型 process,更新 AGENTS/REASONIX/架构文档

- 补充文件树/主编辑器下载与上下文菜单相关实现、bug 记录和 smoke 脚本

验证:git diff --check;codegraph sync .;cargo test -p mnote-web;node --check scripts/task476-filetree-editor-context-menu-download-smoke.js
2026-05-21 09:04:13 +08:00

11 KiB
Raw Blame History

1-7 Local Workspace Root Page Bundle Checklist v1

状态:done

归档说明(2026-05-21):默认 local workspace root/page bundle 契约、时间戳新页面、同名目录子页面容器与文件树不合并规则均已由 Rust 单测、HTTP smoke 和浏览器验收覆盖。本文件从 process/ 归档到 done/

背景

当前默认本地工作区初始化会创建 pages/assets/mindmaps/ai-sessions/ 等顶层目录。这更像早期产品内部布局,不像用户真实打开的 VSCode 文件夹,也不利于页面导出。

本轮收口方向:

  • 默认工作区只保留用户内容根目录和 .mnote/ 系统目录。
  • 页面树允许把 页面.md + 页面/ 解释成一个页面节点与其子页面容器。
  • 文件树必须真实展示文件系统,不合并、不隐藏同名 .md 和目录。
  • 新建页面使用 新页面HHMMSS 命名,避免重复创建同名 新页面.md
  • 新建页面时同时创建 新页面HHMMSS.md新页面HHMMSS/
  • 附件、思维导图等页面资源默认进入同名目录下,而不是顶层 assets/ / mindmaps/

参考代码位置

  • 当前默认工作区初始化:rust/crates/mnote-web/src/routes/local_folder_source.rs
    • create_default_local_workspace_for_actor_at_base
    • ensure_default_workspace_directories
    • ensure_default_workspace_home_page
  • 当前本地页面创建:rust/crates/mnote-web/src/routes/local_folder_source.rs
    • execute_local_tree_command
    • create_local_markdown_page
  • 当前本地资源上传:rust/crates/mnote-web/src/routes/local_folder_source.rs
    • write_local_markdown_asset
  • 当前文件树真实扫描:rust/crates/mnote-web/src/routes/local_folder_source.rs
    • load_local_folder_file_tree_snapshot
    • scan_directory
  • 当前页面树扫描:rust/crates/mnote-web/src/routes/local_folder_source.rs
    • load_local_folder_page_tree_snapshot
    • scan_markdown_page_tree
  • 当前根入口默认初始化测试:rust/crates/mnote-web/src/routes/gateway.rs
    • root_entry_initializes_default_local_workspace_page
  • 当前上传 smokescripts/task443-local-markdown-asset-upload-smoke.js

目标目录契约

默认初始化

默认工作区初始化后应该是:

my-space/
  .mnote/
    workspace.json
    trash/
  初始化的新页面.md

不再默认创建:

pages/
assets/
mindmaps/
ai-sessions/

ai-sessions 后续如果要落本地,应放入 .mnote/ai-sessions/,本轮只禁止默认创建顶层 ai-sessions/

新建页面

在父目录下新建页面时,默认标题若为 新页面,真实落盘应为:

新页面HHMMSS.md
新页面HHMMSS/

例如:

新页面061240.md
新页面061240/

要求:

  • HHMMSS 使用本地时间或现有时间工具生成,精确到秒。
  • 即使有时间戳,也必须做磁盘冲突检查;同秒冲突时使用 -2-3 追加。
  • documentId 仍使用路径型 local-md:<relative_path>
  • .md 正文标题应使用最终安全标题,例如 # 新页面061240

页面树和文件树边界

  • 页面树:允许把 X.md 与同目录 X/ 解释为一个页面 X,并把 X/ 内 Markdown 作为页面 X 的子页面。
  • 文件树:必须真实展示 X.mdX/ 两个磁盘条目。不得把二者合并为一个节点。

页面资源

X.md 上传附件或图片时,默认写入:

X/assets/<file>

对 mindmap,后续默认写入:

X/mindmaps/<file>

本轮先完成 write_local_markdown_asset 的附件/图片路径迁移。正文链接相对 X.md 应为:

![](X/assets/photo.png)
[spec.pdf](X/assets/spec.pdf)

顺序执行 Checklist

A0 基线保护

  • 记录当前 git status --short,不要清理与本任务无关的脏改动。
  • 运行当前相关定向测试,确认改前基线:
    • cargo test --manifest-path rust/Cargo.toml -p mnote-web create_default_local_workspace_uses_managed_data_root -- --nocapture
    • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_asset_upload_copies_next_to_markdown_with_relative_path -- --nocapture

验收:

  • 基线通过。两项测试均 ok。

A1 默认工作区目录最小化

  • 修改 ensure_default_workspace_directories:默认只创建 .mnote/trash
  • 修改 ensure_default_workspace_home_page:初始化页改为 root/初始化的新页面.md,不再放入 pages/
  • 更新 create_default_local_workspace_uses_managed_data_root
    • 断言 初始化的新页面.md 位于 root 下。
    • 断言 pages/assets/mindmaps/、顶层 ai-sessions/ 不存在。
    • 保留 .mnote/workspace.json.mnote/trash 断言。
  • 更新 root_entry_initializes_default_local_workspace_page
    • 断言 root 下存在 初始化的新页面.md
    • 不再断言 pages/初始化的新页面.md

验收:

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web create_default_local_workspace_uses_managed_data_root -- --nocapture — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_initializes_default_local_workspace_page -- --nocapture — ok

A2 新建页面创建 .md + 同名目录

  • 新增 helper determine_page_stem
    • 输入默认标题 新页面 输出 新页面HHMMSS(本地时间 HHMMSS,失败时回退 UTC)。
    • 对非默认标题,仍使用用户标题的安全文件名。
    • 对冲突同时检查 <name>.md<name>/,追加 -2-3
  • 修改 create_local_markdown_page
    • 创建 <name>.md
    • 同步创建 <name>/
    • 返回的 relativePath 仍是 <name>.md
    • 返回的 documentId 仍是 local-md:<relative_path>
  • 增加单测 local_tree_command_create_page_creates_timestamped_markdown_and_sibling_folder
    • 执行 execute_local_tree_command(..., "create", ..., Some("新页面"))
    • 断言产生 新页面\d{6}.md 和同名目录。
    • 断言返回 documentId 指向 .md

验收:

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_tree_command_create_page_creates_timestamped_markdown_and_sibling_folder -- --nocapture — ok

A3 页面树使用同名目录作为页面子容器

  • 修改 scan_markdown_page_tree
    • 如果同目录存在 X.mdX/,页面树只显示 X.md 对应的页面节点。
    • X/ 内的 Markdown 节点挂在 X.md 页面节点下。
    • 没有同名 .md 的普通目录,如果包含 Markdown,继续作为 page group 显示。
  • 增加单测:
    • 构造 Root.mdRoot/Child.md
    • 页面树断言 Child.mdparent_node_idRoot.mddocumentId
    • 页面树断言没有单独的 local:page-group:Root 行。

验收:

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_tree_uses_sibling_directory_as_page_children_container -- --nocapture — ok

A4 文件树保持真实

  • 确认 scan_directory 不做任何同名 .md + folder 合并。
  • 增加单测:
    • 构造 Root.mdRoot/Child.md
    • 文件树断言同时存在 Root.md markdown 行和 Root folder 行。

验收:

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_file_tree_keeps_markdown_and_sibling_folder_as_separate_rows -- --nocapture — ok

A5 附件上传进入页面同名目录

  • 修改 write_local_markdown_asset
    • README.md 的附件目录从 README.assets/ 改为 README/assets/
    • 返回的 sourcePath / file_url 相对 Markdown 文件目录,例如 README/assets/photo.png
    • 保留重复文件名 photo-1.png 逻辑。
  • 更新单测 local_markdown_asset_upload_copies_next_to_markdown_with_relative_path
  • 更新 scripts/task443-local-markdown-asset-upload-smoke.js 中对 README.assets/... 的断言为 README/assets/...
  • 只改本地 local_folder 上传路径,不改 cloud / Convex media 路径。

验收:

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_asset_upload_copies_next_to_markdown_with_relative_path -- --nocapture — ok
  • node scripts/task443-local-markdown-asset-upload-smoke.js — ok,证据:tmp/task443-local-markdown-asset-upload-smoke/result.json

A6 重命名 bundle 规则最小收口

  • 检查 rename_local_entry
    • 当重命名 X.md 且同目录存在 X/ 时,同步重命名目录为新标题目录。
    • 如果目标目录已存在,使用与文件一致的冲突策略或返回可解释错误,禁止覆盖。
  • 增加单测:
    • Old.md + Old/Child.md 重命名为 New
    • 断言 New.mdNew/Child.md 存在。
    • 页面树中 Child.md 仍挂在 New.md 下。

验收:

  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_rename_markdown_page_renames_sibling_page_folder -- --nocapture — ok

A7 文档同步

  • 更新本 checklist 状态。
  • 本 checklist 已明确覆盖旧的默认顶层 pages/assets/mindmaps/ai-sessions 初始化结构;未大范围改旧 done 文档。

验收:

  • checklist 中已记录实际完成情况。

A8 最终验证

  • cargo fmt --check --all --manifest-path rust/Cargo.toml — ok
  • cargo check --manifest-path rust/Cargo.toml -p mnote-web — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web create_default_local_workspace_uses_managed_data_root -- --nocapture — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_tree_command_create_page_creates_timestamped_markdown_and_sibling_folder -- --nocapture — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_tree_uses_sibling_directory_as_page_children_container -- --nocapture — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_file_tree_keeps_markdown_and_sibling_folder_as_separate_rows -- --nocapture — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_asset_upload_copies_next_to_markdown_with_relative_path -- --nocapture — ok
  • cargo test --manifest-path rust/Cargo.toml -p mnote-web local_rename_markdown_page_renames_sibling_page_folder -- --nocapture — ok
  • node scripts/task443-local-markdown-asset-upload-smoke.js — ok

浏览器验收由 Codex 复核执行:

  • 使用临时 MNOTE_LOCAL_WORKSPACE_BASE_DIR=/mnt/Data1T/mnote/tmp/page-bundle-data 启动 mnote-web
  • 登录/注入测试 actor 后打开首页。
  • 截图保存到 tmp/
    • tmp/page-bundle-home-3003.png
    • tmp/page-bundle-after-create-3003.png
    • tmp/page-bundle-filetree-3003.png
  • 检查磁盘:
    • root 下有 初始化的新页面.md
    • root 下没有默认 pages/assets/mindmaps/、顶层 ai-sessions/
    • 新建页面后有 新页面HHMMSS.md新页面HHMMSS/
    • 文件树能看到 .md 与同名目录两个真实条目。
    • 页面树把同名目录内子页面挂到 .md 页面下。

非目标

  • 不迁移用户历史数据。
  • 不删除正式 /mnt/Data1T/Mnote_data/users 下旧测试目录。
  • 不实现完整 mindmap 新路径落盘;本轮只定义默认目标并完成附件/图片上传路径迁移。
  • 不把文件树改成页面语义树。
  • 不重新引入 mnote_id frontmatter。