feat(backthink): 自排 C1-2 本体 + TriggerUnknown 修 fail-open(V1)
- selfwake.go:WakeNow + 待跑槽容量 1(先探槽合并不过闸、槽空才过闸、同锁)+ 闸 4 轮/小时
($DATA_DIR/config/backthink.json,零新 env 键)+ 审计四条 what + 启动日志两键 + 组合根接线
- G-A(主人已批):TriggerUnknown 作零值、既有两值编号后移(语义不动),未知触发走**最紧分支**
- 5 个变异自检(拆闸/≤0 未回落/过闸挪到探槽前=G1 失效形态/槽无界/零值挪走)
⚠️ 服务侧已就绪但**她仍调不到**(自排工具面属 C1-3)⇒ 不得写「C 已完成」
This commit is contained in:
1 parent
301f8b4276
commit
ed5005307a
12 files changed
+1193
-8
No files matched your search
@@ -286,3 +286,59 @@ func TestDecide_ActivityGate_SelfWakeNeedsDeclaredUrgency(t *testing.T) {
|
||||
t.Fatalf("long idle heartbeat should still push (07.4.3), got %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTriggerKind_ZeroValueIsUnknownAndTight 是 G-A 修法(design/12 §C.4 的零值语义,
|
||||
// 主人 2026-10-06 已批)的探针:`TriggerKind` 的零值必须是 **TriggerUnknown**,且它
|
||||
// 在判定侧走**最紧**分支 —— 长期静默段里不豁免(`{Unknown, Low/Normal}` 扣下)、
|
||||
// 推送侧不被当成事件(照旧过 PushGate)。
|
||||
//
|
||||
// 它守的是 fail-open 的方向:改动前零值 = TriggerHeartbeat,「漏填 Kind」静默变成
|
||||
// 「拿最宽的豁免」。变异:把 TriggerUnknown 挪出零值(如让 TriggerHeartbeat 回到 0),
|
||||
// 或把零值分支混进心跳豁免 ⇒ 本用例变红。
|
||||
func TestTriggerKind_ZeroValueIsUnknownAndTight(t *testing.T) {
|
||||
t.Parallel()
|
||||
var zero TriggerKind
|
||||
if zero != TriggerUnknown {
|
||||
t.Fatalf("TriggerKind 的零值 = %v(%d),want TriggerUnknown", zero, int(zero))
|
||||
}
|
||||
if TriggerUnknown != 0 {
|
||||
t.Fatalf("TriggerUnknown = %d, want 0(零值必须是最紧的那一档)", int(TriggerUnknown))
|
||||
}
|
||||
if got := TriggerUnknown.String(); got != "unknown" {
|
||||
t.Fatalf("TriggerUnknown.String() = %q, want %q", got, "unknown")
|
||||
}
|
||||
// 既有三个值的标签一个字都不许变(落库/面板/审计都以它们为真源)。
|
||||
for kind, want := range map[TriggerKind]string{
|
||||
TriggerHeartbeat: "heartbeat",
|
||||
TriggerEvent: "event",
|
||||
TriggerSelfWake: SelfWakeSource,
|
||||
} {
|
||||
if got := kind.String(); got != want {
|
||||
t.Fatalf("TriggerKind(%d).String() = %q, want %q", int(kind), got, want)
|
||||
}
|
||||
}
|
||||
|
||||
b := testBackthink()
|
||||
longIdle := DecisionInput{
|
||||
Trigger: "unknown.source",
|
||||
At: dayAt(),
|
||||
UserState: UserState{Boredom: 0.9},
|
||||
LastActivity: dayAt().Add(-3 * time.Hour),
|
||||
HasActivity: true,
|
||||
ConclusionPush: true,
|
||||
Kind: TriggerUnknown, // 零值:漏填 Kind 的等价形态
|
||||
}
|
||||
for _, imp := range []Importance{ImportanceLow, ImportanceNormal} {
|
||||
in := longIdle
|
||||
in.Import = imp
|
||||
if got := b.Decide(in); got.ShouldPush {
|
||||
t.Fatalf("未知触发(Import=%v)在长期静默段必须扣下,got %+v", imp, got)
|
||||
}
|
||||
}
|
||||
// 高重要度仍按「值得说」的既有规则放行:G-A 收的是**豁免**,不是把未知一律封死。
|
||||
high := longIdle
|
||||
high.Import = ImportanceHigh
|
||||
if got := b.Decide(high); !got.ShouldPush {
|
||||
t.Fatalf("未知触发但重要度高(>= High)应放行,got %+v", got)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user