审计真实 documents.content/documents.save/Convex content contract

把 LeptosTiptapEditorHost 从 query-string 初始化改成 bootstrap postMessage

让 8123 runtime 支持 bootstrap/reset 并避免跨文档复用错误状态

补 iframe 高度同步

建立 stored content 到 tiptap doc 的正式 adapter

建立 tiptap doc 到 stored content 的正式 adapter

接通真实保存与 persisted meta 回写

固定 /documents/[id] 默认直接编辑态

固定 EditorHost 主路径为 tiptap 并把 BlockNote 退到 compat

补验收脚本并完成主编辑器人工验收准备

补验收脚本并完成主编辑器人工验收准备


node_modules/
**/node_modules/
.next/
**/.next/
dist/
**/dist/
build/
**/build/
.turbo/
pnpm-debug.log*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
.DS_Store
.env
.env.*
env-archive/
.vscode/
*.local
tmp/
temp/
**/.venv/
.venv/
**/__pycache__/
__pycache__/
# Convex 本机开发临时目录
**/.convex-tmp/
# 大模型文件，防止超出 LFS 配额
services/mineru/models/
*.safetensors
# ONLYOFFICE 挂载/同步目录（体积很大，禁止入库）
/onlyoffice-web-apps/
/onlyoffice-plugins/
/onlyoffice-data/
/onlyoffice-fonts/
/onlyoffice-pg-data/
# ONLYOFFICE 现已移至 src/components/onlyoffice 下，整目录忽略
/src/components/onlyoffice/
# 参考资料目录
cankao/
# services 整体忽略（包含本地模型与临时服务）
/services/
**/test/
pw-tests/**
pw-tests/
wolai-frontend/public/documents/
wolai-frontend/public/documents/**
artifacts/
artifacts/**
tmp

# Rust 本地构建与调试产物
/rust/target/
/rust/spikes/**/target/

# Harness 本地运行状态/调试产物
/.harness-active
/.harness-stop-counter
/harness-tasks.json.bak


artifacts/
artifacts/**
tmp

# Rust 本地构建与调试产物
/rust/target/
/rust/spikes/**/target/

# Harness 本地运行状态/调试产物
/.harness-active
/.harness-stop-counter
/harness-tasks.json.bak

# pnpm 本地缓存
/.pnpm-store/
.playwright-mcp
rust/spikes/leptos-tiptap-spike/trunk-8123.err
rust/spikes/leptos-tiptap-spike/trunk-8123.out
design/05-editor-mainline/reference-code

# 本地 Wolai 对标取证与截图产物
/test-results/
/wolai-*.png
/wolai-*.md

# 任务型浏览器 smoke 只作本地验收，不纳入项目运行面
/scripts/task*-wolai-*.js
/scripts/task*-rust-web-wolai-*-smoke.js

# 下载的 Wolai 静态页面参考包，不作为可维护设计稿入库
design/design/html/
recycle


- 页面 AI 编辑长期主路径为 `mnote.doc.markdown_edit`（search/replace 或 full_content，markdown 文本级），在线文档和本地 `.md` 文件共用。当前过渡实现为 `POST /api/page-ai/block-edit-workflow` fast-path（`local_rule` planner，Phase B 退役）。`mnote.block.*` 保留为结构性辅助（拖拽排序等）。两层操作模型已获 CLI Main（Lark Doc）参考实现验证。流式 apply + suggest/review（参考 BlockNote AI）作为 Phase C 设计冻结，当前不实施。复杂任务进入 `/api/hermes/client/runs`。模型不直接产出 unified diff 或 blockId 操作；最终定位、校验和写入由 Rust runtime 负责。
- 需要架构判断时，优先参考：
  - `/mnt/Data1T/mnote/ARCHITECTURE.md`
  - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
  - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
  - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md`


- 前端可以做展示、交互和局部适配，但不要新增第二套树真相、排序真相或 projection 契约。
- `mnote-web` 的 `compat route` 可以承接过渡流量，但不要把新的长期业务逻辑继续堆进 compat。
- 涉及文档页标题、页面设置、正文保存、页头与树一致性时，优先判断是否应收口到 `Page Aggregate`，不要继续在页面壳或 island 外侧拼第二份页面真相。
- 涉及页面新建、重命名、移动、归档、恢复、嵌入时，优先沿 `tree.*` 正式命名推进；`documents.*` 只视为兼容层，不应继续扩写为长期命令面。
- 涉及 AI 读取、定位或精确编辑页面块时，优先沿 `mnote.doc.*` / `mnote.block.*` Hermes tools 与 Rust `EditorCommand` 推进；`mnote.page.save` 只作为页面级兜底写入工具。
- 涉及资源归属（哪个页面拥有哪个 mindmap/附件）、object identity 和资源生命周期时，优先沿 Resource Tree → File Tree projection → `tree.resource.*` 命令面推进；`tree.*` 是正式命令面，`documents.*` 只视为兼容层。
- 页面 AI 编辑长期主路径为 `mnote.doc.markdown_edit`（search/replace 或 full_content，markdown 文本级），在线文档和本地 `.md` 文件共用。当前过渡实现为 `POST /api/page-ai/block-edit-workflow` fast-path（`local_rule` planner，Phase B 退役）。`mnote.block.*` 保留为结构性辅助（拖拽排序等）。两层操作模型已获 CLI Main（Lark Doc）参考实现验证。流式 apply + suggest/review（参考 BlockNote AI）作为 Phase C 设计冻结，当前不实施。复杂任务进入 `/api/hermes/client/runs`。模型不直接产出 unified diff 或 blockId 操作；最终定位、校验和写入由 Rust runtime 负责。
- 需要架构判断时，优先参考：
  - `/mnt/Data1T/mnote/ARCHITECTURE.md`
  - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
  - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
  - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/process/2-tree-first-graph-convex-rust-long-term-architecture-v1.md`
  - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
  - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
  - `/mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
  - `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`
  - `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`
  - `/mnt/Data1T/mnote/design/10-review/process/08-kernel-architecture-next-priority-review-and-checklist.md`
  - `/mnt/Data1T/mnote/design/10-review/process/09-page-ai-fast-block-edit-runtime-review.md`
  - `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.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`

## 设计稿目录规则

- `design/01-*` 到 `design/07-*` 主线大类统一按 `process/` 与 `done/` 分层。
- 主线设计稿在推进中时放到对应大类的 `process/`。
- 主线设计稿在当前真实代码中已完成后，必须移动到对应大类的 `done/`。
- 已被后续实现或更新稿明确覆盖、但又不属于 `done` 的旧主线稿，必须移动到 `design/old/` 并标记为 `[recycle]`，不要继续占用活跃 `process/`。
- `design/old/` 下的历史废弃稿也统一按 `process/` 与 `done/` 分层，但标题继续标记 `[recycle]`。
- `design/90-reference/` 只放参考资料，不参与 `process/done` 状态迁移。

## Bugs 目录规则

- `bugs/` 默认镜像 `design/` 的主线分类方式，按对应大类放置缺陷。
- 每个大类继续按 `process/` 与 `done/` 分层：
  - `process/`：缺陷已确认存在，仍在修复或验证中
  - `done/`：缺陷已修复，且已有真实代码与验证证据
- 新确认的缺陷先放 `process/`；修复完成后必须移动到同类目的 `done/`，不要在两个目录同时保留同一条缺陷。
- 缺陷归类以真正 owner 和长期主线为准，不以表面症状命名：
  - `sidebar/topbar/breadcrumb/文档页壳/Wolai 体验对齐` 优先归到 `05-editor-mainline/`
  - `projection/row model/selection/focus/keyboard/DnD/tree command` 优先归到 `04-tree-domain/`

## 协作边界

- 仅修改与当前任务直接相关的文件。
- 不要擅自恢复、覆盖、删除用户已有改动。
- 若发现与当前任务无关的脏改动，保持不动；若怀疑会影响当前任务，先确认再处理。
- 若当前问题只是 UI 表现异常，先确认是否是实验性 tree shell、compat 路径、轮询或 fallback 混入首屏主链，而不是直接怀疑 Convex 本身。

## 常用命令

- 根目录热启动：`npm run desktop:hot`
- 前端开发：`cd /mnt/Data1T/mnote/wolai-frontend && pnpm dev`
- 前端检查：`cd /mnt/Data1T/mnote/wolai-frontend && pnpm lint && pnpm test`
- 后端开发：`cd /mnt/Data1T/mnote/wolai-backend && uvicorn app.main:app --reload --port 8000`
- Convex 自托管：参考 `/mnt/Data1T/mnote/infra/convex/README.md`

## 默认测试账号


# MNOTE 当前架构梳理

> 更新时间：2026-05-16
>
> **当前阶段：初步 MVP 已达成。** 3000 下文档页完整读写链路（标题、正文、页面设置、Page Aggregate）、Sidebar/File Tree/Page Tree 三层树模型、tree command（`tree.*` preferred）、tree realtime SSE（snapshot/delta/resync）、页面 AI 快速编辑（fast-path < 1s，当前 `local_rule` planner 为过渡实现，长期方向为 markdown 级编辑，见 7-14）均已通过真实浏览器 smoke 验证。剩余工作集中在单一真源收口、live cache 统一、AI 编辑路径从块级收敛到 markdown 文本层，而非继续证明架构可行性。

本文只描述当前仓库中真实成立的主线结构，以及当前最优先的架构收口点。

## 1. 当前主线结论

当前仓库的主线不是：

- `BlockNote-first`
- `Mindmap-first`
- “先拆掉 Convex 再谈 Rust”

当前主线固定为：

1. `tree-first graph kernel` 是长期对象真相层
2. `Convex` 继续保留为当前自托管存储 / 实时 / 文件协作底座
3. `mnote-web` 是当前 Rust Web 主执行面，负责 3000 gateway、server-first shell、query / command / projection / transport 与 realtime stream；Next App Router 已降为 legacy compat / island bundle source，不再是当前主入口
4. 文档页默认主编辑器已切到页面内 `leptos-tiptap` island
5. `BlockNote` 已退出文档页默认主路径，仅作为历史参考实现 / 对照材料保留
6. 页面 AI 工具链已建立最小闭环：`POST /api/page-ai/block-edit-workflow` 提供快速编辑 route（当前 `local_rule` planner 为过渡态，长期方向为 `mnote.doc.markdown_edit` 文本级搜索替换，见 7-14）；Hermes tools（`mnote.doc.*` / `mnote.block.*`）承担复杂任务编排

一句话收口：

> **Rust 持有语义主导权，Convex 保留底座，前端逐步从重壳转向消费稳定 projection 与少量交互 island。**

补充口径：

> **主 Web 执行面当前以 `mnote-web` 为 3000 owner；Next App Router 只保留为 legacy compat、交互 island bundle source 与显式 debug/迁移辅助边界。**
>
> **页面 AI 块编辑当前以 fast-path `local_rule` planner（`/api/page-ai/block-edit-workflow`）为简单操作首选；Hermes agent（`mnote.doc.*` / `mnote.block.*` tools）保留为复杂任务编排器。**

## 2. 当前主线目录

- `/mnt/Data1T/mnote/wolai-frontend/`
  当前 React island 与 legacy compat 代码来源；Next.js App Router 不再作为 3000 主入口，只在显式 legacy/debug 迁移边界继续服务重交互运行态与对照链。
- `/mnt/Data1T/mnote/rust/crates/core-protocol/`
  Kernel 类型、projection 协议、编辑器协议、树 / 图核心术语。
- `/mnt/Data1T/mnote/rust/crates/bridge-runtime/`
  Kernel query / command、bridge transport、Tiptap <-> editor document 适配、命令解释层。
- `/mnt/Data1T/mnote/rust/crates/mnote-web/`
  Rust Web route、kernel projection、documents/search/mindmap shell、Hermes bridge、stream transport、compat，以及显式开启时的 debug shell；它是当前主 Web gateway / shell / transport owner。
- `/mnt/Data1T/mnote/wolai-frontend/convex/`
  仍在使用的 Convex functions 与数据侧逻辑。
- `/mnt/Data1T/mnote/infra/convex/`
  Convex 自托管部署与基础设施。
- `/mnt/Data1T/mnote/wolai-backend/`
  辅助后端与异步处理，不是当前页面主链。

## 3. 当前页面运行结构

### 3.1 根布局与 legacy island source

- `/mnt/Data1T/mnote/wolai-frontend/src/app/layout.tsx`

职责：


Convex 自托管部署与基础设施。
- `/mnt/Data1T/mnote/wolai-backend/`
  辅助后端与异步处理，不是当前页面主链。

## 3. 当前页面运行结构

### 3.1 根布局与 legacy island source

- `/mnt/Data1T/mnote/wolai-frontend/src/app/layout.tsx`

职责：

- 作为 legacy Next / React island bundle source 的根布局
- 保留 Convex / Query Provider 等前端 island 运行时依赖
- 服务显式 legacy/debug/迁移边界

注意：

> **3000 当前根入口与文档页 shell 由 `mnote-web` 持有；Next App Router 不再是 3000 主入口或页面根布局事实源。**

### 3.2 工作区壳与 Sidebar

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

当前 Sidebar 不是单一路径，而是三层组合：

1. 初始 SSR / route snapshot
2. query / refetch snapshot
3. tree stream live snapshot

`PreferredSidebarSnapshotProvider` 负责在 query 与 tree stream 之间做 freshness 选择，再把同一份 preferred snapshot 同时给：

- `Sidebar`
- `Breadcrumb`
- 文档页头标题消费链

这说明当前工作区树链已经进入正式收口阶段：`3000` 主壳已直接接入 `/api/tree/events` 的 snapshot / delta / resync consumer，并有浏览器 smoke 验证；但 preferred snapshot、page subtree、filetree 与补偿链还没有完全统一成唯一 live cache。

## 4. 文档页主链

### 4.1 入口

- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate-loader.ts`
- `/mnt/Data1T/mnote/wolai-frontend/src/app/(app)/documents/[id]/page.tsx`

