Kaynağa Gözat

fix(ac15): **F25 撤销** —— 是我的断言写成了 x−x,不是 target 缺陷;W4-B 转绿 54/54

## 一、F25 是假的,错在我

`trc.Recommended - trc.Recommended` —— 我把"target 校区 Δ"的被减数与减数写成了**同一个快照**,
于是它恒等于 0,打印成 `+0`,看起来就像"target 没给校区加推荐金额"。
改成 `trc.Recommended - tbc.Recommended`(After − Before)后,W4-B③ 立刻转绿。

**先前的所有推断都建立在这个 0 上**:我据此报了
「target 在建行路径上不记 total_recommended_amount」的 P0 级嫌疑,
还把 team-lead 引去读 `AddColumnsGlobalThenCampus` / `UpsertCampus` / `normalizeExpenseAmounts`,
并跑了一次隔离 probe(probe 只证明了 AddColumns 本身正常,等于在**验证一个不存在的问题**)。
**实际 target 实现是对的**,六个数全对:两表两列都 +5000、无 NULL。

## 二、这条比"实现有 bug"更值得记:它伪装成实现缺陷

`x − x` 恒为 0,红出来的样子和"实现少写一列"**完全一样**。
它是本清单里唯一一条"断言自称在比、其实什么也没比"的形态 ⇒ 已写进 §18.3 第 9 条:
**凡是减法,先问"这两个操作数是不是同一个来源"**;Δ 类量一律用 helper 生成,不在 `Sprintf` 里手写。

## 三、顺带补 §18.3 第 8 条(team-lead 上一轮要求)

**报数自伤:把两个门禁写成一句"全绿"**。双跑红=实现问题;单测红=分母闸在叫。
我上一轮把"加 case 没改分母闸"的 `go test` FAIL 报成了"53/53 PASS",等于**把还在叫的警报报成安静**。
硬规则:①数字一律从落盘输出 grep;②"全绿"只允许用于刚贴出来的那段输出;③提交信息里两个门禁分两行写。

## 四、报数(粘贴)

```
分母:DefaultCases=54 | 应跑 54 | 实跑 54
总览:54 个 case,54 PASS,0 FAIL
Skipped 98 | 业务 329 | 基建 113 | 未分类 0
代价 SQL 3202 次 / 19.959s / 59.3 次 per case / 370ms per case
grep "本块比对了" = 4
活动域 15(2/2/1/10)| 课酬 2(1/1)| 跨域 17 + 资金 37 = 54
go build OK | go vet OK | go test OK            ← 单测与双跑分两行
```

未 push。工作树里 `divide_service*.go` 是别的工程师的 F20 改动,我没碰。
Q 8 saat önce
ebeveyn
işleme
26535f9302

+ 7 - 1
internal/academic/dualrun/stage2/README.md

@@ -1236,7 +1236,7 @@ $sql = "SELECT SUM(t1.discount_amount-t1.discount_amount_used) as amount FROM ..
 反过来,三处**故意选严**的:`toleranceCents = 0`(容差永不放宽)、
 哨兵的 `nonzeroSamples > 0`(非空自检)、`未分类断言数 == 0`。
 
-### 18.3 「自伤清单」:新基建最容易把旧判据悄悄弄坏的八种方式
+### 18.3 「自伤清单」:新基建最容易把旧判据悄悄弄坏的十种方式
 
 每一条都**不产生明显症状**,只让断言变松或整块消失。加基建/加 case 时逐条对照。
 
@@ -1251,6 +1251,12 @@ $sql = "SELECT SUM(t1.discount_amount-t1.discount_amount_used) as amount FROM ..
 | **6** | **一个对象挂多个分类标签,然后"按标签计数"**(批 3a 真踩:`activityChainVerdict` 把命中的标签用 `" + "` 拼起来 ⇒ SA-2 / SC-2 同时是 `intentional-strict` 与 `零写入守卫`) | **两份数各自都对,加起来不等于总数** —— 报出 `12 + 2 + 1 + 2 = 17`,而该批只有 **15** 条。没有任何一条断言会红,因为它错在**记账**不在**判定** | 分类**唯一化**:`Case.ActivityShape()` 按优先级(`legacy-defect > strict > 零写入 > equality`)取**第一个**命中;统计只走 `ActivityShapeTally`,并把 `Σ N == len(cases)` 与"判定文案恰好命中 1 个标签"写成单测 |
 | **7** | **需要读库的断言写进"通用断言块"**(批 3c 真踩:精度口径那条要在原始 3 位精度上读一次种子行) | **红的是断言自己,而 case 是绿的。** 通用断言块跑在两侧各自 `Rollback()` **之后** ⇒ 种子行已随事务回滚,查回来 **0 行**、`SUM(...)` 返回 **NULL**(`sql: Scan error: converting NULL to int64 is unsupported`)。报告上是一条红,看着像"实现错了",实际是**断言读了一个已经不存在的数据** | 需要读库的断言只能放在 **per-side 快照**(事务内)或 **`xxxCompare` 阶段**(它拿到的是 `SideResult.Before/After` 快照)。本批的落地方式:在 `readArrears*` 的 SELECT 里**多读两列原始精度**(`ArrearsRow.RemainderRaw3/NetRaw3`,**不参与比对口径**),断言搬到 `arrearsCompare` |
 
