Files
mnote/.codex/reasonix-tasks/results/batch-b-worker-d-pageaggregate-gfm-tail.md
T

14 KiB
Raw Blame History

Batch B Worker DPage Aggregate / GFM AST Tail Audit

审计时间:2026-05-21 审计范围:

  • design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md
  • design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md

一、GFM AST 解析器迁移(3-13)审计

1.1 已完成可追认

清单项 证据 说明
11.1 选型:comrak 解析器 local_markdown_parser.rs:131-152 使用 comrak::parse_document 确认选型完成,comrak 为成熟 GFM AST 实现
11.2 MarkdownAstDocument 中间结构 local_markdown_parser.rs:14-17 结构体已定义
11.2 block 级节点映射(全类型) local_markdown_parser.rs:19-51 enum MarkdownBlock 覆盖 paragraph/heading/list/task/quote/code/divider/table/image/mindmap/media
11.2 inline 级节点映射 local_markdown_parser.rs:103-113 struct MarkdownInline + MarkdownInlineStyles 覆盖 text/code/bold/italic/strike/underline/link
11.2 task list checked 状态 local_markdown_parser.rs:210-215 TaskItem.symbol 正确分离 checked/unchecked
11.2 表格单元格 inline mark local_markdown_parser.rs:256-259 行内组合子复用 已验证 local_folder_source.rs 测试 local_markdown_parser_preserves_pipe_table_as_table_block
11.3 映射集中到单一模块 local_markdown_parser.rs 全文件 手写 parser 读侧分支已从 local_folder_source.rs 迁出
11.3 block document 与 web_shell 兼容 local_markdown_parser.rs:443-570 输出格式 tiptap 表结构、legacy 样式格式都输出
11.3 block id 稳定性 local_markdown_parser.rs:451 format!("local-block-{block_number}") 基于块序号生成稳定 ID
11.3 frontmatter 保存边界 local_folder_source.rs:2131-2135 保存时保留 frontmatter 有测试 local_markdown_save_preserves_frontmatter_and_writes_basic_blocks
11.3 unsupported 节点降级 local_markdown_parser.rs:173-179 HTML block→paragraph 转义为普通段落
11.4 inline code/bold/italic/strike/link 解析 local_markdown_parser.rs:264-305 全部通过 comrak AST 节点→MarkdownInline
11.4 回写 inline mark local_folder_source.rs:6998-7100 inline_nodes_to_markdown 测试 local_markdown_save_writes_task_list_and_inline_marks 验证回写
11.4 表格单元格 inline mark 回写 local_folder_source.rs:7838-7880 测试 local_markdown_save_writes_table_inline_marks + local_markdown_save_round_trips_tiptap_table_marks
11.5 task list 全部 6 项 解析→回写→smoke 测试 local_markdown_parser_preserves_task_list_and_inline_marks 解析 + local_markdown_save_writes_task_list_and_inline_marks 回写
11.6 pipe table 解析规则 local_markdown_parser.rs:150 options.extension.table = true comrak 原生支持
11.6 单元格 inline IR 输出 local_markdown_parser.rs:593-610 tiptap_inline_nodes_to_json local_markdown_parser_preserves_pipe_table_as_table_block 验证单元格内 code/bold
11.6 表头/单元格类型稳定性 local_markdown_parser.rs:230-236 基于 TableRow(true) row header 标志通过 AST 保留
11.6 保存回写保留表格分隔行和列数 local_folder_source.rs:7838-7880 local_markdown_save_writes_table_alignment_markers 验证列对齐标记
11.7 frontmatter title/mnote_id/页面设置写回 local_folder_source.rs:2175-2178 测试 local_markdown_save_preserves_frontmatter
11.7 正文保存走 page.body.save 命令 local_folder_source.rs:2204-2205 canonicalCommand: "page.body.save"
11.8 local_markdown 测试组 local_folder_source.rs 多个测试 覆盖基础块、task list、inline marks、table、附件链接
11.8 新增 AST 解析器单测 local_markdown_parser.rs:734-747 仅一个测试 markdown_image_parses_as_image_block(较薄弱)
11.8 保存回写单测(round-trip local_folder_source.rs local_markdown_save_writes_* 系列 task、inline marks、table 均已覆盖
11.8 浏览器 smoke scripts/task* 系列 task164-page-options-visible-effect-smoke.js 等,GFM smoke 待确认
11.8 确认 file tree / page tree / page aggregate 不受影响 web_shell.rs 测试 document_shell_renders_local_markdown_with_same_sidebar_surfaces 验证
11.9 移除 local_folder_source.rs 临时解析函数 读侧已使用 local_markdown_parser.rs 手写读 parser 已移除,写侧 inline_nodes_to_markdown 保留
11.9 保留回归测试和兼容层 全部测试保留 删除策略不会清除验证资产

1.2 仍需代码修复

清单项 当前状态 问题描述 修复建议
11.2 标题优先级frontmatter title > H1 > 文件名 标记 [x] 但未实现 parse_markdown_page (local_markdown_parser.rs:86-94) 始终使用 file_stem_title(file_name),完全忽略 frontmatter 中的 title 字段和正文 H1。tree.rs:7307 测试显式断言 frontmatter title 不应出现 resolve_local_markdown_page_aggregate 中增加优先级解析:解析 frontmatter 取 title key,若无则扫描 AST 第一个 H1,若无则 fallback 到 file_stem_title。同时修改 tree.rs:7275-7307 测试预期。
11.4 legacy→Tiptap→legacy 不丢 marks 标记 [ ] 仍待处理 web_shell.rs:831-882legacyStylesToTiptapMarks/legacyMarkArrayToTiptapMarks/mergeTiptapMarks 做了一次方向转换,但反向(Tiptap→legacy)转换没有等价的自动化测试。 新增 web_shell.rs 测试:构造带所有 mark 类型(bold/italic/code/strike/link)的 editor block → 调用 legacyInlineContentToTiptap → 结果再反转换回 legacy → 断言 marks 未丢失。
11.6 列对齐、空单元格、带 mark 单元格的降级策略 标记 [ ] 仍待处理 列对齐已有测试 local_markdown_save_writes_table_alignment_markers(但 checklist 未更新)。空单元格降级策略无测试覆盖。 为空的 table cell 补充 markdown_to_blocks 测试,明确返回空 paragraph 或 skip。为不支持的对齐/跨列行为补充降级断言。
11.7 保存失败时保留编辑器状态 标记 [ ] 仍待处理 save_local_markdown_page 在冲突/写文件失败时返回 WebError,但无机制保证前端编辑器状态保留。前端 side 的 error handler 未在测试中验证。 补集成测试:注入错误的 expected_conflict_detection_key → 断言返回 local_markdown_external_change 错误码 + HTTP 409,且前端编辑缓冲区不清空。
11.8 web shell 单测:legacy↔Tiptap 转换不丢 marks 标记 [ ] 仍待处理 document_shell_bootstrap_preserves_inline_mark_conversion 只验证 JS 转换函数的存在性(字符串包含),未验证实际转换结果。 补充利用 app().oneshot() 请求文档页 → 从 HTML 中提取 JS 上下文 → page.evaluate() 调用转换函数 → 验证输出 JSON 中 marks 完整性。
11.9 手写 parser 标记为过渡实现 标记 [ ] 仍待处理 local_folder_source.rsinline_nodes_to_markdown / editor_blocks_to_markdown_with_rewrite / editor_blocks_to_markdown_for_file 等写侧函数无任何 #[deprecated] 或文档注释标注过渡状态。 在写侧函数上加 /// Transitional: will be replaced by AST-based reverse mapping in Step 3 标注,并在模块文档注释中明确过渡路线。
11.9 web_shell.rs 临时适配分支 标记 [ ] 仍待处理 web_shell.rs:831-882legacyStylesToTiptapMarks 等 JS 函数是前端 legacy→Tiptap 适配层,仍无删除计划。 标注 // TODO(step-4): remove when legacy block format is fully deprecated,或跟踪 issue 链接。

1.3 追认修正:应标记为 [x] 但未标记

清单项 证据 操作建议
11.6 保存回写保留列对齐标记 local_markdown_save_writes_table_alignment_markers 测试 应勾选
11.8 浏览器 smoke 验证 checkbox/code/strong 等 scripts/task* smoke 系列(需确认具体文件名) 如果存在则勾选

1.4 仅参考保留 / future

说明
11.2 空段落/空表格单元格/空引用块 这是 comrak 行为边界,G FM AST 天然处理,但规则未文档化。建议只补一个测试断言当前行为,不必在 P1 前重做。
11.6 列对齐降级策略 列对齐已实现,剩下"空单元格"和"不支持对齐方式的降级"可降为 future
11.9 移除 web_shell.rs 临时适配分支 只有 legacy block 格式全面下线后才能移除,当前为 P2+ 项

二、Page Aggregate 清单(5-6)审计

2.1 已完成可追认

清单项 证据
Phase F 全部 4.x 设计层和代码层标准 大量补充记录确认 contract 已冻结、page-aggregate-loader.ts 消费 Rust endpoint、写入命令面收口
Phase G 加载链路收口 + 散落 props 收口 /api/page-aggregate/:id 为主读链、TS builder 降级为 410、page-aggregate-client-state.ts 统一 client state
Phase H 最小接入优先级 + 页面设置面板纠偏 wideLayout/smallText/layoutDensity/showHeadingNumbers/embedDefaultBlockId 全部 wiredInspector 文案统一
Phase I 标题真相纠偏 + 回归验证 task110-page-title-single-truth-smoke.js 验证页头/面包屑/sidebar/page tree/file tree/刷新/切页一致性
Phase J 语义边界 + 命令名收口 命令名统一为 page.head.updateTitle / page.layout.updateOptions / page.body.save
补充:EditorBlockDocument 块文档优先级 + 原生快照保留 build_page_aggregate_projection_resulteditorDocument→blockDocument→documents.content 优先级构建
补充:page.body.save 原生快照失败语义 不可解析 editorDocument 时返回 validation error,不回退到 tiptapDocument

2.2 仍需代码修复

当前状态 问题描述 修复建议
P1.1 写侧未完全退出 /api/documents/save 标记 [ ] 部分解决 local_folder 已切到 /api/page-body/write,但 Convex workspace 仍走 /api/documents/save 明确 documents/save 的定位:是 Convex 独占路径还是中间态 compat。如果是前者则更新文档说明,如果是后者则排期移除。
P1.2 tiptap dirty + AI/外部文件变更的 conflict UI 标记 [ ] 仍待处理 本地 folder 已有 external-change-conflict DOM 和 mnote-conflict-* 面板元素(web_shell.rs:4990-5015),但前端 side 的 conflict 流程覆盖测试不完备。 补充 Playwright smokewatch 外部修改文件 → 断言 conflict 面板出现 → 选择"保留当前"或"接受磁盘" → 断言结果正确。
P1.3 pageOptions 收口到 leptos-tiptap island 运行时语义 标记 [ ] 部分解决 分类已完成(page-option-semantics.ts),但 protectEditing/showBlockRefCount 仍为 planned 未真正 wired。 如果 P1 目标不要求这些,建议在文档中明确标注"P2: protectEditing/showBlockRefCount 接线"。
P1.4 AI 写入口对齐"授权文件引用+白名单目录+后台文件写入+前台同步" 标记 [ ] 部分解决 命令面已收口到 page family,但"授权文件引用范围"allowedRoots/aiAccessScope)的实现未在审计中验证。 检查 Hermes tool surface 上是否限制了 allowedRoots,以及 page.body.write 是否做了路径 escape 检查。
Phase G 退出标准 2:新增字段不再需 props 链外扩 标记 [ ] 部分解决 入口级痕迹消失但新增字段仍需补 loader/route/island 消费链。 建议在 page-aggregate-loader.ts + page-aggregate-client-state.ts 中增加一个 passthroughUnknownFields 机制,使新增聚合字段无需修改 loader 即可透传到消费侧。

