收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目 - 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线 - 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径 验证: - cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1 - cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1 - git diff --check - git diff --cached --check - codegraph index . --force && codegraph status . - codegraph sync . && codegraph status .
This commit is contained in:
+22
-8
@@ -1,5 +1,9 @@
|
||||
# Office local-first 预览、编辑与插件噪音缺口审查 v1
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
|
||||
## 背景
|
||||
|
||||
本轮只审查 OnlyOffice 与 MNote local-first 工作区之间的三个接口缺口:
|
||||
@@ -24,9 +28,9 @@ Context7 查询 `/onlyoffice/api.onlyoffice.com` 得到的关键口径:
|
||||
- `rust/crates/mnote-web/src/routes/media.rs` 的 `/api/media/sign` 当前只服务 Convex media asset,内部查询 `mediaAssets:getById` / `mediaAssets:refreshUrl`。local-folder asset 形如 `local:asset:<path>`,不在 Convex media 表中,所以返回 404 是现状契约不匹配。
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice.rs` 的 `/onlyoffice` 页面在 `resolveAssetUrlAndKey()` 中只要有 `assetId` 就请求 `/api/media/sign`;失败后静默保留传入的 `fileUrl`。这就是“当前 view 模式不阻断打开”的原因。
|
||||
- local-folder Office 预览已经可以直接使用 `/api/local-folder/files/open?rootUri=...&path=...` 作为 `fileUrl`,不需要经过 `/api/media/sign`。
|
||||
- `/api/onlyoffice/callback` 当前仍代理 legacy Next writeback。对 local-folder 的 `status 2/6 -> 下载 body.url -> 原文件覆盖写回 -> watcher 同步` 没有完整闭环。
|
||||
- `layout.rs` 中正文附件菜单已有 `new-window`,但没有“使用编辑模式打开”。部分路径当前会默认生成 `mode=edit`,这与“默认只读、显式编辑”的产品口径不一致。
|
||||
- annotation 插件 404 / pageerror 暂未证明会影响文档渲染;当前更像是 OnlyOffice 静态插件包或自定义插件配置缺失导致的 console 噪音。
|
||||
- `/api/onlyoffice/callback` 已由 `5-36` 补齐 local-folder `status 2/6 -> 下载 body.url -> 原文件覆盖写回` 的后端契约;legacy Next 代理仍保留为 cloud/compat 边界。
|
||||
- `layout.rs` / browser runtime 已提供“使用编辑模式打开”入口,默认打开保持 `mode=view`,显式 edit 保留 guard。
|
||||
- annotation/custom assistant 插件噪音已由 `5-37` / `5-40` 分类:插件噪音不等于主文档失败,真实 `errorCode=-18`、WebSocket 失败或 iframe 空白不能被噪音掩盖。
|
||||
|
||||
## 根因判断
|
||||
|
||||
@@ -46,7 +50,7 @@ P0 目标:
|
||||
- 菜单提供“使用编辑模式打开”入口。
|
||||
- 编辑入口必须带 guard:清楚标记为实验能力,或在 local-folder writeback 未闭环时阻止/提示。
|
||||
|
||||
P1 目标才是实现 local-folder callback 写回。
|
||||
P1 目标是实现 local-folder callback 写回;该项已由 `5-36` 完成后端契约。
|
||||
|
||||
### 3. annotation 插件 404 / pageerror
|
||||
|
||||
@@ -70,7 +74,7 @@ P0 目标:
|
||||
- P0:若保存闭环未完成,编辑入口必须有 guard / 实验标记。
|
||||
|
||||
- `5-36-onlyoffice-local-edit-save-callback-contract-v1.md`
|
||||
- P1:设计或实现 local-folder callback 写回。
|
||||
- P1:local-folder callback 写回。
|
||||
- P1:覆盖 status 2/6、下载 URL rewrite、路径权限、冲突保护。
|
||||
|
||||
- `5-37-onlyoffice-annotation-plugin-noise-policy-v1.md`
|
||||
@@ -99,9 +103,19 @@ P0 目标:
|
||||
- 菜单提供“使用编辑模式打开”,进入前有实验 guard。
|
||||
- 已存在 Office resource tab 从 view 切 edit 会刷新同一 iframe URL,不再只激活旧 tab。
|
||||
- annotation/custom assistant 插件 404 已归类为 non-critical noise,不作为主文档打开失败。
|
||||
- P1 仍保留:
|
||||
- local-folder Office edit/save callback 写回闭环未实现,编辑模式仍不能承诺保存到原文件。
|
||||
- `onRequestEditRights` 事件重新初始化 edit URL 未实现。
|
||||
- P1 已拆分并收口:
|
||||
- `5-36`:local-folder Office edit/save callback 写回后端契约已完成,覆盖 status 2/6、非写状态、未认证、路径越界。
|
||||
- `5-35`:`onRequestEditRights` 事件会重新初始化为 `mode=edit` URL,不只 reload 当前 view。
|
||||
- 本 umbrella review 仅作为缺口审查和拆分记录归档;后续真实 Office 编辑保存端到端浏览器验收若发现新问题,应另建具体 bug。
|
||||
- 浏览器证据:
|
||||
- Reasonix:`tmp/reasonix-office-view-edit-plugin-2026-05-21/result.json` 与截图。
|
||||
- Codex:`tmp/codex-office-view-edit-plugin-2026-05-21/result.json`、`01-office-view-mode.png`、`02-office-edit-mode.png`。
|
||||
- 2026-06-01 验证:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_page -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_callback -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_local_callback -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p adapter-onlyoffice callback_preparation -- --test-threads=1`
|
||||
- `node --check scripts/task463-onlyoffice-resolver-smoke.js`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task463-onlyoffice-resolver-smoke.js`
|
||||
- `node --check scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
+21
-3
@@ -1,5 +1,9 @@
|
||||
# 5-35 Office 编辑模式菜单与保护
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
|
||||
## 目标
|
||||
|
||||
Office 文件默认只读打开;在正文附件三点菜单和文件树资源右键菜单中增加“使用编辑模式打开”入口。保存闭环未完成前,编辑入口必须有 guard 或明确实验标记。
|
||||
@@ -28,16 +32,30 @@ ONLYOFFICE `mode=edit` 只是编辑器初始化模式。若 MNote 没有完成 c
|
||||
- [x] 编辑模式入口明确使用 `mode=edit` 打开到主 resource tab 或新窗口,行为与现有打开目标一致。
|
||||
- [x] 在 local-folder save callback 未闭环时,编辑入口有 guard:可提示“编辑保存仍在实验中”,或通过 data/status 标记便于测试识别。
|
||||
- [x] 已存在 Office resource tab 从 `mode=view` 切到显式 `mode=edit` 时,刷新同一 tab 的 iframe URL,而不是只激活旧 tab。
|
||||
- [ ] 若启用 `onRequestEditRights`,必须重新初始化为 edit URL,不只 reload 当前 view。(P1,未实现)
|
||||
- [x] 若启用 `onRequestEditRights`,必须重新初始化为 edit URL,不只 reload 当前 view。
|
||||
|
||||
## 验收
|
||||
|
||||
- `cargo test -p mnote-web sidebar_tree_js -- --test-threads=1`
|
||||
- `cargo test -p mnote-web onlyoffice -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_opens_office_assets_through_resource_shell -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_page -- --test-threads=1`
|
||||
- 浏览器截图:默认打开是只读;菜单中存在编辑入口;点击编辑入口后的页面 URL / debug state 包含 `mode=edit` 或 guard 提示。
|
||||
|
||||
## 本轮执行记录
|
||||
|
||||
- 2026-06-01:Codex 补齐 `onRequestEditRights` P1。
|
||||
- `/onlyoffice` 页面新增 `editModeLocationHref()`,触发 `onRequestEditRights` 时将当前 URL 的 `mode` 设置为 `edit` 并 `window.location.replace(editHref)`,避免只 reload 当前 view。
|
||||
- 新增 `onlyoffice_page_reinitializes_edit_url_on_request_edit_rights` 契约测试,断言事件处理、debug 标记和 edit URL replacement 存在。
|
||||
- 复测 `task463` 时发现并修复普通 `openTarget="new-window"` 被 `forceNewWindow || forceEditMode` 误判成 `mode=edit` 的状态泄漏;现在只有 `forceEditMode` 才会进入 edit,普通新窗口继续默认 view / `/office-preview`。
|
||||
- 验证通过:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_page -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_opens_office_assets_through_resource_shell -- --test-threads=1`
|
||||
- `node --check scripts/task463-onlyoffice-resolver-smoke.js`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task463-onlyoffice-resolver-smoke.js`
|
||||
- `node --check scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
|
||||
- 2026-05-21:Reasonix worker B 卡在计划阶段后由 Codex 终止;本条实现来自其它 Reasonix 结果与 Codex 复核修正。
|
||||
- `buildOnlyOfficeOpenUrl` 默认 mode 从 `'edit'` 改为 `'view'`。
|
||||
- `buildOnlyOfficeOpenPath` 默认 mode 从 `'edit'` 改为 `'view'`。
|
||||
+30
-9
@@ -1,12 +1,16 @@
|
||||
# 5-36 OnlyOffice local-folder 编辑保存 callback 契约
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
|
||||
## 目标
|
||||
|
||||
为 local-folder Office 编辑保存建立 callback 写回契约。P1 可以先落设计和测试骨架;若实现,必须覆盖 status 2/6 的下载写回和路径安全。
|
||||
|
||||
## 原因
|
||||
|
||||
ONLYOFFICE 官方保存链路要求 callback `status === 2` 或 `status === 6` 时,集成后端下载 `body.url` 并写回原文件。当前 `mnote-web` callback 仍代理 legacy Next,local-folder 写回未闭环。
|
||||
ONLYOFFICE 官方保存链路要求 callback `status === 2` 或 `status === 6` 时,集成后端下载 `body.url` 并写回原文件。此前 `mnote-web` callback 仍代理 legacy Next,local-folder 写回未闭环。本轮已补齐 local-folder callback 写回路径。
|
||||
|
||||
## 允许修改
|
||||
|
||||
@@ -23,21 +27,38 @@ ONLYOFFICE 官方保存链路要求 callback `status === 2` 或 `status === 6`
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] 明确 local asset callback 定位:从 `assetId` / `fileUrl` / session 推导 rootUri 与 path。
|
||||
- [ ] status 非 2/6 时返回 `{ "error": 0 }`,不写文件。
|
||||
- [ ] status 2/6 时下载 rewritten `body.url`。
|
||||
- [ ] 写回前校验目标路径属于当前 local root allowed roots。
|
||||
- [ ] 写回后触发 watcher / projection 刷新或说明现有 watcher 如何感知。
|
||||
- [ ] 若本轮不实现完整写回,必须在编辑入口保留 guard,不让用户以为保存已支持。
|
||||
- [x] 明确 local asset callback 定位:callback URL 携带 `rootUri` / `path` / `sessionId` / `token`,并用 bridge session 校验 asset match。
|
||||
- [x] status 非 2/6 时返回 `{ "error": 0 }`,不写文件。
|
||||
- [x] status 2/6 时下载 rewritten `body.url`。
|
||||
- [x] 写回前校验目标路径属于当前 local root allowed roots。
|
||||
- [x] 写回后写入原始本地文件;后续页面刷新由现有 local-folder watcher / projection refresh 感知。浏览器编辑保存端到端 smoke 另列后续验收,不作为本 bug 阻塞项。
|
||||
- [x] 编辑入口仍保留 guard,不让用户误解为完整协作编辑能力已产品化。
|
||||
|
||||
## 验收
|
||||
|
||||
- `cargo test -p mnote-web onlyoffice_callback -- --test-threads=1`
|
||||
- `cargo test -p mnote-web -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_callback -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_local_callback -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p adapter-onlyoffice callback_preparation -- --test-threads=1`
|
||||
- 后续浏览器编辑保存验收需单独设计,不纳入 P0。
|
||||
|
||||
## 本轮执行记录
|
||||
|
||||
- 2026-06-01:Codex 复核当前实现并补齐 callback 写回回归测试。
|
||||
- `OnlyOfficeCallbackQuery` 已携带 `root_uri` / `path` / `session_id` / `token`。
|
||||
- `buildCallbackUrl()` 已把 local-folder Office asset 的 root/path/session/token 写入 callback query。
|
||||
- `local_folder_onlyoffice_callback()` 已校验 root/path、bridge session token、session asset match,并通过 `resolve_onlyoffice_local_file_path()` 阻断 root escape。
|
||||
- status 2/6 通过 `prepare_callback()` rewrite 后下载 `body.url` 并写回原始本地文件;非 2/6 返回成功且不写文件。
|
||||
- 新增/确认测试覆盖:
|
||||
- `onlyoffice_local_callback_rejects_unauthenticated_local_write`
|
||||
- `onlyoffice_local_callback_writes_status_two_body_to_original_file`
|
||||
- `onlyoffice_local_callback_writes_status_six_body_to_original_file`
|
||||
- `onlyoffice_local_callback_rejects_root_escape_path`
|
||||
- `onlyoffice_local_callback_ignores_non_write_status`
|
||||
- 验证通过:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_callback -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_local_callback -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p adapter-onlyoffice callback_preparation -- --test-threads=1`
|
||||
|
||||
- 2026-05-21:Reasonix worker C 分析完成。
|
||||
**结论:本轮不应实现完整写回。**
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
# 5-37 OnlyOffice annotation 插件噪音策略
|
||||
|
||||
> 2026-06-01 复核:本文是 2026-05-21 旧口径。后续 `5-40-onlyoffice-bridge-plugin-noise-regression-v1` 引入了新的 OnlyOffice bridge/plugin 注入口径,已覆盖“当前代码无 autostart 注入”的结论。本文仅保留 annotation/custom assistant 内置插件噪音分类;主文档失败分类、`errorCode=-18`、iframe 空白和 bridge plugin 噪音治理以 `5-40` 的最终分类为准。`5-40` 已通过 `task518-onlyoffice-real-iframe-session-scope-smoke.js` 的 `errorClassification` 验收,本文不再作为活跃 process 缺陷。
|
||||
|
||||
## 目标
|
||||
|
||||
定位 OnlyOffice annotation / custom assistant 插件 404 或 pageerror 来源,并把它从主打开失败中剥离:能禁用则禁用,不能禁用则在测试和日志中明确为 non-critical。
|
||||
@@ -25,7 +27,7 @@ ONLYOFFICE 插件通过 `editorConfig.plugins.autostart` 和 `pluginsData` 注
|
||||
- [x] 找到 annotation/custom assistant 插件配置或请求来源。
|
||||
- [x] 若 MNote 当前不依赖该插件,禁用无效 autostart / pluginsData。(确认当前代码已无 autostart 注入,无需额外禁用)
|
||||
- [x] 若属于 OnlyOffice 内置可选插件缺资源,记录为 non-critical,并更新浏览器测试过滤口径。
|
||||
- [ ] 补测试或文档,确保 `errorCode=-18` / WebSocket 失败仍被视为失败,不被插件噪音掩盖。(P1,后续补充)
|
||||
- [x] 补测试或文档,确保 `errorCode=-18` / WebSocket 失败仍被视为失败,不被插件噪音掩盖。(由 `5-40` / `task518-onlyoffice-real-iframe-session-scope-smoke.js` 的 `errorClassification.mainDocument` 覆盖)
|
||||
|
||||
## 验收
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# 5-40 ONLYOFFICE bridge 插件注入后噪音口径需复测
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:05-editor-mainline / OnlyOffice preview-edit / browser smoke
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
旧缺陷 `5-37` 的结论是当前代码没有注入 `plugins.autostart` / `pluginsData`,因此 annotation/custom assistant 插件 404 可作为 OnlyOffice 内置非关键噪音处理。但近期 ONLYOFFICE live bridge 已在 `onlyoffice.rs` 中重新注入 MNote bridge 插件,这会让旧噪音策略失效,必须重新区分 MNote bridge 插件失败、OnlyOffice 内置插件噪音和真正主文档加载失败。
|
||||
|
||||
## 证据
|
||||
|
||||
- `bugs/05-editor-mainline/process/5-37-onlyoffice-annotation-plugin-noise-policy-v1.md` 记录:“当前代码已无 autostart 注入,无需额外禁用”。
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice.rs` 当前 `editorConfig.plugins` 注入:
|
||||
- `autostart: [MNOTE_AGENT_PLUGIN_GUID]`
|
||||
- `pluginsData: [bridgePluginConfigUrl.toString()]`
|
||||
|
||||
## 影响
|
||||
|
||||
- clean browser 测试里 MNote bridge 插件加载失败可能被错误归类为“OnlyOffice 内置插件噪音”。
|
||||
- 相反,主 iframe 空白、`errorCode=-18`、DocumentServer WebSocket 失败也可能被泛化过滤掉。
|
||||
- Office live agent 能力上线后,插件加载失败将不再是纯噪音,而是 AI Office 工具不可用。
|
||||
|
||||
## 最小复现建议
|
||||
|
||||
1. clean browser 打开 docx/xlsx/pptx。
|
||||
2. 记录 console、pageerror、network 404/500、OnlyOffice iframe ready state。
|
||||
3. 分别验证:
|
||||
- MNote bridge 插件 config/index 是否成功加载。
|
||||
- OnlyOffice 内置 annotation/custom assistant 缺资源是否仍为 non-critical。
|
||||
- 主 iframe 空白、`errorCode=-18`、WebSocket 失败仍判定为失败。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- 更新 `5-37` 或新 smoke 的错误分类规则:MNote bridge 插件错误不等同于内置插件噪音。
|
||||
- 在 OnlyOffice smoke 中输出插件分类字段,例如 `mnoteBridgePluginOk`、`builtinPluginNoiseOnly`、`mainDocumentReady`。
|
||||
- 对 bridge 插件 config/index 增加 route 单测和 browser smoke 断言。
|
||||
|
||||
## 本轮进展
|
||||
|
||||
- 2026-06-01:新增并通过 `scripts/task517-onlyoffice-bridge-plugin-direct-smoke.js`,在真实 Chromium 中 mock `Asc.plugin` 后直接加载 bridge plugin index,验证 session 注册、token 错误拒绝、`selection.get` / `document.insert_text` command 消费和 result 回传。
|
||||
- 该 smoke 证明 MNote bridge plugin index / HTTP loop 自身可用,但不覆盖真实 ONLYOFFICE iframe / DocumentServer 的 autostart、内置插件噪音、主文档 ready 或 `errorCode=-18` 分类,因此本 bug 仍保留在 `process/`。
|
||||
- 2026-06-01:新增并通过 `scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`,在真实 ONLYOFFICE iframe / DocumentServer 下验证 MNote bridge 插件可 autostart 注册并回收命令。该 smoke 记录到的 404 均为 `/api/onlyoffice/bridge/plugin/index/translations/langs.json` 与 `/api/onlyoffice/bridge/plugin/index/translations/zh-CN.json`,当前可归类为 bridge 插件翻译资源噪声;它没有阻断 session 注册、`selection.get` 或工具层授权 dry-run。
|
||||
|
||||
- 2026-06-01:增强并通过 `scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`,输出 `errorClassification`:
|
||||
- `mnoteBridgePlugin.httpErrors=[]`
|
||||
- `mnoteBridgePlugin.sessionsRegistered=true`
|
||||
- `mnoteBridgePlugin.commandLoopOk=true`
|
||||
- `bridgeTranslationNoise.count=6`,仅包含 `/api/onlyoffice/bridge/plugin/index/translations/langs.json` 和 `zh-CN.json` 404
|
||||
- `builtinPluginNoise.count=0`
|
||||
- `mainDocument.ready=true`
|
||||
- `mainDocument.failures=[]`
|
||||
- `mainDocument.errorCodeMinus18=false`
|
||||
该分类把 MNote bridge 插件失败、bridge 翻译资源噪音、OnlyOffice 内置插件噪音和主文档失败分开;`errorCode=-18` / 主 iframe 空白不会被插件噪音吞掉。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Clean browser smoke 能区分三类错误:MNote bridge 插件失败、OnlyOffice 内置插件噪音、主文档加载失败。
|
||||
- [x] `errorCode=-18` / 主 iframe 空白不会被插件噪音过滤吞掉。
|
||||
- [x] bridge 插件不可用时,Office 文档预览结论和 Office AI 工具可用性结论分开报告。
|
||||
|
||||
## 最终验证
|
||||
|
||||
- `node --check scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
@@ -0,0 +1,57 @@
|
||||
# 5-41 Page Aggregate compat 瘦身剩余缺口
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:05-editor-mainline / Page Aggregate / browser document conversion
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
Page Aggregate 已经完成 local-first 主链和多个单一真源修复。2026-06-01 复核后,`body.content`、compat join 和浏览器 legacy content 降级被明确收口为 v1 兼容面:它们仍是 active runtime 的兼容读写字段,但不再作为 local-first 浏览器主事实源。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/core-protocol/src/page_aggregate.rs` 中 `PageBody.content` 仍是必填字段。
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_source.rs` 的 local-first aggregate 仍会填充 `body.content`。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs` 的 cloud/compat aggregate 路径仍支持 `CompatMetaContentJoin`。
|
||||
- `rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js` 已在 2026-06-01 调整为 `blockDocument/editorDocument` 优先,`local_markdown.content` 与 `compat.legacy_content` 只作为缺少 block document 时的降级路径;`scripts/task522-page-aggregate-compat-fallback-contract.js` 覆盖了这几个 source 分支。
|
||||
- `rust/crates/mnote-web/src/routes/documents.rs` 的保存兼容面仍同时携带 `editorDocument`、`content`、`tiptapDocument`。
|
||||
- `rust/crates/mnote-web/src/page_aggregate/builder.rs` 的默认 source 已在 2026-06-01 改为 `KernelProjection`,显式 `.source(PageAggregateSource::CompatMetaContentJoin)` 仍保留 compat 路径。
|
||||
|
||||
## 影响
|
||||
|
||||
- “Page Aggregate 单一真源”仍保留 v1 兼容数据面:前端和后端都需要继续维护 legacy content,但只作为镜像 / fallback。
|
||||
- local-first 浏览器转换层已优先消费 block document;后端协议和 compat 保存面继续保留 legacy content,保证 cloud/compat fallback 和旧保存面不被破坏。
|
||||
- 后续若要删除或改为可选 `body.content`,必须进入 `mnote.page_aggregate.v2` 或等价协议升级 checklist,不能在 v1 中直接删除。
|
||||
|
||||
## 下一步建议
|
||||
|
||||
- `PageBody.content` 在 `mnote.page_aggregate.v1` 中继续保持必填兼容字段,用于 legacy/cloud/compat fallback、旧保存面和降级回读。
|
||||
- `body.blockDocument` / `editorDocument` 是 local-first 浏览器主消费面;`body.content` 不再作为 active editor 初始化的优先事实源。
|
||||
- `CompatMetaContentJoin` 继续只作为显式 cloud/compat substrate 边界保留;`PageAggregateBuilder::new()` 默认 source 保持 `KernelProjection`。
|
||||
- `body.content` 改为可选或删除的退出条件:必须先有 `mnote.page_aggregate.v2` 协议或迁移 checklist,证明 cloud/compat 保存面、外部 agent、历史 fixture 和降级读取全部不再依赖该字段。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] local-first 浏览器转换优先消费 `body.blockDocument`,`pageBodyTiptapDocumentSource(...)` 在 `blockDocument` 存在时返回 `page_aggregate.block_document`。
|
||||
- [x] `body.content` 仅在缺少 `blockDocument` / `editorDocument` 时作为 local markdown legacy fallback。
|
||||
- [x] Builder 默认 source 不再是 `CompatMetaContentJoin`。
|
||||
- [x] `compat.legacy_content` 降级路径有明确 source 标记和测试覆盖。
|
||||
- [x] 设计稿明确 `body.content` 的长期状态:v1 保留为必填兼容镜像 / fallback;v2 或后续协议升级再评估可选或删除。
|
||||
|
||||
## 2026-06-01 验证
|
||||
|
||||
- `node --check rust/crates/mnote-web/browser/document-tiptap-conversion-runtime.js`
|
||||
- `node --check scripts/task522-page-aggregate-compat-fallback-contract.js`
|
||||
- `node scripts/task522-page-aggregate-compat-fallback-contract.js`
|
||||
- `cargo test -p mnote-web document_shell_returns_page_aggregate_snapshot -- --test-threads=1`
|
||||
- `cargo test -p mnote-web page_aggregate::builder::tests -- --test-threads=1`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js`
|
||||
|
||||
## 2026-06-01 收口决策
|
||||
|
||||
- `PageBody.content`:v1 保留必填,语义降级为兼容镜像 / fallback,不作为 local-first active editor 主事实源。
|
||||
- `body.blockDocument` / `editorDocument`:local-first 浏览器转换优先消费的 canonical block view。
|
||||
- `compat.legacy_content`:仅当缺少 `blockDocument` / `editorDocument` 时可被浏览器转换层使用,并通过 `task522-page-aggregate-compat-fallback-contract.js` 固化 source 标记。
|
||||
- `CompatMetaContentJoin`:显式 compat substrate 边界,保留到 cloud/legacy 保存面完成协议升级,不再是 builder 默认 source。
|
||||
Reference in New Issue
Block a user