2026-05-21 09:04:13 +08:00
|
|
|
|
# 7-16 [done] Page Block AI 真实页面 Smoke 后续矩阵 v1
|
2026-05-18 17:01:35 +08:00
|
|
|
|
|
|
|
|
|
|
> 创建时间:2026-05-18
|
|
|
|
|
|
>
|
2026-05-21 09:04:13 +08:00
|
|
|
|
> 当前状态:`DONE`
|
|
|
|
|
|
>
|
|
|
|
|
|
> 归档说明(2026-05-21):本文范围内可执行的真实页面 smoke 已完成;剩余 UI / Review Mode 属于 Phase C 冻结范围,解冻时应新建设计稿承接。
|
2026-05-18 17:01:35 +08:00
|
|
|
|
>
|
|
|
|
|
|
> 来源:
|
|
|
|
|
|
> - `/mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
|
|
|
|
|
|
> - `/mnt/Data1T/mnote/design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md`
|
|
|
|
|
|
> - `/mnt/Data1T/mnote/design/10-review/process/08-kernel-architecture-next-priority-review-and-checklist.md`
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 目的
|
|
|
|
|
|
|
|
|
|
|
|
`7-10` 已完成大量工具合同、Rust route 测试和部分真实 3000 smoke,但仍有一组“必须真实页面验证”的矩阵项未完成。
|
|
|
|
|
|
|
|
|
|
|
|
本文只承接这些剩余 smoke,不新增 AI 功能面,不改变当前主路径:
|
|
|
|
|
|
|
2026-05-19 08:11:58 +08:00
|
|
|
|
- local-first 普通 Markdown 编辑主路径仍是“授权文件引用 + agent 原生 patch/diff + watcher 同步”;本文只验证 mnote tools 的结构性辅助和 compat fallback。
|
|
|
|
|
|
- `mnote.doc.markdown_edit` 只作为 cloud / remote agent / compat fallback 的 smoke 对象。
|
2026-05-18 17:01:35 +08:00
|
|
|
|
- `mnote.block.*` 仍只作为结构性辅助。
|
|
|
|
|
|
- `mnote.page.save` 仍只作为页面级粗粒度兜底。
|
|
|
|
|
|
- Phase C review / streaming apply 仍冻结,当前只验证基础合同和安全边界。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 当前已具备的前置证据
|
|
|
|
|
|
|
|
|
|
|
|
- `mnote.doc.fetch scope=selection format=page_xml/text`、`mnote.block.fetch format=page_xml/text`、manifest annotations、`mnote.doc.apply_block_ops allowedTargetBlockIds` 越界阻断已有真实 3000 smoke:
|
|
|
|
|
|
- `tmp/page-block-ai-context-format-smoke/mp8ddr4n.json`
|
|
|
|
|
|
- `tmp/page-block-ai-context-format-smoke/mp8ddr4n-page.png`
|
|
|
|
|
|
- `mnote.block.replace` stale revision / stale blockRevisionRef / idempotency replay 已有真实 3000 smoke:
|
|
|
|
|
|
- `scripts/task-page-block-ai-conflict-idempotency-smoke.js`
|
|
|
|
|
|
- 证据记录在 `08` 的 Page Block AI Tooling 章节。
|
|
|
|
|
|
- `mnote.block.insert_after` 多块插入和 20 个边界已有 Rust route 回归:
|
|
|
|
|
|
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_insert_after -- --nocapture`
|
|
|
|
|
|
- `mnote.block.move_after` 复杂块阻断矩阵已有隔离 route smoke:
|
|
|
|
|
|
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_block_move_after_blocks_complex_and_nested_blocks -- --nocapture`
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 后续真实页面 Smoke 矩阵
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 Page Aggregate Block Projection
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 登录测试账号并创建综合页面 `TEST-AI-BLOCK-TOOLS-<timestamp>`。
|
|
|
|
|
|
- [x] 写入段落、标题、todo、列表、mindmap/resource 占位。
|
|
|
|
|
|
- [x] 调 `/api/page-aggregate/:id` 并保存响应到 `tmp/hermes-tester/page-block-ai-tools-<run-id>/page-aggregate.json`。
|
|
|
|
|
|
- [x] 断言所有普通可编辑块有 `blockId` 和 `revisionRef`。
|
|
|
|
|
|
- [x] 页面刷新后 block ids 不变化。
|
|
|
|
|
|
- [x] projection 中 `blockCount` 与页面块数量一致;复杂块返回 `editable=false` 与 `unsupportedReason`。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- Page Aggregate:`tmp/hermes-tester/page-block-ai-tools-mpag4966/page-aggregate.json`
|
|
|
|
|
|
- 覆盖:Page Aggregate `blockProjectionVersion=1`、`projectionSource=documents.content`、`aggregateBlockCount=10`,与 `mnote.doc.fetch` 块顺序一致;刷新后 `afterRefreshBlockIds` 保持最终顺序;`table_1/mindmap_1/resource_1` 返回 `editable=false` 与 `unsupportedReason=复杂块暂不开放 AI 精确写入`。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 `mnote.doc.fetch` / `mnote.doc.find`
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 使用综合页面 `TEST-AI-BLOCK-TOOLS-<timestamp>`。
|
|
|
|
|
|
- [x] `mnote.doc.fetch scope=full detail=with_ids` 读取整页。
|
|
|
|
|
|
- [x] `mnote.doc.fetch scope=outline detail=with_ids` 只返回标题结构。
|
|
|
|
|
|
- [x] `mnote.doc.find query=<唯一前缀>` 定位目标段落。
|
|
|
|
|
|
- [x] 保存工具返回到 `tmp/hermes-tester/page-block-ai-tools-<run-id>/doc-fetch-find.json`。
|
|
|
|
|
|
- [x] 确认读取不依赖浏览器 DOM。
|
|
|
|
|
|
- [x] `doc.find` 返回的 block id 能被 `block.fetch` 读取。
|
|
|
|
|
|
- [x] 大页面默认分块或限制输出,不默认返回无限正文。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- 命令:`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- 工具返回:`tmp/hermes-tester/page-block-ai-tools-mpag4966/doc-fetch-find.json`
|
|
|
|
|
|
- 覆盖:`doc.fetch.initial` 读取 `heading_1/p_1/p_2/p_3` 等 10 个块;`doc.fetch scope=outline` 只返回 `heading_1/heading_parent`;`doc.find query="第二段 <suffix>"` 定位 `p_2`;`maxBlocks=2` 返回 `truncated=true`。
|
|
|
|
|
|
- 静态边界:`mnote.doc.fetch` 从 Page Aggregate block projection 构建上下文,不读取浏览器 DOM;默认 `maxBlocks=120` 且 clamp 到 `1..=240`。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 `mnote.block.fetch`
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 从 `doc.find` 结果选择一个段落 block。
|
|
|
|
|
|
- [x] 调 `mnote.block.fetch includeChildren=true contextBefore=1 contextAfter=1`。
|
|
|
|
|
|
- [x] 断言 before/after 只来自同父级。
|
|
|
|
|
|
- [x] `revisionRef` 可被后续 dry-run 使用。
|
|
|
|
|
|
- [x] 复杂块不会伪装成完全可编辑。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- 覆盖:`block.fetch` 读取 `p_2`,返回 `revisionRef=pageRev:1:block:p_2:...`,同父级 `before=["p_1"]`、`after=["p_3"]`;`table_1/mindmap_1/resource_1` 均返回 `editable=false` 与 `unsupportedReason=复杂块暂不开放 AI 精确写入`。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.4 `mnote.doc.plan_update` / dry-run
|
|
|
|
|
|
|
|
|
|
|
|
- [x] `command=str_replace` 不再作为当前 `plan_update` 目标;普通正文 search/replace 已由 `mnote.doc.markdown_edit` 合同承接。
|
|
|
|
|
|
- [x] 对目标段落执行 `block_replace dryRun=true`。
|
|
|
|
|
|
- [x] 对目标段落执行 `block_insert_after dryRun=true`。
|
|
|
|
|
|
- [x] 对两个同父级普通块执行 `block_move_after dryRun=true`。
|
|
|
|
|
|
- [x] dry-run 前后分别读取 `/api/page-aggregate/:id` 与 `mnote.doc.fetch`,确认 revision / conflictDetectionKey / 正文文本不变。
|
|
|
|
|
|
- [x] dry-run 不写 Convex。
|
|
|
|
|
|
- [x] plan 能解释 before/after。
|
|
|
|
|
|
- [x] 不支持场景返回 `blocked=true`。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- 覆盖:`plan_update.dry_run.replaceDiff.before/after`、`insertDiff.after/content`、不支持 `move_after p_3 -> p_3` 返回 `blocked=true`、dry-run 后 `revisionAfterDryRun=1` 且正文未变化。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.5 `mnote.block.replace`
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 使用综合页面 `TEST-AI-BLOCK-TOOLS-<timestamp>`。
|
|
|
|
|
|
- [x] 写入两段不同文本。
|
|
|
|
|
|
- [x] `doc.find` 找到第二段 block id。
|
|
|
|
|
|
- [x] `block.replace dryRun=true` 查看 plan。
|
|
|
|
|
|
- [x] `block.replace dryRun=false` 替换第二段。
|
|
|
|
|
|
- [x] 页面截图和工具回读证明只替换第二段。
|
|
|
|
|
|
- [x] `/api/page-aggregate/:id` 回读证明持久化。
|
|
|
|
|
|
- [x] `mnote.doc.fetch` 再次证明 AI 可读回。
|
|
|
|
|
|
- [x] 相邻块不变化。
|
|
|
|
|
|
- [x] 目标 block id 保持不变。
|
|
|
|
|
|
- [x] 旧 revision 写入返回 conflict。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- 截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`
|
|
|
|
|
|
- 覆盖:`block.replace.changedBlocks[0]={blockId:"p_2",op:"replace"}`,`targetBlockStillExists=true`,`adjacentTexts=["第一段 mpag4966","第三段 mpag4966"]`,`mnote.doc.fetch` 与 Page Aggregate 均回读到 `第二段已替换 mpag4966`,Page Aggregate `aggregateRevision=2`、`aggregateConflictDetectionKey=tree_1779062903017_3:2`。
|
|
|
|
|
|
- 冲突 / 幂等补充 JSON:`tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs.json`,截图:`tmp/page-block-ai-conflict-idempotency-smoke/mpafrevs-page.png`。
|
|
|
|
|
|
- 覆盖:首次 `block.replace` 写入 `p_2` 后,重复 `idempotencyKey=idem_conflict_replace_mpafrevs` replay 同一 `commandId=page_body_save_req_1779062303997_525` 且 `revisionAfterFirst=2`、`revisionAfterReplay=2`;旧 `revision/conflictDetectionKey` 与旧 `blockRevisionRef` 均返回 HTTP `400` / `mnote_tool_conflict`,最终文本保持 `第二段首次替换 mpafrevs`。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.6 `mnote.block.insert_after`
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 使用综合页面 `TEST-AI-BLOCK-TOOLS-<timestamp>`。
|
|
|
|
|
|
- [x] 在第一段后插入 todo。
|
|
|
|
|
|
- [x] 截图证明位置准确。
|
|
|
|
|
|
- [x] 刷新页面后再次确认。
|
|
|
|
|
|
- [x] `mnote.block.fetch` 能读取新 block id。
|
|
|
|
|
|
- [x] 插入位置准确。
|
|
|
|
|
|
- [x] 新 block id 不是 AI 自造未校验 id。
|
|
|
|
|
|
- [x] 重复同一 `idempotencyKey` 不重复插入。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- 命令:`PLAYWRIGHT_CHROME_EXECUTABLE=/snap/bin/chromium MNOTE_UI_BASE_URL=http://127.0.0.1:3000 MNOTE_AUTH_BASE_URL=http://127.0.0.1:3000 node scripts/task-page-block-ai-tools-smoke.js`
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- 截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`
|
|
|
|
|
|
- 说明:该 smoke 使用 `TEST-AI-BLOCK-TOOLS-*` 综合页面,不是单独 `TEST-AI-BLOCK-INSERT-*` 页面;已覆盖 todo 插入、插入后顺序、block.fetch 回读、刷新后可见和重复 idempotencyKey replay。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.7 `mnote.block.move_after`
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 使用综合页面 `TEST-AI-BLOCK-TOOLS-<timestamp>`,包含三段普通段落。
|
|
|
|
|
|
- [x] dry-run 移动第三段到第一段后。
|
|
|
|
|
|
- [x] 正式执行同父级移动。
|
|
|
|
|
|
- [x] 截图证明顺序为第一段、第三段、第二段。
|
|
|
|
|
|
- [x] `mnote.doc.fetch` 回读顺序一致。
|
|
|
|
|
|
- [x] 对标题带子块、列表项、表格、mindmap 执行 move dry-run,必须返回 blocked。
|
|
|
|
|
|
- [x] moving block id 保持不变。
|
|
|
|
|
|
- [x] 同父级顺序正确。
|
|
|
|
|
|
- [x] 复杂块不被误移动。
|
|
|
|
|
|
|
|
|
|
|
|
证据(2026-05-18):
|
|
|
|
|
|
|
|
|
|
|
|
- JSON:`tmp/page-block-ai-tools-smoke/mpag4966.json`
|
|
|
|
|
|
- 截图:`tmp/page-block-ai-tools-smoke/mpag4966-page.png`
|
|
|
|
|
|
- 覆盖:`heading_parent/list_item_1/table_1/mindmap_1/resource_1` 的 `plan_update block_move_after dryRun=true` 与正式 `mnote.block.move_after dryRun=false` 均返回 `blocked=true` / `block_move_after_blocked`,阻断后 `revisionAfterBlocked=1` 且正文不变化;普通 `p_3 -> p_1` 正式移动后顺序为 `heading_1,p_1,p_3,ai_block_...,p_2,...`,`movingBlockStillExists=true`。
|
|
|
|
|
|
|
|
|
|
|
|
### 3.8 UI / Review Mode 边界
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] Hermes tool event UI 展示 `plan/diff/warnings/risk`。
|
|
|
|
|
|
- [ ] 本地 preview/suggestion 与持久 comment/tracked-change 分开。
|
|
|
|
|
|
- [ ] 协作可见审阅必须另走正式 comment/history/tracked-change 设计。
|
|
|
|
|
|
- [ ] 用户能看到 AI 将改哪个 block。
|
|
|
|
|
|
- [ ] `blocked=true` 的工具调用不会出现写入按钮。
|
|
|
|
|
|
- [ ] preview 不写入正式 comment/history。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 验证原则
|
|
|
|
|
|
|
|
|
|
|
|
- 所有真实页面 smoke 必须使用测试账号,优先从 `http://localhost:3000/auth` 快速登录。
|
|
|
|
|
|
- 证据必须保留 JSON 和截图,默认放入 `tmp/hermes-tester/<run-id>/` 或专项 `tmp/page-block-ai-*` 目录。
|
|
|
|
|
|
- 发现失败时按 owner 写入 `bugs/<category>/process/`,不要只在本矩阵中留描述。
|
|
|
|
|
|
- 不用此矩阵扩展新 AI 功能;只补真实页面证据和安全边界。
|