panatgithub_AnkiHeadingSync/docs/semantic-qa-gap-report.md

5.7 KiB
Raw Permalink Blame History

Semantic QA Gap Report

Scope

本报告只针对当前仓库里正在使用的 manual-sync 主链,审查它与“#标签 触发的语义 QA 列表拆卡”需求之间的差距。

主链边界是:

  • settings / validation
  • CardIndexingService
  • RenderConfigService
  • ManualCardRenderer
  • DiffPlannerService
  • AnkiBatchExecutor
  • MarkdownWriteBackService
  • PluginState

Current Reality

1. 当前主链确实是 manual-sync不是旧 syncRegistry 链

真实入口是:

  • FileIndexerService
  • CardIndexingService
  • DiffPlannerService
  • AnkiBatchExecutor
  • MarkdownWriteBackService
  • PluginState

domain/card/*domain/sync/* 仍在仓库里,但本轮新功能应接入 manual-sync而不是另起一条平行同步路径。

2. 当前 card type 只支持两种内部类型

CardType 目前只有:

  • basic
  • cloze

这一点贯穿了:

  • PluginSettings
  • NoteModelFieldMapping
  • RenderConfigService
  • NoteFieldMappingService
  • CardIndexingService
  • PluginState
  • ManualCardRenderer

因此,语义 QA 如果要使用“独立 note type + 独立 field mapping”就不能只伪装成现有 basic

3. 当前索引模型是“一段标题块 -> 一张 IndexedCard”

CardIndexingService 现在的基本假设是:

  • 命中 QA 标题级别 -> 生成一张 basic
  • 命中 Cloze 标题级别 -> 生成一张 cloze

标题块 body 目前被整体视为一张卡的正文,没有任何“标题块内部拆多卡”的结构。

4. 当前 marker / writeback / pending-writeback 已经支持“一个文件多卡”,但默认单位是 block

当前 marker 语义是:

  • <!--ID: noteId-->
  • MarkdownWriteBackService 依赖 blockStartLine + rawBlockHash
  • CardMarkerService.applyBatch() 禁止同一个 blockStartLine 的重复写回

这意味着:

  • 语义 QA 可以接入现有 marker 主链
  • 但每张子卡必须拥有自己的 block 定位,而不能继续共用父标题的 blockStartLine

ManualCardRenderer 当前会把 card.heading 同时用于:

  • 题面标题渲染
  • createBacklink() 的 heading anchor

这与语义 QA 需求直接冲突:

  • 题面应是“父标题 + 子项名”
  • backlink 仍应回到原父标题,而不是 synthetic question

所以现有模型缺少“显示题面”和“源 heading anchor”之间的分离。

6. 当前 settings UI 只暴露 basic / cloze 两套 mapping

PluginSettingTab 当前只支持:

  • QA / Basic note type + mapping
  • Cloze note type + mapping

没有:

  • semantic marker 输入
  • semantic QA note type
  • semantic QA field mapping
  • settings-driven preview area

7. 当前 settings validation 是“结构校验 + lazy mapping”不是“强制所有 mapping 已就绪”

validatePluginSettings() 现在会校验:

  • 枚举值
  • note type 非空
  • 已存在 mapping 的结构是否合法

它不会强制 basic/cloze mapping 必须已经存在。

这意味着 prior AI plan 中“全局 settings validation 必须立即强制 semantic mapping 完整”不完全适配当前仓库;若直接照搬,会破坏现有“先刷新 note type 再保存 mapping”的使用方式。

What Can Be Reused

以下能力可以直接复用:

  • 现有 manual-sync 主链
  • RenderConfigService 的 note type / deck 决策位置
  • NoteFieldMappingService 的 title/body 映射逻辑
  • ManualCardRenderer 的 markdown / backlink / media 渲染能力
  • PluginState 的 noteId 主身份持久化
  • MarkdownWriteBackService / pendingWriteBack / orphan handling 主流程

What The Prior AI Plan Got Right

以下方向与真实仓库吻合:

  • 需要独立内部 card type不能只伪装成现有 QA/basic
  • 应接入当前主链,而不是新建平行路径
  • 最适合的解析入口确实是标题解析阶段
  • 需要 settings 页输入 marker、note type、field mapping、preview
  • 需要稳定子卡 identity且必须兼顾 marker writeback 与 orphan handling

What The Prior AI Plan Got Wrong Or Overstated

1. “只改标题解析”是不够的

真实仓库里还必须同步改:

  • CardType
  • PluginSettings
  • NoteFieldMappingService
  • RenderConfigService
  • PluginState
  • ManualCardRenderer backlink anchor

2. “全局 settings validation 立即强制 semantic mapping 完整”不适合当前仓库

当前仓库的 note field mapping 是 lazy workflow

  • 先选 note type
  • 从 Anki 读字段
  • 再保存 mapping

所以 semantic mapping 应沿用这套模式,而不是把默认设置直接变成不合法。

这不是 prior AI plan 中显式提到的点,但在当前实现里是必须补的模型差距。

Main Gaps To Implement

  1. 扩展 CardType 为三态,并让 semantic QA 走 basic-like field mapping 但独立 note type。
  2. PluginSettings / settings UI 中新增 semantic marker、semantic note type、semantic mapping controls 和 preview。
  3. CardIndexingService 中实现“带标签的 QA 级标题 -> 按一级列表项拆多卡”。
  4. 为子卡引入稳定的 child key / child-local block identity。
  5. 分离“显示题面标题”和“backlink 锚点标题”。
  6. 让 marker writeback 能在同一父标题块内为多个子卡各自写 <!--ID: noteId-->
  7. 补 settings / indexing / rendering / sync / persistence 的测试闭环。

Scope Risks

本轮容易漂移的点:

  • 做成编辑器实时预览
  • 引入 emoji / wikilink trigger
  • 改动旧 syncRegistry 链
  • 重构 deck 逻辑或 orphan 逻辑
  • 试图做“一级列表同行正文智能截断”之外的复杂 NLP 解析

这些都不在本轮范围内。