feat(backthink): 静默期自排 C 的判定侧(C1-1,切片三之一)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 追加 TriggerSelfWake(不动既有编号)+ SelfWakeRequest(走既有 urgency 声明通道,不新造第二条)
- escalateImport 折算收成唯一 foldDeclared(事件轮与自排轮共用、只上抬取较高档)
- DecisionInput.Heartbeat bool → Kind TriggerKind;心跳仍免检、**自排不享心跳豁免**(要求声明 ≥ warning)
- push 与档位分离:push.go 零改动即自动正确(eventDriven 对 self_wake 为 false ⇒ 照旧过 PushGate)
- 4 个变异自检(折算空操作/删自排分支/常量传心跳/非事件一律豁免)
⚠️ 本片只是判定侧:WakeNow/worker/闸属 C1-2,工具面与提示词属 C1-3 ⇒ **不得写『C 已完成』**
⚠️ 遗留 G-A(建议登记进 design/12 C.4):TriggerKind 零值=TriggerHeartbeat ⇒ 漏填=最宽豁免(fail-open);
   本片已在字段注释与单测里显式化,但规格侧应补一句或引入 TriggerUnknown
This commit is contained in:
AskaEth committed 2026-10-06 18:46:27 +08:00
1 parent ea43f07bce
commit 94da6bcd6d
8 files changed
+353 -26

No files matched your search

+20 -8
View File
@@ -43,7 +43,8 @@ func (i Importance) String() string {
// longIdleThreshold 是主人最后一条消息之后的间隔,超过它活动门
// 只放行真正重要的事件——但心跳轮除外,
// 它们永远不会被长期静默的间隔拦下(裁决 07.4.3)。编译期
// 它们永远不会被长期静默的间隔拦下(裁决 07.4.3)。自排轮也不享这条豁免
// (design/12 §N C.4):要求她声明 ≥ warning 才放行。编译期
// 常量,因此不出现魔法数字。
const longIdleThreshold = 2 * time.Hour
@@ -95,9 +96,15 @@ type DecisionInput struct {
// ConclusionPush 是她自己思考结论的推送意愿(design/20 §2 P2):「想不想说」
// 由她的结论决定,本决策只回答「此刻能不能说」。
ConclusionPush bool
// Heartbeat 标记本轮是否为心跳类触发(周期心跳或启动轮)。裁决 07.4.3:
// 长期静默段不拦心跳——主人久未回应时,主动关心仍可照常发出。
Heartbeat bool
// Kind 是本轮的触发类别(周期心跳 / 启动轮 / 真实事件 / 自我唤醒),取代了
// 原来的布尔 Heartbeat —— 布尔表达不了三种触发(design/12 §N C.4)。判定侧只
// 需要区分「周期性心跳(含启动轮,享长期静默豁免)」与「她自己排起的一轮
// (要求她说得出理由)」,其余类别按事件处理。
//
// ⚠️ TriggerKind 的零值是 TriggerHeartbeat:生产调用方**必须**显式传 tr.Kind
// (backthink.go 的 think 是唯一调用点,已显式传)。省略 Kind 等于按心跳豁免,
// 那是最宽的一档,别让「漏填」静默变成「免检」。
Kind TriggerKind
}
// Decision 是 Decide 的结果。
@@ -120,7 +127,8 @@ type Decision struct {
// 4. 其余时段 → 放行(是否值得说已由她的结论回答)。
//
// 通过硬门后再过活动软门:刚聊过天或主人久未回应时酌情扣下;心跳类触发在
// 长期静默段不拦(裁决 07.4.3,主动关心照常发出)。频率门(PushGate)在推送器
// 长期静默段不拦(裁决 07.4.3,主动关心照常发出),自排轮则要求她说得出理由
// (design/12 §N C.4)。频率门(PushGate)在推送器
// 内部单独生效,不在此处。
func (b *Backthink) Decide(in DecisionInput) Decision {
if !in.ConclusionPush {
@@ -166,7 +174,8 @@ func (b *Backthink) baseDecide(in DecisionInput) Decision {
// 不在聊天中途插话;
// - 超过 longIdleThreshold 未说话 → 主人久未回应,仅真正重要的事件值得唤醒;
// 心跳类触发(含启动轮)例外照常放行(裁决 07.4.3:长期静默段不拦心跳,
// 主动关心仍可发出);
// 主动关心仍可发出);她自己排起的一轮**不享**这条豁免(design/12 §N C.4)
// ——要求她说得出理由(Import ≥ High,即声明 ≥ warning),否则扣下;
// - 其间与活动状态未知 → 不软化,照硬门结论通过。
//
// 活动是软信号:硬门(baseDecide)独立生效,频率门(PushGate)在推送器内部生效。
@@ -182,12 +191,15 @@ func (b *Backthink) activityHeld(in DecisionInput) (bool, string) {
return true, "owner just chatted"
}
if since >= longIdleThreshold {
if in.Heartbeat {
return false, ""
if in.Kind == TriggerHeartbeat {
return false, "" // 07.4.3 原样:例行心跳(含启动轮)不拦
}
if in.Import >= ImportanceHigh {
return false, ""
}
if in.Kind == TriggerSelfWake {
return true, "self-initiated wake held: owner away and no declared urgency"
}
return true, "owner away a long while"
}
return false, ""