chore: align local-first control plane and editor fixes

- wire SQLite control-plane access/session paths into Rust web local-folder routes

- preserve local Markdown attachment semantics across upload, reload, and secondary-pane resource tabs

- refresh design governance docs, Reasonix task templates, and bug records

- retire root .mcp.json local MCP config
This commit is contained in:
lix-2026
2026-05-23 23:38:42 +08:00
parent 42fb58310c
commit 5f97800489
110 changed files with 5344 additions and 889 deletions
@@ -1,564 +0,0 @@
# 1-1 [process] Tree-First Graph 内核实施清单 v2
> 更新时间:2026-04-16
>
> 基于以下实际状态重写:
> - 当前未提交代码
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
> - `/mnt/Data1T/mnote/harness-tasks.json`
## 1. 这版为什么要重写
`v1` 已经把长期方向改到了 `tree-first graph kernel`,方向是对的,但完成口径仍然偏乐观。
当前真实代码已经证明:
- Kernel 文档、Rust 类型、query/command 协议、`mnote-web` kernel route 不是空想,已经存在
- 但 Sidebar、搜索、AI、Mindmap、阅读页、`BlockNote` 还没有真正切到 kernel projection 主路径
- `mnote-web` 的 kernel route 已接到真实 `sidebar.dataset.list` query plan,但仍保留 fixture/testing fallback,不能写成“主流量切换完成”
因此 `v2` 的目的不是推翻之前工作,而是把长期清单统一到:
> **既承认已落地的 Rust kernel 代码,也不再把骨架、样板和 host/runtime 拆分误写成长期阶段已完成。**
> 说明(2026-04-22):
> 这份清单仍保留为 `Kernel Phase` 全景参考,但当前第一优先级已经转移到
> `Page Aggregate`、`tree command cutover`、`tree realtime event stream`。
> 当前执行优先级请先看 `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`。
---
## 2. 状态口径
### `DONE`
- 已有真实代码进入主线
- 不只是文档或骨架
- 对应阶段的最小目标已经成立
### `PARTIAL`
- 已有真实代码和明确接缝
- 但主路径仍未完成切换
- 仍存在 fixture/testing fallback、旧对象模型或重前端壳残留
### `NOT_STARTED`
- 还停留在设计、口径或局部能力
- 尚未形成稳定主链
---
## 3. 当前总判断
当前基线应统一理解为:
- `Kernel Phase 0``DONE`
- `Kernel Phase 1``DONE`
- `Kernel Phase 2``DONE`
- `Kernel Phase 3``DONE`
- `Kernel Phase 4``DONE`
- `Kernel Phase 5``PARTIAL`
- `Kernel Phase 6``PARTIAL`
- `Kernel Phase 7``PARTIAL`
- `Kernel Phase 8``PARTIAL`
- `Kernel Phase 9``NOT_STARTED`
一句话总结:
> **Kernel 基础层与 Rust Web 承载层已经进入真实主线,树域 consumer 已统一到稳定 projection family;但旧前端壳仍然是主要执行面,下一步才是独立 Rust Web tree shell 重构。**
### 面向“全部用 Rust 重写”的附加口径
如果把长期总目标进一步固定为:
> **主执行面最终也迁到 Rust 家族,而不只是“Rust 拥有 kernel 语义”。**
那么当前状态应再补一层判断:
- `Kernel Phase 0-4` 解决的是“树域真相和协议先收口”
- 但距离“主要 UI 执行面改由 Rust 承接”还差一个关键中段:
- `Sidebar / 页面树 / 文件树` 独立 Rust Web 子系统重构
- 这一步不是可选优化,而是旧前端壳退场前必须先完成的第一块主执行面切换
也就是说:
> **如果目标是“全部用 Rust 重写”,那么当前最优先的下一步,不是继续在旧 React 壳里打补丁,而是启动树域独立 Rust Web 重构。**
---
## 4. 新架构的真实验收定义
只有同时满足下面几条,才能说新架构真正成立。
| 验收项 | 当前状态 | 说明 |
| --- | --- | --- |
| Rust 中存在统一的 kernel node / edge / projection / subtree 真相层 | `DONE` | `core-protocol` 已落地 |
| Rust 中存在统一的 kernel query / command 面 | `DONE` | `bridge-runtime` 已支持 kernel 查询与写协议 |
| Rust Web 能承接 kernel route | `DONE` | `mnote-web` 已具备通用 query transport、kernel/bridge/compat route、测试专用 fixture 边界,以及可切到真实 Sidebar 流量的兼容入口 |
| Sidebar / 页面树 / 文件树直接消费 kernel projection | `PARTIAL` | 前端主 Sidebar 已以 `kernelSidebarTree` 作为主树来源,但仍是重客户端壳,其他树域仍残留旧拼树 helper |
| 搜索直接消费 kernel-aware 检索结果 | `PARTIAL` | cron 已有 `kernel-aware refresh` 过渡链,搜索结果也已带 `nodeId` / `subtreeRootId` / `evidence`,但仍是 LightRAG 过渡口径,不是 kernel 真相层检索 |
| 阅读页直接消费 page subtree projection | `PARTIAL` | `DocumentReadView` 已直接消费 `pageSubtree`,但 projection 仍在前端读链内生成,不是 Rust/kernel 真相层输出 |
| AI 直接面向 node / subtree / edge 操作 | `PARTIAL` | AI runtime 与 Hermes bridge 已开始携带 `node` / `subtree` / `outline` / `evidence` 上下文,但还不是完整的 kernel-first tool 面 |
| Mindmap 正式退化为 projection / editor | `PARTIAL` | 理念已定,独立页已去 stub,但数据真相尚未下沉到 kernel |
| `BlockNote` 只负责内容节点编辑 | `PARTIAL` | 阅读态已先走 `pageSubtree`,编辑器也已按需挂载,但外围 panel / drawer 仍集中在 `DocumentContent` |
| 旧前端壳不再承担对象真相 | `NOT_STARTED` | 当前主入口仍是 Next/React |
---
## 5. Kernel Phase 0:边界冻结与术语统一
**当前状态:`DONE`**
### 已落地
- `tree-first-graph-kernel-v1.md` 已冻结:
- `node`
- `edge`
- `projection`
- `subtree`
- `content node`
- `reference edge`
- `summary node`
- `index node`
- 已明确四层边界:
- 事实源
- 投影
- 编辑器
- 外挂
- 已明确:
- `Mindmap` 不是事实源
- `BlockNote` 不是事实源
- 页面树/文件树不是事实源
### 完成判定
- 后续长期任务不再把导图页、页面树、`BlockNote` 文档结构当作独立真相层
---
## 6. Kernel Phase 1Node / Edge / Projection 基础模型落地
**当前状态:`DONE`**
### 已落地
- `rust/crates/core-protocol/src/kernel.rs` 已存在统一类型
- `rust/crates/core-protocol/src/lib.rs` 已导出 kernel 类型
### 已有能力
- `KernelNode`
- `KernelEdge`
- `KernelProjectionRequest`
- `KernelProjectionResult`
- `KernelSubtreeRef`
- `KernelSubtreeResult`
- `KernelAuditStamp`
- `KernelNodeType`
- `KernelEdgeType`
- `KernelProjectionKind`
### 当前结论
- 这一阶段不应再回退为“只有文档设计”
- 这里已经是实际代码事实
---
## 7. Kernel Phase 2Kernel Query / Command / Subtree / Graph Traversal 协议落地
**当前状态:`DONE`**
### 已落地
- `rust/crates/bridge-runtime/src/lib.rs` 已支持:
- `kernel.node.get`
- `kernel.subtree.get`
- `kernel.children.list`
- `kernel.edges.list`
- `kernel.graph.traverse`
- `kernel.project_view`
- `kernel.node.create`
- `kernel.node.update`
- `kernel.subtree.move`
- `kernel.edge.attach`
- `kernel.edge.detach`
- `rust/crates/storage-convex-bridge/src/mapping.rs` 已补 kernel query / command 映射
### 当前结论
- 这一阶段也不应再写成“待设计”
- 真实缺口不在协议是否存在,而在谁来真正消费这些协议
---
## 8. Kernel Phase 3Rust Web 接入 kernel,成为主承载层
**当前状态:`DONE`**
### 已落地
- `rust/crates/mnote-web/` 已进入 workspace
- 已存在 `axum` app/router/context/middleware 骨架
- 已有 kernel route
- `/api/kernel/projections/sidebar`
- `/api/kernel/subtree`
- `/api/kernel/edges`
- `/api/kernel/graph`
- sidebar kernel route 已通过 `sidebar.dataset.list` runtime plan + 通用 Convex query transport 读取真实数据集
- 已新增真实 workspace / bridge 查询 route
- `/api/bridge/workspace`
- `/api/bridge/request`
- `/api/bridge/trace`
- 已新增 Next 兼容切流入口:
- `/api/compat/next/sidebar`
- `MNOTE_WEB_QUERY_FIXTURES_JSON` + `allow_dev_fixtures` 已把 fixture 收紧到测试/开发边界
- `/api/sidebar` 与服务端 Sidebar 首包当前已优先走 Rust bridge query + source-specific transportConvex transport 仅属于显式 cloud / compat source,正式同源 `3000` 主链不再依赖 `MNOTE_WEB_BASE_URL``mnote-web` 的 Sidebar compat route
- `mnote-web` transport 已优先转发真实 `Authorization`,否则才退回开发态 admin/dev identity
- 已有 kernel / bridge 路由测试,说明 route 不只是声明
### 已完成判定
- Sidebar 这条 kernel 查询主链已不依赖生产态 fixture fallback
- `mnote-web` 已不再只有 Sidebar 单点 transport,而是能承接更广的 query / bridge 主链
- Next -> Rust Web 的第一条真实切流边界已经明确并可启用
### 当前保留边界
- `kernel.subtree.get` / `kernel.project_view` 仍主要建立在 `sidebar.dataset.list` 这一份页面树数据集之上
- 更广义的 node pool、reference edge、summary/index node 真相层,仍属于后续阶段
- `mnote-web` 还不是整个产品的唯一 Web 入口,这属于更后的双栈收缩问题
### 完成判定
- 至少一条不依赖 fixture fallback 的 kernel 查询主链已在 `mnote-web` 上稳定运行
- 至少一条真实 workspace / bridge 查询主链已通过 `mnote-web` 对外提供
- 至少一条真实前端流量已具备默认切到 `mnote-web` 的兼容边界
---
## 9. Kernel Phase 4Sidebar / 页面树 / 文件树切到 kernel projection
**当前状态:`DONE`**
### 本阶段边界
这个阶段只解决一件事:
> **把树域 consumer 全部统一到稳定的 kernel projection / tree protocol。**
这里刻意**不**包含:
- 独立 Rust Web tree shell
- `Leptos` / `Dioxus` / `Yew` 树域 UI 重写
- Sidebar 整体壳替换
这些属于 `Phase 4` 完成之后的独立大任务,即:
- [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
### 已落地
- [x] Sidebar 已有服务端首包
- [x] Rust runtime 已能把 `sidebar.dataset.list` 转为统一 kernel subtree / projection 结果
- [x] `sidebar-data.ts` 已生成 `kernel_sidebar_projection``kernelSidebarTree`
- [x] 前端主 Sidebar 已以 `sidebarData.kernelSidebarTree` 作为初始化与同步的主树来源
- [x] 已新增统一 `page_tree` projection protocol
- `rowId`
- `nodeId`
- `parentNodeId`
- `projectionKind`
- `depth`
- `position`
- `capabilities`
- `resourceMeta`
- [x] `PrivateTree` 已改为直接消费 `page_tree` projection 可见行,而不是自行 flatten 嵌套树
- [x] 文件树已改为只消费 `page_tree projection + asset 映射``buildVisibleRows(...)` 收口为 projection -> visible rows
- [x] `move-embed picker` 空查询态已直接消费 `kernelSidebarTree -> page_tree projection`,不再调用 `buildDocumentTree(...)`
- [x] `SidebarInitialData` / `sidebar-data.ts` 已将 `kernelSidebarProjection``kernelSidebarTree` 收紧为主路径必备字段,不再在映射阶段对缺失 projection 做主路径 fallback
### 本阶段完成后仍保留的问题
- 前端主 Sidebar 仍是超大客户端组件
- 页面树 / 文件树 / picker 虽已统一协议,Rust Web 3000 主文档壳也已补齐 create/delete 与页面内 mindmap create 的 no-refresh 回显;但这只代表主入口局部刷新链路已闭环,不等于 Sidebar 整体壳已完成 Rust 化瘦身
- 文件树中的 `asset-folder` / `asset` / `index` 仍由前端 adapter 基于现有数据集补齐,不是 Rust 直接输出的 `file_tree projection`
- `buildDocumentTree(...)` 仍保留在兼容 helper 与旧单测中,但已退出树域主路径
- 现在可以进入树域 Rust Web 壳重写,但不能把这一步与本阶段混写成同一任务
### 下一阶段任务
- 独立推进 [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
- 继续把当前 `page_tree` / `file_tree` protocol 下沉成更稳定的 Rust Web route / shell;验收必须以 `3000 /documents/<id>` 主文档壳为准,`/tree` debug route 与 3001/Next 不能替代主链验收
- 继续缩小 Sidebar 超大客户端壳,只保留局部交互岛
- 让文件树中的更宽对象投影逐步由 Rust projection 直接输出
### 面向“全部用 Rust 重写”的优先级解释
如果长期目标只是“Rust 持有语义”,这里可以被理解为下一批独立大任务。
但如果长期目标已经固定为“全部用 Rust 重写”,那么这里应升级为:
- **最近主线 P0**
- **旧前端壳的第一块正式替换带**
- **后续阅读页 / 搜索 / AI / Mindmap Rust 化之前的必经步骤**
原因是:
- 树域是工作区主导航与对象结构入口,替换价值最高
- `page_tree / file_tree` 协议已经冻结,返工风险最低
- 当前 Sidebar 仍是旧前端壳里最重、最容易继续扩散语义的一块
- 如果不先把树域执行面剥离出来,后续文档页、搜索页、导图页 Rust 化会继续被旧壳牵制
### 完成判定
- [x] 主 Sidebar 以及相关树域已直接消费 kernel projection
- [x] 页面树 / 文件树 / 嵌入移动器等不再通过旧对象数组拼树
- [x] tree row / projection protocol 已冻结,足以支撑下一步独立 Rust Web tree shell 重构
- [x] 当前 React/Next 树域可以继续作为 consumer 壳存在,但不再定义树结构真相
---
## 10. Kernel Phase 5:结构知识刷新与 kernel-aware 检索
**当前状态:`PARTIAL`**
### 新口径
这阶段不再按“单独搭一个 RAG 系统”来定义。
长期正确方向是:
- 用 cron 定时刷新知识
- 直接把结构知识写回 kernel
- 用 kernel-aware 检索命中 node / subtree / evidence
这与传统 `LightRAG-first` 不同,更接近:
- Karpathy 的知识刷新思路:https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f
- `llmwiki-cli` 这类结构化知识索引方案:https://github.com/doum1004/llmwiki-clihttps://github.com/stellarlinkco/llm-wiki/blob/main/README.zh-CN.md
- `Mindmap` / `BookMindmap` / 章节树 / 书籍子树作为结构索引投影
当前已经有直接代码证据,但只能算过渡态:
- `convex/crons.ts` 已新增 `kernel_aware_refresh_daily_transition`
- `convex/jobs.ts` 已新增 `enqueueKernelAwareRefresh``enqueueKernelAwareRefreshSweep``refresh.kernel_aware_transition`
- 刷新结果已带 `nodeIds``subtreeRootIds``evidenceAssetIds`
- 搜索结果类型与 adapter 已返回 `nodeId` / `subtreeRootId` / `evidence`
但这条链目前仍通过 `LightRAG` 过渡入库,还不能写成“kernel 真相层知识刷新已完成”。
### 已落地
- 已有 cron 驱动的 `kernel-aware refresh` 过渡入口
- 已有工作空间成员校验与刷新目标选择:
- workspace
- document
- mindmap
- asset
- 已有搜索结果结构化返回:
- `nodeId`
- `subtreeRootId`
- `evidence`
- recent / 常规搜索在缺失 Rust evidence 时,也会补齐最小 evidence fallback
### 当前真实问题
- 结构知识仍未直接写回 kernel node / edge
- `summary node``ai_note node``index node``reference edge` 还没有形成真实 kernel 写链
- `BookMindmap` / 章节树还没有形成统一 kernel index 主链
- 当前刷新任务仍是 `kernel_aware_transition`,不是最终的 kernel truth pipeline
- 搜索虽已消费 kernel-aware 结果形状,但还没有 server-first 搜索页或 Rust Web 检索壳
### 下一阶段必须完成
-`kernel_aware_transition` 从 LightRAG 过渡链继续收口到真实 kernel 节点/边写链
- 明确 `summary node``ai_note node``index node``reference edge` 的真实写入协议
- 把增量刷新输入继续稳定到:
- workspace
- page
- subtree
- book
- pdf
- 把刷新输出真正落到:
- `summary node`
- `ai_note node`
- `index node`
- `reference edge`
- `book subtree`
- `chapter subtree`
- 把检索面继续推进到:
- 按 node type 过滤
- 按 subtree 过滤
- 按 edge 过滤
- 返回正文/附件/页码/证据回查
- 定义残留 `LightRAG` 的过渡边界与移除计划
### 完成判定
- 至少一条 cron 驱动的知识刷新链能稳定更新 kernel 节点/边
- 至少一条搜索链能直接返回 kernel node / subtree / evidence
---
## 11. Kernel Phase 6Mindmap 降级为 projection / editor
**当前状态:`PARTIAL`**
### 已落地
- 理念上已经确认 `Mindmap` 不是中心
- Rust 侧已有 Mindmap 对象协议
- 独立导图页已先在服务端获取 initial projection,再进入客户端页面壳
- 独立导图页已直接使用 `StandaloneMindmapView`,不再以 `editorStub` 作为入口
- `MindmapBlock.tsx` 已出现 `standalone` / `documentBridge` 边界
- 文档内嵌导图已默认走 preview-first 入口
### 当前真实问题
- 独立导图页仍是客户端重壳,并且仍复用同一个重型 `MindmapBlock` 族组件
- 文档内嵌导图虽然已经 preview-first,但进入编辑/沉浸态后仍复用同一个重型 `MindmapSurfaceView`
- 导图操作尚未直接回写统一 kernel subtree
### 下一阶段必须完成
- 继续把独立导图页壳从文档编辑上下文与共享重组件中拆开
- 内嵌导图改为轻预览/轻编辑入口
- 把导图编辑动作收口为 kernel command
### 完成判定
- 独立导图页与文档内嵌导图都只作为 kernel projection / editor 入口,而不是独立事实源
---
## 12. Kernel Phase 7:文档阅读页与 AI 面板切到 kernel projection
**当前状态:`PARTIAL`**
### 已落地
- 阅读态/编辑态已经分离
- `DocumentContent` 已生成 `pageSubtree`
- `DocumentReadView` 已直接消费 `pageSubtree`
- 阅读态结构面板已直接消费 `outline` / `evidence`
- `BlockNote` 默认不再首屏强挂
- AI 已做 host/runtime 拆分
- 历史 React 页面 AI runtime 曾向旧 AI route 发送 `node` / `subtree` / `outline` / `evidence`;该证据只保留为过渡记录
- 2026-05-14 起当前页面 AI 主线已改为 Hermes client proxy + mnote Hermes plugin/tool:页面上下文进入 Hermes run/session context,最新页面事实由 Hermes 通过 `mnote.page.get` 等工具回读,不再把旧 `/api/ai-agent/run` 写成长期入口
### 当前真实问题
- 阅读页已经是 page subtree projection 驱动,但 projection 仍在前端读链内生成,不是 Rust/kernel 真相层直接输出
- `DocumentContent` 仍集中挂载 `DocumentAiAgentPanel``DocumentHistoryDrawer``DocumentCommentsDrawer``PageBacklinksPanel``PageOptionsSidebar`
- 当前页面 AI 的会话真相已交给 Hermes;剩余问题是继续把 `node` / `subtree` / `edge` 工具契约下沉到 Rust kernel 稳定协议,而不是回到页面级私有 AI runtime
- 搜索/AI/阅读页之间虽然开始共享 `node` / `subtree` / `outline` / `evidence` 口径,但还没有统一到稳定的 node / subtree / edge 真相协议面
### 下一阶段必须完成
- 把 page subtree projection 从前端读链继续下沉到更稳定的 Rust/kernel 输出边界
- 阅读页大纲、回链、结构信息改读 kernel edge / subtree
- mnote Hermes plugin tools 继续扩展为直接面向 node / subtree / edge
- AI 可以创建:
- `summary node`
- `ai_note node`
- `reference edge`
- 搜索与 AI 共用 kernel-aware 检索上下文
### 完成判定
- 阅读页与 AI 至少各有一条主路径直接消费 kernel projection
---
## 13. Kernel Phase 8BlockNote 退化为内容编辑挂件
**当前状态:`PARTIAL`**
### 已落地
- `src/lib/documents/page-subtree.ts` 已显式定义 `pageSubtree`
- `DocumentContent` 已把阅读态与编辑态拆开
- `DocumentContent` 已通过 `isEditing` / `keepEditorMounted``BlockNote` 按需挂载
- 阅读态已不再默认依赖 `BlockNote` 才能渲染正文
### 当前真实问题
- 页面结构仍未与 `BlockNote` 内容结构彻底解耦
- `DocumentContent` 仍挂着大量外围 panel / drawer
- page subtree 与 content node 的边界还没有正式回写到 kernel / editor 分层
### 下一阶段必须完成
- 把 page subtree 与 content node 的边界继续固化到更稳定的 kernel / editor 分层
- 明确哪些节点继续由 `BlockNote` 编辑
- 明确哪些结构节点改由 kernel-aware editor 处理
- 把外围 panel 从编辑宿主中继续拆走
### 完成判定
- 页面结构不再由 `BlockNote` 数据结构定义
- `BlockNote` 只承担内容节点编辑
---
## 14. Kernel Phase 9:旧前端壳与旧对象模型下线
**当前状态:`NOT_STARTED`**
### 当前真实问题
- 当前主应用入口仍是 Next App Router
- 当前主文档页、主 Sidebar、主搜索、主导图页都仍运行在旧前端壳内
### 下一阶段必须完成
- 盘点旧对象真相残留
- 盘点旧 helper / adapter 残留
- 删除已被 kernel projection 替代的旧 route
- 删除已被 kernel command/query 替代的旧 adapter
- 明确最终双栈收缩与切流计划
### 完成判定
- 旧前端壳与旧对象模型都不再承担主事实来源
---
## 15. 推荐执行顺序
如果按当前真实代码继续推进,建议顺序是:
1. 维持 `Kernel Phase 4` 已完成口径,不再回头重做 consumer 统一
2. 立即启动 `Phase 4` 后继任务:
- [sidebar-pagetree-filetree-rust-web-rebuild-v1.md](/mnt/Data1T/mnote/design/04-tree-domain/process/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md)
3. 在树域 Rust Web 子系统形成稳定主链后,再继续把文档阅读页的 `page subtree / read_view` 下沉到 Rust 输出边界
4. 再推进 `Kernel Phase 5`
5. 再推进 `Kernel Phase 6`
6. 再推进 `Kernel Phase 7`
7. 最后进入 `Kernel Phase 8``Kernel Phase 9`
原因很简单:
-`Phase 4` 的目标已经完成,真正未完成的是“树域执行面 Rust 化”
- 树域是所有主页面里最先具备 Rust 化条件的一块,因为 projection / command protocol 已经先被冻结
- 文档阅读页、搜索、AI、Mindmap 后续都会依赖这条更稳定的树域与 projection 分发主链
- 如果继续把精力分散到旧 React 壳上的局部修补,会拖慢真正的执行面切换
- 知识刷新与 kernel-aware 检索仍然重要,但它们更适合在树域主承载面开始切换后并行推进,而不是抢在前面替代主导航改造
---
## 16. 最终结论
当前最准确的表述不是“长期阶段已经做完”,而是:
> **Kernel 基础层已经做出来了,真正难的部分才刚开始,也就是让所有主视图与主工具链逐步切到 kernel projection。**
所以后续主线必须固定为:
> **先完成树域的 kernel projection 统一,再基于稳定协议做 Sidebar / 页面树 / 文件树的独立 Rust Web 重构;之后再并行推进结构知识刷新、Mindmap、阅读页、AI、`BlockNote` 与旧壳退场。**
如果把总目标进一步固定为“全部用 Rust 重写”,则这里还应再明确一句:
> **下一步应该继续的,不是旧定义里的 `Kernel Phase 4` 本身,而是它的后继任务,也就是 `Sidebar / 页面树 / 文件树 Rust Web 重构`;这是旧前端壳退场前最值得优先拿下的第一块执行面。**
@@ -1,114 +0,0 @@
# 1-13 [process] Batch F P1/P2 活跃尾项执行 checklist v1
> 创建时间:2026-05-21
>
> 当前状态:`PROCESS`
>
> 上位入口:`design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`
>
> 阶段:Batch F / P1-P2 真实活跃尾项收口
## 1. 目标
本批次只处理 1-8 中仍有明确未完成项、且可以互不冲突并行推进的三类尾项:
1. `3-13` GFM AST 迁移尾项:表格/不支持结构降级策略、`web_shell.rs` legacy marks 适配分支去留判定、process 状态收口。
2. `3-3` tree realtime live cache:复核当前 `3000` 主界面 page subtree / filetree / preferred snapshot 是否已经统一到 WS push + SSE fallback live cache,可归档则补证据归档,不能则列最小缺口。
3. `1-6` Gap A7 / `1-5` command context:复核 `ai.canWrite` 是否已经进入 Hermes / AI 写入守卫;如未接入,补最小守卫和测试。
本批次不扩 Wolai 对标、不做 Phase C Review Mode、不恢复 Convex 正文主存储、不重开旧 Next / BlockNote 默认路径。
## 2. 已完成追认
- [x] `4-38` resource lifecycle / open target 原始 checklist 已归档;后续 `4-39` / `4-40` / `4-41` / `4-44` / `4-45` 已覆盖其未完成项。
- [x] `5-26` resource open resolver 已归档,正文附件与 FileTree open target 已不再作为 Batch B/C 尾项。
- [x] `1-12` Batch E 中 BufferStore conflict UI 与 FileTree CommandContext 接入已完成;仅保留本批次对 AI 写入守卫的补齐。
- [x] `3-13` GFM AST 迁移尾项已由 Codex 本地复核并归档到 `design/03-rust-web/done/`;Worker A 超时,结果不采纳。
- [x] `1-6` Gap A7 AI 写入守卫已接入 `args.commandContext``ai.canWrite` / `workspace.readonly`,并补共享守卫测试;Codex 复核后补齐 `mnote.artifact.*``mnote.mindmap.apply_ops` 两个旧守卫漏口。
## 3. Reasonix Worker 拆分
### Worker AGFM AST 降级策略与 process 收口
Owner
- `design/03-rust-web/done/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `rust/crates/mnote-web/src/routes/local_markdown_parser.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
目标:
- 补齐或追认 `3-13` 中“列对齐 / 空单元格 / 带 mark 单元格 / 不支持结构降级策略”的测试证据。
- 审查 `web_shell.rs``legacyStylesToTiptapMarks` 等 legacy marks 适配分支是否仍被当前 bootstrap 使用。不能安全移除时,将其状态改为“保留兼容层并有退出条件”,不要强行删除。
-`3-13` 只剩保留兼容层的合理说明且测试通过,则把 `3-13` 从 process 迁移到 `design/03-rust-web/done/`
验收:
- `cargo test -p mnote-web local_markdown -- --test-threads=1`
- `cargo test -p mnote-web web_shell -- --test-threads=1`
- `git diff --check`
### Worker BTree realtime live cache 可归档性复核
Owner
- `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
- `design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
- 只读审查:`rust/crates/mnote-web/src/routes/ws.rs``rust/crates/mnote-web/src/routes/tree_events.rs``rust/crates/mnote-web/src/ssr/pages/layout.rs`、相关 tree live smoke。
目标:
- 复核 `3-3` 的三个未完成判断:sidebar、page subtree、filetree 是否仍未统一 live cacheWS 是否仍只算骨架;preferred snapshot 补偿链是否仍是阻塞。
- 对照当前 smoke`task432``task446``task447``task448``task449``task165``task123` 等,判断 `3-3` 是否可移动到 done。
- 如果可以归档,只更新文档并移动到 `design/03-rust-web/done/`;如果不能,写明最小剩余缺口和建议新增 smoke,不改 runtime 代码。
验收:
- 至少运行语法检查:相关 smoke 的 `node --check`
- 如当前 3000 服务可用,优先实跑 1-2 个代表性 smoke;若环境不具备,必须在文档中说明阻塞。
- `git diff --check`
### Worker CAI 写入 CommandContext 守卫
Owner
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
- `rust/crates/core-protocol/src/command.rs`
- `rust/crates/mnote-web/src/hermes_tools/doc.rs`
- `rust/crates/mnote-web/src/hermes_tools/page.rs`
- 相关 Hermes / AI 写入单测
目标:
- 复核 `CommandContext.ai.canWrite` 是否已被 AI / Hermes 写入路径消费。
- 若未消费,补最小写入守卫:只读 workspace 或无写权限时,Hermes 写正文/页面命令应返回明确拒绝,不进入文件写入。
- 补针对性单测,避免只停留在 `core-protocol` evaluator 层。
- 更新 `1-6` / `1-5` 对应状态。
验收:
- `cargo test -p core-protocol command_context -- --test-threads=1`
- `cargo test -p mnote-web hermes_tools -- --test-threads=1`
- 如 hermes_tools 过滤过大,可运行新增测试名的 targeted cargo test。
- `git diff --check`
## 4. Codex 复核项
- [x] 读取每个 worker 的结果文件与 diff,不凭文字结论直接验收。
- [x] 对 Worker A 运行 `cargo test -p mnote-web local_markdown -- --test-threads=1``cargo test -p mnote-web web_shell -- --test-threads=1`
- [x] 对 Worker B 至少复跑 `node --check`,并按可用环境实跑代表性 live cache smoke。
- [x] 对 Worker C 运行 `cargo test -p core-protocol command_context -- --test-threads=1` 与相关 Hermes targeted tests。
- [x] 更新 `1-8``1-12``3-13``3-3``1-6` 的状态,不保留互相矛盾的 process 口径。
- [ ] 提交前运行 `git diff --check``codegraph sync .``codegraph status .`
## 5. 本轮执行记录
- 2026-05-21Codex 新建 Batch F checklist,准备并行派发 Worker A/B/C。
- 2026-05-21Worker A 长时间未生成 `final.md` / `result.json`Codex 终止并记录 `.codex/reasonix-tasks/results/batch-f-worker-a-gfm-archive-timeout.md`
- 2026-05-21Worker B 输出串线到 Worker C 方向,不能作为 `3-3` 归档证据;记录 `.codex/reasonix-tasks/results/batch-f-worker-b-tree-live-cache-misrouted.md`。Codex 已复跑 `task123/task432/task446/task447/task448/task449``node --check`
- 2026-05-21Codex 本地复核 `3-13`,确认 colspan 降级、table marks、alignment、web shell legacy marks 兼容层均有证据,已移动到 `design/03-rust-web/done/`
- 2026-05-21Codex 整理 Worker C 半截补丁,新增 `ToolCallInput::command_context_bridge``ensure_write_authorized`,让 Hermes 写入工具消费 `ai.canWrite` / `workspace.readonly`
- 2026-05-21Codex 复核发现 `artifact.rs``resource.rs` 仍保留旧写入守卫,已改为委托 `ensure_write_authorized`。验证:`cargo test --manifest-path rust/Cargo.toml -p core-protocol command_context -- --test-threads=1` 7 passed`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --test-threads=1` 64 passed`cargo test --manifest-path rust/Cargo.toml -p mnote-web ensure_write_authorized -- --test-threads=1` 2 passed。
- 2026-05-21Batch H 已完成 `3-3` live cache 归档与 `1-5` command context 状态收口;`task487` / `task471` / `task476` 已在当前 3000 环境复跑通过,`1-8` Batch D 状态已更新。
@@ -1,107 +0,0 @@
# 1-14 [process] Batch H P2 live cache / command context 收口 checklist v1
> 创建时间:2026-05-21
>
> 当前状态:`DONE`
>
> 上位入口:`design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`
>
> 阶段:Batch H / P2 文档状态与浏览器证据收口
## 1. 目标
本批次只处理 `1-8` Batch D 中仍会干扰后续判断的两类尾项:
1. `3-3` tree realtime live cache 是否仍应保留在 `process/`,还是已被 `3-14` / `3-18` / `task487` 证据覆盖后可以归档。
2. `1-5` / `1-6` command context 文档状态是否仍把 FileTree 菜单、快捷键、AI 写入守卫标成未完成。
本批次不新增 runtime 功能,不扩 AI 产品面,不实现 Phase C Review Mode,不恢复 Convex 正文主存储。
## 2. Reasonix Worker 拆分
### Worker A`3-3` live cache 可归档性复核
Owner
- `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
- 如确认可归档,可移动到 `design/03-rust-web/done/3-3-rust-web-tree-realtime-event-stream-v1.md`
目标:
- 对照 `3-14` WS push、`3-18` local folder tree live consumer、`task123/task432/task446/task447/task448/task449/task487` 证据,判断 `3-3` 的 9.2 未完成项是否已经过期。
- 如果可归档,只更新 `3-3` 文档状态并移动到 `done/`
- 如果不可归档,只在 `3-3` 写明最小剩余缺口,不改 runtime 代码。
验收:
- `node --check scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
- `node --check scripts/task487-local-folder-tree-live-consumer-smoke.js`
- `git diff --check`
### Worker B`1-5` / `1-6` command context 状态收口
Owner
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
目标:
- 只更新 command context / context key 相关 checklist 状态。
- 对照当前代码和已通过测试,修正仍写着“File Tree 右键菜单、快捷键、AI 写入能力尚未消费 CommandContext”的过期表述。
- 保留真实未完成项,例如更完整的 editor/object tab context 或未实跑的 readonly DnD smoke,不要把范围外事项误勾完成。
验收:
- `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 ensure_write_authorized -- --test-threads=1`
- `node --check scripts/task471-local-folder-bulk-resource-trash-smoke.js`
- `node --check scripts/task476-filetree-editor-context-menu-download-smoke.js`
- `git diff --check`
### Worker CBatch D 浏览器 smoke 证据整理
Owner
- `.codex/reasonix-tasks/results/batch-h-worker-c-browser-smoke-evidence.md`
目标:
- 不修改产品代码和设计主文档。
- 只读检查当前相关 smoke 脚本、已有 `tmp/` 结果和截图路径。
- 输出一份证据表,区分“已实跑通过”“仅 node --check 通过”“需要后续实跑”的 smoke。
- 优先覆盖:`task123``task432``task446``task447``task448``task449``task471``task476``task487`
验收:
- 结果文件列出每个 smoke 的脚本路径、最近证据路径、是否可用于人工网页核查。
- 不改 runtime 代码。
- `git diff --check`
## 3. Codex 复核项
- [x] 读取 Worker A/B/C 的 `final.md` / `result.json` / diff,不凭文字结论直接验收。
- [x] 确认 Worker A 若移动 `3-3` 到 done,不会掩盖仍未完成的 live cache 缺口。
- [x] 确认 Worker B 只勾 command context 已完成范围,不把 editor/object tab future 项误判完成。
- [x] 根据 Worker C 的证据表决定本轮是否需要再实跑浏览器 smoke。
- [x] 更新 `1-8` / `1-13` / 本文件执行记录。
- [ ] 提交前运行 `codegraph sync .``codegraph status .`
## 4. 本轮执行记录
- 2026-05-21Codex 新建 Batch H checklist,准备派发 Worker A/B/C。
- 2026-05-21Reasonix Worker A/B/C 均 completed,并通过 completion hook 写入 `tmp/reasonix-hook-test/batch-h-*.json`
- 2026-05-21Codex 复核 Worker A diff,确认 `3-3` 已移动到 `design/03-rust-web/done/`9.2 未完成项已由 `3-14``3-18``task487` 覆盖。
- 2026-05-21Codex 复核 Worker B diff,采纳 `1-5` Phase A7 的 command context 状态更新;保留 editor/object tab context 和更完整按钮 enablement 作为后续扩展,不把它们误判为本轮完成。
- 2026-05-21Codex 复核 Worker C 证据表,纠正 `task471` 证据漏查,并实跑关键 smoke`task487` 通过,`task471` 通过,`task476` 使用 `/usr/bin/google-chrome-stable` 通过。`/snap/bin/chromium` 下的 `download.saveAs ENOENT` 判定为 Playwright 下载临时文件噪音,不作为产品失败。
- 2026-05-21:验证命令已通过:
- `git diff --check`
- `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 ensure_write_authorized -- --test-threads=1`
- `node --check scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
- `node --check scripts/task487-local-folder-tree-live-consumer-smoke.js`
- `node --check scripts/task471-local-folder-bulk-resource-trash-smoke.js`
- `node --check scripts/task476-filetree-editor-context-menu-download-smoke.js`
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/snap/bin/chromium PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium node scripts/task487-local-folder-tree-live-consumer-smoke.js`
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/snap/bin/chromium PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium node scripts/task471-local-folder-bulk-resource-trash-smoke.js`
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable PLAYWRIGHT_CHROME_EXECUTABLE=/usr/bin/google-chrome-stable node scripts/task476-filetree-editor-context-menu-download-smoke.js`
@@ -1,170 +0,0 @@
# 1-2 [process] Tree-First Graph 产品愿景进展评估 v1
> 更新时间:2026-05-19
>
> **目的**:对照原始产品愿景(Wolai 编辑体验 + VSCode 文件树 + simple-mindmap + OnlyOffice + AI 操作一切),评估当前代码在 1.0 MVP 后的实际进展和剩余的缺口。
>
> 关联:
> - `design/01-05-current-priority-overview.md`
> - `design/10-review/done/11-current-full-architecture-review-v1.md`
> - `CURRENT_ARCHITECTURE.md`
> - `design/11-wolai/help-center-reference.md` — Wolai 帮助中心功能对照基准
## 1. 一句话结论
核心链路已经全部立住,但 AI 对思维导图和 Office 文件的操作是明确的功能缺口,其余模块处于"已可用、收口中"状态。
---
## 2. 各模块状态
### 2.1 Wolai 编辑体验 + UI — ✅ 收口中
#### 架构层
| 子能力 | 状态 | 关键证据 |
|--------|------|----------|
| 默认编辑器已切到 leptos-tiptap island | DONE | `design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md` |
| Page Aggregate 读链已切到 Rust projection | DONE | `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md` |
| 文档页入口优先消费 Rust page-aggregate 快照 | DONE | `CURRENT_ARCHITECTURE.md` §2.5 |
| 前端壳已切换到 Rust SSR`:3000`| DONE | `CURRENT_ARCHITECTURE.md` §3.1 |
| Page Aggregate 写链(标题/正文/设置)smoke 通过 | DONE | `scripts/task110-*`, `task-page-aggregate-*-smoke.js` |
| 页面设置运行时语义 / 页头回流 / AI 设置面未完全闭环 | PROCESS | `design/05-editor-mainline/reference/5-5-page-aggregate-single-truth-alignment-v1.md` |
| Wolai UI 行为持续对齐 | PROCESS | `design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md` |
| `documents.content` 仍是兼容后备而非唯一真源 | PARTIAL | `CURRENT_ARCHITECTURE.md` §3.2 |
#### 基础块类型覆盖(对照 Wolai 帮助中心)
| 块类型 | Wolai | mnote | 备注 |
|--------|-------|-------|------|
| 文本 | ✅ | ✅ | |
| 标题 H1/H2/H3 | ✅ | ✅ | |
| 无序列表 | ✅ | ✅ | |
| 有序列表 | ✅ | ✅ | |
| 待办列表(checkbox| ✅ | ✅ | |
| **折叠列表(toggle** | ✅ | ❌ | 缺口 |
| 页面块(subpage| ✅ | Partial | Tiptap 嵌入页 |
| 引述文字(blockquote| ✅ | ✅ | |
| **着重文字(callout** | ✅ | ❌ | 缺口 |
| 代码片段 | ✅ | ✅ | |
| 数学公式 | ✅ | ✅ | LaTeX |
| 分割线 | ✅ | ✅ | |
#### 进阶块类型覆盖(仅基础级引用)
| 块类型 | Wolai | mnote | 备注 |
|--------|-------|-------|------|
| 简单表格 | ✅ | ✅ | Tiptap table |
| **数据表格(database** | ✅ | ❌ | 缺口,类似 Notion DB |
| 统计图表 | ✅ | ❌ | |
| Mermaid 绘图 | ✅ | ❌ | |
| 思维导图 | ✅ | ✅ | Phase 6 leptos-mindmap |
| 页面目录(TOC| ✅ | ❌ | |
| 网址书签 | ✅ | ✅ | |
| 嵌入第三方应用 | ✅ | Partial | OnlyOffice 已接,其他待定 |
| 文件附件 | ✅ | ✅ | OnlyOffice adapter |
| 按钮 / 进度条 / 仪表数字 | ✅ | ❌ | 基础缺口 |
#### 基础操作覆盖
| 操作 | Wolai | mnote | 备注 |
|------|-------|-------|------|
| 基本编辑(输入/换行/拖拽/缩进)| ✅ | ✅ | |
| `/` 快捷命令菜单 | ✅ | ✅ | Tiptap slash command |
| **模板创建页面** | ✅ | ❌ | 缺口 |
| 页面选项/设置 | ✅ | ✅ | Page Aggregate 写链已通 |
| **页面关系图** | ✅ | ❌ | 缺口 |
| 全局搜索(Ctrl+P| ✅ | ✅ | Search modal smoke 通过 |
| **导入/导出** | ✅ | ❌ | 缺口(Local/Cloud 桥接仅 P3 |
| 垃圾桶 | ✅ | ✅ | `tree.resource.*` 实现 |
| **回收站恢复位置+reveal** | ✅ | ✅ | 已实现(`design/04-tree-domain/process/4-28` |
| 个人设置(主题/语言)| ✅ | Partial | 暗色模式有,设置 UI 不全 |
| 图标/表情选择器 | ✅ | Partial | |
| **快捷方式/星标栏** | ✅ | ❌ | 缺口 |
完整对照表见 `design/11-wolai/help-center-reference.md`
### 2.2 VSCode 文件树 — ✅ 大部完成
| 子能力 | 状态 | 关键证据 |
|--------|------|----------|
| VSCode Explorer 对标设计 | DONE | `design/04-tree-domain/done/4-20-vscode-explorer-file-tree-alignment-v1.md` |
| 三层模型(Resource → File → Page Tree)固定 | DONE | `design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md` |
| `tree.*` 命令切流(Stage 2| DONE | `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md` |
| 文件树资源行与页面命令分离 | DONE | `bugs/04-tree-domain/done/4-46-filetree-resource-row-document-command-leak-v1.md` |
| 垃圾箱恢复位置记录 + reveal/focus | DONE | `design/04-tree-domain/process/4-28-trash-restore-location-reveal-focus-v1.md` §2 |
| 资源生命周期命令 `tree.resource.*`(核心 cutover 已完成,局部尾项由后续文档承接)| DONE/PARTIAL tails | `design/04-tree-domain/done/4-27-resource-lifecycle-command-cutover-v1.md`,尾项见 `4-28` / `4-34` / `4-44` / `4-45` |
| 双浏览器 no-refresh(页面 + 文件 + 垃圾箱实时同步)| PROCESS | `design/04-tree-domain/process/4-34-filetree-trash-dual-browser-no-refresh-v1.md` |
| Local/Cloud 桥接 | P3 CANDIDATE | `design/04-tree-domain/reference/4-23-local-cloud-explicit-bridge-p3-candidate-v1.md` |
### 2.3 Simple-mindmap — ✅ 编辑器完成,AI 未覆盖
| 子能力 | 状态 | 关键证据 |
|--------|------|----------|
| Phase 6 口径修正(Rust kernel + simple-mind-map adapter| DONE | `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md` §2 |
| Leptos UI shell | DONE | git `c64487c6` |
| 即时显示修复 | DONE | git `3be102a4` |
| Filetree 切换稳定 | DONE | git `b300562a` |
| KMind 级详细验收 | DONE | `design/06-mindmap/done/6-mindmap-phase6-kmind-parity-detail-checklist-v1.md` |
| **AI 读写思维导图(增删改节点)** | **GAP** | Hermes manifest 无 `mnote.mindmap.*` 工具 |
### 2.4 OnlyOffice — ✅ 打开/链路完成,AI 未覆盖
| 子能力 | 状态 | 关键证据 |
|--------|------|----------|
| adapter-onlyoffice crate | DONE | `rust/crates/adapter-onlyoffice/` |
| Rust 路由 | DONE | `rust/crates/mnote-web/src/routes/onlyoffice.rs` |
| Docker 部署 | DONE | `infra/onlyoffice/docker-compose.yml` |
| 附件打开链路修复 | DONE | git `7bd0c9e9` |
| Kernel 工具(session/签名/proxy/callback/forcesave| DONE | `core-protocol/src/tool.rs` `ONLYOFFICE_TOOL_*` |
| **AI 读写 Office 文件内容(.docx/.xlsx/.pptx** | **GAP** | 无 `mnote.office.*` Hermes 工具 |
### 2.5 AI 操作文档 — ✅ 主线已完成
| 子能力 | 状态 | 关键证据 |
|--------|------|----------|
| 在线文档 markdown 级读写 | DONE | `mnote.doc.markdown_edit` + `mnote.doc.fetch`smoke 验证通过 |
| 本地 `.md` 文件读/写 | DONE/PARTIAL | `mnote.doc.fetch` 已支持本地文件,写侧仍在过渡(`design/03-rust-web/process/3-13`|
| 结构性块操作(移动/插入/删除)| DONE | `mnote.block.*` 工具群,Hermes 完整 smoke |
| 工具权限 / dryRun / 幂等 / revision 约束 | DONE | `bugs/07-ai/done/7-22` ~ `7-30` |
| ACP runtime + UI | DONE | `design/07-ai/done/7-25-acp-session-runtime-enhancement-plan-v1.md` |
| Phase C(流式 apply / review session| FROZEN | `design/07-ai/process/7-14` — 当前不实施 |
---
## 3. 缺口汇总
| 缺口 | 分类 | 说明 |
|------|------|------|
| AI 操作思维导图 | 功能缺失 | Hermes 13 个工具没有 mindmap 操作。AI 无法在导图中增删改节点 |
| AI 操作 Office 文件 | 功能缺失 | 无 `mnote.office.*` 工具。OnlyOffice 可手动编辑,但 AI 无法读写 `.docx`/`.xlsx`/`.pptx` 内容 |
用户确认 "日记" 即当前主编辑区(Tiptap / `.md` 文件),不构成独立缺口。
---
## 4. 当前架构收口主线(已完成进度 ~70%)
```
第一优先:Page Aggregate 单一真源 ████████████░░░░░░ 70%
└─ EditorBlockDocument 原生落库 ██████░░░░░░░░░░░░ 40%
└─ 页面设置运行时闭环 ████████░░░░░░░░░░ 50%
└─ AI 写入链走统一 markdown_edit 口径 ██████████████░░░░ 80%
第二优先:Tree Command Cutover ████████████░░░░░░ 70%
└─ tree.resource.* 命令收口 ██████████░░░░░░░░ 60%
└─ 资源 rename 补全 ████░░░░░░░░░░░░░░ 30%
第三优先:Tree Realtime Event Stream ██████████████░░░░ 80%
└─ WS 主链已上线 (57ec8322) ████████████████░░ 90%
└─ 双浏览器 no-refresh 统一 live cache ██████░░░░░░░░░░░░ 45%
└─ SSE fallback / 补偿链减少 ██████████░░░░░░░░ 60%
```
---
## 5. 后续方向建议
1. **继续推进三条架构收口主线**Page Aggregate → Tree Command → Tree Realtime),这是当前最优先的工程投入点。
2. **确认两个功能缺口是否需要本轮填补**:AI 操作思维导图和 Office 文件。如果需要在 MVP 后立即补齐,需要为 `mnote.mindmap.*``mnote.office.*` 工具做设计;如果可推迟,则当前架构收口完成后再说。
3. Local/Cloud 桥接(P3 candidate)和 Phase C AI(流式 apply)属于明确的"下一轮"范围,不在此轮冲刺内。
@@ -1,474 +0,0 @@
# 1-3 [process] 当前主线持续推进 checklist v1
> 创建时间:2026-05-19
>
> 当前状态:`PROCESS`
>
> 上位依据:
> - `/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. 总原则
- [x] 产品形态固定为 `VSCode 简化版工作区内核 + tiptap markdown 编辑器 + Hermes / Reasonix agent + simplemindmap / office 插件 + Wolai 风格 web 壳 + 鉴权控制面`
- [x] 本地文件夹是默认数据真相;Rust kernel 是唯一语义真相。
- [x] Rust SQLite control-plane 承接账号、分享、同步、协作和 AI 隔离默认控制面;Convex 仅保留历史迁移源、显式 cloud source / compat / sync replica 边界。
- [x] AI 默认尽量使用 agent 原生文件读写、diff、patch 能力;MNote 只提供授权 root、页面定位、必要元数据和特殊资源工具。
- [x] 每完成一个阶段后,把对应过程稿移动到该分类 `done/`,并在本 checklist 写入验证证据。
- 本轮说明:`1-3` 本身仍作为当前持续执行清单保留在 `process/`;已完成的上位 local-first 设计和已完成阶段稿继续放在各自 `done/`,仍处于阶段性收口的 P7 文档保留在 `process/` 并在本 checklist 写明证据。
---
## 1. P0 管理员目录授权控制面
对应 `01-05` 执行顺序:`1. 管理员目录授权 UI / API`
### 1.1 后端 API 与权限底座
- [x] 管理员身份支持 `MNOTE_ADMIN_USER_IDS`
- [x] access policy 默认路径固定为 `/mnt/Data1T/Mnote_data/control-plane/access-policy.json`
- [x] 管理员可读写任意本地目录,普通用户只能访问 owner / grant 授权目录。
- [x] read grant 只能读,write grant 可写。
- [x] 新增 `GET /api/admin/access-policy`
- [x] 新增 `POST /api/admin/access-policy/validate-root`
- [x] 新增 `POST /api/admin/access-policy/grants`
- [x] 新增 `DELETE /api/admin/access-policy/grants/{grantId}`
- [x] 全入口权限审计完成:local folder、page body、tree command、Hermes / Reasonix、shared AI session。
验证证据:
- [x] `cargo test -p mnote-web local_access_policy -- --nocapture`
- [x] `cargo test -p mnote-web local_workspace_access -- --nocapture`
- [x] `cargo test -p mnote-web local_folder -- --nocapture`
- [x] `cargo test -p mnote-web hermes_client_local_acp -- --nocapture`
- [x] `cargo test -p mnote-web hermes_tools_markdown_edit_shared_read_is_forbidden -- --nocapture`
### 1.2 管理员 UI
目标:让管理员不用手写 JSON 就能管理用户目录授权。
- [x] 定位当前 Rust SSR 设置 / 管理入口,确认管理员页面应挂在 `rust/crates/mnote-web/src/ssr/pages/` 的哪个壳内。
- [x] 增加管理员访问入口:非管理员不可见,管理员可进入本地目录授权管理页。
- 实现:`/admin/access-policy` + `PageLayout` 管理员 quick action。
- [x] 增加 policy 列表:显示 `policyPath`、env admins、policy admins、grant 列表。
- 实现:`rust/crates/mnote-web/src/ssr/pages/admin.rs` 调用 `GET /api/admin/access-policy` 并展示 JSON。
- [x] 增加 validate root 表单:输入 `rootPath``rootUri` 后显示 canonical path / rootUri。
- [x] 增加 create grant 表单:`userId``permission``recursive``capabilities`
- [x] 增加 delete grant 操作:删除前显示授权目录和用户,删除后刷新列表。
- [x] 增加 UI 错误态:未登录、非管理员、目录不存在、重复授权、无效 capability。
- 实现:页面展示 API 错误;SSR route 对非管理员返回 403;API 保持后端错误码。
- [x] 补管理员 UI smoke:真实登录管理员可新增 read grant,普通用户可读不可写。
- 验证:`node scripts/task450-admin-access-policy-ui-smoke.js`
- [x] 补普通用户 smoke:普通用户无法打开管理员授权页,直接调用 API 返回 403。
- 验证:`cargo test -p mnote-web admin_access_policy -- --nocapture`
建议验证:
- [x] `cargo test -p mnote-web local_access_policy -- --nocapture`
- [x] `cargo test -p mnote-web local_workspace_access -- --nocapture`
- [x] `node 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 冲突模型收口
- [x] 盘点现有 `fileVersion` / `conflictDetectionKey` 的生成、传递和校验路径。
- 结论:本地 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`
- [x] 统一冲突错误 envelope:错误码、当前磁盘版本、编辑器基线版本、documentId、rootUri、建议动作。
- 实现:本地 markdown stale 写入返回 `details.conflict`,包含 `code``currentDiskVersion``editorBaseVersion``documentId``rootUri``suggestedActions`
- [x] 让冲突错误不丢失当前编辑器内容,前端可以继续保留未保存 buffer。
- 实现:冲突态优先从当前挂载的 ProseMirror DOM 捕获文本;“保留当前编辑器版本”会用最新 `fileVersion` 重新提交当前 buffer。
### 2.2 冲突交互
- [x] 设计冲突 modal / side panel:显示当前编辑器版本、磁盘版本、文件路径、最后修改来源。
- 实现:文档页内嵌 `mnote-editor-conflict-panel`,展示文件标识、来源为本地文件变更,并提供磁盘 / 当前 / diff 三个动作。
- [x] 实现“接受磁盘版本”:重新读取文件,替换编辑器 buffer。
- [x] 实现“保留编辑器版本”:用最新 fileVersion 重新提交当前编辑器内容。
- [x] 实现“打开 diff”:支持 markdown 文本并排查看;复杂块结构先降级为 markdown 文本。
- [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>`
建议验证:
- [x] 新增单测:stale `expectedFileVersion` 被拒绝。
- 验证:`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`
- [x] 新增 browser smokeagent 修改同一 `.md` 文件后,tiptap 保存触发冲突 UI。
- 验证:`node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js` 覆盖 agent 修改同一 `.md` 后的冲突 UI,并断言面板出现 `agent run <runId>`
补充验证:
- [x] `cargo fmt --check --all --manifest-path rust/Cargo.toml`
- [x] `cargo test -p mnote-web document_shell_renders_local_markdown_with_same_sidebar_surfaces -- --nocapture`
- [x] `cargo test -p mnote-web local_folder -- --nocapture`
- [x] `npm 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 审计事件模型
- [x] 设计本地审计目录:建议放在 `/mnt/Data1T/Mnote_data/control-plane/agent-audit/`
- [x] 定义审计事件 JSONL 字段:`eventId``actorId``agentKind``runId``rootUri``permission``changedFiles``diffSummary``createdAt`
- [x] 区分 agent 原生文件修改与 MNote tool 写入:二者都要能归入同一个 run audit。
- 实现:agent 原生文件修改继续由 run 前后 root snapshot 生成 `changedFiles`;MNote tool 本地成功写入额外追加 `origin=mnote_tool` 的同 `runId` audit event,只读拒绝追加 `writeAttemptRejected=true` 的同 `runId` audit event。
- [x] 对只读 grant 的 agent run 写入尝试记录拒绝事件。
- 实现:`mnote.doc.markdown_edit` / `mnote.page.save` / `mnote.block.*``read_only` AI scope 下直接拒绝写入;本地 mnote tool 写入拒绝会按同一 `runId` 追加 control-plane `agent-audit.jsonl` 事件,标记 `writeAttemptRejected=true`
### 3.2 写入采集
- [x] 在 Hermes / Reasonix run 启动前记录 root snapshot:文件 mtime、size、hash。
- [x] run 结束后对比 root snapshot,生成 changed files。
- [x] 对 markdown 文件生成简短 diff summary;大文件只记录 hash / size / path。
- [x] 把审计事件落盘到 control-plane,不写进用户正文目录。
- [x] 在 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`
建议验证:
- [x] 单测:run 前后文件变化可生成 changed files。
- 验证:`cargo test -p mnote-web local_agent_audit_snapshot_detects_changed_files -- --nocapture`
- [x] 单测:只读授权下写入被拒绝并产生拒绝审计事件。
- 验证:`cargo test -p mnote-web hermes_tools_markdown_edit_shared_read -- --nocapture`
- [x] 单测:MNote tool 本地写入归入同一 run audit,并标记 `origin=mnote_tool`
- 验证:`cargo test -p mnote-web hermes_tools_page_save_local_folder_writes_markdown_file -- --nocapture`
- [x] browser smokeAI 修改一篇本地 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 摘要。
补充验证:
- [x] `cargo fmt --check --all`
- [x] `cargo test -p mnote-web hermes_client_acp_run_registers_scoped_runtime_record_in_convex -- --nocapture`(历史测试名;当前默认运行态记录已由 SQLite control-plane 承接,旧 Convex store 只保留显式 compat 覆盖)
- [x] `cargo test -p mnote-web hermes_client_local_acp -- --nocapture`
- [x] `cargo test -p mnote-web document_shell_renders_local_markdown_with_same_sidebar_surfaces -- --nocapture`
---
## 4. P3 本地搜索、反链和资源引用索引
对应 `01-05` 执行顺序:`4. 本地搜索、反链和资源引用索引`
目标:本地 workspace 不依赖 Convex search 也能搜索正文、查反链、查资源引用和标签。
### 4.1 索引边界
- [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` 文件重建索引。
- [x] 定义索引输入: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`
- [x] 定义索引输出:全文 search、backlinks、resource refs、tags、recent changes。
- 实现:本地索引输出 `results``recentChanges`,结果包含全文命中、`backlinks``resourceRefs``tags``path``resourceType`,并可返回 mindmap / office 资源元数据搜索结果。
### 4.2 索引更新
- [x] 本地文件 watcher 事件触发增量索引。
- 实现:本地 markdown / mindmap / office watcher 事件会调用单文件索引更新 `refresh_local_search_index_for_path`,避免等到搜索时才重建。
- 验证:`cargo test -p mnote-web watcher_event_refreshes_local_search_index_path -- --nocapture`
- [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 搜索体验
- [x] 全局搜索优先搜索当前 workspace 本地索引。
- 实现:页面搜索请求携带 `sourceKind/rootUri``local_folder` 请求直接查本地索引,不先走 Convex runtime。
- [x] 搜索结果显示文件路径、标题、命中片段和资源类型。
- 实现:搜索结果行显示标题、命中片段、路径和 `resourceType` 标识;API 同步返回 `path/title/snippet/resourceType`
- [x] 反链面板读取本地索引,不再依赖云端搜索。
- 实现:页面设置 drawer 新增“索引”页签,按当前授权 root 读取 `GET /api/search/local-index/backlinks`
- [x] 标签列表读取本地索引。
- 实现:页面设置 drawer 新增“索引”页签,按当前授权 root 读取 `GET /api/search/local-index/tags`
建议验证:
- [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] 单测:mindmap / office 原生资源元数据可进入本地索引结果。
- 验证:`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] 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`
---
## 5. P4 分享与同步闭环
对应 `01-05` 执行顺序:`5. 分享与同步闭环`
目标:个人本地空间默认隔离,显式分享后才产生共享 workspace / shared AI session;权限、同步和协作默认由 Rust SQLite control-plane 承接,Convex 只保留历史迁移源、显式 cloud source / compat / sync replica 边界。
### 5.1 分享权限
- [x] 定义 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。
- [x] 增加 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` 覆盖测试路径。
- [x] share grant 支持 `read` / `write` / `ai` capability,并可映射到 `AiAccessScope.permissionLevel=shared_read/shared_write`
- 实现:Hermes local ACP run 从 canonical share grant 生成 `shared_read/shared_write`
- [x] 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。
- [x] 管理员可查看、创建和撤销 share grant;owner 后续可管理自己创建的 share grant。
- 实现:管理员页面新增独立“分享授权”面板,与目录授权面板分离。
### 5.2 同步缓存
- [x] 设计 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`,均只写 `.mnote` sidecar。
- [x] 云端同步到本地 cache 时保留来源、版本、权限和冲突信息:至少记录 `shareId``remoteVersion``baseVersion``permission``sourceActor``syncedAt`
- 实现:`.mnote/share-cache.json` 写入 `mnote.share_cache.v1`
- [x] 本地修改同步回云端前进行权限和版本校验:`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。
- [x] 离线期间记录 pending changes,恢复在线后生成同步报告;pending changes 只能来自已有授权 cache,离线不能新增或扩大授权。
- 实现:pending changes 写入 `.mnote/sync-state.json`,写入前强制校验 canonical share grant。
### 5.3 同步冲突报告
- [x] 同步冲突复用 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 消费。
- [x] 冲突报告包含本地版本、远端版本、base version、修改 actor、shareId 和 resource id。
- 验证:`shared_cache_and_sync_state_record_permissions_and_conflict_report`
- [x] 管理员或 owner 可导出冲突报告;报告写入 control-plane 或 workspace `.mnote/sync-reports/`,不写入正文目录。
- 实现:报告写入授权 workspace 的 `.mnote/sync-reports/`
建议验证:
- [x] 单测:share read 不允许写入。
- 验证:`cargo test -p mnote-web shared_cache -- --nocapture``cargo test -p mnote-web hermes_client_local_shared -- --nocapture`
- [x] 单测:shared AI session 不扩大 workspace root。
- 验证:`cargo test -p mnote-web acp_runtime_env_limits_shared_scope_to_granted_files -- --nocapture`
- [x] 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 对象统一
- [x] 盘点 simplemindmap 当前 object identity、保存路径、打开路径。
- 证据:`/mindmap/{documentId}/{mindmapId}` 对象壳输出 `resource:mindmap:{documentId}:{mindmapId}``mindmap_shell_returns_rust_object_shell_contract`;保存仍通过 `mindmaps.put` / `mindmap.command.apply` 兼容链。
- [x] 盘点 office 当前 object identity、保存路径、打开路径。
- 证据:新增 `/office/{documentId}/{assetId}` 对象壳输出 `resource:onlyoffice:{documentId}:{assetId}`,内部复用 `/onlyoffice` bootstrap`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`
- [x] 统一资源创建、重命名、移动、删除、恢复命令到 `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`
- [x] filetree 显示资源行,pagetree 只显示页面导航投影。
- 验证:`cargo test -p mnote-web file_tree_projection_ -- --nocapture``cargo test -p bridge-runtime file_tree_projection -- --nocapture`
- [x] Markdown 中插入资源引用时,只写相对链接或嵌入引用。
- 证据:`task169-mindmap-realtime-smoke.js``assertDocumentMindmapBlockUsesReferenceSource` 验证 `mindmapId/rootNodeId` 且不写入 `data`;附件/office 仍通过链接或 asset 引用打开对象壳。
### 6.2 AI 资源工具
- [x] 设计 `mnote.mindmap.*` 工具:读取结构、增删改节点、移动节点、导出 markdown summary。
- 设计:`design/07-ai/reference/7-28-resource-ai-tool-contract-v1.md`
- [x] 设计 `mnote.office.*` 工具:读取文本摘要、写入建议、导出变更摘要;真实编辑优先复用 officecli / OnlyOffice 保存链。
- 设计:`design/07-ai/reference/7-28-resource-ai-tool-contract-v1.md`
- [x] 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`
- [x] agent changed files 审计包含 mindmap / office 资源文件。
- 验证:`cargo test -p mnote-web local_agent_audit_snapshot_detects -- --nocapture`;覆盖 `.mindmap.json` 和同大小二进制 `.docx` 内容变更。
建议验证:
- [x] 单测:resource rename 不走 document command。
- 验证:`cargo test -p mnote-web resource_rename_uses_resource_command_not_document_command -- --nocapture`
- [x] 单测: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` 写入语义。
- [x] 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 导出与备份
- [x] 设计 Web 导出入口:选择 Convex workspace,选择目标本地 root。
- 设计:`design/03-rust-web/reference/3-17-convex-export-web-entry-v1.md`;当前 CLI fixture 入口已支持 dry-run / apply / manifest / conflict report / rollbackWeb route 与 admin 面板仍按该设计后续落码。
- [x] 导出前创建 manifest,记录迁移计划、目标 root、workspace、操作列表、冲突、created files、backup dir 与索引刷新结果。
- 实现:`scripts/export-convex-workspace-to-local.js --manifest <file>`apply 成功后同时写入 `<root>/.mnote/migration-manifest.json`
- [x] 导出页面为 `.md`,资源写为本地文件,附件保持相对路径。
- 验证:`node scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- [x] 导出过程记录进度:总页面数、资源数、AI session 数、操作列表、冲突数。
- 实现:manifest 写入 `documentCount``resourceCount``aiSessionCount``operations``conflicts`
### 7.2 冲突与回滚
- [x] 目标 root 已有同名文件时生成冲突报告,不直接覆盖。
- 验证:`node scripts/task455-convex-export-plan-rollback-smoke.js`
- [x] 支持 dry run:只生成迁移计划,不写文件。
- 验证:`node scripts/task455-convex-export-plan-rollback-smoke.js`
- [x] 支持回滚:根据 manifest 删除本次新增文件或恢复备份文件。
- 验证:`node scripts/task455-convex-export-plan-rollback-smoke.js`;当前最小闭环覆盖新增文件删除,备份文件恢复按 manifest 字段实现,覆盖写入仍由冲突策略禁止。
- [x] 导出完成后自动跑本地索引重建。
- 实现:CLI apply 写入 `.mnote/index/search-index.json`,包含导出页面与资源。
建议验证:
- [x] 脚本 smokeConvex fixture 导出到本地 root。
- 验证:`node scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- [x] 脚本 smoke:同名文件冲突时不覆盖。
- 验证:`node scripts/task455-convex-export-plan-rollback-smoke.js`
- [x] 脚本 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 单一真源
- [x] 盘点 `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.get` compat`bridge-runtime` 仍将 `CompatMetaContentJoin` / legacy `documents.content` 投影为 `blockDocument`
- [x] 让本地 `.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。
- [x] 标题、正文、页面设置写入后只通过 page aggregate 刷新 UI。
- 阶段进展:文档壳初始化、外部刷新和冲突比较已优先消费 `body.blockDocument` / `editorDocument`,再 fallback 到 legacy `body.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`
- [x] 删除或降级前端手工拼 `meta + content` 的 runtime fallback。
- 证据:文档壳初始化与外部刷新优先消费 `/api/page-aggregate`,前端只在 Page Aggregate `body.blockDocument/editorDocument` 缺失时把 legacy `body.content` 作为兼容读取;非 local 的后端 `CompatMetaContentJoin` 仍作为 cloud / legacy substrate 边界保留,不再描述为前端 runtime 真相。
- [x] AI 页面设置写入走统一 page aggregate / page command 入口。
- 证据:Hermes `mnote.page.update_options``page.layout.updateOptions`local-first 分支走 `update_local_page_options`
建议验证:
- [x] `cargo test -p mnote-web page_aggregate -- --nocapture`
- 验证:7 个测试通过。
- [x] browser smoke:标题、正文、页面设置保存后刷新仍一致。
- 证据:`scripts/task-page-aggregate-refresh-persistence-smoke.js`,结果 `tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.json`
### 8.2 Tree command cutover
- [x] 盘点仍在 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` 等历史命名。
- [x] 新增命令统一命名为 `tree.*``tree.resource.*`
- 证据:`cargo test -p mnote-web tree_command -- --nocapture` 覆盖 `tree_commands_prefer_tree_protocol_names_in_command_wire`
- [x] 对历史 `documents.*` 命令只保留 compat adapter,不再扩展新语义。
- 证据:`4-6` 已冻结 `documents.* -> tree.*` 兼容映射;`cargo test -p mnote-web tree_command -- --nocapture` 覆盖 alias mapping。
- [x] 资源 rename、移动、删除、恢复补齐 `tree.resource.*`
- 证据:资源生命周期已输出 `tree.resource.rename/move/archive/restore/purge``cargo test -p mnote-web tree_command -- --nocapture` 覆盖本地 folder asset trash / restore / purge。
建议验证:
- [x] `cargo test -p mnote-web tree_command -- --nocapture`
- 验证:15 个测试通过。
- [x] browser smokefiletree 新建、重命名、移动、删除、恢复不触发页面 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
- [x] 盘点 Sidebar、page subtree、filetree、preferred snapshot 的数据来源。
- 证据:Sidebar/filetree 已消费 Rust projection 与 live tree eventslocal folder 仍有 `/api/tree/local-folder-watch` + HTML refetch 补偿链;WS 主链已启用 `/api/realtime/ws`SSE 为 close 后 fallback。
- [x] 统一 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`
- [x] 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`
- [x] 双浏览器文件树操作无刷新同步。
- 证据:`task432` 页面生命周期、`task433` file asset、`task434` mindmap/table、`task446` rename、`task447` move、`task448` resync 已在 3000 主入口通过,详见 `3-3``4-34`
- [x] 删除不必要的 polling / refetch 补偿链。
- 证据:文档壳 local folder watch 已从整页 `text/html` refetch 改为 projection API 刷新;`cargo test -p mnote-web sidebar_tree_runtime_polls_local_folder_without_browser_reload -- --nocapture`
建议验证:
- [x] `cargo test -p mnote-web local_folder -- --nocapture`
- 验证:59 个测试通过。
- [x] 双浏览器 smoke:页面、文件、垃圾箱操作互相同步且不刷新页面。
- 证据:`task432` / `task433` / `task434` / `task446` / `task447` / `task448`
---
## 9. 每轮推进固定验证包
后续每次持续推进本 checklist,至少执行与改动相关的子集;跨域改动需要执行完整包。
- [x] `cargo fmt --check --all`
- [x] `cargo test -p mnote-web local_folder -- --nocapture`
- [x] `cargo test -p mnote-web local_access_policy -- --nocapture`
- [x] `cargo test -p mnote-web hermes_client_local_acp -- --nocapture`
- [x] `cargo test -p mnote-web tree_command -- --nocapture`
- [x] `cargo test -p mnote-web page_aggregate -- --nocapture`
- [x] `npm run check:local-first-convex-guard`
- [x] `git diff --check -- <changed-files>`
- [x] 影响 UI / 交互时补 browser smoke,并把脚本名写回对应阶段。
---
## 10. done 迁移标准
本文件迁入 `done/` 前必须满足:
- [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`
- [x] P2 agent changed files / diff 审计完成,并能在 AI 会话 UI 查看。
- [x] P3 本地搜索 / 反链 / 资源引用索引完成最小闭环。
- [x] 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`
- [x] P5 simplemindmap / office 资源模型完成 Resource Tree 产品化。
- 证据:资源命令面、资源对象壳、AI resource tools 与 changed-files 审计已闭环;`node scripts/task456-resource-object-shell-sync-smoke.js` 已通过。
- [x] 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/03-rust-web/reference/3-17-convex-export-web-entry-v1.md`
- [x] 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 记录阶段性瘦身结果。
- [x] `01-05-current-priority-overview.md` 同步更新状态,不再把已完成项描述为当前第一优先级。
- 证据:`01-05` 已把 P0-P6 改为已完成最小闭环,把 P7 改为阶段性瘦身完成,并把下一阶段顺序更新为 WorkspacePath / BufferStore / Page Aggregate kernel-native / command context / live cache。
@@ -1,204 +0,0 @@
# 1-4 [process] 下一阶段执行路线图 v1
> 创建时间:2026-05-19
>
> 当前状态:`PROCESS`
>
> 上位依据:
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-3-current-priority-execution-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference/5-14-zed-lapce-vscode-reference-adoption-matrix-v1.md`
> - `/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md`
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
>
> 目标:把当前已经推进到混合态的主线,收口成一条更清晰的下一阶段路线,避免“功能继续堆、底座继续散”的问题。
---
## 1. 结论
下一阶段不是全量重写,也不是继续优先补 UI 功能,而是:
> **以架构收口为主,带着产品化闭环往前推。**
这里的“架构收口”指的是把当前已经存在但还没完全统一的底座收束到少数几条正式主链上:
- `WorkspacePath / ProjectPath / ObjectIdentity`
- `BufferStore / fileVersion / dirty / external change`
- `Page Aggregate`
- `tree.*` / `tree.resource.*`
- `live cache` 的 snapshot / delta / resync 主链
- agent 文件编辑权限与 allowed roots
这里的“产品化闭环”指的是在底座稳定后,继续做:
- 分享与同步
- simplemindmap / office 资源模型
- 旧 Convex 数据迁移
---
## 2. 为什么不是先做功能补全
`1-3` 里已经完成了比较多的可见功能:
- 管理员目录授权控制面。
- VSCode-like 冲突处理 UI。
- agent changed files / diff 审计。
- 本地搜索、反链、资源引用索引。
`5-14` 也说明了一个更底层的事实:
- Zed 更适合作为主架构参考。
- Lapce 更适合作为轻量实现参考。
- SideX 更适合作为 VS Code 行为迁移到 Rust 后端的桥接参考。
- 官方 VS Code 只适合作为行为规格参考。
这意味着当前真正缺的不是“再加一个功能”,而是:
> **把这些功能压在同一套身份、版本、命令、搜索和 watcher 主链上。**
如果继续优先做功能补全,结果通常是:
- 新功能依赖旧兼容面。
- 新能力继续长在不同命令面上。
- UI 看起来越来越完整,但底层真相越来越散。
---
## 3. 下一阶段三段式推进
### 3.1 阶段 A:架构收口
这是当前最优先的阶段。
目标:
> **把 workspace identity、buffer/version、page aggregate、tree command、live cache 收成统一主链。**
应优先推进的事:
- 统一 `WorkspacePath` / `ProjectPath` / `ObjectIdentity`
- 统一 `BufferStore` / dirty / save / external change 模型。
- 继续瘦身 `documents.*`,新增只走 `tree.*` / `tree.resource.*`
-`Page Aggregate` 继续成为页面主投影唯一入口。
- 统一 Sidebar / filetree / page subtree / WS / SSE 到同一 live cache。
- 让 agent 文件编辑权限、allowed roots、symlink escape、文件版本仲裁保持一致。
不该做的事:
- 不重建通用 IDE 壳。
- 不优先做 terminal / debugger / extension host。
- 不继续扩展旧兼容命令面。
### 3.2 阶段 B:资源与共享产品化
架构收口后,第二阶段做产品化闭环。
目标:
> **把 simplemindmap / office / 分享 / AI 访问权限纳入同一套资源与权限模型。**
应优先推进的事:
- `share grant``local access grant` 的关系。
- `shared AI session` 的权限边界。
- `tree.resource.*` 的资源创建 / 重命名 / 移动 / 删除 / 恢复。
- simplemindmap / office 的 ObjectIdentity 和 Resource Tree 映射。
### 3.3 阶段 C:迁移与运营闭环
最后做迁移和收尾。
目标:
> **把旧 Convex 资产迁移成本变成可视、可回滚、可验证的产品流程。**
应优先推进的事:
- Convex workspace 导出到本地 root。
- dry run。
- 备份。
- 冲突报告。
- 回滚。
---
## 4. 和 `1-3` / `5-14` 的关系
### 4.1 `1-3` 负责告诉我们“当前还剩什么没收口”
`1-3` 已经把主线拆得很清楚:
- P0 到 P3 基本是已完成或接近完成的产品能力。
- P4 到 P7 仍然是后续主线。
- 其中 P7 明确指出:Page Aggregate / tree command / realtime 兼容链还要继续瘦身。
### 4.2 `5-14` 负责告诉我们“底座应该参考什么”
`5-14` 的作用不是多拉一个功能列表,而是告诉我们:
- workspace / project / buffer / search / agent 权限,主参考应是 Zed。
- RPC / proxy / config / keymap 的轻量 Rust 组织方式,可以参考 Lapce 和 SideX。
- VS Code 只用于行为规格校准。
### 4.3 合起来的结论
两份文档合起来,结论就是:
> **下一阶段的主线是“架构收口 + 产品化闭环”,不是“全量功能扩展”也不是“推倒重写”。**
---
## 5. 明确优先级
### P0
- `WorkspacePath / ProjectPath / ObjectIdentity`
- `BufferStore / fileVersion / dirty / conflict`
- `Page Aggregate`
- `tree.*` / `tree.resource.*`
- `live cache` 主链
- agent 文件编辑权限
### P1
- 本地搜索与快速打开继续巩固
- File Tree / Page Tree / Resource Tree 命令上下文统一
- command context / context key 统一
### P2
- share / sync
- simplemindmap / office 资源产品化
- 本地迁移与导出
### P3
- terminal / PTY 边界
- MultiBuffer / excerpts
- review diff / streaming apply
- 插件运行时
---
## 6. 当前不做什么
- 不把 MNote 拉成通用 IDE。
- 不先做 extension host / debugger / terminal 优先级升级。
- 不让 `documents.*` 继续长成新主命令面。
- 不把 Tauri desktop shell 当成当前主壳方向。
- 不把 UI 功能增长误当成主线前进。
---
## 7. 下一步建议
接下来最合理的动作是:
1. 先按 `1-4` 这条路线图推进架构收口。
2.`1-3` 里把 `P7` 拆成明确的收口子任务。
3. 等底座稳定后,再把 `P4-P6` 推成产品化闭环。
一句话结论:
> **先收口主链,再做产品化。**
@@ -1,696 +0,0 @@
# 1-5 [process] 下一阶段顺序执行 checklist v1
> 创建时间:2026-05-19
>
> 当前状态:`PROCESS`
>
> 上位依据:
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-4-next-phase-execution-roadmap-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-3-current-priority-execution-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference/5-14-zed-lapce-vscode-reference-adoption-matrix-v1.md`
>
> 目标:给出一份可以顺序执行、每一步都有完成验收、且标明参考源码位置的下一阶段执行清单。
---
## 0. 执行原则
- [ ] 每个阶段必须先补或确认测试,再改实现。
- [ ] 每个阶段只能收口当前主链,不新增通用 IDE 产品面。
- [ ] 新增长期命令必须优先走 `tree.*` / `tree.resource.*` / `page.*`,不能继续扩 `documents.*`
- [ ] 涉及本地文件写入时,必须携带 workspace identity、allowed roots、expected file version。
- [ ] 涉及 UI / live stream 时,必须补 browser smoke 或复用既有 smoke。
- [ ] 每完成一个阶段,必须把验证命令和 smoke 证据写回本文件或 `1-3`
---
## 1. Phase A0:建立执行基线
目标:
> **确认当前 P0-P3 既有能力仍然稳定,避免在架构收口前带着红灯推进。**
### 1.1 基线验证
- [ ] 运行 Rust 格式检查。
- 命令:`cargo fmt --check --all --manifest-path rust/Cargo.toml`
- 完成验收:命令通过。
- [ ] 运行 local-first 权限与 workspace 基线测试。
- 命令:`cargo test -p mnote-web local_folder -- --nocapture`
- 命令:`cargo test -p mnote-web local_access_policy -- --nocapture`
- 命令:`cargo test -p mnote-web hermes_client_local_acp -- --nocapture`
- 完成验收:三组测试通过。
- [ ] 运行 Page Aggregate / tree command 基线测试。
- 命令:`cargo test -p mnote-web page_aggregate -- --nocapture`
- 命令:`cargo test -p mnote-web tree_command -- --nocapture`
- 完成验收:两组测试通过。
- [ ] 运行 local-first guard。
- 命令:`npm run check:local-first-convex-guard`
- 完成验收:命令通过,新增路径没有把 Convex 恢复成默认正文主存储。
### 1.2 基线 smoke
- [ ] 冲突 UI smoke。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`
- 完成验收:tiptap 与外部/agent 写入冲突仍能显示、合并、保存。
- [ ] 本地搜索索引 smoke。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task452-local-search-index-browser-smoke.js`
- 完成验收:新建、重命名、搜索、反链、标签仍稳定。
- [ ] agent changed files smoke。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task453-local-folder-page-ai-changed-files-smoke.js`
- 完成验收:AI 写入后 UI 能显示 `agent.changed_files`
---
## 2. Phase A1:统一 WorkspacePath / ProjectPath / ObjectIdentity
目标:
> **用一个 Rust 侧稳定身份表达“哪个 workspace root 下的哪个对象 / 文件 / 资源”。**
### 2.1 当前代码锚点
- MNote
- `rust/crates/core-protocol/src/kernel.rs`
- `KernelObjectIdentity`
- `PageBodyWriteRequest`
- `rust/crates/core-protocol/src/lib.rs`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `fileObjectIdentity`
- `readFileTreeObjectIdentity`
- `rust/crates/mnote-web/src/routes/tree.rs`
### 2.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/project/src/project.rs`
- `design/05-editor-mainline/reference-code/zed/crates/project/src/worktree_store.rs`
- `design/05-editor-mainline/reference-code/zed/crates/worktree/src/worktree.rs`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/path_util.rs`
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/multi_root.rs`
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/workspace.rs`
### 2.3 顺序任务
- [ ] 盘点当前所有 path key / object key。
- 搜索命令:`rg -n "ObjectIdentity|objectIdentity|rootUri|relativePath|fileVersion|documentId" rust/crates/mnote-web rust/crates/core-protocol -S`
- 完成验收:列出哪些入口仍只用 `documentId`、绝对路径或前端拼接 key。
- [ ]`core-protocol` 中明确 `WorkspacePath` 或等价结构。
- 建议字段:`workspace_id``root_uri``root_id``relative_path``source_kind``object_identity``resource_kind`
- 完成验收:结构能表达页面 `.md`、mindmap、office、附件、目录节点。
- [ ] 给 local folder file tree 输出稳定 identity。
- 重点文件:`rust/crates/mnote-web/src/routes/local_folder_source.rs`
- 完成验收:File Tree row、Page Tree row、Resource row 均能携带统一 identity,不再由前端临时猜测。
- [ ] 前端 sidebar/filetree 消费统一 identity。
- 重点文件:`rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 完成验收:`fileObjectIdentity` / `readFileTreeObjectIdentity` 不再作为事实源,只作为兼容读取层。
### 2.4 验收
- [ ] 单测覆盖 identity 序列化与反序列化。
- 建议命令:`cargo test -p core-protocol object_identity -- --nocapture`
- [ ] local folder tree 测试覆盖页面 `.md` 和资源对象 identity。
- 建议命令:`cargo test -p mnote-web local_folder -- --nocapture`
- [ ] 浏览器 smoke 覆盖 filetree active reveal 和标题同步。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task438-filetree-title-md-active-reveal-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task440-page-title-filetree-md-sync-smoke.js`
完成标准:
- [ ] 本地页面、资源、附件、目录节点都能映射到同一套 workspace identity。
- [ ] UI 不再需要通过文件名或 DOM 属性临时反推对象身份。
---
## 3. Phase A2:建立 BufferStore / DocumentBuffer 最小模型
目标:
> **让 tiptap autosave、AI 文件写入、外部编辑器修改围绕同一份打开态、dirty 状态和文件版本仲裁。**
### 3.1 当前代码锚点
- `rust/crates/core-protocol/src/kernel.rs`
- `PageBodyWriteRequest.expected_file_version`
- `rust/crates/mnote-web/src/routes/documents.rs`
- `/api/page-body/write`
- `/api/documents/save` compat
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `write_local_markdown_page_body`
- `save_local_markdown_page`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- `conflictDetectionKey`
- `fileVersion`
- `rust/crates/mnote-web/src/routes/editor.rs`
### 3.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/project/src/buffer_store.rs`
- `design/05-editor-mainline/reference-code/zed/crates/editor/src/editor.rs`
- Lapce
- `design/05-editor-mainline/reference-code/lapce/lapce-app/src/doc.rs`
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/files`
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/services/filesConfiguration`
### 3.3 顺序任务
- [ ] 盘点所有文件写入口。
- 搜索命令:`rg -n "documents.save|page.body.save|page-body/write|write_local_markdown|expectedFileVersion|conflictDetectionKey" rust/crates -S`
- 完成验收:列出 tiptap、Hermes tool、page save、documents compat、外部 watcher 入口。
- [ ] 定义 `DocumentBuffer` 最小字段。
- 建议字段:`workspace_path``file_version``base_content_hash``current_content_hash``dirty_state``external_change_state``last_loaded_at``last_saved_at`
- 完成验收:字段能覆盖 clean、dirty、stale、external modified、deleted 五种状态。
- [ ]`/api/page-body/write` 成为本地 `.md` 正文写入主入口。
- 重点文件:`rust/crates/mnote-web/src/routes/documents.rs`
- 完成验收:本地 markdown 写入必须携带 `expectedFileVersion``documents/save` 只作为 compat adapter。
- [ ] 把冲突 UI 的 source 数据改为来自 buffer 状态。
- 重点文件:`rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 完成验收:冲突面板能显示 editor base version、disk version、source actor。
### 3.4 验收
- [ ] stale 写入被拒绝。
- 命令:`cargo test -p mnote-web local_folder_documents_save_rejects_stale_expected_file_version -- --nocapture`
- [ ] Page Body 写入测试通过。
- 命令:`cargo test -p mnote-web page_aggregate -- --nocapture`
- [ ] 冲突 UI smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`
完成标准:
- [ ] tiptap、AI、外部修改共享同一版本仲裁模型。
- [ ] 外部文件变更不能静默覆盖 dirty buffer。
- [ ] `documents/save` 不再被新增路径当作长期主入口。
---
## 4. Phase A3Page Aggregate 写读主链继续收口
目标:
> **页面头部、正文、设置、树投影都通过 Page Aggregate 主链回流 UI。**
### 4.1 当前代码锚点
- `rust/crates/core-protocol/src/page_aggregate.rs`
- `rust/crates/mnote-web/src/routes/documents.rs`
- `rust/crates/mnote-web/src/ssr/pages/document.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `rust/crates/storage-convex-bridge/src/mapping.rs`
### 4.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/project/src/project.rs`
- `design/05-editor-mainline/reference-code/zed/crates/project/src/worktree_store.rs`
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files`
### 4.3 顺序任务
- [ ] 盘点仍直接拼 `meta + content` 或读取 `documents.content` 的 runtime 路径。
- 搜索命令:`rg -n "documents.content|getContent|getMeta|meta \\+ content|page_aggregate|pageAggregate" rust/crates wolai-frontend src -S`
- 完成验收:列出仍存在的 runtime fallback,并标记 `compat only` 或待删除。
- [ ] 标题、正文、页面设置写入后只触发 Page Aggregate refresh。
- 重点文件:`rust/crates/mnote-web/src/routes/documents.rs`
- 重点文件:`rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 完成验收:写入后 UI 更新来源能追溯到 Page Aggregate,而不是局部补偿状态。
- [ ] 明确 `documents.*` 在 Page Aggregate 中的兼容边界。
- 重点文件:`rust/crates/storage-convex-bridge/src/mapping.rs`
- 完成验收:新增语义不再加到 `documents.*`,旧命令只作为 adapter。
### 4.4 验收
- [ ] Page Aggregate Rust 测试通过。
- 命令:`cargo test -p mnote-web page_aggregate -- --nocapture`
- 命令:`cargo test -p bridge-runtime page_aggregate -- --nocapture`
- [ ] 页面标题、正文、设置 smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task110-page-title-single-truth-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task-page-aggregate-body-sync-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task-page-aggregate-options-sync-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task-page-aggregate-refresh-persistence-smoke.js`
完成标准:
- [ ] 文档页不再新增 `meta + content` 拼装真相。
- [ ] 标题、正文、页面设置刷新后仍一致。
- [ ] Page Aggregate provenance 能清楚区分 local markdown、compat join、kernel projection。
---
## 5. Phase A4tree command / tree.resource command 收口
目标:
> **页面和资源操作统一走 `tree.*` / `tree.resource.*``documents.*` 只保留兼容。**
### 5.1 当前代码锚点
- `rust/crates/mnote-web/src/routes/tree.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `rust/crates/storage-convex-bridge/src/mapping.rs`
- `rust/crates/mnote-cli/src/lib.rs`
- `rust/crates/mnote-web/src/transport/convex.rs`
### 5.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/project_panel/src/project_panel.rs`
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/commands`
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/src-tauri/src/commands/fs.rs`
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/file_operations.rs`
### 5.3 顺序任务
- [ ] 盘点仍使用 `documents.*` 的命令入口。
- 搜索命令:`rg -n "\"documents\\.|documents\\." rust/crates infra src wolai-frontend -S`
- 完成验收:输出保留清单,逐项标记为 `compat adapter``test fixture``must cutover`
- [ ] 将新增页面操作固定为 `tree.node.*`
- 完成验收:新建、重命名、移动、归档、恢复、删除不再新增 `documents.*` 调用。
- [ ] 将资源操作固定为 `tree.resource.*`
- 完成验收:mindmap、office、附件 rename/move/archive/restore/purge 都有正式命令名。
- [ ] mnote-cli 中历史 `documents.*` 命令降级为 compat。
- 重点文件:`rust/crates/mnote-cli/src/lib.rs`
- 完成验收:CLI 新增命令使用 `tree.*``tree.resource.*`
### 5.4 验收
- [ ] tree command 单测通过。
- 命令:`cargo test -p mnote-web tree_command -- --nocapture`
- 命令:`cargo test -p bridge-runtime tree_command -- --nocapture`
- [ ] 文件树操作 smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task179-tree-create-delete-no-reload-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task446-tree-rename-dual-browser-live-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task447-tree-move-order-dual-browser-live-smoke.js`
完成标准:
- [ ] 新增长期命令面不再出现 `documents.*`
- [ ] `storage-convex-bridge` 只把正式命令映射到 Convex 兼容 mutation。
- [ ] 资源生命周期命令能统一产生 tree stream delta hint。
---
## 6. Phase A5tree realtime live cache 收口
目标:
> **Sidebar、File Tree、Page Tree、Page Subtree 都消费同一套 snapshot / delta / resync live cache。**
### 6.1 当前代码锚点
- `rust/crates/mnote-web/src/routes/sse.rs`
- `rust/crates/mnote-web/src/routes/realtime_ws.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `tree:snapshot`
- `tree:delta`
- `tree:resync`
- `deltaNeedsProjectionRefresh`
- `rust/crates/bridge-runtime/src/lib.rs`
- `tree_stream_delta_hint`
- `materialize_tree_stream_delta`
### 6.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/worktree/src/worktree.rs`
- `design/05-editor-mainline/reference-code/zed/crates/project/src/worktree_store.rs`
- Lapce
- `design/05-editor-mainline/reference-code/lapce/lapce-proxy/src/watcher.rs`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/watcher.rs`
### 6.3 顺序任务
- [ ] 盘点所有 tree/page/filetree 刷新来源。
- 搜索命令:`rg -n "tree:snapshot|tree:delta|tree:resync|EventSource|WebSocket|poll|refetch|preferred snapshot|pageSubtree" rust/crates/mnote-web/src -S`
- 完成验收:标出 WS 主链、SSE fallback、polling/refetch 补偿链。
- [ ] 明确 WS 是主链、SSE 是 fallback。
- 重点文件:`rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 完成验收:SSE 不和 WS 并行长期消费同一事件,只有 WS 不可用时 fallback。
- [ ] delta 能处理时直接 apply,不能处理时 resync。
- 重点文件:`rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 重点文件:`rust/crates/bridge-runtime/src/lib.rs`
- 完成验收:`deltaNeedsProjectionRefresh` 不再成为常态路径。
- [ ] 删除或降级不必要的 polling / refetch 补偿链。
- 完成验收:页面/文件树操作不靠定时轮询才能稳定。
### 6.4 验收
- [ ] realtime route 单测通过。
- 命令:`cargo test -p mnote-web tree_realtime -- --nocapture`
- 命令:`cargo test -p mnote-web local_folder -- --nocapture`
- [ ] 双浏览器 live smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task446-tree-rename-dual-browser-live-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task447-tree-move-order-dual-browser-live-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task448-tree-resync-recovery-dual-browser-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js`
完成标准:
- [ ] 双浏览器页面、文件、垃圾箱操作不刷新页面也能同步。
- [ ] WS 主链可用时 SSE 只作为 fallback。
- [ ] resync 是异常恢复路径,不是每次 delta 后的默认补偿。
---
## 7. Phase A6agent 文件编辑权限与 audit 收口
目标:
> **Hermes / Reasonix 只能在 allowed roots 内编辑文件,写前受版本约束,写后 watcher / audit / projection 能同步。**
### 7.1 当前代码锚点
- `rust/crates/core-protocol/src/ai.rs`
- `AiAccessScope`
- `allowed_roots`
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- `acp_allowed_roots_env_for_payload`
- `local_agent_audit_collect_snapshot`
- `local_agent_audit_finalize_run`
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
- `local_agent_audit_record_write_rejected`
- `local_agent_audit_record_tool_write`
- `rust/crates/mnote-web/src/hermes_tools/doc.rs`
- `rust/crates/mnote-web/src/hermes_tools/page.rs`
### 7.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/agent/src/tools/edit_file_tool.rs`
- `design/05-editor-mainline/reference-code/zed/crates/agent/src/tools/read_file_tool.rs`
- `design/05-editor-mainline/reference-code/zed/crates/agent/src/tools/write_file_tool.rs`
- `design/05-editor-mainline/reference-code/zed/crates/agent/src/tools/edit_session.rs`
- `design/05-editor-mainline/reference-code/zed/crates/agent/src/tool_permissions.rs`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/src-tauri/src/commands/validation.rs`
- `design/05-editor-mainline/reference-code/sidex-main/src-tauri/src/commands/fs.rs`
### 7.3 顺序任务
- [ ] 盘点所有 agent 写入口。
- 搜索命令:`rg -n "mnote.doc.markdown_edit|mnote.page.save|mnote.block|allowed_roots|AiAccessScope|write_rejected|agent_audit" rust/crates/mnote-web/src rust/crates/core-protocol/src -S`
- 完成验收:列出 Hermes tool、Reasonix/ACP、agent native file edit、MNote tool 写入入口。
- [ ] allowed roots 校验统一到一个 Rust helper。
- 完成验收:所有写入口共用同一套 root canonicalization、symlink escape、read/write permission 校验。
- [ ] agent 写入前读取当前 file version。
- 完成验收:MNote tool 写入必须携带 `expectedFileVersion`agent native 写入完成后必须进入 audit + watcher 同步路径。
- [ ] audit event 统一 run id 和 changed files 结构。
- 重点文件:`rust/crates/mnote-web/src/routes/hermes_client.rs`
- 完成验收:agent native 写入和 MNote tool 写入可归入同一个 run audit。
### 7.4 验收
- [ ] allowed roots env 测试通过。
- 命令:`cargo test -p mnote-web acp_allowed_roots -- --nocapture`
- [ ] audit 单测通过。
- 命令:`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`
- 命令:`cargo test -p mnote-web hermes_tools_page_save_local_folder_writes_markdown_file -- --nocapture`
- [ ] changed files smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task453-local-folder-page-ai-changed-files-smoke.js`
完成标准:
- [ ] read grant 下写入被拒绝并记录 audit。
- [ ] write grant 下写入后 UI 能显示 changed files。
- [ ] agent 不能通过 symlink 或 `..` 逃出 allowed roots。
---
## 8. Phase A7command context / context key 最小落地
目标:
> **File Tree / Page Tree / Object Tab / Editor selection 贡献统一 command context,右键菜单和快捷键不再各自临时判断。**
### 8.1 当前代码锚点
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `rust/crates/mnote-web/src/routes/tree.rs`
- `rust/crates/core-protocol/src/command.rs`
### 8.2 参考代码位置
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/contextkey`
- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/commands`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-keymap/src/context.rs`
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-keymap/src/resolver.rs`
- Lapce
- `design/05-editor-mainline/reference-code/lapce/lapce-app/src/command.rs`
- `design/05-editor-mainline/reference-code/lapce/lapce-app/src/keypress/loader.rs`
### 8.3 顺序任务
- [x] 盘点当前菜单、快捷键、按钮 enablement 条件。
- 搜索命令:`rg -n "disabled|aria-disabled|context|shortcut|keydown|menu|right-click|data-action" rust/crates/mnote-web/src/ssr/pages/layout.rs -S`
- 完成验收:列出至少 filetree、pagetree、editor、AI panel 的条件来源。
- **执行记录(Batch C Worker C**FileTree 右键菜单(delete-trash/rename/new-file/new-folder/paste-into 共 5 项,均携带 `when`)和 Delete/Backspace 快捷键已审计并完成上下文接入。AI panel 条件由 `ai.canWrite` 在 hermes_tools 守卫中消费。
- [x] 定义 MNote 最小 command context。
- 完成 key`workspace.sourceKind``workspace.readonly``tree.focusKind``tree.selectionCount``tree.selectionResourceKind``editor.dirty``editor.hasSelection``ai.canWrite` 共 8 个 key。
- 完成验收:这些 key 已覆盖当前右键菜单、快捷键和 AI write guard enablement。
- **代码证明**`rust/crates/core-protocol/src/command.rs``CommandContext` 结构体包含全部 8 个字段;7 个单测覆盖 when 求值全路径。
- [x] 先在 Rust 侧定义 context schema,再让前端消费。
- Rust 侧:`core-protocol/src/command.rs``CommandContext` + `WhenExprNode` + `evaluate_when` / `check_when` / `is_command_enabled`
- 前端侧(SSR `layout.rs`):`buildSidebarFileTreeContext()` + `evaluateSidebarFileTreeWhen()` 两个 JS 等价物用于右键菜单启禁。
- 完成验收:FileTree 右键菜单和 Delete/Backspace 共享同一 `evaluateSidebarFileTreeWhen` 路径。
### 8.4 验收
- [x] command context 单测覆盖 `when` 判断。
- 命令:`cargo test -p core-protocol command_context -- --nocapture`
- **结果(2026-05-21**7 passed — 覆盖 key、`!key``==``!=``&&``||`、括号分组、number comparison、parse error fallback、`ai.canWrite` gate、`workspace.readonly` gate。
- [x] 文件树 DnD / readonly conflict smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task431-vscode-explorer-dnd-readonly-conflict-smoke.js`
- **证据**`tmp/task431-vscode-explorer-dnd-readonly-conflict-smoke/result.json``ok: true`readonly-target 和 readonly-source 均返回 400。
完成标准:
- [x] 同一命令在菜单、快捷键、按钮上使用同一 context 判断。
- FileTree 右键菜单(delete-trash/rename/new-file/new-folder/paste-into)与 Delete/Backspace 快捷键共享 `evaluateSidebarFileTreeWhen` 路径。
- **保留的缺口**editor/object tab 上下文尚未接入 command contextAI tool 写入守卫 `ensure_write_authorized` 是独立路径(读 `args.commandContext`,非前端 context key)。
- [x] readonly / read grant / selected resource kind 的禁用态一致。
- FileTree 右键菜单 readonly 禁用已通过 `!workspace.readonly` when 表达式统一实现。
---
## 9. Phase B1:分享与同步闭环
目标:
> **个人本地空间默认隔离,显式分享后才产生 shared workspace / shared AI session。**
### 9.1 当前代码锚点
- `rust/crates/mnote-web/src/routes/local_access_policy.rs`
- `rust/crates/mnote-web/src/routes/hermes_client.rs`
- `rust/crates/core-protocol/src/ai.rs`
- `recycle/20260522-convex-runtime-retirement/infra/convex/`(历史 Convex 自托管对照;不作为 active 部署入口)
### 9.2 参考代码位置
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/platform/files`
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/collab`
- 仅参考权限和协作边界,不采用完整协作产品。
### 9.3 顺序任务
- [ ] 定义 share grant 与 local access grant 的关系。
- 完成验收:share grant 不自动扩大本机文件系统权限。
- [ ] shared AI session 只消费 share grant 允许的资源。
- 完成验收:shared AI session 不能访问 owner 本机未授权 root。
- [ ] 同步 cache 目录设计。
- 完成验收:cache 中保留来源、版本、actor、权限、冲突信息。
- [ ] 同步冲突复用 Phase A2 冲突 UI。
- 完成验收:冲突报告包含 local、remote、base、actor。
### 9.4 验收
- [ ] 单测:share read 不允许写入。
- [ ] 单测:shared AI session 不扩大 workspace root。
- [ ] browser smoke:共享页面只读用户无法通过 AI 写入正文。
完成标准:
- [ ] share / sync 不破坏 local-first 权限模型。
- [ ] shared AI session 不能越权读写。
---
## 10. Phase B2simplemindmap / office Resource Tree 产品化
目标:
> **simplemindmap / office 是 Resource Tree 对象,Markdown 正文只保留链接或嵌入引用。**
### 10.1 当前代码锚点
- `rust/crates/mnote-web/src/routes/mindmap_api.rs`
- `rust/crates/mnote-web/src/routes/mindmap_shell.rs`
- `rust/crates/mnote-web/src/ssr/pages/mindmap.rs`
- `rust/crates/mnote-web/src/routes/tree.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `src/components/onlyoffice/`
### 10.2 参考代码位置
- Zed
- `design/05-editor-mainline/reference-code/zed/crates/project_panel/src/project_panel.rs`
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/file_tree.rs`
### 10.3 顺序任务
- [ ] 盘点 simplemindmap object identity、保存路径、打开路径。
- 搜索命令:`rg -n "mindmap|tree.resource.mindmap|resource:mindmap|mindmapId" rust/crates src -S`
- 完成验收:列出所有 mindmap 创建、打开、保存、删除路径。
- [ ] 盘点 office object identity、保存路径、打开路径。
- 搜索命令:`rg -n "onlyoffice|office|tree.resource.office|resource:office|assetId" rust/crates src -S`
- 完成验收:列出所有 office 创建、打开、保存、删除路径。
- [ ] 统一资源生命周期到 `tree.resource.*`
- 完成验收:rename / move / archive / restore / purge 不再走 document command。
- [ ] Markdown 中只写资源引用。
- 完成验收:mindmap / office 内容不塞回 `.md` 正文真相。
### 10.4 验收
- [ ] 单测:resource rename 不走 document command。
- [ ] 单测:mindmap resource 删除 / 恢复不破坏 markdown 页面。
- [ ] browser smokemindmap 长中文保存和 index.md 不互相污染。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task443-filetree-mindmap-click-active-row-smoke.js`
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task445-filetree-mindmap-switch-no-flicker-smoke.js`
完成标准:
- [ ] Page Tree 只做页面导航投影。
- [ ] File Tree 能显示资源行。
- [ ] Resource Tree 才是资源归属真相。
---
## 11. Phase C1:旧 Convex 数据迁移产品化
目标:
> **把旧 Convex workspace 迁移成本变成可视、可回滚、可验证的产品流程。**
### 11.1 当前代码锚点
- `rust/crates/storage-convex-bridge/src/mapping.rs`
- `rust/crates/mnote-web/src/transport/convex.rs`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `scripts/task444-convex-workspace-export-local-fixture-smoke.js`
### 11.2 参考代码位置
- VS Code
- `design/05-editor-mainline/reference-code/vscode/src/vs/workbench/contrib/files/browser/fileImportExport.ts`
- SideX
- `design/05-editor-mainline/reference-code/sidex-main/crates/sidex-workspace/src/file_operations.rs`
### 11.3 顺序任务
- [ ] 设计 dry run 导出计划。
- 完成验收:计划列出页面、资源、附件、目标路径、冲突项,不写文件。
- [ ] 导出前生成 backup / manifest。
- 完成验收:manifest 能记录本次新增、覆盖、跳过、失败。
- [ ] 目标 root 同名冲突不覆盖。
- 完成验收:生成冲突报告,用户明确选择前不写入。
- [ ] 回滚按 manifest 删除新增或恢复备份。
- 完成验收:回滚后 root 回到导出前状态。
- [ ] 导出完成后重建本地索引。
- 完成验收:迁移后页面可搜索,资源引用可查。
### 11.4 验收
- [ ] Convex fixture 导出 smoke 通过。
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- [ ] 新增 smoke:同名文件冲突时不覆盖。
- [ ] 新增 smoke:回滚后 root 回到导出前状态。
完成标准:
- [ ] 迁移支持 dry run。
- [ ] 迁移支持备份、冲突报告、回滚。
- [ ] 迁移后 local index 可用。
---
## 12. 每阶段统一收尾验收
每完成一个 Phase,执行:
- [ ] `cargo fmt --check --all --manifest-path rust/Cargo.toml`
- [ ] `git diff --check -- <changed-files>`
- [ ] 与阶段相关的 Rust 单测。
- [ ] 与阶段相关的 browser smoke。
- [ ] `npm run check:local-first-convex-guard`
- [ ] 把验证证据写回本 checklist。
阶段迁移到 `done/` 的标准:
- [ ] checklist 项全部勾选。
- [ ] 真实代码已落地。
- [ ] 单测与 smoke 有证据。
- [ ] 不再依赖临时 compat 作为新增能力主路径。
@@ -1,302 +0,0 @@
# 1-6 [process] 下一阶段缺口补齐 checklist v1
> 创建时间:2026-05-19
>
> 当前状态:`PARTIALLY_DONE`Reasonix 已补 A2/A7 基础设施与 A2 保存/watcher 接入;A2 浏览器刷新可见读回、A0 browser smoke、A7 UI 接入、B2/C1 产品化仍未闭环)
>
> 上位依据:
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-4-next-phase-execution-roadmap-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference/5-14-zed-lapce-vscode-reference-adoption-matrix-v1.md`
>
> 目的:基于 Reasonix 执行汇总和本轮核对结果,列出仍不能标完成的缺口,形成可继续顺序执行的补充清单。
---
## 0. 当前核对结论
本轮已确认:
- [x] `ObjectWorkspacePath` 已在 `core-protocol` 定义并导出。
- [x] local folder projection 已输出 `resourceMeta.workspacePath`
- [x] 前端 `fileObjectIdentity` 已优先读取 `resourceMeta.workspacePath.objectIdentity`
- [x] `DocumentBuffer` / `DocBufferDirtyState` 已在 `core-protocol` 定义并有状态机测试。
- [x] `mnote.mindmap.*` / `mnote.office.*` Hermes resource tools 已出现,且最小测试通过。
本轮 Reasonix 执行后新增:
- [x] **Gap A0(部分)**: local-first guard 已通过 ✅(`npm run check:local-first-convex-guard``{ok: true, guard: "local-first-convex"}`);browser smoke 尚未复跑。
- [x] **Gap A2(基础设施+运行时接入)**: `BufferStore` 已在 `rust/crates/mnote-web/src/document_buffer_store.rs` 实现,并加入 `AppState.buffer_store`。运行时接入完成:`write_local_markdown_page_body``mark_saved`、watcher → `mark_external_modified`、Hermes tools → 共享 helper。11 个单测全部通过(含 3 个新集成测试)。
- [x] **Gap A2(运行时接入—完成)**: `write_local_markdown_page_body` 已接入 `BufferStore.mark_saved()``documents.rs` `page_body_write``save` 路由已传递 `state.buffer_store`Hermes `doc_apply_block_ops``page_command` 已传递 `state.buffer_store`watcher 已在 `spawn_local_folder_watcher` 内调用 `mark_external_modified`。3 个新集成测试覆盖运行时入口。
- [x] **Gap A2(浏览器可见闭环)**: Codex 复核新增 `task484-local-folder-page-body-refresh-readback-smoke.js`,验证 `POST /api/page-body/write` 后磁盘 `.md`、Page Aggregate、刷新后的 ProseMirror 均读回新正文;旧的“刷新后 editor DOM 未显示正文”证据已判定为过期。
- [x] **Gap A7(基础设施)**: `CommandContext` + `when` evaluator 已在 `core-protocol/src/command.rs` 实现。7 个单测全部通过(含 3 个必补测试)。
- [ ] **Gap A7UI/命令接入)**: File Tree 右键菜单、快捷键、AI 写入能力尚未消费 `CommandContext`
- [x] **Gap B2(部分)**: `resource_rename_uses_resource_command_not_document_command` 测试通过;`local_agent_audit_snapshot_detects_resource_files` 通过;mindmap 生命周期测试通过。
- [x] **Gap C1(部分)**: `scripts/task444-convex-workspace-export-local-fixture-smoke.js` 通过;`export-convex-workspace-to-local.js` 既有 `--dry-run``--manifest``--conflict-report``--rollback` 能力已确认,但本轮未新增对应 smoke。
- [x] **core-protocol 全量测试**: 41 单元 + 6 editor bridge + 2 resource tree = 49 通过 ✅
- [x] **mnote-web 相关测试**: BufferStore 8 通过 + resource_trash 6 通过 + audit 2 通过 = 16 通过 ✅
---
## 1. Gap A0:补齐基线验证证据 — 🔄 部分完成
### 1.1 必跑命令
- [x] 运行 local-first guard。
- 命令:`npm run check:local-first-convex-guard`
- 结果:`{"ok": true, "checkedLines": 1, "guard": "local-first-convex"}`
- 验收:通过 ✅
- [x] 冲突 UI smoke — `task451-local-markdown-conflict-resolution-ui-smoke.js`
- 编号已存在脚本目录,需在完整后端环境下运行(依赖 mnote-web + Convex
- 验收:已复跑 browser smoke;单测级 `document_buffer_state_transitions` 不能替代 UI smoke。
- **执行记录(2026-05-21, Batch B Worker C**:✅ **PASS** — 4/4 子步骤全部通过(accept-disk, keep-current, merge-save, agent-conflict-source)。结果文件:`tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`
- [x] 本地搜索索引 smoke — `task452-local-search-index-browser-smoke.js`
- 编号已存在脚本目录
- 验收:已复跑 browser smoke。
- **执行记录(2026-05-21, Batch B Worker C**:❌ **FAIL**`/api/search/documents` 返回 200 结果正确,但 page-settings 本地索引 panelbacklinks/tags30s 超时未渲染。归因:**UI 渲染回归**。结果文件:`tmp/task452-local-search-index-browser-smoke/result.json`
- **Codex 复核(2026-05-21**:✅ **PASS** — 诊断显示 API 和 UI 均已渲染,失败来自 smoke 仍期待 frontmatter 标题 `Search Smoke`;当前索引面板按路径口径展示 `README` / `README.md` 和链接片段。已更新断言并实跑通过。
- [x] agent changed files smoke — `task453-local-folder-page-ai-changed-files-smoke.js`
- 编号已存在脚本目录
- 验收:已复跑 browser smoke`local_agent_audit_snapshot_detects_changed_files``detects_resource_files` 只能覆盖 audit 单元路径。
- **执行记录(2026-05-21, Batch B Worker C**:✅ **PASS** — capturedKinds: session/run/events 全部捕获。结果文件:`tmp/task453-local-folder-page-ai-changed-files-smoke/result.json`
### 1.2 收尾
- [x] guard 输出已记录(见上)
- [x] browser smokes 需完整后端环境运行(mnote-web + Convex + Playwright
- [x] 失败归属:本轮已无 A0 smoke 失败;`task452` 为旧断言假失败
---
## 2. Gap A2:把 DocumentBuffer 接入运行时 BufferStore — 🔄 基础设施与运行时接入完成,浏览器可见读回未闭环
### 2.1 当前代码锚点(更新后)
- `rust/crates/mnote-web/src/document_buffer_store.rs`**新增**`BufferStore` + `BufferKey` 实现
- `rust/crates/mnote-web/src/app.rs``buffer_store: BufferStore` 已加入 `AppState`
- `rust/crates/mnote-web/src/lib.rs``pub mod document_buffer_store` 已注册
- `rust/crates/core-protocol/src/kernel.rs``DocumentBuffer` / `DocBufferDirtyState` / `ObjectWorkspacePath`(已有未改)
### 2.2 顺序任务完成情况
- [x] 新增运行时 `BufferStore` owner`rust/crates/mnote-web/src/document_buffer_store.rs`,通过 `AppState.buffer_store` 持有
- [x] 定义 buffer key:以 `ObjectWorkspacePath``workspace_id + source_kind + root_uri + relative_path + document_id` 作为稳定 `BufferKey`
- [x] 接入 tiptap 保存链:`write_local_markdown_page_body` / `documents.rs` `save` 路由已调用 `state.buffer_store.mark_saved()`
- [x] 接入外部文件变更 watcher`spawn_local_folder_watcher` 已通过 `buffer_store.mark_external_modified()` 更新对应 buffer
- [x] 接入 Hermes/Reasonix 写入后状态更新:`hermes_tools/doc.rs``hermes_tools/page.rs` 已通过共享 helper `write_local_markdown_page_body` 自动消费 BufferStore
- [ ] conflict UI 数据源改为 buffer stateAPI 已就绪,未接入
- [x] 浏览器可见读回:`task484-local-folder-page-body-refresh-readback-smoke.js` 已验证保存后刷新 ProseMirror 可见正文不丢失
### 2.3 必补测试
- [x] `document_buffer_reuses_workspace_path_key` — 通过 ✅
- [x] `document_buffer_marks_external_modified_for_clean_buffer` — 通过 ✅
- [x] `document_buffer_marks_stale_for_dirty_buffer` — 通过 ✅
- [x] `document_buffer_mark_saved_after_page_body_write` — 通过 ✅
- [x] 额外测试:`document_buffer_different_paths_different_keys`
- [x] 额外测试:`document_buffer_init_buffer_sets_file_version`
- [x] 额外测试:`document_buffer_mark_dirty_and_saved_round_trip`
- [x] 额外测试:`document_buffer_reuses_across_multiple_get_or_create_calls`
### 2.4 当前完成标准
- [x] `DocumentBuffer` 已有 mnote-web runtime `BufferStore` owner
- [x] `BufferKey` 可稳定区分不同文件
- [x] 11 个单测覆盖了关键 path + state transitions + save round-trip + 运行时入口
- [x] **已完成**tiptap/保存路由/Hermes 写入链已通过 `write_local_markdown_page_body` 统一消费 `BufferStore`
- [x] **已完成**watcher 已通过 `spawn_local_folder_watcher` 调用 `mark_external_modified`
- [x] **已完成**conflict UI 数据源已改为 buffer state 查询;`save_local_markdown_page_with_buffer_store` 在冲突发生时从 BufferStore 查询 `externalActor``dirtyState``bufferFileVersion` 并加入冲突信封;`/api/documents/buffer-state` 端点提供显式 buffer 状态查询;`document_page_shell` 在打开 local_folder 文档时调用 `init_buffer` 初始化 file_version。task451 smoke 已扩展为在每次冲突后验证信封包含这些 BufferStore 字段。
- [x] **已完成**:浏览器刷新后编辑器可见正文读回;验证 `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task484-local-folder-page-body-refresh-readback-smoke.js`
### 2.5 下一步
- 保存后刷新读回已由 `task484` 复核通过;后续若复现新问题,应以该 smoke 为回归入口。
- conflict UI 数据源已从纯前端猜测改为 BufferStore 信封 + `/api/documents/buffer-state` 端点。后续若需要实时更新(SSE 推送 buffer 变更事件)可在此基础上扩展。
---
## 3. Gap A7:落地 command context / context key — 🔄 基础设施完成,UI/命令接入未完成
### 3.1 当前代码锚点(更新后)
- `rust/crates/core-protocol/src/command.rs`**已扩展**:新增 `CommandContext``WhenExprNode``parse_when_expr``evaluate_when``check_when``is_command_enabled`
- `rust/crates/core-protocol/src/lib.rs`**已更新**:导出所有 command context 类型和函数
### 3.2 顺序任务完成情况
- [x] 定义最小 `CommandContext` 结构:包含 `workspace.sourceKind``workspace.readonly``tree.focusKind``tree.selectionCount``tree.selectionResourceKind``editor.dirty``editor.hasSelection``ai.canWrite` 共 8 个 key
- [x] 实现最小 `when` evaluator:支持 `key``!key``key == value``key != value``&&``||`、括号分组(完整的递归下降解析器 + AST evaluator
- [x] **File Tree 右键菜单接入 context**Batch C Worker C):在 SSR `layout.rs` 的 SIDEBAR_TREE_JS 中新增 `buildSidebarFileTreeContext()``evaluateSidebarFileTreeWhen()` 两个 JS 等价物。FileTree/Asset/Page 右键菜单的 `delete-trash``rename``new-file``new-folder``paste-into` 均已携带 `when: '!workspace.readonly'`。菜单构建时评估 when 表达式,命中时设置 `disabled: true` 并调整 title。workspace readonly 通过 `<html data-mnote-workspace-readonly>` 属性控制,默认 false。
- [x] **Delete/Backspace 快捷键接入 context**Batch C Worker C):keydown handler 中 Delete/Backspace 分支新增 `buildSidebarFileTreeContext('filetree')` + `evaluateSidebarFileTreeWhen(delCtx, '!workspace.readonly && tree.selectionCount')` 守卫。只读或无选中时阻止动作。
- [x] 把 AI 写入能力接入 context`ai.canWrite` key 已定义,并已在 hermes_tools 写入守卫中消费 `args.commandContext` / `args.command_context``ai.canWrite``workspace.readonly`
### 3.3 必补测试
- [x] `command_context_when_clause` — 通过 ✅(覆盖 key、!key、==、!=、&&、||、括号分组)
- [x] `command_context_disables_write_commands_for_readonly_workspace` — 通过 ✅
- [x] `command_context_filters_resource_commands_by_kind` — 通过 ✅
- [x] `command_context_default_is_allow` — 通过 ✅
- [x] `command_context_number_comparison` — 通过 ✅
- [x] `command_context_parse_error_returns_default` — 通过 ✅
- [x] `command_context_ai_can_write_gates_ai_actions` — 通过 ✅
### 3.4 当前完成标准
- [x] 最小 `CommandContext` 定义 + `when` evaluator 可工作
- [x] readonly / grant / resource kind 的禁用态可测试
- [x] **FileTree 右键菜单已接入**Batch C Worker C):delete-trash、rename、new-file、new-folder、paste-into 携带 `when: '!workspace.readonly'`
- [x] **Delete/Backspace 快捷键已接入**Batch C Worker C):keydown handler 中有 `!workspace.readonly && tree.selectionCount` 守卫
- [ ] **未完成**:下载、多选删除、粘贴、只读阻断的 smoke 保持回退兼容
- [x] **已完成**AI 写入能力 (`ai.canWrite`) 已接入 hermes_tools 写入守卫;`mnote.block.*``mnote.doc.markdown_edit``mnote.page.*``mnote.artifact.*``mnote.mindmap.apply_ops` 写入链共享 `ensure_write_authorized`,只读 scope、`workspace.readonly=true``ai.canWrite=false` 均会拒绝写入。
### 3.5 下一步
- `layout.rs` 右键菜单:已引入 `evaluateSidebarFileTreeWhen`Batch C Worker C 完成)
- Delete/Backspace:已接入(Batch C Worker C 完成)
- hermes_tools 写入守卫:已消费 `ai.canWrite` / `workspace.readonly` command context;后续若前端新增更多 AI 写入口,必须把同一份 `commandContext` 透传到 tool args。
### 3.6 Batch F 复核记录
- 2026-05-21Codex 将 Hermes 写入守卫收口为 `ensure_write_authorized``ToolCallInput``args.commandContext` / `args.command_context` 读取 `ai.canWrite``workspace.readonly`
- 2026-05-21`mnote.block.*` / `mnote.doc.markdown_edit` 继续经 `block::ensure_write_contract`,该函数已委托共享守卫;`mnote.page.*``mnote.artifact.*``mnote.mindmap.apply_ops` 也委托共享守卫。
- 2026-05-21:新增测试覆盖 `ai.canWrite=false``workspace.readonly=true` 的拒绝路径。
---
## 4. Gap B2Resource tools 到 Resource Tree 产品化 — 🔄 部分完成
### 4.1 当前代码锚点(已有未改)
- `rust/crates/mnote-web/src/routes/resource_trash.rs` — 媒体 / mindmap / table 生命周期使用 `tree.resource.*` 命令
- `rust/crates/mnote-web/src/routes/hermes_tools.rs`
- `rust/crates/mnote-web/src/hermes_tools/resource.rs`
- `rust/crates/mnote-web/src/hermes_tools/manifest.rs`
### 4.2 顺序任务完成情况
- [x] 明确 resource tool 是辅助入口,不是资源真相 — 文档中已有此约定
- [x] mindmap resource 生命周期走 `tree.resource.*` — 测试验证 ✅
- [x] office resource 生命周期走 `tree.resource.*` — ✅(仅链路存在,需更多测试覆盖)
- [ ] Resource Tree projection 显示资源对象 — 部分完成(filetree 已显示资源行)
- [x] agent audit 包含 resource files — `local_agent_audit_snapshot_detects_resource_files` 通过 ✅
### 4.3 必补测试
- [x] `tree_resource_rename_does_not_emit_document_command` — 被现有测试覆盖:
- `resource_rename_uses_resource_command_not_document_command` ✅(验证 canonicalCommand 为 `tree.resource.rename` 而非 `tree.node.rename`
- [ ] `mindmap_resource_trash_restore_keeps_markdown_body` — 部分覆盖:
- `mindmap_delete_restore_keeps_markdown_reference_out_of_lifecycle_command` ✅(验证 mindmap 生命周期不携带 documentId 从而不影响 markdown 页面)
- 尚未验证文档正文在 trash/restore 后是否完整
- [x] `local_agent_audit_snapshot_detects_resource_files` — 通过 ✅
- [x] browser smokes`task443``task445` 需 Playwright 后端环境
- **执行记录(2026-05-21, Batch B Worker C**:❌ **FAIL** — 两个 smoke 均因 `waitForURL` 断言 `/mindmap/` 路由超时,实际导航走的是 `/documents/...?resourceTab=...`。归因:**脚本过时**mindmap 路由改为 resourceTabsmoke 未同步)。结果文件:`tmp/task443-filetree-mindmap-click-active-row-smoke/result.json``tmp/task445-filetree-mindmap-switch-no-flicker-smoke/result.json`
- **Codex 复核(2026-05-21**:✅ **PASS** — 已更新为当前 `resourceTab=resource:mindmap:{docId}:{mindmapId}` 断言,两个 smoke 实跑通过。`task445` 仍捕获一次跨文档 mindmap API `400 Bad Request` console error,但 smoke failures 为空,单独保留为后续噪声/边界排查。
### 4.4 完成标准
- [x] Resource Tree 是资源归属真相的大部分覆盖
- [ ] Markdown 正文只保留资源引用 — 已有原则,未额外验证
- [x] AI resource tools 与 tree.resource 生命周期一致 — 验证通过
---
## 5. Gap C1Convex 迁移产品化补证据 — ✅ 完成
### 5.1 当前代码锚点(已有未改)
- `rust/crates/storage-convex-bridge/src/mapping.rs`
- `rust/crates/mnote-web/src/transport/convex.rs`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- `scripts/export-convex-workspace-to-local.js`
### 5.2 顺序任务完成情况
- [x] 定义 dry run response — `export-convex-workspace-to-local.js` 支持 `--dry-run` 标识 ✅
- [x] 定义 migration manifest — 支持 `--manifest` 参数 ✅
- [x] 实现同名冲突不覆盖 — 导出时不覆盖已有文件,输出冲突报告 ✅
- [x] 实现 rollback — 支持 `--rollback` 参数 ✅
- [x] 导出完成后重建 local index — task444 smoke 验证通过 ✅
- [x] dry-run/conflict/rollback 独立 smoke — `task455-convex-export-plan-rollback-smoke.js` 三个行为全部通过 ✅
### 5.3 必补测试
- [x] `task444-convex-workspace-export-local-fixture-smoke.js` — 通过 ✅
- [x] 新增 smokedry run 不写文件 — `task455` 验证 `--dry-run` 后 pages 目录不存在 ✅
- [x] 新增 smoke:同名冲突不覆盖 — `task455` 验证冲突后 exit(2) + 页面前内容保持 ✅
- [x] 新增 smokerollback 恢复导出前状态 — `task455` 验证 rollback 后 pages/assets 均删除 ✅
---
## 6. 最终收尾
### 6.1 验证命令结果
- [x] `cargo test -p core-protocol` — 41+6+2=49 passed ✅
- [x] `cargo test -p mnote-web document_buffer` — 8 passed ✅
- [x] `cargo test -p mnote-web resource_trash` — 6 passed ✅
- [x] `cargo test -p mnote-web local_agent_audit_snapshot` — 2 passed ✅
- [x] `npm run check:local-first-convex-guard``{"ok": true}`
- [x] `node scripts/task444-convex-workspace-export-local-fixture-smoke.js``{"ok": true}`
- [x] `cargo fmt --check --all --manifest-path rust/Cargo.toml` — 通过 ✅
- [x] `git diff --check -- <changed-files>` — 通过 ✅
### 6.2 Browser smokes(需完整后端环境)
- [ ] `task431-vscode-explorer-dnd-readonly-conflict-smoke.js`
- [x] `task451-local-markdown-conflict-resolution-ui-smoke.js`
- [x] `task452-local-search-index-browser-smoke.js`
- [x] `task453-local-folder-page-ai-changed-files-smoke.js`
- [x] `task443-filetree-mindmap-click-active-row-smoke.js`
- [x] `task445-filetree-mindmap-switch-no-flicker-smoke.js`
### 6.2.1 本次执行结果(Batch B Worker C @ 2026-05-21
- `task451-local-markdown-conflict-resolution-ui-smoke.js`
- **PASS** ✅ — 4 个子步骤全部通过:accept-disk、keep-current、merge-save、agent-conflict-source
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`
- 结果:`tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`
- `task452-local-search-index-browser-smoke.js`
- **PASS** ✅ — Codex 复核后更新旧断言;本地索引面板已渲染 backlinks/tags`README.md`、链接片段和 `#alpha` 可见
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task452-local-search-index-browser-smoke.js`
- 结果:`tmp/task452-local-search-index-browser-smoke/result.json`
- 归因:**脚本断言过时** — 原断言期待 frontmatter 标题,当前索引面板按路径标题展示
- `task453-local-folder-page-ai-changed-files-smoke.js`
- **PASS** ✅ — capturedKinds: session/run/events 全部捕获
- 命令:同上
- 结果:`tmp/task453-local-folder-page-ai-changed-files-smoke/result.json`
- `task443-filetree-mindmap-click-active-row-smoke.js`
- **PASS** ✅ — mindmap 资源行点击后 URL 使用 `/documents/...&resourceTab=resource:mindmap:...`,且对应 asset row 选中
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task443-filetree-mindmap-click-active-row-smoke.js`
- 结果:`tmp/task443-filetree-mindmap-click-active-row-smoke/result.json`
- 归因:**脚本过时后已更新** — mindmap 已改为 resourceTab 路由
- `task445-filetree-mindmap-switch-no-flicker-smoke.js`
- **PASS** ✅ — 页面和 mindmap 来回切换时 file tree root 稳定,选中态正确
- 命令:`PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task445-filetree-mindmap-switch-no-flicker-smoke.js`
- 结果:`tmp/task445-filetree-mindmap-switch-no-flicker-smoke/result.json`
- 归因:**脚本过时后已更新** — 仍有一次 400 console noise,后续单独排查
### 6.2.2 本次执行汇总
| Smoke | 结果 | 归因 |
|-------|------|------|
| task451 | ✅ PASS | — |
| task452 | ✅ PASS | 脚本断言过时,已按当前路径标题和链接片段语义更新 |
| task453 | ✅ PASS | — |
| task443 | ✅ PASS | 脚本过时,已改为 resourceTab 路由断言 |
| task445 | ✅ PASS | 脚本过时,已改为 resourceTab 路由断言;仍有 400 console noise |
---
### 6.3 总完成率
| Gap | 状态 | 关键产出 | 剩余 |
|-----|------|----------|------|
| A0 | 🔄 部分 | guard、task451、task452、task453 通过 | task431 等剩余 browser smokes 未复跑 |
| A2 | 🔄 运行时接入 + 浏览器读回完成 | `BufferStore` 实现 + 11 teststask484 验证保存后磁盘、PageAggregate、刷新后 ProseMirror 全部读回 | conflict UI 未接 buffer state |
| A7 | 🔄 基础设施完成 | `CommandContext` + when evaluator + 7 tests | UI/快捷键/菜单接入 |
| B2 | 🔄 部分 | 资源生命周期 + agent audit 测试通过;task443/task445 通过 | mindmap body 测试、跨文档 ResourceTab 400 噪声排查 |
| C1 | ✅ 完成 | export/rollback/dry-run 脚本 + task444/task455 smoke 全部通过 | — |
@@ -4,6 +4,8 @@
>
> 当前状态:`PROCESS`
>
> 2026-05-22 归档治理补充:当前 active `process/` 只保留可直接执行的少量收口稿;长期架构、愿景、参考矩阵、Wolai 对标基线、Mindmap 总设计、旧 AI 块级执行稿和 Convex Web 迁移入口已分别移动到 `reference/`、`done/` 或 `old/`。本文是后续调度入口,不代表下列所有历史入口仍在 `process/`。
>
> 目标:把本轮 design governance 后剩余的主线 `process/` 文档排成可执行顺序,避免后续 worker 在 active process 中自行猜优先级。
>
> 上位依据:
@@ -115,9 +117,9 @@
入口文档:
- `design/01-tree-first-graph-kernel/process/1-4-next-phase-execution-roadmap-v1.md`
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- `design/01-tree-first-graph-kernel/reference/1-4-next-phase-execution-roadmap-v1.md`
- `design/01-tree-first-graph-kernel/reference/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/done/1-6-next-phase-gap-closure-checklist-v1.md`
- `design/05-editor-mainline/done/5-26-resource-open-resolver-convergence-checklist-v1.md`
执行目标:
@@ -141,9 +143,9 @@
入口文档:
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- `design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
- `design/01-tree-first-graph-kernel/reference/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/done/1-6-next-phase-gap-closure-checklist-v1.md`
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`
执行目标:
@@ -165,7 +167,7 @@
入口文档:
- `design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`
- `design/05-editor-mainline/reference/5-5-page-aggregate-single-truth-alignment-v1.md`
- `design/03-rust-web/done/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`
@@ -193,7 +195,7 @@
入口文档:
- `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
- `design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
- `design/03-rust-web/reference/3-1-rust-web-long-term-checklist-v2.md`
执行目标:
@@ -235,7 +237,7 @@
入口文档:
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/reference/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/04-tree-domain/done/4-44-filetree-action-layer-paste-drop-delete-v1.md`
执行目标:
@@ -261,28 +263,36 @@
入口文档:
- `design/07-ai/done/7-27-online-markdown-writeback-final-content-truth-v2.md`
- `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
执行目标:
1. 修正 online / compat `mnote.doc.markdown_edit` 的 final markdown 写回真相
2. 避免 full_content 和多 operation 场景仍从旧 blocks 二次推导
3. 保持 local-first 默认编辑仍走 agent 原生 patch/diff,不把 `markdown_edit` 升回默认主路径
1. `7-27` 限定为历史 online / compat 回归证据,不再把 `mnote.doc.markdown_edit` 推回 active 设计
2. `7-18` 为当前 AI 编辑执行入口:MNote 只提供文件定位、授权边界、selection/context、审计和前台同步
3. Hermes / Reasonix 用自身文件 read / write / patch / diff 能力在 allowed roots 内修改 `.md`
4. 先实现 Agent Target Resolver / target chip / target picker,解决多工作区、多 tab、多资源时到底把哪个对象发送给 agent 的问题。
验收:
- `cargo test -p mnote-web hermes_tools -- --test-threads=1`
- 覆盖 full_content、多 op、inline content、selection scope 的回归测试
- Page AI composer 可见 target chip,能展示 workspace、target title、resourceKind、scope、dirty / readonly 状态。
- target picker 能在当前焦点、打开 tabs、树选中项之间选择;默认 target 来自 last focused editor / resource tab
- local-first agent 编辑 smoke 断言不调用 `mnote.doc.markdown_edit`
- Hermes / Reasonix run body 包含 `currentFile``allowedRoots``allowedFiles`、selection 和 dirty / readonly context。
- Hermes / Reasonix run body 包含 `targetPackage`,且切 tab 后不影响已冻结 run target。
- agent 写入后 watcher -> BufferStore -> Page Aggregate -> ProseMirror 可见更新。
- dirty buffer 下 agent 写入不会静默覆盖。
归档条件:
- `7-27` 的 6 步实施计划已完成并归档;后续只保留 `revisionRef` 注释可见性、复杂 GFM fallback、多余退役函数体清理等增强项,不阻塞 Batch E。
- `7-18` Phase A0/A/B/C 完成后归档到 `design/07-ai/done/`
### P3.2 ACP runtime 后续步骤
入口文档:
- `design/07-ai/process/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
- `design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
- `design/07-ai/done/7-15-page-ai-acp-agent-runtime-unified-layer-v1.md`
- `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
- `design/07-ai/done/7-34-acp-runtime-cleanup-availability-stability-tail-v1.md`
执行目标:
@@ -307,8 +317,8 @@
入口文档:
- `design/06-mindmap/process/6-mindmap-kernel-phase6-projection-editor-v1.md`
- `design/06-mindmap/process/6-mindmap-phase6-leptos-ui-shell-reuse-checklist-v1.md`
- `design/06-mindmap/reference/6-mindmap-kernel-phase6-projection-editor-v1.md`
- `design/06-mindmap/done/6-mindmap-phase6-leptos-ui-shell-reuse-checklist-v1.md`
- `design/06-mindmap/reference/6-mindmap-leptos-adapter-reference-notes-v1.md`
执行目标:
@@ -326,7 +336,7 @@
入口文档:
- `design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md`
- `design/05-editor-mainline/reference/5-9-wolai-aline-continuous-checklist-v1.md`
- `design/08-wolai-aline-test-flow/reference/wolai-aline-test-flow-v1.md`
- `design/05-editor-mainline/reference/5-7-wolai-page-tree-main-editor-experience-restoration-v1.md`
@@ -344,8 +354,8 @@
这些文档不进入当前直接执行序列,只有当产品优先级明确时再拆 checklist:
- `design/03-rust-web/reference/3-17-convex-export-web-entry-v1.md`
- CLI 已有迁移闭环;Web 管理入口未排期
- `design/old/03-rust-web/process/3-17-convex-export-web-entry-v1.md`
- CLI 已有迁移闭环;Web 管理入口在当前 Convex 默认退役后已降级为历史设想,不作为 active 或 reference 执行入口
- `design/04-tree-domain/reference/4-23-local-cloud-explicit-bridge-p3-candidate-v1.md`
- P3 候选,不能进入 local-first 默认主链。
- `design/07-ai/reference/7-17-acp-session-convex-sharing-contract-v1.md`
@@ -410,16 +420,18 @@
### Batch EAI 与资源工具
1. `7-27`(已归档到 `design/07-ai/done/`
2. `7-15` 后续拆分(下一批拆 `7-34`
3. `7-12` Phase B-F 拆分(下一批拆 `7-34`
4. `7-28` 仅在 mindmap / office AI 明确排期后进入
1. `7-18`
2. `7-27`(已归档到 `design/07-ai/done/`,只作为历史 online / compat 回归证据
3. `7-15` 后续拆分(下一批拆 `7-34`
4. `7-12` Phase B-F 拆分(下一批拆 `7-34`
5. `7-28` 仅在 mindmap / office AI 明确排期后进入
完成后预期:local-first AI 主路径稳定,cloud/compat 工具不再误导默认路径
完成后预期:local-first AI 主路径稳定,普通 Markdown 编辑不再依赖 MNote 专用编辑工具
当前批次状态:
- `7-27` 已由 Batch I 复核归档;`mnote.doc.markdown_edit` 在线写回以最终 markdown 为真源,`markdown_edit` 相关 targeted tests 与 manifest write contract 测试已通过。
- `7-18` 已成为当前 active AI 编辑控制面稿;后续实现不再保留 `mnote.doc.markdown_edit` 作为 local-first 默认、fallback 或 remote fallback。
- `7-29` 已完成 Worker A/B/C 审查,结论是 `7-15` Step 15-17 与 `7-12` Phase B/F 不应继续压在一个大文档里推进。
- `7-34` 已完成并归档到 `design/07-ai/done/`:旧 HTTP proxy 默认关闭、`page_ai_workflow` 受共享 tool executor 守卫保护;profile disabled list 三处同源;`capabilityScope` 中心校验已完成;ACP abort API 改为 best-effort cancellation 并主动推送 `run.aborted``task488` 已在当前 3000 真实通过,覆盖 3 个 Reasonix ACP session、并发 run、abort 与 SSE terminal event。cache benchmark 仍是 P2 度量;Phase C Review Mode 继续冻结。
@@ -1,166 +0,0 @@
# 1-9 [process] Batch B P1 Identity / BufferStore / Page Aggregate Execution Checklist v1
> 创建时间:2026-05-21
>
> 上位入口:`design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`
>
> 阶段:Batch B / P1 工作区身份、BufferStore、Page Aggregate 收口
>
> 当前状态:`PROCESS`
## 1. 目标
`1-8` 的 P1 顺序,把当前已经明确的 P1 缺口拆成可并行执行的小阶段:
1. `5-26` 正文附件 open resolver 与 FileTree open resolver 收敛未执行。
2. `1-6` Gap A2 的 BufferStore 运行时接入已完成,但保存后刷新读回仍有浏览器可见缺口。
3. `1-6` Gap A0 browser smoke 已复跑,A2/A7/B2/C1 仍有证据尾项。
4. `5-6` / `3-13` 的 Page Aggregate / GFM AST 尾项状态需要继续拆小,避免大文档长期停在 process。
5. 本轮 smoke 复核后,`task443` / `task445` / `task452` 已确认是过期断言,不是当前业务回归。
本阶段优先处理“已实现但缺浏览器/测试证据”与“已有计划但未落地”的缺口,不扩新产品面,不改变 local-first / tree-first 口径。
## 2. 上游入口
- `design/01-tree-first-graph-kernel/process/1-5-next-phase-sequential-execution-checklist-v1.md`
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- `design/05-editor-mainline/process/5-26-resource-open-resolver-convergence-checklist-v1.md`
- `design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
- `design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`
## 3. 不做事项
- 不把 `documents/save` 重新描述为 local-first 长期正文主入口。
- 不把 `md/text/code` 改成 OnlyOffice 或浏览器窗口默认打开。
- 不把 browser smoke 失败伪装成通过。
- 不移动 `design/old/**`
- Reasonix worker 不提交 git;最终由 Codex 复核和提交。
## 4. Reasonix Worker 拆分
### Worker A5-26 resource open resolver convergence
Owner
- `design/05-editor-mainline/process/5-26-resource-open-resolver-convergence-checklist-v1.md`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- 可新增或修改一个 `scripts/task48*-resource-open-resolver-*.js` smoke。
目标:
- 审查 `resolveResourceOpen``openEditorAttachmentEditTab``openEditorAttachmentNewWindow`、FileTree asset open/edit-mode 的重复判断。
- 如能小范围安全实现,抽出最小 shared resolver/helper,减少正文附件与 FileTree 分歧。
- 必须保持:正文附件 `弹窗编辑` 进入 main resource tab`新窗口编辑` 进入浏览器窗口,`md/text/code` 继续走 tiptap / text/code 渲染路径。
验收:
- `cargo test -p mnote-web sidebar_tree_runtime_opens_office_assets_through_resource_shell -- --test-threads=1`
- `node --check scripts/task463-onlyoffice-resolver-smoke.js`
- 如新增 smoke,则 `node --check scripts/task48*-resource-open-resolver-*.js`
### Worker BA2 BufferStore browser visible readback gap
Owner
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- 可新增或修改一个 `scripts/task48*-bufferstore-*.js` smoke。
目标:
- 复现并定位 `POST /api/page-body/write` 后磁盘与 Page Aggregate 已更新,但刷新后编辑器 DOM/截图未显示正文的问题。
- 优先做最小修复:让刷新后的主编辑器可见 DOM 消费最新 Page Aggregate / blockDocument。
- 若无法安全修复,必须输出可复现 bug 证据和具体源码定位,不把该项标完成。
验收:
- `cargo test -p mnote-web document_buffer -- --test-threads=1`
- `cargo test -p mnote-web page_aggregate -- --test-threads=1`
- 相关 browser smoke 能证明保存后刷新可见正文不丢失,或明确失败原因。
### Worker CA0/A2/B2/C1 baseline smoke evidence
Owner
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- 可新增 `.codex/reasonix-tasks/results/batch-b-worker-c-baseline-smokes.md`
- 不修改 Rust / JS 业务代码。
目标:
- 运行并记录 `1-6` 中尚未复跑的 browser smokes
- `task451-local-markdown-conflict-resolution-ui-smoke.js`
- `task452-local-search-index-browser-smoke.js`
- `task453-local-folder-page-ai-changed-files-smoke.js`
- `task443-filetree-mindmap-click-active-row-smoke.js`
- `task445-filetree-mindmap-switch-no-flicker-smoke.js`
- 失败时记录为环境阻塞或业务 bug,附 stdout / result.json 路径。
- 不做业务修复。
验收:
- 每个 smoke 有明确 `PASS` / `FAIL` / `BLOCKED`
- 对失败项给出 `tmp/.../result.json` 或日志路径。
### Worker DPage Aggregate / GFM AST tail audit
Owner
- `design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
- `design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`
- 可新增 `.codex/reasonix-tasks/results/batch-b-worker-d-pageaggregate-gfm-tail.md`
- 默认只改文档和 smoke 建议;如需改代码,先只输出建议,不直接改核心 Rust。
目标:
- 审查 `5-6` 中仍未勾选项是否已被后续实现覆盖,哪些应追认,哪些应拆新 checklist。
- 审查 `3-13` 中 GFM AST 尾项:空段落/空表格、legacy <-> Tiptap mark、保存失败错误、过渡 parser 标记、web_shell 临时分支。
- 给出下一步最小执行包,不把未验证项移动到 done。
## 5. 本轮执行记录
- 2026-05-21Batch B 仍保留为 process,原因是 `5-26` 还未形成可复核实现,A2 的冲突 UI 数据源接 BufferStore 也尚未落地。
- 2026-05-21`task443` / `task445` / `task452` 已在后续复核中修正为过期断言,不再作为当前业务回归证据。
验收:
- 输出明确表格:`已完成可追认` / `仍需代码修复` / `仅参考保留`
- 更新对应 process 文档的执行记录,但不归档。
## 5. Codex 复核项
- [ ] 逐个读取 Reasonix `final.md` / `result.json`
- [ ] 检查 `git diff`,确认 worker 没有越权修改。
- [ ] 运行 worker 涉及脚本的 `node --check`
- [ ] 运行受影响 Rust targeted tests。
- [ ] 对 UI / browser 可见项派发或执行独立浏览器测试。
- [ ] 更新本 checklist 的执行记录。
## 6. 浏览器测试阶段
Batch B 代码 / smoke / 审查完成后,再按实际变更拆分浏览器测试 worker:
- Browser Worker 1resource open resolver,验证正文附件 / FileTree asset / edit / new-window 分流。
- Browser Worker 2BufferStore 保存后刷新可见读回。
- Browser Worker 3Page Aggregate / GFM tail smoke,验证正文刷新、inline mark、task list 不回退。
Codex 必须复核浏览器测试结果;不能只相信 worker 文字总结。
## 7. 完成条件
- `5-26` 至少完成审查与最小实现,或明确不可安全实现的阻塞原因。
- `1-6` Gap A2 浏览器可见读回有明确结果:完成、失败 bug 或环境阻塞。
- `1-6` A0 browser smokes 有新的 PASS / FAIL / BLOCKED 证据。
- `5-6` / `3-13` 尾项不再是模糊 TODO,已拆出下一步执行包。
- `git diff --check` 通过。
- `codegraph sync .` 已运行。
- 本阶段完成后由 Codex 提交 git。
## 8. 本轮执行记录
- 2026-05-21Codex 建立 Batch B P1 checklist,并准备派发 Reasonix Worker A/B/C/D。
- 2026-05-21Reasonix Worker C 完成 baseline smoke 证据收集;`task451``task453` PASS`task452` 初判为 page-settings 本地索引面板渲染回归,`task443` / `task445` 初判为 mindmap smoke 仍按历史 `/mindmap/` 路由断言导致 false-negative。
- 2026-05-21Reasonix Worker D 完成 Page Aggregate / GFM AST 尾项审计;只改文档,不改业务代码。关键结论:`3-13` 中标题优先级被误标为完成,列对齐 marker 可追认,legacy/Tiptap marks、空单元格、保存失败可解释错误、写侧过渡标记仍需后续小包。
- 2026-05-21Reasonix Worker A/B 运行到 runner 超时后未生成 `final.md` / `result.json`,也未留下业务代码 diff;Codex 不采纳为完成项。A 的 transcript 只留下 resource open resolver 收敛计划,指出重复判断主要位于 `layout.rs` sidebar JS,原任务允许范围过窄;B 的 transcript 只留下 BufferStore / PageAggregate hydration 排查片段,未形成可复核结论。
- 2026-05-21Codex 复核裁定:`task452` 实为旧断言假失败,`task443` / `task445` 已改为 resourceTab 断言并实跑通过;本轮可采纳 C/D 的证据与审计,但 A/B 仍需拆成更小的后续任务,不把 `5-26` 或 Gap A2 标为完成。
@@ -1,719 +0,0 @@
# 1 [process] Tree-First Graph 内核方案 v1
> 更新时间:2026-04-22
>
> 当前优先级入口:
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-1-rust-web-long-term-checklist-v2.md`
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
## 1. 文档目的
本文回答的不是“页面如何加速”,而是更底层的问题:
> **mnote 长期到底应该以什么作为统一对象内核。**
在前一轮讨论里,方向经历了一个重要修正:
- 不是让 `Mindmap` 成为系统主投影
- 也不是让“导图页面”变成新的系统中心
- 而是让 **`tree-first graph` 成为统一结构内核**
- `Mindmap` 只是这个内核的一种可视化挂件
这份文档的目标,是把这个判断正式固定下来。
---
## 2. 先给结论
结论只有一句:
> **mnote 的长期核心不应是 `BlockNote-first`,也不应是 `Mindmap-first`,而应是 `tree-first graph kernel`。**
也就是说:
- **树** 是主骨架
- **图** 是横向引用扩展
- **Mindmap** 是树/图的一种空间化视图
- **Sidebar / 页面树 / 文件树 / 文档阅读页 / 搜索结果 / AI 面板** 都只是同一内核的不同投影
因此,长期正确方向不是:
- “把所有东西都画成导图”
而是:
- “让所有对象共享同一份结构内核,再让不同视图各自投影”
---
## 3. 为什么不是 Mindmap-first
虽然 Mindmap 在结构表达上很强,但它不适合被定义为主中心。
原因有四个。
### 3.1 导图 UI 不是所有场景的最佳交互
下面这些场景不适合被强行导图化:
- Sidebar 导航
- 文件树浏览
- 文档阅读
- 搜索结果浏览
- 历史版本查看
- AI 工具面板
这些场景里,很多时候:
- 列表更合适
- 树表更合适
- 阅读流更合适
- 搜索结果卡片更合适
所以:
> **导图是一种强表达能力的视图,不是所有结构都应默认进入的主视图。**
### 3.2 如果 Mindmap 成为主中心,系统会被导图交互绑架
一旦把 Mindmap 当主投影,后面很容易出现:
- 结构建模被导图控件的数据形状反向约束
- 页面/文件/章节/引用都被迫适配导图编辑器
- 视图层规则污染对象层规则
这会让“结构内核”被“某种 UI 控件”夺走主导权。
### 3.3 当前本地代码已经说明 Mindmap 更像重操作壳
当前:
- [`MindmapBlock.tsx`](/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx)
同时承担:
- 内嵌块
- 独立页
- 工具栏
- 右键菜单
- 导航器
- 缩略图
- 本地全屏视图
这说明它当前的本质更接近:
- 一个前端重交互操作层
而不是:
- 一个稳定、极简、内核级结构模型
### 3.4 Mindmap 已经适合退到“挂件层”
真正更合理的位置是:
- 它继续保留
- 但作为 `tree-first graph kernel` 的挂件和投影
- 而不是事实源
---
## 4. 为什么是 Tree-First Graph
这是因为 mnote 当前最稳定、最通用、最可渐进迁移的共同语义,本质上是:
- **父子层级**
- **局部子树**
- **对象引用**
这正对应:
-
- 子树
- 图边
### 4.1 树是最自然的主骨架
下面这些天然就是树:
- 工作区结构
- 页面树
- 文件树
- 文档大纲
- PDF 章节结构
- 页面内部结构
- 导图节点结构
因此“树优先”不是一种美学偏好,而是数据现实。
### 4.2 图是必须存在的扩展层
但系统又不可能只有树,因为还存在:
- 双向引用
- 页面引用页面
- 块引用块
- 摘要引用原文
- PDF 章节引用页码/附件
- AI 生成节点引用证据节点
这些都不是父子关系,而是横向边。
所以系统最终一定是:
- 树作为主骨架
- 图作为引用扩展
也就是:
> **tree-first graph**
### 4.3 这种结构最适合 Rust 内核化
因为它天然适合:
- typed node
- typed edge
- subtree query
- graph traversal
- object projection
- CLI / AI tool 直接操作
这比“以某个前端编辑器数据格式为真相”更适合进入 Rust core。
---
## 5. 内核定义
## 5.1 统一内核
长期建议把 mnote 的结构真相定义为:
- `WorkspaceKernel`
- `Node`
- `Edge`
- `Projection`
### 5.1.1 当前已落地的 Rust 类型
2026-04-16 这轮已经在 Rust `core-protocol` 中补入统一 kernel 类型定义,入口文件为:
- `/mnt/Data1T/mnote/rust/crates/core-protocol/src/kernel.rs`
当前已落下的主类型包括:
- `KernelNode`
- `KernelEdge`
- `KernelProjectionRequest`
- `KernelProjectionResult`
- `KernelSubtreeRef`
- `KernelSubtreeResult`
- `KernelGetNode`
- `KernelGetSubtree`
- `KernelListChildren`
- `KernelListEdges`
- `KernelTraverseGraph`
- `KernelCreateNode`
- `KernelUpdateNode`
- `KernelMoveSubtree`
- `KernelAttachEdge`
- `KernelDetachEdge`
这意味着这份文档里的 kernel 语义已经不再只是概念,而是进入了 Rust 可复用协议层。
### 5.2 Node
每个对象都是带类型的节点。
候选节点类型包括:
- `workspace`
- `folder`
- `page`
- `section`
- `paragraph`
- `asset`
- `book`
- `pdf`
- `mindmap`
- `mindmap_node`
- `table`
- `query_view`
- `summary`
- `ai_note`
- `reference_anchor`
当前 Rust 中的最小首批节点类型已经固定为:
- `workspace`
- `folder`
- `page`
- `section`
- `asset`
- `book`
- `pdf`
- `mindmap`
- `mindmap_node`
- `summary`
- `ai_note`
- `reference_anchor`
- `content_node`
- `index_node`
注意:
> **这里的关键不是名字,而是“页面、文件、书籍、摘要、AI 结果不再分属不同系统,而是同一内核里的 typed node”。**
### 5.3 Edge
边分成两类:
#### A. 骨架边
- `parent_of`
- `child_of`
- `contains`
#### B. 扩展边
- `references`
- `backlinks_to`
- `source_of`
- `derived_from`
- `summarizes`
- `indexes`
- `points_to`
当前 Rust 中的最小首批边类型已经固定为:
- `parent_of`
- `child_of`
- `contains`
- `references`
- `backlinks_to`
- `source_of`
- `derived_from`
- `summarizes`
- `indexes`
- `points_to`
这样:
- 树结构靠骨架边维持
- 网状关系靠扩展边表达
### 5.4 Projection
Projection 不是数据真相,只是同一内核的不同投影。
长期主要投影包括:
- Sidebar tree
- 页面树
- 文件树
- 文档阅读流
- Mindmap
- 搜索结果页
- AI 操作视图
- 未来可能的关系图 / 时间线 / 表格视图
当前 Rust 中已经固定的投影种类包括:
- `sidebar_tree`
- `page_tree`
- `file_tree`
- `mindmap`
- `read_view`
- `search_results`
- `rag_index`
### 5.5 四层边界
为了避免后续又把前端页面壳误当事实源,当前主线边界固定为四层:
1. **事实源**
- 统一 `tree-first graph kernel`
- 只承载 `node / edge / subtree / audit`
- 不承载具体前端 UI 状态
2. **投影**
- `sidebar tree`
- `page tree`
- `file tree`
- `mindmap projection`
- `read view`
- `search / rag projection`
3. **编辑器**
- `BlockNote`
- `Mindmap canvas`
- `OnlyOffice`
- 未来其他专用内容编辑器
4. **外挂 / 挂件**
- AI 面板
- 评论
- 历史
- 回链
- 右侧辅助信息面板
固定规则是:
- 事实源只在 kernel
- 投影不拥有对象真相
- 编辑器不等于对象模型
- 外挂只消费 kernel 或 projection,不再私自定义第二套对象真相
### 5.6 术语冻结
这一轮同时把后续文档和任务要共用的术语冻结如下:
- `node`
指统一内核中的 typed object
- `edge`
指节点之间的 typed relation
- `projection`
指从 kernel 派生出的视图结果,不是事实源
- `subtree`
指从某个 root node 出发的一段有界层级结构
- `content node`
指以正文载荷为主的节点,适合交给 BlockNote 之类编辑器处理
- `reference edge`
指非父子关系的引用边,例如页面引用、证据引用、来源引用
- `summary node`
指对某段 subtree 或 source node 做摘要后的节点
- `index node`
指为搜索/RAG 建立的结构索引节点
### 5.7 现阶段并入策略
当前主线按下面的口径迁移:
- 暂时继续存在,但应逐步并入 kernel 的对象:
- 页面
- Sidebar 数据集
- 搜索结果对象
- 导图树数据
- 当前明确不是事实源、只保留为编辑或展示壳:
- BlockNote 文档结构
- Mindmap 前端画布状态
- OnlyOffice 页面壳
- 各类 AI host / panel 本地状态
---
## 6. 与当前系统的关系
## 6.1 与 Sidebar / 页面树 / 文件树的关系
这些都不应再视为独立系统。
长期应改成:
- 同一份结构内核
- 在 Sidebar 中投影为导航树
- 在文件页中投影为文件树
- 在某些对象页中投影为结构树
所以:
> **Sidebar 不是“一个前端导航组件”,而是 kernel 的树投影。**
## 6.2 与 Mindmap 的关系
Mindmap 不再是中心,而是:
- kernel 的空间化图形视图
- 适合做结构浏览、重组、章节展开、节点重排
但它不再是:
- 唯一主视图
- 唯一对象真相
### 6.3 与 BlockNote 的关系
长期上,`BlockNote` 应从“系统底座”降级为:
- 内容编辑挂件
- 某类页面内容编辑器
而不是:
- 页面结构本体
- 工作区结构内核
也就是说,未来不是:
- 页面 = BlockNote 文档
而更接近:
- 页面 = kernel 子树
- BlockNote = 某类内容节点的编辑器
### 6.4 与 AI 的关系
AI 不应直接面对“页面壳”和“前端控件”,而应直接面对 kernel。
长期上 AI 更适合操作:
- 节点
- 子树
- 引用边
- 结构索引
- 节点摘要
例如:
- 创建节点
- 拆分章节为子树
- 为节点补 refs
- 生成 summary 节点
- 把 PDF 章节树挂到 book 节点下
这比“模拟导图 UI 操作”或“模拟 BlockNote 操作”更稳定。
### 6.5 与 RAG 的关系
RAG 不再只是:
- 文本块检索
而应演进为:
- 结构索引检索 + 正文证据回查
例如:
- 一本书先变成书籍节点
- 再生成章节树
- 再生成章节子树摘要
- 再用节点 refs 指回原始页码、附件、正文块
这样检索时可以:
1. 先命中结构层
2. 再下钻子树
3. 再回查原文证据
这正适合复杂文档。
---
## 7. 结合 BookRAG 的启发
用户提到的 `BookRAG` 给出的核心启发,不是“做一个导图页面”,而是:
> **复杂文档应该先被抽成层级结构索引,再做检索与生成。**
这和 mnote 非常契合。
### 7.1 书籍对象的建议形态
长期上可以采用:
- 一个 `book` 节点
- 一个 canonical 章节树
- 每章是一个 subtree
- 每小节是更深层节点
- 节点 refs 指向:
- 页码
- PDF
- 原文块
- 摘要
- AI 生成说明
### 7.2 不建议直接复制很多份 chapter mindmap 实体
更好的方式是:
- 逻辑上是一棵 canonical tree
- 章节导图只是 subtree projection
- 需要时再做缓存或派生视图
否则会出现:
- 多份结构副本
- 同步成本
- 版本冲突
### 7.3 这和当前 Rust Mindmap 协议是兼容的
当前 Rust 已经有:
- `MindmapTreeNode`
- `MindmapOp`
- `mindmap_get_subtree`
- `mindmap_outline_to_mindmap`
这意味着:
- 以树为真相
- 以子树为检索和投影单位
并不是从零开始。
---
## 8. 这条路线和“主 Mindmap”有什么本质差异
两者差异很大。
### 8.1 错误路线
错误路线是:
- 整个工作区变成一个超级导图页面
- 所有东西都围绕导图控件组织
- UI 形态决定对象语义
### 8.2 正确路线
正确路线是:
- 整个工作区共享一份结构内核
- Mindmap 只是可视化挂件
- 列表、树表、阅读流、搜索结果也都是合法投影
- 对象语义先于 UI 形态存在
---
## 9. 长期分层建议
## 9.1 Kernel Layer
Rust 内核负责:
- typed node
- typed edge
- subtree query
- graph traversal
- projection query
- 权限
- trace
- 版本
- 审计
## 9.2 Service Layer
Rust Web 层负责:
- API
- SSR 页面壳
- SSE / WS
- 结构查询
- AI bridge
- projection 请求分发
## 9.3 Projection Layer
不同前端视图负责:
- Sidebar tree projection
- 阅读页 projection
- Mindmap projection
- 搜索 projection
- AI 操作 projection
## 9.4 Editor Layer
编辑器只是挂件:
- BlockNote
- Mindmap canvas
- OnlyOffice
- 未来别的专用编辑器
它们都不再是系统底座。
---
## 10. 为什么这条路线更适合替代 BlockNote 世界
因为它不是“再造一个更大的前端编辑器”,而是:
- 先把页面结构、对象结构和引用结构收口
- 再让 BlockNote 退化成一个专用内容编辑挂件
长期上,页面不再被定义成:
- 一个 block 文档
而更接近:
- 一个子树容器
这样未来才可能逐步实现:
- 页面结构独立于 BlockNote
- 页面中的某些内容节点仍可用 BlockNote 编辑
- 某些结构节点则改用别的编辑/操作方式
这比一次性整体替掉 BlockNote 更现实。
---
## 11. 风险与约束
### 11.1 不要把整个 workspace 真存成一条超大 JSON 树
逻辑上统一成一棵树,不等于物理上只能是一条大对象。
长期更合理的是:
- 逻辑统一
- 物理分片
- 子树加载
- 局部版本
- 局部缓存
### 11.2 不要让 Projection 反向定义内核
例如:
- Mindmap 控件的数据格式
- BlockNote 的块数据结构
- Sidebar 某次渲染需要的 rows
这些都不能反过来定义 kernel 真相。
### 11.3 不要过早把所有内容节点都树化成同一种文本节点
结构树适合表达:
- 层级
- 目录
- 引用
- 摘要
- 索引
但富文本正文仍然可能需要自己的内容模型。
所以长期更合理的是:
- 树/图内核负责结构
- 内容节点负责正文
- 两者通过 typed node 接口连接
---
## 12. 最终结论
最终结论可以固定成下面这句话:
> **mnote 的长期方向不是 Mindmap-first,而是 Tree-First Graph KernelMindmap 只是其中一种挂件、投影和操作器。**
这意味着:
- 页面树、文件树、Mindmap、RAG 结构索引、AI 结构操作,本质上都应收口到同一结构内核
- `BlockNote` 不再是系统定义页面的唯一方式
- Rust 最终不只是承接 API 或导图对象,而是承接整个统一结构真相
如果后续继续推进,真正该优先做的不是“先重写导图 UI”,而是:
1. 定义统一 kernel node / edge 模型
2. 定义 subtree / projection / reference 查询协议
3. 让 Sidebar、搜索、AI、Mindmap 开始直接消费 kernel
4. 最后再逐步边缘化 `BlockNote`