Repository navigation
Expand file tree
/
Copy pathcardanswer_test.go
More file actions
122 lines (116 loc) · 4.58 KB
/
Copy pathcardanswer_test.go
File metadata and controls
122 lines (116 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package session
import (
"context"
"encoding/json"
"strings"
"sync/atomic"
"testing"
"github.com/Agent-Field/agentfield/sdk/go/ai"
"github.com/Agent-Field/codeaf/internal/automation"
)
// A PERSON'S CARD ANSWER IS ON THE PAGE THE CHECKER READS, and a gap that
// answer already chose does not carry the turn on.
//
// The measured failure: the person declined the card, the tool said nothing
// was set up, the digest clipped that result, and the checker raised "the
// recurring reminder was never set up" three times. This drives [Agent.checkerPage]
// through [Agent.checkpointReopen]. A checker that cannot see the answer line
// raises the gap, so the test is red without the line on the page.
func TestAPersonsCardAnswerReachesTheCheckerAndClosesTheGap(t *testing.T) {
const gap = "The recurring reminder was never set up"
var page atomic.Value
var done atomic.Int64
steps := make([]step, 20)
for index := range steps {
steps[index] = func(_ context.Context, messages []ai.Message) (*ai.Response, error) {
if askedForSketch(messages) {
return textResponse("one job, still in this conversation"), nil
}
if askedForHandoff(messages) {
return textResponse("a draft of what is left"), nil
}
if askedToWriteHandoff(messages) {
return textResponse("a brief somebody could work from"), nil
}
if askedForRemains(messages) {
shown := messageText(messages[len(messages)-1])
page.Store(shown)
if strings.Contains(shown, "Don't save") {
return textResponse(checkpointNothingLeft), nil
}
return textResponse(gap), nil
}
switch call := done.Add(1); call {
case 1:
return toolResponse("s1", "automation", aRepeatingReminder()), nil
case 2:
// The last act is a write, so the cheap turn is still read
// (checkpoint.go's exposure gate) without climbing a mark.
return toolResponse("w1", "write", `{"path":"note.txt","content":"time to leave"}`), nil
default:
return textResponse("I stopped. The reminder was never set up."), nil
}
}
}
store := automationStoreFor(t)
completer := &scriptedCompleter{steps: steps}
agent := checkpointWritingAgent(t, completer, func(config *Config) {
config.Automations = &Automations{Store: store, Zone: "UTC"}
})
events, err := agent.Submit(watchedContext(agent), "check the marketing slack every 3 hours")
if err != nil {
t.Fatalf("Submit: %v", err)
}
collected := drainAnsweringAutomation(t, events, func(event Event) {
if err := agent.ResolveQuestion(Answer{
Kind: QuestionAutomation, ID: event.Automation.ID, Key: DeclineKey,
}); err != nil {
t.Errorf("ResolveQuestion: %v", err)
}
})
shown, _ := page.Load().(string)
if !strings.Contains(shown, `the person answered the card "wants to remind you": Don't save`) {
t.Fatalf("the checker was not shown the card answer:\npage:\n%s\ntranscript:\n%s\nevents: %v", shown, transcriptText(agent), kinds(collected))
}
if strings.Contains(transcriptText(agent), checkpointCarryOnLead) {
t.Fatal("a gap the person chose carried the turn on")
}
if saved, err := store.List(); err != nil || len(saved) != 0 {
t.Fatalf("a declined card saved %v (%v)", saved, err)
}
_ = collected
}
// aRepeatingReminder is the measured proposal: a line said every three hours.
func aRepeatingReminder() string {
body := map[string]any{
"op": "propose",
"title": "marketing slack",
"words": "check the marketing slack every 3 hours",
"when": map[string]any{"every": "3h", "words": "every 3 hours"},
"say": "check the marketing slack",
}
raw, _ := json.Marshal(body)
return string(raw)
}
func TestPersonCardAnswerLineNamesThePickedAnswer(t *testing.T) {
card := Question{
Kind: QuestionAutomation,
Head: "wants to schedule work",
Options: AutomationOptions(automation.Automation{Schedule: automation.Schedule{Every: "1h"}, Action: automation.Action{Do: "run the tests"}}),
}
declined := personCardAnswerLine(card, Answer{Kind: QuestionAutomation, Key: DeclineKey, DecidedBy: DecidedByPerson})
if declined != `the person answered the card "wants to schedule work": Don't save` {
t.Fatalf("decline line = %q", declined)
}
consent := Question{
Kind: QuestionConsent, Head: "may I run the tests",
Options: AnswerOptions(QuestionConsent),
}
allowed := personCardAnswerLine(consent, Answer{Kind: QuestionConsent, Key: "1", DecidedBy: DecidedByPerson})
if allowed != `the person answered the card "may I run the tests": allow once` {
t.Fatalf("consent line = %q", allowed)
}
if personCardAnswerLine(consent, Answer{Kind: QuestionConsent, Key: "1", DecidedBy: DecidedByDial}) != "" {
t.Fatal("a dial's answer was written as the person's")
}
}