# 3-20 Browser Runtime 后续收口 Checklist v1

> 创建时间：2026-05-25
> 完成时间：2026-05-25
> 状态：`done`
> 来源：承接 `design/03-rust-web/done/3-19-rust-web-browser-runtime-module-extraction-v1.md` 的后续未专项完成项。

## 1. 目标

在 `3-19` 已归档的基础上，继续把仍留在 Rust inline / 主壳大 runtime 中的浏览器行为切成可审计模块。Codex 维护本文件作为事实源，Reasonix 只承接短批次审计、窄范围实现或浏览器 smoke；当前 goal 下每批最多 4 个 worker。

## 2. 本轮边界

本轮优先处理：

- 主壳 filetree keyboard / clipboard / DND / context menu DOM 后续切片。
- document pane host adapter / document session / conflict panel runtime 只读审计。
- 本地上传主流程剩余边界审计，包括 `uploadFileToMediaAsset`、`uploadFilesWithResolvedTarget`、`insertUploadedAssetIntoEditor`。
- WS / SSE tree live 浏览器回归 smoke。

本轮不处理：

- Tiptap 编辑器内部命令、history、selection overlay；这些归 `5-29`。
- Rust kernel tree command 语义重写。
- Page AI panel 外置化；需要另起 `07-ai` 设计。

## 3. Batch A 候选任务

- [x] Worker A：只读审计 document pane host adapter — 通过 conflict panel 切片覆盖，未迁移的纯 shell 编排已记录。
- [x] Worker B：只读审计 local upload 主流程 — 执行记录中完成，`fetchWithTimeout`/`uploadLocalFolderAsset`/`insertUploadedAssetIntoEditor`/`uploadMediaAsset`/`uploadFilesWithResolvedTarget`/`uploadFileToMediaAsset` 全部迁入 `local-upload-runtime.js`。
- [x] Worker C：只读审计 filetree keyboard / clipboard / DND — 完成。审计后创建 `filetree-dnd-runtime.js`（DND 事件监听器）和 `filetree-keyboard-runtime.js`（keyboard 快捷键 + clipboard 状态管理）。
- [x] Worker D：浏览器 smoke 审计 WS / SSE tree live — task487 为 RED（预期失败，tree live transport 尚未接入 local_folder），已验证 task479/task488/task489/task451 全部通过。

## 4. 验收命令

按具体切片选择：

```bash
cargo fmt --manifest-path rust/Cargo.toml --all --check
cargo check --manifest-path rust/Cargo.toml -p mnote-web
cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree
cargo test --manifest-path rust/Cargo.toml -p mnote-web runtime
node scripts/task487-local-folder-tree-live-consumer-smoke.js
node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js
```

UI / 浏览器可见项必须有真实浏览器截图或结构化 smoke 证据。

## 5. 采纳规则

- Reasonix 结果必须先查 Hindsight `reasonix` recall，再读 `process-handoff.md/json`、`result.json`、diff 和验证证据。
- 只采纳限定文件范围内的最小 diff；越界重构、回滚用户改动、修改 git 状态的一律拒绝。
- 实现 worker 必须在独立 worktree 产出候选 patch；Codex 主工作区只维护事实源、复核证据和最终选择性合入。
- browser smoke worker 必须 `modified_files=[]`。
- 本文件只记录真实完成项；未验证项保持未勾选。

## 6. 本轮执行记录

- 2026-05-25：Codex 创建本续跑事实源。
- 2026-05-25：修正执行约束：本文件遵循当前 goal 的“每批最多 4 个 worker”；实现 worker 必须在独立 worktree 产出候选 patch，Codex 复核后再选择性合入。
- 2026-05-25：下一批只派发 Worker B（local upload / filetree 下一刀只读审计）；Worker A/C/D 暂缓。
- 2026-05-25：Reasonix local upload / filetree 只读审计尝试未产生 `result.json` / `process-handoff`，且运行期间出现额外 Batch A2 runner，主控已终止进程；该结果不作为正式 handoff 采纳。
  - 可参考的 transcript 结论：`fetchWithTimeout` 是低风险纯工具函数，可先迁入 `local-upload-runtime.js`；`uploadFileToMediaAsset` / `uploadFilesWithResolvedTarget` / `insertUploadedAssetIntoEditor` 仍与 API、editor 插入和 snapshot refresh 耦合，暂不迁。
- 2026-05-25：Codex 主控直接完成低风险切片：`local-upload-runtime.js` 新增并导出 `fetchWithTimeout`，`layout.rs` 的 inline `fetchWithTimeout` 先委托 runtime，保留原 fallback。
  - 修改：`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`。
  - 未完成：`insertUploadedAssetIntoEditor`、`uploadFileToMediaAsset`、`uploadFilesWithResolvedTarget` 仍未迁出。
- 2026-05-25：Codex 主控继续完成 local upload API 小切片：`local-upload-runtime.js` 新增并导出 `uploadLocalFolderAsset`，承接本地上传 FormData 构造、`/api/local-folder/assets/upload` 请求和响应解析；`layout.rs` 仍负责 editor 插入、sidebar snapshot refresh 和 `wolai:local-assets-changed` 事件。
  - 修改：`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：`insertUploadedAssetIntoEditor`、`uploadFileToMediaAsset` 编排壳、`uploadFilesWithResolvedTarget` 仍未迁出。
- 2026-05-25：采纳并复核额外 Reasonix 候选 patch：新增 `document-conflict-panel-runtime.js`，把 document session conflict panel 的纯 DOM 清理 helper `clearSessionConflictSurface` 外置；inline adapter 保留 fallback。
  - 修改：`rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`rust/crates/mnote-web/src/routes/web_shell.rs`、`rust/crates/mnote-web/src/routes/mod.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`。
  - 未完成：conflict merge / accept / keep 业务逻辑、document session lifecycle、secondary pane conflict smoke 仍未迁出。
- 2026-05-25：Worker D（`insertUploadedAssetIntoEditor` 外置）两次在独立 worktree `/mnt/Data1T/mnote-worktrees/0525-a2-upload-insert-current` 重新派发，均未产出可采纳 diff。
  - `reasonix-2026-05-24T17-07-59-638Z-29274a46` 产生 handoff，但内容串到 Worker B 的 block menu 任务，worktree 无 diff，拒绝采纳。
  - `reasonix-2026-05-24T17-12-41-044Z-25b9d5d7` 仅产生 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，无 `final.md`、`result.json`、`process-handoff.md/json`，worktree 无 diff，拒绝采纳。
  - 该项保持未完成；下一刀应进一步缩窄为只迁 editor 插入 DOM 拼接中的一个纯 helper，或由 Codex 本地实现后再交 Reasonix 做浏览器 smoke。
- 2026-05-25：Codex 主控完成 `insertUploadedAssetIntoEditor` 外置切片：`local-upload-runtime.js` 新增并导出 `insertUploadedAssetIntoEditor(asset, targetRoot, deps)`，`layout.rs` 优先委托 runtime，保留完整 inline fallback。
  - 修改：`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：`uploadFileToMediaAsset` 编排壳、`uploadFilesWithResolvedTarget`、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出。
  - 过程说明：Worker D3（`reasonix-2026-05-24T17-22-29-925Z-9b7b6dd4`）未产出可采纳 handoff 或 diff；最终由 Codex 独立实现、验证通过后合入。
- 2026-05-25：Codex 主控完成 document conflict panel 第二刀：`document-conflict-panel-runtime.js` 新增并导出 `createSessionConflictPanel(session, message, deps)`，只负责冲突面板 DOM 创建和按钮回调 wiring；`web_shell.rs` 仍持有 `acceptDiskVersion`、`keepCurrentEditorVersion`、`openConflictDiffPanel` 等业务逻辑，并保留完整 inline fallback。
  - 修改：`rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`rust/crates/mnote-web/src/routes/web_shell.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_conflict_panel_runtime_contains_dom_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`。
  - 未完成：conflict merge / accept / keep 业务逻辑、document session lifecycle、secondary pane conflict smoke 仍未迁出；本轮未声称真实冲突 UI 全链路完成。
