Skip to content

44 · RVH 回执:删扫描页 → cloze 行仍在(doc 23 §7.3 / 待验项 C)

方向:RVH → RB 回的是docs/cross-end/23-rb-ocr-cloze-context-decision.md §7.3(验证建议第 3 条) 在 relay 台账里的编号:待验项 Cdocs/verification/cross-end-cloze-pool-2026-08.md 收尾时记为「🚫 未验」——阻塞原因见 §1。 日期:2026-08-28 · RVH HEAD 517179d · 真机 Xiaomi 24094RAD4C / Android 16 账号<测试账号 A> · user_id = <test-user-id-A>(与 relay 同一个)


0. 一句话

C 成立:删扫描页不会带走它贡献的 cloze 行(本地 33 条、云端 33 条,deleted_at 全为 null)。

先修后验 —— relay 收尾时 C 被标成「不排期」,理由是 RVH 的删页当时是硬删 + FK CASCADE,那种状态下跑 C 会把坏行为记成「既有行为」。该缺陷已于同日修复(软删 + 手工级联,红线 #6e/#6f/#6g),本次是在修复后的实现上验的。


1. 为什么之前不能验

relay 台账 §0 把 C 标为「🚫 本轮不排期」,原文理由成立,这里补上代码位置:

reading_page_datasource.dart:131  deleteReadingPage      db.delete   ← 硬删
                          :580  deleteReadingPages      db.delete   ← 硬删(批量)
01_create_tables.sql:377/403     word_page_links.reading_page_id ... ON DELETE CASCADE

在那个实现下,「cloze 行仍在」这个观测会成立,但成立的理由是错的:页行被物理删掉、 删除信号根本不上行,同时 CASCADE 把 word_page_links 的墓碑一并抹掉。 把这种状态记成「§3.1 第 1 条的既有行为,别当 bug 报」,等于给一个真缺陷发了通行证。

修复见 RVH commit 517179d(软删 + 同事务手工级联子树 + deleted_at/updated_at 同值), 13 例回归 + 四处注入缺陷实证。


2. 夹具

刻意不用 relay 的 cave,也不碰 B 轨的 silence 新造一页地质题材的渲染页, 经相册导入走完整 OCR → 收割链路:

笔记CROSSEND-C-FIXTURE45df2428-e6ed-49df-8f52-3551206ff69d
1f77e68e-2567-4800-929a-fb8aa54b1e63Scan 2026-08-28 11:50 #1
source_refNULL ← doc 23 §3.1 第 1 条的根
收割Cloze: 33 词入库,33 词通过闸门,新增 33 条语境
33 条 cloze 的 source_url33/33 全为 NULL(doc 23 §3 裁定:不造合成 URL)

删除前已 sync 到静默(pushed=95, pulled=34 → 连续两轮 pushed=0), 云端该页 deleted_at = nullserver_updated_at = 2026-08-28T03:51:35.279338+00:00


3. 动作与观测(原始输出)

动作:来源图片列表里点垃圾桶删除该页(reading_page_list_page.dart:112 那条 UI 路径)。 确认弹窗文案:Delete "Scan 2026-08-28 11:50 #1"? Associated word links will also be removed.

3.1 本地(删除后、sync 前)

page_row_exists      1                                  ← 行还在(软删,不是物理消失)
page_deleted_at      2026-08-28T04:24:08.457491Z
page_updated_at      2026-08-28T04:24:08.457491Z        ← 与 deleted_at 逐位相同(#6g W)
links_rows           33
links_tombstoned     33                                 ← CASCADE 下这 33 行会整个消失
cloze_still_active   33                                 ← ★ C 的判据
learning_entries     17/17 活(抽查 20 个词)            ← 词仍在生词本,只失去这个出处

3.2 sync 两轮

Sync complete: pushed=0,  pulled=0,  syncStartAt=2026-08-28T04:23:53Z   ← 删除前
Sync complete: pushed=34, pulled=34, syncStartAt=2026-08-28T04:24:53Z   ← 1 页 + 33 link 墓碑
Sync complete: pushed=0,  pulled=0,  syncStartAt=2026-08-28T04:25:53Z   ← 无回声环

3.3 云端(Supabase)

user_reading_pages / id=eq.1f77e68e-…
  deleted_at         2026-08-28T04:24:08.457491Z
  updated_at         2026-08-28T04:24:08.457491Z
  server_updated_at  2026-08-28T04:24:54.422534+00:00
  source_ref         null

user_word_page_links / reading_page_id=eq.1f77e68e-… , deleted_at=not.is.null
  count = 33,33 行的 deleted_at 全部 = 2026-08-28T04:24:08.457491Z

user_word_cloze_contexts / created_at=gte.2026-08-28T03:50:40 , deleted_at=is.null
  count = 33  ← ★ 一条没少,source_url 全为 null
  behind / beneath / boundary / carry / downstream / drill / explore / far / fine /
  geology / glacier / grind / ancient / ice / imagery / leave / mark / measure / news /
  powder / range / retreat / satellite / science / sediment / shape / stake / summer /
  surface / thin / track / valley / whole

判定:✅ C 成立。


4. 🔴 一处订正:现在「为什么仍在」的理由变了

doc 23 §3.1 第 1 条给的理由是:

级联键是 source_url,NULL 匹配不上任何行 ⇒ 在 RVH 删掉某张扫描页不会带走它贡献的 cloze 行。

那句话描述的是 RB 侧页级清理(notes.rs:1010-1014source_url = reading_pages.source_ref 精确等值)。RVH 侧的理由不是这个 —— RVH 的删页路径压根不碰 word_cloze_contexts

deleteReadingPage 事务里只有两条 UPDATE:
  ① UPDATE reading_pages     SET deleted_at = ?, updated_at = ? WHERE id = ?
  ② UPDATE word_page_links   SET deleted_at = ?, updated_at = ? WHERE reading_page_id = ?

两个理由都指向同一个观测,但来源不同、失效条件也不同:RB 那条会在 source_url 恰好 等于某个 source_ref 时改变行为(这正是 doc 23 §3 禁止造合成 URL 的原因); RVH 这条是结构性的,与 source_url 取值无关。记下来是因为日后若有人给 RVH 的删页加 「按来源清语境」,读 doc 23 会以为 NULL 就万事大吉。


5. 🆕 需要 RB 确认的一件事(本次修复带来的新暴露面)

修复前 RVH 的删页不上行;现在会了。 于是 RB 会开始收到来自 RVH 的 user_reading_pages 墓碑——这是 relay 期间不存在的输入。

请 RB 确认:pull 到一条页墓碑时,会不会触发那条按 source_url 的页级级联清理?

  • 按 RVH 的读码理解,那条清理长在 RB 的本地删除命令里(notes.rs), pull 路径不会调它 ⇒ cloze 行在 RB 侧同样保留,与 C 的结论一致。
  • 但这是 RVH 读 RB 代码得出的推断,没有实测,且 RVH 这侧的 cloze 行 source_url 恒为 NULL、匹配不上任何 source_ref,就算真调了也应当无事发生。
  • ⇒ 判断是「双保险,应该没问题」,但请 RB 自己核一遍,不要采信我的读码结论。

顺带:RVH 侧 word_page_links 的墓碑现在也会随删页上行(本次 33 条已实测到云端)。 按红线 #6f,RB 拉到后应把对应本地 link 打成墓碑,不需要额外动作。


6. 遗留

  • 夹具已清理(2026-08-28,用户确认后执行)。顺带又是一次 #6e 的实证:

    reading_notes   deleted_at = updated_at = 2026-08-28T05:19:13.928966Z   ← 逐位相同(#6g W)
    reading_pages   墓碑 1 / 活 0
    learning_entries  33/33 仍在生词本                ← #6e 语义:词仍在,只失去这个出处
    word_cloze_contexts  33 条仍活跃                  ← 删笔记同样不带走语境
    Supabase        笔记墓碑 server_updated_at = 05:19:16.546447+00:00(删后约 3 秒)
    之后连续 4 轮 pushed=0                            ← 无回声环

    确认弹窗文案与 #6e 语义一致: Its reading sources and word links are removed too. The words stay in your notebook. 33 个词(glacier / sediment / moraine …)作为正常收割产物保留在生词本。 推到设备相册的三张测试图也已删除。

  • doc 23 §7.3 可以标为已验;relay 台账已停止更新,故结论落在本文件。


7. 相关

  • doc 23 §3.1 / §7.3 —— 本文件回的就是它
  • relay 台账 ~/reading-browser/docs/verification/cross-end-cloze-pool-2026-08.md(已收尾)
  • 红线 #6e / #6f / #6g —— 修复所依据的三条
  • RVH commit 517179d(软删修复 + 13 例回归);backlog 对应条目已标 ✅