Layered prompt rendering from embedded templates
Skill prompts are text/template files embedded from internal/ai/skills/prompts/, one per skill version (<skill>.v<version>.tmpl) defining "instructions" and "user". Templates are parsed at start with missingkey=error; a bad template stops the service. harness.Render builds the system prompt in fixed order (D-007): [1] the admin persona from ai_settings.system_prompt (Config.Persona), [2] the skill instructions, [3] the household context block, [4] the output contract quoting the JSON Schema. The contract is always the last layer whatever the persona says. The household context is a seam of titled sections until BuildContext lands; an empty one renders a defined block. Skill.Render is replaced by Skill.PromptInput. Golden tests pin the exact system prompt and user message for the echo test skill, with and without persona, and under a hostile persona that says "ignore the format below" or fakes a contract heading. checksums.txt records each template's SHA-256; a template change without a version bump fails TestTemplateChecksums naming the skill and version. Maestro task bceca7a8-bc30-48df-944d-84557a1f4154. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,11 @@ func TestRunValidFirstTime(t *testing.T) {
|
||||
t.Fatalf("Echo = %q, want post-processed %q", out.Echo, "taco")
|
||||
}
|
||||
req := assertCalls(t, &f, 1)[0]
|
||||
if req.System != "You repeat words." || len(req.Messages) != 1 ||
|
||||
want, err := Render("", echoSkill{}, echoInput{Word: "taco"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if req.System != want.System || len(req.Messages) != 1 ||
|
||||
req.Messages[0] != (llm.Message{Role: llm.RoleUser, Content: "Repeat: taco"}) {
|
||||
t.Fatalf("request prompt = %+v", req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user