From 09066e1e4e928487bea0658492fd8573607c77c3 Mon Sep 17 00:00:00 2001 From: Claudomator Agent Date: Wed, 8 Jul 2026 09:11:14 +0000 Subject: feat(agentchannel,executor): add ReportVerdict for structured approve/reject signals --- internal/executor/channel_test.go | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'internal/executor/channel_test.go') diff --git a/internal/executor/channel_test.go b/internal/executor/channel_test.go index 84cad89..23f0661 100644 --- a/internal/executor/channel_test.go +++ b/internal/executor/channel_test.go @@ -607,3 +607,65 @@ func TestStoreChannel_ProposeRoleConfig_EmitsRoleConfigProposedEvent(t *testing. t.Errorf("payload mismatch: %+v (want role=evaluator_quality version=%d)", payload, version) } } + +// TestStoreChannel_ReportVerdict_EmitsVerdictReportedEvent proves calling +// ReportVerdict records a KindVerdictReported event attached to the +// *calling task's* own ID, payload {approved, reasoning} — +// internal/scheduler.StoryOrchestrator's finalizeArbitration later reads an +// arbitration task's own event stream for this to decide REVIEW_READY vs +// NEEDS_FIX. Mirrors TestStoreChannel_ProposeRoleConfig_EmitsRoleConfigProposedEvent. +func TestStoreChannel_ReportVerdict_EmitsVerdictReportedEvent(t *testing.T) { + store := &fakeChannelStore{} + ch := newStoreChannel(store, "arbitration-task-1") + + if err := ch.ReportVerdict(context.Background(), false, "the CSS change breaks the running-log panel"); err != nil { + t.Fatalf("ReportVerdict: %v", err) + } + + if len(store.createdEvents) != 1 { + t.Fatalf("expected 1 event, got %d", len(store.createdEvents)) + } + ev := store.createdEvents[0] + if ev.Kind != event.KindVerdictReported { + t.Errorf("expected KindVerdictReported, got %q", ev.Kind) + } + if ev.Actor != event.ActorAgent { + t.Errorf("expected ActorAgent, got %q", ev.Actor) + } + if ev.TaskID != "arbitration-task-1" { + t.Errorf("expected event attached to calling task ID, got %q", ev.TaskID) + } + + var payload struct { + Approved bool `json:"approved"` + Reasoning string `json:"reasoning"` + } + if err := json.Unmarshal(ev.Payload, &payload); err != nil { + t.Fatalf("unmarshal payload: %v", err) + } + if payload.Approved != false || payload.Reasoning != "the CSS change breaks the running-log panel" { + t.Errorf("unexpected payload: %+v", payload) + } +} + +// TestStoreChannel_ReportVerdict_Approved proves the approved=true case +// round-trips correctly too — not just the rejection path exercised above. +func TestStoreChannel_ReportVerdict_Approved(t *testing.T) { + store := &fakeChannelStore{} + ch := newStoreChannel(store, "arbitration-task-2") + + if err := ch.ReportVerdict(context.Background(), true, "meets all acceptance criteria"); err != nil { + t.Fatalf("ReportVerdict: %v", err) + } + + var payload struct { + Approved bool `json:"approved"` + Reasoning string `json:"reasoning"` + } + if err := json.Unmarshal(store.createdEvents[0].Payload, &payload); err != nil { + t.Fatalf("unmarshal payload: %v", err) + } + if !payload.Approved { + t.Error("expected approved=true to round-trip as true") + } +} -- cgit v1.2.3