Commit Graph

2 Commits

Author SHA1 Message Date
eeb8f277fd test(mails.status): "正当"落成可判约束(只许比 archived);欠账补一条"还清即转绿"的判据
pi 2026-09-14 的三点接续。

1. **§1「正当」不能只是标注,要是可判的约束**:按文件+计数登记挡得住"新增一处",
   挡不住"**已登记的那一处改变性质**"。已判出来:**任何与 m.status 的比较只允许比
   'archived'**(全局属性),与 'read'/'unread' 比较 ⇒ 红(per-reader 属性必须走 mail_reads)。
   第一次跑就抓到第三处:`migrate.go` 的 `WHERE m.status = 'read'` ×2 —— 它是**回填**,
   职责就是读那个遗留值,所以按文件登记**窄豁免**(allowedCompareExempt,带理由),
   而不是"凡是迁移都放行"。

2. **§2 欠账要有"还清即转绿"的判据**:新增 `mail_status_derived_test.go`,三态形状
   (与套件探针同族)—— 欠账未清 ⇒ `t.Skip` 并写明还欠什么(**不假红也不假绿**);
   还清 ⇒ 开始实跑并断言"详情路径的 status == 按读者派生的值",**转绿即可测事件**;
   迁一半(只改 GetMailByID 漏 GetThread)⇒ 红。
   实测当前状态:`--- SKIP ... 详情路径返回行级 status="read",而按读者派生说 "unread"`。
   **没写成"现在就红"**:恒红的门 = 挂在错误相位的门(CRITERIA.md §11 已为它付过学费)。
   另按他的更正把 `migrate.go` 的理由写准:不是"跑过即结束",而是
   **"全新安装/空库路径必须存在"**(措辞会影响下一个人敢不敢删它)。

3. **§3 (b) 的语义契约补归属**:归属 = dsh(鸿蒙侧实现者),WebUI 侧不需改动;
   落点 = 跨端对齐判据那一族(形状与"预设清单逐项一致"同构,比语义不比数值);
   **如实标注当前未建**及原因(鸿蒙侧还没有日历页/手势代码 ⇒ 此时"两端逐项相同"只有一端存在,
   建出来就是假判据);到期前提 = P6 第 3 步出现手势代码时立即建,此前是**登记的欠账**。
2026-09-14 17:11:36 +08:00
b7f624bc45 fix(已读/手势): 读侧也拒绝空读者;mails.status 读者/写者登记成"新增即红";阈值契约显式选 (b)
pi 2026-09-14 的三条裁定,逐条落地。

1. **§2 读侧守卫**(他只钉了写侧,读侧是另一半,而且更隐蔽):`reader` 在查询里是**过滤条件**,
   空串不写坏数据也不报错,只会**算出一个错误的数** —— `unreadFor('')` 的
   `NOT EXISTS(... reader_name = '')` 恒真 ⇒ `CountUnread(ctx,"")` 把**所有**邮件算成未读
   (用户看到"全都没读"),`ListInbox(...,"read")` 恒空。
   二选一里选 **①报错**(当前没有任何调用方需要"汇总"语义;选②就要立刻定义"汇总"是什么,
   而那是个还不存在的需求 —— 将来要就新增一个名字里带汇总的函数,别让空串偷偷兼职)。
   `requireReader` 装到 `ListInboxScoped`/`CountUnread`/`CountUnreadInSession`,
   判据两条:空 reader 三个入口都**必须报错**且**不返回数**;正例防止写成"一律拒绝"。
   变异验证:撤掉 `CountUnread` 的守卫 → `CountUnread("") 必须报错,实际返回 0`。

2. **§1 `mails.status` 收口**:先做他要求的第 1 步(枚举读者,他没有 shell)。
   枚举结果:**没有**"零功能读者"那条路 —— 行级值仍会进邮件 JSON(`GetMailByID`、
   `GetThread` 都 select 它),`migrate.go` 的两处是**一次性回填**(正当),
   `unreadFor`/`readStateFor` 只用它判 `archived`(正当,且已注明"不再作为未读判据")。
   所以走第三步:**登记欠账 + 增量判据**。
   - 「只为兼容保留」**没有**当结语:`mail_status_readers_test.go` 是一张**机器检查的清册**,
     按**文件 + 次数**登记(repo.go 13 / thread.go 1 / migrate.go 4),
     **新增一处读取就红**并强制当场回答"这处合不合规";写入点同样登记
     (多一处 `UPDATE mails SET status` 就红)。
   - **第一次跑就抓到第二处写入**:`MarkAllInboxReadForSession`(整会话批量已读)里还有一句
     `UPDATE mails SET status='read'`,我原先只看到 `MarkMailRead` 那一处 ——
     这正是"新增即红"的价值:靠人 grep 会漏,靠判据不会。
   - 收口路径(写进判据注释):行级 `'read'` 迁到 `markReadFor`;
     但在"详情/线程仍返回行级 status"两处读者迁移**之前**不能只删写入
     —— 那会让 JSON 里的 status 永远是 unread,是另一种错误事实。
   变异验证:在已允许的文件里新增一处读取 → 清册对不上,红。

3. **§3 阈值契约显式选 (b)**:不要求数值一致(`40px`/`600ms` 在触摸屏与鼠标、手机与大屏上
   的人体工学合理值天然不同,强行同值会两边都不舒服),契约改钉**语义层**(左滑/右滑=什么、
   边界是否回弹、"快滑"是感知档)。并写清推论:选了 (b) ⇒ **鸿蒙不得引用 WebUI 的三个数**
   (引了就等于偷偷选了 (a),还是"我抄的那一份"那种)。
2026-09-14 17:08:31 +08:00