收口 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:
@@ -0,0 +1,46 @@
|
||||
# 3-26 Sidebar dev hot reload 在主 runtime 中使用 setInterval
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:03-rust-web / browser runtime
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
`sidebar-tree-runtime.js` 在主 Sidebar runtime 中安装 dev hot reload,并无前置环境 guard 地执行 `window.setInterval(tick, 1000)`。项目架构约束要求浏览器主链默认禁止新增基于 `setInterval`、周期 `setTimeout` 或轮询 fallback 的数据刷新和状态同步;即使这是 dev hot reload,也应该显式限制在 debug/dev 边界。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/browser/sidebar-tree-runtime.js` 中 `installMnoteDevHotReload()` 执行:
|
||||
- 先调用 `tick()`。
|
||||
- 再执行 `timer = window.setInterval(tick, 1000)`。
|
||||
- 末尾无条件调用 `installMnoteDevHotReload()`。
|
||||
- `tick()` 每次请求 `/api/dev/hot-reload`,服务端返回 `enabled !== true` 时才清理 timer;这仍意味着页面启动后先进入轮询逻辑。
|
||||
|
||||
## 影响
|
||||
|
||||
- 主 Sidebar runtime 默认携带 dev 轮询逻辑,和“主链不叠加轮询 fallback”的架构约束冲突。
|
||||
- 若 `/api/dev/hot-reload` 异常或返回形态变化,可能引入重复请求、console 噪音或性能误判。
|
||||
- 后续 worker 可能把这种模式复制到其他可见路径。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- 在 Rust SSR bootstrap 中显式注入 dev/hot 模式标记,只有 dev hot 模式启用时才安装该逻辑。
|
||||
- 或将 hot reload 逻辑迁到 debug/internal runtime,不放在默认 Sidebar runtime。
|
||||
- 如果仍保留定时器,必须在设计/注释中说明退出条件和边界,并保证生产/普通 desktop 模式不可达。
|
||||
|
||||
## 本轮进展
|
||||
|
||||
- 2026-05-31:
|
||||
- `sidebar-tree-runtime.js` 已改为通过 `import.meta.url` 检查当前模块 URL 是否携带 `devHot` cache buster;只有 dev hot 模式才安装 hot reload 轮询。
|
||||
- 已补 `layout.rs` 静态断言,禁止无条件 `installMnoteDevHotReload()` 回流。
|
||||
- 已通过 `node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js` 和 Rust 定点测试。
|
||||
- 已补 `scripts/task514-sidebar-dev-hot-reload-gating-smoke.js` 固化真实浏览器验证。
|
||||
- 验证通过:`node scripts/task514-sidebar-dev-hot-reload-gating-smoke.js`。结果显示普通入口 `hotReloadRequestCount=0`、`scriptHasDevHot=false`;hot 模式 `scriptHasDevHot=true`、`hotReloadRequestCount=2`、`data-mnote-dev-hot-reload=enabled`,且无 console/network 错误。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] 普通 `desktop` / 非 hot 入口打开后,浏览器不会请求 `/api/dev/hot-reload`。
|
||||
- [x] `npm run desktop:hot` 或等价 hot 模式下仍能刷新。
|
||||
- [x] `setInterval(tick, 1000)` 仅在 `mnoteDevHotReloadEnabled()` guard 后可达。
|
||||
@@ -0,0 +1,103 @@
|
||||
# 3-27 MinerU OCR 后端 runtime 已落地但缺前端任务链
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:process
|
||||
- Owner:03-rust-web / local-folder OCR / MinerU sidecar
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
P2 目标中包含 MinerU OCR 能力收口。当前已补后端 mock route、真实 MinerU HTTP client、sidecar 写入、`.mnote/ocr-index.json`、`includeOcr=true` 搜索命中和浏览器 API smoke。但 active job store、realtime job event、前端入口/任务栏仍未落地。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/local_ocr.rs` 已注册 `jobs/status/read/insert`,`provider=mock` 能写入 `{pageStem}.ocr/*.ocr.md` 和 `.mnote/ocr-index.json`。
|
||||
- `provider=mineru` 在有 token 时会走真实 HTTP client:申请上传 URL、PUT 上传、轮询结果、下载 zip、提取 Markdown,并写入 `{pageStem}.ocr/*.ocr.md` / `.mnote/ocr-index.json`。该路径已有本地 HTTP mock 单测覆盖,不访问外网。
|
||||
- `local_search_index` 已支持 `includeOcr=true` 命中 OCR owner page;`task526-local-folder-ocr-api-smoke.js` 已覆盖浏览器上下文调用 mock OCR API、搜索命中和显式 insert 链接。
|
||||
- 设计中的活动任务生命周期、`local_ocr.job.updated` 和浏览器可见入口仍未完成。
|
||||
|
||||
## 影响
|
||||
|
||||
- 用户无法从本地图片或图片型 PDF 通过 UI 手动触发 MinerU OCR。
|
||||
- 搜索的 `includeOcr=true` 已能命中 OCR sidecar,但真实 MinerU 结果仍缺 UI 触发链和任务状态可见性。
|
||||
- AI 资源上下文无法优先读取已有 OCR Markdown。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 已完成后端 mock 最小闭环:路径规划、sidecar frontmatter、`.mnote/ocr-index.json`、mock OCR job route、status/read、Page Tree 过滤和 `includeOcr=true` 搜索 owner page。
|
||||
2. 已补浏览器 API smoke:mock OCR、status/read/jobs、`includeOcr=true` 搜索 owner page 和显式 insert link。
|
||||
3. 下一步接长任务状态、`local_ocr.job.updated` realtime 事件、前端 OCR 入口/任务栏和 AI 资源上下文读取 OCR sidecar。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Rust route/helper 测试覆盖 sidecar 路径/frontmatter、index stale/error redaction、mock OCR job 写入、status/read、无 token、越界拒绝、非图片/PDF 拒绝、mock 失败 response 脱敏和显式 insert link。
|
||||
- [x] local search 测试覆盖 OCR 命中返回 owner page,OCR sidecar 不作为普通页面。
|
||||
- [x] Page Tree 测试覆盖 OCR sidecar 可在 File Tree 作为资源文件可见,但不作为普通页面进入 Page Tree。
|
||||
- [x] Rust route 测试覆盖无 token response 形态。
|
||||
- [x] Rust route 测试覆盖 `provider=mineru` 后端成功路径:申请上传 URL、PUT 上传、轮询结果、下载 zip、提取 Markdown、写 sidecar/index。
|
||||
- [~] 浏览器 smoke 覆盖图片/PDF 手动 OCR、任务栏状态、打开 OCR、搜索 OCR 命中和插入 OCR 链接。(当前 `task526` 覆盖 mock OCR API、搜索命中、显式 insert link 和 OCR sidecar resource tab 打开;真实 UI 入口/任务栏待做)
|
||||
|
||||
## 2026-06-01 后端 mock 闭环记录
|
||||
|
||||
新增 `rust/crates/mnote-web/src/routes/local_ocr.rs`,注册:
|
||||
|
||||
- `POST /api/local-folder/ocr/jobs`
|
||||
- `GET /api/local-folder/ocr/jobs`
|
||||
- `GET /api/local-folder/ocr/status`
|
||||
- `GET /api/local-folder/ocr/read`
|
||||
- `POST /api/local-folder/ocr/insert`
|
||||
|
||||
当前真实 MinerU HTTP client 仍未接入;`provider=mock` 用于本地后端闭环和测试,`provider=mineru` 在缺少 token 时返回 `mineru_token_missing`,有 token 时返回 `mineru_runtime_not_enabled`,避免伪装真实能力已完成。
|
||||
|
||||
已通过验证:
|
||||
|
||||
- `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 local_search_ocr -- --test-threads=1`
|
||||
|
||||
2026-06-01 续补 route 边界测试:
|
||||
|
||||
- 越界来源路径拒绝:`local_ocr_path_escape`
|
||||
- 非图片/PDF 来源拒绝:`local_ocr_source_type_unsupported`
|
||||
- provider `mineru` 且缺 token 时拒绝:`mineru_token_missing`
|
||||
- mock 失败响应与 `.mnote/ocr-index.json` 失败条目统一脱敏为 `provider_error_redacted`
|
||||
- 显式 `ocr/insert` link 模式把 `[OCR:photo.png](./Page.ocr/photo.png.ocr.md)` 追加进 owner Markdown;未调用 insert 时不改正文
|
||||
|
||||
本文继续保持 `process`:前端 OCR 入口、任务栏和 active job / realtime event 尚未完成。
|
||||
|
||||
2026-06-01 只读复核更新:
|
||||
|
||||
- Subagent 复核确认:route/mock/search 测试已落地,早期“没有实际 OCR route”的描述已经过时。
|
||||
- 本 bug 不归档的阻塞点更新为:真实 MinerU HTTP client、active job store / `local_ocr.job.updated`、前端入口/任务栏、AI 资源上下文读取 OCR sidecar 和完整 UI browser smoke。
|
||||
|
||||
2026-06-01 浏览器 API smoke 续补:
|
||||
|
||||
- 新增 `scripts/task526-local-folder-ocr-api-smoke.js`,在真实浏览器登录态页面内通过 `fetch` 串起 `POST /api/local-folder/ocr/jobs`、`status`、`read`、`jobs`、`/api/search/documents includeOcr=true` 和 `POST /api/local-folder/ocr/insert`。
|
||||
- 已验证 `provider=mock` 会生成 `docs/Page.ocr/photo.png.ocr.md`,`includeOcr=false` 不命中 OCR 文本,`includeOcr=true` 返回 owner page 且带 `hasOcr/ocrEvidence`,显式 insert 才向 owner Markdown 追加 `[OCR:photo.png](...)`。
|
||||
- 续补后已验证 OCR sidecar 可作为 Markdown resource tab 打开,截图写入 `tmp/task526-local-folder-ocr-api-smoke/02-ocr-resource-tab.png`。
|
||||
- 已通过:
|
||||
- `node --check scripts/task526-local-folder-ocr-api-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/task526-local-folder-ocr-api-smoke.js`
|
||||
- `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 local_search_ocr -- --test-threads=1`
|
||||
|
||||
2026-06-01 MinerU API 口径复核:
|
||||
|
||||
- 官方文档当前仍以 `https://mineru.net/api/v4/extract/task` 或 `https://mineru.net/api/v4/file-urls/batch` 作为精准解析入口;`model_version` 支持 `pipeline` / `vlm` / `MinerU-HTML`,Markdown/JSON 为默认结果格式,图片/PDF/Office 等文档受文件大小与页数限制。
|
||||
- 当前设计稿中的“申请上传地址 -> PUT 上传 -> 轮询批量结果 -> 下载 zip -> 提取 Markdown”方向仍成立。
|
||||
- 旧回归测试曾要求 `MNOTE_MINERU_API_TOKEN` 存在且 provider 为 `mineru` 时返回 `mineru_runtime_not_enabled`;该口径已被真实后端 runtime 合同测试替代。
|
||||
|
||||
## 2026-06-01 后端 MinerU runtime 补齐记录
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/local_ocr.rs` 已补 `mineru_api_base_url`、`mineru_poll_interval`、`mineru_max_polls` 配置函数。
|
||||
- `provider=mineru` 后端路径已接入 `run_mineru_ocr`,执行申请上传 URL、PUT 上传、轮询 batch 结果、下载 zip、提取 Markdown。
|
||||
- 新增本地 HTTP mock 测试 `local_ocr_jobs_route_runs_mineru_runtime_against_http_mock`,不访问外网,验证真实 client 合同和 sidecar/index 写入。
|
||||
- 已通过:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1`
|
||||
|
||||
本文继续保持 `process` 的剩余原因:
|
||||
|
||||
- 前端本地文件 / 图片 / PDF 资源没有 OCR 操作入口。
|
||||
- OCR jobs 仍是同步请求 + `.mnote/ocr-index.json` 历史状态,没有 active job store。
|
||||
- 缺少 `local_ocr.job.updated` realtime event 与全局任务栏 UI。
|
||||
- AI 资源上下文还没有优先读取 OCR sidecar。
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# 4-52 resource lifecycle 兼容路径在 Convex 退役后丢失 artifact / 拒绝 mindmap lifecycle
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:04-tree-domain / bridge-runtime / resource lifecycle command
|
||||
- 发现时间:2026-06-01
|
||||
- 修复时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
执行 `cargo test -p mnote-web mindmap -- --test-threads=1` 时,mindmap 相关宽测曾失败:
|
||||
|
||||
- `mindmap_put_derives_workspace_and_returns_tree_artifacts`
|
||||
- `mindmap_command_apply_returns_object_artifacts_without_tree_resync`
|
||||
- `mindmap_delete_restore_keeps_markdown_reference_out_of_lifecycle_command`
|
||||
- `mindmap_trash_routes_delete_restore_purge_and_empty`
|
||||
- `sidebar_runtime_routes_local_mindmap_and_office_assets`
|
||||
|
||||
前两个失败是 `execute_retired_command_plan_with_artifacts()` 在 Convex 退役后仍能执行 fixture mutation,但把 Rust artifact plan 置为 `None`,导致调用方拿不到 `commandLog` / `domainEvent`。
|
||||
|
||||
后两个失败是 `tree.resource.archive/restore/purge/rename` 在 bridge-runtime 生成 resource-kind 专用函数名前,会先经过 `build_write_request()`;旧 `legacy_command_function_name()` 没有这些正式 tree resource command 名,导致 mindmap lifecycle 被 `retired_bridge_error()` 拒绝。
|
||||
|
||||
最后一个失败是 runtime 拆分后,OnlyOffice local open 逻辑已迁到 `sidebar-filetree-open-runtime.js`,但 Rust include 断言仍检查旧 `sidebar-tree-runtime.js` 字符串。
|
||||
|
||||
## 根因
|
||||
|
||||
- Convex 退役后,artifact 持久化不能再作为兼容路径成功条件;Rust artifact plan 应继续返回给调用方和 realtime consumer。
|
||||
- resource lifecycle command 的正式命名已从表面 compat alias 收口到 `tree.resource.*`,但旧 Convex 兼容映射表未允许这些名称通过前置 request 构造。
|
||||
- browser runtime 模块拆分后,测试断言没有同步到新的 canonical owner 文件。
|
||||
|
||||
## 修复
|
||||
|
||||
- `rust/crates/mnote-web/src/transport/convex.rs`
|
||||
- `persist_runtime_command_artifacts()` 在 Convex 退役路径改为 no-op 成功。
|
||||
- `execute_retired_command_plan_with_artifacts()` 重新构造并返回 `RuntimeCommandArtifactPlan`,不重新启用 Convex artifact 持久化。
|
||||
- `rust/crates/bridge-runtime/src/lib.rs`
|
||||
- `legacy_command_function_name()` 允许 `tree.resource.archive/restore/purge/rename` 通过前置 request 构造;最终函数名仍由 resource lifecycle plan 按资源类型分流。
|
||||
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
|
||||
- OnlyOffice local open 断言改查 `SIDEBAR_FILETREE_OPEN_RUNTIME_JS`。
|
||||
- `rust/crates/mnote-web/src/routes/resource_trash.rs`
|
||||
- `delete_json()` test helper 在状态码失败时输出 body,便于后续定位真实错误码。
|
||||
|
||||
## 验证
|
||||
|
||||
- `cargo test -p mnote-web mindmap -- --test-threads=1`
|
||||
|
||||
结果:26 个 mindmap 相关测试全部通过。
|
||||
@@ -0,0 +1,33 @@
|
||||
# 4-53 WorkspacePath / ObjectIdentity 浏览器矩阵缺口
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:04-tree-domain / 05-editor-mainline runtime identity
|
||||
- 发现时间:2026-06-01
|
||||
- 修复时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
P1 `WorkspacePath / ObjectIdentity` 已有底层协议与部分 smoke,但缺少一个单独覆盖 page `.md`、directory、raw file、mindmap、OnlyOffice 的浏览器矩阵。FileTree row、Resource Tab、OpenEditorsSnapshot、URL `resourceTab` 和 active tab identity 如果各自拼身份,后续 raw resource / mindmap / Office target 容易继续退化为“当前页”语义。
|
||||
|
||||
## 根因
|
||||
|
||||
已有 smoke 分散覆盖 copy-id、resource tab、mindmap 和 Office,但没有把统一 `ObjectWorkspacePath` 当作运行时消费合同统一断言。local folder 目录行还缺少 `local-dir:*` 的稳定 `documentId` fallback,resource tab 在部分本地资源打开路径下也缺少 `sourceKind` / `rootUri` fallback。
|
||||
|
||||
## 修复
|
||||
|
||||
- `rust/crates/mnote-web/browser/filetree-runtime.js`
|
||||
- `readWorkspacePathFromRow()` 为 folder / index 行合成 `local-dir:{relativePath}`。
|
||||
- `fileTreeRowDocumentId()` 允许读取 `data-owner-document-id`,用于资源行 owner page 对齐。
|
||||
- `rust/crates/mnote-web/browser/document-resource-tab-runtime.js`
|
||||
- 在 URL 参数缺失时,从 body dataset 或 FileTree row workspacePath 兜底读取 `sourceKind` / `rootUri`。
|
||||
- `scripts/task524-workspace-object-identity-matrix-smoke.js`
|
||||
- 新增 browser smoke,覆盖 page `.md`、directory、raw text、mindmap、OnlyOffice 的 FileTree row workspacePath、OpenEditorsSnapshot、URL `resourceTab`、active tab identity 对齐。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js`
|
||||
- [x] `node --check rust/crates/mnote-web/browser/filetree-runtime.js`
|
||||
- [x] `node --check scripts/task524-workspace-object-identity-matrix-smoke.js`
|
||||
- [x] `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task524-workspace-object-identity-matrix-smoke.js`
|
||||
+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。
|
||||
@@ -0,0 +1,69 @@
|
||||
# 7-45 ONLYOFFICE live bridge session 隔离风险
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / OnlyOffice live bridge / mnote-web
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
ONLYOFFICE live bridge session 当前是进程内全局状态。工具调用未显式传 `onlyofficeSessionId` / `bridgeSessionId` 时,会 fallback 到最近活跃 session;同一文档的 bridge session id 又由 `docKey` 派生,重复打开同文档或多用户/多 tab 打开不同 Office 文档时,存在命令落到错误 Office tab 或 token 覆盖的风险。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice.rs` 生成 `bridgeSessionId = "mnote-oo-" + fileState.docKey`。
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice_bridge.rs` 的 `register_session` 按 `session_id` 覆盖 `token`、`document_id`、`asset_id` 和 `last_seen_millis`。
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice_bridge.rs` 的 `current_session_info()` 直接取全局 `last_seen_millis` 最大值。
|
||||
- `rust/crates/mnote-web/src/hermes_tools/onlyoffice_live.rs` 的 `resolve_session_id()` 在未传 session id 时 fallback 到 `current_session_info()`。
|
||||
|
||||
## 影响
|
||||
|
||||
- Page AI 或 Hermes tool 可能在多 Office tab 场景写错目标文档。
|
||||
- 同一文档重复打开可能互相覆盖 bridge token,使旧 tab 的插件长轮询或结果回传失效。
|
||||
- 多用户共享同一 mnote-web 进程时,全局最近 session 可能跨用户泄漏目标选择。
|
||||
|
||||
## 最小复现建议
|
||||
|
||||
1. 打开两个不同 Office 文档,或同一文档两个 tab。
|
||||
2. 不传 `onlyofficeSessionId` 调用 `mnote.onlyoffice.session.current` 与一个写工具,例如 `mnote.onlyoffice.document.insert_text`。
|
||||
3. 观察返回 session 是否只由最近活跃 tab 决定。
|
||||
4. 同一文档双 tab 复测 token 覆盖后,旧 tab `commands/next` 或 `results` 是否返回 401 / 无结果。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- bridge session id 加入 browser tab 级随机后缀,不只使用 `docKey`。
|
||||
- Page AI run payload 必须携带当前 resource tab 的 explicit `onlyofficeSessionId`;写工具禁止默认 fallback 写入。
|
||||
- `current_session` 只能作为只读诊断工具,不能作为写工具默认目标。
|
||||
- session state 至少按 actor/session/document/resource 维度过滤。
|
||||
|
||||
## 本轮进展
|
||||
|
||||
- 2026-05-31:
|
||||
- `onlyoffice.rs` 已把 `bridgeSessionId` 从仅基于 `docKey` 改为 `docKey + bridgeSessionSalt`,避免同文档多 tab 共用同一个 bridge session id。
|
||||
- `onlyoffice_live.rs` 已禁止读/写 action fallback 到全局最近 session;读写工具必须显式传 `onlyofficeSessionId` / `bridgeSessionId`。
|
||||
- `onlyoffice.rs` 的本地 callback 已绑定 bridge `sessionId` + `token`,未认证本地写回返回 401 并保持原文件不变。
|
||||
- 已补 Rust 定点测试覆盖缺 explicit session 的读/写工具拒绝,以及未认证 local callback 拒绝。
|
||||
- 已补 `scripts/task515-onlyoffice-live-scope-http-smoke.js` 覆盖 HTTP 层缺 explicit session 返回 400。
|
||||
- 已补并通过 `scripts/task516-onlyoffice-bridge-multisession-browser-smoke.js`,在真实浏览器中验证 A/B 两个 bridge session 的 token 校验、command queue 和 result 回收互不串台,错误 token 返回 401。
|
||||
- 2026-06-01:
|
||||
- 已补并通过 `scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`,在真实 ONLYOFFICE iframe / DocumentServer 下打开同一 docx 两个 tab 和另一个 docx,验证同文档双 tab 共享 `docKey` 但使用不同 `bridgeSessionId`,三个插件 session 均能注册并各自回收 `selection.get` command。
|
||||
- `task518` 同时覆盖显式传 B session 但 scope=A 时返回 403,以及 scope=B 授权 dry-run 返回 200,证明真实 iframe session 能被工具层按显式 session 和 resource scope 约束。
|
||||
- Page AI open editors snapshot / target package 已开始保留 Office `onlyofficeSessionId` / `bridgeSessionId`,服务端 `agentTargetPackage` 与 `aiAccessScope.allowedResourceIds` 已能保留 Office asset、session 和 `resource:onlyoffice:{documentId}:{assetId}` 候选;新增 Rust 定点测试 `hermes_client_run_body_preserves_onlyoffice_target_scope` 覆盖该合同。
|
||||
- ONLYOFFICE iframe 在设置 `__MNOTE_ONLYOFFICE_DEBUG__` 后会向父窗口发送 `mnote:onlyoffice-bridge-ready`,resource tab runtime 收到后事件驱动刷新 open editors snapshot;Page AI 发送前也会拒绝没有 `onlyofficeSessionId` 的 Office target,避免 bridge 未就绪时发起 run。
|
||||
- `task518` 已扩展并通过非 dry-run 写入落点验证:通过 `mnote.onlyoffice.document.insert_text` 向 Office B 写入唯一 marker,再通过真实 iframe bridge `document.export` 导出 A/B 内容,断言 B 包含 marker 且 A 不包含;截图保存到 `tmp/task518-onlyoffice-real-iframe-session-scope-smoke/screenshots/office-a-after-write.png` 和 `office-b-after-write.png`。
|
||||
- 已补并通过 `scripts/task523-page-ai-onlyoffice-real-target-session-smoke.js`:在真实文档页内打开 Office resource tab,等待 iframe bridge ready 后通过 Page AI target picker 选择 Office target,断言 `editorTarget` 与 `targetPackage` 均冻结真实 `onlyofficeSessionId` / `bridgeSessionId`,并断言无 `consoleErrors` / `networkFailures` / `httpErrors`。
|
||||
|
||||
当前 P0 安全阻断已覆盖“工具不能隐式读写全局最近 session”“callback 不能未认证写本地文件”“bridge session/token/queue 在浏览器 HTTP 层隔离”“真实 iframe 插件 session 显式授权边界”“Page AI run scope 不再被服务端压回当前页面 id”“Office bridge 未就绪时 Page AI 发送前拒绝”“真实 iframe 非 dry-run 写入不串台”和“真实 Page AI UI 从 Office iframe 目标取到 live `bridgeSessionId` 并冻结进 run payload”。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Rust 单测覆盖页面生成同 docKey 双 session 随机 session id。
|
||||
- [x] Rust 单测覆盖读/写工具缺 explicit session id 时拒绝。
|
||||
- [x] Rust 单测覆盖本地 callback 缺 bridge session/token 时拒绝。
|
||||
- [x] Browser smoke 覆盖两个 bridge session 的 token、command queue 与 result 隔离。
|
||||
- [x] Browser smoke 覆盖两个真实 Office iframe tab 下插件注册、sessionId 隔离和 command 回收。
|
||||
- [x] Rust 单测覆盖 Page AI Office target package / aiAccessScope 保留 session 与 resource scope。
|
||||
- [x] 前端事件驱动刷新覆盖 ONLYOFFICE bridge ready 消息,发送前拒绝缺 session 的 Office target。
|
||||
- [x] Browser smoke 覆盖两个真实 Office iframe tab 下非 dry-run 写入目标不串台。
|
||||
- [x] Browser smoke 覆盖 Page AI target picker 从真实 Office iframe target 读取 live `onlyofficeSessionId` 并写入 run payload。
|
||||
@@ -0,0 +1,66 @@
|
||||
# 7-46 ONLYOFFICE live tool 缺少 resource scope 绑定
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Hermes tools / OnlyOffice live bridge
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
ONLYOFFICE live 写工具当前主要检查 `idempotencyKey`、`dryRun`、`aiAccessScope.permissionLevel` 和 command context 写权限,但没有把 bridge session 的 `documentId` / `assetId` 与 `aiAccessScope.allowedResourceIds` 绑定。调用方如果显式传入另一个已注册 Office session,存在越过当前 target resource scope 写入非授权资源的风险。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs` 注册了多组 `mnote.onlyoffice.*` 读写工具。
|
||||
- `rust/crates/mnote-web/src/hermes_tools/onlyoffice_live.rs` 的 `run_write_action()` 调用 `ensure_write_authorized()` 后立即解析 session 并执行 bridge command。
|
||||
- `rust/crates/mnote-web/src/hermes_tools/onlyoffice_live.rs` 的 `resolve_session_id()` 只解析显式 session 或全局 current session,没有验证该 session 对应的 resource 是否在 `allowedResourceIds` 内。
|
||||
- 对照 `rust/crates/mnote-web/src/hermes_tools/resource.rs`,mindmap/resource 工具已有 `ensure_resource_scope_allowed()` 校验 `allowedResourceIds` / `objectIdentity`。
|
||||
|
||||
## 影响
|
||||
|
||||
- Page AI 当前 target 是资源 A 时,模型或恶意调用可传入资源 B 的 `bridgeSessionId`,尝试写入 B。
|
||||
- 写入审计会显示工具有写权限,但缺少“写的是哪个 resource、是否被 allowedResourceIds 授权”的闭环。
|
||||
- 多 tab session fallback 与本 bug 叠加时,错误写入更难被用户发现。
|
||||
|
||||
## 最小复现建议
|
||||
|
||||
1. 构造 `aiAccessScope.allowedResourceIds = [A]`。
|
||||
2. 注册两个 ONLYOFFICE bridge session:A 和 B。
|
||||
3. 调用 `mnote.onlyoffice.sheet.set_value` 或 `mnote.onlyoffice.document.insert_text`,显式传 B 的 `bridgeSessionId`。
|
||||
4. 期望:返回 403;当前风险:只要通用写权限通过就可能执行。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- `BridgeSessionInfo` 暴露稳定 `documentId`、`assetId`、`objectIdentity`。
|
||||
- ONLYOFFICE live 工具增加与 resource 工具等价的 scope 校验。
|
||||
- 写工具返回 receipt 时包含 `resourceKind=office`、`documentId`、`assetId`、`onlyofficeSessionId` 与 permission decision。
|
||||
- dry-run 也必须执行 scope 校验,不能只返回 wouldWrite。
|
||||
|
||||
## 本轮进展
|
||||
|
||||
- 2026-05-31:
|
||||
- `onlyoffice_live.rs` 已在读/写 action 执行前校验 explicit session 对应的 `sessionId` / `documentId` / `assetId` / `resource:office:{documentId}:{assetId}` 是否包含在 `aiAccessScope.allowedResourceIds`。
|
||||
- 缺失 `aiAccessScope` 或空 `allowedResourceIds` 现在返回 403,不再兼容放行;dry-run 同样执行该 scope 校验。
|
||||
- `manifest.rs` 已为 OnlyOffice live 工具声明 `aiAccessScope.allowedResourceIds` 和 `onlyofficeSessionId` / `bridgeSessionId` 的 `anyOf` 合同。
|
||||
- 已补 Rust 定点测试覆盖 `allowedResourceIds=[asset_a]` 时禁止写入 session `asset_b`,`allowedResourceIds=[asset_allowed]` 时允许生成 dry-run plan,缺 scope 时返回 403,以及 manifest 合同。
|
||||
- 已补并通过 `scripts/task515-onlyoffice-live-scope-http-smoke.js`:HTTP 层覆盖缺 explicit session 400、scope 不匹配 403、缺 scope 403、授权 scope dry-run 200。
|
||||
- 2026-06-01:
|
||||
- resource scope candidate 已同时接受 `resource:office:{documentId}:{assetId}` 与 FileTree / resource 对象侧使用的 `resource:onlyoffice:{documentId}:{assetId}`,避免真实 Page AI target 使用 OnlyOffice object identity 时被误拒。
|
||||
- 已补并通过 `scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`:真实 ONLYOFFICE iframe / DocumentServer 下,显式传入 Office B 的 `bridgeSessionId` 但 `aiAccessScope.allowedResourceIds=[Office A]` 时返回 403;授权 B 的 `assetId` / `resource:onlyoffice:{documentId}:{assetId}` 时 dry-run 返回 200。
|
||||
- `document-resource-tab-runtime.js` / `sidebar-page-ai-runtime.js` / `hermes_client.rs` 已补最小 target scope 链路:Office resource target 可把 `onlyofficeSessionId` 写入 target package,服务端 sanitize 不再丢弃 `primaryTargetId`、`targets[]`、`assetId`、`onlyofficeSessionId`,本地 run instructions 的 `aiAccessScope.allowedResourceIds` 会包含 Office asset、session 和 `resource:onlyoffice:{documentId}:{assetId}`。新增 `hermes_client_run_body_preserves_onlyoffice_target_scope` 证明服务端不再把 Office target scope 降级为当前页面 id。
|
||||
- 本地 agent instructions 已明确要求从 `agentRunEnvelope.targetPackage.onlyofficeSessionId` 或对应 target 取值传给 `mnote.onlyoffice.*` 工具,不允许 fallback 到最近活跃 Office session。
|
||||
- `task518` 已扩展并通过授权 B session 的非 dry-run 写入验证:写入后 `document.export` 证明 B 包含唯一 marker,A 不包含,补齐真实 iframe 层写入落点证据。
|
||||
- 已补并通过 `scripts/task523-page-ai-onlyoffice-real-target-session-smoke.js`:真实 Page AI UI 选择 Office resource target 后,run payload 中 `editorTarget.onlyofficeSessionId`、`targetPackage.onlyofficeSessionId` 和 `targetPackage.targets[0].onlyofficeSessionId` 均等于 iframe live bridge session;`allowedFiles` 只包含选中 Office 文件路径,且无 buffer-state 404 / console error。
|
||||
|
||||
当前 `task518` 已覆盖真实 iframe session + 工具层 resource scope + 非 dry-run 写入落点,`task523` 补齐真实 Page AI UI target picker 到 live Office session 的 run payload 绑定;组合后覆盖“UI 只授权选中 Office target,工具层拒绝未授权 session”的端到端安全边界。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Rust 单测覆盖 allowedResourceIds 不包含 session resource 时写工具返回 403。
|
||||
- [x] Rust 单测覆盖 allowedResourceIds 包含 `assetId` 或 `objectIdentity` 时允许执行。
|
||||
- [x] Rust 单测覆盖缺失 / 空 resource scope 时拒绝。
|
||||
- [x] HTTP smoke 覆盖工具层 session/scope 边界。
|
||||
- [x] Browser smoke 覆盖真实 ONLYOFFICE iframe session 的工具层 A/B resource scope 边界。
|
||||
- [x] Rust 单测覆盖 Page AI Office target package 生成的 aiAccessScope 包含 Office asset/session/object identity。
|
||||
- [x] Browser smoke 覆盖真实 Page AI UI 选择 Office target 后只把该 Office 的 live session 与 relativePath 冻结进 run payload;工具层 A/B session 越权拒写由 `task518` 覆盖。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 7-47 local-first Page AI 写入入口口径漂移
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Page AI / design governance
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
`design/10-review/done/11-current-full-architecture-review-v1.md` 保留了“`mnote.doc.markdown_edit` 已是简单正文编辑主路径”的历史结论。但当前 AGENTS、ARCHITECTURE、CURRENT_ARCHITECTURE 与 `7-18` 都已经把 local-first 普通 Markdown 编辑主路径改为:定位真实 `.md` 文件,Hermes/Reasonix 在 allowed roots 内使用自身 patch/diff/文件编辑能力写入,再由 watcher / BufferStore / Page Aggregate 同步。旧 review 结论容易误导后续 worker 继续扩 `mnote.doc.markdown_edit`。
|
||||
|
||||
## 证据
|
||||
|
||||
- `design/10-review/done/11-current-full-architecture-review-v1.md` 写明 `mnote.doc.markdown_edit` 是简单正文编辑主路径。
|
||||
- `AGENTS.md` 与 `ARCHITECTURE.md` 当前口径明确:`mnote.doc.markdown_edit` 只作为 cloud / remote agent / compat fallback。
|
||||
- `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 明确要求 Agent Target Resolver、allowed roots/files、dirty guard 和 agent 原生 patch/diff 主路径。
|
||||
|
||||
## 影响
|
||||
|
||||
- 后续 Page AI 任务可能绕开 `7-18` 的 target package / allowed roots / dirty conflict 模型。
|
||||
- AI 写入审计、文件版本冲突和 watcher 同步难以统一。
|
||||
- `mnote.page.save` / `mnote.doc.markdown_edit` 可能被继续当成 local-first 精确编辑入口扩张。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- 在 `11-current-full-architecture-review-v1.md` 补充“历史快照,local-first 主路径已被 `7-18` 覆盖”的说明,或在 `7-18` 中添加覆盖旧 review 的显式引用。
|
||||
- Page AI 新任务默认从 `7-18` 拆 checklist,不从 `11` 的旧 tool 主路径拆实现。
|
||||
- 对 Hermes tool guidance 增加断言:local-first 普通 Markdown 编辑优先文件引用和 patch/diff,工具写入只作为 fallback。
|
||||
|
||||
## 本轮进展
|
||||
|
||||
- 2026-05-31:已在 `design/10-review/done/11-current-full-architecture-review-v1.md` 顶部补充历史快照说明,明确当前 local-first 普通 Markdown 编辑主路径以 `AGENTS.md`、`CURRENT_ARCHITECTURE.md`、`ARCHITECTURE.md` 和 `7-18` 为准。
|
||||
- 2026-06-01:已补 Page AI run 的 `mnote.agent_target_package.v1` 输入和后端 sanitize / allowedFiles 派生,避免 local-first agent 只拿到目录级 `allowedRoots` 而缺少冻结目标文件。该进展只覆盖运行时输入合同,target chip / picker、真实 agent 写入回收和 dirty conflict 仍按 `7-18` 后续阶段推进。
|
||||
|
||||
- 2026-06-01:已检查 Hermes client runtime guidance。local-first 分支已经明确“普通 Markdown 编辑优先使用 agent 原生 patch/diff 写入真实文件”;remote/cloud/compat 分支中 `mnote_doc_markdown_edit` 的描述已补上“远端 / cloud / compat”限定,测试名同步改为 `hermes_client_run_guidance_prefers_markdown_edit_only_for_remote_compat_plain_body_edits`。
|
||||
- 2026-06-01:已给 `design/10-review/done/08`、`09`、`10` 补充历史快照说明,避免旧 review 结论继续覆盖 `7-18` 当前口径。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `rg -n "markdown_edit.*主路径|简单正文编辑主路径|应优先调用 mnote_doc_markdown_edit|mnote_doc_markdown_edit for plain" design AGENTS.md ARCHITECTURE.md CURRENT_ARCHITECTURE.md rust/crates/mnote-web/src/routes/hermes_client.rs -g '*.md' -g '*.rs'` 的剩余命中均属于历史快照说明、已退役/降级口径、old 目录或 remote/cloud/compat 分支。
|
||||
- [x] Page AI task prompt / skill guidance 中明确区分 local-first 主路径与 cloud/remote/compat fallback。
|
||||
- [x] `cargo test -p mnote-web hermes_client_run_guidance_prefers_markdown_edit_only_for_remote_compat_plain_body_edits -- --test-threads=1`
|
||||
@@ -0,0 +1,29 @@
|
||||
# 7-48 Mindmap create_from_outline envelope 单测缺少 embed 边界
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Hermes tools / mindmap resource
|
||||
- 发现时间:2026-05-31
|
||||
- 修复时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
`cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib hermes_tools_mindmap -- --test-threads=1` 中 `hermes_tools_mindmap_create_from_outline_writes_default_envelope` 失败,返回:
|
||||
|
||||
```text
|
||||
mnote_resource_page_not_found: 找不到要绑定 mindmap 的本地 Markdown 页面
|
||||
```
|
||||
|
||||
## 根因
|
||||
|
||||
该用例只验证 `mnote.mindmap.create_from_outline` 生成默认 `.mindmap.json` envelope,但 payload 未显式设置 `embedIntoPage`。当前工具合同中 `embedIntoPage` 默认是 `true`,而测试 fixture 没有创建 `README.md`,导致资源文件写入后进入页面 embed 阶段并失败。
|
||||
|
||||
## 修复
|
||||
|
||||
- 在纯 envelope 写入用例里显式设置 `embedIntoPage: false`。
|
||||
- 保留另一个 `hermes_tools_mindmap_create_from_outline_can_embed_into_local_markdown_page` 用例继续覆盖显式 `embedIntoPage=true` 的页面绑定行为。
|
||||
|
||||
## 验证
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib hermes_tools_mindmap -- --test-threads=1`
|
||||
@@ -0,0 +1,51 @@
|
||||
# 7-50 ONLYOFFICE session.current 诊断工具缺少 resource scope 过滤
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / OnlyOffice live bridge / Hermes tools
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
`mnote.onlyoffice.session.current` 是诊断工具,不会执行读写动作;但它在未传 `onlyofficeSessionId` / `bridgeSessionId` 时仍会 fallback 到进程内最近活跃 OnlyOffice bridge session,并返回 `sessionId`、`documentId`、`assetId`、`fileType`、pending command/result 计数等元数据。
|
||||
|
||||
这与读写工具已经要求 explicit session + `aiAccessScope.allowedResourceIds` 的收口方向不完全一致。若 Page AI 当前 target=A,但进程内最近活跃 Office session 属于 resource=B,模型可能通过 `session.current` 看到 B 的 session 元数据。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/onlyoffice_live.rs` 中 `session_current()` 允许缺 session id 时使用 `current_session_info()`。
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice_bridge.rs` 中 `current_session_info()` 返回全局最近活跃 session。
|
||||
- Reasonix 只读复核 `reasonix-2026-05-31T16-38-36-472Z-63f3a41c` 判断:这不是写绕过,但属于低级信息泄漏风险;建议 `session_current` 也加入 scope 校验或过滤返回字段。
|
||||
|
||||
## 影响
|
||||
|
||||
- 不会直接写入或读取文档正文,因此风险低于 `7-45` / `7-46`。
|
||||
- 可能暴露非当前 target resource 的 Office session 元数据,给后续工具调用或模型选择目标带来混淆。
|
||||
- 与 OnlyOffice live bridge 的最小权限口径不一致。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- `session_current` 若传 explicit session id,应校验该 session 对应的 `sessionId` / `documentId` / `assetId` / `resource:office:{documentId}:{assetId}` 是否在 `aiAccessScope.allowedResourceIds` 中。
|
||||
- `session_current` 若未传 explicit session id,不应返回全局最近活跃 session;可以改为返回 `mnote_onlyoffice_session_explicit_required`,或只在 debug/admin 边界允许。
|
||||
- 如果保留诊断 fallback,至少过滤 `documentId` / `assetId` 等跨 resource 元数据,并在 manifest 标注只用于诊断。
|
||||
|
||||
## 本轮处理
|
||||
|
||||
- 2026-06-01:`mnote.onlyoffice.session.current` 已改为复用 `resolve_explicit_session_id()` 与 `ensure_onlyoffice_resource_scope_allowed()`。
|
||||
- 已删除 `onlyoffice_bridge::current_session_info()` 全局最近 session fallback,避免诊断工具继续返回非当前 target 的 session 元数据。
|
||||
- `manifest.rs` 中 `mnote.onlyoffice.session.current` 已复用 OnlyOffice live 工具 schema,要求 `aiAccessScope.allowedResourceIds`,并通过 `anyOf` 要求 `onlyofficeSessionId` 或 `bridgeSessionId`。
|
||||
- `scripts/task515-onlyoffice-live-scope-http-smoke.js` 已扩展覆盖 `session.current` 缺 explicit session 返回 400、scope 不匹配返回 403、授权 scope 返回 session 元数据。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Rust 单测覆盖 target=A 的 `aiAccessScope.allowedResourceIds` 不能通过 `session.current` 获取 resource=B 的 session 元数据。
|
||||
- [x] 缺 explicit session id 的 `session.current` 不再返回全局最近 session。
|
||||
- [x] `task515` HTTP smoke 覆盖 `session.current` scope 边界。
|
||||
|
||||
验证:
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib hermes_tools_onlyoffice_session_current -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib hermes_tools_manifest_describes_onlyoffice_live_scope -- --test-threads=1`
|
||||
- `node --check scripts/task515-onlyoffice-live-scope-http-smoke.js`
|
||||
- `node scripts/task515-onlyoffice-live-scope-http-smoke.js`
|
||||
@@ -0,0 +1,57 @@
|
||||
# 7-51 mindmap apply_ops 结构化写入与可见性缺口
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done,工具层最小结构化写入、刷新后 embedded mindmap id 保真、真实 mindmap resource tab Page AI target 已修复;更丰富 `apply_ops` 指令面扩展另拆 follow-up,不阻塞本 bug 归档
|
||||
- Owner:07-ai / Hermes tools / mindmap resource
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
`mnote.mindmap.apply_ops` 的非 dry-run 路径曾不执行结构化写入,而是在完成资源路径和写权限校验后返回 `mnote_resource_native_patch_required`,提示 agent 使用原生 patch 编辑授权文件。这与 `7-42` 第二批目标“把 `apply_ops` 从提示 agent patch 文件收口到结构化写入”不一致。
|
||||
|
||||
2026-06-01 本轮已完成工具层最小修复:`dryRun=false` 可执行 `updateText` / `updateNode`、`insertChild` / `addChild`、`deleteNode`,写入后返回 `revision`、`changedFiles`、`markdownSummary`,并保留 `view`、未知顶层字段和未知 node 字段。当前缺口转为浏览器可见性和 Page AI resource target 集成。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/resource.rs` 的 `mindmap_apply_ops()` 已实现最小结构化写入。
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs` 已有 `hermes_tools_mindmap_apply_ops_writes_and_preserves_envelope_fields`,覆盖非 dry-run 写入、`view` 和未知字段保留。
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs` 已有 `hermes_tools_mindmap_apply_ops_rejects_stale_revision`,覆盖 `expectedRevision` 不匹配拒写。
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs` 已有 `hermes_tools_mindmap_apply_ops_shared_read_is_forbidden`,说明 shared/read-only scope 拒写已经有单测证据,不是当前主要缺口。
|
||||
- `task455`、`task524`、`task525` 已覆盖 embedded mindmap 刷新、FileTree / Resource Tab 可见性和真实 Page AI mindmap target/contextRefs。
|
||||
|
||||
## 影响
|
||||
|
||||
- Agent 已可通过结构化 tool 安全执行最小增删改 mindmap node,不再只能回退到文件 patch。
|
||||
- `view`、未知字段保留和 revision conflict 已由工具层单测覆盖。
|
||||
- Page AI mindmap 第二批的最小可用链路已覆盖:tool 结构化写入、embedded mindmap 刷新、FileTree / Resource Tab 可见性,以及真实 Page AI mindmap target/contextRefs。后续缺口转为真实编辑需求下的更丰富 `apply_ops` 指令面。
|
||||
|
||||
## 修复建议
|
||||
|
||||
- 按真实编辑需求继续扩展 `apply_ops`,例如 `moveNode`、`insertSiblingAfter`、`setHyperlink`、`setRefs`、`appendNote`、`patchView`。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Rust 单测覆盖 `apply_ops` 非 dry-run 结构化写入。
|
||||
- [x] Rust 单测覆盖 `view` 和未知字段保留。
|
||||
- [x] Rust 单测覆盖 revision mismatch 返回 conflict。
|
||||
- [x] Rust 单测覆盖 shared/read-only scope 下写工具拒绝。
|
||||
- [x] Browser smoke 覆盖 mindmap 资源在 File Tree / Resource Tab 可见。
|
||||
- [x] Browser smoke 覆盖真实 mindmap resource tab 注入 Page AI `active_editor` contextRef / `targetPackage`。
|
||||
- [x] `task455-local-folder-mindmap-clean-smoke.js` 刷新后 `mindmapId` 保持新建资源文件名,不退化为 `"mindmap"`。
|
||||
|
||||
## 本轮验证
|
||||
|
||||
- `cargo test -p mnote-web hermes_tools_mindmap_apply_ops -- --test-threads=1`
|
||||
- `cargo test -p mnote-web mindmap -- --test-threads=1`
|
||||
- 2026-06-01 复跑失败:`MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task455-local-folder-mindmap-clean-smoke.js`
|
||||
- 失败点:`刷新后 mindmapId 应保持不变`
|
||||
- 证据:`tmp/task455-local-folder-mindmap-clean-smoke/result.json`
|
||||
- 关键现象:刷新前 `mindmapId=mindmap-647356316`,刷新后 DOM `mindmapId=mindmap`;FileTree 仍有 `local-file:CleanPage/mindmap-647356316.json` 行。
|
||||
- 2026-06-01 修复后复跑通过:`MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task455-local-folder-mindmap-clean-smoke.js`
|
||||
- 修复点:`blockDocument.blocks[].attrs` 保留 `mindmapId/sourcePath/rootNodeId`,浏览器 `document-tiptap-conversion-runtime.js` 消费 blockDocument 时读取这些 attrs。
|
||||
- 补充验证:`cargo test -p bridge-runtime page_aggregate_block_document_preserves_mindmap_projection_attrs -- --test-threads=1`,`cargo test -p mnote-web local_markdown_generated_mindmap_id_roundtrips_as_mindmap_block -- --test-threads=1`。
|
||||
- 回归验证:`MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task524-workspace-object-identity-matrix-smoke.js`。
|
||||
- 2026-06-01 新增并通过:`MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task525-page-ai-mindmap-resource-target-smoke.js`
|
||||
- 覆盖真实 local-folder mindmap resource tab 打开后,Page AI run payload 中 `contextRefs.active_editor.resourceKind=mindmap`。
|
||||
- 覆盖 `targetPackage.primaryTargetId`、`targetPackage.currentFile`、`allowedFiles` 指向 `Page/map.mindmap.json`。
|
||||
@@ -0,0 +1,52 @@
|
||||
# 7-52 Page AI target picker 与 resource target 仍未闭环
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Page AI sidebar runtime / Agent Target Resolver
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
Page AI 已经有 agent selector、contextRefs popover、`targetPackage` 和 `allowedFiles` 基础输入。2026-06-01 已补上 page target、mindmap resource target、OnlyOffice resource target 与 raw local resource target 的 composer chip / picker / payload 冻结闭环;mindmap / raw file `active_editor` contextRef 均已携带 `targetId` / `objectIdentity` / `resourceKind` / `assetId` / `relativePath`。跨 workspace 多 target 的产品化确认继续由 `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 跟踪,不再由本文阻塞 target picker 收口。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js` 已有 `currentPageAiOpenEditorsSnapshot()`、`currentPageAiEditorTarget()`、target chip / picker 和 `pageAiBuildAgentTargetPackage()`。
|
||||
- `scripts/task502-page-ai-agent-selector-context-smoke.js` 已覆盖 page target button / popover / chip,并通过 mock `resourceEditors` 覆盖 mindmap target 选择,断言 run payload 的 `targetPackage.primaryTargetId`、`targets[]`、`resourceKind=mindmap`、`relativePath=maps/Task502.mindmap.json`、`policy.writeRequiresExplicitTarget=true`;同时断言 `active_editor` contextRef 携带 mindmap `targetId` / `objectIdentity` / `resourceKind` / `assetId` / `relativePath`。
|
||||
- 2026-06-01 验证命令:`MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task502-page-ai-agent-selector-context-smoke.js`,截图:`tmp/task502-page-ai-agent-selector-context-smoke/01-agent-selector-context.png`。
|
||||
- `scripts/task453-local-folder-page-ai-changed-files-smoke.js` 覆盖 target snapshot 冻结、跨 workspace 阻断、dirty buffer 阻断,但不是 target picker UI。
|
||||
- `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` 仍把 target chip/picker、跨 workspace 多选确认、真实 agent 写入回收列为未完成。
|
||||
- OnlyOffice 工具层 session/scope 已有 `task515` - `task518` 覆盖;`task523-page-ai-onlyoffice-real-target-session-smoke.js` 已补真实 Page AI target 到 `onlyofficeSessionId` / `bridgeSessionId` 的 UI 绑定。
|
||||
- `mnote-mindmap` skill 已注册,mindmap target 的 `active_editor` contextRef 已携带资源身份;`task525-page-ai-mindmap-resource-target-smoke.js` 已补真实 mindmap resource tab target / contextRefs 复测。
|
||||
- `scripts/task520-page-ai-raw-resource-target-smoke.js` 已补真实 raw local resource tab:打开 `Page/notes.txt` 后 Page AI target chip 指向 `notes.txt`,run payload 冻结 `targetPackage.primaryTargetId=resource:file:{rootUri}:Page/notes.txt`,`allowedFiles=["Page/notes.txt"]`,并断言 `objectIdentity` 不再退化成 `[object Object]`。
|
||||
|
||||
## 影响
|
||||
|
||||
- Page AI UI 已能清楚告诉用户当前 page / mindmap / OnlyOffice target。
|
||||
- Page AI target picker 已覆盖 page / mindmap / OnlyOffice / raw file 四类当前 P1 目标,不再退化为“当前页”语义。
|
||||
- 真实 agent 写入回收、跨 workspace 多 target 和 mindmap resource tab 自动注入的更大产品化项继续由 `7-18` / `7-42` / `7-51` 跟踪。
|
||||
|
||||
## 下一步建议
|
||||
|
||||
- 若继续扩多目标选择,新增跨 workspace 多 target 确认 smoke,并归入 `7-18`。
|
||||
- 若继续扩 mindmap 多目标或更细粒度 contextRefs,另拆 `7-42` / `7-51` follow-up,不再由本文阻塞 target picker 收口。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] Page AI composer 可见 page target chip。
|
||||
- [x] target picker 可在当前 page 与 mindmap resource tab 间切换。
|
||||
- [x] run payload 冻结 page `targetPackage.targets[0]`。
|
||||
- [x] run payload 冻结 mindmap resource `targetPackage.targets[0]`。
|
||||
- [x] OnlyOffice target 不会把 A 页授权误用于 B session。
|
||||
- [x] mindmap resource tab 可作为 `resourceKind=mindmap` target。
|
||||
- [x] mindmap `active_editor` contextRef 携带 `targetId` / `objectIdentity` / `resourceKind` / `assetId`。
|
||||
- [x] raw file target 可从真实 resource tab 候选选择并冻结到 run payload。
|
||||
- [x] 真实 mindmap resource tab target / contextRefs 已由 `task525` 覆盖。
|
||||
|
||||
## 本轮验证
|
||||
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
|
||||
- `node --check scripts/task520-page-ai-raw-resource-target-smoke.js`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 node scripts/task520-page-ai-raw-resource-target-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/task502-page-ai-agent-selector-context-smoke.js`
|
||||
@@ -0,0 +1,134 @@
|
||||
# 7-53 Page AI runtime 已触发继续拆分阈值
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Page AI sidebar runtime
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
`rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js` 曾约 3458 行,已经超过 `design/07-ai/process/7-38-page-ai-sidebar-runtime-owner-split-v1.md` 中设定的 2500 行继续拆分阈值。文件内仍混合 UI render、run orchestration 等多类职责。
|
||||
|
||||
同时,`sidebar-tree-runtime.js` 已不再初始化 `pageUiState.pageAi*` 状态,Page AI 默认值由 `sidebar-page-ai-runtime.js` 的 `ensurePageAiStateFacade()` 集中兜底;但 tree runtime 仍传入共享 `pageUiState` 对象并保留少量 Page AI 代理函数,后续子模块拆分仍未闭环。
|
||||
|
||||
## 证据
|
||||
|
||||
- `sidebar-page-ai-runtime.js` 曾约 3458 行;2026-06-01 render helper 续补后降至 2498 行。
|
||||
- `sidebar-page-ai-markdown-runtime.js` 已承接 Page AI Markdown / conversation 文本渲染 helper。
|
||||
- `sidebar-page-ai-profile-runtime.js` 已承接 provider/profile/history filter/usage helper。
|
||||
- `sidebar-page-ai-permission-runtime.js` 已承接 ACP permission message/dialog/resolve helper。
|
||||
- `sidebar-page-ai-session-runtime.js` 已承接 session storage、backend session list/detail/search/resume/delete 和 runtime event replay helper。
|
||||
- `sidebar-page-ai-skill-runtime.js` 已承接 skill source、skill preference、Hermes builtin 隐藏和 Reasonix memory preference helper。
|
||||
- `sidebar-page-ai-target-runtime.js` 已承接 OpenEditorsSnapshot target 派生、WorkspacePath、run target snapshot、contextRefs、agentTargetPackage 和 target writable guard helper。
|
||||
- `sidebar-page-ai-runtime.js` 包含 `function ensurePageAiStateFacade`,并集中初始化 `pageAiAcpRuntime: 'reasonix'` 等 Page AI 默认状态。
|
||||
- `sidebar-tree-runtime.js` 不再包含 `pageAiAcpRuntime: 'reasonix'` 等 `pageAi*:` 默认字段,只保留 `open-page-ai` 主壳入口和代理函数。
|
||||
- `design/07-ai/process/7-38-page-ai-sidebar-runtime-owner-split-v1.md` 第 3 节仍将以下项列为未完成:
|
||||
- 若 `sidebar-page-ai-runtime.js` 超过 2500 行,再按 render / conversation / run orchestration 继续拆成子模块。
|
||||
|
||||
## 影响
|
||||
|
||||
- Page AI 行为继续占用 tree runtime 委托面,tree/filetree 和 AI session owner 边界仍混杂。
|
||||
- 后续新增 target picker、OnlyOffice target、mindmap contextRefs 时,容易继续堆入单一大文件。
|
||||
- 浏览器事件委托散落在 tree runtime 与 Page AI runtime 之间,增加回归风险。
|
||||
|
||||
## 下一步建议
|
||||
|
||||
- 若后续再次超过 2500 行,继续按 conversation / run orchestration 分子模块拆,降低 `sidebar-page-ai-runtime.js` 单文件职责。
|
||||
- 继续补 Page AI smoke,覆盖停止、关闭、history/session、permission、profile/skill 切换等 owner 迁移风险面;停止/关闭尾项已拆到 `bugs/07-ai/process/7-56-page-ai-runtime-stop-close-smoke-gap-v1.md`。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `sidebar-tree-runtime.js` 不再包含 `[data-page-ai-action=...]` click/change 分发。
|
||||
- [x] `sidebar-page-ai-runtime.js` 导出并安装 `installPageAiDelegates()`。
|
||||
- [x] `pageUiState.pageAi*` 从通用 sidebar state 下沉或有明确兼容 getter/setter。
|
||||
- [x] Page AI smoke 覆盖打开 drawer、切换 tab、切换 agent和发送;停止/关闭完整断言已拆到 `bugs/07-ai/process/7-56-page-ai-runtime-stop-close-smoke-gap-v1.md`,不阻塞本文拆分阈值归档。
|
||||
|
||||
## 2026-06-01 第一刀验证
|
||||
|
||||
已完成第一刀:`sidebar-tree-runtime.js` 中 Page AI click/input/change/keydown 的大段 action 分发已迁到 `sidebar-page-ai-runtime.js` 的 `handlePageAiClick`、`handlePageAiKeyDown`、`handlePageAiInput`、`handlePageAiChange`,并由 `installPageAiDelegates()` 在 Page AI owner runtime 内安装事件监听。tree runtime 仅保留 `data-mnote-action="open-page-ai"` 主壳入口。
|
||||
|
||||
已通过验证:
|
||||
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web page_ai_agent_target_picker_contract_is_visible_and_serialized -- --test-threads=1`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task490-runtime-surfaces-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/task502-page-ai-agent-selector-context-smoke.js`
|
||||
|
||||
2026-06-01 续补:
|
||||
|
||||
- `sidebar-page-ai-runtime.js` 新增并导出 `installPageAiDelegates()`,内部一次性安装 Page AI click/keydown/input/change delegate。
|
||||
- `sidebar-tree-runtime.js` 不再调用 `sidebarPageAi.handlePageAi*`,只在自身监听注册后调用 `sidebarPageAi.installPageAiDelegates()`。
|
||||
- 已在临时 `127.0.0.1:3301` Rust web 实例补跑 `task490-runtime-surfaces-smoke.js` 与 `task502-page-ai-agent-selector-context-smoke.js`,验证 drawer、agent/context/target/skills 和发送 payload 链路仍可用。
|
||||
|
||||
2026-06-01 state facade 续补:
|
||||
|
||||
- `sidebar-page-ai-runtime.js` 新增 `ensurePageAiStateFacade()`,集中初始化 Page AI 默认状态并导出该 facade。
|
||||
- `sidebar-tree-runtime.js` 的 `pageUiState` 初始对象删除 `pageAi*:` 默认字段,tree runtime 不再定义 Page AI 状态真相。
|
||||
- Rust include/assert 已更新为:Page AI runtime 包含 `ensurePageAiStateFacade` 与 `pageAiAcpRuntime: 'reasonix'`,tree runtime 不包含该默认字段。
|
||||
|
||||
2026-06-01 conversation helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-markdown-runtime.js`,抽出 `textFromUnknown`、`renderPageAiMarkdown` 和 inline Markdown 渲染 helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiMarkdownRuntime()`,保留现有 assistant message 渲染行为。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-markdown-runtime.js` 静态 runtime asset,并更新 runtime asset mount 测试。
|
||||
|
||||
2026-06-01 profile helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-profile-runtime.js`,抽出 provider/profile/chat-only profile/history filter/usage helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiProfileRuntime()` 并保留同名代理常量,降低调用点扰动。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-profile-runtime.js` 静态 runtime asset,并更新 runtime asset mount 测试。
|
||||
- 已在临时 `127.0.0.1:3301` Rust web 实例补跑 `task490-runtime-surfaces-smoke.js` 与 `task502-page-ai-agent-selector-context-smoke.js`,验证二级 import 后 drawer、agent/context/target/skills 和发送 payload 链路仍可用。
|
||||
|
||||
2026-06-01 permission helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-permission-runtime.js`,抽出 ACP permission message、dialog show/hide 和 resolve-permission helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiPermissionRuntime()` 并保留同名代理常量,主 runtime 继续负责事件流持久化、会话同步和 conversation 渲染入口。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-permission-runtime.js` 静态 runtime asset,并更新 runtime asset mount/layout 断言。
|
||||
- 已在临时 `127.0.0.1:3301` Rust web 实例补跑 `task490-runtime-surfaces-smoke.js` 与 `task502-page-ai-agent-selector-context-smoke.js`,验证新增 permission 二级 import 后 sidebar/Page AI 仍可加载和发送。
|
||||
|
||||
2026-06-01 session helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-session-runtime.js`,抽出 session storage、backend session list/detail/search/resume/delete、session message sync 和 backend runtime event replay helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiSessionRuntime()` 并保留同名代理常量,主 runtime 继续负责 Page AI UI render、run orchestration 和事件委托入口。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-session-runtime.js` 静态 runtime asset,并更新 runtime asset mount/layout 断言。
|
||||
- 已在临时 `127.0.0.1:3301` Rust web 实例补跑 `task490-runtime-surfaces-smoke.js` 与 `task502-page-ai-agent-selector-context-smoke.js`,验证新增 session 二级 import 后 Page AI drawer、agent/context/target/skills 和发送 payload 链路仍可用。
|
||||
|
||||
2026-06-01 skill helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-skill-runtime.js`,抽出 skill source、skill preference、Hermes builtin 隐藏和 Reasonix memory preference helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiSkillRuntime()` 并保留同名代理常量,主 runtime 继续负责 skill 异步加载、target/run orchestration 和 UI render。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-skill-runtime.js` 静态 runtime asset,并更新 runtime asset mount/layout 断言。
|
||||
- 已通过 `node --check` 覆盖 Page AI 主 runtime、skill/session/permission/profile/markdown helper 和 `sidebar-tree-runtime.js`。
|
||||
- 已通过 `cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`、`page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch`、`page_ai_agent_target_picker_contract_is_visible_and_serialized`。
|
||||
- 已在临时 `127.0.0.1:3301` Rust web 实例补跑 `task490-runtime-surfaces-smoke.js` 与 `task502-page-ai-agent-selector-context-smoke.js`,验证新增 skill 二级 import 后 Page AI drawer、agent/context/target/skills 和发送 payload 链路仍可用。
|
||||
|
||||
2026-06-01 target helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-target-runtime.js`,抽出 OpenEditorsSnapshot target 派生、WorkspacePath、run target snapshot、contextRefs、agentTargetPackage 和 target writable guard helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiTargetRuntime()` 并保留同名代理常量,主 runtime 继续负责 target popover UI、事件分发和 run orchestration 顺序。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-target-runtime.js` 静态 runtime asset,并更新 runtime asset mount/layout 断言;target picker 合同测试改为在 target helper 中断言 `primaryTargetId` / `targets` / `policy`。
|
||||
- 已通过 `node --check` 覆盖 Page AI 主 runtime、target/skill/session/permission/profile/markdown helper 和 `sidebar-tree-runtime.js`。
|
||||
- 已通过 `cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`、`page_ai_uses_backend_acp_session_runtime_store`、`page_ai_agent_target_picker_contract_is_visible_and_serialized`、`page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch`。
|
||||
- 已在临时 `127.0.0.1:3301` Rust web 实例补跑 `task490-runtime-surfaces-smoke.js`、`task502-page-ai-agent-selector-context-smoke.js`、`task520-page-ai-raw-resource-target-smoke.js`、`task525-page-ai-mindmap-resource-target-smoke.js`,验证新增 target 二级 import 后 Page AI drawer、agent/context/target/skills、raw resource target、mindmap target 和发送 payload 链路仍可用。
|
||||
|
||||
2026-06-01 render helper 续补:
|
||||
|
||||
- 新增 `sidebar-page-ai-render-runtime.js`,抽出 target/context display helper、agent/profile/model label、skill filter、drawer shell、controls render、suggestions render、conversation render 和 response humanize helper。
|
||||
- `sidebar-page-ai-runtime.js` 改为 import `createSidebarPageAiRenderRuntime()`,保留同名转发函数,主 runtime 继续负责 state facade、agent/run orchestration、session/permission/skill/target runtime 接线与事件委托。
|
||||
- 补注册 `/api/mnote-browser-runtime/sidebar-page-ai-render-runtime.js` 静态 runtime asset,并更新 runtime asset mount/layout 断言,避免浏览器 ES module import 404。
|
||||
- `sidebar-page-ai-runtime.js` 当前 2498 行,已低于 `7-38` 设定的 2500 行继续拆分阈值。
|
||||
- 已通过 `node --check` 覆盖 Page AI 主 runtime、render/target/skill/session/permission/profile/markdown helper 和 `sidebar-tree-runtime.js`。
|
||||
- 已通过 `cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`、`page_ai_uses_backend_acp_session_runtime_store`、`page_ai_agent_target_picker_contract_is_visible_and_serialized`、`page_ai_acp_runtime_defaults_to_reasonix_and_keeps_hermes_switch`。
|
||||
|
||||
最终验证:
|
||||
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task490-runtime-surfaces-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/task502-page-ai-agent-selector-context-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/task504-page-ai-history-agent-filter-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/task520-page-ai-raw-resource-target-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/task525-page-ai-mindmap-resource-target-smoke.js`
|
||||
|
||||
本文已满足归档条件:`sidebar-page-ai-runtime.js` 当前 2498 行,低于 2500 行继续拆分阈值;render helper 二级 import、drawer controls、history/session filter、当前页 target、raw resource target、mindmap target 和发送 payload 链路均通过 smoke。
|
||||
@@ -0,0 +1,33 @@
|
||||
# 7-54 Page AI Office target 误查 Markdown buffer-state
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Page AI target runtime / OnlyOffice resource target
|
||||
- 发现时间:2026-06-01
|
||||
- 修复时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
真实 Page AI UI 选择 Office resource target 后,发送 run 前的 buffer guard 会按 Office 文件相对路径请求 `/api/documents/buffer-state`:
|
||||
|
||||
```text
|
||||
/api/documents/buffer-state?...&relativePath=Page/office-a.docx
|
||||
```
|
||||
|
||||
该端点只服务 local Markdown 文档 buffer,Office target 会返回 404。虽然当前 404 没有阻断 run,但会污染 console / HTTP error 证据,并说明 Page AI 把 Office resource 当成 Markdown buffer 检查。
|
||||
|
||||
## 根因
|
||||
|
||||
`rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js` 的 `fetchPageAiTargetBufferState()` 只判断 `sourceKind=local_folder`,没有区分 target `resourceKind`。Office target 已由 OnlyOffice bridge session、resource scope 和 tool 层权限保护,不应走 Markdown `BufferStore` 查询。
|
||||
|
||||
## 修复
|
||||
|
||||
- `fetchPageAiTargetBufferState()` 对 `office` / `only_office` / `onlyoffice` target 直接返回 `null`。
|
||||
- `scripts/task523-page-ai-onlyoffice-real-target-session-smoke.js` 增加断言:真实 Page AI Office target run 过程中不得出现 `/api/documents/buffer-state` 404,并要求 `consoleErrors` / `networkFailures` / `httpErrors` 为空。
|
||||
|
||||
## 验收
|
||||
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
|
||||
- `node --check scripts/task523-page-ai-onlyoffice-real-target-session-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/task523-page-ai-onlyoffice-real-target-session-smoke.js`
|
||||
@@ -0,0 +1,32 @@
|
||||
# 7-55 Page AI 当前页目标继承 stale workspacePath
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Page AI target runtime
|
||||
- 发现时间:2026-06-01
|
||||
- 修复时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
`task504-page-ai-history-agent-filter-smoke.js` 在模拟 stale `OpenEditorsSnapshot` 后,用户偏好已将 `active_editor` contextRef 关闭,只发送当前页上下文;但 `sendPageAiMessage()` 仍强制把 `scopedContext.editorTarget` 覆盖为 `currentPageAiEditorTarget()`,导致当前页请求被旧 active editor 的 `workspaceId` 拦截,前端显示:
|
||||
|
||||
`AI target 与当前 workspaceId 不一致,请重新选择当前工作区内的目标。`
|
||||
|
||||
## 根因
|
||||
|
||||
- `pageAiScopedPageContext()` 已按 contextRefs 计算 scoped target,但发送链路又覆盖为 `currentPageAiEditorTarget()`。
|
||||
- `currentPageAiPageEditorTarget()` 从 page editor snapshot 读取 `workspacePath` 时,没有把当前 `workspaceId/sourceKind/rootUri/relativePath/documentId` 写回,stale snapshot 会把旧 workspace 信息带进当前页目标。
|
||||
|
||||
## 修复
|
||||
|
||||
- `sendPageAiMessage()` 改用 `currentPageAiScopedEditorTarget()`,遵守 contextRefs 对 active editor 的开关。
|
||||
- `currentPageAiPageEditorTarget()` 在复用 page editor snapshot 时显式覆盖当前 workspace path 字段,避免 stale snapshot 污染当前页 target。
|
||||
|
||||
## 验证
|
||||
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-target-runtime.js`
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3301 MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3301 node scripts/task504-page-ai-history-agent-filter-smoke.js`
|
||||
|
||||
验证结果:`task504` 通过,`capturedRuns[0].contextRefs` 只包含 `current_page`,`runTargetSnapshot.editorTarget.workspaceId` 为当前 `local-ws:mnote-e2e:task504`,未继续使用 stale workspaceId。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 7-56 Page AI runtime stop / close smoke 缺口
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / Page AI runtime
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
`7-53` 已完成 Page AI runtime 拆分并把 `sidebar-page-ai-runtime.js` 降到 2500 行阈值以下,但停止运行与关闭抽屉的浏览器断言仍不完整。
|
||||
|
||||
## 证据
|
||||
|
||||
- `bugs/07-ai/done/7-53-page-ai-runtime-split-threshold-triggered-v1.md` 已记录拆分完成,但验收中仍保留 stop/close smoke 的 `[~]` 项。
|
||||
- `scripts/task490-runtime-surfaces-smoke.js` 会打开并点击关闭 Page AI 抽屉,但缺少明确的 drawer hidden / closed state 断言。
|
||||
- 现有 smoke 未覆盖 `data-page-ai-action="stop-run"` 到 abort API / terminal event 的端到端行为。
|
||||
|
||||
## 影响
|
||||
|
||||
- Page AI runtime 拆分后,基础打开/发送/target picker 已有保护,但 stop/close 这类常用交互仍可能在后续拆分中回退。
|
||||
- `7-53` 虽然可作为拆分阈值 bug 归档,但停止/关闭行为应继续作为独立 P2 测试缺口跟踪。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 扩展现有 `task490` 或新增窄 smoke,断言关闭后 Page AI drawer 进入隐藏状态,且页面根状态同步清除。
|
||||
2. 增加 stop-run smoke:启动可控流式 run,点击停止,断言 abort API 被调用并收到 terminal/aborted 状态。
|
||||
3. 将验证命令补入 `scripts/TESTING_REFERENCE.md`。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] 浏览器 smoke 明确断言 Page AI drawer close 后不可见。
|
||||
- [x] 浏览器 smoke 覆盖 stop-run -> abort/terminal event。
|
||||
- [x] `node --check` 与对应 smoke 通过。
|
||||
|
||||
## 2026-06-01 修复记录
|
||||
|
||||
- `scripts/task490-runtime-surfaces-smoke.js` 已扩展 Page AI close 断言:关闭后 drawer 必须 `hidden=true`,浮动 AI 按钮 `data-state=closed` 且 `aria-expanded=false`。
|
||||
- `task490` 已新增可控 mock run:发送后等待 run 进入 `running`,点击 `data-page-ai-action="stop-run"`,断言 abort API 被调用一次、请求 reason 为 `page_ai_user_stop`,并等待 UI 进入 `aborted` 状态。
|
||||
- 修复 Page AI render helper 拆分漏注入:`sidebar-page-ai-render-runtime.js` 使用 `pageAiProviderLabel()` 渲染 provider card,但 `sidebar-page-ai-runtime.js` 未传入该 helper;已补 context 注入。
|
||||
- 已通过:
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-runtime.js`
|
||||
- `node --check rust/crates/mnote-web/browser/sidebar-page-ai-render-runtime.js`
|
||||
- `node --check scripts/task490-runtime-surfaces-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/task490-runtime-surfaces-smoke.js`
|
||||
|
||||
截图:
|
||||
|
||||
- `tmp/task490-runtime-surfaces-smoke/02b-page-ai-stopped.png`
|
||||
@@ -0,0 +1,80 @@
|
||||
# 7-57 OnlyOffice Plugin Bridge P2 产品化尾项
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / ONLYOFFICE live bridge / Page AI target runtime
|
||||
- 发现时间:2026-06-01
|
||||
- 归档时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
OnlyOffice live bridge 的 P0 安全阻断已完成:显式 session、resource scope、真实 iframe 多 session 隔离、非 dry-run 写入落点和 Page AI target picker 到 live session 的绑定均已有 smoke 证据。但 `7-43` 仍保留若干 P2 产品化尾项,缺少独立 bug 落点。
|
||||
|
||||
## 证据
|
||||
|
||||
- `design/07-ai/process/7-43-onlyoffice-plugin-bridge-design-v1.md` 仍保留 P2 follow-up:Office 主文档加载噪音治理、第三批 recipe 逐项实测。`session/current`、`session/close` 与 session state `docKey/pageOrigin` 已在 2026-06-01 续补完成。
|
||||
- 已有 smoke 证明安全主路径完成:
|
||||
- `scripts/task515-onlyoffice-live-scope-http-smoke.js`
|
||||
- `scripts/task516-onlyoffice-bridge-multisession-browser-smoke.js`
|
||||
- `scripts/task517-onlyoffice-bridge-plugin-direct-smoke.js`
|
||||
- `scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js`
|
||||
- `scripts/task523-page-ai-onlyoffice-real-target-session-smoke.js`
|
||||
|
||||
## 影响
|
||||
|
||||
- P0 安全问题已经不再阻塞;P2 recipe 扩展已收窄为 `7-43` 的矩阵化后续,不再作为本文 bug 阻塞项。
|
||||
- `7-43` 已区分已完成安全项和后续产品化项。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 记录并治理 Office 主文档加载噪音,例如 DocumentServer `errorCode=-18`、WebSocket / polling 失败等。(已完成)
|
||||
2. 第三批 recipe 必须逐项实测后再暴露给 agent,不凭 API 名称直接开放。(已收窄为矩阵规则)
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `session/current` 与 `session/close` API 已实现并有 Rust route 测试。
|
||||
- [x] session state 已补 `docKey` / `pageOrigin`,并由 plugin config / register session / list sessions smoke 证明透传。
|
||||
- [x] Office 加载噪音有明确分类、截图或日志证据,并不误判为打开成功。
|
||||
- [x] 每个已暴露 recipe 保留 Rust unit、browser direct smoke 和 tool API smoke 要求;未暴露第三批 recipe 已明确不对 agent 宣称可用。
|
||||
- [x] 插件执行层已补 editorType guard,`document.*` / `sheet.*` / `presentation.*` 不能跨 Word / Excel / PPT session 串用。
|
||||
- [x] `design/07-ai/process/7-43...` 中 P0 已完成项与 P2 尾项拆分清晰。
|
||||
|
||||
## 2026-06-01 session lifecycle 续补记录
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/onlyoffice_bridge.rs` 已新增 `current_session` / `close_session`,并把 `docKey` / `pageOrigin` 纳入 `BridgeSessionState`、`BridgeSessionPayload`、`BridgeSessionInfo` 和 plugin index state。
|
||||
- `/onlyoffice` 页面会把当前 `fileState.docKey` 与 `location.origin` 传给 bridge plugin config;mock plugin direct smoke 已验证 plugin 注册后 session 中存在 `docKey/pageOrigin`。
|
||||
- `scripts/task516-onlyoffice-bridge-multisession-browser-smoke.js` 已扩展 `session/current`、`session/close` 和关闭后 session 不再可取 command 的断言。
|
||||
- 已通过:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1`
|
||||
- `node scripts/task517-onlyoffice-bridge-plugin-direct-smoke.js`
|
||||
- `MNOTE_UI_BASE_URL=http://127.0.0.1:3302 node scripts/task516-onlyoffice-bridge-multisession-browser-smoke.js`
|
||||
|
||||
## 2026-06-01 噪音分类与主文档失败断言
|
||||
|
||||
- `scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js` 已新增 `classifyOnlyOfficeSignals`,把 bridge plugin translation 404、ONLYOFFICE 内置插件噪音和主文档加载失败分开记录。
|
||||
- smoke 会断言 MNote bridge session 注册与 command loop 可用,同时要求 `mainDocument.ready=true`。
|
||||
- `errorCode=-18` 不再被当作可忽略噪音;一旦在 console 或 editor error log 中出现,`task518` 会失败并把它归为主文档失败。
|
||||
- `design/07-ai/process/7-43-onlyoffice-plugin-bridge-design-v1.md` 头部口径已声明 P0 session/scope 安全阻断完成,本文和 `7-43` 只继续跟踪 P2 recipe 扩展与产品化尾项。
|
||||
|
||||
本文继续保持 `process` 的剩余原因:
|
||||
|
||||
- 已归档。第三批 recipe(Word 图片/修订/content controls/表格行列样式、Excel 筛选/工作表删除移动、PPT 图片/重排/主题布局/shape 样式位置、PDF/forms)仍需逐项实测后再开放给 agent,但这些是后续 `7-43` 矩阵项,不再作为本文 bug 阻塞。
|
||||
|
||||
## 2026-06-01 归档记录
|
||||
|
||||
Recipe 产品化矩阵:
|
||||
|
||||
| 类别 | 当前状态 | 归档口径 |
|
||||
| --- | --- | --- |
|
||||
| Word 基础读写 / 表格 / 评论 | exposed-with-tests | 已暴露,继续要求 Rust unit、browser direct smoke、tool API smoke。 |
|
||||
| Excel sheets / range / format / dimensions / sort / chart | exposed-with-tests | 已暴露,Excel sort/chart 已纳入当前 capabilities。 |
|
||||
| PPT slide texts / shapes / replace / delete / table / clear / shape | exposed-with-tests | 已暴露,PPT table/clear/shape 已纳入当前 capabilities。 |
|
||||
| Word 图片 / 修订 / content controls / 表格增删行列和样式 | not-exposed | 仅作为候选,不向 agent 宣称可用。 |
|
||||
| Excel 筛选 / 工作表删除移动 | not-exposed | 仅作为候选,不向 agent 宣称可用。 |
|
||||
| PPT 图片 / slide 重排 / 主题布局 / shape 样式位置 | not-exposed | 仅作为候选,不向 agent 宣称可用。 |
|
||||
| PDF / forms 字段读取与填写 | blocked-by-api | 需先确认 ONLYOFFICE 社区版 Plugin API 可行性。 |
|
||||
|
||||
验证:
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1`
|
||||
@@ -0,0 +1,57 @@
|
||||
# 7-58 Mindmap P2 apply_ops 与真实 UI smoke 缺口
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:07-ai / mindmap resource skill / Page AI target runtime
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
Mindmap skill 和资源 target 已有最小闭环:`fetch`、`create_from_outline`、`apply_ops` 基础工具、`mnote-mindmap` skill 注册、`task503` 和 `task525` smoke 已覆盖 API/target 绑定。但 P2 第二批仍缺 apply_ops 操作白名单文档化、长文本节点策略和真实 UI 交互 smoke。
|
||||
|
||||
## 证据
|
||||
|
||||
- `rust/crates/mnote-web/src/hermes_tools/resource.rs` 已有 mindmap `fetch/apply_ops/create_from_outline` 入口。
|
||||
- `scripts/task503-mindmap-skill-capability-smoke.js` 覆盖 skill、create_from_outline、fetch、embed。
|
||||
- `scripts/task525-page-ai-mindmap-resource-target-smoke.js` 覆盖 mindmap resource tab / Page AI targetPackage。
|
||||
- 当前缺口未在独立 bugs/process 中跟踪;历史相关 bug `7-48/7-51/7-52` 已作为各自窄问题归档。
|
||||
|
||||
## 影响
|
||||
|
||||
- agent 可以调用 mindmap 工具,但不清楚 `apply_ops` 的稳定操作白名单和限制。
|
||||
- 长文本节点可能破坏 mindmap 渲染或 agent 输出可读性。
|
||||
- 现有 smoke 偏 API/target 合同,未覆盖用户真实 UI 交互,例如节点编辑、拖拽、导出等。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 已文档化 `apply_ops` 支持的操作白名单、payload schema、dry-run/revision 规则和失败形态。
|
||||
2. 已为关键 ops 增加 Rust 单测,覆盖成功、别名、delete、dry-run、revision mismatch、越权/只读拒绝、unsupported op、invalid ops payload、禁止删除 root。
|
||||
3. 已在 skill prompt 中明确长文本节点压缩策略:短语化、避免长段落,拆成 child nodes。
|
||||
4. 已用真实 mindmap UI smoke 覆盖打开、插入、渲染、样式抽屉默认状态、slash 菜单层级和 resize 基础交互。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `apply_ops` 白名单与限制写入 design / skill。
|
||||
- [x] `hermes_tools_mindmap_apply_ops_*` targeted tests 覆盖核心操作。
|
||||
- [x] 长文本节点策略有测试或明确降级说明。
|
||||
- [x] 至少一条真实 mindmap UI browser smoke 通过并登记到 `scripts/TESTING_REFERENCE.md`。
|
||||
|
||||
## 2026-06-01 修复记录
|
||||
|
||||
- `skills/mnote-mindmap/SKILL.md` 已新增 `Current apply_ops contract`,明确支持 `updateText/updateNode`、`insertChild/addChild`、`deleteNode`,记录 `nodeId/id`、`text/title`、`parentId/nodeId` 等 payload schema、未知 op 拒绝、禁止删除 root、长文本短语化/拆子节点、`dryRun` 与 revision checks。
|
||||
- `rust/crates/mnote-web/src/routes/hermes_tools.rs` 已补齐 apply_ops targeted tests:
|
||||
- `hermes_tools_mindmap_apply_ops_accepts_common_aliases`
|
||||
- `hermes_tools_mindmap_apply_ops_deletes_child_node`
|
||||
- `hermes_tools_mindmap_apply_ops_dry_run_returns_diff_without_writing`
|
||||
- `hermes_tools_mindmap_apply_ops_rejects_invalid_ops_payload`
|
||||
- `hermes_tools_mindmap_apply_ops_rejects_root_delete`
|
||||
- `hermes_tools_mindmap_apply_ops_rejects_unsupported_op`
|
||||
- 已通过:
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_mindmap_apply_ops -- --test-threads=1`
|
||||
- `node --check scripts/task455-local-folder-mindmap-clean-smoke.js`
|
||||
- `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3303 node scripts/task455-local-folder-mindmap-clean-smoke.js`
|
||||
|
||||
真实 UI smoke 结果:
|
||||
|
||||
- `tmp/task455-local-folder-mindmap-clean-smoke/result.json`
|
||||
@@ -0,0 +1,98 @@
|
||||
# 7-49 ChatOnly OpenClaw provider 单测缺口
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:process
|
||||
- Owner:07-ai / OpenClaw provider integration
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
`7-44` 的 MNote 侧 ChatOnly provider conversation binding 已完成并通过真实浏览器 smoke。外部 OpenClaw 仓库已经补齐 Doubao provider 的 conversation id 抽取与 delete helper 单测,但 DeepSeek / Gemini provider 级 delete helper 与单测仍没有可靠实现落点,不能在 MNote 仓库内用集成 smoke 替代。
|
||||
|
||||
## 已有 MNote 侧证据
|
||||
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p control-plane external_conversation -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib provider_conversation -- --test-threads=1`
|
||||
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web --lib chatonly_doubao_session_delete_calls_provider_and_marks_remote_deleted -- --test-threads=1`
|
||||
- `node scripts/task512-chatonly-doubao-sync-smoke.js`
|
||||
- `node scripts/task513-chatonly-provider-sync-smoke.js deepseek`
|
||||
- `node scripts/task513-chatonly-provider-sync-smoke.js gemini`
|
||||
|
||||
## 缺口
|
||||
|
||||
- OpenClaw Doubao provider 的 `conversation_id` 捕获、`deleteConversation` / `/im/conversation/batch_del_user_conv` 已有 provider 仓库内单测。
|
||||
- DeepSeek / Gemini provider delete helper 仍缺 provider 仓库内实现与单测;如果没有可靠远端删除 API,不应伪造 `remote_deleted`。
|
||||
|
||||
## 2026-06-01 外部仓库复核
|
||||
|
||||
- 本机存在 `/home/lix/openclaw-zero-token-latest`,但该仓库已有未提交改动:
|
||||
- `src/zero-token/providers/doubao-web-client-browser.ts`
|
||||
- `src/zero-token/providers/gemini-web-client-browser.ts`
|
||||
- `src/zero-token/streams/doubao-web-stream.ts`
|
||||
- `src/zero-token/providers/web-session-binding.test.ts`
|
||||
- 只读核对显示,现有 `web-session-binding.test.ts` 覆盖 Doubao `conversation_id` SSE 提取和 Gemini URL / stale candidate,但未覆盖 `deleteConversation`、`/im/conversation/batch_del_user_conv`、`remote_deleted` / `remote_delete_failed`。
|
||||
- 尝试运行 `pnpm exec vitest run src/zero-token/providers/web-session-binding.test.ts` 与 `pnpm exec vitest run --config vitest.unit.config.ts src/zero-token/providers/web-session-binding.test.ts` 均因缺少 `@vitest/browser-playwright` 启动失败;`scripts/vitest.zero-token-web.config.ts` 当前 include 不包含 provider test。
|
||||
|
||||
结论:本缺口仍是 OpenClaw 外部仓库未完成项,不应在 MNote 仓库内标记 done。
|
||||
|
||||
## 下一步
|
||||
|
||||
在 OpenClaw provider 源码仓库中继续补 DeepSeek / Gemini provider 删除能力,覆盖:
|
||||
|
||||
- 删除成功返回 `remote_deleted`。
|
||||
- 删除失败返回 `remote_delete_failed` 且脱敏错误。
|
||||
|
||||
## 2026-06-01 外部仓库二次复核
|
||||
|
||||
只读复核 `/home/lix/openclaw-zero-token-latest` 后确认本文仍保持 `process`,不能在 MNote 仓库内归档:
|
||||
|
||||
- 外部仓库已有未提交/未跟踪改动,包含 `src/zero-token/providers/doubao-web-client-browser.ts`、`src/zero-token/providers/gemini-web-client-browser.ts`、`src/zero-token/streams/doubao-web-stream.ts`、`src/zero-token/providers/web-session-binding.test.ts`,本轮不得覆盖或清理。
|
||||
- Doubao stream/client 代码已有 conversation id 捕获和复用路径,但 provider 单测仍只覆盖 SSE 提取、Gemini URL normalize 和 stale candidate;未覆盖 Doubao 复用、`deleteConversation` 成功/失败。
|
||||
- `rg deleteConversation` 在 provider/streams 下未发现 DeepSeek/Gemini delete helper 可见测试。
|
||||
- 默认 `pnpm exec vitest run src/zero-token/providers/web-session-binding.test.ts --reporter=dot` 仍会因根 `vitest.config.ts` 导入但本机缺少 `@vitest/browser-playwright` 失败;`scripts/vitest.zero-token-web.config.ts` 又未 include provider test,强行指定 provider 文件会 `No test files found`。
|
||||
|
||||
结论:MNote 侧 ChatOnly/Doubao binding smoke 可作为 MNote 集成证据,但 `7-49` 的 owner 是 OpenClaw provider 仓库单测缺口,仍需在外部仓库补测试配置和 provider 单测后才能移动到 `done`。
|
||||
|
||||
## 2026-06-01 外部仓库三次推进
|
||||
|
||||
在 `/home/lix/openclaw-zero-token-latest` 已补并验证 Doubao provider 级测试:
|
||||
|
||||
- `src/zero-token/providers/doubao-web-client-browser.ts`
|
||||
- 新增 `ProviderConversationDeleteResult`、`DoubaoConversationDeleteRequest`、`buildDoubaoConversationDeleteRequest`、`deleteDoubaoConversationWithFetch` 和 `DoubaoWebClientBrowser.deleteConversation`。
|
||||
- 删除请求固定走 `/im/conversation/batch_del_user_conv`,失败响应会脱敏 `sessionid` / `ttwid`。
|
||||
- `src/zero-token/providers/web-session-binding.test.ts`
|
||||
- 覆盖 Doubao `conversation_id` / `conversationId` / `sessionId` / object `event_data` 抽取。
|
||||
- 覆盖 `conversation_id:"0"`、malformed SSE、`[DONE]` 跳过。
|
||||
- 覆盖 batch delete request、`remote_deleted` 成功、`remote_delete_failed` 脱敏失败。
|
||||
- `scripts/vitest.zero-token-web.config.ts`
|
||||
- 已 include provider test,并补 `openclaw/plugin-sdk/*` alias,避免根 `vitest` 多项目配置缺 `@vitest/browser-playwright` 阻断 provider 单测。
|
||||
|
||||
已通过:
|
||||
|
||||
- `pnpm exec vitest run --config scripts/vitest.zero-token-web.config.ts src/zero-token/providers/web-session-binding.test.ts --reporter=dot`
|
||||
- `pnpm exec vitest run --config scripts/vitest.zero-token-web.config.ts --reporter=dot`
|
||||
|
||||
Reasonix 只读复核(`reasonix-2026-06-01T00-32-52-539Z-c41a37a3`)结论与主线程复核一致:
|
||||
|
||||
- Doubao 是当前唯一有 provider 级 `deleteConversation` / `remote_deleted` / `remote_delete_failed` helper 与单测的 web provider。
|
||||
- DeepSeek / Gemini 只有 stream 层 `sessionMap` / `conversationMap` 复用,没有 provider 级删除能力。
|
||||
- Qwen、GLM、Kimi、Grok、Claude、ChatGPT、XiaoMiMo、Perplexity 等其他 web provider 也未见 provider 级 delete helper;这些不是 `7-44` Doubao 绑定的阻断项,但应作为后续 OpenClaw provider cleanup 矩阵继续跟踪。
|
||||
- `pnpm exec vitest run --config scripts/vitest.zero-token-web.config.ts --reporter=dot` 在外部仓库通过,3 files / 14 tests passed。
|
||||
|
||||
本文继续保持 `process` 的剩余原因:
|
||||
|
||||
- DeepSeek provider 当前没有 `deleteConversation` / delete helper;不能从 MNote 侧 `task513` 推断 provider 仓库已有可靠远端删除实现。
|
||||
- Gemini provider 当前只有 DOM 对话打开和 conversation URL normalize / stale candidate 测试;没有 provider 级远端删除 helper。
|
||||
- 后续需要先核实 DeepSeek / Gemini 真实远端删除 API 或明确降级语义,再补 provider 单测;不能为了归档而把本地删除伪装成 `remote_deleted`。
|
||||
|
||||
## 2026-06-01 subagent 复核确认
|
||||
|
||||
只读 subagent 复核 `/home/lix/openclaw-zero-token-latest` 后确认本文继续保持 `process`:
|
||||
|
||||
- Doubao provider delete/helper/test 与 Vitest 专用配置已补齐。
|
||||
- `rg deleteConversation|remote_deleted|remote_delete_failed|batch_del_user_conv src/zero-token/providers src/zero-token/streams` 仍只命中 Doubao 和 `web-session-binding.test.ts`,未命中 DeepSeek / Gemini。
|
||||
- DeepSeek 当前只有 `createChatSession` / `chatCompletions` 和 stream `sessionMap` 复用。
|
||||
- Gemini 当前只有 URL normalize、DOM 打开/复用对话和 `conversation_id: page.url()`,没有 provider 级远端删除 helper。
|
||||
|
||||
因此 MNote 侧 `7-44` / `task512` / `task513` 只能证明 MNote provider conversation binding,不足以替代外部 provider 仓库的 DeepSeek / Gemini 删除单测。
|
||||
@@ -0,0 +1,45 @@
|
||||
# 10-18 design process 状态与入口漂移
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:10-review / design governance
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
当前 design 主入口和若干 `process/` 文档状态已经与真实口径不一致,容易误导后续 worker 重复执行已完成任务,或从已迁移到 `old/` 的历史文档读取优先级。
|
||||
|
||||
## 证据
|
||||
|
||||
- `design/README.md` 曾把 `design/01-05-current-priority-overview.md` 写成当前优先级入口,但实际文件已在 `design/old/01-05-current-priority-overview.md`。
|
||||
- `design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md` 曾在上位依据中引用同一个已迁移文件,并仍引用已归档的 `3-3` / `3-18` 作为 active process 入口。
|
||||
- `design/03-rust-web/process/3-24-global-content-type-page-width-settings-v1.md` checklist 全部完成,曾仍位于 `process/`;2026-05-31 已迁入 `design/03-rust-web/done/`。
|
||||
- `design/05-editor-mainline/process/5-31-page-settings-sqlite-preference-convergence-v1.md` 已记录 Phase A/B/C/D 最小闭环完成,但末尾推荐仍建议从 Phase A/B 开始;2026-05-31 已迁入 `design/05-editor-mainline/done/`。
|
||||
- `design/05-editor-mainline/process/5-32-filetree-lazy-loading-sidex-alignment-v1.md` checklist 和验证记录已完成;2026-05-31 已迁入 `design/05-editor-mainline/done/`。
|
||||
- `design/07-ai/process/7-37-claudecode-reasonix-worker-evaluation-0524-bug2-v1.md` 是 worker 评估材料,不是 active implementation checklist;2026-05-31 已迁入 `design/07-ai/reference/`。
|
||||
- `design/07-ai/process/7-41-page-ai-hermes-reasonix-user-profile-isolation-v2.md` 文件头状态为 `done`,但仍在 `process/`;2026-06-01 已迁入 `design/07-ai/done/`。`design/10-review/process/17-sidex-mnote-workbench-gap-execution-checklist-v1.md` 曾有同类漂移,2026-05-31 已迁入 `design/10-review/done/`。
|
||||
|
||||
## 影响
|
||||
|
||||
- 后续 agent 可能按旧入口恢复过时优先级。
|
||||
- 已完成设计继续占用 active process 队列,导致任务拆分噪声。
|
||||
- process/done 目录语义被削弱,bug 和设计治理难以闭环。
|
||||
|
||||
## 修复建议
|
||||
|
||||
1. 已先修正 `design/README.md` 和 `1-8` 的失效入口。
|
||||
2. 下一步逐个迁移或拆分状态漂移文档:
|
||||
- `3-24` 已迁入 `03-rust-web/done/`,Mindmap 真实消费另拆 follow-up。
|
||||
- `5-31` 已迁入 `05-editor-mainline/done/`,剩余风险另拆。
|
||||
- `5-32` 已迁入 `05-editor-mainline/done/`。
|
||||
- `5-33` 拆成已完成核心和 breadcrumb/fetch guard follow-up。
|
||||
- `7-37` 已迁入 `07-ai/reference/`。
|
||||
- `7-41` 已迁入 `07-ai/done/`。
|
||||
- `17` 已迁入 `10-review/done/`。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `find design -path '*/process/*' -type f ! -path 'design/old/*'` 中不再出现文件头状态为 `done` 的文档。
|
||||
- [x] `rg -n "design/01-05-current-priority-overview.md" design/README.md design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md` 无 active 入口引用。
|
||||
- [x] 每个保留在 `process/` 的文档都有未完成 checklist、owner 和验收条件;`5-33` 仍保留在 `process/` 是因为 breadcrumb/fetch guard 等 follow-up 未完成。
|
||||
@@ -0,0 +1,43 @@
|
||||
# 10-19 AGENTS.md 架构参考路径断裂
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:10-review / AGENTS 协作规则
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
发现时,`AGENTS.md` 的“需要架构判断时,优先参考”列表中有两个路径已经断裂。因为 `AGENTS.md` 是 agent 的顶层协作规则,这类失效引用会直接误导后续任务启动时的架构判断。
|
||||
|
||||
## 证据
|
||||
|
||||
- 发现时,`AGENTS.md` 仍引用 `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`,但该文件实际位于 `design/old/01-05-current-priority-overview.md`,根路径不存在。
|
||||
- 发现时,`AGENTS.md` 仍引用 `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`,但实际文件位于 `design/01-tree-first-graph-kernel/reference/1-tree-first-graph-kernel-v1.md`。
|
||||
- 本轮已更新 `design/README.md`、`1-8` 与 `AGENTS.md` 的 active 入口。
|
||||
|
||||
## 影响
|
||||
|
||||
- 后续 agent 按 `AGENTS.md` 查架构依据时会遇到文件不存在。
|
||||
- worker 可能因此退回旧记忆或自己猜测口径。
|
||||
- `01-05` 已迁入 `old/` 但仍被当作当前入口,削弱 `design/README.md` 和 `1-8` 的入口权威。
|
||||
|
||||
## 修复建议
|
||||
|
||||
修复方式:
|
||||
|
||||
- 将 `01-05-current-priority-overview.md` 替换为 `CURRENT_ARCHITECTURE.md` 与 `design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`。
|
||||
- 将 `process/1-tree-first-graph-kernel-v1.md` 改为 `reference/1-tree-first-graph-kernel-v1.md`,或删除该历史参考入口。
|
||||
|
||||
## 本轮进展
|
||||
|
||||
- 2026-05-31:用户设置的 P0 goal 已明确包含 `AGENTS/design 断裂引用`,本轮已更新 `AGENTS.md`:
|
||||
- 新增 `CURRENT_ARCHITECTURE.md`。
|
||||
- 新增 `design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`。
|
||||
- 将 `1-tree-first-graph-kernel-v1.md` 指向 `reference/`。
|
||||
- 移除已不存在的 root `design/01-05-current-priority-overview.md` 引用。
|
||||
|
||||
## 验收
|
||||
|
||||
- `test -f` 验证 `AGENTS.md` 中列出的每个绝对路径都存在。
|
||||
- `rg -n "design/01-05-current-priority-overview.md|process/1-tree-first-graph-kernel-v1.md" AGENTS.md` 不再命中 active 断裂引用。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 10-20 新增 smoke 未及时进入 TESTING_REFERENCE
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:10-review / smoke governance
|
||||
- 发现时间:2026-05-31
|
||||
|
||||
## 现象
|
||||
|
||||
近期新增的 Page AI / mindmap / ChatOnly smoke 未及时进入 `scripts/TESTING_REFERENCE.md`。该文件是当前 smoke 分类与默认基线的权威入口,缺失会导致新增能力线没有明确验证入口。
|
||||
|
||||
## 证据
|
||||
|
||||
本轮发现时,`scripts/TESTING_REFERENCE.md` 未收录:
|
||||
|
||||
- `scripts/task503-mindmap-skill-capability-smoke.js`
|
||||
- `scripts/task504-page-ai-history-agent-filter-smoke.js`
|
||||
- `scripts/task512-chatonly-doubao-sync-smoke.js`
|
||||
- `scripts/task513-chatonly-provider-sync-smoke.js`
|
||||
|
||||
## 本轮处理
|
||||
|
||||
已把上述 smoke 补入 `scripts/TESTING_REFERENCE.md`:
|
||||
|
||||
- `task503` 归入资源对象与 mindmap / Page AI mindmap skill。
|
||||
- `task504` 归入 Page AI history / agent filter。
|
||||
- `task512`、`task513` 归入 ChatOnly / provider session 绑定。
|
||||
- 另补入本轮新增的 `task514-sidebar-dev-hot-reload-gating-smoke.js`、`task515-onlyoffice-live-scope-http-smoke.js`、`task516-onlyoffice-bridge-multisession-browser-smoke.js`。
|
||||
|
||||
## 剩余问题
|
||||
|
||||
本轮已实跑:
|
||||
|
||||
- `node scripts/task503-mindmap-skill-capability-smoke.js`
|
||||
- `node scripts/task504-page-ai-history-agent-filter-smoke.js`
|
||||
- `node scripts/task512-chatonly-doubao-sync-smoke.js`
|
||||
- `node scripts/task514-sidebar-dev-hot-reload-gating-smoke.js`
|
||||
- `node scripts/task515-onlyoffice-live-scope-http-smoke.js`
|
||||
- `node scripts/task516-onlyoffice-bridge-multisession-browser-smoke.js`
|
||||
- `node scripts/task513-chatonly-provider-sync-smoke.js deepseek`
|
||||
- `node scripts/task513-chatonly-provider-sync-smoke.js gemini`
|
||||
|
||||
当前 smoke reference 缺项已补齐并实跑。后续若新增 smoke,需要继续按本文件规则同步 `scripts/TESTING_REFERENCE.md`。
|
||||
|
||||
## 验收
|
||||
|
||||
- `rg -n "task503|task504-page-ai-history|task512|task513" scripts/TESTING_REFERENCE.md` 能命中新条目。
|
||||
- `task513-chatonly-provider-sync-smoke.js deepseek` 与 `task513-chatonly-provider-sync-smoke.js gemini` 已实跑通过,并已写回 `design/07-ai/done/7-44-chatonly-doubao-session-binding-v1.md`。
|
||||
@@ -0,0 +1,39 @@
|
||||
# 10-21 design done 文件头状态漂移
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:10-review / design governance
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
部分已经位于 `design/**/done/` 的设计稿,文件头仍保留 `process` / `PROCESS` 状态。后续 agent 或脚本如果只读文件头而不看目录状态,容易把已归档文档误判为 active process。
|
||||
|
||||
## 证据
|
||||
|
||||
- `design/07-ai/done/7-42-page-ai-mindmap-skill-and-resource-generation-v1.md` 位于 `done/`,但文件头仍写 `状态:process`。
|
||||
- `design/05-editor-mainline/done/5-31-page-settings-sqlite-preference-convergence-v1.md` 位于 `done/`,但文件头仍写 `状态:process`。
|
||||
- `design/05-editor-mainline/done/5-32-filetree-lazy-loading-sidex-alignment-v1.md` 位于 `done/`,但文件头仍写 `状态:process`。
|
||||
- `design/04-tree-domain/done/4-49-local-file-operation-event-contract-v1.md` 位于 `done/`,但文件头仍写 `状态:process`。
|
||||
|
||||
## 影响
|
||||
|
||||
- 设计治理扫描和 worker 任务拆分可能重复打开已完成文档。
|
||||
- `process/` 与 `done/` 的事实源边界被削弱。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 扫描 `design/**/done/*.md` 中的文件头状态字段。
|
||||
2. 将已经归档且证据充分的文档头部状态统一改为 `done`。
|
||||
3. 对确实仍有未完成项的文档,不留在 `done/` 中混用;应拆 follow-up 到 `process/`。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `design/**/done/*.md` 中不再出现文件头 `状态:process` / `当前状态:PROCESS`。
|
||||
- [x] 若存在例外,必须在文档头部说明其为历史快照而非 active process。
|
||||
|
||||
## 2026-06-01 修复记录
|
||||
|
||||
- 已将当前扫描命中的 `design/**/done/*.md` 文件头状态统一改为 `done` / `DONE`。
|
||||
- 已验证:`rg -n '^> (当前状态|状态):`?(PROCESS|process)' design/*/done design/old/*/done -g '*.md'` 无结果。
|
||||
@@ -0,0 +1,38 @@
|
||||
# 10-22 done bug 文档残留过时 follow-up 文案
|
||||
|
||||
## 状态
|
||||
|
||||
- 状态:done
|
||||
- Owner:10-review / bugs governance
|
||||
- 发现时间:2026-06-01
|
||||
|
||||
## 现象
|
||||
|
||||
部分已经位于 `bugs/**/done/` 的 bug 文档仍保留过时的“仍缺 / 待复测”文案,而同文档或后续 bug 已经补充了完成证据。这会让 reviewer 误判 done 条目仍未完成。
|
||||
|
||||
## 证据
|
||||
|
||||
- `bugs/07-ai/done/7-52-page-ai-target-picker-resource-target-gap-v1.md` 仍写“仍缺真实 mindmap resource tab 自动打开后的 UI 级 contextRefs 复测”,但后续 `task525-page-ai-mindmap-resource-target-smoke.js` 已覆盖 mindmap resource target。
|
||||
- `bugs/07-ai/done/7-53-page-ai-runtime-split-threshold-triggered-v1.md` 的主 bug 已完成拆分阈值,但残留 stop/close smoke `[~]` 项;该缺口已拆为 `bugs/07-ai/process/7-56-page-ai-runtime-stop-close-smoke-gap-v1.md` 后,应避免继续让 `7-53` 看起来未完成。
|
||||
|
||||
## 影响
|
||||
|
||||
- `done/` bug 的完成边界不清晰。
|
||||
- 后续 agent 容易重复寻找已覆盖缺口,或者把独立 follow-up 误认为原 bug 未完成。
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 扫描近期 `bugs/**/done/*.md`,把已被后续 smoke 覆盖的旧 follow-up 文案改成完成记录。
|
||||
2. 对仍未完成的尾项,拆到独立 `bugs/**/process/`,并在原 done 文档写明“尾项已拆出”。
|
||||
3. 不改变 bug 事实源:没有真实验证的项不得从文案中删除,只能拆出跟踪。
|
||||
|
||||
## 验收
|
||||
|
||||
- [x] `7-52` 中 mindmap resource tab 复测口径与 `task525` 证据一致。
|
||||
- [x] `7-53` 中 stop/close 缺口指向 `7-56`,主拆分 bug 保持 done。
|
||||
- [x] `git diff --check` 通过。
|
||||
|
||||
## 2026-06-01 修复记录
|
||||
|
||||
- `7-52` 已改为记录 `task525-page-ai-mindmap-resource-target-smoke.js` 覆盖真实 mindmap resource tab target / contextRefs 复测。
|
||||
- `7-53` 已把停止/关闭完整断言尾项拆到 `bugs/07-ai/process/7-56-page-ai-runtime-stop-close-smoke-gap-v1.md`,主拆分阈值 bug 保持 done。
|
||||
Reference in New Issue
Block a user