Kaynağa Gözat

revert(ac15): 撤销 RS-4 声明体与 Divergence 扩展(结构性不可表达)

Reverts baf1856.

理由(实测,不是"没时间"):target 的 `t_courses_activity_signed` 有唯一键
`uk_user_activity(user_id, activity_id)`(NON_UNIQUE=0),写第二条同活动报名报
1062 Duplicate entry ⇒ "同一活动 k 条报名"在 target **不可表达** ⇒ A21 扇出
在 target 恒不存在 ⇒ 两侧**必然相等** ⇒ 没有"两侧应不同"这种 case 可写。

按 team-lead 判断,不为一条回答不了真问题的 case 保留 legacy-only 模式 /
Divergence / Preconditions 任何投机性扩展;A21 的真实影响改由一次**只读测量**
回答(见下一个 commit 的 README §22)。

删除:rs_bill_fanout.go(322 行声明体)、resultset_compare.go 的
Divergence 类型 / 字段 / FATAL 保护(+46 行)。
Q 5 saat önce
ebeveyn
işleme
dbb1f8093d

+ 0 - 42
internal/academic/dualrun/stage2/resultset_compare.go

@@ -144,34 +144,6 @@ type TargetOnlyColumn struct {
 	Note   string
 }
 
-// Divergence 描述一条**已知有意分歧**:两侧本来就不该相等,且我们知道为什么。
-//
-// 它同时是**双向探针**:若 target 被改坏成与 legacy 一样(缺陷被"补回去")⇒ 两侧相等 ⇒ 判定行红;
-// 若分歧扩散到没声明的列 ⇒ 判定行红(分歧面被声明限定,这本身也是断言)。
-// 详见 `rs_bill_fanout.go`(A21 扇出)。
-type Divergence struct {
-	// Reason 是登记的分歧原因(写进判定行与 Diff 备注,**必填**)。
-	Reason string
-	// Columns 是**允许分歧**的列(dump 列名);其余被认领的列仍必须逐列相等。
-	Columns []string
-	// Legacy / Target 是各侧自己的期望值(列名 → 归一后的字符串)。
-	Legacy map[string]string
-	Target map[string]string
-}
-
-// inColumns 判断某列是否在"允许分歧"的集合里。
-func (d *Divergence) inColumns(col string) bool {
-	if d == nil {
-		return false
-	}
-	for _, c := range d.Columns {
-		if c == col {
-			return true
-		}
-	}
-	return false
-}
-
 // ResultSetInput 是一次结果集比对的全部输入。
 type ResultSetInput struct {
 	CaseName string
@@ -190,12 +162,6 @@ type ResultSetInput struct {
 	// 为什么要有第三腿:只有"两侧相等"时,**两侧同时错**会得到一条完全对称的绿。
 	// 期望值从声明算(不写死实现输出),改种子后期望自动跟着变,"算错了"才会红。
 	Expect map[string]string
-	// Divergence 非 nil 时,本 case 是**已知有意分歧**(两侧本来就不该相等)。
-	//
-	// ⚠ 求值逻辑**尚未落地**(下一轮)。在那之前,一旦有人把它接进来,
-	// `compareResultSet` 会**直接 FATAL**(见该函数开头)—— 不许静默忽略:
-	// 忽略它会让这类 case 退化成"按普通 case 比相等",而它本来就该不相等 ⇒ 一条必然红。
-	Divergence *Divergence
 	// PrecisionCheck=true 时,逐列断言 dump 侧金额列第 3 位小数为 0。
 	PrecisionCheck bool
 	// ExtraAssertions 是该 case 自己的领域断言(账单恒等式 / FLOAT 可表示性 / 命中层级…)。
@@ -264,14 +230,6 @@ func RawCellOf(rs ResultSet, row int, col string) (Cell, bool) {
 
 // compareResultSet 是结果集比对的唯一入口。
 func compareResultSet(in ResultSetInput) ([]Diff, []Assertion) {
-	// 声明了"已知有意分歧"却没有求值逻辑 ⇒ **必须 FATAL**,不能静默按普通 case 比。
-	//
-	// 理由与 `hasDispatch` 同族:这类 case 的两侧**本来就该不相等**,静默走普通比对
-	// 会得到一条永远红、且文案指向"实现错了"的假红 —— 那比漏测更贵(会训练人忽略红)。
-	if in.Divergence != nil {
-		panic(fmt.Sprintf("case %q 声明了 Divergence(%s),但骨架的**分歧求值尚未实现**:"+
-			"拒绝静默忽略(否则会退化成按普通 case 比相等,必然假红)", in.CaseName, in.Divergence.Reason))
-	}
 	var diffs []Diff
 	var asserts []Assertion
 

+ 0 - 151
internal/academic/dualrun/stage2/rs_bill_fanout.go

@@ -1,151 +0,0 @@
-package stage2
-
-// 本文件是 **A21 扇出**(RS-4)的**声明体**。
-//
-// 本轮只落"声明",不落种子 / 求值 / 单测 —— 分步提交,避免再出现"回合结束但零产出"。
-// ⚠ 因此 `rsFanoutCase()` **暂不注册**进 `ResultSetCases()`:它的 `Divergence` 求值还没实现,
-// 注册进去会让 `resultsets` 直接 FATAL(骨架已显式拒绝静默忽略,见 `compareResultSet` 开头)。
-//
-// # 分歧是什么
-//
-// `sp_user_expense_get_bill` 的赠送结余:
-//
-//	SELECT SUM(activity_money), SUM(activity_money_used)
-//	  FROM (SELECT singed.activity_money, usedLog.activity_money_used
-//	          FROM temp_courses_activity_signed singed
-//	          RIGHT JOIN (SELECT activityid, SUM(activity_money_used) activity_money_used
-//	                        FROM temp_courses_activity_used_log
-//	                       WHERE create_time < cutoff GROUP BY activityid) usedLog
-//	          ON singed.activityid = usedLog.activityid) t1;
-//
-// `usedLog` 已按活动聚合成**一行**,再与 signed 逐行 JOIN ⇒ 同一活动有 k 条 signed 时,
-// 这一行被复制 k 份,`SUM(activity_money_used)` 被计入 **k 次**。
-// Go(`giveBalanceBefore`)按 activity 去重,used 只计 **1 次** —— 设计 §3.8-A21 的
-// **DUMP-BUGFIX:刻意不复刻**。
-//
-// # 两条硬约束(下一轮落地时用)
-//
-//  1. **k 必须先被断言再被使用**:机制式 `used×(k−1)` 里的 k 来自声明。若种子静默只落 1 条,
-//     该式退化为 0 ⇒ 两侧相等 ⇒ 判定行红,而归因会误指"缺陷被补回去了"。
-//  2. ⚠ **target 侧当前 schema 无法表达该状态**:`t_courses_activity_signed` 上有唯一键
-//     `uk_user_activity(user_id, activity_id)`(实测插入第二条报 1062)。所以"两侧都种 k=2"
-//     在本库**做不到**;落地形态要与 team-lead 确认是"legacy-only 登记 + target 唯一键拒绝断言"
-//     还是别的。本文件的 per-side 期望按"target 能表达"的假设写,仅为把声明先固定下来。
-
-// 本 case 用独立的活动 id(与 RS-1/RS-2 的 9000xx 不重叠,报告里一眼能分开)。
-const rsFanoutActivityID int64 = 900011
-
-// 扇出条数 k。**它是声明**,不是"从库里读出来直接用"——库里读到的值只用于断言它。
-const rsFanoutK = 2
-
-// 种子的单一声明(金额单位:分)。
-const (
-	rsFanoutSignedMoneyCents int64 = 10000 // 每条 signed 的 activity_money = 100.00
-	rsFanoutUsedPrevCents    int64 = 4000  // 期初前发生的 used = 40.00
-	rsFanoutUsedEndCents     int64 = 6500  // 期末前累计 used = 40.00 + 25.00 = 65.00
-)
-
-// rsFanoutCommonCols 是"仍必须相等"的 6 列(分歧面之外)。
-func rsFanoutCommonCols() []string {
-	return []string{
-		"previousBalance", "currentTotalIncome", "currentTotalActivityMoney",
-		"currentTotalRecommendedAmount", "currentSpend", "currentBalance",
-	}
-}
-
-// rsFanoutDivergentCols 是"允许且要求不相等"的 3 列。
-func rsFanoutDivergentCols() []string {
-	return []string{"previousGiveCost", "currentGiveCost", "currentUsedGiveCost"}
-}
-
-// rsFanoutExpected 由声明算出**两侧各自**应得的值(先算再跑,不跑出来再抄)。
-//
-//	legacy:k 条 signed × (activity_money + used) —— used 被计入 k 次
-//	target:Σ activity_money(k 条都算)+ used(只算 1 次)
-//
-// 其余 6 列两侧相同:期初 1000、窗口内只有一条 −100、无 type=100/107 流水。
-func rsFanoutExpected() (legacy, target map[string]int64) {
-	signedSum := rsFanoutSignedMoneyCents * rsFanoutK
-	usedPrev := rsFanoutUsedPrevCents
-	usedEnd := rsFanoutUsedEndCents
-
-	common := map[string]int64{
-		"previousBalance":               100000,
-		"currentTotalIncome":            0,
-		"currentTotalActivityMoney":     0,
-		"currentTotalRecommendedAmount": 0,
-		"currentSpend":                  -10000,
-		"currentBalance":                90000,
-	}
-
-	legacy = map[string]int64{
-		"previousBalance":               common["previousBalance"],
-		"previousGiveCost":              signedSum + usedPrev*rsFanoutK,
-		"currentTotalIncome":            common["currentTotalIncome"],
-		"currentTotalActivityMoney":     common["currentTotalActivityMoney"],
-		"currentTotalRecommendedAmount": common["currentTotalRecommendedAmount"],
-		"currentSpend":                  common["currentSpend"],
-		"currentUsedGiveCost":           (signedSum + usedPrev*rsFanoutK) - (signedSum + usedEnd*rsFanoutK),
-		"currentBalance":                common["currentBalance"],
-		"currentGiveCost":               signedSum + usedEnd*rsFanoutK,
-	}
-	target = map[string]int64{
-		"previousBalance":               common["previousBalance"],
-		"previousGiveCost":              signedSum + usedPrev,
-		"currentTotalIncome":            common["currentTotalIncome"],
-		"currentTotalActivityMoney":     common["currentTotalActivityMoney"],
-		"currentTotalRecommendedAmount": common["currentTotalRecommendedAmount"],
-		"currentSpend":                  common["currentSpend"],
-		"currentUsedGiveCost":           (signedSum + usedPrev) - (signedSum + usedEnd),
-		"currentBalance":                common["currentBalance"],
-		"currentGiveCost":               signedSum + usedEnd,
-	}
-	return legacy, target
-}
-
-// rsFanoutExpectedNorm 把一侧的期望渲染成 2 位小数字符串(比对口径)。
-func rsFanoutExpectedNorm(side map[string]int64) map[string]string {
-	out := make(map[string]string, len(side))
-	for k, v := range side {
-		out[k] = yuan(v)
-	}
-	return out
-}
-
-// rsFanoutCase 是 RS-4 的声明体。
-//
-// ⚠ **暂不注册**(见文件头):`Divergence` 求值未实现,注册即 FATAL。
-func rsFanoutCase() ResultSetCase {
-	lgExp, tgExp := rsFanoutExpected()
-	return ResultSetCase{
-		Name: "RS-4 账单·A21 扇出(同一活动 k 条报名 ⇒ used 重复计入)INTENTIONAL-STRICT",
-		Note: "两侧**本来就不该相等**:legacy 把 used 计入 k 次(dump 缺陷),target 去重只计 1 次(刻意不复刻)。" +
-			"绿的含义是「分歧已登记且双方符合各自声明」;两侧一旦相等就红(缺陷被补回 / target 被改坏)," +
-			"分歧扩散到声明外的列也红",
-		LegacyUserID:  rsUserID,
-		InstitutionID: rsInstID,
-		CampusID:      rsCampusID,
-		// LegacyCall / Seed* / CallTarget / Preconditions / ExtraAssertions 下一轮补。
-		Compare: func(lg ResultSet, tg TargetPayload) ResultSetInput {
-			common := make(map[string]string, len(rsFanoutCommonCols()))
-			for _, col := range rsFanoutCommonCols() {
-				common[col] = yuan(lgExp[col])
-			}
-			return ResultSetInput{
-				CaseName:   "RS-4",
-				Legacy:     lg,
-				ExpectRows: 1,
-				Target:     tg,
-				Columns:    rsBillColumns(), // 照抄 RS-2 的 9 列映射
-				Expect:     common,          // 其余 6 列按相等处理
-				Divergence: &Divergence{
-					Reason: "A21 扇出:legacy 对同一活动的 k 条 signed 把 used 额重复计入 k 次;" +
-						"target 按 activity 去重只计 1 次(dump 缺陷,刻意不复刻)",
-					Columns: rsFanoutDivergentCols(),
-					Legacy:  rsFanoutExpectedNorm(lgExp),
-					Target:  rsFanoutExpectedNorm(tgExp),
-				},
-			}
-		},
-	}
-}