Thread optional grounding into the coach prompt

The ai.Coach interface gains a grounding parameter carrying standing
context about the user (the knowledge port). buildPrompt injects an
"About the user" block only when grounding is non-empty, so an empty
grounding produces a byte-for-byte unchanged prompt. Drift judge and
nudge are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 08:01:03 -04:00
parent 09061f8e30
commit 0f1790c8d5
2 changed files with 36 additions and 11 deletions
+23 -3
View File
@@ -22,7 +22,7 @@ func (f *fakeBackend) Name() string { return "fake" }
func TestServiceCoachSuccess(t *testing.T) {
fb := &fakeBackend{out: `here you go {"next_action":"Write tests","success_condition":"green","timebox_minutes":30}`}
svc := NewService(fb)
p, err := svc.Coach(context.Background(), "write the tests")
p, err := svc.Coach(context.Background(), "write the tests", "")
if err != nil {
t.Fatalf("coach: %v", err)
}
@@ -36,14 +36,34 @@ func TestServiceCoachSuccess(t *testing.T) {
func TestServiceCoachBackendError(t *testing.T) {
fb := &fakeBackend{err: errors.New("boom")}
if _, err := NewService(fb).Coach(context.Background(), "x"); err == nil {
if _, err := NewService(fb).Coach(context.Background(), "x", ""); err == nil {
t.Fatal("want backend error")
}
}
func TestServiceCoachUnparseable(t *testing.T) {
fb := &fakeBackend{out: "I cannot help with that."}
if _, err := NewService(fb).Coach(context.Background(), "x"); !errors.Is(err, ErrNoJSON) {
if _, err := NewService(fb).Coach(context.Background(), "x", ""); !errors.Is(err, ErrNoJSON) {
t.Fatalf("want ErrNoJSON, got %v", err)
}
}
func TestCoachPromptIncludesGrounding(t *testing.T) {
fb := &fakeBackend{out: `{"next_action":"a","success_condition":"b","timebox_minutes":20}`}
if _, err := NewService(fb).Coach(context.Background(), "ship it", "I value small diffs."); err != nil {
t.Fatalf("coach: %v", err)
}
if !strings.Contains(fb.gotPrompt, "About the user") || !strings.Contains(fb.gotPrompt, "small diffs") {
t.Fatalf("prompt missing grounding block: %s", fb.gotPrompt)
}
}
func TestCoachEmptyGroundingUnchanged(t *testing.T) {
withFb := &fakeBackend{out: `{"next_action":"a","success_condition":"b","timebox_minutes":20}`}
_, _ = NewService(withFb).Coach(context.Background(), "ship it", "")
// The empty-grounding prompt must equal buildPrompt(intent) of the old shape:
// it must NOT contain the grounding header.
if strings.Contains(withFb.gotPrompt, "About the user") {
t.Fatalf("empty grounding leaked a header: %s", withFb.gotPrompt)
}
}