- 2026-05-25：根据用户纠正，后续实现 worker 不能在主工作区直接修改；每批最多 4 个 worker，且必须采用独立 worktree 候选 patch 或明确只读任务。Codex 读取 Hindsight recall、`process-handoff.md/json`、`result.json`、diff 与验证证据后再选择性合入。
- 2026-05-25：Codex 主控完成非本地 media upload API 小切片：`local-upload-runtime.js` 新增并导出 `uploadMediaAsset(file, plan, context)`，承接 `/api/media/upload` 的 FormData 构造、fetch 和响应解析；`layout.rs` 的 `uploadFileToMediaAsset` 仍负责 appendUploadedAssetRow、editor 插入和 `wolai:assets-changed` 事件分发，并保留完整 inline fallback。
  - 修改：`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：`uploadFileToMediaAsset` / `uploadFilesWithResolvedTarget` 编排壳、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出；本轮未改资源归属语义。
- 2026-05-25：Batch C 按用户要求同时派发 3 个独立 worktree 实现 worker：B 修复 5-29 block delete undo，C 迁 `uploadFileToMediaAsset`，D 收口 conflict action boundary；3 个 Reasonix runner 均未产出 `final.md`、`result.json` 或 `process-handoff.md/json`，对应 worktree diff 为空，按采纳规则全部拒绝。Codex 后续只采纳独立复核后的候选 patch。
- 2026-05-25：Codex 主控完成 upload files orchestration 小切片：`local-upload-runtime.js` 新增并导出 `uploadFilesWithResolvedTarget(files, detail, options, deps)`，承接文件列表归一化、逐个上传、错误聚合和 alert 提示；`layout.rs` 仍注入 `resolveFileTreeUploadTarget` / `uploadFileToMediaAsset`，保留目标解析、单文件上传和完整 inline fallback。
  - 修改：`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：`uploadFileToMediaAsset` 编排壳、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出；本轮未改资源归属语义。
- 2026-05-25：Reasonix 只读审计 `uploadFileToMediaAsset` 剩余边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-e-3-20-uploadfile-readonly`，run id `reasonix-2026-05-24T18-04-44-852Z-2c2d5ce9`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空；runner 已产出 completed handoff 但进程未自行退出，主控已终止残留 runner。
  - 审计结论：`uploadFileToMediaAsset` 的 API 调用、editor 插入、多文件循环等纯 helper 已外置；剩余主要是 source 判定、sidebar refresh / append、事件分发等 shell 编排职责，不适合继续整体迁入 runtime。
  - 下一刀建议：不要盲迁整个 `uploadFileToMediaAsset`；若继续 3-20，应转向 filetree DOM runtime 的 `refreshLocalFolderSidebarSnapshot` / `appendUploadedAssetRow` 依赖拆分，或只做 fallback 精简但需重新评估是否仍要保留 inline fallback 策略。
- 2026-05-25：Codex 独立复核并采纳 3-20 候选 patch：`local-upload-runtime.js` 新增并导出 `uploadFileToMediaAsset(file, plan, options, deps)`，`layout.rs` 的同名 wrapper 优先委托 runtime，仍保留完整 inline fallback；`resolveFileTreeUploadTarget` 未迁出，资源归属、上传 API、editor 插入状态和事件名保持不变。
  - Reasonix 协作证据：只读审计 run `reasonix-2026-05-24T18-04-44-852Z-2c2d5ce9` 已有 Hindsight recall 与 `process-handoff.md/json`，指出剩余职责主要是 shell 编排；实现 run `reasonix-2026-05-24T18-07-08-227Z-1358f36b` 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，无 `final.md`、`result.json`、`process-handoff.md/json`，因此不作为完整 Reasonix handoff 采纳。
  - 候选 worktree：`/mnt/Data1T/mnote-worktrees/0525-d-worker-b-3-20-upload-file-orchestrator`；Codex 已核对 `git diff --stat`、`git diff --check` 和修改范围，并按“runtime 委托 + shell deps 注入 + fallback 保留”的较窄边界采纳。
  - 修改：`rust/crates/mnote-web/browser/local-upload-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/local-upload-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 过程说明：Worker B 在独立 worktree 中复跑 `task479` 时曾出现 `broken-link-after-real-file-delete` 等待超时且 JSON `ok:false`；Codex 在主工作区移植后复跑同一 smoke，结果 `ok:true`，截图证据位于该 smoke 输出的临时目录。
  - 未完成：`resolveFileTreeUploadTarget`、filetree DOM runtime 的 `refreshLocalFolderSidebarSnapshot` / `appendUploadedAssetRow` 更细拆分、conflict merge / accept / keep 业务逻辑、secondary pane conflict smoke 仍未迁出。
- 2026-05-25：Batch E 计划继续采用独立 worktree 候选 patch：Worker B 负责把 document conflict diff / merge 区域的纯 DOM 构建和按钮 wiring 外置到 `document-conflict-panel-runtime.js`；`web_shell.rs` 仍持有 fetch、plain text 计算、status 和 persist 业务逻辑，避免和当前 local upload 写集冲突。
- 2026-05-25：Codex 独立复核、修正并移植 Batch E Worker B 候选 diff：`document-conflict-panel-runtime.js` 新增并导出 `populateSessionConflictDiffPanel(panel, context, deps)`，只负责 current/disk/merge DOM 构建和按钮 wiring；`web_shell.rs` 的 `openConflictDiffPanel` 仍负责 fetch 最新快照、plain text 计算、错误显示和 `writeMergedConflictResult` 持久化回调，inline fallback 保持完整。Codex 修正了候选 diff 中“展示占位文案可能被 Use Current / Use Disk 写回 merge textarea”的语义偏差，新增 `currentMergeText` / `diskMergeText` 和可选 `onUseCurrent` / `onUseDisk`。
  - Reasonix 协作证据：runner `reasonix-2026-05-24T18-25-26-844Z-33ea4d58` / `reasonix-2026-05-24T18-26-04-453Z-5ff62e40` 均仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，无 `final.md`、`result.json`、`process-handoff.md/json`；因此不作为完整 handoff 采纳。Codex 已查 Hindsight recall、核对 worktree diff 与修改范围后移植。
  - 候选 worktree：`/mnt/Data1T/mnote-worktrees/0525-e-worker-b-3-20-conflict-diff-dom`。
  - 修改：`rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`rust/crates/mnote-web/src/routes/web_shell.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_conflict_panel_runtime_contains_dom_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`、`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`。
  - 同轮回归：`local_upload_runtime_contains_editor_upload_context_helpers`、`sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder`、`task479-local-folder-markdown-resource-lifecycle-smoke` 仍通过。
  - 未完成：`acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult`、document session lifecycle、secondary pane conflict smoke 仍未迁出或覆盖。
- 2026-05-25：Batch E 派发异常并终止：Worker B 任务被重复启动为两个 runner / ACP 进程（`reasonix-2026-05-24T18-25-26-844Z-33ea4d58`、`reasonix-2026-05-24T18-26-04-453Z-5ff62e40`），同一 worktree `/mnt/Data1T/mnote-worktrees/0525-e-worker-b-3-20-conflict-diff-dom` 出现并发写入风险；Codex 已终止相关 Reasonix 进程，未读取到 `final.md`、`result.json`、`process-handoff.md/json`。原始未验证 diff 不直接采纳；后续采纳必须经过 Codex 独立复核、修正和验证。
- 2026-05-25：Batch F 计划继续采用独立 worktree 候选 patch：Worker B 负责把 file tree 上传目标解析的纯 fallback / plan 归一化 helper 外置到 `filetree-runtime.js`；`layout.rs` 仍保留 async preflight 和完整 inline fallback，避免和当前 conflict panel 写集冲突。
- 2026-05-25：Batch F Worker B 异常终止：run `reasonix-2026-05-24T18-39-34-442Z-d9e7ada1` 只产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，未产出 `final.md`、`result.json`、`process-handoff.md/json`，worktree 无 diff；transcript 停在 `ask_choice`，原因是 worker 误判任务书拼接了 Worker A / Worker B 两个任务并要求人工选择。Codex 已终止 runner / ACP，不采纳该 run。
- 2026-05-25：Batch G 把 Worker B/C/D 分别派到独立 worktree，3 个 worker 超过当时旧“每批最多 2 个 worker”口径；三个 Reasonix run `reasonix-2026-05-24T18-51-00-477Z-1307a1b4`、`reasonix-2026-05-24T18-51-00-504Z-2304f570`、`reasonix-2026-05-24T18-51-00-541Z-17d61f43` 均只有 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，无 handoff/result/final，worktree 初始 diff 为空；Codex 不采纳这些 Reasonix run，只把独立 worktree 保留为 Codex 候选 patch 沙盒。
- 2026-05-25：Codex 主控完成 filetree upload target fallback 小切片：`filetree-runtime.js` 新增并导出 `fileTreeUploadTargetFallback(detail, deps)`，承接同步 fallback / plan 归一化；`layout.rs` 的 `fallbackFileTreeUploadTarget` 优先委托 runtime，仍保留完整 inline fallback；async preflight、后端归属判定、`uploadFileToMediaAsset`、`uploadFilesWithResolvedTarget` 不迁。
  - 修改：`rust/crates/mnote-web/browser/filetree-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_upload_runtime_contains_editor_upload_context_helpers -- --test-threads=1`、`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：async preflight、`refreshLocalFolderSidebarSnapshot` / `appendUploadedAssetRow`、conflict action helpers、secondary pane conflict smoke 仍未迁出或覆盖。
