pi 2026-09-14 三条(他接受了我对"恒红=相位错"的反驳,但指出 Skip 带来的两处漏洞)。
1. **Skip 必须进余额、条件必须是测量**:
- **条件**:跳过与否由 `measureMailStatusDebt` **实测**(详情路径的 status 是否真的
等于按读者派生),不是常量、不是"我们还没迁完"这种没人会更新的事实;
- **余额**:`docs/DEBTS.json` 是**唯一登记**,Go 侧判据 `TestDebtLedgerMatchesMeasurement`
**自己测量**后与登记比对(第一版我让余额由另一条判据写入 ⇒ **排序依赖**,
Go 同包内按源文件顺序跑,登记那条先跑就读到 0 —— 排序依赖是隐蔽的假绿,已抽成自足函数);
- **可见性**:`go test` 跑通时**不打印包的输出**,我第一版把余额打在 TestMain 里,
常态运行一个字都看不见 —— 正是 pi 说的"不显形"。所以常态可见的那份打在
electron 套件的 RESULT 行:`RESULT phase=install static=5 debts=7
(static-criteria:5,mails-status-derived:1,gesture-semantics:1) probe=ok`。
2. **豁免从"按文件"改成"按文件 + 次数"**:`migrate.go` 这类比较**上限 2 处**(附理由),
多一处即红。我在读侧清册上自己修过这个洞,豁免那格却退了一格 —— pi 指出得对。
3. **三笔欠账合成一处**:原先各自表达(`RESULT static=5` / `t.Skip` 无余额 /
文档里的到期前提无余额),**没有一处能一眼看全**。现在统一登记在 `docs/DEBTS.json`
(id / 余额 / 到期前提 / 判据位置),两端读同一份:Go 侧比对实测,electron 侧打进 RESULT 行。
还清那天:登记要跟着清 —— 不清则由 `TestDebtLedgerMatchesMeasurement` 报
"**欠账已还清**,但登记还记着 N"(还清是可测事件,这正是那条判据存在的意义)。
153 lines
5.1 KiB
Go
153 lines
5.1 KiB
Go
package repo
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"sort"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
/*
|
||
欠账的**单一余额**(pi 2026-09-14 裁定 §3)。
|
||
|
||
背景:到这轮为止已经有三笔不同类型、各自表达方式的欠账 ——
|
||
鸿蒙静态判据(`RESULT static=5`,有余额、有到期探针)、
|
||
`mails.status` 行级派生化(`t.Skip`,**原先无余额**)、
|
||
手势语义契约(登记的到期前提,**原先无余额**)。
|
||
三笔都"可判",但**没有一处能一眼看全**;而"欠账不显形就等于没有"。
|
||
|
||
所以:**登记在一处、可打印**。Go 这一侧打印在 TestMain 收尾,
|
||
跨端的净值同时登记在 `docs/DEBTS.json`(两端都能读,见该文件)。
|
||
*/
|
||
|
||
type debt struct {
|
||
ID string
|
||
Count int
|
||
Due string // 到期前提(什么时候该还清)
|
||
Where string // 判据在哪
|
||
}
|
||
|
||
var debts = map[string]debt{
|
||
"mails-status-derived": {
|
||
ID: "mails-status-derived",
|
||
Count: 0, // 由 mail_status_derived_test.go **实测**后标 1(不许手写:常量余额 = 永久 Skip 的死法)
|
||
Due: "详情/线程改为按读者派生(readStateFor)之后 —— 那时 mail_status_derived_test.go 从 Skip 转实跑",
|
||
Where: "server/internal/repo/mail_status_derived_test.go",
|
||
},
|
||
"static-criteria": {
|
||
ID: "static-criteria",
|
||
Count: 5,
|
||
Due: "本工作区能装、能点设备(探针三值转 true 时自动变红)",
|
||
Where: "client/electron/test/run-all.mjs(STATIC_ONLY)",
|
||
},
|
||
"gesture-semantics": {
|
||
ID: "gesture-semantics",
|
||
Count: 1,
|
||
Due: "P6 第 3 步:鸿蒙侧出现滑动手势代码时立即建(此前建 = 只有一端存在的假判据)",
|
||
Where: "docs/HARMONY-ALIGN-PLAN.md P6 段",
|
||
},
|
||
}
|
||
|
||
// debtMark 由各条欠账的判据在"确认未清"时调用(余额来自**测量**,不是常量)。
|
||
func debtMark(id string) {
|
||
d := debts[id]
|
||
d.Count = 1
|
||
debts[id] = d
|
||
}
|
||
|
||
type debtLedger struct {
|
||
Debts []struct {
|
||
ID string `json:"id"`
|
||
Count int `json:"count"`
|
||
Due string `json:"due"`
|
||
Where string `json:"where"`
|
||
} `json:"debts"`
|
||
}
|
||
|
||
// loadDebts 读**那一份**登记(docs/DEBTS.json):两端的余额必须来自同一处。
|
||
func loadDebts(t *testing.T) debtLedger {
|
||
t.Helper()
|
||
b, err := os.ReadFile(filepath.Join("..", "..", "..", "docs", "DEBTS.json"))
|
||
if err != nil {
|
||
t.Fatalf("读欠账登记 docs/DEBTS.json 失败:%v(登记丢了 = 欠账不显形)", err)
|
||
}
|
||
var l debtLedger
|
||
if err := json.Unmarshal(b, &l); err != nil {
|
||
t.Fatalf("欠账登记不是合法 JSON:%v", err)
|
||
}
|
||
return l
|
||
}
|
||
|
||
/*
|
||
★ 登记与**实测**必须一致(pi 2026-09-14 裁定 §1:Skip 的条件要是测量结果,"跳过"要进余额)。
|
||
|
||
这条判据挡的是两种死法:
|
||
① 条件被写死:余额不是从测量来的,而是某人抄的数字 ⇒ 这里用 debtMark 的实测值比对;
|
||
② 跳过不可数:`go test` 对 Skip 是退出码 0、`--- SKIP` 只是一行输出
|
||
|
||
(而且**跑得通时 go test 根本不打印包的输出** —— 我第一次就把余额打在 TestMain 里,
|
||
结果常态运行时一个字都看不见,正是"不显形")。所以可见的那份在
|
||
electron 套件的 RESULT 行(它读同一个文件),这里保证两处**同源**。
|
||
*/
|
||
func TestDebtLedgerMatchesMeasurement(t *testing.T) {
|
||
l := loadDebts(t)
|
||
seen := map[string]bool{}
|
||
for _, d := range l.Debts {
|
||
seen[d.ID] = true
|
||
if d.Due == "" || d.Where == "" {
|
||
t.Fatalf("欠账 %s 必须写明到期前提与判据位置(否则它只是「存在」,不是「欠账」)", d.ID)
|
||
}
|
||
}
|
||
for _, id := range []string{"static-criteria", "mails-status-derived", "gesture-semantics"} {
|
||
if !seen[id] {
|
||
t.Fatalf("欠账登记里缺 %s —— 三笔必须同处登记,否则审计只会找到一处就当全部", id)
|
||
}
|
||
}
|
||
// 本包能实测的那一笔:**自己测**(不依赖别的测试先跑过 —— 排序依赖是隐蔽的假绿)
|
||
var want int
|
||
for _, d := range l.Debts {
|
||
if d.ID == "mails-status-derived" {
|
||
want = d.Count
|
||
}
|
||
}
|
||
outstanding, detail, derived := measureMailStatusDebt(t)
|
||
got := 0
|
||
if outstanding {
|
||
got = 1
|
||
}
|
||
if got != want {
|
||
if got == 0 {
|
||
t.Fatalf("**欠账已还清**(详情 %q 已等于按读者派生 %q),但 docs/DEBTS.json 还记着 %d —— "+
|
||
"还清是可测事件,登记要跟着清(这正是这条判据存在的意义)", detail, derived, want)
|
||
}
|
||
t.Fatalf("mails-status-derived 的余额:登记说 %d,实测说 %d —— 登记与测量分叉了", want, got)
|
||
}
|
||
}
|
||
|
||
// debtSummary 供 TestMain 打印:一处能看全的余额 + 到期前提。
|
||
func debtSummary() string {
|
||
ids := make([]string, 0, len(debts))
|
||
total := 0
|
||
for id, d := range debts {
|
||
if d.Count > 0 {
|
||
ids = append(ids, id)
|
||
total += d.Count
|
||
}
|
||
}
|
||
if len(ids) == 0 {
|
||
return " debts=0(全部还清)"
|
||
}
|
||
sort.Strings(ids)
|
||
var b strings.Builder
|
||
fmt.Fprintf(&b, "\n======= 欠账余额 debts=%d =======", total)
|
||
for _, id := range ids {
|
||
d := debts[id]
|
||
fmt.Fprintf(&b, "\n · %s ×%d\n 判据:%s\n 到期:%s", d.ID, d.Count, d.Where, d.Due)
|
||
}
|
||
fmt.Fprintf(&b, "\n(三笔合在一处看全:分散登记时,审计只会找到一处就当全部)")
|
||
return b.String()
|
||
}
|