diff --git a/cmd/antidriftd/main.go b/cmd/antidriftd/main.go index 012abac..1b9cc30 100644 --- a/cmd/antidriftd/main.go +++ b/cmd/antidriftd/main.go @@ -11,6 +11,7 @@ import ( "time" "antidrift/internal/ai" + "antidrift/internal/enforce" "antidrift/internal/evidence" "antidrift/internal/knowledge" "antidrift/internal/session" @@ -72,6 +73,9 @@ func main() { log.Printf("status: writing %s", statusPath) } + ctrl.SetGuard(enforce.NewGuard()) + log.Printf("enforce: window-minimize guard") + src := evidence.NewSource() go src.Watch(context.Background(), ctrl.RecordWindow) diff --git a/internal/web/web_test.go b/internal/web/web_test.go index 834006f..2de24f4 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -362,6 +362,37 @@ func TestReflectionFlowsToReviewThenPlanning(t *testing.T) { } } +type stubJudge struct{ verdict ai.Verdict } + +func (j stubJudge) JudgeDrift(ctx context.Context, commitment, class, title string) (ai.Verdict, error) { + return j.verdict, nil +} + +func TestEnforceTogglePutsEnforcedOnTheWire(t *testing.T) { + s := newTestServer(t) + r := s.Router() + s.ctrl.SetDriftJudge(stubJudge{verdict: ai.Verdict{OnTask: false, Reason: "off-task"}}) + + if w := post(t, r, "/planning", ""); w.Code != http.StatusOK { + t.Fatalf("/planning code %d", w.Code) + } + body := `{"next_action":"a","success_condition":"b","timebox_secs":1500,"allowed_window_classes":["code"],"enforce":true}` + if w := post(t, r, "/commitment", body); w.Code != http.StatusOK { + t.Fatalf("/commitment code %d body %s", w.Code, w.Body.String()) + } + + s.ctrl.RecordWindow(evidence.WindowSnapshot{Class: "firefox", Title: "YouTube", Health: evidence.EvidenceHealth{Available: true}}) + + deadline := time.Now().Add(2 * time.Second) + for time.Now().Before(deadline) { + if strings.Contains(s.stateJSON(), `"enforced":true`) { + return + } + time.Sleep(5 * time.Millisecond) + } + t.Fatalf("state never reported enforced:true; last: %s", s.stateJSON()) +} + func TestKnowledgePathSelectionReloads(t *testing.T) { s := newTestServer(t) s.ctrl.SetKnowledge(stubSource{profile: knowledge.Profile{Path: "/default"}})