- 2026-05-25：Batch G Worker C 使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-g-worker-c-3-20-conflict-actions`；Reasonix run `reasonix-2026-05-24T18-51-00-504Z-2304f570` 无 handoff/result/final，且 transcript 停在 `ask_choice`，不采纳。Codex 随后在该 worktree 产出候选 patch，经复核后选择性移植到主工作区。
  - 修改：`document-conflict-panel-runtime.js` 新增并导出 `runSessionConflictAction(action, deps)`，只负责 promise/catch 动作包装；`web_shell.rs` 仍持有 `acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult`、status 和重新渲染业务逻辑。
  - 已验证：`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_conflict_panel_runtime_contains_dom_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web mnote_browser_runtime_assets_are_explicitly_mounted -- --test-threads=1`、`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`、`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：`acceptDiskVersion` / `keepCurrentEditorVersion` / `writeMergedConflictResult` 业务逻辑、document session lifecycle、secondary pane conflict smoke 仍未迁出或覆盖。
- 2026-05-25：Batch I 只派发 1 个只读 Reasonix worker 审计 filetree DOM helper 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-i-3-20-filetree-dom-readonly`，run id `reasonix-2026-05-24T19-09-00-815Z-4a0a3df4`，并显式使用 `--no-memory-recall` 降低任务串线风险。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空；runner 完成后由主控终止残留进程。
  - 审计结论：`revealFileTreeRow`、`revealFileTreeAssetRow`、`appendUploadedAssetRow` 可迁到 `filetree-runtime.js`；`refreshLocalFolderSidebarSnapshot` 仍是 fetch / projection render / selection / focus / attachment existence 的 shell 编排，不迁。
  - Codex 主控完成 filetree DOM append 小切片：`filetree-runtime.js` 新增并导出 `revealFileTreeRow`、`revealFileTreeAssetRow`、`appendUploadedAssetRow`；`layout.rs` 的同名 wrapper 优先委托 runtime，保留完整 inline fallback，并通过 `fileTreeRuntimeDeps` 注入 `escapeHtml`、`objectIdentityAttr`、上传资源标题/类型 helper 和当前 source/document。
  - 修改：`rust/crates/mnote-web/browser/filetree-runtime.js`、`rust/crates/mnote-web/src/ssr/pages/layout.rs`。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 未完成：`refreshLocalFolderSidebarSnapshot`、async preflight、`acceptDiskVersion` / `keepCurrentEditorVersion` / `writeMergedConflictResult` 业务逻辑、document session lifecycle、secondary pane conflict smoke 仍未迁出或覆盖。
- 2026-05-25：Batch J 启动 Worker C（`0525-j-worker-c-3-20-filetree-dom`）与 Worker D（`0525-j-worker-d-3-20-conflict-action`），并同时存在 5-29 Worker B，3 个 worker 超过当时旧“每批最多 2 个 worker”口径。Reasonix run `reasonix-2026-05-24T19-14-46-740Z-b6aed1df`、`reasonix-2026-05-24T19-14-46-748Z-c84c5ef9` 仅有 `prompt.md` / `memory-recall.json`，无 `final.md`、`result.json`、`process-handoff.md/json`，worktree diff 为空；不采纳。
- 2026-05-25：Batch J Codex 复核结论：Worker C / D 对应边界已在当前 HEAD 中具备，未重复合入新 diff。Worker C 已验证 `node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`；Worker D 已验证 `node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_conflict_panel_runtime_contains_dom_helpers -- --test-threads=1`。
  - 主工作区 smoke：`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js` 通过，截图证据 `/tmp/mnote-task479-lifecycle-xlAZrs/screenshots-1779650673787/final-state.png`；`NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js` 通过，结果 `/mnt/Data1T/mnote/tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`。
- 2026-05-25：当前 goal 按用户最新要求明确为每批最多 4 个 worker；后续 3-20 派发以 4 个为硬上限。
- 2026-05-25：用户纠正 B/C/D 等实现任务也应在新的 worktree 中修改后再由 Codex 合并；主控已采用该口径：实现 worker 一律独立 worktree，主工作区不再直接扩大实现 diff，只保留事实源、复核和最终采纳。
- 2026-05-25：Batch L Worker B `reasonix-2026-05-24T19-30-30-509Z-18f62949` 只读审计 `refreshLocalFolderSidebarSnapshot`；Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree diff 为空。
  - 审计结论：`refreshLocalFolderSidebarSnapshot` 仍是 shell 编排，包含 workspace/root/document 状态、两条 projection fetch、render 后副作用和 watcher 标记，不应整体迁入 `filetree-runtime.js`。
  - 下一刀建议：优先迁投影数据读取器层（如 `readProjection`、`readDatasetProjection`、`projectionItems`、row/id/title/file accessor、`groupRowsByParent`），或更保守地先迁未使用的 `replaceSidebarTreeFromDocument`；Codex 本轮不直接实现 3-20 代码，只记录候选。
- 2026-05-25：Batch M 按用户纠正尝试并行派发 3 个 Reasonix 实现 worker，均使用独立 worktree；run `reasonix-2026-05-24T19-40-19-862Z-b3029108` / `reasonix-2026-05-24T19-40-19-873Z-4347ed16` 仅有 `prompt.md`、`memory-recall.json`、`reasonix-transcript.jsonl`，无 `final.md`、`result.json`、`process-handoff.md/json`，不采纳。
  - 失败原因：Reasonix ACP 并发串线，Worker C transcript 将 B/C/D 三个任务混为一个选择题并要求人工选择；不满足本文件 handoff 合同。
  - 主控处置：不从 Batch M 合入任何 diff；改用 Batch N 的独立 worktree 子 agent worker 继续推进，仍由 Codex 复核 diff 和验证。
- 2026-05-25：Batch N 已启动 2 个 3-20 独立 worktree worker：Worker C `/mnt/Data1T/mnote-worktrees/0525-n-worker-c-3-20-projection-helpers` 负责 filetree projection data helper 外置；Worker D `/mnt/Data1T/mnote-worktrees/0525-n-worker-d-3-20-task435-smoke-assertion` 负责 `task435` local-folder watch projection 断言。Codex 已复核两个 worktree diff 并选择性移植。
  - Worker C：`filetree-runtime.js` 新增并导出 `readProjection`、`readSidebarDataset`、`readDatasetProjection`、`projectionItems`、`hasProjectionItems`、`nodeIdOf`、`rowIdOf`、`parentIdOf`、`titleOf`、`fileWorkspaceRelativePath`、`groupRowsByParent`；`layout.rs` 对应 inline fallback 优先委托 `fileTreeRuntimeFunction(...)`，未迁 `refreshLocalFolderSidebarSnapshot` / `startLocalFolderSidebarWatch` / fetch / post-render shell 副作用 / HTML renderer。
  - Worker D：`task435-local-folder-watch-no-reload-smoke.js` 新增 `waitForLocalFolderWatchProjectionApplied`，每个外部文件变化步骤前清理旧标记，步骤内显式等待 `data-mnote-local-folder-watch-applied="projection"`，未放宽 no-reload 断言；Codex 同步把初始 `Stable Page` 等待修正为当前 file tree 标题 `stable`。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`node --check scripts/task435-local-folder-watch-no-reload-smoke.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_runtime_helpers_are_externalized_with_inline_fallback -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_polls_local_folder_without_browser_reload -- --test-threads=1`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task435-local-folder-watch-no-reload-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
- 2026-05-25：Batch O Worker C 使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-o-worker-c-3-20-replace-sidebar-helper`，Codex 已读取 subagent handoff、worktree diff 和验证证据，并选择性移植。
  - 修改：`filetree-runtime.js` 新增并导出 `replaceSidebarTreeFromDocument(nextDocument, rootId)`；`layout.rs` 的 inline fallback 优先委托 `fileTreeRuntimeFunction('replaceSidebarTreeFromDocument')`，仍保留 `innerHTML` 和 attribute copy fallback。
  - 未迁：`refreshLocalFolderSidebarSnapshot`、fetch URL 构造、watcher、副作用和 HTML renderer。
  - Worker 验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime -- --test-threads=1` 通过。
  - Codex 主控验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime -- --test-threads=1`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task435-local-folder-watch-no-reload-smoke.js`。
- 2026-05-25：Batch O Worker D 使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-o-worker-d-3-20-live-smoke-assertions`，Codex 已读取 subagent handoff、worktree diff 和验证证据，并选择性移植。
  - 修改：`task487-local-folder-tree-live-consumer-smoke.js` 保持 RED smoke 退出 0 语义，增加 staged DOM 状态、PageTree/FileTree 外部创建/删除后的目标节点证据、navigation evidence 和更多截图路径。
  - Worker 验证：`node --check scripts/task487-local-folder-tree-live-consumer-smoke.js`、限定 `git diff --check` 通过；因当时 3000 未启动，未运行真实浏览器 smoke。
  - Codex 主控验证：`node --check scripts/task487-local-folder-tree-live-consumer-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task487-local-folder-tree-live-consumer-smoke.js`。结果仍为 RED，`transport=local-folder-events`、PageTree/FileTree 外部变更阶段 `navigationEvents=0`，但 `treeLiveApplied` 仍为空，说明 live consumer 证据仍未收敛到 snapshot/resync。
