chore: 收口 review 执行清单与 runtime 验证

- 补齐 design/10-review 执行清单、验收标准与相关设计治理记录

- 迁移已完成的 tree、mindmap、runtime fallback、AI kernel 等设计和缺陷条目

- 推进 Rust Web runtime、tree/sidebar、page aggregate、mindmap 与 OnlyOffice 路由侧验证支撑

- 增加 task177-task180 smoke/audit 脚本及前端相关测试覆盖
This commit is contained in:
lix-2026
2026-05-14 05:52:08 +08:00
parent b4a452a8b7
commit 96e03645f7
69 changed files with 4780 additions and 979 deletions
@@ -3,10 +3,11 @@
> 状态说明:
> - 本稿定义的 default gate 已在当前主线代码中成立:`mnote-web` 是 `3000` ownerNext 仅保留 legacy compat/debug 边界
> - `MNOTE_WEB_ENABLE_LEGACY_NEXT_COMPAT` 默认关闭与 `task117` guard 已落地,故迁入 `done/`
> - 2026-05-13 追加说明:本文中“长期 agent 执行面收口到 `mnote-cli`”是历史完成口径;当前 AI 长期方向已由 `design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖为 Hermes 页面内客户端 + mnote Hermes skill/plugin
## 目标
将 Next App Router 从 3000 默认主链降级为显式 legacy/debug/internal 兼容边界。默认首页、文档页、搜索页、导图页、tree SSE 与 AI bridge host 均由 `mnote-web` 承接;长期 agent 执行面收口到 `mnote-cli`
将 Next App Router 从 3000 默认主链降级为显式 legacy/debug/internal 兼容边界。默认首页、文档页、搜索页、导图页、tree SSE 与 AI bridge host 均由 `mnote-web` 承接;AI 会话与编排长期归 Hermesmnote 通过 Hermes skill/plugin 暴露业务工具
## 当前 owner
@@ -15,7 +16,7 @@
- `/search`Rust Web `search::shell`Search projection owner `rust-kernel`
- `/mindmap/{doc_id}/{mindmap_id}`Rust Web `mindmap_shell::mindmap_object_shell`Mindmap projection owner `rust-kernel`
- `/api/tree/events`Rust Web SSEstream owner `rust-web`
- `/api/hermes/bridge`Rust Web 兼容 AI bridge;仅作为过渡边界,不是长期 agent 执行面
- `/api/hermes/bridge`Rust Web 兼容 AI bridge;仅作为过渡边界,不是新的 Hermes 页面内客户端代理
- `/api/compat/next/*`legacy compat boundary,仅用于迁移期兼容与调试。
## Gate
@@ -23,12 +24,12 @@
- `MNOTE_WEB_ENABLE_LEGACY_NEXT_COMPAT` 默认关闭;只有显式设置为 `1/true/yes` 才允许 fallback proxy。
- 默认主路径不得返回 `x-mnote-legacy-upstream: next-app-router`
- 导图、搜索、文档页 contract 不得再把 `next-app-router` 声明为主 runtime。
- `/api/ai-agent/run` 当前仍保留兼容 route,但长期应降为 `mnote-cli` host / adapter,结构化写入必须经 Rust runtime,外置 agent 不得拥有第二执行面
- `/api/ai-agent/run` 当前仍保留兼容 route,但长期应退出页面 AI 主路径;新的页面 AI 应走 Hermes client proxy,结构化写入必须经 mnote Hermes plugin -> Rust runtime / kernel
## 删除条件
- 删除 `/api/compat/next/sidebar`Sidebar、workspace shell、file tree smoke 均证明 Rust projection 可覆盖默认入口。
- 删除 `/api/compat/next/ai-agent/run`AI 面板默认请求统一 CLI hostlegacy 调用方清零;Hermes 仅保留为可插拔外置 agent
- 删除 `/api/compat/next/ai-agent/run`AI 面板默认请求统一 Hermes client proxylegacy 调用方清零;`mnote-cli` 只可作为 mnote plugin 内部适配器或调试入口
- 删除 fallback proxy`task117` 默认关闭 legacy compat 后覆盖首页、文档页、搜索页、导图页与 tree SSE。
## 验收命令
@@ -1,4 +1,4 @@
# 3-15 [process] Runtime Fallback 退场 Checklist v1
# 3-15 [done] Runtime Fallback 退场 Checklist v1
> 更新时间:2026-05-09
>
@@ -9,7 +9,12 @@
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
>
> 2026-05-13 口径修正:
> - 本清单中“删除 `provider=hermes` 直连分支”的结论仍然有效,含义是旧 `/api/ai-agent/run` 不再把 Hermes 当 fallback/provider 分支。
> - 新长期方向不是恢复旧 provider 分支,而是新增正式 Hermes 页面内客户端与 mnote Hermes skill/plugin。
> - 因此“退场旧 direct Hermes fallback”和“页面 AI 长期调用 Hermes”不冲突。
## 1. 目标
@@ -59,14 +64,14 @@
- [x] 把 `BlockNote` 从“系统默认可退回编辑器”降为 `recycle` 参考实现
- [x] 活跃设计稿中不再把 `BlockNote` 写成系统 fallback,只允许写成历史对照链
### 4.2 AI provider 直连分支
### 4.2 AI provider 直连分支
- [x] 删除 Next `/api/ai-agent/run``provider=codex` 直连分支
- [x] 删除 Next `/api/ai-agent/run``provider=hermes` 直连分支
- [x] 删除 Rust `compat` 中代理到 legacy Next AI route 的优先分支
- [x] 删除 Rust `compat` 中 direct Hermes 分支
- [x] 删除 Rust `compat` 中 document AI orchestrator fallback 分支
- [x] 统一只保留 `mnote-cli` host 作为默认主执行入口
- [x] `/api/ai-agent/run` 不再作为页面 AI 主执行入口;历史 `mnote-cli` host 只保留为兼容/内部适配,不再代表长期页面 AI 主线
- [x] 次要 provider 请求改为明确失败,不再静默降级
### 4.3 legacy Next compat / alias / debug 壳
@@ -133,7 +138,7 @@
### 8.2 必须消失
- [x] 默认运行时不再自动触发 `BlockNote` fallback
- [x] 默认 AI 运行时不再直连 `Hermes` / `Codex` / 旧 orchestrator compat
- [x] 默认 AI 运行时不再通过旧 `/api/ai-agent/run` provider 分支直连 `Hermes` / `Codex` / 旧 orchestrator compat;新的 Hermes 页面内客户端应走正式 Hermes client proxy
- [x] 默认 gateway 不再把 legacy Next 当作未迁路径的总兜底
- [x] `/api/documents/page` 不再在 runtime 中组装 TS fallback projection
@@ -120,6 +120,8 @@
### 3.2 Hermes / AI tool registry 归属仍需继续收口
2026-05-13 口径更新:本文记录的是 `3104` 退役阶段的历史边界判断,不再代表当前 AI 长期编排主线。当前 AI 长期方向已改为 Hermes 页面内客户端 + mnote Hermes skill/plugin;旧 `openai-agents-python` 编排层与旧 `/api/hermes/bridge` 只作为历史或兼容路径看待。
当前前端 AI route 与后端 `openai-agents-python` 编排层已经不再通过 `MNOTE_WEB_BASE_URL` 回源 `mnote-web /api/hermes/bridge` 获取 runtime tool 结果,而是走后端 orchestration + 本地 `bridge-runtime`
这意味着:
@@ -302,12 +304,12 @@
目标:
- AI runtime tool bridge 不再强依赖 `MNOTE_WEB_BASE_URL`
- `openai-agents-python` 编排层与工具桥接边界收口到正式 AI 服务面
- 历史 `openai-agents-python` 编排层不再被描述为长期 AI 服务面;新的正式边界是 Hermes client proxy + mnote Hermes plugin
推荐方向:
-`wolai-backend:8000` 持有 AI orchestration 正式入口
- Rust `bridge-runtime` 作为内核语义执行层
-Hermes 持有 session / run / tool event / usage / model 编排
- Rust `bridge-runtime` 作为 mnote 业务语义执行层,经 Hermes skill/plugin 暴露
- `3000` 只负责页面与同源 API 边界,不再额外绕回 `3104`
当前状态:
@@ -47,7 +47,7 @@
- [x] `Phase 2` 文档阅读态分离已经有实质代码
- [x] `Phase 3` 主 Sidebar 已出现 `kernelSidebarTree` 消费接缝
- [x] `Phase 4` 搜索已做 host/runtime 拆分,并开始返回 `nodeId` / `subtreeRootId` / `evidence`
- [x] `Phase 5` AI 面板已做 host/runtime 拆分,并开始向 AI bridge 传递 `node` / `subtree` / `outline` / `evidence`长期执行面应收口到 `mnote-cli`Web 只作为 CLI host / client`openai-agents-python` / Hermes / Codex 只作为可插拔外置 agent
- [x] `Phase 5` AI 面板已做 host/runtime 拆分,并开始向 AI bridge 传递 `node` / `subtree` / `outline` / `evidence`2026-05-13 起长期方向改为 Hermes 页面内客户端 + mnote Hermes skill/plugin,旧 CLI host 只保留为历史/兼容证据
- [x] `Phase 6` Mindmap 独立页已去掉 `editorStub` 主入口
- [x] `Phase 7` 阅读态已直接消费 `pageSubtree``BlockNote` 也已不是文档页默认唯一入口
@@ -107,7 +107,7 @@
| Phase 2 | 文档阅读页 server-first 化 | `PARTIAL` 接近 `DONE` | 阅读态/编辑态已明显分离,但仍有旧链回退和大量客户端状态集中在 `DocumentContent` |
| Phase 3 | Sidebar / 树结构 Rust 化 | `PARTIAL` 偏早期 | 主 Sidebar 已以 `kernelSidebarTree` 作为主树来源,但整体仍是超大客户端组件 |
| Phase 4 | 搜索 Rust 化与 island 化 | `PARTIAL` | host/runtime 懒加载拆分已做,结果形状也开始带 `nodeId` / `subtreeRootId` / `evidence`,但仍未完成 server-first 搜索页 |
| Phase 5 | AI 面板 bridge island 化 | `PARTIAL` | host/runtime 拆分已做,且已开始把 `node` / `subtree` / `outline` / `evidence` 送入 AI bridge长期执行统一落在 `mnote-cli`Web 面板只做 host / adapter,但 runtime 仍重,协议也未统一到真正“最小壳” |
| Phase 5 | AI 面板 Hermes client island 化 | `PARTIAL` | host/runtime 拆分已做,且已开始把 `node` / `subtree` / `outline` / `evidence` 送入 AI bridge后续应把 Web 面板退成 Hermes 页面内客户端,把 mnote 能力注册为 Hermes skill/plugin,但 runtime 仍重,协议也未统一到 Hermes session/run/tool contract |
| Phase 6 | Mindmap 独立对象化 | `PARTIAL` | 独立页脱离 editor stub 主入口,并补出 `standalone` / `documentBridge` 边界,但仍是客户端重壳 |
| Phase 7 | BlockNote 孤岛化 | `PARTIAL` | 阅读态已直接消费 `pageSubtree`,编辑器按需挂载,但外围 drawer/panel 仍集中在同一内容组件 |
| Phase 8 | 旧前端壳下线 | `PARTIAL` 接近 `DONE` | 3000 gateway / 文档 shell / tree realtime / Search / AI bridge / Mindmap object shell 已由 `mnote-web` 持有;Next App Router 降为 legacy compat / island bundle source,旧链彻底删除仍待后续 |
@@ -290,10 +290,15 @@
---
## 10. Phase 5AI 面板进一步收口为 `mnote-cli` host / 纯桥接 island
## 10. Phase 5AI 面板收口为 Hermes 页面内客户端 / mnote plugin bridge
**当前状态:`PARTIAL`**
> 2026-05-13 口径更新:
> - 本阶段原先把页面 AI 继续收口为 `mnote-cli host / client`,现在已被 `design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖。
> - 新长期方向是:页面 AI 面板使用 Leptos 实现 Hermes 页面内客户端;Hermes session/message/tool event/usage/model 是 AI 会话真相;mnote 只通过 Hermes skill/plugin 暴露页面、树、artifact、edge 等业务能力。
> - `mnote-cli` 只能作为 plugin 内部适配器或调试入口,不能再被写成页面 AI 唯一长期执行面。
### 10.1 已落地事实
- [x] 文档页 AI 已拆为 host + runtime
@@ -301,24 +306,24 @@
- [DocumentAiAgentPanel.runtime.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx)
- [x] Mindmap / OnlyOffice 也有类似 runtime 拆分
- [x] `GlobalAiAgentHost` 已不在 `(app)/layout.tsx` 主布局中挂载
- [x] 文档页 AI 已开始把 `node` / `subtree` / `outline` / `evidence` 传给 AI bridge长期执行面应收口到 `mnote-cli`,页面 AI 只保留为 CLI host / client`openai-agents-python` / Hermes / Codex 只作为可插拔外置 agent
- [x] 文档页 AI 已开始把 `node` / `subtree` / `outline` / `evidence` 传给 AI bridge后续应改为 Hermes run/session context,由 Hermes 通过 mnote skill/plugin 回读和写入业务事实
- [DocumentAiAgentPanel.runtime.tsx](/mnt/Data1T/mnote/wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx)
- [route.ts](/mnt/Data1T/mnote/wolai-frontend/src/app/api/ai-agent/run/route.ts)
### 10.2 当前还没完成
- [ ] runtime 组件仍然非常重
- [ ] “最小会话协议、统一流式协议、页面上下文协议”还更多体现在组件内部,不是系统级协议层
- [ ] 还不能说 AI 面板已经变成“纯桥接壳
- [ ] “最小会话协议、统一流式协议、页面上下文协议”还更多体现在组件内部,不是 Hermes client proxy / plugin tool contract
- [ ] 还不能说 AI 面板已经变成“只调用 Hermes 的页面内客户端
- [ ] 页面级 AI adapter 仍然很大,只是改成了懒加载
- [ ] AI 已能消费 `node` / `subtree` / `outline` / `evidence` 上下文,但还不是统一的 `mnote-cli` kernel-first tool 协议;Web 面板也还没有彻底退成单纯 host / adapter
- [ ] AI 已能消费 `node` / `subtree` / `outline` / `evidence` 上下文,但还没有进入稳定 Hermes session/run context 与 mnote plugin tool 协议;Web 面板也还没有彻底退成 Hermes client
### 10.3 v2 后续任务
- [ ] 继续下沉 runtime 内状态与协议
- [ ] 把会话、tool event、client action 收口到共享协议层
- [ ] 让 runtime 再减重,而不只是拆文件
- [ ] 把当前上下文注入进一步收口为稳定 kernel node / subtree / edge bridge
- [ ] 移除页面 AI 对旧 `/api/ai-agent/run` 主路径的长期依赖,改走正式 Hermes client proxy
- [ ] 把会话、message、tool event、usage、model 选择交给 Hermes session 存储
- [ ] 把 mnote 页面、树、artifact、edge 能力注册为 Hermes skill/plugin tools
- [ ] 把当前上下文注入进一步收口为 Hermes run context + 稳定 kernel node / subtree / edge tool bridge
---
@@ -25,7 +25,7 @@
- Rust 内核和 Web 承载层如何分工
- 前端哪些部分应该退出当前重 React 壳
- `axum``Leptos` 这类 Rust Web 方案是否值得采用
- `mnote-cli` 应如何成为唯一长期 agent 执行面
- 页面 AI 应如何作为 Hermes 页面内客户端接入,并让 mnote 通过 Hermes skill/plugin 暴露业务能力
- 默认主编辑器已经切到页面内 `leptos-tiptap` island 后,剩余重编辑兼容链应如何继续收口
---
@@ -57,7 +57,8 @@
- **业务执行面:现有 Rust workspace 继续作为唯一业务真执行面**
- **Web 承载层:`axum`**
- **页面渲染模型:`Leptos Islands`**
- **AI 执行面:`mnote-cli`**
- **AI 会话与编排面:Hermes**
- **mnote AI 能力面:Hermes skill/plugin -> Rust runtime / kernel**
- **最后保留的重编辑兼容孤岛:少量编辑器 runtime(当前默认主编辑器已是页面内 `leptos-tiptap` island`BlockNote` 仅保留为 `recycle/` 历史参考副本)**
一句话概括:
@@ -180,7 +181,7 @@
- 文档查询与聚合层
- 树结构装配层
- 搜索服务层
- AI bridge / CLI host 层;长期 agent 执行面只收口到 `mnote-cli`Hermes 仅作为历史兼容 bridge / 外置 adapter
- Hermes client proxy 与 mnote tool bridge 层;页面 AI 会话真相归 Hermes,mnote 业务工具最终回到 Rust runtime / kernel
- 页面 SSR 外壳承载层
- 流式更新、通知、事件推送层
@@ -413,14 +414,15 @@ Leptos Islands 很适合承接下面这类长期目标:
目标:
- 继续桥接统一 CLI host
- 不再承担前端本地 orchestration
- 作为 Hermes 页面内客户端运行
- 不再承担前端本地 orchestration 或私有会话存储
- 不再成为常驻大壳的一部分
归位:
- 面板只保留最小 UI 与上下文桥接
- 工具执行全部走 `mnote-cli` + Rust tools
- 面板只保留 Hermes chat/session/run/tool event 的页面内子集 UI 与当前页面上下文桥接
- Hermes 持有 session / message / usage / model 真相
- mnote 通过 Hermes skill/plugin 暴露页面、树、artifact、edge 工具,最终执行回到 Rust runtime / kernel
- 面板按页面需要懒挂载成单独 island
### 9.5 Mindmap
@@ -5,6 +5,7 @@
> 状态说明:
> - 本稿对应的页面设置 `popover`、页面 AI `drawer`、入口位置与最小接线方案已在当前 `mnote-web` 文档壳中落地,故迁入 `done/`
> - 本稿中的 deferred 项继续由后续独立任务推进,不影响这一轮“页面设置 + 页面 AI 交互壳”完成判定
> - 2026-05-13 追加说明:本文中 `CLI-first` / `mnote-cli host` 是 2026-05-06 完成时的历史接线口径;当前 AI 长期方向已由 `design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖为 Hermes 页面内客户端 + mnote Hermes skill/plugin
>
> 关联文档:
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
@@ -13,7 +14,7 @@
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-7-wolai-page-tree-main-editor-experience-restoration-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-9-wolai-aline-continuous-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
> - `/mnt/Data1T/mnote/design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md`
## 1. 文档目的
@@ -28,7 +29,7 @@
1. 页面设置入口与面板形态
2. 页面级 AI 入口与右侧抽屉
3. 两者与现有 `Page Aggregate` / `mnote-cli` 主线的接线方式
3. 两者与现有 `Page Aggregate` / Hermes 面板主线的接线方式
一句话收口:
@@ -45,7 +46,7 @@
- 页面设置 UI 已存在:`PageOptionsSidebar`
- 页面设置写链已存在:`page.layout.updateOptions`
- 页面 AI host 已存在:`DocumentAiAgentPanel`
- AI 执行面已收口到 `mnote-cli` host / client
- 历史实现已具备 `mnote-cli` host / client 最小返回链;后续应替换为 Hermes 页面内客户端
- 评论 drawer、历史 drawer、分享 dialog 都已有各自最小实现
但当前现网文档页仍然存在两个明显错位:
@@ -141,14 +142,15 @@
> **把现有设置项放进更接近 Wolai 的容器,同时继续显式区分“已正式接通”和“仅保存字段/待接通”。**
### 4.2 页面 AI 继续服从 `CLI-first`
### 4.2 页面 AI 继续服从 Hermes 面板主线
页面 AI 界面可以继续做产品壳,但它不能重新变成独立执行面。
当前长期方向已经明确
当前长期方向已经改为
- `mnote-cli` 是唯一长期 agent 执行面
- Web AI 面板只是 host / client
- Hermes 是 AI session/message/tool event/usage/model 的会话真相
- Web AI 面板只是页面内 Hermes client
- mnote 通过 Hermes skill/plugin 暴露页面、树、artifact、edge 等业务工具
因此本轮 AI 界面改造只处理:
@@ -326,7 +328,7 @@ Wolai 里页面设置和 AI 的第一感受,首先是:
本轮不改:
- `mnote-cli` 执行链
- 历史 `mnote-cli` 执行链
- 工具注册表
- 页面 AI 读写命令族
- AI 输出协议
@@ -510,11 +512,11 @@ Rust `mnote-web` 当前在文档壳中已有顶栏占位按钮与右下角浮动
### 9.3 页面 AI 重复造轮子的风险
如果为了像 Wolai 而新做一层 AI drawer runtime,会直接偏离 `CLI-first`
如果为了像 Wolai 而在 mnote 里新做一层独立 AI drawer runtime,会直接偏离当前 Hermes 面板主线
本轮约束:
> **页面 AI 只换壳,不换执行面。**
> **页面 AI 交互壳可继续沿用本轮成果,执行主线后续切到 Hermes client proxy + mnote plugin。**
### 9.4 范围失控风险
@@ -535,11 +537,11 @@ Rust `mnote-web` 当前在文档壳中已有顶栏占位按钮与右下角浮动
- 页面设置入口、容器、关闭语义已和 Wolai 基线一致
- 页面 AI 入口、容器、关闭语义已和 Wolai 基线一致
- 页面设置仍继续走 `page.layout.updateOptions`
- 页面 AI 仍继续走 `DocumentAiAgentPanel -> mnote-cli`
- 页面 AI 交互壳保留右下角入口 + 右侧抽屉;后续执行主线切到 Hermes client proxy + mnote plugin
- 没有新增第二套页面设置真相
- 没有新增第二套页面 AI 执行面
- 评论、协作、成员、演示模式没有被混入首批范围
本轮完成后的正确口径应是:
> **文档页“页面设置”和“AI 界面”的交互壳开始按 Wolai 收口,但页面域单一真源与 CLI-first AI 的主线保持不变。**
> **文档页“页面设置”和“AI 界面”的交互壳开始按 Wolai 收口页面域单一真源保持不变,AI 长期主线改为 Hermes 面板 + mnote plugin。**
@@ -5,6 +5,7 @@
> 状态说明:
> - 本清单覆盖的页面设置 A/B、页面 AI C、集成护栏 D 均已完成并有 `task160/161/162` 证据,故迁入 `done/`
> - `X1-X6` 属于明确 deferred 项,不构成这轮完成阻塞
> - 2026-05-13 追加说明:本文保留 `task161/162` 对旧 `/api/ai-agent/run -> mnote-cli` 返回链的历史验证证据;AI 长期方向已由 `design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 改为 Hermes 页面内客户端 + mnote Hermes skill/plugin
>
> 本清单服务于:
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-10-wolai-page-settings-and-ai-surface-alignment-v1.md`
@@ -231,9 +232,9 @@ GREEN
| C4 | GREEN | 顶栏 `页面AI` 降级 | `task161` 已断言顶栏不再保留 `页面AI` 主入口 |
| C5 | GREEN | 页面 AI 容器类型 | `task161` 已断言页面 AI 以右侧抽屉打开、无遮罩、不跳新页面 |
| C6 | GREEN | 页面 AI 关闭语义 | `task161` 已断言 `Esc`、点击空白无效,右上角 `X` 有效 |
| C7 | GREEN | 页面 AI 页面级绑定 | `task161` 已断言 `/api/ai-agent/run` 请求体携带当前 `documentId``pageOptions` |
| C8 | GREEN | 页面 AI 执行面不分裂 | 真实 `/api/ai-agent/run` 改为 `Next 兼容优先 -> 本地 mnote-cli fallback -> 旧 orchestrator 兜底``task162` 已验证真实页面可返回文本结果 |
| C9 | GREEN | AI chrome 与 Wolai 接近 | `task161` 已断言标题、输入区、`新会话 / 历史会话 / mnote-cli` chrome 可见 |
| C7 | GREEN | 页面 AI 页面级绑定 | `task161` 已断言历史 `/api/ai-agent/run` 请求体携带当前 `documentId``pageOptions`;后续同类上下文应进入 Hermes run/session context |
| C8 | GREEN | 页面 AI 历史返回链不分裂 | 真实 `/api/ai-agent/run` 改为 `Next 兼容优先 -> 本地 mnote-cli fallback -> 旧 orchestrator 兜底``task162` 已验证真实页面可返回文本结果;长期主线已改为 Hermes client proxy + mnote plugin |
| C9 | GREEN | AI chrome 与 Wolai 接近 | `task161` 已断言标题、输入区、`新会话 / 历史会话 / mnote-cli` chrome 可见;后续文案应改为 Hermes session / model / history |
### 6.1 本阶段 smoke 建议
@@ -286,8 +287,8 @@ GREEN
- `Esc` 和点击空白不会关闭
- 右上角关闭按钮可关闭
- 抽屉标题、输入区、会话/模型 chrome 可见
- 发送动作继续命中 `/api/ai-agent/run`
- 请求体继续携带当前 `documentId``pageOptions`
- 发送动作在历史实现中继续命中 `/api/ai-agent/run`
- 历史请求体继续携带当前 `documentId``pageOptions`;后续应转为 Hermes run/session context
新增证据:
@@ -306,6 +307,7 @@ GREEN
- 同时补了认证头显式转发,避免被 Next 侧 307 `/auth` 拦截
- 页面 AI drawer 默认 provider 改为 `hermes`
- 新增 `task162-wolai-page-ai-real-response-smoke.js`,并在真实后端端口 `41327` 上验证:页面 AI 不再显示 `page_ai_failed_502` / “没有返回文本结果”,而是会回填真实文本
- 2026-05-13 口径更新:上述修复只证明历史页面 AI 抽屉可返回文本,不再作为后续 AI 长期执行面依据;新的页面 AI 应直接调用 Hermes client proxy,不再通过 `/api/ai-agent/run` 的 provider / fallback 分支模拟 Hermes
### 6.3 task162 页面 AI 真实返回执行记录
@@ -325,7 +327,7 @@ GREEN
| ID | 状态 | 任务 | 验收要点 |
| --- | --- | --- | --- |
| D1 | GREEN | 不新造页面设置真相 | 页面设置继续围绕 `currentPageOptions -> /api/documents/options -> page.layout.updateOptions` |
| D2 | GREEN | 不新造 AI 执行面 | 页面 AI 继续围绕 `/api/ai-agent/run -> mnote-cli` |
| D2 | GREEN | 不新造 AI 执行面 | 历史实现围绕 `/api/ai-agent/run -> mnote-cli` 保持单链;后续不在 mnote 内新造 AI 执行面,而是调用 Hermes 并通过 mnote plugin 写回业务事实 |
| D3 | GREEN | 文档页 owner 行为合同固定 | `task160/task161` 已断言 owner 态页面设置和 AI 都不跳新页面、不改 URL |
| D4 | GREEN | mobile / 窄屏退化 | `task160/task161` 已断言移动视口下 popover / drawer 不超出视口 |
| D5 | GREEN | React compat 与 Rust SSR 边界 | 本轮实现仅落在 `mnote-web` Rust SSR 壳、脚本与样式;未再把入口回接 React compat |
@@ -355,9 +357,9 @@ GREEN
- 页面设置 `popover` 行为已通过本地 smoke 和 Wolai 基线复核
- 页面 AI 抽屉行为已通过本地 smoke 和 Wolai 基线复核
- 页面设置仍然围绕 `page.layout.updateOptions`
- 页面 AI 仍然围绕 `/api/ai-agent/run -> mnote-cli`
- 页面 AI 历史验证仍然围绕 `/api/ai-agent/run -> mnote-cli`;后续执行主线改为 Hermes client proxy + mnote plugin
- deferred 项没有被误报为已完成
本轮完成后的正确口径:
> **页面设置与页面 AI 的交互壳已开始对齐 Wolai,评论、协作、成员、演示模式仍是后续独立任务。**
> **页面设置与页面 AI 的交互壳已开始对齐 Wolai;AI 后续执行主线改为 Hermes 面板 + mnote plugin,评论、协作、成员、演示模式仍是后续独立任务。**
@@ -1,6 +1,6 @@
# 5-5-1 [done] Page Aggregate Contract v1
> 更新时间:2026-04-22
> 更新时间:2026-05-14
>
> 关联文档:
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
@@ -92,13 +92,13 @@
说明:
- 这层不是“页面设置面板 UI state”,而是页面设置的持久化真相。
- 其中只有一部分已经进入 `leptos-tiptap` island 运行时
- 以下字段已经进入 `leptos-tiptap` island runtime payload
- `wideLayout`
- `smallText`
- `layoutDensity`
- 下面两项当前只完成字段贯通,不可描述成“正式支持”:
- `showHeadingNumbers`
- `embedDefaultBlockId`
- 其中 `showHeadingNumbers` / `embedDefaultBlockId` 已进入 runtime payload,但深层语义仍是阶段性桥接:前者还不能描述成完整 heading 编号渲染闭环,后者还不能描述成完整嵌入默认落点闭环。
### 2.4 `page_body`
@@ -152,6 +152,24 @@
- 统计不是页面身份,也不是布局或正文真相,但它是 page aggregate 的附属部分。
### 2.7 `source` / provenance
作用:
> **标识这份 `PageAggregateProjection` 的真实构建来源。**
当前允许值:
- `KernelProjection`:底层已经由 Rust kernel 原生 page aggregate projection 产出。
- `CompatMetaContentJoin`:对外已经是 Rust `mnote.page_aggregate.v1` 契约,但底层仍由 Rust runtime adapter 消费 `documents:getMeta + documents:getContent` substrate 后聚合。
- `Fixture`:仅允许在测试或显式 dev fixture 场景出现。
说明:
- 当前 `/api/page-aggregate/:id` 主读链属于 `CompatMetaContentJoin`,不是完整 `KernelProjection`
- `source_label()` / `x-mnote-page-aggregate-owner` 必须与实际来源一致:`KernelProjection -> rust-kernel``CompatMetaContentJoin -> compat-join``Fixture -> fixture`
- 前端 loader 只消费 `mnote.page_aggregate.v1` 契约,不应因为来源是 `CompatMetaContentJoin` 而恢复 TS runtime builder 主链。
## 3. 哪些字段是 aggregate truth,哪些只是 UI state
### 3.1 Aggregate Truth
@@ -194,7 +212,7 @@
- 标题输入中的 debounce 缓冲态
- 正文 host 内的未保存 dirty 态
- 只影响单次交互的面板/菜单开关
- `showHeadingNumbers/embedDefaultBlockId` 的“字段已贯通,但深语义未完成”阶段性桥接逻辑
- `showHeadingNumbers/embedDefaultBlockId` 的“runtime payload 已贯通,但深语义未完成”阶段性桥接逻辑
约束:
@@ -222,11 +240,11 @@
- 树标题与页头标题已经证明消费同一份更新后的 projection
- AI 写入口已经完全脱离 editor bridge,正式执行 page body command
- `showHeadingNumbers` 真正进入 heading 渲染语义
- `embedDefaultBlockId` 真正进入嵌入默认落点逻辑
- `showHeadingNumbers` 完整进入 heading 编号渲染语义
- `embedDefaultBlockId` 完整进入嵌入默认落点逻辑
## 7. 当前可对外口径
当前可以准确描述为:
> **文档页已开始消费统一 `Page Aggregate`,标题/页面设置/正文保存也开始按 `page_head / page_layout / page_body` 收口;树域投影统一与 AI 正式 page body 写入口仍未完全完成。**
> **文档页已开始消费统一 `Page Aggregate`,标题/页面设置/正文保存也开始按 `page_head / page_layout / page_body` 收口;当前 Rust route 主读链已成立,但 `/api/page-aggregate/:id` 底层仍是 `CompatMetaContentJoin`树域投影统一与 AI 正式 page body 写入口仍未完全完成。**
@@ -115,6 +115,8 @@
- 页面本地 aggregate state reducer
- preferred sidebar snapshot / 页头标题补偿链
需要特别区分“Rust route 主读链”和“完整 kernel-native projection”:当前 `/api/page-aggregate/:id` 已经由 Rust route / Rust runtime adapter 对外提供稳定 `mnote.page_aggregate.v1` 契约,前端不再恢复 TS runtime builder 主链;但底层 substrate 仍主要来自 `documents:getMeta + documents:getContent` 的兼容聚合。因此当前 projection provenance 应标记为 `CompatMetaContentJoin`,不能写成已经完全由 Rust kernel 原生投影闭环的 `KernelProjection`
这意味着当前已经不再是:
- `page.tsx` 手工拉 `meta + content` 再现场拼 props
@@ -81,6 +81,8 @@
补充:当前 `page-aggregate-loader.ts`、文档页 SSR 入口与 `DocumentContent` 的内容重试补拉都已经直接消费 Rust `/api/page-aggregate/:id` 返回的 `PageAggregateProjection`,不再由 `page.tsx` 手工拼 `meta + content`。Next `/api/documents/page` 已降级为显式 `410` 的 compat 边界;`page-aggregate-loader.ts` 现在只校验并消费这条 Rust 正式读链,不再在 runtime 中回退到 TS builder。与此同时,`storage-convex-bridge``bridge-runtime` 已开始接受 `page.head.updateTitle / page.layout.updateOptions / page.body.save` 这组 page command family 的命名口径,因此这里按“读取契约已进入 Rust-first,写入命令面已开始收口”勾选完成。
补充:这里的“Rust-first”只表示 `/api/page-aggregate/:id` 的对外 route、契约校验和前端消费主链已经收口到 Rust,不表示底层已经完全 kernel-native。2026-05-13 复核后,当前 route 仍由 Rust runtime adapter 消费 `documents:getMeta + documents:getContent` substrate 构建聚合快照,因此 response `source` / `x-mnote-page-aggregate-owner` 应反映为 `CompatMetaContentJoin` / `compat-join`。只有底层真实改为 kernel 原生 page aggregate projection 后,才应标记为 `KernelProjection` / `rust-kernel`
### 4.3 退出标准
- [x] 后续再讨论标题、页面设置、正文保存时,能够直接定位到它属于 `page_head / page_layout / page_body / page_tree` 的哪一层。
@@ -113,6 +115,8 @@
补充:当前首屏 SSR 与客户端内容重试补拉都已经直接走 `/api/page-aggregate/:id -> PageAggregateProjection`,因此“页面明显由 `meta + content` 两次查询拼起来”的入口级痕迹已经消失;但新增字段仍可能需要继续补 loader / route / island 消费链,所以第二项继续保留未完成。
补充:入口级痕迹消失不等于来源语义消失。当前前端 loader 仍只接受 `mnote.page_aggregate.v1`,不会恢复 TS builder 主链;但 Rust response 的 provenance 必须继续暴露真实构建来源,避免把兼容 join 误验收成 kernel-native projection。
补充:本轮已新增 `page-aggregate-client-state.ts`,并让 `DocumentContent` 把原先分散维护的 `title / options / content / serverContentSnapshot / serverPageSubtreeSnapshot / contentRevision / conflictDetectionKey` 继续收口为同一份 client aggregate state reducer;标题的 draft / committed / persisted 语义也已并入这份 reducer,不再额外维护独立 `usePageHeadTitle` hook 或 `serverPageSubtreeTitle` 双轨状态。这里代表“页面本地 `head/body/layout/tree` 真相已经开始统一”,不等于页面设置运行时分类、AI 对页面设置面的正式入口都已闭环,因此本阶段不提前宣称聚合完成。对应最小回归测试为 `page-aggregate-client-state.test.ts``document-content.test.ts`
---
@@ -160,7 +164,7 @@
- [x] 对未支持项给出显式降级说明,而不是仅保存字段。
- [x] 让“页面设置有值但编辑器内部没变化”这类状态在产品上消失。
补充:本轮已把 AI 页面设置结构化结果接入 `DocumentContent` 现有的 `patch_page_options + page.layout.updateOptions` 正式链路,页面设置不再只能靠人类点击 inspector 才能进入同一条页面命令面。同时,AI 正式写回白名单已限制为 `page-option-semantics.ts``runtimeSupport === "wired"` 的字段,`protectEditing / showStructure / showBlockRefCount` 继续排除在正式写回外,避免把 planned / ui_only 选项误描述为稳定能力。`mnote-cli host` 侧也已新增最小服务端分支:命中页面设置 patch 时,直接执行 `page.layout.updateOptions` 并产出结构化 `tool_result(action=update_page_options)`,不再只把整段结果退化成 assistant 文本。对应最小回归测试为 `DocumentAiAgentPanel.runtime.test.tsx``mnote-cli-agent-host.test.ts`
补充:本轮已把 AI 页面设置结构化结果接入 `DocumentContent` 现有的 `patch_page_options + page.layout.updateOptions` 正式链路,页面设置不再只能靠人类点击 inspector 才能进入同一条页面命令面。同时,AI 正式写回白名单已限制为 `page-option-semantics.ts``runtimeSupport === "wired"` 的字段,`protectEditing / showStructure / showBlockRefCount` 继续排除在正式写回外,避免把 planned / ui_only 选项误描述为稳定能力。历史 `mnote-cli host` 侧也已新增最小服务端分支:命中页面设置 patch 时,直接执行 `page.layout.updateOptions` 并产出结构化 `tool_result(action=update_page_options)`,不再只把整段结果退化成 assistant 文本。2026-05-13 起后续主线改为 Hermes tool call -> mnote skill/plugin -> `page.layout.updateOptions`,该 host 分支只保留为过渡证据。对应最小回归测试为 `DocumentAiAgentPanel.runtime.test.tsx``mnote-cli-agent-host.test.ts`
补充:本轮继续把 Inspector 产品态与这份分类对齐:`showHeadingNumbers / embedDefaultBlockId` 已按“正式接通项”更新文案,不再继续显示“已保存字段但未接通”;`showBlockRefCount` 作为纯占位项也不再继续暴露可点击的假开关,而是明确显示 `待接线`。对应最小回归测试为 `page-options-sidebar.test.tsx``page-option-semantics.test.ts`
### 6.4 退出标准
@@ -230,7 +234,7 @@
补充:本轮继续把 AI 面板的读取边界开始收口到统一页面本地快照:`DocumentContent` 不再向 `DocumentAiAgentPanel` 透传 `getLatestBlocks / getLatestPageSubtree / getLatestPersistedMeta` 三组分散 getter,而是改为一份 `getLatestPageAggregateSnapshot`,由 `page-aggregate-client-state` 导出 `blocks / pageSubtree / persistedMeta / pageOptions`。这代表 AI 面板已开始消费同一份页面本地 aggregate state,而不是继续拼接独立局部真相;但页面设置写工具本身仍未进入 Hermes 正式 tool surface,因此这里仍然只算“开始收口”,不提前打满。
补充:本轮还把 `pageOptions``editorRuntimePageOptions` 一并带入 `/api/ai-agent/run -> buildHermesInstructions`,因此 AI 在服务端至少能看到“当前页面设置是什么”以及“哪些设置已经进入 island runtime payload”,不再只依赖正文块快照和树快照来猜测页面语义。对应回归测试为 `src/app/api/ai-agent/run/route.test.ts`。这推进的是 AI 读侧上下文,不等于页面设置写工具已经具备正式入口。
补充:本轮还把 `pageOptions``editorRuntimePageOptions` 一并带入历史 `/api/ai-agent/run -> buildHermesInstructions`,因此 AI 在服务端至少能看到“当前页面设置是什么”以及“哪些设置已经进入 island runtime payload”,不再只依赖正文块快照和树快照来猜测页面语义。对应回归测试为 `src/app/api/ai-agent/run/route.test.ts`2026-05-13 起同类上下文后续应改为 Hermes run/session context,并由 Hermes 通过 mnote plugin 回读最新 page aggregate这推进的是 AI 读侧上下文,不等于页面设置写工具已经具备正式入口。
### 8.2 与主编辑区的关系
@@ -240,27 +244,27 @@
补充:进入这一步后,当前最大的真实 blocker 已经明确下来,不再继续靠口头描述模糊处理:
- `/api/ai-agent/run` 当前默认主路仍是 `mnote-cli host`
- `mnote-cli host` 目前没有真正的模型/tool 执行环,而是 `cargo ... tool run --tool-name doc_get --mode explain-plan` 的说明型入口
- 因此,当前新增的 `page_options_patch -> page.layout.updateOptions -> structured tool_result` 仍然只是“主路内的最小结构化兼容分支”,不是完整正式 tool surface
- 页面 AI 历史实现仍残留 `/api/ai-agent/run` / `mnote-cli host` 路径
- Hermes 页面内客户端、Hermes session 真相和 mnote skill/plugin tool surface 还没有在页面 AI 主链落地
- 因此,当前新增的 `page_options_patch -> page.layout.updateOptions -> structured tool_result` 仍然只是“历史主路内的最小结构化兼容分支”,不是完整正式 Hermes tool surface
这意味着 `8.2` 后续完成标准必须至少包含:
1. 页面设置进入真正的 agent/tool 执行环,而不是继续靠 host 内部的自然语言/最小规则分支识别
2. 页面设置结构化结果由正式 tool 调用产出,而不是只由 host 自己补一条兼容 `tool_result`
1. 页面设置进入 Hermes tool call -> mnote plugin -> Rust runtime 的正式执行环,而不是继续靠 host 内部的自然语言/最小规则分支识别
2. 页面设置结构化结果由正式 mnote plugin tool 调用产出,而不是只由 host 自己补一条兼容 `tool_result`
3. 树标题 / 页头 / 页面设置三条 AI 写回链在同一条正式 page aggregate command family 中闭环,并补 smoke 验证
### 8.3 退出标准
- [x] AI 写入口已经可以被明确描述为“操作 page aggregate command family”,而不是“绕过系统写编辑器”。
注:当前 `/api/ai-agent/run``Hermes tool.completed` 后,会优先尝试把 `slash_run / doc_insert_blocks / doc_replace_range` 恢复成 `mnote-web bridge-runtime` 的结构化 `tool_result`,不再只把 Hermes 事件当作薄日志。其后:
注:历史 `/api/ai-agent/run``Hermes tool.completed` 后,会优先尝试把 `slash_run / doc_insert_blocks / doc_replace_range` 恢复成 `mnote-web bridge-runtime` 的结构化 `tool_result`,不再只把 Hermes 事件当作薄日志。2026-05-13 起这只保留为过渡证据;新的主线应由 Hermes 直接发起 mnote plugin tool call,再由 Rust runtime / kernel 返回 tool result。其后:
- `doc_insert_blocks / doc_replace_range` 继续按 `page.body.save` 语义落到 `/api/documents/save`,再正式回显主编辑区 island。
- `slash_run(rename current page)` 会把结构化结果回接到当前页 `DocumentContent` 的同一条标题提交链,并继续广播 `emitDocumentsChanged(documentId)`,因此页头标题与树标题不再靠 AI 面板内部本地状态各自漂移。
- 当前 AI 面板已经能消费结构化 `update_page_options` 结果,并把 `pageOptionsPatch` 回接到当前页 `DocumentContent` 的同一条 `patch_page_options + page.layout.updateOptions` 提交链;同时只允许 `runtimeSupport === "wired"` 的字段进入正式写回,避免 planned / ui_only 页面设置混入主链。对应最小回归测试为 `DocumentAiAgentPanel.runtime.test.tsx``document-content.test.ts`
- `mnote-cli host` 现已能在命中页面设置 patch 时直接执行 `page.layout.updateOptions`,并向前端回放结构化 `tool_call/tool_result` 事件;这意味着“服务端完全没有页面设置结构化写回结果”的状态已经结束。
-`pageOptions` 仍没有进入 Hermes 正式 tool surface,当前服务端 patch 识别也仍是最小规则分支而不是完整模型工具编排,因此“页面设置类 AI 命令”仍不能算整条线已闭环;`8.2` 的最后一项继续保留未完成,避免误判为整条线已经闭环。
- 历史 `mnote-cli host` 现已能在命中页面设置 patch 时直接执行 `page.layout.updateOptions`,并向前端回放结构化 `tool_call/tool_result` 事件;这意味着“服务端完全没有页面设置结构化写回结果”的状态已经结束。
-`pageOptions` 仍没有进入 mnote Hermes plugin 的正式 tool surface,当前服务端 patch 识别也仍是最小规则分支而不是完整 Hermes tool 编排,因此“页面设置类 AI 命令”仍不能算整条线已闭环;`8.2` 的最后一项继续保留未完成,避免误判为整条线已经闭环。
---
@@ -391,8 +391,8 @@ GREEN
- E26 执行前口径纠偏:2026-05-02 已按 `5-2``5-4``5-5``5-6``5-7` 设计确认,E26 不是“前端引入 Tiptap UniqueID 即完成”。正式块身份必须来自 Rust `EditorBlock.block_id`,经 Tiptap bridge 映射为 `data-block-id` 供浏览器 runtime 使用,再通过 `page.body.save` 写回 Convex-backed 持久化底座;复制锚点和 hash 定位只能消费这个真源 id。Tiptap `UniqueID` 可以参考其节点身份策略,但不得生成长期块 id、不得绕过 Rust `EditorBlockDocument`、不得绕过 Page Aggregate / `page.body.save`
- E26 执行边界:实现与 smoke 必须把 `EditorBlockDocument -> Tiptap attrs.blockId/data-block-id -> DOM 锚点 -> page.body.save -> /api/documents/content -> reload/hash 定位` 串成一条链。允许在浏览器 runtime 内用 `UniqueID` 或等价插件帮助定位节点,但只能读取/补齐已有 Rust block id;若某块缺少正式 id,应通过 Rust/保存适配链生成并持久化,而不是让前端临时 id 成为长期合同。Convex 只验证持久化结果可读可刷新,不作为语义真源。
- E26 最小真源闭环:2026-05-02 已完成第一段 anchor 切片。`leptos-tiptap` paragraph/heading/blockquote/codeBlock/image/table 的 `blockId` 渲染同时输出 `data-block-id` 与 DOM `id`,浏览器 hash 命中走 `id=:blockId` + CSS `:target`,不是手工给 ProseMirror DOM 写临时 classRust runtime 复制链接和 hash 滚动继续只消费 `EditorBlock.block_id` 派生的 `attrs.blockId``mnote-web` 保存 payload 不再发送空 `content: []`,而是派生 `editorDocument`、legacy `content``tiptapDocument``blockCount` 写回 Convex-backed 持久化底座;reload 侧 legacy block 恢复继续补 `attrs.blockId`。本地 smoke `node scripts/task154-e26-anchor-smoke.js` 已覆盖保存请求、`/api/documents/content`、复制链接、DOM `id/:target` 和 reload/hash 定位,截图目录:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/task154-e26-anchor-local-smoke`。剩余:列表项/分割线等更多块类型的统一 anchor attr 覆盖、Wolai 视觉细节、块引用/页面引用预览与移动端行为另拆后续。
- E27 执行前口径纠偏:AI 编辑能力包不是“补 AI 菜单文案”或“点击后显示 feedback”。最小闭环必须从 `leptos-tiptap` 的 slash / 块菜单入口出发,携带当前 `documentId``workspaceId`、Rust `blockId`、selection 摘要和 Tiptap JSON 快照,调用 mnote 现有 AI 真源边界。2026-05-05 起长期口径进一步修正:`/api/ai-agent/run` 不应长期绑定 `openai-agents-python` sidecar,而应收口为 `mnote-cli` 唯一长期 agent 执行面上的 host / adapter`openai-agents-python``Hermes``Codex` 只允许作为可插拔外置 agent。结构化写入仍必须回到 Rust tool / `page.body.save` / Convex-backed content 链。E27 第一刀已完成的 sidecar 路径只作为过渡基线,不宣称是长期主线。
- E27 AI agent 入口与写入闭环:2026-05-02 已完成主路径纠偏,修正此前把 `/api/hermes/bridge` 当作 E27 主入口的误导口径。块菜单 `AI 助理` 现在从当前 `leptos-tiptap` editor 读取 `documentId``workspaceId`、Rust `blockId`、selection state、selected text 与 `tiptapDocument` 快照,发起 `/api/ai-agent/run` 请求,payload 固定 `scope=document``stream=true``options.ai.provider=online`,并显示 `mnote-leptos-tiptap-ai-status``idle/pending/ready/error` 状态;请求上下文标记 `source=leptos-tiptap-island``action=ask_ai`Next sidecar adapter 已透传 `workspaceId / selectedBlockId / selection / tiptapDocument` 等 block 级上下文给 `openai-agents-python`。本地 smoke `node scripts/task155-e27-ai-edit-smoke.js` 已先 RED 于 Hermes 主入口,再 GREEN 覆盖请求 URL、payload 真源字段和 ready 状态;`node scripts/task156-e27-ai-writeback-smoke.js` 已先 RED 于只返回不写入,再 GREEN 覆盖 SSE `doc_replace_range` tool_result -> 编辑器改写 -> `/api/documents/save` -> `/api/documents/content` 真源读回 -> reload 后页面读回。截图目录:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/task156-e27-ai-writeback-local-smoke`。剩余:Wolai AI 菜单视觉基线断流未完成、slash AI/selection toolbar AI、Improve/continue/regenerate/summarize/translate 子命令、SSE 分段 UI 状态、`doc_insert_blocks` 多块插入和标题 `slash_run` 应用另拆后续。
- E27 执行前口径纠偏:AI 编辑能力包不是“补 AI 菜单文案”或“点击后显示 feedback”。最小闭环必须从 `leptos-tiptap` 的 slash / 块菜单入口出发,携带当前 `documentId``workspaceId`、Rust `blockId`、selection 摘要和 Tiptap JSON 快照,进入 Hermes run/session contextHermes 通过 mnote skill/plugin 调用稳定工具,再由 Rust runtime / `page.body.save` / Convex-backed content 链写回。2026-05-13 起长期口径进一步修正:`/api/ai-agent/run``openai-agents-python` sidecar `mnote-cli host` 都只作为历史/兼容路径,不再代表页面 AI 长期主入口;`mnote-cli` 只可作为 mnote plugin 内部适配器或调试入口。E27 第一刀已完成的 sidecar / `/api/ai-agent/run` 路径只作为过渡基线,不宣称是长期主线。
- E27 AI agent 入口与写入闭环:2026-05-02 已完成历史主路径纠偏,修正此前把 `/api/hermes/bridge` 当作 E27 主入口的误导口径。块菜单 `AI 助理` 当时从当前 `leptos-tiptap` editor 读取 `documentId``workspaceId`、Rust `blockId`、selection state、selected text 与 `tiptapDocument` 快照,发起 `/api/ai-agent/run` 请求,payload 固定 `scope=document``stream=true``options.ai.provider=online`,并显示 `mnote-leptos-tiptap-ai-status``idle/pending/ready/error` 状态;请求上下文标记 `source=leptos-tiptap-island``action=ask_ai`Next sidecar adapter 已透传 `workspaceId / selectedBlockId / selection / tiptapDocument` 等 block 级上下文给 `openai-agents-python`。本地 smoke `node scripts/task155-e27-ai-edit-smoke.js` 已先 RED 于 Hermes 主入口,再 GREEN 覆盖请求 URL、payload 真源字段和 ready 状态;`node scripts/task156-e27-ai-writeback-smoke.js` 已先 RED 于只返回不写入,再 GREEN 覆盖 SSE `doc_replace_range` tool_result -> 编辑器改写 -> `/api/documents/save` -> `/api/documents/content` 真源读回 -> reload 后页面读回。截图目录:`/mnt/Data1T/mnote/tmp/wolai-editor-parity/task156-e27-ai-writeback-local-smoke`。剩余:把入口改为 Hermes client proxy,把上下文作为 Hermes run/session context 传入,把写入工具注册为 mnote Hermes pluginWolai AI 菜单视觉基线、slash AI/selection toolbar AI、Improve/continue/regenerate/summarize/translate 子命令、Hermes streaming UI 状态、`doc_insert_blocks` 多块插入和标题写入另拆后续。
- E27 Auth 真源纠偏:2026-05-02 补充确认,AI 编辑和在线 smoke 的用户身份必须以真实 Convex Auth 为主线;不同账号的数据隔离依赖 Convex `getAuthUserId(ctx)` 解析真实 identity subject,并由 workspace/member 权限链校验。`/auth` 复用既有 Convex Auth 页面和统一测试账号 `test@example.com` / `Test123456``/api/auth/session``/api/auth/whoami`、AI orchestrator 转发和 Convex transport 均应真实 token / forwarded actor 优先。`DEV_USER_ID` / admin acting identity 只允许作为本地底层 fallback 或显式 `MNOTE_DEV_AUTH=1` 联调模式,不能作为 E27 在线验收、多账号隔离或正式数据真源。
- E27 当前推进备注:2026-05-03 主线程先切到 E28 Mention / EmojiE27 暂停在 online smoke 模型网关层排障状态。当前已确认 `3000 -> 8000` orchestrator 主链可达,本地 `task155/task156` 通过;剩余在线阻塞点在 `20128 /v1/responses` 上游模型/渠道可用性,以及后续真实 actor 收口。恢复 E27 时应从这两点继续,不要回退成“主入口未迁完”。
- E28/E29 暂停与 E30 先行口径:2026-05-03 主线程先暂停 E28 Mention / Emoji 与 E29 Comment / History,转入 E30 Menu / Floating 状态机能力包。E28 已有 Wolai Hermes baseline 显示正文输入 `@` 当前弹出提醒/会议/成员候选,并可插入成员 mention,不是页面引用搜索;证据目录为 `/mnt/Data1T/mnote/tmp/wolai-editor-parity/task157-e28-wolai-mention-baseline/`,恢复 E28 时需先重新定 `@mention` 口径,不要按“页面引用第一刀”继续。E29 在评论/历史后端边界未重新确认前保持暂停。E30 第一刀只收敛已有 slash、块菜单、二级菜单、selection/image/table floating toolbar 的打开互斥、Esc、外部点击、方向键、Enter 与层级收起,不扩新业务命令。
@@ -418,7 +418,7 @@ GREEN
| E24 | PARTIAL | Image / Media 能力包 | 已完成图片 `/tp` 最小真源闭环与图片 toolbar 小闭环:slash `媒体与附件` 分组入口调用 `leptos-tiptap` 官方 `set_image(TiptapImageResource)`,启用 `TiptapExtension::Image`,保存链新增 `EditorBlockType::Image` / `TiptapNode::Image` 并保留 `props.tiptapImage``mnote-web` reload 恢复 `<img>`;点击图片后出现 `image-floating-toolbar`,删除入口本切片保持禁用,左/中/右对齐通过官方 Image 扩展 `addAttributes("data-align")` + `updateAttributes("image")` 持久化,同源下载通过隐藏 `<a download>` 触发且不改文档;`task152-e24-image-smoke.js` 覆盖入口、实际图片加载、toolbar、同源下载、删除入口禁用态、居中对齐、保存请求、content API 和刷新恢复;剩余上传、最近上传、caption、resize、replace、跨源下载 fallback、删除保存链、移动端 toolbar、失败态 |
| E25 | PARTIAL | TOC 能力包 | 已完成 `/toc` 最小真源闭环:`leptos-tiptap` 新增真实 `tocNode` schema/commandslash `页面目录 /toc` 插入真实节点,NodeView 从当前 headings 派生目录项,支持点击定位、标题显示开关、保存请求、content API 与 reload 恢复;`task153-e25-toc-smoke.js` 覆盖入口、动态 heading 更新、hash 定位和 `props.tiptapTocNode`。剩余 TOC sidebar、active heading 高亮、完整块菜单入口、官方 TableOfContents v3 数据源评估、移动端与像素级视觉 |
| E26 | PARTIAL | UniqueID / Anchor 能力包 | 已完成最小真源闭环:Rust `EditorBlock.block_id` -> Tiptap `attrs.blockId` -> DOM `data-block-id` + `id` -> 复制链接 hash -> `page.body.save` / `/api/documents/content` -> reload 后 `:target` 定位;`task154-e26-anchor-smoke.js` 覆盖保存、复制、reload/hash 和截图。Tiptap `UniqueID` 仍只作为参考/辅助口径,不作为正式块 id;剩余更多块类型 anchor 覆盖、Wolai 视觉细节、引用预览和移动端行为。 |
| E27 | PARTIAL | AI 编辑能力包 | 已完成块菜单 `AI 助理` 主路径与最小写入闭环:点击后发起 `/api/ai-agent/run`payload 使用 `scope=document``stream=true``options.ai.provider=online`,携带 `documentId``workspaceId`、Rust `blockId`、selection、selected text、Tiptap 快照和 `action=ask_ai`历史基线里 `Hermes` 只作为 `/api/ai-agent/run` 内部 fallback。2026-05-05 起长期口径改为:`/api/ai-agent/run` 后续应收口为 `mnote-cli` 唯一长期 agent 执行面上的 host / adapter`Hermes``Codex``openai-agents-python` 仅为可插拔外置 agent`task155-e27-ai-edit-smoke.js``task156-e27-ai-writeback-smoke.js` 保留为过渡主链行为基线。Auth 验收必须复用 Convex Auth 测试账号,真实 token / actor 优先,`dev identity` 仅为本地 fallback。当前主线程已于 2026-05-03 先切 E28E27 暂停在 online smoke 的 `20128 /v1/responses` 模型网关排障与真实 actor 收口。剩余 slash AI/selection toolbar AI、Improve/continue/regenerate/summarize/translate、Wolai 视觉基线、SSE 分段 UI、`doc_insert_blocks` 多块插入与标题写入。 |
| E27 | PARTIAL | AI 编辑能力包 | 已完成历史块菜单 `AI 助理` 主路径与最小写入闭环:点击后发起 `/api/ai-agent/run`payload 使用 `scope=document``stream=true``options.ai.provider=online`,携带 `documentId``workspaceId`、Rust `blockId`、selection、selected text、Tiptap 快照和 `action=ask_ai``task155-e27-ai-edit-smoke.js``task156-e27-ai-writeback-smoke.js` 保留为过渡行为基线。2026-05-13 起长期口径改为:页面 AI 入口应调用 Hermes client proxyHermes session/message/tool event/usage/model 是会话真相,mnote 通过 Hermes skill/plugin 执行 `page.body.save`、标题、页面设置、artifact、edge 等业务工具;`mnote-cli` 仅可作为 plugin 内部适配器。Auth 验收必须复用 Convex Auth 测试账号,真实 token / actor 优先,`dev identity` 仅为本地 fallback。当前主线程已于 2026-05-03 先切 E28E27 暂停在 online smoke 的 `20128 /v1/responses` 模型网关排障与真实 actor 收口。剩余 Hermes client proxy 接入、mnote plugin tool 注册、slash AI/selection toolbar AI、Improve/continue/regenerate/summarize/translate、Wolai 视觉基线、Hermes streaming UI、`doc_insert_blocks` 多块插入与标题写入。 |
| E28 | PAUSED | Mention / Emoji 能力包 | 2026-05-03 暂停;Wolai baseline 已纠偏:正文 `@` 当前是提醒/会议/成员候选入口,不是页面引用搜索,恢复时先重定 mention/emoji 口径,再决定 Tiptap JSON 与 mnote 保存链 |
| E29 | PAUSED | Comment / History 能力包 | 2026-05-03 暂停;评论/历史入口仍需后端边界和 Wolai 基线复核,待 E30 统一菜单状态机后再接入,避免继续复制独立弹层逻辑 |
| E30 | PARTIAL | Menu / Floating 状态机能力包 | 2026-05-03 第一刀已完成本地统一状态机 smoke:`task158` 覆盖 slash、块菜单/二级菜单、selection toolbar/color panel、image floating toolbar、table toolbar/options 的互斥打开、Esc 统一关闭、方向键 active,以及打开块菜单时关闭 image toolbarWolai 只读基线已确认 selection/type menu 和 table popper 的 Esc/外部点击/URL 不变,slash 与块菜单二级菜单在只读条件下不稳定,后续仍需 editable-test 复核 Enter、外部点击和更多层级收起 |
@@ -1,4 +1,4 @@
# 6 [process] Mindmap Phase 6 KMind/simple-mind-map Parity Detail Checklist v1
# 6 [done] Mindmap Phase 6 KMind/simple-mind-map Parity Detail Checklist v1
> 日期:2026-05-11
>
@@ -10,7 +10,8 @@
> 状态说明:
> - 本稿对应 `Phase 7 v2` 的“文档页 AI 最小闭环”已完成,故迁入 `done/`
> - 本稿完成不等于整个 `Phase 7 v2` 已完成;结构化知识写链仍以后续阶段继续推进
> - 2026-05-05 追加说明:本稿记录的是 `openai-agents-python` sidecar 作为过渡主链的完成状态,不代表当前长期方向;长期口径由 `/mnt/Data1T/mnote/design/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 收口为 `mnote-cli` 是唯一长期 agent 执行面
> - 2026-05-05 追加说明:本稿记录的是 `openai-agents-python` sidecar 作为过渡主链的完成状态,不代表当前长期方向;当时长期口径由 `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 收口为 `mnote-cli` 是唯一长期 agent 执行面
> - 2026-05-13 追加说明:`mnote-cli` 唯一长期 agent 执行面口径已被 `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖;新的长期方向是页面 AI 面板仅作为 Hermes 页面内客户端,Hermes 持有 AI 会话真相,mnote 通过 Hermes skill/plugin 暴露业务工具
---
@@ -12,8 +12,14 @@
>
> 2026-05-05 追加说明:
> - 本稿的对象模型、artifact 写链与 kernel 边界仍然有效
> - 触发与执行口径被 `/mnt/Data1T/mnote/design/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 覆盖
> - 当前凡是提到“页面 AI 面板触发”的位置,都应理解为“页面 AI 面板作为 `mnote-cli` host 触发”,而不是独立内置编排主线
> - 当时触发与执行口径被 `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 覆盖
> - 该 `mnote-cli` host 口径现已被 2026-05-13 的 Hermes 面板主线覆盖
>
> 2026-05-13 追加说明:
> - 本稿的对象模型、artifact 写链、projection-only `AI Artifacts` 分组与 kernel 边界继续有效
> - 触发与执行口径改由 `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖
> - 当前凡是提到“页面 AI 面板触发”或“页面 AI host 固定动作”的位置,都应理解为:
> 页面 AI 面板作为 Hermes 页面内客户端发起意图,Hermes 通过 mnote skill/plugin 调用正式 artifact 工具,最终写入仍回到 Rust runtime / kernel
---
@@ -43,7 +49,7 @@
- `ai_note node`
- `reference edge`
2. 只允许围绕**当前文档页**创建,不做跨页、跨工作区写链
3. 触发方式不是自然语言,不做自动推断,只允许页面 AI host 暴露的固定动作触发;当前页面 AI 面板只是 `mnote-cli` host 的一个页面内入口
3. 第一版允许页面 AI 面板提供固定快捷入口,但快捷入口本质是向 Hermes 发送意图;Hermes 通过 mnote skill/plugin 调用 artifact 工具,不再由页面 AI host 或 `mnote-cli` host 私有触发
4. 点击按钮后直接创建,不走“先预览再确认”的两阶段流
5. `summary node` 默认单例覆盖更新;`ai_note node` 每次新建
6. 两者都落成**可编辑的页面型节点**
@@ -278,21 +284,25 @@
## 6. 触发方式与产品入口
### 6.1 允许固定按钮触发
### 6.1 允许固定按钮触发,但必须经过 Hermes
第一版不走自然语言触发。
不支持:
- “帮我顺手创建一个摘要节点”
- “看起来像摘要就自动生成”
- “只要语义明确就直接落库”
只支持页面 AI host 面板里的两个固定按钮:
第一版允许页面 AI 面板提供两个固定快捷按钮:
- `创建 Summary`
- `创建 AI Note`
但按钮不得绕过 Hermes 或 mnote plugin 直接写入。正确链路是:
```text
Leptos 页面 AI 面板
-> Hermes session/run
-> mnote Hermes skill/plugin
-> Rust runtime / kernel
-> artifact node / reference edge
```
自然语言触发可以作为后续能力加入,但第一版验收仍以固定按钮或固定 tool intent 为准,避免 artifact 写链质量和权限边界同时失控。
### 6.2 点击后直接创建
第一版点击按钮后,直接创建,不走预览确认。
@@ -307,6 +317,7 @@
- 正式写链成立
- kernel node / edge 成立
- 文件树 projection 成立
- Hermes tool call -> mnote plugin -> Rust kernel 的边界成立
而不是先做复杂的人机审核流。
@@ -0,0 +1,427 @@
# 7-3 [process] 页面 AI Hermes 面板与 mnote Plugin 主线方案 v1
> 更新时间:2026-05-13
>
> 上位依据:
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
> - `/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/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-10-wolai-page-settings-and-ai-surface-alignment-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18`
> - `/mnt/Data1T/mnote/design/07-ai/process/7-2-phase7-structured-artifact-write-chain-v1.md`
>
> 覆盖关系:
> - 覆盖 `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md`
> 中“`mnote-cli` 是唯一长期 agent 执行面”的口径。
> - 保留 `v4` 中“Web 不应拥有第二套工具注册表、结构化写入必须回到 Rust runtime / kernel”的判断。
> - 保留 `7-2` 中 `summary node / ai_note node / reference edge` 的对象模型和写入边界,
> 但触发方改为 Hermes tool call,而不是页面 AI host 私有按钮或 `mnote-cli` host。
---
## 1. 文档目的
这份稿只回答一个问题:
> **未来页面 AI 的长期主语到底是谁。**
当前冻结答案是:
> **页面 AI 面板只是 Hermes 的页面内客户端;Hermes session/message/tool event/usage/model 才是会话真相;mnote 通过 Hermes skill/plugin 暴露业务能力。**
这不是把 mnote 的业务真相交给 Hermes。长期边界必须分清:
- Hermes 负责 AI 编排、会话、模型、tool call 调度和聊天历史。
- mnote 负责页面、树、正文、artifact、edge、projection 和审计事实。
- 页面 AI 面板只负责在文档页里打开一个 Hermes 客户端。
一句话收口:
> **AI 会话归 Hermesmnote 能力归 Rust kernel;页面 AI 面板只连接两者。**
---
## 2. 为什么要从 CLI-first 改为 Hermes-first
`CLI-first` 解决了一个真实问题:避免 Web 前端继续拥有私有 AI 编排、私有工具注册表和私有写链。
但它也带来了新的错位:
- 页面 AI 面板开始伪装成 `mnote-cli` 图形客户端。
- Hermes、Codex、`openai-agents-python` 被统一压成“外置 agent”,但实际用户希望页面 AI 就是 Hermes 面板。
- 真实会话能力、模型选择、tool event、thinking、usage、历史搜索这些已经是 Hermes 的强项,mnote 自己再做一套会重复。
- 当前运行态已经出现冲突:页面壳默认发送 `provider=hermes`,而 `/api/ai-agent/run` 又按旧退场口径返回 `ai_provider_bridge_unavailable`
因此新的主线不是恢复旧的 Web 私有 AI 编排,而是:
> **把页面 AI 从 `mnote-cli host` 改成 Hermes client,把 mnote 能力从 Web 私有 tool 改成 Hermes 可发现、可调用的 skill/plugin。**
---
## 3. 长期分层
### 3.1 Hermes
Hermes 负责:
- session 创建、恢复、重命名、删除、搜索
- message 存储与 conversation history
- model / provider / profile 选择
- streaming 事件、thinking / reasoning、usage
- tool call 调度、排队、取消、恢复
- skill / plugin 的发现和启停
Hermes 不负责:
- 直接写 mnote 的 Convex 表
- 直接构造第二套 page aggregate
- 直接决定页面树、文件树、artifact、edge 的事实结构
### 3.2 mnote Rust kernel / runtime
mnote Rust 负责:
- `Page Aggregate`
- `page.*` command
- `tree.*` command
- `kernel.*` query / edge
- `summary node / ai_note node / reference edge`
- projection、audit、idempotency、workspace / actor scope
Rust 不负责:
- 存储 Hermes 聊天历史
- 维护 Hermes session 列表
- 重做 Hermes 模型、profile、usage 管理
### 3.3 页面 AI Leptos 面板
页面 AI 面板负责:
- 右下角入口与右侧抽屉壳层继续服从 Wolai 对齐结果
- 用 Leptos 实现 Hermes chat 的页面内子集
- 调用 Hermes session / run / stream API
- 把当前页面上下文作为 Hermes run 的输入或 session workspace context
- 展示 Hermes 返回的 message、reasoning、tool event、error、usage
页面 AI 面板不负责:
- 自己保存聊天真相
- 自己维护工具注册表
- 自己执行页面写入
- 自己 fallback 到 `mnote-cli` 或旧 sidecar
### 3.4 mnote Hermes skill/plugin
mnote 需要作为 Hermes skill/plugin 暴露能力。
第一版建议能力分组:
- `mnote.page.get`
- `mnote.page.save`
- `mnote.page.update_title`
- `mnote.page.update_options`
- `mnote.tree.create`
- `mnote.tree.move`
- `mnote.search.documents`
- `mnote.artifact.create_summary`
- `mnote.artifact.create_ai_note`
- `mnote.kernel.attach_reference`
这些工具可以由 plugin 内部调用:
- Rust Web 同源 tool bridge
- `mnote-cli` JSON adapter
- 或后续更稳定的 Rust plugin bridge
但对 Hermes 来说,它们必须表现为一组稳定 Hermes tools,而不是页面前端私有函数。
---
## 4. 会话真相
页面 AI 的会话真相固定在 Hermes。
mnote 不保存:
- 聊天消息列表
- assistant 文本历史
- thinking / reasoning 历史
- tool event 完整展开状态
- session 标题、分组、usage
mnote 可以保存:
- 结构化写入产生的 audit
- artifact node
- reference edge
- page/body/title/options 的正式变更
- 与一次 Hermes tool call 对应的 request / trace / actor / reason
也就是说,mnote 只保存“对 mnote 事实源造成影响的结果”,不复制 Hermes 的聊天数据库。
---
## 5. 页面上下文进入 Hermes 的方式
页面 AI 面板打开时,mnote 应提供最小上下文包:
- `workspaceId`
- `documentId`
- 页面标题
- `pageAggregate` 摘要
- 当前选区 / blockId / selected text
- 页面设置
- 当前用户 actor / capability 摘要
上下文传入 Hermes 有两种可接受方式:
1. 作为 run input / instructions 的结构化上下文。
2. 作为 Hermes session workspace context,由 mnote panel 在创建或恢复 session 时设置。
第一版优先采用简单方式:
> 页面 AI 面板每次发起 run 时附带当前页面上下文摘要;Hermes 如需读取最新正文,再通过 `mnote.page.get` 工具回读。
这样可以避免把 page aggregate 大对象长期塞进 Hermes session,也避免 stale context 变成事实源。
---
## 6. API 与路由边界
### 6.1 退役 `/api/ai-agent/run` 主路径
`/api/ai-agent/run` 不再作为页面 AI 的长期主入口。
允许状态:
- 暂时保留为 legacy compat,明确返回旧接口退场信息
- 或只用于旧 smoke / 对照验证
禁止状态:
- 页面 AI 新实现继续向它发送 `provider=hermes`
- `/api/ai-agent/run` 继续作为 Hermes 面板的主代理
- 它继续持有 mnote 私有工具注册表或执行编排
### 6.2 新增 Hermes client proxy
浏览器不应直接暴露 Hermes API key。
建议在 `mnote-web` 中提供同源薄代理:
- `/api/hermes/client/sessions`
- `/api/hermes/client/runs`
- `/api/hermes/client/events`
- `/api/hermes/client/models`
- `/api/hermes/client/tools`
这层只做:
- auth / cookie / token 转发
- 同源安全边界
- 页面上下文最小注入
- 错误码标准化
这层不做:
- session 真相存储
- message 真相存储
- tool 执行编排
- 旧 provider fallback
### 6.3 mnote tool bridge
Hermes 调用 mnote 工具时,应进入窄桥:
```text
Hermes tool call
-> mnote Hermes plugin
-> mnote-web /api/hermes/tools/mnote/*
-> Rust runtime / kernel command/query
-> Hermes tool result
```
第一版不要求一次性冻结最终 URL,但要求协议字段稳定:
- `toolName`
- `arguments`
- `workspaceId`
- `documentId`
- `actor`
- `sessionId`
- `traceId`
- `idempotencyKey`
- `dryRun`
- `capabilityScope`
---
## 7. Leptos 面板参考范围
参考 `hermes-web-ui-0.5.18`,但只采用页面内必要子集。
第一版采用:
- Chat session list
- Message list
- Chat input
- streaming delta
- thinking / reasoning 展开
- tool started / tool completed 展开
- model selector
- error / retry / abort
- session search 可后置
第一版不采用:
- 平台 Channels 管理
- Jobs / Cron 管理
- Profiles 管理全页面
- Logs 全页面
- Files 全浏览器
- Terminal
- Group Chat
- Hermes 全局 Settings
这些能力属于 Hermes 管理台,不属于 mnote 页面 AI 抽屉。
实现要求:
- 使用 Leptos island 实现,不引入 Vue / Naive UI。
- 保留 Wolai 对齐的右下角入口和右侧 drawer 容器。
- 文案和视觉以 mnote 文档页密度为准,不照搬 Hermes Web UI 的整站导航。
---
## 8. 结构化 Artifact 写链
`7-2` 的对象模型继续成立:
- `summary node`
- `ai_note node`
- `reference edge`
- `AI Artifacts` projection-only 分组
但触发方改为 Hermes tool call
- 用户可以在 Hermes 面板中自然语言要求总结当前页。
- Hermes 决定调用 `mnote.artifact.create_summary`
- 或页面面板提供快捷按钮,但按钮本质也是向 Hermes 发送意图,不是绕过 Hermes 直接写 mnote。
第一版允许两个快捷入口:
- `创建 Summary`
- `创建 AI Note`
但它们必须走:
```text
Leptos panel -> Hermes session/run -> mnote plugin tool call -> Rust kernel
```
不得走:
```text
Leptos panel -> /api/documents/* 直接写 artifact
```
---
## 9. 权限与安全
Hermes 可以调度工具,但 mnote 必须做最终授权。
每个 mnote tool call 至少校验:
- 当前 actor 是否登录
- actor 是否属于 workspace
- 当前页面是否允许 AI 读取
- 当前页面是否允许 AI 写入
- 当前工具是否允许写入页面 / 树 / artifact
- 是否需要 `dryRun` 或确认
默认第一版:
- 读当前页:允许
- 写当前页:按页面 capability
- 创建 summary / ai_note:按当前页 artifact capability
- 跨页写入:默认禁止
- 跨 workspace:禁止
---
## 10. 迁移步骤
### Phase A:设计口径统一
- 新增本稿作为 07-ai 当前主线。
-`7-v4 CLI-first` 移入 `design/old/07-ai/process/` 并标记 `[recycle]`
- 更新仍引用 `mnote-cli` 唯一长期 agent 执行面的活跃设计稿。
### Phase B:薄 Hermes client proxy
-`mnote-web` 补 Hermes client proxy。
- 页面 AI 面板不再调用 `/api/ai-agent/run`
- 浏览器不直接持有 Hermes API key。
### Phase CLeptos Hermes 面板最小子集
- 实现 session 创建 / 恢复。
- 实现 run / stream / abort。
- 实现 message / reasoning / tool event 展示。
- 保留 Wolai 右侧抽屉壳。
### Phase Dmnote Hermes plugin
- 暴露 mnote tool manifest。
- 先接 `mnote.page.get``mnote.page.save``mnote.artifact.create_summary``mnote.artifact.create_ai_note`
- 工具结果回到 Hermes tool event。
### Phase E:退役旧面板执行链
- `/api/ai-agent/run` 从主路径移除。
- 旧 React `DocumentAiAgentPanel.runtime` 降为历史参考或 compat。
- `provider=hermes/codex/claudecode` 502 不再出现在新页面 AI 主链。
---
## 11. 验收标准
第一版完成时必须满足:
- 页面 AI 抽屉打开后可创建或恢复 Hermes session。
- Hermes session 存储中能看到页面 AI 的消息历史。
- mnote 本地不复制聊天消息真相。
- 发送消息后,事件流来自 Hermes run。
- tool call 展示使用 Hermes tool event。
- Hermes 可调用至少一个只读 mnote 工具读取当前页面。
- Hermes 可调用至少一个写入工具,经 Rust runtime 写回当前页或创建 artifact。
- 页面刷新后,AI 会话从 Hermes 恢复,而不是从 mnote 本地 state 恢复。
- 结构化写入产生的 artifact / edge 能在 mnote projection 中验证。
---
## 12. 禁止项
- 不再新增 Web 私有 AI tool registry。
- 不再把 `mnote-cli` 写成页面 AI 唯一长期执行面。
- 不把 Hermes 聊天历史复制进 mnote page aggregate。
- 不让 Hermes plugin 直接写 Convex。
- 不在 Leptos 面板里重做 Hermes 后台管理台。
- 不把 `AI Artifacts` 做成真实 kernel node。
- 不绕过 Rust runtime 创建 artifact / edge。
---
## 13. 最终冻结口径
> **页面 AI 是 Hermes 面板,不是 mnote-cli 面板。**
> **Hermes 持有 AI 会话真相,mnote 持有业务对象真相。**
> **mnote 通过 Hermes skill/plugin 暴露工具,工具最终回到 Rust runtime / kernel。**
> **Leptos 负责页面内 Hermes 客户端体验,不负责 AI 编排。**
@@ -6,7 +6,7 @@
## 结论
总体方向与当前主线基本一致:Mindmap 已明确降为 `tree-first graph kernel` 的视图/编辑挂件AI 主执行面正在向 `mnote-cli` 收口OnlyOffice 仍是独立页面型编辑器边界,SiYuan 参考稿没有发现被提升为上位架构来源的证据。
总体方向与当前主线基本一致:Mindmap 已明确降为 `tree-first graph kernel` 的视图/编辑挂件AI 长期方向已从 `mnote-cli` 收口改为 Hermes 页面内客户端 + mnote Hermes skill/pluginOnlyOffice 仍是独立页面型编辑器边界,SiYuan 参考稿没有发现被提升为上位架构来源的证据。
主要风险集中在三处:OnlyOffice 在 `mnote-web` 主入口里的 callback/forcesave 仍是 no-op,而 legacy Next route 已有真实写回链;Mindmap 的导出 action 在 schema/action map 中暴露,但 bridge 安全命令集不支持;Mindmap AI 补完 route 当前硬返回 501,和 Rust tool 已登记的能力面不闭合。
@@ -17,7 +17,7 @@
| F-01 | 实现偏差 / 风险 | P1 | OnlyOffice `mnote-web` 主入口已挂载 callback/forcesave,但当前实现不写回,只返回成功或 noop,可能让 3000 主链下的 OnlyOffice 保存丢失。 | `rust/crates/mnote-web/src/routes/mod.rs:78-84` 挂载 `/api/onlyoffice/callback``/api/onlyoffice/forcesave``rust/crates/mnote-web/src/routes/onlyoffice.rs:717-759` callback 只记录日志并返回 `{error:0}`forcesave 返回 `mnote-web-rust-noop`;而 `wolai-frontend/src/app/api/onlyoffice/callback/route.ts:93-192` 有真实 Convex 写回链。 | 优先把 Rust route 接到 `onlyoffice_prepare_callback` / media asset writeback,或显式把该路由代理到 legacy Next,避免主入口 shadow 掉真实写回。 |
| F-02 | 未完成 | P2 | Mindmap `export` 动作暴露给 UI action map,但 simple-mind-map 安全执行器不允许 `EXPORT`,默认路径可能显示能力却执行失败。 | `wolai-frontend/src/lib/mindmap/mindmap-action-map.ts:65``export` 映射为 runtimeCommand `EXPORT``wolai-frontend/src/lib/mindmap/simple-mind-map-bridge.ts:119-129``SIMPLE_MIND_MAP_SAFE_COMMANDS` 不包含 `EXPORT`。 | 要么把 `EXPORT` 加入安全命令并补 smoke,要么在 UI state 中继续禁用导出并标注为延期。 |
| F-03 | 未完成 / 方向变化 | P2 | Mindmap AI 补完 route 当前直接 501,后续大段旧 Supabase/在线 AI 实现被注释;但 Rust tool registry 已登记 `mindmap_expand_node`,形成“工具存在、产品入口不可用”的断层。 | `wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts:90-97` 校验后直接返回 `501`;同文件后续注释块仍保留旧 Supabase/AI 逻辑;`rust/crates/core-protocol/src/tool.rs:231-240``rust/crates/bridge-runtime/src/lib.rs:3803-3812` 已有 `mindmap_expand_node`。 | 若该能力仍在 Phase 6/7 范围内,应按 CLI/Rust bridge 路线重接;若延期,应把 route 标成 retired/debug,避免前端或测试误以为可用。 |
| F-04 | 方向变化 / 文档滞后 | P2 | AI 主 Web route 收口到 `mnote-cli` host,但 `wolai-backend` 仍暴露 `openai_agents_python` 文档 agent route 与旧工具面;是否仍部署为可访问入口需进一步验证。 | `wolai-frontend/src/app/api/ai-agent/run/route.ts:36-57` 明确拒绝 codex/hermes/claudecode 并进入 `startMnoteCliAgentHostRun``wolai-backend/app/routers/ai_agent.py:34-57` 仍暴露 `/ai-agent/health``/ai-agent/document/run`health 返回 `bridge: openai_agents_python``wolai-backend/app/services/ai_document_agent.py:1174-1470` 仍指令 agent 使用 `doc_insert_blocks``doc_replace_range``slash_run`。 | 在设计或代码注释中明确该后端只作为可插拔外置 agent/对照链;若不再使用,补退场计划和访问边界,尤其确认 `mnote_ai_orchestrator_api_key` 未配置时的开放行为。 |
| F-04 | 方向变化 / 文档滞后 | P2 | AI 主 Web route 收口到 `mnote-cli` host,但新主线已改为 Hermes 页面内客户端;`wolai-backend` 仍暴露 `openai_agents_python` 文档 agent route 与旧工具面,旧 route / sidecar / host 的退场关系需重新明确。 | `wolai-frontend/src/app/api/ai-agent/run/route.ts:36-57` 明确拒绝 codex/hermes/claudecode 并进入 `startMnoteCliAgentHostRun``wolai-backend/app/routers/ai_agent.py:34-57` 仍暴露 `/ai-agent/health``/ai-agent/document/run`health 返回 `bridge: openai_agents_python``wolai-backend/app/services/ai_document_agent.py:1174-1470` 仍指令 agent 使用 `doc_insert_blocks``doc_replace_range``slash_run`。 | `design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 为上位依据,明确页面 AI 改走 Hermes client proxymnote 能力通过 Hermes skill/plugin 暴露;`openai-agents-python``mnote-cli host` 只保留为历史/兼容/内部适配路径并补退场计划。 |
| F-05 | 文档治理风险 | P3 | `design/90-reference` 符合“参考资料”目录定位,但内容仍带有问答式残留,容易被后续 worker 误用为正式设计结论。 | `design/README.md` 明确 `90-reference/` 不参与 process/done 状态判断;`design/90-reference/90-1-filetree.md` 末尾保留“需要我给你...”类对话尾巴;`design/90-reference/90-2-yemianshu.md` 同样保留示例请求口吻。 | 低优先级清理为中性参考笔记,并在引用时强制以 `ARCHITECTURE.md` 和主线设计为上位依据。 |
## 证据
@@ -31,9 +31,9 @@
### AI
- CLI-first 入口基本对齐:`wolai-frontend/src/app/api/ai-agent/run/route.ts:36-57` 将默认执行入口限定到 `mnote-cli host`,拒绝旧 provider。
- 结构化 artifact 设计仍有未完成项:`design/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 中“后续权限收口”仍未勾选,说明 AI 写链权限模型尚未完全闭环
- 旧后端 agent 仍存在:`wolai-backend/app/routers/ai_agent.py:47-57` 仍提供 streaming run;这可作为外置 agent,但需要避免被误认为长期默认主编排。
- CLI-first 入口基本对齐:`wolai-frontend/src/app/api/ai-agent/run/route.ts:36-57` 将默认执行入口限定到 `mnote-cli host`,拒绝旧 provider;但该口径已被 2026-05-13 的 Hermes 面板主线覆盖
- 结构化 artifact 设计应继续保留对象模型,但触发方改为 Hermes tool call -> mnote plugin -> Rust runtime / kernel,见 `design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md``design/07-ai/process/7-2-phase7-structured-artifact-write-chain-v1.md`
- 旧后端 agent 仍存在:`wolai-backend/app/routers/ai_agent.py:47-57` 仍提供 streaming run;这只能作为历史/兼容/对照链,不能被误认为长期默认主编排。
### OnlyOffice
@@ -55,8 +55,8 @@
1. P1:补齐或明确代理 OnlyOffice Rust callback/forcesave 写回链,避免主入口下保存成功但内容未持久化。
2. P2:收口 Mindmap action 可用性,先修 `export` 映射与安全命令不一致,再继续扩展 UI 能力。
3. P2:处理 Mindmap AI route:要么按 Rust bridge/CLI-first 重接 `mindmap_expand_node`,要么显式退役该 Next route。
4. P2:明确 `wolai-backend` AI agent 的外置/对照定位访问边界,避免与 `mnote-cli` 唯一执行面口径冲突。
3. P2:处理 Mindmap AI route:要么按 Hermes plugin / Rust bridge 重接 `mindmap_expand_node`,要么显式退役该 Next route。
4. P2:明确 `wolai-backend` AI agent、旧 `/api/ai-agent/run``mnote-cli host` 的历史/兼容定位访问边界,避免与 Hermes 面板 + mnote plugin 新主线冲突。
5. P3:清理 `design/90-reference` 的问答残留,并在引用规范里再次强调它不是 process/done 设计稿。
## 本次修改文件
+2
View File
@@ -1,3 +1,5 @@
> 执行状态:本文是 10-review 阶段的树域判断记录,不再作为待办清单直接执行。Resource Tree / File Tree / Page Tree 真源合同已由 `/mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md` 承接;主编辑区 Object Tab、mindmap object editor、`index.md` 隔离已由 `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md` 承接。后续只需按 `06-execution-checklist-and-acceptance.md` 保留 smoke 防回归。
结论
我基本同意你的方向,但要把“文件树为根源”说得更精确:不应让“文件树 UI 组件”成为真源,而应让 Rust kernel 中的 workspace resource tree / file tree projection 背后
的资源层级 成为页面、附件、mindmap、OnlyOffice 等对象的组织根源。页面树则是从这棵资源/对象树派生出的导航投影,类似快捷方式、收藏视图或文档视图。
@@ -0,0 +1,438 @@
# 10-review 顺序执行清单与验收标准
> 更新时间:2026-05-14
>
> 上游依据:
> - `/mnt/Data1T/mnote/design/10-review/README.md`
> - `/mnt/Data1T/mnote/design/10-review/01-rust-kernel-web-review.md`
> - `/mnt/Data1T/mnote/design/10-review/02-frontend-editor-tree-review.md`
> - `/mnt/Data1T/mnote/design/10-review/03-convex-realtime-storage-review.md`
> - `/mnt/Data1T/mnote/design/10-review/04-secondary-domains-and-design-governance-review.md`
> - `/mnt/Data1T/mnote/design/10-review/05-tree.md`
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md`
## 0. 执行前提
`05-tree.md` 中关于 Resource Tree / File Tree / Page Tree、ObjectIdentity、mindmap 与 `index.md` 隔离的核心事项已经由 `4-24``5-12` 承接到 `done/`。后续执行不要重复重做该主线,只需要保留防回归验证。
本清单按依赖顺序执行。原则是先修会影响主链一致性和数据可信度的 P0,再处理 realtime / projection / compat 收口,最后做次级域和文档治理。
## 1. P0:修正 Rust page write artifact 一致性
状态:已完成第 1 项代码侧与 Rust 3000 验证。Rust `/api/documents/title``/api/documents/options` 已切到 artifact 记录路径;`page.layout.updateOptions` 已补 tree domain event / conservative resync artifactRust 3000 下已直接验证标题更新后 `bridge.workspace.overview` 可读到 `page.head.updateTitle` command log 与 `tree.node.renamed` domain event`/api/tree/events` 也能输出包含新标题的 stream 事件。完整 File Tree 标题同步仍归入第 6 项继续处理。
目标:让 3000 主入口下的页面标题、页面设置、正文保存拥有一致 side effect,确保 tree stream 能看到相关变更。
建议修改范围:
- `rust/crates/mnote-web/src/routes/documents.rs`
- `rust/crates/mnote-web/src/routes/command_support.rs`
- `rust/crates/mnote-web/src/transport/convex.rs`
- 必要时补充 `wolai-frontend/convex/bridgeLogs.ts` 查询侧验证
执行项:
- [x] 将 Rust `/api/documents/title` 的命令执行切到 artifact 记录路径,或统一复用正式 page/tree command route。
- [x] 将 Rust `/api/documents/options` 的命令执行切到 artifact 记录路径,或统一复用正式 page/tree command route。
- [x] 明确 artifact 写失败策略:主 mutation 成功但 artifact 失败时至少要有可观测错误,并触发保守 resync 或明确降级。
- [x] 补回归测试:标题更新后,`bridgeLogs:listWorkspaceOverview` 能看到对应 command/domain event。
验收标准:
- [x] `cargo test -p mnote-web documents_title -- --nocapture` 或等价标题 route 测试通过。
- [x] `cargo test -p mnote-web documents_options -- --nocapture` 或等价页面设置 route 测试通过。
- [x] 手动或脚本验证:更新标题后 `/api/tree/events` 输出 delta 或 resync,不需要刷新整页才能看到 sidebar/breadcrumb 变化。
- [x] Rust route 与 Next legacy adapter 对同一 page write 的 canonical command / artifacts 语义一致。
证据:
- `rust/crates/mnote-web/src/routes/documents.rs``title` / `options` 使用 `execute_runtime_command_via_convex_with_artifacts`,响应 `meta.artifacts` / `meta.artifactError`
- `rust/crates/bridge-runtime/src/lib.rs``page.layout.updateOptions` 产生 `page.layout.options_updated` domain event 与 `resync_required` stream delta。
- `rust/crates/mnote-web/src/transport/convex.rs`:发送给 legacy `documents:updateOptions` 时剥离 artifact-only 字段。
- 已通过:Rust 3000 目标脚本创建临时页面后调用 `/api/documents/title`,再查 `/api/bridge/workspace?targetPageId=<documentId>&aggregateType=page&aggregateId=<documentId>`,确认返回 `command_name=page.head.updateTitle``event_type=tree.node.renamed`
- 已通过:Rust 3000 目标脚本创建临时页面后调用 `/api/documents/title`,再查 `/api/tree/events?workspaceId=<workspaceId>&maxPolls=1&pollMs=250`,确认 stream 文本包含新标题且包含 `snapshot` / `delta` / `resync` 事件。
- 已通过:`node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
- 部分通过:`node scripts/task110-page-title-single-truth-smoke.js` 在 Rust 3000 下已通过页头、breadcrumb、sidebar、page tree 标题一致性检查;当前失败点为 File Tree 标题行,继续归入第 6 项处理。
- 已通过:`pnpm test -- src/app/api/documents/route-adapters.test.ts src/lib/documents/page-write-command-adapter.test.ts`Vitest 实际执行 112 个测试文件、457 个测试),Next `documents/title` route 与 page write adapter 继续生成 `page.head.updateTitle` canonical command 并进入 Rust artifact writer。
- 已通过:`cargo test -p mnote-web documents_ -- --nocapture`
- 已通过:`cargo test -p bridge-runtime page_head_update_title_command_plan_uses_canonical_page_command -- --nocapture`
- 已通过:`cargo test -p bridge-runtime page_layout_update_options_command_plan_uses_canonical_page_command -- --nocapture`
- 已通过:`cargo test -p bridge-runtime document_options_command_plan_maps_to_documents_update_options -- --nocapture`
- 已通过:`cargo test -p mnote-web convex_command_args_strips_page_options_artifacts_for_legacy_mutation -- --nocapture`
## 2. P0:移除或显式隔离假 fallback 数据
状态:已完成代码侧收口与负向单测。Search 默认不再返回内置固定业务结果;workspace shell projection 默认进入 degraded emptySidebar/File Tree dev fallback 仅在 `allow_dev_fixtures` 开启时可用,并带隐藏 debug/dev 标识。
目标:失败、超时、Convex 不可用时,不再返回看起来像真实业务数据的 fixture / fallback。
建议修改范围:
- `rust/crates/mnote-web/src/routes/search.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- `rust/crates/mnote-web/src/routes/snapshot_support.rs`
执行项:
- [x] 优先移除或强门禁 Search 的 `fallback_search_dataset`
- [x] 对 workspace shell projection 加载失败的最小合成数据做决策:返回明确 degraded/empty/error,不能伪装成真实 workspace projection。
- [x]`allow_dev_fixtures` 相关 sidebar/filetree fallback 增加明确 debug/dev 标识,并确认 3000 默认启动链不会误开。
- [x] 增加负向 smokeConvex/query 不可用时,Search、Sidebar、File Tree 不返回固定假业务数据。
验收标准:
- [x] 关闭或破坏 Convex query 后,Search 不返回内置固定结果。
- [x] 关闭或破坏 projection 加载后,workspace/sidebar/filetree 响应带明确错误、空状态或 degraded 标记。
- [x] 3000 默认入口不会出现 fixture workspace、fixture document、fixture filetree row。
- [x] 相关负向测试或 smoke 在失败场景下能稳定断言“不返回假数据”。
证据:
- `rust/crates/mnote-web/src/routes/search.rs`Convex/search query 失败时,默认返回空结果与 `meta.degraded=true`;仅 `allow_dev_fixtures` 开启时使用 `fallback_search_dataset`
- `rust/crates/mnote-web/src/routes/web_shell.rs`workspace shell projection 失败时,默认返回 degraded empty datasetSidebar/File Tree dev fallback HTML 增加 `data-mnote-dev-fixture` 标识。
- `rust/crates/mnote-web/src/workspace_shell.rs`workspace shell projection/render 支持 `degraded` / `devFixture` 标识。
- 已通过:`cargo test -p mnote-web search_ -- --nocapture`
- 已通过:`cargo test -p mnote-web sidebar_and_filetree_do_not_return_dev_fixtures_by_default -- --nocapture`
- 已通过:`cargo test -p mnote-web workspace_shell_sidebar_html_marks_degraded_and_dev_fixture_states -- --nocapture`
- 已通过:`cargo test -p mnote-web document_shell_returns_page_aggregate_snapshot -- --nocapture`
## 3. P1:补齐 OnlyOffice Rust callback / forcesave 写回链
状态:已完成代码侧与真实 Rust 3000 smoke。Rust `/api/onlyoffice/callback``/api/onlyoffice/forcesave` 已改为显式代理 legacy Next 写回链;未配置 legacy Next 时返回明确失败/unsupported,不再静默 no-op 成功;`task174` 已验证 OnlyOffice 附件打开链路与 docKey。
目标:Rust Web 主入口下 OnlyOffice 保存不能出现“返回成功但未写回”的情况。
建议修改范围:
- `rust/crates/mnote-web/src/routes/onlyoffice.rs`
- `rust/crates/adapter-onlyoffice/`
- `wolai-frontend/src/app/api/onlyoffice/callback/route.ts` 作为 legacy 写回参考
- `scripts/task174-rust-onlyoffice-attachment-open-smoke.js`
执行项:
- [x] 决定 Rust route 直接写回,还是显式代理到 legacy Next 写回链。
- [x] 如果直接写回,接入 media asset / storage 写回与 callback 状态处理。(本轮选择显式代理 legacy Next 写回链,直接写回不适用。)
- [x] forcesave 不再返回纯 `mnote-web-rust-noop` 成功语义;若仍未支持,必须返回明确 unsupported/degraded。
- [x] 补真实浏览器 smoke:打开 OnlyOffice 附件、触发保存、重新打开后内容或版本可验证。
验收标准:
- [x] `/api/onlyoffice/callback` 在需要写回的 status 下会产生真实持久化写入或明确失败。
- [x] `/api/onlyoffice/forcesave` 不再静默 no-op 成功。
- [x] `node scripts/task174-rust-onlyoffice-attachment-open-smoke.js` 或等价 smoke 通过。
- [x] 3000 主链不会 shadow 掉 legacy Next 的真实写回能力。
证据:
- `rust/crates/mnote-web/src/routes/onlyoffice.rs`callback / forcesave 代理到 `legacy_next_base_url` 的 Next `/api/onlyoffice/*` 写回链;缺少 legacy 写回链时返回明确失败。
- 已通过:`cargo test -p mnote-web onlyoffice_ -- --nocapture`
- 已通过:`node scripts/task174-rust-onlyoffice-attachment-open-smoke.js`,输出 `ok=true`,创建临时 docx attachment,打开 `/onlyoffice` 编辑 URL,并返回稳定 `docKey`
## 4. P1:收口 Page Aggregate provenance
状态:已完成代码侧与文档侧收口。`/api/page-aggregate/:id` 继续保持 Rust `mnote.page_aggregate.v1` 主读链,但当底层由 `documents:getMeta + documents:getContent` substrate 构建时,response `source` 与 owner header 已明确标为 `CompatMetaContentJoin` / `compat-join`,不再误写成完整 `KernelProjection`
目标:Page Aggregate 读链保持 Rust-first,但来源标识必须反映真实构建路径,避免把 `meta/content join` 写成完整 kernel-native projection。
建议修改范围:
- `rust/crates/core-protocol/src/page_aggregate.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts`
- `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
- `design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
执行项:
- [x] 明确 `PageAggregateSource::KernelProjection``CompatMetaContentJoin` 的边界。
- [x] 如果 route 仍由 `getMeta + getContent` 构建,返回 source 或 response header 应反映真实来源。
- [x] 文档更新为“Rust route 主读链已成立,底层仍是 Rust runtime adapter 消费 meta/content substrate”。
- [x] 保留或删除 `PageAggregateSource::Fixture` 前,先确认测试和本地 fixture 依赖。(本轮保留,仅用于测试或显式 dev fixture 语义。)
验收标准:
- [x] `/api/page-aggregate/:id` 返回的 source/provenance 与实际构建路径一致。
- [x] 文档中不再出现“Page Aggregate 已完全 kernel-native projection 闭环”的过满表述。
- [x] 前端 loader 仍只消费 `mnote.page_aggregate.v1`,不恢复 TS runtime builder 主链。
- [x] Page Aggregate provenance 变更有 Rust 单测或 loader 单测覆盖。
证据:
- `rust/crates/bridge-runtime/src/lib.rs``page.aggregate.get` 根据 query data 是否包含 `meta + content` 选择 `CompatMetaContentJoin`,否则保留 `KernelProjection`
- `rust/crates/mnote-web/src/routes/web_shell.rs``/api/page-aggregate/:id` 测试断言 `x-mnote-page-aggregate-owner=compat-join``result.source=CompatMetaContentJoin`
- `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`:补充 Rust route 主读链与完整 kernel-native projection 的边界。
- `design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`:补充 `CompatMetaContentJoin` / `KernelProjection` 验收口径。
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`:新增 `source` / provenance 契约说明。
- 已通过:`cargo test -p bridge-runtime page_aggregate_get_query_executes_into_core_projection -- --nocapture`
- 已通过:`cargo test -p mnote-web page_aggregate_endpoint_returns_snapshot_contract -- --nocapture`
## 5. P1:优化 tree stream 的 Convex 查询模型
状态:已完成代码侧与基础 Rust 3000 smoke。`bridgeLogs:listWorkspaceOverview` 已从全量 workspace collect 后内存分页,改为基于 `workspace_id + created_at + id` 及常用过滤组合索引的有界窗口查询;Rust `/api/tree/events` 的 snapshot/delta/resync 协议未改,polling-backed SSE 仍按“最新窗口 + Rust cursor 比较”工作,当前定位为过渡实现。`task123` 和标题更新目标脚本已证明 Rust 3000 stream 可输出 snapshot / title event;完整浏览器 tree integration smoke 仍因 sidebar row 可见性失败,归入第 6 项继续处理。
目标:让 `/api/tree/events` 可以长期承载主链,避免 SSE 轮询叠加全量日志扫描。
建议修改范围:
- `wolai-frontend/convex/bridgeLogs.ts`
- `wolai-frontend/convex/schema.ts`
- `rust/crates/mnote-web/src/routes/sse.rs`
- `rust/crates/mnote-web/src/routes/stream_support.rs`
- `wolai-frontend/src/lib/tree-stream/`
执行项:
- [x]`command_logs` 增加按 `workspace_id + created_at/id` 的查询路径。
- [x]`domain_events` 增加按 `workspace_id + created_at/id` 或 command cursor 的查询路径。
- [x] `listWorkspaceOverview` 不再每轮 collect 全量 workspace 日志后内存分页。
- [x] 明确 polling-backed SSE 是过渡实现还是长期实现;若长期使用,补连接数、日志量、延迟边界测试。(本轮明确为过渡实现,live 轮询仍查最新窗口并由 Rust 做 cursor 比较。)
验收标准:
- [x] 大量历史日志存在时,overview 查询仍按 cursor / limit 返回,不全量 collect。
- [x] `/api/tree/events` 的 snapshot/delta/resync 协议不破坏现有前端消费。
- [x] `pnpm --dir wolai-frontend test -- tree-stream` 或等价 tree-stream 测试通过。
- [x] 至少一条 smoke 覆盖标题/树变更后 SSE 能正常输出增量或保守 resync。
证据:
- `wolai-frontend/convex/schema.ts``command_logs` 增加 `by_workspace_created_at``by_workspace_status_created_at``by_workspace_target_page_created_at``by_workspace_target_block_created_at``domain_events` 增加 `by_workspace_created_at``by_workspace_status_created_at``by_workspace_aggregate_created_at`
- `wolai-frontend/convex/bridgeLogs.ts``listWorkspaceOverview` 改为 `fetchCommandLogWindow` / `fetchDomainEventWindow`,通过索引 `order("desc").take(scanLimit)` 和 cursor 上界查询,移除对 workspace 全量日志的 `.collect()`
- `rust/crates/mnote-web/src/routes/sse.rs`:live poll 语义保持不变,仍清空 bridge pagination cursor 后查最新窗口,再由 Rust 比较当前 stream cursor。
- 已通过:`pnpm test -- src/lib/tree-stream`Vitest 实际执行 111 个测试文件、452 个测试)。
- 已通过:`cargo test -p mnote-web stream_ -- --nocapture`
- 已通过:`cargo test -p mnote-web tree_realtime_route_returns_rust_web_owned_snapshot_event -- --nocapture`
- 已通过:`pnpm exec eslint convex/bridgeLogs.ts convex/schema.ts` 无错误;保留既有 `any` 风格警告。
- 已通过:`node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`
- 已通过:Rust 3000 目标脚本验证 `/api/documents/title``/api/tree/events?maxPolls=1&pollMs=250` 输出包含新标题的 stream 事件。
- 未通过:`node scripts/task120-rust-web-tree-integration-smoke.js` 在等待 child sidebar row 可见时超时,完整浏览器 tree integration 继续归入第 6 项处理。
- 受既有问题阻塞:`pnpm exec tsc --noEmit --pretty false` 仍被 `.next` 旧生成文件、Convex documents 类型和 mindmap/OnlyOffice 等无关类型错误阻塞;本轮新增 `bridgeLogs.ts` / `schema.ts` 未出现在错误列表中。
## 6. P1:统一 Sidebar live cache
状态:代码侧完成,真实浏览器 create / rename / move / archive / purge 主链已通过。`usePreferredSidebarSnapshot` 已从“sync key 与 initial 对比”的特殊规则,改为基于 `getSidebarDataFreshness()` 的 version 仲裁,并保留 tree stream cursor 作为 live 元信息;`AppLayoutShell`、Sidebar、Breadcrumb、SearchPalette 继续消费同一份 preferred snapshot。Rust 3000 下已修复 `tree:delta` 的 title-only patch 被误当成空 projection 渲染的问题,`task110` 已通过页头、breadcrumb、sidebar、page tree、File Tree 标题一致性检查;`task120` 已通过 child sidebar row 可见性、rename live delta、reload 与 purge 后消失检查;`task177` 已覆盖 move / archive live DOM reducer,确认页面树与 File Tree 行局部更新后文档页头仍稳定。
目标:减少 `initial / query / tree_stream` 三源 freshness 选择,把 Sidebar、Breadcrumb、文档页头收口到统一 live cache / projection version 仲裁。
建议修改范围:
- `wolai-frontend/src/components/app-layout-shell.tsx`
- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts`
- `wolai-frontend/src/components/sidebar/sidebar.tsx`
- `wolai-frontend/src/lib/tree-stream/`
执行项:
- [x] 定义 live snapshot 的 version/cursor 仲裁字段。
- [x] `usePreferredSidebarSnapshot` 从 sync key 比较推进到 cursor/version 比较。
- [x] Sidebar、Breadcrumb、文档页头统一消费同一 preferred/live snapshot。
- [x] 保留 initial snapshot 只作为首屏启动输入,不能长期压过更新后的 stream/query。
验收标准:
- [x] 新建、重命名、移动、归档页面后,Sidebar、Breadcrumb、文档页头无互相打架或回退闪烁。
- [x] `usePreferredSidebarSnapshot` 有单测覆盖 query、initial、tree_stream 三者的新旧仲裁。
- [x] 真实浏览器 smoke 覆盖至少一个 tree command 后 Sidebar 与文档页头一致。
证据:
- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts`:输出 `version``cursor`,以 `getSidebarDataFreshness()` 比较 initial/query/tree_streamstream fallback 时不参与仲裁,同版本时优先 live stream。
- `wolai-frontend/src/components/app-layout-shell.tsx`:向 preferred snapshot 仲裁传入 `treeStream.cursor`Sidebar、Breadcrumb、SearchPalette 仍消费同一 preferred data。
- `wolai-frontend/src/components/sidebar/sidebar.tsx`Sidebar 内部 fallback 路径也传入 `treeStream.cursor`
- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx`:新增 query 比旧 stream 更新时优先 query、stream 与 query 同版本时优先 stream 并暴露 cursor 的回归用例。
- 已通过:`pnpm test -- src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx src/components/app-layout-shell.test.tsx src/components/sidebar/sidebar-sync.test.tsx`Vitest 实际执行 111 个测试文件、454 个测试)。
- 已通过:`pnpm exec eslint src/components/sidebar/use-preferred-sidebar-snapshot.ts src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx src/components/app-layout-shell.tsx src/components/sidebar/sidebar.tsx` 无错误;`sidebar.tsx` 保留既有 warning。
- 已通过:`cargo test -p mnote-web tree_realtime_route_returns_rust_web_owned_snapshot_event -- --nocapture`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs``renderSidebarSnapshot` 只在 payload 明确带 projection items 时重渲染 Sidebar;单文档 `upsert_document` title patch 改为只调用 `updateTitleEverywhere()`,避免 rename delta 把 Sidebar 渲染成空树。
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`:新增 `move_document` / `remove_document` 的小型 DOM reducer,移动时更新页面树与 File Tree 行的父节点,归档时局部移除对应行。
- `rust/crates/mnote-web/src/transport/convex.rs`tree create / rename / archive / restore / move 及 compat documents mutation 发送给 legacy Convex mutation 前剥离 artifact-only 字段,避免 validator 因 `streamDeltaHint` / `domainEventPlan` 返回 502。
- 已通过:`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`
- 已通过:`cargo test -p mnote-web convex_command_args_strips -- --nocapture`
- 已通过:`node scripts/task110-page-title-single-truth-smoke.js` 在 Rust 3000 下通过页头、breadcrumb、sidebar、page tree、File Tree 标题一致性检查。
- 已通过:`node scripts/task120-rust-web-tree-integration-smoke.js` 在 Rust 3000 下通过 root/child row、active 切换、rename live delta、reload 与 purge 检查。
- 已通过:`node scripts/task112-tree-rust-family-regression-smoke.js`,但当前页面选项壳缺少 move/embed 入口导致 picker 部分 skipped`move_embed_entry_missing_in_current_page_options_shell`
- 已通过:`node scripts/task177-tree-move-archive-live-smoke.js`,覆盖 `move_document` live event 后页面树 / File Tree 行 `data-parent-id` 更新,以及 `remove_document` live event 后两棵树局部移除归档行;文档页头和标题输入保持根页面标题稳定。
- 已通过:`node scripts/task179-tree-create-delete-no-reload-smoke.js`,覆盖 Rust `/tree` `convex_workspace` 下 File Tree create/delete 与 Page Tree create;最新结果 `ok=true``filetree.createMs=120``filetree.deleteMs=178``page.createMs=120`,对应 mode 内 `navigationEvents.length=0`URL 前后一致。
## 7. P1:明确 `pageSubtree` 与本地正文编辑关系
状态:已完成。策略已固定为:server `pageSubtree` 仍是正式 projection 来源;当本地标题草稿或正文快照与 server snapshot 不一致时,前端允许用当前本地 content 生成临时 `pageSubtree`,并通过 `pageSubtreeSource=local` 暴露给 AI context,避免继续复用旧 server subtree 或让 AI 长期失去结构上下文。React `DocumentAiAgentPanel` 侧已有 client state / runtime 单测覆盖;Rust 3000 当前文档页使用全局浮动页面 AI,本轮已补同等 local `pageSubtree` context 构造与真实浏览器 smoke。
目标:用户正常编辑正文后,AI 面板、阅读视图、结构视图不能因为本地 content 与 server snapshot 不同而长期失去结构上下文。
建议修改范围:
- `wolai-frontend/src/components/editor/page-aggregate-client-state.ts`
- `wolai-frontend/src/components/editor/document-content.tsx`
- `wolai-frontend/src/lib/documents/page-subtree.ts`
- `rust/crates/core-protocol/src/page_aggregate.rs`
执行项:
- [x] 决定 `pageSubtree` 是 server-only projection,还是允许本地临时 projection。
- [x] 标记本地临时 projection 与 server projection 的来源差异,避免把本地临时结构误认为 kernel projection。
- [x] AI 面板读取最新 page aggregate snapshot 时,能拿到本地编辑后的结构上下文。
- [x] 真实编辑器 smoke 验证本地正文编辑后阅读态结构 / AI context 不丢失。
验收标准:
- [x] 本地正文或标题草稿变化后,不再继续复用旧 server `pageSubtree`
- [x] 本地正文或标题草稿变化后,可生成临时 `pageSubtree` 供阅读态 / AI context 使用。
- [x] AI context 明确带 `pageSubtreeSource`,可区分 `server` / `local` / `none`
- [x] 浏览器实测:编辑正文标题或 heading 后,结构面板 / AI context 能看到本地最新结构。
证据:
- `wolai-frontend/src/components/editor/page-aggregate-client-state.ts`:新增 `PageAggregateClientPageSubtreeSource`server 快照未变时返回 `source=server`;本地 content/title 草稿变化时用 `buildPageSubtreeProjection` 生成 `source=local`
- `wolai-frontend/src/components/editor/DocumentAiAgentPanel.tsx``PageAggregateAiSnapshot` 增加 `pageSubtreeSource`
- `wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx`AI request context 增加 `pageSubtreeSource`
- `wolai-frontend/src/components/editor/page-aggregate-client-state.test.ts`:覆盖本地正文/标题变化后生成临时 `pageSubtree`,以及 server snapshot 下 `pageSubtreeSource=server`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`Rust 3000 全局浮动页面 AI 在发送 `/api/ai-agent/run` 前读取当前 Leptos/Tiptap 编辑器 DOM;当本地内容与 server aggregate 正文不同,生成 `source=local` 的临时 subtree / outline / evidence,并在 context 中发送 `pageSubtreeSource=local`
- 已通过:`pnpm test -- src/components/editor/page-aggregate-client-state.test.ts src/components/editor/DocumentAiAgentPanel.runtime.test.tsx src/components/editor/document-content.test.ts`Vitest 实际执行 111 个测试文件、454 个测试)。
- 已通过:`node scripts/task178-page-ai-local-subtree-context-smoke.js`,Rust 3000 下打开真实文档页,把编辑器内容改成本地 heading 后打开页面 AI,并拦截 `/api/ai-agent/run` 请求确认 `context.pageSubtreeSource=local``documentBlocks` / `outline` / `subtree.stats.headingCount` 均包含本地最新 heading。
- 已通过:`pnpm exec eslint src/components/editor/page-aggregate-client-state.ts src/components/editor/page-aggregate-client-state.test.ts src/components/editor/DocumentAiAgentPanel.tsx src/components/editor/DocumentAiAgentPanel.runtime.tsx` 无错误;`DocumentAiAgentPanel.runtime.tsx` 保留既有 `any` warning。
## 8. P2:清理或标注 stale mapping / legacy compat
状态:已完成。`/api/documents/page``/api/mnote-web/stream` 已经明确退场为 410 compat 边界;`storage-convex-bridge` 不再把 `page.aggregate.get` 映射到不存在的 Convex `documents:getPageAggregate`,而是显式落到 `queries:unknown` 边界。Rust `/api/page-aggregate/:id` 当前先读取 `documents.meta.get` / `documents.content.get`,再交给 `bridge-runtime``page.aggregate.get` 组装 core projection,不走缺失 Convex function。
目标:保留的 compat 都必须是显式迁移/调试边界;不再让后续实现误把 stale mapping 当主链。
建议修改范围:
- `rust/crates/storage-convex-bridge/src/mapping.rs`
- `rust/crates/mnote-web/src/app.rs`
- `rust/crates/mnote-web/src/routes/gateway.rs`
- `rust/crates/mnote-web/src/routes/documents.rs`
- `wolai-frontend/src/lib/documents/page-aggregate-builder.ts`
- `wolai-frontend/src/app/api/documents/page/route.ts`
- `wolai-frontend/src/app/api/mnote-web/stream/route.ts`
执行项:
- [x] 处理 `page.aggregate.get -> documents:getPageAggregate`:实现、移除或标注为 future/stale。
- [x] legacy Next proxy 保留时,命名与注释统一为 explicit migration/debug boundary。
- [x] TS `page-aggregate-builder` 保留为测试/adapter/reference,不得重新接回 runtime 主链。
- [x] 410 compat route 保持退场语义,并补负向测试防止重新启用。
验收标准:
- [x] 全仓没有生产路径调用不存在的 `documents:getPageAggregate`
- [x] 默认 3000 主链不走 legacy Next proxy。
- [x] `/api/documents/page``/api/mnote-web/stream` 继续保持明确退场响应。
- [x] 文档和代码注释不会把 compat/fallback 描述成当前主路径。
证据:
- `rust/crates/storage-convex-bridge/src/mapping.rs``page.aggregate.get` 显式标注为 stale/future compat placeholder。
- `wolai-frontend/src/app/api/documents/page/route.ts`:返回 410,提示直接使用 `/api/page-aggregate/:documentId`
- `wolai-frontend/src/app/api/mnote-web/stream/route.ts`:返回 410,提示直接使用 `/api/tree/events`
- `design/10-review/03-convex-realtime-storage-review.md`:已明确指出 `documents:getPageAggregate` 目前缺少 Convex 实现,属于方向滞后或 future/stale 口径。
- `rust/crates/storage-convex-bridge/src/mapping.rs``page.aggregate.get` 改为 `queries:unknown`,并注释说明 page aggregate 主读链由 Rust route + `bridge-runtime` 组装,避免误触发不存在的 `documents:getPageAggregate`
- 已通过:`rg -n "documents:getPageAggregate|getPageAggregate" rust wolai-frontend src -g '!node_modules' -g '!target'`,仅剩 mapping 注释说明,无生产调用。
- 已通过:`cargo test -p storage-convex-bridge -- --nocapture`
- 已通过:`cargo test -p bridge-runtime page_aggregate_get_query_executes_into_core_projection -- --nocapture`
## 9. P2:处理 Mindmap 次级未闭合项
状态:已完成。Mindmap `export` 不再映射到未受控的 `EXPORT` runtime commandUI state 继续禁用该入口;旧 `/api/mindmap-ai/expand-node` Next route 已显式退役为 410,并指向 AI Agent 内置 `mindmap_expand_node` 工具;legacy MindmapSidebar 中调用旧 route 的补完入口已从渲染层关闭。mindmap 内容保存主链仍保持 `mindmap.command.apply`,没有回退到 Page Aggregate body 保存链;真实 3000 `task169` 已通过,证明 mindmap 与 `index.md` 隔离未回归。
目标:`05-tree.md` 的 mindmap 污染 `index.md` 已完成;剩余处理能力入口与实现不一致的问题。
建议修改范围:
- `wolai-frontend/src/lib/mindmap/mindmap-action-map.ts`
- `wolai-frontend/src/lib/mindmap/simple-mind-map-bridge.ts`
- `wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts`
- `rust/crates/core-protocol/src/tool.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
执行项:
- [x] 处理 Mindmap `EXPORT`:加入安全命令并补 smoke,或在 UI state 中禁用并标注延期。
- [x] 处理 Mindmap AI expand route:按 Hermes plugin / Rust bridge 重接,或显式退役该 route。
- [x] 保持 mindmap 内容保存继续走 `mindmap.command.apply`,不得回退到 Page Aggregate body 保存链。
验收标准:
- [x] UI 不展示无法执行的 Mindmap export 能力,或 export 能真实执行并有测试覆盖。
- [x] `/api/mindmap-ai/expand-node` 不再表现为“产品入口存在但稳定 501”而无说明。
- [x] `node scripts/task169-mindmap-realtime-smoke.js` 继续通过,证明 mindmap 与 `index.md` 隔离未回归。
证据:
- `wolai-frontend/src/lib/mindmap/mindmap-action-map.ts``export` 改为 `localView`,不再下发 `runtimeCommand: "EXPORT"`
- `wolai-frontend/src/lib/mindmap/mindmap-ui-state.ts``unsupportedActionIds` 继续包含 `export`,UI state 中该入口保持禁用。
- `wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts`:旧 Next route 返回 410`x-mnote-compat-boundary=mindmap-expand-node-route-retired`,并指向 `/api/ai-agent/run` + `mindmap_expand_node`
- `wolai-frontend/src/components/editor/blocks/MindmapSidebar.tsx`legacy 侧栏中调用 `/api/mindmap-ai/expand-node` 的补完入口通过 `MINDMAP_LEGACY_EXPAND_NODE_ENTRY_ENABLED=false` 从渲染层关闭。
- 已通过:`pnpm test -- src/lib/mindmap/mindmap-action-map.test.ts src/lib/mindmap/mindmap-ui-state.test.ts src/lib/mindmap/simple-mind-map-bridge.test.ts src/app/api/mindmap-ai/expand-node/route.test.ts`Vitest 实际执行 112 个测试文件、457 个测试)。
- 已通过:`pnpm exec eslint src/lib/mindmap/mindmap-action-map.ts src/lib/mindmap/mindmap-action-map.test.ts src/lib/mindmap/mindmap-ui-state.test.ts src/app/api/mindmap-ai/expand-node/route.ts src/app/api/mindmap-ai/expand-node/route.test.ts src/components/editor/blocks/MindmapSidebar.tsx` 无错误;保留 `MindmapSidebar.tsx` 既有 `AiPanel` 未使用 warning。
- 已通过:`cargo test -p bridge-runtime mindmap_command_apply_plan_uses_kernel_command_facade -- --nocapture`,确认 mindmap 内容保存仍走 `mindmap.command.apply` / `mindmaps:applyCommand` facade。
- 已通过:`node scripts/task169-mindmap-realtime-smoke.js`(Rust 3000 已启动,脚本退出码 0)。最终结果 `ok=true``failures=[]`,同一页面最终 File Tree mindmap row 数量为 1`mindmapId=mindmap_1778695090474`object identity 仍为同一 `objectKind:"mindmap"`
- 已通过:`cargo test -p bridge-runtime mindmap -- --nocapture`17 个 mindmap 相关测试通过;普通 `mindmap.command.apply` 改为 `mindmap.content.updated + noop``mindmaps.put createOnly=false` 也不再默认 tree resync。
- 已通过:`cargo test -p mnote-web mindmap -- --nocapture`,6 个 mindmap 相关测试通过,覆盖 Rust `/api/mindmap/:docId/:mindmapId` 的 object artifact 响应。
- 已通过:`node scripts/task169-mindmap-realtime-smoke.js` 最新结果 `ok=true``failures=[]`6 条可解析普通 `mindmap.command.apply` 响应均为 `eventType=mindmap.content.updated``streamOp=noop`,另有 1 条 Playwright response body 读取失败被记录为 `skippedReadFailures=1`;同一页面最终仍只有 1 条 mindmap asset row`mindmapId=mindmap_1778698542703`
## 10. P3:文档治理与回填
状态:已完成文档侧收口。`10-review/README.md` 已指向本执行清单;`05-tree.md` 已标明 Resource Tree / ObjectIdentity / mindmap 与 `index.md` 隔离主线由 `4-24` / `5-12` 承接完成;`5-5-1` 已修正 `showHeadingNumbers` / `embedDefaultBlockId` 的 runtime payload 口径;`90-reference` 的对话式尾巴已清理,`design/README.md` 已补充参考目录不能覆盖主线设计的引用边界。
目标:让设计目录继续反映真实主线,不把参考资料或过时清单误当成当前架构。
建议修改范围:
- `design/10-review/README.md`
- `design/10-review/05-tree.md`
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
- `design/90-reference/`
执行项:
- [x]`05-tree.md` 中补充最终采用的 object editor / object tab 方案摘要,并说明核心执行已由 `4-24` / `5-12` 完成。
- [x] 修正 `5-5-1``showHeadingNumbers` / `embedDefaultBlockId` 已接入 runtime payload 的滞后描述。
- [x] 清理 `design/90-reference` 问答式残留,保留为中性参考笔记。
- [x] 在引用规范中再次强调 `90-reference` 不参与 process/done 状态判断,不是上位架构来源。
验收标准:
- [x] `design/10-review/README.md` 能指向本执行清单。
- [x] `05-tree.md` 不再让后续 worker 误以为 Resource Tree / ObjectIdentity / mindmap 止血仍未执行。
- [x] `design/90-reference` 的内容不再保留“需要我给你...”这类对话尾巴。
- [x] 主线文档对 Page Aggregate、tree realtime、ObjectIdentity 的描述与当前代码事实一致。
证据:
- `design/10-review/README.md`:补充执行入口,指向 `06-execution-checklist-and-acceptance.md`,并说明 `05-tree.md` 核心主线已由 `4-24` / `5-12` 承接。
- `design/10-review/05-tree.md`:开头增加执行状态,明确本文不再作为待办清单直接执行。
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md``showHeadingNumbers` / `embedDefaultBlockId` 改为“runtime payload 已贯通,但深语义未完成”的口径。
- `design/90-reference/90-1-filetree.md``design/90-reference/90-2-yemianshu.md`:删除“需要我给你...”对话尾巴。
- `design/README.md`:补充 `90-reference/` 只能作为生态资料或背景材料,不能覆盖主线设计稿。
- `design/03-rust-web/done/3-15-runtime-fallback-retirement-checklist-v1.md`:从 `process/` 迁入 `done/`,文件自身 checkbox 已全部完成,且与第 2 / 第 8 项 fallback、compat 退场证据一致。
- `design/06-mindmap/done/6-mindmap-phase6-kmind-parity-detail-checklist-v1.md`:从 `process/` 迁入 `done/`,文件自身 checkbox 已全部完成,并已有 `task167-mindmap-kmind-parity-smoke.js` 等 Phase 6 KMind parity 证据。
- 已验证:`rg -n "需要我给你|我可以|你要不要|是否需要|请告诉我|如果你愿意|要我|我来" design/90-reference` 无匹配。
- 已完成只读状态审计:`design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md``design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md``design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md``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/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 仍有未完成项或长期主线尾项,继续保留在 `process/``design/old/**` 不纳入活跃 process/done 迁移判断。
## 11. 全局 Done Gate
全部清单完成前,不应把 10-review 状态描述为“单一真源已完全闭环”。可以描述为:
> Rust 已经持有主语义主导权,Page Aggregate、tree command、tree realtime、Resource/ObjectIdentity 主线已经切入;剩余工作集中在 artifact 一致性、fallback 退场、realtime 查询模型、provenance 口径和 compat 边界收口。
全部完成的最低验收:
- [x] P0 两项全部完成,且有自动化测试或真实 smoke 证据。
- [x] `05-tree.md` 已执行事项通过 `task169` 或等价 smoke 防回归。
- [x] `/api/page-aggregate/:id` provenance 与真实来源一致。
- [x] `/api/tree/events` 不依赖全量日志扫描作为长期查询模型。
- [x] 3000 默认主链不返回假业务数据、不走 legacy proxy、不静默 no-op 保存。
- [x] 设计文档中的 `process/``done/` 状态与真实代码和验收证据一致。
## 12. 关联缺陷验收状态
本执行清单本身已完成;与本轮用户反馈直接相关的两个 bug 文档状态如下:
- `bugs/04-tree-domain/done/4-25-tree-command-create-delete-reload-latency-v1.md`Rust `/tree` debug shell 的 create / delete 强刷新子项已关闭,`task179` 已通过无 reload 与耗时验收;React Sidebar 直接 create/delete 已改为本地更新,不再同步等待 `refreshTree()`Rust-family React shell 的 create / rename / move host mutation 回调也已改为本地 apply,不再默认整树 refetch。文档已迁入 `done/`
- `bugs/05-editor-mainline/done/5-11-mindmap-ghost-assets-and-tree-command-latency-v1.md`ghost mindmap asset 增长的 NodeView document id 误归属链路已止血,`task169` 已通过同一页面单 mindmap row 与 object identity 稳定验收;普通 mindmap 节点编辑 artifact 语义已收窄为 `mindmap.content.updated + noop`;已新增 `task180` 只读候选审计脚本用于清理前取证,并支持 `/api/tree/projections/file` 的只读投影输入以审计 File Tree duplicate row。真实 File Tree projection 审计已确认当前 workspace 下 `fileTreeMindmapRows=10``duplicateFileTreeMindmapRows=0``candidateCount=0`。历史清理候选为 0,因此本轮不执行数据删除;文档已迁入 `done/`
+3
View File
@@ -2,6 +2,8 @@
本目录汇总当前设计与实现的偏差审查结果。四份分域报告分别覆盖 Rust kernel/web、前端编辑器与树体验、Convex/realtime/storage、次级域与设计治理。
执行入口以 [顺序执行清单与验收标准](./06-execution-checklist-and-acceptance.md) 为准;`05-tree.md` 的 Resource Tree / File Tree / Page Tree、ObjectIdentity、mindmap 与 `index.md` 隔离主线已经由 `4-24``5-12` 承接到 `done/`,后续只保留防回归验证。
## 结论
当前主线方向总体没有跑偏,但实现仍停留在“主链已切、收口未完”的状态。最需要继续收口的是:
@@ -36,6 +38,7 @@
- [前端编辑器 / 树体验实现偏差审查](./02-frontend-editor-tree-review.md)
- [Convex / Realtime / Storage 实现偏差审查](./03-convex-realtime-storage-review.md)
- [次级域与设计治理实现偏差审查](./04-secondary-domains-and-design-governance-review.md)
- [顺序执行清单与验收标准](./06-execution-checklist-and-acceptance.md)
## 统一判断
+1 -3
View File
@@ -79,6 +79,4 @@ yy - 复制,p - 粘贴
:q - 退出
```
如果需要将文件树组件集成到自己的Rust项目中,建议根据应用类型(终端/GUI)选择对应的组件库,或参考filetree的实现方式进行定制开发。
需要我给你一份在 Ratatui 中快速集成 tui-file-explorer 实现 VSCode 风格单面板文件树的最小可运行代码示例吗?
如果需要将文件树组件集成到 Rust 项目中,建议根据应用类型(终端/GUI)选择对应的组件库,或参考 filetree 的实现方式进行定制开发。
-2
View File
@@ -143,5 +143,3 @@ fn main() -> eframe::Result<()> {
### 七、总结
Rust生态中已有丰富的工具可构建Notion风格页面树,从底层数据结构到完整应用全覆盖。若需快速开发,可选择现成组件库或应用;若需高度定制,可基于树结构库+UI框架组合实现,充分发挥Rust的性能与安全优势。
需要我给你一份在 Tauri 中结合 Rust 后端与前端实现可拖拽 Notion 风格页面树的最小可运行示例吗?
+2 -1
View File
@@ -1,6 +1,6 @@
# design 设计稿索引
> 更新时间:2026-05-11
> 更新时间:2026-05-14
>
> 状态口径以当前仓库真实代码为准:
> - `[done]`:对应阶段或收口目标已经在当前主线代码中成立
@@ -51,6 +51,7 @@
- `90-reference/`
- 参考资料,不参与 `[done]/[process]/[recycle]` 状态判断
- 引用时只能作为生态资料或背景材料,不能覆盖 `ARCHITECTURE.md``AGENTS.md``01-05` 当前优先级或对应主线 `process/` / `done/` 设计稿
- `old/`
- 已废弃或被替代的历史稿件,标题统一标记 `[recycle]`
- 每个大类继续按 `process/``done/` 分层
@@ -1,7 +1,17 @@
# 7 [process] mnote Kernel Phase 7 CLI-First 外置 Agent 统一执行面方案 v4
# 7 [recycle] mnote Kernel Phase 7 CLI-First 外置 Agent 统一执行面方案 v4
> 更新时间:2026-05-09
>
> 回收说明(2026-05-13):
> - 本稿的 `mnote-cli` 唯一长期 agent 执行面口径已被
> `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`
> 覆盖。
> - 后续长期方向改为:页面 AI 面板只是 Hermes 的页面内客户端;
> Hermes session/message/tool event/usage/model 才是会话真相;
> mnote 通过 Hermes skill/plugin 暴露页面、树、artifact 与 edge 能力。
> - 本稿仅作为历史决策记录保留。`mnote-cli` 仍可作为 mnote plugin 内部适配器,
> 但不再是页面 AI 的唯一长期执行面。
>
> 上位依据:
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`