# 10-review 顺序执行清单与验收标准

> 执行状态：已完成并归档到 `design/10-review/done/`。本文是本轮 10-review 的最终验收依据与防回归口径，根目录 `design/10-review/README.md` 仅保留索引。

> 更新时间：2026-05-14
>
> 上游依据：
> - `/mnt/Data1T/mnote/design/10-review/README.md`
> - `/mnt/Data1T/mnote/design/10-review/done/01-rust-kernel-web-review.md`
> - `/mnt/Data1T/mnote/design/10-review/done/02-frontend-editor-tree-review.md`
> - `/mnt/Data1T/mnote/design/10-review/done/03-convex-realtime-storage-review.md`
> - `/mnt/Data1T/mnote/design/10-review/done/04-secondary-domains-and-design-governance-review.md`
> - `/mnt/Data1T/mnote/design/10-review/done/05-tree.md`
> - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md`
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-12-main-editor-object-tab-resource-alignment-checklist-v1.md`

## 0. 执行前提

`05-tree.md` 中关于 Resource Tree / File Tree / Page Tree、ObjectIdentity、mindmap 与 `index.md` 隔离的核心事项已经由 `4-24` 与 `5-12` 承接到 `done/`。后续执行不要重复重做该主线，只需要保留防回归验证。

本清单按依赖顺序执行。原则是先修会影响主链一致性和数据可信度的 P0，再处理 realtime / projection / compat 收口，最后做次级域和文档治理。

## 1. P0：修正 Rust page write artifact 一致性

状态：已完成第 1 项代码侧与 Rust 3000 验证。Rust `/api/documents/title` 与 `/api/documents/options` 已切到 artifact 记录路径；`page.layout.updateOptions` 已补 tree domain event / conservative resync artifact；Rust 3000 下已直接验证标题更新后 `bridge.workspace.overview` 可读到 `page.head.updateTitle` command log 与 `tree.node.renamed` domain event，`/api/tree/events` 也能输出包含新标题的 stream 事件。完整 File Tree 标题同步仍归入第 6 项继续处理。

目标：让 3000 主入口下的页面标题、页面设置、正文保存拥有一致 side effect，确保 tree stream 能看到相关变更。

建议修改范围：

- `rust/crates/mnote-web/src/routes/documents.rs`
- `rust/crates/mnote-web/src/routes/command_support.rs`
- `rust/crates/mnote-web/src/transport/convex.rs`
- 必要时补充 `wolai-frontend/convex/bridgeLogs.ts` 查询侧验证

执行项：

- [x] 将 Rust `/api/documents/title` 的命令执行切到 artifact 记录路径，或统一复用正式 page/tree command route。
- [x] 将 Rust `/api/documents/options` 的命令执行切到 artifact 记录路径，或统一复用正式 page/tree command route。
- [x] 明确 artifact 写失败策略：主 mutation 成功但 artifact 失败时至少要有可观测错误，并触发保守 resync 或明确降级。
- [x] 补回归测试：标题更新后，`bridgeLogs:listWorkspaceOverview` 能看到对应 command/domain event。

验收标准：

- [x] `cargo test -p mnote-web documents_title -- --nocapture` 或等价标题 route 测试通过。
- [x] `cargo test -p mnote-web documents_options -- --nocapture` 或等价页面设置 route 测试通过。
- [x] 手动或脚本验证：更新标题后 `/api/tree/events` 输出 delta 或 resync，不需要刷新整页才能看到 sidebar/breadcrumb 变化。
- [x] Rust route 与 Next legacy adapter 对同一 page write 的 canonical command / artifacts 语义一致。

证据：

- `rust/crates/mnote-web/src/routes/documents.rs`：`title` / `options` 使用 `execute_runtime_command_via_convex_with_artifacts`，响应 `meta.artifacts` / `meta.artifactError`。
- `rust/crates/bridge-runtime/src/lib.rs`：`page.layout.updateOptions` 产生 `page.layout.options_updated` domain event 与 `resync_required` stream delta。
- `rust/crates/mnote-web/src/transport/convex.rs`：发送给 legacy `documents:updateOptions` 时剥离 artifact-only 字段。
- 已通过：Rust 3000 目标脚本创建临时页面后调用 `/api/documents/title`，再查 `/api/bridge/workspace?targetPageId=<documentId>&aggregateType=page&aggregateId=<documentId>`，确认返回 `command_name=page.head.updateTitle` 与 `event_type=tree.node.renamed`。
- 已通过：Rust 3000 目标脚本创建临时页面后调用 `/api/documents/title`，再查 `/api/tree/events?workspaceId=<workspaceId>&maxPolls=1&pollMs=250`，确认 stream 文本包含新标题且包含 `snapshot` / `delta` / `resync` 事件。
- 已通过：`node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`。
- 部分通过：`node scripts/task110-page-title-single-truth-smoke.js` 在 Rust 3000 下已通过页头、breadcrumb、sidebar、page tree 标题一致性检查；当前失败点为 File Tree 标题行，继续归入第 6 项处理。
- 已通过：`pnpm test -- src/app/api/documents/route-adapters.test.ts src/lib/documents/page-write-command-adapter.test.ts`（Vitest 实际执行 112 个测试文件、457 个测试），Next `documents/title` route 与 page write adapter 继续生成 `page.head.updateTitle` canonical command 并进入 Rust artifact writer。
- 已通过：`cargo test -p mnote-web documents_ -- --nocapture`。
- 已通过：`cargo test -p bridge-runtime page_head_update_title_command_plan_uses_canonical_page_command -- --nocapture`。
- 已通过：`cargo test -p bridge-runtime page_layout_update_options_command_plan_uses_canonical_page_command -- --nocapture`。
- 已通过：`cargo test -p bridge-runtime document_options_command_plan_maps_to_documents_update_options -- --nocapture`。
- 已通过：`cargo test -p mnote-web convex_command_args_strips_page_options_artifacts_for_legacy_mutation -- --nocapture`。