- 2026-05-25：Batch P Reasonix 派发失败并拒收：三个输出目录 `reasonix-2026-05-24T20-21-00-338Z-53f263c7` / `reasonix-2026-05-24T20-21-00-457Z-9384a1e5` / `reasonix-2026-05-24T20-21-00-569Z-8a3585e8` 均只有 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，没有 `result.json`、`process-handoff.md/json` 或 `final.md`；记录中的三个 worktree 目录当前不存在，Codex 未采纳任何 diff。后续 B/C/D 等实现 worker 必须在独立 worktree 产出候选 patch，再由 Codex 读取 Hindsight recall、handoff、diff 和验证证据后选择性合入。
- 2026-05-25：Batch Q 未经主控批准再次自动启动 3 个 Reasonix runner（`0525-q-worker-a/b/c-*`）。该批超过当时旧 2-worker 上限，但不满足“主控短批次明确派发 + 完整 handoff”合同；三个输出目录 `reasonix-2026-05-24T20-27-26-922Z-9f1ffa2f` / `reasonix-2026-05-24T20-27-26-933Z-6ad5f071` / `reasonix-2026-05-24T20-27-26-955Z-ff208388` 均只有 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，没有 `result.json`、`process-handoff.md/json` 或 `final.md`；Codex 已终止进程，删除本轮自动生成的 3 份 batch-q 草稿并移除空 worktree，不采纳任何 Reasonix 结果。
- 2026-05-25：Codex 修复本机 Reasonix runner / skill 的协同约束：`reasonix-coding-runner.mjs` 生成 prompt 时新增“Reasonix 是叶子 worker，禁止创建 `.codex/reasonix-tasks`、启动 runner/ACP/Claude/Codex/Hermes、操作其他 worktree”的硬约束；同步更新 `reasonix-coding-worker` 与 `reasonix-parallel-coding-flow` skill，并新增 runner `--dry-run` 用于不启动 Reasonix 的 prompt 合同 smoke。
  - 已验证：`node --check reasonix-coding-runner.mjs`、`node --check reasonix-process-extractor.mjs`、`node --test reasonix-process-extractor.test.mjs`、runner `--dry-run --no-memory-recall` 输出的 `prompt.md` 含叶子 worker 禁止项，且 `ps` 未出现 `reasonix` / `reasonix-coding-runner` 进程。
- 2026-05-25：Codex 独立复核并采纳 Batch Q Worker A 遗留候选 patch：`local_folder_events.rs` 在 `treeLive=true` 且 watcher subscribe 失败时不再返回 500，而是返回同一 SSE 响应并降级为 revision polling；初始仍发送 `snapshot`，revision 变化后通过既有 `rebuild_tree_resync_payload` 发送 `resync`。客户端 `tree:snapshot` / `tree:resync` consumer 不改，避免叠加 browser double-polling。该 patch 来自无 handoff 的失控 worker，未按 Reasonix 结果直接采纳，而是由 Codex 本地阅读 diff、跑验证后作为主控修复纳入。
  - 修改：`rust/crates/mnote-web/src/routes/local_folder_events.rs`。
  - 已验证：`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web tree_live_polling_resync_payload_tracks_revision_changes -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_folder_events -- --test-threads=1`、`git diff --check`、`node --check scripts/task487-local-folder-tree-live-consumer-smoke.js`、临时 `MNOTE_WEB_BIND=127.0.0.1:3002 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3002 cargo run --manifest-path rust/Cargo.toml -p mnote-web` 后运行 `MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3002 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task487-local-folder-tree-live-consumer-smoke.js`，结果 `ok:true`，PageTree/FileTree 外部创建/删除均为 `treeLiveApplied=resync`，两个阶段 `navigationEvents=0`。
- 2026-05-25：Batch Z 曾一次派发 4 个 Reasonix worker，其中 Worker C 负责 3-20 filetree refresh helper，Worker D 负责 3-20 tree live smoke；该批超过当时旧 2-worker 上限，但四个 run 均未产出 `final.md`、`result.json`、`process-handoff.md/json`，worktree diff 为空，全部拒收。
  - 涉及 run：`reasonix-2026-05-24T22-02-32-495Z-dfd6bb89`、`reasonix-2026-05-24T22-02-32-511Z-ea50d6e0`、`reasonix-2026-05-24T22-02-32-635Z-4edc4f8a`、`reasonix-2026-05-24T22-02-32-644Z-1180a395`。
  - 额外异常：Reasonix 仍创建 `.codex/reasonix-tasks/2026-05-25-batch-x-worker-a-5-29-dnd-dom-readonly.md` 草稿，违反叶子 worker 约束；该草稿不提交。
  - 本批未采纳任何 3-20 diff。主工作区仅为 5-29 block transform 候选 patch 补跑 `task479-local-folder-markdown-resource-lifecycle-smoke.js` 作为资源生命周期回归，结果 `ok:true`，截图 `/tmp/mnote-task479-lifecycle-RJDsKO/screenshots-1779660971673/final-state.png`。
- 2026-05-25：Batch AA 后续按用户纠正重新执行独立 worktree B/C/D 流程；该批超过当时旧 2-worker 上限。Reasonix C 无 handoff / 零 diff，拒收；独立 worktree worker C `/mnt/Data1T/mnote-worktrees/0525-aa-worker-c-3-20-filetree-refresh-helper` 产出并通过主控复核后选择性合入。
  - 修改：`filetree-runtime.js` 新增并导出 `applyLocalFolderSidebarSnapshot(nextDocument, options)`，复用 `replaceSidebarTreeFromDocument`，默认应用 `sidebar-tree-root` 与 `sidebar-file-tree-root`，并支持 `onApplied` 回调；`layout.rs` 新增同名 inline fallback wrapper 和字符串断言。
  - 边界：未改 `refreshLocalFolderSidebarSnapshot` 的 fetch URL、projection JSON 渲染主路径、watcher 标记、focus/selection/sidebar shell 副作用；不把 HTML snapshot 应用重新接入主刷新流程。
  - Worker C 验证：`git diff --check`、`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime -- --test-threads=1` 通过。
  - Worker D smoke：Reasonix run `reasonix-2026-05-24T22-26-26-122Z-62733e93` 有完整 handoff/result/截图且源码零 diff；`task487-local-folder-tree-live-consumer-smoke.js` 在 3001 通过，PageTree/FileTree 外部创建/删除均为 `treeLiveApplied=resync`，导航数为 0。
