Files
mnote/.codex/reasonix-tasks/results/batch-i-worker-b-7-27-code-test-gap.md
T

158 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Worker B7-27 代码/测试缺口审查
> 审查时间:2026-05-20
> 审查范围:`doc.rs` + `hermes_tools.rs` + `manifest.rs`
> 分级:🟢 已满足 / 🟡 可作为后续增强 / 🔴 必须修复才能归档
---
## 检查点 1`blocks_to_markdown` 是否能从 legacy `content`/`contentNodes` 读取文本
**结论:🟢 已满足**
`legacy_block_text()` 函数 (`doc.rs:973-986`) 逐级回退:
```rust
fn legacy_block_text(block: &Value) -> String {
if let Some(text) = block.get("text").and_then(Value::as_str) { ... }
if let Some(content) = block.get("content") { return inline_text_value(content); }
if let Some(content_nodes) = block.get("contentNodes") { return inline_text_value(content_nodes); }
}
```
`block_text()` (`doc.rs:1265-1270`) 只读 `text` 字段用于 markdown 序列化,但 `build_page_content` 中的 `update_legacy_block_text_for_markdown_edit` 正确使用了 `legacy_block_text` 做比较,且写回时通过 `replace_single_inline_text_value` 递归处理 `content`/`contentNodes` 中的 `text` 字段。
**证据**: `inline_text_value()` (`doc.rs:988-1000`) 递归处理 Array/Object/String 三种形态。
---
## 检查点 2:注释格式是否与 7-27 文档一致,是否保留 `revisionRef`
**结论:🟡 可作为后续增强**
当前格式:
```
<!-- block:{id}:{type} -->
<!-- block:{id}:{type}:level={level} --> (heading 块)
```
7-27 §3.3.1 设计为:
```
<!-- block:{id}:{type}:{revision_ref} -->
<!-- block:{id}:{type}:{revision_ref}:level={level} --> (with level)
```
**差异**`revisionRef` 不写入注释,而是从原始 `block.revisionRef` 读取。解析器 `extract_block_comment()` 虽然支持 `type:rev` 格式(`splitn(3, ':')``parts[2]` 可以包含 rev),但 `blocks_to_markdown` 写回时不输出 rev。
**影响**: 低。`revisionRef` 在整个 `build_page_content` 路径中被正确继承(`doc.rs:685-690`),只是注释里看不到。不阻碍归档,可后续增强。
---
## 检查点 3`full_content` 是否不再被拒绝,是否保留复杂块
**结论:🟢 已满足**
- `doc_markdown_edit``full_content` 转为 `{search: current_md.trim(), replace: full.trim()}` 单个 operation,进入新路径
- `parse_final_markdown_to_blocks` 对无注释行创建 `is_new=true` 的 paragraph 块
- `build_page_content` 追加新块并保留原始复杂块(`is_complex_markdown_original_block`
- 测试 `test_build_page_content_full_content_replaces_old_text_blocks_but_keeps_complex_blocks` 验证复杂块保留
- 集成测试 `hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping` 注释确认:"7-27 修复后:full_content 不再被拒绝"
---
## 检查点 4`dryRun` 是否不写入
**结论:🟢 已满足**
`doc_markdown_edit` 中:
```rust
if input.dry_run == Some(true) {
return Ok(json!({
"written": false,
"dryRun": true,
"diff": changed_blocks
}));
}
```
本地文件路径也有独立的 `dryRun` 分支只返回预览。
测试 `hermes_tools_markdown_edit_local_dry_run_does_not_write` 验证。
---
## 检查点 5selection / allowedTargetBlockIds 是否阻止越界写入
**结论:🟢 已满足**
`ensure_markdown_changed_blocks_within_allowed()` (`doc.rs:1050-1073`) 在写回前检查每个 `changedBlocks` 中的 `blockId` 是否在 `allowed_block_ids` 中。越界时返回 `mnote_markdown_edit_target_out_of_scope` 错误。
测试 `hermes_tools_markdown_edit_rejects_selection_out_of_scope` 验证。
`selected_block_ids()` (`doc.rs:1075-1094`) 从 `selectedBlockIds` / `allowedTargetBlockIds` / `selectedBlockId` / `blockId` 四个参数中收集。
---
## 检查点 6:旧 `build_block_ops_from_markdown_edit` 推导路径
**结论:🟢 已满足(已退役)**
`doc.rs:1911` 末尾注释:
```rust
// 退役:7-27 改为 final_md → blocks → execute_page_body_save_from_aggregate 直接写回
```
函数体仍保留但不再被任何调用者引用(`doc.rs` 内只有一处调用已在步骤 4 中被替换)。可用 `cargo +nightly deadlinks` 确认死代码状态,但已足够安全。
---
## 检查点 7:测试覆盖是否足以归档
**结论:🟢 已满足**
### 单元测试覆盖(doc.rs 内)
| 测试 | 验证点 |
|------|--------|
| `test_search_replace_exact` | 精确匹配替换 |
| `test_search_replace_normalized_chinese_byte_boundaries` | 中文忽略空白 + byte 边界保留 |
| `test_search_replace_full_content` | 全文替换 = 全文精确匹配 |
| `test_blocks_to_markdown_with_ids` | 块 ID 注释输出 |
| `test_blocks_to_markdown_heading` | heading 类型 + level 注释 |
| `test_blocks_to_markdown_complex_block_uses_raw_marker` | 复杂块 raw marker |
| `test_build_page_content_full_content_replaces_old_text_blocks_but_keeps_complex_blocks` | full_content 新块 + 复杂块保留 |
| `test_build_page_content_preserves_missing_original_text_block_when_some_ids_remain` | ID 消失时保留原始块 |
| `test_build_page_content_preserves_unchanged_inline_content_nodes` | 行内 marks 保留(不变时) |
| `test_build_page_content_preserves_single_inline_node_marks_when_text_changes` | 行内 marks 保留(文本改变时) |
### 集成测试覆盖(hermes_tools.rs
| 测试 | 验证点 |
|------|--------|
| `hermes_tools_markdown_edit_online_full_content_rejects_unsafe_block_mapping` | full_content 不再拒绝 |
| `hermes_tools_markdown_edit_merges_same_block_operations` | 同块多次操作累积 |
| `hermes_tools_markdown_edit_maps_normalized_search_to_block` | 非精确匹配 + changedBlocks 验证 |
| `hermes_tools_markdown_edit_online_page_body_save_carries_revision_conflict_key` | revision/conflict key 传递 |
| `hermes_tools_markdown_edit_rejects_no_applied_operations` | 空 operations 拒绝 |
| `hermes_tools_markdown_edit_rejects_selection_out_of_scope` | selection 越界拒绝 |
| `hermes_tools_markdown_edit_reports_empty_block_mapping_before_apply` | 空 block mapping 错误报告 |
| `hermes_tools_markdown_edit_shared_read_is_forbidden` | 共享文档只读拒绝 |
| `hermes_tools_markdown_edit_local_dry_run_does_not_write` | local dryRun |
| `hermes_tools_markdown_edit_local_folder_writes_same_markdown_file` | local folder 写入 |
| `hermes_tools_markdown_edit_local_requires_write_contract` | local write contract 要求 |
### 缺口:建议后续增强(非阻塞)
1. **无块注释的多段 markdown 还原**:当前行级解析器假设每行一个块;多段文本(如代码块)需 GFM fallback(§5.3 有分析但未实现)
2. **`revisionRef` 在注释中可见**:当前只在内存中保留,注释格式未输出 rev(§3.3.1 设计差异)
3. **`build_block_ops_from_markdown_edit` 函数体删除**:当前已退役但函数体仍在文件中
---
## 总结
| 分级 | 数量 | 项目 |
|------|------|------|
| 🔴 必须修复才能归档 | 0 | — |
| 🟡 后续增强 | 3 | 注释写 rev, GFM fallback 多段文本, 死代码体删除 |
| 🟢 已满足 | 7/7 | 全部检查点通过 |