## 2. P0：移除或显式隔离假 fallback 数据

状态：已完成代码侧收口与负向单测。Search 默认不再返回内置固定业务结果；workspace shell projection 默认进入 degraded empty；Sidebar/File Tree dev fallback 仅在 `allow_dev_fixtures` 开启时可用，并带隐藏 debug/dev 标识。

目标：失败、超时、Convex 不可用时，不再返回看起来像真实业务数据的 fixture / fallback。

建议修改范围：

- `rust/crates/mnote-web/src/routes/search.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- `rust/crates/mnote-web/src/routes/snapshot_support.rs`

执行项：

- [x] 优先移除或强门禁 Search 的 `fallback_search_dataset`。
- [x] 对 workspace shell projection 加载失败的最小合成数据做决策：返回明确 degraded/empty/error，不能伪装成真实 workspace projection。
- [x] 对 `allow_dev_fixtures` 相关 sidebar/filetree fallback 增加明确 debug/dev 标识，并确认 3000 默认启动链不会误开。
- [x] 增加负向 smoke：Convex/query 不可用时，Search、Sidebar、File Tree 不返回固定假业务数据。

验收标准：

- [x] 关闭或破坏 Convex query 后，Search 不返回内置固定结果。
- [x] 关闭或破坏 projection 加载后，workspace/sidebar/filetree 响应带明确错误、空状态或 degraded 标记。
- [x] 3000 默认入口不会出现 fixture workspace、fixture document、fixture filetree row。
- [x] 相关负向测试或 smoke 在失败场景下能稳定断言“不返回假数据”。

证据：

- `rust/crates/mnote-web/src/routes/search.rs`：Convex/search query 失败时，默认返回空结果与 `meta.degraded=true`；仅 `allow_dev_fixtures` 开启时使用 `fallback_search_dataset`。
- `rust/crates/mnote-web/src/routes/web_shell.rs`：workspace shell projection 失败时，默认返回 degraded empty dataset；Sidebar/File Tree dev fallback HTML 增加 `data-mnote-dev-fixture` 标识。
- `rust/crates/mnote-web/src/workspace_shell.rs`：workspace shell projection/render 支持 `degraded` / `devFixture` 标识。
- 已通过：`cargo test -p mnote-web search_ -- --nocapture`。
- 已通过：`cargo test -p mnote-web sidebar_and_filetree_do_not_return_dev_fixtures_by_default -- --nocapture`。
- 已通过：`cargo test -p mnote-web workspace_shell_sidebar_html_marks_degraded_and_dev_fixture_states -- --nocapture`。
- 已通过：`cargo test -p mnote-web document_shell_returns_page_aggregate_snapshot -- --nocapture`。

## 3. P1：补齐 OnlyOffice Rust callback / forcesave 写回链

状态：已完成代码侧与真实 Rust 3000 smoke。Rust `/api/onlyoffice/callback` 与 `/api/onlyoffice/forcesave` 已改为显式代理 legacy Next 写回链；未配置 legacy Next 时返回明确失败/unsupported，不再静默 no-op 成功；`task174` 已验证 OnlyOffice 附件打开链路与 docKey。

目标：Rust Web 主入口下 OnlyOffice 保存不能出现“返回成功但未写回”的情况。

建议修改范围：

- `rust/crates/mnote-web/src/routes/onlyoffice.rs`
- `rust/crates/adapter-onlyoffice/`
- `wolai-frontend/src/app/api/onlyoffice/callback/route.ts` 作为 legacy 写回参考
- `scripts/task174-rust-onlyoffice-attachment-open-smoke.js`

执行项：

- [x] 决定 Rust route 直接写回，还是显式代理到 legacy Next 写回链。
- [x] 如果直接写回，接入 media asset / storage 写回与 callback 状态处理。（本轮选择显式代理 legacy Next 写回链，直接写回不适用。）
- [x] forcesave 不再返回纯 `mnote-web-rust-noop` 成功语义；若仍未支持，必须返回明确 unsupported/degraded。
- [x] 补真实浏览器 smoke：打开 OnlyOffice 附件、触发保存、重新打开后内容或版本可验证。

验收标准：

- [x] `/api/onlyoffice/callback` 在需要写回的 status 下会产生真实持久化写入或明确失败。
- [x] `/api/onlyoffice/forcesave` 不再静默 no-op 成功。
- [x] `node scripts/task174-rust-onlyoffice-attachment-open-smoke.js` 或等价 smoke 通过。
- [x] 3000 主链不会 shadow 掉 legacy Next 的真实写回能力。

证据：

- `rust/crates/mnote-web/src/routes/onlyoffice.rs`：callback / forcesave 代理到 `legacy_next_base_url` 的 Next `/api/onlyoffice/*` 写回链；缺少 legacy 写回链时返回明确失败。
- 已通过：`cargo test -p mnote-web onlyoffice_ -- --nocapture`。
- 已通过：`node scripts/task174-rust-onlyoffice-attachment-open-smoke.js`，输出 `ok=true`，创建临时 docx attachment，打开 `/onlyoffice` 编辑 URL，并返回稳定 `docKey`。

## 4. P1：收口 Page Aggregate provenance

状态：已完成代码侧与文档侧收口。`/api/page-aggregate/:id` 继续保持 Rust `mnote.page_aggregate.v1` 主读链，但当底层由 `documents:getMeta + documents:getContent` substrate 构建时，response `source` 与 owner header 已明确标为 `CompatMetaContentJoin` / `compat-join`，不再误写成完整 `KernelProjection`。

目标：Page Aggregate 读链保持 Rust-first，但来源标识必须反映真实构建路径，避免把 `meta/content join` 写成完整 kernel-native projection。

建议修改范围：

- `rust/crates/core-protocol/src/page_aggregate.rs`
- `rust/crates/bridge-runtime/src/lib.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
- `wolai-frontend/src/lib/documents/page-aggregate-loader.ts`
- `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`

执行项：

- [x] 明确 `PageAggregateSource::KernelProjection` 与 `CompatMetaContentJoin` 的边界。
- [x] 如果 route 仍由 `getMeta + getContent` 构建，返回 source 或 response header 应反映真实来源。
- [x] 文档更新为“Rust route 主读链已成立，底层仍是 Rust runtime adapter 消费 meta/content substrate”。
- [x] 保留或删除 `PageAggregateSource::Fixture` 前，先确认测试和本地 fixture 依赖。（本轮保留，仅用于测试或显式 dev fixture 语义。）

验收标准：

- [x] `/api/page-aggregate/:id` 返回的 source/provenance 与实际构建路径一致。
- [x] 文档中不再出现“Page Aggregate 已完全 kernel-native projection 闭环”的过满表述。
- [x] 前端 loader 仍只消费 `mnote.page_aggregate.v1`，不恢复 TS runtime builder 主链。
- [x] Page Aggregate provenance 变更有 Rust 单测或 loader 单测覆盖。

证据：

- `rust/crates/bridge-runtime/src/lib.rs`：`page.aggregate.get` 根据 query data 是否包含 `meta + content` 选择 `CompatMetaContentJoin`，否则保留 `KernelProjection`。
- `rust/crates/mnote-web/src/routes/web_shell.rs`：`/api/page-aggregate/:id` 测试断言 `x-mnote-page-aggregate-owner=compat-join` 且 `result.source=CompatMetaContentJoin`。
- `design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`：补充 Rust route 主读链与完整 kernel-native projection 的边界。
- `design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`：补充 `CompatMetaContentJoin` / `KernelProjection` 验收口径。
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`：新增 `source` / provenance 契约说明。
- 已通过：`cargo test -p bridge-runtime page_aggregate_get_query_executes_into_core_projection -- --nocapture`。
- 已通过：`cargo test -p mnote-web page_aggregate_endpoint_returns_snapshot_contract -- --nocapture`。

## 5. P1：优化 tree stream 的 Convex 查询模型

状态：已完成代码侧与基础 Rust 3000 smoke。`bridgeLogs:listWorkspaceOverview` 已从全量 workspace collect 后内存分页，改为基于 `workspace_id + created_at + id` 及常用过滤组合索引的有界窗口查询；Rust `/api/tree/events` 的 snapshot/delta/resync 协议未改，polling-backed SSE 仍按“最新窗口 + Rust cursor 比较”工作，当前定位为过渡实现。`task123` 和标题更新目标脚本已证明 Rust 3000 stream 可输出 snapshot / title event；完整浏览器 tree integration smoke 仍因 sidebar row 可见性失败，归入第 6 项继续处理。

目标：让 `/api/tree/events` 可以长期承载主链，避免 SSE 轮询叠加全量日志扫描。

建议修改范围：

- `wolai-frontend/convex/bridgeLogs.ts`
- `wolai-frontend/convex/schema.ts`
- `rust/crates/mnote-web/src/routes/sse.rs`
- `rust/crates/mnote-web/src/routes/stream_support.rs`
- `wolai-frontend/src/lib/tree-stream/`

执行项：

- [x] 为 `command_logs` 增加按 `workspace_id + created_at/id` 的查询路径。
- [x] 为 `domain_events` 增加按 `workspace_id + created_at/id` 或 command cursor 的查询路径。
- [x] `listWorkspaceOverview` 不再每轮 collect 全量 workspace 日志后内存分页。
- [x] 明确 polling-backed SSE 是过渡实现还是长期实现；若长期使用，补连接数、日志量、延迟边界测试。（本轮明确为过渡实现，live 轮询仍查最新窗口并由 Rust 做 cursor 比较。）

验收标准：

- [x] 大量历史日志存在时，overview 查询仍按 cursor / limit 返回，不全量 collect。
- [x] `/api/tree/events` 的 snapshot/delta/resync 协议不破坏现有前端消费。
- [x] `pnpm --dir wolai-frontend test -- tree-stream` 或等价 tree-stream 测试通过。
- [x] 至少一条 smoke 覆盖标题/树变更后 SSE 能正常输出增量或保守 resync。

证据：

- `wolai-frontend/convex/schema.ts`：`command_logs` 增加 `by_workspace_created_at`、`by_workspace_status_created_at`、`by_workspace_target_page_created_at`、`by_workspace_target_block_created_at`；`domain_events` 增加 `by_workspace_created_at`、`by_workspace_status_created_at`、`by_workspace_aggregate_created_at`。
- `wolai-frontend/convex/bridgeLogs.ts`：`listWorkspaceOverview` 改为 `fetchCommandLogWindow` / `fetchDomainEventWindow`，通过索引 `order("desc").take(scanLimit)` 和 cursor 上界查询，移除对 workspace 全量日志的 `.collect()`。
- `rust/crates/mnote-web/src/routes/sse.rs`：live poll 语义保持不变，仍清空 bridge pagination cursor 后查最新窗口，再由 Rust 比较当前 stream cursor。
- 已通过：`pnpm test -- src/lib/tree-stream`（Vitest 实际执行 111 个测试文件、452 个测试）。
- 已通过：`cargo test -p mnote-web stream_ -- --nocapture`。
- 已通过：`cargo test -p mnote-web tree_realtime_route_returns_rust_web_owned_snapshot_event -- --nocapture`。
- 已通过：`pnpm exec eslint convex/bridgeLogs.ts convex/schema.ts` 无错误；保留既有 `any` 风格警告。
- 已通过：`node scripts/task123-rust-web-tree-live-stream-consumer-smoke.js`。
- 已通过：Rust 3000 目标脚本验证 `/api/documents/title` 后 `/api/tree/events?maxPolls=1&pollMs=250` 输出包含新标题的 stream 事件。
- 未通过：`node scripts/task120-rust-web-tree-integration-smoke.js` 在等待 child sidebar row 可见时超时，完整浏览器 tree integration 继续归入第 6 项处理。
- 受既有问题阻塞：`pnpm exec tsc --noEmit --pretty false` 仍被 `.next` 旧生成文件、Convex documents 类型和 mindmap/OnlyOffice 等无关类型错误阻塞；本轮新增 `bridgeLogs.ts` / `schema.ts` 未出现在错误列表中。

## 6. P1：统一 Sidebar / File Tree live cache 与主文档壳 no-refresh 验收

状态：代码侧完成，真实浏览器 create / rename / move / archive / purge 主链已通过。`usePreferredSidebarSnapshot` 已从“sync key 与 initial 对比”的特殊规则，改为基于 `getSidebarDataFreshness()` 的 version 仲裁，并保留 tree stream cursor 作为 live 元信息；`AppLayoutShell`、Sidebar、Breadcrumb、SearchPalette 继续消费同一份 preferred snapshot。Rust 3000 下已修复 `tree:delta` 的 title-only patch 被误当成空 projection 渲染的问题，`task110` 已通过页头、breadcrumb、sidebar、page tree、File Tree 标题一致性检查；`task120` 已通过 child sidebar row 可见性、rename live delta、reload 与 purge 后消失检查；`task177` 已覆盖 move / archive live DOM reducer，确认页面树与 File Tree 行局部更新后文档页头仍稳定。2026-05-14 追加验收口径：File Tree / Page Tree 的“无刷新可见”必须在 `http://127.0.0.1:3000/documents/<id>` 主文档壳验证，覆盖页面 `tree.node.create`、页面 delete/purge、页面内 mindmap create 三条链路；`/tree` debug route 与 3001/Next 路径只能作为补充排查，不能替代 3000 主入口验收。

目标：减少 `initial / query / tree_stream` 三源 freshness 选择，把 Sidebar、Breadcrumb、文档页头收口到统一 live cache / projection version 仲裁。

建议修改范围：

- `wolai-frontend/src/components/app-layout-shell.tsx`
- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts`
- `wolai-frontend/src/components/sidebar/sidebar.tsx`
- `wolai-frontend/src/lib/tree-stream/`

执行项：

- [x] 定义 live snapshot 的 version/cursor 仲裁字段。
- [x] `usePreferredSidebarSnapshot` 从 sync key 比较推进到 cursor/version 比较。
- [x] Sidebar、Breadcrumb、文档页头统一消费同一 preferred/live snapshot。
- [x] 保留 initial snapshot 只作为首屏启动输入，不能长期压过更新后的 stream/query。
- [x] Rust 3000 主文档壳监听 `tree:local-command`，在 `tree.node.create` / delete 成功后即时更新 Page Tree 与 File Tree DOM。
- [x] Rust 3000 主文档壳监听同页 mindmap 资产写入：slash 插入触发 `/api/mindmap/<doc>/<mindmap>` POST 成功后，File Tree 即时出现 `mindmap-<id>.json` asset row。

验收标准：

- [x] 新建、重命名、移动、归档页面后，Sidebar、Breadcrumb、文档页头无互相打架或回退闪烁。
- [x] `usePreferredSidebarSnapshot` 有单测覆盖 query、initial、tree_stream 三者的新旧仲裁。
- [x] 真实浏览器 smoke 覆盖至少一个 tree command 后 Sidebar 与文档页头一致。
- [x] 真实浏览器 smoke 必须覆盖 `3000 /documents/<id>` 主文档壳中页面新建后 Page Tree / File Tree 立即出现，且 `navigationEvents.length=0`。
- [x] 真实浏览器 smoke 必须覆盖同一主文档壳中页面删除或 purge 后 Page Tree / File Tree 立即移除，且不依赖浏览器刷新。
- [x] 真实浏览器 smoke 必须覆盖同一主文档壳中页面内新建 mindmap 后 File Tree 立即出现对应 asset row；该链路是 `/api/mindmap/<doc>/<mindmap>` 资产写入，不等同于页面 `tree.node.create`。

证据：

- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts`：输出 `version` 与 `cursor`，以 `getSidebarDataFreshness()` 比较 initial/query/tree_stream；stream fallback 时不参与仲裁，同版本时优先 live stream。
- `wolai-frontend/src/components/app-layout-shell.tsx`：向 preferred snapshot 仲裁传入 `treeStream.cursor`，Sidebar、Breadcrumb、SearchPalette 仍消费同一 preferred data。
- `wolai-frontend/src/components/sidebar/sidebar.tsx`：Sidebar 内部 fallback 路径也传入 `treeStream.cursor`。
- `wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx`：新增 query 比旧 stream 更新时优先 query、stream 与 query 同版本时优先 stream 并暴露 cursor 的回归用例。
- 已通过：`pnpm test -- src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx src/components/app-layout-shell.test.tsx src/components/sidebar/sidebar-sync.test.tsx`（Vitest 实际执行 111 个测试文件、454 个测试）。
- 已通过：`pnpm exec eslint src/components/sidebar/use-preferred-sidebar-snapshot.ts src/components/sidebar/use-preferred-sidebar-snapshot.test.tsx src/components/app-layout-shell.tsx src/components/sidebar/sidebar.tsx` 无错误；`sidebar.tsx` 保留既有 warning。
- 已通过：`cargo test -p mnote-web tree_realtime_route_returns_rust_web_owned_snapshot_event -- --nocapture`。
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`：`renderSidebarSnapshot` 只在 payload 明确带 projection items 时重渲染 Sidebar；单文档 `upsert_document` title patch 改为只调用 `updateTitleEverywhere()`，避免 rename delta 把 Sidebar 渲染成空树。
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`：新增 `move_document` / `remove_document` 的小型 DOM reducer，移动时更新页面树与 File Tree 行的父节点，归档时局部移除对应行。
- `rust/crates/mnote-web/src/transport/convex.rs`：tree create / rename / archive / restore / move 及 compat documents mutation 发送给 legacy Convex mutation 前剥离 artifact-only 字段，避免 validator 因 `streamDeltaHint` / `domainEventPlan` 返回 502。
- 已通过：`cargo test -p mnote-web sidebar_tree_runtime -- --nocapture`。
- 已通过：`cargo test -p mnote-web convex_command_args_strips -- --nocapture`。
- 已通过：`node scripts/task110-page-title-single-truth-smoke.js` 在 Rust 3000 下通过页头、breadcrumb、sidebar、page tree、File Tree 标题一致性检查。
- 已通过：`node scripts/task120-rust-web-tree-integration-smoke.js` 在 Rust 3000 下通过 root/child row、active 切换、rename live delta、reload 与 purge 检查。
- 已通过：`node scripts/task112-tree-rust-family-regression-smoke.js`，但当前页面选项壳缺少 move/embed 入口导致 picker 部分 skipped：`move_embed_entry_missing_in_current_page_options_shell`。
- 已通过：`node scripts/task177-tree-move-archive-live-smoke.js`，覆盖 `move_document` live event 后页面树 / File Tree 行 `data-parent-id` 更新，以及 `remove_document` live event 后两棵树局部移除归档行；文档页头和标题输入保持根页面标题稳定。
- 已通过：`node scripts/task179-tree-create-delete-no-reload-smoke.js`，覆盖 Rust `/tree` `convex_workspace` 下 File Tree create/delete 与 Page Tree create；该脚本只作为 debug route 补充证据，不再代表最终主链验收。
- 已通过：`MNOTE_UI_BASE_URL=http://127.0.0.1:3000 node scripts/task426-mnote-web-main-no-reload-smoke.js`，覆盖 `3000 /documents/<id>` 主文档壳：页面 create/delete 无刷新即时更新 Page Tree / File Tree，页面内 mindmap create 经 `/api/mindmap/<doc>/<mindmap>` POST 后即时插入 `asset:<mindmapId>` File Tree row，`afterMindmapCreate.localApplied=true`。

## 7. P1：明确 `pageSubtree` 与本地正文编辑关系

状态：已完成。策略已固定为：server `pageSubtree` 仍是正式 projection 来源；当本地标题草稿或正文快照与 server snapshot 不一致时，前端允许用当前本地 content 生成临时 `pageSubtree`，并通过 `pageSubtreeSource=local` 暴露给 AI context，避免继续复用旧 server subtree 或让 AI 长期失去结构上下文。React `DocumentAiAgentPanel` 侧已有 client state / runtime 单测覆盖；Rust 3000 当前文档页使用全局浮动页面 AI，本轮已补同等 local `pageSubtree` context 构造与真实浏览器 smoke。

目标：用户正常编辑正文后，AI 面板、阅读视图、结构视图不能因为本地 content 与 server snapshot 不同而长期失去结构上下文。

建议修改范围：

- `wolai-frontend/src/components/editor/page-aggregate-client-state.ts`
- `wolai-frontend/src/components/editor/document-content.tsx`
- `wolai-frontend/src/lib/documents/page-subtree.ts`
- `rust/crates/core-protocol/src/page_aggregate.rs`

执行项：

- [x] 决定 `pageSubtree` 是 server-only projection，还是允许本地临时 projection。
- [x] 标记本地临时 projection 与 server projection 的来源差异，避免把本地临时结构误认为 kernel projection。
- [x] AI 面板读取最新 page aggregate snapshot 时，能拿到本地编辑后的结构上下文。
- [x] 真实编辑器 smoke 验证本地正文编辑后阅读态结构 / AI context 不丢失。

验收标准：

- [x] 本地正文或标题草稿变化后，不再继续复用旧 server `pageSubtree`。
- [x] 本地正文或标题草稿变化后，可生成临时 `pageSubtree` 供阅读态 / AI context 使用。
- [x] AI context 明确带 `pageSubtreeSource`，可区分 `server` / `local` / `none`。
- [x] 浏览器实测：编辑正文标题或 heading 后，结构面板 / AI context 能看到本地最新结构。

证据：

- `wolai-frontend/src/components/editor/page-aggregate-client-state.ts`：新增 `PageAggregateClientPageSubtreeSource`，server 快照未变时返回 `source=server`；本地 content/title 草稿变化时用 `buildPageSubtreeProjection` 生成 `source=local`。
- `wolai-frontend/src/components/editor/DocumentAiAgentPanel.tsx`：`PageAggregateAiSnapshot` 增加 `pageSubtreeSource`。
- `wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsx`：AI request context 增加 `pageSubtreeSource`。
- `wolai-frontend/src/components/editor/page-aggregate-client-state.test.ts`：覆盖本地正文/标题变化后生成临时 `pageSubtree`，以及 server snapshot 下 `pageSubtreeSource=server`。
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`：历史 Rust 3000 全局浮动页面 AI 曾在发送旧 `/api/ai-agent/run` 前读取当前 Leptos/Tiptap 编辑器 DOM；当本地内容与 server aggregate 正文不同，生成 `source=local` 的临时 subtree / outline / evidence，并在 context 中发送 `pageSubtreeSource=local`。2026-05-14 起这只保留为历史上下文证据；当前页面 AI 主链已改为 Hermes client proxy，页面上下文进入 Hermes run/session context，最新页面事实由 Hermes 通过 mnote plugin tool 回读。
- 已通过：`pnpm test -- src/components/editor/page-aggregate-client-state.test.ts src/components/editor/DocumentAiAgentPanel.runtime.test.tsx src/components/editor/document-content.test.ts`（Vitest 实际执行 111 个测试文件、454 个测试）。
- 已通过：`node scripts/task178-page-ai-local-subtree-context-smoke.js`，Rust 3000 下打开真实文档页，把编辑器内容改成本地 heading 后打开页面 AI，并拦截旧 `/api/ai-agent/run` 请求确认 `context.pageSubtreeSource=local`、`documentBlocks` / `outline` / `subtree.stats.headingCount` 均包含本地最新 heading。该 smoke 已在 2026-05-14 默认退役为 historical smoke，脚本现本机归档于 gitignored `recycle/scripts/retired-ai-agent-run-smokes/`；当前页面 AI 长期验收以 `scripts/task-hermes-page-ai-*.js` 和 `scripts/task-page-block-ai-tools-smoke.js` 矩阵为准。
- 已通过：`pnpm exec eslint src/components/editor/page-aggregate-client-state.ts src/components/editor/page-aggregate-client-state.test.ts src/components/editor/DocumentAiAgentPanel.tsx src/components/editor/DocumentAiAgentPanel.runtime.tsx` 无错误；`DocumentAiAgentPanel.runtime.tsx` 保留既有 `any` warning。

## 8. P2：清理或标注 stale mapping / legacy compat

状态：已完成。`/api/documents/page` 与 `/api/mnote-web/stream` 已经明确退场为 410 compat 边界；`storage-convex-bridge` 不再把 `page.aggregate.get` 映射到不存在的 Convex `documents:getPageAggregate`，而是显式落到 `queries:unknown` 边界。Rust `/api/page-aggregate/:id` 当前先读取 `documents.meta.get` / `documents.content.get`，再交给 `bridge-runtime` 的 `page.aggregate.get` 组装 core projection，不走缺失 Convex function。

目标：保留的 compat 都必须是显式迁移/调试边界；不再让后续实现误把 stale mapping 当主链。

建议修改范围：

- `rust/crates/storage-convex-bridge/src/mapping.rs`
- `rust/crates/mnote-web/src/app.rs`
- `rust/crates/mnote-web/src/routes/gateway.rs`
- `rust/crates/mnote-web/src/routes/documents.rs`
- `wolai-frontend/src/lib/documents/page-aggregate-builder.ts`
- `wolai-frontend/src/app/api/documents/page/route.ts`
- `wolai-frontend/src/app/api/mnote-web/stream/route.ts`

执行项：

- [x] 处理 `page.aggregate.get -> documents:getPageAggregate`：实现、移除或标注为 future/stale。
- [x] legacy Next proxy 保留时，命名与注释统一为 explicit migration/debug boundary。
- [x] TS `page-aggregate-builder` 保留为测试/adapter/reference，不得重新接回 runtime 主链。
- [x] 410 compat route 保持退场语义，并补负向测试防止重新启用。

验收标准：

- [x] 全仓没有生产路径调用不存在的 `documents:getPageAggregate`。
- [x] 默认 3000 主链不走 legacy Next proxy。
- [x] `/api/documents/page` 与 `/api/mnote-web/stream` 继续保持明确退场响应。
- [x] 文档和代码注释不会把 compat/fallback 描述成当前主路径。

证据：

- `rust/crates/storage-convex-bridge/src/mapping.rs`：`page.aggregate.get` 显式标注为 stale/future compat placeholder。
- `wolai-frontend/src/app/api/documents/page/route.ts`：返回 410，提示直接使用 `/api/page-aggregate/:documentId`。
- `wolai-frontend/src/app/api/mnote-web/stream/route.ts`：返回 410，提示直接使用 `/api/tree/events`。
- `design/10-review/done/03-convex-realtime-storage-review.md`：已明确指出 `documents:getPageAggregate` 目前缺少 Convex 实现，属于方向滞后或 future/stale 口径。
- `rust/crates/storage-convex-bridge/src/mapping.rs`：`page.aggregate.get` 改为 `queries:unknown`，并注释说明 page aggregate 主读链由 Rust route + `bridge-runtime` 组装，避免误触发不存在的 `documents:getPageAggregate`。
- 已通过：`rg -n "documents:getPageAggregate|getPageAggregate" rust wolai-frontend src -g '!node_modules' -g '!target'`，仅剩 mapping 注释说明，无生产调用。
- 已通过：`cargo test -p storage-convex-bridge -- --nocapture`。
- 已通过：`cargo test -p bridge-runtime page_aggregate_get_query_executes_into_core_projection -- --nocapture`。

## 9. P2：处理 Mindmap 次级未闭合项

状态：已完成。Mindmap `export` 不再映射到未受控的 `EXPORT` runtime command，UI state 继续禁用该入口；旧 `/api/mindmap-ai/expand-node` Next route 已显式退役为 410，并指向 AI Agent 内置 `mindmap_expand_node` 工具；legacy MindmapSidebar 中调用旧 route 的补完入口已从渲染层关闭。mindmap 内容保存主链仍保持 `mindmap.command.apply`，没有回退到 Page Aggregate body 保存链；真实 3000 `task169` 已通过，证明 mindmap 与 `index.md` 隔离未回归。

目标：`05-tree.md` 的 mindmap 污染 `index.md` 已完成；剩余处理能力入口与实现不一致的问题。

建议修改范围：

- `wolai-frontend/src/lib/mindmap/mindmap-action-map.ts`
- `wolai-frontend/src/lib/mindmap/simple-mind-map-bridge.ts`
- `wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts`
- `rust/crates/core-protocol/src/tool.rs`
- `rust/crates/bridge-runtime/src/lib.rs`

执行项：

- [x] 处理 Mindmap `EXPORT`：加入安全命令并补 smoke，或在 UI state 中禁用并标注延期。
- [x] 处理 Mindmap AI expand route：按 Hermes plugin / Rust bridge 重接，或显式退役该 route。
- [x] 保持 mindmap 内容保存继续走 `mindmap.command.apply`，不得回退到 Page Aggregate body 保存链。

验收标准：

- [x] UI 不展示无法执行的 Mindmap export 能力，或 export 能真实执行并有测试覆盖。
- [x] `/api/mindmap-ai/expand-node` 不再表现为“产品入口存在但稳定 501”而无说明。
- [x] `node scripts/task169-mindmap-realtime-smoke.js` 继续通过，证明 mindmap 与 `index.md` 隔离未回归。

证据：

- `wolai-frontend/src/lib/mindmap/mindmap-action-map.ts`：`export` 改为 `localView`，不再下发 `runtimeCommand: "EXPORT"`。
- `wolai-frontend/src/lib/mindmap/mindmap-ui-state.ts`：`unsupportedActionIds` 继续包含 `export`，UI state 中该入口保持禁用。
- `wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts`：旧 Next route 返回 410，`x-mnote-compat-boundary=mindmap-expand-node-route-retired`。该退役说明不再指向旧 `/api/ai-agent/run` 作为长期承接面；后续若恢复 Mindmap AI，应按 Mindmap domain 另拆 Hermes plugin / Rust bridge 迁移。
- `wolai-frontend/src/components/editor/blocks/MindmapSidebar.tsx`：legacy 侧栏中调用 `/api/mindmap-ai/expand-node` 的补完入口通过 `MINDMAP_LEGACY_EXPAND_NODE_ENTRY_ENABLED=false` 从渲染层关闭。
- 已通过：`pnpm test -- src/lib/mindmap/mindmap-action-map.test.ts src/lib/mindmap/mindmap-ui-state.test.ts src/lib/mindmap/simple-mind-map-bridge.test.ts src/app/api/mindmap-ai/expand-node/route.test.ts`（Vitest 实际执行 112 个测试文件、457 个测试）。
- 已通过：`pnpm exec eslint src/lib/mindmap/mindmap-action-map.ts src/lib/mindmap/mindmap-action-map.test.ts src/lib/mindmap/mindmap-ui-state.test.ts src/app/api/mindmap-ai/expand-node/route.ts src/app/api/mindmap-ai/expand-node/route.test.ts src/components/editor/blocks/MindmapSidebar.tsx` 无错误；保留 `MindmapSidebar.tsx` 既有 `AiPanel` 未使用 warning。
- 已通过：`cargo test -p bridge-runtime mindmap_command_apply_plan_uses_kernel_command_facade -- --nocapture`，确认 mindmap 内容保存仍走 `mindmap.command.apply` / `mindmaps:applyCommand` facade。
- 已通过：`node scripts/task169-mindmap-realtime-smoke.js`（Rust 3000 已启动，脚本退出码 0）。最终结果 `ok=true`、`failures=[]`，同一页面最终 File Tree mindmap row 数量为 1，`mindmapId=mindmap_1778695090474`，object identity 仍为同一 `objectKind:"mindmap"`。
- 已通过：`cargo test -p bridge-runtime mindmap -- --nocapture`，17 个 mindmap 相关测试通过；普通 `mindmap.command.apply` 改为 `mindmap.content.updated + noop`，`mindmaps.put createOnly=false` 也不再默认 tree resync。
- 已通过：`cargo test -p mnote-web mindmap -- --nocapture`，6 个 mindmap 相关测试通过，覆盖 Rust `/api/mindmap/:docId/:mindmapId` 的 object artifact 响应。
- 已通过：`node scripts/task169-mindmap-realtime-smoke.js` 最新结果 `ok=true`、`failures=[]`；6 条可解析普通 `mindmap.command.apply` 响应均为 `eventType=mindmap.content.updated`、`streamOp=noop`，另有 1 条 Playwright response body 读取失败被记录为 `skippedReadFailures=1`；同一页面最终仍只有 1 条 mindmap asset row，`mindmapId=mindmap_1778698542703`。

## 10. P3：文档治理与回填

状态：已完成文档侧收口。`10-review/README.md` 已指向本执行清单；`05-tree.md` 已标明 Resource Tree / ObjectIdentity / mindmap 与 `index.md` 隔离主线由 `4-24` / `5-12` 承接完成；`5-5-1` 已修正 `showHeadingNumbers` / `embedDefaultBlockId` 的 runtime payload 口径；`90-reference` 的对话式尾巴已清理，`design/README.md` 已补充参考目录不能覆盖主线设计的引用边界。

目标：让设计目录继续反映真实主线，不把参考资料或过时清单误当成当前架构。

建议修改范围：

- `design/10-review/README.md`
- `design/10-review/done/05-tree.md`
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`
- `design/90-reference/`

执行项：

- [x] 在 `05-tree.md` 中补充最终采用的 object editor / object tab 方案摘要，并说明核心执行已由 `4-24` / `5-12` 完成。
- [x] 修正 `5-5-1` 中 `showHeadingNumbers` / `embedDefaultBlockId` 已接入 runtime payload 的滞后描述。
- [x] 清理 `design/90-reference` 问答式残留，保留为中性参考笔记。
- [x] 在引用规范中再次强调 `90-reference` 不参与 process/done 状态判断，不是上位架构来源。

验收标准：

- [x] `design/10-review/README.md` 能指向本执行清单。
- [x] `05-tree.md` 不再让后续 worker 误以为 Resource Tree / ObjectIdentity / mindmap 止血仍未执行。
- [x] `design/90-reference` 的内容不再保留“需要我给你...”这类对话尾巴。
- [x] 主线文档对 Page Aggregate、tree realtime、ObjectIdentity 的描述与当前代码事实一致。

证据：

- `design/10-review/README.md`：补充执行入口，指向 `done/06-execution-checklist-and-acceptance.md`，并说明 `done/05-tree.md` 核心主线已由 `4-24` / `5-12` 承接。
- `design/10-review/done/05-tree.md`：开头增加执行状态，明确本文不再作为待办清单直接执行。
- `design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`：`showHeadingNumbers` / `embedDefaultBlockId` 改为“runtime payload 已贯通，但深语义未完成”的口径。
- `design/90-reference/90-1-filetree.md`、`design/90-reference/90-2-yemianshu.md`：删除“需要我给你...”对话尾巴。
- `design/README.md`：补充 `90-reference/` 只能作为生态资料或背景材料，不能覆盖主线设计稿。
- `design/03-rust-web/done/3-15-runtime-fallback-retirement-checklist-v1.md`：从 `process/` 迁入 `done/`，文件自身 checkbox 已全部完成，且与第 2 / 第 8 项 fallback、compat 退场证据一致。
- `design/06-mindmap/done/6-mindmap-phase6-kmind-parity-detail-checklist-v1.md`：从 `process/` 迁入 `done/`，文件自身 checkbox 已全部完成，并已有 `task167-mindmap-kmind-parity-smoke.js` 等 Phase 6 KMind parity 证据。
- 已验证：`rg -n "需要我给你|我可以|你要不要|是否需要|请告诉我|如果你愿意|要我|我来" design/90-reference` 无匹配。
- 已完成只读状态审计：`design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`、`design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`、`design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md`、`design/06-mindmap/reference/6-mindmap-kernel-phase6-projection-editor-v1.md`、`design/06-mindmap/done/6-mindmap-phase6-leptos-ui-shell-reuse-checklist-v1.md` 仍有未完成项或长期主线尾项，继续保留在 `process/`；`design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`、`design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`、`design/07-ai/done/7-5-hermes-client-proxy-contract-v1.md`、`design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md` 已迁入 `done/`，后续活跃 AI 体验面由 `design/07-ai/process/7-7-page-ai-mini-hermes-control-surface-v1.md` 承接；`design/old/**` 不纳入活跃 process/done 迁移判断。

## 11. 全局 Done Gate

全部清单完成前，不应把 10-review 状态描述为“单一真源已完全闭环”。可以描述为：

> Rust 已经持有主语义主导权，Page Aggregate、tree command、tree realtime、Resource/ObjectIdentity 主线已经切入；剩余工作集中在 artifact 一致性、fallback 退场、realtime 查询模型、provenance 口径和 compat 边界收口。

全部完成的最低验收：

- [x] P0 两项全部完成，且有自动化测试或真实 smoke 证据。
- [x] `05-tree.md` 已执行事项通过 `task169` 或等价 smoke 防回归。
- [x] `/api/page-aggregate/:id` provenance 与真实来源一致。
- [x] `/api/tree/events` 不依赖全量日志扫描作为长期查询模型。
- [x] 3000 默认主链不返回假业务数据、不走 legacy proxy、不静默 no-op 保存。
- [x] 设计文档中的 `process/`、`done/` 状态与真实代码和验收证据一致。

## 12. 关联缺陷验收状态

本执行清单本身已完成；与本轮用户反馈直接相关的两个 bug 文档状态如下：

- `bugs/04-tree-domain/done/4-25-tree-command-create-delete-reload-latency-v1.md`：Rust `/tree` debug shell 的 create / delete 强刷新子项已关闭，`task179` 已通过无 reload 与耗时验收；React Sidebar 直接 create/delete 已改为本地更新，不再同步等待 `refreshTree()`；Rust-family React shell 的 create / rename / move host mutation 回调也已改为本地 apply，不再默认整树 refetch。文档已迁入 `done/`。
- `bugs/05-editor-mainline/done/5-11-mindmap-ghost-assets-and-tree-command-latency-v1.md`：ghost mindmap asset 增长的 NodeView document id 误归属链路已止血，`task169` 已通过同一页面单 mindmap row 与 object identity 稳定验收；普通 mindmap 节点编辑 artifact 语义已收窄为 `mindmap.content.updated + noop`；已新增 `task180` 只读候选审计脚本用于清理前取证，并支持 `/api/tree/projections/file` 的只读投影输入以审计 File Tree duplicate row。真实 File Tree projection 审计已确认当前 workspace 下 `fileTreeMindmapRows=10`、`duplicateFileTreeMindmapRows=0`、`candidateCount=0`。历史清理候选为 0，因此本轮不执行数据删除；文档已迁入 `done/`。