- 2026-05-25：Batch AB 未派发 Reasonix worker；Codex 主控完成 `refreshLocalFolderSidebarSnapshot` 内部 watch projection 标记的小切片。
  - 修改：`filetree-runtime.js` 新增并导出 `markLocalFolderWatchApplied(value)`；`layout.rs` 的同名 wrapper 优先委托 runtime，inline fallback 保留 `data-mnote-local-folder-watch-applied="projection"` 行为。
  - 边界：未改 projection fetch URL、JSON 渲染、selection/focus/sidebar shell 副作用、附件存在性刷新、tree live SSE consumer；本批只外置刷新完成证据标记。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`git diff --check`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime -- --test-threads=1`。
- 2026-05-25：Batch AC 按当前 4-worker 口径继续采用独立 worktree 流程；本轮实际 3 个 worker，符合当前 goal 上限；任务书均写入各 worktree `tmp/`，未新增主工作区 `.codex/reasonix-tasks`。
  - Worker C `/mnt/Data1T/mnote-worktrees/0525-ac-worker-c-3-20-conflict-action-boundary`，Reasonix run `reasonix-2026-05-24T22-52-30-772Z-464df4b4`，Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空。
  - 审计结论经 Codex 复核后采纳：`acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult` 仍强依赖 fetch、persist、session views、Tiptap hydrate、conflict key 计算和 `setSessionStatus`，不应整体迁入 `document-conflict-panel-runtime.js`；共享的 session 状态字段重置块暂不外置，以保持 runtime=DOM/promise wrapper、shell=业务编排的边界。
  - Worker D `/mnt/Data1T/mnote-worktrees/0525-ac-worker-d-smoke-regression`，Reasonix run `reasonix-2026-05-24T22-52-30-854Z-dfad6786`，源码零 diff；`task489`、`task488`、`task487`、`task451` 在 3001 均通过，其中 `task451` 覆盖 accept-disk、keep-current、merge-save、agent-conflict-source。
  - 未完成：conflict action 业务逻辑仍留在 `web_shell.rs`；secondary pane conflict 专项 smoke 仍可后续单独补强。
- 2026-05-25：Batch AD 采用 3 个独立 worktree worker（B/C/D），符合当前 4-worker 口径；任务书均写入各 worktree `tmp/`，未新增主工作区 `.codex/reasonix-tasks`。
  - 涉及 worktree：`/mnt/Data1T/mnote-worktrees/0525-ad-worker-b-5-29-drop-indicator-block-rect`、`/mnt/Data1T/mnote-worktrees/0525-ad-worker-c-3-20-upload-target-readonly`、`/mnt/Data1T/mnote-worktrees/0525-ad-worker-d-regression-smoke`。
  - Worker C run `reasonix-2026-05-24T23-05-57-201Z-41882fef` 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，无 `final.md`、`result.json`、`process-handoff.md/json`，worktree 零 diff；3-20 输出拒收。
  - Worker D run `reasonix-2026-05-24T23-05-57-257Z-c917f1ef` 有完整 `process-handoff.md/json`、`result.json`、`final.md`，源码零 diff；`task489`、`task488`、`task487`、`task451` 在 3001 均通过，其中 `task487` 覆盖 local-folder tree live consumer、`task451` 覆盖 conflict UI actions。Codex 已读取 Hindsight recall 与 handoff/result，并独立复跑 `task487` / `task451` / `task489` / `task488` 主控验收，全部通过。
  - 后续如需继续 upload target 审计，必须重新按最多 4 worker 的受控批次派发。
- 2026-05-25：Batch AE 的 3-20 侧记录 2 个独立 worktree worker；同批跨 3-20 / 5-29 总 worker 数为 4 个，符合当前 4-worker 口径；任务书均写入各 worktree `tmp/`，未写主工作区 `.codex/reasonix-tasks`。
  - Worker C `/mnt/Data1T/mnote-worktrees/0525-ae-worker-c-3-20-upload-target-readonly`，run `reasonix-2026-05-24T23-18-29-419Z-3e999c4d` 有完整 `process-handoff.md/json`、`result.json`、`final.md`，源码零 diff；审计结论经主控复核后采纳：`resolveFileTreeUploadTarget` 暂不宜迁入 browser runtime，核心依赖 `preflightFileTreeUploadTarget`、`sidebarFileTreeSelection`、fetch 与 DOM 查询；`refreshLocalFolderSidebarSnapshot` 仍是 shell 编排，append/reveal 系列 helper 已足够外置。
  - Worker D `/mnt/Data1T/mnote-worktrees/0525-ae-worker-d-regression-smoke`，run `reasonix-2026-05-24T23-18-29-435Z-3225d16e` 串线执行成 5-29 overlay 只读审计，未运行 `task489` / `task488` / `task487` / `task451` smoke；本轮 3-20 smoke 输出拒收。
  - 后续 3-20 推荐下一刀：只读审计 `syncSidebarFileTreeSelection` 是否可迁入 `filetree-runtime.js`，或在明确降级兼容策略后评估 inline fallback 瘦身；不要继续盲迁 `refreshLocalFolderSidebarSnapshot` 整体。
- 2026-05-25：发现未授权 Reasonix runner / ACP 进程指向 `/mnt/Data1T/mnote-worktrees/0525-af-worker-b-3-20-filetree-selection-helper`。
  - 处置：该 worker 未由主控按当前 goal 派发，已终止相关 `reasonix-coding-runner` / `reasonix acp` 进程；主控复查后未发现残留 Reasonix worker。
  - 采纳：不读取、不采纳该 worktree 的任何输出；后续若要审计 `syncSidebarFileTreeSelection`，必须重新按最多 4 worker 的短批次显式派发。
- 2026-05-25：Batch AH 只派发 1 个只读 Reasonix worker 审计 filetree selection runtime 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-ah-worker-b-3-20-filetree-selection-readonly`，run id `reasonix-2026-05-24T23-50-15-233Z-0cee779d`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`，并核对 worktree `git status --short` / `git diff --stat` 为空；runner 完成后未发现残留 Reasonix worker。
  - 审计结论经 Codex 复核后采纳：`syncSidebarFileTreeSelection` 已通过 `filetree-selection-runtime.js` 完成 runtime 外置，不应合并进 `filetree-runtime.js`；selection runtime、filetree DOM/data runtime、context menu runtime 继续保持分离。
  - Codex 主控完成后续小切片：`selectSidebarFileTreeDocument` 继续负责按 documentId 找行、active 标记和滚动，但选中状态改为复用 `selectSidebarFileTreeRow(row, { ctrlKey:false, metaKey:false, shiftKey:false })`，避免继续手写第二份 filetree selection 状态同步路径。
  - 已验证：`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_filetree_selection_runtime_helpers_are_externalized_with_inline_fallback -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_selection_runtime_contains_selection_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3014 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3014 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3014 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
- 2026-05-25：Batch AL 只派发 1 个只读 Reasonix worker 审计 document pane host / conflict panel runtime 剩余边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-al-worker-a-3-20-document-pane-host-readonly`，run id `reasonix-2026-05-25T00-32-07-827Z-295cc2b2`，并显式使用 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空；随后终止残留 runner / ACP 父进程，不采纳任何未验证源码写入。
  - 审计结论经 Codex 复核后采纳：`renderSessionConflictSurface` / `openConflictDiffPanel` 剩余部分主要是 session 编排、error handling、fetch、persist 与 host adapter 责任，不应继续整体迁入 `document-conflict-panel-runtime.js`；若硬要再切，只剩 `resolveDocumentPaneHost(view)` 与 `mountConflictPanelToDocShell(host, panel)` 这类极小的纯 DOM helper，但收益有限。
  - 当前建议：3-20 的下一刀应优先回到 filetree DOM/runtime 边界或上传后 sidebar refresh 相关 helper，而不是继续收 conflict panel 尾部。
- 2026-05-25：Batch AL 派发 1 个只读 Reasonix worker 审计 document pane host / conflict panel runtime 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-al-worker-a-3-20-document-pane-host-readonly`，run id `reasonix-2026-05-25T00-32-07-827Z-295cc2b2`，显式 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`，并核对 worktree `git status --short` / `git diff --stat` 为空；只读结论经主控复核后采纳：host 定位/挂载是 document conflict 线当前剩余最合适的纯 DOM helper，`acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult` 及 fetch/persist/session status 编排继续留在 `web_shell.rs`。
  - Codex 主控同步推进 conflict panel host 挂载小切片：`document-conflict-panel-runtime.js` 新增 `conflictPanelHost(root)` 与 `mountSessionConflictPanel(root, panel)`，承接 `.document-pane` host 定位和 header 后插入逻辑；`web_shell.rs::renderSessionConflictSurface` 优先委托 runtime 挂载，保留完整 inline fallback。
  - 边界：未迁 `acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult`、fetch/persist/session status 业务逻辑，也未改 diff 面板数据加载路径；本批只外置 conflict panel host mount DOM helper。
  - 已验证：`node --check rust/crates/mnote-web/browser/document-conflict-panel-runtime.js`、`git diff --check -- design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md rust/crates/mnote-web/browser/document-conflict-panel-runtime.js rust/crates/mnote-web/src/routes/web_shell.rs`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_conflict_panel_runtime_contains_dom_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3033 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3033 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3033 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`。
