# 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`。
