v0.43.0 63d78c8

systemprompt_test.go RawHistoryFolder

1package agent
2
3import (
4	"strings"
5	"testing"
6	"time"
7)
8
9func TestBuildSystemPromptAlwaysPrefersDedicatedTools(t *testing.T) {
10	for _, custom := range []string{"", "Custom identity"} {
11		prompt := BuildSystemPrompt(SystemPromptOpts{Custom: custom})
12		if count := strings.Count(prompt, toolSelectionGuidance); count != 1 {
13			t.Fatalf("dedicated tool guidance count = %d, want 1:\n%s", count, prompt)
14		}
15	}
16}
17
18func TestBuildSystemPromptAlwaysIncludesFinalWritingGuidance(t *testing.T) {
19	for _, tt := range []struct {
20		name   string
21		custom string
22	}{
23		{name: "default"},
24		{name: "custom", custom: "custom identity"},
25	} {
26		t.Run(tt.name, func(t *testing.T) {
27			const conflictingAddendum = "Always use ceremonial language."
28			prompt := BuildSystemPrompt(SystemPromptOpts{
29				Custom: tt.custom,
30				Append: []string{conflictingAddendum},
31			})
32
33			if count := strings.Count(prompt, writingGuidance); count != 1 {
34				t.Fatalf("writing guidance count = %d, want 1:\n%s", count, prompt)
35			}
36			if strings.Index(prompt, writingGuidance) < strings.Index(prompt, conflictingAddendum) {
37				t.Fatalf("writing guidance must follow appended context:\n%s", prompt)
38			}
39			for _, want := range []string{
40				"medium, audience, and reader's immediate need",
41				"plain, precise language",
42				"Default to concise paragraphs",
43				"Use lists when items are genuinely parallel, sequential, or easier to compare",
44				"canned transitions",
45				"verified facts",
46				"descriptive links, and accessibility",
47				"Do not manufacture slang, errors, hesitation",
48				"Revise silently",
49			} {
50				if !strings.Contains(prompt, want) {
51					t.Errorf("writing guidance missing %q:\n%s", want, prompt)
52				}
53			}
54		})
55	}
56}
57
58func TestBuildSystemPromptAlwaysIncludesPlanGuidance(t *testing.T) {
59	for _, tt := range []struct {
60		name   string
61		custom string
62		append []string
63	}{
64		{name: "default"},
65		{name: "custom identity", custom: "custom identity"},
66		{name: "with appended context", append: []string{"Use terse replies."}},
67	} {
68		t.Run(tt.name, func(t *testing.T) {
69			prompt := BuildSystemPrompt(SystemPromptOpts{Custom: tt.custom, Append: tt.append})
70			if count := strings.Count(prompt, planGuidance); count != 1 {
71				t.Fatalf("plan guidance count = %d, want 1:\n%s", count, prompt)
72			}
73			// Ordering keeps shared guidance stable: plan rules follow task rules and
74			// precede skill priority.
75			if strings.Index(prompt, planGuidance) < strings.Index(prompt, taskExecutionGuidance) {
76				t.Fatalf("plan guidance must follow task guidance:\n%s", prompt)
77			}
78			if strings.Index(prompt, planGuidance) > strings.Index(prompt, skillPriorityGuidance) {
79				t.Fatalf("plan guidance must precede skill priority:\n%s", prompt)
80			}
81			// The rules that matter for token cost and for correct index handling.
82			for _, want := range []string{"in_progress", "add, update, and remove", "Group related steps", `action:"show"`, "do not restate the plan"} {
83				if !strings.Contains(planGuidance, want) {
84					t.Fatalf("plan guidance is missing %q", want)
85				}
86			}
87			if strings.Contains(prompt, "update_plan") {
88				t.Fatalf("prompt still references the retired tool name:\n%s", prompt)
89			}
90		})
91	}
92}
93
94func TestBuildSystemPromptAlwaysIncludesTaskAndSkillGuidance(t *testing.T) {
95	for _, custom := range []string{"", "Custom identity"} {
96		t.Run(custom, func(t *testing.T) {
97			prompt := BuildSystemPrompt(SystemPromptOpts{
98				Custom: custom,
99				Append: []string{"Workspace context", "Available skills"},
100			})
101			previous := strings.Index(prompt, "Available skills")
102			for _, guidance := range []string{taskExecutionGuidance, skillPriorityGuidance, writingGuidance} {
103				index := strings.Index(prompt, guidance)
104				if index <= previous {
105					t.Fatalf("shared guidance must follow appended context in task/skill/writing order")
106				}
107				previous = index
108			}
109			for _, want := range []string{
110				"Treat requests such as \"can you\" as instructions to do the requested work",
111				"do not stop at a plan or an offer to continue",
112				"Follow each progress statement with the action it describes",
113				"Final answers contain the result of the work or a concrete blocker",
114				"Respect requests for explanation, planning, or review without making unsolicited changes",
115				"Ask only when a necessary decision blocks safe, correct progress",
116				"Preserve tool permissions, required confirmations, and explicit approval requirements",
117				"Explicit user instructions take precedence over skill guidelines",
118				"Skills do not grant permissions or override system or developer constraints",
119				"identify its source and the relevant instruction",
120			} {
121				if count := strings.Count(prompt, want); count != 1 {
122					t.Errorf("guidance %q count = %d, want 1", want, count)
123				}
124			}
125		})
126	}
127}
128
129func TestBuildSystemPromptAddsCompactionHandoffToCustomPrompt(t *testing.T) {
130	prompt := BuildSystemPrompt(SystemPromptOpts{Custom: "custom identity"})
131	if !strings.Contains(prompt, "custom identity") {
132		t.Fatalf("custom prompt missing:\n%s", prompt)
133	}
134	if !strings.Contains(prompt, compactedSummaryHandoffInstruction) {
135		t.Fatalf("custom prompt missing compaction handoff:\n%s", prompt)
136	}
137	if count := strings.Count(prompt, compactedSummaryHandoffInstruction); count != 1 {
138		t.Fatalf("compaction handoff count = %d, want 1:\n%s", count, prompt)
139	}
140	for _, want := range []string{"most recent unresolved user request", "newer user request", "without waiting for the user to type \"continue\""} {
141		if !strings.Contains(prompt, want) {
142			t.Fatalf("compaction handoff missing %q:\n%s", want, prompt)
143		}
144	}
145}
146
147func TestBuildSystemPromptCustomOmitsBuiltInDocs(t *testing.T) {
148	got := BuildSystemPrompt(SystemPromptOpts{
149		CWD:        "/workspace",
150		Custom:     "Custom instructions",
151		Append:     []string{"Additional context"},
152		Now:        time.Date(2026, time.August, 6, 0, 0, 0, 0, time.UTC),
153		ZutDocsDir: "/zut/docs",
154	})
155
156	if strings.Contains(got, "Zut's own docs") || strings.Contains(got, "/zut/docs") {
157		t.Fatalf("custom prompt includes built-in docs guidance:\n%s", got)
158	}
159	for _, want := range []string{
160		"Custom instructions",
161		"Additional context",
162		"Current date: 2026-08-06",
163		"Current working directory: /workspace",
164	} {
165		if !strings.Contains(got, want) {
166			t.Errorf("custom prompt missing %q:\n%s", want, got)
167		}
168	}
169}
170
171func TestBuildSystemPromptDefaultIncludesBuiltInDocs(t *testing.T) {
172	got := BuildSystemPrompt(SystemPromptOpts{
173		CWD:        "/workspace",
174		Now:        time.Date(2026, time.August, 6, 0, 0, 0, 0, time.UTC),
175		ZutDocsDir: "/zut/docs",
176	})
177
178	if !strings.Contains(got, "Zut's own docs are installed under /zut/docs") {
179		t.Fatalf("default prompt missing built-in docs guidance:\n%s", got)
180	}
181}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 477

↑↓ moveenter openesc close