panatgithub_AnkiHeadingSync/docs/2026-04-17PLAN3.md
Dusk fbb289850a 清理 legacy sync 链与旧 syncRegistry 残留,保留 current manual-sync 主链 / Remove legacy sync chain and old syncRegistry remnants while preserving the current manual-sync path
删除已失效的 legacy sync use case、syncRegistry 持久化与旧 card pipeline,运行入口保持 ManualSyncService 主链。

Remove the obsolete legacy sync use cases, syncRegistry persistence, and old card pipeline while keeping the runtime on ManualSyncService.
2026-04-21 11:23:27 +08:00

5.9 KiB
Raw Blame History

模块 2将 Anki noteId 回写到标题块末尾,使用短标记 <!-- AHS:123 -->

归档说明2026-04-21本文描述的是已移除的 legacy syncRegistry 主链方案。 当前保留的生产同步链路是 ManualSyncService 所在的 manual-sync 体系。

Summary

  • 新增卡片同步成功后,把返回的 Anki noteId 写回对应标题块的块尾,格式固定为 <!-- AHS:<noteId> -->
  • 以后该标题块再次同步时,优先按这个 noteId 更新原卡,不再依赖 标题 + 行号 + 路径
  • 标记放在块尾而不是标题下方,用来明确卡片边界。
  • 老卡不迁移,只对以后新增的卡生效;老卡继续走当前 cardKey 逻辑。
  • 如果块尾 noteId 在 Anki 中不存在,自动重建新卡并替换为新的 noteId
  • 如果同一标题块从 basic 切到 cloze 或反过来,直接报错并要求用户重建。

Key Changes

1. 标记语法与块尾规则

  • 唯一支持的标记格式:
    • <!-- AHS:<正整数> -->
  • 标记位置固定:
    • 当前标题块最后一个非空内容行之后
    • 下一个同级或更高层级标题之前
  • 写回规则固定:
    • 有正文时:写在正文末尾
    • 无正文时:写在标题下一行
    • 标记本身视为块尾终止标记
  • 读取规则固定:
    • 只识别当前标题块块尾的最后一个合法 AHS 标记
    • 标记不进入 bodyMarkdown
    • 同一标题块多个合法 AHS 标记时报错
    • 非法 AHS 格式时报错,不静默忽略

2. 提取与渲染

  • CardDraft 增加 embeddedNoteId?: number
  • SourceLocation 增加块尾定位信息,至少包括:
    • blockEndLine
    • contentEndLine
    • markerLine?: number
  • CardExtractionService 负责:
    • 识别块尾 <!-- AHS:... -->
    • 从正文中剔除该标记
    • embeddedNoteId 放入 CardDraft
  • CardRenderingService 不处理身份标记,继续只处理标题和正文渲染

3. 同步身份与本地状态

  • 同步身份模式固定为三种:
    • embedded-note-id
    • legacy-card-key
    • pending-note-id-write
  • SyncRecord 增加:
    • identityMode: "embedded-note-id" | "legacy-card-key" | "pending-note-id-write"
    • legacyCardKey?: string
    • noteId: number
    • filePath
    • sourceHash
    • lastSyncedAt
    • orphan
  • SyncRegistry 必须支持:
    • findByNoteId(noteId)
    • get(cardKey) 保留兼容
  • 新卡首次成功写回 AHS 标记后,记录保存为 embedded-note-id
  • 老卡无标记时,继续保存为 legacy-card-key
  • 建卡成功但标记写回失败时,保存为 pending-note-id-write 并保留 legacyCardKey

4. 规划逻辑

  • 匹配优先级固定:
    1. embeddedNoteId 时,按 noteId
    2. embeddedNoteId 时,按当前 legacy cardKey
  • embeddedNoteId 卡片:
    • registry 有同 noteId 记录:按 sourceHash 判断是否更新
    • registry 无记录:仍视为已有卡,可直接更新
  • orphan 判定拆成两套:
    • embedded-note-id 按本轮看到的 noteId
    • legacy-card-key 按本轮看到的 cardKey
  • 老卡不自动补写 AHS,只保留当前行为

5. 执行逻辑

  • AnkiGateway 增加:
    • getNoteSummaries(noteIds: number[]): Promise<Array<{ noteId: number; modelName: string }>>
  • 执行前先校验所有 embeddedNoteId
    • note 存在且模型一致:updateNote
    • note 不存在:自动重建
    • note 存在但模型不一致:报错并停止该卡同步
  • 自动重建流程固定:
    1. addNote
    2. 用新的 noteId 替换块尾旧 AHS 标记
    3. registry 更新为新的 embedded-note-id
  • 新卡首次同步流程固定:
    1. AHS 标记,先 addNote
    2. 把返回的 noteId 写到块尾
    3. registry 保存为 embedded-note-id

6. Markdown 写回

  • VaultGateway 增加:
    • replaceMarkdownFile(path: string, expectedContent: string, nextContent: string): Promise<void>
  • 写回采用 optimistic concurrency
    • 文件内容仍等于扫描时内容才允许写回
  • 块尾写回算法固定:
    • 定位当前标题块结束边界
    • 移除旧 AHS 标记(若存在)
    • 将新标记写到块内最后一个非空内容行之后
    • 不允许落到下一个标题之后
  • addNote 成功但写回失败:
    • 不删除新建 note
    • registry 记为 pending-note-id-write
    • 以后只要还能按 legacy key 命中,就继续尝试补写 AHS
  • 补写成功后,记录转为 embedded-note-id

Public Interfaces / Types

  • CardDraft.embeddedNoteId?: number
  • SourceLocation.contentEndLine: number
  • SourceLocation.markerLine?: number
  • SyncRecord.identityMode
  • SyncRecord.legacyCardKey?: string
  • VaultGateway.replaceMarkdownFile(...)
  • AnkiGateway.getNoteSummaries(...)
  • HeadingSyncMarker
    • noteId: number
    • raw: string
    • lineIndex: number

Test Plan

  • 块尾 <!-- AHS:123 --> 能被正确识别
  • AHS 标记不会进入 bodyMarkdown
  • 同一标题块多个 AHS 标记时报错
  • 非法 AHS 格式时报错
  • 新卡创建后,AHS 被写到块尾
  • 空正文标题块时,AHS 写在标题下一行
  • AHS 的卡在改标题、改正文、块内移动、文件移动后仍更新原 note
  • registry 丢失时,带 AHS 的卡仍能按 noteId 更新
  • noteId 在 Anki 不存在时会自动重建,并替换块尾 AHS
  • basic/cloze 类型切换时报错
  • 老卡无 AHS 时继续走 legacy cardKey
  • addNote 成功但写回失败时,生成 pending-note-id-write
  • 下次命中后可补写 AHS 并转正
  • orphan 检测对 embedded-note-idlegacy-card-key 都正确

Assumptions

  • 第一版只支持块尾短标记 <!-- AHS:<noteId> -->
  • 不兼容旧的 ID: 行、长注释格式、frontmatter、block ID
  • 老卡不迁移;只有以后新增的卡获得稳定 noteId 身份
  • basiccloze 的跨类型切换第一版不自动处理
  • 允许插件改写 Markdown 文件,在标题块末尾插入或替换 AHS 标记