返回 DeepSeek-Reasonix
subagent_context_test.go
根目录 / internal / agent / subagent_context_test.go
1 package agent
2
3 import (
4 "context"
5 "path/filepath"
6 "strings"
7 "testing"
8
9 "reasonix/internal/evidence"
10 )
11
12 func TestComposeChildTaskPromptUsesFactsPack(t *testing.T) {
13 got := composeChildTaskPrompt(ProfileExecSpec{
14 Task: TaskSpec{Objective: "review the gate"},
15 Context: ContextRequest{
16 Decisions: []acceptedDecision{{ID: "dec-1", Question: "ship?", Answer: "yes"}},
17 EvidenceSummary: "tests passed",
18 FileAnchors: []string{"internal/agent/ask.go"},
19 OutputFormat: "verdict only",
20 },
21 })
22 for _, want := range []string{"## Task", "review the gate", "dec-1", "tests passed", "ask.go", "verdict only", "Do not copy"} {
23 if !strings.Contains(got, want) {
24 t.Fatalf("missing %q in:\n%s", want, got)
25 }
26 }
27 }
28
29 func TestApplyReviewBudgetDefaults(t *testing.T) {
30 spec := ProfileExecSpec{Worker: WorkerSpec{Profile: "review"}}
31 applyReviewBudget(&spec)
32 if spec.Sched.MaxSteps != defaultReviewMaxSteps || spec.Sched.MaxOutputTokens != defaultReviewOutputTokens {
33 t.Fatalf("budget = %+v", spec.Sched)
34 }
35 }
36
37 func TestFillChildFactsFromParentTurnAndLedger(t *testing.T) {
38 turn := &turnRuntime{}
39 turn.loop.rememberDecision("dec-9", "ship?", "yes")
40 ledger := evidence.NewLedger()
41 ledger.Record(evidence.Receipt{
42 ToolName: "edit_file", Success: true, Write: true, Mutation: true,
43 Paths: []string{"internal/agent/ask.go"},
44 })
45 ctx := withTurnState(evidence.WithLedger(context.Background(), ledger), turn)
46 spec := ProfileExecSpec{Task: TaskSpec{Objective: "review the gate"}}
47 fillChildFacts(ctx, &spec)
48 if len(spec.Context.Decisions) != 1 || spec.Context.Decisions[0].ID != "dec-9" {
49 t.Fatalf("decisions = %+v", spec.Context.Decisions)
50 }
51 if !strings.Contains(spec.Context.EvidenceSummary, "1 successful") {
52 t.Fatalf("summary = %q", spec.Context.EvidenceSummary)
53 }
54 if len(spec.Context.FileAnchors) != 1 || filepath.ToSlash(spec.Context.FileAnchors[0]) != "internal/agent/ask.go" {
55 t.Fatalf("anchors = %v", spec.Context.FileAnchors)
56 }
57 prompt := composeChildTaskPrompt(spec)
58 if strings.Contains(prompt, "parent session") && !strings.Contains(prompt, "Do not copy") {
59 t.Fatalf("prompt missing isolation note:\n%s", prompt)
60 }
61 }
62
63 func TestChildMaxStepsForSpecStampsReviewOutputBudget(t *testing.T) {
64 task := &TaskTool{}
65 spec := ProfileExecSpec{Worker: WorkerSpec{Profile: "review"}}
66 ctx, steps := task.childMaxStepsForSpec(context.Background(), &spec)
67 if steps != defaultReviewMaxSteps {
68 t.Fatalf("steps = %d", steps)
69 }
70 if childOutputBudgetFrom(ctx) != defaultReviewOutputTokens {
71 t.Fatalf("output budget = %d", childOutputBudgetFrom(ctx))
72 }
73 opts := task.subagentOptions(ctx, steps, nil, 0, 1, "", nil)
74 if opts.MaxOutputTokens != defaultReviewOutputTokens {
75 t.Fatalf("child options max output = %d", opts.MaxOutputTokens)
76 }
77 }
78
79 func TestPrepareReviewSubagentContextAddsBoundedVerifiedFacts(t *testing.T) {
80 ledger := evidence.NewLedger()
81 exit := 0
82 ledger.Record(evidence.Receipt{
83 ToolName: "go_test", Success: true, Read: true, Paths: []string{"z.go", "a.go"},
84 OutputBytes: 42, OutputDigest: "0123456789abcdef", ExitCode: &exit, Verification: evidence.VerificationPassed,
85 })
86 prompt, steps, tokens, ok := PrepareReviewSubagentContext(evidence.WithLedger(context.Background(), ledger), "review", "review change")
87 if !ok || steps != defaultReviewMaxSteps || tokens != defaultReviewOutputTokens {
88 t.Fatalf("review budget = ok:%v steps:%d tokens:%d", ok, steps, tokens)
89 }
90 for _, want := range []string{"tool=go_test", "output_bytes=42", "output_digest=0123456789ab", "verification=passed", "a.go", "verdict"} {
91 if !strings.Contains(prompt, want) {
92 t.Fatalf("review prompt missing %q:\n%s", want, prompt)
93 }
94 }
95 if _, _, _, ok := PrepareReviewSubagentContext(context.Background(), "explore", "look"); ok {
96 t.Fatal("non-review profile must retain its existing runner budget")
97 }
98 }
99
99 lines GO