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
@@ -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、外部点击和更多层级收起 |