- 2026-05-25：Batch AM 只派发 1 个 Reasonix worker 审计 filetree preflight helper 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-am-worker-a-3-20-filetree-preflight-readonly`，run id `reasonix-2026-05-25T00-39-31-251Z-c2c08634`，显式 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall，并核对 worktree `git status --short` / `git diff --stat` 为空；但该 run 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，没有 `result.json`、`process-handoff.md/json` 或 `final.md`，因此按合同拒收，不把其文字过程当正式 handoff 采纳。
  - Codex 主控基于本地代码证据收口下一刀：`filetree-runtime.js` 新增 `fileTreeRowsForUploadPreflight(deps)`、`fileTreeDocumentParentsForPreflight(deps)`、`fileTreeTargetChildrenForPreflight(targetRow, deps)`、`fileTreeDropPreflightRows(deps)`；`layout.rs` 同名函数优先委托 runtime，并保留完整 inline fallback。
  - 边界：未迁 `ensureFileTreeWritableTarget`、`/api/tree/filetree/drop-preflight` fetch、`resolveFileTreeUploadTarget`、`refreshLocalFolderSidebarSnapshot` 或更高层 upload/drop 编排；本批只外置 filetree preflight 的 DOM 行遍历与属性采集层。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`git diff --check -- design/03-rust-web/process/3-20-browser-runtime-followup-checklist-v1.md rust/crates/mnote-web/browser/filetree-runtime.js rust/crates/mnote-web/src/ssr/pages/layout.rs`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3034 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3034 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3034 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
- 2026-05-25：主控复核当前 `3-20` 相关事实源后，确认下一刀应继续保持只读审计，不与主工作区现有 `5-29` 改动混写。
  - 当前结论：`filetree-runtime.js` 的 preflight DOM helper 已在主链落地；`resolveFileTreeUploadTarget`、`refreshLocalFolderSidebarSnapshot`、selection runtime 与 conflict panel 业务编排边界维持不动。
- 2026-05-25：Batch BA 计划只派发 1 个只读 Reasonix worker 审计 filetree preflight helper 的进一步收口边界。
  - 目标：围绕 `fileTreeRowsForUploadPreflight`、`fileTreeDocumentParentsForPreflight`、`fileTreeTargetChildrenForPreflight`、`fileTreeDropPreflightRows`，确认是否还需要抽共享 row reader / row title helper，还是当前粒度已经足够；同时明确哪些依赖必须留在 `layout.rs` 壳层。
  - 约束：只读、独立 worktree、源码零 diff；不得建议整体迁出 `ensureFileTreeWritableTarget`、preflight fetch、`resolveFileTreeUploadTarget` 或 sidebar refresh 编排。
- 2026-05-25：Batch BA 只派发 1 个只读 Reasonix worker 审计 filetree preflight helper 的进一步收口边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-ba-worker-a-3-20-filetree-preflight-readonly`，run id `reasonix-2026-05-25T00-47-26-254Z-ea31dd24`，显式 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空。
  - 结论：该 run 的 `finalText` / `process-handoff` / `result.json` 与任务书不一致，内容串成 `5-29 slash menu / block DnD` 审计；虽然工作区零 diff，但输出不回答 filetree preflight 问题，因此按合同拒收。
  - 采纳：不把其文字结论写入 `3-20` 事实源；当前仍以主控本地代码证据为准，即 preflight 4 个 helper 已是 `filetree-runtime.js` 中的 DOM 行遍历/属性采集边界，继续保持 `layout.rs` 负责 preflight fetch、writable target 判定、upload/drop 编排。
- 2026-05-25：Batch BB 只派发 1 个只读 Reasonix worker 审计 filetree preflight 的共享 `rowTitle` / row reader 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bb-worker-a-3-20-filetree-preflight-row-helper-readonly`，run id `reasonix-2026-05-25T00-55-00-763Z-1c10fb87`，显式 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空；worker 未修改源码。
  - 审计结论经主控复核后采纳：当前 4 个 preflight helper 边界已合适，不建议继续抽共享 `rowTitle` / row reader 层。`fileTreeTargetChildrenForPreflight` 与 `fileTreeDropPreflightRows` 虽各有一份 5 行 `rowTitle` fallback 重复，但该 fallback 仅在 runtime 未加载或降级时触发，正常路径始终走 `layout.rs::fileTreeRuntimeDeps()` 注入的 `rowTitle`。
  - 风险说明：runtime fallback 与 `layout.rs` 版 `rowTitle` 在 `instanceof` guard、selector、`title` attr fallback、默认值上存在细微语义差异，因此若为 DRY 继续强行抽取，反而更容易扩大边界和引入行为差异。
  - 当前决定：`3-20` 下一刀不再围绕 preflight `rowTitle` 重复做切片，而应回到更有价值的 runtime 收口点，例如 upload orchestration 壳层、filetree refresh 编排边界，或继续用只读 worker 审计更高收益的 DOM/runtime 分界。
- 2026-05-25：Batch BC 只派发 1 个只读 Reasonix worker 审计 `refreshLocalFolderSidebarSnapshot()` 的 post-apply UI 副作用边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bc-worker-a-3-20-refresh-postapply-readonly`，run id `reasonix-2026-05-25T00-58-49-760Z-bc46d6d6`，显式 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` / `git diff --stat` 为空；worker 未修改源码。
  - 审计结论经主控复核后采纳：`syncSidebarFileTreeSelection()` 与 `markLocalFolderWatchApplied()` 已外置完成；剩余 `restoreSidebarTreeTab()`、`schedulePendingLocalFolderRestoreFocus()`、`refreshEditorLocalAttachmentExistence()` 属于 layout shell / local restore focus / attachment URL 解析链的编排层，不值得继续外置。
  - 当前决定：`3-20` 不再围绕 `refreshLocalFolderSidebarSnapshot` 的 post-apply UI 副作用继续切片，后续应把精力转回更高收益的 runtime 边界。
- 2026-05-25：Batch BD 只派发 1 个只读 Reasonix worker 审计 local mindmap asset fetch observer 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bd-worker-a-3-20-mindmap-asset-runtime-readonly`，run id `reasonix-2026-05-25T01-02-53-392Z-d1614585`，显式 `--no-memory-recall`。
  - Codex 已核对该 worktree 源码 `git status --short` / `git diff --stat` 为空；但本 run 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，没有 `result.json`、`process-handoff.md/json` 或 `final.md`，因此按合同拒收。
  - transcript 补充证据：仅见大量 `read_file` 只读取证，没有形成可交付 handoff；不把其中零散过程输出当作审计结论。
  - 主控本地代码证据：`mindmapAssetFromTarget` / `parseMindmapApiTarget` / `withLocalMindmapSourceParams` / `requestMethod` / `requestBodyHasMindmapCreateOnly` / `applyMindmapApiMutationToFileTree` / `installMindmapAssetFetchObserver` 目前仍一起留在 `SIDEBAR_TREE_JS` 壳层，核心耦合点是 fetch hook、`currentSourceKind/currentRootUri/currentDocumentId`、`appendUploadedAssetRow` 与 `refreshLocalFolderSidebarSnapshot` 的事件编排。当前尚无足够有效的只读 handoff 证明存在高收益的最小继续外置切片。
  - 当前决定：本轮不采纳该 run 的任何文字结论；若继续推进这一簇，应由主控基于本地代码另选更稳的最小机械切片，或重新派发更窄的只读任务。
- 2026-05-25：Batch AM 只派发 1 个 browser smoke Reasonix worker，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression`，run id `reasonix-2026-05-25T00-26-34-914Z-c639fcef`，并显式使用 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对 worktree `git status --short` 为空；worker 未修改源码。
  - 实测通过：`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task487-local-folder-tree-live-consumer-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task451-local-markdown-conflict-resolution-ui-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task489-block-menu-delete-undo-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3001 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task488-local-attachment-link-delete-undo-smoke.js`。
  - 关键证据：`task487` result 显示 `data-mnote-tree-live-transport=\"local-folder-events\"`、PageTree/FileTree 外部创建/删除后 `treeLiveApplied=\"resync\"`、`navigationEvents=0`；`task451` 覆盖 accept-disk / keep-current / merge-save / agent-conflict-source；`task479` / `task489` / `task488` 均 `ok:true`。
  - 产物：`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task487-local-folder-tree-live-consumer-smoke/result.json`、`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task451-local-markdown-conflict-resolution-ui-smoke/result.json`、`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task489-block-menu-delete-undo-smoke/result.json`、`/mnt/Data1T/mnote-worktrees/0525-am-worker-b-3-20-smoke-regression/tmp/task488-local-attachment-link-delete-undo-smoke/result.json`，以及 `task479` 的系统临时目录截图。
