Files
mnote/design/old/10-review/done/06-execution-checklist-and-acceptance.md
Agent Board b798f628ee chore: land tree view-state, vault, Pi module split, and repo hygiene
Persist PageTree expand state via control-plane view-state and align
chevron/DOM with restored expansion; keep Sidex-style shallow page-tree
scan and drop the unused recursive scanner that only added cargo noise.

Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi
into a module package, and retire Hermes/ACP/OpenHub recycle + root
harness evidence from the index while gitignoring recycle and local
diag dumps.

Archive superseded design/bugs docs under old/, point architecture at
ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor
regressions so the working tree can stay clean.
2026-07-21 05:13:05 +08:00

41 KiB
Raw Permalink Blame History

[recycle] 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-245-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 artifactRust 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 查询侧验证

执行项:

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

验收标准:

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

证据:

  • rust/crates/mnote-web/src/routes/documents.rstitle / options 使用 execute_runtime_command_via_convex_with_artifacts,响应 meta.artifacts / meta.artifactError
  • rust/crates/bridge-runtime/src/lib.rspage.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.updateTitleevent_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.tsVitest 实际执行 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 emptySidebar/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

执行项:

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

验收标准:

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

证据:

  • rust/crates/mnote-web/src/routes/search.rsConvex/search query 失败时,默认返回空结果与 meta.degraded=true;仅 allow_dev_fixtures 开启时使用 fallback_search_dataset
  • rust/crates/mnote-web/src/routes/web_shell.rsworkspace shell projection 失败时,默认返回 degraded empty datasetSidebar/File Tree dev fallback HTML 增加 data-mnote-dev-fixture 标识。
  • rust/crates/mnote-web/src/workspace_shell.rsworkspace 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

执行项:

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

验收标准:

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

证据:

  • rust/crates/mnote-web/src/routes/onlyoffice.rscallback / 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

执行项:

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

验收标准:

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

证据:

  • rust/crates/bridge-runtime/src/lib.rspage.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-joinresult.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/

执行项:

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

验收标准:

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

证据:

  • wolai-frontend/convex/schema.tscommand_logs 增加 by_workspace_created_atby_workspace_status_created_atby_workspace_target_page_created_atby_workspace_target_block_created_atdomain_events 增加 by_workspace_created_atby_workspace_status_created_atby_workspace_aggregate_created_at
  • wolai-frontend/convex/bridgeLogs.tslistWorkspaceOverview 改为 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-streamVitest 实际执行 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/

执行项:

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

验收标准:

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

证据:

  • wolai-frontend/src/components/sidebar/use-preferred-sidebar-snapshot.ts:输出 versioncursor,以 getSidebarDataFreshness() 比较 initial/query/tree_streamstream fallback 时不参与仲裁,同版本时优先 live stream。
  • wolai-frontend/src/components/app-layout-shell.tsx:向 preferred snapshot 仲裁传入 treeStream.cursorSidebar、Breadcrumb、SearchPalette 仍消费同一 preferred data。
  • wolai-frontend/src/components/sidebar/sidebar.tsxSidebar 内部 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.tsxVitest 实际执行 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.rsrenderSidebarSnapshot 只在 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.rstree 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 部分 skippedmove_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 rowafterMindmapCreate.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

执行项:

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

验收标准:

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

证据:

  • wolai-frontend/src/components/editor/page-aggregate-client-state.ts:新增 PageAggregateClientPageSubtreeSourceserver 快照未变时返回 source=server;本地 content/title 草稿变化时用 buildPageSubtreeProjection 生成 source=local
  • wolai-frontend/src/components/editor/DocumentAiAgentPanel.tsxPageAggregateAiSnapshot 增加 pageSubtreeSource
  • wolai-frontend/src/components/editor/DocumentAiAgentPanel.runtime.tsxAI 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.tsVitest 实际执行 111 个测试文件、454 个测试)。
  • 已通过:node scripts/task178-page-ai-local-subtree-context-smoke.js,Rust 3000 下打开真实文档页,把编辑器内容改成本地 heading 后打开页面 AI,并拦截旧 /api/ai-agent/run 请求确认 context.pageSubtreeSource=localdocumentBlocks / 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-*.jsscripts/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-runtimepage.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

执行项:

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

验收标准:

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

证据:

  • rust/crates/storage-convex-bridge/src/mapping.rspage.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.rspage.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 commandUI 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

执行项:

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

验收标准:

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

证据:

  • wolai-frontend/src/lib/mindmap/mindmap-action-map.tsexport 改为 localView,不再下发 runtimeCommand: "EXPORT"
  • wolai-frontend/src/lib/mindmap/mindmap-ui-state.tsunsupportedActionIds 继续包含 exportUI state 中该入口保持禁用。
  • wolai-frontend/src/app/api/mindmap-ai/expand-node/route.ts:旧 Next route 返回 410x-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.tsxlegacy 侧栏中调用 /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.tsVitest 实际执行 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=truefailures=[],同一页面最终 File Tree mindmap row 数量为 1mindmapId=mindmap_1778695090474object identity 仍为同一 objectKind:"mindmap"
  • 已通过:cargo test -p bridge-runtime mindmap -- --nocapture17 个 mindmap 相关测试通过;普通 mindmap.command.apply 改为 mindmap.content.updated + noopmindmaps.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=truefailures=[]6 条可解析普通 mindmap.command.apply 响应均为 eventType=mindmap.content.updatedstreamOp=noop,另有 1 条 Playwright response body 读取失败被记录为 skippedReadFailures=1;同一页面最终仍只有 1 条 mindmap asset rowmindmapId=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/

执行项:

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

验收标准:

  • design/10-review/README.md 能指向本执行清单。
  • 05-tree.md 不再让后续 worker 误以为 Resource Tree / ObjectIdentity / mindmap 止血仍未执行。
  • design/90-reference 的内容不再保留“需要我给你...”这类对话尾巴。
  • 主线文档对 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.mdshowHeadingNumbers / embedDefaultBlockId 改为“runtime payload 已贯通,但深语义未完成”的口径。
  • design/90-reference/90-1-filetree.mddesign/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.mddesign/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.mddesign/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.mddesign/06-mindmap/reference/6-mindmap-kernel-phase6-projection-editor-v1.mddesign/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.mddesign/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.mddesign/07-ai/done/7-5-hermes-client-proxy-contract-v1.mddesign/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 边界收口。

全部完成的最低验收:

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

12. 关联缺陷验收状态

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

  • bugs/04-tree-domain/done/4-25-tree-command-create-delete-reload-latency-v1.mdRust /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.mdghost 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=10duplicateFileTreeMindmapRows=0candidateCount=0。历史清理候选为 0,因此本轮不执行数据删除;文档已迁入 done/