+| **8** | **报数自伤:把两个门禁写成一句"全绿"**(批 3d 真踩:加了 case 却没改 `c2_refund_test.go` 的分母闸,`go test` FAIL,而提交信息写"53/53 PASS") | 双跑绿 + 单测红。**双跑红=实现有问题;单测红=分母闸在叫("分母变了就要重新核一遍")**。写成"全绿"等于**把一个还在叫的警报报成安静** —— 而这条闸存在的唯一目的,就是逼"加 case 的人解释一遍" | ①**任何数字一律从落盘输出 grep 出来再贴,不许回忆**;②**"全绿"这个词只允许用于刚刚贴出来的那段输出**;③提交信息里"go test"与"双跑"必须分两行写 |
+| **9** | **断言里自比较(`x − x`)恒为 0**(批 3d 真踩:`trc.Recommended - trc.Recommended` 被写成"target 校区 Δ",于是恒定 0) | **它看起来像"实现没加钱"**。一次就此报出了一个 P0 级的 `TARGET-DEFECT` 嫌疑("目标实现在建行路径上不记推荐金额"),并把 team-lead 引去读实现、找修法。实际目标实现是**对的**,错的是断言 | ①**差值的被减数与减数必须来自不同的快照**(before/after、legacy/target)—— 写 `A - A` 在语义上永远无意义;②同族自检:差值断言先在**已知会变**的构型上跑一次,若恒 0 立即怀疑自比较;③`Δ` 类量优先用**同一个 helper**(`delta(before, after)`)生成,别在 `fmt.Sprintf` 里手写 |
+
+第 9 条比前八条都更值得记一笔:它是**唯一一条"断言自称在比、其实什么也没比"**的形态,
+而且**它的红看起来完全像实现缺陷**。判据可以压成一句话:**凡是减法,先问"这两个操作数是不是同一个来源"**。
+
 第 7 条的现场(写在这儿免得下一个人再踩):第一版把精度断言放在 `run.go` 的通用断言块里,
 报数是 `legacy 非 2 位 0/0 行(err=...converting NULL to int64 is unsupported)`——
 `0/0` 就是信号:**它一行都没读到**。当时 case 的 11 条业务断言全绿,只有这一条红。

+ 3 - 3
internal/academic/dualrun/stage2/recommend_compare.go

@@ -109,7 +109,7 @@ func recommendCompare(c Case, lg, tg *SideResult) ([]Diff, []Assertion) {
 		add("W4-B·推荐人 Δrecommended(全局/校区)",
 			fmt.Sprintf("%+d/%+d (NULL %v/%v)", lr.Recommended-lb.Recommended, lrc.Recommended-lbc.Recommended,
 				lr.Nulls.Recommended, lrc.Nulls.Recommended),
-			fmt.Sprintf("%+d/%+d (NULL %v/%v)", tr.Recommended-tb.Recommended, trc.Recommended-trc.Recommended,
+			fmt.Sprintf("%+d/%+d (NULL %v/%v)", tr.Recommended-tb.Recommended, trc.Recommended-tbc.Recommended,
 				tr.Nulls.Recommended, trc.Nulls.Recommended),
 			c.IntentionalStrict, "两张表这一列都无 DEFAULT")
 		asserts = append(asserts, Assertion{Kind: AssertionBusiness,
@@ -117,14 +117,14 @@ func recommendCompare(c Case, lg, tg *SideResult) ([]Diff, []Assertion) {
 			Pass: lr.Nulls.Recommended && lrc.Nulls.Recommended &&
 				lr.Recommended-lb.Recommended == 0 && lrc.Recommended-lbc.Recommended == 0 &&
 				!tr.Nulls.Recommended && !trc.Nulls.Recommended &&
-				tr.Recommended-tb.Recommended == want && trc.Recommended-trc.Recommended == want,
+				tr.Recommended-tb.Recommended == want && trc.Recommended-tbc.Recommended == want,
 			Detail: fmt.Sprintf("legacy 全局 %+d NULL=%v、校区 %+d NULL=%v | target 全局 %+d NULL=%v、校区 %+d NULL=%v。"+
 				"⚠ **NULL 不是 0**:它表示「累计推荐金额从未被写成功过」,与「推荐金额确实是 0」在账上完全不同;"+
 				"按 Q1 口径 target 应为 0.00 的地方这里是 +m —— 这就是缺陷本身",
 				lr.Recommended-lb.Recommended, lr.Nulls.Recommended,
 				lrc.Recommended-lbc.Recommended, lrc.Nulls.Recommended,
 				tr.Recommended-tb.Recommended, tr.Nulls.Recommended,
-				trc.Recommended-trc.Recommended, trc.Nulls.Recommended),
+				trc.Recommended-tbc.Recommended, trc.Nulls.Recommended),
 		})
 	}