Why: roles had no way to carry operational know-how into executions (RoleSpec.Skills never reached the backend), and the system prompt was assembled ad hoc with no environment facts or skill index. Evidence: go vet ./... clean; go test ./... 12 packages ok. Skills cases cover directory-authoritative naming, optional frontmatter description extraction, invalid dir skipping, root priority on name clash, missing-root tolerance, fail-closed unknown-skill loads, 64 KiB body / 200-byte description caps, load_skill registration only when skills exist, and end-to-end adapter wiring (index in system prompt, declared-skill validation failing closed on ghost skills, workspace .agent/skills auto-merge). promptx cases pin section order, sorted index, reasoning-profile sentence, and env defaults. Impact: new runtime/skills + runtime/promptx packages; native.Options gains SkillsRoots; adapters/native system prompt now composes instructions + environment (date/model/workspace/profile) + declared skills + index + deliverables; declared-but-missing skills fail the step (configuration error, not silent). Refs: plan B6; RoleSpec.Skills plumbing lands with the squad batch.
63 lines
1.9 KiB
Go
63 lines
1.9 KiB
Go
package promptx
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestComposeSections(t *testing.T) {
|
|
out := Compose(Parts{
|
|
Instructions: "你是审校员",
|
|
SkillsDeclared: []string{"docx-author"},
|
|
SkillsIndex: []IndexEntry{
|
|
{Name: "xlsx-author", Description: "表格交付"},
|
|
{Name: "docx-author", Description: "文档交付"},
|
|
},
|
|
Deliverables: []string{"output.md"},
|
|
Env: EnvFacts{Date: "2026-10-01(周四)", Model: "m1", WorkspaceRoot: "/w", Profile: "workspace"},
|
|
})
|
|
for _, want := range []string{
|
|
"你是审校员",
|
|
"## Environment",
|
|
"date: 2026-10-01(周四)",
|
|
"model: m1",
|
|
"workspace: /w",
|
|
"tool profile: workspace",
|
|
"## Skills",
|
|
"Declared for this task",
|
|
"docx-author",
|
|
"- docx-author — 文档交付", // 索引按名排序
|
|
"- xlsx-author — 表格交付",
|
|
"Deliverable files: output.md.",
|
|
"Use workspace tools to write those files.",
|
|
} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("missing %q in composed prompt:\n%s", want, out)
|
|
}
|
|
}
|
|
// 排序断言:docx 行在 xlsx 行之前。
|
|
if strings.Index(out, "- docx-author") > strings.Index(out, "- xlsx-author") {
|
|
t.Errorf("index must be sorted by name:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestComposeReasoningProfileAndMinimal(t *testing.T) {
|
|
out := Compose(Parts{Deliverables: []string{"out.md"}, Env: EnvFacts{Profile: "reasoning"}})
|
|
if !strings.Contains(out, "Reply with the deliverable text only. Tools are disabled.") {
|
|
t.Fatalf("reasoning sentence missing:\n%s", out)
|
|
}
|
|
if strings.Contains(out, "## Environment") && !strings.Contains(out, "date:") {
|
|
t.Fatalf("unexpected env block:\n%s", out)
|
|
}
|
|
if strings.Contains(out, "## Skills") {
|
|
t.Fatalf("no skills should render no skill block:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestComposeEnvDefaultsToDate(t *testing.T) {
|
|
out := Compose(Parts{Deliverables: []string{"o.md"}})
|
|
if !strings.Contains(out, "date: ") {
|
|
t.Fatalf("env should default to today's date:\n%s", out)
|
|
}
|
|
}
|