当前文档页读取主链已经不再由页面入口手工拼若干独立字段，而是：

1. 页面 SSR 入口通过 `page-aggregate-loader.ts` 直接请求 Rust `/api/page-aggregate/:id`
2. `DocumentContent` 的内容重试补拉也直接请求同一条 `/api/page-aggregate/:id` 正式读链
3. 读取主链不再回退到 TS builder；snapshot 不可用或不可信时直接显式失败
4. Next `/api/documents/page` 已退场为明确 `410` 的 compat 边界，不再参与文档页运行时主路径

对应聚合类型定义：

- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate.ts`

### 4.2 文档壳


当前文档页读取主链已经不再由页面入口手工拼若干独立字段，而是：

1. 页面 SSR 入口通过 `page-aggregate-loader.ts` 直接请求 Rust `/api/page-aggregate/:id`
2. `DocumentContent` 的内容重试补拉也直接请求同一条 `/api/page-aggregate/:id` 正式读链
3. 读取主链不再回退到 TS builder；snapshot 不可用或不可信时直接显式失败
4. Next `/api/documents/page` 已退场为明确 `410` 的 compat 边界，不再参与文档页运行时主路径

对应聚合类型定义：

- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/page-aggregate.ts`

### 4.2 文档壳

- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-shell.tsx`
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/document-content.tsx`

`DocumentShell` 现在只是薄封装，文档页核心状态和交互壳集中在 `DocumentContent`。

### 4.3 默认编辑器 host

- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host-config.ts`
- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/editor-host.tsx`

当前默认 host 为：

- `leptos_tiptap_island`

文档页主路径已不再暴露显式 debug host 选择；历史 `iframe_debug` 宿主仅剩源码参考，不再参与页面 host 选择面。

### 4.4 正式主编辑器

- `/mnt/Data1T/mnote/wolai-frontend/src/components/editor/leptos-tiptap-island-editor-host.tsx`

当前默认主编辑器已经是页面内 Leptos island，不再是 iframe bridge。

它负责：

- 加载 Leptos/WASM 编辑器本体
- 接收初始化内容与 page options
- 处理同页事件、命令、保存与状态同步

### 4.5 BlockNote 的当前位置

- `/mnt/Data1T/mnote/recycle/wolai-frontend/src/components/editor/blocknote-editor.tsx`

`BlockNote` 当前仍保留回收副本，但其定位已变成：

- 历史参考实现
- 对照材料 / 调试素材

而不是默认主编辑器方向。

## 5. 当前编辑器与页面聚合关系

当前文档页已经开始消费统一的前端侧 `PageAggregateProjection`，但仍不能说“页面域已完全统一成 Rust 单一真源”。

当前真实状态是：

- 读取主链已优先消费 Rust `mnote.page_aggregate.v1` snapshot
- Page Aggregate 已输出 `blockDocument`、`blockProjectionVersion` 与 `projectionSource`


- 对照材料 / 调试素材

而不是默认主编辑器方向。

## 5. 当前编辑器与页面聚合关系

当前文档页已经开始消费统一的前端侧 `PageAggregateProjection`，但仍不能说“页面域已完全统一成 Rust 单一真源”。

当前真实状态是：

- 读取主链已优先消费 Rust `mnote.page_aggregate.v1` snapshot
- Page Aggregate 已输出 `blockDocument`、`blockProjectionVersion` 与 `projectionSource`
- 标题链路已开始与工作区树 canonical snapshot 对齐
- 正文默认由 `leptos-tiptap` island 编辑与保存
- 页面设置已有一部分进入 island 运行时语义
- `page_tree` / `pageSubtree` 已进入统一聚合入口
- 页面/块 AI tools 已开始通过 Page Aggregate block projection 读取、定位、dry-run、替换、插入和受限移动块，并经 `page.body.save -> documents:updateContent` 持久化

2026-05-16 更新：以下四项已通过真实 3000 browser smoke 验证（证据见 `tmp/page-aggregate-*` 目录）：

- 标题写入后 Page Aggregate、页头、Breadcrumb、Sidebar、Page Tree、File Tree 一致性（`task110`）
- 正文保存后 `body.revision` / `conflictDetectionKey` / `blockDocument` 同步（`task-page-aggregate-body-sync-smoke`）
- 页面设置写入后 `layout.pageOptions` 与 island runtime DOM 同步，刷新后不退回（`task-page-aggregate-options-sync-smoke` / `task-page-aggregate-refresh-persistence-smoke`）
- Convex 不可用时返回 degraded error（503 + `x-error-code=convex_unavailable`），不返回伪 fixture（route 级负向测试）

仍未完成的关键点是：

1. Rust 侧已提供最小 `Page Aggregate` snapshot 和 block projection v1，但当前 block projection 仍主要从 `documents.content` / local markdown content 投影（`projectionSource=documents.content`），不是 EditorBlockDocument 原生落库完成态
2. 标题 / 正文 / 页面设置虽已收口到 `page.*` family 并通过 smoke，但客户端仍保留 `PageAggregateClientState` reducer（混合 server snapshot / draft title / local content），页面域单一真源仍未完全闭环
3. AI 块工具最小闭环已启动且 fast-path < 1s，但 `scope=selection`、`format=page_xml/text`、多块插入、复杂块移动矩阵和持久审阅 UI 仍未完成
4. Sidebar 仍通过 preferred snapshot（initial / query / tree_stream）做 freshness 选择，tree realtime live cache 未统一

因此当前正确表述应是：

> **文档页读取主链已进入 Rust-first `page aggregate` 过渡态，标题/正文/页面设置 smoke 已通过，但页面域单一真源（ClientState → Rust snapshot）与 tree realtime live cache 仍未闭环。**

## 6. 当前树域结构

### 6.1 Kernel projection 与 route

- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/kernel.rs`

当前 Rust 已提供：

- `sidebar_tree`
- `page_tree`
- `file_tree`
- `subtree`
- `edges`
- `graph`

这些 route 说明树域 projection family 已经在 Rust 侧具备清晰边界。

### 6.2 树命令现状

- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/tree-command-client.ts`
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/tree.rs`
- `/mnt/Data1T/mnote/rust/crates/storage-convex-bridge/src/mapping.rs`

当前树命令已经开始以 `tree.*` 为 preferred command name，例如：


- `edges`
- `graph`

这些 route 说明树域 projection family 已经在 Rust 侧具备清晰边界。

### 6.2 树命令现状

- `/mnt/Data1T/mnote/wolai-frontend/src/lib/documents/tree-command-client.ts`
- `/mnt/Data1T/mnote/rust/crates/mnote-web/src/routes/tree.rs`
- `/mnt/Data1T/mnote/rust/crates/storage-convex-bridge/src/mapping.rs`

当前树命令已经开始以 `tree.*` 为 preferred command name，例如：

- `tree.node.create`
- `tree.node.rename`
- `tree.subtree.move`

但兼容层仍广泛保留：

- `documents.create`
- `documents.title.update`
- `documents.move`

这说明 tree command cutover 已开始，但还没有完成。

### 6.3 树 realtime 现状

当前 Sidebar 已有：

- query snapshot
- tree stream
- preferred snapshot 选择

正式的 snapshot + delta 主链已经固定到 `mnote-web` 的 `/api/tree/events`，Next `/api/mnote-web/stream` 只保留 compat alias。

因此当前正确表述应是：

> **树域 realtime 主链由 Rust Web `/api/tree/events` 持有，前端只消费稳定 stream contract 与 projection。**

补充：

> **`3000` 主壳当前已不再通过 `/api/tree/projections/*` 额外维护第二条 runtime live fallback；snapshot / delta / resync 直接消费 `/api/tree/events` 载荷。**

### 6.4 Resource Tree / File Tree / Page Tree 三层模型

当前树域已建立三层语义（合同见 `design/04-tree-domain/done/4-24-*` 与 `design/05-editor-mainline/done/5-12-*`）：

- **Resource Tree**：长期 canonical 对象组织树，Rust kernel 持有语义。资源类型包括 page、mindmap、attachment、onlyoffice、code 等（`KernelObjectIdentity`、`KernelProjectionResourceKind` 已在 `core-protocol` 落地）。
- **File Tree**：Resource Tree 的主组织投影。展示页面文件夹、`{title}.md`（非 `index.md`）、mindmap、附件等资源。文件树行消费 Rust `/api/tree/projections/file` 或等价 projection，不在前端临时拼装第二真相。
- **Page Tree**：面向阅读/导航的快捷投影。只显示页面关系和导航语义，不拥有排序、父子、附件归属的最终真相。

关键规则：

- 文件树点击 mindmap 打开 mindmap object editor，不被 `{title}.md` 吞掉
- `{title}.md` 只代表页面正文（Page Aggregate body），mindmap / OnlyOffice / 附件是不同 object model
- 页面树不持有独立结构真相，只显示文档导航关系

## 7. Mindmap 与 OnlyOffice

### 7.1 Mindmap


- **File Tree**：Resource Tree 的主组织投影。展示页面文件夹、`{title}.md`（非 `index.md`）、mindmap、附件等资源。文件树行消费 Rust `/api/tree/projections/file` 或等价 projection，不在前端临时拼装第二真相。
- **Page Tree**：面向阅读/导航的快捷投影。只显示页面关系和导航语义，不拥有排序、父子、附件归属的最终真相。

关键规则：

- 文件树点击 mindmap 打开 mindmap object editor，不被 `{title}.md` 吞掉
- `{title}.md` 只代表页面正文（Page Aggregate body），mindmap / OnlyOffice / 附件是不同 object model
- 页面树不持有独立结构真相，只显示文档导航关系

## 7. Mindmap 与 OnlyOffice

### 7.1 Mindmap

当前 Mindmap 不再是系统中心，而是：

- `tree-first graph kernel` 的一种视图 / 编辑挂件

它仍有：

- 文档内嵌块形态
- 独立页面形态

但不应再被理解为对象真相层。

### 7.2 OnlyOffice

OnlyOffice 仍然是：

- 独立页面型编辑器

它不直接嵌入正文主编辑画布；正文中通常通过附件块跳转进入。

## 8. 当前最优先的架构收口

当前最优先的三条主线，不是继续大规模 UI 重写，而是：

### 8.1 Page Aggregate

目标：

- 让标题 / 页面设置 / 正文 / page tree 统一成同一组 page aggregate projection 与 command family
- 前端不在页面壳、island 外侧、Sidebar preferred snapshot 外再拼第二份页面真相

当前进度（2026-05-16）：

- ✅ 文档页读取主链已 Rust-first，TS builder 退出 runtime，Next compat 返回 410
- ✅ 标题单一真源 smoke 通过（`task110`）：页头/Breadcrumb/Sidebar/Page Tree/File Tree 一致
- ✅ 正文写入后 `body.revision` / `blockDocument` 同步 smoke 通过
- ✅ 页面设置写入后 `pageOptions` 同步与刷新持久化 smoke 通过
- ✅ Convex 不可用时返回 degraded error，不返回伪 fixture
- ⬜ block projection 从 EditorBlockDocument 原生落库（当前仍从 `documents.content` 投影）
- ⬜ 客户端 `PageAggregateClientState` reducer 退役，页面域完全以 Rust snapshot 为单一运行时真相

对应设计稿：

- `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
- `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
- `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`

### 8.2 Tree Command Cutover


- ✅ 页面设置写入后 `pageOptions` 同步与刷新持久化 smoke 通过
- ✅ Convex 不可用时返回 degraded error，不返回伪 fixture
- ⬜ block projection 从 EditorBlockDocument 原生落库（当前仍从 `documents.content` 投影）
- ⬜ 客户端 `PageAggregateClientState` reducer 退役，页面域完全以 Rust snapshot 为单一运行时真相

对应设计稿：

- `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
- `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
- `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-5-1-page-aggregate-contract-v1.md`

### 8.2 Tree Command Cutover

目标：

- 让 `tree.*` 成为正式命令面
- `documents.*` 降为兼容层

对应设计稿：

- `/mnt/Data1T/mnote/design/04-tree-domain/done/4-6-tree-command-protocol-cutover-stage2-v1.md`

### 8.3 Tree Realtime Event Stream

目标：

- 让 snapshot + delta 正式成为树域实时主链
- 减少当前 query/refetch/freshness 补偿链的长期存在

对应设计稿：

- `/mnt/Data1T/mnote/design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md`

### 8.4 Page / Block AI Tooling

目标：

- 让 AI 读取、定位、dry-run 和精确块写入统一走 Rust Hermes tools、Page Aggregate block projection 与 `EditorCommand`
- 把 `mnote.page.save` 固定为页面级兜底写入工具，不再代表长期精确块编辑主入口
- 补齐 `scope=selection`、`format=page_xml/text`、多块插入、复杂块移动矩阵和持久审阅 UI

当前进度（2026-05-16，按 7-14 v2 更新）：

- ✅ 页面 AI 快速编辑 route 已上线：`POST /api/page-ai/block-edit-workflow`
- ✅ 简单编辑本地 planner（`local_rule`）已验证：788ms（过渡实现，将被 `markdown_edit` 替代）
- ✅ Hermes tools（`mnote.doc.*` / `mnote.block.*`）已可读取和写入块投影
- ✅ 参考实现已确认两层模型可行性：CLI Main（Lark Doc）`str_replace` 对应 `markdown_edit`，`block_*` 对应 `apply_block_ops`
- ⬜ **Phase A（当前唯一活跃实施）**：`mnote.doc.markdown_edit` + `mnote.doc.fetch` 增强（search/replace + `format: "markdown"` + local source + Hermes 注册）
- ⬜ **Phase B（下一阶段）**：退役 `direct_block_edit_operations`，`page_ai_workflow.rs` 改走 `markdown_edit`，system prompt 产 search/replace 对
- ⬜ `scope=selection`、`format=page_xml/text` 稳定输出
- ⬜ tool manifest annotations（`readonly` / `destructive` / `requiresApproval` 等）
- ⬜ **Phase C（设计冻结，不实施）**：`StreamApplyController` + `ReviewSession` + `GhostTextOverlay`（流式 apply + suggest/review）
- ⬜ stale revision / idempotency 重放保护（已有 revision 乐观锁覆盖，补端到端 smoke）

关键原则（2026-05-16 补充，按 7-14 v2 更新）：

- **两层操作模型**（参考 CLI Main Lark Doc）：文本级 `mnote.doc.markdown_edit`（str_replace）为主路径（覆盖 80%+ 场景），块级 `mnote.doc.apply_block_ops` 为结构性辅助（< 20%）
- AI 编辑主路径应从块级降维到 markdown 文本层：新增 `mnote.doc.markdown_edit`（search/replace 或 full_content）作为主要 AI 写入工具
- `mnote.block.*` 降级为结构性辅助（拖拽排序、精确块删除等），不删除
- 当前 `local_rule` planner（`direct_block_edit_operations`）是过渡实现，应退役并替换为模型产出 search/replace 对


- ⬜ **Phase B（下一阶段）**：退役 `direct_block_edit_operations`，`page_ai_workflow.rs` 改走 `markdown_edit`，system prompt 产 search/replace 对
- ⬜ `scope=selection`、`format=page_xml/text` 稳定输出
- ⬜ tool manifest annotations（`readonly` / `destructive` / `requiresApproval` 等）
- ⬜ **Phase C（设计冻结，不实施）**：`StreamApplyController` + `ReviewSession` + `GhostTextOverlay`（流式 apply + suggest/review）
- ⬜ stale revision / idempotency 重放保护（已有 revision 乐观锁覆盖，补端到端 smoke）

关键原则（2026-05-16 补充，按 7-14 v2 更新）：

- **两层操作模型**（参考 CLI Main Lark Doc）：文本级 `mnote.doc.markdown_edit`（str_replace）为主路径（覆盖 80%+ 场景），块级 `mnote.doc.apply_block_ops` 为结构性辅助（< 20%）
- AI 编辑主路径应从块级降维到 markdown 文本层：新增 `mnote.doc.markdown_edit`（search/replace 或 full_content）作为主要 AI 写入工具
- `mnote.block.*` 降级为结构性辅助（拖拽排序、精确块删除等），不删除
- 当前 `local_rule` planner（`direct_block_edit_operations`）是过渡实现，应退役并替换为模型产出 search/replace 对
- 在线 Convex 文档和本地 `.md` 文件共用同一条 markdown AI 写入路径（`resolve_source` → Convex | LocalFS）
- Markdown 既是 AI 编辑格式也是人类可读格式，不需要 XML 中间层
- 流式 apply + suggest/review（参考 BlockNote AI 的 `StreamToolExecutor` + `suggestChanges`）仅作为 Phase C 设计冻结，当前不实施

对应设计稿：

- `/mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md`
- `/mnt/Data1T/mnote/design/10-review/process/09-page-ai-fast-block-edit-runtime-review.md`
- `/mnt/Data1T/mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md`

### 8.5 定向 Bug Hunt 与质量基线

目标：

- 围绕已知架构风险做定向排查，而非泛泛"找 bug"

优先关注：

- Page Aggregate 回流不一致
- AI 块写入 conflict / stale revision
- File Tree `{title}.md` 与 page title 同步
- tree stream resync / 双浏览器一致性
- debug / compat / fallback 是否混入首屏主链

已有 smoke 体系（`scripts/` 目录）：

- `task110-page-title-single-truth-smoke.js`：标题全链路一致性
- `task-page-aggregate-body-sync-smoke.js`：正文写入后 Page Aggregate 同步
- `task-page-aggregate-options-sync-smoke.js`：页面设置写入后同步
- `task-page-aggregate-refresh-persistence-smoke.js`：刷新持久化
- `task123-rust-web-tree-live-stream-consumer-smoke.js`：tree realtime 消费
- `task169-mindmap-realtime-smoke.js`：mindmap 隔离与持久化
- `task179-tree-create-delete-no-reload-smoke.js`：树操作无刷新

## 9. 当前不该再用的旧口径

下面这些说法现在都不准确：

- “默认文档页仍然是 `BlockNoteEditor`”
- “主编辑器还没切到页面内 Leptos island”
- “当前最优先是继续证明 `leptos-tiptap` 能不能跑”
- “当前最优先是先做树域 UI 重构”
- “Rust Web 还只是实验，没有接真实页面链路”
- “页面 AI 编辑必须经过 Hermes agent，没有快路径”

当前真正的卡点已经从“能不能跑”变成：

> **单一真源如何收口（Page Aggregate ClientState → Rust snapshot），projection / command / realtime 三条链如何从兼容态进入正式主链，以及 AI 块编辑如何从“能写”变成产品级可靠（selection、review、conflict、idempotency）。**


下面这些说法现在都不准确：

- “默认文档页仍然是 `BlockNoteEditor`”
- “主编辑器还没切到页面内 Leptos island”
- “当前最优先是继续证明 `leptos-tiptap` 能不能跑”
- “当前最优先是先做树域 UI 重构”
- “Rust Web 还只是实验，没有接真实页面链路”
- “页面 AI 编辑必须经过 Hermes agent，没有快路径”

当前真正的卡点已经从“能不能跑”变成：

> **单一真源如何收口（Page Aggregate ClientState → Rust snapshot），projection / command / realtime 三条链如何从兼容态进入正式主链，以及 AI 块编辑如何从“能写”变成产品级可靠（selection、review、conflict、idempotency）。**


# Design System Inspired by Notion

## 1. Visual Theme & Atmosphere

Notion's website embodies the philosophy of the tool itself: a blank canvas that gets out of your way. The design system is built on warm neutrals rather than cold grays, creating a distinctly approachable minimalism that feels like quality paper rather than sterile glass. The page canvas is pure white (`#ffffff`) but the text isn't pure black -- it's a warm near-black (`rgba(0,0,0,0.95)`) that softens the reading experience imperceptibly. The warm gray scale (`#f6f5f4`, `#31302e`, `#615d59`, `#a39e98`) carries subtle yellow-brown undertones, giving the interface a tactile, almost analog warmth.

The custom NotionInter font (a modified Inter) is the backbone of the system. At display sizes (64px), it uses aggressive negative letter-spacing (-2.125px), creating headlines that feel compressed and precise. The weight range is broader than typical systems: 400 for body, 500 for UI elements, 600 for semi-bold labels, and 700 for display headings. OpenType features `"lnum"` (lining numerals) and `"locl"` (localized forms) are enabled on larger text, adding typographic sophistication that rewards close reading.

What makes Notion's visual language distinctive is its border philosophy. Rather than heavy borders or shadows, Notion uses ultra-thin `1px solid rgba(0,0,0,0.1)` borders -- borders that exist as whispers, barely perceptible division lines that create structure without weight. The shadow system is equally restrained: multi-layer stacks with cumulative opacity never exceeding 0.05, creating depth that's felt rather than seen.

**Key Characteristics:**
- NotionInter (modified Inter) with negative letter-spacing at display sizes (-2.125px at 64px)
- Warm neutral palette: grays carry yellow-brown undertones (`#f6f5f4` warm white, `#31302e` warm dark)
- Near-black text via `rgba(0,0,0,0.95)` -- not pure black, creating micro-warmth
- Ultra-thin borders: `1px solid rgba(0,0,0,0.1)` throughout -- whisper-weight division
- Multi-layer shadow stacks with sub-0.05 opacity for barely-there depth
- Notion Blue (`#0075de`) as the singular accent color for CTAs and interactive elements
- Pill badges (9999px radius) with tinted blue backgrounds for status indicators
- 8px base spacing unit with an organic, non-rigid scale

## 2. Color Palette & Roles

### Primary
- **Notion Black** (`rgba(0,0,0,0.95)` / `#000000f2`): Primary text, headings, body copy. The 95% opacity softens pure black without sacrificing readability.
- **Pure White** (`#ffffff`): Page background, card surfaces, button text on blue.
- **Notion Blue** (`#0075de`): Primary CTA, link color, interactive accent -- the only saturated color in the core UI chrome.

### Brand Secondary
- **Deep Navy** (`#213183`): Secondary brand color, used sparingly for emphasis and dark feature sections.
- **Active Blue** (`#005bab`): Button active/pressed state -- darker variant of Notion Blue.

### Warm Neutral Scale
- **Warm White** (`#f6f5f4`): Background surface tint, section alternation, subtle card fill. The yellow undertone is key.
- **Warm Dark** (`#31302e`): Dark surface background, dark section text. Warmer than standard grays.
- **Warm Gray 500** (`#615d59`): Secondary text, descriptions, muted labels.
- **Warm Gray 300** (`#a39e98`): Placeholder text, disabled states, caption text.

### Semantic Accent Colors
- **Teal** (`#2a9d99`): Success states, positive indicators.
- **Green** (`#1aae39`): Confirmation, completion badges.
- **Orange** (`#dd5b00`): Warning states, attention indicators.
- **Pink** (`#ff64c8`): Decorative accent, feature highlights.
- **Purple** (`#391c57`): Premium features, deep accents.
- **Brown** (`#523410`): Earthy accent, warm feature sections.

### Interactive
- **Link Blue** (`#0075de`): Primary link color with underline-on-hover.
- **Link Light Blue** (`#62aef0`): Lighter link variant for dark backgrounds.
- **Focus Blue** (`#097fe8`): Focus ring on interactive elements.


- **Badge Blue Bg** (`#f2f9ff`): Pill badge background, tinted blue surface.
- **Badge Blue Text** (`#097fe8`): Pill badge text, darker blue for readability.

### Shadows & Depth
- **Card Shadow** (`rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.84688px, rgba(0,0,0,0.02) 0px 0.8px 2.925px, rgba(0,0,0,0.01) 0px 0.175px 1.04062px`): Multi-layer card elevation.
- **Deep Shadow** (`rgba(0,0,0,0.01) 0px 1px 3px, rgba(0,0,0,0.02) 0px 3px 7px, rgba(0,0,0,0.02) 0px 7px 15px, rgba(0,0,0,0.04) 0px 14px 28px, rgba(0,0,0,0.05) 0px 23px 52px`): Five-layer deep elevation for modals and featured content.
- **Whisper Border** (`1px solid rgba(0,0,0,0.1)`): Standard division border -- cards, dividers, sections.

## 3. Typography Rules

### Font Family


- **Focus Blue** (`#097fe8`): Focus ring on interactive elements.
- **Badge Blue Bg** (`#f2f9ff`): Pill badge background, tinted blue surface.
- **Badge Blue Text** (`#097fe8`): Pill badge text, darker blue for readability.

### Shadows & Depth
- **Card Shadow** (`rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.84688px, rgba(0,0,0,0.02) 0px 0.8px 2.925px, rgba(0,0,0,0.01) 0px 0.175px 1.04062px`): Multi-layer card elevation.
- **Deep Shadow** (`rgba(0,0,0,0.01) 0px 1px 3px, rgba(0,0,0,0.02) 0px 3px 7px, rgba(0,0,0,0.02) 0px 7px 15px, rgba(0,0,0,0.04) 0px 14px 28px, rgba(0,0,0,0.05) 0px 23px 52px`): Five-layer deep elevation for modals and featured content.
- **Whisper Border** (`1px solid rgba(0,0,0,0.1)`): Standard division border -- cards, dividers, sections.

## 3. Typography Rules

### Font Family
- **Primary**: `NotionInter`, with fallbacks: `Inter, -apple-system, system-ui, Segoe UI, Helvetica, Apple Color Emoji, Arial, Segoe UI Emoji, Segoe UI Symbol`
- **OpenType Features**: `"lnum"` (lining numerals) and `"locl"` (localized forms) enabled on display and heading text.

### Hierarchy

| Role | Font | Size | Weight | Line Height | Letter Spacing | Notes |
|------|------|------|--------|-------------|----------------|-------|
| Display Hero | NotionInter | 64px (4.00rem) | 700 | 1.00 (tight) | -2.125px | Maximum compression, billboard headlines |
| Display Secondary | NotionInter | 54px (3.38rem) | 700 | 1.04 (tight) | -1.875px | Secondary hero, feature headlines |
| Section Heading | NotionInter | 48px (3.00rem) | 700 | 1.00 (tight) | -1.5px | Feature section titles, with `"lnum"` |
| Sub-heading Large | NotionInter | 40px (2.50rem) | 700 | 1.50 | normal | Card headings, feature sub-sections |
| Sub-heading | NotionInter | 26px (1.63rem) | 700 | 1.23 (tight) | -0.625px | Section sub-titles, content headers |
| Card Title | NotionInter | 22px (1.38rem) | 700 | 1.27 (tight) | -0.25px | Feature cards, list titles |
| Body Large | NotionInter | 20px (1.25rem) | 600 | 1.40 | -0.125px | Introductions, feature descriptions |
| Body | NotionInter | 16px (1.00rem) | 400 | 1.50 | normal | Standard reading text |
| Body Medium | NotionInter | 16px (1.00rem) | 500 | 1.50 | normal | Navigation, emphasized UI text |
| Body Semibold | NotionInter | 16px (1.00rem) | 600 | 1.50 | normal | Strong labels, active states |
| Body Bold | NotionInter | 16px (1.00rem) | 700 | 1.50 | normal | Headlines at body size |
| Nav / Button | NotionInter | 15px (0.94rem) | 600 | 1.33 | normal | Navigation links, button text |
| Caption | NotionInter | 14px (0.88rem) | 500 | 1.43 | normal | Metadata, secondary labels |
| Caption Light | NotionInter | 14px (0.88rem) | 400 | 1.43 | normal | Body captions, descriptions |
| Badge | NotionInter | 12px (0.75rem) | 600 | 1.33 | 0.125px | Pill badges, tags, status labels |
| Micro Label | NotionInter | 12px (0.75rem) | 400 | 1.33 | 0.125px | Small metadata, timestamps |

### Principles
- **Compression at scale**: NotionInter at display sizes uses -2.125px letter-spacing at 64px, progressively relaxing to -0.625px at 26px and normal at 16px. The compression creates density at headlines while maintaining readability at body sizes.
- **Four-weight system**: 400 (body/reading), 500 (UI/interactive), 600 (emphasis/navigation), 700 (headings/display). The broader weight range compared to most systems allows nuanced hierarchy.
- **Warm scaling**: Line height tightens as size increases -- 1.50 at body (16px), 1.23-1.27 at sub-headings, 1.00-1.04 at display. This creates denser, more impactful headlines.
- **Badge micro-tracking**: The 12px badge text uses positive letter-spacing (0.125px) -- the only positive tracking in the system, creating wider, more legible small text.

## 4. Component Stylings

### Buttons

**Primary Blue**
- Background: `#0075de` (Notion Blue)
- Text: `#ffffff`
- Padding: 8px 16px
- Radius: 4px (subtle)
- Border: `1px solid transparent`
- Hover: background darkens to `#005bab`


- Active: scale(0.9) transform
- Focus: `2px solid` focus outline, `var(--shadow-level-200)` shadow
- Use: Primary CTA ("Get Notion free", "Try it")

**Secondary / Tertiary**
- Background: `rgba(0,0,0,0.05)` (translucent warm gray)
- Text: `#000000` (near-black)


### Navigation
- Clean horizontal nav on white, not sticky
- Brand logo left-aligned (33x34px icon + wordmark)
- Links: NotionInter 15px weight 500-600, near-black text
- Hover: color shift to `var(--color-link-primary-text-hover)`
- CTA: blue pill button ("Get Notion free") right-aligned
- Mobile: hamburger menu collapse
- Product dropdowns with multi-level categorized menus

### Image Treatment
- Product screenshots with `1px solid rgba(0,0,0,0.1)` border
- Top-rounded images: `12px 12px 0px 0px` radius
- Dashboard/workspace preview screenshots dominate feature sections
- Warm gradient backgrounds behind hero illustrations (decorative character illustrations)

### Distinctive Components

**Feature Cards with Illustrations**
- Large illustrative headers (The Great Wave, product UI screenshots)
- 12px radius card with whisper border
- Title at 22px weight 700, description at 16px weight 400
- Warm white (`#f6f5f4`) background variant for alternating sections

**Trust Bar / Logo Grid**
- Company logos (trusted teams section) in their brand colors
- Horizontal scroll or grid layout with team counts
- Metric display: large number + description pattern

**Metric Cards**
- Large number display (e.g., "$4,200 ROI")
- NotionInter 40px+ weight 700 for the metric
- Description below in warm gray body text
- Whisper-bordered card container

## 5. Layout Principles

### Spacing System
- Base unit: 8px
- Scale: 2px, 3px, 4px, 5px, 6px, 7px, 8px, 11px, 12px, 14px, 16px, 24px, 32px
- Non-rigid organic scale with fractional values (5.6px, 6.4px) for micro-adjustments

### Grid & Container
- Max content width: approximately 1200px
- Hero: centered single-column with generous top padding (80-120px)
- Feature sections: 2-3 column grids for cards
- Full-width warm white (`#f6f5f4`) section backgrounds for alternation
- Code/dashboard screenshots as contained with whisper border

### Whitespace Philosophy
- **Generous vertical rhythm**: 64-120px between major sections. Notion lets content breathe with vast vertical padding.
- **Warm alternation**: White sections alternate with warm white (`#f6f5f4`) sections, creating gentle visual rhythm without harsh color breaks.
- **Content-first density**: Body text blocks are compact (line-height 1.50) but surrounded by ample margin, creating islands of readable content in a sea of white space.

### Border Radius Scale
- Micro (4px): Buttons, inputs, functional interactive elements
- Subtle (5px): Links, list items, menu items
- Standard (8px): Small cards, containers, inline elements
- Comfortable (12px): Standard cards, feature containers, image tops
- Large (16px): Hero cards, featured content, promotional blocks


### Whitespace Philosophy
- **Generous vertical rhythm**: 64-120px between major sections. Notion lets content breathe with vast vertical padding.
- **Warm alternation**: White sections alternate with warm white (`#f6f5f4`) sections, creating gentle visual rhythm without harsh color breaks.
- **Content-first density**: Body text blocks are compact (line-height 1.50) but surrounded by ample margin, creating islands of readable content in a sea of white space.

### Border Radius Scale
- Micro (4px): Buttons, inputs, functional interactive elements
- Subtle (5px): Links, list items, menu items
- Standard (8px): Small cards, containers, inline elements
- Comfortable (12px): Standard cards, feature containers, image tops
- Large (16px): Hero cards, featured content, promotional blocks
- Full Pill (9999px): Badges, pills, status indicators
- Circle (100%): Tab indicators, avatars

## 6. Depth & Elevation

| Level | Treatment | Use |
|-------|-----------|-----|
| Flat (Level 0) | No shadow, no border | Page background, text blocks |
| Whisper (Level 1) | `1px solid rgba(0,0,0,0.1)` | Standard borders, card outlines, dividers |
| Soft Card (Level 2) | 4-layer shadow stack (max opacity 0.04) | Content cards, feature blocks |
| Deep Card (Level 3) | 5-layer shadow stack (max opacity 0.05, 52px blur) | Modals, featured panels, hero elements |
| Focus (Accessibility) | `2px solid var(--focus-color)` outline | Keyboard focus on all interactive elements |

**Shadow Philosophy**: Notion's shadow system uses multiple layers with extremely low individual opacity (0.01 to 0.05) that accumulate into soft, natural-looking elevation. The 4-layer card shadow spans from 1.04px to 18px blur, creating a gradient of depth rather than a single hard shadow. The 5-layer deep shadow extends to 52px blur at 0.05 opacity, producing ambient occlusion that feels like natural light rather than computer-generated depth. This layered approach makes elements feel embedded in the page rather than floating above it.

### Decorative Depth
- Hero section: decorative character illustrations (playful, hand-drawn style)
- Section alternation: white to warm white (`#f6f5f4`) background shifts
- No hard section borders -- separation comes from background color changes and spacing

## 7. Responsive Behavior

### Breakpoints
| Name | Width | Key Changes |
|------|-------|-------------|
| Mobile Small | <400px | Tight single column, minimal padding |
| Mobile | 400-600px | Standard mobile, stacked layout |
| Tablet Small | 600-768px | 2-column grids begin |
| Tablet | 768-1080px | Full card grids, expanded padding |
| Desktop Small | 1080-1200px | Standard desktop layout |
| Desktop | 1200-1440px | Full layout, maximum content width |
| Large Desktop | >1440px | Centered, generous margins |

### Touch Targets
- Buttons use comfortable padding (8px-16px vertical)
- Navigation links at 15px with adequate spacing
- Pill badges have 8px horizontal padding for tap targets
- Mobile menu toggle uses standard hamburger button

### Collapsing Strategy
- Hero: 64px display -> scales to 40px -> 26px on mobile, maintains proportional letter-spacing
- Navigation: horizontal links + blue CTA -> hamburger menu
- Feature cards: 3-column -> 2-column -> single column stacked
- Product screenshots: maintain aspect ratio with responsive images
- Trust bar logos: grid -> horizontal scroll on mobile
- Footer: multi-column -> stacked single column
- Section spacing: 80px+ -> 48px on mobile


- Pill badges have 8px horizontal padding for tap targets
- Mobile menu toggle uses standard hamburger button

### Collapsing Strategy
- Hero: 64px display -> scales to 40px -> 26px on mobile, maintains proportional letter-spacing
- Navigation: horizontal links + blue CTA -> hamburger menu
- Feature cards: 3-column -> 2-column -> single column stacked
- Product screenshots: maintain aspect ratio with responsive images
- Trust bar logos: grid -> horizontal scroll on mobile
- Footer: multi-column -> stacked single column
- Section spacing: 80px+ -> 48px on mobile

### Image Behavior
- Workspace screenshots maintain whisper border at all sizes
- Hero illustrations scale proportionally
- Product screenshots use responsive images with consistent border radius
- Full-width warm white sections maintain edge-to-edge treatment

## 8. Accessibility & States

### Focus System
- All interactive elements receive visible focus indicators
- Focus outline: `2px solid` with focus color + shadow level 200
- Tab navigation supported throughout all interactive components
- High contrast text: near-black on white exceeds WCAG AAA (>14:1 ratio)

### Interactive States
- **Default**: Standard appearance with whisper borders
- **Hover**: Color shift on text, scale(1.05) on buttons, underline on links
- **Active/Pressed**: scale(0.9) transform, darker background variant
- **Focus**: Blue outline ring with shadow reinforcement
- **Disabled**: Warm gray (`#a39e98`) text, reduced opacity

### Color Contrast
- Primary text (rgba(0,0,0,0.95)) on white: ~18:1 ratio
- Secondary text (#615d59) on white: ~5.5:1 ratio (WCAG AA)
- Blue CTA (#0075de) on white: ~4.6:1 ratio (WCAG AA for large text)
- Badge text (#097fe8) on badge bg (#f2f9ff): ~4.5:1 ratio (WCAG AA for large text)

## 9. Agent Prompt Guide

### Quick Color Reference
- Primary CTA: Notion Blue (`#0075de`)
- Background: Pure White (`#ffffff`)
- Alt Background: Warm White (`#f6f5f4`)
- Heading text: Near-Black (`rgba(0,0,0,0.95)`)
- Body text: Near-Black (`rgba(0,0,0,0.95)`)
- Secondary text: Warm Gray 500 (`#615d59`)
- Muted text: Warm Gray 300 (`#a39e98`)
- Border: `1px solid rgba(0,0,0,0.1)`
- Link: Notion Blue (`#0075de`)
- Focus ring: Focus Blue (`#097fe8`)

### Example Component Prompts
- "Create a hero section on white background. Headline at 64px NotionInter weight 700, line-height 1.00, letter-spacing -2.125px, color rgba(0,0,0,0.95). Subtitle at 20px weight 600, line-height 1.40, color #615d59. Blue CTA button (#0075de, 4px radius, 8px 16px padding, white text) and ghost button (transparent bg, near-black text, underline on hover)."
- "Design a card: white background, 1px solid rgba(0,0,0,0.1) border, 12px radius. Use shadow stack: rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.85px, rgba(0,0,0,0.02) 0px 0.8px 2.93px, rgba(0,0,0,0.01) 0px 0.175px 1.04px. Title at 22px NotionInter weight 700, letter-spacing -0.25px. Body at 16px weight 400, color #615d59."
- "Build a pill badge: #f2f9ff background, #097fe8 text, 9999px radius, 4px 8px padding, 12px NotionInter weight 600, letter-spacing 0.125px."
- "Create navigation: white header. NotionInter 15px weight 600 for links, near-black text. Blue pill CTA 'Get Notion free' right-aligned (#0075de bg, white text, 4px radius)."
- "Design an alternating section layout: white sections alternate with warm white (#f6f5f4) sections. Each section has 64-80px vertical padding, max-width 1200px centered. Section heading at 48px weight 700, line-height 1.00, letter-spacing -1.5px."


- Muted text: Warm Gray 300 (`#a39e98`)
- Border: `1px solid rgba(0,0,0,0.1)`
- Link: Notion Blue (`#0075de`)
- Focus ring: Focus Blue (`#097fe8`)

### Example Component Prompts
- "Create a hero section on white background. Headline at 64px NotionInter weight 700, line-height 1.00, letter-spacing -2.125px, color rgba(0,0,0,0.95). Subtitle at 20px weight 600, line-height 1.40, color #615d59. Blue CTA button (#0075de, 4px radius, 8px 16px padding, white text) and ghost button (transparent bg, near-black text, underline on hover)."
- "Design a card: white background, 1px solid rgba(0,0,0,0.1) border, 12px radius. Use shadow stack: rgba(0,0,0,0.04) 0px 4px 18px, rgba(0,0,0,0.027) 0px 2.025px 7.85px, rgba(0,0,0,0.02) 0px 0.8px 2.93px, rgba(0,0,0,0.01) 0px 0.175px 1.04px. Title at 22px NotionInter weight 700, letter-spacing -0.25px. Body at 16px weight 400, color #615d59."
- "Build a pill badge: #f2f9ff background, #097fe8 text, 9999px radius, 4px 8px padding, 12px NotionInter weight 600, letter-spacing 0.125px."
- "Create navigation: white header. NotionInter 15px weight 600 for links, near-black text. Blue pill CTA 'Get Notion free' right-aligned (#0075de bg, white text, 4px radius)."
- "Design an alternating section layout: white sections alternate with warm white (#f6f5f4) sections. Each section has 64-80px vertical padding, max-width 1200px centered. Section heading at 48px weight 700, line-height 1.00, letter-spacing -1.5px."

### Iteration Guide
1. Always use warm neutrals -- Notion's grays have yellow-brown undertones (#f6f5f4, #31302e, #615d59, #a39e98), never blue-gray
2. Letter-spacing scales with font size: -2.125px at 64px, -1.875px at 54px, -0.625px at 26px, normal at 16px
3. Four weights: 400 (read), 500 (interact), 600 (emphasize), 700 (announce)
4. Borders are whispers: 1px solid rgba(0,0,0,0.1) -- never heavier
5. Shadows use 4-5 layers with individual opacity never exceeding 0.05
6. The warm white (#f6f5f4) section background is essential for visual rhythm
7. Pill badges (9999px) for status/tags, 4px radius for buttons and inputs
8. Notion Blue (#0075de) is the only saturated color in core UI -- use it sparingly for CTAs and links


#!/usr/bin/env bash
set -euo pipefail

# Harness 会在每次会话启动时执行此脚本，这里只做轻量且可重复执行的环境检查。
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT_DIR"

command -v git >/dev/null || {
  echo "ERROR: 缺少 git"
  exit 1
}

command -v python3 >/dev/null || {
  echo "ERROR: 缺少 python3"
  exit 1
}

if [ -d "$ROOT_DIR/wolai-frontend" ]; then
  if ! command -v node >/dev/null; then
    echo "WARN: 缺少 node，前端相关任务可能无法执行"
  fi
  if ! command -v pnpm >/dev/null; then
    echo "WARN: 缺少 pnpm，前端相关任务可能无法执行"
  fi
fi

echo "Harness 环境检查完成"


[2026-04-26T23:38:55Z] [SESSION-1] Completed [task-005] (commit skipped by repo rule; validated by mnote-web tree_shell_embeds_renderer_input_contract, tree-shell iframe/surface vitest, task112 and task113 smoke)
[2026-04-26T23:38:55Z] [SESSION-1] Starting [task-006] Phase L：将 tree.subtree.move 最终写路径继续下沉 Rust kernel (base=6d89f5ca)
[2026-04-26T23:38:55Z] [SESSION-1] STATS tasks_total=9 completed=5 failed=0 pending=3 in_progress=1 blocked=0 attempts_total=6 checkpoints=5
[2026-04-26T23:52:20Z] [SESSION-2] LOCK acquired (pid=3897587)
[2026-04-26T23:52:20Z] [SESSION-2] RECOVERY [task-006] action="validate existing uncommitted changes" reason="previous session left task-006 in_progress after implementation and validation"
[2026-04-26T23:52:52Z] [SESSION-2] CHECKPOINT [task-006] step=1/1 "已引入 Rust treeWriteOperation，Convex 优先执行 Rust write operation；目标验证通过。"
[2026-04-26T23:52:52Z] [SESSION-2] Completed [task-006] (commit skipped by repo rule; validated by bridge-runtime tree_subtree_move_command and 39 frontend target tests)
[2026-04-26T23:52:52Z] [SESSION-2] Starting [task-007] Phase M：补齐 block/save-snapshot formal artifact 与 domain-event contract (base=6d89f5ca)
[2026-04-26T23:52:52Z] [SESSION-2] STATS tasks_total=9 completed=6 failed=0 pending=2 in_progress=1 blocked=0 attempts_total=7 checkpoints=6
[2026-04-27T00:09:12Z] [SESSION-2] CHECKPOINT [task-007] step=1/1 "formal domainEventPlan payload schema 与 resync_required delta 已落地；目标验证通过。"
[2026-04-27T00:09:12Z] [SESSION-2] Completed [task-007] (commit skipped by repo rule; validated by bridge-runtime formal contract tests and frontend adapter/runtime tests)
[2026-04-27T00:09:12Z] [SESSION-2] Starting [task-008] Phase N：继续收薄 3000 Next route 过渡职责 (base=6d89f5ca)
[2026-04-27T00:09:12Z] [SESSION-2] STATS tasks_total=9 completed=7 failed=0 pending=1 in_progress=1 blocked=0 attempts_total=8 checkpoints=7
[2026-04-27T00:18:38Z] [SESSION-3] LOCK acquired (pid=codex-session-3)
[2026-04-27T00:18:38Z] [SESSION-3] RECOVERY [task-008] action="resume diagnostics with uncommitted changes" reason="previous session implemented Phase N but task112 picker move wait timed out"
[2026-04-27T00:28:56Z] [SESSION-3] CHECKPOINT [task-008] step=1/2 "已定位 task112 超时根因：move 请求实际发出但 Convex 运行态 documents.move validator 未加载 treeWriteOperation，执行 npx convex dev --once 刷新本地 functions。"
[2026-04-27T00:34:43Z] [SESSION-3] CHECKPOINT [task-008] step=2/2 "目标 Vitest 与 task112 smoke 通过；补充 iframe ready 等待，运行态 Convex functions 已刷新。"
[2026-04-27T00:34:43Z] [SESSION-3] Completed [task-008] (commit skipped by repo rule; validated by tree route/delta/boundary vitest and task112 smoke)
[2026-04-27T00:34:43Z] [SESSION-3] Starting [task-009] Phase O：搜索语义 hardening 与资源类型 fixture 补齐 (base=6d89f5ca)
[2026-04-27T00:41:58Z] [SESSION-3] CHECKPOINT [task-009] step=1/1 "file_tree 搜索 hardening 前端契约与资源 fixture 已完成；目标验证通过。"
[2026-04-27T00:41:58Z] [SESSION-3] Completed [task-009] (commit skipped by repo rule; validated by tree-delta, projection-client, kernel-file-tree tests and task112 smoke)
[2026-04-27T00:41:58Z] [SESSION-3] STATS tasks_total=9 completed=9 failed=0 pending=0 in_progress=0 blocked=0 attempts_total=9 checkpoints=9
[2026-04-27T00:41:58Z] [SESSION-3] LOCK released
[2026-04-28T00:00:00Z] [SESSION-4] CORRECTION final-renderer-gate="task-001..task-009 were Rust/WASM reducer runtime completion, not final DOM shell completion"
[2026-04-28T00:00:00Z] [SESSION-4] INIT Added task-010..task-013 from design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md
[2026-04-28T00:00:00Z] [SESSION-4] NEXT task-010 must add negative gates so default page/filetree/picker success path cannot remain iframe_srcdoc inline DOM shell
[2026-04-28T00:00:00Z] [SESSION-4] STATS tasks_total=13 completed=9 failed=0 pending=4 in_progress=0 blocked=0 attempts_total=9 checkpoints=9
[2026-04-28T02:44:47Z] [SESSION-5] LOCK acquired (pid=manual-codex)
[2026-04-28T02:44:47Z] [SESSION-5] INIT Harness environment check: PASS


[2026-04-28T02:44:47Z] [SESSION-5] Starting [task-010] Phase P-0：补 final DOM shell 负向门禁，阻止 iframe_srcdoc 被误判为完成 (base=81ba21c5)
[2026-04-28T02:52:03Z] [SESSION-6] LOCK acquired (pid=manual-codex-session-6)
[2026-04-28T02:52:03Z] [SESSION-6] RECOVERY [task-010] action="resume in-progress final DOM shell gate" reason="previous session lock stale; preserve existing uncommitted changes"
[2026-04-28T03:28:16.005752Z] [SESSION-6] CHECKPOINT [task-010] step=1/4 "已新增默认非 iframe rust_wasm_dom_shell_host 与共享 DOM projection model，legacy iframe 改为显式环境开关。"
[2026-04-28T03:34:10.533972Z] [SESSION-6] CHECKPOINT [task-010] step=2/4 "tree-shell-surface 与 legacy iframe host 目标 Vitest 通过；默认主路径断言已切成 rust_wasm_dom_shell_host 且负向排除 iframe_srcdoc。"
[2026-04-28T03:36:24.605315Z] [SESSION-6] CHECKPOINT [task-010] step=3/4 "move/embed picker 测试已切到 DOM shell；默认 picker 不再以 iframe postMessage 为成功条件，目标前端 Vitest 29 项通过。"
[2026-04-28T04:40:14Z] [SESSION-7] RECOVERY [task-010] action="resume stale leased final DOM shell cutover" reason="previous session left task-010 in_progress; existing changes validated without rollback"
[2026-04-28T04:40:14Z] [SESSION-7] CHECKPOINT [task-010] step=4/4 "已补默认 DOM shell 负向门禁：tree-shell-host/surface/picker 测试与 smoke 默认拒绝 iframe_srcdoc 成功路径。"


[2026-04-28T06:29:15Z] [SESSION-7] INIT Added task-014..task-018 for remaining 4-16 final runtime closure: normalizedMove fallback removal, snapshot event/Page Aggregate, embed pageReference migration, and file_tree indexing visibility.
[2026-04-28T06:29:15Z] [SESSION-7] Starting [task-014] Phase L2：删除 normalizedMove fallback，强制 move 主写路径使用 treeWriteOperation (base=81ba21c5)
[2026-04-28T06:37:40Z] [SESSION-7] CHECKPOINT [task-014] step=1/1 "已删除 normalizedMove fallback；bridge-runtime/TS transport/Convex move 写路径只保留 treeWriteOperation，目标测试通过。"
[2026-04-28T06:37:40Z] [SESSION-7] Completed [task-014] (commit skipped by repo rule; validated by bridge-runtime tree_subtree_move_command and 20 frontend target tests)
[2026-04-28T06:37:40Z] [SESSION-7] Starting [task-015] Phase M2：定稿 document.snapshot.saved 独立事件并避免与 page.body.saved payload 重叠 (base=81ba21c5)
[2026-04-28T07:44:37Z] [SESSION-8] CHECKPOINT [task-015] step=1/1 "page.body.saved 与 document.snapshot.saved 已拆成 domainEventPlans 双事件；artifact writer 支持 domainEvents 批量落库，page.body.saved payload 不再夹带 snapshot。"
[2026-04-28T07:44:37Z] [SESSION-8] Completed [task-015] (commit skipped by repo rule; validated by bridge-runtime --lib and rust-runtime/bridge-log/page-write tests)
[2026-04-28T07:44:37Z] [SESSION-8] Starting [task-016] Phase M3：把 snapshot 独立事件接入 Page Aggregate 深层收口文档与客户端边界 (base=81ba21c5)
[2026-04-28T07:44:37Z] [SESSION-8] CHECKPOINT [task-016] step=1/1 "Page Aggregate contract 与 5-6 清单已固定 page.body.saved / document.snapshot.saved 职责边界；4-16 snapshot 项已勾选。"
[2026-04-28T07:44:37Z] [SESSION-8] Completed [task-016] (commit skipped by repo rule; validated by page-aggregate-client-state/page-aggregate-builder/page-write tests and git diff --check)
[2026-04-28T07:44:37Z] [SESSION-8] Starting [task-017] Phase N2：将 tree.node.embed 的 pageReference 组装迁到 Page Aggregate / Rust artifact 边界 (base=81ba21c5)
[2026-04-28T07:44:37Z] [SESSION-8] CHECKPOINT [task-017] step=1/1 "Rust pageAggregateEmbedPlan 已产出 pageReference block 与 next content；3000 route/documents embed adapter 只传 substrate preflight，tree-route-boundary compatPending 已清空。"
[2026-04-28T07:44:37Z] [SESSION-8] Completed [task-017] (commit skipped by repo rule; validated by bridge-runtime --lib, route tests, and tree-route-boundary tests)
[2026-04-28T07:44:37Z] [SESSION-8] Starting [task-018] Phase O2：补 file_tree 搜索索引可见性指标边界并收口 4-16 (base=81ba21c5)
[2026-04-28T07:44:37Z] [SESSION-8] CHECKPOINT [task-018] step=1/1 "file_tree 搜索 indexingVisibility 已提升为 projection result meta；mnote-web 与 3000 同源 projection route 均透传该指标，4-16 无未勾选项。"
[2026-04-28T07:44:37Z] [SESSION-8] Completed [task-018] (commit skipped by repo rule; validated by bridge-runtime --lib, mnote-web file_tree_projection, file projection route/client tests)
[2026-04-28T07:44:37Z] [SESSION-8] STATS tasks_total=18 completed=18 failed=0 pending=0 in_progress=0 blocked=0 attempts_total=18 checkpoints=20
[2026-04-28T08:53:21Z] [SESSION-9] INIT Added task-019..task-026 from design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md for Rust Web main execution plane cutover.
[2026-04-28T08:53:21Z] [SESSION-9] NEXT task-019 must freeze main execution owner manifest before gateway implementation.
[2026-04-28T08:53:21Z] [SESSION-9] STATS tasks_total=26 completed=18 failed=0 pending=8 in_progress=0 blocked=0 attempts_total=18 checkpoints=21
[2026-04-28T09:06:04Z] [SESSION-9] LOCK acquired (pid=codex-main-session)
[2026-04-28T09:06:04Z] [SESSION-9] Starting [task-019] Phase W-0：冻结 Rust Web 主执行面 owner manifest 与负向门禁 (base=9f6a2c86)
[2026-04-28T09:07:58Z] [SESSION-9] CHECKPOINT [task-019] step=1/1 "已新增 Rust Web 主执行面 owner manifest 与负向门禁；目标 Vitest 与 diff check 通过。"
[2026-04-28T09:07:58Z] [SESSION-9] Completed [task-019] (commit skipped by repo rule; validated by rust-web-main-execution-boundary/runtime-config vitest and git diff --check)
[2026-04-28T09:07:58Z] [SESSION-9] Starting [task-020] Phase W-1：让 mnote-web 默认成为 3000 gateway owner (base=9f6a2c86)


[2026-04-28T09:13:44Z] [SESSION-9] CHECKPOINT [task-020] step=1/1 "mnote-web gateway owner、desktop-hot 默认启动计划与 task114 smoke 已落地；目标验证通过。"
[2026-04-28T09:13:44Z] [SESSION-9] Completed [task-020] (commit skipped by repo rule; validated by desktop-hot node tests, mnote-web gateway cargo tests, and task114 smoke)
[2026-04-28T09:13:44Z] [SESSION-9] Starting [task-021] Phase W-2：把 runtime config 与 auth/session handoff 迁到 Rust Web (base=9f6a2c86)
[2026-04-28T09:16:15Z] [SESSION-9] WARN validation.commands="修正 task-021..task-025 Cargo 多过滤词写法；原命令会被 cargo test 拒绝为 unexpected argument。"
[2026-04-28T09:16:28Z] [SESSION-9] CHECKPOINT [task-021] step=1/1 "mnote-web runtime config/session routes 与 Next token compat/debug route 已落地；修正后目标验证通过。"
[2026-04-28T09:16:28Z] [SESSION-9] Completed [task-021] (commit skipped by repo rule; validated by mnote-web session/runtime_config tests and runtime-config/token vitest)
[2026-04-28T09:16:28Z] [SESSION-9] Starting [task-022] Phase W-3：建立 Rust-owned 文档页 / Page Aggregate server-first shell (base=9f6a2c86)
[2026-04-28T09:20:22Z] [SESSION-9] CHECKPOINT [task-022] step=1/1 "Rust-owned document shell 与 Page Aggregate snapshot contract 已落地；目标验证通过。"
[2026-04-28T09:20:22Z] [SESSION-9] Completed [task-022] (commit skipped by repo rule; validated by document_shell/page_aggregate tests, document-content/page-aggregate-builder vitest, and task115 smoke)
[2026-04-28T09:20:22Z] [SESSION-9] Starting [task-023] Phase W-4：把主 API transport 从 Next route 收薄为 Rust Web owned (base=9f6a2c86)
[2026-04-28T09:26:14Z] [SESSION-9] CHECKPOINT [task-023] step=1/1 "Rust Web documents/tree/search transport owner 口径已落地；目标验证通过，page_aggregate 过滤词命中 0 项已记录为配置缺口。"
[2026-04-28T09:26:14Z] [SESSION-9] Completed [task-023] (commit skipped by repo rule; validated by mnote-web documents_api/tree_commands/search tests, bridge-runtime tree_subtree_move_command tests, and frontend route/search tests)
[2026-04-28T09:26:14Z] [SESSION-9] Starting [task-024] Phase W-5：把 tree realtime stream 固定为 Rust Web 主链 (base=9f6a2c86)
[2026-04-28T12:33:41Z] [SESSION-10] CHECKPOINT [task-024] step=1/1 "Rust Web /api/tree/events owner、snapshot/delta stream、Next /api/mnote-web/stream compat alias 与 consumer URL cutover 已通过验证；task112 legacy renderer crash 仅在 Target crashed 时新 context 恢复。"
[2026-04-28T12:33:41Z] [SESSION-10] Completed [task-024] (commit skipped by repo rule; validated by mnote-web tree_realtime/stream tests, frontend stream/documents route tests, and task112 smoke)
[2026-04-28T12:33:41Z] [SESSION-10] NEXT task-025 Phase W-6：把 Search / AI / Mindmap 页面壳迁到 Rust Web shell + islands
[2026-04-28T12:34:53Z] [SESSION-10] Starting [task-025] Phase W-6：把 Search / AI / Mindmap 页面壳迁到 Rust Web shell + islands (base=9f6a2c86)
[2026-04-28T12:34:53Z] [SESSION-10] CHECKPOINT [task-025] step=0/4 "初始 Rust 目标过滤词 search_shell/ai_bridge/mindmap_shell 命中 0 tests；先补 owner/contract 门禁。"
[2026-04-28T12:44:04Z] [SESSION-10] CHECKPOINT [task-025] step=1/1 "Search / AI / Mindmap shell owner 已迁到 Rust Web；Hermes contract 固定 session/tool/client action owner；React runtime 标记为 island 或 legacy compat。"
[2026-04-28T12:44:04Z] [SESSION-10] Completed [task-025] (commit skipped by repo rule; validated by mnote-web search_shell/ai_bridge/mindmap_shell tests, frontend AI/search target tests, task116 smoke, and git diff --check)
[2026-04-28T12:44:04Z] [SESSION-10] NEXT task-026 Phase W-7：退役 Next App Router 主入口并完成 03-rust-web 文档收口
[2026-04-28T12:50:07Z] [SESSION-10] Starting [task-026] Phase W-7：退役 Next App Router 主入口并完成 03-rust-web 文档收口 (base=9f6a2c86)
[2026-04-28T12:50:07Z] [SESSION-10] CHECKPOINT [task-026] step=1/1 "task117 证明 SKIP_NEXT_LEGACY=1 核心 shell 可脱离 Next legacy；desktop-hot 跳过 Next legacy 分支、ARCHITECTURE/3-1 口径与 3-5/3-6 done 迁移已完成。"
[2026-04-28T12:50:07Z] [SESSION-10] Completed [task-026] (commit skipped by repo rule; validated by task097/task114/task115/task116/task117 and git diff --check)


[2026-04-28T12:50:07Z] [SESSION-10] STATS tasks_total=26 completed=26 failed=0 pending=0 in_progress=0 blocked=0 attempts_total=26 checkpoints=29


{
  "version": 2,
  "created": "2026-04-26T23:16:13Z",
  "session_config": {
    "concurrency_mode": "exclusive",
    "max_tasks_per_session": 9,
    "max_sessions": 80
  },
  "tasks": [
    {
      "id": "task-001",
      "title": "Phase K-1：定义 tree shell Rust runtime artifact 边界",
      "status": "completed",
      "priority": "P0",
      "depends_on": [],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web renderer_input && cargo test -p mnote-web runtime_artifact && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx",
        "timeout_seconds": 1800
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已在 mnote-web rendererInput 中定义 rust_tree_shell_runtime_artifact_v1，覆盖 rendererInput/projection/expanded/selected/active/focused 输入、DOM patch/intent/command 输出和 focus/keyboard/expand/selection/context/drag/pick 事件边界；3000 inline host 同步输出同名 runtimeArtifact。",
          "timestamp": "2026-04-26T23:22:39Z"
        }
      ],
      "completed_at": "2026-04-26T23:22:39Z"
    },
    {
      "id": "task-002",
      "title": "Phase K-2：建立 page tree Rust-side runtime 状态机",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-001"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web page_renderer && cargo test -p mnote-web page_focus && cargo test -p mnote-web page_runtime && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx && node /mnt/Data1T/mnote/scripts/task112-tree-rust-family-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 page_runtime reducer，组合 focus/expand/open/context-menu/drop feedback/tree.subtree.move dispatch 输出，验证 page_renderer/page_focus/page_runtime、tree-shell iframe/surface vitest 与 task112 smoke 均通过。",


"command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web page_renderer && cargo test -p mnote-web page_focus && cargo test -p mnote-web page_runtime && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx && node /mnt/Data1T/mnote/scripts/task112-tree-rust-family-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 page_runtime reducer，组合 focus/expand/open/context-menu/drop feedback/tree.subtree.move dispatch 输出，验证 page_renderer/page_focus/page_runtime、tree-shell iframe/surface vitest 与 task112 smoke 均通过。",
          "timestamp": "2026-04-26T23:27:43Z"
        }
      ],
      "completed_at": "2026-04-26T23:27:43Z"
    },
    {
      "id": "task-003",
      "title": "Phase K-3：建立 file tree Rust-side runtime 状态机",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-001"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web filetree_renderer && cargo test -p mnote-web filetree_selection && cargo test -p mnote-web filetree_runtime && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/lib/file-tree/shell.test.ts src/lib/tree-stream/tree-delta.test.ts && node /mnt/Data1T/mnote/scripts/task112-tree-rust-family-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 filetree_runtime reducer，覆盖 selection/range/context/normalize、drag row ids、copy/move effect、drop target feedback，以及 doc/index/asset-folder/asset 的 open/context-menu intent；目标测试与 task112 smoke 通过。",
          "timestamp": "2026-04-26T23:32:11Z"
        }
      ],
      "completed_at": "2026-04-26T23:32:11Z"
    },
    {
      "id": "task-004",
      "title": "Phase K-4：建立 picker Rust-side runtime 状态机",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-001"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web picker_renderer && cargo test -p mnote-web picker_state && cargo test -p mnote-web picker_runtime && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/documents/move-embed-picker-dialog.test.tsx src/components/sidebar/tree-shell-iframe-host.test.tsx && node /mnt/Data1T/mnote/scripts/task113-picker-keyboard-regression-smoke.js",
        "timeout_seconds": 2400


"title": "Phase K-4：建立 picker Rust-side runtime 状态机",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-001"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web picker_renderer && cargo test -p mnote-web picker_state && cargo test -p mnote-web picker_runtime && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/documents/move-embed-picker-dialog.test.tsx src/components/sidebar/tree-shell-iframe-host.test.tsx && node /mnt/Data1T/mnote/scripts/task113-picker-keyboard-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 picker_runtime reducer，覆盖 normalize/next/previous/home/end/focus/pick、excluded ids、root pick，以及搜索输入不被键盘运行时抢 DOM 焦点的 focus_dom 输出边界；目标测试与 task113 smoke 通过。",
          "timestamp": "2026-04-26T23:34:59Z"
        }
      ],
      "completed_at": "2026-04-26T23:34:59Z"
    },
    {
      "id": "task-005",
      "title": "Phase K-5：3000 host 调用正式 runtime artifact，并将 mnote-web /tree 固定为 debug/internal",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-002",
        "task-003",
        "task-004"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx && node /mnt/Data1T/mnote/scripts/task112-tree-rust-family-regression-smoke.js && node /mnt/Data1T/mnote/scripts/task113-picker-keyboard-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已固定 3000 inline host 与 mnote-web debug shell 均输出 rust_tree_shell_runtime_artifact_v1，并通过 tree-shell 目标测试、task112 与 task113 证明 3000 主路径继续使用 inline iframe host，/api/tree/shell debug 直连默认禁用。",
          "timestamp": "2026-04-26T23:38:55Z"
        }
      ],
      "completed_at": "2026-04-26T23:38:55Z"
    },
    {
      "id": "task-006",


"checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已固定 3000 inline host 与 mnote-web debug shell 均输出 rust_tree_shell_runtime_artifact_v1，并通过 tree-shell 目标测试、task112 与 task113 证明 3000 主路径继续使用 inline iframe host，/api/tree/shell debug 直连默认禁用。",
          "timestamp": "2026-04-26T23:38:55Z"
        }
      ],
      "completed_at": "2026-04-26T23:38:55Z"
    },
    {
      "id": "task-006",
      "title": "Phase L：将 tree.subtree.move 最终写路径继续下沉 Rust kernel",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-005"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime tree_subtree_move_command && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/app/api/tree/commands/route.test.ts src/lib/documents/tree-command-client.test.ts src/lib/tree-stream/tree-delta.test.ts",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已引入 Rust treeWriteOperation，Convex documents.move 优先执行 Rust write operation，normalizedMove 保留旧 fallback；bridge-runtime move 单测与前端 39 个目标测试通过。",
          "timestamp": "2026-04-26T23:52:52Z"
        }
      ],
      "completed_at": "2026-04-26T23:52:52Z"
    },
    {
      "id": "task-007",
      "title": "Phase M：补齐 block/save-snapshot formal artifact 与 domain-event contract",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-006"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime documents_save_command_plans_include_formal_domain_event_contract && cargo test -p bridge-runtime block_commands_include_formal_domain_event_contract && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/documents/rust-runtime.test.ts src/lib/documents/page-write-command-adapter.test.ts src/lib/blocks/block-command-adapter.test.ts",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {


"max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime documents_save_command_plans_include_formal_domain_event_contract && cargo test -p bridge-runtime block_commands_include_formal_domain_event_contract && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/documents/rust-runtime.test.ts src/lib/documents/page-write-command-adapter.test.ts src/lib/blocks/block-command-adapter.test.ts",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已将 page.body.saved、block.patched、block.moved、block.embedded 切到 formal domainEventPlan payload schema；复合命令 streamDelta 使用 resync_required 保守策略，command log/domain event 通过同一 command_id 关联；目标验证通过。",
          "timestamp": "2026-04-27T00:09:12Z"
        }
      ],
      "completed_at": "2026-04-27T00:09:12Z"
    },
    {
      "id": "task-008",
      "title": "Phase N：继续收薄 3000 Next route 过渡职责",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-007"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/app/api/tree/commands/route.test.ts src/lib/tree-stream/tree-delta.test.ts && node /mnt/Data1T/mnote/scripts/task112-tree-rust-family-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 2,
          "total": 2,
          "description": "Phase N route thinning manifest 与 task112 iframe-ready smoke 同步已完成；刷新本地 Convex functions 后 move treeWriteOperation 运行态验证通过，目标 Vitest 与 task112 smoke 均通过。",
          "timestamp": "2026-04-27T00:34:43Z"
        }
      ],
      "completed_at": "2026-04-27T00:34:43Z"
    },
    {
      "id": "task-009",
      "title": "Phase O：搜索语义 hardening 与资源类型 fixture 补齐",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-008"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",


},
    {
      "id": "task-009",
      "title": "Phase O：搜索语义 hardening 与资源类型 fixture 补齐",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-008"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "6d89f5ca",
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/tree-stream/tree-delta.test.ts",
        "timeout_seconds": 1800
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已补 file_tree 搜索 hardening 前端契约：projection-client 固定 maxResults=80、祖先补全/排序/异步可见性元数据，tree-delta fixture 覆盖 index、asset-folder、mindmap child、book、pdf；目标验证与 task112 smoke 通过。",
          "timestamp": "2026-04-27T00:41:58Z"
        }
      ],
      "completed_at": "2026-04-27T00:41:58Z"
    },
    {
      "id": "task-010",
      "title": "Phase P-0：补 final DOM shell 负向门禁，阻止 iframe_srcdoc 被误判为完成",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-009"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",
      "scope": [
        "wolai-frontend/src/components/sidebar/tree-shell-host*.test.tsx",
        "wolai-frontend/src/components/sidebar/tree-shell-iframe-host.test.tsx",
        "scripts/task112-tree-rust-family-regression-smoke.js",
        "scripts/task113-picker-keyboard-regression-smoke.js"
      ],
      "acceptance": [
        "默认 page/filetree/picker 主路径测试不得再把 srcDoc inline DOM shell 作为成功条件",
        "默认主路径测试不得出现 data-tree-browser-bridge=\"iframe_srcdoc\" 正向断言",
        "旧 TreeShellIframeHost 的 srcdoc 合同测试必须迁入 legacy/debug 分组或显式标注为 legacy",
        "task112/task113 smoke 需要验证新 Rust/WASM DOM shell 主路径，而不是证明旧 inline iframe host 继续可用"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js && node scripts/task113-picker-keyboard-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null


"acceptance": [
        "默认 page/filetree/picker 主路径测试不得再把 srcDoc inline DOM shell 作为成功条件",
        "默认主路径测试不得出现 data-tree-browser-bridge=\"iframe_srcdoc\" 正向断言",
        "旧 TreeShellIframeHost 的 srcdoc 合同测试必须迁入 legacy/debug 分组或显式标注为 legacy",
        "task112/task113 smoke 需要验证新 Rust/WASM DOM shell 主路径，而不是证明旧 inline iframe host 继续可用"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js && node scripts/task113-picker-keyboard-regression-smoke.js",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 4,
          "description": "已新增默认非 iframe rust_wasm_dom_shell_host 与共享 DOM projection model，legacy iframe 改为显式环境开关。",
          "timestamp": "2026-04-28T03:28:16.005752Z"
        },
        {
          "step": 2,
          "total": 4,
          "description": "tree-shell-surface 与 legacy iframe host 目标 Vitest 通过；默认主路径断言已切成 rust_wasm_dom_shell_host 且负向排除 iframe_srcdoc。",
          "timestamp": "2026-04-28T03:34:10.533972Z"
        },
        {
          "step": 3,
          "total": 4,
          "description": "move/embed picker 测试已切到 DOM shell；默认 picker 不再以 iframe postMessage 为成功条件，目标前端 Vitest 29 项通过。",
          "timestamp": "2026-04-28T03:36:24.605315Z"
        },
        {
          "step": 4,
          "total": 4,
          "description": "已补默认 DOM shell 负向门禁：tree-shell-host/surface/picker 测试与 smoke 默认拒绝 iframe_srcdoc 成功路径。",
          "timestamp": "2026-04-28T04:40:14Z"
        }
      ],
      "completed_at": "2026-04-28T04:40:14Z"
    },
    {
      "id": "task-011",
      "title": "Phase P-1：拆分默认 Rust DOM shell host 与 legacy TreeShellIframeHost",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-010"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",
      "scope": [
        "wolai-frontend/src/components/sidebar/tree-shell-host.tsx",
        "wolai-frontend/src/components/sidebar/tree-shell-iframe-host.tsx",
        "wolai-frontend/src/components/sidebar/tree-shell-*.test.tsx"
      ],
      "acceptance": [


"task-010"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",
      "scope": [
        "wolai-frontend/src/components/sidebar/tree-shell-host.tsx",
        "wolai-frontend/src/components/sidebar/tree-shell-iframe-host.tsx",
        "wolai-frontend/src/components/sidebar/tree-shell-*.test.tsx"
      ],
      "acceptance": [
        "TreeShellHost 默认选择 rust_wasm_dom_shell_host 或等价新 host",
        "TreeShellIframeHost 改为 LegacyTreeShellIframeHost 或显式 legacy/debug host",
        "旧 iframe srcdoc host 只能通过 debug/legacy flag 进入",
        "默认 page tree 首屏不依赖 LOCAL_TREE_SHELL_TEMPLATE"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx src/components/sidebar/tree-shell-iframe-host.test.tsx",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "TreeShellHost 默认选择 rust_wasm_dom_shell_host；TreeShellIframeHost 仅由 NEXT_PUBLIC_TREE_SHELL_LEGACY_IFRAME_HOST=1 进入。",
          "timestamp": "2026-04-28T04:40:14Z"
        }
      ],
      "completed_at": "2026-04-28T04:40:14Z"
    },
    {
      "id": "task-012",
      "title": "Phase Q：page / picker / filetree 迁入 Rust/WASM DOM shell 主路径",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-011"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",
      "scope": [
        "rust/crates/tree-shell-runtime-wasm",
        "rust/crates/mnote-web/src/tree_shell",
        "wolai-frontend/src/components/sidebar"
      ],
      "acceptance": [
        "page tree 默认主路径由新 Rust/WASM DOM shell 渲染与绑定事件",
        "picker 默认主路径由新 Rust/WASM DOM shell 渲染与绑定事件",
        "filetree 默认主路径由新 Rust/WASM DOM shell 渲染与绑定事件，并保留 doc/index/asset-folder/asset 能力",
        "3000 host 只保留挂载、auth、transport、文件读取、postMessage/command bridge"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web tree_shell && cargo test -p tree-shell-runtime-wasm -- --nocapture && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx src/components/documents/move-embed-picker-dialog.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js && node scripts/task113-picker-keyboard-regression-smoke.js",


"rust/crates/tree-shell-runtime-wasm",
        "rust/crates/mnote-web/src/tree_shell",
        "wolai-frontend/src/components/sidebar"
      ],
      "acceptance": [
        "page tree 默认主路径由新 Rust/WASM DOM shell 渲染与绑定事件",
        "picker 默认主路径由新 Rust/WASM DOM shell 渲染与绑定事件",
        "filetree 默认主路径由新 Rust/WASM DOM shell 渲染与绑定事件，并保留 doc/index/asset-folder/asset 能力",
        "3000 host 只保留挂载、auth、transport、文件读取、postMessage/command bridge"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web tree_shell && cargo test -p tree-shell-runtime-wasm -- --nocapture && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx src/components/documents/move-embed-picker-dialog.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js && node scripts/task113-picker-keyboard-regression-smoke.js",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "page / filetree / picker 默认 DOM host 已消费 TreeShellRuntimeRequest/Result，并由 WASM artifact 或同源 Rust seam 驱动 hostEvents/commandEvents。",
          "timestamp": "2026-04-28T04:40:14Z"
        }
      ],
      "completed_at": "2026-04-28T04:40:14Z"
    },
    {
      "id": "task-013",
      "title": "Phase R/S：删除默认 JS DOM state machine 并完成文档收口",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-012"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/process/4-18-tree-final-dom-shell-cutover-hard-gate-v1.md",
      "scope": [
        "wolai-frontend/src/components/sidebar",
        "design/04-tree-domain/process",
        "design/04-tree-domain/done"
      ],
      "acceptance": [
        "默认主路径不再调用 LOCAL_TREE_SHELL_TEMPLATE、buildInlinePageTreeHtml、buildInlineFileTreeHtml、buildInlinePickerHtml",
        "默认主路径不再标注 data-tree-browser-bridge=\"iframe_srcdoc\"",
        "旧 JS fallback reducer/state machine 只在 legacy/debug host 中存在",
        "4-11、4-16、4-18 的 final DOM shell gate 按真实代码完成状态更新"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx src/components/documents/move-embed-picker-dialog.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js && node scripts/task113-picker-keyboard-regression-smoke.js && git diff --check -- wolai-frontend/src/components/sidebar design/04-tree-domain",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [


"旧 JS fallback reducer/state machine 只在 legacy/debug host 中存在",
        "4-11、4-16、4-18 的 final DOM shell gate 按真实代码完成状态更新"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/sidebar/tree-shell-iframe-host.test.tsx src/components/sidebar/tree-shell-surface.test.tsx src/components/documents/move-embed-picker-dialog.test.tsx && cd /mnt/Data1T/mnote && node scripts/task112-tree-rust-family-regression-smoke.js && node scripts/task113-picker-keyboard-regression-smoke.js && git diff --check -- wolai-frontend/src/components/sidebar design/04-tree-domain",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已清理 1.md、4-11、4-16、4-18 的 final DOM shell 口径；旧 JS renderer/state machine 只作为 legacy iframe host 保留。",
          "timestamp": "2026-04-28T04:40:14Z"
        }
      ],
      "completed_at": "2026-04-28T04:40:14Z"
    },
    {
      "id": "task-014",
      "title": "Phase L2：删除 normalizedMove fallback，强制 move 主写路径使用 treeWriteOperation",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-013"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
      "scope": [
        "rust/crates/bridge-runtime/src/lib.rs",
        "wolai-frontend/convex/documents.ts",
        "wolai-frontend/convex/_utils/documentMoveOrder.ts",
        "wolai-frontend/src/lib/documents/rust-runtime.ts",
        "wolai-frontend/src/lib/documents/*move*test.ts",
        "wolai-frontend/src/lib/documents/rust-runtime.test.ts"
      ],
      "acceptance": [
        "bridge-runtime 不再输出 normalizedMove 作为 documents.move 参数",
        "Convex documents.move 不再接受 normalizedMove fallback；缺少 treeWriteOperation 时显式拒绝",
        "TS Rust mutation transport 不再透传 normalizedMove",
        "相关测试改为验证 treeWriteOperation 是唯一主写入计划"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime tree_subtree_move_command && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/documents/document-move-order.test.ts src/lib/documents/document-move-order-convex.test.ts src/lib/documents/rust-runtime.test.ts src/app/api/tree/commands/route.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已删除 normalizedMove fallback：Rust plan 不再输出 normalizedMove，TS transport 强制 treeWriteOperation，Convex documents.move 只接受 treeWriteOperation；目标 Rust/Vitest 通过。",


"command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime tree_subtree_move_command && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/documents/document-move-order.test.ts src/lib/documents/document-move-order-convex.test.ts src/lib/documents/rust-runtime.test.ts src/app/api/tree/commands/route.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已删除 normalizedMove fallback：Rust plan 不再输出 normalizedMove，TS transport 强制 treeWriteOperation，Convex documents.move 只接受 treeWriteOperation；目标 Rust/Vitest 通过。",
          "timestamp": "2026-04-28T06:37:40Z"
        }
      ],
      "completed_at": "2026-04-28T06:37:40Z"
    },
    {
      "id": "task-015",
      "title": "Phase M2：定稿 document.snapshot.saved 独立事件并避免与 page.body.saved payload 重叠",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-014"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
      "scope": [
        "rust/crates/bridge-runtime/src/lib.rs",
        "wolai-frontend/src/lib/documents/rust-runtime.ts",
        "wolai-frontend/src/lib/documents/bridge-log.ts",
        "wolai-frontend/src/lib/documents/*test.ts"
      ],
      "acceptance": [
        "page.body.saved 保留正文保存事件语义",
        "snapshot 版本/hash/updatedAt 拆入 document.snapshot.saved 独立 domain event plan 或 artifact plan",
        "SSE 对 snapshot 独立事件有明确 resync 或 noop 策略",
        "测试覆盖两个事件不再 payload 重叠"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime documents_save_command_plans_include_formal_domain_event_contract && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/documents/rust-runtime.test.ts src/lib/documents/bridge-log.test.ts src/lib/documents/page-write-command-adapter.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已将 page.body.saved 与 document.snapshot.saved 拆成 domainEventPlans 双事件；artifact writer 支持 domainEvents 批量落库，page.body.saved payload 不再夹带 snapshot。",
          "timestamp": "2026-04-28T07:44:37Z"
        }
      ],
      "completed_at": "2026-04-28T07:44:37Z"
    },
    {


"error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已将 page.body.saved 与 document.snapshot.saved 拆成 domainEventPlans 双事件；artifact writer 支持 domainEvents 批量落库，page.body.saved payload 不再夹带 snapshot。",
          "timestamp": "2026-04-28T07:44:37Z"
        }
      ],
      "completed_at": "2026-04-28T07:44:37Z"
    },
    {
      "id": "task-016",
      "title": "Phase M3：把 snapshot 独立事件接入 Page Aggregate 深层收口文档与客户端边界",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-015"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
      "scope": [
        "wolai-frontend/src/components/editor/page-aggregate*",
        "wolai-frontend/src/lib/documents/page-aggregate*",
        "design/05-editor-mainline/process",
        "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md"
      ],
      "acceptance": [
        "Page Aggregate 文档明确 page.body.saved 与 document.snapshot.saved 的职责边界",
        "客户端 Page Aggregate 状态不再把 snapshot 保存当作正文保存的附带语义",
        "4-16 对应 snapshot/Page Aggregate 项可勾选或明确剩余迁移边界"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/components/editor/page-aggregate-client-state.test.ts src/lib/documents/page-aggregate-builder.test.ts src/lib/documents/page-write-command-adapter.test.ts && git diff --check -- design/04-tree-domain design/05-editor-mainline wolai-frontend/src/components/editor wolai-frontend/src/lib/documents",
        "timeout_seconds": 2400
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已更新 Page Aggregate contract 与 5-6 清单，固定 page.body.saved 与 document.snapshot.saved 的职责边界，并同步 4-16 勾选状态。",
          "timestamp": "2026-04-28T07:44:37Z"
        }
      ],
      "completed_at": "2026-04-28T07:44:37Z"
    },
    {
      "id": "task-017",
      "title": "Phase N2：将 tree.node.embed 的 pageReference 组装迁到 Page Aggregate / Rust artifact 边界",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-015"
      ],


}
      ],
      "completed_at": "2026-04-28T07:44:37Z"
    },
    {
      "id": "task-017",
      "title": "Phase N2：将 tree.node.embed 的 pageReference 组装迁到 Page Aggregate / Rust artifact 边界",
      "status": "completed",
      "priority": "P1",
      "depends_on": [
        "task-015"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
      "scope": [
        "rust/crates/bridge-runtime/src/lib.rs",
        "wolai-frontend/src/app/api/tree/commands/route.ts",
        "wolai-frontend/src/lib/documents/page-command-adapter.ts",
        "wolai-frontend/src/lib/tree-route-boundary.ts",
        "wolai-frontend/src/app/api/tree/commands/route.test.ts"
      ],
      "acceptance": [
        "3000 route 不再本地决定 pageReference block 的结构与插入位置语义",
        "Rust plan 或 Page Aggregate artifact 接收 source/target/anchor/content snapshot 并产出 next content 或 block patch plan",
        "tree-route-boundary manifest 移除该 compatPending 项",
        "测试覆盖 tree.node.embed 的 pageReference 组装来源已迁移"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime documents_embed_command_plan_maps_to_documents_update_content tree_embed_and_copy_aliases_keep_tree_command_names && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/app/api/tree/commands/route.test.ts src/lib/tree-route-boundary.test.ts src/lib/documents/rust-runtime.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 Rust pageAggregateEmbedPlan；3000 route 与 documents.embed adapter 只传 source/target/anchor/content snapshot preflight，不再本地决定 pageReference block 结构与插入位置。",
          "timestamp": "2026-04-28T07:44:37Z"
        }
      ],
      "completed_at": "2026-04-28T07:44:37Z"
    },
    {
      "id": "task-018",
      "title": "Phase O2：补 file_tree 搜索索引可见性指标边界并收口 4-16",
      "status": "completed",
      "priority": "P2",
      "depends_on": [
        "task-014"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
      "scope": [


"id": "task-018",
      "title": "Phase O2：补 file_tree 搜索索引可见性指标边界并收口 4-16",
      "status": "completed",
      "priority": "P2",
      "depends_on": [
        "task-014"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "81ba21c5",
      "source_doc": "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md",
      "scope": [
        "rust/crates/bridge-runtime/src/lib.rs",
        "rust/crates/mnote-web/src/routes/kernel.rs",
        "wolai-frontend/src/lib/server/kernel-file-tree.ts",
        "wolai-frontend/src/app/api/tree/projections/file/route.ts",
        "design/04-tree-domain/done/4-16-tree-rust-family-remaining-final-runtime-checklist-v1.md"
      ],
      "acceptance": [
        "file_tree 搜索 projection 输出稳定 indexing/visibility meta，不再只是临时 request meta",
        "3000 同源 projection route 透传该 meta",
        "move-embed picker/file tree 搜索可观察 index visibility 状态",
        "4-16 中 Phase O 未完成项完成或明确转为非树域 runtime 后续"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p bridge-runtime file_tree && cargo test -p mnote-web file_tree_projection && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/app/api/tree/projections/file/route.test.ts src/lib/tree-stream/tree-delta.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已把 file_tree 搜索 indexingVisibility 提升为 projection result meta；mnote-web 与 3000 同源 projection route 均透传该指标，4-16 Phase O 已收口。",
          "timestamp": "2026-04-28T07:44:37Z"
        }
      ],
      "completed_at": "2026-04-28T07:44:37Z"
    },
    {
      "id": "task-019",
      "title": "Phase W-0：冻结 Rust Web 主执行面 owner manifest 与负向门禁",
      "status": "completed",
      "priority": "P0",
      "depends_on": [],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "wolai-frontend/src/lib/rust-web-main-execution-boundary.ts",
        "wolai-frontend/src/lib/rust-web-main-execution-boundary.test.ts",
        "ARCHITECTURE.md",
        "design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md"
      ],
      "acceptance": [


"attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "wolai-frontend/src/lib/rust-web-main-execution-boundary.ts",
        "wolai-frontend/src/lib/rust-web-main-execution-boundary.test.ts",
        "ARCHITECTURE.md",
        "design/03-rust-web/process/3-6-rust-web-main-execution-plane-checklist-v1.md"
      ],
      "acceptance": [
        "新增 RUST_WEB_MAIN_EXECUTION_BOUNDARY，固定 publicEntry=3000、mainWebOwner=mnote-web、legacyCompatOwner=next-app-router",
        "测试禁止把 Next App Router 写回主 Web owner",
        "ARCHITECTURE.md 明确树域完成不等于主 Web 执行面完成"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/rust-web-main-execution-boundary.test.ts src/lib/runtime-config.test.ts && cd /mnt/Data1T/mnote && git diff --check -- ARCHITECTURE.md design/03-rust-web wolai-frontend/src/lib",
        "timeout_seconds": 1800
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 Rust Web 主执行面 owner manifest 与负向门禁，并更新 ARCHITECTURE.md / 3-6 checklist 口径。",
          "timestamp": "2026-04-28T09:07:58Z"
        }
      ],
      "completed_at": "2026-04-28T09:07:58Z"
    },
    {
      "id": "task-020",
      "title": "Phase W-1：让 mnote-web 默认成为 3000 gateway owner",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-019"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "rust/crates/mnote-web/src/app.rs",
        "rust/crates/mnote-web/src/routes/mod.rs",
        "rust/crates/mnote-web/src/routes/gateway.rs",
        "scripts/desktop-hot.js",
        "scripts/desktop-hot.test.js",
        "scripts/task114-rust-web-gateway-entry-smoke.js"
      ],
      "acceptance": [
        "mnote-web 支持 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3000 并作为默认 3000 owner",
        "Next dev 默认改为 legacy upstream port，不再拥有 3000",
        "task114 smoke 证明 3000 响应来自 Rust gateway 且 3104 不作为公开入口"
      ],


"rust/crates/mnote-web/src/app.rs",
        "rust/crates/mnote-web/src/routes/mod.rs",
        "rust/crates/mnote-web/src/routes/gateway.rs",
        "scripts/desktop-hot.js",
        "scripts/desktop-hot.test.js",
        "scripts/task114-rust-web-gateway-entry-smoke.js"
      ],
      "acceptance": [
        "mnote-web 支持 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3000 并作为默认 3000 owner",
        "Next dev 默认改为 legacy upstream port，不再拥有 3000",
        "task114 smoke 证明 3000 响应来自 Rust gateway 且 3104 不作为公开入口"
      ],
      "validation": {
        "command": "node --test /mnt/Data1T/mnote/scripts/desktop-hot.test.js && cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web gateway && cd /mnt/Data1T/mnote && node scripts/task114-rust-web-gateway-entry-smoke.js",
        "timeout_seconds": 3600
      },
      "on_failure": {
        "cleanup": null
      },
      "error_log": [],
      "checkpoints": [
        {
          "step": 1,
          "total": 1,
          "description": "已新增 mnote-web gateway route/owner header、desktop-hot Rust gateway 默认启动计划与 task114 smoke。",
          "timestamp": "2026-04-28T09:13:44Z"
        }
      ],
      "completed_at": "2026-04-28T09:13:44Z"
    },
    {
      "id": "task-021",
      "title": "Phase W-2：把 runtime config 与 auth/session handoff 迁到 Rust Web",
      "status": "completed",
      "priority": "P0",
      "depends_on": [
        "task-020"
      ],
      "attempts": 1,
      "max_attempts": 3,
      "started_at_commit": "9f6a2c86",
      "source_doc": "design/03-rust-web/done/3-6-rust-web-main-execution-plane-checklist-v1.md",
      "plan_doc": "design/03-rust-web/done/3-5-rust-web-main-execution-plane-cutover-plan-v1.md",
      "scope": [
        "rust/crates/mnote-web/src/routes/session.rs",
        "rust/crates/mnote-web/src/routes/mod.rs",
        "wolai-frontend/src/lib/runtime-config.ts",
        "wolai-frontend/src/lib/runtime-config.test.ts",
        "wolai-frontend/src/app/api/auth/mnote-web-token/route.ts"
      ],
      "acceptance": [
        "mnote-web 输出 /api/runtime/config、/api/auth/session 与 session refresh/handoff route",
        "浏览器 runtime config 不泄漏 internal MNOTE_WEB_* base URL",
        "Next /api/auth/mnote-web-token 降级为 compat/debug"
      ],
      "validation": {
        "command": "cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web session && cargo test -p mnote-web runtime_config && cd /mnt/Data1T/mnote/wolai-frontend && pnpm vitest run src/lib/runtime-config.test.ts src/app/api/auth/mnote-web-token/route.test.ts",
        "timeout_seconds": 3000
      },
      "on_failure": {