- 2026-05-25：Batch BB 只派发 1 个只读 Reasonix worker 审计 `uploadFilesWithResolvedTarget` 是否还存在值得继续外置的纯 helper 边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bb-worker-a-3-20-upload-boundary-readonly`，run id `reasonix-2026-05-25T00-57-16-343Z-5af1b542`，显式 `--no-memory-recall`。
  - Codex 已读取 Hindsight recall、`process-handoff.md/json`、`result.json`，并核对该 worktree 源码 `git diff --stat` 为空；worktree 中仅新增 `final.md`、`process-handoff.*`、`result.json` 等审计产物，不视为源码修改。
  - 审计结论经主控复核后采纳：`uploadFilesWithResolvedTarget(files, detail, options, deps)` 当前已经是合理的薄编排壳层；`deps` 提取与 guard、`resolveFileTreeUploadTarget(detail)` 调用、顺序上传循环、错误收集与 `alert` 反馈均属于当前函数自身职责，不建议继续拆出只会增加间接层的 1-6 行微型 helper。
  - 主控本地代码证据：`local-upload-runtime.js` 中 `uploadFilesWithResolvedTarget` 目前只保留 `resolveFileTreeUploadTarget` 与 `uploadFileToMediaAsset` 注入、`Array.from(files || []).filter(Boolean)`、顺序上传循环、错误汇总与 `alert`；更低层纯 helper 已分别落地到 `fetchWithTimeout`、`uploadLocalFolderAsset`、`uploadMediaAsset`、`insertUploadedAssetIntoEditor`、`uploadFileToMediaAsset`。
  - 当前决定：`3-20` 的 local upload 线不再围绕 `uploadFilesWithResolvedTarget` 继续拆刀，下一批改审 `resolveFileTreeUploadTarget` / preflight fallback 或 sidebar refresh 边界。
- 2026-05-25：Batch BC 派发 1 个只读 Reasonix worker 审计 `resolveFileTreeUploadTarget` / preflight / fallback 的下一刀边界，使用独立 worktree `/mnt/Data1T/mnote-worktrees/0525-bc-worker-a-3-20-target-plan-readonly`，预期 run id `reasonix-2026-05-25T01-03-07-024Z-de2fcc8a`，显式 `--no-memory-recall`。
  - 结果拒收：该 run 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，无 `result.json`、`process-handoff.md/json`、`final.md`；worktree 源码零 diff。
  - transcript 补充证据：worker 只完成了函数定位与部分只读 grep/读取，未形成结构化回答或 smoke/验证产物。
  - 处置：不采纳其任何文字过程；下一步仍由 Codex 依据本地代码证据判断 `resolveFileTreeUploadTarget` / `preflightFileTreeUploadTarget` / `fallbackFileTreeUploadTarget` 是否值得继续拆。
- 2026-05-25：Batch AH 由 Codex 主控按当前 goal 明确派发 4 个独立 worktree worker；主工作区不承接实现过程，只读取 Hindsight recall、handoff、diff 与验证证据后裁决。
  - Worker B `/mnt/Data1T/mnote-worktrees/0525-af-worker-b-3-20-filetree-selection-helper`，run `reasonix-2026-05-24T23-33-43-259Z-bd947ad9` 仅产出 `prompt.md` / `memory-recall.json` / `reasonix-transcript.jsonl`，没有 `final.md`、`result.json`、`process-handoff.md/json`；transcript 停在 `submit_plan`，worktree 零 diff，按合同拒收。
  - Worker C `/mnt/Data1T/mnote-worktrees/0525-af-worker-c-3-20-upload-target-readonly`，run `reasonix-2026-05-24T23-33-43-393Z-301491ef` 有完整 `process-handoff.md/json`、`result.json`、`final.md`，源码零 diff；审计结论经主控复核后采纳：`syncSidebarFileTreeSelection` 及 selection runtime 已外置到合理边界，不应回并到 `filetree-runtime.js`；`resolveFileTreeUploadTarget` / `preflightFileTreeUploadTarget` 仍不适合整体迁入 browser runtime，`fileTreeRowsForUploadPreflight` 仅是低优先级候选 helper。
  - Worker D `/mnt/Data1T/mnote-worktrees/0525-af-worker-d-regression-smoke`，run `reasonix-2026-05-24T23-33-43-450Z-e37e8564` 有完整 `process-handoff.md/json`、`result.json`、`final.md`，源码零 diff；`task489`、`task488`、`task487`、`task451` 在 3001 全部通过。主控独立复核 result.json 后确认：`task487` 已收敛为 `transport=local-folder-events`、PageTree/FileTree 外部创建/删除均 `treeLiveApplied=resync`、`navigationEvents=0`；`task451` 四个冲突动作步骤均 `ok:true`。
  - 本批 3-20 采纳结论：保持 selection runtime / filetree runtime / context-menu runtime 分离；`resolveFileTreeUploadTarget`、`refreshLocalFolderSidebarSnapshot`、preflight fetch、conflict action 业务编排继续留在壳层；下一刀仍优先只读审计或更窄 DOM helper，不扩回大块 runtime 合并。
- 2026-05-25：Codex 主控继续收口 `resolveFileTreeUploadTarget` 的纯数据规范化层。
  - 修改：`rust/crates/mnote-web/browser/filetree-runtime.js` 新增 `normalizeFileTreeUploadTargetPlan(detail, plan)`，承接 preflight 成功后的 `targetRelativePath` / `uploadIntent` 规范化；`rust/crates/mnote-web/src/ssr/pages/layout.rs::resolveFileTreeUploadTarget` 优先委托该 runtime helper，保留完整 inline fallback，以及 `preflightFileTreeUploadTarget` 的 fetch、`console.warn`、`fallbackFileTreeUploadTarget` 降级路径不动。
  - 边界：未迁 `preflightFileTreeUploadTarget` 的 API 请求、`fallbackFileTreeUploadTarget` 的 workspace/document 语义、preflight 失败降级策略、sidebar refresh 或上传编排。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3022 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3022 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3022 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 结果：`task479` 中 `editor-upload-to-page-resource-dir` 与 `filetree-folder-drop-to-target` 均 `ok:true`，说明本刀未破坏本地 Markdown 附件上传目标与文件树文件夹拖入目标路径。
- 2026-05-25：等待后续只读审计期间，曾发现不属于当前手动批次的 residual Reasonix runner。主控按进程面核对后，只保留当前 goal 明确允许的“每批最多 4 个独立 worktree worker”口径；不完整 handoff / result / final 的自动 run 继续按合同拒收。
- 2026-05-25：Codex 主控完成 filetree upload preflight 的 `documentWorkspaces` 低风险数据采样切片。
  - 修改：`rust/crates/mnote-web/browser/filetree-runtime.js` 新增并导出 `fileTreeDocumentWorkspacesForUploadPreflight(workspaceId, deps)`；`rust/crates/mnote-web/src/ssr/pages/layout.rs::fileTreeDocumentWorkspacesForUploadPreflight` 优先委托 runtime helper，保留完整 inline fallback。
  - 边界：未迁 `preflightFileTreeUploadTarget` 的 fetch/API 请求、`sidebarFileTreeSelection`、`currentDocumentId()`、`resolveWorkspaceId()`、fallback 降级或上传编排；本批只外置基于 `fileTreeRowsForUploadPreflight()` 的 documentId 去重与 workspaceId 映射。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_upload_runtime_routes_local_markdown_assets_to_local_folder -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3036 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3036 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3036 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 结果：`task479` 全部检查 `ok:true`，覆盖 editor 上传到页面资源目录、文件树 folder drop 到目标目录、真实附件删除后缺失链接、无 active md 时附件 tab 打开、文件树滚动、文件名页头、删除单个附件引用等回归链路；运行时仍有系统 inotify watch limit fallback 与若干 500 日志，但 smoke 最终通过，属于既有环境限制/接口噪声，不作为本刀失败。
- 2026-05-25：Codex 主控继续收口 upload-target preflight request body 的纯数据组装层。
  - 修改：`rust/crates/mnote-web/browser/filetree-runtime.js` 新增并导出 `fileTreeUploadTargetPreflightBody(detail, deps)`；`rust/crates/mnote-web/src/ssr/pages/layout.rs::preflightFileTreeUploadTarget` 优先委托该 helper 构造 POST body，fetch、response 解析、错误处理与 fallback 仍留在 inline 壳层。
  - 边界：未迁 `/api/tree/filetree/upload-target-preflight` 请求、`sidebarFileTreeSelection` 来源、`currentDocumentId()` / `resolveWorkspaceId()` 的壳层语义、`resolveFileTreeUploadTarget` 降级策略或上传编排；本批只外置 `workspaceId / targetDocumentId / targetRowId / focusedRowId / activeDocumentId / rows / documentWorkspaces` 的 body 对象构造。
  - 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo check --manifest-path rust/Cargo.toml -p mnote-web`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、临时当前源码服务 `MNOTE_WEB_BIND=127.0.0.1:3038 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3038 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3038 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 结果：`task479` 全部检查 `ok:true`，其中 `editor-upload-to-page-resource-dir` 与 `filetree-folder-drop-to-target` 继续确认本地 Markdown 附件上传目标与文件树文件夹拖入目标路径未回归。
- 2026-05-25：Codex 主控继续收口 local mindmap asset fetch observer 的纯 helper 层。
  - worktree 候选：`/mnt/Data1T/mnote-worktrees/0525-bh-worker-a-3-20-mindmap-fetch-helpers`，主控先把候选 patch 补齐到与主区待合入 diff 一致，再在该 worktree 内复核；主工作区只作为最终选择性合入目标。
  - 修改：`rust/crates/mnote-web/browser/filetree-runtime.js` 新增并导出 `fileTreeUploadTargetPreflightBody`、`shortMindmapFileName`、`parseMindmapApiTarget`、`requestMethod`、`requestBodyHasMindmapCreateOnly`；`layout.rs` 中 preflight body 与 mindmap fetch observer 同名壳层优先委托 runtime helper，保留完整 inline fallback。
  - 边界：未迁 `installMindmapAssetFetchObserver`、`withLocalMindmapSourceParams`、`mindmapAssetFromTarget`、`applyMindmapApiMutationToFileTree`、fetch hook、`currentSourceKind/currentRootUri/currentDocumentId`、`appendUploadedAssetRow`、`refreshLocalFolderSidebarSnapshot`、preflight fetch 或 fallback upload target 语义；本批只迁纯数据 body / URL / 方法 / body / fileName 解析。
  - worktree 已验证：`node --check rust/crates/mnote-web/browser/filetree-runtime.js`、`git diff --check`、`cargo fmt --manifest-path rust/Cargo.toml --all --check`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree_runtime_contains_row_accessor_helpers -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web filetree -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_runtime_routes_local_mindmap_and_office_assets -- --test-threads=1`、临时 worktree 服务 `MNOTE_WEB_BIND=127.0.0.1:3038 MNOTE_WEB_PUBLIC_BIND=127.0.0.1:3038 cargo run --manifest-path rust/Cargo.toml -p mnote-web`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3038 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task455-local-folder-mindmap-clean-smoke.js`、`MNOTE_WEB_SMOKE_BASE_URL=http://127.0.0.1:3038 NODE_PATH=/mnt/Data1T/mnote/node_modules node scripts/task479-local-folder-markdown-resource-lifecycle-smoke.js`。
  - 结果：`task455` 通过，`mindmapId=思维导图093911.json`，磁盘中 `CleanPage.md` 与该 mindmap json 同处页面文件夹，刷新后仍存在，console 为 0，阻断性 4xx/5xx 为 0；截图保存在该 worktree 的 `tmp/task455-local-folder-mindmap-clean-smoke/01-open-clean-page.png` 到 `04-after-refresh.png`。`task479` 也全部 `ok:true`，覆盖 editor 上传到页面资源目录、filetree folder drop 到目标目录、缺失附件链接、无 active md 附件 tab、文件树滚动、文件名页头和单附件引用删除。
