- 统一 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
11 KiB
11 KiB
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.rscreate_default_local_workspace_for_actor_at_baseensure_default_workspace_directoriesensure_default_workspace_home_page
- 当前本地页面创建:
rust/crates/mnote-web/src/routes/local_folder_source.rsexecute_local_tree_commandcreate_local_markdown_page
- 当前本地资源上传:
rust/crates/mnote-web/src/routes/local_folder_source.rswrite_local_markdown_asset
- 当前文件树真实扫描:
rust/crates/mnote-web/src/routes/local_folder_source.rsload_local_folder_file_tree_snapshotscan_directory
- 当前页面树扫描:
rust/crates/mnote-web/src/routes/local_folder_source.rsload_local_folder_page_tree_snapshotscan_markdown_page_tree
- 当前根入口默认初始化测试:
rust/crates/mnote-web/src/routes/gateway.rsroot_entry_initializes_default_local_workspace_page
- 当前上传 smoke:
scripts/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.md和X/两个磁盘条目。不得把二者合并为一个节点。
页面资源
对 X.md 上传附件或图片时,默认写入:
X/assets/<file>
对 mindmap,后续默认写入:
X/mindmaps/<file>
本轮先完成 write_local_markdown_asset 的附件/图片路径迁移。正文链接相对 X.md 应为:

[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 -- --nocapturecargo 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。
- 断言 root 下存在
验收:
cargo test --manifest-path rust/Cargo.toml -p mnote-web create_default_local_workspace_uses_managed_data_root -- --nocapture— okcargo 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.md和X/,页面树只显示X.md对应的页面节点。 X/内的 Markdown 节点挂在X.md页面节点下。- 没有同名
.md的普通目录,如果包含 Markdown,继续作为 page group 显示。
- 如果同目录存在
- 增加单测:
- 构造
Root.md、Root/Child.md。 - 页面树断言
Child.md的parent_node_id是Root.md的documentId。 - 页面树断言没有单独的
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.md、Root/Child.md。 - 文件树断言同时存在
Root.mdmarkdown 行和Rootfolder 行。
- 构造
验收:
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— oknode 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.md和New/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— okcargo check --manifest-path rust/Cargo.toml -p mnote-web— okcargo test --manifest-path rust/Cargo.toml -p mnote-web create_default_local_workspace_uses_managed_data_root -- --nocapture— okcargo test --manifest-path rust/Cargo.toml -p mnote-web local_tree_command_create_page_creates_timestamped_markdown_and_sibling_folder -- --nocapture— okcargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_tree_uses_sibling_directory_as_page_children_container -- --nocapture— okcargo test --manifest-path rust/Cargo.toml -p mnote-web local_file_tree_keeps_markdown_and_sibling_folder_as_separate_rows -- --nocapture— okcargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown_asset_upload_copies_next_to_markdown_with_relative_path -- --nocapture— okcargo test --manifest-path rust/Cargo.toml -p mnote-web local_rename_markdown_page_renames_sibling_page_folder -- --nocapture— oknode 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.pngtmp/page-bundle-after-create-3003.pngtmp/page-bundle-filetree-3003.png
- 检查磁盘:
- root 下有
初始化的新页面.md。 - root 下没有默认
pages/、assets/、mindmaps/、顶层ai-sessions/。 - 新建页面后有
新页面HHMMSS.md和新页面HHMMSS/。 - 文件树能看到
.md与同名目录两个真实条目。 - 页面树把同名目录内子页面挂到
.md页面下。
- root 下有
非目标
- 不迁移用户历史数据。
- 不删除正式
/mnt/Data1T/Mnote_data/users下旧测试目录。 - 不实现完整 mindmap 新路径落盘;本轮只定义默认目标并完成附件/图片上传路径迁移。
- 不把文件树改成页面语义树。
- 不重新引入
mnote_idfrontmatter。