2.3 仅参考保留 / future

说明
Phase G exit 2props 链不扩容) 这是理想终态,当前架构下每个新字段仍需 route→loader→state→consumer 链改动。建议标记为 P2 方向性目标,不作为 P1 exit criterion。
AI 写入口完整对齐 命令面已收口,但"授权引用"和"后台写入+前台同步"涉及 Hermes skill 层和 BufferStore,建议在专项 Hermes 审计中评估。
Convex workspace 写侧仍走 documents/save 如果 Convex 作为 cloud source 长期保留,documents/save 就是它的正式路径而不是 compat,应调整文档口径。

三、下一步最小执行包建议

立即修复(P1,本 worker 批次)

# 描述 难度 涉及文件
1 修复 11.2 标题优先级:parse_markdown_page 改为 frontmatter > H1 > filename local_markdown_parser.rs + tree.rs 测试预期
2 补 11.6 空单元格 / 降级策略测试 local_markdown_parser.rs 测试
3 补 11.8 web shell 单测验证 legacy→Tiptap 转换不丢 marks web_shell.rs 测试
4 标注写侧函数为过渡实现(11.9 local_folder_source.rs 文档注释
5 标注 web_shell.rs 临时 JS 适配为过渡(11.9) web_shell.rs JS 注释

文档追认(本 worker

# 描述 涉及文档
1 3-13 清单 11.2 标题优先级应改为 [ ](当前误标为 [x]) design/03-rust-web/process/3-13-*.md
2 3-13 清单 11.6 列对齐已有测试,可追认 [x] design/03-rust-web/process/3-13-*.md
3 5-6 清单补充本次审计时间戳和裁定结论 design/05-editor-mainline/process/5-6-*.md

后续优先级(P2+

# 描述
1 写侧从 inline_nodes_to_markdown 手写规则迁移到 AST 反向映射(3-13 Step 3
2 移除 web_shell.rs legacy→Tiptap JS 适配层
3 Convex workspace 写侧统一到 page.body.write
4 conflict UI Playwright smoke

四、风险

  • 标题优先级误标 [x]:如果 frontmatter title 与 filename 不一致,用户看到的是文件名而非意图标题。当前文件取名时 frontmatter title 通常与文件名一致,所以影响有限,但仍须修复。
  • 手写写侧函数仍无 AST 反向映射inline_nodes_to_markdown 等函数维护着一套独立的样式→markdown 规则,与 AST 解析器的理解可能产生细微不一致(如 _* 的选择)。不是 P1 blocker,但建议在 Step 3 统一。
  • documents/save 双重语义:对 local_folder 是 compat,对 convex_workspace 是正式路径。如果口径不统一,未来新增 workspace type 时会困惑。