Wire the reviewer and assert reflection on the wire

main injects the AI service as the reviewer alongside the other roles.
A web test drives a session to Review and asserts the recap rides the
state payload, then to the next Planning and asserts the carry-forward
does too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 09:38:13 -04:00
parent a4f0c00cac
commit ff100b2472
2 changed files with 47 additions and 1 deletions
+2 -1
View File
@@ -45,7 +45,8 @@ func main() {
ctrl.SetCoach(svc) ctrl.SetCoach(svc)
ctrl.SetDriftJudge(svc) ctrl.SetDriftJudge(svc)
ctrl.SetNudge(svc) ctrl.SetNudge(svc)
log.Printf("ai: %s backend (coach + drift judge + nudge)", backend.Name()) ctrl.SetReviewer(svc)
log.Printf("ai: %s backend (coach + drift judge + nudge + reviewer)", backend.Name())
} }
// Wire the Tasks port: Amazing Marvin via ampy's `am` CLI. The command is // Wire the Tasks port: Amazing Marvin via ampy's `am` CLI. The command is
+45
View File
@@ -317,6 +317,51 @@ func TestPlanningStatePayloadCarriesKnowledge(t *testing.T) {
} }
} }
type stubReviewer struct {
refl ai.Reflection
}
func (s stubReviewer) Review(ctx context.Context, finished, history string) (ai.Reflection, error) {
return s.refl, nil
}
func TestReflectionFlowsToReviewThenPlanning(t *testing.T) {
s := newTestServer(t)
s.ctrl.SetReviewer(stubReviewer{refl: ai.Reflection{Recap: "held focus well", CarryForward: "start in the editor"}})
r := s.Router()
_ = post(t, r, "/planning", "")
body := `{"next_action":"a","success_condition":"b","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())
}
if w := post(t, r, "/complete", ""); w.Code != http.StatusOK {
t.Fatalf("/complete code %d", w.Code)
}
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
if rv := s.ctrl.State().Reflection; rv != nil && rv.Status == "ready" {
break
}
time.Sleep(5 * time.Millisecond)
}
js := s.stateJSON()
if !strings.Contains(js, `"recap":"held focus well"`) {
t.Fatalf("review payload missing recap: %s", js)
}
// End -> Locked -> Planning: the carry-forward should surface on planning.
if w := post(t, r, "/end", ""); w.Code != http.StatusOK {
t.Fatalf("/end code %d", w.Code)
}
if w := post(t, r, "/planning", ""); w.Code != http.StatusOK {
t.Fatalf("/planning code %d", w.Code)
}
js2 := s.stateJSON()
if !strings.Contains(js2, `"carry_forward":"start in the editor"`) {
t.Fatalf("planning payload missing carry-forward: %s", js2)
}
}
func TestKnowledgePathSelectionReloads(t *testing.T) { func TestKnowledgePathSelectionReloads(t *testing.T) {
s := newTestServer(t) s := newTestServer(t)
s.ctrl.SetKnowledge(stubSource{profile: knowledge.Profile{Path: "/default"}}) s.ctrl.SetKnowledge(stubSource{profile: knowledge.Profile{Path: "/default"}})