From 63418d106fdfb204ba67188090867defcb8295d3 Mon Sep 17 00:00:00 2001 From: Felix Martin Date: Sun, 31 May 2026 12:52:55 -0400 Subject: [PATCH] M1: broadcast focus updates, expiry uses Expire outcome Co-Authored-By: Claude Opus 4.8 --- internal/web/web.go | 8 +++++--- internal/web/web_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/internal/web/web.go b/internal/web/web.go index 044f4d8..9529d56 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -31,7 +31,9 @@ type Server struct { } func NewServer(ctrl *session.Controller) *Server { - return &Server{ctrl: ctrl, bcast: NewBroadcaster()} + s := &Server{ctrl: ctrl, bcast: NewBroadcaster()} + ctrl.SetOnChange(s.broadcast) + return s } func (s *Server) Router() *gin.Engine { @@ -154,7 +156,7 @@ func (s *Server) armExpiry() { d = 0 } s.timer = time.AfterFunc(d, func() { - if err := s.ctrl.Complete(); err == nil { + if err := s.ctrl.Expire(); err == nil { s.broadcast() } }) @@ -179,7 +181,7 @@ func (s *Server) Init() { s.armExpiry() return } - if err := s.ctrl.Complete(); err == nil { + if err := s.ctrl.Expire(); err == nil { s.broadcast() } } diff --git a/internal/web/web_test.go b/internal/web/web_test.go index 34b1a05..dc4dd66 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -8,6 +8,7 @@ import ( "testing" "antidrift/internal/domain" + "antidrift/internal/evidence" "antidrift/internal/session" "github.com/gin-gonic/gin" @@ -69,3 +70,31 @@ func TestIllegalTransitionReturns409(t *testing.T) { t.Fatalf("expected 409, got %d", w.Code) } } + +func TestActiveStatePayloadCarriesEvidence(t *testing.T) { + s := newTestServer(t) + r := s.Router() + _ = post(t, r, "/planning", "") + body := `{"next_action":"Build web","success_condition":"web tests pass","timebox_secs":1500}` + if w := post(t, r, "/commitment", body); w.Code != http.StatusOK { + t.Fatalf("/commitment code %d body %s", w.Code, w.Body.String()) + } + // A focus update should appear in the serialized state. + s.ctrl.RecordWindow(evidence.WindowSnapshot{Class: "code", Title: "antidrift", Health: evidence.EvidenceHealth{Available: true}}) + + js := s.stateJSON() + if !strings.Contains(js, `"evidence"`) { + t.Fatalf("payload missing evidence object: %s", js) + } + if !strings.Contains(js, `"class":"code"`) { + t.Fatalf("payload missing current window: %s", js) + } +} + +func TestLockedStateHasNullEvidence(t *testing.T) { + s := newTestServer(t) + js := s.stateJSON() + if !strings.Contains(js, `"evidence":null`) { + t.Fatalf("locked payload should have null evidence: %s", js) + } +}