chore: land tree view-state, vault, Pi module split, and repo hygiene
Persist PageTree expand state via control-plane view-state and align chevron/DOM with restored expansion; keep Sidex-style shallow page-tree scan and drop the unused recursive scanner that only added cargo noise. Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi into a module package, and retire Hermes/ACP/OpenHub recycle + root harness evidence from the index while gitignoring recycle and local diag dumps. Archive superseded design/bugs docs under old/, point architecture at ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor regressions so the working tree can stay clean.
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
# [recycle] Rust Kernel / Web 实现偏差审查
|
||||
|
||||
> 执行状态:已归档到 `design/10-review/done/`。本文保留为历史审查快照,当前活跃口径以 `./06-execution-checklist-and-acceptance.md` 为准。
|
||||
|
||||
## 范围
|
||||
|
||||
本次只审查 Rust kernel / protocol / bridge-runtime / mnote-web / storage-convex-bridge 与当前设计主线的一致性。
|
||||
|
||||
重点对照设计:
|
||||
|
||||
- `ARCHITECTURE.md`
|
||||
- `design/01-05-current-priority-overview.md`
|
||||
- `design/01-tree-first-graph-kernel/reference/1-tree-first-graph-kernel-v1.md`
|
||||
- `design/01-tree-first-graph-kernel/reference/1-1-tree-first-graph-kernel-checklist-v2.md`
|
||||
- `design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md`
|
||||
- `design/03-rust-web/reference/3-rust-web-long-term-architecture-v1.md`
|
||||
- `design/03-rust-web/reference/3-1-rust-web-long-term-checklist-v2.md`
|
||||
- `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
- `design/03-rust-web/process/3-15-runtime-fallback-retirement-checklist-v1.md`
|
||||
- `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||||
|
||||
重点代码:
|
||||
|
||||
- `rust/crates/core-protocol/`
|
||||
- `rust/crates/core-domain/`
|
||||
- `rust/crates/bridge-runtime/`
|
||||
- `rust/crates/mnote-web/`
|
||||
- `rust/crates/storage-convex-bridge/`
|
||||
|
||||
## 结论
|
||||
|
||||
Rust kernel / protocol / bridge-runtime / mnote-web 的主干方向与当前设计基本一致:`core-protocol` 已有统一 kernel node / edge / projection / page aggregate 协议;`bridge-runtime` 已能执行 kernel query / command / projection;`mnote-web` 已持有 3000 主入口、文档页 shell、`/api/page-aggregate/:id`、tree command、`/api/tree/events`、Search shell 等关键接缝;`storage-convex-bridge` 明确把 `tree.*` / `page.*` 等语义命令映射到底层 Convex mutation。
|
||||
|
||||
主要偏差不是“Rust 主线没有落地”,而是当前仍存在几类收口缺口:
|
||||
|
||||
1. 部分 Rust Web 主路径仍会静默合成 fallback / fixture 数据,和 `Runtime Fallback 退场` 的激进口径不完全一致。
|
||||
2. Page Aggregate 对外标记为 `KernelProjection`,但 Rust Web 仍先取 `documents.meta/content` 再在 runtime 内拼成聚合;这是合理过渡实现,但“单一 kernel 真源”口径容易被写得过满。
|
||||
3. Tree realtime 已有正式 SSE 主链,但实现仍偏轮询 bridge overview,WS 仍是 snapshot/resync 骨架,尚未成为完整实时主链。
|
||||
4. Legacy / compat 能力默认大多关闭,但配置位、proxy 辅助函数和兼容源枚举仍在,设计文档中“全部退场”的勾选状态可能偏乐观。
|
||||
|
||||
## 关键发现表格
|
||||
|
||||
| 编号 | 分类 | 严重度 | 发现 | 证据 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| RK-01 | 未完成 | 中 | Kernel projection 仍主要从 `sidebar.dataset.list` 这一份 Convex 数据集构建,广义 node pool / reference edge / summary/index 真相层尚未完全独立。 | `rust/crates/mnote-web/src/routes/snapshot_support.rs:27` 定义 `sidebar.dataset.list`;`:93` 先加载 sidebar dataset 再执行 `kernel.project_view`;`rust/crates/bridge-runtime/src/lib.rs:8010` 后续由 sidebar 数据构建 subtree;`:8111` 再转为 projection。 |
|
||||
| RK-02 | 实现偏差 / 风险 | 高 | Rust Web 主路径仍存在静默 fallback:workspace shell 在 projection 加载失败时无条件合成最小 workspace/documents;sidebar/filetree 在 `allow_dev_fixtures` 时合成开发数据;Search 在 Convex query 失败时返回内置搜索数据。这与 `3-15-runtime-fallback-retirement` 的“默认不再 fallback”口径冲突。 | `rust/crates/mnote-web/src/routes/web_shell.rs:2379` 到 `:2401` 无条件 fallback 最小 dataset;`:2432` 到 `:2461` 合成 sidebar dev dataset;`:2493` 到 `:2519` 合成 filetree dev dataset;`rust/crates/mnote-web/src/routes/search.rs:233` 到 `:247` 搜索失败后走 `fallback_search_dataset`。 |
|
||||
| RK-03 | 方向变化 / 文档滞后 | 中 | Page Aggregate 已由 Rust Web 暴露正式 route,但实现仍是先读 `documents.meta/content`,再把 joined data 交给 `page.aggregate.get` 生成 projection。代码对外 source 标成 `KernelProjection`,但底层仍是 meta/content join 的迁移形态;需要文档明确这是 Rust runtime adapter,而非完整 kernel storage 真源。 | `rust/crates/mnote-web/src/routes/web_shell.rs:2282` 到 `:2315` 先读 meta/content 再执行 `page.aggregate.get`;`rust/crates/bridge-runtime/src/lib.rs:8246` 到 `:8253` 将 source 传为 `PageAggregateSource::KernelProjection`;`:5303` 到 `:5324` 从 meta/content 中抽取 title/content/revision。 |
|
||||
| RK-04 | 未完成 | 中 | Tree realtime 正式 route 已存在,但当前 SSE 仍通过 polling `bridge.workspace.overview` 生成 snapshot/delta/resync;WS 只发送初始 snapshot,并只支持客户端请求 resync,不是完整主实时链路。 | `rust/crates/mnote-web/src/routes/sse.rs:51` 到 `:91` 循环 sleep/poll overview 后生成 delta;`rust/crates/mnote-web/src/routes/ws.rs:32` 发送 snapshot,`:40` 到 `:80` 只处理 resync 或 unsupported ack。 |
|
||||
| RK-05 | 风险 / 文档滞后 | 低 | Legacy Next compat 默认关闭,但代码仍保留 env-gated proxy 能力、Next proxy 辅助函数和 compat 配置位。若文档继续写“legacy compat 已删除”,会与代码事实不一致;若保留,应写成显式调试/迁移边界。 | `rust/crates/mnote-web/src/app.rs:42` 到 `:54` 仍读取 `MNOTE_WEB_LEGACY_NEXT_BASE_URL` / `MNOTE_WEB_ENABLE_LEGACY_NEXT_COMPAT`;`rust/crates/mnote-web/src/routes/gateway.rs:86` 到 `:87` auth API 可转 legacy proxy;`:402` 到 `:499` legacy proxy 实现仍在;`rust/crates/mnote-web/src/routes/documents.rs:153` 当前 `should_proxy_via_next` 返回 false,但 Next proxy helper 仍保留。 |
|
||||
| RK-06 | 实现一致 | 低 | Tree command cutover Stage 2 的长期命名面已在 Rust route / bridge / storage mapping 中落地,`documents.*` 仍作为 alias/底层 Convex mutation 名称存在,和 Stage 2A/2B 过渡口径基本一致。 | `rust/crates/storage-convex-bridge/src/mapping.rs:37` 到 `:43` 映射 `tree.*`;`:52` 到 `:68` 保留 `documents.*` / `page.*` alias;`rust/crates/mnote-web/src/tree_shell/dispatcher.rs:16` 到 `:23` 使用 `tree.*`;`rust/crates/mnote-web/src/routes/tree.rs:7634` 附近测试确认 documents alias 映射仍存在。 |
|
||||
| RK-07 | 实现一致 | 低 | Debug shell 默认关闭,符合 `3104`/debug 壳默认退场口径。 | `rust/crates/mnote-web/src/routes/mod.rs:120` 到 `:124` 仅在 `enable_debug_shell_routes` 时挂 `/tree` 和 `/document-debug`;同文件测试 `:142` 到 `:184` 验证默认 404。 |
|
||||
|
||||
## 证据
|
||||
|
||||
### 1. Kernel / projection 已落地,但底层仍主要基于 sidebar dataset
|
||||
|
||||
- `rust/crates/core-protocol/src/kernel.rs:7` 到 `:42` 定义 `KernelNodeType`、`KernelEdgeType`、`KernelProjectionKind`。
|
||||
- `rust/crates/mnote-web/src/routes/kernel.rs:62` 到 `:85` 暴露 projection route,并经 `load_projection_snapshot` 获取投影。
|
||||
- `rust/crates/mnote-web/src/routes/snapshot_support.rs:27` 到 `:47` 固定 `sidebar.dataset.list` 为数据获取入口。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs:8119` 到 `:8137` 在没有 root 时由当前数据构建 subtree,再生成 projection;`file_tree` 另走 `build_file_tree_projection_result`。
|
||||
|
||||
判断:这符合 `1-1` 中“真实主线已落地,但更广义 node pool/reference edge 仍后续”的口径,不应被写成“尚未开始”;也不应被写成“完整 kernel 真源已闭环”。
|
||||
|
||||
### 2. Runtime fallback 仍在主路径附近
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2379` 到 `:2401`:workspace shell projection 加载失败时合成 `active_workspace_id`、`workspaces`、`documents`。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2432` 到 `:2461`:`allow_dev_fixtures` 时合成 sidebar dev dataset。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2493` 到 `:2519`:`allow_dev_fixtures` 时合成 filetree dev dataset。
|
||||
- `rust/crates/mnote-web/src/routes/search.rs:233` 到 `:247`:搜索 Convex 查询失败后执行 `fallback_search_dataset(workspace_id)`。
|
||||
|
||||
判断:这与 `design/03-rust-web/process/3-15-runtime-fallback-retirement-checklist-v1.md` 的“默认运行时不再 fallback / 不静默降级”存在偏差。尤其 Search fallback 会产生看似真实的固定结果,风险高于空状态降级。
|
||||
|
||||
### 3. Page Aggregate 是 Rust route,但仍是迁移期 adapter
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs:53` 到 `:56` 挂载 `/api/page-aggregate/{document_id}`。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2264` 到 `:2319` 构建 aggregate。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs:5291` 到 `:5442` 从 meta/content 形状构建 `PageAggregateProjection`。
|
||||
- `rust/crates/core-protocol/src/page_aggregate.rs:4` 到 `:10` 协议层仍保留 `KernelProjection`、`CompatMetaContentJoin`、`Fixture` 三种 source。
|
||||
- `rust/crates/mnote-web/src/page_aggregate/builder.rs:66` 默认 source 仍是 `CompatMetaContentJoin`,但当前检索未发现该 builder 进入主要 route 主路径。
|
||||
|
||||
判断:当前代码已摆脱 TS builder runtime 主链,但还不是“页面域全部直接来自独立 kernel storage 真源”。文档应保留“Rust-first Page Aggregate 过渡态”的精确口径。
|
||||
|
||||
### 4. Tree realtime 主链已成立,但不是完整实时闭环
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs:113` 挂载 `/api/tree/events`,`:114` 挂载 `/api/stream/events`,`:115` 挂载 `/api/realtime/ws`。
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs:127` 到 `:146` 给 `/api/tree/events` 打 `x-mnote-web-owner: mnote-web` 与 `x-mnote-tree-stream-owner: rust-web`。
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs:51` 到 `:91` 通过 sleep/poll bridge overview 发现变化。
|
||||
- `rust/crates/mnote-web/src/routes/ws.rs:32` 到 `:80` WebSocket 当前只发 snapshot,并响应 resync 请求。
|
||||
|
||||
判断:这与 `3-3` 中“已完成 route / snapshot / delta / resync 基础,WS 尚未成为主链,live cache 未完全统一”一致;若 `3-15` 写成 tree realtime 补偿链已完全删除,则偏乐观。
|
||||
|
||||
### 5. Compat / legacy 未成为默认主链,但未完全删除
|
||||
|
||||
- `rust/crates/mnote-web/src/app.rs:42` 到 `:54` 仍读取 legacy Next 相关环境变量,默认 `enable_legacy_next_compat` 为 false。
|
||||
- `rust/crates/mnote-web/src/routes/gateway.rs:86` 到 `:87` 在 compat 开启且配置 legacy base URL 时,`/api/auth` 可走 legacy proxy。
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs:153` 当前 `should_proxy_via_next` 返回 false,说明文档 API 默认不走 Next proxy。
|
||||
|
||||
判断:代码实际状态更像“默认关闭、残留显式迁移/调试能力”,不是“所有 legacy proxy 代码已删除”。
|
||||
|
||||
## 建议优先级
|
||||
|
||||
### P0
|
||||
|
||||
- 移除或显式隔离 Search 的 `fallback_search_dataset`。失败时应返回明确错误或空 projection,并带可观测错误头;不要返回固定假结果。
|
||||
- 对 `load_workspace_shell_projection` 的无条件合成 dataset 做决策:若用于首屏容错,应在响应或 contract 中显式标记 degraded;若严格执行 fallback 退场,应改为失败或空树,不再伪装成真实 workspace projection。
|
||||
|
||||
### P1
|
||||
|
||||
- 将 `allow_dev_fixtures` 相关 fallback 全部加上更明确的 debug/dev 标识,并确认 `desktop:hot` / 3000 默认启动链不会误开。
|
||||
- 补一条 Rust Web smoke:Convex/query 不可用时,搜索、Sidebar、filetree 不应返回假业务数据。
|
||||
- Page Aggregate 文档补一句:当前 Rust route 已是主读链,但底层仍通过 Rust runtime adapter 消费 meta/content substrate;完整页面域单一真源仍在推进。
|
||||
|
||||
### P2
|
||||
|
||||
- Tree realtime 后续应把 SSE polling overview 与真正 domain event stream 的边界写清楚,并继续推进 page subtree / filetree / preferred snapshot 同一 live cache。
|
||||
- Legacy Next proxy 若仍需保留,建议统一命名为 explicit migration/debug boundary;若不再需要,后续单独删除 proxy helper、配置位和测试样例,避免和 `3-15` 的退场状态长期冲突。
|
||||
- `PageAggregateSource::CompatMetaContentJoin` / `Fixture` 是否继续保留在 `core-protocol` 需要架构决策:保留则标记为迁移态 source;删除则需先确认没有测试、local folder、fixture 依赖。
|
||||
|
||||
## 修改的文件路径
|
||||
|
||||
- `/mnt/Data1T/mnote/design/10-review/done/01-rust-kernel-web-review.md`
|
||||
@@ -0,0 +1,125 @@
|
||||
# [recycle] 前端编辑器 / 树体验实现偏差审查
|
||||
|
||||
> 执行状态:已归档到 `design/10-review/done/`。本文保留为历史审查快照,当前活跃口径以 `./06-execution-checklist-and-acceptance.md` 为准。
|
||||
|
||||
## 范围
|
||||
|
||||
本报告只审查 `wolai-frontend` 当前文档页、`leptos-tiptap` island host、`Page Aggregate` 消费链、Sidebar / tree shell / tree stream 与设计文档的一致性。
|
||||
|
||||
重点对照设计:
|
||||
|
||||
- `ARCHITECTURE.md`
|
||||
- `design/01-05-current-priority-overview.md`
|
||||
- `design/04-tree-domain/done/4-sidebar-pagetree-filetree-rust-web-rebuild-v1.md`
|
||||
- `design/04-tree-domain/done/4-2-sidebar-pagetree-filetree-product-interaction-contract-v1.md`
|
||||
- `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||||
- `design/04-tree-domain/done/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md`
|
||||
- `design/04-tree-domain/process/4-23-local-cloud-explicit-bridge-p3-candidate-v1.md`
|
||||
- `design/05-editor-mainline/done/5-4-leptos-tiptap-mainline-correction-v1.md`
|
||||
- `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
||||
|
||||
重点读取代码:
|
||||
|
||||
- `wolai-frontend/src/app/(app)/documents/`
|
||||
- `wolai-frontend/src/components/editor/`
|
||||
- `wolai-frontend/src/lib/documents/`
|
||||
- `wolai-frontend/src/components/sidebar/`
|
||||
- `wolai-frontend/src/lib/tree-stream/`
|
||||
- `wolai-frontend/src/lib/documents/tree-command-client.ts`
|
||||
|
||||
## 结论
|
||||
|
||||
当前实现总体方向与最新设计主线一致:文档页 SSR 入口已消费 Rust `mnote.page_aggregate.v1`,默认主编辑器已折返到页面内 `leptos_tiptap_island`,tree command 前端主调用已进入 `/api/tree/commands` + `tree.*` 元数据口径,Sidebar 的默认 tree shell 已切到 `rust_wasm_dom_shell_host`。2026-05-16 后,历史 `iframe_srcdoc` host 与显式 legacy flag 已从 active source 移除并进入 `recycle/`。
|
||||
|
||||
但不能把当前状态描述为“前端编辑器 / 树体验已经完全收口到 Rust 单一真源”。主要未完成点集中在三处:页面本地 aggregate reducer 仍承担临时真相与补偿选择,`pageSubtree` 在本地正文变更后会被置空而不是形成同一份可持续 projection,Sidebar 仍通过 initial / query / tree stream 三源 freshness 选择维持一致性。此外,部分页面设置仍明确是 `planned` / `downgrade`,属于设计清单中尚未完成的范围。
|
||||
|
||||
## 关键发现表格
|
||||
|
||||
| 编号 | 分类 | 严重度 | 发现 | 证据 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| F-01 | 未完成 | 中 | Page Aggregate 读链已 Rust-first,但客户端仍有本地 aggregate reducer 持有标题、正文、设置、子树快照等临时真相;设计中的“页面域单一真源”尚未闭环。 | `wolai-frontend/src/components/editor/page-aggregate-client-state.ts:7` 定义本地 `PageAggregateClientState`,包含 `serverPageTitle / persistedPageTitle / draftPageTitle / options / content / serverPageSubtreeSnapshot / contentRevision / conflictDetectionKey`;`wolai-frontend/src/components/editor/document-content.tsx:144` 使用 reducer 作为文档页核心状态。 |
|
||||
| F-02 | 实现偏差 | 中 | `pageSubtree` 不是跟随本地正文和标题持续更新的 page projection;一旦本地正文对象与服务器快照不同,就直接返回 `null`,AI 面板和阅读结构面会失去这份 projection。 | `wolai-frontend/src/components/editor/page-aggregate-client-state.ts:163` 通过 `content === serverContentSnapshot` 判断;`wolai-frontend/src/components/editor/page-aggregate-client-state.ts:166` 只有未改动时返回 `serverPageSubtreeSnapshot`,否则 `:169` 返回 `null`;`wolai-frontend/src/components/editor/document-content.tsx:867` 将该选择结果作为 AI snapshot 的 `pageSubtree` 来源,`:1162` 也传给阅读视图。 |
|
||||
| F-03 | 未完成 | 中 | Sidebar / Breadcrumb / 文档页头已共享 preferred snapshot,但 live cache 仍不是唯一来源;当前仍在 initial、query refetch、tree stream 之间做 freshness 选择。 | `wolai-frontend/src/components/app-layout-shell.tsx:20` 同时创建 `useSidebarData` 与 `useSidebarTreeStream`;`:22` 用 `usePreferredSidebarSnapshot` 选择;`wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts:29` 在 `query / tree_stream / initial` 间选择;`wolai-frontend/src/components/sidebar/sidebar.tsx:201` Sidebar 内部也消费同样选择结果。 |
|
||||
| F-04 | 未完成 | 低 | 页面设置运行时语义已有代码级分类,但仍有正式 UI 中可见的 planned / downgrade 项;这与 `5-6` 清单中“页面设置未整体收口”的口径一致。 | `wolai-frontend/src/lib/documents/page-option-semantics.ts:60` 将 `protectEditing` 标记为 `planned`,`:84` 将 `showBlockRefCount` 标记为 `planned`;`wolai-frontend/src/components/editor/page-options-sidebar.tsx:416` 对 downgrade 项显示 `待接线`,`:71` 让 `showBlockRefCount` 成为不可交互占位。 |
|
||||
| F-05 | 方向变化/文档滞后 | 低 | `5-5-1` 中仍写着 `showHeadingNumbers / embedDefaultBlockId` 只完成字段贯通,但当前代码已把它们纳入 island runtime payload;这里更像文档滞后,而不是实现偏差。 | `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md` 第 2.3 节仍说明两项“不可描述成正式支持”;`wolai-frontend/src/lib/documents/page-option-semantics.ts:48` 与 `:88` 均标记为 `wired`;`:121` 的 `pickLeptosTiptapRuntimePageOptions` 会把 `showHeadingNumbers / embedDefaultBlockId` 传给 island,`wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx:717` 运行时更新这些选项。 |
|
||||
| F-06 | 已收口 | 低 | Tree final DOM shell 默认路径已符合 `4-18`,legacy iframe host 与环境变量回退入口已删除;后续只需保持负向 smoke,防止 active source 重新引入 `iframe_srcdoc`。 | `wolai-frontend/src/components/sidebar/tree-shell-host.tsx` 默认 `rust_family` 且有 workspace 时只使用 `TreeShellRustDomShellHost`;`wolai-frontend/src/components/sidebar/tree-shell-host.test.tsx` 覆盖默认不能进入 `iframe_srcdoc`。 |
|
||||
|
||||
## 证据
|
||||
|
||||
### 已对齐部分
|
||||
|
||||
1. 文档页读取主链已进入 Rust Page Aggregate。
|
||||
- `wolai-frontend/src/app/(app)/documents/[id]/page.tsx:51` 调用 `loadPageAggregateFromNextHeaders`。
|
||||
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts:170` 请求 `/api/page-aggregate/:id`。
|
||||
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts:130` 校验 schema 必须是 `mnote.page_aggregate.v1`。
|
||||
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts:225` 的 `loadPageAggregate` 只返回 Rust snapshot,不再在运行时 fallback 到 TS builder。
|
||||
|
||||
2. 默认编辑器 host 已是页面内 `leptos_tiptap_island`。
|
||||
- `wolai-frontend/src/components/editor/editor-host-config.ts:1` 只保留 `EditorHostKind = "leptos_tiptap_island"`。
|
||||
- `wolai-frontend/src/components/editor/editor-host.tsx:7` 动态加载 `leptos-tiptap-island-editor-host`。
|
||||
- `wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx:619` 直接调用 runtime module 的 `mount(container, ...)`,不是 iframe。
|
||||
|
||||
3. 页面写命令已经开始按 page command family 收口。
|
||||
- `wolai-frontend/src/lib/documents/page-command-contract.ts:15` 固定 `page.head.updateTitle / page.layout.updateOptions / page.body.save`。
|
||||
- `wolai-frontend/src/lib/documents/page-command-client.ts:72` 标题写入发送 `commandName: page.head.updateTitle`。
|
||||
- `wolai-frontend/src/lib/documents/page-command-client.ts:89` 页面设置写入发送 `commandName: page.layout.updateOptions`。
|
||||
- `wolai-frontend/src/app/api/documents/save/route.ts:35` 服务端 envelope 使用 `PAGE_COMMAND_NAMES.saveBody`。
|
||||
|
||||
4. tree command 前端调用主面已以 `tree.*` 为结果元数据口径。
|
||||
- `wolai-frontend/src/lib/documents/tree-command-client.ts:26` 定义 `tree.node.create / tree.node.rename / tree.subtree.move / tree.node.archive` 等 preferred command。
|
||||
- `wolai-frontend/src/lib/documents/tree-command-client.ts:185` 统一 POST 到 `/api/tree/commands`。
|
||||
- `wolai-frontend/src/app/api/tree/commands/route.ts:205` 由 `action` 分发 tree command,`:278` 创建命令使用 `tree.node.create`。
|
||||
|
||||
5. tree final DOM shell 默认路径已符合硬门禁。
|
||||
- `wolai-frontend/src/components/sidebar/tree-shell-host.tsx:139` 默认 implementation 为 `rust_wasm_dom_shell_host`。
|
||||
- `wolai-frontend/src/components/sidebar/tree-shell-surface.tsx:113` 旧 React page tree renderer 已显示为 removed fallback,不再作为正常 renderer。
|
||||
- `wolai-frontend/src/components/sidebar/tree-shell-host.test.tsx:189` 测试断言默认 implementation 是 `rust_wasm_dom_shell_host`,`:194` 断言没有 iframe host。
|
||||
|
||||
### 仍需收口部分
|
||||
|
||||
1. 客户端 `PageAggregateClientState` 仍是混合态。
|
||||
- 它把 server snapshot、persisted title、draft title、本地 content、server subtree snapshot、revision/conflict key 放在同一个前端 reducer 中。
|
||||
- 这符合当前过渡态,但不等于 Rust page aggregate 已成为页面域唯一运行时真相。
|
||||
|
||||
2. `pageSubtree` 与正文编辑没有同源更新。
|
||||
- 本地正文变更只会触发 `apply_local_content_snapshot`,不会同步生成新的 `pageSubtree`。
|
||||
- selector 在内容对象不同于 server snapshot 时返回 `null`,因此 `AI / read view / TOC` 只能等待后续持久化与重新拉取。
|
||||
|
||||
3. Sidebar live cache 仍有 freshness 选择层。
|
||||
- `AppLayoutShell` 和 `Sidebar` 都依赖 `usePreferredSidebarSnapshot`。
|
||||
- 当前算法没有基于 Rust stream cursor / projection version 做统一仲裁,而是通过 sync key 与 `treeStreamStatus` 在 `query` 与 `tree_stream` 之间选择。
|
||||
|
||||
4. 页面设置中仍有显式未完成项。
|
||||
- `protectEditing` 与 `showBlockRefCount` 的代码状态已经诚实标为 `planned / downgrade`。
|
||||
- 这避免了误导用户,但也说明 `Page Aggregate -> island runtime` 的设置语义还没有完全完成。
|
||||
|
||||
## 建议优先级
|
||||
|
||||
### P0
|
||||
|
||||
暂无需要立即阻断主链的 P0。默认 Page Aggregate 读链、默认 island host、默认 DOM tree shell 均未发现与最新主线相反的实现。
|
||||
|
||||
### P1
|
||||
|
||||
1. 收口 `pageSubtree` 与本地正文编辑的关系。
|
||||
- 至少明确它是“server projection only”还是“本地编辑也应生成临时 projection”。
|
||||
- 如果 AI 面板需要稳定结构上下文,不应在用户正常编辑后直接拿到 `null`。
|
||||
|
||||
2. 将 Sidebar 的三源 preferred snapshot 推进为统一 live cache。
|
||||
- 建议以 Rust stream cursor / projection version / query snapshot version 为仲裁字段,减少仅靠 sync key 的 freshness 判断。
|
||||
|
||||
### P2
|
||||
|
||||
1. 继续移除或强门禁 legacy tree iframe host。
|
||||
- 当前显式 env flag 符合设计,但应保留负向 smoke,避免后续默认路径误回退。
|
||||
|
||||
2. 同步修正文档滞后。
|
||||
- `5-5-1` 对 `showHeadingNumbers / embedDefaultBlockId` 的描述已经落后于当前代码,应在后续文档整理时更新。
|
||||
|
||||
3. 页面设置 planned 项保持降级展示,直到真正进入 island runtime 或被产品侧移除。
|
||||
|
||||
## 修改的文件
|
||||
|
||||
- `/mnt/Data1T/mnote/design/10-review/done/02-frontend-editor-tree-review.md`
|
||||
@@ -0,0 +1,217 @@
|
||||
# [recycle] Convex / Realtime / Storage 实现偏差审查
|
||||
|
||||
> 执行状态:已归档到 `design/10-review/done/`。本文保留为历史审查快照,当前活跃口径以 `./06-execution-checklist-and-acceptance.md` 为准。
|
||||
|
||||
## 范围
|
||||
|
||||
本报告审查 Convex / storage bridge / realtime / Page Aggregate 数据底座与当前设计主线的一致性,重点对照以下方向:
|
||||
|
||||
- Convex 保留为自托管 storage / realtime substrate,不作为树语义 owner。
|
||||
- Rust kernel / bridge-runtime / mnote-web 持有 tree-first graph、projection、command、Page Aggregate 的语义主导权。
|
||||
- `/api/page-aggregate/:id` 作为文档页 Rust-first 读取主链。
|
||||
- `/api/tree/events` 作为 Rust Web tree realtime snapshot / delta / resync 主链。
|
||||
- 前端只消费稳定 projection、Page Aggregate 与 tree stream,不重新持有第二套对象真相。
|
||||
|
||||
重点读取范围:
|
||||
|
||||
- `ARCHITECTURE.md`
|
||||
- `design/01-05-current-priority-overview.md`
|
||||
- `design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md`
|
||||
- `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/done/5-6-page-aggregate-alignment-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
|
||||
- `recycle/20260522-convex-runtime-retirement/infra/convex/README.md`
|
||||
- `wolai-frontend/convex/`
|
||||
- `rust/crates/storage-convex-bridge/`
|
||||
- `rust/crates/mnote-web/src/transport/convex.rs`
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs`
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs`
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs`
|
||||
- `rust/crates/mnote-web/src/routes/stream_support.rs`
|
||||
- `wolai-frontend/src/lib/documents/`
|
||||
- `wolai-frontend/src/lib/tree-stream/`
|
||||
|
||||
## 结论
|
||||
|
||||
整体方向与设计主线基本一致:Convex 没有被拆掉,仍承担本地自托管存储、文件和实时底座;Rust Web 已注册 `/api/page-aggregate/:document_id` 与 `/api/tree/events`;前端 tree stream 已直接使用 `EventSource("/api/tree/events")`;Next 的 `/api/documents/page` 与 `/api/mnote-web/stream` 已明确作为 410 compat 边界退场。
|
||||
|
||||
但当前实现仍是明显过渡态,主要问题集中在三处;本文里的 `infra/convex` 现在应按历史对照理解,因为自托管 compose 已软删除到 recycle,不再作为 active 部署入口:
|
||||
|
||||
1. Page Aggregate 读链虽然是 Rust endpoint,但实际数据仍由 `documents:getMeta` + `documents:getContent` 分别取回后在 bridge-runtime 中拼 projection,`source` 标识存在“看起来比真实实现更 canonical”的风险。
|
||||
2. Rust Web 的 `/api/documents/title`、`/api/documents/options` 与 Next route 的 page write adapter 在 bridge artifact 记录上不一致;tree stream 依赖 bridgeLogs 时,Rust page write 路径可能漏掉 command/domain event。
|
||||
3. `/api/tree/events` 是正式 SSE 入口,但当前 change detection 依赖周期性查询 `bridgeLogs:listWorkspaceOverview`,且 Convex query 内部会按 workspace collect 全量 command/domain rows 后内存过滤排序,实时性和规模风险较高。
|
||||
|
||||
## 关键发现表格
|
||||
|
||||
| 编号 | 分类 | 严重度 | 发现 | 证据 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| F1 | 实现偏差 | 高 | Rust Page Aggregate endpoint 对外是 `mnote.page_aggregate.v1`,但构建过程仍先分别读取 meta/content,再在 bridge-runtime 中拼 projection;`source=KernelProjection` 容易掩盖底层仍是兼容 join。 | `rust/crates/mnote-web/src/routes/web_shell.rs:2282`、`:2291`、`:2301`;`rust/crates/bridge-runtime/src/lib.rs:8246`、`:8252`、`:5291`、`:5303`、`:5386` |
|
||||
| F2 | 风险 | 高 | Rust Web 的 title/options 写路由只执行 Convex mutation,不记录 bridge command/domain artifacts;而 tree stream 以 bridgeLogs 为事件来源,主路径 page 写入可能不进入实时流。 | `rust/crates/mnote-web/src/routes/documents.rs:714`、`:802`;`rust/crates/mnote-web/src/routes/command_support.rs:65`;对比 `rust/crates/mnote-web/src/routes/tree.rs:6529` 与 `wolai-frontend/src/lib/documents/page-write-command-adapter.ts:63` |
|
||||
| F3 | 风险 | 高 | tree realtime 当前是 SSE transport,但后端通过 `sleep(poll_ms)` 周期性轮询 Convex bridgeLogs,而不是直接利用 Convex subscription/push;默认 2s,实时性和负载都需继续验证。 | `rust/crates/mnote-web/src/routes/sse.rs:25`、`:26`、`:51`、`:58`、`:60`;`rust/crates/mnote-web/src/routes/stream_support.rs:610` |
|
||||
| F4 | 风险 | 中高 | `bridgeLogs:listWorkspaceOverview` 会按 workspace collect 全量 `command_logs` 与 `domain_events` 后在内存中过滤、排序、分页;SSE 轮询叠加后,历史日志增长会放大 Convex 读压力。 | `wolai-frontend/convex/bridgeLogs.ts:283`、`:287`、`:292`、`:301`、`:305` |
|
||||
| F5 | 方向变化/文档滞后 | 中 | `storage-convex-bridge` 把 `page.aggregate.get` 映射到 `documents:getPageAggregate`,但 Convex `documents.ts` 只发现 `getMeta` / `getContent`,未发现 `getPageAggregate` export;当前真实 Page Aggregate 路线已绕到 Rust adapter。 | `rust/crates/storage-convex-bridge/src/mapping.rs:101`;`wolai-frontend/convex/documents.ts:684`、`:803`;全仓 `rg getPageAggregate` 未发现 Convex 实现 |
|
||||
| F6 | 已完成/方向一致 | 中 | Next compat 读链和旧 stream alias 已显式退场,前端 tree stream 直接构造 `/api/tree/events`,符合当前主线口径。 | `wolai-frontend/src/app/api/documents/page/route.ts:17`;`wolai-frontend/src/app/api/mnote-web/stream/route.ts:9`;`wolai-frontend/src/lib/tree-stream/protocol.ts:66`、`:72` |
|
||||
| F7 | 未完成 | 中 | Page Aggregate TS builder 已不在 runtime 主链中被引用,但文件仍保留;当前定位应继续写成 fallback / adapter / 测试材料,不能作为运行时主路径描述。 | `wolai-frontend/src/lib/documents/page-aggregate-builder.ts:51`;全仓非测试引用仅剩定义,runtime loader 只调用 Rust snapshot:`wolai-frontend/src/lib/documents/page-aggregate-loader.ts:225` |
|
||||
|
||||
## 证据
|
||||
|
||||
### 1. Page Aggregate 读链已收口到 Rust endpoint,但仍由 meta/content join 生成
|
||||
|
||||
`mnote-web` 注册了正式 endpoint:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs:53` 到 `:56` 注册 `/api/page-aggregate/{document_id}`。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2227` 到 `:2252` 返回 `schema: "mnote.page_aggregate.v1"` 和 `result: aggregate`。
|
||||
|
||||
但实际构建流程仍是:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2282` 到 `:2290` 读取 `load_document_meta_result`。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2291` 到 `:2299` 读取 `load_document_content_result`。
|
||||
- `rust/crates/mnote-web/src/routes/web_shell.rs:2301` 到 `:2315` 把 `meta + content` 作为数据传给 `page.aggregate.get`。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs:5291` 到 `:5312` 的 `build_page_aggregate_projection_result` 明确从 `data.meta` 和 `data.content` 中拆字段。
|
||||
|
||||
这说明“Rust-first 读取主链”成立,但“Page Aggregate 已经由 kernel 原生投影独立产出”仍未成立。该点与 `5-5/5-6` 的过渡态判断一致,但需要在后续文档和汇报中保持精确。
|
||||
|
||||
需进一步验证:`PageAggregateSource::KernelProjection` 是否应在这种 `meta/content join` 场景下继续使用,还是应保留 `CompatMetaContentJoin` 以避免 provenance 误导。
|
||||
|
||||
### 2. Page write side effects 在 Rust route 与 Next adapter 之间不一致
|
||||
|
||||
Next route 侧:
|
||||
|
||||
- `wolai-frontend/src/app/api/documents/title/route.ts:58` 调用 `executePageWriteBridgeCommand`。
|
||||
- `wolai-frontend/src/app/api/documents/options/route.ts:47` 调用 `executePageWriteBridgeCommand`。
|
||||
- `wolai-frontend/src/app/api/documents/save/route.ts:43` 调用 `executePageWriteBridgeCommand`。
|
||||
- `wolai-frontend/src/lib/documents/page-write-command-adapter.ts:63` 到 `:69` 成功后调用 `recordRustBridgeCommandArtifacts`。
|
||||
|
||||
Rust route 侧:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs:579` 的正文保存使用 `execute_runtime_command_via_convex_with_artifacts`。
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs:714` 的标题更新使用 `execute_runtime_command_via_convex`。
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs:802` 的页面设置更新使用 `execute_runtime_command_via_convex`。
|
||||
- `rust/crates/mnote-web/src/routes/command_support.rs:65` 到 `:73` 的 `execute_runtime_command_via_convex` 只执行 Convex command plan,不持久化 artifacts。
|
||||
- `rust/crates/mnote-web/src/routes/command_support.rs:75` 到 `:93` 的 artifact 版本才会调用 `execute_convex_command_plan_with_artifacts`。
|
||||
|
||||
tree command route 是一致的:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/tree.rs:6529` 使用 `execute_runtime_command_via_convex_with_artifacts`。
|
||||
|
||||
风险是:如果当前 3000 主入口走 Rust Web `/api/documents/title` 或 `/api/documents/options`,这些 page 写入不会进入 `command_logs/domain_events`,而 `/api/tree/events` 正是通过 bridgeLogs 检测变化。标题类写入尤其可能影响 sidebar/breadcrumb/page tree 的实时一致性。
|
||||
|
||||
需进一步验证:当前文档页标题编辑在 3000 主壳中到底走 `/api/documents/title` 还是 `/api/tree/commands`;如果走前者,应补 Rust route artifact 记录或统一到 tree/page command route。
|
||||
|
||||
### 3. Tree realtime 是正式 SSE 入口,但实现仍是 polling-backed stream
|
||||
|
||||
正式入口成立:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/mod.rs:130` 注册 `/api/tree/events`。
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs:127` 到 `:146` 给 tree events 加 `x-mnote-tree-stream-owner: rust-web`。
|
||||
- `wolai-frontend/src/lib/tree-stream/protocol.ts:66` 到 `:77` 构造 `/api/tree/events` URL。
|
||||
- `wolai-frontend/src/lib/tree-stream/use-sidebar-tree-stream.ts:136` 到 `:143` 用 `EventSource` 监听 `snapshot/delta/resync`。
|
||||
|
||||
但服务端 change detection 是轮询:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs:25` 到 `:26` 读取 `max_polls` 和 `poll_ms`,默认 `2000ms`,最低 `250ms`。
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs:51` 到 `:58` 循环中 `sleep(Duration::from_millis(poll_ms))`。
|
||||
- `rust/crates/mnote-web/src/routes/sse.rs:60` 到 `:67` 每轮调用 `load_stream_overview` 再 `resolve_stream_change`。
|
||||
- `rust/crates/mnote-web/src/routes/stream_support.rs:610` 到 `:625` 的 `load_stream_overview` 通过 `bridge.workspace.overview` 查询 Convex。
|
||||
|
||||
这与“Rust Web 提供正式实时 transport”一致,但还不是“基于 Convex realtime subscription 的 push stream”。短期可接受为过渡实现,长期如果继续承载主链,需要明确性能和延迟边界。
|
||||
|
||||
### 4. bridgeLogs overview 查询存在规模风险
|
||||
|
||||
`wolai-frontend/convex/bridgeLogs.ts` 的 `listWorkspaceOverview`:
|
||||
|
||||
- `:283` 到 `:286` 按 workspace 查询并 `collect()` 所有 `command_logs`。
|
||||
- `:287` 到 `:290` 按 workspace 查询并 `collect()` 所有 `domain_events`。
|
||||
- `:292` 到 `:301` 在内存中过滤、排序、切片 command logs。
|
||||
- `:303` 到 `:312` 再用 commandId 集合过滤 domain events。
|
||||
|
||||
该实现作为调试/小数据过渡可以工作,但被 `/api/tree/events` 默认每 2 秒调用时,日志增长后会造成:
|
||||
|
||||
- Convex query 读放大。
|
||||
- SSE 连接数量增加时成倍放大。
|
||||
- cursor 语义依赖内存排序,历史数据规模变大后容易出现延迟或超时。
|
||||
|
||||
建议后续至少增加按 `workspace_id + created_at/id` 的索引分页,避免每次 stream poll 全量扫描。
|
||||
|
||||
### 5. storage-convex-bridge 中存在疑似过时的 `page.aggregate.get` 映射
|
||||
|
||||
`rust/crates/storage-convex-bridge/src/mapping.rs:101`:
|
||||
|
||||
```text
|
||||
"page.aggregate.get" => "documents:getPageAggregate"
|
||||
```
|
||||
|
||||
但当前 `wolai-frontend/convex/documents.ts` 中只发现:
|
||||
|
||||
- `getMeta`:`wolai-frontend/convex/documents.ts:684`
|
||||
- `getContent`:`wolai-frontend/convex/documents.ts:803`
|
||||
|
||||
全仓搜索未发现 Convex `documents:getPageAggregate` 实现。当前真实 Page Aggregate 读取是 Rust Web 先读 meta/content,再由 bridge-runtime 生成 projection。因此该映射要么是未来目标,要么已经滞后;如果有代码路径直接通过 storage-convex-bridge 执行 `page.aggregate.get`,会存在运行时函数不存在风险。
|
||||
|
||||
需进一步验证:`storage-convex-bridge` 是否仍有生产路径直接执行 `page.aggregate.get -> documents:getPageAggregate`;如果没有,应把映射标注为 future/stale,或改为当前真实读链。
|
||||
|
||||
### 6. Convex substrate 定位总体一致
|
||||
|
||||
正向证据:
|
||||
|
||||
- `recycle/20260522-convex-runtime-retirement/infra/convex/README.md` 明确自托管 Convex backend/dashboard、HTTP Actions 和文件存储的历史对照口径。
|
||||
- `wolai-frontend/convex/schema.ts:35` 到 `:91` 的 `documents` 表继续承接页面结构、正文、页面设置、统计字段。
|
||||
- `wolai-frontend/convex/schema.ts:121` 到 `:162` 的 `media_assets` 与 `_storage` 字段保留文件底座。
|
||||
- `rust/crates/storage-convex-bridge/README.md:5` 到 `:13` 明确 bridge 只做协议到 Convex 读写请求映射,不复制主事实层、不维护第二数据库。
|
||||
|
||||
这与“Convex 不拆,Rust 收口语义”的设计一致。
|
||||
|
||||
## 建议优先级
|
||||
|
||||
### P0:修正 Rust page write route 的 artifact 一致性
|
||||
|
||||
目标:让当前 3000 主入口的 `/api/documents/title`、`/api/documents/options` 至少在 side effect 上与 Next `page-write-command-adapter` 保持一致。
|
||||
|
||||
建议:
|
||||
|
||||
- 将 `rust/crates/mnote-web/src/routes/documents.rs` 中 title/options 的 `execute_runtime_command_via_convex` 改为 artifact 版本,或统一复用 tree/page command route。
|
||||
- 对标题更新补最小回归:更新标题后 `bridgeLogs:listWorkspaceOverview` 能看到对应 command/domain event,`/api/tree/events` 能输出 delta 或 resync。
|
||||
- 对 artifact 写失败的策略重新定级:当前 `rust/crates/mnote-web/src/transport/convex.rs:759` 到 `:770` 是主 mutation 成功、artifact 失败仍返回成功;对 tree-relevant command 至少应打可观测错误并触发保守 resync。
|
||||
|
||||
### P1:收口 Page Aggregate provenance
|
||||
|
||||
目标:避免把 `meta/content join` 误标为已完成的 kernel-native projection。
|
||||
|
||||
建议:
|
||||
|
||||
- 明确 `PageAggregateSource::KernelProjection` 与 `CompatMetaContentJoin` 的使用边界。
|
||||
- 如果 `build_page_aggregate_snapshot` 仍通过 `load_document_meta_result + load_document_content_result` 构建,应在返回 source 或 header 中反映真实来源。
|
||||
- 如果目标是 kernel-native projection,则补正式 kernel/query 数据路径,避免 route 层长期手工拼装。
|
||||
|
||||
### P1:优化 tree stream 的 Convex 查询模型
|
||||
|
||||
目标:让 `/api/tree/events` 可以长期承载主链,而不是随着日志增长退化。
|
||||
|
||||
建议:
|
||||
|
||||
- 为 `command_logs`、`domain_events` 增加按 `workspace_id + created_at/id` 的查询索引和 cursor 查询。
|
||||
- `listWorkspaceOverview` 不再 `collect()` 全量 workspace 日志后内存分页。
|
||||
- 明确 polling-backed SSE 是过渡实现,还是长期 realtime transport;若长期使用,应补连接数、日志量、延迟上限的 smoke/bench。
|
||||
|
||||
### P2:清理或标注 stale mapping
|
||||
|
||||
目标:降低后续 worker 误用 `page.aggregate.get -> documents:getPageAggregate` 的风险。
|
||||
|
||||
建议:
|
||||
|
||||
- 若 `documents:getPageAggregate` 不计划实现,移除或注释 `storage-convex-bridge` 中的映射。
|
||||
- 若计划实现,补 Convex query 与最小测试,并让 mnote-web Page Aggregate route 直接消费它或说明为什么不消费。
|
||||
|
||||
### P2:保留 Next compat 退场边界,但避免双实现继续发散
|
||||
|
||||
目标:Next route 继续作为 legacy/adapter 参考时,不与 Rust Web 主入口形成不同 side effects。
|
||||
|
||||
建议:
|
||||
|
||||
- 对 `/api/documents/title/options/save` 明确 owner:Rust Web 主路径与 Next legacy 路径只能有一份 canonical side-effect 规则。
|
||||
- 对 `wolai-frontend/src/lib/documents/page-aggregate-builder.ts` 保留测试/adapter 标签,避免被重新接回 runtime 主链。
|
||||
|
||||
## 修改的文件路径
|
||||
|
||||
- `design/10-review/done/03-convex-realtime-storage-review.md`
|
||||
@@ -0,0 +1,68 @@
|
||||
# [recycle] 次级域与设计治理实现偏差审查
|
||||
|
||||
> 执行状态:已归档到 `design/10-review/done/`。本文保留为历史审查快照,当前活跃口径以 `./06-execution-checklist-and-acceptance.md` 为准。
|
||||
|
||||
## 范围
|
||||
|
||||
本次只审查 Mindmap、AI、OnlyOffice、Wolai-aline、SiYuan reference,以及 `design/process/done` 治理口径与当前实现方向的一致性。重点读取了 `ARCHITECTURE.md`、`design/README.md`、`design/06-mindmap/process/*`、`design/07-ai/process/*`、`design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md`、`design/09-siyuan-reference/process/9-siyuan-reference-boundary-and-adoption-v1.md`、`design/90-reference/*`,并对照了 Mindmap、AI route/lib、OnlyOffice route/adapter 与相关 Convex/Rust runtime 代码。
|
||||
|
||||
## 结论
|
||||
|
||||
总体方向与当前主线基本一致:Mindmap 已明确降为 `tree-first graph kernel` 的视图/编辑挂件;AI 长期方向已从 `mnote-cli` 收口改为 Hermes 页面内客户端 + mnote Hermes skill/plugin;OnlyOffice 仍是独立页面型编辑器边界,SiYuan 参考稿没有发现被提升为上位架构来源的证据。
|
||||
|
||||
主要风险集中在三处:OnlyOffice 在 `mnote-web` 主入口里的 callback/forcesave 仍是 no-op,而 legacy Next route 已有真实写回链;Mindmap 的导出 action 在 schema/action map 中暴露,但 bridge 安全命令集不支持;Mindmap AI 补完 route 当前硬返回 501,和 Rust tool 已登记的能力面不闭合。
|
||||
|
||||
## 关键发现表格
|
||||
|
||||
| ID | 分类 | 级别 | 发现 | 简短证据 | 建议 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 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,但新主线已改为 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/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 为上位依据,明确页面 AI 改走 Hermes client proxy,mnote 能力通过 Hermes skill/plugin 暴露;`openai-agents-python` 和 `mnote-cli host` 只保留为历史/兼容/内部适配路径并补退场计划。 |
|
||||
|
||||
2026-05-14 复核:F-04 的长期方向已由 `design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md` 继续落地到 mnote-web Hermes client proxy 与 mnote Hermes tool routes;旧 `/api/ai-agent/run` 已在 mnote-web 中收口为 `legacy_ai_agent_run_retired` guard,旧 `openai-agents-python` / `mnote-cli host` 只按历史/兼容/对照链处理,不再代表页面 AI 长期主入口。
|
||||
| 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` 和主线设计为上位依据。 |
|
||||
|
||||
## 证据
|
||||
|
||||
### Mindmap
|
||||
|
||||
- 设计口径清晰:`design/06-mindmap/reference/6-mindmap-kernel-phase6-projection-editor-v1.md` 明确 Phase 6 主线是 `Rust kernel truth -> mindmap.simple_mind_map_scene.v1 -> leptos-mindmap editor island -> simple-mind-map runtime -> command bridge`,并禁止把 runtime data 当唯一事实源。
|
||||
- 实现已对齐主线壳:`rust/crates/mnote-web/src/routes/mindmap_shell.rs:105-123` 输出 `mnote.mindmap_shell.v1`、`mindmap.simple_mind_map_scene.get` 与 `mindmap.command.apply`;`rust/crates/mnote-web/src/ssr/pages/mindmap.rs:50-56` 提供 standalone island 挂载点。
|
||||
- 旧 React 块已自我标注为 legacy/compat/reference:`wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx:3-5` 明确 3000 文档页默认主链是 `leptos-tiptap NodeView + leptos-mindmap adapter`。
|
||||
- 仍有 compat 数据层:`wolai-frontend/convex/schema.ts:164-184` 的 `mindmaps.data: v.any()` 仍承载导图数据;这与当前过渡态兼容,但不应被描述为长期 canonical truth。
|
||||
|
||||
### AI
|
||||
|
||||
- 旧 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/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 与 `design/07-ai/done/7-2-phase7-structured-artifact-write-chain-v1.md`。
|
||||
- 旧后端 agent 仍存在:`wolai-backend/app/routers/ai_agent.py:47-57` 仍提供 streaming run;这只能作为历史/兼容/对照链,不能被误认为长期默认主编排。
|
||||
|
||||
### OnlyOffice
|
||||
|
||||
- 正确边界已有:`rust/crates/adapter-onlyoffice/src/lib.rs:124-126` 明确 adapter 只定义资产定位、会话、签名、代理、callback/forcesave 边界,不把 OnlyOffice 变成主事实层。
|
||||
- `mnote-web` 页面是独立编辑页:`rust/crates/mnote-web/src/routes/onlyoffice.rs:325-630` 直接渲染 `/onlyoffice`,通过 DocsAPI 创建编辑器,未嵌入正文主编辑画布。
|
||||
- 关键风险是写回链 owner 分裂:Rust 主入口 no-op 与 legacy Next 真写回并存,见 F-01。
|
||||
|
||||
### Wolai-aline
|
||||
|
||||
- 流程文档严格要求“Wolai 基线 -> RED smoke -> 小范围实现 -> 本地验证 -> subagent 复测 -> 主线程截图复核”:`design/08-wolai-aline-test-flow/process/wolai-aline-test-flow-v1.md`。
|
||||
- 本次不是具体 Wolai 对标实现任务,未执行浏览器对标;未发现该流程被错误提升为产品架构来源。
|
||||
|
||||
### SiYuan Reference
|
||||
|
||||
- `design/09-siyuan-reference/process/9-siyuan-reference-boundary-and-adoption-v1.md` 的边界与当前主线一致:思源只作为产品能力与交互参考,不替代 `tree-first graph kernel`、Rust kernel、Page Aggregate、tree command 与 tree realtime。
|
||||
- 当前只读检索未发现将 SiYuan `.sy`、SQL API 或前端 runtime 直接提升为 mnote 长期事实源的实现证据;若后续新增属性视图/数据库视图,应继续先做对象域评估。
|
||||
|
||||
## 建议优先级
|
||||
|
||||
1. P1:补齐或明确代理 OnlyOffice Rust callback/forcesave 写回链,避免主入口下保存成功但内容未持久化。
|
||||
2. P2:收口 Mindmap action 可用性,先修 `export` 映射与安全命令不一致,再继续扩展 UI 能力。
|
||||
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` 的历史/兼容定位和访问边界;页面 AI 长期主链已经由 `7-4` 收口到 Hermes 面板 + mnote plugin,后续只在各自 legacy domain 里拆迁移。
|
||||
5. P3:清理 `design/90-reference` 的问答残留,并在引用规范里再次强调它不是 process/done 设计稿。
|
||||
|
||||
## 本次修改文件
|
||||
|
||||
- `/mnt/Data1T/mnote/design/10-review/done/04-secondary-domains-and-design-governance-review.md`
|
||||
@@ -0,0 +1,156 @@
|
||||
# [recycle] 05 Tree 历史审查
|
||||
|
||||
> 执行状态:本文已归档到 `design/old/10-review/done/`,是 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 等对象的组织根源。页面树则是从这棵资源/对象树派生出的导航投影,类似快捷方式、收藏视图或文档视图。
|
||||
|
||||
对当前 bug 来说,最核心的规则应固定为:
|
||||
|
||||
1. index.md 只代表页面正文,即 Page Aggregate body。
|
||||
2. mindmap 不是主编辑区正文替身,而是页面内某个 block 关联的 object / asset editor。
|
||||
3. 文件树点击 mindmap 应打开 mindmap object editor,不能被 index.md 吞掉,也不能用临时 mindmap-only bootstrap 覆盖真实页面正文。
|
||||
4. 页面树不应再持有独立结构真相,只显示“文档导航关系”;文件树显示“资源组织关系”。
|
||||
|
||||
主编辑区与插件
|
||||
主编辑区应该是一个工作区 tab host,而不是只等于文档正文编辑器。index.md 是其中一种 tab,mindmap、OnlyOffice、附件预览、代码附件都是其他 object tab/model。
|
||||
|
||||
Siyuan 这里值得借鉴:doc 和 asset 是不同 model/tab,asset 打开不伪装成 doc 正文;插件、widgets、assets、.sy 文档也在存储层分开。mnote 也应采用类似原则:插件/附
|
||||
件/mindmap 可以嵌入页面,但它们的持久化真源不能变成页面正文本身,只能通过 block-asset relation 被 Page Aggregate 引用。
|
||||
|
||||
文件树与页面树
|
||||
我建议改成三层表述:
|
||||
|
||||
- Resource Tree:长期 canonical 对象组织树,Rust kernel 持有语义,Convex 只是底层存储。
|
||||
- File Tree:Resource Tree 的主组织投影,展示页面文件夹、index.md、附件、mindmap、OnlyOffice、代码附件等。
|
||||
- Page Tree:面向阅读/导航的快捷投影,只显示页面关系,不拥有排序、父子、附件归属的最终真相。
|
||||
|
||||
这样你的“文件树为根源”可以落地,但不会和现有 tree-first graph kernel 冲突。更准确地说:文件树背后的 resource hierarchy 是根源,页面树是它的页面导航视图。
|
||||
|
||||
建议路线
|
||||
短期先修当前 bug:保留 /mindmap/{doc}/{asset} 作为 object editor,但彻底隔离草稿 key、保存 command 和 Page Aggregate body,smoke 必须真实网页验证“点击 mindmap、
|
||||
编辑长中文、保存、切回 index.md、再打开 mindmap 内容仍在”。
|
||||
|
||||
中期做主编辑区 asset tab:index.md | mindmap.json | xxx.docx,每个 tab 有自己的 object identity 和保存命令。
|
||||
|
||||
长期补 resource projection / block-asset relation:文件树 row、页面内 mindmap block、mindmap projection 必须来自同一关系索引,而不是前端临时按路由拼。
|
||||
|
||||
|
||||
|
||||
推荐路线
|
||||
我建议采用“分阶段收口 Resource Tree”的方案,不做一次性大重构。
|
||||
|
||||
可选方案有三种:
|
||||
|
||||
- 只修 mindmap bug:最快,但会继续留下页面树/文件树真源分裂。
|
||||
- 分阶段建立 Resource Tree -> File Tree/Page Tree projection:最稳,能解决当前 bug,也能承接附件、OnlyOffice、插件对象。
|
||||
- 全量重写 tree/page/resource 模型:理论干净,但当前工作区和主链都很活跃,风险太高。
|
||||
|
||||
我推荐第二种。
|
||||
|
||||
阶段 0:冻结合同
|
||||
历史建议曾是:先把 design/10-review/05-tree.md 升级为正式设计合同,后续移动或补一份到:
|
||||
|
||||
- design/04-tree-domain/process/4-24-resource-tree-filetree-pagetree-source-contract-v1.md
|
||||
- 或 design/05-editor-mainline/process/5-11-main-editor-object-tab-resource-tree-alignment-v1.md
|
||||
|
||||
合同里固定四句话:
|
||||
|
||||
- Resource Tree 是组织真源。
|
||||
- File Tree 是资源组织主投影。
|
||||
- Page Tree 是页面导航投影/快捷视图。
|
||||
- index.md、mindmap、OnlyOffice、附件是不同 object model,不能互相伪装。
|
||||
|
||||
阶段 1:先关闭当前 mindmap bug
|
||||
目标:文件树 mindmap 仍可编辑保存,但不污染 index.md。
|
||||
|
||||
重点文件:
|
||||
|
||||
- rust/crates/mnote-web/src/ssr/pages/layout.rs
|
||||
- rust/crates/mnote-web/src/routes/mindmap_shell.rs
|
||||
- rust/spikes/leptos-tiptap-spike/src/lib.rs
|
||||
- scripts/task169-mindmap-realtime-smoke.js
|
||||
|
||||
验收必须是真实浏览器链路:
|
||||
|
||||
1. 从文件树点击 mindmap asset。
|
||||
2. 进入明确的 mindmap object editor。
|
||||
3. 输入长中文并保存。
|
||||
4. 切回同页 index.md,正文仍是 Page Aggregate body。
|
||||
5. 再打开 mindmap,刚才内容仍在。
|
||||
6. 确认没有用真实 documentId 写 standalone 草稿 key。
|
||||
|
||||
阶段 2:补 Resource Projection 协议
|
||||
在 Rust 协议层明确资源节点和 block-asset 关系。
|
||||
|
||||
建议新增或扩展:
|
||||
|
||||
- ResourceNode
|
||||
- ResourceKind: page | index | mindmap | attachment | onlyoffice | code
|
||||
- ObjectIdentity: { objectKind, documentId, blockId?, assetId? }
|
||||
- BlockAssetRelation: { documentId, blockId, assetId, assetKind }
|
||||
|
||||
落点优先看:
|
||||
|
||||
- rust/crates/core-protocol/
|
||||
- rust/crates/bridge-runtime/
|
||||
- rust/crates/mnote-web/src/tree_shell/
|
||||
|
||||
这个阶段不要求立刻迁移所有数据,只要协议和 projection 能表达清楚。
|
||||
|
||||
阶段 3:让 File Tree 从 Resource Tree 派生
|
||||
当前 file tree 已有 resourceMeta、assetKind、file_tree projection 基础。下一步是收口为:
|
||||
|
||||
- 页面节点下面固定有 index.md
|
||||
- mindmap/附件/OnlyOffice 都是同一 Resource Tree 下的 child resource
|
||||
- 文件树 row 不再临时从多份前端数据拼出第二真相
|
||||
|
||||
同时保留 tree.asset.open,但它必须只表达 object open intent,不直接决定“把谁当正文”。
|
||||
|
||||
阶段 4:Page Tree 降级为导航投影
|
||||
Page Tree 不再拥有资源归属、附件归属、mindmap 归属。它只显示页面导航关系:
|
||||
|
||||
- 页面标题
|
||||
- 页面层级
|
||||
- 快捷入口/收藏/最近打开这类导航语义
|
||||
|
||||
如果页面树需要显示某个资源状态,也只读 Resource Projection,不自己维护。
|
||||
|
||||
阶段 5:主编辑区变成 Object Tab Host
|
||||
这是中期关键体验:
|
||||
|
||||
- index.md tab:加载 Page Aggregate body。
|
||||
- mindmap.json tab:加载 mindmap projection/command。
|
||||
- xxx.docx tab:打开 OnlyOffice object editor。
|
||||
- attachment tab:预览/代码编辑/外部打开。
|
||||
|
||||
这样 mindmap 不会被 index.md 吞掉,也不会伪装成正文。
|
||||
|
||||
阶段 6:命令面统一
|
||||
后续命令应逐步收口到:
|
||||
|
||||
- tree.asset.attach
|
||||
- tree.asset.detach
|
||||
- tree.resource.rename
|
||||
- tree.resource.move
|
||||
- page.body.save
|
||||
- mindmap.command.apply
|
||||
|
||||
关键规则:改资源关系走 tree/resource command,改页面正文走 page command,改导图内容走 mindmap command。
|
||||
|
||||
|
||||
落地入口
|
||||
|
||||
以上判断已落实到两个主线 done checklist:
|
||||
|
||||
- /mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md
|
||||
用于冻结 Resource Tree / File Tree / Page Tree 的真源合同和 projection / command 边界。
|
||||
- /mnt/Data1T/mnote/design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md
|
||||
用于落实主编辑区 Object Tab、mindmap object editor、草稿隔离和真实浏览器 smoke 验收。
|
||||
|
||||
执行顺序固定为:
|
||||
|
||||
1. 先完成 4-24:协议、projection、command 边界必须能表达 `index.md`、mindmap、OnlyOffice、附件、代码附件的不同 object identity,并确认 Page Tree 只是页面导航投影。
|
||||
2. 再完成 5-12:主编辑区只消费 4-24 输出的 object identity / resourceMeta / open intent,先关闭 mindmap 污染 `index.md` 的 P0 bug,再推进 Object Tab Host。
|
||||
3. 验收以真实浏览器 smoke 为准,尤其是 `scripts/task169-mindmap-realtime-smoke.js` 覆盖“打开 mindmap、长中文编辑、保存、切页、回 `index.md`、再开 mindmap”的链路。
|
||||
@@ -0,0 +1,446 @@
|
||||
# [recycle] 10-review 顺序执行清单与验收标准
|
||||
|
||||
> 执行状态:已完成并归档到 `design/10-review/done/`。本文是本轮 10-review 的最终验收依据与防回归口径,根目录 `design/10-review/README.md` 仅保留索引。
|
||||
|
||||
> 更新时间:2026-05-14
|
||||
>
|
||||
> 上游依据:
|
||||
> - `/mnt/Data1T/mnote/design/10-review/README.md`
|
||||
> - `/mnt/Data1T/mnote/design/10-review/done/01-rust-kernel-web-review.md`
|
||||
> - `/mnt/Data1T/mnote/design/10-review/done/02-frontend-editor-tree-review.md`
|
||||
> - `/mnt/Data1T/mnote/design/10-review/done/03-convex-realtime-storage-review.md`
|
||||
> - `/mnt/Data1T/mnote/design/10-review/done/04-secondary-domains-and-design-governance-review.md`
|
||||
> - `/mnt/Data1T/mnote/design/10-review/done/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 artifact;Rust 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 empty;Sidebar/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] 增加负向 smoke:Convex/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 dataset;Sidebar/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/done/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/done/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 / File Tree live cache 与主文档壳 no-refresh 验收
|
||||
|
||||
状态:代码侧完成,真实浏览器 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 行局部更新后文档页头仍稳定。2026-05-14 追加验收口径:File Tree / Page Tree 的“无刷新可见”必须在 `http://127.0.0.1:3000/documents/<id>` 主文档壳验证,覆盖页面 `tree.node.create`、页面 delete/purge、页面内 mindmap create 三条链路;`/tree` debug route 与 3001/Next 路径只能作为补充排查,不能替代 3000 主入口验收。
|
||||
|
||||
目标:减少 `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] Rust 3000 主文档壳监听 `tree:local-command`,在 `tree.node.create` / delete 成功后即时更新 Page Tree 与 File Tree DOM。
|
||||
- [x] Rust 3000 主文档壳监听同页 mindmap 资产写入:slash 插入触发 `/api/mindmap/<doc>/<mindmap>` POST 成功后,File Tree 即时出现 `mindmap-<id>.json` asset row。
|
||||
|
||||
验收标准:
|
||||
|
||||
- [x] 新建、重命名、移动、归档页面后,Sidebar、Breadcrumb、文档页头无互相打架或回退闪烁。
|
||||
- [x] `usePreferredSidebarSnapshot` 有单测覆盖 query、initial、tree_stream 三者的新旧仲裁。
|
||||
- [x] 真实浏览器 smoke 覆盖至少一个 tree command 后 Sidebar 与文档页头一致。
|
||||
- [x] 真实浏览器 smoke 必须覆盖 `3000 /documents/<id>` 主文档壳中页面新建后 Page Tree / File Tree 立即出现,且 `navigationEvents.length=0`。
|
||||
- [x] 真实浏览器 smoke 必须覆盖同一主文档壳中页面删除或 purge 后 Page Tree / File Tree 立即移除,且不依赖浏览器刷新。
|
||||
- [x] 真实浏览器 smoke 必须覆盖同一主文档壳中页面内新建 mindmap 后 File Tree 立即出现对应 asset row;该链路是 `/api/mindmap/<doc>/<mindmap>` 资产写入,不等同于页面 `tree.node.create`。
|
||||
|
||||
证据:
|
||||
|
||||
- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts`:输出 `version` 与 `cursor`,以 `getSidebarDataFreshness()` 比较 initial/query/tree_stream;stream 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;该脚本只作为 debug route 补充证据,不再代表最终主链验收。
|
||||
- 已通过:`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task426-mnote-web-main-no-reload-smoke.js`,覆盖 `3000 /documents/<id>` 主文档壳:页面 create/delete 无刷新即时更新 Page Tree / File Tree,页面内 mindmap create 经 `/api/mindmap/<doc>/<mindmap>` POST 后即时插入 `asset:<mindmapId>` File Tree row,`afterMindmapCreate.localApplied=true`。
|
||||
|
||||
## 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`。2026-05-14 起这只保留为历史上下文证据;当前页面 AI 主链已改为 Hermes client proxy,页面上下文进入 Hermes run/session context,最新页面事实由 Hermes 通过 mnote plugin tool 回读。
|
||||
- 已通过:`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。该 smoke 已在 2026-05-14 默认退役为 historical smoke,脚本现本机归档于 gitignored `recycle/scripts/retired-ai-agent-run-smokes/`;当前页面 AI 长期验收以 `scripts/task-hermes-page-ai-*.js` 和 `scripts/task-page-block-ai-tools-smoke.js` 矩阵为准。
|
||||
- 已通过:`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/done/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 command,UI 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 AI,应按 Mindmap domain 另拆 Hermes plugin / Rust bridge 迁移。
|
||||
- `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/done/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`:补充执行入口,指向 `done/06-execution-checklist-and-acceptance.md`,并说明 `done/05-tree.md` 核心主线已由 `4-24` / `5-12` 承接。
|
||||
- `design/10-review/done/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/done/5-6-page-aggregate-alignment-checklist-v1.md`、`design/06-mindmap/reference/6-mindmap-kernel-phase6-projection-editor-v1.md`、`design/06-mindmap/done/6-mindmap-phase6-leptos-ui-shell-reuse-checklist-v1.md` 仍有未完成项或长期主线尾项,继续保留在 `process/`;`design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`、`design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`、`design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`、`design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md` 已迁入 `done/`,后续活跃 AI 体验面由 `design/07-ai/process/7-7-page-ai-mini-hermes-control-surface-v1.md` 承接;`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/`。
|
||||
+607
@@ -0,0 +1,607 @@
|
||||
# [recycle] 08 [done] Kernel 架构收口与下一阶段优先级 Review / Checklist v1
|
||||
|
||||
> 更新时间:2026-05-18
|
||||
>
|
||||
> 执行状态:`done`
|
||||
>
|
||||
> 关联文档:
|
||||
> - `/mnt/Data1T/mnote/AGENTS.md`
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.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/done/5-6-page-aggregate-alignment-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-14-local-first-ai-markdown-editing-convergence-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
|
||||
>
|
||||
> 2026-06-01 口径补充:本文是 2026-05-18 的历史 review / checklist 快照。文中把 `mnote.doc.markdown_edit` 描述为简单正文编辑主路径的结论已被后续 local-first agent 文件编辑控制面覆盖;当前 local-first 普通 Markdown 编辑主路径以 `AGENTS.md`、`ARCHITECTURE.md` 与 `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 为准。`mnote.doc.markdown_edit` 只保留为 remote/cloud/compat fallback 或结构校验辅助。
|
||||
|
||||
---
|
||||
|
||||
## 1. Review 结论
|
||||
|
||||
当前内核架构的骨架已经基本成立,但还不能说“已完善”。
|
||||
|
||||
已经成立的部分:
|
||||
|
||||
- `tree-first graph kernel` 是长期语义事实源。
|
||||
- `mnote-web` 已是 `3000` 主 Web 执行面。
|
||||
- 文档页默认主编辑器已切到页面内 `leptos-tiptap` island。
|
||||
- Page Aggregate 已进入 Rust-first 读取链,并已输出 `blockDocument / blockProjectionVersion / projectionSource`。
|
||||
- 页面/块 AI tools 已开始走 `mnote.doc.*` / `mnote.block.*`、Page Aggregate block projection 和 Rust `EditorCommand`。
|
||||
|
||||
仍未完善的部分:
|
||||
|
||||
- Page Aggregate 仍是过渡态,block projection 主要从 `documents.content` / local markdown content 投影,不是 EditorBlockDocument 原生落库完成态。
|
||||
- 标题、正文、页面设置、page tree、AI 写入口尚未完全闭环到同一组 projection / command family。
|
||||
- `tree.*` 已是 preferred command name,但 `documents.*` 兼容命令面仍未完全降级。
|
||||
- `/api/realtime/ws` 已是 tree realtime 主链,`/api/tree/events` 已降级为 SSE fallback;live cache 的后续重点是减少兼容 fallback 与补偿链。
|
||||
- AI 块工具已有最小闭环,但当前编辑路径以块操作(blockId)为中心——AI 被迫理解 UI 层概念,且本地 `.md` 文件无 AI 写入路径。`7-14` 提出应将 AI 编辑主路径修正为 markdown 文本级(search/replace),`mnote.block.*` 降级为结构性辅助,在线 Convex 文档与本地文件共用同一条 markdown 写入路径。
|
||||
|
||||
补充 BlockNote / Tiptap AI 参考后的判断:
|
||||
|
||||
- BlockNote AI 和 Tiptap AI Toolkit 可以参考的是 AI runtime shape 与 tool contract,不是 mnote 的事实源。
|
||||
- AI 方向下一步只应补基础底座:`PageAIContextBuilder`、`scope=selection`、`page_xml/text`、tool manifest annotations、`PageAIReviewSession`、accept/reject/retry/abort 状态机。
|
||||
- 2026-05-18 补充(`7-18` 到 `7-25` 收口后):AI 简单正文编辑主路径已切到 markdown 文本级 search/replace / full_content,`page_ai_workflow.rs` 当前通过模型生成 markdown 编辑意图后调用 `mnote.doc.markdown_edit`,并复用统一 mnote tool executor;`local_rule` planner(`direct_block_edit_operations`)不再作为当前 runtime 口径。在线 Convex 文档和本地 `.md` 文件共用同一条 markdown 写入合同。
|
||||
- 在非 AI 主架构和 AI 基础合同打牢之前,不应扩展新的 AI agent 工作流、复杂 AI UI 或跨页面智能功能。
|
||||
|
||||
因此,下一阶段不应优先“大改架构”或“大量加新功能”,而应优先做:
|
||||
|
||||
> **架构收口 + 验收矩阵 + 定向找 bug。**
|
||||
|
||||
更具体地说:
|
||||
|
||||
> **先打牢除 AI 以外的 Page Aggregate / tree command / tree realtime 主架构,再把 AI 的基础读写、上下文、冲突与审阅底座打牢;之后才进入 AI 功能扩展。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 下一步总优先级
|
||||
|
||||
总原则:
|
||||
|
||||
- 非 AI 主架构优先级高于 AI 功能扩展。
|
||||
- AI 当前只推进基础设施,不推进新功能面。
|
||||
- AI 基础设施必须服从 Rust kernel / Page Aggregate / EditorCommand / Hermes audit,不引入 BlockNote runtime 或 Tiptap/ProseMirror editor truth。
|
||||
|
||||
### P0:Page Aggregate 单一真源收口
|
||||
|
||||
目标:
|
||||
|
||||
- 标题、正文、页面设置、page tree、AI 读写入口继续收敛到同一组 Page Aggregate projection / command family。
|
||||
- 前端不在页面壳、island 外侧、Sidebar preferred snapshot 外再拼第二份页面真相。
|
||||
- 明确当前 block projection 过渡态和长期 EditorBlockDocument 原生落库目标之间的边界。
|
||||
|
||||
判断标准:
|
||||
|
||||
- 页面读取只走 Rust `/api/page-aggregate/:id` 正式读链。
|
||||
- 页面标题、正文、页面设置写入后,Page Aggregate、页头、Sidebar、Breadcrumb、File Tree、AI fetch 回读一致。
|
||||
- 失败时明确 degraded / conflict / stale,不返回看似成功的旧快照。
|
||||
|
||||
### P0:Tree Command Cutover
|
||||
|
||||
目标:
|
||||
|
||||
- `tree.*` 成为正式命令面。
|
||||
- `documents.*` 只保留为兼容层,不再继续扩写长期业务语义。
|
||||
- 页面新建、重命名、移动、归档、恢复、删除、资源生命周期都能通过正式 tree/page command 解释。
|
||||
|
||||
### P0:Tree Realtime Live Cache 统一
|
||||
|
||||
目标:
|
||||
|
||||
- `/api/realtime/ws` 的 snapshot / delta / resync 成为 Sidebar、Page Tree、File Tree、page subtree 的共同 live cache 来源,`/api/tree/events` 仅作为 SSE fallback。
|
||||
- 减少 query/refetch/freshness 补偿链和旧快照回闪。
|
||||
|
||||
### P0:AI 基础工具与审阅底座
|
||||
|
||||
目标:
|
||||
|
||||
- 不继续扩新 AI 功能,先把当前 `mnote.doc.*` / `mnote.block.*` 变成可靠、可测、可审阅、可回滚的基础工具链。
|
||||
- 参考 BlockNote / Tiptap 的架构形态,但保持 mnote Rust-owned tool runtime。
|
||||
|
||||
必须补齐:
|
||||
|
||||
- `PageAIContextBuilder` 与 `scope=selection`。
|
||||
- `format=page_xml/text/json` 的稳定输出边界。
|
||||
- tool manifest annotations:`readonly/destructive/requiresApproval/selectionEffect/runtimeOwner/writeOwner`。
|
||||
- `PageAIReviewSession`:preview -> accept/reject/retry/abort。
|
||||
- 多块插入的边界与 inserted block ids。
|
||||
- 复杂块移动阻断矩阵。
|
||||
- stale revision / stale blockRevisionRef / idempotency 重放。
|
||||
|
||||
明确不做:
|
||||
|
||||
- 不新增跨页面 AI agent 工作流。
|
||||
- 不新增复杂 AI 自动化功能面。
|
||||
- 不把 `@blocknote/xl-ai` 或 BlockNote `AIExtension` 作为 runtime dependency。
|
||||
- 不让 AI 写入绕过 `dryRun/idempotencyKey/revision/conflictDetectionKey/revisionRef`。
|
||||
- **不把 `direct_block_edit_operations` / `local_rule` planner 当作当前或长期 AI 编辑路径;简单正文编辑当前主路径是 `mnote.doc.markdown_edit`,结构性块操作才使用 `apply_block_ops` / `mnote.block.*`。**
|
||||
- **不新增 `mnote.block.*` 工具;现有保留为结构性辅助。**
|
||||
|
||||
**2026-05-18 状态更新(`7-18` 到 `7-25` 收口后):**
|
||||
|
||||
- **Phase A / B 主路径已完成当前收口**:`mnote.doc.markdown_edit` + `mnote.doc.fetch` 增强、Hermes manifest / guidance、`page_ai_workflow.rs` → `markdown_edit`、统一 mnote tool executor、local / online markdown source 合同已同步。
|
||||
- **Phase C 仍冻结**:流式 apply + suggest/review 只保留设计边界,当前不实施、不扩新 AI 功能。
|
||||
- 在线 Convex 文档和本地 `.md` 文件共用同一条 markdown AI 写入路径(`resolve_source` → Convex | LocalFS)。
|
||||
|
||||
### P1:定向 Bug Hunt
|
||||
|
||||
目标:
|
||||
|
||||
- 不做泛泛“找 bug”,只围绕已知架构风险做定向排查。
|
||||
|
||||
优先找:
|
||||
|
||||
- Page Aggregate 回流不一致。
|
||||
- AI 块写入 conflict / stale revision。
|
||||
- File Tree `{title}.md` 与 page title sync。
|
||||
- tree stream resync / 双浏览器一致性。
|
||||
- debug / compat / fallback 是否混入主链。
|
||||
|
||||
---
|
||||
|
||||
## 3. 可执行 Checklist
|
||||
|
||||
### 3.1 Page Aggregate 单一真源
|
||||
|
||||
- [x] 盘点历史 `wolai-frontend/src/lib/documents/page-aggregate-loader.ts` 是否仍有 runtime fallback 或 TS builder 读取分支。
|
||||
- [x] 盘点历史 `wolai-frontend/src/lib/documents/page-aggregate-builder*` 的引用,确认只剩 test helper / historical adapter。
|
||||
- [x] 检查 `/api/documents/page` 仍返回明确 `410`,不参与 runtime 主链。
|
||||
- [x] 跑文档页打开 smoke,记录 `/api/page-aggregate/:id` 是首要读链。
|
||||
- [x] 新建页面后检查 Page Aggregate `identity/head/body/tree/stats` 字段完整。
|
||||
- [x] 修改标题后检查 Page Aggregate、页头、Breadcrumb、Sidebar、File Tree `{title}.md` 同步。
|
||||
- [x] 修改正文后检查 Page Aggregate `body.revision/conflictDetectionKey/blockDocument` 同步。
|
||||
- [x] 修改页面设置后检查 Page Aggregate 与 island runtime page options 同步。
|
||||
- [x] 刷新页面后检查标题、正文、页面设置不回退到旧快照。
|
||||
- [x] 破坏或暂停 Convex query,检查响应是 degraded/error,不返回伪 fixture。
|
||||
- [x] 在 `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md` 勾选已验证项,并补证据路径。
|
||||
|
||||
2026-05-16 静态审计证据(历史 Next 前端退役前快照):
|
||||
|
||||
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts` 只通过 `loadPageAggregateFromRustSnapshot` 请求 Rust `/api/page-aggregate/:documentId`,`loadPageAggregate` 不再回退 TS builder;`buildServerBridgeRequest("/documents/page")` 只用于构造转发 header/request,不调用 Next `/api/documents/page` compat route。
|
||||
- `rg -n "buildPageAggregateFromDocumentPayloads|page-aggregate-builder" wolai-frontend/src rust scripts design --glob '!design/05-editor-mainline/reference-code/**' --glob '!node_modules/**'` 显示 runtime 非测试引用仅剩 builder 定义;代码引用只有 `wolai-frontend/src/lib/documents/page-aggregate-builder.test.ts`。
|
||||
- `wolai-frontend/src/app/api/documents/page/route.ts` 明确返回 `410`,错误文案指向 `/api/page-aggregate/:documentId`;`route.test.ts` 覆盖该行为。
|
||||
- 已通过:`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate`、`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate`、`cd wolai-frontend && pnpm test src/app/api/documents/page/route.test.ts src/lib/documents/page-aggregate-builder.test.ts`。
|
||||
- 全仓 `git diff --check` 当前被既有删除文件 `rust/spikes/leptos-tiptap-spike/trunk-8123.err` 阻断:`fatal: unable to generate checkdiff for ...`;本轮未清理该无关脏改动,已改用限定文件 diff check 复核。
|
||||
|
||||
2026-05-16 真实 3000 smoke 证据:
|
||||
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task110-page-title-single-truth-smoke.js` 通过,证据:`tmp/page-aggregate-single-truth-smoke/20260516-182244/task110.stdout.json`。该脚本创建两页、修改标题,并验证页头、Breadcrumb、Sidebar、Page Tree、File Tree 与刷新后标题一致。
|
||||
- 文档页打开与 Page Aggregate snapshot 验证通过,证据:`tmp/page-aggregate-single-truth-smoke/20260516-182244/page-open-snapshot.stdout.json`,截图:`tmp/page-aggregate-single-truth-smoke/20260516-182244/page-open-snapshot.png`。文档响应 `x-mnote-web-owner=mnote-web`、`x-mnote-web-shell=document`,HTML 包含 `data-page-aggregate-snapshot="mnote.page_aggregate.v1"` 与 `data-page-tree-source="page_aggregate.tree.pageSubtree"`,同一临时页 `/api/page-aggregate/:id` 回读 `schema=mnote.page_aggregate.v1`。
|
||||
- 新建页面 Page Aggregate 字段完整性验证通过,证据:`tmp/page-aggregate-single-truth-smoke/20260516-182244/page-aggregate-fields.stdout.json`。`identity/head/body/tree/stats` 全部为 true,`body.blockProjectionVersion=1`,`projectionSource=documents.content`。
|
||||
- 说明:曾尝试在浏览器 network 中直接捕获 `/api/page-aggregate/:id`,证据 `page-open-network.stderr.log`;当前 Rust SSR 主入口会直接把 Page Aggregate snapshot 写入 HTML,浏览器侧不必出现该 API 请求,因此该尝试不作为失败验收项。
|
||||
|
||||
2026-05-16 正文写入后 body 同步证据:
|
||||
|
||||
- 新增并运行 `scripts/task-page-aggregate-body-sync-smoke.js`,通过真实 3000 文档页输入正文,等待 `/api/documents/save` 成功后轮询 `/api/page-aggregate/:id`。
|
||||
- 证据:`tmp/page-aggregate-body-sync-smoke/mp87mgz7.json`,截图:`tmp/page-aggregate-body-sync-smoke/mp87mgz7.png`,stdout:`tmp/page-aggregate-body-sync-smoke/latest.stdout.json`。
|
||||
- 验证结果:`body.revision` 从 `0` 更新到 `1`,`body.conflictDetectionKey` 从 `tree_1778927703753_1:0` 更新到 `tree_1778927703753_1:1`,`body.blockProjectionVersion=1`,`projectionSource=documents.content`,`blockDocument.blocks[0]` 回读到文本 `Page Aggregate body sync mp87mgz7` 与 `revisionRef=pageRev:1:block:block_1:hash:fnv1a64:208e0e63c853eef5`。
|
||||
- 配套测试已通过:`cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_save_route_executes_page_body_save_command`、`cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate_get_projects_legacy_content_to_block_document`。
|
||||
|
||||
2026-05-16 页面设置写入后 pageOptions 同步证据:
|
||||
|
||||
- 新增并运行 `scripts/task-page-aggregate-options-sync-smoke.js`,通过真实 3000 文档页打开页面设置,依次修改 `wideLayout=true`、`smallText=true`、`layoutDensity=compact`,等待 `/api/documents/options` 返回 `page.layout.updateOptions` 后轮询 `/api/page-aggregate/:id`。
|
||||
- 证据:`tmp/page-aggregate-options-sync-smoke/mp87y6j3.json`,截图:`tmp/page-aggregate-options-sync-smoke/mp87y6j3.png`,stdout:`tmp/page-aggregate-options-sync-smoke/latest.stdout.json`。
|
||||
- 验证结果:Page Aggregate `layout.pageOptions` 依次回读到 `wideLayout=true`、`smallText=true`、`layoutDensity=compact`;运行时 DOM 同步为 `data-page-wide-layout="true"`、`data-page-small-text="true"`、`data-layout-density="compact"`,island `editorRoot` 与 `.editor-surface` 同步,字号从 `16px` 到 `15px`,段落间距从 `8px` 到 `4px`。
|
||||
- 配套测试已通过:`cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_options_route_executes_page_layout_update_options`、`cd wolai-frontend && pnpm test src/lib/documents/page-command-client.test.ts src/components/editor/leptos-tiptap-island-editor-host.test.tsx src/lib/documents/page-option-semantics.test.ts`。
|
||||
|
||||
2026-05-16 刷新后标题 / 正文 / 页面设置不回退证据:
|
||||
|
||||
- 新增并运行 `scripts/task-page-aggregate-refresh-persistence-smoke.js`,同一临时页内依次通过 UI 写入标题、正文和页面设置,等待 Page Aggregate 回读最新 `head/body/layout` 后刷新页面,再断言页头、正文 DOM、页面设置控件、runtime DOM 与 `/api/page-aggregate/:id` 均保持最新值。
|
||||
- 证据:`tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.json`,截图:`tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.png`,stdout:`tmp/page-aggregate-refresh-persistence-smoke/latest.stdout.json`。
|
||||
- 验证结果:刷新前后 `head.title=Page Aggregate refresh mp88fr6k`、`body.revision=1`、`body.conflictDetectionKey=tree_1778929070007_1:1`、`blockDocument.blocks[0].text=Page Aggregate refresh body mp88fr6k`、`layout.pageOptions.wideLayout/smallText/layoutDensity=true/true/compact`;刷新后页头标题、`.ProseMirror` 正文、设置控件、`documentElement`、`.document-shell`、island root 和 `.editor-surface` 均保持最新值。
|
||||
- 修复点:`rust/crates/mnote-web/src/ssr/pages/layout.rs` 将 `initializePageUiSurfaces` 延后到 `DOMContentLoaded` 后执行,避免 layout 脚本早于嵌入 Page Aggregate JSON / island DOM 完成时把 runtime 属性按默认 pageOptions 应用。
|
||||
- 2026-05-18 复测并扩展 `scripts/task-page-aggregate-refresh-persistence-smoke.js`,补充 `mnote.doc.fetch` / `tree.pageSubtree` 同组回读证据。证据:`tmp/page-aggregate-refresh-persistence-smoke/mpagfma1.json`,截图:`tmp/page-aggregate-refresh-persistence-smoke/mpagfma1.png`。
|
||||
- 验证结果:刷新前后 `head.title=Page Aggregate refresh mpagfma1`、`body.revision=1`、`body.conflictDetectionKey=tree_1779063433221_1:1`、`blockDocument.blocks[0].text=Page Aggregate refresh body mpagfma1`、`layout.pageOptions.wideLayout/smallText/layoutDensity=true/true/compact`、`tree.pageSubtree.rootNodeId=tree_1779063433221_1` 均一致;`mnote.doc.fetch` 返回 `schema=mnote.page_ai_context.v1`,`revision/conflictDetectionKey/revisionRef/text` 与同一 Page Aggregate block 完全一致。
|
||||
- 缺陷记录:`bugs/05-editor-mainline/done/5-12-page-options-refresh-runtime-attrs-v1.md`。
|
||||
- 配套测试已通过:`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_options_route_executes_page_layout_update_options`。
|
||||
|
||||
2026-05-16 Convex query 失败时不返回伪 fixture 证据:
|
||||
|
||||
- 在 `rust/crates/mnote-web/src/routes/web_shell.rs` 补充负向 route 测试,构造隔离配置 `convex_url=http://127.0.0.1:9`、`allow_dev_fixtures=false`、`query_fixtures_json=None`,模拟 Convex query 不可达。
|
||||
- `/api/page-aggregate/doc_1?workspaceId=ws_demo` 返回 `503 SERVICE_UNAVAILABLE`、`x-error-code=convex_unavailable`、`x-error-phase=query_send`、`x-upstream-service=convex`,body 为 `ok=false/code=convex_unavailable`,且没有 `schema` / `result`。
|
||||
- `/documents/doc_1?workspaceId=ws_demo` 返回同类错误 JSON,HTML body 不包含 `mnote.page_aggregate.v1`、`data-mnote-dev-fixture`、`data-page-aggregate-snapshot`。
|
||||
- 验证命令已通过:`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate_endpoint_errors_without_convex_or_fixture -- --nocapture`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_errors_without_convex_or_fixture -- --nocapture`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate`。
|
||||
|
||||
验证命令建议:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate
|
||||
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime page_aggregate
|
||||
node scripts/task110-page-title-single-truth-smoke.js
|
||||
node scripts/task-page-aggregate-body-sync-smoke.js
|
||||
node scripts/task-page-aggregate-options-sync-smoke.js
|
||||
node scripts/task-page-aggregate-refresh-persistence-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate_endpoint_errors_without_convex_or_fixture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_errors_without_convex_or_fixture
|
||||
```
|
||||
|
||||
### 3.2 Page Block AI Tooling
|
||||
|
||||
- [x] 以 `7-10` 为工具执行清单、`7-12` 为当前 AI runtime 基础设计,逐 phase 检查已勾选项是否都有代码、测试、smoke 证据。
|
||||
- [x] 给 `mnote.doc.fetch scope=selection` 补真实选区上下文输入和返回结构。
|
||||
- [x] 给 `mnote.doc.fetch format=page_xml` 补最小 PageXML 输出。
|
||||
- [x] 给 `mnote.block.fetch format=text/page_xml` 补格式分支。
|
||||
- [x] 给 tool manifest 补 `annotations`,区分 readonly / destructive / requiresApproval / selectionEffect。
|
||||
- [x] 定义 `mnote.page_ai_context.v1`,明确 context 只来自 Page Aggregate projection。
|
||||
- [x] 定义 `mnote.page_ai_review_session.v1`,明确 accept / reject / retry / abort 语义。
|
||||
- [x] 给 `mnote.block.insert_after` 补多块插入限制和返回 inserted block ids。
|
||||
- [x] 给 `mnote.block.move_after` 补标题带子块阻断 smoke。
|
||||
- [x] 给 `mnote.block.move_after` 补列表项阻断 smoke。
|
||||
- [x] 给 `mnote.block.move_after` 补表格 / mindmap / resource 阻断 smoke。
|
||||
- [x] 给 `mnote.block.replace` 补 stale revision 失败用例。
|
||||
- [x] 给 `mnote.block.replace` 补 stale blockRevisionRef 失败用例。
|
||||
- [x] 给写工具补重复 idempotencyKey 的端到端用例。
|
||||
- [x] 检查 `mnote.page.save` 在 manifest / UI 中继续标为页面级兜底,不显示为精确块编辑主入口。
|
||||
- [x] 检查任何新增 AI surface 是否只是基础 review/context/tooling 验收,不是新功能扩展。
|
||||
- [x] 更新 `design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md` 的每个 phase 证据。
|
||||
- [x] 更新 `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 的 Hermes 工具路由与审阅面 checklist。
|
||||
|
||||
2026-05-18 `mnote.block.insert_after` 多块插入证据:
|
||||
|
||||
- 代码:`rust/crates/mnote-web/src/hermes_tools/block.rs` 支持 `content` / `block` / `blocks` 输入;`blocks` 限制 `1..=20`;多块写入按输入顺序连续插入,并在结果返回 `insertedBlockIds`。
|
||||
- manifest:`rust/crates/mnote-web/src/hermes_tools/manifest.rs` 已声明 `blocks.minItems=1/maxItems=20`,并用 `anyOf` 表达 `content | block | blocks` 三种输入。
|
||||
- 测试:`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_insert_after -- --nocapture` 通过,覆盖多块成功和超过 20 个失败。
|
||||
- 回归:`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 通过,覆盖 Hermes tools 38 项。
|
||||
|
||||
2026-05-18 `mnote.block.move_after` 复杂块阻断证据:
|
||||
|
||||
- 代码:`rust/crates/mnote-web/src/hermes_tools/block.rs` 继续以同父级、叶子块、可移动类型、editable、自身移动为真实写入前阻断条件。
|
||||
- 测试:`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_move_after_blocks_complex_and_nested_blocks -- --nocapture` 通过,覆盖标题带子块、列表项、表格、mindmap、resource 均返回 `blocked=true` / `block_move_after_blocked`。
|
||||
- 回归:`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture` 通过,当前 Hermes tools 共 39 项。
|
||||
|
||||
**新增(2026-05-18,`7-18` 到 `7-25` 收口状态):**
|
||||
|
||||
**Phase A / B(当前主路径已收口):**
|
||||
- [x] `mnote.doc.markdown_edit` 核心实现:resolve_source → search_replace / full_content → write_target
|
||||
- [x] `mnote.doc.markdown_edit` 搜索替换语义:精确匹配、归一化匹配、多操作同块合并、无法安全映射时失败
|
||||
- [x] `mnote.doc.markdown_edit` Convex 在线写入 adapter 以最终 markdown 映射写回
|
||||
- [x] `mnote.doc.markdown_edit` 本地文件写入 adapter 尊重 dryRun / idempotency
|
||||
- [x] `mnote.doc.fetch format: "markdown"` 增强
|
||||
- [x] Hermes tool manifest 注册并描述 `mnote.doc.markdown_edit` 写入合同
|
||||
- [x] 乐观锁:revision / conflictDetectionKey 前置校验
|
||||
- [x] `direct_block_edit_operations` / `local_rule` 不再作为 `page_ai_workflow.rs` 当前主路径
|
||||
- [x] `page_ai_workflow.rs` 改为模型产 markdown search/replace / full_content,底层走 `mnote.doc.markdown_edit`
|
||||
- [x] 单元测试覆盖 markdown_edit / page_ai_workflow / Hermes guidance / ACP tool contract
|
||||
|
||||
**Phase C(设计冻结,不实施):**
|
||||
- [x] 流式 apply + suggest/review 设计已冻结,不阻塞 Phase A/B 实施
|
||||
|
||||
2026-05-18 Review Session 合同定义:
|
||||
|
||||
- 已在 `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 定义 `mnote.page_ai_review_session.v1` 最小 schema。
|
||||
- 已明确状态:`draft/planning/previewing/awaiting_user/accepted/rejected/applying/applied/failed/aborted/stale`。
|
||||
- 已明确动作约束:`accept` 必须重新读 Page Aggregate 并校验 revision / conflictDetectionKey / revisionRef,`reject/abort` 不产生写入,`retry` 必须生成新 proposal 或 dry-run preview,yolo 模式也应生成同构 audit 数据。
|
||||
- 边界:这里只完成合同定义,不实施 Phase C 流式 apply 或新的审阅 UI。
|
||||
|
||||
2026-05-16 Page Block AI context / format focused smoke 证据:
|
||||
|
||||
- 新增并运行 `scripts/task-page-block-ai-context-format-smoke.js`,通过真实 3000 + 测试账号创建临时页,用 `mnote.page.save` 初始化 `p_1/p_2/p_3`,再验证 Hermes mnote tools 的 context / format / annotations / selection guard。
|
||||
- 证据:`tmp/page-block-ai-context-format-smoke/mp8ddr4n.json`,截图:`tmp/page-block-ai-context-format-smoke/mp8ddr4n-page.png`。
|
||||
- 验证结果:
|
||||
- `GET /api/hermes/tools/mnote/manifest` 中 `mnote.doc.fetch`、`mnote.block.fetch`、`mnote.block.replace`、`mnote.page.save` 均包含 `readonly/destructive/idempotent/requiresApproval/approvalMode/runtimeOwner/writeOwner/selectionEffect`;`mnote.page.save` 明确 `destructive=true`、`approvalMode=yolo`,继续定位为页面级粗粒度兜底。
|
||||
- `mnote.doc.fetch scope=selection selectedBlockIds=["p_2"] format=page_xml` 返回 `schema=mnote.page_ai_context.v1`、`allowedTargetBlockIds=["p_2"]`、`revision/conflictDetectionKey`、`revisionRef`,`content` 只包含 `p_2` 与 `第二段 mp8ddr4n`,不包含未选中的 `p_1/p_3`。
|
||||
- `mnote.doc.fetch scope=selection format=text` 返回 `[p_2] 第二段 mp8ddr4n`,不包含未选中块。
|
||||
- `mnote.block.fetch blockId=p_2 format=page_xml/text` 返回目标块、`revisionRef` 和同父级 before/after 上下文 `p_1/p_3`。
|
||||
- `mnote.doc.apply_block_ops dryRun=true allowedTargetBlockIds=["p_2"]` 尝试 replace `p_1` 被拒绝,HTTP `400`,错误码 `mnote_block_target_out_of_scope`。
|
||||
- 边界:本轮只验证 `mnote.doc.apply_block_ops` 的 selection scope guard;单个 `mnote.block.replace/insert_after/move_after` 尚未校验 `allowedTargetBlockIds`,不能据此勾选完整 selection 写保护矩阵。
|
||||
- 配套验证已通过:`node --check scripts/task-page-block-ai-context-format-smoke.js`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture`、`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-context-format-smoke.js`。
|
||||
|
||||
2026-05-16 / 2026-05-18 Page Block AI stale / idempotency focused smoke 证据:
|
||||
|
||||
- 新增并运行 `scripts/task-page-block-ai-conflict-idempotency-smoke.js`,通过真实 3000 + 测试账号创建临时页,用 `mnote.page.save` 初始化 `p_1/p_2`,再围绕 `mnote.block.replace` 验证冲突和幂等安全边界。
|
||||
- 证据:`tmp/page-block-ai-conflict-idempotency-smoke/mp8dyqiq.json`,截图:`tmp/page-block-ai-conflict-idempotency-smoke/mp8dyqiq-page.png`。
|
||||
- 最新复测证据:`tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs.json`,截图:`tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs-page.png`。
|
||||
- 验证结果:
|
||||
- 首次 `mnote.block.replace` 携带最新 `revision/conflictDetectionKey/blockRevisionRef/idempotencyKey` 成功写入 `p_2`,返回 `commandName=page.body.save` 与 `commandId=page_body_save_req_1778938353824_11`。
|
||||
- 使用同一 `idempotencyKey=idem_conflict_replace_mp8dyqiq` 再次调用 `mnote.block.replace`,即使请求 content 改成不同文本,也 replay 同一 `commandId`,Page Aggregate 回读 `p_2` 仍为首次写入文本,`revisionAfterFirst=2`、`revisionAfterReplay=2`,确认不重复写入。
|
||||
- 使用旧 `revision/conflictDetectionKey` 调用 `mnote.block.replace` 返回 HTTP `400`、错误码 `mnote_tool_conflict`,正文保持首次写入结果。
|
||||
- 使用最新 `revision/conflictDetectionKey` 但旧 `blockRevisionRef` 调用 `mnote.block.replace` 返回 HTTP `400`、错误码 `mnote_tool_conflict`,正文保持首次写入结果。
|
||||
- 2026-05-18 复测中,重复 `idempotencyKey=idem_conflict_replace_mpafrevs` replay 同一 `commandId=page_body_save_req_1779062303997_525`,`revisionAfterFirst=2`、`revisionAfterReplay=2`;旧 revision 与旧 blockRevisionRef 仍均返回 `mnote_tool_conflict`。
|
||||
- 边界:本轮只覆盖 `mnote.block.replace` 直接 tool executor 的 stale revision / stale blockRevisionRef / idempotency replay;不代表 `mnote.block.insert_after` 幂等矩阵、所有写工具幂等矩阵、review session accept stale 或 accept/reject/retry/abort 已完成。
|
||||
- 配套验证已通过:`node --check scripts/task-page-block-ai-conflict-idempotency-smoke.js`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture`、`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-conflict-idempotency-smoke.js`。
|
||||
|
||||
验证命令建议:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools
|
||||
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime editor_document
|
||||
node scripts/task-page-block-ai-tools-smoke.js
|
||||
```
|
||||
|
||||
### 3.3 Tree Command Cutover
|
||||
|
||||
- [x] `rg -n "documents\\.create|documents\\.title\\.update|documents\\.move|documents\\.archive|documents\\.restore" rust wolai-frontend` 盘点旧命令引用。
|
||||
- [x] 将仍在 runtime 主链的旧命令按 owner 分类:必须迁移、兼容保留、测试 fixture。
|
||||
- [x] 确认页面新建默认输出 `tree.node.create` 或正式 tree command。
|
||||
- [x] 确认页面重命名默认输出 `tree.node.rename` 或正式 page/tree command。
|
||||
- [x] 确认移动页面默认输出 `tree.subtree.move`。
|
||||
- [x] 确认归档 / 恢复 / 永久删除的 command family 与 resource lifecycle 设计一致。
|
||||
- [x] 对 compat alias 返回增加 owner / deprecated 标识,避免被当作主链。
|
||||
- [x] 更新 `design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md` 或新增后续 process checklist。
|
||||
|
||||
2026-05-18 当前 Tree Command Cutover 状态:
|
||||
|
||||
- `3000` 当前主壳由 Rust `mnote-web` SSR 承载;主要页面生命周期动作通过 Rust `/api/tree/commands` route 生成 `tree.node.create`、`tree.node.rename`、`tree.subtree.move`、`tree.node.archive`、`tree.node.restore`、`tree.node.purge`、`tree.subtree.copy`。
|
||||
- `bridge-runtime` 仍接受历史 `documents.*` alias,但 execution plan / artifact 会标记 `preferredCommandName`、`compatCommandName`、`deprecatedAlias`,避免兼容命令被当成当前主链。
|
||||
- `wolai-frontend` / Next tree command route 只作为退役前历史审计材料,不再作为当前运行时主链依据。
|
||||
|
||||
2026-05-16 Tree Command Cutover 静态盘点与 alias 标识证据(历史 Next 前端退役前快照):
|
||||
|
||||
- 只读盘点命令:`rg -n "documents\\.(create|title\\.update|move|archive|restore|delete|purge|copy_tree)" rust wolai-frontend scripts --glob '!node_modules/**'`。
|
||||
- 退役前 Next 前端主链:`wolai-frontend/src/lib/documents/tree-command-client.ts` 的新建、重命名、移动、归档、恢复、永久删除和复制均 POST `/api/tree/commands`,返回 meta 使用 `TREE_COMMAND_PROTOCOL.*.preferredCommandName`;`TREE_COMMAND_PROTOCOL` 中的 `documents.*` 只保留为 `compatCommandName`。
|
||||
- 退役前 Next tree command route:`wolai-frontend/src/app/api/tree/commands/route.ts` 在 create / move / rename / archive / restore 分支分别构造 `tree.node.create`、`tree.subtree.move`、`tree.node.rename`、`tree.node.archive`、`tree.node.restore`。
|
||||
- Rust Web tree route:`rust/crates/mnote-web/src/routes/tree.rs` 的 `create_command_wire` 输出 `tree.node.create`、`tree.node.rename`、`tree.subtree.move`、`tree.node.archive`、`tree.node.restore`、`tree.node.purge`、`tree.subtree.copy`;`tree_command` route 只接收 action,不接收旧 `documents.*` command name 作为主链输入。
|
||||
- 兼容保留:`bridge-runtime` 仍接受 `documents.create/title.update/move/delete/restore/purge/copy_tree`,但本轮已在对应 execution plan 的 `args_json.commandProtocol` 增加 `family=tree`、`owner=rust-runtime-kernel`、`preferredCommandName`、`compatCommandName`、`deprecatedAlias`;旧 `documents.*` alias 会标记 `deprecatedAlias=true`。
|
||||
- Transport 边界:`rust/crates/mnote-web/src/transport/convex.rs` 发送给 Convex legacy mutation 前会剥离 `commandProtocol`、`streamDeltaHint`、`domainEventHint`、`domainEventPlan(s)`,避免 legacy validator 把审计字段当写入参数。
|
||||
- 仍保留为后续兼容收口点:`wolai-frontend/src/app/api/documents/create-child/route.ts` / `page-command-adapter.ts` 仍构造 `documents.create`,当前分类为 compat-only;`mnote-cli` 仍有历史 `documents.*` CLI 构造,不属于 3000 主交互链。
|
||||
|
||||
2026-05-16 Tree Command Cutover 验证命令:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime tree_ -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_strips_tree_archive_artifacts_for_legacy_mutation -- --nocapture
|
||||
```
|
||||
|
||||
验证命令建议:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p bridge-runtime tree_command
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command
|
||||
node scripts/task122-rust-web-create-page-ui-smoke.js
|
||||
```
|
||||
|
||||
### 3.4 Tree Realtime Live Cache
|
||||
|
||||
- [x] 2026-05-17 WS push 迁移(`57ec8322`):`/api/realtime/ws` 成为主 transport,SSE 降级为 fallback。mutation 后通过 `stream_delta_tx` broadcast channel 推送 delta,WS 客户端零 Convex 查询。
|
||||
- [x] 盘点 Sidebar、Page Tree、File Tree、page subtree 仍依赖 query/refetch/freshness 补偿的位置。
|
||||
- [x] 确认 `/api/realtime/ws`(主)和 `/api/tree/events` SSE fallback 的 snapshot / delta / resync payload 覆盖 page/file/resource row。
|
||||
- [x] 新建页面后,双浏览器 A/B 检查另一端无需刷新出现页面。
|
||||
- [x] 重命名页面后,双浏览器 A/B 检查 Sidebar / Breadcrumb / File Tree 一致更新。
|
||||
- [x] 移动页面后,双浏览器 A/B 检查 tree order 不回闪。
|
||||
- [x] 删除 / 恢复后检查 trash 与主树事件一致。
|
||||
- [x] 断开 WS / SSE fallback 后恢复,检查 resync 能把 UI 拉回正确状态。
|
||||
- [x] 更新 `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md` 的已验证项。
|
||||
|
||||
2026-05-18 当前状态更新:
|
||||
|
||||
- `/api/realtime/ws` 已成为 Rust SSR 主壳默认 transport(`convex-command-log-ws`),`/api/tree/events` 仅作为 SSE fallback。
|
||||
- `CURRENT_ARCHITECTURE.md` 与 `bugs/03-rust-web/done/3-16` 到 `3-18` 已同步:原“WS 文档口径 / 前端 SSE 实现”冲突、SSE push 跳过 polling safety net、WS/SSE delta 合同分裂均已修复。
|
||||
- 2026-05-16 下面的 SSE smoke 证据保留为迁移前历史验证材料,不再代表当前主链。
|
||||
|
||||
2026-05-16 Tree Realtime Live Cache 静态盘点与 payload 覆盖证据(WS 迁移前历史快照):
|
||||
|
||||
- 退役前只读审查确认 React `AppLayoutShell` 同时接入 `useSidebarData` 与 `useSidebarTreeStream`,`usePreferredSidebarSnapshot` 负责 freshness 仲裁;Page Tree / File Tree 通过 preferred snapshot 消费 `kernelSidebarTree` 与 `kernelFileTreeProjection`。
|
||||
- 退役前未统一的补偿链:`useSidebarData` 还保留 Convex query、HTTP `/api/sidebar` fallback 与手动 `refetch`;mutation 后仍有 `refreshTree` / `sidebarQuery.refetch`;Rust SSR shell 与 React hook 当时各自可建立 EventSource;page subtree 当时仍从 Page Aggregate client state 派生。
|
||||
- Rust stream 历史盘点确认 workspace snapshot 已同时加载 `KernelProjectionKind::SidebarTree` 与 `KernelProjectionKind::FileTree`,SSE payload 的 `data.dataset.kernel_sidebar_projection` / `data.dataset.kernel_file_tree_projection` 覆盖 page/file row;subtree snapshot 当时只覆盖 `page_tree`,不含 file tree projection。
|
||||
- 本轮新增 `stream_change_preserves_remove_asset_delta_fields`,确认 `tree.resource.delete` 的 `remove_asset` delta 字段保真;`structural_delta_requires_projection_snapshot` 已覆盖 `remove_asset` 需要 projection snapshot,避免资源删除类事件只靠局部 patch。
|
||||
- 本轮强化 `scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`:解析 SSE `snapshot` data,断言 `kind=snapshot`、`stream=workspace`、`projection=sidebar_tree`、`x-mnote-tree-stream-owner=rust-web`,并确认 workspace snapshot 中有 `kernel_sidebar_projection`、`kernel_file_tree_projection` 和临时页 `doc:<documentId>` file tree row。
|
||||
|
||||
2026-05-16 Tree Realtime Live Cache 验证命令:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web stream_change_preserves_remove_asset_delta_fields -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web structural_delta_requires_projection_snapshot -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web routes::stream_support -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_events -- --nocapture
|
||||
cd wolai-frontend && pnpm test src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx src/lib/tree-stream/use-sidebar-tree-stream.test.tsx
|
||||
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
|
||||
```
|
||||
|
||||
3000 smoke 证据:
|
||||
|
||||
- `tmp/tree-live-cache-smoke/20260516-task123/task123.stdout.json`,结果 `owner=rust-web`、`stream=/api/tree/events`、`snapshotProjection=sidebar_tree`、`fileTreeRows=59`。
|
||||
|
||||
2026-05-16 双浏览器 no-refresh 验证与 purge 修复证据:
|
||||
|
||||
- 复用 `scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js` 做真实 3000 双浏览器验证:A 端通过真实 auth + `/api/tree/commands` 创建页面、归档、恢复、彻底删除与清空垃圾箱;B 端同时打开 File Tree 与 Trash,断言目标行无刷新出现 / 消失,且 `navigationEvents` 在初始打开后为空。
|
||||
- 修复前该 smoke 在 `purge-visible-on-b` 前失败:`documents:purge` 收到 artifact-only 字段 `commandProtocol`,Convex legacy validator 返回 `ArgumentValidationError: Object contains extra field commandProtocol`。
|
||||
- 修复点:`rust/crates/mnote-web/src/transport/convex.rs` 将 `tree.node.purge` / `documents.purge` 加入 `strip_tree_artifact_fields` 剥离范围,避免 compat mutation 接收 tree command audit 字段。
|
||||
- 缺陷记录:`bugs/04-tree-domain/done/4-41-tree-node-purge-command-protocol-leaks-to-convex-v1.md`。
|
||||
- 修复后 `task432` 通过,证据 `tmp/tree-live-cache-smoke/20260516-task432/result.json`:`ok=true`,`create-visible-on-b` 通过 `tree:resync` 出现在 B 端 File Tree;`archive-visible-on-b` 通过 `tree:delta remove_document` 同步 File Tree 与 Trash;`restore-visible-on-b` 通过 `tree:delta upsert_document` 同步恢复;`purge-visible-on-b` 与 `empty-trash-visible-on-b` 通过 `tree:resync` 拉回正确状态。
|
||||
|
||||
2026-05-16 purge 修复验证命令:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_strips_tree_purge_artifacts_for_legacy_mutation -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web convex_command_args_strips_tree_archive_artifacts_for_legacy_mutation -- --nocapture
|
||||
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js
|
||||
```
|
||||
|
||||
2026-05-16 双浏览器 rename live cache 修复与验证记录:
|
||||
|
||||
- 新增 regression smoke:`scripts/task446-tree-rename-dual-browser-live-smoke.js`。脚本使用 A/B 独立 browser context,A 端通过正式 `/api/tree/commands` 创建并执行 `{ action: "rename", workspaceId, documentId, title }`,B 端分别保持目标文档页与 File Tree 页面不刷新,记录 `tree:snapshot/tree:delta/tree:resync`、`/api/tree/events` 请求、DOM 状态、导航事件与截图路径。
|
||||
- RED 证据:修复前真实 3000 smoke 失败,B 端已收到 `tree:delta`、`op=upsert_document`、`liveApplied=delta`、`liveError=""`,Sidebar / Page Tree 与 File Tree `{renamedTitle}.md` 已更新,但当前打开目标文档页的 `titleInputValue`、Breadcrumb 与 `document.title` 仍停留旧标题,导致 `waitForBDocumentRename` 超时。
|
||||
- 根因:Rust SSR 文档 shell 的 `updateTitleEverywhere` 已更新 Page Tree / File Tree 行标题,但 title-only `upsert_document` 未同步当前文档页 chrome(页头标题输入框、Breadcrumb、`document.title`)。
|
||||
- 修复点:`rust/crates/mnote-web/src/ssr/pages/layout.rs` 的 `updateTitleEverywhere(documentId, title)` 现在同步当前文档 chrome,并保持 scoped tree row 更新;字符串合同单测新增当前页 title input 与 Breadcrumb selector 覆盖。
|
||||
- 构建阻断补齐:当前工作区已有 `AppState.editor_actor` / `AppConfig.enable_editor_actor` 脏改动但缺少真实 `editor_actor.rs` 文件,导致 mnote-web 无法编译启动;本轮把 misplaced 的 `EditorRuntimeActor` 实现补到 `rust/crates/mnote-web/src/editor_actor.rs`,并补齐测试配置中的 `enable_editor_actor` 字段,以恢复 3000 验证入口。
|
||||
- GREEN 证据:重新启动最新 `desktop:hot` 后运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task446-tree-rename-dual-browser-live-smoke.js` 通过。证据 `tmp/tree-live-cache-smoke/20260516-task446-rename/result.json`,截图 `tmp/tree-live-cache-smoke/20260516-task446-rename/b-document-after-rename.png` 与 `tmp/tree-live-cache-smoke/20260516-task446-rename/b-filetree-after-rename.png`。
|
||||
- 验证结果:B 文档页 `documentTitle/titleInputValue/breadcrumbTitle/sidebarTitle` 均为新裸标题;B File Tree `fileTreeTitle={renamedTitle}.md`;两端 `liveApplied=delta`、`liveError=""`;rename 后 `navigationEvents=[]`,确认 B 端无刷新 / 无导航。
|
||||
- 缺陷记录:`bugs/05-editor-mainline/done/5-13-tree-rename-live-document-chrome-stale-v1.md`。
|
||||
|
||||
2026-05-16 双浏览器 move order live cache 修复与验证记录:
|
||||
|
||||
- 新增 regression smoke:`scripts/task447-tree-move-order-dual-browser-live-smoke.js`。脚本使用 A/B 独立 browser context,A 端创建同父级 `A/B/C` 后执行正式 `/api/tree/commands`:`{ action: "move", documentId: C, parentId: root, sortOrder: 1 }`,B 端分别保持目标文档页与 File Tree 页面不刷新,记录 direct child order、tree events、导航事件与截图路径。
|
||||
- RED 证据:合法 `sortOrder=1` 下修复前真实 3000 smoke 失败。B 端收到 `tree:delta move_document`,payload 含 `sortOrder=1`,`liveApplied=delta` 且 `liveError=""`,但 Page Tree / File Tree DOM 顺序仍为 `A/B/C`,导致 `waitForExpectedOrder` 超时。
|
||||
- 根因:Rust SSR 文档 shell 的 `applyMoveDocumentDelta(data)` 未读取 `sortOrder`,`moveDocumentRowForMode(mode, documentId, parentId)` 固定 `appendChild` 到目标父节点末尾;`tree:local-command` optimistic move 分支也未传 `body.sortOrder`。
|
||||
- 修复点:`rust/crates/mnote-web/src/ssr/pages/layout.rs` 增加 `sortOrderFromDelta` 与 `insertTreeNodeAtSortOrder`,让 live delta 与 local command move 按目标父节点直系 sibling index 插入;`rust/crates/mnote-web/src/routes/tree.rs` 补充 route/artifact 单测断言 `sortOrder` 保留在 result 与 `streamDelta`。
|
||||
- GREEN 证据:重启最新 `desktop:hot` 后运行 `MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task447-tree-move-order-dual-browser-live-smoke.js` 通过。证据 `tmp/tree-live-cache-smoke/20260516-task447-move-order/result.json`,截图 `tmp/tree-live-cache-smoke/20260516-task447-move-order/b-document-after-move.png` 与 `tmp/tree-live-cache-smoke/20260516-task447-move-order/b-filetree-after-move.png`。
|
||||
- 验证结果:B 文档页与 B File Tree 页面中的 Page Tree / File Tree direct child order 均从 `A/B/C` 变为 `A/C/B`;两端 `liveApplied=delta`、`liveError=""`;move 后 `navigationEvents=[]`,确认无刷新 / 无导航。
|
||||
- 缺陷记录:`bugs/04-tree-domain/done/4-42-tree-move-live-delta-sort-order-ignored-v1.md`。
|
||||
|
||||
2026-05-16 SSE resync / reconnect recovery 验证记录:
|
||||
|
||||
- 新增 `scripts/task448-tree-resync-recovery-dual-browser-smoke.js`,通过 B 端 EventSource URL 注入 `pollMs=5000`,A 端在同一 poll 间隔内连续创建两个子页,强制 `/api/tree/events` 进入非单条 delta 的 `event: resync` 分支。
|
||||
- `task448` 验证结果:B 文档页与 B File Tree 页面均收到 `tree:resync`,`liveApplied=resync`、`liveError=""`,Page Tree / File Tree direct child order 从仅有初始子页恢复为包含新增 `b/c` 两个子页,`navigationEvents=[]`。证据 `tmp/tree-live-cache-smoke/20260516-task448-resync/result.json`,截图 `tmp/tree-live-cache-smoke/20260516-task448-resync/b-document-after-resync.png` 与 `tmp/tree-live-cache-smoke/20260516-task448-resync/b-filetree-after-resync.png`。
|
||||
- 新增 `scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js`,通过 Playwright `context.setOffline(true)` 模拟 B 端 SSE 断线,A 端离线期间连续创建两个子页,B 端恢复在线后等待 EventSource 恢复事件把 UI 拉回最新。
|
||||
- `task449` 验证结果:B 文档页与 B File Tree 页面恢复后 `liveStatus=connected`、`liveApplied=resync`、`liveError=""`,新增 `b/c` 子页出现在 Page Tree 与 File Tree,`navigationEvents=[]`。证据 `tmp/tree-live-cache-smoke/20260516-task449-reconnect/result.json`,截图 `tmp/tree-live-cache-smoke/20260516-task449-reconnect/b-document-after-reconnect.png` 与 `tmp/tree-live-cache-smoke/20260516-task449-reconnect/b-filetree-after-reconnect.png`。
|
||||
- 说明:当前 Rust SSR controller 的真实恢复合同是“断线期间错过多条变化后,恢复时通过 snapshot/resync 类完整投影拉回 UI”;本轮实测恢复事件为 `tree:resync`,不是浏览器刷新或导航。
|
||||
|
||||
验证命令建议:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_events
|
||||
node --check scripts/task447-tree-move-order-dual-browser-live-smoke.js
|
||||
node --check scripts/task448-tree-resync-recovery-dual-browser-smoke.js
|
||||
node --check scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js
|
||||
node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js
|
||||
node scripts/task432-filetree-trash-page-dual-browser-no-refresh-smoke.js
|
||||
node scripts/task446-tree-rename-dual-browser-live-smoke.js
|
||||
node scripts/task447-tree-move-order-dual-browser-live-smoke.js
|
||||
node scripts/task448-tree-resync-recovery-dual-browser-smoke.js
|
||||
node scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js
|
||||
```
|
||||
|
||||
### 3.5 定向 Bug Hunt
|
||||
|
||||
- [x] 建立 bugs 分类:Page Aggregate 问题归 `bugs/05-editor-mainline/process/`。
|
||||
- [x] Tree command / realtime / File Tree 问题归 `bugs/04-tree-domain/process/`。
|
||||
- [x] AI tools 问题归 `bugs/07-ai/process/`。
|
||||
- [x] 每个 bug 必须包含复现步骤、期望、实际、证据截图或 JSON、owner 判断。
|
||||
- [x] 先补最小 failing smoke 或定向单测,再修实现。
|
||||
- [x] 修复后移动到对应 `done/`,并记录验证命令。
|
||||
|
||||
重点 bug 方向:
|
||||
|
||||
- [x] Page Aggregate 与页头标题不一致。
|
||||
- [x] Page Aggregate 与 File Tree `{title}.md` 不一致。
|
||||
- [x] AI 写入成功但 Page Aggregate 回读旧内容。
|
||||
- [x] stale revision 未阻断写入。
|
||||
- [x] tree stream 断线恢复后 UI 停在旧快照。
|
||||
- [x] 旧 compat / debug route 在 3000 首屏被误用。
|
||||
|
||||
2026-05-18 定向 bug hunt 收口:
|
||||
|
||||
- `find bugs -path '*/process/*' -type f | wc -l` 结果为 `0`。
|
||||
- 本轮 P0 / 内核优先缺陷已迁入 `bugs/*/done/`,并在 `CURRENT_ARCHITECTURE.md`、`design/10-review/done/10-current-mnote-ai-runtime-review-v1.md`、`design/10-review/done/11-current-full-architecture-review-v1.md` 中同步验证证据。
|
||||
|
||||
### 3.6 2026-05-16 暂停交接进展摘要
|
||||
|
||||
本轮按用户要求暂停继续实现,只记录当前进展与恢复点;未继续推进 `3.2 Page Block AI Tooling` 的实现或 smoke。
|
||||
|
||||
已完成到可继续接力的阶段:
|
||||
|
||||
- `3.1 Page Aggregate 单一真源` 已完成本 checklist 中的当前验证项,并补齐真实 3000 smoke、Rust route 测试、前端单测和失败降级证据。关键证据包括 `tmp/page-aggregate-single-truth-smoke/20260516-182244/`、`tmp/page-aggregate-body-sync-smoke/mp87mgz7.json`、`tmp/page-aggregate-options-sync-smoke/mp87y6j3.json`、`tmp/page-aggregate-refresh-persistence-smoke/mp88fr6k.json`。
|
||||
- `3.3 Tree Command Cutover` 已完成当前收口:3000 主链使用 `tree.*` preferred command,`documents.*` 保留为 compat alias,并在 Rust execution plan / artifact 中标记 owner、preferredCommandName、compatCommandName、deprecatedAlias。
|
||||
- `3.4 Tree Realtime Live Cache` 已完成本轮重点验证与修复:snapshot/delta/resync payload 覆盖、双浏览器新建 / 归档 / 恢复 / purge、rename 当前文档 chrome 同步、move sortOrder live 排序、SSE resync 与 reconnect recovery。
|
||||
- 本轮新增或更新的 Tree Realtime 关键证据:
|
||||
- `tmp/tree-live-cache-smoke/20260516-task432/result.json`
|
||||
- `tmp/tree-live-cache-smoke/20260516-task446-rename/result.json`
|
||||
- `tmp/tree-live-cache-smoke/20260516-task447-move-order/result.json`
|
||||
- `tmp/tree-live-cache-smoke/20260516-task448-resync/result.json`
|
||||
- `tmp/tree-live-cache-smoke/20260516-task449-reconnect/result.json`
|
||||
- 本轮已归档的缺陷:
|
||||
- `bugs/04-tree-domain/done/4-41-tree-node-purge-command-protocol-leaks-to-convex-v1.md`
|
||||
- `bugs/04-tree-domain/done/4-42-tree-move-live-delta-sort-order-ignored-v1.md`
|
||||
- `bugs/05-editor-mainline/done/5-12-page-options-refresh-runtime-attrs-v1.md`
|
||||
- `bugs/05-editor-mainline/done/5-13-tree-rename-live-document-chrome-stale-v1.md`
|
||||
|
||||
当前暂停点:
|
||||
|
||||
- 已进入 `3.2 Page Block AI Tooling` 的只读审计阶段,但尚未勾选 3.2 的任何新 checkbox。
|
||||
- 已确认现有代码中 `mnote.doc.fetch` 已支持 `scope=selection`、`format=page_xml/text/markdown/json`、`schema=mnote.page_ai_context.v1`、`allowedTargetBlockIds`、truncation/warnings/continuation 等基础字段;`mnote.block.fetch` 已支持 `format=page_xml/text`;manifest 已包含 `readonly/destructive/idempotent/requiresApproval/approvalMode/runtimeOwner/writeOwner/selectionEffect` 等 annotations。
|
||||
- 已确认 `scripts/task-page-block-ai-tools-smoke.js` 覆盖基础块工具闭环,但还没有专门覆盖 `scope=selection`、`page_xml/text`、manifest annotations、选区外写入阻断、review session / conflict / idempotency 的完整验收。
|
||||
- 用户目标中提到的 `design/07-ai/process/7-11-blocknote-tiptap-ai-reference-and-mnote-ai-tool-runtime-v1.md` 当前在 `design/07-ai/process/` 下不存在;实际可读历史参考位于 `design/old/07-ai/process/7-11-blocknote-tiptap-ai-reference-and-mnote-ai-tool-runtime-v1.md`,当前执行口径应继续以 `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 为准。
|
||||
|
||||
恢复时的下一步建议:
|
||||
|
||||
- 继续从 `3.2 Page Block AI Tooling` 开始,不要跳到定向 bug hunt。
|
||||
- 优先新增一个聚焦 smoke,例如 `scripts/task-page-block-ai-context-format-smoke.js`,只验证基础底座,不扩新 AI 功能:
|
||||
- manifest annotations 中 `mnote.doc.fetch` / `mnote.block.fetch` / `mnote.block.replace` / `mnote.page.save` 的 readonly、destructive、requiresApproval、selectionEffect、owner 字段。
|
||||
- `mnote.doc.fetch scope=selection selectedBlockIds=[...] format=page_xml` 只返回选区块,返回 `schema=mnote.page_ai_context.v1`、`allowedTargetBlockIds`、`revisionRef`。
|
||||
- `mnote.doc.fetch scope=selection format=text` 不包含未选中块。
|
||||
- `mnote.block.fetch format=page_xml/text` 返回稳定块内容和 `revisionRef`。
|
||||
- 选区冻结后写工具尝试修改 `allowedTargetBlockIds` 外块时返回 `mnote_block_target_out_of_scope`。
|
||||
- 配套验证建议从这些命令开始:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools -- --nocapture
|
||||
node --check scripts/task-page-block-ai-context-format-smoke.js
|
||||
MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-context-format-smoke.js
|
||||
```
|
||||
|
||||
交接注意事项:
|
||||
|
||||
- 当前工作区有大量既有未提交改动和文件迁移,继续执行时必须只碰当前小项相关文件,不要回滚、删除或清理无关脏文件。
|
||||
- `git diff --check` 全仓可能仍会被既有删除文件阻断;验证本轮改动时可先用限定路径或 `git diff --no-index --check /dev/null <new-file>`。
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs` 中已有 Tree Realtime rename / move order 相关改动,同时可能混有先前 editor actor 相关改动;继续改动前必须重新读 diff,避免覆盖用户或前序 agent 的更改。
|
||||
- `3000` 相关 smoke 必须继续保留 JSON / 截图证据路径,并在本 checklist 与对应 `design/07-ai` 或 `bugs/*` 文档同步记录。
|
||||
|
||||
---
|
||||
|
||||
## 4. 当前不优先做
|
||||
|
||||
- [x] 不优先增加新的编辑器 UI 大功能。
|
||||
- [x] 不优先新增 AI Agent 工作流功能。
|
||||
- [x] 不优先扩展 AI 功能面;AI 只做基础上下文、工具合同、审阅会话、冲突与回滚。
|
||||
- [x] 不优先大规模替换 Convex。
|
||||
- [x] 不优先重写 `leptos-tiptap` 输入层。
|
||||
- [x] 不把 `mnote.page.save` 包装成精确块编辑长期方案。
|
||||
- [x] 不在 compat route 继续扩写长期业务语义。
|
||||
- [x] 不把 BlockNote / Tiptap AI runtime 作为 mnote runtime 依赖。
|
||||
- [x] 不把 `direct_block_edit_operations` / `local_rule` planner 当作当前或长期 AI 编辑路径;简单正文编辑当前主路径是 `mnote.doc.markdown_edit`。
|
||||
- [x] 不新增 `mnote.block.*` 工具;`mnote.block.*` 保留为结构性辅助,不做块操作向新工具的扩张。
|
||||
|
||||
2026-05-18 静态边界核验证据:
|
||||
|
||||
- `rg -n "mnote\\.block\\.[a-zA-Z0-9_]+" rust/crates/mnote-web/src/hermes_tools rust/crates/mnote-web/src/routes/hermes_tools.rs scripts/reasonix-acp-wrapper.mjs | ... | sort -u` 仅发现既有 `mnote.block.fetch/delete/replace/insert_after/move_after`,本轮未新增 `mnote.block.*` 工具。
|
||||
- `page_ai_workflow.rs` 注释与当前实现仍指向 `mnote.doc.markdown_edit`;`direct_block_edit_operations` 只保留为历史/测试辅助,不作为当前主路径。
|
||||
- `mnote.page.save` 在 manifest / 7-10 / 7-12 / 本 checklist 中继续描述为页面级粗粒度兜底,不是精确块编辑主入口。
|
||||
- 未引入 `@blocknote/xl-ai`、BlockNote `AIExtension` 或新的 AI Agent workflow;本轮改动仅限 Hermes tools 合同、阻断矩阵测试和 checklist 证据。
|
||||
|
||||
2026-05-18 `7-12` runtime 口径闭合证据:
|
||||
|
||||
- `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md` 已明确 `7-11` 旧“自有 AI runtime”口径移入 `design/old/07-ai/process/`,当前执行口径以 `7-12` 为准。
|
||||
- `7-12` 已定义 `PageAIContextBuilder`、`MnoteAIToolManifestProvider`、`PageAICommandRouter`、`PageAIReviewSession` 的边界,并明确 Phase C review / streaming apply 只做设计冻结,当前不实施。
|
||||
- `7-12` Phase B 到 Phase F 保留 manifest / router / review session / event / smoke 的后续 checklist;其中 selection 外写入 blocked 已有真实 smoke 证据,其余未实施项继续保留未勾。
|
||||
|
||||
2026-05-18 `7-10` 剩余真实页面矩阵拆分:
|
||||
|
||||
- 新增 `design/07-ai/process/7-16-page-block-ai-real-smoke-followup-matrix-v1.md`,专门承接 `7-10` 中仍未完成的真实 3000 smoke 和 UI/review 边界矩阵。
|
||||
- `7-10` 继续保留为页面块 AI 工具执行总 checklist;`7-16` 只用于后续真实页面证据补齐,不扩新 AI 功能。
|
||||
|
||||
2026-05-18 真实页面 insert_after smoke 补充:
|
||||
|
||||
- `scripts/task-page-block-ai-tools-smoke.js` 增加 `PLAYWRIGHT_CHROME_EXECUTABLE` 支持,用系统 Chromium 运行,避免当前系统无法下载 Playwright 官方 chromium 的阻断。
|
||||
- 命令:`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
|
||||
- 证据:`tmp/page-block-ai-tools-smoke/mpag4966.json`,截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
|
||||
- Page Aggregate:`tmp/hermes-tester/page-block-ai-tools-mpag4966/page-aggregate.json`,doc/fetch/find:`tmp/hermes-tester/page-block-ai-tools-mpag4966/doc-fetch-find.json`。
|
||||
- 覆盖:`mnote.block.insert_after` 插入 `todo` 块、插入顺序、`mnote.block.fetch` 回读新块、重复 `idempotencyKey` 不二次插入、刷新后文本可见。
|
||||
- 覆盖:`table_1/mindmap_1/resource_1` 在 AI block projection 中返回 `editable=false` 与 `unsupportedReason=复杂块暂不开放 AI 精确写入`;`heading_parent/list_item_1/table_1/mindmap_1/resource_1` 的 `plan_update block_move_after` dry-run 与正式 `mnote.block.move_after` 均阻断,阻断后 revision / 正文不变化。
|
||||
|
||||
2026-05-18 真实页面 doc.fetch / doc.find / block.fetch smoke 补充:
|
||||
|
||||
- 命令:`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
|
||||
- 证据:`tmp/page-block-ai-tools-smoke/mpag4966.json`,截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
|
||||
- 覆盖:`doc.fetch full`、`doc.fetch outline`、`doc.find` 定位 `p_2`、`maxBlocks=2` 截断、`block.fetch includeChildren=true contextBefore=1 contextAfter=1` 返回 `revisionRef` 与同父级 before/after;复杂块投影不伪装完全可编辑。
|
||||
|
||||
2026-05-18 真实页面 plan_update / replace / move smoke 补充:
|
||||
|
||||
- 命令:`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
|
||||
- 证据:`tmp/page-block-ai-tools-smoke/mpag4966.json`,截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`。
|
||||
- 覆盖:`block_replace/block_insert_after/block_move_after` dry-run、dry-run 不写入、blocked 场景、replace 相邻块不变与 id 保持、replace 后 Page Aggregate 回读持久化、复杂块 move 阻断、普通块 move dry-run 与正式移动、moving block id 保持、刷新后 block ids 稳定。
|
||||
|
||||
---
|
||||
|
||||
## 5. 完成定义
|
||||
|
||||
本 checklist 不能迁入 `done/`,直到:
|
||||
|
||||
- [x] Page Aggregate 的标题 / 正文 / 页面设置 / page tree / AI fetch 回读形成同一组可验证真相。
|
||||
- [x] `7-10` 的 Page Block AI Tooling 剩余矩阵完成或明确拆出后续 process 文档。
|
||||
- [x] `7-12` 的 AI 基础 runtime 口径完成:context、format、manifest annotations、review session、状态机边界都有代码或明确后续 checklist。
|
||||
- [x] 7-14 v2 Phase A + B 当前主路径完成(`mnote.doc.markdown_edit` + `mnote.doc.fetch` 增强 + `page_ai_workflow.rs` 收口);Phase C 设计已冻结。设计稿修正已由 7-14 v2 与 `7-18` 到 `7-25` bug 修复共同完成。
|
||||
- [x] `tree.*` command 面对主要页面生命周期动作成为唯一 preferred runtime 主链。
|
||||
- [x] tree realtime live cache 覆盖 Sidebar、Page Tree、File Tree、page subtree 的关键写后更新。
|
||||
- [x] 至少一轮定向 bug hunt 完成,所有 P0/P1 blocker 已归档到 `bugs/*/done/` 或明确保留为后续 process。
|
||||
|
||||
---
|
||||
|
||||
## 6. 给后续 /goal 的持续执行 Prompt
|
||||
|
||||
```text
|
||||
/goal objective: 持续核验 /mnt/Data1T/mnote/design/10-review/done/08-kernel-architecture-next-priority-review-and-checklist.md 的已归档结论,按 P0 -> P1 顺序推进 MNOTE 架构防回归、AI 基础底座和定向 bug hunt。每轮开始先读取 /home/lix/.codex/memories/PROFILE.md 与 ACTIVE.md,再读取 AGENTS.md、ARCHITECTURE.md、design/01-05-current-priority-overview.md、本 checklist、design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md、design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md、design/old/07-ai/process/7-14-local-first-ai-markdown-editing-convergence-v1.md。必须保护用户已有未提交改动,不回滚、不覆盖、不删除无关文件。优先使用多个 subagent 并行做只读审查和浏览器验证,主线程只整合证据和做小范围实现。执行顺序固定为:1) Page Aggregate 单一真源防回归;2) tree command cutover 防回归;3) tree realtime live cache 防回归;4) AI 基础工具与审阅底座,当前简单正文编辑主路径已是 `mnote.doc.markdown_edit` + `mnote.doc.fetch` + `page_ai_workflow.rs` 统一 tool executor,Phase C(流式 apply + suggest/review)仅设计冻结不实施;继续补 context/selection/page_xml/tool annotations,不扩新 AI 功能;5) 定向 bug hunt。每完成一个小项都要更新对应 design/bugs 文档,补真实验证命令或证据路径。不要优先扩新功能,不要大改架构,不要把 compat/debug/fallback 当主链,不要把 BlockNote/Tiptap AI runtime 作为 mnote runtime 依赖。验证至少包含 scoped git diff --check、相关 cargo test / smoke;如涉及 3000 页面,使用 mnote-tester 或浏览器自动化并保留截图/JSON 证据。
|
||||
```
|
||||
@@ -0,0 +1,270 @@
|
||||
# [recycle] 09 页面 AI 快速块编辑 Runtime Review
|
||||
|
||||
> 状态:`done`
|
||||
>
|
||||
> 日期:2026-05-16
|
||||
>
|
||||
> 关联主线:
|
||||
> - `design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
|
||||
> - `design/07-ai/reference/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
|
||||
> - `design/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md`
|
||||
> - `design/05-editor-mainline/done/5-13-page-block-identity-and-command-contract-v1.md`
|
||||
|
||||
---
|
||||
|
||||
## 0. 归档说明
|
||||
|
||||
本文件是 2026-05-16 的页面 AI fast block edit 历史审查快照。其记录的 `local_rule` / `doc_apply_block_ops` 快路径曾被后续 `mnote.doc.markdown_edit` 阶段替代,但该阶段也已被 local-first agent 文件编辑控制面覆盖;本文不再作为当前 runtime 口径。
|
||||
|
||||
2026-06-01 当前有效口径:local-first 普通 Markdown 编辑默认走授权文件引用 + allowed roots/files + agent 原生 patch/diff + watcher/BufferStore/Page Aggregate 同步;`mnote.doc.markdown_edit` 只保留为 remote/cloud/compat fallback 或结构校验辅助。
|
||||
|
||||
当前有效口径见:
|
||||
|
||||
- [10-current-mnote-ai-runtime-review-v1](./10-current-mnote-ai-runtime-review-v1.md)
|
||||
- [11-current-full-architecture-review-v1](./11-current-full-architecture-review-v1.md)
|
||||
- [7-18 AI markdown_edit 阶段状态合同漂移](../../../bugs/07-ai/done/7-18-ai-markdown-edit-phase-state-contract-drift-v1.md)
|
||||
- [7-20 page_ai_workflow 绕过 Hermes tool executor / audit / toggle](../../../bugs/07-ai/done/7-20-page-ai-workflow-bypasses-hermes-tool-executor-v1.md)
|
||||
|
||||
归档时的历史结论:`page-ai/block-edit-workflow` 仍是简单编辑 fast-path 入口,但当时实现应通过模型生成 markdown search/replace / full_content 后调用 `mnote.doc.markdown_edit`,并复用统一 mnote tool executor;`mnote.doc.apply_block_ops` / `mnote.block.*` 只作为结构性块操作辅助。该结论不指导当前 local-first 新实现。
|
||||
|
||||
## 1. 本轮结论
|
||||
|
||||
页面 AI 块写入的慢点不在 Rust 块工具本身,也不在 Convex 持久化本身。
|
||||
|
||||
本轮真实浏览器 smoke 显示:
|
||||
|
||||
- 旧路径:页面 AI 先调用 `/api/page-ai/block-edit-workflow`,快路径模型阶段耗时约 `7695ms`,随后因模型输出的块定位未命中 projection,`doc_apply_block_ops` 返回 `mnote_block_not_found`,前端继续 fallback 到 `/api/hermes/client/runs`,最终由 Hermes agent 调 `mnote.doc.apply_block_ops` 写入,总可见耗时约 `13246ms`。
|
||||
- 新路径:同一类简单中文块操作先由 mnote 本地 planner 解析为 `replace/insert_after/delete` operations,再直接调用 Rust `doc_apply_block_ops`。浏览器 smoke 回读可见耗时 `788ms`;后端日志显示 `operation_source=local_rule`、`model_ms=0`、`apply_ms=42`、`total_ms=42`,且未进入 `/api/hermes/client/runs`。
|
||||
|
||||
因此,当前要继续做的是 Hermes 工具路由、上下文冻结、dry-run/review 与 Rust 写入校验基建,而不是继续让 Hermes agent 反复执行同一个端到端动作,也不是在 mnote 内新建第二套 AI runtime。
|
||||
|
||||
---
|
||||
|
||||
## 2. 慢的直接原因
|
||||
|
||||
### 2.1 通用 agent 编排链路太长
|
||||
|
||||
旧页面 AI 编辑链路实际包含:
|
||||
|
||||
```text
|
||||
浏览器输入
|
||||
-> Hermes session/profile/tools 初始化
|
||||
-> 模型理解页面任务
|
||||
-> 模型选择工具
|
||||
-> 工具调用
|
||||
-> 回读或继续推理
|
||||
-> 最终回复
|
||||
```
|
||||
|
||||
这适合复杂任务,但不适合“把 A 替换为 B / 在 A 后插入 C / 删除 D”这类明确块操作。
|
||||
|
||||
### 2.2 模型直接产 operations 不够可靠
|
||||
|
||||
本轮快路径第一版已经绕开 Hermes agent,但仍让 DeepSeek 直接根据 `page_xml/page_text` 生成 operations。失败点是:
|
||||
|
||||
```text
|
||||
model_ms ~= 7695
|
||||
operations = 3
|
||||
apply -> mnote_block_not_found
|
||||
fallback -> Hermes agent
|
||||
```
|
||||
|
||||
说明模型输出了看似正确的操作数,但 block 定位信息没有命中当前 Page Aggregate projection。这个问题不能靠增加 smoke 次数解决,必须让 mnote runtime 在模型前后都掌握定位与校验。
|
||||
|
||||
### 2.3 失败后 fallback 放大了耗时
|
||||
|
||||
第一版前端逻辑在快路径失败时继续进入 `/api/hermes/client/runs`。这导致一次用户请求可能经历:
|
||||
|
||||
```text
|
||||
快路径模型失败成本 + Hermes agent 成功成本
|
||||
```
|
||||
|
||||
本轮已改为:只有后端明确返回 `page_ai_workflow_not_block_edit` 才允许 fallback;其他快路径错误直接在本轮 run 中失败展示,避免重复写入和重复等待。
|
||||
|
||||
---
|
||||
|
||||
## 3. 已完成的修正
|
||||
|
||||
### 3.1 页面 AI 快速块编辑 route
|
||||
|
||||
已新增并接入:
|
||||
|
||||
- `POST /api/page-ai/block-edit-workflow`
|
||||
- 文件:`rust/crates/mnote-web/src/routes/page_ai_workflow.rs`
|
||||
|
||||
该 route 负责:
|
||||
|
||||
- 接收当前页面冻结后的 `mnote.page_ai_context.v1`。
|
||||
- 为明确中文编辑语句先走本地 operation planner。
|
||||
- 无法本地解析时才调用小模型生成 operations。
|
||||
- 最终统一走 `mnote.doc.apply_block_ops`,由 Rust 生成 canonical content 并保存。
|
||||
|
||||
### 3.2 简单块操作本地 planner
|
||||
|
||||
已支持这类明确表达:
|
||||
|
||||
```text
|
||||
把「A」替换为「B」;
|
||||
在「C」后插入「D」;
|
||||
删除「E」。
|
||||
```
|
||||
|
||||
输出直接是:
|
||||
|
||||
```json
|
||||
[
|
||||
{"op":"replace","matchText":"A","content":"B"},
|
||||
{"op":"insert_after","matchText":"C","content":"D"},
|
||||
{"op":"delete","matchText":"E"}
|
||||
]
|
||||
```
|
||||
|
||||
该层的目的不是做通用自然语言理解,而是把高频、低歧义、可确定的块编辑从模型路径剥离出来。
|
||||
|
||||
### 3.3 快路径可观测性
|
||||
|
||||
后端日志已记录:
|
||||
|
||||
- workflow started/completed
|
||||
- operation source:`local_rule` 或 `model`
|
||||
- operations 数量
|
||||
- model 耗时
|
||||
- apply 耗时
|
||||
- total 耗时
|
||||
|
||||
smoke 也已在失败时写出 evidence JSON,并记录 `/api/page-ai/*` 与 `/api/hermes/client/*` 请求/响应。
|
||||
|
||||
---
|
||||
|
||||
## 4. 验证证据
|
||||
|
||||
命令:
|
||||
|
||||
```bash
|
||||
cargo test -p mnote-web page_ai_workflow -- --nocapture
|
||||
cargo test -p mnote-web hermes_tools -- --nocapture
|
||||
MNOTE_PAGE_AI_FAST_TIMEOUT_MS=60000 node scripts/task-page-ai-block-edit-workflow-smoke.js
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `page_ai_workflow`:2 passed。
|
||||
- `hermes_tools`:22 passed。
|
||||
- 浏览器 smoke:通过。
|
||||
|
||||
最新浏览器 evidence:
|
||||
|
||||
```text
|
||||
/mnt/Data1T/mnote/tmp/page-ai-block-edit-workflow-smoke/mp86uciu.json
|
||||
```
|
||||
|
||||
关键值:
|
||||
|
||||
```json
|
||||
{
|
||||
"timingsMs": {
|
||||
"pageAiWriteVisible": 788
|
||||
},
|
||||
"usedFastWorkflow": true,
|
||||
"usedHermesRun": false,
|
||||
"finalTexts": [
|
||||
"第一段 mp86uciu",
|
||||
"插入段 mp86uciu",
|
||||
"第二段已修改 mp86uciu"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
后端关键日志:
|
||||
|
||||
```text
|
||||
operation_source="local_rule"
|
||||
model_ms=0
|
||||
apply_ms=42
|
||||
total_ms=42
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 剩余问题
|
||||
|
||||
### 5.1 不能把本地 planner 当成完整 AI runtime
|
||||
|
||||
当前本地 planner 只覆盖低歧义中文引号表达。它证明了正确的 runtime 方向,但不是最终答案。
|
||||
|
||||
需要继续建设:
|
||||
|
||||
- PageAIContextBuilder:冻结 selection/page context,减少模型输入。
|
||||
- PageAIIntentParser:先判定是明确块操作、结构化改写、摘要问答、还是复杂编辑。
|
||||
- PageAIOperationPlanner:把明确操作转成 `mnote.doc.apply_block_ops`,复杂操作才调用小模型。
|
||||
- PageAIOperationValidator:模型输出后必须用 projection 校验 blockId/matchText/allowedTargetBlockIds。
|
||||
- PageAIApplyController:统一处理 yolo 写入、失败展示、回读验证、审计日志。
|
||||
|
||||
### 5.2 模型输出 operations 仍需修
|
||||
|
||||
当请求不能被本地 planner 解析时,仍会调用模型。该路径必须补:
|
||||
|
||||
- 输出 schema 更严格,禁止模型臆造 blockId。
|
||||
- 优先 `matchText` 或由服务端根据 text resolve block,而不是信任模型 blockId。
|
||||
- 模型输出后做 dry-run validate,不命中时不要 fallback Hermes agent 重新跑。
|
||||
- 把 validation error 反馈给用户或进入后续 clarify/retry,而不是隐式整页写。
|
||||
|
||||
### 5.3 Hermes agent 不应承担短路径编辑
|
||||
|
||||
Hermes 仍适合:
|
||||
|
||||
- 多步骤页面理解。
|
||||
- 跨页面检索。
|
||||
- 工具不可直接表达的复杂任务。
|
||||
- 外部 skill/plugin 编排。
|
||||
|
||||
但对当前页面小段落块增删改,mnote 自己的 runtime 应该在浏览器/Rust route 内完成 `intent -> operations -> apply -> readback`。
|
||||
|
||||
---
|
||||
|
||||
## 6. 下一步建议
|
||||
|
||||
优先级应从“继续 smoke”切到“补 AI runtime 基建”:
|
||||
|
||||
1. `PageAIIntentParser`
|
||||
- 输入:用户 prompt、scope、selection、page context。
|
||||
- 输出:`direct_block_ops | model_block_ops | question | unsupported`。
|
||||
- 目标:不让每条简单编辑都进入 Hermes agent。
|
||||
|
||||
2. `PageAIOperationPlanner`
|
||||
- 扩展当前本地 planner。
|
||||
- 支持常见中文/英文明确表达。
|
||||
- 支持选区内“改成/润色/拆成列表”等可控操作。
|
||||
|
||||
3. `PageAIOperationValidator`
|
||||
- 所有 operations apply 前先 resolve projection。
|
||||
- 对 blockId、matchText、allowedTargetBlockIds、editable、children、revisionRef 做统一校验。
|
||||
- 失败返回结构化错误,不 fallback 通用 agent。
|
||||
|
||||
4. `PageAIApplyController`
|
||||
- 统一 yolo 模式下的写入、回读、状态展示和失败提示。
|
||||
- 后续再接 preview/review session,而不是现在把 review 作为默认阻塞。
|
||||
|
||||
5. 模型路径瘦身
|
||||
- 对必须调用小模型的任务,发送 `page_xml/text + allowed operations schema`。
|
||||
- 模型只负责生成候选 operations;最终定位、校验和写入仍由 Rust runtime 负责。
|
||||
|
||||
---
|
||||
|
||||
## 7. 当前判断
|
||||
|
||||
小段落编辑低于 10s 已经被证明可达,且当前 smoke 为 `788ms`。
|
||||
|
||||
下一阶段的关键不是再证明“能写”,而是把这条快路径产品化:
|
||||
|
||||
```text
|
||||
用户意图
|
||||
-> mnote intent/parser
|
||||
-> operation planner
|
||||
-> projection validator
|
||||
-> Rust apply
|
||||
-> readback
|
||||
-> UI 状态/审计
|
||||
```
|
||||
|
||||
Hermes agent 应从默认编辑执行器退回到复杂任务编排器。
|
||||
@@ -0,0 +1,100 @@
|
||||
# [recycle] 10 [done] 当前 mnote 项目 AI / Page Aggregate 定向 Review v1
|
||||
|
||||
> 更新时间:2026-05-17
|
||||
>
|
||||
> 执行状态:`done`
|
||||
>
|
||||
> 范围:当前主线中 Page Aggregate 单一真源、页面 AI 快速编辑、`mnote.doc.markdown_edit` 与 ACP / Hermes runtime 的源码级定向审查。
|
||||
>
|
||||
> 2026-06-01 口径补充,2026-06-06 收紧:本文是 2026-05-17 的历史 review 快照。文中“`mnote.doc.markdown_edit` 是简单正文编辑主路径”的结论已被后续 local-first agent 文件编辑控制面覆盖;当前 local-first 普通 Markdown 编辑主路径以 `AGENTS.md`、`ARCHITECTURE.md` 与 `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 为准,即授权文件引用 + allowed roots/files + agent 原生 patch/diff + watcher/BufferStore/Page Aggregate 同步。`mnote.doc.markdown_edit` 只保留为历史 online/cloud/compat 回归和结构校验对照,不作为 local-first 新 agent run 的推荐 fallback。
|
||||
|
||||
## 1. 本轮结论
|
||||
|
||||
本轮没有发现 Page Aggregate 读取主链重新回退到 Next compat builder 的证据。后续修复已进一步收口:AI context 的 page subtree 只读 Rust Page Aggregate projection,不再由前端本地构造第二份 page tree 真相。
|
||||
|
||||
本轮识别出的 `07-ai` markdown 编辑 P0 风险已修复:
|
||||
|
||||
- `mnote.doc.markdown_edit` 是简单正文编辑主路径,并被 `/api/page-ai/block-edit-workflow` 通过统一 mnote tool executor 调用。
|
||||
- 中文归一化匹配不再把 UTF-8 byte index 当 char index。
|
||||
- 同一块内多次 markdown_edit 会合并到最终 markdown 结果后再映射写回,避免后续 block op 覆盖前序修改。
|
||||
- 在线写回在无法安全映射最终 markdown 时明确拒绝,避免 `ok=true` 伪成功。
|
||||
- Hermes guidance、manifest、tool toggle、dryRun / idempotency、revision / conflictDetectionKey 与 Reasonix ACP payload 已同步到当前工具合同。
|
||||
|
||||
剩余长期方向仍是 Phase C review session / streaming apply;当前设计冻结,不属于本轮 bug 修复范围。
|
||||
|
||||
## 2. 发现的问题
|
||||
|
||||
### R-01 P0:中文归一化匹配使用 byte index 当 char index
|
||||
|
||||
关联缺陷:
|
||||
|
||||
- `bugs/07-ai/done/7-16-markdown-edit-normalized-search-byte-index-v1.md`
|
||||
|
||||
证据:
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/doc.rs:920-936`
|
||||
|
||||
`search_replace` 的 Level 2 先在 `norm_line` 上用 `find()` 得到 UTF-8 byte offset,却把这个 offset 传给 `line.char_indices().nth(start)` 当作字符序号使用。中文、多字节标点或中英混排时,替换范围会偏移,可能替错片段或生成错误内容。
|
||||
|
||||
影响:
|
||||
|
||||
- 页面 AI 对中文正文的“忽略空白 / 全半角差异”替换不可靠。
|
||||
- 该问题位于 `mnote.doc.markdown_edit` 的基础 search/replace 层,会同时影响在线文档和本地 `.md` 文件。
|
||||
|
||||
### R-02 P0:同一块内多次 markdown_edit 会被后续 block op 覆盖
|
||||
|
||||
关联缺陷:
|
||||
|
||||
- `bugs/07-ai/done/7-17-markdown-edit-same-block-multi-op-overwrite-v1.md`
|
||||
|
||||
证据:
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/doc.rs:779-824`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/doc.rs:854-878`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/doc.rs:976-1008`
|
||||
|
||||
`doc_markdown_edit` 已经在 `md` 变量中顺序应用了所有 search/replace,但 Convex 写入时没有使用这个最终 markdown。它重新读取原始 Page Aggregate blocks,并对每个 operation 用原始 block 文本生成一个 replace block op。
|
||||
|
||||
如果两个 operation 命中同一个 block,第二个 block op 的 `content` 仍从原始文本计算,会覆盖第一个 operation 的结果。也就是说,markdown 层算出的最终结果与实际落库结果可能不一致。
|
||||
|
||||
影响:
|
||||
|
||||
- AI 一次请求中常见的“把同一段里的 A 改成 B,同时把 C 改成 D”可能只保留最后一次修改。
|
||||
- 前端快路径仍可能显示“已通过页面 markdown 编辑快路径完成写入”,但正文只部分生效。
|
||||
|
||||
## 3. 修复状态
|
||||
|
||||
- `mnote.doc.markdown_edit` manifest 已补齐 required / write contract。
|
||||
- 本地 `.md` 写入已尊重 dryRun / idempotency。
|
||||
- 在线写回已使用最终 markdown 作为真源进行映射;无法安全映射时返回明确错误。
|
||||
- `mnote.doc.apply_block_ops` 批量写入已补 revision / conflictDetectionKey 前置校验。
|
||||
- `/api/page-ai/block-edit-workflow` 已从绕过 executor 改为复用统一 Hermes mnote tool executor。
|
||||
- ACP Reasonix wrapper 已透传 actor/session/run/toolCall/trace/dryRun/idempotency/workspace/document 等上下文字段。
|
||||
|
||||
## 4. 后续边界
|
||||
|
||||
1. Phase C 的 review session / streaming apply 仍冻结,当前不扩新 AI 功能。
|
||||
2. 复杂结构编辑继续走 `apply_block_ops` / `mnote.block.*`,不要把正文 search/replace 回退到结构性块工具。
|
||||
3. Page Aggregate 仍需后续推进 EditorBlockDocument 原生落库真相。
|
||||
|
||||
## 5. 本轮验证
|
||||
|
||||
静态检查范围:
|
||||
|
||||
```bash
|
||||
rg -n "markdown_edit|apply_block_ops|block-edit-workflow|local_rule|page_ai_workflow|mnote\\.doc\\.fetch" rust wolai-frontend scripts design/07-ai design/10-review --glob '!rust/target/**' --glob '!node_modules/**'
|
||||
rg -n "page-aggregate|pageAggregate|PageAggregate|blockDocument|projectionSource|documents/save|documents/options|/api/page-aggregate" rust/crates/mnote-web rust/crates/bridge-runtime wolai-frontend/src scripts --glob '!rust/target/**' --glob '!node_modules/**'
|
||||
```
|
||||
|
||||
修复后定向验证:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_workflow -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_client -- --nocapture
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_session_manager -- --nocapture
|
||||
node --check scripts/reasonix-acp-wrapper.mjs
|
||||
MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs
|
||||
```
|
||||
|
||||
结果:上述验证均通过。
|
||||
@@ -0,0 +1,55 @@
|
||||
# [recycle] 11 [done] 当前完整架构 Review v1
|
||||
|
||||
> 更新时间:2026-05-17
|
||||
>
|
||||
> 状态:`done`
|
||||
>
|
||||
> 参考总文档:[CURRENT_ARCHITECTURE.md](../../../CURRENT_ARCHITECTURE.md)
|
||||
|
||||
## 1. 结论
|
||||
|
||||
> 2026-05-31 口径补充:本文是 2026-05-17 的阶段性 review 快照。文中关于 `mnote.doc.markdown_edit` “已是简单正文编辑主路径”的表述已被后续 local-first agent 文件编辑控制面覆盖;当前 local-first 普通 Markdown 编辑主路径以 `AGENTS.md`、`CURRENT_ARCHITECTURE.md`、`ARCHITECTURE.md` 与 `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 为准,即“授权文件引用 + allowed roots + agent 原生 patch/diff + watcher/BufferStore/Page Aggregate 同步”。`mnote.doc.markdown_edit` 只作为 cloud / remote agent / compat fallback 或复杂结构辅助边界。
|
||||
|
||||
当前 mnote 的主线架构已经成形。本轮 review 识别出的 P0 / 内核优先缺陷已完成代码修复、文档迁移和定向验证。
|
||||
|
||||
已修复的四个关键冲突:
|
||||
|
||||
1. Tree realtime 的发布口径与前端消费口径不一致。
|
||||
2. FileTree 资源行误走页面命令。
|
||||
3. Page Aggregate / AI context 保留前端第二真相。
|
||||
4. AI 写入链路在 `markdown_edit`、`apply_block_ops`、Hermes / ACP / Reasonix 之间的合同漂移。
|
||||
|
||||
仍需后续主线继续推进的不是本轮 P0 bug,而是长期架构收口:Page Aggregate 原生 EditorBlockDocument 落库、正文保存主命令面、`tree.resource.*` 资源生命周期和兼容路由瘦身。
|
||||
|
||||
## 2. 关键判断
|
||||
|
||||
- Rust kernel 与 bridge-runtime 已经承担语义主导权。
|
||||
- Rust SSR 主壳已默认以 `/api/realtime/ws` 为实时主链,SSE 是 fallback。
|
||||
- FileTree 资源行的 owner document 与页面命令目标已分离。
|
||||
- AI context 的 page subtree 已只读 Rust Page Aggregate projection。
|
||||
- `mnote.doc.markdown_edit` 已是简单正文编辑主路径,`page_ai_workflow` 复用统一 mnote tool executor,`apply_block_ops` / `mnote.block.*` 仅作为结构性辅助。
|
||||
- ACP / Reasonix 的 mnote tool payload、response、run context 与幂等字段已同步到当前工具合同。
|
||||
|
||||
## 3. 已闭合 bug
|
||||
|
||||
- Rust Web / realtime / ACP:`bugs/03-rust-web/done/3-16` 到 `3-21`
|
||||
- Tree domain:`bugs/04-tree-domain/done/4-46`
|
||||
- Editor mainline:`bugs/05-editor-mainline/done/5-15` 到 `5-18`
|
||||
- AI:`bugs/07-ai/done/7-18` 到 `7-25`
|
||||
|
||||
## 4. 验证
|
||||
|
||||
- `find bugs -path '*/process/*' -type f | wc -l`
|
||||
- 结果:`0`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web markdown_edit -- --nocapture`
|
||||
- 结果:9 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_workflow -- --nocapture`
|
||||
- 结果:3 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web acp_client -- --nocapture`
|
||||
- 结果:6 passed
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web ssr::pages::layout::tests -- --nocapture`
|
||||
- 结果:14 passed
|
||||
|
||||
## 5. 备注
|
||||
|
||||
本次审查的详细架构说明已统一写入根目录:[CURRENT_ARCHITECTURE.md](../../../CURRENT_ARCHITECTURE.md)。
|
||||
@@ -0,0 +1,131 @@
|
||||
# [recycle] 15 Design Governance MVP 后阶段 Review v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 归档说明(2026-05-21):四个 Reasonix worker 审查、Codex 复核、done 归档与 reference 迁移已完成;本文归档为本轮 design governance 事实源。
|
||||
>
|
||||
> 创建时间:2026-05-21
|
||||
>
|
||||
> 目标:在 local-first MVP 已初步完成、架构方向基本稳定后,统一 `design/` 目录口径,归档过时 process 文档,并明确仍需继续执行的 process 文档。
|
||||
|
||||
## 1. 当前统一口径
|
||||
|
||||
MNote 当前已进入 local-first MVP 后阶段:
|
||||
|
||||
- 本地 workspace folder 是默认数据真相。
|
||||
- 本地 `.md` 是页面正文真相。
|
||||
- Rust kernel / projection / command 持有树、页面、资源和权限语义。
|
||||
- `mnote-web` 是 3000 主 Web 执行面。
|
||||
- Rust SQLite control-plane 承接 auth、membership、share grants、sync state、AI policy、ACP/Hermes runtime session 等默认控制面;Convex / 服务端仅保留历史迁移源、显式 cloud source、compat 和 sync replica 边界。
|
||||
- 旧 Next / React / wolai-frontend、BlockNote 默认主链、Convex 默认正文/附件/AI 会话全文主存储口径均为 legacy / recycle / compat 背景。
|
||||
|
||||
## 2. 本轮整理原则
|
||||
|
||||
1. 仍代表长期方向或当前执行入口的文档保留在 `process/`,但必须叠加 MVP 后阶段口径。
|
||||
2. 已完成、已有真实代码或 smoke 证据的执行清单移动到同域 `done/`,并补充归档说明。
|
||||
3. 被后续文档覆盖、但不代表已完成的旧稿移动到 `design/old/<domain>/process/`,标题或开头标记 `[recycle]`。
|
||||
4. 参考资料、外部产品取证、长期方向说明可以保留在 `process/`,但需要明确“不作为当前执行 checklist”。
|
||||
5. 不把旧 Convex / Next / BlockNote 说法改写成当前主线;若保留,必须标注为历史快照或反例。
|
||||
|
||||
## 3. 并行审查切片
|
||||
|
||||
- Worker A:`01` / `02` / `03`,重点判断 long-term 架构稿和 Rust Web process 是否仍应继续执行。
|
||||
- Worker B:`04-tree-domain`,重点判断 file tree / resource lifecycle / trash / open target 相关 process 是否已由近期 bug/smoke 完成。
|
||||
- Worker C:`05-editor-mainline` / `06-mindmap` / `08-wolai` / `09-siyuan`,重点判断编辑器 tab、OnlyOffice、mindmap 和对标流程文档是否仍活跃。
|
||||
- Worker D:`07-ai` / `10-review`,重点判断 AI process 与 review process 是否已被后续 Hermes/ACP/local-first 文件编辑口径覆盖。
|
||||
|
||||
## 4. Codex 归档判定状态
|
||||
|
||||
本轮使用以下状态:
|
||||
|
||||
- `keep-process`:继续留在 `process/`,后续仍需执行。
|
||||
- `mark-reference`:留在 `reference/`,但只作为长期方向、流程或参考基线,不作为当前直接执行 checklist。
|
||||
- `archive-done`:移动到同域 `done/`,因为目标已完成或当前代码已覆盖。
|
||||
- `archive-old-process`:移动到 `design/old/<domain>/process/` 并标 `[recycle]`,因为被后续稿覆盖或口径过时但未必完成。
|
||||
- `needs-human-confirmation`:证据不足,暂不移动。
|
||||
|
||||
## 5. 初步本地发现
|
||||
|
||||
- `design/01-tree-first-graph-kernel/process/1-1-*` 与 `design/03-rust-web/process/3-1-*` 已自述为全景参考,当前优先级应由 `01-05` 和后续 MVP 后阶段 checklist 驱动。
|
||||
- `design/03-rust-web/process/3-15-*` 已写明代码、单测和 HTTP smoke 完成,仅真实浏览器 smoke 待补;需要核对是否已有后续真实浏览器证据。
|
||||
- `design/04-tree-domain/process/4-38` 到 `4-45` 是 2026-05-20 之后针对资源生命周期、open target、下载、拖拽上传等 bug 的近期 checklist,可能有部分已经由当前未提交 smoke / bug done 覆盖。
|
||||
- `design/05-editor-mainline/process/5-15` 到 `5-26` 初始盘点时大多是近期 main editor tab/resource open/OnlyOffice 收口 checklist;本轮已按真实代码和 smoke 证据逐项判定,`5-15` 到 `5-25` 中除继续执行项外已归档到 `done/`,`5-26` 继续保留为 active process。
|
||||
- `design/07-ai/process/7-15` 已包含大量完成记录,但仍有 HTTP proxy compat、session resume、permission loop、tool location 等后续切片,不能仅凭完成项归档。
|
||||
|
||||
## 6. 本轮不做事项
|
||||
|
||||
- 不改 `design/11-wolai/` 和 `design/90-reference/` 的参考资料归类。
|
||||
- 不删除历史文档。
|
||||
- 不把 `old/` 下历史稿移动回主线。
|
||||
- 不改代码,不跑浏览器 smoke;本轮只做设计文档治理。
|
||||
|
||||
## 7. Reasonix 并行审查结果
|
||||
|
||||
本轮已派发四个只读 Reasonix worker,报告路径如下:
|
||||
|
||||
- Worker A(01/02/03):`.codex/reasonix-tasks/results/design-governance-worker-a-01-03.md`
|
||||
- Worker B(04 tree-domain):`.codex/reasonix-tasks/results/design-governance-worker-b-04-tree.md`
|
||||
- Worker C(05/06/08/09):`.codex/reasonix-tasks/results/design-governance-worker-c-05-06-08-09.md`
|
||||
- Worker D(07/10):`.codex/reasonix-tasks/results/design-governance-worker-d-07-10.md`
|
||||
|
||||
Codex 已复核 worker 输出,并额外核对 `3-15` 的真实浏览器 smoke 证据。
|
||||
|
||||
## 8. 本轮归档到 done
|
||||
|
||||
以下文档已从 `process/` 移动到对应 `done/`,并在文件头部补充 `done` 状态或归档说明:
|
||||
|
||||
- `design/01-tree-first-graph-kernel/done/1-7-local-workspace-root-page-bundle-checklist-v1.md`
|
||||
- `design/02-convex-rust-long-term-architecture/done/2-3-local-workspace-access-control-productization-v1.md`
|
||||
- `design/03-rust-web/done/3-15-local-markdown-asset-upload-relative-path-v1.md`
|
||||
- `design/04-tree-domain/done/4-27-resource-lifecycle-command-cutover-v1.md`
|
||||
- `design/04-tree-domain/done/4-39-filetree-open-target-resource-identity-checklist-v1.md`
|
||||
- `design/04-tree-domain/done/4-40-resource-lifecycle-command-cutover-smoke-checklist-v1.md`
|
||||
- `design/04-tree-domain/done/4-41-filetree-bulk-resource-command-cutover-v1.md`
|
||||
- `design/04-tree-domain/done/4-42-filetree-open-target-rooturi-contract-v1.md`
|
||||
- `design/04-tree-domain/done/4-43-local-folder-sort-order-persistence-v1.md`
|
||||
- `design/05-editor-mainline/done/5-15-local-markdown-path-identity-unification-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-16-editor-group-resource-tab-safety-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-17-resource-editor-kind-and-smoke-matrix-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-18-main-editor-tab-state-and-error-placeholder-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-19-resource-open-resolver-and-onlyoffice-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-20-resource-tab-close-confirm-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-21-editor-tab-url-active-identity-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-22-mindmap-main-editor-tab-kind-v1.md`
|
||||
- `design/05-editor-mainline/done/5-23-editor-side-target-secondary-pane-v1.md`
|
||||
- `design/05-editor-mainline/done/5-24-open-editors-lightweight-view-checklist-v1.md`
|
||||
- `design/05-editor-mainline/done/5-25-editor-tab-keyboard-and-mru-checklist-v1.md`
|
||||
- `design/07-ai/done/7-16-page-block-ai-real-smoke-followup-matrix-v1.md`
|
||||
- `design/07-ai/done/7-30-acp-session-load-resume-checklist-v1.md`
|
||||
- `design/07-ai/done/7-31-acp-permission-decision-loop-checklist-v1.md`
|
||||
- `design/07-ai/done/7-32-acp-tool-location-and-open-action-checklist-v1.md`
|
||||
- `design/07-ai/done/7-33-acp-session-info-plan-ui-checklist-v1.md`
|
||||
|
||||
## 9. 本轮迁入 reference
|
||||
|
||||
以下文档已迁入 `reference/`,不再作为当前直接执行 checklist:
|
||||
|
||||
- `design/old/03-rust-web/process/3-17-convex-export-web-entry-v1.md`
|
||||
- `design/04-tree-domain/reference/4-23-local-cloud-explicit-bridge-p3-candidate-v1.md`
|
||||
- `design/05-editor-mainline/reference/5-2-tiptap-notion-like-template-adoption-v1.md`
|
||||
- `design/05-editor-mainline/reference/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
- `design/05-editor-mainline/reference/5-7-wolai-page-tree-main-editor-experience-restoration-v1.md`
|
||||
- `design/05-editor-mainline/reference/5-14-zed-lapce-vscode-reference-adoption-matrix-v1.md`
|
||||
- `design/06-mindmap/reference/6-mindmap-leptos-adapter-reference-notes-v1.md`
|
||||
- `design/07-ai/reference/7-17-acp-session-convex-sharing-contract-v1.md`
|
||||
- `design/07-ai/reference/7-28-resource-ai-tool-contract-v1.md`
|
||||
- `design/08-wolai-aline-test-flow/reference/wolai-aline-test-flow-v1.md`
|
||||
- `design/09-siyuan-reference/reference/9-siyuan-reference-boundary-and-adoption-v1.md`
|
||||
- `design/10-review/reference/12-sidex-vscode-workbench-gap-review-v1.md`
|
||||
- `design/10-review/reference/13-hermes-vscode-plugin-gap-review-v1.md`
|
||||
- `design/10-review/reference/14-sidex-workbench-gap-followup-review-v1.md`
|
||||
|
||||
## 10. 仍需继续执行的 process
|
||||
|
||||
本轮保留为 active process 的主要文档与原因:
|
||||
|
||||
- `01`:`1-tree-first-graph-kernel-v1`、`1-1`、`1-2`、`1-3`、`1-4`、`1-5`、`1-6` 仍分别承担长期主线、全景参考、产品愿景、持续执行与下一阶段缺口跟踪。
|
||||
- `03`:`3-rust-web-long-term-architecture-v1`、`3-1`、`3-3`、`3-13` 仍有架构或未完成迁移项。
|
||||
- `04`:`4-28`、`4-34`、`4-38`、`4-44`、`4-45` 仍有 restore focus、local folder no-refresh、open target 前端 cutover、action-layer smoke 和 open editors lifecycle 验证缺口。
|
||||
- `05`:`5-6`、`5-9`、`5-26` 仍分别跟踪 Page Aggregate 小尾项、Wolai-aline 持续项和 resource open resolver 收敛。
|
||||
- `06`:`6-mindmap-kernel-phase6-projection-editor-v1` 与 `6-mindmap-phase6-leptos-ui-shell-reuse-checklist-v1` 仍保留 mindmap 内核/视觉尾项。
|
||||
- `07`:`7-10`、`7-12`、`7-14`、`7-15`、`7-27` 仍分别承担 Phase 8 冻结缺口、Hermes tool routing、online/local markdown convergence、ACP 统一层和 final markdown truth 后续修复。
|
||||
@@ -0,0 +1,694 @@
|
||||
# [recycle] 17 Sidex / MNote Workbench Gap Execution Checklist v1
|
||||
|
||||
> 状态:done
|
||||
>
|
||||
> 创建时间:2026-05-27
|
||||
>
|
||||
> Owner:10-review / 05-editor-mainline / 03-rust-web / 04-tree-domain / 07-ai
|
||||
>
|
||||
> 目标:把本轮 Sidex / VSCode 对照审查转成可执行 checklist,优先补齐 MNote 当前最缺的统一工作台服务层,而不是继续扩散 UI 分支或照搬 VSCode 全量复杂度。
|
||||
|
||||
## 1. 背景结论
|
||||
|
||||
MNote 当前已经具备知识库型 VSCode-like workspace 骨架:
|
||||
|
||||
- Rust Web 3000 主壳、SSR 文档页和独立 browser runtime asset。
|
||||
- Resource Tree / File Tree / Page Tree 三层模型。
|
||||
- local-first Markdown、Page Aggregate、leptos-tiptap island。
|
||||
- resource tab、secondary pane、DocumentBuffer、冲突面板。
|
||||
- tree live WS/SSE、本地文件夹 watcher、本地搜索索引。
|
||||
- Hermes / Reasonix local-first 文件编辑控制面。
|
||||
|
||||
相比 Sidex / VSCode,当前最大差距不是某个控件,而是统一服务层仍分散:
|
||||
|
||||
- `ObjectWorkspacePath` / `KernelObjectIdentity` 没有成为 browser runtime 的统一身份消费入口。
|
||||
- `BufferStore` 已存在,但 tiptap、watcher、AI、resource tab 仍有各自 session / dirty / conflict 状态。
|
||||
- resource tab / open editors 仍是页面内 Map,不是可恢复、可被 Sidebar / 快捷键 / AI target resolver 共同消费的轻量 workbench editor model。
|
||||
- command context / when / keybinding / menu enablement 在 Rust core-protocol 与 browser JS 中各有简化版本。
|
||||
- tree live 仍有 local-folder SSE、watch batch、projection refresh、polling fallback 多链并存。
|
||||
- `tree.*` 已是 preferred command,但 compat 路径仍保留 `documents.*` 映射。
|
||||
|
||||
## 2. Sidex 对照边界
|
||||
|
||||
### 2.1 值得借鉴
|
||||
|
||||
| Sidex / VSCode 模型 | MNote 对应改进 |
|
||||
| --- | --- |
|
||||
| `ExplorerModel -> ExplorerService -> ExplorerView` 三层分工 | FileTree / PageTree 继续拆成 projection data source、runtime service、DOM view state,不让 DOM 自己拼真相 |
|
||||
| `IEditorService.openEditor` + `IEditorGroup` | 建立轻量 `MNoteEditorInput` / `MNoteEditorGroupState` / `OpenEditorsSnapshot`,统一 page、resource tab、secondary pane |
|
||||
| `EditorGroupModel` 管 active / preview / sticky / transient / MRU / selection | resource tab 补 preview / pinned / dirty close veto / MRU / keyboard action 的模型层 |
|
||||
| ContextKeyService + CommandsRegistry + KeybindingsRegistry | MNote 建立统一 command context,用 Rust command contract 驱动 browser menu / shortcut enablement |
|
||||
| Working Copy dirty / save / backup / conflict | BufferStore 成为 tiptap、watcher、AI、resource tab 的唯一打开态和版本仲裁面 |
|
||||
| Explorer watcher 节流、编辑中抑制、局部 parent refresh | local-folder watcher / filetree projection 只刷新受影响 parent,避免整树替换与编辑冲突 |
|
||||
|
||||
### 2.2 不照搬
|
||||
|
||||
- 不引入完整 VSCode DI / service container。
|
||||
- 不把文件系统 Explorer 当成 MNote 树真相;MNote 真相仍是 Rust kernel projection。
|
||||
- 不引入完整 Extension Host、Editor Override、Auxiliary Editor、多窗口 grid。
|
||||
- 不把 Monaco TextModel 当成 Markdown 事实源。
|
||||
- 不把 Sidex 的 `ResourceFileEdit` 直接替代 `tree.*` / `tree.resource.*` 命令面。
|
||||
- terminal / git / debug / tasks / extensions 是否进入 MNote,单独做产品边界决策,不混入本 checklist 的 P0。
|
||||
|
||||
## 3. 执行原则
|
||||
|
||||
- 优先做服务层统一,不做大规模视觉重写。
|
||||
- 每一批必须有 smoke 或 Rust/JS 定点测试;浏览器可见能力必须有真实用户路径验证。
|
||||
- 不新增第二套树真相、页面真相或资源生命周期真相。
|
||||
- 不碰与本 checklist 无关的历史清理、回收区删除或用户已有改动。
|
||||
- 对旧 compat 只做可证明的瘦身;不能为了“干净”破坏 cloud / remote / legacy 边界。
|
||||
|
||||
## 4. Checklist
|
||||
|
||||
### Batch 0:基线取证与任务拆分
|
||||
|
||||
状态:done
|
||||
|
||||
- [x] 对照 Sidex Explorer / EditorGroup / command / working copy 模型。
|
||||
- [x] 对照 MNote Rust Web / browser runtime 当前能力。
|
||||
- [x] 对照现有 design / smoke / process backlog。
|
||||
- [x] 确认本轮不做代码清理、不改用户已有脏改动。
|
||||
- [x] 输出本执行 checklist,并把后续工作拆成 P0 / P1 / P2。
|
||||
|
||||
验收证据:
|
||||
|
||||
- `reference-code/sidex-main/src/vs/workbench/contrib/files/common/explorerModel.ts`
|
||||
- `reference-code/sidex-main/src/vs/workbench/contrib/files/browser/explorerService.ts`
|
||||
- `reference-code/sidex-main/src/vs/workbench/common/editor/editorGroupModel.ts`
|
||||
- `rust/crates/core-protocol/src/kernel.rs`
|
||||
- `rust/crates/mnote-web/src/document_buffer_store.rs`
|
||||
- `rust/crates/mnote-web/browser/document-resource-tab-runtime.js`
|
||||
- `rust/crates/mnote-web/browser/tree-live-controller.js`
|
||||
|
||||
### Batch 1:轻量 Workbench Editor Model
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- 定义 MNote 轻量 `EditorInput` / `EditorGroupState` / `OpenEditorsSnapshot` 合同。
|
||||
- 统一 page、resource tab、secondary pane 的打开态。
|
||||
- 让 Sidebar、快捷键、AI target resolver 和 smoke 都能消费同一个 open editors snapshot。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- Sidex `EditorGroupModel` 持有 active editor、preview editor、sticky count、MRU、selection。
|
||||
- Sidex Explorer 打开资源统一走 `IEditorService.openEditor({ resource, options })`。
|
||||
- MNote 不照搬多 group grid,只保留 primary / secondary pane 和 resource tab 的轻量模型。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 盘点当前 open editor 数据源:page tab、resource tab registry、secondary pane、URL query、`window.__mnoteOpenEditorsSnapshot`。
|
||||
- [x] 写一个最小合同文档或 Rust/JS 类型注释,明确字段:
|
||||
- `objectIdentity`
|
||||
- `workspacePath`
|
||||
- `paneRole`
|
||||
- `editorKind`
|
||||
- `active`
|
||||
- `dirtyState`
|
||||
- `preview`
|
||||
- `pinned`
|
||||
- `lastActiveAt`
|
||||
- [x] 为 `document-resource-tab-runtime.js` 的 open editors snapshot 增加 primary/secondary 分组语义。
|
||||
- [x] 补 smoke:打开页面、打开 mindmap/office/code/text 资源、切换 tab、刷新后至少能恢复 active page/resource 的可见状态。
|
||||
- [x] 补 AI target resolver 输入:last focused editor / resource tab 应来自 open editors snapshot,不再从 DOM 临时猜。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] `OpenEditorsSnapshot` 保持 `mnote.open_editors_snapshot.v1`,新增 `groups.primary` / `groups.secondary`。
|
||||
- [x] page editor entry 与 resource editor entry 均输出 `paneRole`、`editorKind`、`preview`、`pinned`、`lastActiveAt`。
|
||||
- [x] resource tab entry 补 `documentId` / `workspaceId`,为后续 `ObjectWorkspacePath` 消费统一预留字段。
|
||||
- [x] page / resource editor entry 补 `workspacePath`,合同以 `mnote.workspace_path.v1` 表达 workspace/source/root/relativePath/documentId/objectIdentity。
|
||||
- [x] passive resource(Office/PDF/image 等无 session tab)激活后也写入 `lastActiveAt`,避免 MRU / AI target resolver 后续只能看到 0。
|
||||
- [x] `task457-main-editor-resource-tab-smoke` 覆盖 primary markdown resource active、primary pinned page、secondary page editor。
|
||||
- [x] `task457-main-editor-resource-tab-smoke` 覆盖 Office passive resource active snapshot 的 `documentId` / `workspaceId` / `editorKind` / `lastActiveAt`。
|
||||
- [x] `task457-main-editor-resource-tab-smoke` 覆盖 canonical `resourceTab` URL 刷新后恢复 active markdown resource tab,并验证 `workspacePath.relativePath`。
|
||||
- [x] `sidebar-page-ai-runtime.js` 从 `OpenEditorsSnapshot` 生成 `mnote.ai_editor_target.v1`,写入 `pageContext.aiContext.activeEditorTarget`、`openEditorsSnapshot` 与 `/api/hermes/client/runs` 的 `editorTarget`。
|
||||
- [x] `hermes_client.rs` 在 local source instructions 中保留 `editorTarget`,避免 agent 只靠 URL / DOM 猜当前编辑目标。
|
||||
- [x] 同步 `task457` 中已过期的 Office 默认 iframe 断言:默认主编辑区是 `/office-preview` 轻量预览,显式 `new-window` 仍走 `/onlyoffice?mode=edit`。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
|
||||
node --check scripts/task457-main-editor-resource-tab-smoke.js
|
||||
git diff --check -- design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md rust/crates/mnote-web/browser/document-resource-tab-runtime.js scripts/task457-main-editor-resource-tab-smoke.js
|
||||
node scripts/task457-main-editor-resource-tab-smoke.js
|
||||
node --check scripts/task501-office-preview-light-viewer-smoke.js
|
||||
node scripts/task501-office-preview-light-viewer-smoke.js
|
||||
node scripts/task490-runtime-surfaces-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web office_preview_page_serves_lightweight_viewer_shell -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_body_local_source_uses_file_scope_not_full_page_context -- --test-threads=1
|
||||
node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
|
||||
node --check scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
```
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
|
||||
node scripts/task496-editor-open-parallel-runtime-aggregate-smoke.js
|
||||
node scripts/task490-runtime-surfaces-smoke.js
|
||||
```
|
||||
|
||||
### Batch 2:ObjectWorkspacePath / Resource Identity runtime 消费统一
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- browser runtime 不再主要依赖 `documentId + rootUri + relativePath + assetId` 临时拼接身份。
|
||||
- FileTree、resource open、copy-id、AI scope、tree command 参数统一消费 `ObjectWorkspacePath` / `KernelObjectIdentity`。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- Sidex ExplorerItem 的 identity 基于 root resource + item resource。
|
||||
- MNote identity 必须多带 `workspaceId/sourceKind/rootUri/relativePath/objectIdentity`,不能照搬纯文件 URI。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 盘点 browser runtime 中手拼 identity 的位置:
|
||||
- `filetree-runtime.js`
|
||||
- `sidebar-filetree-command-runtime.js`
|
||||
- `document-resource-tab-runtime.js`
|
||||
- `resource-open-runtime.js`
|
||||
- `sidebar-filetree-open-runtime.js`
|
||||
- [x] 为 FileTree row 输出 / 读取建立统一 helper:`readWorkspacePathFromRow(row)`。
|
||||
- [x] `copy-id` 和 resource tab key 优先使用 workspacePath / objectIdentity。
|
||||
- [x] local_folder 下绝对路径还原继续从 `rootUri + relativePath` 得出,但只作为展示/复制结果,不作为内部 identity。
|
||||
- [x] 补 smoke:local_folder Markdown、folder、asset、mindmap、office row 的 identity 一致且复制路径不暴露 `local:*` projection id。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] `filetree-runtime.js` 新增 `readWorkspacePathFromRow(row, deps)`,统一读取 `workspaceId/sourceKind/rootUri/relativePath/objectIdentity/rowId/documentId/assetId/title`。
|
||||
- [x] `fileTreeAssetDownloadDetail` 与 filetree context menu detail 开始携带 `workspacePath`,为后续 copy/open/command context 统一消费铺底。
|
||||
- [x] `task495-filetree-copy-id-absolute-path-smoke` 增加 `readWorkspacePathFromRow` 定点断言,同时保持 local_folder 复制 ID 返回绝对路径。
|
||||
- [x] `sidebar-tree-runtime.js` 的 filetree open / asset open event detail 携带 `workspacePath`,resource open 不再只能从 DOM 临时拼身份。
|
||||
- [x] `sidebar-filetree-open-runtime.js` 用 `workspacePath.objectIdentity/rootUri/relativePath/assetId` 生成 resource tab object identity,覆盖 local file、mindmap、office、pdf 和侧栏打开。
|
||||
- [x] `document-resource-tab-runtime.js` 在 resource tab entry 中保留 `workspacePath`,snapshot 输出优先沿同一 workspacePath 合同。
|
||||
- [x] `local_folder_source.rs` 的真实 local_folder projection 为 mindmap/office/asset row 写入 `workspacePath.objectIdentity.assetId`,避免资源行 identity 碰撞。
|
||||
- [x] `filetree-runtime.js` 收窄 `isLocalFolder` 判定:只信 `sourceKind=local_folder`,或 sourceKind 缺失时的明确 local row/doc 前缀,避免误伤 cloud/compat id。
|
||||
- [x] 移除已退役表格引擎的 legacy 分类分支;表格资源只按当前 `table` object/icon kind 识别。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/filetree-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-filetree-open-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js
|
||||
node --check scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
node scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_file_tree_classifies_mindmap_and_office_assets -- --test-threads=1
|
||||
node scripts/task457-main-editor-resource-tab-smoke.js
|
||||
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
node scripts/task490-runtime-surfaces-smoke.js
|
||||
node scripts/task501-office-preview-light-viewer-smoke.js
|
||||
git diff --check -- rust/crates/mnote-web/browser/document-resource-tab-runtime.js rust/crates/mnote-web/browser/filetree-runtime.js rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js rust/crates/mnote-web/browser/sidebar-filetree-open-runtime.js rust/crates/mnote-web/browser/sidebar-tree-runtime.js rust/crates/mnote-web/src/routes/local_folder_source.rs scripts/task495-filetree-copy-id-absolute-path-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- `node scripts/task456-resource-object-shell-sync-smoke.js` 当前走已退役 Convex compat,失败为 `503 convex_retired`,不作为 local-first Batch 2 验收入口。
|
||||
- `node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` 暴露上传后 dirty conflict 与删除附件后等待超时,归入 Batch 3/资源生命周期后续处理,不在本 identity 切片扩大修复面。
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/filetree-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js
|
||||
node --check scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
node scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
node scripts/task456-resource-object-shell-sync-smoke.js
|
||||
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
|
||||
```
|
||||
|
||||
### Batch 3:BufferStore / Working Copy 唯一打开态
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- `BufferStore` 成为 tiptap、watcher、AI、resource tab 的统一 dirty / stale / external modified / deleted 仲裁面。
|
||||
- browser session 的 dirty/conflict 状态不再独立形成第二套事实。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- VSCode WorkingCopyService 统一 dirty、save、backup、conflict。
|
||||
- MNote 不引入完整 backup service;先把 local-first Markdown 的 dirty/stale/deleted 状态统一到 BufferStore。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 盘点 `documentSessionRegistry`、localStorage draft、`conflictDetectionKey`、`expectedFileVersion` 当前写读链。
|
||||
- [x] 让 watcher 标记外部修改后,前端 session 从 BufferStore / aggregate 读取状态,而不是只靠本地 session flag。
|
||||
- [x] 资源 tab close guard 读取统一 dirty state。
|
||||
- [x] AI 写入前检查目标 buffer dirty/stale;dirty 时必须显式确认或阻断。
|
||||
- [x] 补 smoke:clean buffer 外部写入可同步;dirty buffer 外部写入弹冲突;删除当前文件后 tab 不静默保留旧正文。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] Sidex 对照:借鉴 `WorkingCopyService` 的统一 dirty/event 仲裁;不引入 VSCode 完整 backup/hot-exit/URI provider。
|
||||
- [x] `documents.rs` 增加 `POST /api/documents/buffer-state/dirty`,浏览器编辑变 dirty 时写入 Rust `BufferStore::mark_dirty`,保存成功仍由 Rust 写链 `mark_saved` 清理。
|
||||
- [x] `document-session-runtime.js` 增加 `fetchSessionBufferState()` / `applyBufferStateToSession()`,外部刷新前先读取 `/api/documents/buffer-state`,对 `Dirty/Stale/Deleted` 进入冲突/删除保护,不再只靠本地 session flag。
|
||||
- [x] `queueSessionSave()` 在 autosave 排队时同步标记 BufferStore dirty,形成 watcher / AI / save 共享的打开态。
|
||||
- [x] clean 外部写入、dirty 外部写入、保存失败保留编辑器内容、AI dirty 写入冲突路径均已通过 smoke。
|
||||
- [x] `sidebar-page-ai-runtime.js` 在发起 Hermes run 前读取 OpenEditorsSnapshot 与 `/api/documents/buffer-state`,当目标 buffer 为 `Dirty` / `Stale` / `Deleted` / `ExternalModified` 时阻断 AI 写入,不再让 dirty buffer 进入 agent 写盘后冲突。
|
||||
- [x] `document-resource-tab-runtime.js` 的 page entry 从当前 `documentSessionRegistry` 输出 dirty state,避免页面刚输入但 `/buffer-state/dirty` 仍未完成时 Page AI 被放行。
|
||||
- [x] `document-resource-tab-runtime.js` 的 resource tab close 在关闭前 await 读取 BufferStore dirty state,并把 `Dirty` / `Stale` / `Deleted` / `ExternalModified` 纳入 close veto;本地 resource session 的小写 `dirty` 同时被 Page AI 阻断逻辑规范化识别。
|
||||
- [x] `local-upload-runtime.js` 上传保存成功后广播 `mnote:local-upload-editor-save-completed`,`document-session-runtime.js` 同步 session fileVersion / conflict key,避免编辑器自己写盘被 watcher 误判为外部冲突。
|
||||
- [x] `sidebar-attachment-open-runtime.js` 为 local_folder 附件链接增加存在性刷新、missing 状态缓存、stat 请求序号和幂等 DOM 写入;删除真实附件后 Markdown 链接保留,点击进入 resource tab missing/error state;stat 非 OK 不再伪装成 missing,而是写入诊断属性。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/document-session-runtime.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_buffer_state_falls_back_to_document_id_without_relative_path -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web document_buffer_mark_dirty_and_saved_round_trip -- --test-threads=1
|
||||
node scripts/task436-local-markdown-open-document-external-change-smoke.js
|
||||
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js
|
||||
node scripts/task486-local-markdown-save-error-editor-preserves-content-smoke.js
|
||||
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
node scripts/task457-main-editor-resource-tab-smoke.js
|
||||
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
|
||||
```
|
||||
|
||||
补充验证证据(2026-05-28):
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/document-session-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/local-upload-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-attachment-open-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js
|
||||
node --check scripts/task460-resource-tab-close-dirty-smoke.js
|
||||
node --check scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
node --check scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
|
||||
git diff --check -- rust/crates/mnote-web/browser/document-resource-tab-runtime.js rust/crates/mnote-web/browser/document-session-runtime.js rust/crates/mnote-web/browser/local-upload-runtime.js rust/crates/mnote-web/browser/sidebar-attachment-open-runtime.js rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js scripts/task453-local-folder-page-ai-changed-files-smoke.js scripts/task460-resource-tab-close-dirty-smoke.js scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web documents_buffer_state_falls_back_to_document_id_without_relative_path -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web document_buffer_mark_dirty_and_saved_round_trip -- --test-threads=1
|
||||
node scripts/task436-local-markdown-open-document-external-change-smoke.js
|
||||
node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js
|
||||
node scripts/task486-local-markdown-save-error-editor-preserves-content-smoke.js
|
||||
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
node scripts/task460-resource-tab-close-dirty-smoke.js
|
||||
node scripts/task457-main-editor-resource-tab-smoke.js
|
||||
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- Sidex / VSCode WorkingCopyService 的统一 dirty / conflict / backup 模型只借鉴为 BufferStore 仲裁面;MNote 暂不引入完整 hot-exit / backup service。
|
||||
- Page AI dirty buffer 策略本批选择阻断,不做确认弹窗;后续若需要“显式确认后继续”,应进入 07-ai 单独交互设计。
|
||||
- `task479` 的 broken link 检查在 headless 中显式调用现有 attachment refresh hook,避免浏览器定时器调度导致误判;真实 runtime 仍保留 tree event 与定时刷新。该项只证明 refresh/runtime 行为正确,不单独证明定时器或 tree event 调度可靠,后续 Batch 5 需要补真实 event 链路覆盖。
|
||||
|
||||
### Batch 4:Page Aggregate legacy fallback 退役
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- 继续减少 `documents.content` / legacy body payload / compat projection 在 runtime 主链中的占比。
|
||||
- Page Aggregate 成为页面读取和编辑刷新事实入口。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- Sidex editor model 有明确输入和持久化模型。
|
||||
- MNote 的主模型不是 Monaco TextModel,而是 local Markdown -> Page Aggregate -> tiptap projection。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 盘点 `document-tiptap-conversion-runtime.js` 中 legacy conversion 的 runtime 调用面。
|
||||
- [x] 区分 local-first `.md` projection、cloud compat projection、fixture/test helper。
|
||||
- [x] 为 Page Aggregate 输出增加或验证 source 标记:避免 UI 偏好 / 页面事实 / compat fallback 混淆。
|
||||
- [x] 逐步把可控 runtime fallback 改成显式 degraded / compat owner。
|
||||
- [x] 补 smoke:刷新后 body/blockDocument/options/source 一致,不能静默读旧 legacy 内容。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] Sidex 对照:借鉴 `EditorInput` 显式 input/resource/capabilities 边界;MNote 不照搬 EditorResolver,只把页面正文 source 分类写入 runtime session。
|
||||
- [x] `document-tiptap-conversion-runtime.js` 增加 `pageBodyTiptapDocumentSource()`,把 `local_markdown.content`、`page_aggregate.block_document`、`compat.legacy_content`、`degraded.fallback_text`、`empty` 显式分类,避免无标记地从 legacy `body.content` 猜正文来源。
|
||||
- [x] `document-session-runtime.js` 保存 `pageBodySource/projectionSource/blockProjectionVersion`,`document-editor-adapter-runtime.js` 在编辑器 root 上写入 `data-mnote-page-body-source` / `data-mnote-projection-source` / `data-mnote-block-projection-version`。
|
||||
- [x] `task167-local-markdown-title-body-options-no-convex-smoke.js` 断言 local-first Page Aggregate 的 `projectionSource=local_markdown.content` 与 `blockDocument` 存在,保存后不退回 legacy `documents.content`。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/document-session-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js
|
||||
node --check scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
|
||||
git diff --check -- rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js rust/crates/mnote-web/browser/document-session-runtime.js rust/crates/mnote-web/browser/document-editor-adapter-runtime.js scripts/task167-local-markdown-title-body-options-no-convex-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
|
||||
node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
|
||||
node scripts/task484-local-folder-page-body-refresh-readback-smoke.js
|
||||
node scripts/task493-page-settings-sqlite-preferences-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web page_aggregate_endpoint_returns_local_markdown_readonly_snapshot -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_page_options_metadata_flows_into_page_aggregate -- --test-threads=1
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- `node scripts/task-page-aggregate-body-sync-smoke.js` 与 `node scripts/task-page-aggregate-refresh-persistence-smoke.js` 当前通过旧 `/api/tree/commands` 入口创建文档,失败为 `503 convex_retired`;它们属于 legacy/cloud helper,不作为 local-first Batch 4 验收入口。
|
||||
- 本批只把 fallback 分类显式化并锁住 local-first source,不删除 legacy conversion 函数;cloud compat / fixture 仍可显式走 `compat.legacy_content`。
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
|
||||
node scripts/task-page-aggregate-body-sync-smoke.js
|
||||
node scripts/task-page-aggregate-refresh-persistence-smoke.js
|
||||
```
|
||||
|
||||
### Batch 5:Tree live cache / FileTree data source 收敛
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- 减少 watcher/SSE/projection refresh/polling 多链并存。
|
||||
- FileTree refresh 按受影响 parent 局部刷新,保留展开、选择和焦点。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- Sidex AsyncDataTree 按 node resolve children,有 in-flight 去重、slow state、view state。
|
||||
- MNote 已有 lazy/cache/view state 设计,继续把它固化为 data source 而不是整树 DOM 替换。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 盘点 `/api/local-folder/events?treeLive=true`、`tree:delta`、`watch_batch`、projection refresh、polling fallback 的调用关系。
|
||||
- [x] 定义统一 local-folder tree event payload 到 filetree parent refresh 的映射。
|
||||
- [x] 整树替换只保留在 root scope 切换或 resync 场景。
|
||||
- [x] view state 保存只存 expanded/selected/focused 等用户状态,不存 rows/cache 派生数据。
|
||||
- [x] 补大目录真实用户路径 smoke:从首页进入本地 workspace,点击星标/Explorer 逐级打开目标 Markdown,记录 longtask 和 editor ready。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] Sidex 对照:借鉴 `ExplorerModel` / `ExplorerService` / `AsyncDataTree` 的节点身份、局部 children resolve、in-flight 去重和 view state 思路;MNote 不照搬 VSCode 完整树控件,仍以 Rust projection / watcher revision 为事实源。
|
||||
- [x] `sidebar-tree-live-apply-runtime.js` 在 `watch_batch` 的 `fallbackResync/requiresResync` 下显式走 `refreshLocalFolderSidebarSnapshot()`,非 resync 情况只刷新受影响 parent。
|
||||
- [x] `watch_batch` 缺少 `affectedParents` 但包含 `changedPaths` 时,从 changed path 派生 parent scope,并写入 `data-mnote-local-folder-watch-batch-derived-parents` 作为诊断标记。
|
||||
- [x] 修复 scoped navigation shell 缺 primary editor host 导致 `openPrimaryDocument()` 抛 `pane_root_missing_primary` 并回退整页 reload 的问题:`HomePage` navigation 分支保留导航 placeholder,同时渲染隐藏 primary `DocumentPane` 供 pane runtime 原地替换。
|
||||
- [x] `document-editor-adapter-runtime.js` 在 primary 文档成功激活后隐藏 navigation placeholder,保持点击 scoped FileTree Markdown 后不重建侧栏、不丢 JS marker。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/sidebar-tree-live-apply-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js
|
||||
rustfmt --edition 2021 --check rust/crates/mnote-web/src/ssr/pages/home.rs
|
||||
git diff --check -- rust/crates/mnote-web/browser/sidebar-tree-live-apply-runtime.js rust/crates/mnote-web/browser/document-editor-adapter-runtime.js rust/crates/mnote-web/src/ssr/pages/home.rs scripts/task494-filetree-lazy-loading-dedup-smoke.js design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md
|
||||
node scripts/task494-filetree-lazy-loading-dedup-smoke.js
|
||||
node scripts/task492-sidebar-starred-shortcuts-smoke.js
|
||||
node scripts/task497-local-page-tree-filetree-open-performance-smoke.js
|
||||
node scripts/task499-sidebar-tree-view-state-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_local_folder_without_page_renders_navigation_page_without_editor_bootstrap -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_local_folder_scope_renders_navigation_without_root_sibling_document -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_active_page_includes_document_panes_bootstrap -- --test-threads=1
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- `watch_batch` 到 FileTree parent refresh 只消费 Rust local-folder event payload;前端不新增 rows/cache 事实源。
|
||||
- `view-state` smoke 证明持久化内容仍是 expanded / selected / focused / active / scroll 等用户状态,不把 rows 或 projection cache 写入 view state。
|
||||
- scoped navigation shell 的 primary `DocumentPane` 是 editor host 能力补齐,不改变 local_folder navigation page 的 Page Aggregate / editor bootstrap 初始加载边界;导航页仍不输出 `__MNOTE_PAGE_AGGREGATE__` 或 `__MNOTE_EDITOR_BOOTSTRAP__`。
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
node scripts/task492-sidebar-starred-shortcuts-smoke.js
|
||||
node scripts/task494-filetree-lazy-loading-dedup-smoke.js
|
||||
node scripts/task497-local-page-tree-filetree-open-performance-smoke.js
|
||||
node scripts/task499-sidebar-tree-view-state-smoke.js
|
||||
```
|
||||
|
||||
### Batch 6:Command Context / Keybinding / Menu Enablement 统一
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- 统一 readonly、selection、resource kind、dirty、AI write capability、source kind 等上下文。
|
||||
- browser menu、快捷键、tree command enablement 消费同一 context,而不是多处硬编码。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- Sidex 由 ContextKeyService + CommandsRegistry + KeybindingsRegistry 统一驱动。
|
||||
- MNote 只做轻量实现,最终执行仍落到 Rust kernel / `tree.*` command。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 对齐 Rust `CommandContext` 和 browser `when` parser 的字段名。
|
||||
- [x] 建立 `buildFileTreeCommandContext(row, openEditorsSnapshot, bufferState)`。
|
||||
- [x] 右键菜单、键盘动作、toolbar action 统一从 command context 判断 enable/disabled/reason。
|
||||
- [x] 为危险动作补 destructive / requiresApproval 元数据。
|
||||
- [x] 补 smoke:readonly、dirty、folder、asset、markdown、多选下的菜单与快捷键状态一致。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] Sidex 对照:借鉴 `ContextKeyService` + `CommandsRegistry` + `KeybindingsRegistry` 的“同一 command id / 同一 context / 同一 when”模型;MNote 不照搬 VSCode DI、Extension Host、完整 menu contribution 或复杂 when 语法。
|
||||
- [x] `filetree-context-menu-runtime.js` 增加 `buildFileTreeCommandContext(row, openEditorsSnapshot, bufferState, deps)`,统一输出 `workspace.sourceKind`、`workspace.readonly`、`tree.focusKind`、`tree.selectionCount`、`tree.selectionResourceKind`、`tree.targetResourceKind`、`tree.targetIsFolder`、`tree.targetIsAsset`、`editor.dirty`、`editor.dirtyState`、`editor.hasSelection`、`ai.canWrite`。
|
||||
- [x] `core-protocol::CommandContext` 对齐新增 target / dirtyState keys,并补单测锁住 Rust when evaluator 语义。
|
||||
- [x] `sidebar-filetree-command-runtime.js` 菜单打开时从 open editors snapshot / bufferState / row selection 生成统一 command context,并把 context、`when`、`data-disabled-reason`、`data-destructive`、`data-requires-approval` 暴露到菜单 DOM。
|
||||
- [x] `filetree-keyboard-runtime.js` 与 `sidebar-tree-runtime.js` inline fallback 的 F2 / Delete / Ctrl+V 使用同一 `buildSidebarFileTreeContext` + when;dirty 目标和 readonly workspace 下不执行写动作。
|
||||
- [x] `task495-filetree-copy-id-absolute-path-smoke.js` 增加 command context 定点断言:markdown dirty target 禁用 rename/delete 类写操作,readonly target 禁用写操作,同时保持 local_folder copy-id 返回绝对路径。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/filetree-context-menu-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/filetree-keyboard-runtime.js
|
||||
node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js
|
||||
node --check scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
rustfmt --edition 2021 --check rust/crates/core-protocol/src/command.rs rust/crates/mnote-web/src/ssr/pages/layout.rs
|
||||
git diff --check -- rust/crates/mnote-web/browser/filetree-context-menu-runtime.js rust/crates/mnote-web/browser/sidebar-filetree-command-runtime.js rust/crates/mnote-web/browser/filetree-keyboard-runtime.js rust/crates/mnote-web/browser/sidebar-tree-runtime.js rust/crates/core-protocol/src/command.rs rust/crates/mnote-web/src/ssr/pages/layout.rs scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p core-protocol command_context -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_command_context_helper_functions_exist -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_context_menu_runtime_contains_command_context_helpers -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_context_menu_items_have_when_for_readonly -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_context_evaluator_uses_readonly_and_selection_count_for_delete_key -- --test-threads=1
|
||||
node scripts/task490-runtime-surfaces-smoke.js
|
||||
node scripts/task495-filetree-copy-id-absolute-path-smoke.js
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- 本批只统一 enablement / reason / metadata / keyboard gating,不改变最终执行面;写动作仍落到现有 Rust `tree.*` / `tree.resource.*` command 路径。
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree -- --test-threads=1` 宽过滤额外暴露两个历史/相邻断言失败:`sidebar_filetree_blocks_readonly_paste_and_drop_with_action_status` 仍期待旧 `ensureFileTreeWritableTarget('paste'` 字符串,`sidebar_filetree_runtime_does_not_keep_retired_table_engine_branches` 命中 legacy table 分支;两者未进入本批定点验收,后续如要清理应单独归入 tree/filetree runtime 测试债。
|
||||
- `node scripts/task476-filetree-editor-context-menu-download-smoke.js` 在菜单验证前等待 `report-a.pdf` asset row 超时;`node scripts/task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.js` 在 restore 后等待 `docs/report.txt` row 超时。两者失败点早于 Batch 6 command context enablement,记录为 FileTree projection / restore 可见性后续风险,不作为本批 command context 验收入口。
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
node scripts/task490-runtime-surfaces-smoke.js
|
||||
node scripts/task476-filetree-editor-context-menu-download-smoke.js
|
||||
node scripts/task473-local-folder-trash-restore-no-refresh-focus-gap-smoke.js
|
||||
```
|
||||
|
||||
### Batch 7:`tree.*` / `tree.resource.*` compat 瘦身
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- `tree.*` / `tree.resource.*` 成为正式命令面。
|
||||
- `documents.*` 只保留为显式 compat alias,不继续承载长期语义。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- Sidex Explorer file actions 统一进入 file operation / bulk edit。
|
||||
- MNote 的统一入口应是 Rust kernel tree/resource command,不是直接文件 bulk edit。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 盘点 `routes/tree.rs` 中 `documents.*` legacy mapping。
|
||||
- [x] 标注每个 compat mapping 的 source kind、保留原因和退役条件。
|
||||
- [x] local-first 路径禁止静默走 legacy cloud command。
|
||||
- [x] resource trash / restore / purge 统一走 `tree.resource.*`。
|
||||
- [x] 补 smoke:create/rename/move/delete/restore/purge 在 local_folder 下不触发 legacy owner。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] Sidex 对照:借鉴 file action 明确区分 `moveFileToTrash` / `deleteFile` 的语义分叉和回收站 fallback;MNote 保持 `archive/restore/purge` 三段语义,不照搬 `ResourceFileEdit` 或 file system-only undo 模型。
|
||||
- [x] `routes/tree.rs` 在 `/api/tree/commands` 中对缺失 `sourceKind` 但 `rootUri` 为 `file://` 的请求推断为 `local_folder`,避免 local-first 请求静默走 legacy cloud executor。
|
||||
- [x] `routes/tree.rs` 增加 `TREE_DOCUMENT_COMPAT_ALIAS_CATALOG`,把 `documents.*` / `tree.*` 兼容边界、保留原因与退役条件显式化。
|
||||
- [x] local_folder 资源 archive / restore / purge smoke 断言 canonicalCommand 分别为 `tree.resource.archive` / `tree.resource.restore` / `tree.resource.purge`。
|
||||
- [x] 本轮不改 `bridge-runtime` 旧 compat executor 的退役状态;`documents.*` 相关 legacy 兼容桥测试属于已退役 cloud 验证面,失败原因是旧 Convex compat bridge 已退役,不作为 local-first Batch 7 主验收入口。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_create_rename_copy_trash_restore_and_purge_use_same_endpoint -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_command_local_folder_asset_trash_restore_and_purge_use_trash_index -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_documents_compat_alias_catalog_marks_cloud_retirement_boundary -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web root_entry_local_folder -- --test-threads=1
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p bridge-runtime documents_lifecycle_aliases_are_marked_as_deprecated_tree_protocol_aliases -- --test-threads=1` 与 `tree_lifecycle_command_aliases_keep_tree_command_names` 当前失败,错误为“旧 Convex 兼容桥已退役;请改用 local-first Rust/SQLite control-plane 路径”。这说明旧 bridge-runtime 兼容桥已不再是 Batch 7 的主线验收目标,不应回拉为当前实现依赖。
|
||||
- local-first 请求的 `sourceKind` 推断只在 `rootUri` 为 `file://` 时触发,不会把非本地请求静默改成本地。
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib routes::tree -- --test-threads=1
|
||||
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
|
||||
node scripts/task471-local-folder-bulk-resource-trash-smoke.js
|
||||
```
|
||||
|
||||
### Batch 8:AI target / changed_files / diff 审计
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- local-first AI 默认走授权文件引用 + agent 原生 patch/diff。
|
||||
- MNote 负责 target resolver、allowed roots/files、dirty conflict、changed_files/diff 审计和前台同步。
|
||||
|
||||
Sidex 对照:
|
||||
|
||||
- VSCode agent/workspace 模式的价值是目标、权限、working copy、diff 和审计统一。
|
||||
- MNote 不继续扩普通 Markdown 的专用 `mnote.doc.markdown_edit` 主路径。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] target chip / picker 使用 open editors snapshot。
|
||||
- [x] run 开始后冻结 target 摘要,避免执行中 DOM 焦点变化改目标。
|
||||
- [x] changed_files 回收并展示:路径、版本、diff 摘要、actor。
|
||||
- [x] dirty buffer 下 agent 写入必须阻断或进入冲突合并。
|
||||
- [x] 补 smoke:clean 写入同步、dirty 写入阻断、跨 workspace target 不串、changed_files 可见。
|
||||
|
||||
本轮最小实现切片:
|
||||
|
||||
- [x] Sidex 对照:借鉴 VSCode/Sidex 的 working copy / SCM / diff 审计分层,把 target、权限、dirty、diff 和 changed files 放在统一 run/audit 边界;MNote 不照搬 SCM provider、QuickDiff provider 或完整 VSCode agent workspace 模型。
|
||||
- [x] `sidebar-page-ai-runtime.js` 的 Page AI target resolver 继续从 `OpenEditorsSnapshot` 生成 `mnote.ai_editor_target.v1`;发 run 前新增 workspace/root/source guard,阻断跨 workspace 或陈旧 target。
|
||||
- [x] `sidebar-page-ai-runtime.js` 新增 `mnote.page_ai_run_target_snapshot.v1`,在 run 发起时冻结 `editorTarget/openEditorsSnapshot/contextScope/rootUri/workspaceId/documentId`,并写入 `/api/hermes/client/runs` payload 与 `pageContext.aiContext.runTargetSnapshot`。
|
||||
- [x] `hermes_client.rs` 对 local source 的 `runTargetSnapshot` 做白名单 sanitization,只保留 target/workspace/open editor 摘要,不把 `pageText/pageXml/contextBlocks` 等正文塞进本地 agent instructions。
|
||||
- [x] `agent.changed_files` 工具卡显示路径、版本/hash/mtime 摘要、diff 摘要和 actor 信息;前台仍只消费 local agent before/after snapshot,不新增第二套 diff 真相。
|
||||
- [x] `task453-local-folder-page-ai-changed-files-smoke.js` 覆盖 clean 写入同步、dirty buffer 阻断、跨 workspace target 不发 run、changed_files 卡片可见、runTargetSnapshot payload 可见。
|
||||
|
||||
本轮验证证据:
|
||||
|
||||
```bash
|
||||
node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js
|
||||
node --check scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
rustfmt --edition 2021 --check rust/crates/mnote-web/src/routes/hermes_client.rs
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_client_run_body_local_source_uses_file_scope_not_full_page_context -- --test-threads=1
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web local_agent_audit_snapshot_detects_changed_files -- --test-threads=1
|
||||
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
git diff --check
|
||||
codegraph sync .
|
||||
```
|
||||
|
||||
边界记录:
|
||||
|
||||
- `node scripts/task-hermes-page-ai-audit-smoke.js` 与 `node scripts/task488-acp-multi-session-stability-smoke.js` 当前通过旧 helper 调 `/api/tree/commands` 创建 Convex 文档,失败为 `503 convex_retired`;两者属于 legacy/cloud 验证入口,需要后续改造为 local-first fixture 后再纳入默认 Batch 8 回归。
|
||||
- Batch 8 不把普通 Markdown 编辑主路径扩回 `mnote.doc.markdown_edit`;local-first 默认仍是授权文件引用 + agent 原生 patch/diff + watcher / Page Aggregate 同步。
|
||||
- `codegraph sync .` 与 `codegraph index . --force` 均已成功执行;`codegraph status .` 仍持续显示 `Pending Changes: Added 3 files`,属于本轮前后均存在的 CodeGraph pending 残留,已记录但不清理用户工作区。
|
||||
|
||||
建议验证:
|
||||
|
||||
```bash
|
||||
node scripts/task453-local-folder-page-ai-changed-files-smoke.js
|
||||
node scripts/task-hermes-page-ai-audit-smoke.js
|
||||
node scripts/task488-acp-multi-session-stability-smoke.js
|
||||
```
|
||||
|
||||
### Batch 9:是否扩展 IDE 面的产品决策
|
||||
|
||||
状态:done
|
||||
|
||||
目标:
|
||||
|
||||
- 明确 MNote 是否需要 Sidex 式 terminal / git / debug / tasks / extension host。
|
||||
- 若需要,单独写 `design/10-review/reference` 或对应 owner 的 process checklist;不混进当前 P0 服务层收口。
|
||||
|
||||
待办:
|
||||
|
||||
- [x] 列出 MNote 用户工作流里真实需要 terminal/git/tasks 的场景。
|
||||
- [x] 区分“知识库 workspace 内嵌工具”与“完整 IDE”。
|
||||
- [x] 给出引入、不引入、延后引入三种方案。
|
||||
- [x] 由用户确认产品边界后再进入实现。
|
||||
|
||||
本轮产品边界结论:
|
||||
|
||||
- [x] Sidex 对照:可借鉴 Sidex terminal 的 PTY/session/event-stream 模型、git porcelain status/diff/log 解析、tasks 的 detect/parse/run/output-stream 模型,以及 extension host 的贡献点/隔离 runtime 概念。
|
||||
- [x] 不引入完整 VSCode/Sidex workbench、DAP debug、VSCode extension host 或通用 Git 写操作;MNote 继续以 Rust kernel / tree-first projection 作为 workspace 真相。
|
||||
- [x] Terminal 若后续引入,只作为 workspace-root-scoped、allowed-roots 约束下的受控 terminal session,不默认开放全局 shell。
|
||||
- [x] Git 若后续引入,优先只做 changed_files / diff / log / status 审计;commit / push / pull / restore / clean / checkout 等 destructive 操作不进 MVP。
|
||||
- [x] Tasks 若后续引入,只支持白名单知识库任务:导出、索引、链接检查、附件整理、smoke;不做完整 `.vscode/tasks.json` 兼容。
|
||||
- [x] Debug 能力收口为 agent runtime / projection / session log inspector;不引入 DAP、breakpoints 或 debug adapter manager。
|
||||
- [x] Extension host 延后,仅保留 importer / exporter / renderer / action provider 的 MNote-native 插件设想,不接 VSCode marketplace 生态。
|
||||
|
||||
三方案:
|
||||
|
||||
- 引入:只引入 MNote-native `Workspace Command Center`,包含受控 terminal session、只读 git status/diff、白名单 tasks、流式输出、changed_files/diff 审计;不引入 VSCode extension host,不暴露 destructive Git。
|
||||
- 不引入:继续让 Hermes/Reasonix 用自身文件与 shell 能力,MNote 只做 target / allowed roots / audit / watcher;这是当前 local-first AI 主线成本最低的方案。
|
||||
- 延后:先把 terminal/git/tasks 作为 Phase B/C 设计冻结项,只落 checklist,不实现;等 changed_files/diff 审计、BufferStore 冲突、workspace readonly 策略稳定后,再开最小 MVP。
|
||||
|
||||
当前建议:
|
||||
|
||||
- 选择“延后 + 小范围引入只读 Git/diff 审计”的产品边界;Terminal/tasks 等 agent runtime 审计闭环稳定后再单独立项,Debug/DAP/VSCode extension host 不进入 MVP。
|
||||
|
||||
## 5. 当前运行状态
|
||||
|
||||
历史运行记录:Batch 1 轻量 Workbench Editor Model。
|
||||
|
||||
第一步只做只读核验和现有状态盘点,不改代码:
|
||||
|
||||
- [x] 读取 `document-resource-tab-runtime.js` 的 open editors snapshot 构建逻辑。
|
||||
- [x] 读取 `document-editor-adapter-runtime.js` 的 page / secondary pane 打开入口。
|
||||
- [x] 读取 `resource-open-runtime.js` 与 FileTree open runtime。
|
||||
- [x] 输出 Batch 1 的具体文件边界与最小 smoke 切片。
|
||||
|
||||
只读核验结论(2026-05-27):
|
||||
|
||||
- `document-resource-tab-runtime.js` 已有 `resourceTabRegistry`、`resourceTabMru`、`buildOpenEditorsSnapshot()` 和 `window.__mnoteOpenEditorsSnapshot`。
|
||||
- 当前 snapshot 已包含 `schema/generatedAt/activeObjectIdentity/editors/resourceEditors`,但没有显式 `groups`,调用方需要自己按 `paneRole` 过滤。
|
||||
- Page entry 已按 `primary/secondary` 生成;secondary entry 仅在有 documentId 或 pane visible 时进入 snapshot。
|
||||
- Resource entry 已有 `objectIdentity/title/kind/badgeKind/active/dirtyGuard/assetId/path`,但缺少 `paneRole/documentId/workspaceId/lastActiveAt/preview/pinned` 等轻量 editor group 字段。
|
||||
- `document-editor-adapter-runtime.js` 的公开入口集中在 `window.__mnoteDocumentPaneRuntime`,包括 `openPrimaryDocument`、`openSecondaryDocument`、`openResourceInActiveTab`、`openResourceAsSideTarget`。
|
||||
- `sidebar-filetree-open-runtime.js` 仍在多处手拼 `resource:file:<rootUri>:<relativePath>`、`resource:mindmap:<documentId>:<assetId>`、`resource:onlyoffice:<documentId>:<assetId>` 等 identity;这应进入 Batch 2,而不是阻塞 Batch 1。
|
||||
|
||||
Batch 1 最小文件边界:
|
||||
|
||||
- 修改:`rust/crates/mnote-web/browser/document-resource-tab-runtime.js`
|
||||
- 可选测试:复用或扩展 `scripts/task496-editor-open-parallel-runtime-aggregate-smoke.js`
|
||||
- 暂不修改:`document-editor-adapter-runtime.js`、`sidebar-filetree-open-runtime.js`、`resource-open-runtime.js`
|
||||
|
||||
Batch 1 最小实现切片:
|
||||
|
||||
- [x] `buildOpenEditorsSnapshot()` 增加 `groups` 字段,按 `primary/secondary` 输出每个 pane 的 active object 与 editors。
|
||||
- [x] `openEditorsSnapshotEntry()` 补 `paneRole`、`documentId`、`workspaceId`、`lastActiveAt`、`preview:false`、`pinned:false`。
|
||||
- [x] Page entry 补 `preview:false`、`pinned:true`、`lastActiveAt` 占位,保持页面 tab 是固定 tab。
|
||||
- [x] 更新 smoke 断言:`window.__mnoteOpenEditorsSnapshot.groups.primary` 存在,secondary 打开后 `groups.secondary` 存在。
|
||||
|
||||
## 6. 最小成功标准
|
||||
|
||||
本 checklist 完成到 P0 时,MNote 至少应满足:
|
||||
|
||||
- 打开页面 / 资源 / secondary pane 的状态有统一 snapshot,可恢复、可被 AI target resolver 消费。
|
||||
- local-first Markdown dirty/stale/conflict 只由 BufferStore / Page Aggregate 主链表达。
|
||||
- FileTree row identity、resource tab identity、AI target identity 不再多处临时拼接。
|
||||
- tree live / local-folder watcher 不再常规整树替换,view state 不丢。
|
||||
- 菜单、快捷键、dangerous action 的 enabled/disabled/reason 有统一 context。
|
||||
- local-first tree/resource command 不静默退回 legacy `documents.*` 主链。
|
||||
|
||||
## 7. 验证总入口
|
||||
|
||||
按改动范围选择运行:
|
||||
|
||||
```bash
|
||||
node scripts/task114-rust-web-gateway-entry-smoke.js
|
||||
node scripts/task159-auth-entry-smoke.js
|
||||
node scripts/task164-desktop-hot-local-folder-main-entry-smoke.js
|
||||
node scripts/task166-local-first-managed-workspace-no-convex-smoke.js
|
||||
node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
|
||||
node scripts/task490-runtime-surfaces-smoke.js
|
||||
cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib -- --test-threads=1
|
||||
git diff --check
|
||||
codegraph sync .
|
||||
codegraph status .
|
||||
```
|
||||
@@ -0,0 +1,99 @@
|
||||
# [recycle] 18 当前 design 口径与 bug 寻找 Review v1
|
||||
|
||||
> 创建时间:2026-05-31
|
||||
>
|
||||
> 状态:`done`
|
||||
>
|
||||
> 范围:当前仓库、历史 design 口径、active `process/`、现有 `bugs/process`、近期未提交 OnlyOffice / Page AI / control-plane 改动。
|
||||
|
||||
## 1. 结论
|
||||
|
||||
当前上位口径仍一致:
|
||||
|
||||
- `tree-first graph kernel` 是长期对象真相层。
|
||||
- local-first workspace 是默认产品形态,本地 `.md` 是页面正文真相。
|
||||
- Rust `mnote-web` 是 3000 主执行面。
|
||||
- `leptos-tiptap` 是默认 Markdown 前端编辑器。
|
||||
- Page Aggregate 是 Rust-first 投影收口方向,但仍需要 compat 瘦身。
|
||||
- Page AI 的 local-first 主路径是“授权文件引用 + allowed roots + Hermes/Reasonix 原生 patch/diff + watcher/BufferStore 同步”,不是继续扩 `mnote.doc.markdown_edit` 或 `/api/page-ai/block-edit-workflow`。
|
||||
|
||||
本轮发现的主要问题不是架构方向动摇,而是 `design/process` 状态与真实完成态、索引入口和 bug 记录之间出现漂移。
|
||||
|
||||
## 2. 已更新口径
|
||||
|
||||
- `design/README.md` 的当前入口已从不存在的 `design/01-05-current-priority-overview.md` 改为 `CURRENT_ARCHITECTURE.md` 与 `1-8`。
|
||||
- `1-8` 已补充 2026-05-31 口径复核:历史 `01-05` 已迁入 `old/`;已归档的 `3-3` / `3-18` 不再作为 active process 入口。
|
||||
- 本 review 作为本轮设计治理和 bug 寻找事实源,后续 worker 不应凭旧路径恢复过时优先级。
|
||||
|
||||
## 3. design 漂移
|
||||
|
||||
已确认的漂移:
|
||||
|
||||
- `design/README.md` 与 `1-8` 曾引用已不存在的 `design/01-05-current-priority-overview.md`。
|
||||
- `design/03-rust-web/process/3-24-global-content-type-page-width-settings-v1.md` checklist 全部完成并有实施记录;2026-05-31 已迁入 `design/03-rust-web/done/3-24-global-content-type-page-width-settings-v1.md`。
|
||||
- `design/05-editor-mainline/done/5-31-page-settings-sqlite-preference-convergence-v1.md` 已迁入 `done/`;剩余风险另按后续 bug / follow-up 跟踪。
|
||||
- `design/05-editor-mainline/done/5-32-filetree-lazy-loading-sidex-alignment-v1.md` 已迁入 `done/`;tree live cache 后续入口改看 `3-23` 与相关 bug。
|
||||
- `design/05-editor-mainline/process/5-33-navigation-page-route-guard-checklist-v1.md` 顶部落地状态和正文 checklist 状态不一致,需要拆成已完成核心与剩余 follow-up。
|
||||
- `design/07-ai/done/7-41-page-ai-hermes-reasonix-user-profile-isolation-v2.md` 文件头状态为 `done`;2026-06-01 已迁入 `done/`。
|
||||
- `design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md` 文件头状态为 `done`;2026-05-31 已迁入 `design/10-review/done/17-sidex-mnote-workbench-gap-execution-checklist-v1.md`。
|
||||
- `design/10-review/done/11-current-full-architecture-review-v1.md` 中“`mnote.doc.markdown_edit` 已是简单正文编辑主路径”的历史判断已被 `7-18` 和当前 AGENTS/ARCHITECTURE 覆盖;只能作为当时 review 快照,不应指导 local-first 新实现。
|
||||
|
||||
## 4. 新增 bug 条目
|
||||
|
||||
本轮已写入:
|
||||
|
||||
- `bugs/10-review/done/10-18-design-process-status-and-stale-entry-v1.md`
|
||||
- `bugs/07-ai/process/7-45-onlyoffice-live-bridge-session-isolation-v1.md`
|
||||
- `bugs/07-ai/process/7-46-onlyoffice-live-tool-resource-scope-bypass-v1.md`
|
||||
- `bugs/05-editor-mainline/process/5-40-onlyoffice-bridge-plugin-noise-regression-v1.md`
|
||||
- `bugs/07-ai/process/7-47-local-first-ai-markdown-edit-mouth-drift-v1.md`
|
||||
- `bugs/10-review/done/10-19-agents-md-broken-design-references-v1.md`
|
||||
- `bugs/03-rust-web/done/3-26-sidebar-dev-hot-reload-setinterval-v1.md`
|
||||
- `bugs/10-review/done/10-20-testing-reference-missing-new-smokes-v1.md`
|
||||
|
||||
其中 `10-18`、`10-19`、`10-20` 已完成治理收口并迁入 `done/`;OnlyOffice / Page AI / Sidebar 等运行时缺陷仍按各自 bug 条目继续跟踪验收。
|
||||
|
||||
## 5. 下一步建议
|
||||
|
||||
P0:
|
||||
|
||||
- [x] 修复 OnlyOffice live bridge session identity 与 resource scope 两个风险,优先补 Rust 单测,随后做 clean browser 多 tab smoke。
|
||||
- [x] 继续补 OnlyOffice 真实浏览器集成证据:真实 Office iframe 双 tab 不串台,以及 Page AI target=A 时不能读写 Office resource=B。
|
||||
- [x] `AGENTS.md` 断裂引用、Sidebar dev hot reload guard、`5-31` / `5-32` / `7-41` 归档已经收口。
|
||||
- [x] `5-33` 已拆出 breadcrumb / fetch guard follow-up;本轮 P0 不再被其阻塞,后续增强继续由 `design/05-editor-mainline/process/5-35-navigation-breadcrumb-fetch-guard-followup-v1.md` 跟踪。
|
||||
- [x] 更新 `10-review/done/11` 或追加覆盖说明,防止 worker 继续把 `mnote.doc.markdown_edit` 当 local-first 主写入口。
|
||||
|
||||
P1:
|
||||
|
||||
- [x] 继续推进 `7-18` Agent Target Resolver:target chip/picker、dirty guard、allowed roots/files、Page AI targetPackage 与 resource OCR context 已补到本轮可验收闭环;跨 workspace 多 target、真实 agent 写后 diff 回收等产品化尾项继续由 `7-18` 跟踪。
|
||||
- [x] 推进 `WorkspacePath/ObjectIdentity + BufferStore + Page Aggregate compat 瘦身`,让 FileTree、Open Editors、resource tab 与 AI target resolver 消费同一身份与 buffer 状态。
|
||||
- [x] 保留 `3-23` 与 `7-38` 作为 runtime owner follow-up,不重新打开 raw string 大拆分议题。
|
||||
|
||||
P2:
|
||||
|
||||
- [x] `7-42` mindmap 第二批:结构化 `apply_ops`、resource tab contextRefs、revision conflict。
|
||||
- [x] `3-25` MinerU OCR:后端真实 MinerU runtime、任务状态事件、全局任务栏 / 任务抽屉、UI 入口和 AI OCR sidecar context 已收口。
|
||||
- [x] `7-43` OnlyOffice Plugin Bridge:P0/P1 安全主路径与 P2 recipe 矩阵已收口,后续新增 recipe 按矩阵逐项验证。
|
||||
- [x] `7-44` ChatOnly Doubao binding:MNote 侧 binding 与 Doubao provider 单测已完成。
|
||||
- [x] `7-49` ChatOnly OpenClaw provider:Doubao provider 真实远端删除 helper / 单测已完成;DeepSeek / Gemini provider 已补明确 unsupported 降级 helper 与单测,返回 `remote_delete_failed` 而不是伪造 `remote_deleted`,缺口已归档到 `bugs/07-ai/done/7-49-chatonly-openclaw-provider-unit-test-gap-v1.md`。
|
||||
|
||||
本轮已将 `task503`、`task504`、`task512`、`task513`、`task514`、`task515`、`task516` 补入 `scripts/TESTING_REFERENCE.md`;对应 smoke 已实跑,运行结果由 `bugs/10-review/done/10-20-*` 记录。
|
||||
|
||||
## 6. 验证记录
|
||||
|
||||
原始 review 是设计/bug 审查和文档更新,未修业务代码,未跑浏览器 smoke。2026-06-01 后续收口已补跑 `task514` 与 `task517`,并登记 `task517` 到 smoke reference;OnlyOffice 真实 iframe / DocumentServer 验收仍未完成。
|
||||
|
||||
已执行只读检查:
|
||||
|
||||
- `git status --short`
|
||||
- `find design -maxdepth 3 -type f`
|
||||
- `find bugs -maxdepth 3 -type f`
|
||||
- `codegraph status .`
|
||||
- 多组 `rg` / `sed` / `nl` 证据核对
|
||||
|
||||
已使用:
|
||||
|
||||
- 2 个只读 subagent:架构口径审查、bug 候选审查。
|
||||
- 2 个 Reasonix 只读 worker:design governance audit、bug hunt candidate audit。
|
||||
|
||||
注意:当前工作区有大量未提交改动,且 CodeGraph 显示新增文件 pending。本轮只对本轮新增/修改的文档负责,不回滚、不移动用户已有改动。
|
||||
@@ -0,0 +1,67 @@
|
||||
# [recycle] 19 Non-LightRAG design cleanup and follow-up checklist v1
|
||||
|
||||
> 创建时间:2026-06-06
|
||||
>
|
||||
> 状态:`done`
|
||||
>
|
||||
> Owner:10-review / 03-rust-web / 05-editor-mainline / 07-ai
|
||||
|
||||
## 1. 边界
|
||||
|
||||
当前另一个 agent 正在修改 LightRAG 相关实现与设计。本 checklist 暂不写 `design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md`、`rust/crates/mnote-web/src/routes/knowledge_rag.rs`、`rust/crates/mnote-web/src/hermes_tools/knowledge_rag.rs` 或 LightRAG smoke。等对方改动稳定后,再单独做 LightRAG diff review。
|
||||
|
||||
本轮先减少非 LightRAG 设计误读:关闭已完成 process、降级历史快照、把真实未完成项拆成更小 checklist。
|
||||
|
||||
## 2. 已执行清理
|
||||
|
||||
- [x] `4-25-filetree-view-state-model-driven-lazy-reveal-v1.md` 从 `04-tree-domain/process/` 移到 `04-tree-domain/done/`,并标注历史 18 秒冷加载当前未复现。
|
||||
- [x] `3-23-sidebar-local-folder-resource-runtime-followup-v1.md` 从 `03-rust-web/process/` 移到 `03-rust-web/reference/`,并固定 `refreshLocalFolderSidebarSnapshot` 的 shell 编排边界。
|
||||
- [x] `5-13-tiptap-runtime-cold-load-mindmap-split-checklist-v1.md` 从 `05-editor-mainline/process/` 移到 `05-editor-mainline/done/`,只归档 mindmap 重包拆分。
|
||||
- [x] 新增并复核 `5-36-tiptap-real-shell-cold-load-followup-v1.md`。当前 checkout 未复现 18 秒冷加载,已降级为 `05-editor-mainline/reference/`。
|
||||
- [x] 给 `3-6-rust-web-main-execution-plane-checklist-v1.md` 增加历史快照说明,避免从旧 `[ ]` 项继续派活。
|
||||
- [x] 收紧 `10-current-mnote-ai-runtime-review-v1.md` 的 `mnote.doc.markdown_edit` 口径,避免把历史工具描述成 local-first 新 agent fallback。
|
||||
|
||||
## 3. 下一批非 LightRAG checklist
|
||||
|
||||
### P0:Tiptap real shell cold-load
|
||||
|
||||
- [x] 执行 7-45 当前 checkout 临时复核:`domContentLoadedMs=102`、`editorVisibleMs=271`、`tableVisibleMs=274`、`mindmapRuntimeRequested=false`。
|
||||
- [x] 因未复现,`5-36` 降为 reference;暂不改 modulepreload、runtime prewarm 或非关键 surface 初始化顺序。
|
||||
|
||||
### P1:Local-folder event bus
|
||||
|
||||
- [x] 已完成并归档 `design/03-rust-web/done/3-24-local-folder-browser-event-bus-v1.md`。
|
||||
- [x] sidebar、document session、resource tab 已订阅同一 local-folder event bus。
|
||||
- [x] `task540` 已验证同一页面只有一个 local-folder EventSource,且后台写入仍能触发 document refresh 与 localized filetree refresh。
|
||||
|
||||
### P2:Page Aggregate local-first hard guard
|
||||
|
||||
- [x] 已完成并归档 `design/05-editor-mainline/done/5-36-page-aggregate-local-first-hard-guard-v1.md`。
|
||||
- [x] 对 `compat.legacy_content` fallback 增加 local-first DOM diagnostic,`task541` 断言 local `.md` 为 `page_aggregate.block_document`。
|
||||
- [x] 保存链路未扩大 legacyBlocks 使用面,`task167` 覆盖 local-first title/body/options 写入回读。
|
||||
|
||||
### P3:7-18 agent edit clean/dirty smoke
|
||||
|
||||
- [x] 在不碰 LightRAG 的前提下,拆出并完成 `design/07-ai/done/7-18-agent-edit-clean-dirty-smoke-checklist-v1.md`。
|
||||
- [x] 验 clean buffer:agent 原生 patch 修改当前 `.md` 后,watcher -> BufferStore -> Page Aggregate -> ProseMirror 可见更新。
|
||||
- [x] 验 dirty buffer:外部 agent 写入不会静默覆盖,必须出现冲突或 review 状态。
|
||||
- [x] 验 readonly:写入型 run 在 ACP/tool 层前置拒绝,而不是只靠审计后置记录。
|
||||
- [x] 剩余归档项已在 `design/10-review/done/20-post-lightrag-runtime-hardening-checklist-v1.md` P1 完成:补真实后端 auditScope / allowed root 外写入拒绝证据后归档 7-18。
|
||||
|
||||
### P4:LightRAG post-commit audit gate
|
||||
|
||||
- [x] LightRAG 已提交并统一为 7-50 主线,旧 LiteParse / evidence / local OCR 默认路径已退役。
|
||||
- [x] 只读检查 7-50 post-commit 状态。
|
||||
- [x] 重点核对 source registry、watcher stale/delete/reindex、query scope、`locatorDegraded`、status bridge、image wrapper 与 smoke 是否互相一致。
|
||||
- [x] 发现的硬化项已拆到 `20-post-lightrag-runtime-hardening-checklist-v1.md` 并完成 `design/07-ai/done/7-51-lightrag-post-commit-hardening-v1.md`。
|
||||
|
||||
### P5:7-47 capability registry / local search / design governance follow-up
|
||||
|
||||
- [x] `20-post-lightrag-runtime-hardening-checklist-v1.md` 已继续完成 capability registry 口径重写、local search 边界和 design 状态治理。
|
||||
- [x] `7-46` / `7-48` 已降级到 `design/07-ai/reference/`,避免 worker 继续按旧 evidence / LiteParse 主线派活。
|
||||
|
||||
## 4. 非目标
|
||||
|
||||
- 不在本 checklist 中修改 LightRAG 实现。
|
||||
- 不删除历史设计证据;只通过移动目录、状态说明和新 checklist 降低误读。
|
||||
- 不把已归档文件中的旧 checklist 当作新 worker 的任务来源。
|
||||
Reference in New Issue
Block a user