Minimize the off-task window on confirmed drift at the block level
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -558,6 +558,91 @@ func TestRestoredActiveSessionCanBeJudged(t *testing.T) {
|
||||
waitDriftStatus(t, second, "drifting")
|
||||
}
|
||||
|
||||
type fakeGuard struct{ calls int32 }
|
||||
|
||||
func (g *fakeGuard) MinimizeActive(context.Context) error {
|
||||
atomic.AddInt32(&g.calls, 1)
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitGuardCalls(t *testing.T, g *fakeGuard, min int32) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
if atomic.LoadInt32(&g.calls) >= min {
|
||||
return
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("guard minimize calls never reached %d (got %d)", min, atomic.LoadInt32(&g.calls))
|
||||
}
|
||||
|
||||
func TestDriftMinimizesAtBlockViaBothPaths(t *testing.T) {
|
||||
c, _ := newTestController(t)
|
||||
c.SetDriftJudge(&fakeJudge{verdict: ai.Verdict{OnTask: false, Reason: "off-task"}})
|
||||
g := &fakeGuard{}
|
||||
c.SetGuard(g)
|
||||
startActiveLevel(t, c, []string{"code"}, domain.EnforcementBlock)
|
||||
|
||||
// Async path: first off-task observation launches the judge; on confirmation
|
||||
// the guard minimizes once.
|
||||
c.RecordWindow(obs("firefox", "YouTube"))
|
||||
st := waitDriftStatus(t, c, "drifting")
|
||||
if !st.Drift.Enforced {
|
||||
t.Fatalf("Enforced should be true while drifting at block: %+v", st.Drift)
|
||||
}
|
||||
waitGuardCalls(t, g, 1)
|
||||
|
||||
// Synchronous cached path: same class again hits the per-class cache, sets
|
||||
// drifting under the lock, and minimizes again without a new judgment.
|
||||
c.RecordWindow(obs("firefox", "Reddit"))
|
||||
waitGuardCalls(t, g, 2)
|
||||
if got := atomic.LoadInt32(&g.calls); got < 2 {
|
||||
t.Fatalf("cached-drift path did not minimize: %d calls", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWarnLevelNeverMinimizes(t *testing.T) {
|
||||
c, _ := newTestController(t)
|
||||
c.SetDriftJudge(&fakeJudge{verdict: ai.Verdict{OnTask: false, Reason: "off-task"}})
|
||||
g := &fakeGuard{}
|
||||
c.SetGuard(g)
|
||||
startActiveLevel(t, c, []string{"code"}, domain.EnforcementWarn)
|
||||
|
||||
c.RecordWindow(obs("firefox", "YouTube"))
|
||||
st := waitDriftStatus(t, c, "drifting")
|
||||
if st.Drift.Enforced {
|
||||
t.Fatalf("Enforced must be false at warn: %+v", st.Drift)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond) // allow any stray enforcement goroutine to run
|
||||
if got := atomic.LoadInt32(&g.calls); got != 0 {
|
||||
t.Fatalf("warn level must not minimize, got %d calls", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOnTaskNeverMinimizes(t *testing.T) {
|
||||
c, _ := newTestController(t)
|
||||
c.SetDriftJudge(&fakeJudge{verdict: ai.Verdict{OnTask: false, Reason: "should not be called"}})
|
||||
g := &fakeGuard{}
|
||||
c.SetGuard(g)
|
||||
startActiveLevel(t, c, []string{"code"}, domain.EnforcementBlock)
|
||||
|
||||
c.RecordWindow(obs("code", "main.go")) // local on-task match
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if got := atomic.LoadInt32(&g.calls); got != 0 {
|
||||
t.Fatalf("on-task must not minimize, got %d calls", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockWithoutGuardDoesNotPanic(t *testing.T) {
|
||||
c, _ := newTestController(t)
|
||||
c.SetDriftJudge(&fakeJudge{verdict: ai.Verdict{OnTask: false, Reason: "off-task"}})
|
||||
// No guard wired.
|
||||
startActiveLevel(t, c, []string{"code"}, domain.EnforcementBlock)
|
||||
c.RecordWindow(obs("firefox", "YouTube"))
|
||||
waitDriftStatus(t, c, "drifting") // must reach drifting without panicking
|
||||
}
|
||||
|
||||
func TestLeavingPlanningClearsCoach(t *testing.T) {
|
||||
c, _ := newTestController(t)
|
||||
_ = c.EnterPlanning()
|
||||
|
||||
Reference in New Issue
Block a user