第 2 回キャンペーンの突き合わせ結果を反映した。triage の state にコードで計算した 最新値の要約(now: プローブ、1 分 p95、1 分エラー率、再起動数、slo_now_ok)と 直近 5 分のエラーログ抜粋を加え、recovering は slo_now_ok を読むだけにした。 layer の choice を irreversible_needed の noul に置き換え、target_valid は カタログ action のときだけ尋ねる。JEV_MODE=enforce では review の 2 ルール (best_action 不一致かつ確信度 0.9 以上、target_valid 0.5 未満)で提案を escalate に格下げし、判断を res.jev.enforced に記録する。既定は shadow のまま。
51 lines
2.2 KiB
Go
51 lines
2.2 KiB
Go
package agent
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/selfheal-lab/selfheal-lab-apps/ai/internal/jev"
|
|
)
|
|
|
|
func f(v float64) *float64 { return &v }
|
|
|
|
func TestEnforceRules(t *testing.T) {
|
|
a := &Agent{jevEnforce: true}
|
|
// rule A: best_action disagrees with high confidence -> escalate
|
|
res := &Result{}
|
|
res.ProposedAction, res.Target = "net.route_change", ""
|
|
rev := &ShadowRecord{Gate: "review", Answers: map[string]any{"best_action": jev.Answer{Type: "choice", Choice: "escalate", Confidence: f(0.95)}}}
|
|
a.Enforce(res, rev)
|
|
if res.ProposedAction != "escalate" || res.Jev["enforced"].(map[string]any)["rule"] != "best_action_disagrees" {
|
|
t.Fatalf("rule A: %+v %v", res.Diagnosis, res.Jev)
|
|
}
|
|
// rule B: target invalid -> escalate
|
|
res = &Result{}
|
|
res.ProposedAction, res.Target = "k8s.rollout_restart", ""
|
|
rev = &ShadowRecord{Gate: "review", Answers: map[string]any{"best_action": jev.Answer{Type: "choice", Choice: "k8s.rollout_restart", Confidence: f(0.9)}, "target_valid": jev.Answer{Type: "noul", Noul: f(0.2)}}}
|
|
a.Enforce(res, rev)
|
|
if res.ProposedAction != "escalate" || res.Jev["enforced"].(map[string]any)["rule"] != "target_invalid" {
|
|
t.Fatalf("rule B: %+v %v", res.Diagnosis, res.Jev)
|
|
}
|
|
// agreement with valid target -> unchanged; none/escalate -> untouched; shadow mode -> untouched
|
|
res = &Result{}
|
|
res.ProposedAction, res.Target = "k8s.rollout_restart", "api"
|
|
rev = &ShadowRecord{Gate: "review", Answers: map[string]any{"best_action": jev.Answer{Choice: "k8s.rollout_restart", Confidence: f(0.99)}, "target_valid": jev.Answer{Noul: f(0.9)}}}
|
|
a.Enforce(res, rev)
|
|
if res.ProposedAction != "k8s.rollout_restart" || res.Jev != nil {
|
|
t.Fatalf("agreement: %+v", res.Diagnosis)
|
|
}
|
|
res = &Result{}
|
|
res.ProposedAction = "none"
|
|
a.Enforce(res, &ShadowRecord{Answers: map[string]any{"best_action": jev.Answer{Choice: "escalate", Confidence: f(0.99)}}})
|
|
if res.ProposedAction != "none" {
|
|
t.Fatalf("none must not change")
|
|
}
|
|
s := &Agent{jevEnforce: false}
|
|
res = &Result{}
|
|
res.ProposedAction = "k8s.scale"
|
|
s.Enforce(res, &ShadowRecord{Answers: map[string]any{"best_action": jev.Answer{Choice: "escalate", Confidence: f(0.99)}}})
|
|
if res.ProposedAction != "k8s.scale" {
|
|
t.Fatalf("shadow must not change")
|
|
}
|
|
}
|