chore: 收口 Batch C 证据与设计口径

This commit is contained in:
lix-2026
2026-05-21 13:28:23 +08:00
parent a0823a7e53
commit b0fa1e53d8
15 changed files with 807 additions and 20 deletions
@@ -0,0 +1,104 @@
# 1-10 [process] Batch C P1 Resource / BufferStore / GFM / Convex Evidence Checklist v1
> 创建时间:2026-05-21
>
> 上位入口:`design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`
>
> 阶段:Batch C / P1 小尾项收口
>
> 当前状态:`PROCESS`
## 1. 目标
在 Batch B 已完成 baseline smoke 复核后,继续处理 P1 中仍会干扰后续判断的尾项:
1. `5-26` resource open resolver 仍未实际收敛。
2. `1-6` Gap A2 保存后刷新可见读回需要以真实 smoke 复核。
3. `3-13` GFM AST 迁移仍有若干可小步关闭的测试 / 过渡标记尾项。
4. `1-6` Gap C1 Convex export dry-run / conflict / rollback 仍缺独立 smoke 证据。
本批次不扩新产品面,不改 OnlyOffice 保存链路,不把 `documents/save` 重新升级为 local-first 长期正文入口。
## 2. Reasonix Worker 拆分
### Worker Aresource open resolver 最小收敛
Owner
- `design/05-editor-mainline/process/5-26-resource-open-resolver-convergence-checklist-v1.md`
- `rust/crates/mnote-web/src/ssr/pages/layout.rs`
- `scripts/task463-onlyoffice-resolver-smoke.js`
目标:
- 审查并最小收敛 `buildLocalOnlyOfficeOpenUrl``openEditorAttachmentEditTab``openEditorAttachmentNewWindow``openConvexAssetFromFileTree``resolveResourceOpen` 的重复 local Office / resource open 判断。
- 若能安全实现,只改 `layout.rs``task463`
- 必须保持:正文附件“弹窗编辑”进 main resource tab,“新窗口编辑”进浏览器窗口,md/text/code 继续走 tiptap/text/code 渲染路径。
验收:
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web sidebar_tree_runtime_opens_office_assets_through_resource_shell -- --test-threads=1`
- `node --check scripts/task463-onlyoffice-resolver-smoke.js`
- 如 smoke 可运行,执行 `node scripts/task463-onlyoffice-resolver-smoke.js`
### Worker BGFM AST 尾项小包
Owner
- `design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md`
- `rust/crates/mnote-web/src/routes/local_folder_source.rs`
- `rust/crates/mnote-web/src/routes/web_shell.rs`
目标:
- 补充或追认 GFM AST 尾项中低风险项:空表格单元格 / 降级策略测试、legacy->Tiptap marks 验证测试、写侧手写 Markdown 函数过渡标记。
- 不修复 frontmatter title 优先级;该项是单独行为变更,保留给后续专项。
- 不移除 web_shell legacy 适配分支,只允许加过渡注释或测试。
验收:
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web local_markdown -- --test-threads=1`
- `cargo test --manifest-path rust/Cargo.toml -p mnote-web web_shell -- --test-threads=1`
### Worker CConvex export 产品化补 smoke
Owner
- `design/01-tree-first-graph-kernel/process/1-6-next-phase-gap-closure-checklist-v1.md`
- `scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- 可新增 `scripts/task48*-convex-export-*.js`
目标:
- 为 C1 已有能力补 smoke:dry-run 不写文件、同名冲突不覆盖、rollback 恢复导出前状态。
- 不修改 Convex runtime,不改 local-first 架构口径。
- 优先复用现有 fixture/export 脚本,新增脚本要可离线运行。
验收:
- `node --check scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- `node scripts/task444-convex-workspace-export-local-fixture-smoke.js`
- 新增 smoke 的 `node --check` 与实跑通过。
## 3. Codex 本地关键路径
Codex 同步处理:
- A2 保存后刷新可见读回:复现 `POST /api/page-body/write` 后刷新 editor DOM 不显示正文的问题,判断是过期证据、hydration 问题,还是当前真实回归。
- 若是 smoke 缺失,补最小 browser smoke;若是真回归,优先修复 web shell / aggregate bootstrap。
## 4. 完成条件
- Reasonix worker 均有 `final.md` / `result.json` 或明确 `TIMEOUT` 记录。
- Codex 独立审查 diff,不直接相信 worker 总结。
- 相关 targeted tests 与 browser smoke 实跑通过或形成明确 bug。
- `1-6``1-9``3-13``5-26` 文档状态更新。
- 提交前运行 `codegraph sync .``codegraph status``git diff --check`
## 5. 本轮执行记录
- 2026-05-21Codex 建立 Batch C checklist,准备派发 Worker A/B/C,并本地排查 A2 刷新读回。
- 2026-05-21Codex):新增并实跑 `task484-local-folder-page-body-refresh-readback-smoke.js`,验证 `POST /api/page-body/write` 后磁盘、Page Aggregate、刷新后 ProseMirror 均读回新正文;`1-6` Gap A2 浏览器可见读回改判为完成,剩余仅为 conflict UI 数据源接 BufferStore。
- 2026-05-21Reasonix Batch C Worker A):worker 只提交 resource resolver 收敛计划,未生成 `final.md` / `result.json`,无业务代码 diff;记录为 `PLAN_ONLY_TIMEOUT``5-26` 保持 `process`。结果文件:`.codex/reasonix-tasks/results/batch-c-worker-a-resource-open-resolver-timeout.md`
- 2026-05-21Reasonix Batch C Worker B):完成 GFM AST 尾项小包,补空引用块 / 空表格单元格测试、web_shell marks 断言、写侧 Markdown 过渡注释,并更新 `3-13`;结果文件:`.codex/reasonix-tasks/results/batch-c-worker-b-gfm-tail.md`
- 2026-05-21Reasonix Batch C Worker C):`task455-convex-export-plan-rollback-smoke.js` 实跑通过,覆盖 dry-run/conflict/rollback 三个行为,`1-6` Gap C1 已可视为完成。结果文件:`.codex/reasonix-tasks/results/batch-c-worker-c-convex-export-smokes.md`
@@ -28,7 +28,7 @@
- [x] **Gap A0(部分)**: local-first guard 已通过 ✅(`npm run check:local-first-convex-guard``{ok: true, guard: "local-first-convex"}`);browser smoke 尚未复跑。
- [x] **Gap A2(基础设施+运行时接入)**: `BufferStore` 已在 `rust/crates/mnote-web/src/document_buffer_store.rs` 实现,并加入 `AppState.buffer_store`。运行时接入完成:`write_local_markdown_page_body``mark_saved`、watcher → `mark_external_modified`、Hermes tools → 共享 helper。11 个单测全部通过(含 3 个新集成测试)。
- [x] **Gap A2(运行时接入—完成)**: `write_local_markdown_page_body` 已接入 `BufferStore.mark_saved()``documents.rs` `page_body_write``save` 路由已传递 `state.buffer_store`Hermes `doc_apply_block_ops``page_command` 已传递 `state.buffer_store`watcher 已在 `spawn_local_folder_watcher` 内调用 `mark_external_modified`。3 个新集成测试覆盖运行时入口。
- [ ] **Gap A2(浏览器可见闭环)**: Codex 浏览器复核显示 `POST /api/page-body/write` 200、磁盘 `.md` 与 local search index 均写入成功、`#__MNOTE_PAGE_AGGREGATE__` 包含保存文本;但刷新后编辑器 DOM/截图未显示正文,仍需修复可见读回或编辑器 hydration
- [x] **Gap A2(浏览器可见闭环)**: Codex 复核新增 `task484-local-folder-page-body-refresh-readback-smoke.js`,验证 `POST /api/page-body/write` 磁盘 `.md`、Page Aggregate、刷新后的 ProseMirror 均读回新正文;旧的“刷新后 editor DOM 未显示正文”证据已判定为过期
- [x] **Gap A7(基础设施)**: `CommandContext` + `when` evaluator 已在 `core-protocol/src/command.rs` 实现。7 个单测全部通过(含 3 个必补测试)。
- [ ] **Gap A7UI/命令接入)**: File Tree 右键菜单、快捷键、AI 写入能力尚未消费 `CommandContext`
- [x] **Gap B2(部分)**: `resource_rename_uses_resource_command_not_document_command` 测试通过;`local_agent_audit_snapshot_detects_resource_files` 通过;mindmap 生命周期测试通过。
@@ -88,7 +88,7 @@
- [x] 接入外部文件变更 watcher`spawn_local_folder_watcher` 已通过 `buffer_store.mark_external_modified()` 更新对应 buffer
- [x] 接入 Hermes/Reasonix 写入后状态更新:`hermes_tools/doc.rs``hermes_tools/page.rs` 已通过共享 helper `write_local_markdown_page_body` 自动消费 BufferStore
- [ ] conflict UI 数据源改为 buffer stateAPI 已就绪,未接入
- [ ] 浏览器可见读回:保存后磁盘与 PageAggregate 已更新,但刷新后 tiptap/editor DOM 未显示保存正文
- [x] 浏览器可见读回:`task484-local-folder-page-body-refresh-readback-smoke.js` 已验证保存后刷新 ProseMirror 可见正文不丢失
### 2.3 必补测试
@@ -109,11 +109,11 @@
- [x] **已完成**tiptap/保存路由/Hermes 写入链已通过 `write_local_markdown_page_body` 统一消费 `BufferStore`
- [x] **已完成**watcher 已通过 `spawn_local_folder_watcher` 调用 `mark_external_modified`
- [ ] **未完成**`documents/save` 的 conflict UI 数据源尚未改为 buffer state 查询
- [ ] **完成**:浏览器刷新后编辑器可见正文读回;证据截图:`reasonix-a2-after-refresh-text-missing.png`
- [x] **完成**:浏览器刷新后编辑器可见正文读回;验证 `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/google-chrome-stable node scripts/task484-local-folder-page-body-refresh-readback-smoke.js`
### 2.5 下一步
- 修复保存后刷新读回`#__MNOTE_PAGE_AGGREGATE__` 已包含正文,但 `mnote-leptos-tiptap-editor-root` 可见 DOM 为空,需要定位 editor hydration / blockDocument 消费路径
- 保存后刷新读回已由 `task484` 复核通过;后续若复现新问题,应以该 smoke 为回归入口
- conflict UI 数据源改为 buffer state:前端查询 `/api/documents/buffer/state` 或 SSE 推送 buffer 变更事件。
---
@@ -193,7 +193,7 @@
---
## 5. Gap C1Convex 迁移产品化补证据 — 🔄 部分完成
## 5. Gap C1Convex 迁移产品化补证据 — 完成
### 5.1 当前代码锚点(已有未改)
@@ -210,13 +210,14 @@
- [x] 实现同名冲突不覆盖 — 导出时不覆盖已有文件,输出冲突报告 ✅
- [x] 实现 rollback — 支持 `--rollback` 参数 ✅
- [x] 导出完成后重建 local index — task444 smoke 验证通过 ✅
- [x] dry-run/conflict/rollback 独立 smoke — `task455-convex-export-plan-rollback-smoke.js` 三个行为全部通过 ✅
### 5.3 必补测试
- [x] `task444-convex-workspace-export-local-fixture-smoke.js` — 通过 ✅
- [ ] 新增 smokedry run 不写文件 — `--dry-run` 已支持,需额外 smoke 验证
- [ ] 新增 smoke:同名冲突不覆盖 — 已有冲突报告逻辑,需 smoke 验证
- [ ] 新增 smokerollback 恢复导出前状态 — 已支持,需 smoke 验证
- [x] 新增 smokedry run 不写文件 — `task455` 验证 `--dry-run` 后 pages 目录不存在 ✅
- [x] 新增 smoke:同名冲突不覆盖 — `task455` 验证冲突后 exit(2) + 页面前内容保持 ✅
- [x] 新增 smokerollback 恢复导出前状态 — `task455` 验证 rollback 后 pages/assets 均删除 ✅
---
@@ -285,7 +286,7 @@
| Gap | 状态 | 关键产出 | 剩余 |
|-----|------|----------|------|
| A0 | 🔄 部分 | guard、task451、task452、task453 通过 | task431 等剩余 browser smokes 未复跑 |
| A2 | 🔄 运行时接入完成,浏览器闭环未完成 | `BufferStore` 实现 + 11 tests保存请求 200;磁盘和 PageAggregate 写入成功 | 刷新后 editor DOM 未显示正文;conflict UI 未接 buffer state |
| A2 | 🔄 运行时接入 + 浏览器读回完成 | `BufferStore` 实现 + 11 teststask484 验证保存后磁盘、PageAggregate、刷新后 ProseMirror 全部读回 | conflict UI 未接 buffer state |
| A7 | 🔄 基础设施完成 | `CommandContext` + when evaluator + 7 tests | UI/快捷键/菜单接入 |
| B2 | 🔄 部分 | 资源生命周期 + agent audit 测试通过;task443/task445 通过 | mindmap body 测试、跨文档 ResourceTab 400 噪声排查 |
| C1 | 🔄 部分 | export/rollback/dry-run 脚本 + smoke 通过 | 额外 smoke 验证 |
| C1 | ✅ 完成 | export/rollback/dry-run 脚本 + task444/task455 smoke 全部通过 | |
@@ -362,6 +362,12 @@
完成后预期:正文真相、runtime projection、compat join 的边界更薄。
当前批次状态:
- `3-13` 的空引用块、空表格单元格、legacy marks、web_shell 过渡注释已由 Batch C 追认。
- `1-6` 的 A2 浏览器可见读回、C1 export smoke 已由 Batch C 复核完成。
- `5-26` 仍保持 process,等待最小可复核实现或新的 browser evidence。
### Batch Dlive cache / command context
1. `3-3`
@@ -118,6 +118,11 @@ Owner
- 审查 `3-13` 中 GFM AST 尾项:空段落/空表格、legacy <-> Tiptap mark、保存失败错误、过渡 parser 标记、web_shell 临时分支。
- 给出下一步最小执行包,不把未验证项移动到 done。
## 5. 本轮执行记录
- 2026-05-21Batch B 仍保留为 process,原因是 `5-26` 还未形成可复核实现,A2 的冲突 UI 数据源接 BufferStore 也尚未落地。
- 2026-05-21`task443` / `task445` / `task452` 已在后续复核中修正为过期断言,不再作为当前业务回归证据。
验收:
- 输出明确表格:`已完成可追认` / `仍需代码修复` / `仅参考保留`
@@ -294,7 +294,8 @@
- [x] 保证表格单元格内的 inline mark 不丢失。
- [ ] 保证空段落、空表格单元格、空引用块的处理规则固定。
- [x] 保证空段落、空表格单元格、空引用块的处理规则固定。
- 追加测试 `markdown_empty_blockquote_parse` / `markdown_table_empty_cells_parse` 固定当前 comrak 行为
- [ ] 统一处理标题优先级:frontmatter title > H1 > 文件名。
@@ -328,7 +329,8 @@
- [x] 确保 table cell 内的 inline mark 在读写两端都保留。
- [ ] 确保 legacy block -> Tiptap -> legacy block 不再丢 marks。
- [x] 确保 legacy block -> Tiptap -> legacy block 不再丢 marks。
- `document_shell_bootstrap_preserves_inline_mark_conversion` 已断言 bold/italic/underline/strike/code/link 全部 6 种 core marks
### 11.5 Task list 双向转换
@@ -376,7 +378,8 @@
- [x] 新增保存回写单测,验证 round-trip 不丢 task / marks。
- [ ] 新增 web shell 单测,验证 legacy <-> Tiptap 转换不丢 marks。
- [x] 新增 web shell 单测,验证 legacy <-> Tiptap 转换不丢 marks。
- `document_shell_bootstrap_preserves_inline_mark_conversion` 已扩展断言 bold/italic/underline/strike/code/link
- [x] 新增浏览器 smoke,验证 checkbox、code、strong、em、strike、link、table cell marks。
@@ -384,7 +387,8 @@
### 11.9 清理与迁移收尾
- [ ] 把手写 parser 标记为过渡实现。11
- [x] 把手写 parser 标记为过渡实现。
- `local_folder_source.rs``inline_nodes_to_markdown``editor_blocks_to_markdown_for_file``editor_blocks_to_markdown_with_rewrite` 均已添加中文过渡注释
- [x] 移除 `local_folder_source.rs` 中不再需要的临时解析函数。
@@ -421,11 +425,11 @@
| 项 | 状态 | 说明 |
|---|------|------|
| 11.2 空段落/空表格单元格/空引用块 | **[ ] 保留** | 行为由 comrak 默认输出决定,无显式规则文档化。建议补单测固定当前行为即可,P1 不阻塞。 |
| 11.4 legacy→Tiptap→legacy marks | **[ ] 保留** | 缺少反向转换集成测试。`document_shell_bootstrap_preserves_inline_mark_conversion` 仅验证函数存在性。 |
| 11.6 列对齐/空单元格/带 mark 单元格降级策略测试 | **[ ] 部分完成** | 列对齐已有测试(应追认),但空单元格和降级策略仍无覆盖。 |
| 11.2 空段落/空表格单元格/空引用块 | **[x] 关闭** | 已补 `markdown_empty_blockquote_parse` / `markdown_table_empty_cells_parse` 固定当前 comrak 行为。 |
| 11.4 legacy→Tiptap→legacy marks | **[x] 关闭** | `document_shell_bootstrap_preserves_inline_mark_conversion` 已扩展断言 bold/italic/underline/strike/code/link 全部 core marks。 |
| 11.6 列对齐/空单元格/带 mark 单元格降级策略测试 | **[ ] 部分完成** | 列对齐已追认,空单元格已补测试关闭。降级策略仍无覆盖。 |
| 11.7 保存失败状态保留 | **[ ] 保留** | 冲突/写失败返回 WebError,前端错误处理和状态保留逻辑无测试。 |
| 11.8 web shell 单测 | **[ ] 保留** | 缺少实际调用 legacyInlineContentToTiptap 并验证输出 marks 完整性的测试。 |
| 11.9 手写 parser 标记为过渡 | **[ ] 保留** | `local_folder_source.rs` `inline_nodes_to_markdown` / `editor_blocks_to_markdown_*` 写侧函数无 `#[deprecated]` 标注。 |
| 11.9 web_shell.rs 临时适配分支移除 | **[ ] 保留** | JS 侧 `legacyStylesToTiptapMarks` 等适配函数仍存在。 |
| 11.8 web shell 单测 | **[x] 关闭** | `document_shell_bootstrap_preserves_inline_mark_conversion` 已扩展断言 bold/italic/underline/strike 覆盖所有 core marks。 |
| 11.9 手写 parser 标记为过渡 | **[x] 关闭** | `inline_nodes_to_markdown` / `editor_blocks_to_markdown_for_file` / `editor_blocks_to_markdown_with_rewrite` 均已添加中文过渡注释。 |
| 11.9 web_shell.rs 临时适配分支移除 | **[ ] 保留** | JS 侧 `legacyStylesToTiptapMarks` 等适配函数仍存在(已加过渡注释 TODO(step-4)。 |
| 11.9 process→done | **[ ] 保留** | 以上未完成项关闭后方可移动。