主题
17 — word_cloze_contexts 跨端同步:RVH 侧镜像完成确认
物理位置:本文件在 RVH 仓库
~/reading_vocab_helper/docs/cross-end/17-rvh-cloze-context-sync-confirmation.md。 对接 RB 交接文档~/reading-browser/docs/cross-end/16-rb-cloze-context-sync-handoff.md(RB 先行,本文件回确认)。创建:2026-07-10 · RVH 镜像完成 · 关联 schema v57
0. 一句话结论
RB 交接文档 §3 的三件事全部落地:① 本地建表(schema v57);② sync 双向适配(忠实复刻 RB 合并算法, 含 reconcile_cloze_pool);③ 复习卡消费真实语境(背面抽屉挖空块)。RVH 定位 pull-only 消费 (不新增本地采集路径,仍参与 reconcile 去重封顶 + 删词/切用户软删墓碑 push 传播收敛)。
1. RVH 侧已完成清单(对照 RB §3)
§3.1 本地建表
- [x] 新建本地
word_cloze_contexts,schema 对齐 RB §1.1(assets/sql/01_create_tables.sql)。表数 10→11。 - [x]
idx_cloze_word(user_id, word COLLATE NOCASE, created_at DESC)(02_create_indexes.sql)。 - [x]
word列COLLATE NOCASE,与learning_entries.word同一归一口径(红线 #9,写入的是 lemmatizer.normalize 后的 lemma)。 - [x] 无 UNIQUE、无 FK(多语境池 + v16 松耦合);无
source_platform列(与远端一致)。 - [x]
_schemaVersion56→57(app_database.dart);三个 SQL 头部 + schema.md + CHANGELOG 同步。开发期删库重建。
§3.2 sync 适配(lib/features/sync/data/repositories/sync_repository_impl.dart)
- [x]
_pushWordClozeContexts:脏检查(synced_at IS NULL OR updated_at>synced_at OR 墓碑新增)+ mapped 10 列 (不含 source_platform)+onConflict='user_id,word,sentence'(红线 #5f)+ markSynced by id。 - [x]
_pullWordClozeContexts:墓碑三分支(remote删+本地有→墓碑 / 本地已删+remote活→skip / 本地无+remote删→skip) + 双活sense_gloss非空回填(双非空不覆盖)+ 本地无+remote活→INSERT OR IGNORE(synced_at=created_at)。 - [x]
_reconcileClozePool(本表独有,忠实复刻 RB pull.rs:771):① 按句 ASCII 折叠去重(幸存=created_at 最早, gloss 同组回填,其余软删)② 去重后 >cap 软删最旧超额(correlated subqueryLIMIT max(0,count-cap))。 去重键用_asciiLower(仅 A–Z),匹配 SQLite COLLATE NOCASE / RBto_ascii_lowercase,不用 Dart UnicodetoLowerCase。 - [x] syncNow 编排:push 循环 + pull 段(均置尾,无 FK)+
observe()watermark;force-full-pull 计数 + getSyncStatus pending 计数各加 cloze 行。 - [x]
_clozePoolCap=5(与 RBCLOZE_POOL_CAP同值)。
§3.2 撤销/删除路径软删
- [x] 删词级联:
notebook_datasource.deleteNotebookEntry软删 learning_entry 后,按 (user_id, word) 软删其 cloze 语境 (镜像 RBclear_cloze_context_for)。软删而非硬删 → 墓碑 push 传播回 RB。 - [x] 用户切换清理:
auth_repository_impl.clearLearningDataForUser加word_cloze_contexts(按 user_id)。
§3.3 复习卡消费("⑥ cloze v2" 主体)
- [x] 批量加载:
notebook_datasource三个 due 查询(ByGroup/ByDate/ByDateRange)跑完后单次 IN 查询附着语境池 (镜像 RBsrs.rsstep 2,created_at ASC, id ASC稳定序,按 lower(word) 分组)。 - [x]
ReviewWordDto/ReviewWordItem加clozeContexts: List<ClozeContextInfo>;loader 两处构造点透传。 - [x]
ClozeBuilder(domain/services/cloze_builder.dart,移植 RBsrc/lib/cloze.ts):整词多形态挖空防同根泄露、 句长 8..220 护栏、pickInitialContextIdx = repetitions % n确定性轮换、blankWidth3–12。 - [x] UX 落点 = 背面释义抽屉(
DefinitionDrawer):RVH 复习卡正面是「原文聚光灯」(图/文本上高亮词), 不套用 RB 的正面挖空范式。当词有可挖空语境 → 抽屉顶部渲染「你读到的原句」挖空块(下划线遮空 + 答案揭晓 +sense_gloss非空时义项显示),其下照常渲染 pos_definitions 策展例句;无语境完全回落现状。 - [x] 多语境轮换:对齐 RB 语义(
repetitions % 语境数),从轮换起点环形扫描第一条可挖空语境。
2. 与 RB 的差异(有意)
| 维度 | RB | RVH | 原因 |
|---|---|---|---|
| 采集 | content-script 双击查词入池 | 无本地采集(pull-only) | RVH 无阅读/双击查词流;交接 §3 未列采集任务 |
| 复习正面 | 文本挖空句 | 原文聚光灯(图/文本高亮词) | RVH 既有复习范式;cloze 落背面抽屉 |
| 消费落点 | position list + 正面挖空 | 背面抽屉挖空块 | 见上;Phase 4 UX 决策 |
RVH pull-only 下 push 只传播 reconcile / 删词 / 切用户产生的软删墓碑——不会产生 RVH 独有的活语境行 (RVH 不采集)。收敛方向:RB 采集 → Supabase → RVH pull + reconcile → RVH push 墓碑 → RB pull 收敛。
3. 请 RB 侧跟进
- RB 交接 §4 留的口子:RVH 镜像已完成,可把
word_cloze_contexts加入scripts/cross-end-check.sh的SHARED_SYNC数组(此前为避免对 RVH 误报缺表未加)。 - 前置:Supabase
user_word_cloze_contexts表 DDL 需已部署(RB 交接 §5)。RVH sync 依赖其存在;未部署时 RVH pull 报错。
4. RVH 侧验证状态
- [x]
flutter analyze— lib/ 0 error(test/ 既存债务与本次无关)。 - [ ] 删本地 dev DB 重启 → schema replay 到 v57、
word_cloze_contexts建成(待设备验证)。 - [ ] 登录触发 sync →
rvh_supabase_query user_word_cloze_contexts确认远端行 +rvh_db_query确认本地落行/synced_at 回写/reconcile ≤5(待设备验证)。 - [ ] 复习有 cloze 语境的词(尤其 RB 采集、RVH 无源图的词)→ 背面抽屉出现真实语境挖空块(待设备验证)。
落地代码见本批 commit + plan rvh/docs/plans/word-cloze-contexts-sync-rvh-plan.md。