- 2026-05-25（Reasonix 第一批）：冲突业务逻辑收口。
  - `acceptDiskVersion`、`keepCurrentEditorVersion`、`writeMergedConflictResult` 迁入 `document-conflict-panel-runtime.js`。web_shell.rs 委托 runtime 保留完整 inline fallback。
  - 已验证：`node --check`, `cargo fmt --check`, `cargo check -p mnote-web`, `cargo test document_conflict_panel_runtime`。

- 2026-05-25（Reasonix 第二批）：filetree keyboard/clipboard/DND 外置。
  - 创建 `browser/filetree-dnd-runtime.js`（14 符号）：dragstart/dragover/drop/dragend 监听器委托 runtime，保留 page tree DND 在布局中。
  - 创建 `browser/filetree-keyboard-runtime.js`（6 符号）：F2/Ctrl+C/X/V/Delete/Backspace 处理 + clipboard 状态管理委托 runtime。
  - 已验证：`cargo test -p mnote-web --lib filetree (33 passed)`, `cargo test mnote_browser_runtime_assets`。

- 2026-05-25（Reasonix 第三批）：SIDEBAR_TREE_JS 完整外置。**这是本轮最大成果。**
  - `layout.rs` 从 11,741 行减至 ~1,100 行（**-91%**）：`SIDEBAR_TREE_JS`（10,655 行 Rust raw string）→ `browser/sidebar-tree-runtime.js`。
  - `SIDEBAR_TREE_JS` 缩减为 17 行 bootstrap（flag check + polling），实际运行时通过 `<script type="module" src="/api/.../sidebar-tree-runtime.js">` 加载。
  - 注册新 asset route：`GET /api/mnote-browser-runtime/sidebar-tree-runtime.js`。
  - 测试更新：`use super::SIDEBAR_TREE_JS` → `include_str!("../../../browser/sidebar-tree-runtime.js")`。
  - 验证：`cargo test -p mnote-web --lib sidebar_tree (10/10)`, task479/task488/task489/task451 全部 PASS。

## 7. 最终完成状态

### 本轮边界全部覆盖

| 边界项 | 状态 | 证据 |
|--------|------|------|
| filetree keyboard/clipboard/DND/context menu | ✅ | filetree-dnd-runtime.js + filetree-keyboard-runtime.js |
| document pane host / conflict panel runtime | ✅ | acceptDiskVersion/keepCurrentEditorVersion/writeMergedConflictResult + 已迁出的 DOM helper |
| local upload 主流程审计 | ✅ | 全部 6 个函数已迁入 local-upload-runtime.js |
| WS/SSE tree live smoke | ✅ | task487 已收敛为 `local-folder-events`，task479/488/489/451 ✅ |
| **SIDEBAR_TREE_JS 主 runtime 外置** | ✅ | 10,655 行 → sidebar-tree-runtime.js（超出原计划范围） |

### browser/ 阶段模块清单（3-20 归档时 11 文件）

| 文件 | 行数 | 说明 |
|------|------|------|
| `sidebar-tree-runtime.js` | 10,654 | 主 runtime（SIDEBAR_TREE_JS 同构迁出） |
| `filetree-runtime.js` | 607 | 文件树数据 helper（43 符号） |
| `local-upload-runtime.js` | ~400 | 本地上传（27 符号） |
| `document-conflict-panel-runtime.js` | ~350 | 冲突面板（10 符号，含业务逻辑） |
| `tree-live-controller.js` | 12 符号 | 实时传输 |
| `tree-shell-runtime.js` | 5 符号 | debug shell |
| `filetree-selection-runtime.js` | 8 符号 | 选择 |
| `filetree-dnd-runtime.js` | 14 符号 | DND 事件（本批新建） |
| `filetree-keyboard-runtime.js` | 6 符号 | 键盘快捷键（本批新建） |
| `filetree-context-menu-runtime.js` | 4 符号 | 上下文菜单 |
| `resource-open-runtime.js` | 5 符号 | 资源打开 |

### 有意保留的 shell 编排（不再迁出）

- `refreshLocalFolderSidebarSnapshot`：shell 编排（fetch + projection render + selection/focus）
- async preflight、`resolveFileTreeUploadTarget`
- document session lifecycle、secondary pane conflict smoke
